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, &ltchars) != -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