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