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