junk.c: file_expand() may fail
[s-mailx.git] / names.c
bloba8148c7f910dabe08fbb352ddf1853001fa877c2
1 /*
2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
41 * Mail -- a mail program
43 * Handle name lists.
46 #include "rcv.h"
47 #include "extern.h"
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <time.h>
52 #include <unistd.h>
53 #include <sys/stat.h>
55 /* Same name, taking care for *allnet*? */
56 static int same_name(char const *n1, char const *n2);
57 /* Delete the given name from a namelist */
58 static struct name * delname(struct name *np, char const *name);
59 /* Put another node onto a list of names and return the list */
60 static struct name * put(struct name *list, struct name *node);
61 /* Grab a single name (liberal name) */
62 static char const * yankname(char const *ap, char *wbuf,
63 char const *separators, int keepcomms);
64 /* Extraction multiplexer that splits an input line to names */
65 static struct name * extract1(char const *line, enum gfield ntype,
66 char const *separators, int keepcomms);
67 /* Recursively expand a group name. We limit the expansion to some fixed level
68 * to keep things from going haywire. Direct recursion is not expanded for
69 * convenience */
70 static struct name * gexpand(struct name *nlist, struct grouphead *gh,
71 int metoo, int ntype);
73 static int
74 same_name(char const *n1, char const *n2)
76 int ret = 0;
77 char c1, c2;
79 if (value("allnet") != NULL) {
80 do {
81 c1 = *n1++;
82 c2 = *n2++;
83 c1 = lowerconv(c1);
84 c2 = lowerconv(c2);
85 if (c1 != c2)
86 goto jleave;
87 } while (c1 != '\0' && c2 != '\0' && c1 != '@' && c2 != '@');
88 ret = 1;
89 } else
90 ret = (asccasecmp(n1, n2) == 0);
91 jleave:
92 return (ret);
95 static struct name *
96 delname(struct name *np, char const *name)
98 struct name *p;
100 for (p = np; p != NULL; p = p->n_flink)
101 if (same_name(p->n_name, name)) {
102 if (p->n_blink == NULL) {
103 if (p->n_flink != NULL)
104 p->n_flink->n_blink = NULL;
105 np = p->n_flink;
106 continue;
108 if (p->n_flink == NULL) {
109 if (p->n_blink != NULL)
110 p->n_blink->n_flink = NULL;
111 continue;
113 p->n_blink->n_flink = p->n_flink;
114 p->n_flink->n_blink = p->n_blink;
116 return (np);
119 static struct name *
120 put(struct name *list, struct name *node)
122 node->n_flink = list;
123 node->n_blink = NULL;
124 if (list != NULL)
125 list->n_blink = node;
126 return (node);
129 static char const *
130 yankname(char const *ap, char *wbuf, char const *separators, int keepcomms)
132 char const *cp;
133 char *wp, c, inquote, lc, lastsp;
135 *(wp = wbuf) = '\0';
137 /* Skip over intermediate list trash, as in ".org> , <xy@zz.org>" */
138 for (c = *ap; blankchar(c) || c == ','; c = *++ap)
140 if (c == '\0') {
141 cp = NULL;
142 goto jleave;
146 * Parse a full name: TODO RFC 5322
147 * - Keep everything in quotes, liberal handle *quoted-pair*s therein
148 * - Skip entire (nested) comments
149 * - In non-quote, non-comment, join adjacent space to a single SP
150 * - Understand separators only in non-quote, non-comment context,
151 * and only if not part of a *quoted-pair* (XXX too liberal)
153 cp = ap;
154 for (inquote = lc = lastsp = 0;; lc = c, ++cp) {
155 c = *cp;
156 if (c == '\0')
157 break;
158 if (c == '\\') {
159 lastsp = 0;
160 continue;
162 if (c == '"') {
163 if (lc != '\\')
164 inquote = ! inquote;
165 else
166 --wp;
167 goto jwpwc;
169 if (inquote || lc == '\\') {
170 jwpwc: *wp++ = c;
171 lastsp = 0;
172 continue;
174 if (c == '(') {
175 ap = cp;
176 cp = skip_comment(cp + 1);
177 if (keepcomms)
178 while (ap < cp)
179 *wp++ = *ap++;
180 --cp;
181 lastsp = 0;
182 continue;
184 if (strchr(separators, c) != NULL)
185 break;
187 lc = lastsp;
188 lastsp = blankchar(c);
189 if (! lastsp || ! lc)
190 *wp++ = c;
192 if (blankchar(lc))
193 --wp;
195 *wp = '\0';
196 jleave:
197 return (cp);
200 static struct name *
201 extract1(char const *line, enum gfield ntype, char const *separators,
202 int keepcomms)
204 struct name *top, *np, *t;
205 char const *cp;
206 char *nbuf;
208 top = NULL;
209 if (line == NULL || *line == '\0')
210 goto jleave;
212 np = NULL;
213 cp = line;
214 nbuf = ac_alloc(strlen(line) + 1);
215 while ((cp = yankname(cp, nbuf, separators, keepcomms)) != NULL) {
216 t = nalloc(nbuf, ntype);
217 if (top == NULL)
218 top = t;
219 else
220 np->n_flink = t;
221 t->n_blink = np;
222 np = t;
224 ac_free(nbuf);
226 jleave:
227 return (top);
230 static struct name *
231 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
233 struct group *gp;
234 struct grouphead *ngh;
235 struct name *np;
236 static int depth;
237 char *cp;
239 if (depth > MAXEXP) {
240 printf(tr(150, "Expanding alias to depth larger than %d\n"),
241 MAXEXP);
242 goto jleave;
244 depth++;
246 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
247 cp = gp->ge_name;
248 if (*cp == '\\')
249 goto quote;
250 if (strcmp(cp, gh->g_name) == 0)
251 goto quote;
252 if ((ngh = findgroup(cp)) != NULL) {
253 nlist = gexpand(nlist, ngh, metoo, ntype);
254 continue;
256 quote:
257 np = nalloc(cp, ntype|GFULL);
259 * At this point should allow to expand
260 * to self if only person in group
262 if (gp == gh->g_list && gp->ge_link == NULL)
263 goto skip;
264 if (! metoo && same_name(cp, myname))
265 np->n_type |= GDEL;
266 skip:
267 nlist = put(nlist, np);
269 --depth;
270 jleave:
271 return (nlist);
275 * Allocate a single element of a name list, initialize its name field to the
276 * passed name and return it.
278 struct name *
279 nalloc(char *str, enum gfield ntype)
281 struct addrguts ag;
282 struct str in, out;
283 struct name *np;
285 np = (struct name*)salloc(sizeof *np);
286 np->n_flink = NULL;
287 np->n_blink = NULL;
288 np->n_type = ntype;
289 np->n_flags = 0;
291 (void)addrspec_with_guts((ntype & (GFULL|GSKIN|GREF)) != 0, str, &ag);
292 if ((ag.ag_n_flags & NAME_NAME_SALLOC) == 0) {
293 ag.ag_n_flags |= NAME_NAME_SALLOC;
294 ag.ag_skinned = savestrbuf(ag.ag_skinned, ag.ag_slen);
296 np->n_fullname = np->n_name = ag.ag_skinned;
297 np->n_flags = ag.ag_n_flags;
299 if (ntype & GFULL) {
300 if (ag.ag_ilen == ag.ag_slen
301 #ifdef USE_IDNA
302 && (ag.ag_n_flags & NAME_IDNA) == 0
303 #endif
305 goto jleave;
306 if (ag.ag_n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
307 goto jleave;
308 #ifdef USE_IDNA
309 if ((ag.ag_n_flags & NAME_IDNA) == 0) {
310 #endif
311 in.s = str;
312 in.l = ag.ag_ilen;
313 #ifdef USE_IDNA
314 } else {
316 * The domain name was IDNA and has been converted.
317 * We also have to ensure that the domain name in
318 * .n_fullname is replaced with the converted version,
319 * since MIME doesn't perform encoding of addresses.
321 size_t l = ag.ag_iaddr_start,
322 lsuff = ag.ag_ilen - ag.ag_iaddr_aend;
323 in.s = ac_alloc(l + ag.ag_slen + lsuff + 1);
324 memcpy(in.s, str, l);
325 memcpy(in.s + l, ag.ag_skinned, ag.ag_slen);
326 l += ag.ag_slen;
327 memcpy(in.s + l, str + ag.ag_iaddr_aend, lsuff);
328 l += lsuff;
329 in.s[l] = '\0';
330 in.l = l;
332 #endif
333 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
334 np->n_fullname = savestr(out.s);
335 free(out.s);
336 #ifdef USE_IDNA
337 if (ag.ag_n_flags & NAME_IDNA)
338 ac_free(in.s);
339 #endif
340 np->n_flags |= NAME_FULLNAME_SALLOC;
341 } else if (ntype & GREF) { /* TODO LEGACY */
342 /* TODO Unfortunately we had to skin GREFerences i.e. the
343 * TODO surrounding angle brackets have been stripped away.
344 * TODO Necessarily since otherwise the plain address check
345 * TODO fails due to them; insert them back so that valid
346 * TODO headers will be created */
347 np->n_fullname = np->n_name = str = salloc(ag.ag_slen + 2 + 1);
348 *(str++) = '<';
349 memcpy(str, ag.ag_skinned, ag.ag_slen);
350 str += ag.ag_slen;
351 *(str++) = '>';
352 *str = '\0';
354 jleave:
355 return (np);
358 struct name *
359 ndup(struct name *np, enum gfield ntype)
361 struct name *nnp;
363 if ((ntype & (GFULL|GSKIN)) && (np->n_flags & NAME_SKINNED) == 0) {
364 nnp = nalloc(np->n_name, ntype);
365 goto jleave;
368 nnp = (struct name*)salloc(sizeof *np);
369 nnp->n_flink = nnp->n_blink = NULL;
370 nnp->n_type = ntype;
371 nnp->n_flags = (np->n_flags &
372 ~(NAME_NAME_SALLOC | NAME_FULLNAME_SALLOC)) |
373 NAME_NAME_SALLOC;
374 nnp->n_name = savestr(np->n_name);
375 if (np->n_name == np->n_fullname || (ntype & (GFULL|GSKIN)) == 0)
376 nnp->n_fullname = nnp->n_name;
377 else {
378 nnp->n_flags |= NAME_FULLNAME_SALLOC;
379 nnp->n_fullname = savestr(np->n_fullname);
381 jleave:
382 return (nnp);
386 * Concatenate the two passed name lists, return the result.
388 struct name *
389 cat(struct name *n1, struct name *n2)
391 struct name *tail;
393 if (n1 == NULL)
394 return (n2);
395 if (n2 == NULL)
396 return (n1);
398 tail = n1;
399 while (tail->n_flink != NULL)
400 tail = tail->n_flink;
401 tail->n_flink = n2;
402 n2->n_blink = tail;
403 return (n1);
407 * Determine the number of undeleted elements in
408 * a name list and return it.
411 count(struct name const*np)
413 int c;
415 for (c = 0; np != NULL; np = np->n_flink)
416 if ((np->n_type & GDEL) == 0)
417 c++;
418 return (c);
422 * Extract a list of names from a line,
423 * and make a list of names from it.
424 * Return the list or NULL if none found.
426 struct name *
427 extract(char const *line, enum gfield ntype)
429 return extract1(line, ntype, " \t,", 0);
432 struct name *
433 lextract(char const *line, enum gfield ntype)
435 return (extract1(line, ntype, ",", 1));
439 * Turn a list of names into a string of the same names.
441 char *
442 detract(struct name *np, enum gfield ntype)
444 char *top, *cp;
445 struct name *p;
446 int comma, s;
448 top = NULL;
449 if (np == NULL)
450 goto jleave;
452 comma = ntype & GCOMMA;
453 ntype &= ~GCOMMA;
454 s = 0;
455 if ((debug || value("debug")) && comma)
456 fprintf(stderr, tr(145, "detract asked to insert commas\n"));
457 for (p = np; p != NULL; p = p->n_flink) {
458 if (ntype && (p->n_type & GMASK) != ntype)
459 continue;
460 s += strlen(p->n_fullname) + 1;
461 if (comma)
462 s++;
464 if (s == 0)
465 goto jleave;
467 s += 2;
468 top = salloc(s);
469 cp = top;
470 for (p = np; p != NULL; p = p->n_flink) {
471 if (ntype && (p->n_type & GMASK) != ntype)
472 continue;
473 cp = sstpcpy(cp, p->n_fullname);
474 if (comma && p->n_flink != NULL)
475 *cp++ = ',';
476 *cp++ = ' ';
478 *--cp = 0;
479 if (comma && *--cp == ',')
480 *cp = 0;
481 jleave:
482 return (top);
486 * Check all addresses in np and delete invalid ones.
488 struct name *
489 checkaddrs(struct name *np)
491 struct name *n;
493 for (n = np; n != NULL;) {
494 if (is_addr_invalid(n, 1)) {
495 if (n->n_blink)
496 n->n_blink->n_flink = n->n_flink;
497 if (n->n_flink)
498 n->n_flink->n_blink = n->n_blink;
499 if (n == np)
500 np = n->n_flink;
502 n = n->n_flink;
504 return (np);
508 * Map all of the aliased users in the invoker's mailrc
509 * file and insert them into the list.
510 * Changed after all these months of service to recursively
511 * expand names (2/14/80).
513 struct name *
514 usermap(struct name *names)
516 struct name *new, *np, *cp;
517 struct grouphead *gh;
518 int metoo;
520 new = NULL;
521 np = names;
522 metoo = (value("metoo") != NULL);
523 while (np != NULL) {
524 if (np->n_name[0] == '\\') {
525 cp = np->n_flink;
526 new = put(new, np);
527 np = cp;
528 continue;
530 gh = findgroup(np->n_name);
531 cp = np->n_flink;
532 if (gh != NULL)
533 new = gexpand(new, gh, metoo, np->n_type);
534 else
535 new = put(new, np);
536 np = cp;
538 return(new);
542 * Remove all of the duplicates from the passed name list by
543 * insertion sorting them, then checking for dups.
544 * Return the head of the new list.
546 struct name *
547 elide(struct name *names)
549 struct name *np, *t, *newn, *x;
551 if (names == NULL)
552 return (NULL);
553 /* Throw away all deleted nodes (XXX merge with plain sort below?) */
554 for (newn = np = NULL; names != NULL; names = names->n_flink)
555 if ((names->n_type & GDEL) == 0) {
556 names->n_blink = np;
557 if (np)
558 np->n_flink = names;
559 else
560 newn = names;
561 np = names;
563 if (newn == NULL)
564 return (NULL);
566 np = newn->n_flink;
567 if (np != NULL)
568 np->n_blink = NULL;
569 newn->n_flink = NULL;
571 while (np != NULL) {
572 t = newn;
573 while (asccasecmp(t->n_name, np->n_name) < 0) {
574 if (t->n_flink == NULL)
575 break;
576 t = t->n_flink;
580 * If we ran out of t's, put the new entry after
581 * the current value of t.
584 if (asccasecmp(t->n_name, np->n_name) < 0) {
585 t->n_flink = np;
586 np->n_blink = t;
587 t = np;
588 np = np->n_flink;
589 t->n_flink = NULL;
590 continue;
594 * Otherwise, put the new entry in front of the
595 * current t. If at the front of the list,
596 * the new guy becomes the new head of the list.
599 if (t == newn) {
600 t = np;
601 np = np->n_flink;
602 t->n_flink = newn;
603 newn->n_blink = t;
604 t->n_blink = NULL;
605 newn = t;
606 continue;
610 * The normal case -- we are inserting into the
611 * middle of the list.
614 x = np;
615 np = np->n_flink;
616 x->n_flink = t;
617 x->n_blink = t->n_blink;
618 t->n_blink->n_flink = x;
619 t->n_blink = x;
623 * Now the list headed up by new is sorted.
624 * Go through it and remove duplicates.
627 np = newn;
628 while (np != NULL) {
629 t = np;
630 while (t->n_flink != NULL &&
631 asccasecmp(np->n_name, t->n_flink->n_name) == 0)
632 t = t->n_flink;
633 if (t == np || t == NULL) {
634 np = np->n_flink;
635 continue;
639 * Now t points to the last entry with the same name
640 * as np. Make np point beyond t.
643 np->n_flink = t->n_flink;
644 if (t->n_flink != NULL)
645 t->n_flink->n_blink = np;
646 np = np->n_flink;
648 return (newn);
651 struct name *
652 delete_alternates(struct name *np)
654 struct name *xp;
655 char **ap;
657 np = delname(np, myname);
658 if (altnames)
659 for (ap = altnames; *ap; ap++)
660 np = delname(np, *ap);
661 if ((xp = lextract(value("from"), GEXTRA|GSKIN)) != NULL)
662 while (xp) {
663 np = delname(np, xp->n_name);
664 xp = xp->n_flink;
666 if ((xp = lextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
667 while (xp) {
668 np = delname(np, xp->n_name);
669 xp = xp->n_flink;
671 if ((xp = extract(value("sender"), GEXTRA|GSKIN)) != NULL)
672 while (xp) {
673 np = delname(np, xp->n_name);
674 xp = xp->n_flink;
676 return (np);
680 is_myname(char const *name)
682 int ret = 1;
683 struct name *xp;
684 char **ap;
686 if (same_name(myname, name))
687 goto jleave;
688 if (altnames)
689 for (ap = altnames; *ap; ap++)
690 if (same_name(*ap, name))
691 goto jleave;
692 if ((xp = lextract(value("from"), GEXTRA|GSKIN)) != NULL)
693 while (xp) {
694 if (same_name(xp->n_name, name))
695 goto jleave;
696 xp = xp->n_flink;
698 if ((xp = lextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
699 while (xp) {
700 if (same_name(xp->n_name, name))
701 goto jleave;
702 xp = xp->n_flink;
704 if ((xp = extract(value("sender"), GEXTRA|GSKIN)) != NULL)
705 while (xp) {
706 if (same_name(xp->n_name, name))
707 goto jleave;
708 xp = xp->n_flink;
710 ret = 0;
711 jleave:
712 return (ret);
716 * For each recipient in the passed name list with a /
717 * in the name, append the message to the end of the named file
718 * and remove him from the recipient list.
720 * Recipients whose name begins with | are piped through the given
721 * program and removed.
723 struct name *
724 outof(struct name *names, FILE *fo, struct header *hp)
726 int pipecnt, xcnt, *fda, i;
727 char *shell, *date;
728 struct name *np;
729 time_t now;
730 FILE *fin = NULL, *fout;
731 (void)hp;
734 * Look through all recipients and do a quick return if no file or pipe
735 * addressee is found.
737 fda = NULL; /* Silence cc */
738 for (pipecnt = xcnt = 0, np = names; np != NULL; np = np->n_flink)
739 switch (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) {
740 case NAME_ADDRSPEC_ISFILE:
741 ++xcnt;
742 break;
743 case NAME_ADDRSPEC_ISPIPE:
744 ++pipecnt;
745 break;
747 if (pipecnt == 0 && xcnt == 0)
748 goto jleave;
751 * Otherwise create an array of file descriptors for each found pipe
752 * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
753 * each pipe subprocess needs its very own file descriptor, and we need
754 * to deal with that.
755 * To make our life a bit easier let's just use the auto-reclaimed
756 * string storage.
758 if (pipecnt == 0) {
759 fda = NULL;
760 shell = NULL;
761 } else {
762 fda = (int*)salloc(sizeof(int) * pipecnt);
763 for (i = 0; i < pipecnt; ++i)
764 fda[i] = -1;
765 if ((shell = value("SHELL")) == NULL)
766 shell = SHELL;
769 time(&now);
770 date = ctime(&now);
772 for (np = names; np != NULL;) {
773 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
774 == 0) {
775 np = np->n_flink;
776 continue;
780 * See if we have copied the complete message out yet.
781 * If not, do so.
783 if (image < 0) {
784 int c;
785 char *tempEdit;
787 if ((fout = Ftemp(&tempEdit, "Re", "w", 0600, 1))
788 == NULL) {
789 perror(tr(146, "Creation of temporary image"));
790 ++senderr;
791 goto jcant;
793 image = open(tempEdit, O_RDWR);
794 if (image >= 0)
795 for (i = 0; i < pipecnt; ++i) {
796 int fd = open(tempEdit, O_RDONLY);
797 if (fd < 0) {
798 (void)close(image);
799 image = -1;
800 pipecnt = i;
801 break;
803 fda[i] = fd;
804 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
806 unlink(tempEdit);
807 Ftfree(&tempEdit);
808 if (image < 0) {
809 perror(tr(147, "Creating descriptor duplicate "
810 "of temporary image"));
811 ++senderr;
812 Fclose(fout);
813 goto jcant;
815 fcntl(image, F_SETFD, FD_CLOEXEC);
817 fprintf(fout, "From %s %s", myname, date);
818 c = EOF;
819 while (i = c, (c = getc(fo)) != EOF)
820 putc(c, fout);
821 rewind(fo);
822 if (i != '\n')
823 putc('\n', fout);
824 putc('\n', fout);
825 fflush(fout);
826 if (ferror(fout)) {
827 perror(tr(148, "Finalizing write of temporary "
828 "image"));
829 Fclose(fout);
830 goto jcantfout;
832 Fclose(fout);
834 /* If we have to serve file addressees, open reader */
835 if (xcnt != 0 && (fin = Fdopen(image, "r")) == NULL) {
836 perror(tr(149, "Failed to open a duplicate of "
837 "the temporary image"));
838 jcantfout: ++senderr;
839 (void)close(image);
840 image = -1;
841 goto jcant;
844 /* From now on use xcnt as a counter for pipecnt */
845 xcnt = 0;
849 * Now either copy "image" to the desired file
850 * or give it as the standard input to the desired
851 * program as appropriate.
854 if (np->n_flags & NAME_ADDRSPEC_ISPIPE) {
855 int pid;
856 sigset_t nset;
858 sigemptyset(&nset);
859 sigaddset(&nset, SIGHUP);
860 sigaddset(&nset, SIGINT);
861 sigaddset(&nset, SIGQUIT);
862 pid = start_command(shell, &nset,
863 fda[xcnt++], -1, "-c", np->n_name + 1, NULL);
864 if (pid < 0) {
865 fprintf(stderr, tr(281,
866 "Message piping to <%s> failed\n"),
867 np->n_name);
868 ++senderr;
869 goto jcant;
871 free_child(pid);
872 } else {
873 char *fname = file_expand(np->n_name);
874 if (fname == NULL) {
875 fprintf(stderr, tr(81,
876 "\"%s\": Expansion failed.\n"),
877 np->n_name);
878 ++senderr;
879 goto jcant;
881 if ((fout = Zopen(fname, "a", NULL)) == NULL) {
882 fprintf(stderr, tr(282,
883 "Message writing to <%s> failed: %s\n"),
884 fname, strerror(errno));
885 ++senderr;
886 goto jcant;
888 rewind(fin);
889 while ((i = getc(fin)) != EOF)
890 putc(i, fout);
891 if (ferror(fout)) {
892 fprintf(stderr, tr(282,
893 "Message writing to <%s> failed: %s\n"),
894 fname, tr(283, "write error"));
895 ++senderr;
897 Fclose(fout);
899 jcant:
901 * In days of old we removed the entry from the
902 * the list; now for sake of header expansion
903 * we leave it in and mark it as deleted.
905 np->n_type |= GDEL;
906 np = np->n_flink;
907 if (image < 0)
908 goto jdelall;
910 jleave:
911 if (fin != NULL)
912 Fclose(fin);
913 for (i = 0; i < pipecnt; ++i)
914 (void)close(fda[i]);
915 if (image >= 0) {
916 close(image);
917 image = -1;
919 return (names);
921 jdelall:
922 while (np != NULL) {
923 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
924 != 0)
925 np->n_type |= GDEL;
926 np = np->n_flink;
928 goto jleave;