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