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