home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume12
/
mush
/
part13
< prev
next >
Wrap
Text File
|
1990-05-05
|
56KB
|
1,907 lines
Newsgroups: comp.sources.misc
subject: v12i041: Mail User's Shell, Part13/19
from: argv@Eng.Sun.COM (Dan Heller)
Sender: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
Posting-number: Volume 12, Issue 41
Submitted-by: argv@Eng.Sun.COM (Dan Heller)
Archive-name: mush/part13
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
# "End of archive 13 (of 19)."
# Contents: mush/hdrs.c mush/setopts.c mush/tooledit.c
# Wrapped by argv@turnpike on Wed May 2 13:59:40 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'mush/hdrs.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'mush/hdrs.c'\"
else
echo shar: Extracting \"'mush/hdrs.c'\" \(21753 characters\)
sed "s/^X//" >'mush/hdrs.c' <<'END_OF_FILE'
X/* hdrs.c (c) copyright 1986 (Dan Heller) */
X
X/*
X * Routines that deal with message headers inside messages
X * msg_get(n, from, count) -- get the From_ line in msg n into "from".
X * header_field(n, str) -- get the header named "str" from msg n.
X * do_hdrs(argc, argv, list) -- diplay message headers.
X * specl_hdrs(argv, list) -- display msgs that share common attributes.
X * compose_hdr(cnt) -- compose a message header from msg n.
X * reply_to(n, all, buf) -- construct a header based on the To: header of n.
X * subject_to(n, buf) -- get the subject for replying to msg n.
X * cc_to(n, buf) -- construct a Cc header based on the Cc of message n.
X */
X#include "mush.h"
X
X#ifdef SUNTOOL
X#define highlight(win,x,y,s) \
X (void) (pw_text(win,x,y, PIX_SRC, mush_font, s), \
X pw_text(win,x+1,y, \
X (ison(glob_flags, REV_VIDEO))? PIX_NOT(PIX_SRC): PIX_SRC|PIX_DST, \
X mush_font, s))
X#endif /* SUNTOOL */
X
X/*
X * Get a message from the current folder by its offset.
X * Copy the From_ line to the second argument if the third arg > 0,
X * and return the second argument, or NULL on an error.
X */
Xchar *
Xmsg_get(n, from, count)
Xint n, count;
Xchar *from;
X{
X if (fseek(tmpf, msg[n].m_offset, L_SET) == -1) {
X error("fseek in %s (msg %d, folder=%s)", tempfile, n+1, mailfile);
X turnon(glob_flags, READ_ONLY);
X return NULL;
X }
X if (count)
X#ifndef MSG_SEPARATOR
X return fgets(from, count, tmpf);
X#else
X *from = '\0';
X#endif
X return from;
X}
X
X/*
X * get which message via the offset and search for the headers which
X * match the string "str". there may be more than one of a field (like Cc:)
X * so get them all and "cat" them together into the static buffer
X * "buf" and return its address.
X */
Xchar *
Xheader_field(n, str)
Xchar *str;
X{
X static char buf[HDRSIZ];
X char tmp[HDRSIZ];
X register char *p, *p2, *b = buf;
X int contd_hdr; /* true if next line is a continuation of the hdr we want */
X
X /* use msg_get as a test for fseek() -- don't let it fgets() (pass 0) */
X if (!msg_get(n, tmp, 0))
X return NULL;
X *b = 0;
X while((p = fgets(tmp, sizeof(tmp), tmpf)) && *p != '\n') {
X if (*p != ' ' && *p != '\t') {
X contd_hdr = 0;
X /* strcmp ignoring case */
X for(p2 = str; *p && *p2 && lower(*p2) == lower(*p); ++p, ++p2);
X /* MATCH is true if p2 is at the end of str and *p is ':' */
X if (*p2 || *p++ != ':')
X continue;
X else
X contd_hdr = 1;
X if (b > buf && (b - buf) < sizeof buf - 2)
X *b++ = ',';
X } else if (!contd_hdr)
X continue;
X skipspaces(0);
X (void) no_newln(p);
X if (strlen(p) + (b - buf) < sizeof buf - 1) {
X if (b > buf)
X *b++ = ' ';
X b += Strcpy(b, p);
X }
X }
X if (*--b == ',')
X *b = 0;
X return (*buf)? buf: NULL;
X}
X
Xdo_hdrs(argc, argv, list)
Xregister char **argv, list[];
X{
X register int pageful = 0;
X SIGRET (*oldint)(), (*oldquit)();
X int show_deleted, srch = 1; /* search forward by default */
X static int cnt, oldscrn = 1;
X register char *p;
X char first_char = (argc) ? **argv: 'h';
X
X if (argc > 1 && !strcmp(argv[1], "-?"))
X return help(0, "headers", cmd_help);
X
X if (!msg_cnt) {
X if (ison(glob_flags, DO_PIPE))
X return 0;
X#ifdef CURSES
X if (iscurses)
X clear();
X#endif /* CURSES */
X#ifdef SUNTOOL
X if (istool)
X mail_status(0);
X#endif /* SUNTOOL */
X return 0;
X }
X if (first_char == ':' || (argc > 1 && argv[1][0] == ':')) {
X if (first_char != ':')
X argv++;
X return specl_hdrs(argv, list);
X } else if (argc > 1 && !strncmp(argv[1], "-H:", 3)) {
X argv[1][0] = ':';
X argv[1][1] = argv[1][3];
X argv[1][2] = 0;
X return specl_hdrs(&argv[1], list);
X }
X
X on_intr();
X
X if (argc && (argv[0][1] == '-' || argc > 1 && !strcmp(argv[1], "-"))) {
X cnt = max(n_array[0], 0);
X srch = -1; /* search backwards */
X } else if (argc && (argv[0][1] == '+' ||
X argc > 1 && !strcmp(argv[1], "+")) ||
X first_char == 'z' && !argv[1]) {
X if (msg_cnt > screen)
X cnt = min(msg_cnt - screen, n_array[0] + screen);
X else
X cnt = 0;
X } else if (argc && *++argv &&
X (isdigit(**argv) || **argv == '^' ||
X **argv == '$' || **argv == '.') ||
X ison(glob_flags, IS_PIPE)) {
X /* if we're coming from a pipe, start display at the first msg bit
X * set in the msg_list
X */
X int fnd;
X if (ison(glob_flags, IS_PIPE)) {
X if (isoff(glob_flags, DO_PIPE))
X for (fnd = 0; fnd < msg_cnt; fnd++)
X if (msg_bit(list, fnd))
X wprint("%s\n", compose_hdr(fnd));
X off_intr();
X return 0;
X }
X /* if a number was given, use it */
X if (!(fnd = chk_msg(*argv))) {
X off_intr();
X return -1;
X }
X for (cnt = fnd - 1; cnt > 0 && cnt + screen > msg_cnt; cnt--)
X ;
X } else if (current_msg < n_array[0] || current_msg > n_array[oldscrn-1] ||
X (iscurses || oldscrn != screen) &&
X (cnt > current_msg + screen || cnt < current_msg - screen))
X cnt = current_msg; /* adjust if reads have passed screen bounds */
X else if (cnt >= msg_cnt || !argc || !*argv)
X /* adjust window to maintain position */
X cnt = (n_array[0] > msg_cnt) ? current_msg : n_array[0];
X
X oldscrn = screen;
X show_deleted = !!do_set(set_options, "show_deleted");
X
X /* Make sure we have at least $screen headers to print */
X if (cnt > 0 && !iscurses && first_char == 'h') {
X int top, bot = cnt;
X /* first count how many messages we can print without adjusting */
X for (pageful = 0; pageful<screen && bot<msg_cnt && bot; bot += srch)
X if (show_deleted || isoff(msg[bot].m_flags, DELETE))
X pageful++;
X /* if we can't print a pagefull of hdrs, back up till we can */
X for (top = cnt-srch; pageful<screen && top && top<msg_cnt; top -= srch)
X if (show_deleted || isoff(msg[top].m_flags, DELETE))
X pageful++;
X if (srch < 0)
X cnt = bot; /* the search was upside down */
X else
X cnt = top + (pageful == screen);
X pageful = 0; /* Used later as an index, so reset */
X } else if (cnt > 0 && srch < 0)
X cnt = max(cnt - screen, 0);
X else
X cnt = max(cnt, 0);
X
X for (;pageful<screen && cnt<msg_cnt && isoff(glob_flags, WAS_INTR); cnt++) {
X if (!iscurses && !show_deleted && first_char == 'h'
X && ison(msg[cnt].m_flags, DELETE))
X continue;
X n_array[pageful++] = cnt;
X /* this message was displayed -- set the bit */
X if (list)
X set_msg_bit(list, cnt);
X /* if do_pipe, don't output anything */
X if (ison(glob_flags, DO_PIPE))
X continue;
X p = compose_hdr(cnt);
X if (!istool && (!iscurses || ison(glob_flags, IS_GETTING)))
X puts(p);
X#ifdef SUNTOOL
X else if (istool) {
X if (cnt == current_msg) /* embolden or reverse-video */
X highlight(hdr_win, 0,pageful*l_height(), p);
X else
X (void) pw_text(hdr_win, 0, pageful * l_height(), PIX_SRC,
X mush_font, p);
X Clrtoeol(hdr_win, strlen(p)*l_width(), pageful*l_height());
X }
X#endif /* SUNTOOL */
X
X#ifdef CURSES
X else if (iscurses) {
X move(pageful, 0);
X printw("%-.*s", COLS-2, p), clrtoeol();
X }
X#endif /* CURSES */
X }
X /* just in case a signal stopped us */
X off_intr();
X pageful++;
X#ifdef CURSES
X if (iscurses && pageful < screen)
X move(pageful, 0), clrtobot();
X#endif /* CURSES */
X if (cnt == msg_cnt) {
X while (pageful <= screen) {
X n_array[pageful-1] = msg_cnt+1; /* assign out-of-range values */
X#ifdef SUNTOOL
X if (istool)
X Clrtoeol(hdr_win, 0, pageful * l_height());
X#endif /* SUNTOOL */
X ++pageful;
X }
X }
X#ifdef SUNTOOL
X if (istool) {
X Scrollbar sb = (Scrollbar) window_get(hdr_sw, WIN_VERTICAL_SCROLLBAR);
X
X if (show_deleted) {
X scrollbar_set(sb,
X SCROLL_OBJECT_LENGTH, msg_cnt,
X SCROLL_VIEW_START, n_array[0],
X 0);
X } else {
X int i, not_deleted, start;
X
X for (i = start = 0; i < n_array[0]; i++)
X if (!ison(msg[i].m_flags, DELETE))
X start++;
X for (not_deleted = start; i < msg_cnt; i++)
X if (!ison(msg[i].m_flags, DELETE))
X not_deleted++;
X scrollbar_set(sb,
X SCROLL_OBJECT_LENGTH, not_deleted,
X SCROLL_VIEW_START, start,
X 0);
X }
X
X scrollbar_paint(sb);
X mail_status(0);
X }
X#endif /* SUNTOOL */
X
X return 0;
X}
X
X#define NEW 1
X#define ALL 2
X
Xspecl_hdrs(argv, list)
Xchar **argv, list[];
X{
X u_long special = 0;
X int n = 0;
X
X while (argv[0][++n])
X switch(argv[0][n]) {
X case 'a': special = ALL;
X when 'n': special = NEW;
X when 'u': special = UNREAD;
X when 'o': special = OLD;
X when 'd': special = DELETE;
X when 'r': special = REPLIED;
X when 's': special = SAVED;
X when 'p': special = PRESERVE;
X otherwise: print("choose from n,u,o,d,r,s,p or a"); return -1;
X }
X if (debug)
X (void) check_flags(special);
X
X for (n = 0; n < msg_cnt; n++) {
X /*
X * First, see if we're looking for NEW messages.
X * If so, then check to see if the msg is unread and not old.
X * If special > ALL, then special has a mask of bits describing
X * the state of the message.
X */
X if (ison(glob_flags, IS_PIPE)&& !msg_bit(list, n))
X continue;
X if (special == ALL || special == NEW &&
X (ison(msg[n].m_flags, UNREAD) && isoff(msg[n].m_flags, OLD))) {
X if (isoff(glob_flags, DO_PIPE))
X print("%s\n", compose_hdr(n));
X if (list)
X set_msg_bit(list, n);
X } else if (special > ALL && ison(msg[n].m_flags, special)) {
X if (isoff(glob_flags, DO_PIPE))
X print("%s\n", compose_hdr(n));
X if (list)
X set_msg_bit(list, n);
X } else {
X if (list)
X unset_msg_bit(list, n);
X if (debug) {
X (void) printf("msg[%d].m_flags: %d", n, msg[n].m_flags);
X (void) check_flags(msg[n].m_flags);
X }
X }
X }
X return 0;
X}
X
X#define Strncpy(buf,p) (void)(strncpy(buf,p,sizeof(buf)),buf[sizeof(buf)-1]=0)
X
X/*
X * format a header from the information about a message (from, to, date,
X * subject, etc..). The header for message number "cnt" is built and is
X * returned in the static buffer "buf". There will be *at least* 9 chars
X * in the buffer which will be something like: " 123 >N " The breakdown
X * is as follows: 4 chars for the message number, 1 space, 1 char for '>'
X * (if current message) and two spaces for message status (new, unread, etc)
X * followed by 1 terminating space.
X * Read other comments in the routine for more info.
X */
Xchar *
Xformat_hdr(cnt, hdr_fmt, show_to)
Xint cnt, show_to;
Xchar *hdr_fmt;
X{
X static char buf[256];
X register char *p, *p2, *b;
X int len, do_pad = FALSE, val, pad, got_dot, isauthor = 0, n;
X char from[HDRSIZ], subject[256], date[64], lines[16];
X char to[256], addr[256], name[256], status[4];
X char Day[3], Mon[4], Tm[8], Yr[5], Wkday[4], Zone[8], *date_p;
X
X /* status of the message */
X if (ison(msg[cnt].m_flags, DELETE))
X status[0] = '*';
X else if (ison(msg[cnt].m_flags, PRESERVE))
X status[0] = 'P';
X else if (ison(msg[cnt].m_flags, SAVED))
X status[0] = 'S';
X else if (ison(msg[cnt].m_flags, OLD) && ison(msg[cnt].m_flags, UNREAD))
X status[0] = 'U';
X else if (ison(msg[cnt].m_flags, PRINTED))
X status[0] = 'p';
X else if (ison(msg[cnt].m_flags, FORWARD))
X status[0] = 'f';
X else if (isoff(msg[cnt].m_flags, UNREAD))
X status[0] = ' ';
X else
X status[0] = 'N';
X
X if (ison(msg[cnt].m_flags, REPLIED))
X status[1] = 'r';
X else
X status[1] = ' ';
X
X to[0] = from[0] = subject[0] = date[0] = lines[0] = addr[0] =
X name[0] = Day[0] = Mon[0] = Tm[0] = Yr[0] = Wkday[0] = 0;
X
X /* who's the message to */
X if ((p = header_field(cnt, "resent-to")) ||
X (p = header_field(cnt, "to")) ||
X (p = header_field(cnt, "apparently-to")))
X Strncpy(to, p);
X
X /* who's the message from */
X if ((p = header_field(cnt, "from")) && strcpy(from, p)
X || (p = reply_to(cnt, 0, from))) {
X /* NOTE: this fails if the sender has '<' or '!' in
X * the RFC822 comment fields -- leading "comment"
X * or trailing (comment) -- but that isn't critical
X */
X if ((p2 = rindex(p, '!')) || (p2 = index(p, '<')))
X p = p2 + 1;
X } else
X p = strcpy(from, "unknown"); /* just in case */
X /* If the From field contains the user's login name, then the message
X * could be from the user -- attempt to give more useful information
X * by telling to whom the message was sent. This is not possible if
X * the "to" header failed to get info (which is probably impossible).
X * Use take_me_off() to be sure the message really is from the current
X * user and not just someone with the same login at another site.
X */
X if (show_to && !strncmp(p, login, strlen(login)))
X (void) take_me_off(from);
X if (show_to && (isauthor = !*from)) { /* assign and test */
X (void) get_name_n_addr(to, name+4, addr+4);
X if (addr[4])
X (void) strncpy(addr, "TO: ", 4);
X if (name[4]) { /* check to see if a name got added */
X (void) strncpy(name, "TO: ", 4);
X Strncpy(from, name);
X } else
X Strncpy(from, addr);
X } else
X (void) get_name_n_addr(from, name, addr);
X
X if (ison(glob_flags, DATE_RECV))
X date_p = msg[cnt].m_date_recv;
X else
X date_p = msg[cnt].m_date_sent;
X (void) date_to_string(date_p, Yr, Mon, Day, Wkday, Tm, Zone, date);
X
X /* and the subject */
X if (p = header_field(cnt, "subject"))
X Strncpy(subject, p);
X
X /* now, construct a header out of a format string */
X if (!hdr_fmt)
X hdr_fmt = hdr_format;
X
X (void) sprintf(buf, "%4.d ", cnt+1);
X b = buf+5;
X *b++ = ((cnt == current_msg && !iscurses)? '>': ' ');
X *b++ = status[0], *b++ = status[1];
X *b++ = ' ';
X /* Count chars since beginning of buf. Initialize to 9 (strlen(buf) so far)
X * This magic number is used in other places in msgs.c and mail.c
X */
X n = 9;
X for (p = hdr_fmt; *p; p++)
X if (*p == '\\')
X switch (*++p) {
X case 't':
X while (n % 8)
X n++, *b++ = ' ';
X when 'n':
X n = 1, *b++ = '\n';
X otherwise: n++, *b++ = *p;
X }
X else if (*p == '%') {
X char fmt[64];
X
X p2 = fmt;
X /* first check for string padding: %5n, %.4a, %10.5f, %-.3l etc. */
X do_pad = pad = val = got_dot = 0;
X *p2++ = '%';
X if (p[1] != '-')
X *p2++ = '-';
X else
X ++p;
X while (isdigit(*++p) || !got_dot && *p == '.') {
X if (*p == '.')
X got_dot = TRUE, val = pad, pad = 0;
X else
X pad = pad * 10 + *p - '0';
X *p2++ = *p;
X }
X if (!got_dot && isdigit(p[-1])) {
X *p2 = 0; /* assure null termination */
X val = atoi(fmt+1);
X if (val < 0)
X val = -val;
X p2 += strlen(sprintf(p2, ".%d", val));
X }
X pad = min(pad, val);
X *p2++ = 's', *p2 = 0;
X if (!*p)
X break;
X switch (*p) {
X case 'f': p2 = from, do_pad = TRUE;
X when 'a':
X if (!*(p2 = addr))
X p2 = from;
X do_pad = TRUE;
X when 'n':
X if (!*(p2 = name))
X p2 = from, do_pad = TRUE;
X when '%': p2 = "%";
X when 't': p2 = to;
X when 's': p2 = subject;
X when 'l': p2 = sprintf(lines, "%d", msg[cnt].m_lines);
X when 'c': p2 = sprintf(lines, "%ld", msg[cnt].m_size);
X when 'i': (p2 = header_field(cnt, "message-id")) || (p2 = "");
X /* date formatting chars */
X when 'd': p2 = date; /* the full date */
X when 'T': p2 = Tm;
X when 'M': p2 = Mon;
X when 'Y': p2 = Yr;
X when 'y': p2 = Yr+2;
X when 'N': p2 = Day;
X when 'D': case 'W': p2 = Wkday;
X when 'Z': p2 = Zone;
X /* Any selected header */
X when '?': {
X p2 = p + 1;
X p = index(p2, '?');
X if (p) {
X *p = 0;
X if (!(p2 = header_field(cnt, p2)))
X p2 = "";
X *p = '?';
X } else {
X p = p2 + (strlen(p2) - 1);
X if (!(p2 = header_field(cnt, p2)))
X p2 = "";
X }
X }
X otherwise: continue; /* unknown formatting char */
X }
X if (do_pad && pad && strlen(p2) > pad) {
X char *old_p2 = p2, *p3;
X /* if addr is too long, move pointer forward till the
X * "important" part is readable only for ! paths/addresses.
X */
X while (p3 = index(p2, '!')) {
X int tmp = strlen(p3+1); /* xenix has compiler problems */
X p2 = p3+1;
X if (tmp + isauthor*4 < pad) {
X if (isauthor && (p2 -= 4) < old_p2)
X p2 = old_p2;
X break;
X }
X }
X if (isauthor && p2 > old_p2+4 && !p3 && strlen(p2) + 4 > pad)
X p2 -= 4;
X if (old_p2 != p2 && isauthor)
X (void) strncpy(p2, "TO: ", 4); /* doesn't null terminate */
X }
X len = strlen(sprintf(b, fmt, p2));
X n += len, b += len;
X /* Get around a bug in 5.5 IBM RT which pads with NULs not ' ' */
X while (n && !*(b-1))
X b--, n--;
X } else
X n++, *b++ = *p;
X for (*b-- = 0; isspace(*b) && *b != '\n'; --b)
X *b = 0;
X return buf;
X}
X
Xchar *
Xcompose_hdr(cnt)
Xint cnt;
X{
X if (!hdr_format)
X hdr_format = DEF_HDR_FMT;
X return format_hdr(cnt, hdr_format, TRUE);
X}
X
X/*
X * Using message "n", build a list of recipients that you would mail to if
X * you were to reply to this message. If "all" is true, then it will take
X * everyone from the To line in addition to the original sender.
X * route_addresses() is called from mail.c, not from here. There are too many
X * other uses for reply_to to always require reconstruction of return paths.
X * Note that we do NOT deal with Cc paths here either.
X * Check to make sure that we in fact return a legit address (i.e. not blanks
X * or null). If such a case occurs, return login name. Always pad end w/blank.
X */
Xchar *
Xreply_to(n, all, buf)
Xchar buf[];
X{
X register char *p = NULL, *p2, *b = buf, *field;
X char line[256], name[256], addr[256], *unscramble_addr();
X
X if (field = do_set(set_options, "reply_to_hdr")) {
X#ifndef MSG_SEPARATOR
X if (!*field)
X goto DoFrom; /* special case -- get the colon-less From line */
X#endif /* MSG_SEPARATOR */
X field = lcase_strcpy(line, field);
X while (*field) {
X if (p2 = any(field, " \t,:"))
X *p2 = 0;
X#ifndef MSG_SEPARATOR
X if (!lcase_strncmp(field, "from_", -1))
X goto DoFrom;
X#endif /* MSG_SEPARATOR */
X if ((p = header_field(n, field)) || !p2)
X break;
X else {
X field = p2+1;
X while (isspace(*field) || *field == ':' || *field == ',')
X field++;
X }
X }
X if (!p)
X print("Warning: message contains no `reply_to_hdr' headers.\n");
X }
X if (p || (!p && ((p = header_field(n, field = "reply-to")) ||
X (p = header_field(n, field = "from")) ||
X (p = header_field(n, field = "return-path")))))
X skipspaces(0);
X else if (!p) {
X#ifndef MSG_SEPARATOR
XDoFrom:
X field = "from_";
X /* if all else fails, then get the first token in "From" line */
X if (p2 = msg_get(n, line, sizeof line))
X p = index(p2, ' ');
X else
X return "";
X skipspaces(1);
X if (p2 = index(p, ' '))
X *p2 = 0;
X (void) unscramble_addr(p, line); /* p is safely recopied to line */
X p = line;
X#else /* MSG_SEPARATOR */
X wprint("Warning: unable to find who msg %d is from!\n", n+1);
X#endif /* MSG_SEPARATOR */
X }
X (void) get_name_n_addr(p, name, addr);
X if (!name[0] && (!lcase_strncmp(field, "return-path", -1) ||
X !lcase_strncmp(field, "from_", -1))) {
X /*
X * Get the name of the author of the message we're replying to from the
X * From: header since that header contains the author's name. Only do
X * this if the address was gotten from the return-path or from_ lines
X * because this is the only way to guarantee that the return address
X * matches the author's name. Reply-To: may not be the same person!
X * Check Resent-From: if the address came from the from_ line, else
X * check From:, and finally Sender: or Name:.
X */
X if (!lcase_strncmp(field, "from_", -1) &&
X (p = header_field(n, "resent-from")) ||
X (p = header_field(n, "from")) ||
X (p = header_field(n, "sender")))
X (void) get_name_n_addr(p, name, NULL);
X if (!name[0] && (p = header_field(n, "name")))
X (void) strcpy(name, p);
X if (name[0]) {
X if ((p = any(name, "(<,\"")) && (*p == ',' || *p == '<'))
X *b++ = '"';
X b += Strcpy(b, name);
X if (p && (*p == ',' || *p == '<'))
X *b++ = '"';
X *b++ = ' ', *b++ = '<';
X }
X b += Strcpy(b, addr);
X if (name[0])
X *b++ = '>', *b = 0;
X } else
X b += Strcpy(buf, p);
X
X /*
X * if `all' is true, append everyone on the "To:" line(s).
X * cc_to(), called separately, will catch the cc's
X */
X if (all) {
X int lim = HDRSIZ - (b - buf) - 2;
X /* Check for overflow on each copy.
X * The assumption that HDRSIZ is correct is unwise, but I know it
X * to be true for Mush. Be forewarned if you call this routine.
X */
X if (lim > 0 && (p = header_field(n, "resent-to")) && *p) {
X *b++ = ',', *b++ = ' ';
X p[lim] = '\0'; /* prevent overflow */
X b += Strcpy(b, p);
X lim = HDRSIZ - (b - buf) - 2;
X }
X if (lim > 0 && (p = header_field(n, "to")) && *p) {
X *b++ = ',', *b++ = ' ';
X p[lim] = '\0'; /* prevent overflow */
X b += Strcpy(b, p);
X lim = HDRSIZ - (b - buf) - 2;
X }
X if (lim > 0 && (p = header_field(n, "apparently-to")) && *p) {
X *b++ = ',', *b++ = ' ';
X p[lim] = '\0'; /* prevent overflow */
X b += Strcpy(b, p);
X lim = HDRSIZ - (b - buf) - 2;
X }
X /* Also append the Resent-From address if there is one. */
X if (lim > 0 && (p = header_field(n, "resent-from")) && *p) {
X *b++ = ',', *b++ = ' ';
X p[lim] = '\0'; /* prevent overflow */
X (void) strcpy(b, p);
X }
X }
X fix_up_addr(buf);
X /* p2 used to save boolean value of $metoo */
X if (!(p2 = do_set(set_options, "metoo"))) {
X /* Save the original name/addr in case it is the only one */
X (void) get_name_n_addr(buf, name, addr);
X take_me_off(buf);
X }
X for (p = buf; *p == ',' || isspace(*p); p++)
X ;
X if (!*p)
X if (p2) /* take_me_off() was not done */
X (void) strcpy(buf, login);
X else
X (void) sprintf(buf, "%s <%s>", name, addr);
X return buf;
X}
X
Xchar *
Xsubject_to(n, buf)
Xregister char *buf;
X{
X register char *p;
X buf[0] = 0; /* make sure it's already null terminated */
X if (!(p = header_field(n, "subject")))
X return NULL;
X if (lcase_strncmp(p, "Re:", 3))
X (void) strcpy(buf, "Re: ");
X return strcat(buf, p);
X}
X
Xchar *
Xcc_to(n, buf)
Xregister char *buf;
X{
X register char *p;
X buf[0] = 0; /* make sure it's already null terminated */
X if (!(p = header_field(n, "cc")))
X return NULL;
X fix_up_addr(p);
X if (!do_set(set_options, "metoo"))
X take_me_off(p);
X return strcpy(buf, p);
X}
END_OF_FILE
if test 21753 -ne `wc -c <'mush/hdrs.c'`; then
echo shar: \"'mush/hdrs.c'\" unpacked with wrong size!
fi
# end of 'mush/hdrs.c'
fi
if test -f 'mush/setopts.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'mush/setopts.c'\"
else
echo shar: Extracting \"'mush/setopts.c'\" \(20739 characters\)
sed "s/^X//" >'mush/setopts.c' <<'END_OF_FILE'
X/* setopts.c (c) copyright 1986 (Dan Heller) */
X
X#include "mush.h"
X#include "bindings.h"
X
Xstatic void
Xinsert_option(list, opt, order)
Xstruct options **list, *opt;
Xint order; /* Insert in sorted order? */
X{
X while (*list && (!order || (strcmp((*list)->option, opt->option) < 1)))
X list = &((*list)->next);
X opt->next = *list;
X *list = opt;
X}
X
X/* add an option indicated by "set option[=value]" or by "alias name alias"
X * function is recursive, so multilists get appended accordingly
X */
Xadd_option(list, argv)
Xregister struct options **list;
Xregister char **argv;
X{
X register struct options *tmp;
X register char *option, *value = NULL;
X
X if (!(option = *argv))
X return 1;
X /* check for one of three forms:
X * option=value option= value option = value
X */
X if (value = index(option, '=')) {
X if (value == option) {
X print("No variable specified\n");
X return 0;
X }
X /* "option=value" strip into option="option" value="value" */
X *value++ = 0; /* option is now a null terminated `option' */
X if (*value || (value = *++argv)) { /* "option= value" */
X ++argv;
X }
X } else if (*++argv && !strcmp(*argv, "=")) {
X if (value = *++argv) /* "option = value" */
X ++argv;
X }
X
X /* check for internal vars that can't be set this way */
X if (*list == set_options && check_internal(option)) {
X print("You can't change %s with \"set\".\n", option);
X return 0;
X }
X
X /* check to see if option is already set by attempting to unset it */
X if (un_set(list, option) == -1)
X return 0;
X
X /* now make a new option struct and set fields */
X if (!(tmp = (struct options *)calloc((unsigned)1,sizeof(struct options)))) {
X error("calloc");
X return -1;
X }
X tmp->option = savestr(option);
X tmp->value = savestr(value); /* strdup handles the NULL case */
X
X insert_option(list, tmp, (list != &own_hdrs));
X
X /* check for options which must have values or are used frequently */
X if (*list == set_options) {
X#if defined(CURSES) || defined(SUNTOOL)
X if (!strcmp(tmp->option, "no_reverse"))
X turnoff(glob_flags, REV_VIDEO);
X else
X#endif /* CURSES || SUNTOOL */
X#ifdef SUNTOOL
X if (!strcmp(tmp->option, "tool_help"))
X if (tmp->value && *(tmp->value))
X strdup(tool_help, tmp->value);
X else {
X int n = 0;
X char *p = getpath(TOOL_HELP, &n);
X if (n)
X strdup(tool_help, "tool_help");
X else
X strdup(tool_help, p);
X strdup(tmp->value, tool_help);
X }
X else
X#endif /* SUNTOOL */
X if (!strcmp(tmp->option, "cmd_help"))
X if (tmp->value && *(tmp->value))
X strdup(cmd_help, tmp->value);
X else {
X int n = 0; /* don't ignore no such file or directory */
X char *p = getpath(COMMAND_HELP, &n);
X if (n)
X strdup(cmd_help, "cmd_help");
X else
X strdup(cmd_help, p);
X strdup(tmp->value, cmd_help);
X }
X else if (!strcmp(tmp->option, "prompt"))
X prompt = (tmp->value)? tmp->value : DEF_PROMPT;
X else if (!strcmp(tmp->option, "warning"))
X turnon(glob_flags, WARNING);
X else if (!strcmp(tmp->option, "mil_time"))
X turnon(glob_flags, MIL_TIME);
X#ifndef MSG_SEPARATOR
X else if (!strcmp(tmp->option, "date_received"))
X turnon(glob_flags, DATE_RECV);
X#endif /* MSG_SEPARATOR */
X else if (!strcmp(tmp->option, "escape"))
X escape = (tmp->value)? tmp->value : DEF_ESCAPE;
X else if (!strcmp(tmp->option, "hdr_format"))
X hdr_format = (tmp->value)? tmp->value : DEF_HDR_FMT;
X else if (!strcmp(tmp->option, "crt")) {
X if (!istool)
X crt = (tmp->value)? max(atoi(tmp->value), 2): 18;
X }
X else if (!strcmp(tmp->option, "screen")) {
X screen = (tmp->value)? max(atoi(tmp->value), 1): 18;
X#ifdef CURSES
X if (iscurses && screen > LINES-2)
X screen = LINES-2;
X#endif /* CURSES */
X } else if (!strcmp(tmp->option, "wrapcolumn")) {
X char wval[16];
X wrapcolumn =
X (tmp->value && *(tmp->value))? max(atoi(tmp->value), 0): 78;
X#ifdef CURSES
X /* Use COLS-2 because of silly terminals like vt100 */
X if (iscurses && wrapcolumn > COLS - 2)
X wrapcolumn = COLS - 2;
X#endif /* CURSES */
X xfree(tmp->value);
X tmp->value = savestr(sprintf(wval, "%d", wrapcolumn));
X } else if (!strcmp(tmp->option, "history"))
X init_history((value && *value)? atoi(value) : 1);
X else if (!strcmp(tmp->option, "realname")) {
X char *new[4];
X new[1] = "NAME";
X new[2] = tmp->value;
X new[3] = NULL;
X (void) Setenv(3, new); /* new[0] is ignored */
X } else if (!strcmp(tmp->option, "known_hosts")) {
X register char *p;
X int n;
X /* in case user separated with commas */
X for (p = index(tmp->value, ','); p; p = index(p+1, ','))
X *p = ' ';
X free_vec(known_hosts);
X known_hosts = mk_argv(tmp->value, &n, FALSE);
X } else if (!strcmp(tmp->option, "hostname")) {
X register char *p;
X int n;
X /* in case user separated with commas */
X for (p = index(tmp->value, ','); p; p = index(p+1, ','))
X *p = ' ';
X free_vec(ourname);
X ourname = mk_argv(tmp->value, &n, FALSE);
X } else if (!strcmp(tmp->option, "complete")) {
X if (value && *value) {
X m_xlate(value); /* use the original, don't change tmp->value */
X complete = value[0];
X complist = value[1];
X } else {
X tmp->value = savestr("\\E\\CD");
X complete = '\033';
X complist = '\004';
X }
X }
X }
X
X if (*argv)
X return add_option(list, argv);
X return 1;
X}
X
X/*
X * If str is NULL, just print options and their values. Note that numerical
X * values are not converted to int upon return. If str is not NULL
X * return the string that matched, else return NULL;
X */
Xchar *
Xdo_set(list, str)
Xregister struct options *list;
Xregister char *str;
X{
X register struct options *opts;
X
X if (!str)
X (void) do_pager(NULL, TRUE); /* page using internal pager */
X
X for (opts = list; opts; opts = opts->next)
X if (!str) {
X (void) do_pager(opts->option, FALSE);
X if (opts->value && *opts->value) {
X (void) do_pager(" \t", FALSE);
X (void) do_pager(opts->value, FALSE);
X }
X if (do_pager("\n", FALSE) == EOF)
X break;
X } else {
X if (strcmp(str, opts->option))
X continue;
X if (opts->value)
X return opts->value;
X else
X return "";
X }
X
X if (!str)
X (void) do_pager(NULL, FALSE); /* terminate internal pager */
X
X /* if we still haven't matched, check for environment vars */
X if (str && list == set_options) {
X register int N, n;
X for (N = 0; environ[N]; N++) {
X char *p = index(environ[N], '=');
X if (p)
X *p = 0;
X n = lcase_strncmp(str, environ[N], -1);
X if (p)
X *p = '=';
X if (!n)
X return p+1;
X }
X }
X return NULL;
X}
X
X/*
X * unset the variable described by p in the list "list".
X * if the variable isn't set, then return 0, else return 1.
X */
Xun_set(list, p)
Xregister struct options **list;
Xregister char *p;
X{
X register struct options *opts = *list, *tmp;
X
X if (!list || !*list || !p || !*p)
X return 0;
X if (*list == set_options) {
X#if defined(CURSES) || defined(SUNTOOL)
X if (!strcmp(p, "no_reverse"))
X turnon(glob_flags, REV_VIDEO);
X else
X#endif /* CURSES || SUNTOOL */
X if (!strcmp(p, "prompt"))
X prompt = DEF_PROMPT;
X else if (!strcmp(p, "warning"))
X turnoff(glob_flags, WARNING);
X else if (!strcmp(p, "mil_time"))
X turnoff(glob_flags, MIL_TIME);
X#ifndef MSG_SEPARATOR
X else if (!strcmp(p, "date_received"))
X turnoff(glob_flags, DATE_RECV);
X#endif /* MSG_SEPARATOR */
X else if (!strcmp(p, "escape"))
X escape = DEF_ESCAPE;
X else if (!strcmp(p, "hdr_format"))
X hdr_format = DEF_HDR_FMT;
X else if (!strcmp(p, "crt"))
X crt = 18;
X else if (!strcmp(p, "screen")) {
X screen = 18;
X#ifdef CURSES
X if (iscurses && screen > LINES-2)
X screen = LINES-2;
X#endif /* CURSES */
X } else
X#ifdef SUNTOOL
X if (!strcmp(p, "tool_help")) {
X int n = 0;
X char *p2 = getpath(TOOL_HELP, &n);
X if (n)
X strdup(tool_help, "tool_help");
X else
X strdup(tool_help, p2);
X } else
X#endif /* SUNTOOL */
X if (!strcmp(p, "cmd_help")) {
X int n = 0; /* don't ignore no such file or directory */
X char *p2 = getpath(COMMAND_HELP, &n);
X if (n)
X strdup(cmd_help, "cmd_help");
X else
X strdup(cmd_help, p2);
X } else if (!strcmp(p, "wrapcolumn"))
X wrapcolumn = 0;
X else if (!strcmp(p, "history"))
X init_history(1);
X else if (!strcmp(p, "known_hosts")) {
X free_vec(known_hosts);
X known_hosts = DUBL_NULL;
X } else if (!strcmp(p, "hostname")) {
X free_vec(ourname);
X ourname = DUBL_NULL;
X } else if (ison(glob_flags, IS_GETTING) && !strcmp(p, "edit_hdrs")) {
X wprint("You mush finish this letter first.\n");
X return -1;
X } else if (!strcmp(p, "complete"))
X complete = complist = 0;
X }
X
X if (!strcmp(p, opts->option)) {
X *list = (*list)->next;
X xfree (opts->option);
X if (opts->value)
X xfree(opts->value);
X xfree((char *)opts);
X return 1;
X }
X for ( ; opts->next; opts = opts->next)
X if (!strcmp(p, opts->next->option)) {
X tmp = opts->next;
X opts->next = opts->next->next;
X xfree (tmp->option);
X if (tmp->value)
X xfree(tmp->value);
X xfree ((char *)tmp);
X return 1;
X }
X return 0;
X}
X
X/* The functions below return 0 since they don't affect
X * messages.
X */
Xset(n, argv, list)
Xregister int n;
Xregister char **argv;
Xchar *list;
X{
X void list_to_str();
X char firstchar = **argv;
X register char *cmd = *argv;
X register struct options **optlist;
X char buf[BUFSIZ];
X
X if (*cmd == 'u')
X cmd += 2;
X if (*++argv && !strcmp(*argv, "-?"))
X return help(0, (*cmd == 'i')? "ignore": "set", cmd_help);
X
X if (*argv && **argv == '?') {
X int incurses;
X if (!strcmp(*argv, "?all")) {
X if (incurses = iscurses) /* assign and compare to TRUE */
X clr_bot_line(), iscurses = FALSE;
X (void) do_pager(NULL, TRUE); /* start internal pager */
X for (n = 0; variable_stuff(n, NULL, buf); n++)
X if (do_pager(strcat(buf, "\n"), FALSE) == EOF)
X break;
X (void) do_pager(NULL, FALSE); /* terminate pager */
X iscurses = incurses;
X } else {
X /* May return null if variable not set. */
X (void) variable_stuff(0, (*argv)+1, buf);
X print("%s\n", buf);
X }
X return 0;
X }
X
X if (firstchar == 'u') {
X if (!*argv) {
X print("%s what?\n", cmd);
X return -1;
X } else {
X optlist = (*cmd == 'i')? &ignore_hdr : &set_options;
X do if (!strcmp(*argv, "*")) {
X while (*optlist)
X (void) un_set(optlist, (*optlist)->option);
X#ifdef SUNTOOL
X if (*cmd != 'i')
X opts_panel_item(NULL);
X#endif /* SUNTOOL */
X } else if (!un_set(optlist, *argv) &&
X do_set(set_options, "warning"))
X print("un%s: %s not set\n",
X (*cmd == 'i')? "ignore" : "set", *argv);
X#ifdef SUNTOOL
X else if (*cmd != 'i')
X opts_panel_item(*argv);
X#endif /* SUNTOOL */
X while (*++argv);
X#ifdef SUNTOOL
X if (*cmd == 'i' && istool > 1)
X update_list_textsw(&ignore_hdr);
X#endif /* SUNTOOL */
X }
X return 0;
X }
X
X if (!*argv) {
X (void) do_set((*cmd == 'i')? ignore_hdr: set_options, NULL);
X return 0;
X }
X
X /*
X * Check for input redirection. If so, set the variable to the ascii
X * value of the current msg_list.
X */
X if (ison(glob_flags, IS_PIPE)) {
X char *newargv[4];
X
X if (*cmd == 'i') {
X print("You can't pipe to the \"%s\" command.\n", cmd);
X return -1;
X }
X if (newargv[0] = index(argv[0], '='))
X *newargv[0] = 0;
X list_to_str(list, buf);
X if (!buf[0] && !do_set(set_options, argv[0])) {
X return 0;
X }
X newargv[0] = argv[0];
X newargv[1] = "=";
X newargv[2] = buf;
X newargv[3] = NULL;
X (void) add_option(&set_options, newargv);
X return 0;
X }
X
X /*
X * finally, just set the variable the user requested.
X */
X (void) add_option((*cmd == 'i')? &ignore_hdr: &set_options, argv);
X#ifdef SUNTOOL
X if (istool > 1)
X if (*cmd == 'i')
X update_list_textsw(&ignore_hdr);
X else
X opts_panel_item(argv[0]);
X#endif /* SUNTOOL */
X return 0;
X}
X
X/*
X * The alts list is a list of hostnames or pathnames where the user
X * has an account. If he doesn't specify "metoo", then when replying
X * to mail, if his address is listed, it will be removed. The syntax
X * is compatible with ucb Mail in that just hostnames can be used.
X * However, there is an added feature that mush provides which another
X * login name or path to another login can be specified by preceding the
X * path or login with a !
X * "argv" may be a file pointer to write the data into by use of save_opts()
X */
Xalts(argc, argv)
Xregister char **argv;
X{
X char buf[BUFSIZ], *p;
X
X /* check here first because a 0 argc means to write it to a file */
X if (argc <= 1) {
X int n;
X if (!alternates)
X return 0;
X if (argc == 0)
X (void) fprintf((FILE *)argv, "alts ");
X for (n = 0; alternates[n]; n++) {
X p = 0;
X buf[0] = 0;
X (void) strcpy(&buf[1], alternates[n]);
X if (buf[1] != '*')
X (void) reverse(&buf[1]);
X if ((p = rindex(&buf[1], '!')) && !lcase_strncmp(p+1, login, -1))
X *p = 0;
X else if (buf[1] != '*')
X buf[0] = '!';
X if (argc == 0)
X (void) fprintf((FILE *)argv, "%s ", *buf? buf : &buf[1]);
X else
X wprint("%s ", *buf? buf : &buf[1]);
X if (p)
X *p = '!';
X }
X if (argc == 0)
X (void) fputc('\n', (FILE *)argv);
X else
X wprint("\n");
X return 0;
X }
X
X if (argc-- && *++argv && !strcmp(*argv, "-?"))
X return help(0, "alts", cmd_help);
X
X free_vec(alternates);
X if (alternates = (char **)calloc((unsigned)argc+1, sizeof(char *)))
X while (argc-- > 0) {
X if (argv[argc][0] == '!')
X alternates[argc] = savestr(reverse(&argv[argc][1]));
X else if (argv[argc][0] == '*') {
X alternates[argc] = savestr(argv[argc]);
X } else {
X p = buf + Strcpy(buf, argv[argc]);
X *p++ = '!', p += Strcpy(p, login);
X alternates[argc] = savestr(reverse(buf));
X }
X }
X return 0;
X}
X
Xsave_opts(cnt, argv)
Xchar **argv;
X{
X char file[MAXPATHLEN], *tmp;
X register FILE *fp;
X
X if (cnt && *++argv && !strcmp(*argv, "-?"))
X return help(0, "source", cmd_help);
X if (cnt && *argv)
X (void) strcpy(file, *argv);
X else if ((tmp = getenv("MUSHRC")) || (tmp = getenv("MAILRC")))
X (void) strcpy(file, tmp);
X else {
X char *home = do_set(set_options, "home");
X if (!home || !*home)
X home = ALTERNATE_HOME;
X /* if .mushrc doesn't exist, check .mailrc. If neither, force .mushrc */
X if (Access(sprintf(file, "%s/%s", home, MAILRC), F_OK) &&
X Access(sprintf(file, "%s/%s", home, ALTERNATE_RC), F_OK))
X (void) sprintf(file, "%s/%s", home, MAILRC);
X }
X
X cnt = 1;
X tmp = getpath(file, &cnt);
X if (cnt) {
X if (cnt == -1) {
X print("%s: %s\n", file, tmp);
X return -1;
X } else {
X print("%s is a directory.\n", tmp);
X return -2;
X }
X }
X /* See if the file exists and confirm overwrite */
X if (!Access(tmp, F_OK)) {
X int overwrite = TRUE;
X char buf[BUFSIZ];
X if (!istool) {
X print("\"%s\" exists. Overwrite? ", trim_filename(tmp));
X if (Getstr(buf, 3, 0) <= 0 || lower(*buf) != 'y')
X overwrite = FALSE;
X }
X#ifdef SUNTOOL
X else {
X sprintf(buf, "\"%s\" exists. Overwrite? ", trim_filename(tmp));
X overwrite = ask(buf);
X }
X#endif /* SUNTOOL */
X if (!overwrite) {
X print("\"%s\" unchanged.\n", tmp);
X return -3;
X }
X }
X if (!(fp = fopen(tmp, "w"))) {
X error("Can't open %s", file);
X return -1;
X }
X
X save_list("basic variable settings", set_options, "set", '=', fp);
X
X save_list("mail headers for outgoing mail", own_hdrs, "my_hdr", 0, fp);
X
X save_list("aliases", aliases, "alias", 0, fp);
X
X (void) alts(0, (char **)fp);
X
X save_list("headers to ignore", ignore_hdr, "ignore", ' ', fp);
X
X save_list("command abbreviations", functions, "cmd", ' ', fp);
X
X save_list("command macros for function keys", fkeys, "fkey", ' ', fp);
X
X#ifdef CURSES
X save_cmd("curses mode key bindings", cmd_map, "bind", 1, fp);
X#endif /* CURSES */
X
X save_cmd("line mode mappings", line_map, "map", 0, fp);
X
X save_cmd("composition mode mappings", bang_map, "map!", 0, fp);
X
X (void) fclose(fp);
X print("All variables and options saved in %s\n", trim_filename(tmp));
X return 0;
X}
X
Xsave_list(title, list, command, equals, fp)
Xstruct options *list;
Xregister char *command, *title, equals;
Xregister FILE *fp;
X{
X register struct options *opts;
X register char *p;
X
X if (!list)
X return;
X (void) fprintf(fp, "#\n# %s\n#\n", title);
X for (opts = list; opts; opts = opts->next) {
X if (list == set_options && !strcmp(opts->option, "cwd"))
X continue; /* don't print $cwd */
X (void) fprintf(fp, "%s %s", command, opts->option);
X if (opts->value && *opts->value) {
X register char *quote;
X if (!equals)
X quote = NO_STRING;
X else if (p = any(opts->value, "\"'"))
X if (*p == '\'')
X quote = "\"";
X else
X quote = "'";
X else
X if (!any(opts->value, " \t;|"))
X quote = NO_STRING;
X else
X quote = "'";
X (void) fputc(equals? equals: ' ', fp);
X (void) fprintf(fp, "%s%s%s", quote, opts->value, quote);
X }
X (void) fputc('\n', fp);
X }
X}
X
Xextern struct cmd_map map_func_names[];
X
Xsave_cmd(title, list, command, equals, fp)
Xstruct cmd_map *list;
Xregister char *command, *title;
Xregister int equals;
Xregister FILE *fp;
X{
X register struct cmd_map *opts;
X register char *p;
X char buf[MAX_MACRO_LEN * 2];
X
X if (!list)
X return;
X (void) fprintf(fp, "#\n# %s\n#\n", title);
X for (opts = list; opts; opts = opts->m_next) {
X register char *quote;
X if ((p = any(opts->m_str, "\"'")) && *p == '\'')
X quote = "\"";
X else
X quote = "'";
X (void) fprintf(fp, "%s %s%s%s", command, quote,
X ctrl_strcpy(buf, opts->m_str, TRUE), quote);
X if (equals && map_func_names[opts->m_cmd].m_str)
X (void) fprintf(fp, " %s", map_func_names[opts->m_cmd].m_str);
X if (opts->x_str && *opts->x_str) {
X if ((p = any(opts->x_str, "\"'")) && *p == '\'')
X quote = "\"";
X else
X quote = "'";
X (void) fprintf(fp, " %s%s%s", quote,
X ctrl_strcpy(buf, opts->x_str, TRUE), quote);
X }
X (void) fputc('\n', fp);
X }
X}
X
X/*
X * do_alias handles aliases, header settings, functions, and fkeys.
X * since they're all handled in the same manner, the same routine is
X * used. argv[0] determines which to use.
X * alias is given here as an example
X *
X * alias identify all aliases
X * alias name identify alias
X * alias name arg1 arg2 arg3... -> name="arg1 arg2 arg3"; call add_option
X * unalias arg1 [arg2 arg3 ... ] unalias args
X *
X * same is true for dealing with your own headers.
X * (also the expand command)
X */
Xdo_alias(argc, argv)
Xregister char **argv;
X{
X register char *cmd = *argv, *p;
X struct options **list;
X char firstchar = *cmd, buf[BUFSIZ];
X
X if (argc == 0)
X return 0 - in_pipe();
X if (firstchar == 'u')
X firstchar = cmd[2];
X if (*++argv && !strcmp(*argv, "-?")) { /* doesn't apply for fkeys */
X register char *help_str;
X if (firstchar == 'a' || firstchar == 'e')
X help_str = "alias";
X else if (firstchar == 'c')
X help_str = "cmd";
X else if (firstchar == 'f')
X help_str = "fkey";
X else
X help_str = "my_hdr";
X return help(0, help_str, cmd_help);
X }
X
X if (firstchar == 'a')
X list = &aliases;
X else if (firstchar == 'c')
X list = &functions;
X else if (firstchar == 'f')
X list = &fkeys;
X else
X list = &own_hdrs;
X
X if (*cmd == 'u') {
X if (!*argv) {
X print("%s what?\n", cmd);
X return -1;
X /* unset a list separated by spaces or ',' */
X } else while (*argv) {
X if (!strcmp(*argv, "*")) /* unset everything */
X while (*list)
X (void) un_set(list, (*list)->option);
X else if (!un_set(list, *argv))
X print("\"%s\" isn't set\n", *argv);
X argv++;
X }
X#ifdef SUNTOOL
X if (istool > 1)
X update_list_textsw(list);
X#endif /* SUNTOOL */
X return 0;
X }
X
X if (!*argv && *cmd != 'e') {
X /* just type out all the aliases or own_hdrs */
X (void) do_set(*list, NULL);
X return 0;
X }
X
X if (*cmd == 'e') { /* command was "expand" (aliases only) */
X if (!*argv) {
X print("expand which alias?\n");
X return -1;
X } else
X do {
X print("%s: ", *argv);
X if (p = alias_to_address(*argv))
X print("%s\n", p);
X } while (*++argv);
X return 0;
X }
X
X /* at this point, *argv now points to a variable name ...
X * check for hdr -- if so, *argv better end with a ':' (check *p)
X */
X if (list == &own_hdrs && !(p = index(*argv, ':'))) {
X print("header labels must end with a ':' (%s)\n", *argv);
X return -1;
X }
X if (!argv[1] && !index(*argv, '='))
X if (p = do_set(*list, *argv))
X print("%s\n", p);
X else
X print("%s is not set\n", *argv);
X else {
X char *tmpargv[2];
X (void) argv_to_string(buf, argv);
X if ((p = any(buf, " \t=")) && *p != '=')
X *p = '=';
X /* if we're setting an alias, enforce the insertion of commas
X * between each well-formed address.
X */
X if (list == &aliases)
X fix_up_addr(p+1);
X tmpargv[0] = buf;
X tmpargv[1] = NULL;
X (void) add_option(list, tmpargv);
X#ifdef SUNTOOL
X if (istool > 1)
X update_list_textsw(list);
X#endif /* SUNTOOL */
X }
X return 0;
X}
END_OF_FILE
if test 20739 -ne `wc -c <'mush/setopts.c'`; then
echo shar: \"'mush/setopts.c'\" unpacked with wrong size!
fi
# end of 'mush/setopts.c'
fi
if test -f 'mush/tooledit.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'mush/tooledit.c'\"
else
echo shar: Extracting \"'mush/tooledit.c'\" \(9136 characters\)
sed "s/^X//" >'mush/tooledit.c' <<'END_OF_FILE'
X/* @(#)tooledit.c (c) copyright 2/14/90 (Dan Heller) */
X
X/*
X * intercept events in the compose window for auto-
X * positioning and tilde command recognition.
X */
X#include "mush.h"
X
Xstatic short dat_bentarrow[] = {
X 0x007F, 0x007F, 0x007F, 0x0007, 0x0407, 0x0C07, 0x1C07, 0x3807,
X 0x7FFF, 0xFFFF, 0x7FFF, 0x3800, 0x1C00, 0x0C00, 0x0400, 0x0000
X};
Xmpr_static(bent_arrow, 16, 16, 1, dat_bentarrow);
XCursor bentarrow;
X
Xextern void do_send(), do_edit();
X
X/* Return the byte position in the textsw of the header specified */
XTextsw_index
Xheader_position(textsw, str)
XTextsw textsw;
Xchar *str;
X{
X char buf[256];
X register char *p = buf, *p2;
X int contd_hdr = 0, add_newline = 0;
X Textsw_index pos = 0L, ret_pos = 0L;
X
X buf[0] = 0;
X for (;;) {
X /* get a line at a time from the textsw */
X (void) window_get(textsw, TEXTSW_CONTENTS, pos, buf, 256);
X if (p = index(buf, '\n'))
X *p = 0;
X else
X add_newline++;
X p = buf;
X skipspaces(0);
X if (!*p) /* newline alone -- end of headers */
X break;
X pos += strlen(buf) + 1; /* advance position to next line */
X if (*p != ' ' && *p != '\t') {
X contd_hdr = 0;
X /* strcmp ignoring case */
X for (p2 = str; *p && *p2 && lower(*p2) == lower(*p); ++p, ++p2)
X ;
X /* MATCH is true if p2 is at the end of str and *p is ':' */
X if (*p2 || *p != ':') {
X if (!*p2 && isspace(*any(p, ": \t"))) {
X /* Not a legal or continued header */
X pos -= strlen(buf) + 1; /* go back to beginning of line */
X break;
X }
X continue;
X } else {
X contd_hdr = 1;
X ret_pos = pos - 1;
X }
X } else if (!contd_hdr)
X continue;
X }
X if (!ret_pos) {
X /* coudn't find the header -- add it */
X window_set(textsw, TEXTSW_INSERTION_POINT, pos, NULL);
X p = buf;
X if (add_newline)
X *p++ = '\n', pos--;
X for (p2 = str; *p2; ++p2) {
X if (p2 == str || p2[-1] == '-')
X *p++ = upper(*p2);
X else
X *p++ = *p2;
X }
X *p++ = ':', *p++ = ' ', *p++ = '\n', *p = 0;
X textsw_insert(textsw, buf, strlen(buf));
X ret_pos = pos + strlen(buf) - 1;
X }
X return ret_pos;
X}
X
X/* position_flags indicates which header to go to when uses tilde commands */
Xstatic u_long position_flags;
Xstatic char *tilde_hdrs[] = {
X#define POSITION_TO ULBIT(0)
X "to",
X#define POSITION_SUBJ ULBIT(1)
X "subject",
X#define POSITION_CC ULBIT(2)
X "cc",
X#define POSITION_BCC ULBIT(3)
X "bcc",
X#define POSITION_FCC ULBIT(4)
X "fcc"
X};
X#define POSITION_ALL \
X ((POSITION_TO) | (POSITION_SUBJ) | (POSITION_CC) | (POSITION_BCC))
X#define POSITION_END ULBIT(5)
X#define TOTAL_POSITIONS 6
X
X/*
X * position_flags identifies which header is requested by the calling func.
X * use header_position to find the position of the header associated with
X * with the flags.
X */
Xstatic void
Xgo_to_next_pos(textsw)
XTextsw textsw;
X{
X Textsw_index pos;
X int i = 0;
X
X while (i < TOTAL_POSITIONS && isoff(position_flags, ULBIT(i)))
X i++;
X if (i == TOTAL_POSITIONS)
X return;
X if (i < ArraySize(tilde_hdrs))
X pos = header_position(textsw, tilde_hdrs[i]);
X else
X pos = (Textsw_index)window_get(textsw, TEXTSW_LENGTH);
X turnoff(position_flags, ULBIT(i));
X if (!position_flags)
X /* restore old cursor */
X window_set(textsw,WIN_CURSOR, window_get(mfprint_sw, WIN_CURSOR), NULL);
X else
X window_set(textsw, WIN_CURSOR, bentarrow, NULL);
X window_set(textsw, TEXTSW_INSERTION_POINT, pos, NULL);
X textsw_normalize_view(textsw, (Textsw_index)0);
X}
X
Xtilde_from_menu(item, value, event)
XPanel_item item;
Xint value;
XEvent *event;
X{
X Textsw textsw = (Textsw)panel_get(panel_get(item, PANEL_PARENT_PANEL),
X PANEL_CLIENT_DATA);
X if (value == 0 || event_id(event) == MS_LEFT)
X position_flags = POSITION_ALL;
X else
X turnon(position_flags, ULBIT(value - 1));
X panel_set_value(item, 0);
X go_to_next_pos(textsw);
X}
X
X/*
X * This interpose function is here to parse for tilde escapes.
X * Note: this is a (currently) undocumented feature and is intended
X * as an accelerator for advanced users. Supported tilde escapes
X * are: t,s,c,b,x,e and v.
X */
XNotify_value
Xedit_msg_textwin(textsw, event, arg, type)
XTextsw textsw;
XEvent *event;
XNotify_arg arg;
XNotify_event_type type;
X{
X char buf[2];
X static char do_tilde;
X Textsw_index pos;
X
X if (do_tilde == 1 && event_is_ascii(event) &&
X /* make sure we are going to catch this switch */
X index("bschetv", event_id(event))) {
X textsw_erase(textsw,
X (unsigned)window_get(textsw, TEXTSW_INSERTION_POINT)-1,
X (unsigned)window_get(textsw, TEXTSW_INSERTION_POINT));
X switch (event_id(event)) {
X case 'h':
X turnon(position_flags, POSITION_ALL);
X when 't':
X turnon(position_flags, POSITION_TO);
X when 's':
X turnon(position_flags, POSITION_SUBJ);
X when 'c':
X turnon(position_flags, POSITION_CC);
X when 'b':
X turnon(position_flags, POSITION_BCC);
X when 'e' : case 'v' : {
X /* shouldn't use global -- hack for now */
X extern Panel_item edit_item;
X do_edit(edit_item);
X return NOTIFY_DONE;
X }
X }
X do_tilde = 0;
X go_to_next_pos(textsw);
X return NOTIFY_DONE;
X }
X do_tilde = 0;
X /* check to see if this is a potential tilde escape */
X if (event_id(event) == *escape) {
X /* get previous character entered */
X pos = (Textsw_index)window_get(textsw, TEXTSW_INSERTION_POINT);
X if (pos > 0)
X (void) window_get(textsw, TEXTSW_CONTENTS, pos-1, buf, 1);
X /* test to see if ~ came at the beginning of a line */
X if (pos < 1 || buf[0] == '\n')
X do_tilde = 1;
X }
X /* check for auto-next-header .. e.g. when you hit CR on To: go to Subj:
X * special case backspace keys since textsw_start_of_display_line() has
X * a bug where it gets the line # wrong when backspacing.
X */
X if (position_flags != 0L && ID != CTRL('H') && ID != 127) {
X Notify_value val;
X if (ID == '\n' || ID == '\r') {
X go_to_next_pos(textsw);
X return NOTIFY_DONE; /* don't process event */
X }
X /* we're still processing this header -- continue to do so unless
X * the event in question changes the line# of the insertion point.
X * first get current position...
X */
X pos = (Textsw_index)window_get(textsw, TEXTSW_INSERTION_POINT);
X /* now let the event be processed... */
X val = notify_next_event_func(textsw, event, arg, type);
X /* see if the line # for the new insertion point has changed. */
X if (textsw_start_of_display_line(textsw, pos) !=
X textsw_start_of_display_line(textsw,
X (Textsw_index)window_get(textsw, TEXTSW_INSERTION_POINT))) {
X /* the event (mouse button, ACTION_??), changed the line # */
X position_flags = 0L; /* disable auto-next-header */
X /* restore cursor */
X window_set(textsw,
X WIN_CURSOR, window_get(mfprint_sw, WIN_CURSOR),
X NULL);
X }
X return val;
X }
X return notify_next_event_func(textsw, event, arg, type);
X}
X
X/*
X * start the compose textsw. This is here because we need position_flags
X * and the tilde-bits to set the insertion point at the To: line if
X * do_position is true.
X */
Xvoid
Xstart_textsw_edit(textsw, do_position)
XTextsw textsw;
X{
X extern char *hfile;
X char *file = (char *)window_get(textsw, TEXTSW_CLIENT_DATA);
X Textsw_index first, last, to_index;
X int i;
X
X strdup(file, hfile);
X#ifdef SUN_4_0 /* SunOS 4.0+ */
X window_set(textsw,
X TEXTSW_CLIENT_DATA, file,
X TEXTSW_FILE_CONTENTS, hfile,
X TEXTSW_READ_ONLY, FALSE,
X TEXTSW_STORE_CHANGES_FILE, FALSE,
X NULL);
X#else /* SUN_4_0 */
X textsw_load_file(textsw, hfile, 1, 0, 0);
X window_set(textsw,
X TEXTSW_CLIENT_DATA, file,
X TEXTSW_READ_ONLY, FALSE,
X TEXTSW_STORE_CHANGES_FILE, FALSE,
X NULL);
X#endif /* SUN_4_0 */
X position_flags = 0L;
X if (do_position) {
X turnon(position_flags, POSITION_TO);
X if (do_set(set_options, "ask") || do_set(set_options, "asksub"))
X turnon(position_flags, POSITION_SUBJ);
X if (do_set(set_options, "askcc"))
X turnon(position_flags, POSITION_CC);
X }
X turnon(position_flags, POSITION_END);
X go_to_next_pos(textsw);
X (void) unlink(hfile);
X xfree(hfile), hfile = NULL;
X}
X
X/*ARGSUSED*/
Xvoid
Xdo_edit(item, value, event)
XPanel_item item;
Xint value;
Xregister Event *event;
X{
X int argc;
X char *file, **argv, *edit, cmd[MAXPATHLEN];
X Panel_item next;
X Panel panel = (Panel)panel_get(item, PANEL_PARENT_PANEL);
X Textsw textsw = (Textsw)panel_get(panel, PANEL_CLIENT_DATA);
X
X file = (char *)window_get(textsw, TEXTSW_CLIENT_DATA);
X if (textsw_store_file(textsw, file, 0, 0)) {
X error("Can't start editor");
X return;
X }
X if ((!(edit = do_set(set_options, "visual")) || !*edit) &&
X (!(edit = do_set(set_options, "editor")) || !*edit))
X edit = DEF_EDITOR;
X (void) sprintf(cmd, "%s %s", edit, file);
X argc = 0;
X if (!(argv = mk_argv(cmd, &argc, FALSE))) {
X unlink(file);
X return;
X }
X if (tool_edit_letter(textsw, argv) > -1) {
X /* skip first panel item */
X item = (Panel_item) panel_get(panel, PANEL_FIRST_ITEM);
X for (item = (Panel_item) panel_get(item, PANEL_NEXT_ITEM);
X item; item = next) {
X next = (Panel_item) panel_get(item, PANEL_NEXT_ITEM);
X (void) panel_set(item, PANEL_SHOW_ITEM, FALSE, NULL);
X }
X position_flags = 0L;
X window_set(textsw,WIN_CURSOR, window_get(mfprint_sw,WIN_CURSOR), NULL);
X }
X free_vec(argv);
X}
END_OF_FILE
if test 9136 -ne `wc -c <'mush/tooledit.c'`; then
echo shar: \"'mush/tooledit.c'\" unpacked with wrong size!
fi
# end of 'mush/tooledit.c'
fi
echo shar: End of archive 13 \(of 19\).
cp /dev/null ark13isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 19 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0