fixup in BIG_ENDIAN macro
[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 *
f15db449 5 * %sccs.include.redist.c%
761330fe
DF
6 */
7
acfc7e9b 8#ifndef lint
a0d23834 9static char sccsid[] = "@(#)names.c 5.17 (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 }
07b0d286
EW
236 fprintf(fout, "From %s %s", myname, date);
237 puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
238 while ((c = getc(fo)) != EOF)
239 (void) putc(c, fout);
240 rewind(fo);
241 (void) putc('\n', fout);
242 (void) fflush(fout);
243 if (ferror(fout))
244 perror(tempEdit);
245 (void) Fclose(fout);
65d3742f
KS
246 }
247
248 /*
249 * Now either copy "image" to the desired file
250 * or give it as the standard input to the desired
251 * program as appropriate.
252 */
253
254 if (ispipe) {
322c8626
EW
255 int pid;
256 char *shell;
257
07b0d286
EW
258 /*
259 * XXX
260 * We can't really reuse the same image file,
261 * because multiple piped recipients will
262 * share the same lseek location and trample
263 * on one another.
264 */
d33aa50d 265 if ((shell = value("SHELL")) == NOSTR)
435e8dff 266 shell = _PATH_CSHELL;
322c8626
EW
267 pid = start_command(shell, sigmask(SIGHUP)|
268 sigmask(SIGINT)|sigmask(SIGQUIT),
269 image, -1, "-c", fname, NOSTR);
270 if (pid < 0) {
65d3742f
KS
271 senderr++;
272 goto cant;
273 }
322c8626 274 free_child(pid);
d33aa50d 275 } else {
07b0d286
EW
276 int f;
277 if ((fout = Fopen(fname, "a")) == NULL) {
65d3742f
KS
278 perror(fname);
279 senderr++;
280 goto cant;
281 }
07b0d286
EW
282 if ((f = dup(image)) < 0) {
283 perror("dup");
284 fin = NULL;
285 } else
286 fin = Fdopen(f, "r");
65d3742f
KS
287 if (fin == NULL) {
288 fprintf(stderr, "Can't reopen image\n");
07b0d286 289 (void) Fclose(fout);
65d3742f
KS
290 senderr++;
291 goto cant;
292 }
293 rewind(fin);
294 while ((c = getc(fin)) != EOF)
751b30cd 295 (void) putc(c, fout);
65d3742f
KS
296 if (ferror(fout))
297 senderr++, perror(fname);
07b0d286
EW
298 (void) Fclose(fout);
299 (void) Fclose(fin);
65d3742f 300 }
65d3742f 301cant:
65d3742f
KS
302 /*
303 * In days of old we removed the entry from the
304 * the list; now for sake of header expansion
305 * we leave it in and mark it as deleted.
306 */
65d3742f
KS
307 np->n_type |= GDEL;
308 np = np->n_flink;
309 }
310 if (image >= 0) {
751b30cd 311 (void) close(image);
65d3742f
KS
312 image = -1;
313 }
314 return(top);
315}
316
5cf80950
KS
317/*
318 * Determine if the passed address is a local "send to file" address.
319 * If any of the network metacharacters precedes any slashes, it can't
320 * be a filename. We cheat with .'s to allow path names like ./...
321 */
a0d23834 322int
5cf80950
KS
323isfileaddr(name)
324 char *name;
325{
326 register char *cp;
5cf80950 327
1c617162 328 if (*name == '+')
3d6f01e5 329 return 1;
5cf80950 330 for (cp = name; *cp; cp++) {
3d6f01e5
EW
331 if (*cp == '!' || *cp == '%' || *cp == '@')
332 return 0;
5cf80950 333 if (*cp == '/')
3d6f01e5 334 return 1;
5cf80950 335 }
3d6f01e5 336 return 0;
5cf80950
KS
337}
338
65d3742f
KS
339/*
340 * Map all of the aliased users in the invoker's mailrc
341 * file and insert them into the list.
342 * Changed after all these months of service to recursively
343 * expand names (2/14/80).
344 */
345
346struct name *
347usermap(names)
348 struct name *names;
349{
350 register struct name *new, *np, *cp;
65d3742f
KS
351 struct grouphead *gh;
352 register int metoo;
353
354 new = NIL;
355 np = names;
65d3742f
KS
356 metoo = (value("metoo") != NOSTR);
357 while (np != NIL) {
358 if (np->n_name[0] == '\\') {
359 cp = np->n_flink;
360 new = put(new, np);
361 np = cp;
362 continue;
363 }
364 gh = findgroup(np->n_name);
365 cp = np->n_flink;
366 if (gh != NOGRP)
367 new = gexpand(new, gh, metoo, np->n_type);
368 else
369 new = put(new, np);
370 np = cp;
371 }
372 return(new);
373}
374
375/*
376 * Recursively expand a group name. We limit the expansion to some
377 * fixed level to keep things from going haywire.
378 * Direct recursion is not expanded for convenience.
379 */
380
381struct name *
382gexpand(nlist, gh, metoo, ntype)
383 struct name *nlist;
384 struct grouphead *gh;
a0d23834 385 int metoo, ntype;
65d3742f
KS
386{
387 struct group *gp;
388 struct grouphead *ngh;
389 struct name *np;
390 static int depth;
391 char *cp;
392
393 if (depth > MAXEXP) {
394 printf("Expanding alias to depth larger than %d\n", MAXEXP);
395 return(nlist);
396 }
397 depth++;
398 for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
399 cp = gp->ge_name;
400 if (*cp == '\\')
401 goto quote;
402 if (strcmp(cp, gh->g_name) == 0)
403 goto quote;
404 if ((ngh = findgroup(cp)) != NOGRP) {
405 nlist = gexpand(nlist, ngh, metoo, ntype);
406 continue;
407 }
408quote:
3d6f01e5 409 np = nalloc(cp, ntype);
65d3742f
KS
410 /*
411 * At this point should allow to expand
412 * to self if only person in group
413 */
414 if (gp == gh->g_list && gp->ge_link == NOGE)
415 goto skip;
416 if (!metoo && strcmp(cp, myname) == 0)
417 np->n_type |= GDEL;
418skip:
419 nlist = put(nlist, np);
420 }
421 depth--;
422 return(nlist);
423}
424
65d3742f
KS
425/*
426 * Concatenate the two passed name lists, return the result.
427 */
65d3742f
KS
428struct name *
429cat(n1, n2)
430 struct name *n1, *n2;
431{
432 register struct name *tail;
433
434 if (n1 == NIL)
435 return(n2);
436 if (n2 == NIL)
437 return(n1);
438 tail = tailof(n1);
439 tail->n_flink = n2;
440 n2->n_blink = tail;
441 return(n1);
442}
443
444/*
445 * Unpack the name list onto a vector of strings.
446 * Return an error if the name list won't fit.
447 */
65d3742f
KS
448char **
449unpack(np)
450 struct name *np;
451{
452 register char **ap, **top;
453 register struct name *n;
03c98b7d 454 int t, extra, metoo, verbose;
65d3742f
KS
455
456 n = np;
3d6f01e5 457 if ((t = count(n)) == 0)
65d3742f 458 panic("No names to unpack");
65d3742f
KS
459 /*
460 * Compute the number of extra arguments we will need.
461 * We need at least two extra -- one for "mail" and one for
462 * the terminating 0 pointer. Additional spots may be needed
2f025913 463 * to pass along -f to the host mailer.
65d3742f 464 */
65d3742f 465 extra = 2;
65d3742f
KS
466 extra++;
467 metoo = value("metoo") != NOSTR;
468 if (metoo)
469 extra++;
03c98b7d
CL
470 verbose = value("verbose") != NOSTR;
471 if (verbose)
472 extra++;
751b30cd 473 top = (char **) salloc((t + extra) * sizeof *top);
65d3742f
KS
474 ap = top;
475 *ap++ = "send-mail";
65d3742f
KS
476 *ap++ = "-i";
477 if (metoo)
478 *ap++ = "-m";
03c98b7d
CL
479 if (verbose)
480 *ap++ = "-v";
322c8626
EW
481 for (; n != NIL; n = n->n_flink)
482 if ((n->n_type & GDEL) == 0)
483 *ap++ = n->n_name;
65d3742f
KS
484 *ap = NOSTR;
485 return(top);
486}
487
65d3742f
KS
488/*
489 * Remove all of the duplicates from the passed name list by
490 * insertion sorting them, then checking for dups.
491 * Return the head of the new list.
492 */
65d3742f
KS
493struct name *
494elide(names)
495 struct name *names;
496{
497 register struct name *np, *t, *new;
498 struct name *x;
499
500 if (names == NIL)
501 return(NIL);
502 new = names;
503 np = names;
504 np = np->n_flink;
505 if (np != NIL)
506 np->n_blink = NIL;
507 new->n_flink = NIL;
508 while (np != NIL) {
509 t = new;
470c33f3 510 while (strcasecmp(t->n_name, np->n_name) < 0) {
65d3742f
KS
511 if (t->n_flink == NIL)
512 break;
513 t = t->n_flink;
514 }
515
516 /*
517 * If we ran out of t's, put the new entry after
518 * the current value of t.
519 */
520
470c33f3 521 if (strcasecmp(t->n_name, np->n_name) < 0) {
65d3742f
KS
522 t->n_flink = np;
523 np->n_blink = t;
524 t = np;
525 np = np->n_flink;
526 t->n_flink = NIL;
527 continue;
528 }
529
530 /*
531 * Otherwise, put the new entry in front of the
532 * current t. If at the front of the list,
533 * the new guy becomes the new head of the list.
534 */
535
536 if (t == new) {
537 t = np;
538 np = np->n_flink;
539 t->n_flink = new;
540 new->n_blink = t;
541 t->n_blink = NIL;
542 new = t;
543 continue;
544 }
545
546 /*
547 * The normal case -- we are inserting into the
548 * middle of the list.
549 */
550
551 x = np;
552 np = np->n_flink;
553 x->n_flink = t;
554 x->n_blink = t->n_blink;
555 t->n_blink->n_flink = x;
556 t->n_blink = x;
557 }
558
559 /*
560 * Now the list headed up by new is sorted.
561 * Go through it and remove duplicates.
562 */
563
564 np = new;
565 while (np != NIL) {
566 t = np;
470c33f3
EW
567 while (t->n_flink != NIL &&
568 strcasecmp(np->n_name, t->n_flink->n_name) == 0)
65d3742f
KS
569 t = t->n_flink;
570 if (t == np || t == NIL) {
571 np = np->n_flink;
572 continue;
573 }
574
575 /*
576 * Now t points to the last entry with the same name
577 * as np. Make np point beyond t.
578 */
579
580 np->n_flink = t->n_flink;
581 if (t->n_flink != NIL)
582 t->n_flink->n_blink = np;
583 np = np->n_flink;
584 }
585 return(new);
586}
587
65d3742f
KS
588/*
589 * Put another node onto a list of names and return
590 * the list.
591 */
65d3742f
KS
592struct name *
593put(list, node)
594 struct name *list, *node;
595{
596 node->n_flink = list;
597 node->n_blink = NIL;
598 if (list != NIL)
599 list->n_blink = node;
600 return(node);
601}
602
603/*
322c8626 604 * Determine the number of undeleted elements in
65d3742f
KS
605 * a name list and return it.
606 */
a0d23834 607int
65d3742f
KS
608count(np)
609 register struct name *np;
610{
3d6f01e5 611 register int c;
65d3742f 612
322c8626
EW
613 for (c = 0; np != NIL; np = np->n_flink)
614 if ((np->n_type & GDEL) == 0)
615 c++;
3d6f01e5 616 return c;
65d3742f
KS
617}
618
619/*
470c33f3 620 * Delete the given name from a namelist.
65d3742f 621 */
65d3742f 622struct name *
470c33f3 623delname(np, name)
65d3742f
KS
624 register struct name *np;
625 char name[];
626{
627 register struct name *p;
628
629 for (p = np; p != NIL; p = p->n_flink)
470c33f3 630 if (strcasecmp(p->n_name, name) == 0) {
65d3742f
KS
631 if (p->n_blink == NIL) {
632 if (p->n_flink != NIL)
633 p->n_flink->n_blink = NIL;
634 np = p->n_flink;
635 continue;
636 }
637 if (p->n_flink == NIL) {
638 if (p->n_blink != NIL)
639 p->n_blink->n_flink = NIL;
640 continue;
641 }
642 p->n_blink->n_flink = p->n_flink;
643 p->n_flink->n_blink = p->n_blink;
644 }
470c33f3 645 return np;
65d3742f
KS
646}
647
65d3742f
KS
648/*
649 * Pretty print a name list
650 * Uncomment it if you need it.
651 */
652
828615a1 653/*
a0d23834 654void
65d3742f
KS
655prettyprint(name)
656 struct name *name;
657{
658 register struct name *np;
659
660 np = name;
661 while (np != NIL) {
662 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
663 np = np->n_flink;
664 }
665 fprintf(stderr, "\n");
666}
828615a1 667*/