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