outof(): drop unused struct header argument
[s-mailx.git] / names.c
blobdf9c463ca9614173bf70f10a98b6a0b013acc284
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Handle name lists, alias expansion; outof(): serve file / pipe addresses.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <fcntl.h>
46 #ifndef O_CLOEXEC
47 # define _OUR_CLOEXEC
48 # define O_CLOEXEC 0
49 # define _SET_CLOEXEC(FD) fcntl((FD), F_SETFD, FD_CLOEXEC)
50 #else
51 # define _SET_CLOEXEC(FD)
52 #endif
54 /* Same name, while taking care for *allnet*? */
55 static bool_t _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);
60 /* Put another node onto a list of names and return the list */
61 static struct name * put(struct name *list, struct name *node);
63 /* Grab a single name (liberal name) */
64 static char const * yankname(char const *ap, char *wbuf,
65 char const *separators, int keepcomms);
67 /* Extraction multiplexer that splits an input line to names */
68 static struct name * _extract1(char const *line, enum gfield ntype,
69 char const *separators, bool_t keepcomms);
71 /* Recursively expand a group name. Limit expansion to some fixed level.
72 * Direct recursion is not expanded for convenience */
73 static struct name * _gexpand(size_t level, struct name *nlist,
74 struct grouphead *gh, bool_t metoo, int ntype);
76 static void _remove_grouplist(struct grouphead *gh);
78 static bool_t
79 _same_name(char const *n1, char const *n2)
81 bool_t rv = FAL0;
82 char c1, c2;
83 NYD_ENTER;
85 if (ok_blook(allnet)) {
86 do {
87 c1 = *n1++;
88 c2 = *n2++;
89 c1 = lowerconv(c1);
90 c2 = lowerconv(c2);
91 if (c1 != c2)
92 goto jleave;
93 } while (c1 != '\0' && c2 != '\0' && c1 != '@' && c2 != '@');
94 rv = 1;
95 } else
96 rv = !asccasecmp(n1, n2);
97 jleave:
98 NYD_LEAVE;
99 return rv;
102 static struct name *
103 delname(struct name *np, char const *name)
105 struct name *p;
106 NYD_ENTER;
108 for (p = np; p != NULL; p = p->n_flink)
109 if (_same_name(p->n_name, name)) {
110 if (p->n_blink == NULL) {
111 if (p->n_flink != NULL)
112 p->n_flink->n_blink = NULL;
113 np = p->n_flink;
114 continue;
116 if (p->n_flink == NULL) {
117 if (p->n_blink != NULL)
118 p->n_blink->n_flink = NULL;
119 continue;
121 p->n_blink->n_flink = p->n_flink;
122 p->n_flink->n_blink = p->n_blink;
124 NYD_LEAVE;
125 return np;
128 static struct name *
129 put(struct name *list, struct name *node)
131 NYD_ENTER;
132 node->n_flink = list;
133 node->n_blink = NULL;
134 if (list != NULL)
135 list->n_blink = node;
136 NYD_LEAVE;
137 return node;
140 static char const *
141 yankname(char const *ap, char *wbuf, char const *separators, int keepcomms)
143 char const *cp;
144 char *wp, c, inquote, lc, lastsp;
145 NYD_ENTER;
147 *(wp = wbuf) = '\0';
149 /* Skip over intermediate list trash, as in ".org> , <xy@zz.org>" */
150 for (c = *ap; blankchar(c) || c == ','; c = *++ap)
152 if (c == '\0') {
153 cp = NULL;
154 goto jleave;
157 /* Parse a full name: TODO RFC 5322
158 * - Keep everything in quotes, liberal handle *quoted-pair*s therein
159 * - Skip entire (nested) comments
160 * - In non-quote, non-comment, join adjacent space to a single SP
161 * - Understand separators only in non-quote, non-comment context,
162 * and only if not part of a *quoted-pair* (XXX too liberal) */
163 cp = ap;
164 for (inquote = lc = lastsp = 0;; lc = c, ++cp) {
165 c = *cp;
166 if (c == '\0')
167 break;
168 if (c == '\\') {
169 lastsp = 0;
170 continue;
172 if (c == '"') {
173 if (lc != '\\')
174 inquote = !inquote;
175 #if 0 /* TODO when doing real RFC 5322 parsers - why have i done this? */
176 else
177 --wp;
178 #endif
179 goto jwpwc;
181 if (inquote || lc == '\\') {
182 jwpwc:
183 *wp++ = c;
184 lastsp = 0;
185 continue;
187 if (c == '(') {
188 ap = cp;
189 cp = skip_comment(cp + 1);
190 if (keepcomms)
191 while (ap < cp)
192 *wp++ = *ap++;
193 --cp;
194 lastsp = 0;
195 continue;
197 if (strchr(separators, c) != NULL)
198 break;
200 lc = lastsp;
201 lastsp = blankchar(c);
202 if (!lastsp || !lc)
203 *wp++ = c;
205 if (blankchar(lc))
206 --wp;
208 *wp = '\0';
209 jleave:
210 NYD_LEAVE;
211 return cp;
214 static struct name *
215 _extract1(char const *line, enum gfield ntype, char const *separators,
216 bool_t keepcomms)
218 struct name *topp, *np, *t;
219 char const *cp;
220 char *nbuf;
221 NYD_ENTER;
223 topp = NULL;
224 if (line == NULL || *line == '\0')
225 goto jleave;
227 np = NULL;
228 cp = line;
229 nbuf = ac_alloc(strlen(line) +1);
230 while ((cp = yankname(cp, nbuf, separators, keepcomms)) != NULL) {
231 t = nalloc(nbuf, ntype);
232 if (topp == NULL)
233 topp = t;
234 else
235 np->n_flink = t;
236 t->n_blink = np;
237 np = t;
239 ac_free(nbuf);
240 jleave:
241 NYD_LEAVE;
242 return topp;
245 static struct name *
246 _gexpand(size_t level, struct name *nlist, struct grouphead *gh, bool_t metoo,
247 int ntype)
249 struct group *gp;
250 struct grouphead *ngh;
251 struct name *np;
252 char *cp;
253 NYD_ENTER;
255 if (UICMP(z, level++, >, MAXEXP)) {
256 printf(tr(150, "Expanding alias to depth larger than %d\n"), MAXEXP);
257 goto jleave;
260 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
261 cp = gp->ge_name;
262 if (*cp == '\\')
263 goto jquote;
264 if (!strcmp(cp, gh->g_name))
265 goto jquote;
266 if ((ngh = findgroup(cp)) != NULL) {
267 /* For S-nail(1), the "group" may *be* the sender in that a name maps
268 * to a full address specification */
269 if (!metoo && ngh->g_list->ge_link == NULL && _same_name(cp, myname))
270 continue;
271 nlist = _gexpand(level, nlist, ngh, metoo, ntype);
272 continue;
274 jquote:
275 np = nalloc(cp, ntype | GFULL);
276 /* At this point should allow to expand itself if only person in group */
277 if (gp == gh->g_list && gp->ge_link == NULL)
278 goto jskip;
279 if (!metoo && _same_name(cp, myname))
280 np->n_type |= GDEL;
281 jskip:
282 nlist = put(nlist, np);
284 jleave:
285 NYD_LEAVE;
286 return nlist;
289 static void
290 _remove_grouplist(struct grouphead *gh)
292 struct group *gp, *gq;
293 NYD_ENTER;
295 if ((gp = gh->g_list) != NULL) {
296 for (; gp; gp = gq) {
297 gq = gp->ge_link;
298 free(gp->ge_name);
299 free(gp);
302 NYD_LEAVE;
305 FL struct name *
306 nalloc(char *str, enum gfield ntype)
308 struct addrguts ag;
309 struct str in, out;
310 struct name *np;
311 NYD_ENTER;
313 np = salloc(sizeof *np);
314 np->n_flink = NULL;
315 np->n_blink = NULL;
316 np->n_type = ntype;
317 np->n_flags = 0;
319 addrspec_with_guts(((ntype & (GFULL | GSKIN | GREF)) != 0), str, &ag);
320 if (!(ag.ag_n_flags & NAME_NAME_SALLOC)) {
321 ag.ag_n_flags |= NAME_NAME_SALLOC;
322 ag.ag_skinned = savestrbuf(ag.ag_skinned, ag.ag_slen);
324 np->n_fullname = np->n_name = ag.ag_skinned;
325 np->n_flags = ag.ag_n_flags;
327 if (ntype & GFULL) {
328 if (ag.ag_ilen == ag.ag_slen
329 #ifdef HAVE_IDNA
330 && !(ag.ag_n_flags & NAME_IDNA)
331 #endif
333 goto jleave;
334 if (ag.ag_n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
335 goto jleave;
336 #ifdef HAVE_IDNA
337 if (!(ag.ag_n_flags & NAME_IDNA)) {
338 #endif
339 in.s = str;
340 in.l = ag.ag_ilen;
341 #ifdef HAVE_IDNA
342 } else {
343 /* The domain name was IDNA and has been converted. We also have to
344 * ensure that the domain name in .n_fullname is replaced with the
345 * converted version, since MIME doesn't perform encoding of addrs */
346 size_t l = ag.ag_iaddr_start,
347 lsuff = ag.ag_ilen - ag.ag_iaddr_aend;
348 in.s = ac_alloc(l + ag.ag_slen + lsuff +1);
349 memcpy(in.s, str, l);
350 memcpy(in.s + l, ag.ag_skinned, ag.ag_slen);
351 l += ag.ag_slen;
352 memcpy(in.s + l, str + ag.ag_iaddr_aend, lsuff);
353 l += lsuff;
354 in.s[l] = '\0';
355 in.l = l;
357 #endif
358 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
359 np->n_fullname = savestr(out.s);
360 free(out.s);
361 #ifdef HAVE_IDNA
362 if (ag.ag_n_flags & NAME_IDNA)
363 ac_free(in.s);
364 #endif
365 np->n_flags |= NAME_FULLNAME_SALLOC;
366 } else if (ntype & GREF) { /* TODO LEGACY */
367 /* TODO Unfortunately we had to skin GREFerences i.e. the
368 * TODO surrounding angle brackets have been stripped away.
369 * TODO Necessarily since otherwise the plain address check
370 * TODO fails due to them; insert them back so that valid
371 * TODO headers will be created */
372 np->n_fullname = np->n_name = str = salloc(ag.ag_slen + 2 +1);
373 *(str++) = '<';
374 memcpy(str, ag.ag_skinned, ag.ag_slen);
375 str += ag.ag_slen;
376 *(str++) = '>';
377 *str = '\0';
379 jleave:
380 NYD_LEAVE;
381 return np;
384 FL struct name *
385 ndup(struct name *np, enum gfield ntype)
387 struct name *nnp;
388 NYD_ENTER;
390 if ((ntype & (GFULL | GSKIN)) && !(np->n_flags & NAME_SKINNED)) {
391 nnp = nalloc(np->n_name, ntype);
392 goto jleave;
395 nnp = salloc(sizeof *np);
396 nnp->n_flink = nnp->n_blink = NULL;
397 nnp->n_type = ntype;
398 nnp->n_flags = (np->n_flags & ~(NAME_NAME_SALLOC | NAME_FULLNAME_SALLOC)) |
399 NAME_NAME_SALLOC;
400 nnp->n_name = savestr(np->n_name);
401 if (np->n_name == np->n_fullname || !(ntype & (GFULL | GSKIN)))
402 nnp->n_fullname = nnp->n_name;
403 else {
404 nnp->n_flags |= NAME_FULLNAME_SALLOC;
405 nnp->n_fullname = savestr(np->n_fullname);
407 jleave:
408 NYD_LEAVE;
409 return nnp;
412 FL struct name *
413 cat(struct name *n1, struct name *n2)
415 struct name *tail;
416 NYD_ENTER;
418 tail = n2;
419 if (n1 == NULL)
420 goto jleave;
421 tail = n1;
422 if (n2 == NULL)
423 goto jleave;
425 while (tail->n_flink != NULL)
426 tail = tail->n_flink;
427 tail->n_flink = n2;
428 n2->n_blink = tail;
429 tail = n1;
430 jleave:
431 NYD_LEAVE;
432 return tail;
435 FL ui32_t
436 count(struct name const*np)
438 ui32_t c;
439 NYD_ENTER;
441 for (c = 0; np != NULL; np = np->n_flink)
442 if (!(np->n_type & GDEL))
443 ++c;
444 NYD_LEAVE;
445 return c;
448 FL struct name *
449 extract(char const *line, enum gfield ntype)
451 struct name *rv;
452 NYD_ENTER;
454 rv = _extract1(line, ntype, " \t,", 0);
455 NYD_LEAVE;
456 return rv;
459 FL struct name *
460 lextract(char const *line, enum gfield ntype)
462 struct name *rv;
463 NYD_ENTER;
465 rv = ((line != NULL && strpbrk(line, ",\"\\(<|"))
466 ? _extract1(line, ntype, ",", 1) : extract(line, ntype));
467 NYD_LEAVE;
468 return rv;
471 FL char *
472 detract(struct name *np, enum gfield ntype)
474 char *topp, *cp;
475 struct name *p;
476 int comma, s;
477 NYD_ENTER;
479 topp = NULL;
480 if (np == NULL)
481 goto jleave;
483 comma = ntype & GCOMMA;
484 ntype &= ~GCOMMA;
485 s = 0;
486 if ((options & OPT_DEBUG) && comma)
487 fprintf(stderr, tr(145, "detract asked to insert commas\n"));
488 for (p = np; p != NULL; p = p->n_flink) {
489 if (ntype && (p->n_type & GMASK) != ntype)
490 continue;
491 s += strlen(p->n_fullname) +1;
492 if (comma)
493 s++;
495 if (s == 0)
496 goto jleave;
498 s += 2;
499 topp = salloc(s);
500 cp = topp;
501 for (p = np; p != NULL; p = p->n_flink) {
502 if (ntype && (p->n_type & GMASK) != ntype)
503 continue;
504 cp = sstpcpy(cp, p->n_fullname);
505 if (comma && p->n_flink != NULL)
506 *cp++ = ',';
507 *cp++ = ' ';
509 *--cp = 0;
510 if (comma && *--cp == ',')
511 *cp = 0;
512 jleave:
513 NYD_LEAVE;
514 return topp;
517 FL struct name *
518 grab_names(char const *field, struct name *np, int comma, enum gfield gflags)
520 struct name *nq;
521 NYD_ENTER;
523 jloop:
524 np = lextract(readstr_input(field, detract(np, comma)), gflags);
525 for (nq = np; nq != NULL; nq = nq->n_flink)
526 if (is_addr_invalid(nq, 1))
527 goto jloop;
528 NYD_LEAVE;
529 return np;
532 FL struct name *
533 checkaddrs(struct name *np)
535 struct name *n;
536 NYD_ENTER;
538 for (n = np; n != NULL;) {
539 if (is_addr_invalid(n, 1)) {
540 if (n->n_blink)
541 n->n_blink->n_flink = n->n_flink;
542 if (n->n_flink)
543 n->n_flink->n_blink = n->n_blink;
544 if (n == np)
545 np = n->n_flink;
547 n = n->n_flink;
549 NYD_LEAVE;
550 return np;
553 FL struct name *
554 usermap(struct name *names, bool_t force_metoo)
556 struct name *new, *np, *cp;
557 struct grouphead *gh;
558 int metoo;
559 NYD_ENTER;
561 new = NULL;
562 np = names;
563 metoo = (force_metoo || ok_blook(metoo));
564 while (np != NULL) {
565 assert(!(np->n_type & GDEL)); /* TODO legacy */
566 if (is_fileorpipe_addr(np) || np->n_name[0] == '\\') {
567 cp = np->n_flink;
568 new = put(new, np);
569 np = cp;
570 continue;
572 gh = findgroup(np->n_name);
573 cp = np->n_flink;
574 if (gh != NULL)
575 new = _gexpand(0, new, gh, metoo, np->n_type);
576 else
577 new = put(new, np);
578 np = cp;
580 NYD_LEAVE;
581 return new;
584 FL struct name *
585 elide(struct name *names)
587 struct name *np, *t, *newn, *x;
588 NYD_ENTER;
590 newn = NULL;
591 if (names == NULL)
592 goto jleave;
594 /* Throw away all deleted nodes (XXX merge with plain sort below?) */
595 for (np = NULL; names != NULL; names = names->n_flink)
596 if (!(names->n_type & GDEL)) {
597 names->n_blink = np;
598 if (np)
599 np->n_flink = names;
600 else
601 newn = names;
602 np = names;
604 if (newn == NULL)
605 goto jleave;
607 np = newn->n_flink;
608 if (np != NULL)
609 np->n_blink = NULL;
610 newn->n_flink = NULL;
612 while (np != NULL) {
613 int cmpres;
615 t = newn;
616 while ((cmpres = asccasecmp(t->n_name, np->n_name)) < 0) {
617 if (t->n_flink == NULL)
618 break;
619 t = t->n_flink;
622 /* If we ran out of t's, put new entry after the current value of t */
623 if (cmpres < 0) {
624 t->n_flink = np;
625 np->n_blink = t;
626 t = np;
627 np = np->n_flink;
628 t->n_flink = NULL;
629 continue;
632 /* Otherwise, put the new entry in front of the current t. If at the
633 * front of the list, the new guy becomes the new head of the list */
634 if (t == newn) {
635 t = np;
636 np = np->n_flink;
637 t->n_flink = newn;
638 newn->n_blink = t;
639 t->n_blink = NULL;
640 newn = t;
641 continue;
644 /* The normal case -- we are inserting into the middle of the list */
645 x = np;
646 np = np->n_flink;
647 x->n_flink = t;
648 x->n_blink = t->n_blink;
649 t->n_blink->n_flink = x;
650 t->n_blink = x;
653 /* Now the list headed up by new is sorted. Remove duplicates */
654 np = newn;
655 while (np != NULL) {
656 t = np;
657 while (t->n_flink != NULL && !asccasecmp(np->n_name, t->n_flink->n_name))
658 t = t->n_flink;
659 if (t == np) {
660 np = np->n_flink;
661 continue;
664 /* Now t points to the last entry with the same name as np.
665 * Make np point beyond t */
666 np->n_flink = t->n_flink;
667 if (t->n_flink != NULL)
668 t->n_flink->n_blink = np;
669 np = np->n_flink;
671 jleave:
672 NYD_LEAVE;
673 return newn;
676 FL struct name *
677 delete_alternates(struct name *np)
679 struct name *xp;
680 char **ap;
681 NYD_ENTER;
683 np = delname(np, myname);
684 if (altnames)
685 for (ap = altnames; *ap != '\0'; ++ap)
686 np = delname(np, *ap);
688 if ((xp = lextract(ok_vlook(from), GEXTRA | GSKIN)) != NULL)
689 while (xp != NULL) {
690 np = delname(np, xp->n_name);
691 xp = xp->n_flink;
694 if ((xp = lextract(ok_vlook(replyto), GEXTRA | GSKIN)) != NULL)
695 while (xp != NULL) {
696 np = delname(np, xp->n_name);
697 xp = xp->n_flink;
700 if ((xp = extract(ok_vlook(sender), GEXTRA | GSKIN)) != NULL)
701 while (xp != NULL) {
702 np = delname(np, xp->n_name);
703 xp = xp->n_flink;
705 NYD_LEAVE;
706 return np;
709 FL int
710 is_myname(char const *name)
712 int rv = 1;
713 struct name *xp;
714 char **ap;
715 NYD_ENTER;
717 if (_same_name(myname, name))
718 goto jleave;
719 if (altnames)
720 for (ap = altnames; *ap != NULL; ++ap)
721 if (_same_name(*ap, name))
722 goto jleave;
724 if ((xp = lextract(ok_vlook(from), GEXTRA | GSKIN)) != NULL)
725 while (xp != NULL) {
726 if (_same_name(xp->n_name, name))
727 goto jleave;
728 xp = xp->n_flink;
731 if ((xp = lextract(ok_vlook(replyto), GEXTRA | GSKIN)) != NULL)
732 while (xp != NULL) {
733 if (_same_name(xp->n_name, name))
734 goto jleave;
735 xp = xp->n_flink;
738 if ((xp = extract(ok_vlook(sender), GEXTRA | GSKIN)) != NULL)
739 while (xp != NULL) {
740 if (_same_name(xp->n_name, name))
741 goto jleave;
742 xp = xp->n_flink;
744 rv = 0;
745 jleave:
746 NYD_LEAVE;
747 return rv;
750 FL struct name *
751 outof(struct name *names, FILE *fo, bool_t *senderror)
753 ui32_t pipecnt, xcnt, i;
754 int *fda;
755 char const *sh;
756 struct name *np;
757 FILE *fin = NULL, *fout;
758 NYD_ENTER;
760 /* Look through all recipients and do a quick return if no file or pipe
761 * addressee is found */
762 fda = NULL; /* Silence cc */
763 for (pipecnt = xcnt = 0, np = names; np != NULL; np = np->n_flink)
764 switch (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) {
765 case NAME_ADDRSPEC_ISFILE:
766 ++xcnt;
767 break;
768 case NAME_ADDRSPEC_ISPIPE:
769 ++pipecnt;
770 break;
772 if (pipecnt == 0 && xcnt == 0)
773 goto jleave;
775 /* Otherwise create an array of file descriptors for each found pipe
776 * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
777 * each pipe subprocess needs its very own file descriptor, and we need
778 * to deal with that.
779 * To make our life a bit easier let's just use the auto-reclaimed
780 * string storage */
781 if (pipecnt == 0) {
782 fda = NULL;
783 sh = NULL;
784 } else {
785 fda = salloc(sizeof(int) * pipecnt);
786 for (i = 0; i < pipecnt; ++i)
787 fda[i] = -1;
788 if ((sh = ok_vlook(SHELL)) == NULL)
789 sh = XSHELL;
792 for (np = names; np != NULL;) {
793 if (!(np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE)) {
794 np = np->n_flink;
795 continue;
798 /* See if we have copied the complete message out yet. If not, do so */
799 if (image < 0) {
800 int c;
801 char *tempEdit;
803 if ((fout = Ftmp(&tempEdit, "outof",
804 OF_WRONLY | OF_HOLDSIGS | OF_REGISTER, 0600)) == NULL) {
805 perror(tr(146, "Creation of temporary image"));
806 *senderror = TRU1;
807 goto jcant;
809 if ((image = open(tempEdit, O_RDWR | O_CLOEXEC)) >= 0) {
810 _SET_CLOEXEC(image);
811 for (i = 0; i < pipecnt; ++i) {
812 int fd = open(tempEdit, O_RDONLY | O_CLOEXEC);
813 if (fd < 0) {
814 close(image);
815 image = -1;
816 pipecnt = i;
817 break;
819 fda[i] = fd;
820 _SET_CLOEXEC(fd);
823 Ftmp_release(&tempEdit);
825 if (image < 0) {
826 perror(tr(147, "Creating descriptor duplicate of temporary image"));
827 *senderror = TRU1;
828 Fclose(fout);
829 goto jcant;
832 fprintf(fout, "From %s %s", myname, time_current.tc_ctime);
833 c = EOF;
834 while (i = c, (c = getc(fo)) != EOF)
835 putc(c, fout);
836 rewind(fo);
837 if ((int)i != '\n')
838 putc('\n', fout);
839 putc('\n', fout);
840 fflush(fout);
841 if (ferror(fout)) {
842 perror(tr(148, "Finalizing write of temporary image"));
843 Fclose(fout);
844 goto jcantfout;
846 Fclose(fout);
848 /* If we have to serve file addressees, open reader */
849 if (xcnt != 0 && (fin = Fdopen(image, "r")) == NULL) {
850 perror(tr(149,
851 "Failed to open a duplicate of the temporary image"));
852 jcantfout:
853 *senderror = TRU1;
854 close(image);
855 image = -1;
856 goto jcant;
859 /* From now on use xcnt as a counter for pipecnt */
860 xcnt = 0;
863 /* Now either copy "image" to the desired file or give it as the standard
864 * input to the desired program as appropriate */
865 if (np->n_flags & NAME_ADDRSPEC_ISPIPE) {
866 int pid;
867 sigset_t nset;
869 sigemptyset(&nset);
870 sigaddset(&nset, SIGHUP);
871 sigaddset(&nset, SIGINT);
872 sigaddset(&nset, SIGQUIT);
873 pid = start_command(sh, &nset, fda[xcnt++], -1, "-c",
874 np->n_name + 1, NULL);
875 if (pid < 0) {
876 fprintf(stderr, tr(281, "Message piping to <%s> failed\n"),
877 np->n_name);
878 *senderror = TRU1;
879 goto jcant;
881 free_child(pid);
882 } else {
883 char c, *fname = file_expand(np->n_name);
884 if (fname == NULL) {
885 *senderror = TRU1;
886 goto jcant;
889 if ((fout = Zopen(fname, "a", NULL)) == NULL) {
890 fprintf(stderr, tr(282, "Message writing to <%s> failed: %s\n"),
891 fname, strerror(errno));
892 *senderror = TRU1;
893 goto jcant;
895 rewind(fin);
896 while ((c = getc(fin)) != EOF)
897 putc(c, fout);
898 if (ferror(fout)) {
899 fprintf(stderr, tr(282, "Message writing to <%s> failed: %s\n"),
900 fname, tr(283, "write error"));
901 *senderror = TRU1;
903 Fclose(fout);
905 jcant:
906 /* In days of old we removed the entry from the the list; now for sake of
907 * header expansion we leave it in and mark it as deleted */
908 np->n_type |= GDEL;
909 np = np->n_flink;
910 if (image < 0)
911 goto jdelall;
913 jleave:
914 if (fin != NULL)
915 Fclose(fin);
916 for (i = 0; i < pipecnt; ++i)
917 close(fda[i]);
918 if (image >= 0) {
919 close(image);
920 image = -1;
922 NYD_LEAVE;
923 return names;
925 jdelall:
926 while (np != NULL) {
927 if (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
928 np->n_type |= GDEL;
929 np = np->n_flink;
931 goto jleave;
934 FL struct grouphead *
935 findgroup(char *name)
937 struct grouphead *gh;
938 NYD_ENTER;
940 for (gh = groups[hash(name)]; gh != NULL; gh = gh->g_link)
941 if (*gh->g_name == *name && !strcmp(gh->g_name, name))
942 break;
943 NYD_LEAVE;
944 return gh;
947 FL void
948 printgroup(char *name)
950 struct grouphead *gh;
951 struct group *gp;
952 NYD_ENTER;
954 if ((gh = findgroup(name)) == NULL) {
955 fprintf(stderr, tr(202, "\"%s\": no such alias\n"), name);
956 goto jleave;
959 printf("%s\t", gh->g_name);
960 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link)
961 printf(" %s", gp->ge_name);
962 putchar('\n');
963 jleave:
964 NYD_LEAVE;
967 FL void
968 remove_group(char const *name)
970 ui32_t h;
971 struct grouphead *gh, *gp;
972 NYD_ENTER;
974 h = hash(name);
976 for (gp = NULL, gh = groups[h]; gh != NULL; gh = gh->g_link) {
977 if (*gh->g_name == *name && !strcmp(gh->g_name, name)) {
978 _remove_grouplist(gh);
979 free(gh->g_name);
980 if (gp != NULL)
981 gp->g_link = gh->g_link;
982 else
983 groups[h] = NULL;
984 free(gh);
985 break;
987 gp = gh;
989 NYD_LEAVE;
992 #ifdef _OUR_CLOEXEC
993 # undef O_CLOEXEC
994 # undef _OUR_CLOEXEC
995 #endif
996 #undef _SET_CLOEXEC
998 /* vim:set fenc=utf-8:s-it-mode */