1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 
  23 /*
  24  * Copyright 2001 Sun Microsystems, Inc.  All rights reserved.
  25  * Use is subject to license terms.
  26  */
  27 
  28 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  29 /*        All Rights Reserved   */
  30 
  31 /*
  32  * University Copyright- Copyright (c) 1982, 1986, 1988
  33  * The Regents of the University of California
  34  * All Rights Reserved
  35  *
  36  * University Acknowledgment- Portions of this document are derived from
  37  * software developed by the University of California, Berkeley, and its
  38  * contributors.
  39  */
  40 
  41 #pragma ident   "%Z%%M% %I%     %E% SMI"
  42 
  43 /*
  44  * mailx -- a modified version of a University of California at Berkeley
  45  *      mail program
  46  *
  47  * Handle name lists.
  48  */
  49 
  50 #include "rcv.h"
  51 #include <locale.h>
  52 
  53 static struct name      *nalloc(char str[]);
  54 static int              isfileaddr(char *name);
  55 static int              lengthof(struct name *name);
  56 static struct name      *gexpand(struct name *nlist, struct grouphead *gh, int metoo, int arg_ntype);
  57 static char             *norm(register char *user, register char *ubuf, int nbangs);
  58 static struct name      *put(struct name *list, struct name *node);
  59 
  60 /*
  61  * Allocate a single element of a name list,
  62  * initialize its name field to the passed
  63  * name and return it.
  64  */
  65 
  66 static struct name *
  67 nalloc(char str[])
  68 {
  69         register struct name *np;
  70 
  71         np = (struct name *) salloc(sizeof *np);
  72         np->n_flink = NIL;
  73         np->n_blink = NIL;
  74         np->n_type = -1;
  75         np->n_full = savestr(str);
  76         np->n_name = skin(np->n_full);
  77         return(np);
  78 }
  79 
  80 /*
  81  * Find the tail of a list and return it.
  82  */
  83 
  84 struct name *
  85 tailof(struct name *name)
  86 {
  87         register struct name *np;
  88 
  89         np = name;
  90         if (np == NIL)
  91                 return(NIL);
  92         while (np->n_flink != NIL)
  93                 np = np->n_flink;
  94         return(np);
  95 }
  96 
  97 /*
  98  * Extract a list of names from a line,
  99  * and make a list of names from it.
 100  * Return the list or NIL if none found.
 101  */
 102 
 103 struct name *
 104 extract(char line[], int arg_ntype)
 105 {
 106         short ntype = (short)arg_ntype;
 107         register char *cp;
 108         register struct name *top, *np, *t;
 109         char nbuf[BUFSIZ], abuf[BUFSIZ];
 110         int comma;
 111 
 112         if (line == NOSTR || strlen(line) == 0)
 113                 return(NIL);
 114         comma = docomma(line);
 115         top = NIL;
 116         np = NIL;
 117         cp = line;
 118         while ((cp = yankword(cp, nbuf, sizeof (nbuf), comma)) != NOSTR) {
 119                 if (np != NIL && equal(nbuf, "at")) {
 120                         nstrcpy(abuf, sizeof (abuf), nbuf);
 121                         if ((cp = yankword(cp, nbuf, sizeof (nbuf),
 122                                 comma)) == NOSTR) {
 123                                 nstrcpy(nbuf, sizeof (nbuf), abuf);
 124                                 goto normal;
 125                         }
 126                         snprintf(abuf, sizeof (abuf), "%s@%s", np->n_name,
 127                                 nbuf); 
 128                         np->n_name = savestr(abuf);
 129                         continue;
 130                 }
 131 normal:
 132                 t = nalloc(nbuf);
 133                 t->n_type = ntype;
 134                 if (top == NIL)
 135                         top = t;
 136                 else
 137                         np->n_flink = t;
 138                 t->n_blink = np;
 139                 np = t;
 140         }
 141         return(top);
 142 }
 143 
 144 /*
 145  * Turn a list of names into a string of the same names.
 146  */
 147 
 148 char *
 149 detract(register struct name *np, int ntype)
 150 {
 151         register int s;
 152         register char *cp, *top;
 153         register struct name *p;
 154 
 155         if (np == NIL)
 156                 return(NOSTR);
 157         s = 0;
 158         for (p = np; p != NIL; p = p->n_flink) {
 159                 if ((ntype && (p->n_type & GMASK) != ntype)
 160                  || (p->n_type & GDEL))
 161                         continue;
 162                 s += strlen(p->n_full) + 2;
 163         }
 164         if (s == 0)
 165                 return(NOSTR);
 166         top = (char *)salloc((unsigned)(++s));
 167         cp = top;
 168         for (p = np; p != NIL; p = p->n_flink) {
 169                 if ((ntype && (p->n_type & GMASK) != ntype)
 170                  || (p->n_type & GDEL))
 171                         continue;
 172                 cp = copy(p->n_full, cp);
 173                 *cp++ = ',';
 174                 *cp++ = ' ';
 175         }
 176         *cp = 0;
 177         return(top);
 178 }
 179 
 180 struct name *
 181 outpre(struct name *to)
 182 {
 183         register struct name *np;
 184 
 185         for (np = to; np; np = np->n_flink)
 186                 if (isfileaddr(np->n_name))
 187                         np->n_type |= GDEL;
 188         return to;
 189 }
 190 
 191 /*
 192  * For each recipient in the passed name list with a /
 193  * in the name, append the message to the end of the named file
 194  * and remove him from the recipient list.
 195  *
 196  * Recipients whose name begins with | are piped through the given
 197  * program and removed.
 198  */
 199 
 200 int
 201 outof(struct name *names, FILE *fo)
 202 {
 203         register int c;
 204         register struct name *np;
 205         time_t now;
 206         char *date, *fname, *shell;
 207         FILE *fout, *fin;
 208         int ispipe;
 209         int nout = 0;
 210         int fd = 0; 
 211 #ifdef preSVr4
 212         char line[BUFSIZ];
 213 #endif
 214 
 215         for (np = names; np != NIL; np = np->n_flink) {
 216                 if (!isfileaddr(np->n_name) && np->n_name[0] != '|')
 217                         continue;
 218                 nout++;
 219                 ispipe = np->n_name[0] == '|';
 220                 if (ispipe)
 221                         fname = np->n_name+1;
 222                 else
 223                         fname = safeexpand(np->n_name);
 224 
 225                 /*
 226                  * See if we have copied the complete message out yet.
 227                  * If not, do so.
 228                  */
 229 
 230                 if (image < 0) {
 231                         fd = open(tempEdit, O_CREAT|O_EXCL|O_APPEND|O_WRONLY,
 232                                         0600);
 233                         if ((fd  < 0) && (errno == EEXIST)) {
 234                                 if ((fd = open(tempEdit, O_APPEND|O_WRONLY,
 235                                                 0600)) < 0) {
 236                                         perror(tempEdit);
 237                                         senderr++;
 238                                         goto cant;
 239                                 }
 240                         }
 241                         if ((fout = fdopen(fd, "a")) == NULL) {
 242                                 perror(tempEdit);
 243                                 senderr++;
 244                                 goto cant;
 245                         }
 246                         image = open(tempEdit, O_RDWR);
 247                         unlink(tempEdit);
 248                         if (image < 0) {
 249                                 perror(tempEdit);
 250                                 senderr++;
 251                                 goto cant;
 252                         } else {
 253                                 rewind(fo);
 254                                 time(&now);
 255                                 date = ctime(&now);
 256                                 fprintf(fout, "From %s %s", myname, date);
 257                                 while ((c = getc(fo)) != EOF)
 258                                         putc(c, fout);
 259                                 rewind(fo);
 260                                 fflush(fout);
 261                                 if (fferror(fout))
 262                                         perror(tempEdit);
 263                                 fclose(fout);
 264                         }
 265                 }
 266 
 267                 /*
 268                  * Now either copy "image" to the desired file
 269                  * or give it as the standard input to the desired
 270                  * program as appropriate.
 271                  */
 272 
 273                 if (ispipe) {
 274                         wait((int *)NULL);
 275                         switch (fork()) {
 276                         case 0:
 277                                 sigchild();
 278                                 sigset(SIGHUP, SIG_IGN);
 279                                 sigset(SIGINT, SIG_IGN);
 280                                 sigset(SIGQUIT, SIG_IGN);
 281                                 close(0);
 282                                 dup(image);
 283                                 close(image);
 284                                 lseek(0, 0L, 0);
 285                                 if ((shell = value("SHELL")) == NOSTR || *shell=='\0')
 286                                         shell = SHELL;
 287                                 (void) execlp(shell, shell, "-c", fname, (char *)0);
 288                                 perror(shell);
 289                                 exit(1);
 290                                 break;
 291 
 292                         case (pid_t)-1:
 293                                 perror("fork");
 294                                 senderr++;
 295                                 goto cant;
 296                         }
 297                 }
 298                 else {
 299                         if ((fout = fopen(fname, "a")) == NULL) {
 300                                 perror(fname);
 301                                 senderr++;
 302                                 goto cant;
 303                         }
 304                         fin = Fdopen(image, "r");
 305                         if (fin == NULL) {
 306                                 fprintf(stderr,
 307                                     gettext("Can't reopen image\n"));
 308                                 fclose(fout);
 309                                 senderr++;
 310                                 goto cant;
 311                         }
 312                         rewind(fin);
 313 #ifdef preSVr4
 314                         putc(getc(fin), fout);
 315                         while (fgets(line, sizeof line, fin)) {
 316                                 if (!strncmp(line, "From ", 5))
 317                                         putc('>', fout);
 318                                 fputs(line, fout);
 319                         }
 320 #else
 321                         while ((c = getc(fin)) != EOF)
 322                                 putc(c, fout);
 323 #endif
 324                         putc('\n', fout);
 325                         fflush(fout);
 326                         if (fferror(fout))
 327                                 senderr++, perror(fname);
 328                         fclose(fout);
 329                         fclose(fin);
 330                 }
 331 cant:
 332                 /*
 333                  * In days of old we removed the entry from the
 334                  * the list; now for sake of header expansion
 335                  * we leave it in and mark it as deleted.
 336                  */
 337 
 338 #ifdef CRAZYWOW
 339                 {
 340                 register struct name *t, *x;
 341 
 342                 if (np == top) {
 343                         top = np->n_flink;
 344                         if (top != NIL)
 345                                 top->n_blink = NIL;
 346                         np = top;
 347                         continue;
 348                 }
 349                 x = np->n_blink;
 350                 t = np->n_flink;
 351                 x->n_flink = t;
 352                 if (t != NIL)
 353                         t->n_blink = x;
 354                 np = t;
 355                 }
 356 #endif
 357 
 358                 np->n_type |= GDEL;
 359         }
 360         if (image >= 0) {
 361                 close(image);
 362                 image = -1;
 363         }
 364         return(nout);
 365 }
 366 
 367 /*
 368  * Determine if the passed address is a local "send to file" address.
 369  * If any of the network metacharacters precedes any slashes, it can't
 370  * be a filename.  We cheat with .'s to allow path names like ./...
 371  * If "fcc" has been unset, then short-circuit those tests, but not
 372  * the +... test.
 373  */
 374 static int 
 375 isfileaddr(char *name)
 376 {
 377         register char *cp;
 378         char *fcc = value("fcc");
 379 
 380         if (any('@', name))
 381                 return(0);
 382         if (*name == '+')
 383                 return(1);
 384         if (fcc == NOSTR)
 385                 return(0);
 386         for (cp = name; *cp; cp++) {
 387                 if (*cp == '.')
 388                         continue;
 389                 if (any(*cp, metanet))
 390                         return(0);
 391                 if (*cp == '/')
 392                         return(1);
 393         }
 394         return(0);
 395 }
 396 
 397 /*
 398  * Map all of the aliased users in the invoker's mailrc
 399  * file and insert them into the list.
 400  * Changed after all these months of service to recursively
 401  * expand names (2/14/80).
 402  */
 403 
 404 struct name *
 405 usermap(struct name *names)
 406 {
 407         register struct name *newnames, *np, *cp;
 408         struct grouphead *gh;
 409         register int metoo;
 410 
 411         newnames = NIL;
 412         np = names;
 413         metoo = (value("metoo") != NOSTR);
 414         while (np != NIL) {
 415                 if (np->n_name[0] == '\\') {
 416                         cp = np->n_flink;
 417                         newnames = put(newnames, np);
 418                         np = cp;
 419                         continue;
 420                 }
 421                 gh = findgroup(np->n_name);
 422                 cp = np->n_flink;
 423                 if (gh != NOGRP)
 424                         newnames = gexpand(newnames, gh, metoo, np->n_type);
 425                 else
 426                         newnames = put(newnames, np);
 427                 np = cp;
 428         }
 429         return(newnames);
 430 }
 431 
 432 /*
 433  * Recursively expand a group name.  We limit the expansion to some
 434  * fixed level to keep things from going haywire.
 435  * Direct recursion is not expanded for convenience.
 436  */
 437 
 438 static struct name *
 439 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int arg_ntype)
 440 {
 441         short ntype = (short)arg_ntype;
 442         struct mgroup *gp;
 443         struct grouphead *ngh;
 444         struct name *np;
 445         static int depth;
 446         register char *cp;
 447 
 448         if (depth > MAXEXP) {
 449                 printf(gettext("Expanding alias to depth larger than %d\n"),
 450                     MAXEXP);
 451                 return(nlist);
 452         }
 453         depth++;
 454         for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
 455                 cp = gp->ge_name;
 456                 if (*cp == '\\')
 457                         goto quote;
 458                 if (strcmp(cp, gh->g_name) == 0)
 459                         goto quote;
 460                 if ((ngh = findgroup(cp)) != NOGRP) {
 461                         nlist = gexpand(nlist, ngh, metoo, ntype);
 462                         continue;
 463                 }
 464 quote:
 465                 np = nalloc(cp);
 466                 np->n_type = ntype;
 467                 /*
 468                  * At this point should allow to expand
 469                  * to self if only person in group
 470                  */
 471                 if (gp == gh->g_list && gp->ge_link == NOGE)
 472                         goto skip;
 473                 if (!metoo && samebody(myname, gp->ge_name, FALSE))
 474                         np->n_type |= GDEL;
 475 skip:
 476                 nlist = put(nlist, np);
 477         }
 478         depth--;
 479         return(nlist);
 480 }
 481 
 482 /*
 483  * Normalize a network name for comparison purposes.
 484  */
 485 static char *
 486 norm(register char *user, register char *ubuf, int nbangs)
 487 {
 488         register char *cp;
 489         int inubuf = 0;
 490 
 491         while (*user++ == '!');
 492         user--;
 493         if (!strchr(user, '!')) {
 494                 snprintf(ubuf, BUFSIZ, "%s!%s", host, user);
 495                 user = ubuf;
 496                 inubuf++;
 497         }
 498         if (nbangs) {
 499                 cp = user + strlen(user);
 500                 while (nbangs--)
 501                         while (cp > user && *--cp != '!');
 502                 user = (cp > user) ? ++cp : cp;
 503                 /*
 504                  * Now strip off all Internet-type
 505                  * hosts.
 506                  */
 507                 if ((cp = strchr(user, '%')) == NOSTR)
 508                         cp = strchr(user, '@');
 509                 if (cp != NOSTR) {
 510                         if (!inubuf) {
 511                                 strncpy(ubuf, user, cp - user);
 512                                 ubuf[cp - user] = '\0';
 513                                 user = ubuf;
 514                         } else
 515                                 *cp = '\0';
 516                 }
 517         }
 518         return user;
 519 }
 520 
 521 /*
 522  * Implement allnet options.
 523  */
 524 int 
 525 samebody(register char *user, register char *addr, int fuzzy)
 526 {
 527         char ubuf[BUFSIZ], abuf[BUFSIZ];
 528         char *allnet = value("allnet");
 529         int nbangs = allnet ? !strcmp(allnet, "uucp") ? 2 : 1 : 0;
 530 
 531         if (fuzzy && value("fuzzymatch")) {
 532                 int i;
 533 
 534                 (void) strlcpy(ubuf, user, BUFSIZ);
 535                 for (i = 0; ubuf[i]; i++)
 536                         ubuf[i] = tolower(ubuf[i]);
 537                 (void) strlcpy(abuf, addr, BUFSIZ);
 538                 for (i = 0; abuf[i]; i++)
 539                         abuf[i] = tolower(abuf[i]);
 540                 return (strstr(abuf, ubuf) != NOSTR);
 541         }
 542         user = norm(user, ubuf, nbangs);
 543         addr = norm(addr, abuf, nbangs);
 544         return strcmp(user, addr) == 0;
 545 }
 546 
 547 /*
 548  * Compute the length of the passed name list and
 549  * return it.
 550  */
 551 static int 
 552 lengthof(struct name *name)
 553 {
 554         register struct name *np;
 555         register int c;
 556 
 557         for (c = 0, np = name; np != NIL; c++, np = np->n_flink)
 558                 ;
 559         return(c);
 560 }
 561 
 562 /*
 563  * Concatenate the two passed name lists, return the result.
 564  */
 565 
 566 struct name *
 567 cat(struct name *n1, struct name *n2)
 568 {
 569         register struct name *tail;
 570 
 571         if (n1 == NIL)
 572                 return(n2);
 573         if (n2 == NIL)
 574                 return(n1);
 575         tail = tailof(n1);
 576         tail->n_flink = n2;
 577         n2->n_blink = tail;
 578         return(n1);
 579 }
 580 
 581 /*
 582  * Unpack the name list onto a vector of strings.
 583  * Return an error if the name list won't fit.
 584  */
 585 
 586 char **
 587 unpack(struct name *np)
 588 {
 589         register char **ap, **top;
 590         register struct name *n;
 591         char hbuf[10];
 592         int t, extra, metoo, verbose;
 593 
 594         n = np;
 595         if ((t = lengthof(n)) == 0)
 596                 panic("No names to unpack");
 597 
 598         /*
 599          * Compute the number of extra arguments we will need.
 600          * We need at least 2 extra -- one for "mail" and one for
 601          * the terminating 0 pointer.
 602          * Additional spots may be needed to pass along -r and -f to 
 603          * the host mailer.
 604          */
 605 
 606         extra = 2;
 607 
 608         if (rflag != NOSTR)
 609                 extra += 2;
 610 #ifdef SENDMAIL
 611         extra++;
 612         metoo = value("metoo") != NOSTR;
 613         if (metoo)
 614                 extra++;
 615         verbose = value("verbose") != NOSTR;
 616         if (verbose)
 617                 extra++;
 618         if (hflag)
 619                 extra += 2;
 620 #endif /* SENDMAIL */
 621         top = (char **) salloc((t + extra) * sizeof (char *));
 622         ap = top;
 623         *ap++ = "mail";
 624         if (rflag != NOSTR) {
 625                 *ap++ = "-r";
 626                 *ap++ = rflag;
 627         }
 628 #ifdef SENDMAIL
 629         *ap++ = "-i";
 630         if (metoo)
 631                 *ap++ = "-m";
 632         if (verbose)
 633                 *ap++ = "-v";
 634         if (hflag) {
 635                 *ap++ = "-h";
 636                 snprintf(hbuf, sizeof (hbuf), "%d", hflag);
 637                 *ap++ = savestr(hbuf);
 638         }
 639 #endif /* SENDMAIL */
 640         while (n != NIL) {
 641                 if (n->n_type & GDEL) {
 642                         n = n->n_flink;
 643                         continue;
 644                 }
 645                 *ap++ = n->n_name;
 646                 n = n->n_flink;
 647         }
 648         *ap = NOSTR;
 649         return(top);
 650 }
 651 
 652 /*
 653  * See if the user named himself as a destination
 654  * for outgoing mail.  If so, set the global flag
 655  * selfsent so that we avoid removing his mailbox.
 656  */
 657 
 658 void 
 659 mechk(struct name *names)
 660 {
 661         register struct name *np;
 662 
 663         for (np = names; np != NIL; np = np->n_flink)
 664                 if ((np->n_type & GDEL) == 0 &&
 665                     samebody(np->n_name, myname, FALSE)) {
 666                         selfsent++;
 667                         return;
 668                 }
 669 }
 670 
 671 /*
 672  * Remove all of the duplicates from the passed name list by
 673  * insertion sorting them, then checking for dups.
 674  * Return the head of the new list.
 675  */
 676 
 677 struct name *
 678 elide(struct name *names)
 679 {
 680         register struct name *np, *t, *newnames;
 681         struct name *x;
 682 
 683         if (names == NIL)
 684                 return(NIL);
 685         newnames = names;
 686         np = names;
 687         np = np->n_flink;
 688         if (np != NIL)
 689                 np->n_blink = NIL;
 690         newnames->n_flink = NIL;
 691         while (np != NIL) {
 692                 t = newnames;
 693                 while (strcmp(t->n_name, np->n_name) < 0) {
 694                         if (t->n_flink == NIL)
 695                                 break;
 696                         t = t->n_flink;
 697                 }
 698 
 699                 /*
 700                  * If we ran out of t's, put the new entry after
 701                  * the current value of t.
 702                  */
 703 
 704                 if (strcmp(t->n_name, np->n_name) < 0) {
 705                         t->n_flink = np;
 706                         np->n_blink = t;
 707                         t = np;
 708                         np = np->n_flink;
 709                         t->n_flink = NIL;
 710                         continue;
 711                 }
 712 
 713                 /*
 714                  * Otherwise, put the new entry in front of the
 715                  * current t.  If at the front of the list,
 716                  * the new guy becomes the new head of the list.
 717                  */
 718 
 719                 if (t == newnames) {
 720                         t = np;
 721                         np = np->n_flink;
 722                         t->n_flink = newnames;
 723                         newnames->n_blink = t;
 724                         t->n_blink = NIL;
 725                         newnames = t;
 726                         continue;
 727                 }
 728 
 729                 /*
 730                  * The normal case -- we are inserting into the
 731                  * middle of the list.
 732                  */
 733 
 734                 x = np;
 735                 np = np->n_flink;
 736                 x->n_flink = t;
 737                 x->n_blink = t->n_blink;
 738                 t->n_blink->n_flink = x;
 739                 t->n_blink = x;
 740         }
 741 
 742         /*
 743          * Now the list headed up by new is sorted.
 744          * Go through it and remove duplicates.
 745          * Remember the best "type" among all the
 746          * duplicates of a name.
 747          */
 748 
 749         np = newnames;
 750         while (np != NIL) {
 751                 int type;
 752 
 753                 t = np;
 754                 type = np->n_type;
 755                 while (t->n_flink!=NIL &&
 756                     strcmp(np->n_name, t->n_flink->n_name) == 0) {
 757                         t = t->n_flink;
 758                         /* "To" before "Cc" before "Bcc" */
 759                         if (t->n_type < type)
 760                                 type = t->n_type;
 761                 }
 762                 if (t == np || t == NIL) {
 763                         np = np->n_flink;
 764                         continue;
 765                 }
 766 
 767                 /*
 768                  * Now t points to the last entry with the same name
 769                  * as np.  Make np point beyond t.
 770                  */
 771 
 772                 np->n_flink = t->n_flink;
 773                 if (t->n_flink != NIL)
 774                         t->n_flink->n_blink = np;
 775                 np->n_type = type;
 776                 np = np->n_flink;
 777         }
 778         return(newnames);
 779 }
 780 
 781 /*
 782  * Put another node onto a list of names and return
 783  * the list.
 784  */
 785 
 786 static struct name *
 787 put(struct name *list, struct name *node)
 788 {
 789         node->n_flink = list;
 790         node->n_blink = NIL;
 791         if (list != NIL)
 792                 list->n_blink = node;
 793         return(node);
 794 }
 795 
 796 
 797 /*
 798  * Delete the given name from a namelist.
 799  */
 800 struct name *
 801 delname(register struct name *np, char name[])
 802 {
 803         register struct name *p;
 804 
 805         for (p = np; p != NIL; p = p->n_flink)
 806                 if (samebody(name, p->n_name, FALSE)) {
 807                         if (p->n_blink == NIL) {
 808                                 if (p->n_flink != NIL)
 809                                         p->n_flink->n_blink = NIL;
 810                                 np = p->n_flink;
 811                                 continue;
 812                         }
 813                         if (p->n_flink == NIL) {
 814                                 if (p->n_blink != NIL)
 815                                         p->n_blink->n_flink = NIL;
 816                                 continue;
 817                         }
 818                         p->n_blink->n_flink = p->n_flink;
 819                         p->n_flink->n_blink = p->n_blink;
 820                 }
 821         return(np);
 822 }
 823 
 824 /*
 825  * Call the given routine on each element of the name
 826  * list, replacing said value if need be.
 827  */
 828 
 829 void 
 830 mapf(register struct name *np, char *from)
 831 {
 832         register struct name *p;
 833 
 834         if (debug) fprintf(stderr, "mapf %lx, %s\n", (long)np, from);
 835         for (p = np; p != NIL; p = p->n_flink)
 836                 if ((p->n_type & GDEL) == 0) {
 837                         p->n_name = netmap(p->n_name, from);
 838                         p->n_full = splice(p->n_name, p->n_full);
 839                 }
 840         if (debug) fprintf(stderr, "mapf %s done\n", from);
 841 }