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