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

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

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

#!/bin/sh
# do not concatenate these parts, unpack them in order with /bin/sh
# file pick.c continued
#
if test ! -r _shar_seq_.tmp; then
	echo 'Please unpack part 1 first!'
	exit 1
fi
(read Scheck
 if test "$Scheck" != 20; 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 pick.c'
else
echo 'x - continuing file pick.c'
sed 's/^X//' << 'SHAR_EOF' >> 'pick.c' &&
X *    1d 1Y
X *
X * Return number of args parsed; -1 on error.
X */
ago_date(argv)
char **argv;
{
#define SECS_PER_DAY   (60 * 60 * 24)
#define SECS_PER_WEEK  (SECS_PER_DAY * 7)
#define SECS_PER_MONTH ((int)(SECS_PER_DAY * 30.5))
#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	    p -= 2;
X	    break; /* really a syntax error, but it could be other pick args */
X	}
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	    ++p; /* 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;
}
SHAR_EOF
echo 'File pick.c is complete' &&
chmod 0644 pick.c ||
echo 'restore of pick.c failed'
Wc_c="`wc -c < 'pick.c'`"
test 17697 -eq "$Wc_c" ||
	echo 'pick.c: original size 17697, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= print.c ==============
if test -f 'print.c' -a X"$1" != X"-c"; then
	echo 'x - skipping print.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting print.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'print.c' &&
/* @(#)print.c	2.4	(c) copyright 10/15/86 (Dan Heller) */
X
#include "mush.h"
#include <varargs.h>
X
/*ARGSUSED*/
/*VARARGS1*/
void
error(fmt, arg1, arg2, arg3, arg4)
register char *fmt;
char *arg1, *arg2, *arg3, *arg4;
{
X    char buf[BUFSIZ];
X
X    (void) sprintf(buf, fmt, arg1, arg2, arg3, arg4);
X    sprintf(buf+strlen(buf), ": %s\n", sys_errlist[errno]);
#ifdef SUNTOOL
X    if (istool > 1)
X	ok_box(buf);
X    else
#endif /* SUNTOOL */
X    print(buf);
}
X
#if defined(SUNTOOL) || defined(CURSES)
/*
X * print just like printf -- to a window, to curses, or to stdout.  Use vprintf
X * if available.  msgbuf is the buffer used to print into if necessary.
X * If you're running SUN3.2 or higher, the typecast (unsigned char *)msgbuf
X * (where indicated) otherwise, msgbuf is not typecast at all.
X */
/*VARARGS*/
void
print(va_alist)
va_dcl
{
X    static char msgbuf[BUFSIZ];
X    char *fmt;
X    va_list args;
#ifndef VPRINTF
X    FILE foo;
#endif /* VPRINTF */
X    static int x; /* position on line saved for continued prints */
X    char *p; /* same type as struct file _ptr,_buf in stdio.h */
X
#ifdef CURSES
X    if (iscurses) {
X	if (isoff(glob_flags, CONT_PRNT))
X	    move(LINES-1, x = 0), refresh();
X    } else
#endif /* CURSES */
X	if (istool < 2) {
X	    va_start(args);
X	    fmt = va_arg(args, char *);
#ifdef VPRINTF
X	    (void) vprintf(fmt, args);
#else /* VPRINTF */
X	    _doprnt(fmt, args, stdout);
#endif /* VPRINTF */
X	    va_end(args);
X	    (void) fflush(stdout);
X	    return;
X	}
X    va_start(args);
X    fmt = va_arg(args, char *);
X    if (fmt) {
#ifdef VPRINTF
X	(void) vsprintf(msgbuf, fmt, args); /* NULL in fmt reprints last msg */
#else /* VPRINTF */
X	foo._cnt = BUFSIZ;
X	foo._base = foo._ptr = msgbuf; /* may have to cast(unsigned char *) */
X	foo._flag = _IOWRT+_IOSTRG;
X	(void) _doprnt(fmt, args, &foo);
X	*foo._ptr = '\0'; /* plant terminating null character */
#endif /* VPRINTF */
X    }
X    va_end(args);
X    if (istool) {
X	wprint("%s", msgbuf);
X	return;
X    }
X    p = msgbuf;
X    if (iscurses)
X	while (p = index(p, '\n'))
X	    *p = ' ';
#ifdef CURSES
X    if (iscurses) {
X	p = msgbuf;
X	for (;;) {
X	    int len = COLS-1-x; /* space remaining at till the eol */
X	    /* don't wrap the line! Just print it and refresh() */
X	    printw("%-.*s", len, p), clrtoeol(), refresh();
X	    /* if length(p) (remainder of msgbuf) doesn't wrap, break loop */
X	    if ((x += strlen(p)) < COLS-1)
X		break;
X	    /* the next print will overwrite bottom line, so \n first */
X	    putchar('\n'), move(LINES-1, x = 0); /* reset x */
X	    /* move p forward the number of chars we were able to display */
X	    p += len;
X	    turnon(glob_flags, CNTD_CMD); /* display ...continue... prompt */
X	}
X	turnoff(glob_flags, CONT_PRNT);
X	(void) fflush(stdout); /* some sys-v's aren't fflushing \n's */
X	return;
X    }
#endif /* CURSES */
}
X
#endif /* SUNTOOL || CURSES */
X
/* for curses mode */
clr_bot_line()
{
X    print("");
}
X
#ifdef SUNTOOL
X
/*
X * wprint prints stuff to a scrollable textsw.  This is intended for
X * print statements that need to be recalled since print() overwrites
X * its last message.
X * For non-suntool mode, wprint() is just like printf().  For curses mode,
X * wprint() does -not- act like print() -- lines length is not counted
X * and newlines are not stripped.
X */
/*VARARGS*/
void
wprint(va_alist)
va_dcl
{
#ifndef VPRINTF
X    FILE foo;
#endif /* VPRINTF */
X    char msgbuf[BUFSIZ]; /* we're not getting huge strings */
X    char *fmt;
X    va_list args;
X
X    if (istool < 2) {
X	va_start(args);
X	fmt = va_arg(args, char *);
#ifdef VPRINTF
X	(void) vprintf(fmt, args);
#else /* VPRINTF */
X	_doprnt(fmt, args, stdout);
#endif /* VPRINTF */
X	va_end(args);
X	(void) fflush(stdout);
X	return;
X    }
X
X    if (!mfprint_sw)
X        return;
X    va_start(args);
X    fmt = va_arg(args, char *);
X    if (fmt) {
#ifdef VPRINTF
X	(void) vsprintf(msgbuf, fmt, args); /* NULL in fmt reprints last msg */
#else /* VPRINTF */
X	foo._cnt = BUFSIZ;
X	foo._base = foo._ptr = msgbuf; /* may have to cast (unsigned char *) */
X	foo._flag = _IOWRT+_IOSTRG;
X	_doprnt(fmt, args, &foo); /* format like printf into msgbuf via foo */
X	*foo._ptr = '\0'; /* plant terminating null character */
#endif /* VPRINTF */
X	textsw_insert(mfprint_sw, msgbuf, strlen(msgbuf));
X    }
X    va_end(args);
}
X
#include <sundev/kbio.h>
#include <sundev/kbd.h>
X
bell()
{
#ifdef KIOCCMD
X    if (istool) {
X	int kbd = open("/dev/kbd", O_WRONLY, 0);
X	struct timeval timer;
X	timer.tv_sec = 0;
X	timer.tv_usec = 100000;
X	if (kbd != -1) {
X	    int cmd = KBD_CMD_BELL;
X	    (void) ioctl(kbd, KIOCCMD, &cmd);
X	    (void) select(32, (fd_set *) 0,(fd_set *) 0,(fd_set *) 0, &timer);
X	    cmd = KBD_CMD_NOBELL;
X	    (void) ioctl(kbd, KIOCCMD, &cmd);
X	    (void) close(kbd);
X	}
X    } else
#endif /* KIOCCMD */
X	(void) fputc('\007', stderr), (void) fflush(stderr);
X    return 0;
}
X
#endif /* SUNTOOL */
X
#ifdef BSD
#undef fputs
X
/*
X * The standard 4.x BSD fputs() does not return any useful value.
X * For compatibility with Sun and SysV fputs(), we use this wrapper.
X */
X
Fputs(line, fp)
char *line;
FILE *fp;
{
X    clearerr(fp);
X    (void) fputs(line, fp);
X    if (ferror(fp))
X	return EOF;
X    return 0;
}
X
#endif /* BSD */
SHAR_EOF
chmod 0644 print.c ||
echo 'restore of print.c failed'
Wc_c="`wc -c < 'print.c'`"
test 5154 -eq "$Wc_c" ||
	echo 'print.c: original size 5154, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= sample.mushrc ==============
if test -f 'sample.mushrc' -a X"$1" != X"-c"; then
	echo 'x - skipping sample.mushrc (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting sample.mushrc (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'sample.mushrc' &&
# sample.mushrc
# By Bart Schaefer and Dan Heller
#
# Change mush's temp file directory, to avoid quota collisions.
# /usr/tmp so tmpfiles won't be rm'd before they can be recovered.
set tmpdir=/usr/tmp
X
# Set the folder and mbox locations; the + expands to value of "folder".
set folder=$HOME/Mail mbox=+mbox
X
# Set up the display early to allow quick exit in headers-only mode.
# The hdrs_only flag is true if the command line was: "mush -H".
# The variable hdr_format is set to change the format of the header
# summaries that are displayed.
if hdrs_only
X    set hdr_format='%28a %M %-2N  %.33s'
X    exit	# Quits reading this file
else
X    set hdr_format='%28a %M %-2N (%3.5l li) %.25s'
endif
X
# Set the prompt to show current time, name of the current folder,
# current message number, and count of total messages.
set prompt="(%T) %f: #%m of %t> "
X
# Hitting <CR> should do nothing (helps make mush more shell-like).  If
# newline is not set, hitting <CR> prints the next message (like Mail).
# This variable could be set to any mush command.
set newline
X
# These variables are helpful for new users:
#	ask		-- always prompt for Subject: of mail
#	ignoreeof	-- ignore end-of-file from keyboard
#	verify		-- query that all is well before sending mail
#	warning		-- report miscellaneous possible problems
set ask verify warning
set ignoreeof="echo 'Use "'"'quit'"'" to quit.'"
X
# When reading messages, don't bother looking at lengthy, boring headers.
ignore message-id received via status
X
# Since mush has csh-like history, you might find it annoying to type
# things like "mail host\!host1\!host2\!user" from within the mush shell.
# Setting nonobang will prevent the "unknown event" and allow the !'s to
# be typed without having to be preceded by backslashes.
set nonobang
X
# By default, mush's history is set to the last command only.  Set it to
# remember the last 100 commands.
set history = 100
X
# If the variable "unix" is set, then any command that isn't a mush command
# will execute the command as if you typed it from the shell.  Note, such
# commands will not go through another shell -- this is it.
set unix
X
# Mush tries to read ~/.mushrc first, then it tries ~/.mailrc.  Assuming
# you use *this* file as your .mushrc, source the contents of .mailrc as
# well in case there are Mail aliases that are set there.
source $HOME/.mailrc
X
# Use a real pager.
set pager=less
X
# When typing in a letter, it is sometimes convenient to have lines wrap
# automatically similar to editors like vi and emacs.  In this example, if
# the user types past column 74, a newline will automatically be inserted.
set wrapcolumn=74
X
# If "autosign" is set, then a file can be read in automatically whenever
# mail is sent.  This file is normally your "signature," that is, your
# name and other information you want included in every message.
set autosign = ~/.signature
X
# When you use the -i option to reply, or use the ~i tilde escape in a letter
# when in compose mode, the current message will be included in your text.
# Put a nice wrapper around those included messages.  Here, show the author's
# name and the subject of his letter, label the end, and add a trailing blank
# to separate each inclusion and make finding the end easier.
set pre_indent_str='On %M %N, %T, %.50n wrote:\n} Subject: %.65s'
set indent_str='} '	# actual message text is preceded by a "}"
set post_indent_str='}-- End of excerpt from %.50n\n'
X
# Label replies with a header showing the who, what, and when of the
# message being replied-to.
set in_reply_to='%f\n\t"%s" (%d)'
X
# Mail routing and address-fixing conveniences.  If auto_route is set, then
# replies to messages take a closer look at the addresses of the recipients.
# If any redundant paths are present, they are pruned.  Also, the path that
# precedes any hosts listed in the "known_hosts" list is truncated.  This is
# useful for uucp sites only, and is therefore commented out in this sample.
# set auto_route known_hosts="sun ucbcad well unicom"
X
# The "alts" command specifies alternate addresses that I have.  Here,
# "*" expands to any "path" whose recipient ends with the user's current
# login name.  If another login name is desired, the login and/or path
# to that login must be preceded by a !.  Otherwise, standard paths are used.
alts "*"
X
# The "map" command can rebind certain key sequences in tty-mode only.
# Here, if the user types two R's in a row at the prompt, then the string
# "reply -ei " will be echoed as if the user typed it.
map RR "reply -ei "
# "rr" will do a reply and do the newline for you so you don't have to.
map rr "reply\n"
X
# The "map!" command is similar to "map" in that you can do keyboard
# acceleration, but map! occurs during letter composition mode only.
map! '\CT' '    '	# ^T generates 4 spaces in composition mode.
# Here, hitting * twice will append a pre-signature.
map! ** "\n			Later,\n"
X
# Be careful with map and map! -- you can cause an infinite loop.
# Your interrupt key (usually ^C) will stop such loops.
X
# The curses mode allows the screen to be set up like a full screen editor.
# There are basic "curses commands" which are bound to keyboard key-sequences
# (usually one character).  The user can rebind these keys to suit his tastes.
# Note that the binding for R below removes the binding of reply-all.
#
set curses_help		# Unset this to remove help message in curses.
bind \n display		# Hit return to display the next message.
bind t top		# Make it easier to see the top few lines.
bind e macro "[line-mode]edit\n"	# Quick edit from curses.
bind P macro "[line-mode]Print\n"	# Show me all the headers.
bind R macro "[line-mode]reply -ei "	# Reply with inclusion and edit.
bind A macro "R[getline]~t\n\CUargv\n"	# R to Dan w/auto address fix.
X
# "cmd" is used to set command line aliases similar to the way "csh"
# does it.  The only difference is that "alias" is a reserved word in
# Mush and Mail, so cmd is used.
#
cmd dq 'd \!*; q'		# Delete a message list, then quit.
cmd unread 'flags \!* U O'	# Mark messages unread.
cmd : curses			# Colon now "toggles" curses mode.
X
# Find messages from mailer-daemon (ignore upper/lower case).
cmd md 'pick -i -f mailer-daemon'
# Because mush can pipe commands to one another, including "cmd"'s, this
# example will delete all messages from mailer-daemon
cmd dmd 'md | delete'
X
# aliases -- just like Mail's, but you can specify "names"
alias argv Dan Heller <argv@sun.com>
alias bart Bart Schaefer <schaefer@cse.ogi.edu>
alias mush-users Mush Users <mush-users-request@apple.com>
SHAR_EOF
chmod 0644 sample.mushrc ||
echo 'restore of sample.mushrc failed'
Wc_c="`wc -c < 'sample.mushrc'`"
test 6535 -eq "$Wc_c" ||
	echo 'sample.mushrc: original size 6535, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= setopts.c ==============
if test -f 'setopts.c' -a X"$1" != X"-c"; then
	echo 'x - skipping setopts.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting setopts.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'setopts.c' &&
/* setopts.c	(c) copyright 1986 (Dan Heller) */
X
#include "mush.h"
#include "bindings.h"
X
static void
insert_option(list, opt, order)
struct options **list, *opt;
int order;    /* Insert in sorted order? */
{
X    while (*list && (!order || (strcmp((*list)->option, opt->option) < 1)))
X	list = &((*list)->next);
X    opt->next = *list;
X    *list = opt;
}
X
/* add an option indicated by "set option[=value]" or by "alias name alias"
X * function is recursive, so multilists get appended accordingly
X */
add_option(list, argv)
register struct options **list;
register char **argv;
{
X    register struct options *tmp;
X    register char *option, *value = NULL;
X
X    if (!(option = *argv))
X	return 1;
X    /* check for one of three forms:
X     * option=value  option= value  option = value
X     */
X    if (value = index(option, '=')) {
X	if (value == option) {
X	    print("No variable specified\n");
X	    return 0;
X	}
X	/* "option=value" strip into option="option" value="value" */
X	*value++ = 0; /* option is now a null terminated `option' */
X	if (*value || (value = *++argv)) { /* "option= value" */
X	    ++argv;
X	}
X    } else if (*++argv && !strcmp(*argv, "=")) {
X	if (value = *++argv) /* "option = value" */
X	    ++argv;
X    }
X
X    /* check for internal vars that can't be set this way */
X    if (*list == set_options && check_internal(option)) {
X	print("You can't change %s with \"set\".\n", option);
X	return 0;
X    }
X
X    /* check to see if option is already set by attempting to unset it */
X    if (un_set(list, option) == -1)
X	return 0;
X
X    /* now make a new option struct and set fields */
X    if (!(tmp = (struct options *)calloc((unsigned)1,sizeof(struct options)))) {
X	error("calloc");
X	return -1;
X    }
X    tmp->option = savestr(option);
X    tmp->value = savestr(value); /* strdup handles the NULL case */
X
X    insert_option(list, tmp, (list != &own_hdrs));
X
X    /* check for options which must have values or are used frequently */
X    if (*list == set_options) {
#if defined(CURSES) || defined(SUNTOOL)
X	if (!strcmp(tmp->option, "no_reverse"))
X	    turnoff(glob_flags, REV_VIDEO);
X	else
#endif /* CURSES || SUNTOOL */
#ifdef SUNTOOL
X	if (!strcmp(tmp->option, "tool_help"))
X	    if (tmp->value && *(tmp->value))
X		strdup(tool_help, tmp->value);
X	    else {
X		int n = 0;
X		char *p = getpath(TOOL_HELP, &n);
X		if (n)
X		    strdup(tool_help, "tool_help");
X		else
X		    strdup(tool_help, p);
X		strdup(tmp->value, tool_help);
X	    }
X	else
#endif /* SUNTOOL */
X	if (!strcmp(tmp->option, "cmd_help"))
X	    if (tmp->value && *(tmp->value))
X		strdup(cmd_help, tmp->value);
X	    else {
X		int n = 0; /* don't ignore no such file or directory */
X		char *p = getpath(COMMAND_HELP, &n);
X		if (n)
X		    strdup(cmd_help, "cmd_help");
X		else
X		    strdup(cmd_help, p);
X		strdup(tmp->value, cmd_help);
X	    }
X	else if (!strcmp(tmp->option, "prompt"))
X	    prompt = (tmp->value)? tmp->value : DEF_PROMPT;
X	else if (!strcmp(tmp->option, "warning"))
X	    turnon(glob_flags, WARNING);
X	else if (!strcmp(tmp->option, "mil_time"))
X	    turnon(glob_flags, MIL_TIME);
#ifndef MSG_SEPARATOR
X	else if (!strcmp(tmp->option, "date_received"))
X	    turnon(glob_flags, DATE_RECV);
#endif /* MSG_SEPARATOR */
X	else if (!strcmp(tmp->option, "escape")) {
X	    escape = (tmp->value)? tmp->value : DEF_ESCAPE;
X	    escape[1] = 0; /* only one character, please */
X	} else if (!strcmp(tmp->option, "hdr_format"))
X	    hdr_format = (tmp->value)? tmp->value : DEF_HDR_FMT;
X	else if (!strcmp(tmp->option, "crt")) {
X	    if (!istool)
X		crt = (tmp->value)? max(atoi(tmp->value), 2): 18;
X	}
X	else if (!strcmp(tmp->option, "screen")) {
X	    screen = (tmp->value)? max(atoi(tmp->value), 1): 18;
#ifdef CURSES
X	    if (iscurses && screen > LINES-2)
X		screen = LINES-2;
#endif /* CURSES */
X	} else if (!strcmp(tmp->option, "wrapcolumn")) {
X	    char wval[16];
X	    wrapcolumn =
X		(tmp->value && *(tmp->value))? max(atoi(tmp->value), 0): 78;
#ifdef CURSES
X	    /* Use COLS-2 because of silly terminals like vt100 */
X	    if (iscurses && wrapcolumn > COLS - 2)
X		wrapcolumn = COLS - 2;
#endif /* CURSES */
X	    xfree(tmp->value);
X	    tmp->value = savestr(sprintf(wval, "%d", wrapcolumn));
X	} else if (!strcmp(tmp->option, "history"))
X	    init_history((value && *value)? atoi(value) : 1);
X	else if (!strcmp(tmp->option, "realname")) {
X	    char *new[4];
X	    new[1] = "NAME";
X	    new[2] = tmp->value;
X	    new[3] = NULL;
X	    (void) Setenv(3, new); /* new[0] is ignored */
X	} else if (!strcmp(tmp->option, "known_hosts")) {
X	    register char *p;
X	    int n;
X	    /* in case user separated with commas */
X	    for (p = index(tmp->value, ','); p; p = index(p+1, ','))
X		*p = ' ';
X	    free_vec(known_hosts);
X	    known_hosts = mk_argv(tmp->value, &n, FALSE);
X	} else if (!strcmp(tmp->option, "hostname")) {
X	    register char *p;
X	    int n;
X	    /* in case user separated with commas */
X	    for (p = index(tmp->value, ','); p; p = index(p+1, ','))
X		*p = ' ';
X	    free_vec(ourname);
X	    ourname = mk_argv(tmp->value, &n, FALSE);
X	} else if (!strcmp(tmp->option, "complete")) {
X	    if (value && *value) {
X		m_xlate(value); /* use the original, don't change tmp->value */
X		complete = value[0];
X		complist = value[1];
X	    } else {
X		tmp->value = savestr("\\E\\CD");
X		complete = '\033';
X		complist = '\004';
X	    }
X	}
X    }
X
X    if (*argv)
X	return add_option(list, argv);
X    return 1;
}
X
/*
X * If str is NULL, just print options and their values. Note that numerical
X * values are not converted to int upon return.  If str is not NULL
X * return the string that matched, else return NULL;
X */
char *
do_set(list, str)
register struct options *list;
register char *str;
{
X    register struct options *opts;
X
X    if (!str)
X	(void) do_pager(NULL, TRUE); /* page using internal pager */
X
X    for (opts = list; opts; opts = opts->next)
X	if (!str) {
X	    (void) do_pager(opts->option, FALSE);
X	    if (opts->value && *opts->value) {
X		(void) do_pager("     \t", FALSE);
X		(void) do_pager(opts->value, FALSE);
X	    }
X	    if (do_pager("\n", FALSE) == EOF)
X		break;
X	} else {
X	    if (strcmp(str, opts->option))
X		continue;
X	    if (opts->value)
X		return opts->value;
X	    else
X		return "";
X	}
X
X    if (!str)
X	(void) do_pager(NULL, FALSE); /* terminate internal pager */
X
X    /* if we still haven't matched, check for environment vars */
X    if (str && list == set_options) {
X	register int N, n;
X	for (N = 0; environ[N]; N++) {
X	    char *p = index(environ[N], '=');
X	    if (p)
X		*p = 0;
X	    n = lcase_strncmp(str, environ[N], -1);
X	    if (p)
X		*p = '=';
X	    if (!n)
X		return p+1;
X	}
X    }
X    return NULL;
}
X
/*
X * unset the variable described by p in the list "list".
X * if the variable isn't set, then return 0, else return 1.
X */
un_set(list, p)
register struct options **list;
register char *p;
{
X    register struct options *opts = *list, *tmp;
X
X    if (!list || !*list || !p || !*p)
X	return 0;
X    if (*list == set_options) {
#if defined(CURSES) || defined(SUNTOOL)
X	if (!strcmp(p, "no_reverse"))
X	    turnon(glob_flags, REV_VIDEO);
X	else
#endif /* CURSES || SUNTOOL */
X	if (!strcmp(p, "prompt"))
X	    prompt = DEF_PROMPT;
X	else if (!strcmp(p, "warning"))
X	    turnoff(glob_flags, WARNING);
X	else if (!strcmp(p, "mil_time"))
X	    turnoff(glob_flags, MIL_TIME);
#ifndef MSG_SEPARATOR
X	else if (!strcmp(p, "date_received"))
X	    turnoff(glob_flags, DATE_RECV);
#endif /* MSG_SEPARATOR */
X	else if (!strcmp(p, "escape"))
X	    escape = DEF_ESCAPE;
X	else if (!strcmp(p, "hdr_format"))
X	    hdr_format = DEF_HDR_FMT;
X	else if (!strcmp(p, "crt"))
X	    crt = 18;
X	else if (!strcmp(p, "screen")) {
X	    screen = 18;
#ifdef CURSES
X	    if (iscurses && screen > LINES-2)
X		screen = LINES-2;
#endif /* CURSES */
X	} else
#ifdef SUNTOOL
X	if (!strcmp(p, "tool_help")) {
X	    int n = 0;
X	    char *p2 = getpath(TOOL_HELP, &n);
X	    if (n)
X		strdup(tool_help, "tool_help");
X	    else
X		strdup(tool_help, p2);
X	} else
#endif /* SUNTOOL */
X	if (!strcmp(p, "cmd_help")) {
X	    int n = 0; /* don't ignore no such file or directory */
X	    char *p2 = getpath(COMMAND_HELP, &n);
X	    if (n)
X		strdup(cmd_help, "cmd_help");
X	    else
X		strdup(cmd_help, p2);
X	} else if (!strcmp(p, "wrapcolumn"))
X	    wrapcolumn = 0;
X	else if (!strcmp(p, "history"))
X	    init_history(1);
X	else if (!strcmp(p, "known_hosts")) {
X	    free_vec(known_hosts);
X	    known_hosts = DUBL_NULL;
X	} else if (!strcmp(p, "hostname")) {
X	    free_vec(ourname);
X	    ourname = DUBL_NULL;
X	} else if (ison(glob_flags, IS_GETTING) && !strcmp(p, "edit_hdrs")) {
X	    wprint("You must finish this letter first.\n");
X	    return -1;
X	} else if (!strcmp(p, "complete"))
X	    complete = complist = 0;
#ifdef SUNTOOL
X	else if (!strcmp(p, "compose_icon")) {
X	    if (ison(glob_flags, IS_GETTING)) {
X		wprint("You must finish this letter first.\n");
X		return -1;
X	    } else
X		/* destroy compose frame so that it is recreated
X		 * later as the proper type (base frame or subframe).
X		 */
X		if (compose_frame)
X		    destroy_compose();
X	}
#endif
X    }
X
X    if (!strcmp(p, opts->option)) {
X	*list = (*list)->next;
X	xfree (opts->option);
X	if (opts->value)
X	    xfree(opts->value);
X	xfree((char *)opts);
X	return 1;
X    }
X    for ( ; opts->next; opts = opts->next)
X	if (!strcmp(p, opts->next->option)) {
X	    tmp = opts->next;
X	    opts->next = opts->next->next;
X	    xfree (tmp->option);
X	    if (tmp->value)
X		xfree(tmp->value);
X	    xfree ((char *)tmp);
X	    return 1;
X	}
X    return 0;
}
X
/* The functions below return 0 since they don't affect
X * messages.
X */
set(n, argv, list)
register int n;
register char **argv;
char *list;
{
X    void list_to_str();
X    char firstchar = **argv;
X    register char *cmd = *argv;
X    register struct options **optlist;
X    char buf[BUFSIZ];
X
X    if (*cmd == 'u')
X	cmd += 2;
X    if (*++argv && !strcmp(*argv, "-?"))
X	return help(0, (*cmd == 'i')? "ignore": "set", cmd_help);
X
X    if (*argv && **argv == '?') {
X	int incurses;
X	if (!strcmp(*argv, "?all")) {
X	    if (incurses = iscurses) /* assign and compare to TRUE */
X		clr_bot_line(), iscurses = FALSE;
X	    (void) do_pager(NULL, TRUE); /* start internal pager */
X	    for (n = 0; variable_stuff(n, NULL, buf); n++)
X		if (do_pager(strcat(buf, "\n"), FALSE) == EOF)
X		    break;
X	    (void) do_pager(NULL, FALSE); /* terminate pager */
X	    iscurses = incurses;
X	} else {
X	    /* May return null if variable not set. */
X	    (void) variable_stuff(0, (*argv)+1, buf);
X	    print("%s\n", buf);
X	}
X	return 0;
X    }
X
X    if (firstchar == 'u') {
X	if (!*argv) {
X	    print("%s what?\n", cmd);
X	    return -1;
X	} else {
X	    optlist = (*cmd == 'i')? &ignore_hdr : &set_options;
X	    do  if (!strcmp(*argv, "*")) {
X		    while (*optlist)
X			(void) un_set(optlist, (*optlist)->option);
#ifdef SUNTOOL
X		    if (*cmd != 'i')
X			opts_panel_item(NULL);
#endif /* SUNTOOL */
X		} else if (!un_set(optlist, *argv) &&
X			do_set(set_options, "warning"))
X		    print("un%s: %s not set\n",
X			(*cmd == 'i')? "ignore" : "set", *argv);
#ifdef SUNTOOL
X		else if (*cmd != 'i')
X		    opts_panel_item(*argv);
#endif /* SUNTOOL */
X	    while (*++argv);
#ifdef SUNTOOL
X	    if (*cmd == 'i' && istool > 1)
X		update_list_textsw(&ignore_hdr);
#endif /* SUNTOOL */
X	}
X	return 0;
X    }
X
X    if (!*argv) {
X	(void) do_set((*cmd == 'i')? ignore_hdr: set_options, NULL);
X	return 0;
X    }
X
X    /*
X     * Check for input redirection.  If so, set the variable to the ascii
X     * value of the current msg_list.
X     */
X    if (ison(glob_flags, IS_PIPE)) {
X	char *newargv[4];
X
X	if (*cmd == 'i') {
X	    print("You can't pipe to the \"%s\" command.\n", cmd);
X	    return -1;
X	}
X	if (newargv[0] = index(argv[0], '='))
X	    *newargv[0] = 0;
X	list_to_str(list, buf);
X	if (!buf[0] && !do_set(set_options, argv[0])) {
X	    return 0;
X	}
X	newargv[0] = argv[0];
X	newargv[1] = "=";
X	newargv[2] = buf;
X	newargv[3] = NULL;
X	(void) add_option(&set_options, newargv);
X	return 0;
X    }
X
X    /*
X     * finally, just set the variable the user requested.
X     */
X    (void) add_option((*cmd == 'i')? &ignore_hdr: &set_options, argv);
#ifdef SUNTOOL
X    if (istool > 1)
X	if (*cmd == 'i')
X	    update_list_textsw(&ignore_hdr);
X	else
X	    opts_panel_item(argv[0]);
#endif /* SUNTOOL */
X    return 0;
}
X
/*
X *   The alts list is a list of hostnames or pathnames where the user
X * has an account.  If he doesn't specify "metoo", then when replying
X * to mail, if his address is listed, it will be removed.  The syntax
X * is compatible with ucb Mail in that just hostnames can be used.
X * However, there is an added feature that mush provides which another
X * login name or path to another login can be specified by preceding the
X * path or login with a !
X * "argv" may be a file pointer to write the data into by use of save_opts()
X */
alts(argc, argv)
register char **argv;
{
X    char buf[BUFSIZ], *p;
X
X    /* check here first because a 0 argc means to write it to a file */
X    if (argc <= 1) {
X	int n;
X	if (!alternates)
X	    return 0;
X	if (argc == 0)
X	    (void) fprintf((FILE *)argv, "alts ");
X	for (n = 0; alternates[n]; n++) {
X	    p = 0;
X	    buf[0] = 0;
X	    (void) strcpy(&buf[1], alternates[n]);
X	    if (buf[1] != '*')
X		(void) reverse(&buf[1]);
X	    if ((p = rindex(&buf[1], '!')) && !lcase_strncmp(p+1, login, -1))
X		*p = 0;
X	    else if (buf[1] != '*')
X		buf[0] = '!';
X	    if (argc == 0)
X		(void) fprintf((FILE *)argv, "%s ", *buf? buf : &buf[1]);
X	    else
X		wprint("%s ", *buf? buf : &buf[1]);
X	    if (p)
X		*p = '!';
X	}
X	if (argc == 0)
X	    (void) fputc('\n', (FILE *)argv);
X	else
X	    wprint("\n");
X	return 0;
X    }
X
X    if (argc-- && *++argv && !strcmp(*argv, "-?"))
X	return help(0, "alts", cmd_help);
X
X    free_vec(alternates);
X    if (alternates = (char **)calloc((unsigned)argc+1, sizeof(char *)))
X	while (argc-- > 0) {
X	    if (argv[argc][0] == '!')
X		alternates[argc] = savestr(reverse(&argv[argc][1]));
X	    else if (argv[argc][0] == '*') {
X		alternates[argc] = savestr(argv[argc]);
X	    } else {
X		if (index(argv[argc], '@'))
X		    bang_form(buf, argv[argc]);
X		else {
X		    p = buf + Strcpy(buf, argv[argc]);
X		    *p++ = '!', p += Strcpy(p, login);
X		}
X		alternates[argc] = savestr(reverse(buf));
X	    }
X	}
X    return 0;
}
X
save_opts(cnt, argv)
char **argv;
{
X    char file[MAXPATHLEN], *tmp;
X    register FILE *fp;
X
X    if (cnt && *++argv && !strcmp(*argv, "-?"))
X	return help(0, "source", cmd_help);
X    if (cnt && *argv)
X	(void) strcpy(file, *argv);
X    else if ((tmp = getenv("MUSHRC")) || (tmp = getenv("MAILRC")))
X	(void) strcpy(file, tmp);
X    else {
X	char *home = do_set(set_options, "home");
X	if (!home || !*home)
X	    home = ALTERNATE_HOME;
X	/* if .mushrc doesn't exist, check .mailrc. If neither, force .mushrc */
X	if (Access(sprintf(file, "%s/%s", home, MAILRC), F_OK) &&
X		Access(sprintf(file, "%s/%s", home, ALTERNATE_RC), F_OK))
X	    (void) sprintf(file, "%s/%s", home, MAILRC);
X    }
X
X    cnt = 1;
X    tmp = getpath(file, &cnt);
X    if (cnt) {
X	if (cnt == -1) {
X	    print("%s: %s\n", file, tmp);
X	    return -1;
X	} else {
X	    print("%s is a directory.\n", tmp);
X	    return -2;
X	}
X    }
X    /* See if the file exists and confirm overwrite */
X    if (!Access(tmp, F_OK)) {
X	int overwrite = TRUE;
X	char buf[BUFSIZ];
X	if (!istool) {
X	    print("\"%s\" exists. Overwrite? ", trim_filename(tmp));
X	    if (Getstr(buf, 3, 0) <= 0 || lower(*buf) != 'y')
X		overwrite = FALSE;
X	}
#ifdef SUNTOOL
X	else {
X	    sprintf(buf, "\"%s\" exists. Overwrite? ", trim_filename(tmp));
X	    overwrite = ask(buf);
X	}
#endif /* SUNTOOL */
X	if (!overwrite) {
X	    print("\"%s\" unchanged.\n", tmp);
X	    return -3;
X	}
X    }
X    if (!(fp = fopen(tmp, "w"))) {
X	error("Can't open %s", file);
X	return -1;
X    }
X
X    save_list("basic variable settings", set_options, "set", '=', fp);
X
X    save_list("mail headers for outgoing mail", own_hdrs, "my_hdr", 0, fp);
X
X    save_list("aliases", aliases, "alias", 0, fp);
X
X    (void) alts(0, (char **)fp);
X
X    save_list("headers to ignore", ignore_hdr, "ignore", ' ', fp);
X
X    save_list("command abbreviations", functions, "cmd", ' ', fp);
X
X    save_list("command macros for function keys", fkeys, "fkey", ' ', fp);
X
#ifdef CURSES
X    save_cmd("curses mode key bindings", cmd_map, "bind", 1, fp);
#endif /* CURSES */
X
X    save_cmd("line mode mappings", line_map, "map", 0, fp);
X
X    save_cmd("composition mode mappings", bang_map, "map!", 0, fp);
X
X    (void) fclose(fp);
X    print("All variables and options saved in %s\n", trim_filename(tmp));
X    return 0;
}
X
save_list(title, list, command, equals, fp)
struct options *list;
register char *command, *title, equals;
register FILE *fp;
{
X    register struct options *opts;
X    register char *p;
X
X    if (!list)
X	return;
X    (void) fprintf(fp, "#\n# %s\n#\n", title);
X    for (opts = list; opts; opts = opts->next) {
X	if (list == set_options && !strcmp(opts->option, "cwd"))
X	    continue; /* don't print $cwd */
X	(void) fprintf(fp, "%s %s", command, opts->option);
X	if (opts->value && *opts->value) {
X	    register char *quote;
X	    if (!equals)
X		quote = NO_STRING;
X	    else if (p = any(opts->value, "\"'"))
X		if (*p == '\'')
X		    quote = "\"";
X		else
X		    quote = "'";
X	    else
X		if (!any(opts->value, " \t;|"))
X		    quote = NO_STRING;
X		else
X		    quote = "'";
X	    (void) fputc(equals? equals: ' ', fp);
X	    (void) fprintf(fp, "%s%s%s", quote, opts->value, quote);
X	}
X	(void) fputc('\n', fp);
X    }
}
X
extern struct cmd_map map_func_names[];
X
save_cmd(title, list, command, equals, fp)
struct cmd_map *list;
register char *command, *title;
register int equals;
register FILE *fp;
{
X    register struct cmd_map *opts;
X    register char *p;
X    char buf[MAX_MACRO_LEN * 2];
X
X    if (!list)
X	return;
X    (void) fprintf(fp, "#\n# %s\n#\n", title);
X    for (opts = list; opts; opts = opts->m_next) {
X	register char *quote;
X	if ((p = any(opts->m_str, "\"'")) && *p == '\'')
X	    quote = "\"";
X	else
X	    quote = "'";
X	(void) fprintf(fp, "%s %s%s%s", command, quote,
X			ctrl_strcpy(buf, opts->m_str, TRUE), quote);
X	if (equals && map_func_names[opts->m_cmd].m_str)
X	    (void) fprintf(fp, " %s", map_func_names[opts->m_cmd].m_str);
X	if (opts->x_str && *opts->x_str) {
X	    if ((p = any(opts->x_str, "\"'")) && *p == '\'')
X		quote = "\"";
X	    else
X		quote = "'";
X	    (void) fprintf(fp, " %s%s%s", quote,
X			ctrl_strcpy(buf, opts->x_str, TRUE), quote);
X	}
X	(void) fputc('\n', fp);
X    }
}
X
/*
X * do_alias handles aliases, header settings, functions, and fkeys.
X * since they're all handled in the same manner, the same routine is
X * used. argv[0] determines which to use.
X * alias is given here as an example
X *
X * alias           identify all aliases
X * alias name      identify alias
X * alias name arg1 arg2 arg3... -> name="arg1 arg2 arg3"; call add_option
X * unalias arg1 [arg2 arg3 ... ]        unalias args
X *
X * same is true for dealing with your own headers.
X * (also the expand command)
X */
do_alias(argc, argv)
register char **argv;
{
X    register char *cmd = *argv, *p;
X    struct options **list;
X    char firstchar = *cmd, buf[BUFSIZ];
X
X    if (argc == 0)
X	return 0 - in_pipe();
X    if (firstchar == 'u')
X	firstchar = cmd[2];
X    if (*++argv && !strcmp(*argv, "-?")) { /* doesn't apply for fkeys */
X	register char *help_str;
X	if (firstchar == 'a' || firstchar == 'e')
X	    help_str = "alias";
X	else if (firstchar == 'c')
X	    help_str = "cmd";
X	else if (firstchar == 'f')
X	    help_str = "fkey";
X	else
X	    help_str = "my_hdr";
X	return help(0, help_str, cmd_help);
X    }
X
X    if (firstchar == 'a')
X	list = &aliases;
X    else if (firstchar == 'c')
X	list = &functions;
X    else if (firstchar == 'f')
X	list = &fkeys;
X    else
X	list = &own_hdrs;
X
X    if (*cmd == 'u') {
X	if (!*argv) {
X	    print("%s what?\n", cmd);
X	    return -1;
X	/* unset a list separated by spaces or ',' */
X	} else while (*argv) {
X	    if (!strcmp(*argv, "*")) /* unset everything */
X		while (*list)
X		    (void) un_set(list, (*list)->option);
X	    else if (!un_set(list, *argv))
X		print("\"%s\" isn't set\n", *argv);
X	    argv++;
X	}
#ifdef SUNTOOL
X	if (istool > 1)
X	    update_list_textsw(list);
#endif /* SUNTOOL */
X	return 0;
X    }
X
X    if (!*argv && *cmd != 'e') {
X	/* just type out all the aliases or own_hdrs */
X	(void) do_set(*list, NULL);
X	return 0;
X    }
X
X    if (*cmd == 'e') {   /* command was "expand" (aliases only) */
X	if (!*argv) {
X	    print("expand which alias?\n");
X	    return -1;
X	} else
X	    do  {
X		print("%s: ", *argv);
X		if (p = alias_to_address(*argv))
X		    print("%s\n", p);
X	    } while (*++argv);
X	return 0;
X    }
X
X    /* at this point, *argv now points to a variable name ...
X     * check for hdr -- if so, *argv better end with a ':' (check *p)
X     */
X    if (list == &own_hdrs && !(p = index(*argv, ':'))) {
X	print("header labels must end with a ':' (%s)\n", *argv);
X	return -1;
X    }
X    if (!argv[1] && !index(*argv, '='))
X	if (p = do_set(*list, *argv))
X	    print("%s\n", p);
X	else
X	    print("%s is not set\n", *argv);
X    else {
X	char *tmpargv[2];
X	(void) argv_to_string(buf, argv);
X	if ((p = any(buf, " \t=")) && *p != '=')
X	    *p = '=';
X	/* if we're setting an alias, enforce the insertion of commas
X	 * between each well-formed address.
X	 */
X	if (list == &aliases)
X	    fix_up_addr(p+1);
X	tmpargv[0] = buf;
X	tmpargv[1] = NULL;
X	(void) add_option(list, tmpargv);
#ifdef SUNTOOL
X	if (istool > 1)
X	    update_list_textsw(list);
#endif /* SUNTOOL */
X    }
X    return 0;
}
SHAR_EOF
chmod 0644 setopts.c ||
echo 'restore of setopts.c failed'
Wc_c="`wc -c < 'setopts.c'`"
test 21221 -eq "$Wc_c" ||
	echo 'setopts.c: original size 21221, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= signals.c ==============
if test -f 'signals.c' -a X"$1" != X"-c"; then
	echo 'x - skipping signals.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting signals.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'signals.c' &&
/* @(#)signals.c	(c) copyright 10/18/86 (Dan Heller) */
X
#include "mush.h"
X
#ifdef SUNTOOL
extern int compose_destroy;
#endif
X
static int was_stopped;
X
#ifndef SYSV
extern char *sys_siglist[];
#else
/* sys-v doesn't have normal sys_siglist */
static char	*sys_siglist[] = {
/* no error */  "no error",
/* SIGHUP */	"hangup",
/* SIGINT */	"interrupt (rubout)",
/* SIGQUIT */	"quit (ASCII FS)",
/* SIGILL */	"illegal instruction (not reset when caught)",
/* SIGTRAP */	"trace trap (not reset when caught)",
/* SIGIOT */	"IOT instruction",
/* SIGEMT */	"EMT instruction",
/* SIGFPE */	"floating point exception",
/* SIGKILL */	"kill (cannot be caught or ignored)",
/* SIGBUS */	"bus error",
/* SIGSEGV */	"segmentation violation",
/* SIGSYS */	"bad argument to system call",
/* SIGPIPE */	"write on a pipe with no one to read it",
/* SIGALRM */	"alarm clock",
/* SIGTERM */	"software termination signal from kill",
/* SIGUSR1 */	"user defined signal 1",
/* SIGUSR2 */	"user defined signal 2",
/* SIGCLD */	"death of a child",
/* SIGPWR */	"power-fail restart"
};
#endif /* SYSV */
X
SIGRET
intrpt(sig)
{
X    if (!was_stopped)
X	Debug("interrupt() caught: %d\n", sig);
X    mac_flush();
X    turnon(glob_flags, WAS_INTR);
}
X
/*
X * catch signals to reset state of the machine.  Always print signal caught.
X * If signals are ignored, return.  If we're running the shell, longjmp back.
X */
/*ARGSUSED*/
SIGRET
catch(sig)
{
X    if (!was_stopped)
X	Debug("Caught signal: %d\n", sig);
X    (void) signal(sig, catch);
X    if (ison(glob_flags, IGN_SIGS) && sig != SIGTERM && sig != SIGHUP)
X	return;
X    mac_flush();
X    turnoff(glob_flags, IS_PIPE);
X    if (istool || sig == SIGTERM || sig == SIGHUP) {
#ifdef SUNTOOL
X	if (istool > 1) { /* istool is 2 if tool is complete */
X	    if (compose_destroy) {
X		if (sig == SIGTERM) /* tty_sw is dying */
X		    return;
X		if (sig == SIGHUP) { /* compose frame went away */
X		    compose_destroy = 0;
X		    compose_frame = 0;
X		    return;
X		}
X	    }
#ifndef SUN_4_0
X	    /* spurious SIGHUPs in 3.5 */
X	    if (sig == SIGHUP && window_get(tool, WIN_SHOW))
X		return;
#endif /* SUN_4_0 */
X	    istool = 1;
X	}
#endif /* SUNTOOL */
X	if (!was_stopped)
X	    print("%s: %s\n", prog_name, sys_siglist[sig]);
X	(void) setjmp(jmpbuf);
X	if (ison(glob_flags, IS_GETTING))
X	    rm_edfile(-1);
X	cleanup(sig);
X    }
X    if (!was_stopped)
X	print("%s: %s\n", prog_name, sys_siglist[sig]);
X    if (ison(glob_flags, DO_SHELL)) {
X	/* wrapcolumn may have been trashed -- restore it */
X	if (ison(glob_flags, IS_GETTING)) {
X	    char *fix = do_set(set_options, "wrapcolumn");
X	    if (fix && *fix)
X		wrapcolumn = atoi(fix);
X	}
X	turnoff(glob_flags, IS_GETTING);
#ifdef SYSV
X	/* Interrupting "await" leaves an alarm timer running, which
X	 * some SysV systems mishandle.  Clean up.
X	 */
X	if (!istool)
X	    (void) signal(SIGALRM, SIG_IGN);
#endif /* SYSV */
X	longjmp(jmpbuf, 1);
X    } else {
X	if (!was_stopped)
X	    puts("exiting");
X	cleanup(sig);
X    }
}
X
#ifdef SIGCONT
#ifdef SIGTTOU
jmp_buf ttoubuf;
X
SIGRET
tostop(sig)
{
X    (void) signal(SIGTTOU, SIG_DFL);
X    if (was_stopped)
X	longjmp(ttoubuf, 1);
}
#endif /* SIGTTOU */
X
SIGRET
stop_start(sig)
{
X    extern FILE *ed_fp;
X
X    Debug("Caught signal: %d", sig);
X    if (sig == SIGCONT) {
X	(void) signal(SIGTSTP, stop_start);
X	(void) signal(SIGCONT, stop_start);
#ifdef SIGTTOU
X	/* Restoring echo mode may cause a SIGTTOU if mush was killed
X	 * while in the background.  Jump around the echo_off() call if
X	 * we get a TTOU when attempting it.  Leave was_stopped on in
X	 * this case, and don't do all the associated prompting.
X	 */
X	(void) signal(SIGTTOU, tostop);
X	if (setjmp(ttoubuf) == 0) {
X	    echo_off();
X	    was_stopped = 0;
X	}
#ifdef CURSES
X	else
X	    iscurses = 0;
#endif /* CURSES */
#endif /* SIGTTOU */
X	if (istool || was_stopped || ison(glob_flags, IGN_SIGS) && !iscurses)
X	    return;
X	/* we're not in an editor but we're editing a letter */
X	if (ison(glob_flags, IS_GETTING)) {
X	    if (ed_fp)
X		print("(Continue editing letter)\n");
X	}
#ifdef CURSES
X	else if (iscurses)
X	    if (ison(glob_flags, IGN_SIGS)) {
X		clr_bot_line();
X		if (msg_cnt)
X		    puts(compose_hdr(current_msg));
X		mail_status(1), addstr("...continue... ");
X		refresh();
X	    } else {
X		int curlin = max(1, current_msg - n_array[0] + 1);
X		redraw();
X		print("Continue");
X		move(curlin, 0);
X		refresh();
X		/* make sure we lose reverse video on continuation */
X		if (ison(glob_flags, REV_VIDEO) && msg_cnt) {
X		    char buf[256];
X		    (void) strncpy(buf, compose_hdr(current_msg), COLS-1);
X		    buf[COLS-1] = 0; /* strncpy does not null terminate */
X		    mvaddstr(curlin, 0, buf);
X		}
X	    }
#endif /* CURSES */
X	else
X	    mail_status(1), (void) fflush(stdout);
X    } else {
#ifdef CURSES
X	if (iscurses) {
X	    /* when user stops mush, the current header is not in reverse
X	     * video -- note that a refresh() has not been called in curses.c!
X	     * so, make sure that when a continue is called, the reverse video
X	     * for the current message returns.
X	     */
X	    turnon(glob_flags, WAS_INTR);
X	    if (isoff(glob_flags, IGN_SIGS) && ison(glob_flags, REV_VIDEO) &&
X		    msg_cnt) {
X		int curlin = max(1, current_msg - n_array[0] + 1);
X		char buf[256];
X		scrn_line(curlin, buf);
X		STANDOUT(curlin, 0, buf);
X	    }
X	    print("Stopping...");
X	}
#endif /* CURSES */
X	echo_on();
X	(void) signal(SIGTSTP, SIG_DFL);
X	(void) signal(SIGCONT, stop_start);
X	was_stopped = 1;
X	(void) kill(getpid(), sig);
X    }
}
#endif /* SIGCONT */
X
/*ARGSUSED*/
void
cleanup(sig)
{
X    char buf[128], c;
X
X    if (sig != SIGTERM && sig != SIGHUP && ison(glob_flags, IGN_SIGS))
X	c = 'n';
X    else
X	c = 'y';
X
#ifdef CURSES
X    if (iscurses && sig != SIGHUP)
X	iscurses = FALSE, endwin();
#endif /* CURSES */
X
X    if (!was_stopped)
X	echo_on();
X
X    if (ison(glob_flags, IS_GETTING))
X	turnoff(glob_flags, IS_GETTING), dead_letter(sig);
X    if ((sig == SIGSEGV || sig == SIGBUS) && isoff(glob_flags, IGN_SIGS)
X	    && *tempfile && !istool) {
X	(void) fprintf(stderr, "remove %s [y]? ", tempfile), (void) fflush(stderr);
X	if (fgets(buf, sizeof(buf), stdin))
X	    c = lower(*buf);
X    }
X    if (c != 'n' && *tempfile) {
X	if (sig == SIGHUP && do_set(set_options, "hangup") &&
X		copyback(NULL, TRUE) && isoff(glob_flags, CORRUPTED))
X	    (void) unlink(tempfile);
X	else if (unlink(tempfile) && !sig && errno != ENOENT)
X	    error(tempfile);
X    }
X    if (sig == SIGSEGV || sig == SIGBUS) {
X	if (isoff(glob_flags, IGN_SIGS) && !istool) {
X	    (void) fprintf(stderr, "coredump [n]? "), (void) fflush(stderr);
X	    if (fgets(buf, sizeof(buf), stdin))
X		c = lower(*buf);
X	}
X	if (c == 'y') {
X	    if (!istool)
X		puts("dumping core for debugging");
X	    abort();
X	}
X    }
X    exit(sig);
}
X
long    last_spool_size = -1;	/* declared here cuz it's initialized here */
X
#ifdef SUNTOOL
Notify_value
do_check()
{
X    if (isoff(glob_flags, IGN_SIGS))
X	(void) check_new_mail();
X    return (NOTIFY_DONE) ;
}
#endif /* SUNTOOL */
X
/*
X * Get any new mail that has arrived.  This function assumes that a
X * call to mail_size() has already been done, so that last_spool_size
X * can be compared to spool_size to decide what should be done.
X *
X * The value for last_spool_size is updated to the new spool_size only
X * if update_size is TRUE.  check_new_mail() depends on the -1 initial
X * value of last_spool_size for correct "New mail" messages, so it
X * uses FALSE and updates last_spool_size itself.
X */
get_new_mail(update_size)
int update_size;
{
X    if (last_spool_size > spool_size && !strcmp(mailfile, spoolfile)) {
X	print("Someone changed \"%s\"!  ", mailfile);
X	if (update_size)
X	    return 1;	/* Don't reinit if called from copyback() */
X	print_more("Reinitializing...\n");
X	if (isoff(glob_flags, READ_ONLY))
X	    (void) emptyfile(&tmpf, tempfile);
X	current_msg = msg_cnt = 0;
X	turnoff(glob_flags, CORRUPTED);
X    }
X    if (ison(glob_flags, CORRUPTED))
X	return 0;
X    if (load_folder(mailfile, 1, NULL) < 1) {
X	print("Can't load new mail: \"%s\" may be corrupted!\n", mailfile);
X	turnon(glob_flags, CORRUPTED);
X	return update_size;
X	/* NOTE: The above is used to stop check_new_mail() from calling
X	 * show_new_mail(), while still allowing copyback() to detect the
X	 * possible error and to query about updating the folder.  There
X	 * should be a better-defined way to handle this.
X	 */
X    }
X    /* Prevent both bogus "new mail" messages and missed new mail */
X    last_size = msg[msg_cnt].m_offset;
X    if (!strcmp(mailfile, spoolfile)) {
X	spool_size = last_size;
X	if (last_spool_size != spool_size)
X	    turnon(glob_flags, NEW_MAIL);
X    } else if (last_spool_size < spool_size)
X	turnon(glob_flags, NEW_MAIL);
X    if (msg_cnt && current_msg < 0)
X	current_msg = 0;
X    if (last_spool_size != spool_size) {
X	if (update_size)
X	    last_spool_size = spool_size;
X	return 1;
X    }
X    return 0;
}
X
#ifdef SUNTOOL
int is_iconic, was_iconic;
#endif /* SUNTOOL */
X
/*
X * Display a summary when new mail has come in.  sprintf it all into one
X * buffer and print that instead of separate print statements to allow
X * the tool mode to make one print statement. The reason for this is that
X * when the tool is refreshed (caused by a resize, reopen, move, top, etc)
X * the last thing printed is displayed -- display the entire line.
X */
show_new_mail()
{
X    char 	   buf[BUFSIZ];
X    register char  *p = buf;
X    int		   noisy = !chk_option("quiet", "newmail");
#ifdef CURSES
X    int new_hdrs = last_msg_cnt;
#endif /* CURSES */
X
X    if (msg_cnt == last_msg_cnt)
X	return 1;	/* Nothing to print */
#ifdef SUNTOOL
X    if (istool) {
X	mail_status(0);
X	(void) do_hdrs(0, DUBL_NULL, NULL);
X	if (noisy && !chk_option("quiet", "tool"))
X	    bell();
X    }
#endif /* SUNTOOL */
X    if (msg_cnt < last_msg_cnt) {
X	last_msg_cnt = msg_cnt;
X	if (!istool)
X	    mail_status(0);
X	if (iscurses && isoff(glob_flags, CNTD_CMD))
X	    (void) do_hdrs(0, DUBL_NULL, NULL);
X	return 0;
X    }
X    if (noisy) {
X	p += Strcpy(p, "New mail ");
X	if (msg_cnt - last_msg_cnt <= 1)
X	    p += strlen(sprintf(p, "(#%d) ", msg_cnt));
X	else
X	    p += strlen(sprintf(p, "(#%d thru #%d)\n", last_msg_cnt+1,msg_cnt));
X    }
#ifdef SUNTOOL
X    /*
X     * If mush is in tool mode and in icon form, don't update
X     * last_msg_cnt so that when the tool is opened, print() will
X     * print the correct number of "new" messages.
X     */
X    if (istool && (was_iconic = (int) window_get(tool, FRAME_CLOSED)))
X	(void) strcpy(p, "\n");
X    else
#endif /* SUNTOOL */
X    {
X	if (!noisy || iscurses && isoff(glob_flags, CNTD_CMD))
X	    last_msg_cnt = msg_cnt;
X	else while (last_msg_cnt < msg_cnt) {
X	    char *p2 = compose_hdr(last_msg_cnt++) + 9;
X	    if (strlen(p2) + (p - buf) >= BUFSIZ-5) {
X		(void) strcpy(p, "...\n");
X		/* force a break by setting last_msg_cnt correctly */
X		last_msg_cnt = msg_cnt;
X	    } else
X		p += strlen(sprintf(p, " %s\n", p2));
X	}
X    }
#ifdef CURSES
X    if (iscurses && isoff(glob_flags, CNTD_CMD)) {
X	if (new_hdrs - n_array[screen-1] < screen)
X	    (void) do_hdrs(0, DUBL_NULL, NULL);
X	print("%s ...", buf);
X    } else
#endif /* CURSES */
X	if (noisy)
X	    print("%s", buf); /* buf might have %'s in them!!! */
X    return 1;
}
X
/*
X * Look for new mail and read it in if any has arrived.
X * return 0 if no new mail, 1 if new mail and -1 if new mail is in system
X * folder, but current mbox is not system mbox.
X */
check_new_mail()
{
X    int 	   ret_value;
X
X    if (ret_value = mail_size()) {
#ifdef SUNTOOL
X	/* if our status has changed from icon to open window, then
X	 * there will already be a message stating number of new
X	 * messages.  reset `n' to msg_cnt so we don't restate
X	 * the same # of new messages upon receipt of yet another new message.
X	 */
X	if (istool && !(is_iconic = ((int) window_get(tool, FRAME_CLOSED))) &&
X		was_iconic)
X	    last_msg_cnt = msg_cnt;
#endif /* SUNTOOL */
X	if (get_new_mail(0) && !show_new_mail())
X	    return 0;
X    } else
#ifdef SUNTOOL
X	if (!istool || !is_iconic)
#endif /* SUNTOOL */
X	    turnoff(glob_flags, NEW_MAIL);
X    if (last_spool_size > -1 && /* handle first case */
X	    strcmp(mailfile, spoolfile) && last_spool_size < spool_size)
X	print("You have new mail in your system mailbox.\n"), ret_value = -1;
X    last_spool_size = spool_size;
X    return ret_value;
}
X
/*ARGSUSED*/   /* we ignore the sigstack, cpu-usage, etc... */
SIGRET
bus_n_seg(sig)
{
X    (void) signal(sig, SIG_DFL);
SHAR_EOF
true || echo 'restore of signals.c failed'
fi
echo 'End of  part 20'
echo 'File signals.c is continued in part 21'
echo 21 > _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.