xref: /netbsd-src/usr.bin/mail/names.c (revision 4fe1ef32f37334167b71a62286b21da7d9542909)
1*4fe1ef32Schristos /*	$NetBSD: names.c,v 1.33 2017/11/09 20:27:50 christos Exp $	*/
288b833a7Schristos 
361f28255Scgd /*
42cb5542fSderaadt  * Copyright (c) 1980, 1993
52cb5542fSderaadt  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
1589aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
327c81c8f3Slukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
3488b833a7Schristos #if 0
3588b833a7Schristos static char sccsid[] = "@(#)names.c	8.1 (Berkeley) 6/6/93";
3688b833a7Schristos #else
37*4fe1ef32Schristos __RCSID("$NetBSD: names.c,v 1.33 2017/11/09 20:27:50 christos Exp $");
3888b833a7Schristos #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
4161f28255Scgd /*
4261f28255Scgd  * Mail -- a mail program
4361f28255Scgd  *
4461f28255Scgd  * Handle name lists.
4561f28255Scgd  */
4661f28255Scgd 
4761f28255Scgd #include "rcv.h"
482cb5542fSderaadt #include "extern.h"
4961f28255Scgd 
5061f28255Scgd /*
5161f28255Scgd  * Allocate a single element of a name list,
5261f28255Scgd  * initialize its name field to the passed
5361f28255Scgd  * name and return it.
5461f28255Scgd  */
55f3098750Schristos PUBLIC struct name *
nalloc(char str[],int ntype)56b127ccccSwiz nalloc(char str[], int ntype)
5761f28255Scgd {
587c81c8f3Slukem 	struct name *np;
5961f28255Scgd 
602283d346Schristos 	np = salloc(sizeof(*np));
61cb6786d4Swiz 	np->n_flink = NULL;
62cb6786d4Swiz 	np->n_blink = NULL;
6361f28255Scgd 	np->n_type = ntype;
6461f28255Scgd 	np->n_name = savestr(str);
65f3098750Schristos 	return np;
6661f28255Scgd }
6761f28255Scgd 
6861f28255Scgd /*
6961f28255Scgd  * Find the tail of a list and return it.
7061f28255Scgd  */
71f3098750Schristos static struct name *
tailof(struct name * name)72b127ccccSwiz tailof(struct name *name)
7361f28255Scgd {
747c81c8f3Slukem 	struct name *np;
7561f28255Scgd 
7661f28255Scgd 	np = name;
77cb6786d4Swiz 	if (np == NULL)
78f3098750Schristos 		return NULL;
79cb6786d4Swiz 	while (np->n_flink != NULL)
8061f28255Scgd 		np = np->n_flink;
81f3098750Schristos 	return np;
8261f28255Scgd }
8361f28255Scgd 
8461f28255Scgd /*
8561f28255Scgd  * Grab a single word (liberal word)
8661f28255Scgd  * Throw away things between ()'s, and take anything between <>.
8761f28255Scgd  */
88f3098750Schristos static char *
yankword(char * ap,char wbuf[])89b127ccccSwiz yankword(char *ap, char wbuf[])
9061f28255Scgd {
917c81c8f3Slukem 	char *cp, *cp2;
9261f28255Scgd 
9361f28255Scgd 	cp = ap;
9461f28255Scgd 	for (;;) {
9561f28255Scgd 		if (*cp == '\0')
96ab850155Swiz 			return NULL;
9761f28255Scgd 		if (*cp == '(') {
987c81c8f3Slukem 			int nesting = 0;
9961f28255Scgd 
10061f28255Scgd 			while (*cp != '\0') {
10161f28255Scgd 				switch (*cp++) {
10261f28255Scgd 				case '(':
10361f28255Scgd 					nesting++;
10461f28255Scgd 					break;
10561f28255Scgd 				case ')':
10661f28255Scgd 					--nesting;
10761f28255Scgd 					break;
10861f28255Scgd 				}
10961f28255Scgd 				if (nesting <= 0)
11061f28255Scgd 					break;
11161f28255Scgd 			}
112d727506fSchristos 		} else if (is_WSP(*cp) || *cp == ',')
11361f28255Scgd 			cp++;
11461f28255Scgd 		else
11561f28255Scgd 			break;
11661f28255Scgd 	}
11761f28255Scgd 	if (*cp ==  '<')
118f3098750Schristos 		for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>'; /*EMPTY*/)
119f3098750Schristos 			continue;
12061f28255Scgd 	else
121b1d8c616Schristos 		for (cp2 = wbuf; *cp && !strchr(" \t,(", *cp); *cp2++ = *cp++)
122f3098750Schristos 			continue;
12361f28255Scgd 	*cp2 = '\0';
12461f28255Scgd 	return cp;
12561f28255Scgd }
12661f28255Scgd 
12761f28255Scgd /*
128f3098750Schristos  * Extract a list of names from a line,
129f3098750Schristos  * and make a list of names from it.
130f3098750Schristos  * Return the list or NULL if none found.
131f3098750Schristos  */
132f3098750Schristos PUBLIC struct name *
extract(char line[],int ntype)133f3098750Schristos extract(char line[], int ntype)
134f3098750Schristos {
135f3098750Schristos 	char *cp;
136f3098750Schristos 	struct name *begin, *np, *t;
137f3098750Schristos 	char nbuf[LINESIZE];
138f3098750Schristos 
139f3098750Schristos 	if (line == NULL || *line == '\0')
140f3098750Schristos 		return NULL;
141f3098750Schristos 	begin = NULL;
142f3098750Schristos 	np = NULL;
143f3098750Schristos 	cp = line;
144f3098750Schristos 	while ((cp = yankword(cp, nbuf)) != NULL) {
145f3098750Schristos 		t = nalloc(nbuf, ntype);
146f3098750Schristos 		if (begin == NULL)
147f3098750Schristos 			begin = t;
148f3098750Schristos 		else
149f3098750Schristos 			np->n_flink = t;
150f3098750Schristos 		t->n_blink = np;
151f3098750Schristos 		np = t;
152f3098750Schristos 	}
153f3098750Schristos 	return begin;
154f3098750Schristos }
155f3098750Schristos 
156f3098750Schristos /* XXX - is this really sufficient? */
need_quotes(char * str)157f3098750Schristos static int need_quotes(char *str)
158f3098750Schristos {
159d727506fSchristos 	return strpbrk(str, " \t") != NULL;
160f3098750Schristos }
161f3098750Schristos 
162f3098750Schristos /*
163f3098750Schristos  * Turn a list of names into a string of the same names.
164f3098750Schristos  */
165f3098750Schristos PUBLIC char *
detract(struct name * np,int ntype)166f3098750Schristos detract(struct name *np, int ntype)
167f3098750Schristos {
168f3098750Schristos 	size_t s;
169f3098750Schristos 	char *cp, *begin;
170f3098750Schristos 	struct name *p;
171f3098750Schristos 	int comma;
172f3098750Schristos 	int quote;
173f3098750Schristos 
174f3098750Schristos 	quote = ntype & GSMOPTS;
175f3098750Schristos 	comma = ntype & GCOMMA;
176f3098750Schristos 	if (np == NULL)
177f3098750Schristos 		return NULL;
178f3098750Schristos 	ntype &= ~GCOMMA;
179f3098750Schristos 	s = 0;
180f3098750Schristos 	if (debug && comma)
181f3098750Schristos 		(void)fprintf(stderr, "detract asked to insert commas\n");
182f3098750Schristos 	for (p = np; p != NULL; p = p->n_flink) {
183f3098750Schristos 		if (ntype && (p->n_type & GMASK) != ntype)
184f3098750Schristos 			continue;
185f3098750Schristos 		s += strlen(p->n_name) + 1;
186f3098750Schristos 		if (comma)
187f3098750Schristos 			s++;
188f3098750Schristos 		if (quote && need_quotes(p->n_name))
189f3098750Schristos 			s += 2;
190f3098750Schristos 	}
191f3098750Schristos 	if (s == 0)
192f3098750Schristos 		return NULL;
193f3098750Schristos 	s += 2;
194f3098750Schristos 	begin = salloc(s);
195f3098750Schristos 	cp = begin;
196f3098750Schristos 	for (p = np; p != NULL; p = p->n_flink) {
197f3098750Schristos 		int do_quotes;
198f3098750Schristos 		if (ntype && (p->n_type & GMASK) != ntype)
199f3098750Schristos 			continue;
200f3098750Schristos 		do_quotes = (quote && need_quotes(p->n_name));
201f3098750Schristos 		if (do_quotes)
202f3098750Schristos 			*cp++ = '"';
203f3098750Schristos 		cp = copy(p->n_name, cp);
204f3098750Schristos 		if (comma && p->n_flink != NULL)
205f3098750Schristos 			*cp++ = ',';
206f3098750Schristos 		if (do_quotes)
207f3098750Schristos 			*cp++ = '"';
208f3098750Schristos 		*cp++ = ' ';
209f3098750Schristos 	}
210f3098750Schristos 	*--cp = 0;
211f3098750Schristos 	if (comma && *--cp == ',')
212f3098750Schristos 		*cp = 0;
213f3098750Schristos 	return begin;
214f3098750Schristos }
215f3098750Schristos 
216f3098750Schristos /*
217f3098750Schristos  * Determine if the passed address is a local "send to file" address.
218f3098750Schristos  * If any of the network metacharacters precedes any slashes, it can't
219f3098750Schristos  * be a filename.  We cheat with .'s to allow path names like ./...
220f3098750Schristos  */
221f3098750Schristos static int
isfileaddr(char * name)222f3098750Schristos isfileaddr(char *name)
223f3098750Schristos {
224f3098750Schristos 	char *cp;
225f3098750Schristos 
226f3098750Schristos 	if (*name == '+')
227f3098750Schristos 		return 1;
228f3098750Schristos 	for (cp = name; *cp; cp++) {
229f3098750Schristos 		if (*cp == '!' || *cp == '%' || *cp == '@')
230f3098750Schristos 			return 0;
231f3098750Schristos 		if (*cp == '/')
232f3098750Schristos 			return 1;
233f3098750Schristos 	}
234f3098750Schristos 	return 0;
235f3098750Schristos }
236f3098750Schristos 
237f3098750Schristos /*
23861f28255Scgd  * For each recipient in the passed name list with a /
23961f28255Scgd  * in the name, append the message to the end of the named file
24061f28255Scgd  * and remove him from the recipient list.
24161f28255Scgd  *
24261f28255Scgd  * Recipients whose name begins with | are piped through the given
24361f28255Scgd  * program and removed.
24461f28255Scgd  */
245f3098750Schristos PUBLIC struct name *
outof(struct name * names,FILE * fo,struct header * hp)246b127ccccSwiz outof(struct name *names, FILE *fo, struct header *hp)
24761f28255Scgd {
248443084c8Swiz 	int c, fd;
2494e972651Swiz 	struct name *np, *begin;
25088b833a7Schristos 	time_t now;
251ece0fd5cSchristos 	char *date;
252ece0fd5cSchristos 	const char *fname;
25361f28255Scgd 	FILE *fout, *fin;
25461f28255Scgd 	int ispipe;
255443084c8Swiz 	char tempname[PATHSIZE];
25661f28255Scgd 
2572a8765d5Schristos 	if (value("expandaddr") == NULL)
2582a8765d5Schristos 		return names;
2592a8765d5Schristos 
2604e972651Swiz 	begin = names;
26161f28255Scgd 	np = names;
26261f28255Scgd 	(void)time(&now);
26361f28255Scgd 	date = ctime(&now);
264cb6786d4Swiz 	while (np != NULL) {
26561f28255Scgd 		if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
26661f28255Scgd 			np = np->n_flink;
26761f28255Scgd 			continue;
26861f28255Scgd 		}
26961f28255Scgd 		ispipe = np->n_name[0] == '|';
27061f28255Scgd 		if (ispipe)
27161f28255Scgd 			fname = np->n_name+1;
2721db8a1b5Schristos 		else {
27361f28255Scgd 			fname = expand(np->n_name);
2741db8a1b5Schristos 			if (fname == NULL) {
2751db8a1b5Schristos 				warnx("Filename expansion of %s failed",
2761db8a1b5Schristos 				    np->n_name);
2771db8a1b5Schristos 				senderr++;
2781db8a1b5Schristos 				goto cant;
2791db8a1b5Schristos 			}
2801db8a1b5Schristos 		}
2811db8a1b5Schristos 
28261f28255Scgd 
28361f28255Scgd 		/*
28461f28255Scgd 		 * See if we have copied the complete message out yet.
28561f28255Scgd 		 * If not, do so.
28661f28255Scgd 		 */
28761f28255Scgd 
28861f28255Scgd 		if (image < 0) {
289443084c8Swiz 			(void)snprintf(tempname, sizeof(tempname),
290443084c8Swiz 			    "%s/mail.ReXXXXXXXXXXXX", tmpdir);
291443084c8Swiz 			if ((fd = mkstemp(tempname)) == -1 ||
292*4fe1ef32Schristos 			    (fout = Fdopen(fd, "aef")) == NULL) {
293443084c8Swiz 				if (fd != -1)
294ca286310Schristos 					(void)close(fd);
295443084c8Swiz 				warn("%s", tempname);
29661f28255Scgd 				senderr++;
29761f28255Scgd 				goto cant;
29861f28255Scgd 			}
2995942983dSchristos 			image = open(tempname, O_RDWR | O_CLOEXEC);
300443084c8Swiz 			(void)unlink(tempname);
30161f28255Scgd 			if (image < 0) {
302443084c8Swiz 				warn("%s", tempname);
30361f28255Scgd 				senderr++;
30461f28255Scgd 				(void)Fclose(fout);
30561f28255Scgd 				goto cant;
30661f28255Scgd 			}
307ca286310Schristos 			(void)fprintf(fout, "From %s %s", myname, date);
3088207b28aSchristos #ifdef MIME_SUPPORT
309f3098750Schristos 			(void)puthead(hp, fout, GTO|GSUBJECT|GCC|GMISC|GMIME|GNL);
3108207b28aSchristos #else
311f3098750Schristos 			(void)puthead(hp, fout, GTO|GSUBJECT|GCC|GMISC|GNL);
3128207b28aSchristos #endif
31361f28255Scgd 			while ((c = getc(fo)) != EOF)
31461f28255Scgd 				(void)putc(c, fout);
31561f28255Scgd 			rewind(fo);
31661f28255Scgd 			(void)putc('\n', fout);
31761f28255Scgd 			(void)fflush(fout);
3182f2497e5Sbad 			if (ferror(fout)) {
319443084c8Swiz 				warn("%s", tempname);
3202f2497e5Sbad 				senderr++;
3212f2497e5Sbad 				(void)Fclose(fout);
3222f2497e5Sbad 				goto cant;
3232f2497e5Sbad 			}
32461f28255Scgd 			(void)Fclose(fout);
32561f28255Scgd 		}
32661f28255Scgd 
32761f28255Scgd 		/*
32861f28255Scgd 		 * Now either copy "image" to the desired file
32961f28255Scgd 		 * or give it as the standard input to the desired
33061f28255Scgd 		 * program as appropriate.
33161f28255Scgd 		 */
33261f28255Scgd 
33361f28255Scgd 		if (ispipe) {
33461f28255Scgd 			int pid;
335ece0fd5cSchristos 			const char *shellcmd;
33688b833a7Schristos 			sigset_t nset;
33761f28255Scgd 
33861f28255Scgd 			/*
33961f28255Scgd 			 * XXX
34061f28255Scgd 			 * We can't really reuse the same image file,
34161f28255Scgd 			 * because multiple piped recipients will
34261f28255Scgd 			 * share the same lseek location and trample
34361f28255Scgd 			 * on one another.
34461f28255Scgd 			 */
345f3098750Schristos 			if ((shellcmd = value(ENAME_SHELL)) == NULL)
3464e972651Swiz 				shellcmd = _PATH_CSHELL;
347ca286310Schristos 			(void)sigemptyset(&nset);
348ca286310Schristos 			(void)sigaddset(&nset, SIGHUP);
349ca286310Schristos 			(void)sigaddset(&nset, SIGINT);
350ca286310Schristos 			(void)sigaddset(&nset, SIGQUIT);
3514e972651Swiz 			pid = start_command(shellcmd, &nset,
352ab850155Swiz 				image, -1, "-c", fname, NULL);
35361f28255Scgd 			if (pid < 0) {
35461f28255Scgd 				senderr++;
35561f28255Scgd 				goto cant;
35661f28255Scgd 			}
35761f28255Scgd 			free_child(pid);
35861f28255Scgd 		} else {
35961f28255Scgd 			int f;
360*4fe1ef32Schristos 			if ((fout = Fopen(fname, "aef")) == NULL) {
361ae38aa87Swiz 				warn("%s", fname);
36261f28255Scgd 				senderr++;
36361f28255Scgd 				goto cant;
36461f28255Scgd 			}
36561f28255Scgd 			if ((f = dup(image)) < 0) {
366ae38aa87Swiz 				warn("dup");
36761f28255Scgd 				fin = NULL;
36861f28255Scgd 			} else
369*4fe1ef32Schristos 				fin = Fdopen(f, "ref");
37061f28255Scgd 			if (fin == NULL) {
371ca286310Schristos 				(void)fprintf(stderr, "Can't reopen image\n");
37261f28255Scgd 				(void)Fclose(fout);
37361f28255Scgd 				senderr++;
37461f28255Scgd 				goto cant;
37561f28255Scgd 			}
37661f28255Scgd 			rewind(fin);
37761f28255Scgd 			while ((c = getc(fin)) != EOF)
37861f28255Scgd 				(void)putc(c, fout);
3792f2497e5Sbad 			if (ferror(fout)) {
380ae38aa87Swiz 				warn("%s", fname);
3812f2497e5Sbad 				senderr++;
3822f2497e5Sbad 				(void)Fclose(fout);
3832f2497e5Sbad 				(void)Fclose(fin);
3842f2497e5Sbad 				goto cant;
3852f2497e5Sbad 			}
38661f28255Scgd 			(void)Fclose(fout);
38761f28255Scgd 			(void)Fclose(fin);
38861f28255Scgd 		}
38961f28255Scgd cant:
39061f28255Scgd 		/*
39161f28255Scgd 		 * In days of old we removed the entry from the
39261f28255Scgd 		 * the list; now for sake of header expansion
39361f28255Scgd 		 * we leave it in and mark it as deleted.
39461f28255Scgd 		 */
39561f28255Scgd 		np->n_type |= GDEL;
39661f28255Scgd 		np = np->n_flink;
39761f28255Scgd 	}
39861f28255Scgd 	if (image >= 0) {
39961f28255Scgd 		(void)close(image);
40061f28255Scgd 		image = -1;
40161f28255Scgd 	}
402f3098750Schristos 	return begin;
40361f28255Scgd }
40461f28255Scgd 
40561f28255Scgd /*
406f3098750Schristos  * Put another node onto a list of names and return
407f3098750Schristos  * the list.
40861f28255Scgd  */
409f3098750Schristos static struct name *
put(struct name * list,struct name * node)410f3098750Schristos put(struct name *list, struct name *node)
41161f28255Scgd {
412f3098750Schristos 	node->n_flink = list;
413f3098750Schristos 	node->n_blink = NULL;
414f3098750Schristos 	if (list != NULL)
415f3098750Schristos 		list->n_blink = node;
416f3098750Schristos 	return node;
41761f28255Scgd }
41861f28255Scgd 
41961f28255Scgd /*
42061f28255Scgd  * Recursively expand a group name.  We limit the expansion to some
42161f28255Scgd  * fixed level to keep things from going haywire.
42261f28255Scgd  * Direct recursion is not expanded for convenience.
42361f28255Scgd  */
424f3098750Schristos PUBLIC struct name *
gexpand(struct name * nlist,struct grouphead * gh,int metoo,int ntype)425b127ccccSwiz gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
42661f28255Scgd {
42761f28255Scgd 	struct group *gp;
42861f28255Scgd 	struct grouphead *ngh;
42961f28255Scgd 	struct name *np;
43061f28255Scgd 	static int depth;
43161f28255Scgd 	char *cp;
43261f28255Scgd 
43361f28255Scgd 	if (depth > MAXEXP) {
434ca286310Schristos 		(void)printf("Expanding alias to depth larger than %d\n", MAXEXP);
435f3098750Schristos 		return nlist;
43661f28255Scgd 	}
43761f28255Scgd 	depth++;
438cb6786d4Swiz 	for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
43961f28255Scgd 		cp = gp->ge_name;
44061f28255Scgd 		if (*cp == '\\')
44161f28255Scgd 			goto quote;
44261f28255Scgd 		if (strcmp(cp, gh->g_name) == 0)
44361f28255Scgd 			goto quote;
444cb6786d4Swiz 		if ((ngh = findgroup(cp)) != NULL) {
44561f28255Scgd 			nlist = gexpand(nlist, ngh, metoo, ntype);
44661f28255Scgd 			continue;
44761f28255Scgd 		}
44861f28255Scgd quote:
44961f28255Scgd 		np = nalloc(cp, ntype);
45061f28255Scgd 		/*
45161f28255Scgd 		 * At this point should allow to expand
45261f28255Scgd 		 * to self if only person in group
45361f28255Scgd 		 */
454cb6786d4Swiz 		if (gp == gh->g_list && gp->ge_link == NULL)
45561f28255Scgd 			goto skip;
45661f28255Scgd 		if (!metoo && strcmp(cp, myname) == 0)
45761f28255Scgd 			np->n_type |= GDEL;
45861f28255Scgd skip:
45961f28255Scgd 		nlist = put(nlist, np);
46061f28255Scgd 	}
46161f28255Scgd 	depth--;
462f3098750Schristos 	return nlist;
463f3098750Schristos }
464f3098750Schristos 
465f3098750Schristos /*
466f3098750Schristos  * Map all of the aliased users in the invoker's mailrc
467f3098750Schristos  * file and insert them into the list.
468f3098750Schristos  * Changed after all these months of service to recursively
469f3098750Schristos  * expand names (2/14/80).
470f3098750Schristos  */
471f3098750Schristos 
472f3098750Schristos PUBLIC struct name *
usermap(struct name * names)473f3098750Schristos usermap(struct name *names)
474f3098750Schristos {
475f3098750Schristos 	struct name *new, *np, *cp;
476f3098750Schristos 	struct grouphead *gh;
477f3098750Schristos 	int metoo;
478f3098750Schristos 
479f3098750Schristos 	new = NULL;
480f3098750Schristos 	np = names;
481f3098750Schristos 	metoo = (value(ENAME_METOO) != NULL);
482f3098750Schristos 	while (np != NULL) {
483f3098750Schristos 		if (np->n_name[0] == '\\') {
484f3098750Schristos 			cp = np->n_flink;
485f3098750Schristos 			new = put(new, np);
486f3098750Schristos 			np = cp;
487f3098750Schristos 			continue;
488f3098750Schristos 		}
489f3098750Schristos 		gh = findgroup(np->n_name);
490f3098750Schristos 		cp = np->n_flink;
491f3098750Schristos 		if (gh != NULL)
492f3098750Schristos 			new = gexpand(new, gh, metoo, np->n_type);
493f3098750Schristos 		else
494f3098750Schristos 			new = put(new, np);
495f3098750Schristos 		np = cp;
496f3098750Schristos 	}
497f3098750Schristos 	return new;
49861f28255Scgd }
49961f28255Scgd 
50061f28255Scgd /*
50161f28255Scgd  * Concatenate the two passed name lists, return the result.
50261f28255Scgd  */
503f3098750Schristos PUBLIC struct name *
cat(struct name * n1,struct name * n2)504b127ccccSwiz cat(struct name *n1, struct name *n2)
50561f28255Scgd {
5067c81c8f3Slukem 	struct name *tail;
50761f28255Scgd 
508cb6786d4Swiz 	if (n1 == NULL)
509f3098750Schristos 		return n2;
510cb6786d4Swiz 	if (n2 == NULL)
511f3098750Schristos 		return n1;
51261f28255Scgd 	tail = tailof(n1);
51361f28255Scgd 	tail->n_flink = n2;
51461f28255Scgd 	n2->n_blink = tail;
515f3098750Schristos 	return n1;
516f3098750Schristos }
517f3098750Schristos 
518f3098750Schristos /*
519f3098750Schristos  * Determine the number of undeleted elements in
520f3098750Schristos  * a name list and return it.
521f3098750Schristos  */
522f3098750Schristos PUBLIC int
count(struct name * np)523f3098750Schristos count(struct name *np)
524f3098750Schristos {
525f3098750Schristos 	int c;
526f3098750Schristos 
527f3098750Schristos 	for (c = 0; np != NULL; np = np->n_flink)
528f3098750Schristos 		if ((np->n_type & GDEL) == 0)
529f3098750Schristos 			c++;
530f3098750Schristos 	return c;
53161f28255Scgd }
53261f28255Scgd 
53361f28255Scgd /*
53461f28255Scgd  * Unpack the name list onto a vector of strings.
53561f28255Scgd  * Return an error if the name list won't fit.
53661f28255Scgd  */
537f3098750Schristos PUBLIC const char **
unpack(struct name * smopts,struct name * np)5382a8765d5Schristos unpack(struct name *smopts, struct name *np)
53961f28255Scgd {
540ece0fd5cSchristos 	const char **ap, **begin;
5417c81c8f3Slukem 	struct name *n;
54261f28255Scgd 	int t, extra, metoo, verbose;
54361f28255Scgd 
54461f28255Scgd 	n = np;
54561f28255Scgd 	if ((t = count(n)) == 0)
546d449716aSchristos 		errx(EXIT_FAILURE, "No names to unpack");
54761f28255Scgd 	/*
54861f28255Scgd 	 * Compute the number of extra arguments we will need.
54961f28255Scgd 	 * We need at least two extra -- one for "mail" and one for
55061f28255Scgd 	 * the terminating 0 pointer.  Additional spots may be needed
55161f28255Scgd 	 * to pass along -f to the host mailer.
55261f28255Scgd 	 */
553e8283fa6Schristos 	extra = 3 + count(smopts);
55461f28255Scgd 	extra++;
555f3098750Schristos 	metoo = value(ENAME_METOO) != NULL;
55661f28255Scgd 	if (metoo)
55761f28255Scgd 		extra++;
558f3098750Schristos 	verbose = value(ENAME_VERBOSE) != NULL;
55961f28255Scgd 	if (verbose)
56061f28255Scgd 		extra++;
5612283d346Schristos 	begin = salloc((t + extra) * sizeof(*begin));
5624e972651Swiz 	ap = begin;
5633da129c2Sghen 	*ap++ = "sendmail";
56461f28255Scgd 	*ap++ = "-i";
56561f28255Scgd 	if (metoo)
56661f28255Scgd 		*ap++ = "-m";
56761f28255Scgd 	if (verbose)
56861f28255Scgd 		*ap++ = "-v";
5692a8765d5Schristos 	for (/*EMPTY*/; smopts != NULL; smopts = smopts->n_flink)
5702a8765d5Schristos 		if ((smopts->n_type & GDEL) == 0)
5712a8765d5Schristos 			*ap++ = smopts->n_name;
5722a8765d5Schristos 	*ap++ = "--";
573d727506fSchristos 	for (/*EMPTY*/; n != NULL; n = n->n_flink)
57461f28255Scgd 		if ((n->n_type & GDEL) == 0)
57561f28255Scgd 			*ap++ = n->n_name;
576ab850155Swiz 	*ap = NULL;
577f3098750Schristos 	return begin;
57861f28255Scgd }
57961f28255Scgd 
58061f28255Scgd /*
58161f28255Scgd  * Remove all of the duplicates from the passed name list by
58261f28255Scgd  * insertion sorting them, then checking for dups.
58361f28255Scgd  * Return the head of the new list.
58461f28255Scgd  */
585f3098750Schristos PUBLIC struct name *
elide(struct name * names)586b127ccccSwiz elide(struct name *names)
58761f28255Scgd {
5887c81c8f3Slukem 	struct name *np, *t, *new;
58961f28255Scgd 	struct name *x;
59061f28255Scgd 
591cb6786d4Swiz 	if (names == NULL)
592f3098750Schristos 		return NULL;
59361f28255Scgd 	new = names;
59461f28255Scgd 	np = names;
59561f28255Scgd 	np = np->n_flink;
596cb6786d4Swiz 	if (np != NULL)
597cb6786d4Swiz 		np->n_blink = NULL;
598cb6786d4Swiz 	new->n_flink = NULL;
599cb6786d4Swiz 	while (np != NULL) {
60061f28255Scgd 		t = new;
60161f28255Scgd 		while (strcasecmp(t->n_name, np->n_name) < 0) {
602cb6786d4Swiz 			if (t->n_flink == NULL)
60361f28255Scgd 				break;
60461f28255Scgd 			t = t->n_flink;
60561f28255Scgd 		}
60661f28255Scgd 
60761f28255Scgd 		/*
60861f28255Scgd 		 * If we ran out of t's, put the new entry after
60961f28255Scgd 		 * the current value of t.
61061f28255Scgd 		 */
61161f28255Scgd 
61261f28255Scgd 		if (strcasecmp(t->n_name, np->n_name) < 0) {
61361f28255Scgd 			t->n_flink = np;
61461f28255Scgd 			np->n_blink = t;
61561f28255Scgd 			t = np;
61661f28255Scgd 			np = np->n_flink;
617cb6786d4Swiz 			t->n_flink = NULL;
61861f28255Scgd 			continue;
61961f28255Scgd 		}
62061f28255Scgd 
62161f28255Scgd 		/*
62261f28255Scgd 		 * Otherwise, put the new entry in front of the
62361f28255Scgd 		 * current t.  If at the front of the list,
62461f28255Scgd 		 * the new guy becomes the new head of the list.
62561f28255Scgd 		 */
62661f28255Scgd 
62761f28255Scgd 		if (t == new) {
62861f28255Scgd 			t = np;
62961f28255Scgd 			np = np->n_flink;
63061f28255Scgd 			t->n_flink = new;
63161f28255Scgd 			new->n_blink = t;
632cb6786d4Swiz 			t->n_blink = NULL;
63361f28255Scgd 			new = t;
63461f28255Scgd 			continue;
63561f28255Scgd 		}
63661f28255Scgd 
63761f28255Scgd 		/*
63861f28255Scgd 		 * The normal case -- we are inserting into the
63961f28255Scgd 		 * middle of the list.
64061f28255Scgd 		 */
64161f28255Scgd 
64261f28255Scgd 		x = np;
64361f28255Scgd 		np = np->n_flink;
64461f28255Scgd 		x->n_flink = t;
64561f28255Scgd 		x->n_blink = t->n_blink;
64661f28255Scgd 		t->n_blink->n_flink = x;
64761f28255Scgd 		t->n_blink = x;
64861f28255Scgd 	}
64961f28255Scgd 
65061f28255Scgd 	/*
65161f28255Scgd 	 * Now the list headed up by new is sorted.
65261f28255Scgd 	 * Go through it and remove duplicates.
65361f28255Scgd 	 */
65461f28255Scgd 
65561f28255Scgd 	np = new;
656cb6786d4Swiz 	while (np != NULL) {
65761f28255Scgd 		t = np;
658cb6786d4Swiz 		while (t->n_flink != NULL &&
65961f28255Scgd 		       strcasecmp(np->n_name, t->n_flink->n_name) == 0)
66061f28255Scgd 			t = t->n_flink;
661cb6786d4Swiz 		if (t == np || t == NULL) {
66261f28255Scgd 			np = np->n_flink;
66361f28255Scgd 			continue;
66461f28255Scgd 		}
66561f28255Scgd 
66661f28255Scgd 		/*
66761f28255Scgd 		 * Now t points to the last entry with the same name
66861f28255Scgd 		 * as np.  Make np point beyond t.
66961f28255Scgd 		 */
67061f28255Scgd 
67161f28255Scgd 		np->n_flink = t->n_flink;
672cb6786d4Swiz 		if (t->n_flink != NULL)
67361f28255Scgd 			t->n_flink->n_blink = np;
67461f28255Scgd 		np = np->n_flink;
67561f28255Scgd 	}
676f3098750Schristos 	return new;
67761f28255Scgd }
67861f28255Scgd 
67961f28255Scgd /*
68061f28255Scgd  * Delete the given name from a namelist.
68161f28255Scgd  */
682f3098750Schristos PUBLIC struct name *
delname(struct name * np,char name[])683b127ccccSwiz delname(struct name *np, char name[])
68461f28255Scgd {
6857c81c8f3Slukem 	struct name *p;
68661f28255Scgd 
687cb6786d4Swiz 	for (p = np; p != NULL; p = p->n_flink)
68861f28255Scgd 		if (strcasecmp(p->n_name, name) == 0) {
689cb6786d4Swiz 			if (p->n_blink == NULL) {
690cb6786d4Swiz 				if (p->n_flink != NULL)
691cb6786d4Swiz 					p->n_flink->n_blink = NULL;
69261f28255Scgd 				np = p->n_flink;
69361f28255Scgd 				continue;
69461f28255Scgd 			}
695cb6786d4Swiz 			if (p->n_flink == NULL) {
696cb6786d4Swiz 				if (p->n_blink != NULL)
697cb6786d4Swiz 					p->n_blink->n_flink = NULL;
69861f28255Scgd 				continue;
69961f28255Scgd 			}
70061f28255Scgd 			p->n_blink->n_flink = p->n_flink;
70161f28255Scgd 			p->n_flink->n_blink = p->n_blink;
70261f28255Scgd 		}
70361f28255Scgd 	return np;
70461f28255Scgd }
70561f28255Scgd 
70661f28255Scgd /*
70761f28255Scgd  * Pretty print a name list
70861f28255Scgd  * Uncomment it if you need it.
70961f28255Scgd  */
710f3098750Schristos #if 0
711f3098750Schristos PUBLIC void
71261f28255Scgd prettyprint(name)
71361f28255Scgd 	struct name *name;
71461f28255Scgd {
7157c81c8f3Slukem 	struct name *np;
71661f28255Scgd 
71761f28255Scgd 	np = name;
718cb6786d4Swiz 	while (np != NULL) {
719ca286310Schristos 		(void)fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
72061f28255Scgd 		np = np->n_flink;
72161f28255Scgd 	}
722ca286310Schristos 	(void)fprintf(stderr, "\n");
72361f28255Scgd }
724f3098750Schristos #endif
725