names.c revision 216564
1139749Simp/*
297883Sgibbs * Copyright (c) 1980, 1993
397883Sgibbs *	The Regents of the University of California.  All rights reserved.
4133122Sgibbs *
5102681Sgibbs * Redistribution and use in source and binary forms, with or without
697883Sgibbs * modification, are permitted provided that the following conditions
797883Sgibbs * are met:
897883Sgibbs * 1. Redistributions of source code must retain the above copyright
997883Sgibbs *    notice, this list of conditions and the following disclaimer.
1097883Sgibbs * 2. Redistributions in binary form must reproduce the above copyright
1197883Sgibbs *    notice, this list of conditions and the following disclaimer in the
1297883Sgibbs *    documentation and/or other materials provided with the distribution.
1397883Sgibbs * 4. Neither the name of the University nor the names of its contributors
1497883Sgibbs *    may be used to endorse or promote products derived from this software
1597883Sgibbs *    without specific prior written permission.
1697883Sgibbs *
1797883Sgibbs * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1897883Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1997883Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2097883Sgibbs * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2197883Sgibbs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2297883Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2397883Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2497883Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2597883Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2697883Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2797883Sgibbs * SUCH DAMAGE.
2897883Sgibbs */
2997883Sgibbs
3097883Sgibbs#ifndef lint
3197883Sgibbs#if 0
3297883Sgibbsstatic char sccsid[] = "@(#)names.c	8.1 (Berkeley) 6/6/93";
3397883Sgibbs#endif
3497883Sgibbs#endif /* not lint */
3597883Sgibbs#include <sys/cdefs.h>
3697883Sgibbs__FBSDID("$FreeBSD: head/usr.bin/mail/names.c 216564 2010-12-19 16:25:23Z charnier $");
3797883Sgibbs
3897883Sgibbs/*
3997883Sgibbs * Mail -- a mail program
4097883Sgibbs *
4197883Sgibbs * Handle name lists.
42129134Sgibbs */
4397883Sgibbs
4497883Sgibbs#include "rcv.h"
4597883Sgibbs#include <fcntl.h>
4697883Sgibbs#include "extern.h"
4797883Sgibbs
4897883Sgibbs/*
4997883Sgibbs * Allocate a single element of a name list,
5097883Sgibbs * initialize its name field to the passed
5197883Sgibbs * name and return it.
5297883Sgibbs */
5397883Sgibbsstruct name *
5497883Sgibbsnalloc(char str[], int ntype)
5597883Sgibbs{
5697883Sgibbs	struct name *np;
5797883Sgibbs
5897883Sgibbs	np = (struct name *)salloc(sizeof(*np));
59104023Sgibbs	np->n_flink = NULL;
60104023Sgibbs	np->n_blink = NULL;
61104023Sgibbs	np->n_type = ntype;
62104023Sgibbs	np->n_name = savestr(str);
63104023Sgibbs	return (np);
64104023Sgibbs}
65104023Sgibbs
66104023Sgibbs/*
6797883Sgibbs * Find the tail of a list and return it.
68107441Sscottl */
69107441Sscottlstruct name *
70107441Sscottltailof(struct name *name)
71107441Sscottl{
72107441Sscottl	struct name *np;
73107441Sscottl
74104023Sgibbs	np = name;
75107441Sscottl	if (np == NULL)
76107441Sscottl		return (NULL);
77107441Sscottl	while (np->n_flink != NULL)
78107441Sscottl		np = np->n_flink;
79107441Sscottl	return (np);
80107441Sscottl}
81107441Sscottl
8297883Sgibbs/*
8397883Sgibbs * Extract a list of names from a line,
8497883Sgibbs * and make a list of names from it.
8597883Sgibbs * Return the list or NULL if none found.
8697883Sgibbs */
8797883Sgibbsstruct name *
8897883Sgibbsextract(char line[], int ntype)
8997883Sgibbs{
9097883Sgibbs	char *cp, *nbuf;
91102681Sgibbs	struct name *top, *np, *t;
92102681Sgibbs
9397883Sgibbs	if (line == NULL || *line == '\0')
9497883Sgibbs		return (NULL);
9597883Sgibbs	if ((nbuf = malloc(strlen(line) + 1)) == NULL)
9697883Sgibbs		err(1, "Out of memory");
9797883Sgibbs	top = NULL;
9897883Sgibbs	np = NULL;
9997883Sgibbs	cp = line;
10097883Sgibbs	while ((cp = yankword(cp, nbuf)) != NULL) {
10197883Sgibbs		t = nalloc(nbuf, ntype);
10297883Sgibbs		if (top == NULL)
10397883Sgibbs			top = t;
10497883Sgibbs		else
105102681Sgibbs			np->n_flink = t;
106102681Sgibbs		t->n_blink = np;
107102681Sgibbs		np = t;
108102681Sgibbs	}
109102681Sgibbs	(void)free(nbuf);
110102681Sgibbs	return (top);
111102681Sgibbs}
112102681Sgibbs
11397883Sgibbs/*
11497883Sgibbs * Turn a list of names into a string of the same names.
11597883Sgibbs */
11697883Sgibbschar *
11797883Sgibbsdetract(struct name *np, int ntype)
11897883Sgibbs{
11997883Sgibbs	int s, comma;
12097883Sgibbs	char *cp, *top;
12197883Sgibbs	struct name *p;
122102681Sgibbs
123107441Sscottl	comma = ntype & GCOMMA;
124107441Sscottl	if (np == NULL)
125102681Sgibbs		return (NULL);
126102681Sgibbs	ntype &= ~GCOMMA;
127102681Sgibbs	s = 0;
128102681Sgibbs	if (debug && comma)
129102681Sgibbs		fprintf(stderr, "detract asked to insert commas\n");
13097883Sgibbs	for (p = np; p != NULL; p = p->n_flink) {
13197883Sgibbs		if (ntype && (p->n_type & GMASK) != ntype)
13297883Sgibbs			continue;
13397883Sgibbs		s += strlen(p->n_name) + 1;
13497883Sgibbs		if (comma)
13597883Sgibbs			s++;
136102681Sgibbs	}
13797883Sgibbs	if (s == 0)
13897883Sgibbs		return (NULL);
13997883Sgibbs	s += 2;
14097883Sgibbs	top = salloc(s);
14197883Sgibbs	cp = top;
14297883Sgibbs	for (p = np; p != NULL; p = p->n_flink) {
14397883Sgibbs		if (ntype && (p->n_type & GMASK) != ntype)
14497883Sgibbs			continue;
145102681Sgibbs		cp += strlcpy(cp, p->n_name, strlen(p->n_name) + 1);
146102681Sgibbs		if (comma && p->n_flink != NULL)
14797883Sgibbs			*cp++ = ',';
14897883Sgibbs		*cp++ = ' ';
14997883Sgibbs	}
15097883Sgibbs	*--cp = '\0';
151102681Sgibbs	if (comma && *--cp == ',')
15297883Sgibbs		*cp = '\0';
15397883Sgibbs	return (top);
15497883Sgibbs}
15597883Sgibbs
156102681Sgibbs/*
15797883Sgibbs * Grab a single word (liberal word)
15897883Sgibbs * Throw away things between ()'s, and take anything between <>.
15997883Sgibbs */
16097883Sgibbschar *
16197883Sgibbsyankword(char *ap, char wbuf[])
162102681Sgibbs{
163102681Sgibbs	char *cp, *cp2;
164102681Sgibbs
165102681Sgibbs	cp = ap;
166102681Sgibbs	for (;;) {
167102681Sgibbs		if (*cp == '\0')
168107441Sscottl			return (NULL);
169109588Sgibbs		if (*cp == '(') {
170109588Sgibbs			int nesting = 0;
171109588Sgibbs
172109588Sgibbs			while (*cp != '\0') {
173109588Sgibbs				switch (*cp++) {
174109588Sgibbs				case '(':
175109588Sgibbs					nesting++;
176109588Sgibbs					break;
177109588Sgibbs				case ')':
178109588Sgibbs					--nesting;
179109588Sgibbs					break;
180109588Sgibbs				}
181109588Sgibbs				if (nesting <= 0)
182109588Sgibbs					break;
183109588Sgibbs			}
184109588Sgibbs		} else if (*cp == ' ' || *cp == '\t' || *cp == ',')
185109588Sgibbs			cp++;
186107441Sscottl		else
187107441Sscottl			break;
188107441Sscottl	}
189107441Sscottl	if (*cp ==  '<')
190114623Sgibbs		for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
191114623Sgibbs			;
192102681Sgibbs	else
19397883Sgibbs		for (cp2 = wbuf; *cp != '\0' && strchr(" \t,(", *cp) == NULL;
19497883Sgibbs		    *cp2++ = *cp++)
19597883Sgibbs			;
19697883Sgibbs	*cp2 = '\0';
19797883Sgibbs	return (cp);
19897883Sgibbs}
19997883Sgibbs
20097883Sgibbs/*
201102681Sgibbs * Grab a single login name (liberal word)
202102681Sgibbs * Throw away things between ()'s, take anything between <>,
203102681Sgibbs * and look for words before metacharacters %, @, !.
204107623Sscottl */
205102681Sgibbschar *
206102681Sgibbsyanklogin(char *ap, char wbuf[])
207102681Sgibbs{
208102681Sgibbs	char *cp, *cp2, *cp_temp;
20997883Sgibbs	int n;
21097883Sgibbs
21197883Sgibbs	cp = ap;
21297883Sgibbs	for (;;) {
21397883Sgibbs		if (*cp == '\0')
21497883Sgibbs			return (NULL);
21597883Sgibbs		if (*cp == '(') {
21697883Sgibbs			int nesting = 0;
217102681Sgibbs
218102681Sgibbs			while (*cp != '\0') {
219102681Sgibbs				switch (*cp++) {
220102681Sgibbs				case '(':
221102681Sgibbs					nesting++;
222102681Sgibbs					break;
223102681Sgibbs				case ')':
22497883Sgibbs					--nesting;
22597883Sgibbs					break;
22697883Sgibbs				}
22797883Sgibbs				if (nesting <= 0)
22897883Sgibbs					break;
22997883Sgibbs			}
23097883Sgibbs		} else if (*cp == ' ' || *cp == '\t' || *cp == ',')
23197883Sgibbs			cp++;
232102681Sgibbs		else
233102681Sgibbs			break;
234102681Sgibbs	}
235102681Sgibbs
236102681Sgibbs	/*
237102681Sgibbs	 * Now, let's go forward till we meet the needed character,
238102681Sgibbs	 * and step one word back.
23997883Sgibbs	 */
24097883Sgibbs
24197883Sgibbs	/* First, remember current point. */
24297883Sgibbs	cp_temp = cp;
24397883Sgibbs	n = 0;
24497883Sgibbs
24597883Sgibbs	/*
24697883Sgibbs	 * Note that we look ahead in a cycle. This is safe, since
24797883Sgibbs	 * non-end of string is checked first.
248102681Sgibbs	 */
249102681Sgibbs	while(*cp != '\0' && strchr("@%!", *(cp + 1)) == NULL)
250102681Sgibbs		cp++;
251102681Sgibbs
252102681Sgibbs	/*
253102681Sgibbs	 * Now, start stepping back to the first non-word character,
254102681Sgibbs	 * while counting the number of symbols in a word.
255102681Sgibbs	 */
25697883Sgibbs	while(cp != cp_temp && strchr(" \t,<>", *(cp - 1)) == NULL) {
25797883Sgibbs		n++;
25897883Sgibbs		cp--;
25997883Sgibbs	}
26097883Sgibbs
26197883Sgibbs	/* Finally, grab the word forward. */
26297883Sgibbs	cp2 = wbuf;
26397883Sgibbs	while(n >= 0) {
26497883Sgibbs		*cp2++=*cp++;
26597883Sgibbs		n--;
26697883Sgibbs	}
26797883Sgibbs
26897883Sgibbs	*cp2 = '\0';
26997883Sgibbs	return (cp);
27097883Sgibbs}
27197883Sgibbs
27297883Sgibbs/*
27397883Sgibbs * For each recipient in the passed name list with a /
27497883Sgibbs * in the name, append the message to the end of the named file
27597883Sgibbs * and remove him from the recipient list.
27697883Sgibbs *
27797883Sgibbs * Recipients whose name begins with | are piped through the given
27897883Sgibbs * program and removed.
279109588Sgibbs */
28097883Sgibbsstruct name *
28197883Sgibbsoutof(struct name *names, FILE *fo, struct header *hp)
282115329Sgibbs{
28397883Sgibbs	int c, ispipe;
28497883Sgibbs	struct name *np, *top;
28597883Sgibbs	time_t now;
28697883Sgibbs	char *date, *fname;
28797883Sgibbs	FILE *fout, *fin;
28897883Sgibbs
289109588Sgibbs	top = names;
29097883Sgibbs	np = names;
291102681Sgibbs	(void)time(&now);
292102681Sgibbs	date = ctime(&now);
293102681Sgibbs	while (np != NULL) {
294102681Sgibbs		if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
295102681Sgibbs			np = np->n_flink;
29697883Sgibbs			continue;
29797883Sgibbs		}
29897883Sgibbs		ispipe = np->n_name[0] == '|';
29997883Sgibbs		if (ispipe)
30097883Sgibbs			fname = np->n_name+1;
30197883Sgibbs		else
302109588Sgibbs			fname = expand(np->n_name);
30397883Sgibbs
304102681Sgibbs		/*
305102681Sgibbs		 * See if we have copied the complete message out yet.
306102681Sgibbs		 * If not, do so.
307102681Sgibbs		 */
308102681Sgibbs
30997883Sgibbs		if (image < 0) {
31097883Sgibbs			int fd;
31197883Sgibbs			char tempname[PATHSIZE];
31297883Sgibbs
31397883Sgibbs			(void)snprintf(tempname, sizeof(tempname),
31497883Sgibbs			    "%s/mail.ReXXXXXXXXXX", tmpdir);
315109588Sgibbs			if ((fd = mkstemp(tempname)) == -1 ||
31697883Sgibbs			    (fout = Fdopen(fd, "a")) == NULL) {
31797883Sgibbs				warn("%s", tempname);
31897883Sgibbs				senderr++;
31997883Sgibbs				goto cant;
32097883Sgibbs			}
32197883Sgibbs			image = open(tempname, O_RDWR);
32297883Sgibbs			(void)rm(tempname);
32397883Sgibbs			if (image < 0) {
32497883Sgibbs				warn("%s", tempname);
32597883Sgibbs				senderr++;
32697883Sgibbs				(void)Fclose(fout);
32797883Sgibbs				goto cant;
32897883Sgibbs			}
32997883Sgibbs			(void)fcntl(image, F_SETFD, 1);
33097883Sgibbs			fprintf(fout, "From %s %s", myname, date);
33197883Sgibbs			puthead(hp, fout,
33297883Sgibbs			    GTO|GSUBJECT|GCC|GREPLYTO|GINREPLYTO|GNL);
33397883Sgibbs			while ((c = getc(fo)) != EOF)
33497883Sgibbs				(void)putc(c, fout);
33597883Sgibbs			rewind(fo);
33697883Sgibbs			fprintf(fout, "\n");
33797883Sgibbs			(void)fflush(fout);
33897883Sgibbs			if (ferror(fout)) {
33997883Sgibbs				warn("%s", tempname);
34097883Sgibbs				senderr++;
34197883Sgibbs				(void)Fclose(fout);
34297883Sgibbs				goto cant;
34397883Sgibbs			}
34497883Sgibbs			(void)Fclose(fout);
34597883Sgibbs		}
34697883Sgibbs
34797883Sgibbs		/*
34897883Sgibbs		 * Now either copy "image" to the desired file
34997883Sgibbs		 * or give it as the standard input to the desired
35097883Sgibbs		 * program as appropriate.
35197883Sgibbs		 */
35297883Sgibbs
35397883Sgibbs		if (ispipe) {
35497883Sgibbs			int pid;
35597883Sgibbs			char *sh;
356102681Sgibbs			sigset_t nset;
357102681Sgibbs
358102681Sgibbs			/*
359102681Sgibbs			 * XXX
360102681Sgibbs			 * We can't really reuse the same image file,
361102681Sgibbs			 * because multiple piped recipients will
362102681Sgibbs			 * share the same lseek location and trample
363102681Sgibbs			 * on one another.
364102681Sgibbs			 */
365102681Sgibbs			if ((sh = value("SHELL")) == NULL)
366102681Sgibbs				sh = _PATH_CSHELL;
367102681Sgibbs			(void)sigemptyset(&nset);
368102681Sgibbs			(void)sigaddset(&nset, SIGHUP);
369102681Sgibbs			(void)sigaddset(&nset, SIGINT);
370102681Sgibbs			(void)sigaddset(&nset, SIGQUIT);
371102681Sgibbs			pid = start_command(sh, &nset, image, -1, "-c", fname,
372102681Sgibbs			    NULL);
373102681Sgibbs			if (pid < 0) {
374102681Sgibbs				senderr++;
37597883Sgibbs				goto cant;
37697883Sgibbs			}
37797883Sgibbs			free_child(pid);
37897883Sgibbs		} else {
37997883Sgibbs			int f;
38097883Sgibbs			if ((fout = Fopen(fname, "a")) == NULL) {
38197883Sgibbs				warn("%s", fname);
38297883Sgibbs				senderr++;
383102681Sgibbs				goto cant;
384102681Sgibbs			}
385102681Sgibbs			if ((f = dup(image)) < 0) {
386102681Sgibbs				warn("dup");
387102681Sgibbs				fin = NULL;
388102681Sgibbs			} else
389102681Sgibbs				fin = Fdopen(f, "r");
390102681Sgibbs			if (fin == NULL) {
39197883Sgibbs				fprintf(stderr, "Can't reopen image\n");
39297883Sgibbs				(void)Fclose(fout);
39397883Sgibbs				senderr++;
39497883Sgibbs				goto cant;
39597883Sgibbs			}
39697883Sgibbs			rewind(fin);
39797883Sgibbs			while ((c = getc(fin)) != EOF)
39897883Sgibbs				(void)putc(c, fout);
39997883Sgibbs			if (ferror(fout)) {
400102681Sgibbs				warnx("%s", fname);
401107441Sscottl				senderr++;
402102681Sgibbs				(void)Fclose(fout);
403102681Sgibbs				(void)Fclose(fin);
404102681Sgibbs				goto cant;
405102681Sgibbs			}
406102681Sgibbs			(void)Fclose(fout);
407102681Sgibbs			(void)Fclose(fin);
408102681Sgibbs		}
409102681Sgibbscant:
410102681Sgibbs		/*
41197883Sgibbs		 * In days of old we removed the entry from the
41297883Sgibbs		 * the list; now for sake of header expansion
41397883Sgibbs		 * we leave it in and mark it as deleted.
41497883Sgibbs		 */
41597883Sgibbs		np->n_type |= GDEL;
41697883Sgibbs		np = np->n_flink;
41797883Sgibbs	}
41897883Sgibbs	if (image >= 0) {
41997883Sgibbs		(void)close(image);
420102681Sgibbs		image = -1;
421102681Sgibbs	}
422102681Sgibbs	return (top);
423102681Sgibbs}
424102681Sgibbs
425102681Sgibbs/*
42697883Sgibbs * Determine if the passed address is a local "send to file" address.
42797883Sgibbs * If any of the network metacharacters precedes any slashes, it can't
42897883Sgibbs * be a filename.  We cheat with .'s to allow path names like ./...
42997883Sgibbs */
43097883Sgibbsint
43197883Sgibbsisfileaddr(char *name)
43297883Sgibbs{
43397883Sgibbs	char *cp;
43497883Sgibbs
435102681Sgibbs	if (*name == '+')
436102681Sgibbs		return (1);
437102681Sgibbs	for (cp = name; *cp != '\0'; cp++) {
438102681Sgibbs		if (*cp == '!' || *cp == '%' || *cp == '@')
439102681Sgibbs			return (0);
440102681Sgibbs		if (*cp == '/')
441102681Sgibbs			return (1);
44297883Sgibbs	}
44397883Sgibbs	return (0);
44497883Sgibbs}
44597883Sgibbs
44697883Sgibbs/*
44797883Sgibbs * Map all of the aliased users in the invoker's mailrc
44897883Sgibbs * file and insert them into the list.
44997883Sgibbs * Changed after all these months of service to recursively
45097883Sgibbs * expand names (2/14/80).
451102681Sgibbs */
452102681Sgibbs
453102681Sgibbsstruct name *
45497883Sgibbsusermap(struct name *names)
45597883Sgibbs{
45697883Sgibbs	struct name *new, *np, *cp;
45797883Sgibbs	struct grouphead *gh;
45897883Sgibbs	int metoo;
45997883Sgibbs
460102681Sgibbs	new = NULL;
461102681Sgibbs	np = names;
462102681Sgibbs	metoo = (value("metoo") != NULL);
463102681Sgibbs	while (np != NULL) {
46497883Sgibbs		if (np->n_name[0] == '\\') {
46597883Sgibbs			cp = np->n_flink;
46697883Sgibbs			new = put(new, np);
46797883Sgibbs			np = cp;
46897883Sgibbs			continue;
46997883Sgibbs		}
47097883Sgibbs		gh = findgroup(np->n_name);
47197883Sgibbs		cp = np->n_flink;
47297883Sgibbs		if (gh != NULL)
473102681Sgibbs			new = gexpand(new, gh, metoo, np->n_type);
474102681Sgibbs		else
475102681Sgibbs			new = put(new, np);
47697883Sgibbs		np = cp;
47797883Sgibbs	}
47897883Sgibbs	return (new);
47997883Sgibbs}
48097883Sgibbs
48197883Sgibbs/*
48297883Sgibbs * Recursively expand a group name.  We limit the expansion to some
48397883Sgibbs * fixed level to keep things from going haywire.
48497883Sgibbs * Direct recursion is not expanded for convenience.
48597883Sgibbs */
48697883Sgibbs
48797883Sgibbsstruct name *
48897883Sgibbsgexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
48997883Sgibbs{
49097883Sgibbs	struct group *gp;
49197883Sgibbs	struct grouphead *ngh;
49297883Sgibbs	struct name *np;
49397883Sgibbs	static int depth;
49497883Sgibbs	char *cp;
49597883Sgibbs
49697883Sgibbs	if (depth > MAXEXP) {
49797883Sgibbs		printf("Expanding alias to depth larger than %d\n", MAXEXP);
49897883Sgibbs		return (nlist);
499107441Sscottl	}
500107441Sscottl	depth++;
501107441Sscottl	for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
502107441Sscottl		cp = gp->ge_name;
503107441Sscottl		if (*cp == '\\')
504107441Sscottl			goto quote;
505107441Sscottl		if (strcmp(cp, gh->g_name) == 0)
506107441Sscottl			goto quote;
507107441Sscottl		if ((ngh = findgroup(cp)) != NULL) {
508107441Sscottl			nlist = gexpand(nlist, ngh, metoo, ntype);
509107441Sscottl			continue;
51097883Sgibbs		}
51197883Sgibbsquote:
51297883Sgibbs		np = nalloc(cp, ntype);
51397883Sgibbs		/*
51497883Sgibbs		 * At this point should allow to expand
51597883Sgibbs		 * to self if only person in group
51697883Sgibbs		 */
51797883Sgibbs		if (gp == gh->g_list && gp->ge_link == NULL)
51897883Sgibbs			goto skip;
51997883Sgibbs		if (!metoo && strcmp(cp, myname) == 0)
52097883Sgibbs			np->n_type |= GDEL;
52197883Sgibbsskip:
52297883Sgibbs		nlist = put(nlist, np);
52397883Sgibbs	}
52497883Sgibbs	depth--;
52597883Sgibbs	return (nlist);
52697883Sgibbs}
52797883Sgibbs
52897883Sgibbs/*
52997883Sgibbs * Concatenate the two passed name lists, return the result.
53097883Sgibbs */
53197883Sgibbsstruct name *
53297883Sgibbscat(struct name *n1, struct name *n2)
53397883Sgibbs{
53497883Sgibbs	struct name *tail;
53597883Sgibbs
53697883Sgibbs	if (n1 == NULL)
53797883Sgibbs		return (n2);
53897883Sgibbs	if (n2 == NULL)
53997883Sgibbs		return (n1);
54097883Sgibbs	tail = tailof(n1);
54197883Sgibbs	tail->n_flink = n2;
54297883Sgibbs	n2->n_blink = tail;
54397883Sgibbs	return (n1);
54497883Sgibbs}
54597883Sgibbs
54697883Sgibbs/*
54797883Sgibbs * Unpack the name list onto a vector of strings.
54897883Sgibbs * Return an error if the name list won't fit.
54997883Sgibbs */
55097883Sgibbschar **
55197883Sgibbsunpack(struct name *np)
55297883Sgibbs{
55397883Sgibbs	char **ap, **top;
55497883Sgibbs	struct name *n;
55597883Sgibbs	int t, extra, metoo, verbose;
55697883Sgibbs
55797883Sgibbs	n = np;
55897883Sgibbs	if ((t = count(n)) == 0)
55997883Sgibbs		errx(1, "No names to unpack");
56097883Sgibbs	/*
56197883Sgibbs	 * Compute the number of extra arguments we will need.
56297883Sgibbs	 * We need at least two extra -- one for "mail" and one for
56397883Sgibbs	 * the terminating 0 pointer.  Additional spots may be needed
56497883Sgibbs	 * to pass along -f to the host mailer.
56597883Sgibbs	 */
56697883Sgibbs	extra = 2;
56797883Sgibbs	extra++;
56897883Sgibbs	metoo = value("metoo") != NULL;
56997883Sgibbs	if (metoo)
57097883Sgibbs		extra++;
57197883Sgibbs	verbose = value("verbose") != NULL;
57297883Sgibbs	if (verbose)
57397883Sgibbs		extra++;
57497883Sgibbs	top = (char **)salloc((t + extra) * sizeof(*top));
57597883Sgibbs	ap = top;
57697883Sgibbs	*ap++ = "send-mail";
57797883Sgibbs	*ap++ = "-i";
57897883Sgibbs	if (metoo)
57997883Sgibbs		*ap++ = "-m";
58097883Sgibbs	if (verbose)
58197883Sgibbs		*ap++ = "-v";
58297883Sgibbs	for (; n != NULL; n = n->n_flink)
583102681Sgibbs		if ((n->n_type & GDEL) == 0)
584102681Sgibbs			*ap++ = n->n_name;
585102681Sgibbs	*ap = NULL;
586102681Sgibbs	return (top);
587102681Sgibbs}
588102681Sgibbs
589102681Sgibbs/*
590102681Sgibbs * Remove all of the duplicates from the passed name list by
591102681Sgibbs * insertion sorting them, then checking for dups.
592102681Sgibbs * Return the head of the new list.
593102681Sgibbs */
594102681Sgibbsstruct name *
595102681Sgibbselide(struct name *names)
596102681Sgibbs{
597102681Sgibbs	struct name *np, *t, *new;
598102681Sgibbs	struct name *x;
599102681Sgibbs
600102681Sgibbs	if (names == NULL)
601102681Sgibbs		return (NULL);
602102681Sgibbs	new = names;
60397883Sgibbs	np = names;
60497883Sgibbs	np = np->n_flink;
60597883Sgibbs	if (np != NULL)
60697883Sgibbs		np->n_blink = NULL;
60797883Sgibbs	new->n_flink = NULL;
60897883Sgibbs	while (np != NULL) {
60997883Sgibbs		t = new;
61097883Sgibbs		while (strcasecmp(t->n_name, np->n_name) < 0) {
61197883Sgibbs			if (t->n_flink == NULL)
61297883Sgibbs				break;
61397883Sgibbs			t = t->n_flink;
61497883Sgibbs		}
61597883Sgibbs
61697883Sgibbs		/*
61797883Sgibbs		 * If we ran out of t's, put the new entry after
61897883Sgibbs		 * the current value of t.
61997883Sgibbs		 */
620102681Sgibbs
621102681Sgibbs		if (strcasecmp(t->n_name, np->n_name) < 0) {
622102681Sgibbs			t->n_flink = np;
623102681Sgibbs			np->n_blink = t;
62497883Sgibbs			t = np;
62597883Sgibbs			np = np->n_flink;
62697883Sgibbs			t->n_flink = NULL;
62797883Sgibbs			continue;
62897883Sgibbs		}
62997883Sgibbs
63097883Sgibbs		/*
63197883Sgibbs		 * Otherwise, put the new entry in front of the
63297883Sgibbs		 * current t.  If at the front of the list,
63397883Sgibbs		 * the new guy becomes the new head of the list.
63497883Sgibbs		 */
63597883Sgibbs
63697883Sgibbs		if (t == new) {
63797883Sgibbs			t = np;
63897883Sgibbs			np = np->n_flink;
63997883Sgibbs			t->n_flink = new;
64097883Sgibbs			new->n_blink = t;
641102681Sgibbs			t->n_blink = NULL;
642102681Sgibbs			new = t;
64397883Sgibbs			continue;
64497883Sgibbs		}
64597883Sgibbs
64697883Sgibbs		/*
64797883Sgibbs		 * The normal case -- we are inserting into the
64897883Sgibbs		 * middle of the list.
64997883Sgibbs		 */
65097883Sgibbs
65197883Sgibbs		x = np;
652102681Sgibbs		np = np->n_flink;
653102681Sgibbs		x->n_flink = t;
65497883Sgibbs		x->n_blink = t->n_blink;
65597883Sgibbs		t->n_blink->n_flink = x;
65697883Sgibbs		t->n_blink = x;
65797883Sgibbs	}
65897883Sgibbs
65997883Sgibbs	/*
66097883Sgibbs	 * Now the list headed up by new is sorted.
66197883Sgibbs	 * Go through it and remove duplicates.
66297883Sgibbs	 */
663102681Sgibbs
664102681Sgibbs	np = new;
66597883Sgibbs	while (np != NULL) {
66697883Sgibbs		t = np;
66797883Sgibbs		while (t->n_flink != NULL &&
66897883Sgibbs		    strcasecmp(np->n_name, t->n_flink->n_name) == 0)
66997883Sgibbs			t = t->n_flink;
67097883Sgibbs		if (t == np || t == NULL) {
67197883Sgibbs			np = np->n_flink;
67297883Sgibbs			continue;
67397883Sgibbs		}
674102681Sgibbs
675102681Sgibbs		/*
676102681Sgibbs		 * Now t points to the last entry with the same name
677102681Sgibbs		 * as np.  Make np point beyond t.
678102681Sgibbs		 */
679102681Sgibbs
68097883Sgibbs		np->n_flink = t->n_flink;
68197883Sgibbs		if (t->n_flink != NULL)
68297883Sgibbs			t->n_flink->n_blink = np;
68397883Sgibbs		np = np->n_flink;
68497883Sgibbs	}
68597883Sgibbs	return (new);
68697883Sgibbs}
68797883Sgibbs
68897883Sgibbs/*
689102681Sgibbs * Put another node onto a list of names and return
69097883Sgibbs * the list.
69197883Sgibbs */
69297883Sgibbsstruct name *
69397883Sgibbsput(struct name *list, struct name *node)
69497883Sgibbs{
69597883Sgibbs	node->n_flink = list;
69697883Sgibbs	node->n_blink = NULL;
69797883Sgibbs	if (list != NULL)
69897883Sgibbs		list->n_blink = node;
699102681Sgibbs	return (node);
70097883Sgibbs}
70197883Sgibbs
70297883Sgibbs/*
70397883Sgibbs * Determine the number of undeleted elements in
70497883Sgibbs * a name list and return it.
70597883Sgibbs */
70697883Sgibbsint
70797883Sgibbscount(struct name *np)
70897883Sgibbs{
709102681Sgibbs	int c;
71097883Sgibbs
71197883Sgibbs	for (c = 0; np != NULL; np = np->n_flink)
71297883Sgibbs		if ((np->n_type & GDEL) == 0)
71397883Sgibbs			c++;
71497883Sgibbs	return (c);
71597883Sgibbs}
71697883Sgibbs
71797883Sgibbs/*
71897883Sgibbs * Delete the given name from a namelist.
719102681Sgibbs */
720102681Sgibbsstruct name *
721102681Sgibbsdelname(struct name *np, char name[])
722102681Sgibbs{
723102681Sgibbs	struct name *p;
724102681Sgibbs
72597883Sgibbs	for (p = np; p != NULL; p = p->n_flink)
72697883Sgibbs		if (strcasecmp(p->n_name, name) == 0) {
72797883Sgibbs			if (p->n_blink == NULL) {
72897883Sgibbs				if (p->n_flink != NULL)
72997883Sgibbs					p->n_flink->n_blink = NULL;
73097883Sgibbs				np = p->n_flink;
73197883Sgibbs				continue;
73297883Sgibbs			}
73397883Sgibbs			if (p->n_flink == NULL) {
734102681Sgibbs				if (p->n_blink != NULL)
73597883Sgibbs					p->n_blink->n_flink = NULL;
73697883Sgibbs				continue;
73797883Sgibbs			}
73897883Sgibbs			p->n_blink->n_flink = p->n_flink;
73997883Sgibbs			p->n_flink->n_blink = p->n_blink;
74097883Sgibbs		}
74197883Sgibbs	return (np);
74297883Sgibbs}
74397883Sgibbs
744102681Sgibbs/*
74597883Sgibbs * Pretty print a name list
74697883Sgibbs * Uncomment it if you need it.
74797883Sgibbs */
74897883Sgibbs
74997883Sgibbs/*
75097883Sgibbsvoid
75197883Sgibbsprettyprint(struct name *name)
75297883Sgibbs{
75397883Sgibbs	struct name *np;
754102681Sgibbs
75597883Sgibbs	np = name;
75697883Sgibbs	while (np != NULL) {
75797883Sgibbs		fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
75897883Sgibbs		np = np->n_flink;
75997883Sgibbs	}
76097883Sgibbs	fprintf(stderr, "\n");
76197883Sgibbs}
76297883Sgibbs*/
76397883Sgibbs