1 1.33 christos /* $NetBSD: names.c,v 1.33 2017/11/09 20:27:50 christos Exp $ */ 2 1.5 christos 3 1.1 cgd /* 4 1.3 deraadt * Copyright (c) 1980, 1993 5 1.3 deraadt * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.17 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.6 lukem #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.5 christos #if 0 35 1.5 christos static char sccsid[] = "@(#)names.c 8.1 (Berkeley) 6/6/93"; 36 1.5 christos #else 37 1.33 christos __RCSID("$NetBSD: names.c,v 1.33 2017/11/09 20:27:50 christos Exp $"); 38 1.5 christos #endif 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.1 cgd /* 42 1.1 cgd * Mail -- a mail program 43 1.1 cgd * 44 1.1 cgd * Handle name lists. 45 1.1 cgd */ 46 1.1 cgd 47 1.1 cgd #include "rcv.h" 48 1.3 deraadt #include "extern.h" 49 1.1 cgd 50 1.1 cgd /* 51 1.1 cgd * Allocate a single element of a name list, 52 1.1 cgd * initialize its name field to the passed 53 1.1 cgd * name and return it. 54 1.1 cgd */ 55 1.24 christos PUBLIC struct name * 56 1.10 wiz nalloc(char str[], int ntype) 57 1.1 cgd { 58 1.6 lukem struct name *np; 59 1.1 cgd 60 1.27 christos np = salloc(sizeof(*np)); 61 1.13 wiz np->n_flink = NULL; 62 1.13 wiz np->n_blink = NULL; 63 1.1 cgd np->n_type = ntype; 64 1.1 cgd np->n_name = savestr(str); 65 1.24 christos return np; 66 1.1 cgd } 67 1.1 cgd 68 1.1 cgd /* 69 1.1 cgd * Find the tail of a list and return it. 70 1.1 cgd */ 71 1.24 christos static struct name * 72 1.10 wiz tailof(struct name *name) 73 1.1 cgd { 74 1.6 lukem struct name *np; 75 1.1 cgd 76 1.1 cgd np = name; 77 1.13 wiz if (np == NULL) 78 1.24 christos return NULL; 79 1.13 wiz while (np->n_flink != NULL) 80 1.1 cgd np = np->n_flink; 81 1.24 christos return np; 82 1.24 christos } 83 1.24 christos 84 1.24 christos /* 85 1.24 christos * Grab a single word (liberal word) 86 1.24 christos * Throw away things between ()'s, and take anything between <>. 87 1.24 christos */ 88 1.24 christos static char * 89 1.24 christos yankword(char *ap, char wbuf[]) 90 1.24 christos { 91 1.24 christos char *cp, *cp2; 92 1.24 christos 93 1.24 christos cp = ap; 94 1.24 christos for (;;) { 95 1.24 christos if (*cp == '\0') 96 1.24 christos return NULL; 97 1.24 christos if (*cp == '(') { 98 1.24 christos int nesting = 0; 99 1.24 christos 100 1.24 christos while (*cp != '\0') { 101 1.24 christos switch (*cp++) { 102 1.24 christos case '(': 103 1.24 christos nesting++; 104 1.24 christos break; 105 1.24 christos case ')': 106 1.24 christos --nesting; 107 1.24 christos break; 108 1.24 christos } 109 1.24 christos if (nesting <= 0) 110 1.24 christos break; 111 1.24 christos } 112 1.26 christos } else if (is_WSP(*cp) || *cp == ',') 113 1.24 christos cp++; 114 1.24 christos else 115 1.24 christos break; 116 1.24 christos } 117 1.24 christos if (*cp == '<') 118 1.24 christos for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>'; /*EMPTY*/) 119 1.24 christos continue; 120 1.24 christos else 121 1.24 christos for (cp2 = wbuf; *cp && !strchr(" \t,(", *cp); *cp2++ = *cp++) 122 1.24 christos continue; 123 1.24 christos *cp2 = '\0'; 124 1.24 christos return cp; 125 1.1 cgd } 126 1.1 cgd 127 1.1 cgd /* 128 1.1 cgd * Extract a list of names from a line, 129 1.1 cgd * and make a list of names from it. 130 1.13 wiz * Return the list or NULL if none found. 131 1.1 cgd */ 132 1.24 christos PUBLIC struct name * 133 1.10 wiz extract(char line[], int ntype) 134 1.1 cgd { 135 1.6 lukem char *cp; 136 1.11 wiz struct name *begin, *np, *t; 137 1.24 christos char nbuf[LINESIZE]; 138 1.1 cgd 139 1.12 wiz if (line == NULL || *line == '\0') 140 1.13 wiz return NULL; 141 1.13 wiz begin = NULL; 142 1.13 wiz np = NULL; 143 1.1 cgd cp = line; 144 1.12 wiz while ((cp = yankword(cp, nbuf)) != NULL) { 145 1.1 cgd t = nalloc(nbuf, ntype); 146 1.13 wiz if (begin == NULL) 147 1.11 wiz begin = t; 148 1.1 cgd else 149 1.1 cgd np->n_flink = t; 150 1.1 cgd t->n_blink = np; 151 1.1 cgd np = t; 152 1.1 cgd } 153 1.11 wiz return begin; 154 1.1 cgd } 155 1.1 cgd 156 1.21 christos /* XXX - is this really sufficient? */ 157 1.21 christos static int need_quotes(char *str) 158 1.21 christos { 159 1.26 christos return strpbrk(str, " \t") != NULL; 160 1.21 christos } 161 1.21 christos 162 1.1 cgd /* 163 1.1 cgd * Turn a list of names into a string of the same names. 164 1.1 cgd */ 165 1.24 christos PUBLIC char * 166 1.10 wiz detract(struct name *np, int ntype) 167 1.1 cgd { 168 1.19 christos size_t s; 169 1.11 wiz char *cp, *begin; 170 1.6 lukem struct name *p; 171 1.6 lukem int comma; 172 1.21 christos int quote; 173 1.1 cgd 174 1.21 christos quote = ntype & GSMOPTS; 175 1.1 cgd comma = ntype & GCOMMA; 176 1.13 wiz if (np == NULL) 177 1.24 christos return NULL; 178 1.1 cgd ntype &= ~GCOMMA; 179 1.1 cgd s = 0; 180 1.1 cgd if (debug && comma) 181 1.19 christos (void)fprintf(stderr, "detract asked to insert commas\n"); 182 1.13 wiz for (p = np; p != NULL; p = p->n_flink) { 183 1.1 cgd if (ntype && (p->n_type & GMASK) != ntype) 184 1.1 cgd continue; 185 1.1 cgd s += strlen(p->n_name) + 1; 186 1.1 cgd if (comma) 187 1.1 cgd s++; 188 1.21 christos if (quote && need_quotes(p->n_name)) 189 1.21 christos s += 2; 190 1.1 cgd } 191 1.1 cgd if (s == 0) 192 1.24 christos return NULL; 193 1.1 cgd s += 2; 194 1.11 wiz begin = salloc(s); 195 1.11 wiz cp = begin; 196 1.13 wiz for (p = np; p != NULL; p = p->n_flink) { 197 1.21 christos int do_quotes; 198 1.1 cgd if (ntype && (p->n_type & GMASK) != ntype) 199 1.1 cgd continue; 200 1.21 christos do_quotes = (quote && need_quotes(p->n_name)); 201 1.21 christos if (do_quotes) 202 1.21 christos *cp++ = '"'; 203 1.1 cgd cp = copy(p->n_name, cp); 204 1.13 wiz if (comma && p->n_flink != NULL) 205 1.1 cgd *cp++ = ','; 206 1.21 christos if (do_quotes) 207 1.21 christos *cp++ = '"'; 208 1.1 cgd *cp++ = ' '; 209 1.1 cgd } 210 1.1 cgd *--cp = 0; 211 1.1 cgd if (comma && *--cp == ',') 212 1.1 cgd *cp = 0; 213 1.24 christos return begin; 214 1.1 cgd } 215 1.1 cgd 216 1.1 cgd /* 217 1.24 christos * Determine if the passed address is a local "send to file" address. 218 1.24 christos * If any of the network metacharacters precedes any slashes, it can't 219 1.24 christos * be a filename. We cheat with .'s to allow path names like ./... 220 1.1 cgd */ 221 1.24 christos static int 222 1.24 christos isfileaddr(char *name) 223 1.1 cgd { 224 1.24 christos char *cp; 225 1.1 cgd 226 1.24 christos if (*name == '+') 227 1.24 christos return 1; 228 1.24 christos for (cp = name; *cp; cp++) { 229 1.24 christos if (*cp == '!' || *cp == '%' || *cp == '@') 230 1.24 christos return 0; 231 1.24 christos if (*cp == '/') 232 1.24 christos return 1; 233 1.1 cgd } 234 1.24 christos return 0; 235 1.1 cgd } 236 1.1 cgd 237 1.1 cgd /* 238 1.1 cgd * For each recipient in the passed name list with a / 239 1.1 cgd * in the name, append the message to the end of the named file 240 1.1 cgd * and remove him from the recipient list. 241 1.1 cgd * 242 1.1 cgd * Recipients whose name begins with | are piped through the given 243 1.1 cgd * program and removed. 244 1.1 cgd */ 245 1.24 christos PUBLIC struct name * 246 1.10 wiz outof(struct name *names, FILE *fo, struct header *hp) 247 1.1 cgd { 248 1.16 wiz int c, fd; 249 1.11 wiz struct name *np, *begin; 250 1.5 christos time_t now; 251 1.18 christos char *date; 252 1.18 christos const char *fname; 253 1.1 cgd FILE *fout, *fin; 254 1.1 cgd int ispipe; 255 1.16 wiz char tempname[PATHSIZE]; 256 1.1 cgd 257 1.31 christos if (value("expandaddr") == NULL) 258 1.31 christos return names; 259 1.31 christos 260 1.11 wiz begin = names; 261 1.1 cgd np = names; 262 1.14 wiz (void)time(&now); 263 1.1 cgd date = ctime(&now); 264 1.13 wiz while (np != NULL) { 265 1.1 cgd if (!isfileaddr(np->n_name) && np->n_name[0] != '|') { 266 1.1 cgd np = np->n_flink; 267 1.1 cgd continue; 268 1.1 cgd } 269 1.1 cgd ispipe = np->n_name[0] == '|'; 270 1.1 cgd if (ispipe) 271 1.1 cgd fname = np->n_name+1; 272 1.30 christos else { 273 1.1 cgd fname = expand(np->n_name); 274 1.30 christos if (fname == NULL) { 275 1.30 christos warnx("Filename expansion of %s failed", 276 1.30 christos np->n_name); 277 1.30 christos senderr++; 278 1.30 christos goto cant; 279 1.30 christos } 280 1.30 christos } 281 1.30 christos 282 1.1 cgd 283 1.1 cgd /* 284 1.1 cgd * See if we have copied the complete message out yet. 285 1.1 cgd * If not, do so. 286 1.1 cgd */ 287 1.1 cgd 288 1.1 cgd if (image < 0) { 289 1.16 wiz (void)snprintf(tempname, sizeof(tempname), 290 1.16 wiz "%s/mail.ReXXXXXXXXXXXX", tmpdir); 291 1.16 wiz if ((fd = mkstemp(tempname)) == -1 || 292 1.33 christos (fout = Fdopen(fd, "aef")) == NULL) { 293 1.16 wiz if (fd != -1) 294 1.19 christos (void)close(fd); 295 1.16 wiz warn("%s", tempname); 296 1.1 cgd senderr++; 297 1.1 cgd goto cant; 298 1.1 cgd } 299 1.29 christos image = open(tempname, O_RDWR | O_CLOEXEC); 300 1.16 wiz (void)unlink(tempname); 301 1.1 cgd if (image < 0) { 302 1.16 wiz warn("%s", tempname); 303 1.1 cgd senderr++; 304 1.14 wiz (void)Fclose(fout); 305 1.1 cgd goto cant; 306 1.1 cgd } 307 1.19 christos (void)fprintf(fout, "From %s %s", myname, date); 308 1.22 christos #ifdef MIME_SUPPORT 309 1.24 christos (void)puthead(hp, fout, GTO|GSUBJECT|GCC|GMISC|GMIME|GNL); 310 1.22 christos #else 311 1.24 christos (void)puthead(hp, fout, GTO|GSUBJECT|GCC|GMISC|GNL); 312 1.22 christos #endif 313 1.1 cgd while ((c = getc(fo)) != EOF) 314 1.14 wiz (void)putc(c, fout); 315 1.1 cgd rewind(fo); 316 1.14 wiz (void)putc('\n', fout); 317 1.14 wiz (void)fflush(fout); 318 1.7 bad if (ferror(fout)) { 319 1.16 wiz warn("%s", tempname); 320 1.7 bad senderr++; 321 1.14 wiz (void)Fclose(fout); 322 1.7 bad goto cant; 323 1.7 bad } 324 1.14 wiz (void)Fclose(fout); 325 1.1 cgd } 326 1.1 cgd 327 1.1 cgd /* 328 1.1 cgd * Now either copy "image" to the desired file 329 1.1 cgd * or give it as the standard input to the desired 330 1.1 cgd * program as appropriate. 331 1.1 cgd */ 332 1.1 cgd 333 1.1 cgd if (ispipe) { 334 1.1 cgd int pid; 335 1.18 christos const char *shellcmd; 336 1.5 christos sigset_t nset; 337 1.1 cgd 338 1.1 cgd /* 339 1.1 cgd * XXX 340 1.1 cgd * We can't really reuse the same image file, 341 1.1 cgd * because multiple piped recipients will 342 1.1 cgd * share the same lseek location and trample 343 1.1 cgd * on one another. 344 1.1 cgd */ 345 1.24 christos if ((shellcmd = value(ENAME_SHELL)) == NULL) 346 1.11 wiz shellcmd = _PATH_CSHELL; 347 1.19 christos (void)sigemptyset(&nset); 348 1.19 christos (void)sigaddset(&nset, SIGHUP); 349 1.19 christos (void)sigaddset(&nset, SIGINT); 350 1.19 christos (void)sigaddset(&nset, SIGQUIT); 351 1.11 wiz pid = start_command(shellcmd, &nset, 352 1.12 wiz image, -1, "-c", fname, NULL); 353 1.1 cgd if (pid < 0) { 354 1.1 cgd senderr++; 355 1.1 cgd goto cant; 356 1.1 cgd } 357 1.1 cgd free_child(pid); 358 1.1 cgd } else { 359 1.1 cgd int f; 360 1.33 christos if ((fout = Fopen(fname, "aef")) == NULL) { 361 1.15 wiz warn("%s", fname); 362 1.1 cgd senderr++; 363 1.1 cgd goto cant; 364 1.1 cgd } 365 1.1 cgd if ((f = dup(image)) < 0) { 366 1.15 wiz warn("dup"); 367 1.1 cgd fin = NULL; 368 1.1 cgd } else 369 1.33 christos fin = Fdopen(f, "ref"); 370 1.1 cgd if (fin == NULL) { 371 1.19 christos (void)fprintf(stderr, "Can't reopen image\n"); 372 1.14 wiz (void)Fclose(fout); 373 1.1 cgd senderr++; 374 1.1 cgd goto cant; 375 1.1 cgd } 376 1.1 cgd rewind(fin); 377 1.1 cgd while ((c = getc(fin)) != EOF) 378 1.14 wiz (void)putc(c, fout); 379 1.7 bad if (ferror(fout)) { 380 1.15 wiz warn("%s", fname); 381 1.7 bad senderr++; 382 1.14 wiz (void)Fclose(fout); 383 1.14 wiz (void)Fclose(fin); 384 1.7 bad goto cant; 385 1.7 bad } 386 1.14 wiz (void)Fclose(fout); 387 1.14 wiz (void)Fclose(fin); 388 1.1 cgd } 389 1.1 cgd cant: 390 1.1 cgd /* 391 1.1 cgd * In days of old we removed the entry from the 392 1.1 cgd * the list; now for sake of header expansion 393 1.1 cgd * we leave it in and mark it as deleted. 394 1.1 cgd */ 395 1.1 cgd np->n_type |= GDEL; 396 1.1 cgd np = np->n_flink; 397 1.1 cgd } 398 1.1 cgd if (image >= 0) { 399 1.14 wiz (void)close(image); 400 1.1 cgd image = -1; 401 1.1 cgd } 402 1.24 christos return begin; 403 1.1 cgd } 404 1.1 cgd 405 1.1 cgd /* 406 1.24 christos * Put another node onto a list of names and return 407 1.24 christos * the list. 408 1.1 cgd */ 409 1.24 christos static struct name * 410 1.24 christos put(struct name *list, struct name *node) 411 1.1 cgd { 412 1.24 christos node->n_flink = list; 413 1.24 christos node->n_blink = NULL; 414 1.24 christos if (list != NULL) 415 1.24 christos list->n_blink = node; 416 1.24 christos return node; 417 1.1 cgd } 418 1.1 cgd 419 1.1 cgd /* 420 1.1 cgd * Recursively expand a group name. We limit the expansion to some 421 1.1 cgd * fixed level to keep things from going haywire. 422 1.1 cgd * Direct recursion is not expanded for convenience. 423 1.1 cgd */ 424 1.24 christos PUBLIC struct name * 425 1.10 wiz gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype) 426 1.1 cgd { 427 1.1 cgd struct group *gp; 428 1.1 cgd struct grouphead *ngh; 429 1.1 cgd struct name *np; 430 1.1 cgd static int depth; 431 1.1 cgd char *cp; 432 1.1 cgd 433 1.1 cgd if (depth > MAXEXP) { 434 1.19 christos (void)printf("Expanding alias to depth larger than %d\n", MAXEXP); 435 1.24 christos return nlist; 436 1.1 cgd } 437 1.1 cgd depth++; 438 1.13 wiz for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) { 439 1.1 cgd cp = gp->ge_name; 440 1.1 cgd if (*cp == '\\') 441 1.1 cgd goto quote; 442 1.1 cgd if (strcmp(cp, gh->g_name) == 0) 443 1.1 cgd goto quote; 444 1.13 wiz if ((ngh = findgroup(cp)) != NULL) { 445 1.1 cgd nlist = gexpand(nlist, ngh, metoo, ntype); 446 1.1 cgd continue; 447 1.1 cgd } 448 1.1 cgd quote: 449 1.1 cgd np = nalloc(cp, ntype); 450 1.1 cgd /* 451 1.1 cgd * At this point should allow to expand 452 1.1 cgd * to self if only person in group 453 1.1 cgd */ 454 1.13 wiz if (gp == gh->g_list && gp->ge_link == NULL) 455 1.1 cgd goto skip; 456 1.1 cgd if (!metoo && strcmp(cp, myname) == 0) 457 1.1 cgd np->n_type |= GDEL; 458 1.1 cgd skip: 459 1.1 cgd nlist = put(nlist, np); 460 1.1 cgd } 461 1.1 cgd depth--; 462 1.24 christos return nlist; 463 1.24 christos } 464 1.24 christos 465 1.24 christos /* 466 1.24 christos * Map all of the aliased users in the invoker's mailrc 467 1.24 christos * file and insert them into the list. 468 1.24 christos * Changed after all these months of service to recursively 469 1.24 christos * expand names (2/14/80). 470 1.24 christos */ 471 1.24 christos 472 1.24 christos PUBLIC struct name * 473 1.24 christos usermap(struct name *names) 474 1.24 christos { 475 1.24 christos struct name *new, *np, *cp; 476 1.24 christos struct grouphead *gh; 477 1.24 christos int metoo; 478 1.24 christos 479 1.24 christos new = NULL; 480 1.24 christos np = names; 481 1.24 christos metoo = (value(ENAME_METOO) != NULL); 482 1.24 christos while (np != NULL) { 483 1.24 christos if (np->n_name[0] == '\\') { 484 1.24 christos cp = np->n_flink; 485 1.24 christos new = put(new, np); 486 1.24 christos np = cp; 487 1.24 christos continue; 488 1.24 christos } 489 1.24 christos gh = findgroup(np->n_name); 490 1.24 christos cp = np->n_flink; 491 1.24 christos if (gh != NULL) 492 1.24 christos new = gexpand(new, gh, metoo, np->n_type); 493 1.24 christos else 494 1.24 christos new = put(new, np); 495 1.24 christos np = cp; 496 1.24 christos } 497 1.24 christos return new; 498 1.1 cgd } 499 1.1 cgd 500 1.1 cgd /* 501 1.1 cgd * Concatenate the two passed name lists, return the result. 502 1.1 cgd */ 503 1.24 christos PUBLIC struct name * 504 1.10 wiz cat(struct name *n1, struct name *n2) 505 1.1 cgd { 506 1.6 lukem struct name *tail; 507 1.1 cgd 508 1.13 wiz if (n1 == NULL) 509 1.24 christos return n2; 510 1.13 wiz if (n2 == NULL) 511 1.24 christos return n1; 512 1.1 cgd tail = tailof(n1); 513 1.1 cgd tail->n_flink = n2; 514 1.1 cgd n2->n_blink = tail; 515 1.24 christos return n1; 516 1.24 christos } 517 1.24 christos 518 1.24 christos /* 519 1.24 christos * Determine the number of undeleted elements in 520 1.24 christos * a name list and return it. 521 1.24 christos */ 522 1.24 christos PUBLIC int 523 1.24 christos count(struct name *np) 524 1.24 christos { 525 1.24 christos int c; 526 1.24 christos 527 1.24 christos for (c = 0; np != NULL; np = np->n_flink) 528 1.24 christos if ((np->n_type & GDEL) == 0) 529 1.24 christos c++; 530 1.24 christos return c; 531 1.1 cgd } 532 1.1 cgd 533 1.1 cgd /* 534 1.1 cgd * Unpack the name list onto a vector of strings. 535 1.1 cgd * Return an error if the name list won't fit. 536 1.1 cgd */ 537 1.24 christos PUBLIC const char ** 538 1.31 christos unpack(struct name *smopts, struct name *np) 539 1.1 cgd { 540 1.18 christos const char **ap, **begin; 541 1.6 lukem struct name *n; 542 1.1 cgd int t, extra, metoo, verbose; 543 1.1 cgd 544 1.1 cgd n = np; 545 1.1 cgd if ((t = count(n)) == 0) 546 1.28 christos errx(EXIT_FAILURE, "No names to unpack"); 547 1.1 cgd /* 548 1.1 cgd * Compute the number of extra arguments we will need. 549 1.1 cgd * We need at least two extra -- one for "mail" and one for 550 1.1 cgd * the terminating 0 pointer. Additional spots may be needed 551 1.1 cgd * to pass along -f to the host mailer. 552 1.1 cgd */ 553 1.32 christos extra = 3 + count(smopts); 554 1.1 cgd extra++; 555 1.24 christos metoo = value(ENAME_METOO) != NULL; 556 1.1 cgd if (metoo) 557 1.1 cgd extra++; 558 1.24 christos verbose = value(ENAME_VERBOSE) != NULL; 559 1.1 cgd if (verbose) 560 1.1 cgd extra++; 561 1.27 christos begin = salloc((t + extra) * sizeof(*begin)); 562 1.11 wiz ap = begin; 563 1.20 ghen *ap++ = "sendmail"; 564 1.1 cgd *ap++ = "-i"; 565 1.1 cgd if (metoo) 566 1.1 cgd *ap++ = "-m"; 567 1.1 cgd if (verbose) 568 1.1 cgd *ap++ = "-v"; 569 1.31 christos for (/*EMPTY*/; smopts != NULL; smopts = smopts->n_flink) 570 1.31 christos if ((smopts->n_type & GDEL) == 0) 571 1.31 christos *ap++ = smopts->n_name; 572 1.31 christos *ap++ = "--"; 573 1.26 christos for (/*EMPTY*/; n != NULL; n = n->n_flink) 574 1.1 cgd if ((n->n_type & GDEL) == 0) 575 1.1 cgd *ap++ = n->n_name; 576 1.12 wiz *ap = NULL; 577 1.24 christos return begin; 578 1.1 cgd } 579 1.1 cgd 580 1.1 cgd /* 581 1.1 cgd * Remove all of the duplicates from the passed name list by 582 1.1 cgd * insertion sorting them, then checking for dups. 583 1.1 cgd * Return the head of the new list. 584 1.1 cgd */ 585 1.24 christos PUBLIC struct name * 586 1.10 wiz elide(struct name *names) 587 1.1 cgd { 588 1.6 lukem struct name *np, *t, *new; 589 1.1 cgd struct name *x; 590 1.1 cgd 591 1.13 wiz if (names == NULL) 592 1.24 christos return NULL; 593 1.1 cgd new = names; 594 1.1 cgd np = names; 595 1.1 cgd np = np->n_flink; 596 1.13 wiz if (np != NULL) 597 1.13 wiz np->n_blink = NULL; 598 1.13 wiz new->n_flink = NULL; 599 1.13 wiz while (np != NULL) { 600 1.1 cgd t = new; 601 1.1 cgd while (strcasecmp(t->n_name, np->n_name) < 0) { 602 1.13 wiz if (t->n_flink == NULL) 603 1.1 cgd break; 604 1.1 cgd t = t->n_flink; 605 1.1 cgd } 606 1.1 cgd 607 1.1 cgd /* 608 1.1 cgd * If we ran out of t's, put the new entry after 609 1.1 cgd * the current value of t. 610 1.1 cgd */ 611 1.1 cgd 612 1.1 cgd if (strcasecmp(t->n_name, np->n_name) < 0) { 613 1.1 cgd t->n_flink = np; 614 1.1 cgd np->n_blink = t; 615 1.1 cgd t = np; 616 1.1 cgd np = np->n_flink; 617 1.13 wiz t->n_flink = NULL; 618 1.1 cgd continue; 619 1.1 cgd } 620 1.1 cgd 621 1.1 cgd /* 622 1.1 cgd * Otherwise, put the new entry in front of the 623 1.1 cgd * current t. If at the front of the list, 624 1.1 cgd * the new guy becomes the new head of the list. 625 1.1 cgd */ 626 1.1 cgd 627 1.1 cgd if (t == new) { 628 1.1 cgd t = np; 629 1.1 cgd np = np->n_flink; 630 1.1 cgd t->n_flink = new; 631 1.1 cgd new->n_blink = t; 632 1.13 wiz t->n_blink = NULL; 633 1.1 cgd new = t; 634 1.1 cgd continue; 635 1.1 cgd } 636 1.1 cgd 637 1.1 cgd /* 638 1.1 cgd * The normal case -- we are inserting into the 639 1.1 cgd * middle of the list. 640 1.1 cgd */ 641 1.1 cgd 642 1.1 cgd x = np; 643 1.1 cgd np = np->n_flink; 644 1.1 cgd x->n_flink = t; 645 1.1 cgd x->n_blink = t->n_blink; 646 1.1 cgd t->n_blink->n_flink = x; 647 1.1 cgd t->n_blink = x; 648 1.1 cgd } 649 1.1 cgd 650 1.1 cgd /* 651 1.1 cgd * Now the list headed up by new is sorted. 652 1.1 cgd * Go through it and remove duplicates. 653 1.1 cgd */ 654 1.1 cgd 655 1.1 cgd np = new; 656 1.13 wiz while (np != NULL) { 657 1.1 cgd t = np; 658 1.13 wiz while (t->n_flink != NULL && 659 1.1 cgd strcasecmp(np->n_name, t->n_flink->n_name) == 0) 660 1.1 cgd t = t->n_flink; 661 1.13 wiz if (t == np || t == NULL) { 662 1.1 cgd np = np->n_flink; 663 1.1 cgd continue; 664 1.1 cgd } 665 1.26 christos 666 1.1 cgd /* 667 1.1 cgd * Now t points to the last entry with the same name 668 1.1 cgd * as np. Make np point beyond t. 669 1.1 cgd */ 670 1.1 cgd 671 1.1 cgd np->n_flink = t->n_flink; 672 1.13 wiz if (t->n_flink != NULL) 673 1.1 cgd t->n_flink->n_blink = np; 674 1.1 cgd np = np->n_flink; 675 1.1 cgd } 676 1.24 christos return new; 677 1.1 cgd } 678 1.1 cgd 679 1.1 cgd /* 680 1.1 cgd * Delete the given name from a namelist. 681 1.1 cgd */ 682 1.24 christos PUBLIC struct name * 683 1.10 wiz delname(struct name *np, char name[]) 684 1.1 cgd { 685 1.6 lukem struct name *p; 686 1.1 cgd 687 1.13 wiz for (p = np; p != NULL; p = p->n_flink) 688 1.1 cgd if (strcasecmp(p->n_name, name) == 0) { 689 1.13 wiz if (p->n_blink == NULL) { 690 1.13 wiz if (p->n_flink != NULL) 691 1.13 wiz p->n_flink->n_blink = NULL; 692 1.1 cgd np = p->n_flink; 693 1.1 cgd continue; 694 1.1 cgd } 695 1.13 wiz if (p->n_flink == NULL) { 696 1.13 wiz if (p->n_blink != NULL) 697 1.13 wiz p->n_blink->n_flink = NULL; 698 1.1 cgd continue; 699 1.1 cgd } 700 1.1 cgd p->n_blink->n_flink = p->n_flink; 701 1.1 cgd p->n_flink->n_blink = p->n_blink; 702 1.1 cgd } 703 1.1 cgd return np; 704 1.1 cgd } 705 1.1 cgd 706 1.1 cgd /* 707 1.1 cgd * Pretty print a name list 708 1.1 cgd * Uncomment it if you need it. 709 1.1 cgd */ 710 1.24 christos #if 0 711 1.24 christos PUBLIC void 712 1.1 cgd prettyprint(name) 713 1.1 cgd struct name *name; 714 1.1 cgd { 715 1.6 lukem struct name *np; 716 1.1 cgd 717 1.1 cgd np = name; 718 1.13 wiz while (np != NULL) { 719 1.19 christos (void)fprintf(stderr, "%s(%d) ", np->n_name, np->n_type); 720 1.1 cgd np = np->n_flink; 721 1.1 cgd } 722 1.19 christos (void)fprintf(stderr, "\n"); 723 1.1 cgd } 724 1.24 christos #endif 725