names.c revision 1590
119370Spst/* 298944Sobrien * Copyright (c) 1980, 1993 398944Sobrien * The Regents of the University of California. All rights reserved. 419370Spst * 5130803Smarcel * Redistribution and use in source and binary forms, with or without 619370Spst * modification, are permitted provided that the following conditions 798944Sobrien * are met: 819370Spst * 1. Redistributions of source code must retain the above copyright 998944Sobrien * notice, this list of conditions and the following disclaimer. 1098944Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1198944Sobrien * notice, this list of conditions and the following disclaimer in the 1298944Sobrien * documentation and/or other materials provided with the distribution. 1319370Spst * 3. All advertising materials mentioning features or use of this software 1498944Sobrien * must display the following acknowledgement: 1598944Sobrien * This product includes software developed by the University of 1698944Sobrien * California, Berkeley and its contributors. 1798944Sobrien * 4. Neither the name of the University nor the names of its contributors 1819370Spst * may be used to endorse or promote products derived from this software 1998944Sobrien * without specific prior written permission. 2098944Sobrien * 2198944Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2298944Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2398944Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2419370Spst * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25130803Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2619370Spst * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2719370Spst * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28130803Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2919370Spst * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3098944Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3198944Sobrien * SUCH DAMAGE. 3246283Sdfr */ 33130803Smarcel 34130803Smarcel#ifndef lint 35130803Smarcelstatic char sccsid[] = "@(#)names.c 8.1 (Berkeley) 6/6/93"; 36130803Smarcel#endif /* not lint */ 37130803Smarcel 38130803Smarcel/* 39130803Smarcel * Mail -- a mail program 40130803Smarcel * 41130803Smarcel * Handle name lists. 42130803Smarcel */ 43130803Smarcel 44130803Smarcel#include "rcv.h" 45130803Smarcel#include <fcntl.h> 46130803Smarcel#include "extern.h" 47130803Smarcel 48130803Smarcel/* 49130803Smarcel * Allocate a single element of a name list, 50130803Smarcel * initialize its name field to the passed 51130803Smarcel * name and return it. 52130803Smarcel */ 53130803Smarcelstruct name * 54130803Smarcelnalloc(str, ntype) 55130803Smarcel char str[]; 56130803Smarcel int ntype; 57130803Smarcel{ 58130803Smarcel register struct name *np; 59130803Smarcel 60130803Smarcel np = (struct name *) salloc(sizeof *np); 61130803Smarcel np->n_flink = NIL; 62130803Smarcel np->n_blink = NIL; 63130803Smarcel np->n_type = ntype; 64130803Smarcel np->n_name = savestr(str); 65130803Smarcel return(np); 66130803Smarcel} 67130803Smarcel 68130803Smarcel/* 69130803Smarcel * Find the tail of a list and return it. 70130803Smarcel */ 71130803Smarcelstruct name * 72130803Smarceltailof(name) 73130803Smarcel struct name *name; 74130803Smarcel{ 75130803Smarcel register struct name *np; 76130803Smarcel 77130803Smarcel np = name; 78130803Smarcel if (np == NIL) 79130803Smarcel return(NIL); 80130803Smarcel while (np->n_flink != NIL) 81130803Smarcel np = np->n_flink; 82130803Smarcel return(np); 83130803Smarcel} 84130803Smarcel 85130803Smarcel/* 86130803Smarcel * Extract a list of names from a line, 87130803Smarcel * and make a list of names from it. 88130803Smarcel * Return the list or NIL if none found. 89130803Smarcel */ 90130803Smarcelstruct name * 91130803Smarcelextract(line, ntype) 92130803Smarcel char line[]; 93130803Smarcel int ntype; 94130803Smarcel{ 9598944Sobrien register char *cp; 9698944Sobrien register struct name *top, *np, *t; 9798944Sobrien char nbuf[BUFSIZ]; 9898944Sobrien 9998944Sobrien if (line == NOSTR || *line == '\0') 10098944Sobrien return NIL; 10198944Sobrien top = NIL; 10298944Sobrien np = NIL; 10398944Sobrien cp = line; 10498944Sobrien while ((cp = yankword(cp, nbuf)) != NOSTR) { 10598944Sobrien t = nalloc(nbuf, ntype); 10698944Sobrien if (top == NIL) 10798944Sobrien top = t; 10898944Sobrien else 10998944Sobrien np->n_flink = t; 11098944Sobrien t->n_blink = np; 11198944Sobrien np = t; 11298944Sobrien } 11398944Sobrien return top; 11498944Sobrien} 11598944Sobrien 11646283Sdfr/* 11798944Sobrien * Turn a list of names into a string of the same names. 11898944Sobrien */ 11998944Sobrienchar * 12046283Sdfrdetract(np, ntype) 12198944Sobrien register struct name *np; 12298944Sobrien int ntype; 12319370Spst{ 12498944Sobrien register int s; 12598944Sobrien register char *cp, *top; 12698944Sobrien register struct name *p; 12798944Sobrien register int comma; 12898944Sobrien 12998944Sobrien comma = ntype & GCOMMA; 13098944Sobrien if (np == NIL) 13198944Sobrien return(NOSTR); 13298944Sobrien ntype &= ~GCOMMA; 13398944Sobrien s = 0; 13498944Sobrien if (debug && comma) 13598944Sobrien fprintf(stderr, "detract asked to insert commas\n"); 13698944Sobrien for (p = np; p != NIL; p = p->n_flink) { 13798944Sobrien if (ntype && (p->n_type & GMASK) != ntype) 13819370Spst continue; 139130803Smarcel s += strlen(p->n_name) + 1; 140130803Smarcel if (comma) 141130803Smarcel s++; 142130803Smarcel } 143130803Smarcel if (s == 0) 14498944Sobrien return(NOSTR); 14598944Sobrien s += 2; 14698944Sobrien top = salloc(s); 14798944Sobrien cp = top; 14898944Sobrien for (p = np; p != NIL; p = p->n_flink) { 14998944Sobrien if (ntype && (p->n_type & GMASK) != ntype) 15098944Sobrien continue; 15198944Sobrien cp = copy(p->n_name, cp); 15219370Spst if (comma && p->n_flink != NIL) 15398944Sobrien *cp++ = ','; 15498944Sobrien *cp++ = ' '; 15598944Sobrien } 15698944Sobrien *--cp = 0; 15798944Sobrien if (comma && *--cp == ',') 15819370Spst *cp = 0; 15998944Sobrien return(top); 16098944Sobrien} 16198944Sobrien 16298944Sobrien/* 16398944Sobrien * Grab a single word (liberal word) 16498944Sobrien * Throw away things between ()'s, and take anything between <>. 16598944Sobrien */ 16619370Spstchar * 16798944Sobrienyankword(ap, wbuf) 16898944Sobrien char *ap, wbuf[]; 16998944Sobrien{ 17019370Spst register char *cp, *cp2; 17198944Sobrien 17298944Sobrien cp = ap; 17398944Sobrien for (;;) { 17498944Sobrien if (*cp == '\0') 17598944Sobrien return NOSTR; 17698944Sobrien if (*cp == '(') { 17798944Sobrien register int nesting = 0; 17819370Spst 17919370Spst while (*cp != '\0') { 18098944Sobrien switch (*cp++) { 18198944Sobrien case '(': 18298944Sobrien nesting++; 18398944Sobrien break; 18498944Sobrien case ')': 18598944Sobrien --nesting; 18619370Spst break; 18719370Spst } 18898944Sobrien if (nesting <= 0) 18998944Sobrien break; 19098944Sobrien } 19198944Sobrien } else if (*cp == ' ' || *cp == '\t' || *cp == ',') 19298944Sobrien cp++; 19398944Sobrien else 19498944Sobrien break; 19598944Sobrien } 19698944Sobrien if (*cp == '<') 19798944Sobrien for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';) 198130803Smarcel ; 199130803Smarcel else 20019370Spst for (cp2 = wbuf; *cp && !index(" \t,(", *cp); *cp2++ = *cp++) 201130803Smarcel ; 202130803Smarcel *cp2 = '\0'; 20398944Sobrien return cp; 20498944Sobrien} 20519370Spst 20698944Sobrien/* 20798944Sobrien * For each recipient in the passed name list with a / 20898944Sobrien * in the name, append the message to the end of the named file 20998944Sobrien * and remove him from the recipient list. 21098944Sobrien * 21198944Sobrien * Recipients whose name begins with | are piped through the given 21298944Sobrien * program and removed. 213130803Smarcel */ 214130803Smarcelstruct name * 215130803Smarceloutof(names, fo, hp) 21698944Sobrien struct name *names; 217130803Smarcel FILE *fo; 218130803Smarcel struct header *hp; 219130803Smarcel{ 22098944Sobrien register int c; 221130803Smarcel register struct name *np, *top; 222130803Smarcel time_t now, time(); 223130803Smarcel char *date, *fname, *ctime(); 224130803Smarcel FILE *fout, *fin; 225130803Smarcel int ispipe; 226130803Smarcel extern char tempEdit[]; 227130803Smarcel 228130803Smarcel top = names; 229130803Smarcel np = names; 230130803Smarcel (void) time(&now); 23198944Sobrien date = ctime(&now); 23298944Sobrien while (np != NIL) { 23398944Sobrien if (!isfileaddr(np->n_name) && np->n_name[0] != '|') { 23498944Sobrien np = np->n_flink; 23598944Sobrien continue; 23698944Sobrien } 23798944Sobrien ispipe = np->n_name[0] == '|'; 23898944Sobrien if (ispipe) 239130803Smarcel fname = np->n_name+1; 24098944Sobrien else 24198944Sobrien fname = expand(np->n_name); 24298944Sobrien 24398944Sobrien /* 24498944Sobrien * See if we have copied the complete message out yet. 24598944Sobrien * If not, do so. 24698944Sobrien */ 24798944Sobrien 24898944Sobrien if (image < 0) { 24998944Sobrien if ((fout = Fopen(tempEdit, "a")) == NULL) { 250130803Smarcel perror(tempEdit); 251130803Smarcel senderr++; 252130803Smarcel goto cant; 253130803Smarcel } 254130803Smarcel image = open(tempEdit, 2); 255130803Smarcel (void) unlink(tempEdit); 256130803Smarcel if (image < 0) { 257130803Smarcel perror(tempEdit); 258130803Smarcel senderr++; 259130803Smarcel (void) Fclose(fout); 260130803Smarcel goto cant; 26198944Sobrien } 262130803Smarcel (void) fcntl(image, F_SETFD, 1); 26398944Sobrien fprintf(fout, "From %s %s", myname, date); 264130803Smarcel puthead(hp, fout, GTO|GSUBJECT|GCC|GNL); 265130803Smarcel while ((c = getc(fo)) != EOF) 266130803Smarcel (void) putc(c, fout); 267130803Smarcel rewind(fo); 268130803Smarcel (void) putc('\n', fout); 269130803Smarcel (void) fflush(fout); 270130803Smarcel if (ferror(fout)) 271130803Smarcel perror(tempEdit); 272130803Smarcel (void) Fclose(fout); 273130803Smarcel } 274130803Smarcel 275130803Smarcel /* 27698944Sobrien * Now either copy "image" to the desired file 27798944Sobrien * or give it as the standard input to the desired 278130803Smarcel * program as appropriate. 27998944Sobrien */ 280130803Smarcel 281130803Smarcel if (ispipe) { 28298944Sobrien int pid; 283130803Smarcel char *shell; 284130803Smarcel 28598944Sobrien /* 28698944Sobrien * XXX 28798944Sobrien * We can't really reuse the same image file, 28898944Sobrien * because multiple piped recipients will 28998944Sobrien * share the same lseek location and trample 29098944Sobrien * on one another. 29198944Sobrien */ 29298944Sobrien if ((shell = value("SHELL")) == NOSTR) 29398944Sobrien shell = _PATH_CSHELL; 29498944Sobrien pid = start_command(shell, sigmask(SIGHUP)| 29598944Sobrien sigmask(SIGINT)|sigmask(SIGQUIT), 29698944Sobrien image, -1, "-c", fname, NOSTR); 29798944Sobrien if (pid < 0) { 29898944Sobrien senderr++; 29998944Sobrien goto cant; 30098944Sobrien } 30198944Sobrien free_child(pid); 30298944Sobrien } else { 30398944Sobrien int f; 30498944Sobrien if ((fout = Fopen(fname, "a")) == NULL) { 30519370Spst perror(fname); 30698944Sobrien senderr++; 30719370Spst goto cant; 30819370Spst } 30919370Spst if ((f = dup(image)) < 0) { 31098944Sobrien perror("dup"); 31198944Sobrien fin = NULL; 31298944Sobrien } else 31398944Sobrien fin = Fdopen(f, "r"); 31419370Spst if (fin == NULL) { 31598944Sobrien fprintf(stderr, "Can't reopen image\n"); 31619370Spst (void) Fclose(fout); 31798944Sobrien senderr++; 31898944Sobrien goto cant; 31998944Sobrien } 320130803Smarcel rewind(fin); 321130803Smarcel while ((c = getc(fin)) != EOF) 32219370Spst (void) putc(c, fout); 323130803Smarcel if (ferror(fout)) 324130803Smarcel senderr++, perror(fname); 32598944Sobrien (void) Fclose(fout); 32698944Sobrien (void) Fclose(fin); 327130803Smarcel } 328130803Smarcelcant: 329130803Smarcel /* 33098944Sobrien * In days of old we removed the entry from the 33198944Sobrien * the list; now for sake of header expansion 33298944Sobrien * we leave it in and mark it as deleted. 33398944Sobrien */ 33498944Sobrien np->n_type |= GDEL; 33598944Sobrien np = np->n_flink; 33698944Sobrien } 33798944Sobrien if (image >= 0) { 33898944Sobrien (void) close(image); 33998944Sobrien image = -1; 34098944Sobrien } 34198944Sobrien return(top); 34298944Sobrien} 34398944Sobrien 34498944Sobrien/* 345130803Smarcel * Determine if the passed address is a local "send to file" address. 34698944Sobrien * If any of the network metacharacters precedes any slashes, it can't 347130803Smarcel * be a filename. We cheat with .'s to allow path names like ./... 348130803Smarcel */ 34998944Sobrienint 35098944Sobrienisfileaddr(name) 35198944Sobrien char *name; 35298944Sobrien{ 35398944Sobrien register char *cp; 35498944Sobrien 35598944Sobrien if (*name == '+') 35698944Sobrien return 1; 357130803Smarcel for (cp = name; *cp; cp++) { 358130803Smarcel if (*cp == '!' || *cp == '%' || *cp == '@') 35998944Sobrien return 0; 36098944Sobrien if (*cp == '/') 36198944Sobrien return 1; 36298944Sobrien } 36398944Sobrien return 0; 36498944Sobrien} 36598944Sobrien 36698944Sobrien/* 36798944Sobrien * Map all of the aliased users in the invoker's mailrc 36898944Sobrien * file and insert them into the list. 36998944Sobrien * Changed after all these months of service to recursively 370130803Smarcel * expand names (2/14/80). 371130803Smarcel */ 372130803Smarcel 373130803Smarcelstruct name * 374130803Smarcelusermap(names) 375130803Smarcel struct name *names; 376130803Smarcel{ 377130803Smarcel register struct name *new, *np, *cp; 378130803Smarcel struct grouphead *gh; 379130803Smarcel register int metoo; 380130803Smarcel 381130803Smarcel new = NIL; 382130803Smarcel np = names; 38398944Sobrien metoo = (value("metoo") != NOSTR); 38498944Sobrien while (np != NIL) { 38598944Sobrien if (np->n_name[0] == '\\') { 38698944Sobrien cp = np->n_flink; 38798944Sobrien new = put(new, np); 38898944Sobrien np = cp; 38998944Sobrien continue; 39098944Sobrien } 39198944Sobrien gh = findgroup(np->n_name); 39298944Sobrien cp = np->n_flink; 39398944Sobrien if (gh != NOGRP) 39498944Sobrien new = gexpand(new, gh, metoo, np->n_type); 39598944Sobrien else 39698944Sobrien new = put(new, np); 39798944Sobrien np = cp; 398130803Smarcel } 399130803Smarcel return(new); 400130803Smarcel} 401130803Smarcel 402130803Smarcel/* 403130803Smarcel * Recursively expand a group name. We limit the expansion to some 404130803Smarcel * fixed level to keep things from going haywire. 405130803Smarcel * Direct recursion is not expanded for convenience. 406130803Smarcel */ 40798944Sobrien 40898944Sobrienstruct name * 40998944Sobriengexpand(nlist, gh, metoo, ntype) 41098944Sobrien struct name *nlist; 41198944Sobrien struct grouphead *gh; 41298944Sobrien int metoo, ntype; 41398944Sobrien{ 41498944Sobrien struct group *gp; 41598944Sobrien struct grouphead *ngh; 416130803Smarcel struct name *np; 417130803Smarcel static int depth; 418130803Smarcel char *cp; 419130803Smarcel 420130803Smarcel if (depth > MAXEXP) { 421130803Smarcel printf("Expanding alias to depth larger than %d\n", MAXEXP); 42298944Sobrien return(nlist); 42398944Sobrien } 42498944Sobrien depth++; 42598944Sobrien for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) { 42698944Sobrien cp = gp->ge_name; 42798944Sobrien if (*cp == '\\') 42898944Sobrien goto quote; 42946283Sdfr if (strcmp(cp, gh->g_name) == 0) 43098944Sobrien goto quote; 43198944Sobrien if ((ngh = findgroup(cp)) != NOGRP) { 43298944Sobrien nlist = gexpand(nlist, ngh, metoo, ntype); 43319370Spst continue; 434130803Smarcel } 435130803Smarcelquote: 436130803Smarcel np = nalloc(cp, ntype); 437130803Smarcel /* 438130803Smarcel * At this point should allow to expand 439130803Smarcel * to self if only person in group 440 */ 441 if (gp == gh->g_list && gp->ge_link == NOGE) 442 goto skip; 443 if (!metoo && strcmp(cp, myname) == 0) 444 np->n_type |= GDEL; 445skip: 446 nlist = put(nlist, np); 447 } 448 depth--; 449 return(nlist); 450} 451 452/* 453 * Concatenate the two passed name lists, return the result. 454 */ 455struct name * 456cat(n1, n2) 457 struct name *n1, *n2; 458{ 459 register struct name *tail; 460 461 if (n1 == NIL) 462 return(n2); 463 if (n2 == NIL) 464 return(n1); 465 tail = tailof(n1); 466 tail->n_flink = n2; 467 n2->n_blink = tail; 468 return(n1); 469} 470 471/* 472 * Unpack the name list onto a vector of strings. 473 * Return an error if the name list won't fit. 474 */ 475char ** 476unpack(np) 477 struct name *np; 478{ 479 register char **ap, **top; 480 register struct name *n; 481 int t, extra, metoo, verbose; 482 483 n = np; 484 if ((t = count(n)) == 0) 485 panic("No names to unpack"); 486 /* 487 * Compute the number of extra arguments we will need. 488 * We need at least two extra -- one for "mail" and one for 489 * the terminating 0 pointer. Additional spots may be needed 490 * to pass along -f to the host mailer. 491 */ 492 extra = 2; 493 extra++; 494 metoo = value("metoo") != NOSTR; 495 if (metoo) 496 extra++; 497 verbose = value("verbose") != NOSTR; 498 if (verbose) 499 extra++; 500 top = (char **) salloc((t + extra) * sizeof *top); 501 ap = top; 502 *ap++ = "send-mail"; 503 *ap++ = "-i"; 504 if (metoo) 505 *ap++ = "-m"; 506 if (verbose) 507 *ap++ = "-v"; 508 for (; n != NIL; n = n->n_flink) 509 if ((n->n_type & GDEL) == 0) 510 *ap++ = n->n_name; 511 *ap = NOSTR; 512 return(top); 513} 514 515/* 516 * Remove all of the duplicates from the passed name list by 517 * insertion sorting them, then checking for dups. 518 * Return the head of the new list. 519 */ 520struct name * 521elide(names) 522 struct name *names; 523{ 524 register struct name *np, *t, *new; 525 struct name *x; 526 527 if (names == NIL) 528 return(NIL); 529 new = names; 530 np = names; 531 np = np->n_flink; 532 if (np != NIL) 533 np->n_blink = NIL; 534 new->n_flink = NIL; 535 while (np != NIL) { 536 t = new; 537 while (strcasecmp(t->n_name, np->n_name) < 0) { 538 if (t->n_flink == NIL) 539 break; 540 t = t->n_flink; 541 } 542 543 /* 544 * If we ran out of t's, put the new entry after 545 * the current value of t. 546 */ 547 548 if (strcasecmp(t->n_name, np->n_name) < 0) { 549 t->n_flink = np; 550 np->n_blink = t; 551 t = np; 552 np = np->n_flink; 553 t->n_flink = NIL; 554 continue; 555 } 556 557 /* 558 * Otherwise, put the new entry in front of the 559 * current t. If at the front of the list, 560 * the new guy becomes the new head of the list. 561 */ 562 563 if (t == new) { 564 t = np; 565 np = np->n_flink; 566 t->n_flink = new; 567 new->n_blink = t; 568 t->n_blink = NIL; 569 new = t; 570 continue; 571 } 572 573 /* 574 * The normal case -- we are inserting into the 575 * middle of the list. 576 */ 577 578 x = np; 579 np = np->n_flink; 580 x->n_flink = t; 581 x->n_blink = t->n_blink; 582 t->n_blink->n_flink = x; 583 t->n_blink = x; 584 } 585 586 /* 587 * Now the list headed up by new is sorted. 588 * Go through it and remove duplicates. 589 */ 590 591 np = new; 592 while (np != NIL) { 593 t = np; 594 while (t->n_flink != NIL && 595 strcasecmp(np->n_name, t->n_flink->n_name) == 0) 596 t = t->n_flink; 597 if (t == np || t == NIL) { 598 np = np->n_flink; 599 continue; 600 } 601 602 /* 603 * Now t points to the last entry with the same name 604 * as np. Make np point beyond t. 605 */ 606 607 np->n_flink = t->n_flink; 608 if (t->n_flink != NIL) 609 t->n_flink->n_blink = np; 610 np = np->n_flink; 611 } 612 return(new); 613} 614 615/* 616 * Put another node onto a list of names and return 617 * the list. 618 */ 619struct name * 620put(list, node) 621 struct name *list, *node; 622{ 623 node->n_flink = list; 624 node->n_blink = NIL; 625 if (list != NIL) 626 list->n_blink = node; 627 return(node); 628} 629 630/* 631 * Determine the number of undeleted elements in 632 * a name list and return it. 633 */ 634int 635count(np) 636 register struct name *np; 637{ 638 register int c; 639 640 for (c = 0; np != NIL; np = np->n_flink) 641 if ((np->n_type & GDEL) == 0) 642 c++; 643 return c; 644} 645 646/* 647 * Delete the given name from a namelist. 648 */ 649struct name * 650delname(np, name) 651 register struct name *np; 652 char name[]; 653{ 654 register struct name *p; 655 656 for (p = np; p != NIL; p = p->n_flink) 657 if (strcasecmp(p->n_name, name) == 0) { 658 if (p->n_blink == NIL) { 659 if (p->n_flink != NIL) 660 p->n_flink->n_blink = NIL; 661 np = p->n_flink; 662 continue; 663 } 664 if (p->n_flink == NIL) { 665 if (p->n_blink != NIL) 666 p->n_blink->n_flink = NIL; 667 continue; 668 } 669 p->n_blink->n_flink = p->n_flink; 670 p->n_flink->n_blink = p->n_blink; 671 } 672 return np; 673} 674 675/* 676 * Pretty print a name list 677 * Uncomment it if you need it. 678 */ 679 680/* 681void 682prettyprint(name) 683 struct name *name; 684{ 685 register struct name *np; 686 687 np = name; 688 while (np != NIL) { 689 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type); 690 np = np->n_flink; 691 } 692 fprintf(stderr, "\n"); 693} 694*/ 695