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

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

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

#!/bin/sh
# do not concatenate these parts, unpack them in order with /bin/sh
# file misc_frame.c continued
#
if test ! -r _shar_seq_.tmp; then
	echo 'Please unpack part 1 first!'
	exit 1
fi
(read Scheck
 if test "$Scheck" != 14; 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 misc_frame.c'
else
echo 'x - continuing file misc_frame.c'
sed 's/^X//' << 'SHAR_EOF' >> 'misc_frame.c' &&
X	    PANEL_LABEL_STRING, "",
X	    NULL);
X    free_vec(argv);
X    panel_set_value(ignore_name, "");
X    return PANEL_NONE;
}
X
static void
ignore_done()
{
X    window_destroy(ignore_frame);
X    ignore_frame = (Frame) 0;
}
X
void
do_ignore()
{
X    Panel	panel;
X
X    if (ignore_frame) {
X	window_set(ignore_frame, WIN_SHOW, TRUE, NULL);
X	return;
X    }
#ifdef SUN_3_5
X    if (nopenfiles(0) < 5) {
X	print("Too many frames; close one first!\n");
X	return;
X    }
#endif /* SUN_3_5 */
X
X    ignore_frame = window_create(tool, FRAME,
X	FRAME_SHOW_LABEL,	TRUE,
X	FRAME_LABEL,		"Ignored Headers",
X	FRAME_NO_CONFIRM,	TRUE,
X	FRAME_DONE_PROC,	ignore_done,
X	WIN_SHOW,		TRUE,
X	WIN_WIDTH,		MY_FRAME_WIDTH,
X	NULL);
X
X    panel = window_create(ignore_frame, PANEL,
X	PANEL_WIDTH,		MY_FRAME_WIDTH,
X	NULL);
X    (void) notify_interpose_event_func(panel, fkey_interposer, NOTIFY_SAFE);
X    (void) panel_create_item(panel, PANEL_BUTTON,
X	PANEL_LABEL_IMAGE,	
X	    panel_button_image(panel, "Help", 4, mush_font),
X	PANEL_NOTIFY_PROC,	frame_help,
X	PANEL_CLIENT_DATA,	"ignore",
X	NULL);
X    (void) panel_create_item(panel, PANEL_BUTTON,
X	PANEL_LABEL_IMAGE,	
X	    panel_button_image(panel, "Set", 3, mush_font),
X	PANEL_NOTIFY_PROC,	set_ignore,
X	PANEL_CLIENT_DATA,	TRUE,
X	NULL);
X    panel_create_item(panel, PANEL_BUTTON,
X	PANEL_LABEL_IMAGE,	
X	    panel_button_image(panel, "Unset", 5, mush_font),
X	PANEL_NOTIFY_PROC,	set_ignore,
X	PANEL_CLIENT_DATA,	FALSE,
X	NULL);
X
X    ignore_msg = panel_create_item(panel, PANEL_MESSAGE,
X	PANEL_LABEL_STRING,
X	    "Type name of header to ignore and then <set> or <unset>",
X	NULL);
X
X    ignore_name = panel_create_item(panel, PANEL_TEXT,
X	PANEL_LABEL_STRING,	"Ignored Header:",
X	PANEL_NOTIFY_PROC,	set_ignore,
X	PANEL_CLIENT_DATA,	1,
X	PANEL_VALUE_DISPLAY_LENGTH, 60,
X	NULL);
X    window_fit_height(panel);
X
X    ignore_list_textsw = window_create(ignore_frame, TEXTSW,
X	WIN_BELOW,		panel,
X	WIN_WIDTH,		MY_FRAME_WIDTH,
X	WIN_HEIGHT,		15 * l_height(),
#ifdef SUN_4_0 /* SunOS 4.0+ */
X	TEXTSW_LINE_BREAK_ACTION,	TEXTSW_WRAP_AT_WORD,
#else /* SUN_4_0 */
X	TEXTSW_LINE_BREAK_ACTION,	TEXTSW_WRAP_AT_CHAR,
#endif /* SUN_4_0 */
X	NULL);
X    (void) notify_interpose_event_func(ignore_list_textsw,
X	fkey_interposer, NOTIFY_SAFE);
X
X    window_fit_height(ignore_frame);
X    update_list_textsw(&ignore_hdr);
}
SHAR_EOF
echo 'File misc_frame.c is complete' &&
chmod 0644 misc_frame.c ||
echo 'restore of misc_frame.c failed'
Wc_c="`wc -c < 'misc_frame.c'`"
test 7659 -eq "$Wc_c" ||
	echo 'misc_frame.c: original size 7659, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= msgs.c ==============
if test -f 'msgs.c' -a X"$1" != X"-c"; then
	echo 'x - skipping msgs.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting msgs.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'msgs.c' &&
/* @(#)msgs.c	(c) copyright 10/18/86 (Dan Heller) */
X
#include "mush.h"
X
void
display_msg(n, flg)
register int n;
u_long flg;
{
X    char buf[32], *pager = NULL;
X
X    if (ison(msg[n].m_flags, DELETE) && !do_set(set_options, "show_deleted")) {
X	print("Message %d deleted; ", n+1);
#ifdef SUNTOOL
X	if (istool)
X	    wprint("Select UNDELETE to read.\n");
X	else
#endif /* SUNTOOL */
X	if (iscurses)
X	    print_more("Type 'u' to undelete.");
X	else
X	    wprint("Type 'undelete %d' to undelete\n", n+1);
X	return;
X    }
X    set_isread(n);
X    if (ison(flg, M_TOP)) {
X	turnon(flg, NO_HEADER);
X	print("Top of "), turnon(glob_flags, CONT_PRNT);
X    }
X
#ifdef MSG_SEPARATOR
X    turnon(flg, NO_SEPARATOR);
#endif /* MMDF */
X    if (!istool && isoff(flg, NO_PAGE) &&
X	    crt < msg[n].m_lines && isoff(flg, M_TOP)) {
X	if (!(pager = do_set(set_options, "pager")))
X	    pager = DEF_PAGER;
X	if (!*pager || !strcmp(pager, "internal"))
X	    pager = NULL; /* default to internal pager if pager set to "" */
X    }
X    (void) do_pager(pager, TRUE); /* start pager */
X    (void) do_pager(sprintf(buf, "Message #%d (%d lines)\n",
X		     n+1, msg[n].m_lines), FALSE);
X    (void) copy_msg(n, NULL_FILE, flg, NULL);
X    (void) do_pager(NULL, FALSE); /* end pager */
}
X
/*
X * copy message 'n' to file "fp" according to various flag arguments
X * return number of lines copied or -1 if system error on fputs.
X * If "fp" is null, send to internal pager.  This can only happen from
X * display_msg above.
X */
copy_msg(n, fp, flags, pattern)
register int n;
register FILE *fp;
u_long flags;
char *pattern;
{
X    register int  ignoring = 0, lines = 0;
X    register char *indent_str, *p, *end_pat = NULL;
X    int		  on_hdr = 1, top, squeeze = 0;
X    long	  still_more = 0;
X    int		  pat_len, pat_seek;
X    char 	  line[BUFSIZ], *show_hdrs = NULL;
X
X    if (ison(flags, M_TOP)) {
X	p = do_set(set_options, "toplines");
X	top = (p)? atoi(p) : crt;
X    }
X    /* When updating to a folder, always write all headers! */
X    if (ison(flags, UPDATE_STATUS))
X	turnon(flags, NO_IGNORE);
X    else if (ison(flags, NO_IGNORE) &&
X	    (p = do_set(set_options, "alwaysignore")) && !*p)
X	turnoff(flags, NO_IGNORE);	/* preserve historic behavior */
X    if (isoff(flags, NO_IGNORE)) {
X	if (do_set(set_options, "squeeze"))
X	    squeeze = 1;
X	show_hdrs = do_set(set_options, "show_hdrs");
X    }
X    if (pattern && *pattern == '/' && (end_pat = index(pattern+1, '/'))) {
X	if (end_pat[1] == ',') {
X	    pattern++;
X	    *end_pat++ = 0;
X	} else
X	    end_pat = NULL;
X    }
X    pat_len = pattern? strlen(pattern) : 0;
X    pat_seek = !!pat_len;
X
#ifdef SUNTOOL
X    xfree(more_prompt), more_prompt = NULL;
#endif /* SUNTOOL */
X
X    if (ison(flags, INDENT)) {
X	if ((indent_str = do_set(set_options, "pre_indent_str"))) {
X	    fputs(format_hdr(n, indent_str, FALSE) + 9, fp); /* magic 9 !! */
X	    fputc('\n', fp);
X	}
X	if (!(indent_str = do_set(set_options, "indent_str")))
X	    indent_str = DEF_INDENT_STR;
X	indent_str = format_hdr(n, indent_str, FALSE) + 9; /* magic 9 !! */
X    }
X    /* "line" used as dummy here, since 0 bytes read */
X    if (!msg_get(n, line, 0)) {
X	error("Unable to find msg %d", n+1);
X	return -1;
X    }
X    while (still_more < msg[n].m_size && fgets(line, sizeof (line), tmpf)) {
X	still_more += strlen(line);
#ifdef MSG_SEPARATOR
X	if (ison(flags, NO_SEPARATOR)) {
#ifdef MMDF
X	    if (!strncmp(line, MSG_SEPARATOR, 4))
#else /* !MMDF */
X	    if (!strncmp(line, MSG_SEPARATOR, strlen(MSG_SEPARATOR)))
#endif /* MMDF */
X		continue;
X	}
#endif /* MMDF */
X	/*
X	 * If squeeze is one, all blanks lines squeeze down to one blank line.
X	 * If squeeze is two, squeezing is in progress so wait for the next \n.
X	 */
X	if (*line == '\n') {
X	    if (on_hdr) {  /* blank line -- end of header */
X		on_hdr = 0;
X		if (ison(flags, NO_HEADER))
X		    continue;
X	    }
X	    if (squeeze > 1 || pat_len && pat_seek)
X		continue;
X	    else if (squeeze)
X		squeeze = 2;
X	} else {
X	    if (squeeze > 1)
X		squeeze = 1;
X	    if (pat_len && (!on_hdr || isoff(flags, NO_HEADER))) {
X		/* If we're looking for a pattern for mush-pipe, then
X		 * continue if this line doesn't match the pattern.
X		 */
X		if (pat_len == 0)
X		    continue;
X		Debug("Seeking (%s) in (%s)", pattern, line);
X		if (strncmp(line, pattern, pat_len)) {
X		    if (pat_seek)
X			continue;
X		} else if (end_pat && *end_pat++ == ',') {
X		    pattern = end_pat;
X		    if (*pattern == '/') {
X			pattern++;
X			if (end_pat = index(pattern, '/'))
X			    *end_pat++ = 0;
X		    }
X		    pat_len = pattern? strlen(pattern) : 0;
X		    pat_seek = !pat_seek;
X		} else {
X		    pat_len = 0;
X		    pat_seek = !pat_seek;
X		}
X	    }
X	}
X
X	if (ison(flags, UPDATE_STATUS))
X	    if (!strncmp(line, "Status:", 7) || !strncmp(line, "Priority:", 9))
X		continue; /* ignore "Status" and "Priority" lines */
X	    else if (!on_hdr) {
X		int i, write_priority = 0;
X		p = line;
X		p += Strcpy(p, "Priority:");
X		for (i = 0; i < MAX_PRIORITY; i++)
X		    if (ison(msg[n].m_flags, M_PRIORITY(i + 1))) {
X			write_priority = 1;
X			*p++ = ' ';
X			*p++ = i + 'A';
X		    }
X		if (write_priority) {
X		    *p++ = '\n', *p = 0;
X		    (void) fputs(line, fp);
X		}
X		/* PRESERVE here avoids changing new message status */
X		if (isoff(flags, PRESERVE) || /* NOT msg[n].m_flags */
X			ison(msg[n].m_flags, OLD) ||
X			isoff(msg[n].m_flags, UNREAD)) {
X		    p = line;
X		    p += Strcpy(p, "Status: O");
X		    if (isoff(msg[n].m_flags, UNREAD))
X			*p++ = 'R';
X		    if (ison(msg[n].m_flags, SAVED))
X			*p++ = 'S';
X		    if (ison(msg[n].m_flags, REPLIED))
X			*p++ = 'r';
X		    if (ison(msg[n].m_flags, PRINTED))
X			*p++ = 'p';
X		    if (ison(msg[n].m_flags, FORWARD))
X			*p++ = 'f';
X		    *p++ = '\n', *p = 0;
X		    (void) fputs(line, fp);
X		}
X		turnoff(flags, UPDATE_STATUS);
X		line[0] = '\n', line[1] = '\0';
X	    }
X	if (on_hdr && (isoff(flags, NO_IGNORE) || ison(flags, FORWARD))) {
X	    p = any(line, " \t:");
X	    if (!p)
X		ignoring = 0, on_hdr = 0;
X	    else if (ignoring)
X		if (*p != ':') {
X		    Debug("Ignoring: %s", line);
X		    continue;
X		} else
X		    ignoring = 0;
X	    if (p && *p == ':') {
X		*p = 0;
X		ignoring = 0;
X		if (ison(flags, FORWARD)) {
X		    if (chk_two_lists(line, IGNORE_ON_FWD, ":, \t"))
X			ignoring = 1;
X		} else if (show_hdrs) {
X		    if (!chk_two_lists(line, show_hdrs, ":, \t"))
X			ignoring = 1;
X		} else {
X		    register struct options *opts;
X		    for (opts = ignore_hdr; opts; opts = opts->next)
X			if (!lcase_strncmp(opts->option, line, -1)) {
X			    ignoring = 1;
X			    break;
X			}
X		}
X		*p = ':';
X		if (ignoring) {
X		    Debug("Ignoring: %s", line);
X		    continue;
X		}
X	    }
X	}
X	if (!on_hdr && ison(flags, M_TOP) && !--top)
X	    break;
X	if (!on_hdr && (still_more < msg[n].m_size || line[0] != '\n') ||
X		isoff(flags, NO_HEADER)) {
X	    /* note that function returns the number of lines */
X	    lines++;
X	    if (ison(flags, INDENT))
X		(void) fputs(indent_str, fp);
X	    if (!fp) {
X		if (do_pager(line, FALSE) == EOF)
X		    return -1;
X	    } else if (fputs(line, fp) == EOF)
X		/* Pipe broken, out of file space, etc */
X		return -1;
X	}
X	if (pat_seek && !pat_len)
X	    break; /* Skip the rest */
X    }
X    if (ison(flags, INDENT) &&
X	(indent_str = do_set(set_options, "post_indent_str")) && *indent_str) {
X	(void) fprintf(fp, "%s\n", format_hdr(n, indent_str, FALSE)+9);
X    }
X    if (fp && fflush(fp) == EOF)
X	return -1;	/* Write failure? */
X    return lines;
}
X
/*
X * copy tempfile back to folder.
X * Return 1 on success, 0 on failure.
X */
copyback(prompt, final)
char *prompt;
int final;	/* Are we exiting or updating? */
{
X    register int	i = 0, held = 0, saved = 0;
X    register u_long	flg = 0;
X    register FILE	*mbox = NULL_FILE, *mail_fp = NULL_FILE;
#ifdef SYSV
X    FILE 		*save_mail_fp = NULL_FILE;
#endif /* SYSV */
X    char		*mbox_file, action = 0;
X    int 		hold = 0, delete_it = 0, dont_unlink = !final;
X    int			isspool, keepsave, write_err = FALSE;
X    static int		first = 1;
X
X    /*
X     * if there is new mail in this folder, the user is notified and
X     * prompted if he really wants to update the folder.  He's either
X     * quitting or changing folders, so let him read the new mail first.
X     */
X    if (!first && mail_size()) {
lost_lock:
X	if ((ison(glob_flags, CORRUPTED) || get_new_mail(TRUE)) &&
X		prompt && isoff(glob_flags, REDIRECT) && show_new_mail()) {
X	    char buf[80];
X	    if (iscurses)
X		putchar('\n'), turnon(glob_flags, CNTD_CMD);
X	    if (!istool)
X		print("%s [n] ", prompt);
X	    buf[0] = 0;
#ifdef SUNTOOL
X	    if (istool) {
X		(void) sprintf(buf, "%s -- %s",
X			ison(glob_flags, CORRUPTED) ? "Error" : "New mail",
X			prompt);
X		if (ask(buf) != TRUE)
X		    return 0;
X	    } else
#endif /* SUNTOOL */
X		if (!Getstr(buf, sizeof (buf), 0) || lower(*buf) != 'y')
X		    return 0;
X	    turnoff(glob_flags, CORRUPTED); /* User says go ahead */
X	}
X    }
X    first = 0;
X
X    /* If the user hasn't changed anything, just return true */
X    if (isoff(glob_flags, DO_UPDATE) || ison(glob_flags, CORRUPTED))
X	return 1;
X    if (ison(glob_flags, READ_ONLY)) {
X	print("Unable to update %s: read only\n", mailfile);
X	return 0; /* user should use "exit" instead of "quit". */
X    }
X    if (!msg_cnt) /* prevent unnecessary overwrite */
X	return 1;
X
#ifdef SUNTOOL
X    if (istool) {
X	(void) notify_set_itimer_func(tool, do_check,
X	    ITIMER_REAL, (struct itimerval *) 0, (struct itimerval *) 0);
X    }
#endif /* SUNTOOL */
X
X    /* We can't lock a file unless we have an fd, but "w+" will zero
X     * the file.  If the lock later failed for any reason (possible
X     * race condition with an MTA), we would lose all current mail.
X     * So, open read/write (if possible) and truncate later.
X     */
X    if (!(mail_fp = lock_fopen(mailfile, "r+"))) {
X	error("WARNING: unable to lock %s -- update aborted", mailfile);
#ifdef SUNTOOL
X	if (istool) {
X	    write_err = 1;	/* forces return 0; below */
X	    goto resume_timer;	/* blecch */
X	}
#else /* !SUNTOOL */
X	return 0;
#endif /* SUNTOOL */
X    }
X    /* Make sure no mail arrived between the last check and when we
X     * got the lock.  If it did, release the lock and try again.
X     */
X    if (mail_size()) {
X	(void) close_lock(mailfile, mail_fp);
X	goto lost_lock;
X    }
X
X    /* open mbox if: "autodelete" AND "hold" are NOT set. */
X    if (!strcmp(mailfile, spoolfile)
X	    && !(delete_it = !!do_set(set_options, "autodelete"))
X	    && !(hold = !!do_set(set_options, "hold"))) {
X	register char *p;
X	int x = 1; /* tell getpath to ignore "ENOENT" if file not found */
X
X	if (!(p = do_set(set_options, "mbox")))
X	    p = DEF_MBOX;
X	mbox_file = getpath(p, &x); /* static data -- copy? */
X	if (x) {
X	    if (x > 0)
X		print("%s is a directory.\n", mbox_file);
X	    else
X		print("Unable to open %s: %s\n", p, mbox_file);
X	    mbox = NULL_FILE;
X	} else {
X	    if (Access(mbox_file, F_OK) == -1) /* does it exist? */
X		mbox = lock_fopen(mbox_file, "w");
X	    else
X		mbox = lock_fopen(mbox_file, "a");
X	    if (!mbox)
X		error("Unable to write to %s", mbox_file);
X	}
X    }
X
X    /* ignore signals before truncating */
X    turnon(glob_flags, IGN_SIGS);
#ifdef SYSV
X    /* SysV can't truncate a file in the middle, so we can't just
X     * write to mail_fp and close.  Instead, we save the mail_fp
X     * and reopen for writing, ignoring our own lock.  After updating,
X     * we can safely fclose both file pointers.
X     */
X    save_mail_fp = mail_fp;
X    /* This could fail if we run out of file descriptors */
X    if (!(mail_fp = fopen(mailfile, "w"))) {
X	error("WARNING: unable to reopen %s for update", mailfile);
X	if (save_mail_fp)
X	    (void) close_lock(mailfile, save_mail_fp);
X	if (mbox)
X	    (void) close_lock(mbox_file, mbox);
X	turnoff(glob_flags, IGN_SIGS);
X	return 0;
X    }
#endif /* SYSV */
X
X    print("Updating \"%s\"", mailfile);
X
X    turnon(flg, UPDATE_STATUS);
X    /* Don't set OLD for new messages on update. */
X    if (!final)
X	turnon(flg, PRESERVE);
X
X    keepsave = !!do_set(set_options, "keepsave");
X    isspool = !strcmp(mailfile, spoolfile);
X
X    for (i = 0; i < msg_cnt; i++) {
X	/* Maintain the current message across update; if this turns out
X	 * to be unnecessary (changing folders), folder() will reset it.
X	 */
X	if (current_msg == i)
X	    current_msg = held;
X	/* Check to see if message is marked for deletion or, if read and not
X	 * preserved, delete it if autodelete is set.  Otherwise, if hold is
X	 * set save the message in the spool file.  If all fails, save in mbox.
X	 */
X	if (ison(msg[i].m_flags, DELETE)
X	||  ison(msg[i].m_flags, SAVED) && !keepsave &&
X	    isoff(msg[i].m_flags, PRESERVE) && isspool
X	||  isoff(msg[i].m_flags, UNREAD) && isoff(msg[i].m_flags, PRESERVE) 
X		&& delete_it) {
X	    Debug("%s %d",
X		(action!='d')? "\ndeleting message:" : "", i+1), action = 'd';
X	    continue;
X	} else if (isoff(msg[i].m_flags, DO_UPDATE) || hold || !mbox ||
X		ison(msg[i].m_flags, UNREAD) ||
X		ison(msg[i].m_flags, PRESERVE)) {
X	    Debug("%s %d",
X		(action!='s')? "\nsaving in spool:" : "", i+1), action = 's';
X	    if (copy_msg(i, mail_fp, flg, NULL) == -1) {
X		error("WARNING: unable to write back to spool");
X		print_more("ALL mail left in %s\n", tempfile);
X		print_more("Spool mailbox may be corrupted.\n");
X		dont_unlink = TRUE;
X		write_err = TRUE;
X		break;
X	    }
X	    held++;
X	} else if (isspool) {   /* copy back to mbox */
X	    if (copy_msg(i, mbox, flg, NULL) == -1) {
X		error("WARNING: unable to write to mbox");
X		print_more("Unresolved mail left in %s\n", tempfile);
X		dont_unlink = TRUE;
X		write_err = TRUE;
X		break;
X	    }
X	    saved++;
X	    Debug("%s %d",
X		(action!='m')? "\nsaving in mbox:" : "", i+1), action = 'm';
X	}
X    }
X    if (write_err)
X	current_msg = 0;
X    else if (current_msg == held)
X	current_msg--;	/* Don't point to a message that got deleted */
X    Debug("\n%s", mailfile);
X
#ifdef SYSV
X    /* Close the write file pointer first */
X    (void) fclose(mail_fp);
X    mail_fp = save_mail_fp;
#else /* !SYSV */
X    /* Truncate the file at the end of what we just wrote.
X     * If you aren't SYSV and you still can't ftruncate(),
X     * you're out of luck?
X     */
X    (void) ftruncate(fileno(mail_fp), ftell(mail_fp));
#endif /* SYSV */
X
X    /* some users like to have zero length folders for frequent usage */
X    if (mbox && close_lock(mbox_file, mbox) == EOF) {
X	error("WARNING: unable to close mbox");
X	print_more("Unresolved mail left in %s\n", tempfile);
X	dont_unlink = TRUE;
X	write_err = TRUE;
X    }
X    if (held) {
X	print_more(": saved %d message%s\n", held, (held==1)? NO_STRING: "s");
X    } else
#ifdef HOMEMAIL
X    if (!dont_unlink && !do_set(set_options, "save_empty"))
#else /* HOMEMAIL */
X    if (strcmp(mailfile, spoolfile) && !dont_unlink &&
X	!do_set(set_options, "save_empty"))
#endif /* HOMEMAIL */
X	if (unlink(mailfile))
X	    turnon(glob_flags, CONT_PRNT), error(": cannot remove");
X	else {
X	    print_more(": removed\n");
X	    held = -1;
X	}
X    else
X	print_more(": empty\n");
X    if (saved)
X	print("saved %d message%s in %s\n",
X			    saved,(saved==1)? NO_STRING:"s", mbox_file);
X
X    if (held > 0) {
X	/* Reset the access time of the spool file to prevent
X	 * bogus "new mail" messages from the shell.
X	 */
X	long times[2];
X	(void) fflush(mail_fp); /* just in case */
X	times[1] = time(&times[0]) - (long)2;
X	if (!strcmp(mailfile, spoolfile) && utime(mailfile, times))
X	    error("utime");
X    }
X
X    if (close_lock(mailfile, mail_fp) == EOF) {
X	error("WARNING: unable to close spool");
X	print_more("ALL mail left in %s\n", tempfile);
X	print_more("Spool mailbox may be corrupted.\n");
X	write_err = TRUE;
X    }
X
#ifdef SUNTOOL
X    if (istool) {
resume_timer:
X	mail_timer.it_value.tv_sec = time_out;
X	mail_timer.it_interval.tv_sec = time_out;
X	(void) notify_set_itimer_func(tool, do_check,
X	    ITIMER_REAL, &mail_timer, (struct itimerval *) 0);
X    }
#endif /* SUNTOOL */
X
X    turnoff(glob_flags, IGN_SIGS);
X
X    /* Return nonzero for success, -1 if file removed */
X    if (write_err)
X	return 0;
X    else if (held < 0)
X	return -1;
X    else
X	return 1;
}
X
/*
X * check the sizes of the current folder (file) and the spool file.
X * spool_size is the size in bytes of the user's main mailbox.
X * last_size is the size of the _current_ folder the last time we checked.
X * return true if the current folder has new mail.  check_new_mail() checks
X * for new mail in the system mailbox since it checks against last_spool_size.
X */
mail_size()
{
X    struct stat buf;
X
X    if (!stat(spoolfile, &buf))
X	spool_size = buf.st_size;
X    else if (!strcmp(mailfile, spoolfile))
X	return 0;
X    if (!is_shell || ison(glob_flags, IS_SENDING))
X	return 0;
X    if (strcmp(mailfile, spoolfile) && stat(mailfile, &buf)) {
X	if (errno != ENOENT)
X	    error("Unable to stat %s", mailfile);
X	return 0;
X    }
X    if (buf.st_size != last_size) {
X	last_size = buf.st_size;
X	return 1;
X    }
X    return 0;
}
X
static
struct mailstat {
X    int new, unread, deleted;
} mail_stat;
X
void
mail_status(as_prompt)
{
X    char buf[MAXPATHLEN];
X    register int cnt;
X
X    mail_stat.new = mail_stat.unread = mail_stat.deleted = 0;
X
X    for (cnt = 0; cnt < msg_cnt; cnt++) {
X	if (ison(msg[cnt].m_flags, UNREAD))
X	    mail_stat.unread++;
X	if (ison(msg[cnt].m_flags, DELETE))
X	    mail_stat.deleted++;
X	if (isoff(msg[cnt].m_flags, OLD))
X	    mail_stat.new++;
X    }
X    if (as_prompt) {
X	/* use %s in case prompt has any %'s in it */
X	print("%s", format_prompt(current_msg, prompt));
X	return;
X    }
X    (void) sprintf(buf,"\"%s\"%s: %d message%s, %d new, %d unread",
X	trim_filename(mailfile),
X	ison(glob_flags, READ_ONLY)? " [read only]" : "",
X	msg_cnt, (msg_cnt != 1)? "s": NO_STRING,
X	mail_stat.new, mail_stat.unread);
X    if (istool || iscurses)
X	(void) sprintf(buf+strlen(buf), ", %d deleted", mail_stat.deleted);
#ifdef SUNTOOL
X    if (istool) {
X	static char ic_text[4];
X	char *lbl;
X	Icon icon;
X	extern struct pixrect mail_icon_image1, mail_icon_image2;
X	(void) sprintf(ic_text, "%3d", msg_cnt);
X	if (!(lbl = (char *)window_get(tool, FRAME_LABEL)) || strcmp(lbl, buf))
X	    (void) window_set(tool, FRAME_LABEL, buf, NULL);
X	icon = (Icon) window_get(tool, FRAME_ICON);
X	(void) icon_set(icon,
X	    ICON_IMAGE, ison(glob_flags, NEW_MAIL)?
X			    &mail_icon_image2 : &mail_icon_image1,
X	    NULL);
X	if (!chk_option("quiet", "iconlabel"))
X	    (void) icon_set(icon, ICON_LABEL, ic_text, NULL);
X	else
X	    (void) icon_set(icon, ICON_LABEL, NO_STRING, NULL);
X	(void) window_set(tool, FRAME_ICON, icon, NULL);
X    } else
#endif /* SUNTOOL */
X
#ifdef CURSES
X	if (iscurses) {
X	    move (0, 0);
X	    printw("%-3d %-.*s",
X		((msg_cnt)? current_msg+1 : 0), COLS-5, buf), clrtoeol();
X	} else
#endif /* CURSES */
X	    puts(buf);
X    return;
}
X
/*
X * Construct a prompt for the given message number using the given format
X */
char *
format_prompt(num, fmt)
int num;
char *fmt;
{
X    static char buf[MAXPATHLEN];
X    register char *p, *b = buf, *mf;
X
X    if (is_shell)
X	mf = mailfile;
X    else
X	mf = "[no folder]";
X
X    for (p = fmt; *p; p++)
X	if (*p == '\\')
X	    switch (*++p) {
X		case 'n': case 'r': *b++ = '\n';
X		when 't': *b++ = '\t';
X		otherwise: *b++ = *p;
X	    }
X	else if (*p == '%')
X	    switch (*++p) {
X		case 'm':
X		    b += strlen(sprintf(b,"%d",(msg_cnt)? num + 1 : 0));
X		when 't':
X		    b += strlen(sprintf(b, "%d", msg_cnt));
X		when 'd':
X		    b += strlen(sprintf(b, "%d", mail_stat.deleted));
X		when 'u':
X		    b += strlen(sprintf(b, "%d", mail_stat.unread));
X		when 'n':
X		    b += strlen(sprintf(b, "%d", mail_stat.new));
X		when 'f':
X		{
X		    char *tail = rindex(mf, '/'); 
X		    if (tail && tail[1])
X			b += Strcpy(b, tail+1);
X		    else {
X			/* Fall through */
X		case 'F':
X			b += Strcpy(b, mf);
X		    }
X		    if (ison(glob_flags, READ_ONLY))
X			b += Strcpy(b, " [read-only]");
X		}
X		when 'T': case 'D': case 'Y': case 'y':
X		case 'M': case 'N': case 'W':
X		    b += Strcpy(b, Time(p, (long)0));
X		when '$':
X		{
X		    struct expand var;
X		    var.orig = p;
X		    if (varexp(&var)) {
X			b += Strcpy(b, var.exp);
X			xfree(var.exp);
X			p = var.rest - 1;
X		    }
X		}
X		otherwise: *b++ = *p;
X	    }
X	else if (*p == '!')
X	    b += strlen(sprintf(b, "%d", hist_no+1));
X	else
X	    *b++ = *p;
X    *b = 0;
X    return buf;
}
X
/*
X *  For uucp mailers that use >From lines with "remote from <path>":
X * (where "path" is a hostname or pathnames)
X *
X *  a. Set the return_path to the empty string.
X *  b. For each From_ or >From_ line:
X *  c. Save the username (second token).
X *  d. Save the date (3-7 tokens).
X *  e. If it has a "remote from" then append the remote host
X *	(last token) followed by a "!" to the return_path.
X *  f. If the saved username has a '@' but no '!' then convert it
X *	to UUCP path form.
X *  g. Append the saved username to return_path.
X */
parse_from(fp, path)
FILE *fp;
char path[];
{
X    char user[256], buf[256]; /* max size for each line in a mail file */
X    register char *p;
X    long save_offset = ftell(fp);
X
X    path[0] = '\0';
X    while (fgets(buf, sizeof buf, fp)) {
X	if (strncmp(buf, ">From ", 6))
X	    break;
X	p = buf + 6;
X
X	(void) sscanf(p, "%s", user);
X
X	while (p = index(p+1, 'r')) {
X	    if (!strncmp(p, "remote from ", 12)) {
X		char *p2 = path+strlen(path);
X		skipspaces(12);
X		(void) sscanf(p, "%s", p2); /* add the new machine to current path */
X		(void) strcat(p2, "!");
X		break;
X	    }
X	}
X
X	if (p)
X	    (void) bang_form(path + strlen(path), user);
X	save_offset = ftell(fp);
X    }
X    (void) fseek(fp, save_offset, L_SET);
}
X
/*
X * Scan a file and select messages from it and append them to the current folder
X *
X * If "append" is 1, start where we left off (held in msg[cnt].m_offset)
X * and scan for messages.  Append all messages found until EOF.
X *
X * If "append" is 2, we're merging in a new file, so start at the end of
X * the present folder and append all messages found until EOF.
X *
X * If "append" is 0, then the message separator must exist once and
X * only once.  All extra occurrences of the separator is preceded by a '>'.
X * The list argument will be the message number to replace in the current
X * folder with the message read in from other filename.
X */
load_folder(file, append, list)
char *file, *list;
int append;
{
X    char	buf[BUFSIZ];
X    int		lines = 0, msg_found = 0, had_error = 1;
X    int		get_status = 1, cnt;
X    long	bytes, ftell();
X    struct msg  old;
X    char	*p, date[64];
X    FILE	*fp;
X    int         warn = ison(glob_flags, WARNING);
#ifdef MMDF
X    int		begin_sep = 0; /* track beginning vs ending separators */
#endif /* MMDF */
X
X    if (!(fp = lock_fopen(file, "r"))) {
X	error("Unable to open %s", file);
X	return -1;
X    }
X
X    if (append) {
X	cnt = msg_cnt;
X	(void) fseek(fp, append == 1 ? msg[cnt].m_offset : 0L, L_SET);
X    } else {
X	cnt = (int)list;
X	old = msg[cnt];
X    }
X
X    if (isoff(glob_flags, READ_ONLY)) {
X	if (tmpf)
X	    (void) fclose(tmpf);
X	if (!(tmpf = mask_fopen(tempfile, "a"))) {
X	    error("Unable to open %s for appending", tempfile);
X	    close_lock(file, fp);
X	    return -1;
X	}
X	(void) fseek(tmpf, 0L, 2); /* assure we're at the end of the file */
X    } else if (append == 2) {
X	/* you can't merge in a folder to a read-only folder */
X	close_lock(file, fp);
X	return -1;
X    }
X
#ifdef MMDF
X    if (!append) {
X	(void) strcpy(buf, MSG_SEPARATOR);
X	goto do_headers;
X    }
#endif /* MMDF */
X    buf[0] = 0;
X    while (fgets(buf, sizeof (buf), fp)) {
#ifndef MSG_SEPARATOR
X	turnoff(glob_flags, WARNING);
X	if (!strncmp(buf, "From ", 5)) {
X	    p = buf + 5;	/* skip "From " */
X	    skipspaces(0);
X	    p = any(p, " \t");	/* skip the address */
X	} else
X	    p = buf;
X	if (p > buf && (p = parse_date(p + 1)) && strcpy(date, p))
#else /* MSG_SEPARATOR */
X	if (!strncmp(buf, MSG_SEPARATOR, strlen(MSG_SEPARATOR)))
#endif /* MSG_SEPARATOR */
X	{
#ifdef MMDF
X	    if (!append)
X		(void) fputc('>', tmpf);
X	    else if (begin_sep = !begin_sep)
do_headers:
#else /* MMDF */
X	    if (!append && msg_found)
X		(void) fputc('>', tmpf);
X	    else
#endif /* MMDF */
X	    {
X		msg_found++;
X		had_error = 0;
X		if (append && cnt == MAXMSGS-append) {
X		    wprint("WARNING: exceeded %d messages.\n", MAXMSGS-append);
X		    wprint("Not all messages have been loaded.\n");
X		    msg_cnt--;
X		    had_error++;
X		    break;
X		}
X		if (ison(glob_flags, READ_ONLY))
X		    bytes = ftell(fp) - strlen(buf);
X		else {
X		    char path[256];
X		    parse_from(fp, path);
X		    if (path[0])
X			(void)sprintf(buf,"From %s %s", path,
X					    date_to_ctime(date));
X		    bytes = ftell(tmpf);
X		}
X		/* finish up message structure from previous message.
X		 * if this is incorporating new mail, check "lines" to
X		 * see if previous message has already been set!
X		 */
X		if (cnt && lines) {
X		    msg[cnt-1].m_size = bytes - msg[cnt-1].m_offset;
X		    msg[cnt-1].m_lines = lines;
X		}
X		if (isoff(glob_flags, READ_ONLY) && fputs(buf, tmpf) == -1) {
X		    error(tempfile);
X		    had_error++;
X		    break;
X		}
X		msg[cnt].m_offset = bytes;
X		msg[cnt].m_flags = 0L;
#ifdef MSG_SEPARATOR
X		lines = 0;
#else /* MSG_SEPARATOR */
X		lines = 1; /* count the From_ line */
X		if (warn)
X		    turnon(glob_flags, WARNING);
X		strdup(msg[cnt].m_date_recv, date);
#endif /* MSG_SEPARATOR */
X		turnon(msg[cnt].m_flags, UNREAD); /* initialize */
X		/* we've read the "From " line(s), now read the rest of
X		 * the message headers till we get to a blank line.
X		 */
X		while (fgets(buf, sizeof (buf), fp) && (*buf != '\n')) {
X		    p = buf;
X		    if (!strncmp(buf, "Date:", 5))
X			strdup(msg[cnt].m_date_sent, parse_date(p+5));
X		    if (!strncmp(buf, "Priority:", 9)) {
X			for (p += 9 ; *p != '\n'; p++) {
X			    if (!isalpha(*p) || upper(*p) > 'A' + MAX_PRIORITY)
X				continue;
X			    turnon(msg[cnt].m_flags,
X				M_PRIORITY(upper(*p) - 'A' + 1));
X			}
X		    }
X		    if (get_status &&
X			    !(get_status = strncmp(p, "Status:", 7))) {
X			/* new mail should not have a Status: field! */
X			turnon(msg[cnt].m_flags, OLD);
X			for (p += 7 ; *p != '\n'; p++) {
X			    if (isspace(*p))
X				continue;
X			    switch(*p) {
X				case 'R': turnoff(msg[cnt].m_flags, UNREAD);
X				when 'P': turnon(msg[cnt].m_flags, UNREAD);
X				when 'N': turnon(msg[cnt].m_flags, UNREAD);
X					  turnoff(msg[cnt].m_flags, OLD);
X				when 'S': turnon(msg[cnt].m_flags, SAVED);
X				when 'r': turnon(msg[cnt].m_flags, REPLIED);
X				when 'O': ; /* do nothing */
X				when 'f': turnon(msg[cnt].m_flags, FORWARD);
X				when 'p': turnon(msg[cnt].m_flags, PRINTED);
X				otherwise :
X				    if (ison(glob_flags, WARNING))
X					print("unknown msg status flag: %c\n",
X						*p);
X			    }
X			}
X		    }
X		    if (isoff(glob_flags,READ_ONLY) && fputs(buf, tmpf) == -1) {
X			error(tempfile);
X			had_error++;
X			break;
X		    }
X		    lines++;
X		}
X		if (!msg[cnt].m_date_sent || !*msg[cnt].m_date_sent)
X		    if (!msg[cnt].m_date_recv || !*msg[cnt].m_date_recv) {
X			wprint("Message %d has *no* date!?\n", cnt+1);
X			msg[cnt].m_date_sent = msg[cnt].m_date_recv =
X			    "0000000000XXX";
X		    } else
X			strdup(msg[cnt].m_date_sent, msg[cnt].m_date_recv);
X		else if (!msg[cnt].m_date_recv || !*msg[cnt].m_date_recv)
X		    strdup(msg[cnt].m_date_recv, msg[cnt].m_date_sent);
X		if (had_error)
X		    break;
X		if (append && list)
X		    set_msg_bit(list, cnt);
X		if (append)
X		    cnt = ++msg_cnt;
X		get_status = 1;
X	    }
X	} else if (!msg_found && buf[0] != '\n') {
X	    /* Allow leading blank lines, but anything else is wrong */
X	    lines++;
X	    had_error++;
X	    break;
X	}
X	if (msg_found) {
X	    lines++;
X	    if (isoff(glob_flags, READ_ONLY) && fputs(buf, tmpf) == -1) {
X		error(tempfile);
X		had_error++;
X		break;
X	    }
X	}
X    }
#ifndef MSG_SEPARATOR
X    if (warn)
X	turnon(glob_flags, WARNING);
#endif /* !MSG_SEPARATOR */
X    if (msg_found && append != 1)
X	turnon(glob_flags, DO_UPDATE);
#ifdef MMDF
X    if (!append)
X	(void) fputs(END_MSG_SEP, tmpf);
#endif /* MMDF */
X    if (had_error) {
X	if (!append)
X	    msg[cnt] = old;
X	else if (msg_found && append == 1 && cnt == MAXMSGS-append) {
X	    /* reset fp to the beginning of the not-loaded message */
X	    bytes = ftell(fp) - strlen(buf);
X	    (void) fseek(fp, bytes, L_SET);
X	}
X	if (!msg_found) {
X	    if (!append)
X		print("File not left in correct message format.\n");
X	    else if (cnt == 0) {
X		if (buf[0]) 
X		    print("\"%s\" does not seem to be a folder\n", file);
X		else
X		    had_error = 0;	/* empty files are OK */
X	    }
X	}
X    } else {
X	if (append)
X	    cnt--;
X	if (isoff(glob_flags, READ_ONLY))
X	    msg[cnt].m_size = ftell(tmpf) - msg[cnt].m_offset;
X	else
X	    msg[cnt].m_size = ftell(fp) - msg[cnt].m_offset;
X	msg[cnt].m_lines = lines;
X	/* remember where we were to seek to for when we append new mail */ 
X	if (append)
X	    cnt++;
X    }
X    if (append == 1) /* merge_folders takes care of this for append == 2 */
X	msg[cnt].m_offset = ftell(fp);
X    close_lock(file, fp);
X    if (isoff(glob_flags, READ_ONLY)) {
X	if (had_error && msg_found && append == 1 && cnt == MAXMSGS-append) {
X	    wprint("Using read-only mode.\n");
X	    turnon(glob_flags, READ_ONLY);
X	    had_error = 0;	/* return successfully anyway */
X	}
X	(void) fclose(tmpf);
X	if (!(tmpf = fopen(tempfile, "r"))) {
X	    error("Unable to open %s for reading", tempfile);
X	    return -1;
X	}
X    }
X    return !had_error;
}
SHAR_EOF
chmod 0644 msgs.c ||
echo 'restore of msgs.c failed'
Wc_c="`wc -c < 'msgs.c'`"
test 28895 -eq "$Wc_c" ||
	echo 'msgs.c: original size 28895, current size' "$Wc_c"
rm -f _shar_wnt_.tmp
fi
# ============= mush.1 ==============
if test -f 'mush.1' -a X"$1" != X"-c"; then
	echo 'x - skipping mush.1 (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting mush.1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'mush.1' &&
.\" Mush Man Page: Copyright (c) 1987, 1989, 1990 Dan Heller
.\" Cleaned up January 1988 by Bart Schaefer <schaefer@cse.ogc.edu>
.\" Patched again December 1989 by Bart Schaefer <schaefer@cse.ogi.edu>
.\" 1990 updates by Bart Schaefer and Bill Randle <billr@saab.cna.tek.com>
.\"
.if n .ds Q \&"
.if n .ds U \&"
.if t .ds Q \&``
.if t .ds U \&''
.if n .ds - --
.if t .ds - \(em
.nh
.TH MUSH 1 "March 17, 1991" "Version 7.2.2"
.SH NAME
The Mail User's Shell \- Shell for electronic mail.
.SH SYNOPSIS
.B mush
[
.B \-n
]
[
.B \-v
]
[
.B \-s
subject
]
[
.B \-c
cc-list
]
[
.B \-b
bcc-list
]
[
address-list
]
.br
.B mush
[
.B \-n
]
[
.B \-v
]
[
.BR \-U [ ! ]
]
.B \-h
draft-file
.br
.B mush
[
mode-options
]
[
file-options
]
.SH INTRODUCTION
The Mail User's Shell (Mush) is an interface for sending and manipulating
a database of electronic mail messages under the
.IR UNIX (TM)
environment.
There are three user interfaces that allow the user to interact with
.I Mush.
The default interface is the conventional tty-based line mode
similar to command line interpreters such as
.I csh
as well as other mailers, such as University of California, Berkeley's
.I Mail
and Bell Lab's System V
.I mailx
interface.
This mode requires nothing from the terminal in terms of screen
capability and may be run on many different versions of the
.IR UNIX (TM)
operating system.
.PP
The text-graphics
.RI ( curses )
interface is reminiscent of the
.I vi
visual editor, but is user-configurable to simulate other editors.
This interface does not require graphics capabilities of
the computer or the terminal on which it is run, but the terminal must
have the minimum capabilities required by any visual screen editor.
.PP
The
.I window
interface for the Sun Workstation utilizes the icon and
menu based (mouse selectable) windowing system.
This
.I tool
(graphics) mode is highly subject to the version of operating system
your Sun may be running.
It is intended to be run on Sun versions 3.5 and higher (those that have the
SunView window system).
.PP
See the corresponding sections for more information on the user
interface desired.
Most of this manual deals with commands, variables
and actions that are common to all three interfaces although
some attention is paid to individual characteristics of each interface.
.PP
The following command line arguments are understood by
.I Mush
(full word forms in parentheses):
.TP
\-b bcc-list
(\-blindcarbon, \-blind)
The list of Blind Carbon Copy recipients is set on the command line.
If more than one address or an address containing spaces is specified, the
entire list should be enclosed in quotes.
This option applies when sending mail only.
If you are entering the shell, curses mode, or the tool mode, this option is
ignored.
.TP
\-C
(\-curses)
Enter the mailer in curses mode upon startup.
.TP
\-c cc-list
(\-carbon, \-copy)
The list of Carbon Copy recipients is set on the command line.
If more than one address or an address containing spaces is specified, the
entire list should be enclosed in quotes.
This option applies when sending mail only.
If you are entering the shell, curses mode, or the tool mode, this option is
ignored.
.TP
\-d
(\-debug)
Turns on the debugging level to 1.
You can change debugging levels from within the shell using the
.B debug
command.
.TP
\-e
(\-echo)
Normally, the program runs with the local echo off and each character
typed is processed individually so as to process certain macros and
keyboard mappings.
This option suppresses this from taking place
and the program only processes input after a carriage return has
been hit.
Under normal circumstances, this action is transparent to
the user and the use of this option is discouraged except when using
a debugger with the program.
Note that if this option is specified,
any key sequences set by map or map! do not substitute their expansions.
This option is ignored for curses mode.
.TP
\-F[!] filename
(\-source)
This file is the same type as the initialization file read on startup
(see INITIALIZATION) with the exception that commands that manipulate
or search messages may be given.
Normally, such commands may not appear in the initialization file since
that file is read before the folder is scanned.
The file specified by \-F is read after the folder is scanned, so
commands that affect messages are allowed.
The optional `!' argument prevents the shell from running after the
file has been sourced.
Otherwise,
.I Mush
continues into whatever interface has been specified.
.TP
\-f [ filename ]
(\-folder)
The optional filename argument specifies a folder containing mail messages.
With no argument,
.B mbox
in the current directory (or the variable
.BR mbox )
is used.
If no filename is given, this option must be last on the command line.
.TP
\-H[:c]
(\-headers)
Have
.I Mush
display mail headers without entering the shell.
See the
.B headers
command for information on the
.B :c
argument.
No colon modifier is equivalent to \*Q\-H:a\*U.
This option prevents the shell from running, so this option turns off the
\-S and \-C flags.
This option is ignored if the tool mode is in effect.
.TP
\-h draft-file
(-draft)
This option specifies a previously prepared message file (called a draft)
which is read in as a new message to be sent.
The current implementation requires that the draft file must contain all the
message headers;
.I Mush
adds only a new \*QDate:\*U and a \*QFrom:\*U header if there is none.
If there is no \*QTo:\*U header, the draft is not sent.
See the
.B mail
command and the section on \*QSending mail\*U for more information.
.TP
\-I[!] filename
(\-init)
This option specifies an initialization file to be read
.I before
any of the other
.I Mush
initialization is done.
The file specified by \-I is read before the default system initialization
file is read (see the INITIALIZATION section for details).
The optional `!' argument prevents
.I Mush
from reading the default system file, so \-I! can be used to specify a
substitute default file.
The user's personal initialization file is read normally.
.TP
\-i
(\-interact)
Forces interactive mode even if input has been redirected to the program.
This is intended for remote host mail sessions (with -e) but also allows
the user to redirect input from a \*Qscript\*U of
.I Mush
commands.
See the INITIALIZATION and MUSH SCRIPTS sections for information on how to
write scripts that deal with mail.
Note that this flag is different from the \*Qignore\*U flag of UCB Mail.
.TP
\-m mailbox-path
(\-mailbox)
The mailbox specified is interpreted as if it is the user's main
(system) mailbox in place of /usr/spool/mail/$USER (or whatever path is
applicable for your system and Mail Transport Agent).
.TP
\-N
(\-noheaders)
Enter
.I Mush
without displaying any message headers.
This argument is passed to the
.B folder
command.
.TP
\-n[!]
(\-noinit)
No initialization is done on start up.
That is, do not source the default system initialization files.
If the `!' argument is given, reading of the user's personal
.I .mushrc
or
.I .mailrc
files is also suppressed.
See the INITIALIZATION section for more information on
startup and the significance of these files.
.TP
\-r
(\-readonly)
Initialize the folder in Read-Only mode; no modification of the folder is
permitted.
This argument is passed on to the
.B folder
command.
.TP
\-S
(\-shell)
This flag allows the user to enter the shell even if the system
mailbox or specified folder is empty or doesn't exist.
.TP
\-s subject
(\-subject)
The subject is set on the command line using this flag.
If the subject has
any spaces or tabs, the entire subject should be enclosed in quotes.
This applies when sending mail only.
If you are entering the shell,
curses mode, or the tool mode, this option is ignored.
.TP
\-T timeout
(\-timeout)
In the tool mode (Sun only),
.I timeout
specifies the length of time (seconds) to wait between each check for new mail.
30 seconds is the smallest time allowed for performance reasons;
60 seconds is the default value.
This option should be used either in place of \-t or immediately after it.
.TP
\-t
(\-tool)
Use the graphics tool mode (Sun only).
This option must be the first one on the command line, before any Sun window
system flags or other \fIMush\fR options.
.sp
.I
NOTE:  The \-t option is obsolete and may be eliminated in future revisions.
The preferred way to run the tool mode of \fIMush\fR is to use the command
.BR mushtool ,
which is a link to
.BR mush .
.TP
\-u [ user ]
(\-user)
The mailbox to use is /usr/spool/mail/\fBuser\fR.
If the login name for user is not specified, then root is used.
.TP
\-U[!]
(-send)
This option may be used only with \-h (\-draft).
It causes the draft file to be sent immediately without further editing
(\*Qunedited\*U, hence \-U).
If the optional `!' is appended, signatures and fortunes are suppressed.
See the
.B mail
command and the section on \*QSending mail\*U for more information.
.TP
\-v
(\-verbose)
Verbose mode is turned on.
This option is passed to the actual mail delivery
subsystem internal to your version of
.IR UNIX (TM).
Some mailers do not have a verbose option, so this flag may not apply
to your system (System V, for example).
This applies when sending mail only.
If you are entering the shell,
curses mode, or the tool mode, this option is ignored.
.SH "GENERAL USAGE"
Because there are three different interfaces available to the user,
the tty characteristics (backspace, kill-word, kill-line, redraw line)
are simulated identically in all interfaces.
When the user has to type something, the 4.2BSD style of tty driver interface
is simulated whether you're in the window system, the curses mode, or
the tty-line mode, and even on System-V machines.
This means that backspacing causes a
backspace-space-backspace effect (erasing the character backspaced over).
The user may reset his tty characteristics using the
.B stty
command.
.PP
.IR "New mail" .
.PP
If during a
.I Mush
session, new mail arrives for you, it is automatically incorporated into
your system mailbox and you are told that new mail has arrived.
.PP
In the default line mode, new mail is checked between each command
issued.
In the curses mode, new mail is checked on each
command and is displayed in the bottom line of the screen.
In the tool based graphics mode, new mail is checked approximately
every minute or the number of seconds specified by the
.B -T
option on the command line.
.PP
If you are using your system mailbox as your \*Qcurrent folder,\*U then the
new mail is added immediately to your current
list of messages and information similar to the following example is
displayed, to tell you whom the mail is from:
.sp
.ti +2
New mail: (#15) argv@zipcode.com (Dan Heller)
.sp
If you are not in your system mailbox, then the new mail is not added
to your list of messages, but you are instead informed of the new arrival.
.sp
If you are using the tool based mode and
.I Mush
is closed to an iconic state, then the number of messages in the current
folder is displayed on the mailbox icon and the flag on the mailbox goes up.
.PP
.IR "Displaying messages" .
.PP
Depending on the interface you use, you can display any message in your
list of messages as long as the message is not marked for deletion.
If the message is marked as deleted, then use the 
.B undelete
command supplied by the interface you are using.
To display a message in line mode, specify the message using
.BR print ,
.BR type ,
.BR p ,
.BR t ,
or type a message number to display that message on the screen.
.PP
In curses mode, move the cursor over the message you want and type
a `t' or `p' to read the message.
You may \*Qbind\*U other keys to call
the function that displays messages if `t' and `p' are uncomfortable.
.PP
In the tool mode, move the cursor over the header summary of the
message you wish to be displayed and select the LEFT mouse button.
The MIDDLE mouse button deletes the message, and the RIGHT button
brings up a menu of additional options, including help.
If the message you want is not visible (in the header subwindow), you may type
the number of the message in the \*QRange:\*U item, and press return.
That message number is displayed.
Finally, the \*QNext\*U item in the panel below the header display
can be used to step through the folder, one message at a time.
.PP
In the line or curses mode, if the message has more lines than the variable
.BR crt ,
then a
.I pager
is invoked to allow the user to page through the message without
having it scroll off the screen.
The pager used is determined by the variable
.BR pager .
If that variable is unset, then a default pager is used.
Note that if pager is set, but not to a value, or is set to the value
of \*Qinternal\*U, then the internal pager is used.
The internal pager
is very simple; the spacebar displays the next
.B crt
lines, carriage return prints the next line, and \*Qq\*U quits the pager.
.PP
In the tool mode, if a message is larger than the size of the message
subwindow, the scrollbar at the left side of the window may be used to
page the message forwards and backwards.
The variable
.B crt_win
may be set in an initialization file to preset the size of the
message display subwindow.
.PP
An alternative to displaying messages is the
.B top
command.
This command prints just the top few lines of a message.
The number of lines is determined by the variable
.BR toplines .
If this variable isn't set,
.B top
prints a number of lines equal to the value of the variable
.BR crt .
.PP
.IR "Sorting mail" .
.PP
.I Mush
allows you to sort your mail according to various constraints such
as time, size, status (new, unread, deleted, etc.), author and subject.
See the
.B sort
command in the COMMANDS section for more information on sorting.
Sorting has a panel item in the tool mode, and is bound by default
to the `o' (sort) and `O' (sort reverse) keys in curses mode.
.PP
.IR "Picking specific messages" .
.PP
You can select messages that contain unique information, or from
messages that have special attributes.
You have the option of restricting your search to messages between dates,
message numbers, author names and other constraints.
See the
.B pick
command in the COMMANDS section for complete details.
This feature is not directly accessible from the tool mode, and is
available only as a search action in curses mode (see, however, the
CURSES INTERFACE section for temporary escapes to line mode).
.PP
.IR "Sending mail" .
.PP
You can send mail by listing addresses on the command line when
.I Mush
is started, by using the
.B mail
command from within
.IR Mush ,
or by responding to other mail.
In curses mode, the `m' key invokes mail, and the `r' key begins a response.
In the tool mode, selecting the \*QCompose\*U or \*QReply\*U items on the main
panel opens a separate frame for message composition.
The message replied-to is either the current message or one specified in
the \*QRange:\*U item.
.PP
When you are sending mail, you are in a mode where everything
you type is added to the contents of the message.
When you are done typing your message in line or curses modes,
you can type `^D' (control-D) to signify the end of the message.
If you have the variable
.B dot
set, then you can end a message with a `.' on a line by itself.
In the tool mode, select the \*QSend\*U item in the composition frame
to finish and send the message.
.PP
While you are composing a message,
.I Mush
treats lines beginning with the character `~' specially.
This is called a
.BR "tilde escape" .
For instance, typing \*Q~i\*U (alone on a line) places a copy
of the \*Qcurrent message\*U into your message body.
It does not include the message headers of the message, just the body of
text that comprises the message.
A subset of these escapes are available in the tool mode, and others are
provided as panel items or as menu selections from the \*QInclude\*U item.
Tilde escapes which alter message headers are not usable when the variable
.B edit_hdrs
is set or when the \-E option was passed to the
.B mail
command.
.PP
The tool mode composition window uses header editing at all times, but
provides some of these escapes anyway; see the descriptions below, and the
description of the
.B edit_hdrs
variable, for complete details.
.PP
Available
.BR "tilde escapes" :
[OPTIONAL arguments in square brackets]
.TP
~a file
Append message buffer to file name.
Accessed via the \*QExport\*U panel item in tool mode.
.TP
~b [bcc-list]
Modify blind carbon recipients; otherwise identical to ~t.
In tool mode, moves the cursor to the Bcc: header, adding one if necessary.
.TP
~c [cc-list]
Modify carbon copy recipients; otherwise identical to ~t.
In tool mode, moves the cursor to the Cc: header, adding one if necessary.
.TP
~E[!]
Erase message buffer; not available in tool mode.
SHAR_EOF
true || echo 'restore of mush.1 failed'
fi
echo 'End of  part 14'
echo 'File mush.1 is continued in part 15'
echo 15 > _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.