[comp.sources.misc] v18i063: mush - Mail User's Shell, Part06/22

argv@zipcode.com (Dan Heller) (04/21/91)

Submitted-by: Dan Heller <argv@zipcode.com>
Posting-number: Volume 18, Issue 63
Archive-name: mush/part06
Supersedes: mush: Volume 12, Issue 28-47

#!/bin/sh
# do not concatenate these parts, unpack them in order with /bin/sh
# file curs_io.c continued
#
if test ! -r _shar_seq_.tmp; then
	echo 'Please unpack part 1 first!'
	exit 1
fi
(read Scheck
 if test "$Scheck" != 6; then
	echo Please unpack part "$Scheck" next!
	exit 1
 else
	exit 0
 fi
) < _shar_seq_.tmp || exit 1
if test ! -f _shar_wnt_.tmp; then
	echo 'x - still skipping curs_io.c'
else
echo 'x - continuing file curs_io.c'
sed 's/^X//' << 'SHAR_EOF' >> 'curs_io.c' &&
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 (complete && (c == complete || c == complist)) {
X	    (void) completion(String, &count, (c == complist), (c == complete));
X	} else if (c == del_line) {
X	    if (count) {
X		do
X		    backspace(String, &count);
X		while (count);
X	    }
X	} else if (c == reprint_line)
X	    String[count] = 0, wprint("\n%s", String);
X	else 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		 */
push_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 {
check_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    (void) 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
static
backspace(str, n)
register char *str;
int *n;
{
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
#undef Addch
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 */
check_map(c, map_list)
char c;
struct cmd_map *map_list;
{
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()
#if !defined(SELECT) && !defined(M_UNIX)
#ifdef FIONREAD
X	    || !ioctl(0, FIONREAD, &n) && n > 0
#else
#ifdef M_XENIX
X	    || rdchk(0) > 0
#endif /* M_XENIX */
#endif /* FIONREAD */
#endif /* SELECT */
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	 */
#if defined(BSD) || defined(M_UNIX) || 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;
#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 */
#else
X	    int rmask = 1, wmask = 0, xmask = 0;
#endif /* FD_SET */
X	    timer.tv_sec = 1;
X	    timer.tv_usec = 0;
X	    n = select(1, &rmask, &wmask, &xmask, &timer);
#else /* !SELECT */
#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);
#else
#ifdef M_XENIX
X	    (void) sleep(1);
X	    n = rdchk(0);
#else
X
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;
#endif /* M_XENIX  */
#endif /* FIONREAD */
#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	(void) mac_push(mbuf + 1);
X    return 0;
}
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 */
line_wrap(string, count)
char *string;	/* The string to be wrapped */
int *count;	/* Offset of string terminator */
{
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
/*
X * Error bell used by completion()
X */
errbell(ret)
int ret;
{
X    if (ret < 0 || !chk_option("quiet", "complete,completion"))
X	bell();
X    return ret;
}
X
/*
X * Perform word completion on the input string
X */
completion(string, count, showlist, ignore)
char *string;	/* The string to be completed */
int *count;	/* Offset of string terminator */
int showlist;	/* Display list, complete if also ignore */
int ignore;	/* Ignore the fignore matches, do complete */
{
X    char buf[MAXPATHLEN], *b = buf, **exp;
X    int n = *count, f, len, prefix, trim, overstrike, expandall;
X
X    if (!*string || !*count)
X	return errbell(-1);
X
X    /* Look for a delimiter */
X    while (n > 0 && !index(DELIM, string[--n]))
X	;
X    if (n > 0 || index(DELIM, string[n]))
X	n++;
X    b = buf + (len = Strcpy(buf, &string[n]));
X    Debug("\nexpanding (%s) ... ", buf);
X    if (!any(buf, FMETA)) {
X	expandall = 0;
X	overstrike = (*buf == '+' || *buf == '~' || *buf == '%');
X	trim = (overstrike && len > 1);
X	if (!overstrike || len > 1 || (*buf == '+' && showlist))
X	    *b++ = '*', *b = 0;
X	/* Previous behavior for '+' completions (trailing '/'):
X	if (len > 1 || *buf != '~' || *buf != '%')
X	    *b++ = '*', *b = 0;
X	*/
X	f = filexp(buf, &exp);
X	if (*--b == '*')
X	    *b = 0; /* We need the original buf below */
X    } else {
X	overstrike = 1;
X	trim = (*buf == '+' || *buf == '~');
X	/*
X	 * Check first to see if the base pattern matches.
X	 * If not, append a '*' and try again.
X	 * Don't expand all matches in the latter case.
X	 */
X	if ((f = filexp(buf, &exp)) < 1) {
X	    *b++ = '*', *b = 0;
X	    f = filexp(buf, &exp);
X	    *--b = 0; /* We need the original buf below */
X	    expandall = 0;
X	} else
X	    expandall = !showlist;
X    }
X    if (ignore)
X	f = fignore(f, &exp);
X    if (f < 0) {
X	Debug("globbing error!\n%s", string);
X	free_vec(exp);
X	return errbell(-1);
X    } else if (f > 0) {
X	Debug("result is: "), print_argv(exp);
X	if (!expandall && f > 1)
X	    prefix = lcprefix(exp, overstrike ? 0 : len);
X	else
X	    prefix = 0;
X	if (showlist && (f > 1 || !ignore)) {
X	    int pfx = prefix;
X	    if (!expandall)
X		while (pfx && exp[0][pfx - 1] != '/')
X		    --pfx;
X	    putchar('\n');
X	    if (columnate(f, exp, pfx) < 0)
X		(void) errbell(-1);
X	    /* Reprint the line */
X	    if (iscurses) {
X		wprint(":%s", string);
X		turnon(glob_flags, CNTD_CMD);
X	    } else {
X		if (isoff(glob_flags, IS_GETTING))
X		    mail_status(1);
X		wprint("%s", string);
X	    }
X	    if (!ignore)
X		overstrike = 0;
X	} 
X	if (ignore || !showlist) {
X	    if (expandall || strlen(exp[0]) > len) {
X		if (!showlist)
X		    Debug("%s", string);
X		if (overstrike && (prefix || expandall || f == 1)) {
X		    char *tmpv[3];
X		    tmpv[0] = buf;
X		    if (trim)
X			tmpv[1] = trim_filename(exp[0]);
X		    else
X			tmpv[1] = exp[0];
X		    tmpv[2] = NULL;
X		    /* Back up as far as is necessary */
X		    len = lcprefix(tmpv, 0);
X		    /* If nothing will be erased, we may need to beep */
X		    if (n + len == *count) {
X			if (!expandall && !tmpv[1][len])
X			    (void) errbell(0);
X		    }
X		    /* Erase the stuff that will complete */
X		    while (*count > n + len)
X			backspace(string,count);
X		    string[*count] = '\0';
X		}
X		if (expandall || f == 1) {
X		    /* Unget the names IN REVERSE ORDER! */
X		    while (f--) {
X			if (trim)
X			    b = trim_filename(exp[f]);
X			else
X			    b = exp[f];
X			if (f) {
X			    Ungetstr(b);
X			    Ungetstr(" ");
X			} else
X			    Ungetstr(b + len);
X		    }
X		} else {
X		    if (prefix > len) {
X			exp[0][prefix] = 0;
X			if (!showlist)
X			    Debug("\ncompletion is (%s)\n%s", exp[0], string);
X			if (trim)
X			    Ungetstr(trim_filename(exp[0]) + len);
X			else
X			    Ungetstr(&exp[0][len]);
X		    } else if (!showlist)
X			Debug("\nno longer prefix\n%s", string);
X		    /* Special case because "+" always tries to expand "+*"
X		     * to get listings and avoid getpath()'s trailing '/'.
X		     * No error bell is needed in those cases.
X		     */
X		    if (strcmp(buf, "+") != 0)
X			(void) errbell(0);
X		}
X	    } else {
X		Debug("no longer prefix\n%s", string);
X		(void) errbell(0);
X	    }
X	}
X    } else {
X	Debug("no match\n%s", string);
X	(void) errbell(0);
X    }
X    free_vec(exp);
X    return 1;
}
X
fignore(argc, argvp)
int argc;
char ***argvp;
{
X    char *fign = do_set(set_options, "fignore");
X    char **flist, buf[MAXPATHLEN], *b = buf;
X    int fcnt, i;
X
X    if (argc < 2 || !fign || !*fign)
X	return argc;
X    if (!argvp || !*argvp && !**argvp)
X	return -1;
X    
X    if ((flist = mk_argv(fign, &fcnt, FALSE)) && fcnt > 0) {
X	*b++ = '*';
X	for (i = 0; i < fcnt; i++) {
X	    if (flist[i][0] == '.' && !any(flist[i], FMETA)) {
X		(void) strcpy(b, flist[i]);
X		(void) strdup(flist[i], buf);
X	    }
X	}
X	Debug("ignoring "), print_argv(flist);
X	fcnt = gdiffv(argc, argvp, fcnt, flist);
X	free_vec(flist);
X	if (fcnt == 0)
X	    fcnt = argc;
X	else {
X	    free_elems(&((*argvp)[fcnt]));
X	    (*argvp)[fcnt] = NULL;
X	}
X    }
X    return fcnt;
}
SHAR_EOF
echo 'File curs_io.c is complete' &&
chmod 0644 curs_io.c ||
echo 'restore of curs_io.c failed'
Wc_c="`wc -c < 'curs_io.c'`"
test 18325 -eq "$Wc_c" ||
	echo 'curs_io.c: original size 18325, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= curses.c ==============
if test -f 'curses.c' -a X"$1" != X"-c"; then
	echo 'x - skipping curses.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting curses.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'curses.c' &&
/* @(#)curses.c	(c) copyright 3/18/87 (Dan Heller) */
X
/* curses.c -- routine to deal with the curses interface */
#ifdef CURSES
X
#include "mush.h"
#include "bindings.h"
X
curses_init(argc, argv)
register char **argv;
{
X    char buf[80];
X    extern char *UP;
#ifndef M_UNIX
X    extern char ttytype[];
#endif /* M_UNIX */
X
X    if (argv && *++argv && !strcmp(*argv, "-?"))
X	return help(0, "curses", cmd_help);
#ifdef SUNTOOL
X    if (istool) {
X	print("Sorry, can't change to curses mode from tool.\n");
X	return -1;
X    } else
#endif /* SUNTOOL */
X    if (!is_shell) {
X	/*
X	 * Can't start curses, but we can prepare to.
X	 * Also allow -C switch to be shut off.
X	 */
X	if (argv && *argv && !lcase_strncmp(*argv, "off", -1))
X	    turnoff(glob_flags, PRE_CURSES);
X	else
X	    turnon(glob_flags, PRE_CURSES);
X	return 0;
X    } else if (argc && (iscurses || ison(glob_flags, PRE_CURSES))) {
X	print("You are already using curses mode.\n");
X	return -1;
X    } else if (ison(glob_flags, IS_GETTING)) {
X	print("Finish your letter first.\n");
X	return -1;
X    }
X
#ifndef attrset		/* terminfo version of curses */
X    /* you can not start curses in no echo mode.. must be in normal mode */
X    echom();
X    nocrmode();
#endif /* attrset */
X    (void) initscr();
#ifdef SIGCONT
X    /* initscr will play with signals -- make sure they're set right. */
X    (void) signal(SIGTSTP, stop_start);
X    (void) signal(SIGCONT, stop_start);
#endif /* SIGCONT */
#if !defined(SYSV) && !defined(USG)
X    if (!UP || !*UP)
#else /* ~SYSV && ~USG */
X    if (!stdscr)
#endif /* ~SYSV && ~USG */
X		 {
X	print("Terminal type %s can not use the curses interface.\n", ttytype);
X	return -1;
X    }
X    iscurses = TRUE;
X    noechom(); /* reset tty state -- */
X    crmode(); /* do not use "echo_on/off()" */
X    scrollok(stdscr, TRUE);
X    /* if the user hasn't set his screen explicitly, set it for him */
X    set_screen_size();
X    if (crt > LINES - 1 || !do_set(set_options, "crt")) {
X	crt = LINES;
X	(void)cmd_line(sprintf(buf, "\\set screen = %d crt = %d", screen, crt),
X	    msg_list);
X    } else
X	(void)cmd_line(sprintf(buf, "\\set screen = %d", screen), msg_list);
X    if (argc) {
X	(void) cmd_line(sprintf(buf, "\\headers %d", current_msg+1), msg_list);
X	(void) curses_help_msg(TRUE);
X    }
X    if (!do_set(set_options, "no_reverse"))
X	turnon(glob_flags, REV_VIDEO);
X    turnoff(glob_flags, CONT_PRNT);
X    return 0; /* doesn't affect messages */
}
X
struct cmd_map *active_cmd;	/* See bindings.h for description */
X
/*
X * get input in cbreak mode and execute the appropriate command.
X * when the command is done (usually), the user is prompted to
X * hit any key to continue. At this point, the user may enter a
X * new command so no screen refreshing needs to be done. This
X * new command is returned to caller and may be passed back.
X *
X * The flag CNTD_CMD (continued command) is set if
X * this routine is called with the passed parameter (c) != 0. If
X * so, then the character passed is the character input by the
X * user at the last "hit return" prompt indicating that he wants
X * to execute a new command and not draw the screen.
X *
X * CNTD_CMD is also set if the command that the user invokes
X * causes any sort of output that requires a screen refresh.  The
X * variable redo is set to 1 if the header page not only requires
X * redrawing, but updating ... (new call to do_hdrs)
X *
X * calls that say: print("%s", compose_hdr(current_msg)) are constructed
X * that way because if the header has a `%' in it, then print will try to
X * expand it.
X */
curses_command(c)
register int c;
{
X    char	buf[BUFSIZ], file[128], list[128];
X    int 	n, curlin;
X    static int  redo = 0;  /* set if headers should be redrawn */
X
X    if (c != 0)
X	turnon(glob_flags, CNTD_CMD);
X    else
X	turnoff(glob_flags, CNTD_CMD);
X    clear_msg_list(msg_list); /* play it safe */
X    if (isoff(glob_flags, CNTD_CMD)) {
X	(void) check_new_mail();
X	curlin = max(1, current_msg - n_array[0] + 1);
X	if (ison(glob_flags, REV_VIDEO) && msg_cnt) {
X	    scrn_line(curlin, buf);
X	    STANDOUT(curlin, 0, buf);
X	}
X	mail_status(0);
X	move(curlin, 0), refresh();
X	/* reprint to remove reverse video from current line (don't refresh) */
X	if (ison(glob_flags, REV_VIDEO) && msg_cnt)
X	    mvaddstr(curlin, 0, buf);
X	c = getcmd(); /* get input AFTER line redrawn without reverse video */
X    }
X    buf[0] = list[0] = file[0] = '\0';
X
X    if (c == C_WRITE_LIST || c == C_SAVE_LIST || c == C_COPY_LIST
X	   || c == C_DELETE_LIST || c == C_UNDEL_LIST) {
X	if (msg_cnt < 1) {
X	    mac_flush();
X	    print("Not enough messages.");
X	    c = C_NULL;
X	} else if (c == C_DELETE_LIST && ison(glob_flags, READ_ONLY)) {
X	    mac_flush();
X	    print("Folder is read-only.");
X	    c = C_NULL;
X	} else if (!curses_msg_list(sprintf(buf, "%s msg list: ",
X		(c == C_WRITE_LIST)? "write" : (c == C_SAVE_LIST)?  "save" :
X		(c == C_COPY_LIST)? "copy" :
X		(c == C_DELETE_LIST)? "delete" : "undelete"), list, msg_list))
X	    c = C_NULL;
X	if (ison(glob_flags, CNTD_CMD))
X	    putchar('\n');
X    }
X
X    /* first do non-mail command type stuff */
X    switch (c) {
X	case C_ERROR :
X	    bell();
X	    mac_flush();
X
X	when C_NULL :
X	    if (isoff(glob_flags, CNTD_CMD))
X		bell();
X
X	/* goto a specific message number */
X	when C_GOTO_MSG :
X	    if (curses_msg_list(strcpy(buf, "goto msg: "), list, msg_list)) {
X		/*
X		 * Reset the current message in case a 
X		 * backquoted command (like `from`) changed it
X		 */
X		n = current_msg;
X		do if (++n >= msg_cnt)
X		    n = 0;
X		while (n != current_msg && !msg_bit(msg_list, n));
X		if (n == current_msg && !msg_bit(msg_list, n)) {
X		    mac_flush(); /* bail out if in macro processing */
X		    print("Message not found.");
X		}
X		else if ((current_msg = n) < n_array[0]
X			|| n > n_array[screen-1])
X		    redo = 1;
X	    } else {
X		mac_flush();
X		bell();
X	    }
X	    if (ison(glob_flags, CNTD_CMD) && msg_cnt)
X		print("%-.*s", COLS-2, compose_hdr(current_msg));
X	    if (ison(glob_flags, CNTD_CMD))
X		putchar('\n');
X
X	/* screen optimization stuff */
X	when C_REVERSE :
X	    if (ison(glob_flags, REV_VIDEO))
X		turnoff(glob_flags, REV_VIDEO);
X	    else
X		turnon(glob_flags, REV_VIDEO);
X
X	when C_REDRAW : redo = 1;
X
X	/*
X	 * screen movement
X	 */
X	when C_NEXT_MSG :
X	    if (current_msg + 2 > msg_cnt ||
X		isoff(glob_flags, CNTD_CMD) && curlin == LINES-2) {
X		mac_flush();	/* Bail out if in macro processing */
X		bell();		/* reached the end */
X	    } else {
X		if (ison(glob_flags, CNTD_CMD)) {
X		    if (++current_msg > n_array[screen-1])
X			redo = 1;
X		    print("%-.*s", COLS-2, compose_hdr(current_msg));
X		    putchar('\n');
X		} else {
X		    if (++current_msg > n_array[screen-1])
X			n_array[screen++] = current_msg;
X		    move(++curlin, 0);
X		    printw("%-.*s", COLS-2, compose_hdr(current_msg));
X		    clrtoeol();
X		}
X	    }
X
X	when C_PREV_MSG :
X	    if (isoff(glob_flags, CNTD_CMD) && curlin == 1 ||
X		    current_msg == 0 || msg_cnt == 0) {
X		mac_flush();	/* Bail out if in macro processing */
X		bell();  	/* at the beginning */
X	    } else {
X		if (--current_msg < n_array[0])
X		    redo = 1;
X		if (ison(glob_flags, CNTD_CMD)) {
X		    print("%-.*s", COLS-2, compose_hdr(current_msg));
X		    putchar('\n');
X		}
X	    }
X
X	when C_FIRST_MSG : case C_LAST_MSG :
X	    if (!msg_cnt) {
X		mac_flush();
X		bell();
X		break;
X	    }
X	    n = current_msg;
X	    move(LINES-1, 0), refresh();
X	    if (c == C_FIRST_MSG && (current_msg = 0) < n_array[0] ||
X		c == C_LAST_MSG && (current_msg = msg_cnt-1)> n_array[screen-1])
X		if (isoff(glob_flags, CNTD_CMD))
X		    (void) cmd_line(sprintf(buf, "\\headers %d", current_msg+1),
X			     msg_list);
X		else
X		    redo = 1;
X	    if (ison(glob_flags, CNTD_CMD) && n != current_msg)
X		print("%-.*s", COLS-2, compose_hdr(current_msg)), putchar('\n');
X
X	/* top and bottom of headers screen */
X	when C_TOP_PAGE : case C_BOTTOM_PAGE :
X	    if (msg_cnt && isoff(glob_flags, CNTD_CMD))
X		if (c == C_TOP_PAGE)
X		    current_msg = n_array[0];
X		else
X		    current_msg = min(n_array[screen-1], msg_cnt-1);
X	    else {
X		mac_flush();
X		bell();
X	    }
X
X	when C_NEXT_SCREEN : /* next page */
X	    move(LINES-1, 0), refresh();
X	    if (msg_cnt-1 > n_array[screen-1]) {
X		clear();
X		set_screen_size();
X		(void) cmd_line(strcpy(buf, "\\headers +"), msg_list);
X		if (current_msg < n_array[0])
X		    current_msg = n_array[0];
X		(void) curses_help_msg(TRUE);
X		return redo = 0;
X	    } else {
X		mac_flush();
X		bell();
X	    }
X
X	when C_PREV_SCREEN : /* previous page */
X	    move(LINES-1, 0), refresh();
X	    if (n_array[0] > 0) {
X		clear();
X		set_screen_size();
X		(void) cmd_line(strcpy(buf, "\\headers -"), msg_list);
X		if (current_msg > n_array[screen-1])
X		    current_msg = n_array[screen-1];
X		(void) curses_help_msg(TRUE);
X		return redo = 0;
X	    } else {
X		mac_flush();
X		bell();
X	    }
X
X	/* read from/save to record file (.mushrc) */
X	when C_SOURCE : case C_SAVEOPTS : {
X	    int argc;
X	    char *argv[3];
X	    print("%s filename [default]: ",
X		(c == C_SOURCE)? "source" : "save options to");
X	    argc = Getstr(file, COLS-40, 0);
X	    clr_bot_line();
X	    if (argc < 0)
X		break;
X	    if (argc > 0)
X		argv[1] = file, argc = 2;
X	    else
X		argc = 1;
X	    argv[argc] = NULL;
X	    turnon(glob_flags, PRE_CURSES);
X	    if (c == C_SOURCE) {
X		(void) source(argc, argv);
X		mac_flush(); /* can't change things in mid-macro */
X		redo = isoff(glob_flags, CNTD_CMD);
X	    } else
X		(void) save_opts(argc, argv);
X	    turnoff(glob_flags, PRE_CURSES);
X	}
X
X	/*
X	 * search commands
X	 */
X	when C_NEXT_SEARCH : case C_PREV_SEARCH : case C_CONT_SEARCH :
X	    if (c != C_CONT_SEARCH)
X		c = search(0 + (c == C_PREV_SEARCH));
X	    else
X		c = search(-1);
X	    if (ison(glob_flags, CNTD_CMD))
X		putchar('\n');
X	    if (c == 0)
X		break;
X	    if (ison(glob_flags, CNTD_CMD))
X		print("%-.*s",COLS-2, compose_hdr(current_msg)), putchar('\n');
X	    if (n_array[0] > current_msg || n_array[screen-1] < current_msg) {
X		redo = 1;
X		if (isoff(glob_flags, CNTD_CMD))
X		    (void) cmd_line(sprintf(buf, "\\headers %d",
X					    current_msg+1), msg_list);
X	    }
X
X	/*
X	 * actions on messages
X	 */
X	/* delete/undelete */
X	when C_DELETE_MSG : case C_DELETE_LIST :
X	case C_UNDEL_MSG : case C_UNDEL_LIST :
X	    if (!msg_cnt) {
X		print("No messages.");
X		if (ison(glob_flags, CNTD_CMD))
X		    putchar('\n');
X		break;
X	    }
X	    if (ison(glob_flags, READ_ONLY)) {
X		mac_flush();
X		print("Folder is read-only.");
X		if (ison(glob_flags, CNTD_CMD))
X		    putchar('\n');
X		break;
X	    }
X	    Debug("current message = %d", current_msg + 1);
X	    if (!*list)
X		set_msg_bit(msg_list, current_msg);
X	    turnon(glob_flags, DO_UPDATE);
X	    for (n = 0; n < msg_cnt; n++)
X		if (msg_bit(msg_list, n)) {
X		    if (c == C_DELETE_MSG || c == C_DELETE_LIST)
X			turnon(msg[n].m_flags, DELETE|DO_UPDATE);
X		    else
X			turnoff(msg[n].m_flags, DELETE);
X		    if (isoff(glob_flags, CNTD_CMD) && (msg_cnt < screen ||
X			n >= n_array[0] && n <= n_array[screen-1])) {
X			move(max(1, n - n_array[0] + 1), 0);
X			printw("%-.*s", COLS-1, compose_hdr(n));
X		    } else
X			redo = 1;
X		}
X	    if (ison(glob_flags, CNTD_CMD) || *list) {
X		/* print(), THEN putchar() -- overwrite line */
X		if (ison(glob_flags, CNTD_CMD)) {
X		    print("%sdeleted %s",
X		    (c == C_DELETE_MSG || c == C_DELETE_LIST)? "":"un", list);
X		    putchar('\n');
X		}
X		if (c == C_DELETE_MSG || c == C_DELETE_LIST) {
X		    if (ison(msg[current_msg].m_flags, DELETE) ||
X			    ison(msg[current_msg].m_flags, SAVED))
X			(void) next_msg();
X		    if (isoff(msg[current_msg].m_flags, DELETE) &&
X			    do_set(set_options, "autoprint"))
X			return C_DISPLAY_MSG;
X		}
X		if (ison(glob_flags, CNTD_CMD))
X		    puts(compose_hdr(current_msg));
X		else if (current_msg < n_array[0]
X			|| current_msg > n_array[screen-1])
X		    redo = 1;
X	    }
X
X	/*
X	 * write/save messages.  If a list is necessary, the user already
X	 * entered it above since he must have used a capital letter. If so,
X	 * list will contain good data (already been validated above).
X	 * if a list is given, set iscurses to 0 so that print statements
X	 * will scroll and the user sees the multiple output. else, one
X	 * line can go on the bottom line just fine.
X	 */
X	when C_WRITE_MSG : case C_SAVE_MSG : case C_COPY_MSG :
X	case C_WRITE_LIST : case C_SAVE_LIST : case C_COPY_LIST : {
X	    register char *p =
X		(c == C_WRITE_MSG || c == C_WRITE_LIST)? "write" :
X		(c == C_SAVE_MSG  || c == C_SAVE_LIST)? "save" : "copy";
X	    if (!msg_cnt) {
X		print("No messages.");
X		if (ison(glob_flags, CNTD_CMD))
X		    putchar('\n');
X		break;
X	    }
X	    if (c != C_WRITE_MSG && c != C_WRITE_LIST) {
X		char *f = do_set(set_options, "mbox");
X		if (f)
X		    (void) sprintf(file, "[%s]", f);
X		else
X		    (void) sprintf(file, "[%s]", DEF_MBOX);
X	    } /* else file is already init'd to "" */
X	    print(sprintf(buf, "filename to %s%s: ", p, file));
X	    if (Getstr(file, COLS-1-strlen(buf), 0) >= 0) {
X		char *argv[3];
X		clr_bot_line();
X		argv[0] = strcpy(buf, p);
X		p = file; skipspaces(0);
X		argv[1] = (*p) ? p : NULL;
X		argv[2] = NULL;
X		if (!*list)
X		    set_msg_bit(msg_list, current_msg);
X		move(LINES-1, 0), refresh();
X		if (*list)
X		    iscurses = FALSE;
X		/* Turn on piping to make save_msg look at msg_list */
X		turnon(glob_flags, IS_PIPE);
X		if (save_msg(1 + (*file != '\0'), argv, msg_list) < 0)
X		    *list = 0;
X		turnoff(glob_flags, IS_PIPE);
X		if (ison(glob_flags, CNTD_CMD))
X		    redo = 1, putchar('\n'), puts(compose_hdr(current_msg));
X		if (*list)
X		    iscurses = redo = TRUE, turnon(glob_flags, CNTD_CMD);
X		else if (isoff(glob_flags, CNTD_CMD) && msg_cnt) {
X		    move(curlin, 0);
X		    printw("%-.*s", COLS-1, compose_hdr(current_msg));
X		    }
X	    } else {
X		print("No messages saved.");
X		if (ison(glob_flags, CNTD_CMD))
X		    putchar('\n');
X	    }
X	}
X
X	/* preserve message or place mark on message */
X	when C_PRESERVE : case C_MARK_MSG :
X	    if (!msg_cnt) {
X		print("No messages.");
X		if (ison(glob_flags, CNTD_CMD))
X		    putchar('\n');
X		break;
X	    }
X	    if (ison(msg[current_msg].m_flags,
X			c == C_MARK_MSG ? M_PRIORITY(0) : PRESERVE))
X		turnoff(msg[current_msg].m_flags,
X			c == C_MARK_MSG ? M_PRIORITY(0) : PRESERVE);
X	    else
X		turnon(msg[current_msg].m_flags,
X			c == C_MARK_MSG ? M_PRIORITY(0) : PRESERVE);
X	    if (c != C_MARK_MSG)
X		turnon(glob_flags, DO_UPDATE);
X	    if (ison(glob_flags, CNTD_CMD)) {
X		wprint("%-.*s\n", COLS-1, compose_hdr(current_msg));
X		redo = 1;
X	    } else {
X		move(curlin, 0);
X		printw("%-.*s", COLS-1, compose_hdr(current_msg));
X	    }
X
X	/* order messages (sort) and redisplay the headers */
X	when C_SORT : case C_REV_SORT :
X	    (void) strcpy(file, "sort");
X	    if (c == C_REV_SORT) {
X		print("Reverse "), turnon(glob_flags, CONT_PRNT);
X		(void) strcat(file, " -");
X	    }
X	    print(
X	"Order messages by [author, date, length, Status, subject, priority]: "
X		);
X	    if ((c = m_getchar()) == 'a' || c == 'd' || c == 'l' ||
X		    c == 'S' || c == 's' || c == 'R' || c == 'p') {
X		print("reordering messages...");
X		(void) cmd_line(sprintf(buf, "%s %c", file, c), msg_list);
X		print_more("done.");
X		if (ison(glob_flags, CNTD_CMD))
X		    putchar('\n'), puts(compose_hdr(current_msg));
X		redo = 1;
X	    } else
X		clr_bot_line();
X
X	when C_QUIT_HARD :
X	    (void) mush_quit(0, DUBL_NULL);
X	    redo = 1; /* new mail must have come in */
X
X	/* quit or update -- vrfy_update (returns 1 if updated) */
X	when C_QUIT : case C_UPDATE : {
X	    clr_bot_line();
X	    redo = (c == C_UPDATE);
X	    if (!vrfy_update(&redo))
X		if (c == C_UPDATE)
X		    break;
X	    if (isoff(glob_flags, CNTD_CMD))
X		(void) cmd_line(sprintf(buf, "\\headers %d", current_msg+1),
X				msg_list);
X	}
X
X	when C_EXIT : case C_EXIT_HARD :
X	    clr_bot_line();
X	    iscurses = FALSE;
X	    if (c != C_EXIT && c != C_EXIT_HARD)
X		putchar('\n');
X	    cleanup(0);
X
X	/* change to a new folder */
X	when C_FOLDER :
X	    for (;;) {
X		SIGRET (*oldint)(), (*oldquit)();
X		on_intr();
X		print("New folder (?=list): ");
X		c = Getstr(file, COLS-22, 0);
X		off_intr();
X		if (c > 0) {
X		    if (!strcmp(file, "?")) {
X			clr_bot_line();
X			iscurses = 0;
X			puts("folders in your folder directory:");
X			(void) cmd_line(strcpy(buf, "\\folders"), msg_list);
X	puts("Precede folder names with a +. `%' to specify system mailbox.");
X			turnon(glob_flags, CNTD_CMD), iscurses = 1;
X			continue;
X		    }
X		    clearok(stdscr, FALSE);
X		    /* if vrfy_update doesn't verify, but folder command fails,
X		     * then we need to reset the updatability of current folder
X		     */
X		    c = (ison(glob_flags, DO_UPDATE))? TRUE : FALSE;
X		    if (strcmp(file, "-?")) {
X			redo = 1; /* so vrfy_update() won't quit */
X			(void) vrfy_update(&redo);
X		    }
X		    move(LINES-1, 0), refresh();
X		    if (cmd_line(sprintf(buf, "folder ! -N %s", file),
X			     msg_list) == -1) {
X			if (c) /* remember state of updatability of folder */
X			    turnon(glob_flags, DO_UPDATE);
X			if (ison(glob_flags, CNTD_CMD))
X			    putchar('\n');
X		    } else
X			redo = 1, turnoff(glob_flags, CNTD_CMD);
X		    break;
X		} else {
X		    print("\"%s\" unchanged.", mailfile);
X		    if (ison(glob_flags, CNTD_CMD))
X			putchar('\n');
X		    break;
X		}
X	    }
X
X	/* shell escape */
X	when C_SHELL_ESC :
X	    print("Shell command: ");
X	    if (Getstr(file, COLS-24, 0) < 0)
X		clr_bot_line();
X	    else {
X		putchar('\n');
X		iscurses = FALSE;
X		(void) cmd_line(sprintf(buf, "sh %s", file), msg_list);
X		iscurses = TRUE;
X		turnon(glob_flags, CNTD_CMD);
X	    }
X
X	/* do a line-mode like command */
X	when C_CURSES_ESC :
X	    print(":");
X	    if (Getstr(buf, COLS-2, 0) < 0)
X		break;
X	    putchar('\n');
X	    iscurses = FALSE;
X	    if (!*buf) {
X		/* return -1 because iscurses = 0 is not enough! */
X		redo = 0;
X		endwin(); /* this turns echoing back on! */
X		echo_off();
X		return -1;
X	    }
X	    /* The "source" and "curses" commands need some indication
X	     * that we are in curses mode, so use the PRE_CURSES flag.
X	     */
X	    turnon(glob_flags, PRE_CURSES);
X	    (void) cmd_line(buf, msg_list);
X	    /* they may have affected message status or had text output */
X	    turnon(glob_flags, CNTD_CMD), redo = 1;
X	    turnoff(glob_flags, PRE_CURSES);
X	    iscurses = TRUE;
X	    if (msg_cnt)
X		puts(compose_hdr(current_msg));
X
X	/* send message to printer, redo to display 'p' status */
X	when C_PRINT_MSG : redo = (lpr(0, DUBL_NULL, msg_list) == 0);
X
X	/* cd */
X	when C_CHDIR :
X	    print("chdir to [~]: ");
X	    if (Getstr(file, COLS-12, 0) < 0)
X		break;
X	    clr_bot_line();
X	    (void) cmd_line(sprintf(buf, "cd %s", file), msg_list);
X	    if (ison(glob_flags, CNTD_CMD))
X		putchar('\n');
X
X	/* variable settings */
X	when C_VAR_SET : case C_IGNORE : case C_ALIAS : case C_OWN_HDR :
X	    curs_vars(c); /* CNTD_CMD is reset if there's output! */
X
X	when C_VERSION :
X	    (void) do_version();
X	    if (ison(glob_flags, CNTD_CMD))
X		putchar('\n');
X
X	when C_MAIL_FLAGS :
X	    print("flags [-?]: ");
X	    if ((c = Getstr(file, COLS-12, 0)) < 0)
X		break;
X	    putchar('\n');
X	    if (c == 0)
X		(void) strcpy(file, "-?");
X	    else
X		redo = 1; /* In case of -f flag, to display the 'f' status */
X	/* Fall thru */
X	case C_MAIL : {
X	    u_long flgs = glob_flags;
X	    turnon(glob_flags, IGN_BANG);
X	    clr_bot_line();
X	    iscurses = FALSE;
X	    (void) cmd_line(sprintf(buf, "mail %s", file), msg_list);
X	    glob_flags = flgs;
X	    iscurses = TRUE, turnon(glob_flags, CNTD_CMD);
X	    if (msg_cnt)
X		print("%-.*s", COLS-2, compose_hdr(current_msg)), putchar('\n');
X	}
X
X	/* reply to mail */
X	when C_REPLY_SENDER : case C_REPLY_ALL : {
X	    register char *p = (c == C_REPLY_ALL)? "replyall" : "replysender";
X	    clr_bot_line();
X	    iscurses = FALSE;
X	    if (isoff(msg[current_msg].m_flags, REPLIED))
X		redo = 1;
X	    (void) cmd_line(sprintf(buf, "%s %d", p, current_msg+1),
X		msg_list);
X	    if (msg_cnt)
X		puts(compose_hdr(current_msg));
X	    iscurses = TRUE, turnon(glob_flags, CNTD_CMD);
X	}
X
X	/* type out a message */
X	when C_DISPLAY_MSG : case C_TOP_MSG : case C_DISPLAY_NEXT :
X	    if (!msg_cnt ||
X		c != C_DISPLAY_NEXT && ison(msg[current_msg].m_flags, DELETE)) {
X		if (!msg_cnt)
X		    print("No messages.");
X		else
X		    print("Message %d deleted; type 'u' to undelete.",
X				      current_msg+1);
X		if (ison(glob_flags, CNTD_CMD))
X		    putchar('\n');
X		break;
X	    }
X	    clr_bot_line();
X	    iscurses = FALSE;
X	    if (ison(glob_flags, CNTD_CMD))
X		putchar('\n');
X	    if (c == C_DISPLAY_MSG)
X		c = cmd_line(strcpy(buf, "type"), msg_list);
X	    else if (c == C_TOP_MSG)
X		c = cmd_line(strcpy(buf, "top"), msg_list);
X	    else {
X		/* "next" screws up the screen whether it displays or not */
X		(void) cmd_line(strcpy(buf, "next"), msg_list);
X		c = 0;
X	    }
X	    if (c > -1)
X		turnon(glob_flags, CNTD_CMD), redo = 1;
X	    iscurses = TRUE;
X	    puts(compose_hdr(current_msg));
X
X	/* bind a key or string to a curses-mode command */
X	when C_BIND :  case C_UNBIND : case C_MAP : case C_BIND_MACRO :
X	case C_MAP_BANG : {
X	    char *argv[2];
X	    argv[0] = (c == C_BIND) ? "bind" :
X		      (c == C_UNBIND) ? "unbind" :
X		      (c == C_MAP) ? "map" :
X		      (c == C_MAP_BANG) ? "map!" : "bind-macro";
X	    argv[1] = NULL;
X	    if (bind_it(1, argv) < -1)
X		turnon(glob_flags, CNTD_CMD);
X	    else if (ison(glob_flags, CNTD_CMD)) /* if it was set anyway */
X		putchar('\n');
X	    else
X		(void) curses_help_msg(TRUE);
X	}
X
X	when C_MACRO : 
X	    turnon(glob_flags, IN_MACRO);
X	    /* Current macro should already be in the mac_stack, so
X	     * all we have to do here is look for the next character
X	     */
X
X	/* help stuff */
X	when C_HELP :
X	    move(LINES-1, 0), refresh();
X	    (void) help(0, "curses", cmd_help);
X	    turnon(glob_flags, CNTD_CMD);
X	    if (msg_cnt)
X		puts(compose_hdr(current_msg));
X
X	otherwise :
X	    mac_flush();
X	    bell();
X	    if (ison(glob_flags, CNTD_CMD)) {
X		/* use print instead of puts to overwrite hit_return msg */
X		print("unknown command"), putchar('\n');
X		redo = 1;
X	    }
X    }
X
X    if (ison(glob_flags, CNTD_CMD)) {
X	int old_cnt = msg_cnt;
X	if (!(c = hit_return()) && !redo && msg_cnt == old_cnt)
X	    redraw();
X	clr_bot_line();
X	if (old_cnt !=  msg_cnt)
X	    redo = 1;
X	if (c)
X	    return c;
X    }
X    if (redo) {
X	set_screen_size(); /* it may have changed */
X	n = current_msg;
X	clear();
X	if (/* msg_cnt < screen || */ n_array[0] < n && n < n_array[screen-1])
X	    (void) do_hdrs(0, DUBL_NULL, NULL);
X	else
X	    (void) cmd_line(sprintf(buf, "\\headers %d", n+1), msg_list);
X	(void) curses_help_msg(TRUE);
X	redo = 0;
X    }
X    return 0;
}
X
vrfy_update(redo)
int *redo;
{
X    char buf[16];
X    int c;
X
X    /* update current folder */
X    if (ison(glob_flags, DO_UPDATE)) {
X	if (ison(glob_flags, READ_ONLY)) {
X	    mac_flush();
X	    print("Folder is read-only.");
X	    if (ison(glob_flags, CNTD_CMD))
X		putchar('\n');
X	    return 0;
X	}
X	print("Update folder [y]? ");
X	if ((c = getchar()) != 'y' && c != 'Y' && c != '\n' && !isspace(c)) {
X	    print("Folder unchanged.");
X	    if (ison(glob_flags, CNTD_CMD))
X		putchar('\n');
X	    return 0;
X	}
X    } else if (*redo)
X	return 1;
X    if (cmd_line(strcpy(buf, *redo? "update" : "quit"), msg_list) != -1
X	    && ison(glob_flags, CNTD_CMD))
X	*redo = 1, turnoff(glob_flags, CNTD_CMD);
X    turnoff(glob_flags, DO_UPDATE);
X    return 1; /* make sure bottom line is clear and no reverse video */
}
X
scrn_line(line, buf)
char *buf;
{
#ifndef A_CHARTEXT
X    (void) strncpy(buf, stdscr->_y[line], COLS-1);
X    buf[COLS-1] = 0; /* strncpy does not null terminate */
#else
X    int n;
X
X    for (n = 0; n < COLS; n++)
X	if ((buf[n] = (mvinch(line, n) & A_CHARTEXT)) == '\0')
X	    break;
X    buf[n] = '\0';
#endif /* A_CHARTEXT */
}
X
/*
X * Generate the help message from the variable curses_help.
X *  If visible is true, the message is displayed,
X *  otherwise its size (in lines) is computed and returned.
X */
curses_help_msg(visible)
int visible;
{
X    int count, i, len, siz = 0, mxm = 0;
X    static int old_siz = 0;
X    register struct cmd_map *list;
X    extern struct cmd_map map_func_names[];
X    char *curs_help = do_set(set_options, "curses_help"), **format;
X
X    if (!curs_help) {
X	if (old_siz && visible) {
X	    int bot = min(n_array[screen-1], msg_cnt-1);
X	    move(max(0, bot - n_array[0]) + 2, 0), clrtobot();
X	    old_siz = 0;
X	}
X	return 0;
X    } else if (!*curs_help)
X	curs_help = DEF_CURSES_HELP;
X    /* Split the help string into words */
X    if (!(format = mk_argv(curs_help, &count, FALSE)) || count <= 0)
X	return 0;
X    /* Generate a help message for each word */
X    for (i = 0; i < count; i++) {
X	char buf[MAX_BIND_LEN*2+MAX_LONG_CMD+5], asc[MAX_BIND_LEN*2];
X
X	buf[0] = '\0'; /* default to empty in case of no match */
X	for (list = cmd_map; list; list = list->m_next) {
X	    if (!strcmp(format[i], map_func_names[list->m_cmd].m_str)) {
X		len = strlen(sprintf(buf, "(%s) %s  ",
X				ctrl_strcpy(asc, list->m_str, FALSE),
X				map_func_names[list->m_cmd].m_str));
X		if (len > mxm)
X		    mxm = len;
X		break;
X	    }
X	}
X	strdup(format[i], buf); /* replace word with its "definition" */
X    }
X    /* Columnate the output nicely */
X    if (mxm > 0) {
X	len = (COLS - 1) / mxm;
X	if (len == 0) {
X	    if (visible)
X		print("Curses help message too long!");
X	    return 0;
X	}
X	siz = count / len;
X	if (count % len)
X	    siz++;
X	if (siz > LINES / 3) {
X	    if (visible)
X		print("Curses help message too long!");
X	    return 0;
X	}
X	if (visible) {
X	    int next = LINES - 1 - siz;
X	    if (old_siz > siz) {
X		int bot = min(n_array[screen-1], msg_cnt-1);
X		move(max(0, bot - n_array[0]) + 2, 0), clrtobot();
X	    }
X	    old_siz = siz;
X	    for (i = 0; i < count; i++) {
X		if (!(i % len))
X		    move(next, 0), clrtoeol(), ++next;
X		if (format[i][0])
X		    printw("%-*.*s", mxm, mxm, format[i]);
X	    }
X	    refresh();
X	}
X    }
X    free_vec(format);
X    return siz;
}
X
set_screen_size()
{
X    int hlp_siz = LINES - 2 - curses_help_msg(FALSE); 
X
X    if (!do_set(set_options, "screen"))
#ifdef USG
X	switch (_tty.sg_ospeed & CBAUD)
#else /* USG */
X	switch (_tty.sg_ospeed)
#endif /* USG */
X	{
X	    case B300 :  screen = min(hlp_siz, 7);
X	    when B1200 : screen = min(hlp_siz, 14);
X	    when B2400 : screen = min(hlp_siz, 22);
X	    otherwise :  screen = hlp_siz;
X	}
X    else
X	screen = min(screen, hlp_siz);
}
X
/*
X * prompt for a carriage return, but return whatever user types unless
X * it's a character which he might regret (like 'q' or 'x'). Ignore
X * interrupts (kind of) because we have nowhere to longjmp to.  When we
X * return, we'll setjmp again (top of loop.c)
X */
hit_return()
{
X    int c;
X
X    turnon(glob_flags, IGN_SIGS);
X    iscurses = FALSE;
X    (void) check_new_mail();
X    iscurses = TRUE;
X    mail_status(1), addstr("...continue... "), refresh();
X    c = getcmd();
X    turnoff(glob_flags, IGN_SIGS);
X
X    /* don't let the user type something he might regret */
X    if (c == C_QUIT || c == C_EXIT)
X	return C_NULL;
X    return c;
}
X
curses_msg_list(str, list, m_list)
register char *str, *list;
char m_list[];
{
X    register char *p = NULL;
X    int c, sv_cur_msg = current_msg;
X
X    print(str);
X    c = Getstr(list, COLS-13, 0);
X    move(LINES-1, 0), refresh();
X    if (c <= 0 || !(p = do_range(list, m_list)) ||
X	(p == list && *p && *p != '$' && *p != '^')) {
X	if (p)
X	    print("Invalid message list: %s", p);
X	current_msg = sv_cur_msg;
X	return 0;
X    }
X    current_msg = sv_cur_msg;
X    return 1;
}
X
curs_vars(which)
int which;  /* really, a char */
{
X    char c, buf[128], buf2[128], *string;
X    struct options **list;
X
X    switch(which) {
X	case C_OWN_HDR : string = "my_hdr", list = &own_hdrs;
X	when C_ALIAS : string = "alias", list = &aliases;
X	when C_IGNORE : string = "ignore", list = &ignore_hdr;
X	when C_VAR_SET : string = "set", list = &set_options;
X	otherwise : clr_bot_line(); return;
X    }
X
X    print("%s [? Set Unset All]: ", string);
X    c = m_getchar();
X    clr_bot_line();
X    switch (Lower(c)) {
X	/* if help, print help -- if "all", show all settings. */
X	case '?' : case 'a' :
X	    if (c == '?') {
X		if (!strcmp(string, "set")) {
X		    print("which variable? [all <var>]: ");
X		    if ((c = Getstr(buf+1, COLS-40, 0)) < 0)
X			return;
X		    clr_bot_line();
X		    buf[0] = '?';
X		    if (c > 0) {
X			char *argv[3];
X			argv[0] = string;
X			argv[1] = buf;
X			argv[2] = NULL;
X			Lower(buf[1]);
X			if (!strcmp(buf+1, "a"))
X			    (void) strcpy(buf+1, "all");
X			if (!strcmp(buf+1, "all"))
X			    turnon(glob_flags, CNTD_CMD);
X			(void) set(2, argv, (char *) 0);
X			break;
X		    }
X		}
X		/* help returns next command (hit_return) */
X		(void) help(0, string, cmd_help);
X		turnon(glob_flags, CNTD_CMD);
X		return;
X	    }
X	    turnon(glob_flags, CNTD_CMD);
X	    (void) do_set(*list, NULL);
X
X	/* if set, prompt for string and let user type */
X	when 's' :
X	    print("set: ");
X	    c = Getstr(buf, COLS-18, 0);
X	    clr_bot_line();
X	    if (c > 0)
X		(void) cmd_line(sprintf(buf2, "%s %s", string, buf), msg_list);
X
X	/* if unset, just as easy as set! */
X	when 'u' :
X	    print("unset: ", string);
X	    if (Getstr(buf, COLS-18, 0) > 0 && !un_set(list, buf))
X		print("%s isn't set", buf);
X    }
X    if (ison(glob_flags, CNTD_CMD))
X	putchar('\n');
X    else
X	(void) curses_help_msg(TRUE);
}
#endif /* CURSES */
SHAR_EOF
chmod 0644 curses.c ||
echo 'restore of curses.c failed'
Wc_c="`wc -c < 'curses.c'`"
test 28914 -eq "$Wc_c" ||
	echo 'curses.c: original size 28914, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= dates.c ==============
if test -f 'dates.c' -a X"$1" != X"-c"; then
	echo 'x - skipping dates.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting dates.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'dates.c' &&
/* @(#)dates.c	3.0	(c) copyright 3/01/90 (Dan Heller, Bart Schaefer) */
X
#include "mush.h"
X
/*
X *   %ld%3c%s	gmt_in_secs weekday orig_timezone
X * The standard "date format" stored in the msg data structure.
X */
char *day_names[] = {
X    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
char *month_names[] = {     /* imported in pick.c */
X    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
X
static int mtbl[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
X
/* Time Zone Stuff */
struct zoneoff {
X    char *zname;
X    int hr_off;
X    int mn_off;
} time_zones[] = {
X    /* Universal Time */
X    { "UT",	  0,  0 },	{ "GMT",	  0,  0 },
X    /* European Time */
X    { "BST",	  1,  0 },				    /* Brit. Summer */
X    { "EET",	  2,  0 },	{ "EEST",	  3,  0 },	/* Eastern */
X    				{ "EET DST",	  3,  0 },
X    { "MET",	  1,  0 },	{ "MEST",	  2,  0 },	/* Middle */
X    				{ "MET DST",	  2,  0 },
X    { "WET",	  0,  0 },	{ "WEST",	  1,  0 },	/* Western */
X    				{ "WET DST",	  1,  0 },
X    /* North American Time */
X    { "NST",	 -3,-30 },				    /* Newfoundland */
X    { "AST",	 -4,  0 },	{ "ADT",	 -3,  0 },	/* Atlantic */
X    { "EST",	 -5,  0 },	{ "EDT",	 -4,  0 },	/* Eastern */
X    { "CST",	 -6,  0 },	{ "CDT",	 -5,  0 },	/* Central */
X    { "MST",	 -7,  0 },	{ "MDT",	 -6,  0 },	/* Mountain */
X    { "PST",	 -8,  0 },	{ "PDT",	 -7,  0 },	/* Pacific */
X    { "YST",	 -9,  0 },	{ "YDT",	 -8,  0 },	/* Yukon */
X    { "HST",	-10,  0 },	{ "HDT",	 -9,  0 },	/* Hawaii */
X    /* Japan and Australia Time */
X    {"JST",	  9,  0 },					/* Japan */
X    {"AEST",	 10,  0 },	{"AESST",	 11,  0 },	/* Eastern */	
X    {"ACST",	  9, 30 },	{"ACSST",	 10, 30 },	/* Central */
X    {"AWST",	  8,  0 },					/* Western */
X    /* Military Time */
X    { "A",	  1,  0 },	{ "N",		 -1,  0 },
X    { "B",	  2,  0 },	{ "O",		 -2,  0 },
X    { "C",	  3,  0 },	{ "P",		 -3,  0 },
X    { "D",	  4,  0 },	{ "Q",		 -4,  0 },
X    { "E",	  5,  0 },	{ "R",		 -5,  0 },
X    { "F",	  6,  0 },	{ "S",		 -6,  0 },
X    { "G",	  7,  0 },	{ "T",		 -7,  0 },
X    { "H",	  8,  0 },	{ "U",		 -8,  0 },
X    { "I",	  9,  0 },	{ "V",		 -9,  0 },
X    { "K",	 10,  0 },	{ "W",		-10,  0 },
X    { "L",	 11,  0 },	{ "X",		-11,  0 },
X    { "M",	 12,  0 },	{ "Y",		-12,  0 },
X    { "Z",	  0,  0 },
X    /* Also legal is +/- followed by hhmm offset from UT */
X    { 0, 0, 0 }
};
X
long
getzoff(zone)
char *zone;
{
X    struct zoneoff *z;
X    int hours, mins;
X    char sign[2];
X
X    if (!zone || !*zone)
X	return 0;
X    if (sscanf(zone, "%1[-+]%2d%2d", sign, &hours, &mins) == 3)
X	return (hours * 3600 + mins * 60) * (*sign == '-' ? -1 : 1);
X    for (z = time_zones; z->zname; z++)
X	if (lcase_strncmp(zone, z->zname, -1) == 0)
X	    return z->hr_off * 3600 + z->mn_off * 60;
X    return 0;
}
X
/*
X * Kind of the reverse of localtime() and gmtime() -- converts a struct tm
X * to time in seconds since 1970.  Valid until 2038.
X * If the "zone" argument is present, it modifies the return value.
X * The zone should be a string, either +/-hhmm or symbolic (above).
X * The "how" argument should be -1 to convert FROM gmt, 1 to convert TO gmt,
X * and (as a "side-effect") 0 if the Zone parameter is to be ignored.
X *
X * Thanks to ktl@wag240.caltech.edu (Kian-Tat Lim) for similar algorithm
X * written in perl from which this was derived.
X */
long
time2gmt(tym, zone, how)
struct tm *tym;
char *zone;
int how;
{
X    long year, julian;
X
X    if (tym->tm_year < 100)
X	year = tym->tm_year + 1900;
X    if (year < 69)
X	year += 100;
X
X    julian = 365 * (year - 1970) + (int)((year - 1970 + 1) / 4) +
X		mtbl[tym->tm_mon] + tym->tm_mday - 1;
X		/* tym->tm_yday might not be valid */
X    if (tym->tm_mon > 1 && year%4 == 0 && (year%100 != 0 || year%400 == 0))
X	julian++;
X    julian *= 86400;	/* convert to seconds */
X    julian += (tym->tm_hour * 60 + tym->tm_min) * 60 + tym->tm_sec;
X    return julian - getzoff(zone) * how;
}
X
struct tm *
time_n_zone(zone)
char *zone;
{
X    struct tm *T;
X    char *tz;
#if defined(SYSV) || defined(TIMEZONE)
X    long	  x;
X
X    (void) time(&x);
X    T = localtime(&x);
#ifndef TIMEZONE
X    {
X	extern char *tzname[];
X	tz = tzname[T->tm_isdst];
X    }
#endif /* TIMEZONE */
#else /* SYSV || TIMEZONE */
X    extern char     *timezone();
X    struct timeval  mytime;
X    struct timezone myzone;
X
X    (void) gettimeofday(&mytime, &myzone);
X    T = localtime(&mytime.tv_sec);
X    tz = timezone(myzone.tz_minuteswest, (T->tm_isdst && myzone.tz_dsttime));
#endif /* !SYSV */
X
#ifdef TIMEZONE
#ifdef DAYLITETZ
X    if (T->tm_isdst)
X	tz = DAYLITETZ;
X    else
#endif /* DAYLITETZ */
X    tz = TIMEZONE;
#endif /* TIMEZONE */
SHAR_EOF
true || echo 'restore of dates.c failed'
fi
echo 'End of  part 6'
echo 'File dates.c is continued in part 7'
echo 7 > _shar_seq_.tmp
exit 0
exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.