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