NYD: maildir.c
[s-mailx.git] / names.c
blob8bb891eaf48556d2826fb7436c567ad5da62f8ce
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 /* Same name, taking care for *allnet*? */
47 static int same_name(char const *n1, char const *n2);
48 /* Delete the given name from a namelist */
49 static struct name * delname(struct name *np, char const *name);
50 /* Put another node onto a list of names and return the list */
51 static struct name * put(struct name *list, struct name *node);
52 /* Grab a single name (liberal name) */
53 static char const * yankname(char const *ap, char *wbuf,
54 char const *separators, int keepcomms);
55 /* Extraction multiplexer that splits an input line to names */
56 static struct name * extract1(char const *line, enum gfield ntype,
57 char const *separators, int keepcomms);
58 /* Recursively expand a group name. We limit the expansion to some fixed level
59 * to keep things from going haywire. Direct recursion is not expanded for
60 * convenience */
61 static struct name * gexpand(struct name *nlist, struct grouphead *gh,
62 int metoo, int ntype);
64 static void _remove_grouplist(struct grouphead *gh);
66 static int
67 same_name(char const *n1, char const *n2)
69 int ret = 0;
70 char c1, c2;
72 if (ok_blook(allnet)) {
73 do {
74 c1 = *n1++;
75 c2 = *n2++;
76 c1 = lowerconv(c1);
77 c2 = lowerconv(c2);
78 if (c1 != c2)
79 goto jleave;
80 } while (c1 != '\0' && c2 != '\0' && c1 != '@' && c2 != '@');
81 ret = 1;
82 } else
83 ret = (asccasecmp(n1, n2) == 0);
84 jleave:
85 return (ret);
88 static struct name *
89 delname(struct name *np, char const *name)
91 struct name *p;
93 for (p = np; p != NULL; p = p->n_flink)
94 if (same_name(p->n_name, name)) {
95 if (p->n_blink == NULL) {
96 if (p->n_flink != NULL)
97 p->n_flink->n_blink = NULL;
98 np = p->n_flink;
99 continue;
101 if (p->n_flink == NULL) {
102 if (p->n_blink != NULL)
103 p->n_blink->n_flink = NULL;
104 continue;
106 p->n_blink->n_flink = p->n_flink;
107 p->n_flink->n_blink = p->n_blink;
109 return (np);
112 static struct name *
113 put(struct name *list, struct name *node)
115 node->n_flink = list;
116 node->n_blink = NULL;
117 if (list != NULL)
118 list->n_blink = node;
119 return (node);
122 static char const *
123 yankname(char const *ap, char *wbuf, char const *separators, int keepcomms)
125 char const *cp;
126 char *wp, c, inquote, lc, lastsp;
128 *(wp = wbuf) = '\0';
130 /* Skip over intermediate list trash, as in ".org> , <xy@zz.org>" */
131 for (c = *ap; blankchar(c) || c == ','; c = *++ap)
133 if (c == '\0') {
134 cp = NULL;
135 goto jleave;
139 * Parse a full name: TODO RFC 5322
140 * - Keep everything in quotes, liberal handle *quoted-pair*s therein
141 * - Skip entire (nested) comments
142 * - In non-quote, non-comment, join adjacent space to a single SP
143 * - Understand separators only in non-quote, non-comment context,
144 * and only if not part of a *quoted-pair* (XXX too liberal)
146 cp = ap;
147 for (inquote = lc = lastsp = 0;; lc = c, ++cp) {
148 c = *cp;
149 if (c == '\0')
150 break;
151 if (c == '\\') {
152 lastsp = 0;
153 continue;
155 if (c == '"') {
156 if (lc != '\\')
157 inquote = ! inquote;
158 #if 0 /* TODO when doing real RFC 5322 parsers - why have i done this? */
159 else
160 --wp;
161 #endif
162 goto jwpwc;
164 if (inquote || lc == '\\') {
165 jwpwc: *wp++ = c;
166 lastsp = 0;
167 continue;
169 if (c == '(') {
170 ap = cp;
171 cp = skip_comment(cp + 1);
172 if (keepcomms)
173 while (ap < cp)
174 *wp++ = *ap++;
175 --cp;
176 lastsp = 0;
177 continue;
179 if (strchr(separators, c) != NULL)
180 break;
182 lc = lastsp;
183 lastsp = blankchar(c);
184 if (! lastsp || ! lc)
185 *wp++ = c;
187 if (blankchar(lc))
188 --wp;
190 *wp = '\0';
191 jleave:
192 return (cp);
195 static struct name *
196 extract1(char const *line, enum gfield ntype, char const *separators,
197 int keepcomms)
199 struct name *topp, *np, *t;
200 char const *cp;
201 char *nbuf;
203 topp = NULL;
204 if (line == NULL || *line == '\0')
205 goto jleave;
207 np = NULL;
208 cp = line;
209 nbuf = ac_alloc(strlen(line) + 1);
210 while ((cp = yankname(cp, nbuf, separators, keepcomms)) != NULL) {
211 t = nalloc(nbuf, ntype);
212 if (topp == NULL)
213 topp = t;
214 else
215 np->n_flink = t;
216 t->n_blink = np;
217 np = t;
219 ac_free(nbuf);
221 jleave:
222 return topp;
225 static struct name *
226 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
228 struct group *gp;
229 struct grouphead *ngh;
230 struct name *np;
231 static int depth;
232 char *cp;
234 if (depth > MAXEXP) {
235 printf(tr(150, "Expanding alias to depth larger than %d\n"),
236 MAXEXP);
237 goto jleave;
239 depth++;
241 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
242 cp = gp->ge_name;
243 if (*cp == '\\')
244 goto quote;
245 if (strcmp(cp, gh->g_name) == 0)
246 goto quote;
247 if ((ngh = findgroup(cp)) != NULL) {
248 /* For S-nail(1), the "group" may *be* the sender in
249 * that a name maps to a full address specification */
250 if (! metoo && ngh->g_list->ge_link == NULL &&
251 same_name(cp, myname))
252 continue;
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);
274 static void
275 _remove_grouplist(struct grouphead *gh)
277 struct group *gp, *gq;
279 if ((gp = gh->g_list) != NULL) {
280 for (; gp; gp = gq) {
281 gq = gp->ge_link;
282 free(gp->ge_name);
283 free(gp);
289 * Allocate a single element of a name list, initialize its name field to the
290 * passed name and return it.
292 FL struct name *
293 nalloc(char *str, enum gfield ntype)
295 struct addrguts ag;
296 struct str in, out;
297 struct name *np;
299 np = (struct name*)salloc(sizeof *np);
300 np->n_flink = NULL;
301 np->n_blink = NULL;
302 np->n_type = ntype;
303 np->n_flags = 0;
305 (void)addrspec_with_guts((ntype & (GFULL|GSKIN|GREF)) != 0, str, &ag);
306 if ((ag.ag_n_flags & NAME_NAME_SALLOC) == 0) {
307 ag.ag_n_flags |= NAME_NAME_SALLOC;
308 ag.ag_skinned = savestrbuf(ag.ag_skinned, ag.ag_slen);
310 np->n_fullname = np->n_name = ag.ag_skinned;
311 np->n_flags = ag.ag_n_flags;
313 if (ntype & GFULL) {
314 if (ag.ag_ilen == ag.ag_slen
315 #ifdef HAVE_IDNA
316 && (ag.ag_n_flags & NAME_IDNA) == 0
317 #endif
319 goto jleave;
320 if (ag.ag_n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
321 goto jleave;
322 #ifdef HAVE_IDNA
323 if ((ag.ag_n_flags & NAME_IDNA) == 0) {
324 #endif
325 in.s = str;
326 in.l = ag.ag_ilen;
327 #ifdef HAVE_IDNA
328 } else {
330 * The domain name was IDNA and has been converted.
331 * We also have to ensure that the domain name in
332 * .n_fullname is replaced with the converted version,
333 * since MIME doesn't perform encoding of addresses.
335 size_t l = ag.ag_iaddr_start,
336 lsuff = ag.ag_ilen - ag.ag_iaddr_aend;
337 in.s = ac_alloc(l + ag.ag_slen + lsuff + 1);
338 memcpy(in.s, str, l);
339 memcpy(in.s + l, ag.ag_skinned, ag.ag_slen);
340 l += ag.ag_slen;
341 memcpy(in.s + l, str + ag.ag_iaddr_aend, lsuff);
342 l += lsuff;
343 in.s[l] = '\0';
344 in.l = l;
346 #endif
347 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
348 np->n_fullname = savestr(out.s);
349 free(out.s);
350 #ifdef HAVE_IDNA
351 if (ag.ag_n_flags & NAME_IDNA)
352 ac_free(in.s);
353 #endif
354 np->n_flags |= NAME_FULLNAME_SALLOC;
355 } else if (ntype & GREF) { /* TODO LEGACY */
356 /* TODO Unfortunately we had to skin GREFerences i.e. the
357 * TODO surrounding angle brackets have been stripped away.
358 * TODO Necessarily since otherwise the plain address check
359 * TODO fails due to them; insert them back so that valid
360 * TODO headers will be created */
361 np->n_fullname = np->n_name = str = salloc(ag.ag_slen + 2 + 1);
362 *(str++) = '<';
363 memcpy(str, ag.ag_skinned, ag.ag_slen);
364 str += ag.ag_slen;
365 *(str++) = '>';
366 *str = '\0';
368 jleave:
369 return (np);
372 FL struct name *
373 ndup(struct name *np, enum gfield ntype)
375 struct name *nnp;
377 if ((ntype & (GFULL|GSKIN)) && (np->n_flags & NAME_SKINNED) == 0) {
378 nnp = nalloc(np->n_name, ntype);
379 goto jleave;
382 nnp = (struct name*)salloc(sizeof *np);
383 nnp->n_flink = nnp->n_blink = NULL;
384 nnp->n_type = ntype;
385 nnp->n_flags = (np->n_flags &
386 ~(NAME_NAME_SALLOC | NAME_FULLNAME_SALLOC)) |
387 NAME_NAME_SALLOC;
388 nnp->n_name = savestr(np->n_name);
389 if (np->n_name == np->n_fullname || (ntype & (GFULL|GSKIN)) == 0)
390 nnp->n_fullname = nnp->n_name;
391 else {
392 nnp->n_flags |= NAME_FULLNAME_SALLOC;
393 nnp->n_fullname = savestr(np->n_fullname);
395 jleave:
396 return (nnp);
400 * Concatenate the two passed name lists, return the result.
402 FL struct name *
403 cat(struct name *n1, struct name *n2)
405 struct name *tail;
407 if (n1 == NULL)
408 return (n2);
409 if (n2 == NULL)
410 return (n1);
412 tail = n1;
413 while (tail->n_flink != NULL)
414 tail = tail->n_flink;
415 tail->n_flink = n2;
416 n2->n_blink = tail;
417 return (n1);
421 * Determine the number of undeleted elements in
422 * a name list and return it.
424 FL int
425 count(struct name const*np)
427 int c;
429 for (c = 0; np != NULL; np = np->n_flink)
430 if ((np->n_type & GDEL) == 0)
431 c++;
432 return (c);
436 * Extract a list of names from a line,
437 * and make a list of names from it.
438 * Return the list or NULL if none found.
440 FL struct name *
441 extract(char const *line, enum gfield ntype)
443 return extract1(line, ntype, " \t,", 0);
446 FL struct name *
447 lextract(char const *line, enum gfield ntype)
449 return ((line && strpbrk(line, ",\"\\(<|")) ?
450 extract1(line, ntype, ",", 1) : extract(line, ntype));
454 * Turn a list of names into a string of the same names.
456 FL char *
457 detract(struct name *np, enum gfield ntype)
459 char *topp, *cp;
460 struct name *p;
461 int comma, s;
463 topp = NULL;
464 if (np == NULL)
465 goto jleave;
467 comma = ntype & GCOMMA;
468 ntype &= ~GCOMMA;
469 s = 0;
470 if ((options & OPT_DEBUG) && comma)
471 fprintf(stderr, tr(145, "detract asked to insert commas\n"));
472 for (p = np; p != NULL; p = p->n_flink) {
473 if (ntype && (p->n_type & GMASK) != ntype)
474 continue;
475 s += strlen(p->n_fullname) + 1;
476 if (comma)
477 s++;
479 if (s == 0)
480 goto jleave;
482 s += 2;
483 topp = salloc(s);
484 cp = topp;
485 for (p = np; p != NULL; p = p->n_flink) {
486 if (ntype && (p->n_type & GMASK) != ntype)
487 continue;
488 cp = sstpcpy(cp, p->n_fullname);
489 if (comma && p->n_flink != NULL)
490 *cp++ = ',';
491 *cp++ = ' ';
493 *--cp = 0;
494 if (comma && *--cp == ',')
495 *cp = 0;
496 jleave:
497 return topp;
500 FL struct name *
501 grab_names(const char *field, struct name *np, int comma, enum gfield gflags)
503 struct name *nq;
504 jloop:
505 np = lextract(readstr_input(field, detract(np, comma)), gflags);
506 for (nq = np; nq != NULL; nq = nq->n_flink)
507 if (is_addr_invalid(nq, 1))
508 goto jloop;
509 return np;
513 * Check all addresses in np and delete invalid ones.
515 FL struct name *
516 checkaddrs(struct name *np)
518 struct name *n;
520 for (n = np; n != NULL;) {
521 if (is_addr_invalid(n, 1)) {
522 if (n->n_blink)
523 n->n_blink->n_flink = n->n_flink;
524 if (n->n_flink)
525 n->n_flink->n_blink = n->n_blink;
526 if (n == np)
527 np = n->n_flink;
529 n = n->n_flink;
531 return (np);
535 * Map all of the aliased users in the invoker's mailrc
536 * file and insert them into the list.
537 * Changed after all these months of service to recursively
538 * expand names (2/14/80).
540 FL struct name *
541 usermap(struct name *names, bool_t force_metoo)
543 struct name *new, *np, *cp;
544 struct grouphead *gh;
545 int metoo;
547 new = NULL;
548 np = names;
549 metoo = (force_metoo || ok_blook(metoo));
550 while (np != NULL) {
551 assert((np->n_type & GDEL) == 0); /* TODO legacy */
552 if (is_fileorpipe_addr(np) || np->n_name[0] == '\\') {
553 cp = np->n_flink;
554 new = put(new, np);
555 np = cp;
556 continue;
558 gh = findgroup(np->n_name);
559 cp = np->n_flink;
560 if (gh != NULL)
561 new = gexpand(new, gh, metoo, np->n_type);
562 else
563 new = put(new, np);
564 np = cp;
566 return new;
570 * Remove all of the duplicates from the passed name list by
571 * insertion sorting them, then checking for dups.
572 * Return the head of the new list.
574 FL struct name *
575 elide(struct name *names)
577 struct name *np, *t, *newn, *x;
579 if (names == NULL)
580 return (NULL);
581 /* Throw away all deleted nodes (XXX merge with plain sort below?) */
582 for (newn = np = NULL; names != NULL; names = names->n_flink)
583 if ((names->n_type & GDEL) == 0) {
584 names->n_blink = np;
585 if (np)
586 np->n_flink = names;
587 else
588 newn = names;
589 np = names;
591 if (newn == NULL)
592 return (NULL);
594 np = newn->n_flink;
595 if (np != NULL)
596 np->n_blink = NULL;
597 newn->n_flink = NULL;
599 while (np != NULL) {
600 t = newn;
601 while (asccasecmp(t->n_name, np->n_name) < 0) {
602 if (t->n_flink == NULL)
603 break;
604 t = t->n_flink;
608 * If we ran out of t's, put the new entry after
609 * the current value of t.
612 if (asccasecmp(t->n_name, np->n_name) < 0) {
613 t->n_flink = np;
614 np->n_blink = t;
615 t = np;
616 np = np->n_flink;
617 t->n_flink = NULL;
618 continue;
622 * Otherwise, put the new entry in front of the
623 * current t. If at the front of the list,
624 * the new guy becomes the new head of the list.
627 if (t == newn) {
628 t = np;
629 np = np->n_flink;
630 t->n_flink = newn;
631 newn->n_blink = t;
632 t->n_blink = NULL;
633 newn = t;
634 continue;
638 * The normal case -- we are inserting into the
639 * middle of the list.
642 x = np;
643 np = np->n_flink;
644 x->n_flink = t;
645 x->n_blink = t->n_blink;
646 t->n_blink->n_flink = x;
647 t->n_blink = x;
651 * Now the list headed up by new is sorted.
652 * Go through it and remove duplicates.
655 np = newn;
656 while (np != NULL) {
657 t = np;
658 while (t->n_flink != NULL &&
659 asccasecmp(np->n_name, t->n_flink->n_name) == 0)
660 t = t->n_flink;
661 if (t == np) {
662 np = np->n_flink;
663 continue;
667 * Now t points to the last entry with the same name
668 * as np. Make np point beyond t.
671 np->n_flink = t->n_flink;
672 if (t->n_flink != NULL)
673 t->n_flink->n_blink = np;
674 np = np->n_flink;
676 return (newn);
679 FL struct name *
680 delete_alternates(struct name *np)
682 struct name *xp;
683 char **ap;
685 np = delname(np, myname);
686 if (altnames)
687 for (ap = altnames; *ap; ap++)
688 np = delname(np, *ap);
689 if ((xp = lextract(ok_vlook(from), GEXTRA | GSKIN)) != NULL)
690 while (xp) {
691 np = delname(np, xp->n_name);
692 xp = xp->n_flink;
694 if ((xp = lextract(ok_vlook(replyto), GEXTRA | GSKIN)) != NULL)
695 while (xp) {
696 np = delname(np, xp->n_name);
697 xp = xp->n_flink;
699 if ((xp = extract(ok_vlook(sender), GEXTRA | GSKIN)) != NULL)
700 while (xp) {
701 np = delname(np, xp->n_name);
702 xp = xp->n_flink;
704 return (np);
707 FL int
708 is_myname(char const *name)
710 int ret = 1;
711 struct name *xp;
712 char **ap;
714 if (same_name(myname, name))
715 goto jleave;
716 if (altnames)
717 for (ap = altnames; *ap; ap++)
718 if (same_name(*ap, name))
719 goto jleave;
720 if ((xp = lextract(ok_vlook(from), GEXTRA | GSKIN)) != NULL)
721 while (xp) {
722 if (same_name(xp->n_name, name))
723 goto jleave;
724 xp = xp->n_flink;
726 if ((xp = lextract(ok_vlook(replyto), GEXTRA | GSKIN)) != NULL)
727 while (xp) {
728 if (same_name(xp->n_name, name))
729 goto jleave;
730 xp = xp->n_flink;
732 if ((xp = extract(ok_vlook(sender), GEXTRA | GSKIN)) != NULL)
733 while (xp) {
734 if (same_name(xp->n_name, name))
735 goto jleave;
736 xp = xp->n_flink;
738 ret = 0;
739 jleave:
740 return (ret);
744 * For each recipient in the passed name list with a /
745 * in the name, append the message to the end of the named file
746 * and remove him from the recipient list.
748 * Recipients whose name begins with | are piped through the given
749 * program and removed.
751 FL struct name *
752 outof(struct name *names, FILE *fo, struct header *hp, bool_t *senderror)
754 ui32_t pipecnt, xcnt, i;
755 int *fda;
756 char const *sh;
757 struct name *np;
758 FILE *fin = NULL, *fout;
759 UNUSED(hp);
762 * Look through all recipients and do a quick return if no file or pipe
763 * addressee is found.
765 fda = NULL; /* Silence cc */
766 for (pipecnt = xcnt = 0, np = names; np != NULL; np = np->n_flink)
767 switch (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) {
768 case NAME_ADDRSPEC_ISFILE:
769 ++xcnt;
770 break;
771 case NAME_ADDRSPEC_ISPIPE:
772 ++pipecnt;
773 break;
775 if (pipecnt == 0 && xcnt == 0)
776 goto jleave;
779 * Otherwise create an array of file descriptors for each found pipe
780 * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
781 * each pipe subprocess needs its very own file descriptor, and we need
782 * to deal with that.
783 * To make our life a bit easier let's just use the auto-reclaimed
784 * string storage.
786 if (pipecnt == 0) {
787 fda = NULL;
788 sh = NULL;
789 } else {
790 fda = (int*)salloc(sizeof(int) * pipecnt);
791 for (i = 0; i < pipecnt; ++i)
792 fda[i] = -1;
793 if ((sh = ok_vlook(SHELL)) == NULL)
794 sh = XSHELL;
797 for (np = names; np != NULL;) {
798 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
799 == 0) {
800 np = np->n_flink;
801 continue;
805 * See if we have copied the complete message out yet.
806 * If not, do so.
808 if (image < 0) {
809 int c;
810 char *tempEdit;
812 /* XXX tempEdit unlink racy - block signals, at least */
813 if ((fout = Ftemp(&tempEdit, "Re", "w", 0600, 1))
814 == NULL) {
815 perror(tr(146, "Creation of temporary image"));
816 *senderror = TRU1;
817 goto jcant;
819 image = open(tempEdit, O_RDWR);
820 if (image >= 0)
821 for (i = 0; i < pipecnt; ++i) {
822 int fd = open(tempEdit, O_RDONLY);
823 if (fd < 0) {
824 (void)close(image);
825 image = -1;
826 pipecnt = i;
827 break;
829 fda[i] = fd;
830 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
832 unlink(tempEdit);
833 Ftfree(&tempEdit);
834 if (image < 0) {
835 perror(tr(147, "Creating descriptor duplicate "
836 "of temporary image"));
837 *senderror = TRU1;
838 Fclose(fout);
839 goto jcant;
841 (void)fcntl(image, F_SETFD, FD_CLOEXEC);
843 fprintf(fout, "From %s %s",
844 myname, time_current.tc_ctime);
845 c = EOF;
846 while (i = c, (c = getc(fo)) != EOF)
847 putc(c, fout);
848 rewind(fo);
849 if ((int)i != '\n')
850 putc('\n', fout);
851 putc('\n', fout);
852 fflush(fout);
853 if (ferror(fout)) {
854 perror(tr(148, "Finalizing write of temporary "
855 "image"));
856 Fclose(fout);
857 goto jcantfout;
859 Fclose(fout);
861 /* If we have to serve file addressees, open reader */
862 if (xcnt != 0 && (fin = Fdopen(image, "r")) == NULL) {
863 perror(tr(149, "Failed to open a duplicate of "
864 "the temporary image"));
865 jcantfout: *senderror = TRU1;
866 (void)close(image);
867 image = -1;
868 goto jcant;
871 /* From now on use xcnt as a counter for pipecnt */
872 xcnt = 0;
876 * Now either copy "image" to the desired file
877 * or give it as the standard input to the desired
878 * program as appropriate.
881 if (np->n_flags & NAME_ADDRSPEC_ISPIPE) {
882 int pid;
883 sigset_t nset;
885 sigemptyset(&nset);
886 sigaddset(&nset, SIGHUP);
887 sigaddset(&nset, SIGINT);
888 sigaddset(&nset, SIGQUIT);
889 pid = start_command(sh, &nset,
890 fda[xcnt++], -1, "-c", np->n_name + 1, NULL);
891 if (pid < 0) {
892 fprintf(stderr, tr(281,
893 "Message piping to <%s> failed\n"),
894 np->n_name);
895 *senderror = TRU1;
896 goto jcant;
898 free_child(pid);
899 } else {
900 char c, *fname = file_expand(np->n_name);
901 if (fname == NULL) {
902 *senderror = TRU1;
903 goto jcant;
905 if ((fout = Zopen(fname, "a", NULL)) == NULL) {
906 fprintf(stderr, tr(282,
907 "Message writing to <%s> failed: %s\n"),
908 fname, strerror(errno));
909 *senderror = TRU1;
910 goto jcant;
912 rewind(fin);
913 while ((c = getc(fin)) != EOF)
914 putc(c, fout);
915 if (ferror(fout)) {
916 fprintf(stderr, tr(282,
917 "Message writing to <%s> failed: %s\n"),
918 fname, tr(283, "write error"));
919 *senderror = TRU1;
921 Fclose(fout);
923 jcant:
925 * In days of old we removed the entry from the
926 * the list; now for sake of header expansion
927 * we leave it in and mark it as deleted.
929 np->n_type |= GDEL;
930 np = np->n_flink;
931 if (image < 0)
932 goto jdelall;
934 jleave:
935 if (fin != NULL)
936 Fclose(fin);
937 for (i = 0; i < pipecnt; ++i)
938 (void)close(fda[i]);
939 if (image >= 0) {
940 close(image);
941 image = -1;
943 return names;
945 jdelall:
946 while (np != NULL) {
947 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
948 != 0)
949 np->n_type |= GDEL;
950 np = np->n_flink;
952 goto jleave;
955 FL struct grouphead *
956 findgroup(char *name)
958 struct grouphead *gh;
960 for (gh = groups[hash(name)]; gh != NULL; gh = gh->g_link)
961 if (*gh->g_name == *name && strcmp(gh->g_name, name) == 0)
962 return(gh);
963 return(NULL);
966 FL void
967 printgroup(char *name)
969 struct grouphead *gh;
970 struct group *gp;
972 if ((gh = findgroup(name)) == NULL) {
973 fprintf(stderr, tr(202, "\"%s\": no such alias\n"), name);
974 return;
976 printf("%s\t", gh->g_name);
977 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link)
978 printf(" %s", gp->ge_name);
979 putchar('\n');
982 FL void
983 remove_group(const char *name)
985 struct grouphead *gh, *gp = NULL;
986 int h = hash(name);
988 for (gh = groups[h]; gh != NULL; gh = gh->g_link) {
989 if (*gh->g_name == *name && strcmp(gh->g_name, name) == 0) {
990 _remove_grouplist(gh);
991 free(gh->g_name);
992 if (gp != NULL)
993 gp->g_link = gh->g_link;
994 else
995 groups[h] = NULL;
996 free(gh);
997 break;
999 gp = gh;