v18i028: Mail user's shell version 6.4, Part06/19
Rich Salz
rsalz at bbn.com
Thu Mar 16 06:27:09 AEST 1989
Submitted-by: Dan Heller <island!argv at sun.com>
Posting-number: Volume 18, Issue 28
Archive-name: mush6.4/part06
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 6 (of 19)."
# Contents: curs_io.c pick.c select.c viewopts.c
# Wrapped by rsalz at papaya.bbn.com on Mon Mar 13 19:25:12 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'curs_io.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'curs_io.c'\"
else
echo shar: Extracting \"'curs_io.c'\" \(12588 characters\)
sed "s/^X//" >'curs_io.c' <<'END_OF_FILE'
X/* @(#)curs_io.c (c) copyright 3/18/87 (Dan Heller) */
X
X/* curs_io.c -- curses based I/O */
X#include "mush.h"
X#include "bindings.h"
X
Xstatic backspace();
X
Xchar *_unctrl[] = {
X "^@", "^A", "^B", "^C", "^D", "^E", "^F", "^G", "^H", "^I", "^J", "^K",
X "^L", "^M", "^N", "^O", "^P", "^Q", "^R", "^S", "^T", "^U", "^V", "^W",
X "^X", "^Y", "^Z", "^[", "^\\", "^]", "^~", "^_",
X " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-",
X ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";",
X "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I",
X "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
X "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e",
X "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
X "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "^?"
X};
X
Xchar del_line; /* tty delete line character */
Xchar del_word; /* tty delete word character */
Xchar del_char; /* backspace */
Xchar reprint_line; /* usually ^R */
Xchar eofc; /* usually ^D */
Xchar lit_next; /* usually ^V */
X
Xtty_settings()
X{
X savetty();
X
X#ifdef SYSV
X eofc = _tty.c_cc[VEOF];
X#else
X#ifdef BSD
X if (ioctl(0, TIOCGETC, &tchars) != -1)
X eofc = tchars.t_eofc;
X else
X#endif /* BSD */
X eofc = CTRL(D);
X#endif /* SYSV */
X
X if (!isatty(0)) {
X del_line = CTRL(U);
X del_char = CTRL(H);
X } else {
X del_line = _tty.sg_kill;
X del_char = _tty.sg_erase;
X }
X
X#ifdef TIOCGLTC
X if (ioctl(0, TIOCGLTC, <chars) != -1) {
X del_word = ltchars.t_werasc;
X reprint_line = ltchars.t_rprntc;
X lit_next = ltchars.t_lnextc;
X } else
X#endif /* TIOCGLTC */
X {
X del_word = CTRL(W);
X reprint_line = CTRL(R);
X lit_next = CTRL(V);
X }
X}
X
X#ifdef Addch
X#undef Addch
X#endif /* Addch */
X
X#ifndef CURSES
X
X/* Make sure all ifs have matching elses! */
X
X#define Addch(c) \
X if (ison(glob_flags, ECHO_FLAG)) \
X {;} \
X else \
X fputc(c, stdout), fflush(stdout)
X
X#else
X
X/* see end of Getstr */
X#define Addch(c) \
X if (iscurses) \
X addch(c), refresh(); \
X else if (ison(glob_flags, ECHO_FLAG)) \
X {;} \
X else \
X fputc(c, stdout), fflush(stdout)
X#endif /* CURSES */
X
X/*
X * get a string of at most 'length' chars.
X * allow backspace-space-backspace, kill word and kill line
X * (options set by user in stty).
X * length is the max length this string can get. offset is from beginning
X * of string.
X * input of ^D returns -1; otherwise, return the number of chars in string.
X */
XGetstr(String, length, offset)
Xchar String[];
Xregister int length;
X{
X register int c, literal_next = FALSE, lit_bs = FALSE;
X struct cmd_map *curr_map;
X int count = offset, save_wc = wrapcolumn;
X
X fflush(stdout); /* make sure everything is flushed before getting input */
X
X if (mac_hide) {
X curr_map = NULL_MAP;
X wrapcolumn = 0;
X } else if (ison(glob_flags, IS_GETTING))
X curr_map = bang_map;
X else if (iscurses)
X curr_map = NULL_MAP;
X else
X curr_map = line_map;
X
X while ((c = m_getchar()) != '\n' && c != '\r' && c != EOF &&
X isoff(glob_flags, WAS_INTR)) {
X /* echo isn't set, so whatever the character, enter it */
X if (ison(glob_flags, QUOTE_MACRO) || ison(glob_flags, ECHO_FLAG)) {
X if (count < length) {
X String[count++] = c;
X /* Note: Addch includes ECHO_FLAG test */
X if (iscntrl(c)) {
X Addch('^');
X Addch(_unctrl[c][1]);
X } else
X Addch(c);
X } else {
X print("Warning: string too long. Truncated at %d chars.\n",
X length);
X break;
X }
X }
X /* ^D as the first char on a line or two ^D's in a row is EOF */
X else if (c == eofc && !count)
X break;
X else if (c == '\\' && count < length) {
X literal_next = TRUE, lit_bs = FALSE;
X Addch(String[count++] = '\\');
X } else if (c == lit_next && count < length) {
X literal_next = lit_bs = TRUE;
X String[count++] = '\\';
X if (!in_macro()) {
X /* if (iscntrl(c)) */
X Addch('^');
X /* Addch(_unctrl[c][1]); */
X }
X } else if (literal_next) {
X struct cmd_map *list;
X
X literal_next = FALSE;
X if (iscntrl(c) || c == del_line || c == del_char || c == del_word
X || c == lit_next || lit_bs)
X if (!in_macro() || !lit_bs)
X backspace(String, &count);
X else
X --count;
X else if (in_macro() && c == MAC_LONG_CMD)
X --count;
X /* check to see if user is escaping a map or map! */
X else
X for (list = curr_map; list; list = list->m_next)
X if (list->m_str[0] == c) {
X if (!in_macro())
X backspace(String, &count);
X else
X --count;
X break;
X }
X /* A literal-next advances the macro offset */
X String[count++] = c;
X if (iscntrl(c) || c == del_char) {
X if (iscntrl(c)) {
X /*
X * Decrement wrapcolumn because two chars added.
X * It will be restored from save_wc before return.
X */
X if (wrapcolumn > 1)
X wrapcolumn--;
X Addch('^');
X }
X Addch(_unctrl[c][1]);
X } else
X Addch(c);
X } else if (c == del_line) {
X if (count) {
X do
X backspace(String, &count);
X while (count);
X }
X } else
X if (c == reprint_line)
X String[count] = 0, wprint("\n%s", String);
X else
X if (c == del_word) /* word erase */
X while (count) {
X backspace(String, &count);
X if (!count ||
X isspace(String[count-1]) && !isspace(String[count]) ||
X !isalnum(String[count-1]) && isalnum(String[count]))
X break;
X }
X else if (c == del_char || c == CTRL(H) || c == 127 /* CTRL(?) */) {
X if (count)
X backspace(String, &count);
X /* if iscurses, then backspacing too far cancels a function */
X else if (!count && iscurses && isoff(glob_flags, LINE_MACRO)) {
X mac_flush();
X String[0] = '\0';
X wrapcolumn = save_wc;
X return -1;
X }
X } else if (count == length)
X bell();
X else if (c == '\t')
X do {
X /* Yuck -- tabs break map! */
X Addch(' ');
X String[count] = ' ';
X } while (++count % 8 && count < length);
X else if (in_macro() && c == MAC_LONG_CMD) {
X char cbuf[MAX_LONG_CMD + 1];
X
X if ((c = read_long_cmd(cbuf)) == 0) {
X c = MAC_LONG_CMD;
X goto check_expand; /* How could I avoid this? */
X } else if (c > 0) {
X int ok;
X
X String[count] = '\0';
X if ((ok = reserved_cmd(cbuf, TRUE)) > 0) {
X /* Reprint the line */
X if (iscurses)
X print(":%s", String);
X else
X wprint("\r%s", String);
X continue; /* Get next char without changing count */
X } else if (ok < 0) {
X String[offset] = '\0';
X wrapcolumn = save_wc;
X return ok;
X } else
X goto push_back;
X } else {
X /*
X * Ooops. We read a bunch of stuff we should not
X * have read, because this isn't really a long command.
X * Use a trick to push the whole thing back, ala ungetc.
X * Wouldn't it be nifty if stdio worked this way? :-)
X */
Xpush_back:
X if (c > 0) {
X cbuf[c++] = MAC_LONG_END;
X cbuf[c] = '\0';
X }
X c = MAC_LONG_CMD;
X Ungetstr(cbuf);
X goto check_expand; /* How could I avoid this goto? */
X }
X } else {
Xcheck_expand:
X if (!curr_map || !check_map(c, curr_map)) {
X /* else if (match != MATCH) */
X if (c != '\t' && iscntrl(c)) {
X Addch('^');
X Addch(_unctrl[c][1]);
X /* Decrement wrapcolumn as above */
X if (wrapcolumn > 1)
X wrapcolumn--;
X } else
X Addch(c);
X String[count++] = c;
X }
X }
X /* Null-terminate for macro lookup purposes.
X * This will be overwritten by the next character.
X */
X String[count] = '\0';
X if (line_wrap(String, &count))
X break;
X }
X fflush(stdout); /* for sys-v folks */
X
X if (c == eofc || c == EOF || ison(glob_flags, WAS_INTR)) {
X if (feof(stdin))
X clearerr(stdin);
X wrapcolumn = save_wc;
X return -1;
X }
X if (count && String[count-1] == '\\') {
X int count2;
X if (isoff(glob_flags, ECHO_FLAG))
X putchar('\n');
X wrapcolumn = save_wc;
X /*
X * NOTE: If the offset passed here is ever made greater than 0,
X * the value of wrapcolumn must again be changed/restored ...
X */
X if ((count2 = Getstr(&String[count-1], length - count + 1, 0)) == -1)
X return -1;
X return count + count2;
X }
X if (!iscurses && isoff(glob_flags, ECHO_FLAG))
X putchar('\n');
X /* Should be null-terminated already, but just in case */
X String[count] = '\0';
X wrapcolumn = save_wc;
X return count;
X}
X
Xstatic
Xbackspace(str, n)
Xregister char *str;
Xint *n;
X{
X (*n)--;
X Addch('\b'); Addch(' '); Addch('\b');
X if (iscntrl(str[*n])) {
X Addch('\b'); Addch(' '); Addch('\b');
X /* Re-increment wrapcolumn -- see Getstr */
X if (wrapcolumn)
X wrapcolumn++;
X }
X}
X
X#undef Addch
X
X/*
X * Check to see if what the user is typing is supposed to be expanded
X * into a longer string. The first char is 'c' and the map list to use
X * is in map_list. Continue looping (reading chars from stdin or a
X * currently active mapping) until a match happens or we've determined
X * that there is no match.
X */
Xcheck_map(c, map_list)
Xchar c;
Xstruct cmd_map *map_list;
X{
X char mbuf[MAX_MACRO_LEN], *p = mbuf;
X struct cmd_map *list;
X int m, n, match;
X
X *p++ = c;
X
X while (isoff(glob_flags, WAS_INTR)) {
X m = 0;
X *p = 0; /* make sure it's null terminated */
X /*
X * loop thru the list of maps and check to see if the typed
X * char matches the mapping. If it matches completely, substitute
X * the stuff in x_str and return. If a partial match occurs, then
X * read the next char until a timeout or no match.
X */
X for (list = map_list; list; list = list->m_next) {
X if ((match = prefix(mbuf, list->m_str)) == MATCH) {
X /* Must turn on flags BEFORE pushing */
X line_macro(list->x_str);
X return 1;
X } else if (match != NO_MATCH)
X m++; /* something matched partially */
X }
X if (!m)
X break;
X /* see if there's anything on the queue to read... */
X if (mac_pending()
X#ifdef FIONREAD
X || !ioctl(0, FIONREAD, &n) && n > 0
X#endif /* FIONREAD */
X )
X *p++ = m_getchar();
X else {
X /* The user has typed the first part of a map or macro. Give him
X * a chance to finish it.
X */
X#if defined(BSD) || defined(SELECT)
X /* If the system has select(), use it. It's much faster and
X * more aesthetic since there is no mandatory timeout.
X */
X struct timeval timer;
X#ifdef FD_SET
X fd_set rmask, wmask, xmask;
X FD_SET(0, &rmask); /* Test stdin for read */
X FD_ZERO(&wmask); /* Don't care about write */
X FD_ZERO(&xmask); /* Don't care about exception */
X#else
X int rmask = 1, wmask = 0, xmask = 0;
X#endif /* FD_SET */
X timer.tv_sec = 1;
X timer.tv_usec = 0;
X n = select(1, &rmask, &wmask, &xmask, &timer);
X#else /* !SELECT */
X#ifdef FIONREAD
X /* system doesn't have select(), so use FIONREAD to see if
X * there are any chars on the queue to read.
X */
X (void) sleep(1);
X (void) ioctl(0, FIONREAD, &n);
X#else
X /* system has neither select() nor FIONREAD, so just set n
X * and force the user to either complete the map or fail it
X * without a timeout. Chars won't echo till he does one or
X * the other.
X */
X n = 1;
X#endif /* FIONREAD */
X#endif /* SELECT */
X if (n > 0)
X /* don't read all 'n' chars -- there may be a match early */
X *p++ = m_getchar(); /* To flush macros and reset flags */
X else /* still nothing to read? User doesn't want to use map */
X break;
X }
X }
X /* no match or a timeout. This isn't a map, just return. */
X *p = 0;
X if (mbuf[1])
X mac_push(mbuf + 1);
X return 0;
X}
X
X/*
X * Check for line wrap. This should happen only in composition mode and
X * only when the variable wrapcolumn has a value greater than zero. Line
X * wrap is implemented using Ungetstr [that is, mac_push()].
X *
X * Returns 1 if the line was wrapped, 0 if not.
X */
Xline_wrap(string, count)
Xchar *string; /* The string to be wrapped */
Xint *count; /* Offset of string terminator */
X{
X char *tail = NULL;
X int n = *count;
X
X if (wrapcolumn < 1 || *count <= wrapcolumn
X || isoff(glob_flags, IS_GETTING) /* Wrap only in msg body */
X || ison(glob_flags, QUOTE_MACRO) /* Don't wrap quoted macros */
X || ison(glob_flags, ECHO_FLAG)) /* Can't wrap in echo mode */
X return 0;
X
X /* Back up past the wrapcolumn point */
X for (; n > wrapcolumn; --n)
X ;
X /* Look for a space */
X while (n && !isspace(string[n]))
X --n;
X /* If no break found, return no wrap */
X if (!n)
X return 0;
X tail = &string[n+1];
X /* Skip the break char and any whitespace */
X while (n && isspace(string[n]))
X --n;
X ++n; /* move back into the whitespace */
X /* Erase the stuff that will wrap */
X while (*count > n)
X backspace(string,count);
X string[*count] = '\0';
X /* Push the tail, if any */
X if (*tail)
X Ungetstr(tail);
X return 1;
X}
END_OF_FILE
if test 12588 -ne `wc -c <'curs_io.c'`; then
echo shar: \"'curs_io.c'\" unpacked with wrong size!
fi
# end of 'curs_io.c'
fi
if test -f 'pick.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'pick.c'\"
else
echo shar: Extracting \"'pick.c'\" \(13226 characters\)
sed "s/^X//" >'pick.c' <<'END_OF_FILE'
X/* @(#)pick.c 2.4 (c) copyright 10/18/86 (Dan Heller) */
X
X#include "mush.h"
X
Xstatic int before, after, search_from, search_subj, search_to, xflg, icase;
Xstatic mdy[3], search_hdr[64];
Xstatic int pick();
X
Xdo_pick(n, argv, list)
Xregister int n;
Xregister char **argv, list[];
X{
X char ret_list[MAXMSGS_BITS];
X
X if (n > 1 && !strcmp(argv[1], "-?"))
X return help(0, "pick", cmd_help);
X
X /* if is_pipe, then the messages to search for are already set.
X * if not piped, then reverse the bits for all message numbers.
X * That is, search EACH message. only those matching will be returned.
X */
X if (isoff(glob_flags, IS_PIPE))
X bitput(list, list, msg_cnt, =~) /* macro, turn on all bits */
X clear_msg_list(ret_list);
X if (pick(n, argv, list, ret_list, isoff(glob_flags, DO_PIPE)) == -1)
X return -1;
X if (istool && isoff(glob_flags, DO_PIPE))
X print("Messages: ");
X for (n = 0; n < msg_cnt; n++)
X if (msg_bit(ret_list, n) && !xflg ||
X !msg_bit(ret_list, n) && xflg) {
X if (isoff(glob_flags, DO_PIPE))
X if (istool)
X print_more("%d ", n+1);
X else
X print("%s\n", compose_hdr(n));
X set_msg_bit(list, n);
X } else
X unset_msg_bit(list, n);
X return 0;
X}
X
X/*
X * search for messages. Even if no messages match, return 0. Errors such
X * as internal errors or syntax errors, return -1.
X */
Xstatic int
Xpick(ret, argv, list, ret_list, verbose)
Xregister int ret;
Xregister char **argv, list[], ret_list[];
X{
X register char c;
X char pattern[256];
X int o_before = before, o_after = after, o_search_from = search_from,
X o_search_subj = search_subj, o_search_to = search_to, o_mdy[3], n;
X
X for (c = 0; c < 3; c++)
X o_mdy[c] = mdy[c];
X
X ret = -1;
X if (!msg_cnt) {
X print("No Messages.\n");
X goto bad;
X }
X
X icase = before = after = search_from = search_subj = xflg = 0;
X mdy[0] = search_hdr[0] = 0;
X while (*argv && *++argv && **argv == '-')
X switch(c = argv[0][1]) {
X /* users specifies a range */
X case 'r': {
X int X = 2;
X /* if not a pipe, then clear all bits cuz we only want
X * to search the message specified here...
X * If it is a pipe, then add to the messages searched for.
X */
X if (isoff(glob_flags, IS_PIPE))
X clear_msg_list(list);
X /* "-r10-15"
X * ^argv[1][2] if NULL, then
X * list detached from "r" e.g. "-r" "5-20"
X */
X if (!argv[0][X])
X argv++, X = 0;
X (*argv) += X;
X n = get_msg_list(argv, list);
X (*argv) -= X;
X if (n == -1)
X goto bad;
X argv += (n-1); /* we're going to increment another up top */
X }
X when 'a': {
X if ((n = ago_date(++argv)) == -1)
X goto bad;
X argv += n;
X }
X when 'd':
X if (!*++argv) {
X print("specify a date for -%c\n", c);
X goto bad;
X }
X if (!date1(*argv))
X goto bad;
X when 's' : case 'f': case 't': case 'h':
X if (search_subj + search_from + search_to + *search_hdr > 1) {
X print("specify one of `s', `f', `t' or `h' only\n");
X goto bad;
X }
X if (c == 's')
X search_subj = 1;
X else if (c == 'f')
X search_from = 1;
X else if (c == 'h')
X if (!*++argv)
X print("Specify header to search for.\n");
X else
X (void) lcase_strcpy(search_hdr, *argv);
X else
X search_to = 1;
X when 'x' : xflg = 1;
X when 'i' : icase = 1;
X otherwise:
X print("pick: unknown flag: %c\n", argv[0][1]);
X clear_msg_list(ret_list);
X return -1;
X }
X pattern[0] = 0;
X (void) argv_to_string(pattern, argv);
X if (verbose) {
X print_more("Searching for messages");
X if (mdy[1] == 0) {
X print(" that %scontain \"%s\"", (xflg)? "does not ": "",
X (*pattern)? pattern: "<previous expression>");
X if (search_subj)
X print_more(" in subject line");
X else if (search_from)
X print_more(" from author names");
X else if (search_to)
X print_more(" from the To: field");
X else if (search_hdr[0])
X print_more(" from the message header: \"%s:\"", search_hdr);
X } else {
X extern char *month_names[]; /* from dates.c */
X print_more(" dated ");
X if (before || after)
X if (xflg)
X print_more("%s ", (!before)? "before": "after");
X else
X print_more("on or %s ", (before)? "before": "after");
X print_more("%s. %d, 19%d.",
X month_names[mdy[0]], mdy[1], mdy[2]);
X }
X print_more("\n");
X }
X if (mdy[1] > 0 && icase)
X print("using date: -i flag ignored.\n");
X ret = find_pattern(pattern, list, ret_list);
Xbad:
X before = o_before, after = o_after, search_from = o_search_from;
X search_subj = o_search_subj, search_to = o_search_to;
X for (c = 0; c < 3; c++)
X mdy[c] = o_mdy[c];
X
X return ret;
X}
X
Xfind_pattern(p, check_list, ret_list)
Xregister char *p;
Xchar check_list[], ret_list[];
X{
X register int n, val, i; /* val is return value from regex or re_exec */
X long bytes = 0;
X char buf[HDRSIZ];
X static char *err = (char *)-1;
X#ifdef REGCMP
X char *regcmp(), *regex();
X#else /* REGCMP */
X char *re_comp();
X#endif /* REGCMP */
X
X if (p && *p == '\\')
X p++; /* take care of escaping special cases (`-', `\') */
X
X /* specify what we're looking for */
X if (p && *p) {
X if (icase)
X p = lcase_strcpy(buf, p);
X#ifdef REGCMP
X if (err && p)
X xfree(err);
X if (p && !(err = regcmp(p, NULL))) {
X print("regcmp error: %s\n", p);
X clear_msg_list(ret_list);
X return -1;
X }
X#else /* REGCMP */
X if (err = re_comp(p)) {
X print("re_comp error: %s\n", err);
X clear_msg_list(ret_list);
X return -1;
X }
X#endif /* REGCMP */
X } else if (err == (char *)-1 && mdy[1] <= 0) {
X print("No previous regular expression\n");
X clear_msg_list(ret_list); /* doesn't matter really */
X return -1;
X }
X /* start searching: set bytes, and message number: n */
X for (n = 0; n < msg_cnt; n++)
X if (msg_bit(check_list, n)) {
X if (mdy[1] > 0) {
X int msg_mdy[3];
X if (ison(glob_flags, DATE_RECV))
X p = msg[n].m_date_recv;
X else
X p = msg[n].m_date_sent;
X (void) sscanf(p, "%2d%2d%2d", /* yr mo da */
X &msg_mdy[2], &msg_mdy[0], &msg_mdy[1]);
X msg_mdy[0]--;
X Debug("checking %d's date: %d-%d-%d ",
X n+1, msg_mdy[0]+1, msg_mdy[1], msg_mdy[2]);
X /* start at year and wrap around.
X * only when match the day (4), check for == (match)
X */
X for (i = 2; i < 5; i++)
X if (before && msg_mdy[i%3] < mdy[i%3]
X || after && msg_mdy[i%3] > mdy[i%3]
X || i == 4 && (msg_mdy[i%3] == mdy[i%3])) {
X Debug("matched (%s).\n",
X (i == 2)? "year" : (i == 3)? "month" : "day");
X set_msg_bit(ret_list, n);
X break;
X } else if (msg_mdy[i%3] != mdy[i%3]) {
X Debug("failed.\n");
X break;
X }
X continue;
X }
X /* we must have the right date -- if we're searching for a
X * string, find it.
X */
X (void) msg_get(n, NULL, 0);
X bytes = 0;
X while (bytes < msg[n].m_size) {
X if (!search_subj && !search_from && !search_to &&
X !*search_hdr && !(p = fgets(buf, sizeof buf, tmpf)))
X break;
X else if (search_subj) {
X if (!(p = header_field(n, "subject")))
X break;
X } else if (search_from) {
X if (!(p = header_field(n, "from"))) {
X /*
X * Check for MSG_SEPARATOR here? Maybe not...
X */
X register char *p2;
X (void) msg_get(n, NULL, 0);
X if (!(p2 = fgets(buf, sizeof buf, tmpf)) ||
X !(p = index(p2, ' ')))
X continue;
X p++;
X if (p2 = any(p, " \t"))
X *p2 = 0;
X }
X } else if (search_to) {
X if (!(p = header_field(n, "to")) &&
X !(p = header_field(n, "apparently-to")))
X break;
X } else if (*search_hdr) {
X if (!(p = header_field(n, search_hdr)))
X break;
X }
X if (icase)
X p = lcase_strcpy(buf, p);
X#ifdef REGCMP
X val = !!regex(err, p, NULL); /* convert value to a boolean */
X#else /* REGCMP */
X val = re_exec(p);
X#endif /* REGCMP */
X if (val == -1) { /* doesn't apply in system V */
X print("Internal error for pattern search.\n");
X clear_msg_list(ret_list); /* it doesn't matter, really */
X return -1;
X }
X if (!val)
X bytes += strlen(p);
X else {
X set_msg_bit(ret_list, n);
X break;
X }
X if (search_subj || search_from || search_to || *search_hdr)
X break;
X }
X }
X return 0;
X}
X
X#ifdef CURSES
X/*
X * search for a pattern in composed message headers -- also see next function
X * flags == 0 forward search (prompt).
X * flags == -1 continue search (no prompt).
X * flags == 1 backward search (prompt).
X */
Xsearch(flags)
Xregister int flags;
X{
X register char *p;
X char pattern[128];
X register int this_msg = current_msg, val = 0;
X static char *err = (char *)-1, direction;
X SIGRET (*oldint)(), (*oldquit)();
X#ifdef REGCMP
X char *regcmp();
X#else /* REGCMP */
X char *re_comp();
X#endif /* REGCMP */
X
X if (msg_cnt <= 1) {
X print("Not enough messages to invoke a search.\n");
X return 0;
X }
X pattern[0] = '\0';
X if (flags == -1)
X print("continue %s search...", direction? "forward" : "backward");
X else
X print("%s search: ", flags? "backward" : "forward");
X if (flags > -1)
X if (Getstr(pattern, COLS-18, 0) < 0)
X return 0;
X else
X direction = !flags;
X#ifdef REGCMP
X if (err && *pattern)
X xfree(err);
X else if (err == (char *)-1 && !*pattern) {
X print("No previous regular expression.");
X return 0;
X }
X if (*pattern && !(err = regcmp(pattern, NULL))) {
X print("Error in regcmp in %s", pattern);
X return 0;
X }
X#else /* REGCMP */
X if (err = re_comp(pattern)) {
X print(err);
X return 0;
X }
X#endif /* REGCMP */
X move(LINES-1, 0), refresh();
X on_intr();
X
X do {
X if (direction)
X current_msg = (current_msg+1) % msg_cnt;
X else
X if (--current_msg < 0)
X current_msg = msg_cnt-1;
X p = compose_hdr(current_msg);
X#ifdef REGCMP
X val = !!regex(err, p, NULL); /* convert value to a boolean */
X#else /* REGCMP */
X val = re_exec(p);
X#endif /* REGCMP */
X if (val == -1) /* doesn't apply in system V */
X print("Internal error for pattern search.\n");
X } while (!val && current_msg != this_msg && isoff(glob_flags, WAS_INTR));
X
X if (ison(glob_flags, WAS_INTR)) {
X print("Pattern search interrupted.");
X current_msg = this_msg;
X } else if (val == 0)
X print("Pattern not found.");
X
X off_intr();
X return val;
X}
X#endif /* CURSES */
X
X/*
X * parse a user given date string and set mdy[] array with correct
X * values. Return 0 on failure.
X */
Xdate1(p)
Xregister char *p;
X{
X register char *p2;
X long t;
X int i;
X struct tm *today;
X
X if (*p == '-' || *p == '+') {
X before = !(after = *p == '+');
X skipspaces(1);
X }
X if (!isdigit(*p) && *p != '/') {
X print("syntax error on date: \"%s\"\n", p);
X return 0;
X }
X (void) time (&t);
X today = localtime(&t);
X for (i = 0; i < 3; i++)
X if (!p || !*p || *p == '/') {
X switch(i) { /* default to today's date */
X case 0: mdy[0] = today->tm_mon;
X when 1: mdy[1] = today->tm_mday;
X when 2: mdy[2] = today->tm_year;
X }
X if (p && *p)
X p++;
X } else {
X p2 = (*p)? index(p+1, '/') : NULL;
X mdy[i] = atoi(p); /* atoi will stop at the '/' */
X if (i == 0 && (--(mdy[0]) < 0 || mdy[0] > 11)) {
X print("Invalid month: %s\n", p);
X return 0;
X } else if (i == 1 && (mdy[1] < 1 || mdy[1] > 31)) {
X print("Invalid day: %s\n", p);
X return 0;
X }
X if (p = p2) /* set p to p2 and check to see if it's valid */
X p++;
X }
X return 1;
X}
X
X/*
X * Parse arguments specifying days/months/years "ago" (relative to today).
X * Legal syntax: -ago [+-][args]
X * where "args" is defined to be:
X * [0-9]+[ ]*[dD][a-Z]*[ ,]*[0-9]+[mM][a-Z]*[ ,]*[0-9]+[ ]*[yY][a-Z]*
X * 1 or more digits, 0 or more spaces, d or D followed by 0 or more chars,
X * 0 or more whitespaces or commas, repeat for months and years...
X * Examples:
X * 1 day, 2 months, 0 years
X * 2 weeks 1 year
X * 10d, 5m
X * 3w
X * 1d 1Y
X *
X * Return number of args parsed; -1 on error.
X */
Xago_date(argv)
Xchar **argv;
X{
X#define SECS_PER_DAY (60 * 60 * 24)
X#define SECS_PER_WEEK (SECS_PER_DAY * 7)
X#define SECS_PER_MONTH ((int)(SECS_PER_DAY * 30.5))
X#define SECS_PER_YEAR (SECS_PER_DAY * 365)
X register char *p;
X char buf[256];
X int n = 0, value;
X long t;
X struct tm *today;
X
X (void) argv_to_string(buf, argv);
X p = buf;
X (void) time (&t); /* get current time in seconds and subtract new values */
X if (*p == '-')
X before = TRUE;
X else if (*p == '+')
X after = TRUE;
X skipspaces(before || after);
X while (*p) {
X if (!isdigit(*p))
X break; /* really a syntax error, but it could be other pick args */
X p = my_atoi(p, &value); /* get 1 or more digits */
X skipspaces(0); /* 0 or more spaces */
X switch (lower(*p)) { /* d, m, or y */
X case 'd' : t -= value * SECS_PER_DAY;
X when 'w' : t -= value * SECS_PER_WEEK;
X when 'm' : t -= value * SECS_PER_MONTH;
X when 'y' : t -= value * SECS_PER_YEAR;
X otherwise: return -1;
X }
X for (p++; Lower(*p) >= 'a' && *p <= 'z'; p++)
X ; /* skip the rest of this token */
X while (*p == ',' || isspace(*p))
X ; /* 0 or more whitespaces or commas */
X }
X today = localtime(&t);
X mdy[0] = today->tm_mon;
X mdy[1] = today->tm_mday;
X mdy[2] = today->tm_year;
X
X /* Count the number of args parsed */
X for (n = 0; p > buf && *argv; n++)
X p -= (strlen(*argv++)+1);
X Debug("parsed %d args\n", n);
X return n;
X}
END_OF_FILE
if test 13226 -ne `wc -c <'pick.c'`; then
echo shar: \"'pick.c'\" unpacked with wrong size!
fi
# end of 'pick.c'
fi
if test -f 'select.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'select.c'\"
else
echo shar: Extracting \"'select.c'\" \(13038 characters\)
sed "s/^X//" >'select.c' <<'END_OF_FILE'
X/* select.c (c) copyright 1986 (Dan Heller) */
X
X/*
X * Routine which handle io (selection on file descriptors) between user and
X * the various windows.
X *
X * In toolmode, the user types characters and each character is interpreted
X * here and, if applicable, is sent to rite.c where it is appended to a
X * string similar to a tty driver and fgets. When the user types a '\n' the
X * rite() routine returns the string and we call add_to_letter to append the
X * string to the letter. Signals are caught here as well. that is the signal
X * characters setup by the user are checked and if one matches, call the signal
X * handling routine as if there were a real signal.
X *
X * Mouse handling is done here. See code for more detail.
X */
X#include "mush.h"
X
X#define READ_MSG (char *)'r'
X#define DEL_MSG (char *)'d'
X#define UNDEL_MSG (char *)'u'
X#define REPL_MSG (char *)'R'
X#define SAVE_MSG (char *)'s'
X#define PRNT_MSG (char *)'p'
X#define PRE_MSG (char *)'P'
X#define E_EDIT (char *)'e'
X#define E_VIEW (char *)'v'
X#define E_INCLUDE (char *)'i'
X#define E_SEND (char *)'S'
X#define E_ABORT (char *)'a'
X#define MENU_HELP (char *)'h'
X#define O_SAVE (char *)'s'
X#define O_QUIT (char *)'q'
X#define O_RSTR (char *)'r'
X
X#define N_MENU_ITEMS 8
X#define E_MENU_ITEMS 6
X
Xmsg_io(gfxsw, ibits, obits, ebits, timer)
Xregister struct gfxsubwindow *gfxsw;
Xregister int *ibits,*obits,*ebits;
Xstruct timeval **timer;
X{
X register char *p;
X struct inputevent event;
X static char lastchar;
X static int line, count;
X
X if (*ibits & ~(1 << gfxsw->gfx_windowfd)) {
X *ibits = *obits = *ebits = 0;
X return;
X }
X if (input_readevent(msg_sw->ts_windowfd, &event) == -1) {
X error("input event");
X return;
X }
X /*
X if (ID == LOC_WINENTER) {
X int x;
X struct inputmask im;
X win_getinputmask(gfxsw->gfx_windowfd, &im, &x);
X win_setinputmask(hdr_sw->ts_windowfd, &im, &im, x);
X }
X */
X if (ID >= KEY_LEFTFIRST)
X if (ison(glob_flags, IS_GETTING))
X print("Finish editing letter first");
X else
X (void) func_key(ID);
X else if (isascii(ID) && (msg_pix || ison(glob_flags, IS_GETTING) ||
X getting_opts)) {
X if (getting_opts) {
X /*
X * txt.x <= 5 indicates not to accept typed input for options
X * and function key setting.
X */
X if (txt.x > 5) {
X /* ^C, ^\ or ^U kills line */
X type_cursor(PIX_XOR);
X if (ID == tchars.t_intrc || ID == tchars.t_quitc ||
X ID == _tty.sg_kill) {
X rite(_tty.sg_kill), txt.x = 5;
X if (getting_opts == 1)
X option_line(line), display_opts(0);
X else
X set_key(0, 0, 0);
X } else if (p = rite((char)ID)) {
X /* if no string entered, preserve old value */
X if (*p && getting_opts == 1)
X add_opt(p, line);
X if (getting_opts == 2)
X set_key(p, 0,0);
X } else
X type_cursor(PIX_XOR);
X }
X }
X /*
X * This section MUST BE BEFORE the following "is_getting" section.
X * If user displays a message while editing a letter, he must hit 'q'
X * to return to edit mode. He may not edit a new letter while one is
X * already being edited.
X */
X else if (msg_pix)
X if (isdigit(ID)) {
X if (!isdigit(lastchar))
X count = 0;
X count = count * 10 + ID - '0';
X } else {
X /* scroll <count> lines */
X if (!count)
X count = 1;
X if (ID == 'k' || ID == 'K' || ID == '-')
X scroll_win(-count);
X else if (ID == '\n' || ID == '\r' || ID == 'j' ||
X ID == 'J' || ID == '+')
X scroll_win(count);
X else if (ID == ' ')
X scroll_win(crt-3);
X else if ((ID == 'q' || ID == 'Q') &&
X ison(glob_flags, IS_GETTING)) {
X pr_destroy(msg_pix), msg_pix = (struct pixrect *)NULL;
X win_setcursor(msg_sw->ts_windowfd, &write_cursor);
X txt.x = 5, txt.y = msg_rect.r_height - l_height(curfont);
X wprint("\n(continue editing letter)\n");
X clr_bot_line();
X type_cursor(PIX_SRC);
X }
X }
X /*
X * If msg_pix is NULL, then we are not reading a message. If we are
X * editing a letter, then enter the keys typed. If we are doing
X * nothing, ignore this input.
X */
X else if (ison(glob_flags, IS_GETTING)) {
X type_cursor(PIX_XOR);
X if (lastchar != ltchars.t_lnextc &&
X (ID == tchars.t_intrc || ID == tchars.t_quitc)) {
X (void) rite(_tty.sg_kill);
X (void) rm_edfile(SIGINT);
X } else {
X register int n = 1;
X if (ID == tchars.t_eofc && txt.x == 5
X || (p = rite((char)ID)) && !(n = add_to_letter(p)))
X finish_up_letter();
X else if (n > 0)
X type_cursor(PIX_XOR);
X }
X }
X lastchar = ID;
X } else switch(ID) {
X when MS_LEFT : case MS_MIDDLE:
X if (getting_opts == 2)
X if (ID == MS_LEFT)
X set_key(NULL, event.ie_locx, event.ie_locy);
X else {
X register char *p = find_key(event.ie_locx, event.ie_locy);
X if (p)
X print("Function key %s: %s", p, key_set_to(p));
X }
X else if (getting_opts) {
X int y = event.ie_locy - 50;
X if (y < -24)
X break;
X if (y < 0) {
X register int x = event.ie_locx;
X register int X = 60*l_width(LARGE);
X if (x >= X && x <= X+16)
X display_opts(-1); /* scroll options back one value */
X else if (x >= X+20 && x <= X+36)
X display_opts(1); /* scroll options forward one value */
X break;
X }
X /* the user was typing something -- stopped by using mouse */
X if (txt.x > 5) {
X type_cursor(PIX_CLR);
X (void) rite(_tty.sg_kill), txt.x = 5;
X option_line(line), display_opts(0);
X }
X line = y/20;
X if (ID == MS_LEFT)
X toggle_opt(line);
X help_opt(line); /* display help (short info) in both cases */
X } else if (msg_pix)
X if (ID == MS_LEFT)
X scroll_win(crt-3);
X else
X scroll_win(-(crt-3));
X when MS_RIGHT:
X if (getting_opts)
X (void) view_opts_menu(&event, gfxsw->gfx_windowfd);
X else if (isoff(glob_flags, IS_GETTING))
X (void) do_menu(&event, gfxsw->gfx_windowfd, current_msg);
X else
X (void) edit_menu(&event, gfxsw->gfx_windowfd);
X otherwise: ;
X }
X *ibits = *obits = *ebits = 0;
X}
X
Xstruct cursor *mice[3] = { &l_cursor, &m_cursor, &r_cursor };
X
Xhdr_io(gfxsw, ibits, obits, ebits, sw_timer)
Xregister struct gfxsubwindow *gfxsw;
Xint *ibits,*obits,*ebits;
Xstruct timeval **sw_timer;
X{
X static int which_cursor;
X struct inputmask im;
X struct inputevent event;
X int line;
X
X if (*ibits & ~(1 << gfxsw->gfx_windowfd)) {
X *ibits = *obits = *ebits = 0;
X return;
X }
X /* make curosr change which button is lit */
X win_setcursor(gfxsw->gfx_windowfd, mice[which_cursor]);
X
X which_cursor = (which_cursor+1) % 3;
X if (input_readevent(hdr_sw->ts_windowfd, &event) == -1) {
X error("input event");
X return;
X }
X /* I'm not sure why I have to do this.
X * I'm doing it because sometimes the IO hangs completely and no input
X * is accepted. What I do here is get the current mask, save it, then
X * reset it. This action seems to flush the IO queue, and I don't have hung
X * IO anymore. This shouldn't be necessary, but it appears to work.
X * (occurances have droped about 90%)
X */
X if (ID == LOC_WINENTER) {
X int x;
X win_getinputmask(gfxsw->gfx_windowfd, &im, &x);
X win_setinputmask(hdr_sw->ts_windowfd, &im, &im, x);
X }
X /* just return -- we just wanted to make the cursor flicker */
X if (ID == LOC_STILL || ID == LOC_MOVE || ID == LOC_WINENTER) {
X *ibits = *obits = *ebits = 0;
X return;
X }
X line = event.ie_locy / l_height(DEFAULT);
X if (ID >= KEY_LEFTFIRST)
X (void) func_key(ID);
X else if (n_array[line] > msg_cnt)
X if (!msg_cnt)
X print("-- You have no messages -- ");
X else
X print("Message out of range. Place mouse over a legal message.");
X else switch(ID) {
X when MS_LEFT: case MS_MIDDLE:
X (void) do_menu((ID == MS_LEFT)? READ_MSG: DEL_MSG, 0,n_array[line]);
X when MS_RIGHT:
X (void) do_menu(&event, gfxsw->gfx_windowfd, n_array[line]);
X otherwise : print("Unkown ID = %d", ID);
X }
X *ibits = *obits = *ebits = 0;
X}
X
X/* if "fd" is 0, then event points to the action to be taken.
X * otherwise, determine action to be taken by displaying a menu.
X * message is the number current_msg should be changed to (may be the same).
X */
Xdo_menu(event, fd, message)
Xcaddr_t event;
X{
X static char buf[20];
X struct menuitem *m_item;
X char *action;
X static struct menuitem msg_menu_items[] = {
X { MENU_IMAGESTRING, "Read", READ_MSG },
X { MENU_IMAGESTRING, "Delete", DEL_MSG },
X { MENU_IMAGESTRING, "Undelete", UNDEL_MSG },
X { MENU_IMAGESTRING, "Reply", REPL_MSG },
X { MENU_IMAGESTRING, "Save", SAVE_MSG },
X { MENU_IMAGESTRING, "Preserve", PRE_MSG },
X { MENU_IMAGESTRING, "Print", PRNT_MSG },
X { MENU_IMAGESTRING, "Help", MENU_HELP }
X };
X static struct menu help_menu = {
X MENU_IMAGESTRING, "Item Help",
X N_MENU_ITEMS, msg_menu_items,
X (struct menu *)NULL, NULL
X };
X static struct menu msgs_menu = {
X MENU_IMAGESTRING, buf, N_MENU_ITEMS,
X msg_menu_items, &help_menu, NULL
X };
X /* to have the menu stack maintain order of menus upon each invokation,
X * declare menu_ptr to be static and remove the following two lines
X * after the declaration.
X */
X struct menu *menu_ptr = &msgs_menu;
X msgs_menu.m_next = &help_menu;
X help_menu.m_next = (struct menu *)NULL;
X
X if (!msg_cnt) {
X print("No Messages.");
X return;
X }
X if (fd) {
X (void) sprintf(buf, "Message #%d", message+1);
X if (m_item = menu_display(&menu_ptr, (struct inputevent *)event, fd))
X action = m_item->mi_data;
X else
X return;
X } else
X action = event;
X
X if (menu_ptr == &help_menu || action == MENU_HELP) {
X switch(action) {
X when DEL_MSG: case UNDEL_MSG:
X (void) help(fd, "menu_delete", tool_help);
X when READ_MSG: (void) help(fd, "next", tool_help);
X when REPL_MSG: (void) help(fd, "menu_respond", tool_help);
X when SAVE_MSG: (void) help(fd, "save", tool_help);
X when PRE_MSG: (void) help(fd, "preserve", tool_help);
X when PRNT_MSG: (void) help(fd, "printer", tool_help);
X when MENU_HELP:
X if (menu_ptr == &help_menu)
X (void) help(fd, "help_menu_help_msg", tool_help);
X else
X (void) help(fd, "msg_menu", tool_help);
X }
X return;
X }
X set_isread(message);
X if (action == SAVE_MSG) {
X panel_set(msg_num_item, PANEL_VALUE, sprintf(buf, "%d", message+1), 0);
X ((struct inputevent *)event)->ie_code = MS_LEFT;
X do_file_dir(save_item, 0, event);
X panel_set(msg_num_item, PANEL_VALUE, NO_STRING, 0);
X return;
X } else if (action == PRNT_MSG || action == PRE_MSG ||
X action == UNDEL_MSG || action == DEL_MSG) {
X fkey_misc(action, message);
X return;
X }
X if (isoff(glob_flags, IS_GETTING)) {
X current_msg = message;
X (void) do_hdrs(0, DUBL_NULL, NULL);
X }
X if (action == REPL_MSG) {
X respond_mail(respond_item, 0, event);
X return;
X } else if (ison(glob_flags, IS_GETTING)) {
X if (exec_pid)
X /* User can read a message as long as he's not in an editor */
X print("Finish editing message first");
X else {
X (void) do_hdrs(0, DUBL_NULL, NULL);
X display_msg(message, (long)0);
X }
X return;
X }
X display_msg(current_msg, (long)0);
X}
X
X/* miscellaneous function key actions there are here because the defines
X * for DEL_MSG, etc are here in this file and the function is called from
X * here more often.
X */
Xfkey_misc(action, message)
Xchar *action;
X{
X int argc;
X register char **argv;
X char buf[30];
X
X print("Message #%d ", message+1);
X if (action == UNDEL_MSG || action == DEL_MSG)
X print_more("%sd. ", sprintf(buf, "%selete",
X (action == DEL_MSG)? "d": "und"));
X else if (action == PRNT_MSG) {
X print_more("sent to printer");
X (void) strcpy(buf, "lpr");
X } else if (action == PRE_MSG)
X print_more("%sd", strcpy(buf, "preserve"));
X (void) sprintf(&buf[strlen(buf)], " %d", message+1);
X if (message == current_msg && action == DEL_MSG)
X do_clear();
X
X if (argv = make_command(buf, DUBL_NULL, &argc))
X (void) do_command(argc, argv, msg_list);
X return;
X}
X
Xview_opts_menu(event, fd)
Xstruct inputevent *event;
X{
X static char buf[5];
X struct menuitem *m_item;
X char *action;
X static struct menuitem opts_items[] = {
X { MENU_IMAGESTRING, "Save Options", O_SAVE },
X { MENU_IMAGESTRING, "Restore Options", O_RSTR },
X { MENU_IMAGESTRING, "Quit Options", O_QUIT },
X { MENU_IMAGESTRING, "Help", MENU_HELP }
X };
X static struct menu msgs_menu = {
X MENU_IMAGESTRING, "Options", 4, opts_items, (struct menu *)NULL, NULL
X };
X struct menu *menu_ptr = &msgs_menu;
X
X if (m_item = menu_display(&menu_ptr, event, fd))
X action = m_item->mi_data;
X else
X return;
X switch(action) {
X case O_SAVE:
X save_opts(0, DUBL_NULL);
X when O_RSTR:
X init();
X if (getting_opts == 1)
X view_options();
X else
X set_fkeys();
X when O_QUIT:
X do_clear();
X unlock_cursors(); /* actually resets msg_win's cursor */
X if (isoff(glob_flags, IS_GETTING) && msg_cnt)
X if (isoff(msg[current_msg].m_flags, DELETE))
X display_msg(current_msg, (long)0);
X else
X (void) read_mail(NO_ITEM, 0, NO_EVENT);
X when MENU_HELP:
X (void) help(fd, (getting_opts == 1)? "options": "fkeys", tool_help);
X }
X}
END_OF_FILE
if test 13038 -ne `wc -c <'select.c'`; then
echo shar: \"'select.c'\" unpacked with wrong size!
fi
# end of 'select.c'
fi
if test -f 'viewopts.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'viewopts.c'\"
else
echo shar: Extracting \"'viewopts.c'\" \(13307 characters\)
sed "s/^X//" >'viewopts.c' <<'END_OF_FILE'
X/* @(#)viewopts.c (c) copyright 10/18/86 (Dan Heller) */
X
X#include "mush.h"
X
Xstruct viewopts {
X char *v_opt;
X char *v_prompt;
X int v_usage;
X#define TOOL 01
X#define TEXT 02
X char *v_description;
X};
X
X/*
X * struct contains the option, a prompt if it has a string value, whether
X * or not it applies to non suntools, line mode, or both, and a
X * string describing what the option does. If the prompt string starts
X * with a minus sign, then the value can be set without a value. This
X * is there to indicate to option_line to print a toggle (cycle) pixrect
X * and to print TRUE/FALSE telling whether the value is on or off regardless
X * of it's "string" value.
X */
Xstruct viewopts viewopts[] = {
X { "alwaysignore", NULL, TOOL | TEXT,
X "Always ignore the message headers on the 'ignored' list." },
X { "ask", NULL, TOOL | TEXT,
X "Prompts for a subject on outgoing mail." },
X { "askcc", NULL, TOOL | TEXT,
X "Ask for list of Carbon Copy recipients whenever sending mail." },
X { "autodelete", NULL, TOOL | TEXT,
X "Automatically delete ALL READ messages whenever you update mail." },
X { "autoedit", NULL, TOOL | TEXT,
X "Automatically enter editor whenever you REPLY to mail." },
X { "autoinclude", NULL, TOOL | TEXT,
X "Include a copy of author's message each time you reply to mail." },
X { "autoprint", NULL, TOOL | TEXT,
X "Display the next message on the list when you delete a message." },
X { "auto_route", NULL, TOOL | TEXT,
X "Remove redundant uucp addresses when replying to messages." },
X { "autosign", "-Filename", TOOL | TEXT,
X "Add file (~/.signature if set but no value) at end of all letters." },
X { "autosign2", "Address : Filename", TOOL | TEXT,
X "Signature to use for specific addresses. \"addr, ... : <signature>\""},
X { "cdpath", "Path", TEXT,
X "Path to search for directories when the \"cd\" command is issued." },
X { "crt", "Lines", TEXT,
X "The number of lines a message must have for 'pager' to be invoked." },
X { "date_received", NULL, TOOL | TEXT,
X "Time displayed for message headers shows date received (or sent)." },
X { "dead", "Filename", TOOL | TEXT,
X "The name of the file to store dead mail (default = ~/dead.letter)." },
X { "dot", NULL, TOOL | TEXT,
X "Allow \".\" on a line by itself to send letter." },
X { "editor", "Editor name/path", TOOL | TEXT,
X "Editor for message editing (default = env EDITOR or \"vi\")." },
X { "escape", "Character", TOOL | TEXT,
X "Escape character for extended editing commands (default = ~)." },
X { "folder", "Pathname", TOOL | TEXT,
X "Full pathname to the directory where personal folders are kept." },
X { "fortune", "-Flag", TOOL | TEXT,
X "Add fortune to end of letters. Flag to \"fortune\" is optional." },
X { "fortunates", "Users", TOOL | TEXT,
X "Those who will receive fortunes if fortune is set (default: All)." },
X { "hdr_format", "Format", TOOL | TEXT,
X "Formatting string for headers. \"headers -?\" or help hdr_format." },
X { "history", "Number", TEXT,
X "How many commands to remember (like csh)." },
X { "hold", NULL, TOOL | TEXT,
X "Read but not deleted messages are saved in spool -- not mbox." },
X { "home", "Directory", TOOL | TEXT,
X "The user's home directory." },
X { "hostname", "Hostname", TOOL | TEXT,
X "User-definable name for the name of your machine." },
X { "ignore_bang", NULL, TEXT,
X "Ignore '!' as a history reference. Otherwise, escape by: \\!" },
X { "ignoreeof", "-Command", TEXT,
X "Ignores ^D as exit, or (if set), execute \"command\"." },
X { "indent_str", "String", TOOL | TEXT,
X "String to offset included messages within your letters." },
X { "in_reply_to", NULL, TOOL | TEXT,
X "When responding to mail, add In-Reply-To: to message headers." },
X { "keepsave", NULL, TOOL | TEXT,
X "Prevents messages from being marked as `deleted' when you `save'." },
X { "known_hosts", "Host list", TOOL | TEXT,
X "List of hosts that your site is known to uucp mail to." },
X { "lister", "Arguments", TOOL | TEXT,
X "Arguments passed to the 'ls' command." },
X { "logfile", "Filename", TOOL | TEXT,
X "Log outgoing mail headers only. Message text not logged." },
X { "mbox", "Filename", TOOL | TEXT,
X "Filename to use instead of ~/mbox for default mailbox." },
X { "metoo", NULL, TOOL | TEXT,
X "When replying to mail, metoo preserves your name on mailing list." },
X { "mil_time", NULL, TOOL | TEXT,
X "24-hour military time format is used whenever a time is printed. " },
X { "newline", "-Command", TEXT,
X "Ignore RETURN. If set to a command, execute that command." },
X { "no_expand", NULL, TEXT | TOOL,
X "Prevents expansion of Mush aliases in outgoing mail headers." },
X { "no_hdrs", NULL, TOOL | TEXT,
X "If set, personalized headers are NOT inserted to outgoing mail." },
X { "no_reverse", NULL, TOOL | TEXT,
X "Disables reverse video in curses mode -- uses \"bold\" in tool mode."},
X { "nonobang", NULL, TEXT,
X "Suppresses errors from unsuccessful history references." },
X { "nosave", NULL, TOOL | TEXT,
X "Prevents aborted mail from being saved in $dead." },
X { "pager", "Program", TEXT,
X "Program name to be used as a pager for messages longer than crt." },
X { "pre_indent_str", "String", TEXT | TOOL,
X "String to precede message text interpolated into message body." },
X { "post_indent_str", "String", TEXT | TOOL,
X "String to succeed message text interpolated into message body." },
X { "print_cmd", "Program", TOOL | TEXT,
X "Alternate program to use to send messages to the printer." },
X { "printer", "Printer", TOOL | TEXT,
X "Printer to send messages to (default = environment PRINTER)." },
X { "prompt", "String", TEXT,
X "Your prompt. \"help prompt\" for more information." },
X { "quiet", NULL, TEXT,
X "Don't print the version number of Mush on startup." },
X { "realname", "Name:", TOOL | TEXT,
X "Your real name." },
X { "record", "Filename", TOOL | TEXT,
X "Save all outgoing mail in specified filename." },
X { "reply_to_hdr", "Headers", TOOL | TEXT,
X "List of headers use to construct reply addresses from a message." },
X { "save_empty", NULL, TOOL | TEXT,
X "Folders which have all messages deleted are NOT removed on updates." },
X { "screen", "Number of Headers", TEXT,
X "Number of headers to print in non-suntools (text) mode." },
X { "screen_win", "Number of Headers", TOOL,
X "Set the size of the header window for the tool mode only." },
X { "show_deleted", NULL, TOOL | TEXT,
X "Show deleted messages in headers listings (unused in curses mode)." },
X { "show_hdrs", "Headers", TOOL | TEXT,
X "When displaying a message, show list of \"headers\" only." },
X { "sendmail", "Program", TOOL | TEXT,
X "Program to use to deliver mail instead of using the default."},
X { "sort", "-Option", TOOL | TEXT,
X "Pre-sorting of messages on mush startup (set to valid sort option)." },
X { "squeeze", NULL, TOOL | TEXT,
X "When reading messages, squeeze all blank lines into one." },
X { "thisfolder", NULL, TEXT,
X "This read-only variable gives the current folder name." },
X { "toplines", "Lines", TOOL | TEXT,
X "Number of lines to print of a message for the 'top' command." },
X { "tmpdir", "Directory", TOOL | TEXT,
X "Directory to use for temporary files used by Mush." },
X { "unix", NULL, TEXT,
X "Non-mush commands are considered to be UNIX commands." },
X { "verify", NULL, TEXT,
X "Verify to send, re-edit, or abort letter after editing." },
X { "visual", "Visual editor", TOOL | TEXT,
X "Visual editor for messages (default = $editor or env VISUAL)."},
X { "warning", NULL, TOOL | TEXT,
X "Print warning messages for non-fatal errors." },
X { "wrap", NULL, TOOL | TEXT,
X "After referencing last message, message pointer wraps to start." },
X { "wrapcolumn", "-Column to wrap [78]", TEXT,
X "Column at which to wrap lines when composing messages." },
X};
X
X#define total_opts (sizeof viewopts / sizeof (struct viewopts))
X
X#ifdef SUNTOOL
X
Xstatic int start_cnt;
X
X#define twenty 5 + 20*l_width(DEFAULT)
X#define forty 5 + 40*l_width(DEFAULT)
X#define image_at(x,y,image) pw_rop(msg_win, x, y, 16, 16, PIX_SRC, image, 0,0)
X
X/* print in default text, but increment in large text segments */
Xview_options()
X{
X if (msg_rect.r_height < 80) {
X print("Window not big enough to display options.");
X return;
X }
X do_clear();
X getting_opts = 1, start_cnt = 0;
X win_setcursor(msg_sw->ts_windowfd, &checkmark);
X highlight(msg_win, txt.x, txt.y, LARGE,
X " : Toggle Value : Description : Menu (Help)");
X image_at(txt.x + 2 * l_width(DEFAULT), txt.y - 12, &mouse_left);
X image_at(txt.x + 25 * l_width(DEFAULT), txt.y - 12, &mouse_middle);
X image_at(txt.x + 48 * l_width(DEFAULT), txt.y - 12, &mouse_right);
X
X pw_vector(msg_win, 0, txt.y+6, msg_rect.r_width, txt.y+6, PIX_SRC, 1);
X pw_vector(msg_win, 0, txt.y+8, msg_rect.r_width, txt.y+8, PIX_SRC, 1);
X
X txt.y += 24;
X
X pw_text(msg_win, 5, txt.y, PIX_SRC, fonts[LARGE], "Option");
X pw_text(msg_win, twenty, txt.y, PIX_SRC, fonts[LARGE], "On/Off");
X pw_text(msg_win, forty, txt.y, PIX_SRC, fonts[LARGE], "Values");
X
X pw_vector(msg_win, 0, txt.y+6, msg_rect.r_width, txt.y+6, PIX_SRC, 1);
X pw_vector(msg_win, 0, txt.y+8, msg_rect.r_width, txt.y+8, PIX_SRC, 1);
X
X pw_text(msg_win, 59*l_width(DEFAULT),txt.y,PIX_SRC,fonts[LARGE],"Scroll:");
X pw_rop(msg_win, 60*l_width(LARGE), txt.y-13,16,16,PIX_SRC, &dn_arrow,0,0);
X pw_rop(msg_win, 60*l_width(LARGE)+20,txt.y-13,16,16,PIX_SRC, &up_arrow,0,0);
X
X display_opts(0); /* create the pixrect and all that */
X}
X
Xdisplay_opts(count)
Xregister int count;
X{
X register int total_displayable = (msg_rect.r_height - 60) / 20;
X
X if (count < 0 && start_cnt + count < 0) {
X print("At the beginning");
X return;
X } else if (count && start_cnt + count + total_displayable > total_opts) {
X print("At the end");
X return;
X }
X start_cnt += count;
X if (!msg_pix) {
X register int x = (total_opts+1) * 20;
X if (x < msg_rect.r_height)
X x = msg_rect.r_height;
X if (!(msg_pix = mem_create(msg_rect.r_width, x, 1))) {
X error("mem_create");
X return;
X }
X pr_rop(msg_pix,0,0, msg_rect.r_width-1, x-1, PIX_CLR,0,0,0);
X for (count = 0; count < total_opts; count++)
X option_line(count);
X }
X pw_rop(msg_win, 0, 50, msg_rect.r_width - 1, msg_rect.r_height - 50,
X PIX_SRC, msg_pix, 0, start_cnt * 20);
X}
X
Xvoid
Xtoggle_opt(line)
X{
X register char *p = viewopts[start_cnt+line].v_prompt;
X
X if (do_set(set_options, viewopts[start_cnt+line].v_opt))
X un_set(&set_options, viewopts[start_cnt+line].v_opt);
X else {
X if (p) {
X txt.x = 5 + 40 * l_width(DEFAULT) +
X (1 + strlen(p) - (*p=='-')) * l_width(DEFAULT);
X txt.y = 50 + line*20 + l_height(curfont);
X }
X if (!p || *p == '-') {
X register char *argv[2];
X argv[0] = viewopts[start_cnt+line].v_opt;
X argv[1] = NULL;
X (void) add_option(&set_options, argv);
X }
X }
X option_line(line);
X display_opts(0);
X if (txt.x > 5)
X type_cursor(PIX_SRC);
X}
X
Xvoid
Xhelp_opt(line)
X{
X print(viewopts[start_cnt+line].v_description);
X}
X
Xadd_opt(p, line)
Xregister char *p;
X{
X char buf[80], **argv;
X int argc;
X u_long save_bang = ison(glob_flags, IGN_BANG);
X
X (void) sprintf(buf, "set %s=\"%s\"", viewopts[start_cnt+line].v_opt, p);
X turnon(glob_flags, IGN_BANG);
X if (argv = make_command(buf, DUBL_NULL, &argc))
X (void) do_command(argc, argv, msg_list);
X if (!save_bang)
X turnoff(glob_flags, IGN_BANG);
X option_line(line); /* make sure new value is entered into database */
X}
X
Xoption_line(count)
Xregister int count;
X{
X register char *p, *v = do_set(set_options, viewopts[start_cnt+count].v_opt);
X struct pr_prpos win;
X
X win.pr = msg_pix;
X win.pos.y = (start_cnt + count) * 20 + 16;
X win.pos.x = 5;
X
X pf_text(win, PIX_SRC, fonts[DEFAULT], blank);
X pf_text(win, PIX_SRC, fonts[DEFAULT], viewopts[start_cnt+count].v_opt);
X win.pos.x = twenty+20;
X
X if (!(p = viewopts[start_cnt+count].v_prompt) || *p == '-') {
X pr_rop(msg_pix, twenty, win.pos.y-10, 16, 16, PIX_SRC, &cycle, 0, 0);
X pf_text(win, PIX_SRC, fonts[DEFAULT], (v)? "TRUE ": "FALSE");
X win.pos.x++;
X pf_text(win, PIX_SRC, fonts[DEFAULT], (v)? "TRUE ": "FALSE");
X }
X if (p) {
X if (*p == '-')
X p++;
X win.pos.x = forty;
X /* highlight */
X pf_text(win, PIX_SRC, fonts[DEFAULT], p);
X win.pos.x++;
X pf_text(win, PIX_SRC, fonts[DEFAULT], p);
X win.pos.x = forty + strlen(p) * l_width(DEFAULT);
X pf_text(win, PIX_SRC, fonts[DEFAULT], ":");
X if (v) {
X win.pos.x += (2 * l_width(DEFAULT));
X pf_text(win, PIX_SRC, fonts[DEFAULT], v);
X }
X }
X}
X
X#endif /* SUNTOOL */
X
X/*
X * return a string describing a variable.
X * parameters: count, str, buf.
X * If str != NULL, check str against ALL variables
X * in viewopts array. The one that matches, set count to it and
X * print up all the stuff from the viewopts[count] into the buffer
X * space in "buf" and return it.
X */
Xchar *
Xvariable_stuff(count, str, buf)
Xregister char *str, buf[];
X{
X if (str)
X for (count = 0; count < total_opts; count++)
X if (!strcmp(str, viewopts[count].v_opt))
X break;
X if (count >= total_opts) {
X (void) sprintf(buf, "%s: Not a default %s variable.",
X str? str : itoa(count), prog_name);
X return NULL;
X }
X return sprintf(buf, "%s: %s",
X viewopts[count].v_opt, viewopts[count].v_description);
X}
END_OF_FILE
if test 13307 -ne `wc -c <'viewopts.c'`; then
echo shar: \"'viewopts.c'\" unpacked with wrong size!
fi
# end of 'viewopts.c'
fi
echo shar: End of archive 6 \(of 19\).
cp /dev/null ark6isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 19 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
--
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
More information about the Comp.sources.unix
mailing list