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