v14i042: Mail User's Shell, version 6.0, Part10/14
Rich Salz
rsalz at bbn.com
Fri Apr 15 03:28:34 AEST 1988
Submitted-by: island!argv at sun.com (Dan Heller)
Posting-number: Volume 14, Issue 42
Archive-name: mush6.0/part10
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 10 (of 14)."
# Contents: hdrs.c
# Wrapped by rsalz at fig.bbn.com on Wed Apr 13 20:04:53 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'hdrs.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'hdrs.c'\"
else
echo shar: Extracting \"'hdrs.c'\" \(30675 characters\)
sed "s/^X//" >'hdrs.c' <<'END_OF_FILE'
X/* hdrs.c (c) copyright 1986 (Dan Heller) */
X
X/* Routines that deal with message headers inside messages */
X#include "mush.h"
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 */
char *
header_field(n, str)
char *str;
X{
X static char buf[BUFSIZ];
X char tmp[BUFSIZ];
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 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 *b = 0;
X while((p = fgets(tmp, sizeof(buf), tmpf)) && *p != '\n') {
X if (*p != ' ' && *p != '\t') {
X contd_hdr = 0;
X /* strcmp ignoring case */
X for(p2 = str; *p && *p2 && *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 } else if (!contd_hdr)
X continue;
X skipspaces(0);
X p2 = no_newln(p);
X *++p2 = ' ', *++p2 = 0;
X if (strlen(p) + (b-buf) < sizeof (buf))
X b += Strcpy(b, p);
X }
X if (b > buf) /* now get rid of the trailing blank */
X *--b = 0;
X return (*buf)? buf: NULL;
X}
X
do_hdrs(argc, argv, list)
register char **argv, list[];
X{
X register int pageful = 0, fnd;
X int (*oldint)(), (*oldquit)(), show_deleted;
X static int cnt;
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 }
X
X on_intr();
X
X if (argc && (argv[0][1] == '+' || 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[0][1] == '-' || argc > 1 && !strcmp(argv[1], "-")))
X cnt = max((cnt - 2*screen), 0);
X else if (argc && *++argv &&
X (isdigit(**argv) || **argv == '^' || **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 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 } else if (current_msg < n_array[0] || current_msg > n_array[screen-1])
X cnt = current_msg; /* adjust if user reads passed screen bounds */
X else if (cnt >= msg_cnt || !argc || !*argv)
X cnt = max((cnt - screen), 0); /* adjust window to maintian position */
X
X show_deleted = !!do_set(set_options, "show_deleted");
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(DEFAULT), DEFAULT,p);
X else
X pw_text(hdr_win, 0, pageful * l_height(DEFAULT), PIX_SRC,
X fonts[DEFAULT], p);
X Clrtoeol(hdr_win, strlen(p)*l_width(DEFAULT),
X pageful*l_height(DEFAULT), DEFAULT);
X }
X#endif /* SUNTOOL */
X#ifdef CURSES
X else if (iscurses)
X mvprintw(pageful, 0, "%-.*s", COLS-2, p), clrtoeol();
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(DEFAULT), DEFAULT);
X#endif /* SUNTOOL */
X ++pageful;
X }
X }
X#ifdef SUNTOOL
X if (istool) {
X if (msg_cnt > screen) {
X panel_set(next_scr, PANEL_SHOW_ITEM, TRUE, 0);
X panel_set(prev_scr, PANEL_SHOW_ITEM, TRUE, 0);
X }
X mail_status(0);
X }
X#endif /* SUNTOOL */
X return 0;
X}
X
X#define NEW 1
X#define ALL 2
X
specl_hdrs(argv, list)
char **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 otherwise: print("choose from n,u,o,d,r, 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#ifndef SYSV
X /*
X * XENIX compiler can't handle "special" in ison() macro.
X * It only works if the second argument is a constant!
X */
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#endif /* SYSV */
X } else {
X if (list)
X unset_msg_bit(list, n);
X if (debug) {
X 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 * compose 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 */
char *
compose_hdr(cnt)
X{
X static char buf[256];
X register char *p, *b;
X char from[256], subject[256], date[17], lines[16], chars[16], line[256];
X char to[256], addr[256], name[256], status[2];
X char Day[3], Mon[4], Tm[8], Yr[5], Wkday[4];
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, OLD) && ison(msg[cnt].m_flags, UNREAD))
X status[0] = 'U';
X else if (ison(msg[cnt].m_flags, PRESERVE))
X status[0] = 'P';
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] = chars[0] = addr[0] =
X name[0] = line[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, "to")) ||
X (p = header_field(cnt, "resent-to")) ||
X (p = header_field(cnt, "apparently-to")))
X Strncpy(to, p);
X
X /* who the messages is from--
X * %f From field
X * %a From address
X * %n From name
X */
X if (!(p = header_field(cnt, "from"))) {
X /* if all else fails, then get the first token in "From" line */
X register char *p2;
X p = ""; /* just in case */
X if (fseek(tmpf, msg[cnt].m_offset, L_SET) == -1 ||
X !(p2 = fgets(line, sizeof(line), tmpf))) {
X error("fseek in %s (msg %d, folder=%s)", tempfile, cnt+1, mailfile);
X turnon(glob_flags, READ_ONLY);
X } else if (!(p = index(p2, ' ')))
X print("Fudged \"From\" line: %s", p2);
X else if (p2 = any(++p, " \t"))
X *p2 = 0;
X }
X skipspaces(0);
X (void) no_newln(p);
X /* if the "from" line produced the user's login name, then the message is
X * from the user -- attempt to give more useful information by telling
X * to whom the message was sent. This is not possible if the "to" header
X * failed to get info (which is probably impossible).
X */
X if (!strcmp(p, login) && *to) {
X (void) strcpy(from, "TO: ");
X (void) strncpy(from+4, to, sizeof(from)-4), from[sizeof(from)-4] = 0;
X (void) get_name_n_addr(from+4, name+4, addr+4);
X if (name[4])
X (void) strncpy(name,"TO: ",4); /* strncpy doesn't null terminate */
X if (addr[4])
X (void) strncpy(addr,"TO: ",4); /* don't overwrite name there */
X } else {
X Strncpy(from, p);
X (void) get_name_n_addr(from, name, addr);
X }
X
X if (p = msg_date(cnt))
X date_to_string(p, Yr, Mon, Day, Wkday, Tm, date);
X
X (void) sprintf(lines, "%d", msg[cnt].m_lines);
X (void) sprintf(chars, "%ld", msg[cnt].m_size);
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_format)
X hdr_format = DEF_HDR_FMT;
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 /* use the cnt variable to count chars since beginning of buf
X * initialize to 9 (strlen(buf) so far). This magic number is
X * used in other places in msgs.c and mail.c
X */
X cnt = 9;
X for (p = hdr_format; *p; p++)
X if (*p == '\\')
X switch (*++p) {
X case 't':
X while (cnt % 8)
X cnt++, *b++ = ' ';
X when 'n':
X cnt = 1, *b++ = '\n';
X otherwise: cnt++, *b++ = *p;
X }
X else if (*p == '%') {
X char fmt[64];
X register char *p2 = fmt;
X int len, got_dot = FALSE;
X
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;
X *p2++ = *p;
X }
X if (!got_dot && isdigit(p[-1])) {
X int val;
X *p2 = 0; /* assure null termination */
X val = atoi(fmt+1);
X p2 += strlen(sprintf(p2, ".%d", (val >= 0 ? val : -val)));
X }
X *p2++ = 's', *p2 = 0;
X switch (*p) {
X case 'f': p2 = from;
X when 'a':
X if (!*(p2 = addr))
X p2 = from;
X when 'n':
X if (!*(p2 = name))
X p2 = from;
X when '%': p2 = "%";
X when 't': p2 = to;
X when 's': p2 = subject;
X when 'l': p2 = lines;
X when 'c': p2 = chars;
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 'N': p2 = Day;
X when 'D': p2 = Wkday;
X otherwise: continue; /* unknown formatting char */
X }
X len = strlen(sprintf(b, fmt, p2));
X cnt += len, b += len;
X /* Get around a bug in 5.5 IBM RT which pads with NULL's not ' ' */
X while (cnt && !*(b-1))
X b--, cnt--;
X } else
X cnt++, *b++ = *p;
X for (*b-- = 0; isspace(*b); --b)
X *b = 0;
X return buf;
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 * fix_address() is caled 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 */
char *
reply_to(n, all, buf)
register char *buf;
X{
X register char *p = NULL, *p2, *b = buf, *field;
X char line[256];
X
X if (field = do_set(set_options, "reply_to_hdr")) {
X#ifndef MSG_SEPARATOR
X if (!*field)
X goto From; /* 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 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, "from")) ||
X (p = header_field(n, "reply-to")) ||
X (p = header_field(n, "return-path")))))
X skipspaces(0);
X else if (!p) {
X#ifndef MSG_SEPARATOR
From:
X /* if all else fails, then get the first token in "From" line */
X if (fseek(tmpf, msg[n].m_offset, L_SET) == -1 ||
X !(p2 = fgets(line, sizeof(line), tmpf))) {
X error("fseek in %s (msg %d, folder=%s)", tempfile, n+1, mailfile);
X turnon(glob_flags, READ_ONLY);
X return "";
X }
X p = index(p2, ' ');
X skipspaces(1);
X if (p2 = index(p, ' '))
X *p2 = 0;
X#else /* MSG_SEPARATOR */
X wprint("Warning: unable to find who msg %d is from!\n", n+1);
X#endif /* MSG_SEPARATOR */
X }
X b += Strcpy(buf, p);
X
X /*
X * if `all' is true, append everyone on the "To:" line.
X * cc_to(), called separately, will catch the cc's
X */
X if (all && (p = header_field(n, "to")) && *p) {
X b += Strcpy(b, ", ");
X /* The assumption that BUFSIZ is correct is unwise, but I know it
X * to be true for Mush. Be forewarned if you call this routine.
X */
X (void) strncpy(b, p, BUFSIZ - (b - buf) - 2);
X buf[BUFSIZ-3] = 0;
X }
X fix_up_addr(buf);
X take_me_off(buf);
X for (p = buf; *p == ',' || isspace(*p); p++);
X if (!*p)
X (void) strcpy(buf, login);
X return strcat(buf, " ");
X}
X
char *
subject_to(n, buf)
register 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 (strncmp(p, "Re:", 3))
X (void) strcpy(buf, "Re: ");
X return strcat(buf, p);
X}
X
char *
cc_to(n, buf)
register 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(buf);
X take_me_off(p);
X return strcpy(buf, p);
X}
X
X/*
X * fix addresses according to the sender's address. If he's on a remote
X * machine, chances are that the addresses of everyone else he mailed to
X * are addresses from his machine. Reconstruct those addresses to route
X * thru the senders machine first.
X */
fix_addresses(to, cc)
char *to, *cc;
X{
X char pre_path[256], addr[256], name[256], buf[BUFSIZ], c, *p2;
X register char *next, *p, *b = buf, *str;
X int pre_len = 0;
X
X pre_path[0] = 0;
X /* Get the address of the sender (which is always listed first) */
X if (!(next = get_name_n_addr(to, name, addr)))
X return;
X
X /* fix up the sender's address; improve_uucp_path to optimize pre_path */
X improve_uucp_paths(addr);
X
X /* if user didn't route via uucp, pre_path will be blank */
X if (p = rindex(addr, '!')) {
X c = *++p, *p = 0;
X (void) strcpy(pre_path, addr); /* the uucp route he used */
X pre_len = strlen(pre_path);
X *p = c;
X Debug("Routing thru \"%s\"\n", pre_path);
X }
X
X b += Strcpy(b, addr);
X if (*name)
X b += strlen(sprintf(b, " (%s)", name));
X while (*next == ',' || isspace(*next)) /* move next to the next address */
X next++;
X if (*next) /* there's more to come on the To line */
X b += Strcpy(b, ", ");
X else {
X (void) strcpy(to, buf);
X if (!cc || !*cc)
X return;
X }
X for (str = next, c = 0; c < 2; str = cc, c++) {
X if (str == cc)
X b = buf;
X *b = 0; /* null terminate beginning in case there's nothing to do */
X if (!str || !*str)
X continue;
X do {
X /* get_name returns a pointer to the next address */
X if (!(p = get_name_n_addr(str, name, addr)))
X break;
X /* check to see if there's enough buffer space to add this addr */
X if ((b - buf) + pre_len + strlen(addr) + strlen(name) + 5 >= BUFSIZ)
X break;
X while (p2 = index(addr, '@'))
X *p2++ = '%'; /* '@' has too high precedence for uucp paths */
X /* don't prepend the sender's path unless required */
X if (pre_len && strncmp(addr, pre_path, pre_len))
X b += Strcpy(b, pre_path);
X b += Strcpy(b, addr);
X if (*name)
X b += strlen(sprintf(b, " (%s)", name));
X while (*p == ',' || isspace(*p))
X p++;
X if (*p)
X b += Strcpy(b, ", ");
X } while (*(str = p));
X for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X improve_uucp_paths(buf);
X if (c == 0)
X (void) strcpy(to, buf);
X else
X (void) strcpy(cc, buf);
X }
X}
X
X/*
X * pass a string describing header like, "Subject: ", current value, and
X * whether or not to prompt for it or to just post the information.
X * If do_prompt is true, "type in" the current value so user can either
X * modify it, erase it, or add to it.
X */
char *
set_header(str, curstr, do_prompt)
register char *str, *curstr;
X{
X static char buf[BUFSIZ];
X int offset = 0;
X register char *p = curstr;
X
X buf[0] = 0;
X wprint(str);
X fflush(stdout); /* force str curstr */
X if (do_prompt) {
X if (curstr)
X for (p = curstr; *p; p++)
X#ifdef SUNTOOL
X if (istool)
X rite(*p); /* mimics typing for the tool */
X else
X#endif /* SUNTOOL */
X if (isoff(glob_flags, ECHO_FLAG))
X fputc((buf[offset++] = *p), stdout);
X else
X#ifdef TIOCSTI
X if (ioctl(0, TIOCSTI, p) == -1) {
X error("ioctl: TIOCSTI");
X wprint("You must retype the entire line.\n%s", str);
X break;
X }
X#else
X {
X wprint("WARNING: -e flag! Type the line over.\n%s", str);
X break;
X }
X#endif /* TIOCSTI */
X
X if (istool)
X return NULL;
X /* simulate the fact that we're getting input for the letter even tho
X * we may not be. set_header is called before IS_GETTING is true,
X * but if we set it to true temporarily, then signals will return to
X * the right place (stop/continue).
X */
X {
X u_long getting = ison(glob_flags, IS_GETTING);
X if (!getting)
X turnon(glob_flags, IS_GETTING);
X if (Getstr(buf, sizeof(buf), offset) == -1)
X buf[0] = 0;
X if (!getting)
X turnoff(glob_flags, IS_GETTING);
X }
X } else
X puts(strcpy(buf, curstr));
X if (debug > 1)
X print("returning (%s) from set_header\n", buf);
X return buf;
X}
X
X/*
X * improve uucp paths by looking at the name of each host listed in the
X * path given.
X * sun!island!pixar!island!argv
X * It's a legal address, but redundant. Also, if we know we talk to particular
X * hosts via uucp, then we can just start with that host and disregard the path
X * preceding it. So, first get the known hosts and save them. Then start
X * at the end of the original path (at the last ! found), and move backwards
X * saving each hostname. If we get to a host that we know about, stop there
X * and use that address. If we get to a host we've already seen, then
X * delete it and all the hosts since then until the first occurance of that
X * hostname. When we get to the beginning, the address will be complete.
X *
X * Return all results into the original buffer passed to us. Since we can
X * at worst not touch the path (shorten it if anything), we know we're not
X * going to overrun the buffer.
X */
improve_uucp_paths(original)
register char *original;
X{
X char *hostnames[128];
X char name[BUFSIZ], addr[BUFSIZ], buf[BUFSIZ], *knowns, *end;
X register char *p, *recipient, *start = original, *b = buf;
X int saved_hosts, i;
X
X if (!original || !*original)
X return;
X
X knowns = do_set(set_options, "known_hosts");
X
X while (end = get_name_n_addr(start, name, addr)) {
X saved_hosts = 0;
X /* no uucp path, just user name [@host] with optional name attached */
X if (!(p = rindex(addr, '!'))) {
X char c = *end;
X *end = 0;
X b += Strcpy(b, start); /* copy the entire address with comments */
X *end = c;
X recipient = NULL;
X } else {
X recipient = p+1;
X while (p > addr) {
X /* null the '!' separating the rest of the path from the part
X * of the path preceding it and move p back to the previous
X * '!' (or beginning to addr) for hostname to point to.
X */
X for (*p-- = 0; p > addr && *p != '!'; p--)
X ;
X /* if p is not at the addr, move it forward past the '!' */
X if (p != addr)
X ++p; /* now points to a null terminated hostname */
X#ifndef SYSV
X /* if host is ourselves, ignore this and preceding hosts */
X for (i = 0; i < MAX_HOST_NAMES && ourname[i]; i++)
X if (!lcase_strcmp(p, ourname[i]))
X break;
X if (i < MAX_HOST_NAMES && ourname[i])
X break; /* our own host is not included in the path */
X#endif /* SYSV */
X /* check already saved hostnames. If host is one of them,
X * delete remaining hostnames since there is a redundant path.
X */
X for (i = 0; i < saved_hosts; i++)
X if (!lcase_strcmp(hostnames[i], p))
X saved_hosts = i;
X
X hostnames[saved_hosts++] = p;
X /* If we know that we call this host, break */
X if (p == addr || knowns && chk_two_lists(p, knowns, " ,\t"))
X break;
X --p; /* move p back onto the '!'; it will not equal addr */
X }
X while (saved_hosts-- > 0) {
X b += Strcpy(b, hostnames[saved_hosts]);
X *b++ = '!';
X }
X if (recipient)
X b += Strcpy(b, recipient);
X if (*name)
X b += strlen(sprintf(b, " (%s)", name));
X }
X for (start = end; *start == ',' || isspace(*start); start++)
X ;
X if (!*start)
X break;
X b += Strcpy(b, ", ");
X }
X (void) strcpy(original, buf);
X}
X
X/*
X * rm_cmts_in_addr() removes the comment lines in addresses that result from
X * sendmail or other mailers which append the user's "real name" on the
X * from lines. See get_name_n_addr().
X */
rm_cmts_in_addr(str)
register char *str;
X{
X char addr[BUFSIZ], buf[BUFSIZ], *start = str;
X register char *b = buf;
X
X *b = 0;
X do {
X if (!(str = get_name_n_addr(str, NULL, addr)))
X break;
X b += Strcpy(b, addr);
X while (*str == ',' || isspace(*str))
X str++;
X if (*str)
X b += Strcpy(b, ", ");
X } while (*str);
X for (b--; b > start && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X (void) strcpy(start, buf);
X}
X
X/*
X * take_me_off() is intended to search for the user's login name in an
X * address string and remove it. Note that string should be legal addresses
X * only -- no (comments) allowed here. See rm_cmts_in_addr().
X * If "metoo" is set, don't touch addr. This implementation is very bug prone
X * because the user's name may be a hostname or the path specified may
X * be incomplete.
X */
take_me_off(str)
char *str;
X{
X int i = 0, rm_me;
X char addr[BUFSIZ], buf[BUFSIZ], *start = str;
X char *Alts;
X register char *p, *b = buf;
X
X if (!str || !*str || do_set(set_options, "metoo"))
X return;
X
X Alts = do_set(set_options, "alternates");
X
X *b = 0;
X do {
X if (!(p = get_name_n_addr(str, NULL, addr)))
X break;
X rm_me = FALSE;
X /* see if user's login is in the address */
X if (!strcmp(login, addr))
X rm_me = TRUE;
X /* if Alts is not set and the above strcmp failed, don't remove him */
X else if (*addr && Alts && *Alts && chk_two_lists(login,addr, "!@%=")) {
X /* To be in this block, there must be a remote address */
X i = 0; /* initialize 'i' in case while loop is skipped */
X#ifndef SYSV
X /* see if the hostnames match our hostname. */
X while (i < MAX_HOST_NAMES && ourname[i])
X if (chk_two_lists(addr, ourname[i++], "!@%="))
X break;
X#endif /* SYSV */
X /* If one of the hostnames in the address is one of user's
X * hostnames, remove this address. If the alternates
X * hostnames listed contains a hostname in the address, remove
X * from the list.
X */
X if (
X#ifndef SYSV
X i < MAX_HOST_NAMES && ourname[i] ||
X#endif /* SYSV */
X *Alts == '*' || !chk_two_lists(addr, Alts, "!@%= \t,"))
X rm_me = TRUE;
X }
X if (!rm_me) {
X char c = *p;
X *p = 0;
X b += Strcpy(b, str);
X *p = c;
X }
X while (*p == ',' || isspace(*p))
X p++;
X if (*p && !rm_me)
X b += Strcpy(b, ", ");
X } while (*(str = p));
X for (b--; b > start && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X (void) strcpy(start, buf);
X}
X
X/*
X * Place commas in between all addresses that don't already have
X * them. Addresses which use comments which are in parens or _not_
X * within angle brackets *must* already have commas around them or
X * you can't determine what is a comment and what is an address.
X */
fix_up_addr(str)
register char *str;
X{
X char buf[BUFSIZ], *start = str, c;
X register char *p, *b = buf;
X
X *b = 0;
X do {
X /* get_name returns a pointer to the next address */
X if (!(p = get_name_n_addr(str, NULL, NULL)))
X break;
X c = *p, *p = 0;
X if (strlen(str) + (b - buf) >= BUFSIZ - 2) {
X /* print("Address too long! Lost address: \"%s\"\n", str); */
X *p = c;
X break;
X }
X for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
X *b = 0;
X for (*p = c; *p == ',' || isspace(*p); p++)
X ;
X if (*p)
X b += Strcpy(b, ", ");
X } while (*(str = p));
X for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X (void) strcpy(start, buf);
X}
X
X/*
X * Get address and name from a string (str) which came from an address header
X * in a message or typed by the user. The string may contain one or more
X * well-formed addresses. Each must be separated by a comma.
X *
X * address, address, address
X * address (comment or name here)
X * comment or name <address>
X * "Comment, even those with comma's!" <address>
X * address (comma, (more parens), etc...)
X *
X * This does *not* handle cases like:
X * comment <address (comment)>
X *
X * find the *first* address here and return a pointer to the end of the
X * address (usually a comma). Return NULL on error: non-matching parens,
X * brackets, quotes...
X */
char *
get_name_n_addr(str, name, addr)
register char *str, *name, *addr;
X{
X register char *p, *p2, *beg_addr = addr, *beg_name = name, c;
X
X if (addr)
X *addr = 0;
X if (name)
X *name = 0;
X if (!str || !*str)
X return NULL;
X
X /* first check to see if there's something to look for */
X if (!(p = any(str, ",(<\""))) {
X /* no comma or indication of a quote character. Find a space and
X * return that. If nothing, the entire string is a complete address
X */
X if (p = any(str, " \t"))
X c = *p, *p = 0;
X if (addr)
X (void) strcpy(addr, str);
X if (p)
X *p = c;
X return p? p : str + strlen(str);
X }
X
X /* comma terminated before any comment stuff. If so, check for whitespace
X * before-hand cuz it's possible that strings aren't comma separated yet
X * and they need to be.
X *
X * address address address, address
X * ^p <- p points here.
X * ^p2 <- should point here.
X */
X if (*p == ',') {
X c = *p, *p = 0;
X if (p2 = any(str, " \t"))
X *p = ',', c = *p2, p = p2;
X if (addr)
X (void) strcpy(addr, str);
X *p = c;
X return p;
X }
X
X /* starting to get hairy -- we found an angle bracket. This means that
X * everything outside of those brackets are comments until we find that
X * all important comma. A comment AFTER the <addr> :
X * <address> John Doe
X * can't call this function recursively or it'll think that "John Doe"
X * is a string with two legal address on it (each name being an address).
X */
X if (*p == '<') { /* note that "str" stil points to comment stuff! */
X if (name && *str) {
X *p = 0;
X name += Strcpy(name, str);
X *p = '<';
X }
X if (!(p2 = index(p+1, '>'))) {
X wprint("Warning! Malformed address: \"%s\"\n", str);
X return NULL;
X }
X if (addr) {
X /* to support <addr (comment)> style addresses, add code here */
X *p2 = 0;
X skipspaces(1);
X addr += Strcpy(addr, p);
X while (addr > beg_addr && isspace(*(addr-1)))
X *--addr = 0;
X *p2 = '>';
X }
X /* take care of the case "... <addr> com (ment)" */
X {
X int p_cnt = 0; /* parenthesis counter */
X p = p2;
X /* don't recurse yet -- scan till null, comma or '<'(add to name) */
X for (p = p2; p[1] && (p_cnt || p[1] != ',' && p[1] != '<'); p++) {
X if (p[1] == '(')
X p_cnt++;
X else if (p[1] == ')')
X p_cnt--;
X if (name)
X *name++ = p[1];
X }
X if (p_cnt) {
X wprint("Warning! Malformed name: \"%s\"\n", name);
X return NULL;
X }
X }
X if (name && name > beg_name) {
X while (isspace(*(name-1)))
X --name;
X *name = 0;
X }
X }
X
X /* this is the worst -- now we have parentheses/quotes. These guys can
X * recurse pretty badly and contain commas within them.
X */
X if (*p == '(' || *p == '"') {
X char *start = p;
X int comment = 1;
X c = *p;
X /* "str" points to address while p points to comments */
X if (addr && *str) {
X *p = 0;
X while (isspace(*str))
X str++;
X addr += Strcpy(addr, str);
X while (addr > beg_addr && isspace(*(addr-1)))
X *--addr = 0;
X *p = c;
X }
X while (comment) {
X if (c == '"' && !(p = index(p+1, '"')) ||
X c == '(' && !(p = any(p+1, "()"))) {
X wprint("Warning! Malformed address: \"%s\"\n", str);
X return NULL;
X }
X if (*p == '(') /* loop again on parenthesis. quote ends loop */
X comment++;
X else
X comment--;
X }
X /* Something like ``Comment (Comment) <addr>''. In this case
X * the name should include both comment parts with the
X * parenthesis. We have to redo addr.
X */
X if ((p2 = any(p+1, "<,")) && *p2 == '<') {
X if (!(p = index(p2, '>'))) {
X wprint("Warning! Malformed address: \"%s\"\n", str);
X return NULL;
X }
X if (addr = beg_addr) { /* reassign addr and compare to null */
X c = *p; *p = 0;
X addr += Strcpy(addr, p2+1);
X while (addr > beg_addr && isspace(*(addr-1)))
X *--addr = 0;
X *p = c;
X }
X if (name) {
X c = *p2; *p2 = 0;
X name += Strcpy(name, str);
X while (name > beg_name && isspace(*(name-1)))
X *--name = 0;
X *p2 = c;
X }
X } else if (name && start[1]) {
X c = *p, *p = 0; /* c may be ')' instead of '(' now */
X name += Strcpy(name, start+1);
X while (name > beg_name && isspace(*(name-1)))
X *--name = 0;
X *p = c;
X }
X }
X skipspaces(1);
X /* this is so common, save time by returning now */
X if (!*p || *p == ',')
X return p;
X return get_name_n_addr(p, name, addr);
X}
END_OF_FILE
if test 30675 -ne `wc -c <'hdrs.c'`; then
echo shar: \"'hdrs.c'\" unpacked with wrong size!
fi
# end of 'hdrs.c'
fi
echo shar: End of archive 10 \(of 14\).
cp /dev/null ark10isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 14 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
--
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
More information about the Comp.sources.unix
mailing list