Subject: v18i028: Mail user's shell version 6.4, Part06/19 Newsgroups: comp.sources.unix Sender: sources Approved: rsalz@uunet.UU.NET Submitted-by: Dan Heller 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 '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: ""); 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 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, ... : \""}, 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