v18i037: Mail user's shell version 6.4, Part15/19
Rich Salz
rsalz at bbn.com
Sat Mar 18 04:37:14 AEST 1989
Submitted-by: Dan Heller <island!argv at sun.com>
Posting-number: Volume 18, Issue 37
Archive-name: mush6.4/part15
#! /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 15 (of 19)."
# Contents: addrs.c
# Wrapped by rsalz at papaya.bbn.com on Mon Mar 13 19:25:21 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'addrs.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'addrs.c'\"
else
echo shar: Extracting \"'addrs.c'\" \(31712 characters\)
sed "s/^X//" >'addrs.c' <<'END_OF_FILE'
X/* addrs.c -- copyright (c) Dan Heller 1/25/1989 */
X
X#include "mush.h"
X
X/*
X * Check to see if all addressees in list1 is in list2.
X * The lists must be as clean as the driven snow (no comments, aliases
X * must have been expanded, all are separated by commas or whitespace.
X *
X * "user" matches "user" and "user at localhost"
X * "*user" matches "user" at any address whatsoever."
X * !host matches any user destined for the specified host.
X * !some!path is the same, but can be more specifiec in the path.
X * @dom.ain can match any user destined for any host within the domain.
X * @berkeley.edu would match: dheller at cory.berkeley.edu
X */
Xcompare_addrs(list1, list2, ret_buf)
Xchar *list1, *list2, ret_buf[];
X{
X register char *p;
X char **addrv, **listv, buf[256]; /* addrs aren't long */
X int addrc, listc, a, l, h, ret_val;
X
X /* autosign2 list contains non-comment addresses */
X listv = mk_argv(list1, &listc, FALSE);
X addrv = mk_argv(list2, &addrc, FALSE);
X
X /* loop thru both lists and convert addresses to !-format
X * then remove ourhost names so "user" matches "user!local"
X * also remove possible trailing commas (from list).
X */
X for (a = 0; a < addrc; a++) {
X if (a != addrc-1 && (p = index(addrv[a], ',')) && !p[1])
X *p = 0;
X if (addrv[a][0] == '!' || addrv[a][0] == '@')
X continue;
X (void) bang_form(buf, addrv[a]);
X if (strcmp(addrv[a], buf)) /* if they differ... */
X (void) strcpy(addrv[a], buf); /* save new version */
X }
X for (l = 0; l < listc; l++) {
X if (l != listc-1 && (p = index(listv[l], ',')) && !p[1])
X *p = 0;
X if (listv[l][0] == '!' || listv[l][0] == '@')
X continue;
X (void) bang_form(buf, listv[l]);
X if (strcmp(listv[l], buf)) /* if they differ... */
X (void) strdup(listv[l], buf); /* save new version */
X }
X
X Debug("\nlist1 = "), print_argv(listv);
X Debug("list2 = "), print_argv(addrv), putchar('\n');
X
X /* loop thru each list comparing each element with the
X * other, if necessary.
X */
X for (l = 0; l < listc; l++) {
X ret_val = 0;
X /* check if local recipient with was specified. */
X if (!(p = rindex(listv[l], '!')))
X for (a = 0; a < addrc; a++) {
X /* we have a local user so far. If addrv[] is
X * not remote, then strcmp() immediately.
X * Note that "!" with no host indicates *all*
X * local users!!!
X */
X if (addrv[a][0] != '!') {
X if (!lcase_strncmp(addrv[a], listv[l], -1) || !addrv[a][1])
X ret_val = 1;
X } else if (addrv[a][0] == '*') {
X /* "*user" == "user" or "*" == login */
X if (!addrv[a][1] && !lcase_strncmp(listv[l], login) ||
X !lcase_strncmp(listv[l], addrv[a]+1, -1))
X ret_val = 1;
X } else for (h = 0; ourname && ourname[h]; h++)
X if (!lcase_strncmp(addrv[a]+1,
X ourname[h], -1)) {
X ret_val = 1;
X break;
X }
X if (ret_val)
X break;
X }
X /* else this is a remote user */
X else {
X /* check all the addresses for @dom.ain stuff or
X * !path!name type stuff only.
X */
X /* first back up p to the previous '!' */
X char *start, *user = p + 1;
X while (p-1 >= listv[l] && *--p != '!')
X ;
X start = p; /* Where to start for _domain_ addrs */
X for (a = 0; a < addrc; a++) {
X int len;
X char *path;
X
X /* first check the cases of address unmodified by @ and !
X * or check to see if *user is specified.
X */
X if (addrv[a][0] != '@' && addrv[a][0] != '!') {
X if (addrv[a][0] == '*') {
X /* we saved the username at "user" declaration. */
X /* if "*" is by itself, check against user's login */
X if (!addrv[a][1] && !lcase_strncmp(user, login, -1) ||
X addrv[a][1] && !lcase_strncmp(user, addrv[a]+1,-1)){
X ret_val = 1;
X break;
X }
X } else if (!lcase_strncmp(addrv[a], listv[l], -1)) {
X ret_val = 1;
X break;
X }
X continue;
X }
X path = addrv[a]+1;
X while (addrv[a][1] == '@' && *path == '.')
X path++;
X if ((len = strlen(path)) == 0)
X continue; /* localhost stuff only -- can't match */
X /* first check against specified domains */
X if (addrv[a][0] == '@') {
X for (p = start; p; (p = index(p, '.')) && ++p)
X if (!lcase_strncmp(p, path, len) &&
X (p[len] == '.' || p[len] == 0 || p[len] == '!')) {
X ret_val = 1;
X break;
X }
X } else if (addrv[a][0] == '!') {
X /* for !path style, start at head of addr */
X for (p = listv[l]; p; (p = index(p, '!')) && ++p)
X if (!lcase_strncmp(p, path, len) &&
X (p[len] == '!' || p[len] == 0)) {
X ret_val = 1;
X break;
X }
X }
X /* If address is in autosign2, goto next addr */
X if (ret_val)
X break;
X }
X }
X if (!ret_val) {
X /* this address isn't in autosign2 list */
X if (ret_buf)
X (void) strcpy(ret_buf, listv[l]);
X break;
X }
X }
X free_vec(listv);
X free_vec(addrv);
X
X return ret_val;
X}
X
X/*
X * Parser for stupidly-formed RFC822 addresses. It has been tested on
X * several bizzare cases as well as the normal stuff and uucp paths. It
X * takes a string which is a bunch of addresses and unscrambles the first
X * one in the string. It returns a pointer to the first char past what it
X * unscrambled and copies the unscrambled address to its second argument.
X *
X * It does NOT deal with trailing (comment) strings --
X * <whoever at somewhere> (This is a comment)
X * ^unscramble_addr return points here
X *
X * It also does not deal well with malformed <addresses> --
X * <whoever at somewhere,nowhere>
X * ^unscramble_addr return points here
X *
X * In each of the above cases, the string "whoever at somewhere" is copied
X * to the second argument.
X *
X * Nothing is done to un-<>ed route-less RFC822/976 addresses, nor to
X * uucp paths, nor to mixed-mode addresses not containing a route.
X * Hopelessly scrambled addresses are not handled brilliantly --
X * @some.dumb.place, at any.other.place:sys2!user%sys3 at sys1
X * parses to
X * sys2!user%sys3 at sys1
X * i.e., the route is simply dropped.
X */
Xchar *
Xunscramble_addr(addr, naddr)
Xchar *addr;
Xchar *naddr;
X{
X char *i, *r, *at;
X char s[BUFSIZ], t[BUFSIZ];
X int anglebrace = 0;
X
X /* Make a copy of the address so we can mangle it freely. */
X if (addr && *addr) {
X /* Skip any leading whitespace. */
X for (i = addr; *i && (r = any(i, " \t")) == i;)
X if (r) i = r + 1;
X if (*i == '\0')
X return NULL;
X /* Skip any leading double-quoted comment. */
X if (*i == '"') {
X if ((i = index(i + 1, '"')) && (*i == '\0' || *(++i) == '\0'))
X return NULL;
X }
X /* Skip any more whitespace. */
X for (; *i && (r = any(i, " \t")) == i;)
X if (r) i = r + 1;
X if (*i == '\0')
X return NULL;
X /* Check for angle braces around the address. */
X if (*i == '<') {
X if (*(++i) == '\0')
X return NULL;
X ++anglebrace;
X }
X /*
X * Look for a route. A route is a comma-separated set of @-tagged
X * domains terminated by a colon. Later versions might try to use
X * the route, but for now it confuses too many mailers.
X */
X if ((*i == '@') && (r = any(i, " \t:"))) {
X if (*r != ':')
X return NULL;
X if (*(r + 1) == '\0')
X return NULL;
X /*
X * Back up to the rightmost @-tagged domain
X * (see note below about unwinding)
X */
X *r = '\0';
X i = rindex(i, '@');
X *r = ':';
X }
X /* Remember how much we've skipped, and copy the rest. */
X at = i;
X (void) strcpy(t,i);
X /* Strip from a trailing angle brace, if present. */
X if (anglebrace) {
X if (r = any(t, "> \t")) {
X if (r == t || *r != '>')
X return NULL;
X else
X *r = '\0';
X --anglebrace;
X } else
X return NULL;
X }
X if (t[0] == '@') {
X /* Chop off any invalid stuff after the address. */
X if (r = any(index(t, ':'), " \t,(<"))
X *r = '\0';
X }
X } else
X return NULL;
X /* Remember where we are so we can return it. */
X at += strlen(t) + 1;
X /*
X * Unscramble the route, if present.
X * NOTE: We assume that a route is present in only two cases:
X * 1) addr was taken from the "From " line of a stupid mailer
X * 2) addr was a well-formed, <> enclosed RFC822 address
X */
X if (t[0] == '@') {
X if (r = index(t, ':'))
X r++;
X else
X return NULL;
X /* Delete the route if extraneous, otherwise unwind it. */
X if (i = index(r, '@'))
X (void) strcpy(s, r);
X else {
X /*
X * NOTE: Unwinding currently uses only the rightmost domain
X * in the route. This will break for mailers that need the
X * entire route. Complete unwinding would require the use
X * of % characters, which are avoided for other reasons.
X */
X (void) strcpy(s, r);
X *(--r) = '\0';
X (void) strcat(s, t);
X }
X } else
X (void) strcpy(s, t);
X /*
X * Ok, now the address should be in the form user at domain and
X * is held in buffer s (t[] is not copied directly to naddr
X * to allow future additional processing to be added here).
X */
X if (debug > 1) /* Don't dump this on trivial debugging */
X print("Converting \"%s\" to \"%s\"\n", addr, s);
X (void) strcpy(naddr, s);
X return at;
X}
X
X/*
X * Convert RFC822 or mixed addresses to RFC976 `!' form,
X * copying the new address to d. The source address is
X * translated according to RFC822 rules.
X * Return a pointer to the end (nul terminus) of d.
X */
Xchar *
Xbang_form (d, s)
Xchar *d, *s;
X{
X char *r, *t, *ab = NULL;
X
X *d = '\0';
X /* If nothing to do, quit now */
X if (!s || !*s) {
X return d;
X }
X /* Avoid any angle braces */
X if (*s == '<') {
X if (ab = index(s + 1, '>'))
X s++, *ab = '\0';
X else
X return NULL;
X }
X /*
X * Look backwards for the first `@'; this gives us the
X * primary domain of the RFC822 address
X */
X if ((t = rindex(s, '@')) && t != s) {
X /* Copy the RFC822 domain as the UUCP head */
X d += Strcpy(d, t + 1);
X *d++ = '!';
X *t = '\0';
X r = bang_form(d, s);
X *t = '@';
X } else if (*s == '@') {
X /* An RFC-822 "@domain1, at domain2:" routing */
X if (t = any(++s, ",:")) {
X char c = *t;
X *t = '\0';
X d += Strcpy(d, s);
X *d++ = '!';
X *t++ = c;
X r = bang_form(d, t);
X } else
X r = NULL;
X } else if (t = index(s, '!')) {
X /* A normal UUCP path */
X *t = '\0';
X d += Strcpy(d, s);
X *t++ = *d++ = '!';
X r = bang_form(d, t);
X } else if (t = rindex(s, '%')) {
X /* An imbedded `%' -- treat as low-priority `@' */
X *t = '@';
X r = bang_form(d, s);
X *t = '%';
X } else
X r = d + Strcpy(d, s); /* No `@', `!', or `%' */
X if (ab)
X *ab = '>';
X return r;
X}
X
X/*
X * Route addresses according to certain criteria. This function is really
X * just a front end for improve_uucp_paths() which does routing (differently).
X * If "route" is null, this routine is being called incorrectly.
X * If route is an address, just call improve_uucp_paths() and return.
X * If route is the null string, then route all addresses via the sender's
X * which is the first name/address on the To: list. 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 */
Xroute_addresses(to, cc, route_path)
Xchar *to, *cc, *route_path;
X{
X char pre_path[256], addr[256];
X register char *next, *p;
X int pre_len = 0;
X
X if (!route_path)
X return;
X if (*route_path) {
X improve_uucp_paths(to, HDRSIZ, route_path);
X improve_uucp_paths(cc, HDRSIZ, route_path);
X return;
X }
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, NULL, addr)))
X return;
X /* check to see if there is only one addr on To: line and no Cc: header */
X if (!*next && (!cc || !*cc))
X return;
X
X /* fix up the sender's address; improve_uucp_paths to optimize pre_path */
X improve_uucp_paths(addr, sizeof addr, NULL);
X
X if (p = rindex(addr, '!')) {
X *p = 0;
X pre_len = Strcpy(pre_path, addr); /* the uucp route he used */
X Debug("Routing thru \"%s\"\n", pre_path);
X }
X
X while (*next == ',' || isspace(*next))
X next++;
X improve_uucp_paths(next, HDRSIZ - (int)(next - to), pre_path);
X improve_uucp_paths(cc, HDRSIZ, pre_path);
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 */
Xchar *
Xset_header(str, curstr, do_prompt)
Xregister char *str, *curstr;
X{
X static char buf[HDRSIZ];
X int offset = 0;
X register char *p = curstr;
X
X if (!str)
X str = "";
X
X buf[0] = 0;
X wprint(str);
X fflush(stdout); /* force str curstr */
X if (do_prompt) {
X if (curstr)
X#ifdef SUNTOOL
X if (istool)
X for (p = curstr; *p; p++)
X rite(*p); /* mimics typing for the tool */
X else
X#endif /* SUNTOOL */
X if (isoff(glob_flags, ECHO_FLAG)) {
X Ungetstr(curstr);
X } else
X#ifdef TIOCSTI
X for (p = curstr; *p; p++)
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 /* !TIOCSTI */
X wprint("WARNING: -e flag! Type the line over.\n%s", str);
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 int wrapping = wrapcolumn;
X /* Funky trick here. If the prompt string is empty,
X * assume that we are allowed to do line wrap;
X * otherwise, temporarily disable line wrap
X */
X if (*str)
X wrapcolumn = 0;
X if (!getting)
X turnon(glob_flags, IS_GETTING);
X if (Getstr(buf, sizeof(buf), offset) == -1) {
X putchar('\n');
X buf[0] = 0;
X }
X if (!getting)
X turnoff(glob_flags, IS_GETTING);
X wrapcolumn = wrapping;
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 occurrence of that
X * hostname. When we get to the beginning, the address will be complete.
X * The route_path is prepended to each address to check make sure this path
X * is used if no known_hosts precede it in that address.
X *
X * Return all results into the original buffer passed to us. If route_path
X * adds to the length of all the paths, then the original buffer could be
X * overwritten. someone should check for this!
X */
Ximprove_uucp_paths(original, size, route_path)
Xchar *original, *route_path;
X{
X char name[256], addr[256], buf[2 * HDRSIZ], *end;
X char *hostnames[32], tmp[sizeof addr];
X register char *p, *p2, *recipient, *start = original, *b = buf;
X int saved_hosts, i;
X
X if (!original || !*original)
X return;
X
X while (end = get_name_n_addr(start, name, tmp)) {
X /* first copy the route path, then the rest of the address. */
X p = addr;
X if (route_path && *route_path) {
X p += Strcpy(addr, route_path);
X *p++ = '!';
X }
X (void) bang_form(p, tmp);
X saved_hosts = 0;
X if (p2 = rindex(addr, '!')) {
X recipient = p2+1;
X /* save the uucp-style address *without* route_path in tmp */
X (void) strcpy(tmp, p);
X for (p = p2; p > addr; p--) {
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 /* if host is ourselves, ignore this and preceding hosts */
X for (i = 0; ourname && ourname[i]; i++)
X if (!lcase_strncmp(p, ourname[i], -1))
X break;
X if (ourname && ourname[i])
X break;
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_strncmp(hostnames[i], p, -1))
X saved_hosts = i;
X
X hostnames[saved_hosts++] = p;
X if (p == addr)
X break;
X /* If we know that we call this host, break */
X for (i = 0; known_hosts && known_hosts[i]; i++)
X if (!lcase_strncmp(p, known_hosts[i], -1))
X break;
X }
X /* temporary holder for where we are in buffer (save address) */
X p2 = b;
X while (saved_hosts-- > 0) {
X b += Strcpy(b, hostnames[saved_hosts]);
X *b++ = '!';
X }
X b += Strcpy(b, recipient);
X if (!strcmp(p2, tmp)) { /* if the same, address was unmodified */
X b = p2; /* reset offset in buf (b) to where we were (p2) */
X goto unmodified;
X }
X if (*name)
X b += strlen(sprintf(b, " (%s)", name));
X } else {
X char c;
Xunmodified:
X c = *end;
X *end = 0;
X b += Strcpy(b, start); /* copy the entire address with comments */
X *end = c;
X }
X if (b - buf > size) {
X print("Warning: address list truncated!\n");
X /* Use a very poor heuristic to find the last complete address */
X for (b = buf+size - 1; *b != ','; b--)
X ;
X print("Lost addresses: %s%s\n", b, end); /* end = not yet parsed */
X while (isspace(*b) || *b == ',')
X b--;
X break;
X }
X for (start = end; *start == ',' || isspace(*start); start++)
X ;
X if (!*start)
X break;
X *b++ = ',', *b++ = ' ', *b = '\0';
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 */
Xrm_cmts_in_addr(str)
Xregister char *str;
X{
X char addr[BUFSIZ], buf[HDRSIZ], *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++ = ',', *b++ = ' ', *b = '\0';
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. If "metoo" is set, return without change.
X * determine which addresses are the "user'"'s addresses by comparing them
X * against the host/path names in alternates. If the "*" is used, then
X * this matches the address against the user's current login and -any- path.
X *
X * Note that the alternates list is an array of addresses stored *reversed*!
X */
Xtake_me_off(str)
Xchar *str;
X{
X int i = 0, rm_me;
X char tmp[256], addr[256], buf[HDRSIZ], *start = str;
X register char *p, *p2, *b = buf;
X
X if (!str || !*str || do_set(set_options, "metoo"))
X return;
X
X Debug("take_me_off()\n");
X *b = 0;
X do {
X /* get the first "addres" and advance p to next addres (ignore name) */
X if (!(p = get_name_n_addr(str, NULL, tmp)))
X break; /* we've reached the end of the address list */
X rm_me = FALSE;
X /* see if user's login is in the address */
X if (!strcmp(login, tmp))
X rm_me = TRUE;
X else {
X /* put address in !-format and store in "addr" */
X (void) bang_form(addr, tmp);
X (void) reverse(addr);
X for (i = 0; alternates && alternates[i] && !rm_me; i++) {
X if (alternates[i][0] == '*' && alternates[i][1] == '\0') {
X (void) strcpy(tmp+1, login), tmp[0] = '!';
X p2 = reverse(tmp);
X } else
X p2 = alternates[i];
X if (!lcase_strncmp(p2, addr, strlen(p2))) {
X Debug("\t%s\n", reverse(addr));
X rm_me = TRUE;
X }
X }
X for (i = 0; !rm_me && ourname && ourname[i]; i++) {
X p2 = tmp + Strcpy(tmp, ourname[i]);
X *p2++ = '!';
X (void) strcpy(p2, login);
X reverse(tmp);
X if (!lcase_strncmp(tmp, addr, strlen(tmp))) {
X Debug("\t%s\n", reverse(addr));
X rm_me = TRUE;
X }
X }
X }
X /* The address is not the user's -- put it into the returned list */
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++ = ',', *b++ = ' ', *b = '\0';
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 */
Xfix_up_addr(str)
Xchar *str;
X{
X char buf[HDRSIZ], *start = str;
X register char c, *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) >= sizeof(buf) - 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++ = ',', *b++ = ' ', *b = '\0';
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 * Remove redundant addresses.
X * Assume improve_uucp_paths, fix_up_addr or whatever have already been called.
X */
Xrm_redundant_addrs(to, cc)
Xchar *to, *cc;
X{
X char tmp[256], addr[256], buf[HDRSIZ];
X char **list; /* a list of addresses for comparison */
X int list_cnt = 0, l;
X register char c, *p, *b = buf, *start = to;
X extern char *calloc();
X
X Debug("rm_redundant_addrs()\n");
X list = (char **) calloc(256, sizeof(char *));
X /* first do the To header */
X do {
X /* get_name returns a pointer to the next address */
X if (!(p = get_name_n_addr(to, NULL, tmp)))
X break;
X c = *p, *p = 0;
X (void) bang_form(addr, tmp);
X for (l = 0; l < list_cnt; l++)
X if (!lcase_strncmp(addr, list[l], -1))
X break;
X /* if l == list_cnt, we got a new address, store it and add to buf */
X if (l == list_cnt) {
X /* Don't overwrite buffer. */
X if (list_cnt < 256)
X list[list_cnt++] = savestr(addr);
X if (b > buf)
X *b++ = ',', *b++ = ' ', *b = '\0';
X for (b += Strcpy(b, to); b > buf && isspace(*(b-1)); b--)
X *b = 0;
X } else
X Debug("\t%s\n", tmp); /* already specified (removed from list) */
X for (*p = c; *p == ',' || isspace(*p); p++)
X ;
X } while (*(to = p));
X for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X (void) strcpy(start, buf);
X b = buf, *b = 0;
X /* Now do the Cc header. If addr is listed in the To field, rm it in cc */
X start = cc;
X do {
X /* get_name returns a pointer to the next address */
X if (!(p = get_name_n_addr(cc, NULL, tmp)))
X break;
X c = *p, *p = 0;
X (void) bang_form(addr, tmp);
X for (l = 0; l < list_cnt; l++)
X if (!lcase_strncmp(addr, list[l], -1))
X break;
X if (l == list_cnt) {
X /* Don't overwrite buffer. */
X if (list_cnt < sizeof(list)/sizeof(char *))
X list[list_cnt++] = savestr(addr);
X if (b > buf)
X *b++ = ',', *b++ = ' ', *b = '\0';
X for (b += Strcpy(b, cc); b > buf && isspace(*(b-1)); b--)
X *b = 0;
X } else
X Debug("\t%s\n", tmp); /* already specified (removed from list) */
X for (*p = c; *p == ',' || isspace(*p); p++)
X ;
X } while (*(cc = p));
X list[list_cnt] = NULL; /* for free_vec */
X free_vec(list);
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 */
Xchar *
Xget_name_n_addr(str, name, addr)
Xregister 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 while (isspace(*str))
X str++;
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" still 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}
X
X/* takes string 's' which can be a name or list of names separated by
X * commas and checks to see if each is aliased to something else.
X * return address of the static buf.
X */
Xchar *
Xalias_to_address(s)
Xregister char *s;
X{
X static char buf[HDRSIZ];
X register char *p, *p2, *tmp;
X char newbuf[HDRSIZ], c;
X static int recursive;
X
X if (!aliases)
X return strcpy(buf, s);
X if (!s || !*s) {
X print("No recipients!?!\n");
X return NULL;
X }
X if (!recursive) {
X bzero(buf, sizeof buf);
X p2 = buf; /* if we're starting all this, p2 starts at &buf[0] */
X } else
X p2 = buf+strlen(buf); /* else, pick up where we left off */
X
X if (++recursive == 30) {
X print("alias references too many addresses!\n");
X recursive = 0;
X return NULL;
X }
X do {
X char addr[256];
X if (!(p = get_name_n_addr(s, NULL, addr)))
X break;
X c = *p, *p = 0;
X
X /* On recursive calls, compare against the entire
X * previous expansion, not just the address part.
X */
X if (recursive > 1)
X (void) strcpy(addr, s);
X
X /* if this is an alias, recurse this routine to expand it out */
X if ((tmp = do_set(aliases, addr)) && *tmp) {
X if (!alias_to_address(strcpy(newbuf, tmp))) {
X *p = c;
X return NULL;
X } else
X p2 = buf+strlen(buf);
X /* Now, make sure the buffer doesn't overflow */
X } else if (strlen(s) + (p2-buf) + 2 > sizeof buf) { /* add ", " */
X print("address length too long.\n");
X recursive = 0;
X *p = c;
X return NULL;
X } else {
X /* append the new alias (or unchanged address) onto the buffer */
X p2 += Strcpy(p2, s);
X *p2++ = ',', *p2++ = ' ', *p2 = '\0';
X }
X for (*p = c; *p == ',' || isspace(*p); p++)
X ;
X } while (*(s = p));
X if (recursive)
X recursive--;
X if (!recursive)
X *(p2-2) = 0; /* get rid of last ", " if end of recursion */
X return buf;
X}
X
X/*
X * Wrap addresses so that the headers don't exceed n chars (typically 80).
X */
Xchar *
Xwrap_addrs(str, n)
Xchar *str;
X{
X char buf[HDRSIZ * 2], *start = str;
X register char *b = buf, *p, c, *line_start = 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 (b > buf) {
X *b++ = ',', *b++ = ' ', *b = '\0';
X if (b - line_start + strlen(str) + 8 /* \t = 8 */ >= n)
X *b++ = '\n', *b++ = '\t', line_start = b;
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 } while (*(str = p));
X for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
X *b = 0;
X return strcpy(start, buf);
X}
END_OF_FILE
if test 31712 -ne `wc -c <'addrs.c'`; then
echo shar: \"'addrs.c'\" unpacked with wrong size!
fi
# end of 'addrs.c'
fi
echo shar: End of archive 15 \(of 19\).
cp /dev/null ark15isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 19 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
--
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
More information about the Comp.sources.unix
mailing list