122461Sdist /*
2*62083Sbostic * Copyright (c) 1980, 1993
3*62083Sbostic * The Regents of the University of California. All rights reserved.
433499Sbostic *
542741Sbostic * %sccs.include.redist.c%
622461Sdist */
722461Sdist
834905Sbostic #ifndef lint
9*62083Sbostic static char sccsid[] = "@(#)names.c 8.1 (Berkeley) 06/06/93";
1034905Sbostic #endif /* not lint */
111241Skas
121241Skas /*
131241Skas * Mail -- a mail program
141241Skas *
151241Skas * Handle name lists.
161241Skas */
171241Skas
181241Skas #include "rcv.h"
1954505Sbostic #include <fcntl.h>
2054505Sbostic #include "extern.h"
211241Skas
221241Skas /*
231241Skas * Allocate a single element of a name list,
241241Skas * initialize its name field to the passed
251241Skas * name and return it.
261241Skas */
271241Skas struct name *
nalloc(str,ntype)2834800Sedward nalloc(str, ntype)
291241Skas char str[];
3054505Sbostic int ntype;
311241Skas {
321241Skas register struct name *np;
331241Skas
341241Skas np = (struct name *) salloc(sizeof *np);
351241Skas np->n_flink = NIL;
361241Skas np->n_blink = NIL;
3734800Sedward np->n_type = ntype;
381241Skas np->n_name = savestr(str);
391241Skas return(np);
401241Skas }
411241Skas
421241Skas /*
431241Skas * Find the tail of a list and return it.
441241Skas */
451241Skas struct name *
tailof(name)461241Skas tailof(name)
471241Skas struct name *name;
481241Skas {
491241Skas register struct name *np;
501241Skas
511241Skas np = name;
521241Skas if (np == NIL)
531241Skas return(NIL);
541241Skas while (np->n_flink != NIL)
551241Skas np = np->n_flink;
561241Skas return(np);
571241Skas }
581241Skas
591241Skas /*
601241Skas * Extract a list of names from a line,
611241Skas * and make a list of names from it.
621241Skas * Return the list or NIL if none found.
631241Skas */
641241Skas struct name *
extract(line,ntype)651241Skas extract(line, ntype)
661241Skas char line[];
6754505Sbostic int ntype;
681241Skas {
691241Skas register char *cp;
701241Skas register struct name *top, *np, *t;
7134800Sedward char nbuf[BUFSIZ];
721241Skas
7334800Sedward if (line == NOSTR || *line == '\0')
7434800Sedward return NIL;
751241Skas top = NIL;
761241Skas np = NIL;
771241Skas cp = line;
781241Skas while ((cp = yankword(cp, nbuf)) != NOSTR) {
7934800Sedward t = nalloc(nbuf, ntype);
801241Skas if (top == NIL)
811241Skas top = t;
821241Skas else
831241Skas np->n_flink = t;
841241Skas t->n_blink = np;
851241Skas np = t;
861241Skas }
8734800Sedward return top;
881241Skas }
891241Skas
901241Skas /*
911241Skas * Turn a list of names into a string of the same names.
921241Skas */
931241Skas char *
detract(np,ntype)941241Skas detract(np, ntype)
951241Skas register struct name *np;
9654505Sbostic int ntype;
971241Skas {
981241Skas register int s;
991241Skas register char *cp, *top;
1001241Skas register struct name *p;
1011241Skas register int comma;
1021241Skas
1031241Skas comma = ntype & GCOMMA;
1041241Skas if (np == NIL)
1051241Skas return(NOSTR);
1061241Skas ntype &= ~GCOMMA;
1071241Skas s = 0;
1081241Skas if (debug && comma)
1091241Skas fprintf(stderr, "detract asked to insert commas\n");
1101241Skas for (p = np; p != NIL; p = p->n_flink) {
1111241Skas if (ntype && (p->n_type & GMASK) != ntype)
1121241Skas continue;
1131241Skas s += strlen(p->n_name) + 1;
1141241Skas if (comma)
1151241Skas s++;
1161241Skas }
1171241Skas if (s == 0)
1181241Skas return(NOSTR);
1191241Skas s += 2;
1201241Skas top = salloc(s);
1211241Skas cp = top;
1221241Skas for (p = np; p != NIL; p = p->n_flink) {
1231241Skas if (ntype && (p->n_type & GMASK) != ntype)
1241241Skas continue;
1251241Skas cp = copy(p->n_name, cp);
1261241Skas if (comma && p->n_flink != NIL)
1271241Skas *cp++ = ',';
1281241Skas *cp++ = ' ';
1291241Skas }
1301241Skas *--cp = 0;
1311241Skas if (comma && *--cp == ',')
1321241Skas *cp = 0;
1331241Skas return(top);
1341241Skas }
1351241Skas
1361241Skas /*
1371241Skas * Grab a single word (liberal word)
13834800Sedward * Throw away things between ()'s, and take anything between <>.
1391241Skas */
1401241Skas char *
yankword(ap,wbuf)1411241Skas yankword(ap, wbuf)
1421241Skas char *ap, wbuf[];
1431241Skas {
1441241Skas register char *cp, *cp2;
1451241Skas
14625459Sserge cp = ap;
14734800Sedward for (;;) {
14834800Sedward if (*cp == '\0')
14934800Sedward return NOSTR;
1501241Skas if (*cp == '(') {
15125459Sserge register int nesting = 0;
15225459Sserge
15325459Sserge while (*cp != '\0') {
15425459Sserge switch (*cp++) {
15525459Sserge case '(':
15625459Sserge nesting++;
15725459Sserge break;
15825459Sserge case ')':
15925459Sserge --nesting;
16025459Sserge break;
16125459Sserge }
16225459Sserge if (nesting <= 0)
16325459Sserge break;
16425459Sserge }
16534800Sedward } else if (*cp == ' ' || *cp == '\t' || *cp == ',')
16634800Sedward cp++;
16734800Sedward else
16834800Sedward break;
16934800Sedward }
17034800Sedward if (*cp == '<')
17134800Sedward for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
17234800Sedward ;
17334800Sedward else
17434987Sedward for (cp2 = wbuf; *cp && !index(" \t,(", *cp); *cp2++ = *cp++)
17534800Sedward ;
1761241Skas *cp2 = '\0';
17734800Sedward return cp;
1781241Skas }
1791241Skas
1801241Skas /*
1811241Skas * For each recipient in the passed name list with a /
1821241Skas * in the name, append the message to the end of the named file
1831241Skas * and remove him from the recipient list.
1841241Skas *
1851241Skas * Recipients whose name begins with | are piped through the given
1861241Skas * program and removed.
1871241Skas */
1881241Skas struct name *
outof(names,fo,hp)1891241Skas outof(names, fo, hp)
1901241Skas struct name *names;
1911241Skas FILE *fo;
1921241Skas struct header *hp;
1931241Skas {
1941241Skas register int c;
19531142Sedward register struct name *np, *top;
19631142Sedward time_t now, time();
19734976Sedward char *date, *fname, *ctime();
1981241Skas FILE *fout, *fin;
19931142Sedward int ispipe;
2001241Skas extern char tempEdit[];
2011241Skas
2021241Skas top = names;
2031241Skas np = names;
20431144Sedward (void) time(&now);
2051241Skas date = ctime(&now);
2061241Skas while (np != NIL) {
2074365Skurt if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
2081241Skas np = np->n_flink;
2091241Skas continue;
2101241Skas }
2111241Skas ispipe = np->n_name[0] == '|';
2121241Skas if (ispipe)
2131241Skas fname = np->n_name+1;
2141241Skas else
2151241Skas fname = expand(np->n_name);
2161241Skas
2171241Skas /*
2181241Skas * See if we have copied the complete message out yet.
2191241Skas * If not, do so.
2201241Skas */
2211241Skas
2221241Skas if (image < 0) {
22343865Sedward if ((fout = Fopen(tempEdit, "a")) == NULL) {
2241241Skas perror(tempEdit);
2251241Skas senderr++;
2261241Skas goto cant;
2271241Skas }
2281241Skas image = open(tempEdit, 2);
22931144Sedward (void) unlink(tempEdit);
2301241Skas if (image < 0) {
2311241Skas perror(tempEdit);
2321241Skas senderr++;
23343865Sedward (void) Fclose(fout);
2341241Skas goto cant;
2351241Skas }
23656291Sedward (void) fcntl(image, F_SETFD, 1);
23743865Sedward fprintf(fout, "From %s %s", myname, date);
23843865Sedward puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
23943865Sedward while ((c = getc(fo)) != EOF)
24043865Sedward (void) putc(c, fout);
24143865Sedward rewind(fo);
24243865Sedward (void) putc('\n', fout);
24343865Sedward (void) fflush(fout);
24443865Sedward if (ferror(fout))
24543865Sedward perror(tempEdit);
24643865Sedward (void) Fclose(fout);
2471241Skas }
2481241Skas
2491241Skas /*
2501241Skas * Now either copy "image" to the desired file
2511241Skas * or give it as the standard input to the desired
2521241Skas * program as appropriate.
2531241Skas */
2541241Skas
2551241Skas if (ispipe) {
25634976Sedward int pid;
25734976Sedward char *shell;
25834976Sedward
25943865Sedward /*
26043865Sedward * XXX
26143865Sedward * We can't really reuse the same image file,
26243865Sedward * because multiple piped recipients will
26343865Sedward * share the same lseek location and trample
26443865Sedward * on one another.
26543865Sedward */
26634963Sedward if ((shell = value("SHELL")) == NOSTR)
26737870Sbostic shell = _PATH_CSHELL;
26834976Sedward pid = start_command(shell, sigmask(SIGHUP)|
26934976Sedward sigmask(SIGINT)|sigmask(SIGQUIT),
27034976Sedward image, -1, "-c", fname, NOSTR);
27134976Sedward if (pid < 0) {
2721241Skas senderr++;
2731241Skas goto cant;
2741241Skas }
27534976Sedward free_child(pid);
27634963Sedward } else {
27743865Sedward int f;
27843865Sedward if ((fout = Fopen(fname, "a")) == NULL) {
2791241Skas perror(fname);
2801241Skas senderr++;
2811241Skas goto cant;
2821241Skas }
28343865Sedward if ((f = dup(image)) < 0) {
28443865Sedward perror("dup");
28543865Sedward fin = NULL;
28643865Sedward } else
28743865Sedward fin = Fdopen(f, "r");
2881241Skas if (fin == NULL) {
2891241Skas fprintf(stderr, "Can't reopen image\n");
29043865Sedward (void) Fclose(fout);
2911241Skas senderr++;
2921241Skas goto cant;
2931241Skas }
2941241Skas rewind(fin);
2951241Skas while ((c = getc(fin)) != EOF)
29631144Sedward (void) putc(c, fout);
2971241Skas if (ferror(fout))
2981241Skas senderr++, perror(fname);
29943865Sedward (void) Fclose(fout);
30043865Sedward (void) Fclose(fin);
3011241Skas }
3021241Skas cant:
3031241Skas /*
3041241Skas * In days of old we removed the entry from the
3051241Skas * the list; now for sake of header expansion
3061241Skas * we leave it in and mark it as deleted.
3071241Skas */
3081241Skas np->n_type |= GDEL;
3091241Skas np = np->n_flink;
3101241Skas }
3111241Skas if (image >= 0) {
31231144Sedward (void) close(image);
3131241Skas image = -1;
3141241Skas }
3151241Skas return(top);
3161241Skas }
3171241Skas
3181241Skas /*
3194365Skurt * Determine if the passed address is a local "send to file" address.
3204365Skurt * If any of the network metacharacters precedes any slashes, it can't
3214365Skurt * be a filename. We cheat with .'s to allow path names like ./...
3224365Skurt */
32354505Sbostic int
isfileaddr(name)3244365Skurt isfileaddr(name)
3254365Skurt char *name;
3264365Skurt {
3274365Skurt register char *cp;
3284365Skurt
3295967Skurt if (*name == '+')
33034800Sedward return 1;
3314365Skurt for (cp = name; *cp; cp++) {
33234800Sedward if (*cp == '!' || *cp == '%' || *cp == '@')
33334800Sedward return 0;
3344365Skurt if (*cp == '/')
33534800Sedward return 1;
3364365Skurt }
33734800Sedward return 0;
3384365Skurt }
3394365Skurt
3404365Skurt /*
3411241Skas * Map all of the aliased users in the invoker's mailrc
3421241Skas * file and insert them into the list.
3431241Skas * Changed after all these months of service to recursively
3441241Skas * expand names (2/14/80).
3451241Skas */
3461241Skas
3471241Skas struct name *
usermap(names)3481241Skas usermap(names)
3491241Skas struct name *names;
3501241Skas {
3511241Skas register struct name *new, *np, *cp;
3521241Skas struct grouphead *gh;
3531241Skas register int metoo;
3541241Skas
3551241Skas new = NIL;
3561241Skas np = names;
3571241Skas metoo = (value("metoo") != NOSTR);
3581241Skas while (np != NIL) {
3591241Skas if (np->n_name[0] == '\\') {
3601241Skas cp = np->n_flink;
3611241Skas new = put(new, np);
3621241Skas np = cp;
3631241Skas continue;
3641241Skas }
3651241Skas gh = findgroup(np->n_name);
3661241Skas cp = np->n_flink;
3671241Skas if (gh != NOGRP)
3681241Skas new = gexpand(new, gh, metoo, np->n_type);
3691241Skas else
3701241Skas new = put(new, np);
3711241Skas np = cp;
3721241Skas }
3731241Skas return(new);
3741241Skas }
3751241Skas
3761241Skas /*
3771241Skas * Recursively expand a group name. We limit the expansion to some
3781241Skas * fixed level to keep things from going haywire.
3791241Skas * Direct recursion is not expanded for convenience.
3801241Skas */
3811241Skas
3821241Skas struct name *
gexpand(nlist,gh,metoo,ntype)3831241Skas gexpand(nlist, gh, metoo, ntype)
3841241Skas struct name *nlist;
3851241Skas struct grouphead *gh;
38654505Sbostic int metoo, ntype;
3871241Skas {
3881241Skas struct group *gp;
3891241Skas struct grouphead *ngh;
3901241Skas struct name *np;
3911241Skas static int depth;
3921241Skas char *cp;
3931241Skas
3941241Skas if (depth > MAXEXP) {
3951241Skas printf("Expanding alias to depth larger than %d\n", MAXEXP);
3961241Skas return(nlist);
3971241Skas }
3981241Skas depth++;
3991241Skas for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
4001241Skas cp = gp->ge_name;
4011241Skas if (*cp == '\\')
4021241Skas goto quote;
4031241Skas if (strcmp(cp, gh->g_name) == 0)
4041241Skas goto quote;
4051241Skas if ((ngh = findgroup(cp)) != NOGRP) {
4061241Skas nlist = gexpand(nlist, ngh, metoo, ntype);
4071241Skas continue;
4081241Skas }
4091241Skas quote:
41034800Sedward np = nalloc(cp, ntype);
4111241Skas /*
4121241Skas * At this point should allow to expand
4131241Skas * to self if only person in group
4141241Skas */
4151241Skas if (gp == gh->g_list && gp->ge_link == NOGE)
4161241Skas goto skip;
4171241Skas if (!metoo && strcmp(cp, myname) == 0)
4181241Skas np->n_type |= GDEL;
4191241Skas skip:
4201241Skas nlist = put(nlist, np);
4211241Skas }
4221241Skas depth--;
4231241Skas return(nlist);
4241241Skas }
4251241Skas
4261241Skas /*
4271241Skas * Concatenate the two passed name lists, return the result.
4281241Skas */
4291241Skas struct name *
cat(n1,n2)4301241Skas cat(n1, n2)
4311241Skas struct name *n1, *n2;
4321241Skas {
4331241Skas register struct name *tail;
4341241Skas
4351241Skas if (n1 == NIL)
4361241Skas return(n2);
4371241Skas if (n2 == NIL)
4381241Skas return(n1);
4391241Skas tail = tailof(n1);
4401241Skas tail->n_flink = n2;
4411241Skas n2->n_blink = tail;
4421241Skas return(n1);
4431241Skas }
4441241Skas
4451241Skas /*
4461241Skas * Unpack the name list onto a vector of strings.
4471241Skas * Return an error if the name list won't fit.
4481241Skas */
4491241Skas char **
unpack(np)4501241Skas unpack(np)
4511241Skas struct name *np;
4521241Skas {
4531241Skas register char **ap, **top;
4541241Skas register struct name *n;
45510582Sleres int t, extra, metoo, verbose;
4561241Skas
4571241Skas n = np;
45834800Sedward if ((t = count(n)) == 0)
4591241Skas panic("No names to unpack");
4601241Skas /*
4611241Skas * Compute the number of extra arguments we will need.
4621241Skas * We need at least two extra -- one for "mail" and one for
4631241Skas * the terminating 0 pointer. Additional spots may be needed
46434783Sedward * to pass along -f to the host mailer.
4651241Skas */
4661241Skas extra = 2;
4671241Skas extra++;
4681241Skas metoo = value("metoo") != NOSTR;
4691241Skas if (metoo)
4701241Skas extra++;
47110582Sleres verbose = value("verbose") != NOSTR;
47210582Sleres if (verbose)
47310582Sleres extra++;
47431144Sedward top = (char **) salloc((t + extra) * sizeof *top);
4751241Skas ap = top;
4761241Skas *ap++ = "send-mail";
4771241Skas *ap++ = "-i";
4781241Skas if (metoo)
4791241Skas *ap++ = "-m";
48010582Sleres if (verbose)
48110582Sleres *ap++ = "-v";
48234976Sedward for (; n != NIL; n = n->n_flink)
48334976Sedward if ((n->n_type & GDEL) == 0)
48434976Sedward *ap++ = n->n_name;
4851241Skas *ap = NOSTR;
4861241Skas return(top);
4871241Skas }
4881241Skas
4891241Skas /*
4901241Skas * Remove all of the duplicates from the passed name list by
4911241Skas * insertion sorting them, then checking for dups.
4921241Skas * Return the head of the new list.
4931241Skas */
4941241Skas struct name *
elide(names)4951241Skas elide(names)
4961241Skas struct name *names;
4971241Skas {
4981241Skas register struct name *np, *t, *new;
4991241Skas struct name *x;
5001241Skas
5011241Skas if (names == NIL)
5021241Skas return(NIL);
5031241Skas new = names;
5041241Skas np = names;
5051241Skas np = np->n_flink;
5061241Skas if (np != NIL)
5071241Skas np->n_blink = NIL;
5081241Skas new->n_flink = NIL;
5091241Skas while (np != NIL) {
5101241Skas t = new;
51134987Sedward while (strcasecmp(t->n_name, np->n_name) < 0) {
5121241Skas if (t->n_flink == NIL)
5131241Skas break;
5141241Skas t = t->n_flink;
5151241Skas }
5161241Skas
5171241Skas /*
5181241Skas * If we ran out of t's, put the new entry after
5191241Skas * the current value of t.
5201241Skas */
5211241Skas
52234987Sedward if (strcasecmp(t->n_name, np->n_name) < 0) {
5231241Skas t->n_flink = np;
5241241Skas np->n_blink = t;
5251241Skas t = np;
5261241Skas np = np->n_flink;
5271241Skas t->n_flink = NIL;
5281241Skas continue;
5291241Skas }
5301241Skas
5311241Skas /*
5321241Skas * Otherwise, put the new entry in front of the
5331241Skas * current t. If at the front of the list,
5341241Skas * the new guy becomes the new head of the list.
5351241Skas */
5361241Skas
5371241Skas if (t == new) {
5381241Skas t = np;
5391241Skas np = np->n_flink;
5401241Skas t->n_flink = new;
5411241Skas new->n_blink = t;
5421241Skas t->n_blink = NIL;
5431241Skas new = t;
5441241Skas continue;
5451241Skas }
5461241Skas
5471241Skas /*
5481241Skas * The normal case -- we are inserting into the
5491241Skas * middle of the list.
5501241Skas */
5511241Skas
5521241Skas x = np;
5531241Skas np = np->n_flink;
5541241Skas x->n_flink = t;
5551241Skas x->n_blink = t->n_blink;
5561241Skas t->n_blink->n_flink = x;
5571241Skas t->n_blink = x;
5581241Skas }
5591241Skas
5601241Skas /*
5611241Skas * Now the list headed up by new is sorted.
5621241Skas * Go through it and remove duplicates.
5631241Skas */
5641241Skas
5651241Skas np = new;
5661241Skas while (np != NIL) {
5671241Skas t = np;
56834987Sedward while (t->n_flink != NIL &&
56934987Sedward strcasecmp(np->n_name, t->n_flink->n_name) == 0)
5701241Skas t = t->n_flink;
5711241Skas if (t == np || t == NIL) {
5721241Skas np = np->n_flink;
5731241Skas continue;
5741241Skas }
5751241Skas
5761241Skas /*
5771241Skas * Now t points to the last entry with the same name
5781241Skas * as np. Make np point beyond t.
5791241Skas */
5801241Skas
5811241Skas np->n_flink = t->n_flink;
5821241Skas if (t->n_flink != NIL)
5831241Skas t->n_flink->n_blink = np;
5841241Skas np = np->n_flink;
5851241Skas }
5861241Skas return(new);
5871241Skas }
5881241Skas
5891241Skas /*
5901241Skas * Put another node onto a list of names and return
5911241Skas * the list.
5921241Skas */
5931241Skas struct name *
put(list,node)5941241Skas put(list, node)
5951241Skas struct name *list, *node;
5961241Skas {
5971241Skas node->n_flink = list;
5981241Skas node->n_blink = NIL;
5991241Skas if (list != NIL)
6001241Skas list->n_blink = node;
6011241Skas return(node);
6021241Skas }
6031241Skas
6041241Skas /*
60534976Sedward * Determine the number of undeleted elements in
6061241Skas * a name list and return it.
6071241Skas */
60854505Sbostic int
count(np)6091241Skas count(np)
6101241Skas register struct name *np;
6111241Skas {
61234800Sedward register int c;
6131241Skas
61434976Sedward for (c = 0; np != NIL; np = np->n_flink)
61534976Sedward if ((np->n_type & GDEL) == 0)
61634976Sedward c++;
61734800Sedward return c;
6181241Skas }
6191241Skas
6201241Skas /*
62134987Sedward * Delete the given name from a namelist.
6221241Skas */
6231241Skas struct name *
delname(np,name)62434987Sedward delname(np, name)
6251241Skas register struct name *np;
6261241Skas char name[];
6271241Skas {
6281241Skas register struct name *p;
6291241Skas
6301241Skas for (p = np; p != NIL; p = p->n_flink)
63134987Sedward if (strcasecmp(p->n_name, name) == 0) {
6321241Skas if (p->n_blink == NIL) {
6331241Skas if (p->n_flink != NIL)
6341241Skas p->n_flink->n_blink = NIL;
6351241Skas np = p->n_flink;
6361241Skas continue;
6371241Skas }
6381241Skas if (p->n_flink == NIL) {
6391241Skas if (p->n_blink != NIL)
6401241Skas p->n_blink->n_flink = NIL;
6411241Skas continue;
6421241Skas }
6431241Skas p->n_blink->n_flink = p->n_flink;
6441241Skas p->n_flink->n_blink = p->n_blink;
6451241Skas }
64634987Sedward return np;
6471241Skas }
6481241Skas
6491241Skas /*
6501241Skas * Pretty print a name list
6511241Skas * Uncomment it if you need it.
6521241Skas */
6531241Skas
65431142Sedward /*
65554505Sbostic void
6561241Skas prettyprint(name)
6571241Skas struct name *name;
6581241Skas {
6591241Skas register struct name *np;
6601241Skas
6611241Skas np = name;
6621241Skas while (np != NIL) {
6631241Skas fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
6641241Skas np = np->n_flink;
6651241Skas }
6661241Skas fprintf(stderr, "\n");
6671241Skas }
66831142Sedward */
669