*m*ail and *M*ail support all recipients! Use lextract()
[s-mailx.git] / names.c
blob6c4a93e7d065915f1c323200b43ebc38328555af
1 /*
2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
41 * Mail -- a mail program
43 * Handle name lists.
46 #include "rcv.h"
47 #include "extern.h"
49 #include <errno.h>
51 #include <fcntl.h>
52 #include <time.h>
53 #include <unistd.h>
54 #include <sys/stat.h>
56 /* Grab a single name (liberal name) */
57 static char const * yankname(char const *ap, char *wbuf,
58 char const *separators, int keepcomms);
59 /* Extraction multiplexer that splits an input line to names */
60 static struct name * extract1(char const *line, enum gfield ntype,
61 char const *separators, int keepcomms);
63 static struct name * tailof(struct name *name);
64 static int same_name(char *n1, char *n2);
65 static struct name * gexpand(struct name *nlist, struct grouphead *gh,
66 int metoo, int ntype);
67 static struct name * put(struct name *list, struct name *node);
68 static struct name * delname(struct name *np, char *name);
70 static char const *
71 yankname(char const *ap, char *wbuf, char const *separators, int keepcomms)
73 char const *cp;
74 char *wp, c, inquote, lc, lastsp;
76 *(wp = wbuf) = '\0';
78 /* Skip over intermediate list trash, as in ".org> , <xy@zz.org>" */
79 for (c = *ap; blankchar(c) || c == ','; c = *++ap)
81 if (c == '\0') {
82 cp = NULL;
83 goto jleave;
87 * Parse a full name: TODO RFC 5322
88 * - Keep everything in quotes, liberal handle *quoted-pair*s therein
89 * - Skip entire (nested) comments
90 * - In non-quote, non-comment, join adjacent space to a single SP
91 * - Understand separators only in non-quote, non-comment context,
92 * and only if not part of a *quoted-pair* (XXX too liberal)
94 cp = ap;
95 for (inquote = lc = lastsp = 0;; lc = c, ++cp) {
96 c = *cp;
97 if (c == '\0')
98 break;
99 if (c == '\\') {
100 lastsp = 0;
101 continue;
103 if (c == '"') {
104 if (lc != '\\')
105 inquote = ! inquote;
106 else
107 --wp;
108 goto jwpwc;
110 if (inquote || lc == '\\') {
111 jwpwc: *wp++ = c;
112 lastsp = 0;
113 continue;
115 if (c == '(') {
116 ap = cp;
117 cp = skip_comment(cp + 1);
118 if (keepcomms)
119 while (ap < cp)
120 *wp++ = *ap++;
121 --cp;
122 lastsp = 0;
123 continue;
125 if (strchr(separators, c) != NULL)
126 break;
128 lc = lastsp;
129 lastsp = blankchar(c);
130 if (! lastsp || ! lc)
131 *wp++ = c;
133 if (blankchar(lc))
134 --wp;
136 *wp = '\0';
137 jleave:
138 return (cp);
141 static struct name *
142 extract1(char const *line, enum gfield ntype, char const *separators,
143 int keepcomms)
145 struct name *top, *np, *t;
146 char const *cp;
147 char *nbuf;
149 top = NULL;
150 if (line == NULL || *line == '\0')
151 goto jleave;
152 np = NULL;
153 cp = line;
154 nbuf = ac_alloc(strlen(line) + 1);
155 while ((cp = yankname(cp, nbuf, separators, keepcomms)) != NULL) {
156 t = nalloc(nbuf, ntype);
157 if (top == NULL)
158 top = t;
159 else
160 np->n_flink = t;
161 t->n_blink = np;
162 np = t;
164 ac_free(nbuf);
165 jleave:
166 return (top);
170 * Allocate a single element of a name list, initialize its name field to the
171 * passed name and return it.
173 struct name *
174 nalloc(char *str, enum gfield ntype)
176 struct addrguts ag;
177 struct str in, out;
178 struct name *np;
180 np = (struct name*)salloc(sizeof *np);
181 np->n_flink = NULL;
182 np->n_blink = NULL;
183 np->n_type = ntype;
184 np->n_flags = 0;
186 (void)addrspec_with_guts((ntype & (GFULL|GSKIN|GREF)) != 0, str, &ag);
187 if ((ag.ag_n_flags & NAME_NAME_SALLOC) == 0) {
188 ag.ag_n_flags |= NAME_NAME_SALLOC;
189 ag.ag_skinned = savestrbuf(ag.ag_skinned, ag.ag_slen);
191 np->n_fullname = np->n_name = ag.ag_skinned;
192 np->n_flags = ag.ag_n_flags;
194 if (ntype & GFULL) {
195 if (ag.ag_ilen == ag.ag_slen
196 #ifdef USE_IDNA
197 && (ag.ag_n_flags & NAME_IDNA) == 0
198 #endif
200 goto jleave;
201 if (ag.ag_n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
202 goto jleave;
203 #ifdef USE_IDNA
204 if ((ag.ag_n_flags & NAME_IDNA) == 0) {
205 #endif
206 in.s = str;
207 in.l = ag.ag_ilen;
208 #ifdef USE_IDNA
209 } else {
211 * The domain name was IDNA and has been converted.
212 * We also have to ensure that the domain name in
213 * .n_fullname is replaced with the converted version,
214 * since MIME doesn't perform encoding of addresses.
216 size_t l = ag.ag_iaddr_start,
217 lsuff = ag.ag_ilen - ag.ag_iaddr_aend;
218 in.s = ac_alloc(l + ag.ag_slen + lsuff + 1);
219 memcpy(in.s, str, l);
220 memcpy(in.s + l, ag.ag_skinned, ag.ag_slen);
221 l += ag.ag_slen;
222 memcpy(in.s + l, str + ag.ag_iaddr_aend, lsuff);
223 l += lsuff;
224 in.s[l] = '\0';
225 in.l = l;
227 #endif
228 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
229 np->n_fullname = savestr(out.s);
230 free(out.s);
231 #ifdef USE_IDNA
232 if (ag.ag_n_flags & NAME_IDNA)
233 ac_free(in.s);
234 #endif
235 np->n_flags |= NAME_FULLNAME_SALLOC;
236 } else if (ntype & GREF) { /* TODO LEGACY */
237 /* TODO Unfortunately we had to skin GREFerences i.e. the
238 * TODO surrounding angle brackets have been stripped away.
239 * TODO Necessarily since otherwise the plain address check
240 * TODO fails due to them; insert them back so that valid
241 * TODO headers will be created */
242 np->n_fullname = np->n_name = str = salloc(ag.ag_slen + 2 + 1);
243 *(str++) = '<';
244 memcpy(str, ag.ag_skinned, ag.ag_slen);
245 str += ag.ag_slen;
246 *(str++) = '>';
247 *str = '\0';
249 jleave:
250 return (np);
253 struct name *
254 ndup(struct name *np, enum gfield ntype)
256 struct name *nnp;
258 if ((ntype & (GFULL|GSKIN)) && (np->n_flags & NAME_SKINNED) == 0) {
259 nnp = nalloc(np->n_name, ntype);
260 goto jleave;
263 nnp = (struct name*)salloc(sizeof *np);
264 nnp->n_flink = nnp->n_blink = NULL;
265 nnp->n_type = ntype;
266 nnp->n_flags = (np->n_flags &
267 ~(NAME_NAME_SALLOC | NAME_FULLNAME_SALLOC)) |
268 NAME_NAME_SALLOC;
269 nnp->n_name = savestr(np->n_name);
270 if (np->n_name == np->n_fullname || (ntype & (GFULL|GSKIN)) == 0)
271 nnp->n_fullname = nnp->n_name;
272 else {
273 nnp->n_flags |= NAME_FULLNAME_SALLOC;
274 nnp->n_fullname = savestr(np->n_fullname);
276 jleave:
277 return (nnp);
281 * Find the tail of a list and return it.
283 static struct name *
284 tailof(struct name *name)
286 struct name *np;
288 np = name;
289 if (np == NULL)
290 return(NULL);
291 while (np->n_flink != NULL)
292 np = np->n_flink;
293 return(np);
297 * Extract a list of names from a line,
298 * and make a list of names from it.
299 * Return the list or NULL if none found.
301 struct name *
302 extract(char const *line, enum gfield ntype)
304 return extract1(line, ntype, " \t,(", 0);
307 struct name *
308 sextract(char const *line, enum gfield ntype)
310 if (line && strpbrk(line, ",\"\\(<"))
311 return extract1(line, ntype, ",", 1);
312 else
313 return extract(line, ntype);
316 struct name *
317 lextract(char const *line, enum gfield ntype)
319 return (extract1(line, ntype, ",", 1));
323 * Turn a list of names into a string of the same names.
325 char *
326 detract(struct name *np, enum gfield ntype)
328 int s;
329 char *cp, *top;
330 struct name *p;
331 int comma;
333 comma = ntype & GCOMMA;
334 if (np == NULL)
335 return(NULL);
336 ntype &= ~GCOMMA;
337 s = 0;
338 if ((debug || value("debug")) && comma)
339 fprintf(stderr, catgets(catd, CATSET, 145,
340 "detract asked to insert commas\n"));
341 for (p = np; p != NULL; p = p->n_flink) {
342 if (ntype && (p->n_type & GMASK) != ntype)
343 continue;
344 s += strlen(p->n_fullname) + 1;
345 if (comma)
346 s++;
348 if (s == 0)
349 return(NULL);
350 s += 2;
351 top = salloc(s);
352 cp = top;
353 for (p = np; p != NULL; p = p->n_flink) {
354 if (ntype && (p->n_type & GMASK) != ntype)
355 continue;
356 cp = sstpcpy(cp, p->n_fullname);
357 if (comma && p->n_flink != NULL)
358 *cp++ = ',';
359 *cp++ = ' ';
361 *--cp = 0;
362 if (comma && *--cp == ',')
363 *cp = 0;
364 return(top);
368 * Check all addresses in np and delete invalid ones.
370 struct name *
371 checkaddrs(struct name *np)
373 struct name *n;
375 for (n = np; n != NULL;) {
376 if (is_addr_invalid(n, 1)) {
377 if (n->n_blink)
378 n->n_blink->n_flink = n->n_flink;
379 if (n->n_flink)
380 n->n_flink->n_blink = n->n_blink;
381 if (n == np)
382 np = n->n_flink;
384 n = n->n_flink;
386 return (np);
390 * For each recipient in the passed name list with a /
391 * in the name, append the message to the end of the named file
392 * and remove him from the recipient list.
394 * Recipients whose name begins with | are piped through the given
395 * program and removed.
397 struct name *
398 outof(struct name *names, FILE *fo, struct header *hp)
400 int pipecnt, xcnt, *fda, i;
401 char *shell, *date;
402 struct name *np;
403 time_t now;
404 FILE *fin = NULL, *fout;
405 (void)hp;
408 * Look through all recipients and do a quick return if no file or pipe
409 * addressee is found.
411 fda = NULL; /* Silence cc */
412 for (pipecnt = xcnt = 0, np = names; np != NULL; np = np->n_flink)
413 switch (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) {
414 case NAME_ADDRSPEC_ISFILE:
415 ++xcnt;
416 break;
417 case NAME_ADDRSPEC_ISPIPE:
418 ++pipecnt;
419 break;
421 if (pipecnt == 0 && xcnt == 0)
422 goto jleave;
425 * Otherwise create an array of file descriptors for each found pipe
426 * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
427 * each pipe subprocess needs its very own file descriptor, and we need
428 * to deal with that.
429 * To make our life a bit easier let's just use the auto-reclaimed
430 * string storage.
432 if (pipecnt == 0) {
433 fda = NULL;
434 shell = NULL;
435 } else {
436 fda = (int*)salloc(sizeof(int) * pipecnt);
437 for (i = 0; i < pipecnt; ++i)
438 fda[i] = -1;
439 if ((shell = value("SHELL")) == NULL)
440 shell = SHELL;
443 time(&now);
444 date = ctime(&now);
446 for (np = names; np != NULL;) {
447 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
448 == 0) {
449 np = np->n_flink;
450 continue;
454 * See if we have copied the complete message out yet.
455 * If not, do so.
457 if (image < 0) {
458 int c;
459 char *tempEdit;
461 if ((fout = Ftemp(&tempEdit, "Re", "w", 0600, 1))
462 == NULL) {
463 perror(tr(146, "Creation of temporary image"));
464 ++senderr;
465 goto jcant;
467 image = open(tempEdit, O_RDWR);
468 if (image >= 0)
469 for (i = 0; i < pipecnt; ++i) {
470 int fd = open(tempEdit, O_RDONLY);
471 if (fd < 0) {
472 (void)close(image);
473 image = -1;
474 pipecnt = i;
475 break;
477 fda[i] = fd;
478 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
480 unlink(tempEdit);
481 Ftfree(&tempEdit);
482 if (image < 0) {
483 perror(tr(147, "Creating descriptor duplicate "
484 "of temporary image"));
485 ++senderr;
486 Fclose(fout);
487 goto jcant;
489 fcntl(image, F_SETFD, FD_CLOEXEC);
491 fprintf(fout, "From %s %s", myname, date);
492 c = EOF;
493 while (i = c, (c = getc(fo)) != EOF)
494 putc(c, fout);
495 rewind(fo);
496 if (i != '\n')
497 putc('\n', fout);
498 putc('\n', fout);
499 fflush(fout);
500 if (ferror(fout)) {
501 perror(tr(148, "Finalizing write of temporary "
502 "image"));
503 Fclose(fout);
504 goto jcantfout;
506 Fclose(fout);
508 /* If we have to serve file addressees, open reader */
509 if (xcnt != 0 && (fin = Fdopen(image, "r")) == NULL) {
510 perror(tr(149, "Failed to open a duplicate of "
511 "the temporary image"));
512 jcantfout: ++senderr;
513 (void)close(image);
514 image = -1;
515 goto jcant;
518 /* From now on use xcnt as a counter for pipecnt */
519 xcnt = 0;
523 * Now either copy "image" to the desired file
524 * or give it as the standard input to the desired
525 * program as appropriate.
528 if (np->n_flags & NAME_ADDRSPEC_ISPIPE) {
529 int pid;
530 sigset_t nset;
532 sigemptyset(&nset);
533 sigaddset(&nset, SIGHUP);
534 sigaddset(&nset, SIGINT);
535 sigaddset(&nset, SIGQUIT);
536 pid = start_command(shell, &nset,
537 fda[xcnt++], -1, "-c", np->n_name + 1, NULL);
538 if (pid < 0) {
539 fprintf(stderr, tr(281,
540 "Message piping to <%s> failed\n"),
541 np->n_name);
542 ++senderr;
543 goto jcant;
545 free_child(pid);
546 } else {
547 char *fname = file_expand(np->n_name);
548 if (fname == NULL) {
549 fprintf(stderr, tr(81,
550 "\"%s\": Expansion failed.\n"),
551 np->n_name);
552 ++senderr;
553 goto jcant;
555 if ((fout = Zopen(fname, "a", NULL)) == NULL) {
556 fprintf(stderr, tr(282,
557 "Message writing to <%s> failed: %s\n"),
558 fname, strerror(errno));
559 ++senderr;
560 goto jcant;
562 rewind(fin);
563 while ((i = getc(fin)) != EOF)
564 putc(i, fout);
565 if (ferror(fout)) {
566 fprintf(stderr, tr(282,
567 "Message writing to <%s> failed: %s\n"),
568 fname, tr(283, "write error"));
569 ++senderr;
571 Fclose(fout);
573 jcant:
575 * In days of old we removed the entry from the
576 * the list; now for sake of header expansion
577 * we leave it in and mark it as deleted.
579 np->n_type |= GDEL;
580 np = np->n_flink;
581 if (image < 0)
582 goto jdelall;
584 jleave:
585 if (fin != NULL)
586 Fclose(fin);
587 for (i = 0; i < pipecnt; ++i)
588 (void)close(fda[i]);
589 if (image >= 0) {
590 close(image);
591 image = -1;
593 return (names);
595 jdelall:
596 while (np != NULL) {
597 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
598 != 0)
599 np->n_type |= GDEL;
600 np = np->n_flink;
602 goto jleave;
605 static int
606 same_name(char *n1, char *n2)
608 int c1, c2;
610 if (value("allnet") != NULL) {
611 do {
612 c1 = (*n1++ & 0377);
613 c2 = (*n2++ & 0377);
614 c1 = lowerconv(c1);
615 c2 = lowerconv(c2);
616 if (c1 != c2)
617 return 0;
618 } while (c1 != '\0' && c2 != '\0' && c1 != '@' && c2 != '@');
619 return 1;
620 } else
621 return asccasecmp(n1, n2) == 0;
625 * Map all of the aliased users in the invoker's mailrc
626 * file and insert them into the list.
627 * Changed after all these months of service to recursively
628 * expand names (2/14/80).
631 struct name *
632 usermap(struct name *names)
634 struct name *new, *np, *cp;
635 struct grouphead *gh;
636 int metoo;
638 new = NULL;
639 np = names;
640 metoo = (value("metoo") != NULL);
641 while (np != NULL) {
642 if (np->n_name[0] == '\\') {
643 cp = np->n_flink;
644 new = put(new, np);
645 np = cp;
646 continue;
648 gh = findgroup(np->n_name);
649 cp = np->n_flink;
650 if (gh != NULL)
651 new = gexpand(new, gh, metoo, np->n_type);
652 else
653 new = put(new, np);
654 np = cp;
656 return(new);
660 * Recursively expand a group name. We limit the expansion to some
661 * fixed level to keep things from going haywire.
662 * Direct recursion is not expanded for convenience.
665 static struct name *
666 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
668 struct group *gp;
669 struct grouphead *ngh;
670 struct name *np;
671 static int depth;
672 char *cp;
674 if (depth > MAXEXP) {
675 printf(catgets(catd, CATSET, 150,
676 "Expanding alias to depth larger than %d\n"), MAXEXP);
677 return(nlist);
679 depth++;
680 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
681 cp = gp->ge_name;
682 if (*cp == '\\')
683 goto quote;
684 if (strcmp(cp, gh->g_name) == 0)
685 goto quote;
686 if ((ngh = findgroup(cp)) != NULL) {
687 nlist = gexpand(nlist, ngh, metoo, ntype);
688 continue;
690 quote:
691 np = nalloc(cp, ntype|GFULL);
693 * At this point should allow to expand
694 * to self if only person in group
696 if (gp == gh->g_list && gp->ge_link == NULL)
697 goto skip;
698 if (!metoo && same_name(cp, myname))
699 np->n_type |= GDEL;
700 skip:
701 nlist = put(nlist, np);
703 depth--;
704 return(nlist);
708 * Concatenate the two passed name lists, return the result.
710 struct name *
711 cat(struct name *n1, struct name *n2)
713 struct name *tail;
715 if (n1 == NULL)
716 return(n2);
717 if (n2 == NULL)
718 return(n1);
719 tail = tailof(n1);
720 tail->n_flink = n2;
721 n2->n_blink = tail;
722 return(n1);
726 * Unpack the name list onto a vector of strings.
727 * Return an error if the name list won't fit.
729 char **
730 unpack(struct name *np)
732 char **ap, **top;
733 struct name *n;
734 int t, extra, metoo, verbose;
736 n = np;
737 if ((t = count(n)) == 0)
738 panic(catgets(catd, CATSET, 151, "No names to unpack"));
740 * Compute the number of extra arguments we will need.
741 * We need at least two extra -- one for "mail" and one for
742 * the terminating 0 pointer. Additional spots may be needed
743 * to pass along -f to the host mailer.
745 extra = 2;
746 extra++;
747 metoo = value("metoo") != NULL;
748 if (metoo)
749 extra++;
750 verbose = value("verbose") != NULL;
751 if (verbose)
752 extra++;
753 /*LINTED*/
754 top = (char **)salloc((t + extra) * sizeof *top);
755 ap = top;
756 *ap++ = "send-mail";
757 *ap++ = "-i";
758 if (metoo)
759 *ap++ = "-m";
760 if (verbose)
761 *ap++ = "-v";
762 for (; n != NULL; n = n->n_flink)
763 if ((n->n_type & GDEL) == 0)
764 *ap++ = n->n_name;
765 *ap = NULL;
766 return(top);
770 * Remove all of the duplicates from the passed name list by
771 * insertion sorting them, then checking for dups.
772 * Return the head of the new list.
774 struct name *
775 elide(struct name *names)
777 struct name *np, *t, *newn, *x;
779 if (names == NULL)
780 return (NULL);
781 /* Throw away all deleted nodes (XXX merge with plain sort below?) */
782 for (newn = np = NULL; names != NULL; names = names->n_flink)
783 if ((names->n_type & GDEL) == 0) {
784 names->n_blink = np;
785 if (np)
786 np->n_flink = names;
787 else
788 newn = names;
789 np = names;
791 if (newn == NULL)
792 return (NULL);
794 np = newn->n_flink;
795 if (np != NULL)
796 np->n_blink = NULL;
797 newn->n_flink = NULL;
799 while (np != NULL) {
800 t = newn;
801 while (asccasecmp(t->n_name, np->n_name) < 0) {
802 if (t->n_flink == NULL)
803 break;
804 t = t->n_flink;
808 * If we ran out of t's, put the new entry after
809 * the current value of t.
812 if (asccasecmp(t->n_name, np->n_name) < 0) {
813 t->n_flink = np;
814 np->n_blink = t;
815 t = np;
816 np = np->n_flink;
817 t->n_flink = NULL;
818 continue;
822 * Otherwise, put the new entry in front of the
823 * current t. If at the front of the list,
824 * the new guy becomes the new head of the list.
827 if (t == newn) {
828 t = np;
829 np = np->n_flink;
830 t->n_flink = newn;
831 newn->n_blink = t;
832 t->n_blink = NULL;
833 newn = t;
834 continue;
838 * The normal case -- we are inserting into the
839 * middle of the list.
842 x = np;
843 np = np->n_flink;
844 x->n_flink = t;
845 x->n_blink = t->n_blink;
846 t->n_blink->n_flink = x;
847 t->n_blink = x;
851 * Now the list headed up by new is sorted.
852 * Go through it and remove duplicates.
855 np = newn;
856 while (np != NULL) {
857 t = np;
858 while (t->n_flink != NULL &&
859 asccasecmp(np->n_name, t->n_flink->n_name) == 0)
860 t = t->n_flink;
861 if (t == np || t == NULL) {
862 np = np->n_flink;
863 continue;
867 * Now t points to the last entry with the same name
868 * as np. Make np point beyond t.
871 np->n_flink = t->n_flink;
872 if (t->n_flink != NULL)
873 t->n_flink->n_blink = np;
874 np = np->n_flink;
876 return (newn);
880 * Put another node onto a list of names and return
881 * the list.
883 static struct name *
884 put(struct name *list, struct name *node)
886 node->n_flink = list;
887 node->n_blink = NULL;
888 if (list != NULL)
889 list->n_blink = node;
890 return(node);
894 * Determine the number of undeleted elements in
895 * a name list and return it.
897 int
898 count(struct name *np)
900 int c;
902 for (c = 0; np != NULL; np = np->n_flink)
903 if ((np->n_type & GDEL) == 0)
904 c++;
905 return c;
909 * Delete the given name from a namelist.
911 static struct name *
912 delname(struct name *np, char *name)
914 struct name *p;
916 for (p = np; p != NULL; p = p->n_flink)
917 if (same_name(p->n_name, name)) {
918 if (p->n_blink == NULL) {
919 if (p->n_flink != NULL)
920 p->n_flink->n_blink = NULL;
921 np = p->n_flink;
922 continue;
924 if (p->n_flink == NULL) {
925 if (p->n_blink != NULL)
926 p->n_blink->n_flink = NULL;
927 continue;
929 p->n_blink->n_flink = p->n_flink;
930 p->n_flink->n_blink = p->n_blink;
932 return np;
936 * Pretty print a name list
937 * Uncomment it if you need it.
941 void
942 prettyprint(struct name *name)
944 struct name *np;
946 np = name;
947 while (np != NULL) {
948 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
949 np = np->n_flink;
951 fprintf(stderr, "\n");
955 struct name *
956 delete_alternates(struct name *np)
958 struct name *xp;
959 char **ap;
961 np = delname(np, myname);
962 if (altnames)
963 for (ap = altnames; *ap; ap++)
964 np = delname(np, *ap);
965 if ((xp = sextract(value("from"), GEXTRA|GSKIN)) != NULL)
966 while (xp) {
967 np = delname(np, xp->n_name);
968 xp = xp->n_flink;
970 if ((xp = sextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
971 while (xp) {
972 np = delname(np, xp->n_name);
973 xp = xp->n_flink;
975 if ((xp = sextract(value("sender"), GEXTRA|GSKIN)) != NULL)
976 while (xp) {
977 np = delname(np, xp->n_name);
978 xp = xp->n_flink;
980 return np;
984 is_myname(char *name)
986 struct name *xp;
987 char **ap;
989 if (same_name(myname, name))
990 return 1;
991 if (altnames)
992 for (ap = altnames; *ap; ap++)
993 if (same_name(*ap, name))
994 return 1;
995 if ((xp = sextract(value("from"), GEXTRA|GSKIN)) != NULL)
996 while (xp) {
997 if (same_name(xp->n_name, name))
998 return 1;
999 xp = xp->n_flink;
1001 if ((xp = sextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
1002 while (xp) {
1003 if (same_name(xp->n_name, name))
1004 return 1;
1005 xp = xp->n_flink;
1007 if ((xp = sextract(value("sender"), GEXTRA|GSKIN)) != NULL)
1008 while (xp) {
1009 if (same_name(xp->n_name, name))
1010 return 1;
1011 xp = xp->n_flink;
1013 return 0;