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 /*
  42  * mailx -- a modified version of a University of California at Berkeley
  43  *      mail program
  44  *
  45  * Handle name lists.
  46  */
  47 
  48 #include "rcv.h"
  49 #include <locale.h>
  50 
  51 static struct name      *nalloc(char str[]);
  52 static int              isfileaddr(char *name);
  53 static int              lengthof(struct name *name);
  54 static struct name      *gexpand(struct name *nlist, struct grouphead *gh,
  55     int metoo, int arg_ntype);
  56 static char             *norm(register char *user, register char *ubuf,
  57     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 ||
 286                                     *shell == '\0')
 287                                         shell = SHELL;
 288                                 (void) execlp(shell, shell, "-c", fname,
 289                                     (char *)0);
 290                                 perror(shell);
 291                                 exit(1);
 292                                 break;
 293 
 294                         case (pid_t)-1:
 295                                 perror("fork");
 296                                 senderr++;
 297                                 goto cant;
 298                         }
 299                 } else {
 300                         if ((fout = fopen(fname, "a")) == NULL) {
 301                                 perror(fname);
 302                                 senderr++;
 303                                 goto cant;
 304                         }
 305                         fin = Fdopen(image, "r");
 306                         if (fin == NULL) {
 307                                 fprintf(stderr,
 308                                     gettext("Can't reopen image\n"));
 309                                 fclose(fout);
 310                                 senderr++;
 311                                 goto cant;
 312                         }
 313                         rewind(fin);
 314 #ifdef preSVr4
 315                         putc(getc(fin), fout);
 316                         while (fgets(line, sizeof (line), fin)) {
 317                                 if (strncmp(line, "From ", 5) == 0)
 318                                         putc('>', fout);
 319                                 fputs(line, fout);
 320                         }
 321 #else
 322                         while ((c = getc(fin)) != EOF)
 323                                 putc(c, fout);
 324 #endif
 325                         putc('\n', fout);
 326                         fflush(fout);
 327                         if (fferror(fout))
 328                                 senderr++, perror(fname);
 329                         fclose(fout);
 330                         fclose(fin);
 331                 }
 332 cant:
 333                 /*
 334                  * In days of old we removed the entry from the
 335                  * the list; now for sake of header expansion
 336                  * we leave it in and mark it as deleted.
 337                  */
 338 
 339 #ifdef CRAZYWOW
 340                 {
 341                 register struct name *t, *x;
 342 
 343                 if (np == top) {
 344                         top = np->n_flink;
 345                         if (top != NIL)
 346                                 top->n_blink = NIL;
 347                         np = top;
 348                         continue;
 349                 }
 350                 x = np->n_blink;
 351                 t = np->n_flink;
 352                 x->n_flink = t;
 353                 if (t != NIL)
 354                         t->n_blink = x;
 355                 np = t;
 356                 }
 357 #endif
 358 
 359                 np->n_type |= GDEL;
 360         }
 361         if (image >= 0) {
 362                 close(image);
 363                 image = -1;
 364         }
 365         return (nout);
 366 }
 367 
 368 /*
 369  * Determine if the passed address is a local "send to file" address.
 370  * If any of the network metacharacters precedes any slashes, it can't
 371  * be a filename.  We cheat with .'s to allow path names like ./...
 372  * If "fcc" has been unset, then short-circuit those tests, but not
 373  * the +... test.
 374  */
 375 static int
 376 isfileaddr(char *name)
 377 {
 378         register char *cp;
 379         char *fcc = value("fcc");
 380 
 381         if (any('@', name))
 382                 return (0);
 383         if (*name == '+')
 384                 return (1);
 385         if (fcc == NOSTR)
 386                 return (0);
 387         for (cp = name; *cp; cp++) {
 388                 if (*cp == '.')
 389                         continue;
 390                 if (any(*cp, metanet))
 391                         return (0);
 392                 if (*cp == '/')
 393                         return (1);
 394         }
 395         return (0);
 396 }
 397 
 398 /*
 399  * Map all of the aliased users in the invoker's mailrc
 400  * file and insert them into the list.
 401  * Changed after all these months of service to recursively
 402  * expand names (2/14/80).
 403  */
 404 
 405 struct name *
 406 usermap(struct name *names)
 407 {
 408         register struct name *newnames, *np, *cp;
 409         struct grouphead *gh;
 410         register int metoo;
 411 
 412         newnames = NIL;
 413         np = names;
 414         metoo = (value("metoo") != NOSTR);
 415         while (np != NIL) {
 416                 if (np->n_name[0] == '\\') {
 417                         cp = np->n_flink;
 418                         newnames = put(newnames, np);
 419                         np = cp;
 420                         continue;
 421                 }
 422                 gh = findgroup(np->n_name);
 423                 cp = np->n_flink;
 424                 if (gh != NOGRP)
 425                         newnames = gexpand(newnames, gh, metoo, np->n_type);
 426                 else
 427                         newnames = put(newnames, np);
 428                 np = cp;
 429         }
 430         return (newnames);
 431 }
 432 
 433 /*
 434  * Recursively expand a group name.  We limit the expansion to some
 435  * fixed level to keep things from going haywire.
 436  * Direct recursion is not expanded for convenience.
 437  */
 438 
 439 static struct name *
 440 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int arg_ntype)
 441 {
 442         short ntype = (short)arg_ntype;
 443         struct mgroup *gp;
 444         struct grouphead *ngh;
 445         struct name *np;
 446         static int depth;
 447         register char *cp;
 448 
 449         if (depth > MAXEXP) {
 450                 printf(gettext("Expanding alias to depth larger than %d\n"),
 451                     MAXEXP);
 452                 return (nlist);
 453         }
 454         depth++;
 455         for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
 456                 cp = gp->ge_name;
 457                 if (*cp == '\\')
 458                         goto quote;
 459                 if (strcmp(cp, gh->g_name) == 0)
 460                         goto quote;
 461                 if ((ngh = findgroup(cp)) != NOGRP) {
 462                         nlist = gexpand(nlist, ngh, metoo, ntype);
 463                         continue;
 464                 }
 465 quote:
 466                 np = nalloc(cp);
 467                 np->n_type = ntype;
 468                 /*
 469                  * At this point should allow to expand
 470                  * to self if only person in group
 471                  */
 472                 if (gp == gh->g_list && gp->ge_link == NOGE)
 473                         goto skip;
 474                 if (!metoo && samebody(myname, gp->ge_name, FALSE))
 475                         np->n_type |= GDEL;
 476 skip:
 477                 nlist = put(nlist, np);
 478         }
 479         depth--;
 480         return (nlist);
 481 }
 482 
 483 /*
 484  * Normalize a network name for comparison purposes.
 485  */
 486 static char *
 487 norm(register char *user, register char *ubuf, int nbangs)
 488 {
 489         register char *cp;
 490         int inubuf = 0;
 491 
 492         while (*user++ == '!')
 493                 ;
 494         user--;
 495         if (!strchr(user, '!')) {
 496                 snprintf(ubuf, BUFSIZ, "%s!%s", host, user);
 497                 user = ubuf;
 498                 inubuf++;
 499         }
 500         if (nbangs) {
 501                 cp = user + strlen(user);
 502                 while (nbangs--)
 503                         while (cp > user && *--cp != '!')
 504                                 ;
 505                 user = (cp > user) ? ++cp : cp;
 506                 /*
 507                  * Now strip off all Internet-type
 508                  * hosts.
 509                  */
 510                 if ((cp = strchr(user, '%')) == NOSTR)
 511                         cp = strchr(user, '@');
 512                 if (cp != NOSTR) {
 513                         if (!inubuf) {
 514                                 strncpy(ubuf, user, cp - user);
 515                                 ubuf[cp - user] = '\0';
 516                                 user = ubuf;
 517                         } else
 518                                 *cp = '\0';
 519                 }
 520         }
 521         return (user);
 522 }
 523 
 524 /*
 525  * Implement allnet options.
 526  */
 527 int
 528 samebody(register char *user, register char *addr, int fuzzy)
 529 {
 530         char ubuf[BUFSIZ], abuf[BUFSIZ];
 531         char *allnet = value("allnet");
 532         int nbangs = allnet ? (strcmp(allnet, "uucp") == 0) ? 2 : 1 : 0;
 533 
 534         if (fuzzy && value("fuzzymatch")) {
 535                 int i;
 536 
 537                 (void) strlcpy(ubuf, user, BUFSIZ);
 538                 for (i = 0; ubuf[i]; i++)
 539                         ubuf[i] = tolower(ubuf[i]);
 540                 (void) strlcpy(abuf, addr, BUFSIZ);
 541                 for (i = 0; abuf[i]; i++)
 542                         abuf[i] = tolower(abuf[i]);
 543                 return (strstr(abuf, ubuf) != NOSTR);
 544         }
 545         user = norm(user, ubuf, nbangs);
 546         addr = norm(addr, abuf, nbangs);
 547         return (strcmp(user, addr) == 0);
 548 }
 549 
 550 /*
 551  * Compute the length of the passed name list and
 552  * return it.
 553  */
 554 static int
 555 lengthof(struct name *name)
 556 {
 557         register struct name *np;
 558         register int c;
 559 
 560         for (c = 0, np = name; np != NIL; c++, np = np->n_flink)
 561                 ;
 562         return (c);
 563 }
 564 
 565 /*
 566  * Concatenate the two passed name lists, return the result.
 567  */
 568 
 569 struct name *
 570 cat(struct name *n1, struct name *n2)
 571 {
 572         register struct name *tail;
 573 
 574         if (n1 == NIL)
 575                 return (n2);
 576         if (n2 == NIL)
 577                 return (n1);
 578         tail = tailof(n1);
 579         tail->n_flink = n2;
 580         n2->n_blink = tail;
 581         return (n1);
 582 }
 583 
 584 /*
 585  * Unpack the name list onto a vector of strings.
 586  * Return an error if the name list won't fit.
 587  */
 588 
 589 char **
 590 unpack(struct name *np)
 591 {
 592         register char **ap, **top;
 593         register struct name *n;
 594         char hbuf[10];
 595         int t, extra, metoo, verbose;
 596 
 597         n = np;
 598         if ((t = lengthof(n)) == 0)
 599                 panic("No names to unpack");
 600 
 601         /*
 602          * Compute the number of extra arguments we will need.
 603          * We need at least 2 extra -- one for "mail" and one for
 604          * the terminating 0 pointer.
 605          * Additional spots may be needed to pass along -r and -f to
 606          * the host mailer.
 607          */
 608 
 609         extra = 2;
 610 
 611         if (rflag != NOSTR)
 612                 extra += 2;
 613         extra++;
 614         metoo = value("metoo") != NOSTR;
 615         if (metoo)
 616                 extra++;
 617         verbose = value("verbose") != NOSTR;
 618         if (verbose)
 619                 extra++;
 620         if (hflag)
 621                 extra += 2;
 622         top = (char **)salloc((t + extra) * sizeof (char *));
 623         ap = top;
 624         *ap++ = "sendmail";
 625         if (rflag != NOSTR) {
 626                 *ap++ = "-r";
 627                 *ap++ = rflag;
 628         }
 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         while (n != NIL) {
 640                 if (n->n_type & GDEL) {
 641                         n = n->n_flink;
 642                         continue;
 643                 }
 644                 *ap++ = n->n_name;
 645                 n = n->n_flink;
 646         }
 647         *ap = NOSTR;
 648         return (top);
 649 }
 650 
 651 /*
 652  * See if the user named himself as a destination
 653  * for outgoing mail.  If so, set the global flag
 654  * selfsent so that we avoid removing his mailbox.
 655  */
 656 
 657 void
 658 mechk(struct name *names)
 659 {
 660         register struct name *np;
 661 
 662         for (np = names; np != NIL; np = np->n_flink)
 663                 if ((np->n_type & GDEL) == 0 &&
 664                     samebody(np->n_name, myname, FALSE)) {
 665                         selfsent++;
 666                         return;
 667                 }
 668 }
 669 
 670 /*
 671  * Remove all of the duplicates from the passed name list by
 672  * insertion sorting them, then checking for dups.
 673  * Return the head of the new list.
 674  */
 675 
 676 struct name *
 677 elide(struct name *names)
 678 {
 679         register struct name *np, *t, *newnames;
 680         struct name *x;
 681 
 682         if (names == NIL)
 683                 return (NIL);
 684         newnames = names;
 685         np = names;
 686         np = np->n_flink;
 687         if (np != NIL)
 688                 np->n_blink = NIL;
 689         newnames->n_flink = NIL;
 690         while (np != NIL) {
 691                 t = newnames;
 692                 while (strcmp(t->n_name, np->n_name) < 0) {
 693                         if (t->n_flink == NIL)
 694                                 break;
 695                         t = t->n_flink;
 696                 }
 697 
 698                 /*
 699                  * If we ran out of t's, put the new entry after
 700                  * the current value of t.
 701                  */
 702 
 703                 if (strcmp(t->n_name, np->n_name) < 0) {
 704                         t->n_flink = np;
 705                         np->n_blink = t;
 706                         t = np;
 707                         np = np->n_flink;
 708                         t->n_flink = NIL;
 709                         continue;
 710                 }
 711 
 712                 /*
 713                  * Otherwise, put the new entry in front of the
 714                  * current t.  If at the front of the list,
 715                  * the new guy becomes the new head of the list.
 716                  */
 717 
 718                 if (t == newnames) {
 719                         t = np;
 720                         np = np->n_flink;
 721                         t->n_flink = newnames;
 722                         newnames->n_blink = t;
 723                         t->n_blink = NIL;
 724                         newnames = t;
 725                         continue;
 726                 }
 727 
 728                 /*
 729                  * The normal case -- we are inserting into the
 730                  * middle of the list.
 731                  */
 732 
 733                 x = np;
 734                 np = np->n_flink;
 735                 x->n_flink = t;
 736                 x->n_blink = t->n_blink;
 737                 t->n_blink->n_flink = x;
 738                 t->n_blink = x;
 739         }
 740 
 741         /*
 742          * Now the list headed up by new is sorted.
 743          * Go through it and remove duplicates.
 744          * Remember the best "type" among all the
 745          * duplicates of a name.
 746          */
 747 
 748         np = newnames;
 749         while (np != NIL) {
 750                 int type;
 751 
 752                 t = np;
 753                 type = np->n_type;
 754                 while (t->n_flink != NIL &&
 755                     strcmp(np->n_name, t->n_flink->n_name) == 0) {
 756                         t = t->n_flink;
 757                         /* "To" before "Cc" before "Bcc" */
 758                         if (t->n_type < type)
 759                                 type = t->n_type;
 760                 }
 761                 if (t == np || t == NIL) {
 762                         np = np->n_flink;
 763                         continue;
 764                 }
 765 
 766                 /*
 767                  * Now t points to the last entry with the same name
 768                  * as np.  Make np point beyond t.
 769                  */
 770 
 771                 np->n_flink = t->n_flink;
 772                 if (t->n_flink != NIL)
 773                         t->n_flink->n_blink = np;
 774                 np->n_type = type;
 775                 np = np->n_flink;
 776         }
 777         return (newnames);
 778 }
 779 
 780 /*
 781  * Put another node onto a list of names and return
 782  * the list.
 783  */
 784 
 785 static struct name *
 786 put(struct name *list, struct name *node)
 787 {
 788         node->n_flink = list;
 789         node->n_blink = NIL;
 790         if (list != NIL)
 791                 list->n_blink = node;
 792         return (node);
 793 }
 794 
 795 
 796 /*
 797  * Delete the given name from a namelist.
 798  */
 799 struct name *
 800 delname(register struct name *np, char name[])
 801 {
 802         register struct name *p;
 803 
 804         for (p = np; p != NIL; p = p->n_flink)
 805                 if (samebody(name, p->n_name, FALSE)) {
 806                         if (p->n_blink == NIL) {
 807                                 if (p->n_flink != NIL)
 808                                         p->n_flink->n_blink = NIL;
 809                                 np = p->n_flink;
 810                                 continue;
 811                         }
 812                         if (p->n_flink == NIL) {
 813                                 if (p->n_blink != NIL)
 814                                         p->n_blink->n_flink = NIL;
 815                                 continue;
 816                         }
 817                         p->n_blink->n_flink = p->n_flink;
 818                         p->n_flink->n_blink = p->n_blink;
 819                 }
 820         return (np);
 821 }
 822 
 823 /*
 824  * Call the given routine on each element of the name
 825  * list, replacing said value if need be.
 826  */
 827 
 828 void
 829 mapf(register struct name *np, char *from)
 830 {
 831         register struct name *p;
 832 
 833         if (debug) fprintf(stderr, "mapf %lx, %s\n", (long)np, from);
 834         for (p = np; p != NIL; p = p->n_flink)
 835                 if ((p->n_type & GDEL) == 0) {
 836                         p->n_name = netmap(p->n_name, from);
 837                         p->n_full = splice(p->n_name, p->n_full);
 838                 }
 839         if (debug) fprintf(stderr, "mapf %s done\n", from);
 840 }