Reorder and "style-up" names.c..
[s-mailx.git] / names.c
blobe5d5d5c872de18d0dba36f13318d91dac5a0adfb
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 * Unpack the name list onto a vector of strings.
487 * Return an error if the name list won't fit.
489 char **
490 unpack(struct name *np)
492 char **ap, **top;
493 struct name *n;
494 int t, extra, metoo, verbose;
496 n = np;
497 if ((t = count(n)) == 0)
498 panic(tr(151, "No names to unpack"));
500 * Compute the number of extra arguments we will need.
501 * We need at least two extra -- one for "mail" and one for
502 * the terminating 0 pointer. Additional spots may be needed
503 * to pass along -f to the host mailer.
505 extra = 2;
506 extra++;
507 metoo = value("metoo") != NULL;
508 if (metoo)
509 extra++;
510 verbose = value("verbose") != NULL;
511 if (verbose)
512 extra++;
513 /*LINTED*/
514 top = (char **)salloc((t + extra) * sizeof *top);
515 ap = top;
516 *ap++ = "send-mail";
517 *ap++ = "-i";
518 if (metoo)
519 *ap++ = "-m";
520 if (verbose)
521 *ap++ = "-v";
522 for (; n != NULL; n = n->n_flink)
523 if ((n->n_type & GDEL) == 0)
524 *ap++ = n->n_name;
525 *ap = NULL;
526 return (top);
530 * Check all addresses in np and delete invalid ones.
532 struct name *
533 checkaddrs(struct name *np)
535 struct name *n;
537 for (n = np; n != NULL;) {
538 if (is_addr_invalid(n, 1)) {
539 if (n->n_blink)
540 n->n_blink->n_flink = n->n_flink;
541 if (n->n_flink)
542 n->n_flink->n_blink = n->n_blink;
543 if (n == np)
544 np = n->n_flink;
546 n = n->n_flink;
548 return (np);
552 * Map all of the aliased users in the invoker's mailrc
553 * file and insert them into the list.
554 * Changed after all these months of service to recursively
555 * expand names (2/14/80).
557 struct name *
558 usermap(struct name *names)
560 struct name *new, *np, *cp;
561 struct grouphead *gh;
562 int metoo;
564 new = NULL;
565 np = names;
566 metoo = (value("metoo") != NULL);
567 while (np != NULL) {
568 if (np->n_name[0] == '\\') {
569 cp = np->n_flink;
570 new = put(new, np);
571 np = cp;
572 continue;
574 gh = findgroup(np->n_name);
575 cp = np->n_flink;
576 if (gh != NULL)
577 new = gexpand(new, gh, metoo, np->n_type);
578 else
579 new = put(new, np);
580 np = cp;
582 return(new);
586 * Remove all of the duplicates from the passed name list by
587 * insertion sorting them, then checking for dups.
588 * Return the head of the new list.
590 struct name *
591 elide(struct name *names)
593 struct name *np, *t, *newn, *x;
595 if (names == NULL)
596 return (NULL);
597 /* Throw away all deleted nodes (XXX merge with plain sort below?) */
598 for (newn = np = NULL; names != NULL; names = names->n_flink)
599 if ((names->n_type & GDEL) == 0) {
600 names->n_blink = np;
601 if (np)
602 np->n_flink = names;
603 else
604 newn = names;
605 np = names;
607 if (newn == NULL)
608 return (NULL);
610 np = newn->n_flink;
611 if (np != NULL)
612 np->n_blink = NULL;
613 newn->n_flink = NULL;
615 while (np != NULL) {
616 t = newn;
617 while (asccasecmp(t->n_name, np->n_name) < 0) {
618 if (t->n_flink == NULL)
619 break;
620 t = t->n_flink;
624 * If we ran out of t's, put the new entry after
625 * the current value of t.
628 if (asccasecmp(t->n_name, np->n_name) < 0) {
629 t->n_flink = np;
630 np->n_blink = t;
631 t = np;
632 np = np->n_flink;
633 t->n_flink = NULL;
634 continue;
638 * Otherwise, put the new entry in front of the
639 * current t. If at the front of the list,
640 * the new guy becomes the new head of the list.
643 if (t == newn) {
644 t = np;
645 np = np->n_flink;
646 t->n_flink = newn;
647 newn->n_blink = t;
648 t->n_blink = NULL;
649 newn = t;
650 continue;
654 * The normal case -- we are inserting into the
655 * middle of the list.
658 x = np;
659 np = np->n_flink;
660 x->n_flink = t;
661 x->n_blink = t->n_blink;
662 t->n_blink->n_flink = x;
663 t->n_blink = x;
667 * Now the list headed up by new is sorted.
668 * Go through it and remove duplicates.
671 np = newn;
672 while (np != NULL) {
673 t = np;
674 while (t->n_flink != NULL &&
675 asccasecmp(np->n_name, t->n_flink->n_name) == 0)
676 t = t->n_flink;
677 if (t == np || t == NULL) {
678 np = np->n_flink;
679 continue;
683 * Now t points to the last entry with the same name
684 * as np. Make np point beyond t.
687 np->n_flink = t->n_flink;
688 if (t->n_flink != NULL)
689 t->n_flink->n_blink = np;
690 np = np->n_flink;
692 return (newn);
695 struct name *
696 delete_alternates(struct name *np)
698 struct name *xp;
699 char **ap;
701 np = delname(np, myname);
702 if (altnames)
703 for (ap = altnames; *ap; ap++)
704 np = delname(np, *ap);
705 if ((xp = lextract(value("from"), GEXTRA|GSKIN)) != NULL)
706 while (xp) {
707 np = delname(np, xp->n_name);
708 xp = xp->n_flink;
710 if ((xp = lextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
711 while (xp) {
712 np = delname(np, xp->n_name);
713 xp = xp->n_flink;
715 if ((xp = extract(value("sender"), GEXTRA|GSKIN)) != NULL)
716 while (xp) {
717 np = delname(np, xp->n_name);
718 xp = xp->n_flink;
720 return (np);
724 is_myname(char const *name)
726 int ret = 1;
727 struct name *xp;
728 char **ap;
730 if (same_name(myname, name))
731 goto jleave;
732 if (altnames)
733 for (ap = altnames; *ap; ap++)
734 if (same_name(*ap, name))
735 goto jleave;
736 if ((xp = lextract(value("from"), GEXTRA|GSKIN)) != NULL)
737 while (xp) {
738 if (same_name(xp->n_name, name))
739 goto jleave;
740 xp = xp->n_flink;
742 if ((xp = lextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
743 while (xp) {
744 if (same_name(xp->n_name, name))
745 goto jleave;
746 xp = xp->n_flink;
748 if ((xp = extract(value("sender"), GEXTRA|GSKIN)) != NULL)
749 while (xp) {
750 if (same_name(xp->n_name, name))
751 goto jleave;
752 xp = xp->n_flink;
754 ret = 0;
755 jleave:
756 return (ret);
760 * For each recipient in the passed name list with a /
761 * in the name, append the message to the end of the named file
762 * and remove him from the recipient list.
764 * Recipients whose name begins with | are piped through the given
765 * program and removed.
767 struct name *
768 outof(struct name *names, FILE *fo, struct header *hp)
770 int pipecnt, xcnt, *fda, i;
771 char *shell, *date;
772 struct name *np;
773 time_t now;
774 FILE *fin = NULL, *fout;
775 (void)hp;
778 * Look through all recipients and do a quick return if no file or pipe
779 * addressee is found.
781 fda = NULL; /* Silence cc */
782 for (pipecnt = xcnt = 0, np = names; np != NULL; np = np->n_flink)
783 switch (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) {
784 case NAME_ADDRSPEC_ISFILE:
785 ++xcnt;
786 break;
787 case NAME_ADDRSPEC_ISPIPE:
788 ++pipecnt;
789 break;
791 if (pipecnt == 0 && xcnt == 0)
792 goto jleave;
795 * Otherwise create an array of file descriptors for each found pipe
796 * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
797 * each pipe subprocess needs its very own file descriptor, and we need
798 * to deal with that.
799 * To make our life a bit easier let's just use the auto-reclaimed
800 * string storage.
802 if (pipecnt == 0) {
803 fda = NULL;
804 shell = NULL;
805 } else {
806 fda = (int*)salloc(sizeof(int) * pipecnt);
807 for (i = 0; i < pipecnt; ++i)
808 fda[i] = -1;
809 if ((shell = value("SHELL")) == NULL)
810 shell = SHELL;
813 time(&now);
814 date = ctime(&now);
816 for (np = names; np != NULL;) {
817 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
818 == 0) {
819 np = np->n_flink;
820 continue;
824 * See if we have copied the complete message out yet.
825 * If not, do so.
827 if (image < 0) {
828 int c;
829 char *tempEdit;
831 if ((fout = Ftemp(&tempEdit, "Re", "w", 0600, 1))
832 == NULL) {
833 perror(tr(146, "Creation of temporary image"));
834 ++senderr;
835 goto jcant;
837 image = open(tempEdit, O_RDWR);
838 if (image >= 0)
839 for (i = 0; i < pipecnt; ++i) {
840 int fd = open(tempEdit, O_RDONLY);
841 if (fd < 0) {
842 (void)close(image);
843 image = -1;
844 pipecnt = i;
845 break;
847 fda[i] = fd;
848 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
850 unlink(tempEdit);
851 Ftfree(&tempEdit);
852 if (image < 0) {
853 perror(tr(147, "Creating descriptor duplicate "
854 "of temporary image"));
855 ++senderr;
856 Fclose(fout);
857 goto jcant;
859 fcntl(image, F_SETFD, FD_CLOEXEC);
861 fprintf(fout, "From %s %s", myname, date);
862 c = EOF;
863 while (i = c, (c = getc(fo)) != EOF)
864 putc(c, fout);
865 rewind(fo);
866 if (i != '\n')
867 putc('\n', fout);
868 putc('\n', fout);
869 fflush(fout);
870 if (ferror(fout)) {
871 perror(tr(148, "Finalizing write of temporary "
872 "image"));
873 Fclose(fout);
874 goto jcantfout;
876 Fclose(fout);
878 /* If we have to serve file addressees, open reader */
879 if (xcnt != 0 && (fin = Fdopen(image, "r")) == NULL) {
880 perror(tr(149, "Failed to open a duplicate of "
881 "the temporary image"));
882 jcantfout: ++senderr;
883 (void)close(image);
884 image = -1;
885 goto jcant;
888 /* From now on use xcnt as a counter for pipecnt */
889 xcnt = 0;
893 * Now either copy "image" to the desired file
894 * or give it as the standard input to the desired
895 * program as appropriate.
898 if (np->n_flags & NAME_ADDRSPEC_ISPIPE) {
899 int pid;
900 sigset_t nset;
902 sigemptyset(&nset);
903 sigaddset(&nset, SIGHUP);
904 sigaddset(&nset, SIGINT);
905 sigaddset(&nset, SIGQUIT);
906 pid = start_command(shell, &nset,
907 fda[xcnt++], -1, "-c", np->n_name + 1, NULL);
908 if (pid < 0) {
909 fprintf(stderr, tr(281,
910 "Message piping to <%s> failed\n"),
911 np->n_name);
912 ++senderr;
913 goto jcant;
915 free_child(pid);
916 } else {
917 char *fname = file_expand(np->n_name);
918 if (fname == NULL) {
919 fprintf(stderr, tr(81,
920 "\"%s\": Expansion failed.\n"),
921 np->n_name);
922 ++senderr;
923 goto jcant;
925 if ((fout = Zopen(fname, "a", NULL)) == NULL) {
926 fprintf(stderr, tr(282,
927 "Message writing to <%s> failed: %s\n"),
928 fname, strerror(errno));
929 ++senderr;
930 goto jcant;
932 rewind(fin);
933 while ((i = getc(fin)) != EOF)
934 putc(i, fout);
935 if (ferror(fout)) {
936 fprintf(stderr, tr(282,
937 "Message writing to <%s> failed: %s\n"),
938 fname, tr(283, "write error"));
939 ++senderr;
941 Fclose(fout);
943 jcant:
945 * In days of old we removed the entry from the
946 * the list; now for sake of header expansion
947 * we leave it in and mark it as deleted.
949 np->n_type |= GDEL;
950 np = np->n_flink;
951 if (image < 0)
952 goto jdelall;
954 jleave:
955 if (fin != NULL)
956 Fclose(fin);
957 for (i = 0; i < pipecnt; ++i)
958 (void)close(fda[i]);
959 if (image >= 0) {
960 close(image);
961 image = -1;
963 return (names);
965 jdelall:
966 while (np != NULL) {
967 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
968 != 0)
969 np->n_type |= GDEL;
970 np = np->n_flink;
972 goto jleave;