names.c revision 216370
1231990Smp/*
259243Sobrien * Copyright (c) 1980, 1993
359243Sobrien *	The Regents of the University of California.  All rights reserved.
459243Sobrien *
559243Sobrien * Redistribution and use in source and binary forms, with or without
659243Sobrien * modification, are permitted provided that the following conditions
759243Sobrien * are met:
859243Sobrien * 1. Redistributions of source code must retain the above copyright
959243Sobrien *    notice, this list of conditions and the following disclaimer.
1059243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1159243Sobrien *    notice, this list of conditions and the following disclaimer in the
1259243Sobrien *    documentation and/or other materials provided with the distribution.
1359243Sobrien * 4. Neither the name of the University nor the names of its contributors
1459243Sobrien *    may be used to endorse or promote products derived from this software
1559243Sobrien *    without specific prior written permission.
1659243Sobrien *
17100616Smp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1859243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1959243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2059243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2159243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2259243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2359243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2459243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2559243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2659243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2759243Sobrien * SUCH DAMAGE.
2859243Sobrien */
2959243Sobrien
3059243Sobrien#ifndef lint
3159243Sobrien#if 0
3259243Sobrienstatic char sccsid[] = "@(#)names.c	8.1 (Berkeley) 6/6/93";
3359243Sobrien#endif
3459243Sobrien#endif /* not lint */
35231990Smp#include <sys/cdefs.h>
3659243Sobrien__FBSDID("$FreeBSD: head/usr.bin/mail/names.c 216370 2010-12-11 08:32:16Z joel $");
3759243Sobrien
3859243Sobrien/*
3959243Sobrien * Mail -- a mail program
4069408Sache *
4159243Sobrien * Handle name lists.
4269408Sache */
4359243Sobrien
44231990Smp#include "rcv.h"
45145479Smp#include <fcntl.h>
46145479Smp#include "extern.h"
47145479Smp
4859243Sobrien/*
4959243Sobrien * Allocate a single element of a name list,
5059243Sobrien * initialize its name field to the passed
5159243Sobrien * name and return it.
52145479Smp */
53145479Smpstruct name *
54145479Smpnalloc(str, ntype)
5559243Sobrien	char str[];
5659243Sobrien	int ntype;
5759243Sobrien{
58167465Smp	struct name *np;
59167465Smp
60167465Smp	np = (struct name *)salloc(sizeof(*np));
61167465Smp	np->n_flink = NULL;
62167465Smp	np->n_blink = NULL;
63167465Smp	np->n_type = ntype;
64195609Smp	np->n_name = savestr(str);
65167465Smp	return (np);
66167465Smp}
67167465Smp
68167465Smp/*
6959243Sobrien * Find the tail of a list and return it.
70167465Smp */
71167465Smpstruct name *
7259243Sobrientailof(name)
73145479Smp	struct name *name;
74167465Smp{
7559243Sobrien	struct name *np;
7659243Sobrien
7759243Sobrien	np = name;
7859243Sobrien	if (np == NULL)
7959243Sobrien		return (NULL);
8059243Sobrien	while (np->n_flink != NULL)
8159243Sobrien		np = np->n_flink;
8259243Sobrien	return (np);
8359243Sobrien}
8459243Sobrien
8559243Sobrien/*
8659243Sobrien * Extract a list of names from a line,
8759243Sobrien * and make a list of names from it.
8859243Sobrien * Return the list or NULL if none found.
8959243Sobrien */
9059243Sobrienstruct name *
9159243Sobrienextract(line, ntype)
9259243Sobrien	char line[];
9359243Sobrien	int ntype;
9459243Sobrien{
9559243Sobrien	char *cp, *nbuf;
9659243Sobrien	struct name *top, *np, *t;
9759243Sobrien
9859243Sobrien	if (line == NULL || *line == '\0')
9959243Sobrien		return (NULL);
10059243Sobrien	if ((nbuf = malloc(strlen(line) + 1)) == NULL)
10159243Sobrien		err(1, "Out of memory");
10259243Sobrien	top = NULL;
10359243Sobrien	np = NULL;
10459243Sobrien	cp = line;
10559243Sobrien	while ((cp = yankword(cp, nbuf)) != NULL) {
10659243Sobrien		t = nalloc(nbuf, ntype);
10759243Sobrien		if (top == NULL)
10859243Sobrien			top = t;
10959243Sobrien		else
11059243Sobrien			np->n_flink = t;
11159243Sobrien		t->n_blink = np;
11259243Sobrien		np = t;
11359243Sobrien	}
11459243Sobrien	(void)free(nbuf);
11559243Sobrien	return (top);
11659243Sobrien}
11759243Sobrien
11859243Sobrien/*
11959243Sobrien * Turn a list of names into a string of the same names.
12059243Sobrien */
12159243Sobrienchar *
12259243Sobriendetract(np, ntype)
12359243Sobrien	struct name *np;
12459243Sobrien	int ntype;
12559243Sobrien{
12659243Sobrien	int s, comma;
12769408Sache	char *cp, *top;
12859243Sobrien	struct name *p;
12969408Sache
13059243Sobrien	comma = ntype & GCOMMA;
13159243Sobrien	if (np == NULL)
13259243Sobrien		return (NULL);
13359243Sobrien	ntype &= ~GCOMMA;
134167465Smp	s = 0;
13559243Sobrien	if (debug && comma)
13659243Sobrien		fprintf(stderr, "detract asked to insert commas\n");
13759243Sobrien	for (p = np; p != NULL; p = p->n_flink) {
13859243Sobrien		if (ntype && (p->n_type & GMASK) != ntype)
13959243Sobrien			continue;
14059243Sobrien		s += strlen(p->n_name) + 1;
14159243Sobrien		if (comma)
14259243Sobrien			s++;
14359243Sobrien	}
14459243Sobrien	if (s == 0)
14559243Sobrien		return (NULL);
14659243Sobrien	s += 2;
14759243Sobrien	top = salloc(s);
14859243Sobrien	cp = top;
14959243Sobrien	for (p = np; p != NULL; p = p->n_flink) {
150167465Smp		if (ntype && (p->n_type & GMASK) != ntype)
15159243Sobrien			continue;
152145479Smp		cp += strlcpy(cp, p->n_name, strlen(p->n_name) + 1);
153145479Smp		if (comma && p->n_flink != NULL)
15459243Sobrien			*cp++ = ',';
15559243Sobrien		*cp++ = ' ';
156167465Smp	}
15759243Sobrien	*--cp = '\0';
15859243Sobrien	if (comma && *--cp == ',')
15959243Sobrien		*cp = '\0';
16059243Sobrien	return (top);
16159243Sobrien}
162167465Smp
16359243Sobrien/*
164167465Smp * Grab a single word (liberal word)
165167465Smp * Throw away things between ()'s, and take anything between <>.
166167465Smp */
16759243Sobrienchar *
16859243Sobrienyankword(ap, wbuf)
16959243Sobrien	char *ap, wbuf[];
17059243Sobrien{
17159243Sobrien	char *cp, *cp2;
17259243Sobrien
17359243Sobrien	cp = ap;
17459243Sobrien	for (;;) {
17559243Sobrien		if (*cp == '\0')
176167465Smp			return (NULL);
17759243Sobrien		if (*cp == '(') {
17859243Sobrien			int nesting = 0;
17959243Sobrien
18059243Sobrien			while (*cp != '\0') {
18159243Sobrien				switch (*cp++) {
182167465Smp				case '(':
18359243Sobrien					nesting++;
18459243Sobrien					break;
18559243Sobrien				case ')':
18659243Sobrien					--nesting;
18759243Sobrien					break;
18859243Sobrien				}
18959243Sobrien				if (nesting <= 0)
190167465Smp					break;
19159243Sobrien			}
19259243Sobrien		} else if (*cp == ' ' || *cp == '\t' || *cp == ',')
19359243Sobrien			cp++;
19459243Sobrien		else
19559243Sobrien			break;
19659243Sobrien	}
19759243Sobrien	if (*cp ==  '<')
19859243Sobrien		for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
199167465Smp			;
20059243Sobrien	else
20159243Sobrien		for (cp2 = wbuf; *cp != '\0' && strchr(" \t,(", *cp) == NULL;
20259243Sobrien		    *cp2++ = *cp++)
20359243Sobrien			;
20459243Sobrien	*cp2 = '\0';
20559243Sobrien	return (cp);
20659243Sobrien}
20759243Sobrien
20859243Sobrien/*
20959243Sobrien * Grab a single login name (liberal word)
21059243Sobrien * Throw away things between ()'s, take anything between <>,
21159243Sobrien * and look for words before metacharacters %, @, !.
212167465Smp */
21359243Sobrienchar *
21459243Sobrienyanklogin(ap, wbuf)
21559243Sobrien	char *ap, wbuf[];
21659243Sobrien{
21759243Sobrien	char *cp, *cp2, *cp_temp;
21859243Sobrien	int n;
21959243Sobrien
220167465Smp	cp = ap;
22159243Sobrien	for (;;) {
222167465Smp		if (*cp == '\0')
22359243Sobrien			return (NULL);
224145479Smp		if (*cp == '(') {
22559243Sobrien			int nesting = 0;
22659243Sobrien
22759243Sobrien			while (*cp != '\0') {
22859243Sobrien				switch (*cp++) {
229167465Smp				case '(':
230167465Smp					nesting++;
231167465Smp					break;
23259243Sobrien				case ')':
23359243Sobrien					--nesting;
234167465Smp					break;
235167465Smp				}
236167465Smp				if (nesting <= 0)
237167465Smp					break;
23859243Sobrien			}
23959243Sobrien		} else if (*cp == ' ' || *cp == '\t' || *cp == ',')
24059243Sobrien			cp++;
24159243Sobrien		else
24259243Sobrien			break;
243167465Smp	}
24459243Sobrien
24559243Sobrien	/*
24659243Sobrien	 * Now, let's go forward till we meet the needed character,
247167465Smp	 * and step one word back.
24859243Sobrien	 */
24959243Sobrien
25059243Sobrien	/* First, remember current point. */
25159243Sobrien	cp_temp = cp;
25259243Sobrien	n = 0;
25359243Sobrien
254167465Smp	/*
25559243Sobrien	 * Note that we look ahead in a cycle. This is safe, since
256145479Smp	 * non-end of string is checked first.
257145479Smp	 */
25859243Sobrien	while(*cp != '\0' && strchr("@%!", *(cp + 1)) == NULL)
25959243Sobrien		cp++;
26059243Sobrien
26159243Sobrien	/*
26259243Sobrien	 * Now, start stepping back to the first non-word character,
26359243Sobrien	 * while counting the number of symbols in a word.
26459243Sobrien	 */
26559243Sobrien	while(cp != cp_temp && strchr(" \t,<>", *(cp - 1)) == NULL) {
266100616Smp		n++;
26759243Sobrien		cp--;
26859243Sobrien	}
26959243Sobrien
27059243Sobrien	/* Finally, grab the word forward. */
27159243Sobrien	cp2 = wbuf;
27259243Sobrien	while(n >= 0) {
27359243Sobrien		*cp2++=*cp++;
27459243Sobrien		n--;
27559243Sobrien	}
27659243Sobrien
27759243Sobrien	*cp2 = '\0';
27859243Sobrien	return (cp);
27959243Sobrien}
28059243Sobrien
281167465Smp/*
28259243Sobrien * For each recipient in the passed name list with a /
28359243Sobrien * in the name, append the message to the end of the named file
28459243Sobrien * and remove him from the recipient list.
28559243Sobrien *
28659243Sobrien * Recipients whose name begins with | are piped through the given
28759243Sobrien * program and removed.
28859243Sobrien */
28959243Sobrienstruct name *
290167465Smpoutof(names, fo, hp)
29159243Sobrien	struct name *names;
29259243Sobrien	FILE *fo;
29359243Sobrien	struct header *hp;
29459243Sobrien{
29559243Sobrien	int c, ispipe;
29659243Sobrien	struct name *np, *top;
29759243Sobrien	time_t now;
29859243Sobrien	char *date, *fname;
29959243Sobrien	FILE *fout, *fin;
300167465Smp
30159243Sobrien	top = names;
302131962Smp	np = names;
30359243Sobrien	(void)time(&now);
30459243Sobrien	date = ctime(&now);
30569408Sache	while (np != NULL) {
306131962Smp		if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
307167465Smp			np = np->n_flink;
308131962Smp			continue;
309167465Smp		}
31059243Sobrien		ispipe = np->n_name[0] == '|';
31159243Sobrien		if (ispipe)
312167465Smp			fname = np->n_name+1;
313131962Smp		else
314131962Smp			fname = expand(np->n_name);
315167465Smp
31659243Sobrien		/*
31759243Sobrien		 * See if we have copied the complete message out yet.
31869408Sache		 * If not, do so.
31959243Sobrien		 */
32059243Sobrien
32159243Sobrien		if (image < 0) {
32259243Sobrien			int fd;
32359243Sobrien			char tempname[PATHSIZE];
32459243Sobrien
325167465Smp			(void)snprintf(tempname, sizeof(tempname),
32659243Sobrien			    "%s/mail.ReXXXXXXXXXX", tmpdir);
32759243Sobrien			if ((fd = mkstemp(tempname)) == -1 ||
32859243Sobrien			    (fout = Fdopen(fd, "a")) == NULL) {
32959243Sobrien				warn("%s", tempname);
330167465Smp				senderr++;
33159243Sobrien				goto cant;
33259243Sobrien			}
33359243Sobrien			image = open(tempname, O_RDWR);
33459243Sobrien			(void)rm(tempname);
33559243Sobrien			if (image < 0) {
33659243Sobrien				warn("%s", tempname);
33759243Sobrien				senderr++;
33859243Sobrien				(void)Fclose(fout);
33959243Sobrien				goto cant;
34059243Sobrien			}
34159243Sobrien			(void)fcntl(image, F_SETFD, 1);
34259243Sobrien			fprintf(fout, "From %s %s", myname, date);
34359243Sobrien			puthead(hp, fout,
34459243Sobrien			    GTO|GSUBJECT|GCC|GREPLYTO|GINREPLYTO|GNL);
345167465Smp			while ((c = getc(fo)) != EOF)
34659243Sobrien				(void)putc(c, fout);
34759243Sobrien			rewind(fo);
34859243Sobrien			fprintf(fout, "\n");
34959243Sobrien			(void)fflush(fout);
35059243Sobrien			if (ferror(fout)) {
35159243Sobrien				warn("%s", tempname);
35259243Sobrien				senderr++;
35359243Sobrien				(void)Fclose(fout);
35459243Sobrien				goto cant;
355167465Smp			}
35659243Sobrien			(void)Fclose(fout);
357145479Smp		}
358145479Smp
35959243Sobrien		/*
36059243Sobrien		 * Now either copy "image" to the desired file
361167465Smp		 * or give it as the standard input to the desired
36259243Sobrien		 * program as appropriate.
36359243Sobrien		 */
36459243Sobrien
36559243Sobrien		if (ispipe) {
36659243Sobrien			int pid;
36759243Sobrien			char *sh;
36859243Sobrien			sigset_t nset;
36959243Sobrien
37059243Sobrien			/*
37159243Sobrien			 * XXX
37259243Sobrien			 * We can't really reuse the same image file,
37359243Sobrien			 * because multiple piped recipients will
37459243Sobrien			 * share the same lseek location and trample
37559243Sobrien			 * on one another.
37659243Sobrien			 */
37759243Sobrien			if ((sh = value("SHELL")) == NULL)
37859243Sobrien				sh = _PATH_CSHELL;
37959243Sobrien			(void)sigemptyset(&nset);
38059243Sobrien			(void)sigaddset(&nset, SIGHUP);
38159243Sobrien			(void)sigaddset(&nset, SIGINT);
38259243Sobrien			(void)sigaddset(&nset, SIGQUIT);
38359243Sobrien			pid = start_command(sh, &nset, image, -1, "-c", fname,
38459243Sobrien			    NULL);
38559243Sobrien			if (pid < 0) {
38659243Sobrien				senderr++;
38759243Sobrien				goto cant;
38859243Sobrien			}
38959243Sobrien			free_child(pid);
39059243Sobrien		} else {
39159243Sobrien			int f;
39259243Sobrien			if ((fout = Fopen(fname, "a")) == NULL) {
393167465Smp				warn("%s", fname);
39459243Sobrien				senderr++;
39559243Sobrien				goto cant;
39659243Sobrien			}
39759243Sobrien			if ((f = dup(image)) < 0) {
39859243Sobrien				warn("dup");
39959243Sobrien				fin = NULL;
40059243Sobrien			} else
40159243Sobrien				fin = Fdopen(f, "r");
402100616Smp			if (fin == NULL) {
40359243Sobrien				fprintf(stderr, "Can't reopen image\n");
40459243Sobrien				(void)Fclose(fout);
40559243Sobrien				senderr++;
40659243Sobrien				goto cant;
407167465Smp			}
40859243Sobrien			rewind(fin);
40959243Sobrien			while ((c = getc(fin)) != EOF)
41059243Sobrien				(void)putc(c, fout);
411167465Smp			if (ferror(fout)) {
412167465Smp				warnx("%s", fname);
41359243Sobrien				senderr++;
41459243Sobrien				(void)Fclose(fout);
41559243Sobrien				(void)Fclose(fin);
41659243Sobrien				goto cant;
417167465Smp			}
41859243Sobrien			(void)Fclose(fout);
41959243Sobrien			(void)Fclose(fin);
42059243Sobrien		}
42159243Sobriencant:
422167465Smp		/*
423167465Smp		 * In days of old we removed the entry from the
424167465Smp		 * the list; now for sake of header expansion
425167465Smp		 * we leave it in and mark it as deleted.
426167465Smp		 */
42759243Sobrien		np->n_type |= GDEL;
42859243Sobrien		np = np->n_flink;
42959243Sobrien	}
430167465Smp	if (image >= 0) {
43159243Sobrien		(void)close(image);
432145479Smp		image = -1;
43359243Sobrien	}
43459243Sobrien	return (top);
43559243Sobrien}
43659243Sobrien
43759243Sobrien/*
43859243Sobrien * Determine if the passed address is a local "send to file" address.
43969408Sache * If any of the network metacharacters precedes any slashes, it can't
44059243Sobrien * be a filename.  We cheat with .'s to allow path names like ./...
44159243Sobrien */
44259243Sobrienint
44359243Sobrienisfileaddr(name)
44459243Sobrien	char *name;
44559243Sobrien{
44659243Sobrien	char *cp;
44759243Sobrien
44859243Sobrien	if (*name == '+')
44959243Sobrien		return (1);
45059243Sobrien	for (cp = name; *cp != '\0'; cp++) {
45159243Sobrien		if (*cp == '!' || *cp == '%' || *cp == '@')
45259243Sobrien			return (0);
45359243Sobrien		if (*cp == '/')
45459243Sobrien			return (1);
455167465Smp	}
45659243Sobrien	return (0);
457145479Smp}
45859243Sobrien
45959243Sobrien/*
46059243Sobrien * Map all of the aliased users in the invoker's mailrc
46159243Sobrien * file and insert them into the list.
46259243Sobrien * Changed after all these months of service to recursively
46359243Sobrien * expand names (2/14/80).
46459243Sobrien */
46559243Sobrien
46659243Sobrienstruct name *
46759243Sobrienusermap(names)
468167465Smp	struct name *names;
469167465Smp{
470167465Smp	struct name *new, *np, *cp;
471167465Smp	struct grouphead *gh;
472167465Smp	int metoo;
47359243Sobrien
47459243Sobrien	new = NULL;
47559243Sobrien	np = names;
47659243Sobrien	metoo = (value("metoo") != NULL);
477167465Smp	while (np != NULL) {
47859243Sobrien		if (np->n_name[0] == '\\') {
47959243Sobrien			cp = np->n_flink;
48059243Sobrien			new = put(new, np);
481167465Smp			np = cp;
482167465Smp			continue;
483167465Smp		}
48459243Sobrien		gh = findgroup(np->n_name);
48559243Sobrien		cp = np->n_flink;
48659243Sobrien		if (gh != NULL)
48759243Sobrien			new = gexpand(new, gh, metoo, np->n_type);
48859243Sobrien		else
489167465Smp			new = put(new, np);
49059243Sobrien		np = cp;
49159243Sobrien	}
49259243Sobrien	return (new);
49359243Sobrien}
49459243Sobrien
49559243Sobrien/*
49659243Sobrien * Recursively expand a group name.  We limit the expansion to some
49759243Sobrien * fixed level to keep things from going haywire.
49859243Sobrien * Direct recursion is not expanded for convenience.
49959243Sobrien */
500167465Smp
50159243Sobrienstruct name *
50259243Sobriengexpand(nlist, gh, metoo, ntype)
50359243Sobrien	struct name *nlist;
50459243Sobrien	struct grouphead *gh;
50559243Sobrien	int metoo, ntype;
50659243Sobrien{
50759243Sobrien	struct group *gp;
50859243Sobrien	struct grouphead *ngh;
509167465Smp	struct name *np;
51059243Sobrien	static int depth;
51159243Sobrien	char *cp;
51259243Sobrien
51359243Sobrien	if (depth > MAXEXP) {
514167465Smp		printf("Expanding alias to depth larger than %d\n", MAXEXP);
51559243Sobrien		return (nlist);
516145479Smp	}
517145479Smp	depth++;
518167465Smp	for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
51959243Sobrien		cp = gp->ge_name;
52059243Sobrien		if (*cp == '\\')
52159243Sobrien			goto quote;
522231990Smp		if (strcmp(cp, gh->g_name) == 0)
523231990Smp			goto quote;
52459243Sobrien		if ((ngh = findgroup(cp)) != NULL) {
525231990Smp			nlist = gexpand(nlist, ngh, metoo, ntype);
52659243Sobrien			continue;
527231990Smp		}
528231990Smpquote:
52959243Sobrien		np = nalloc(cp, ntype);
53059243Sobrien		/*
53159243Sobrien		 * At this point should allow to expand
53259243Sobrien		 * to self if only person in group
53359243Sobrien		 */
534167465Smp		if (gp == gh->g_list && gp->ge_link == NULL)
53559243Sobrien			goto skip;
536167465Smp		if (!metoo && strcmp(cp, myname) == 0)
537167465Smp			np->n_type |= GDEL;
53859243Sobrienskip:
53959243Sobrien		nlist = put(nlist, np);
54059243Sobrien	}
541167465Smp	depth--;
54259243Sobrien	return (nlist);
54359243Sobrien}
544167465Smp
54559243Sobrien/*
54659243Sobrien * Concatenate the two passed name lists, return the result.
54759243Sobrien */
54859243Sobrienstruct name *
54969408Sachecat(n1, n2)
55059243Sobrien	struct name *n1, *n2;
55159243Sobrien{
55259243Sobrien	struct name *tail;
55359243Sobrien
55459243Sobrien	if (n1 == NULL)
55559243Sobrien		return (n2);
55659243Sobrien	if (n2 == NULL)
557167465Smp		return (n1);
558167465Smp	tail = tailof(n1);
55959243Sobrien	tail->n_flink = n2;
56059243Sobrien	n2->n_blink = tail;
56159243Sobrien	return (n1);
56259243Sobrien}
563167465Smp
56459243Sobrien/*
565145479Smp * Unpack the name list onto a vector of strings.
566145479Smp * Return an error if the name list won't fit.
56759243Sobrien */
56859243Sobrienchar **
56959243Sobrienunpack(np)
57059243Sobrien	struct name *np;
57159243Sobrien{
57259243Sobrien	char **ap, **top;
57359243Sobrien	struct name *n;
57459243Sobrien	int t, extra, metoo, verbose;
57559243Sobrien
576167465Smp	n = np;
577167465Smp	if ((t = count(n)) == 0)
578167465Smp		errx(1, "No names to unpack");
57959243Sobrien	/*
58059243Sobrien	 * Compute the number of extra arguments we will need.
58159243Sobrien	 * We need at least two extra -- one for "mail" and one for
582167465Smp	 * the terminating 0 pointer.  Additional spots may be needed
58359243Sobrien	 * to pass along -f to the host mailer.
58459243Sobrien	 */
585167465Smp	extra = 2;
58659243Sobrien	extra++;
58759243Sobrien	metoo = value("metoo") != NULL;
58869408Sache	if (metoo)
58959243Sobrien		extra++;
590231990Smp	verbose = value("verbose") != NULL;
59159243Sobrien	if (verbose)
59259243Sobrien		extra++;
59359243Sobrien	top = (char **)salloc((t + extra) * sizeof(*top));
59459243Sobrien	ap = top;
59559243Sobrien	*ap++ = "send-mail";
59659243Sobrien	*ap++ = "-i";
59759243Sobrien	if (metoo)
59859243Sobrien		*ap++ = "-m";
59959243Sobrien	if (verbose)
60059243Sobrien		*ap++ = "-v";
60159243Sobrien	for (; n != NULL; n = n->n_flink)
60259243Sobrien		if ((n->n_type & GDEL) == 0)
60359243Sobrien			*ap++ = n->n_name;
60459243Sobrien	*ap = NULL;
60559243Sobrien	return (top);
60659243Sobrien}
60759243Sobrien
60859243Sobrien/*
609167465Smp * Remove all of the duplicates from the passed name list by
61059243Sobrien * insertion sorting them, then checking for dups.
611167465Smp * Return the head of the new list.
612167465Smp */
61369408Sachestruct name *
61459243Sobrienelide(names)
615167465Smp	struct name *names;
61659243Sobrien{
61759243Sobrien	struct name *np, *t, *new;
618167465Smp	struct name *x;
61959243Sobrien
62059243Sobrien	if (names == NULL)
62159243Sobrien		return (NULL);
62259243Sobrien	new = names;
62359243Sobrien	np = names;
624167465Smp	np = np->n_flink;
62559243Sobrien	if (np != NULL)
62659243Sobrien		np->n_blink = NULL;
62759243Sobrien	new->n_flink = NULL;
62859243Sobrien	while (np != NULL) {
62959243Sobrien		t = new;
63059243Sobrien		while (strcasecmp(t->n_name, np->n_name) < 0) {
631167465Smp			if (t->n_flink == NULL)
632167465Smp				break;
63359243Sobrien			t = t->n_flink;
63459243Sobrien		}
63559243Sobrien
63659243Sobrien		/*
637167465Smp		 * If we ran out of t's, put the new entry after
63859243Sobrien		 * the current value of t.
63959243Sobrien		 */
64059243Sobrien
64159243Sobrien		if (strcasecmp(t->n_name, np->n_name) < 0) {
64259243Sobrien			t->n_flink = np;
643167465Smp			np->n_blink = t;
644167465Smp			t = np;
64559243Sobrien			np = np->n_flink;
64659243Sobrien			t->n_flink = NULL;
64759243Sobrien			continue;
648167465Smp		}
64959243Sobrien
65059243Sobrien		/*
65159243Sobrien		 * Otherwise, put the new entry in front of the
65259243Sobrien		 * current t.  If at the front of the list,
65359243Sobrien		 * the new guy becomes the new head of the list.
65459243Sobrien		 */
65559243Sobrien
65659243Sobrien		if (t == new) {
65759243Sobrien			t = np;
65859243Sobrien			np = np->n_flink;
65959243Sobrien			t->n_flink = new;
66059243Sobrien			new->n_blink = t;
66159243Sobrien			t->n_blink = NULL;
66259243Sobrien			new = t;
66359243Sobrien			continue;
664167465Smp		}
66559243Sobrien
66659243Sobrien		/*
66759243Sobrien		 * The normal case -- we are inserting into the
66859243Sobrien		 * middle of the list.
669167465Smp		 */
67059243Sobrien
671100616Smp		x = np;
67259243Sobrien		np = np->n_flink;
673100616Smp		x->n_flink = t;
674100616Smp		x->n_blink = t->n_blink;
675100616Smp		t->n_blink->n_flink = x;
676100616Smp		t->n_blink = x;
677167465Smp	}
678167465Smp
679100616Smp	/*
680167465Smp	 * Now the list headed up by new is sorted.
681167465Smp	 * Go through it and remove duplicates.
682167465Smp	 */
683167465Smp
68459243Sobrien	np = new;
685167465Smp	while (np != NULL) {
686167465Smp		t = np;
687167465Smp		while (t->n_flink != NULL &&
688167465Smp		    strcasecmp(np->n_name, t->n_flink->n_name) == 0)
689167465Smp			t = t->n_flink;
69059243Sobrien		if (t == np || t == NULL) {
69159243Sobrien			np = np->n_flink;
69259243Sobrien			continue;
693195609Smp		}
694195609Smp
69559243Sobrien		/*
69659243Sobrien		 * Now t points to the last entry with the same name
69759243Sobrien		 * as np.  Make np point beyond t.
69859243Sobrien		 */
69959243Sobrien
700167465Smp		np->n_flink = t->n_flink;
70159243Sobrien		if (t->n_flink != NULL)
70259243Sobrien			t->n_flink->n_blink = np;
70359243Sobrien		np = np->n_flink;
704167465Smp	}
705167465Smp	return (new);
70659243Sobrien}
70759243Sobrien
70859243Sobrien/*
709167465Smp * Put another node onto a list of names and return
71059243Sobrien * the list.
71159243Sobrien */
71259243Sobrienstruct name *
71359243Sobrienput(list, node)
71459243Sobrien	struct name *list, *node;
71559243Sobrien{
71659243Sobrien	node->n_flink = list;
71759243Sobrien	node->n_blink = NULL;
71859243Sobrien	if (list != NULL)
71959243Sobrien		list->n_blink = node;
72059243Sobrien	return (node);
72159243Sobrien}
72259243Sobrien
72359243Sobrien/*
72459243Sobrien * Determine the number of undeleted elements in
72559243Sobrien * a name list and return it.
72659243Sobrien */
72759243Sobrienint
72859243Sobriencount(np)
72959243Sobrien	struct name *np;
73059243Sobrien{
73159243Sobrien	int c;
73259243Sobrien
73359243Sobrien	for (c = 0; np != NULL; np = np->n_flink)
73459243Sobrien		if ((np->n_type & GDEL) == 0)
73559243Sobrien			c++;
73659243Sobrien	return (c);
737145479Smp}
738167465Smp
73959243Sobrien/*
740145479Smp * Delete the given name from a namelist.
74159243Sobrien */
74259243Sobrienstruct name *
74359243Sobriendelname(np, name)
74459243Sobrien	struct name *np;
74559243Sobrien	char name[];
74659243Sobrien{
74759243Sobrien	struct name *p;
74859243Sobrien
749167465Smp	for (p = np; p != NULL; p = p->n_flink)
75059243Sobrien		if (strcasecmp(p->n_name, name) == 0) {
75159243Sobrien			if (p->n_blink == NULL) {
75259243Sobrien				if (p->n_flink != NULL)
753167465Smp					p->n_flink->n_blink = NULL;
75459243Sobrien				np = p->n_flink;
755167465Smp				continue;
756145479Smp			}
757131962Smp			if (p->n_flink == NULL) {
758131962Smp				if (p->n_blink != NULL)
759195609Smp					p->n_blink->n_flink = NULL;
76059243Sobrien				continue;
761167465Smp			}
76259243Sobrien			p->n_blink->n_flink = p->n_flink;
76359243Sobrien			p->n_flink->n_blink = p->n_blink;
76459243Sobrien		}
76569408Sache	return (np);
76659243Sobrien}
767231990Smp
76859243Sobrien/*
76959243Sobrien * Pretty print a name list
770167465Smp * Uncomment it if you need it.
77159243Sobrien */
772195609Smp
773195609Smp/*
774195609Smpvoid
775195609Smpprettyprint(name)
776195609Smp	struct name *name;
777195609Smp{
778195609Smp	struct name *np;
779195609Smp
780195609Smp	np = name;
78169408Sache	while (np != NULL) {
78259243Sobrien		fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
78359243Sobrien		np = np->n_flink;
784167465Smp	}
785167465Smp	fprintf(stderr, "\n");
786195609Smp}
787195609Smp*/
788195609Smp