rsalz@bbn.com (Rich Salz) (03/18/89)
Submitted-by: Dan Heller <island!argv@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@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@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@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@somewhere> (This is a comment) X * ^unscramble_addr return points here X * X * It also does not deal well with malformed <addresses> -- X * <whoever@somewhere,nowhere> X * ^unscramble_addr return points here X * X * In each of the above cases, the string "whoever@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,@any.other.place:sys2!user%sys3@sys1 X * parses to X * sys2!user%sys3@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@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,@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@uunet.uu.net.