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.