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