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