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(×[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.