[comp.sources.unix] v14i042: Mail User's Shell, version 6.0, Part10/14

rsalz@bbn.com (Rich Salz) (04/15/88)

Submitted-by: island!argv@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@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@uunet.uu.net.