nail.h: use MAXPATHLEN as fallback for struct cw
[s-mailx.git] / names.c
blobdad50b9d39230981449a7169537c57dc3ccef1be
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 - 2013 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 #include "nail.h"
42 #include <fcntl.h>
44 /* Same name, taking care for *allnet*? */
45 static int same_name(char const *n1, char const *n2);
46 /* Delete the given name from a namelist */
47 static struct name * delname(struct name *np, char const *name);
48 /* Put another node onto a list of names and return the list */
49 static struct name * put(struct name *list, struct name *node);
50 /* Grab a single name (liberal name) */
51 static char const * yankname(char const *ap, char *wbuf,
52 char const *separators, int keepcomms);
53 /* Extraction multiplexer that splits an input line to names */
54 static struct name * extract1(char const *line, enum gfield ntype,
55 char const *separators, int keepcomms);
56 /* Recursively expand a group name. We limit the expansion to some fixed level
57 * to keep things from going haywire. Direct recursion is not expanded for
58 * convenience */
59 static struct name * gexpand(struct name *nlist, struct grouphead *gh,
60 int metoo, int ntype);
62 static void _remove_grouplist(struct grouphead *gh);
64 static int
65 same_name(char const *n1, char const *n2)
67 int ret = 0;
68 char c1, c2;
70 if (value("allnet") != NULL) {
71 do {
72 c1 = *n1++;
73 c2 = *n2++;
74 c1 = lowerconv(c1);
75 c2 = lowerconv(c2);
76 if (c1 != c2)
77 goto jleave;
78 } while (c1 != '\0' && c2 != '\0' && c1 != '@' && c2 != '@');
79 ret = 1;
80 } else
81 ret = (asccasecmp(n1, n2) == 0);
82 jleave:
83 return (ret);
86 static struct name *
87 delname(struct name *np, char const *name)
89 struct name *p;
91 for (p = np; p != NULL; p = p->n_flink)
92 if (same_name(p->n_name, name)) {
93 if (p->n_blink == NULL) {
94 if (p->n_flink != NULL)
95 p->n_flink->n_blink = NULL;
96 np = p->n_flink;
97 continue;
99 if (p->n_flink == NULL) {
100 if (p->n_blink != NULL)
101 p->n_blink->n_flink = NULL;
102 continue;
104 p->n_blink->n_flink = p->n_flink;
105 p->n_flink->n_blink = p->n_blink;
107 return (np);
110 static struct name *
111 put(struct name *list, struct name *node)
113 node->n_flink = list;
114 node->n_blink = NULL;
115 if (list != NULL)
116 list->n_blink = node;
117 return (node);
120 static char const *
121 yankname(char const *ap, char *wbuf, char const *separators, int keepcomms)
123 char const *cp;
124 char *wp, c, inquote, lc, lastsp;
126 *(wp = wbuf) = '\0';
128 /* Skip over intermediate list trash, as in ".org> , <xy@zz.org>" */
129 for (c = *ap; blankchar(c) || c == ','; c = *++ap)
131 if (c == '\0') {
132 cp = NULL;
133 goto jleave;
137 * Parse a full name: TODO RFC 5322
138 * - Keep everything in quotes, liberal handle *quoted-pair*s therein
139 * - Skip entire (nested) comments
140 * - In non-quote, non-comment, join adjacent space to a single SP
141 * - Understand separators only in non-quote, non-comment context,
142 * and only if not part of a *quoted-pair* (XXX too liberal)
144 cp = ap;
145 for (inquote = lc = lastsp = 0;; lc = c, ++cp) {
146 c = *cp;
147 if (c == '\0')
148 break;
149 if (c == '\\') {
150 lastsp = 0;
151 continue;
153 if (c == '"') {
154 if (lc != '\\')
155 inquote = ! inquote;
156 #if 0 /* TODO when doing real RFC 5322 parsers - why have i done this? */
157 else
158 --wp;
159 #endif
160 goto jwpwc;
162 if (inquote || lc == '\\') {
163 jwpwc: *wp++ = c;
164 lastsp = 0;
165 continue;
167 if (c == '(') {
168 ap = cp;
169 cp = skip_comment(cp + 1);
170 if (keepcomms)
171 while (ap < cp)
172 *wp++ = *ap++;
173 --cp;
174 lastsp = 0;
175 continue;
177 if (strchr(separators, c) != NULL)
178 break;
180 lc = lastsp;
181 lastsp = blankchar(c);
182 if (! lastsp || ! lc)
183 *wp++ = c;
185 if (blankchar(lc))
186 --wp;
188 *wp = '\0';
189 jleave:
190 return (cp);
193 static struct name *
194 extract1(char const *line, enum gfield ntype, char const *separators,
195 int keepcomms)
197 struct name *topp, *np, *t;
198 char const *cp;
199 char *nbuf;
201 topp = NULL;
202 if (line == NULL || *line == '\0')
203 goto jleave;
205 np = NULL;
206 cp = line;
207 nbuf = ac_alloc(strlen(line) + 1);
208 while ((cp = yankname(cp, nbuf, separators, keepcomms)) != NULL) {
209 t = nalloc(nbuf, ntype);
210 if (topp == NULL)
211 topp = t;
212 else
213 np->n_flink = t;
214 t->n_blink = np;
215 np = t;
217 ac_free(nbuf);
219 jleave:
220 return topp;
223 static struct name *
224 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
226 struct group *gp;
227 struct grouphead *ngh;
228 struct name *np;
229 static int depth;
230 char *cp;
232 if (depth > MAXEXP) {
233 printf(tr(150, "Expanding alias to depth larger than %d\n"),
234 MAXEXP);
235 goto jleave;
237 depth++;
239 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
240 cp = gp->ge_name;
241 if (*cp == '\\')
242 goto quote;
243 if (strcmp(cp, gh->g_name) == 0)
244 goto quote;
245 if ((ngh = findgroup(cp)) != NULL) {
246 /* For S-nail(1), the "group" may *be* the sender in
247 * that a name maps to a full address specification */
248 if (! metoo && ngh->g_list->ge_link == NULL &&
249 same_name(cp, myname))
250 continue;
251 nlist = gexpand(nlist, ngh, metoo, ntype);
252 continue;
254 quote:
255 np = nalloc(cp, ntype|GFULL);
257 * At this point should allow to expand
258 * to self if only person in group
260 if (gp == gh->g_list && gp->ge_link == NULL)
261 goto skip;
262 if (! metoo && same_name(cp, myname))
263 np->n_type |= GDEL;
264 skip:
265 nlist = put(nlist, np);
267 --depth;
268 jleave:
269 return (nlist);
272 static void
273 _remove_grouplist(struct grouphead *gh)
275 struct group *gp, *gq;
277 if ((gp = gh->g_list) != NULL) {
278 for (; gp; gp = gq) {
279 gq = gp->ge_link;
280 free(gp->ge_name);
281 free(gp);
287 * Allocate a single element of a name list, initialize its name field to the
288 * passed name and return it.
290 struct name *
291 nalloc(char *str, enum gfield ntype)
293 struct addrguts ag;
294 struct str in, out;
295 struct name *np;
297 np = (struct name*)salloc(sizeof *np);
298 np->n_flink = NULL;
299 np->n_blink = NULL;
300 np->n_type = ntype;
301 np->n_flags = 0;
303 (void)addrspec_with_guts((ntype & (GFULL|GSKIN|GREF)) != 0, str, &ag);
304 if ((ag.ag_n_flags & NAME_NAME_SALLOC) == 0) {
305 ag.ag_n_flags |= NAME_NAME_SALLOC;
306 ag.ag_skinned = savestrbuf(ag.ag_skinned, ag.ag_slen);
308 np->n_fullname = np->n_name = ag.ag_skinned;
309 np->n_flags = ag.ag_n_flags;
311 if (ntype & GFULL) {
312 if (ag.ag_ilen == ag.ag_slen
313 #ifdef HAVE_IDNA
314 && (ag.ag_n_flags & NAME_IDNA) == 0
315 #endif
317 goto jleave;
318 if (ag.ag_n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
319 goto jleave;
320 #ifdef HAVE_IDNA
321 if ((ag.ag_n_flags & NAME_IDNA) == 0) {
322 #endif
323 in.s = str;
324 in.l = ag.ag_ilen;
325 #ifdef HAVE_IDNA
326 } else {
328 * The domain name was IDNA and has been converted.
329 * We also have to ensure that the domain name in
330 * .n_fullname is replaced with the converted version,
331 * since MIME doesn't perform encoding of addresses.
333 size_t l = ag.ag_iaddr_start,
334 lsuff = ag.ag_ilen - ag.ag_iaddr_aend;
335 in.s = ac_alloc(l + ag.ag_slen + lsuff + 1);
336 memcpy(in.s, str, l);
337 memcpy(in.s + l, ag.ag_skinned, ag.ag_slen);
338 l += ag.ag_slen;
339 memcpy(in.s + l, str + ag.ag_iaddr_aend, lsuff);
340 l += lsuff;
341 in.s[l] = '\0';
342 in.l = l;
344 #endif
345 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
346 np->n_fullname = savestr(out.s);
347 free(out.s);
348 #ifdef HAVE_IDNA
349 if (ag.ag_n_flags & NAME_IDNA)
350 ac_free(in.s);
351 #endif
352 np->n_flags |= NAME_FULLNAME_SALLOC;
353 } else if (ntype & GREF) { /* TODO LEGACY */
354 /* TODO Unfortunately we had to skin GREFerences i.e. the
355 * TODO surrounding angle brackets have been stripped away.
356 * TODO Necessarily since otherwise the plain address check
357 * TODO fails due to them; insert them back so that valid
358 * TODO headers will be created */
359 np->n_fullname = np->n_name = str = salloc(ag.ag_slen + 2 + 1);
360 *(str++) = '<';
361 memcpy(str, ag.ag_skinned, ag.ag_slen);
362 str += ag.ag_slen;
363 *(str++) = '>';
364 *str = '\0';
366 jleave:
367 return (np);
370 struct name *
371 ndup(struct name *np, enum gfield ntype)
373 struct name *nnp;
375 if ((ntype & (GFULL|GSKIN)) && (np->n_flags & NAME_SKINNED) == 0) {
376 nnp = nalloc(np->n_name, ntype);
377 goto jleave;
380 nnp = (struct name*)salloc(sizeof *np);
381 nnp->n_flink = nnp->n_blink = NULL;
382 nnp->n_type = ntype;
383 nnp->n_flags = (np->n_flags &
384 ~(NAME_NAME_SALLOC | NAME_FULLNAME_SALLOC)) |
385 NAME_NAME_SALLOC;
386 nnp->n_name = savestr(np->n_name);
387 if (np->n_name == np->n_fullname || (ntype & (GFULL|GSKIN)) == 0)
388 nnp->n_fullname = nnp->n_name;
389 else {
390 nnp->n_flags |= NAME_FULLNAME_SALLOC;
391 nnp->n_fullname = savestr(np->n_fullname);
393 jleave:
394 return (nnp);
398 * Concatenate the two passed name lists, return the result.
400 struct name *
401 cat(struct name *n1, struct name *n2)
403 struct name *tail;
405 if (n1 == NULL)
406 return (n2);
407 if (n2 == NULL)
408 return (n1);
410 tail = n1;
411 while (tail->n_flink != NULL)
412 tail = tail->n_flink;
413 tail->n_flink = n2;
414 n2->n_blink = tail;
415 return (n1);
419 * Determine the number of undeleted elements in
420 * a name list and return it.
423 count(struct name const*np)
425 int c;
427 for (c = 0; np != NULL; np = np->n_flink)
428 if ((np->n_type & GDEL) == 0)
429 c++;
430 return (c);
434 * Extract a list of names from a line,
435 * and make a list of names from it.
436 * Return the list or NULL if none found.
438 struct name *
439 extract(char const *line, enum gfield ntype)
441 return extract1(line, ntype, " \t,", 0);
444 struct name *
445 lextract(char const *line, enum gfield ntype)
447 return ((line && strpbrk(line, ",\"\\(<|")) ?
448 extract1(line, ntype, ",", 1) : extract(line, ntype));
452 * Turn a list of names into a string of the same names.
454 char *
455 detract(struct name *np, enum gfield ntype)
457 char *topp, *cp;
458 struct name *p;
459 int comma, s;
461 topp = NULL;
462 if (np == NULL)
463 goto jleave;
465 comma = ntype & GCOMMA;
466 ntype &= ~GCOMMA;
467 s = 0;
468 if ((options & OPT_DEBUG) && comma)
469 fprintf(stderr, tr(145, "detract asked to insert commas\n"));
470 for (p = np; p != NULL; p = p->n_flink) {
471 if (ntype && (p->n_type & GMASK) != ntype)
472 continue;
473 s += strlen(p->n_fullname) + 1;
474 if (comma)
475 s++;
477 if (s == 0)
478 goto jleave;
480 s += 2;
481 topp = salloc(s);
482 cp = topp;
483 for (p = np; p != NULL; p = p->n_flink) {
484 if (ntype && (p->n_type & GMASK) != ntype)
485 continue;
486 cp = sstpcpy(cp, p->n_fullname);
487 if (comma && p->n_flink != NULL)
488 *cp++ = ',';
489 *cp++ = ' ';
491 *--cp = 0;
492 if (comma && *--cp == ',')
493 *cp = 0;
494 jleave:
495 return topp;
498 struct name *
499 grab_names(const char *field, struct name *np, int comma, enum gfield gflags)
501 struct name *nq;
502 jloop:
503 np = lextract(readstr_input(field, detract(np, comma)), gflags);
504 for (nq = np; nq != NULL; nq = nq->n_flink)
505 if (is_addr_invalid(nq, 1))
506 goto jloop;
507 return np;
511 * Check all addresses in np and delete invalid ones.
513 struct name *
514 checkaddrs(struct name *np)
516 struct name *n;
518 for (n = np; n != NULL;) {
519 if (is_addr_invalid(n, 1)) {
520 if (n->n_blink)
521 n->n_blink->n_flink = n->n_flink;
522 if (n->n_flink)
523 n->n_flink->n_blink = n->n_blink;
524 if (n == np)
525 np = n->n_flink;
527 n = n->n_flink;
529 return (np);
533 * Map all of the aliased users in the invoker's mailrc
534 * file and insert them into the list.
535 * Changed after all these months of service to recursively
536 * expand names (2/14/80).
538 struct name *
539 usermap(struct name *names, bool_t force_metoo)
541 struct name *new, *np, *cp;
542 struct grouphead *gh;
543 int metoo;
545 new = NULL;
546 np = names;
547 metoo = (force_metoo || value("metoo") != NULL);
548 while (np != NULL) {
549 assert((np->n_type & GDEL) == 0); /* TODO legacy */
550 if (is_fileorpipe_addr(np) || np->n_name[0] == '\\') {
551 cp = np->n_flink;
552 new = put(new, np);
553 np = cp;
554 continue;
556 gh = findgroup(np->n_name);
557 cp = np->n_flink;
558 if (gh != NULL)
559 new = gexpand(new, gh, metoo, np->n_type);
560 else
561 new = put(new, np);
562 np = cp;
564 return new;
568 * Remove all of the duplicates from the passed name list by
569 * insertion sorting them, then checking for dups.
570 * Return the head of the new list.
572 struct name *
573 elide(struct name *names)
575 struct name *np, *t, *newn, *x;
577 if (names == NULL)
578 return (NULL);
579 /* Throw away all deleted nodes (XXX merge with plain sort below?) */
580 for (newn = np = NULL; names != NULL; names = names->n_flink)
581 if ((names->n_type & GDEL) == 0) {
582 names->n_blink = np;
583 if (np)
584 np->n_flink = names;
585 else
586 newn = names;
587 np = names;
589 if (newn == NULL)
590 return (NULL);
592 np = newn->n_flink;
593 if (np != NULL)
594 np->n_blink = NULL;
595 newn->n_flink = NULL;
597 while (np != NULL) {
598 t = newn;
599 while (asccasecmp(t->n_name, np->n_name) < 0) {
600 if (t->n_flink == NULL)
601 break;
602 t = t->n_flink;
606 * If we ran out of t's, put the new entry after
607 * the current value of t.
610 if (asccasecmp(t->n_name, np->n_name) < 0) {
611 t->n_flink = np;
612 np->n_blink = t;
613 t = np;
614 np = np->n_flink;
615 t->n_flink = NULL;
616 continue;
620 * Otherwise, put the new entry in front of the
621 * current t. If at the front of the list,
622 * the new guy becomes the new head of the list.
625 if (t == newn) {
626 t = np;
627 np = np->n_flink;
628 t->n_flink = newn;
629 newn->n_blink = t;
630 t->n_blink = NULL;
631 newn = t;
632 continue;
636 * The normal case -- we are inserting into the
637 * middle of the list.
640 x = np;
641 np = np->n_flink;
642 x->n_flink = t;
643 x->n_blink = t->n_blink;
644 t->n_blink->n_flink = x;
645 t->n_blink = x;
649 * Now the list headed up by new is sorted.
650 * Go through it and remove duplicates.
653 np = newn;
654 while (np != NULL) {
655 t = np;
656 while (t->n_flink != NULL &&
657 asccasecmp(np->n_name, t->n_flink->n_name) == 0)
658 t = t->n_flink;
659 if (t == np) {
660 np = np->n_flink;
661 continue;
665 * Now t points to the last entry with the same name
666 * as np. Make np point beyond t.
669 np->n_flink = t->n_flink;
670 if (t->n_flink != NULL)
671 t->n_flink->n_blink = np;
672 np = np->n_flink;
674 return (newn);
677 struct name *
678 delete_alternates(struct name *np)
680 struct name *xp;
681 char **ap;
683 np = delname(np, myname);
684 if (altnames)
685 for (ap = altnames; *ap; ap++)
686 np = delname(np, *ap);
687 if ((xp = lextract(value("from"), GEXTRA|GSKIN)) != NULL)
688 while (xp) {
689 np = delname(np, xp->n_name);
690 xp = xp->n_flink;
692 if ((xp = lextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
693 while (xp) {
694 np = delname(np, xp->n_name);
695 xp = xp->n_flink;
697 if ((xp = extract(value("sender"), GEXTRA|GSKIN)) != NULL)
698 while (xp) {
699 np = delname(np, xp->n_name);
700 xp = xp->n_flink;
702 return (np);
706 is_myname(char const *name)
708 int ret = 1;
709 struct name *xp;
710 char **ap;
712 if (same_name(myname, name))
713 goto jleave;
714 if (altnames)
715 for (ap = altnames; *ap; ap++)
716 if (same_name(*ap, name))
717 goto jleave;
718 if ((xp = lextract(value("from"), GEXTRA|GSKIN)) != NULL)
719 while (xp) {
720 if (same_name(xp->n_name, name))
721 goto jleave;
722 xp = xp->n_flink;
724 if ((xp = lextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
725 while (xp) {
726 if (same_name(xp->n_name, name))
727 goto jleave;
728 xp = xp->n_flink;
730 if ((xp = extract(value("sender"), GEXTRA|GSKIN)) != NULL)
731 while (xp) {
732 if (same_name(xp->n_name, name))
733 goto jleave;
734 xp = xp->n_flink;
736 ret = 0;
737 jleave:
738 return (ret);
742 * For each recipient in the passed name list with a /
743 * in the name, append the message to the end of the named file
744 * and remove him from the recipient list.
746 * Recipients whose name begins with | are piped through the given
747 * program and removed.
749 struct name *
750 outof(struct name *names, FILE *fo, struct header *hp, bool_t *senderror)
752 int pipecnt, xcnt, *fda, i;
753 char const *sh;
754 struct name *np;
755 FILE *fin = NULL, *fout;
756 (void)hp;
759 * Look through all recipients and do a quick return if no file or pipe
760 * 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;
776 * Otherwise create an array of file descriptors for each found pipe
777 * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
778 * each pipe subprocess needs its very own file descriptor, and we need
779 * to deal with that.
780 * To make our life a bit easier let's just use the auto-reclaimed
781 * string storage.
783 if (pipecnt == 0) {
784 fda = NULL;
785 sh = NULL;
786 } else {
787 fda = (int*)salloc(sizeof(int) * pipecnt);
788 for (i = 0; i < pipecnt; ++i)
789 fda[i] = -1;
790 if ((sh = value("SHELL")) == NULL)
791 sh = SHELL;
794 for (np = names; np != NULL;) {
795 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
796 == 0) {
797 np = np->n_flink;
798 continue;
802 * See if we have copied the complete message out yet.
803 * If not, do so.
805 if (image < 0) {
806 int c;
807 char *tempEdit;
809 /* XXX tempEdit unlink racy - block signals, at least */
810 if ((fout = Ftemp(&tempEdit, "Re", "w", 0600, 1))
811 == NULL) {
812 perror(tr(146, "Creation of temporary image"));
813 *senderror = TRU1;
814 goto jcant;
816 image = open(tempEdit, O_RDWR);
817 if (image >= 0)
818 for (i = 0; i < pipecnt; ++i) {
819 int fd = open(tempEdit, O_RDONLY);
820 if (fd < 0) {
821 (void)close(image);
822 image = -1;
823 pipecnt = i;
824 break;
826 fda[i] = fd;
827 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
829 unlink(tempEdit);
830 Ftfree(&tempEdit);
831 if (image < 0) {
832 perror(tr(147, "Creating descriptor duplicate "
833 "of temporary image"));
834 *senderror = TRU1;
835 Fclose(fout);
836 goto jcant;
838 (void)fcntl(image, F_SETFD, FD_CLOEXEC);
840 fprintf(fout, "From %s %s",
841 myname, time_current.tc_ctime);
842 c = EOF;
843 while (i = c, (c = getc(fo)) != EOF)
844 putc(c, fout);
845 rewind(fo);
846 if (i != '\n')
847 putc('\n', fout);
848 putc('\n', fout);
849 fflush(fout);
850 if (ferror(fout)) {
851 perror(tr(148, "Finalizing write of temporary "
852 "image"));
853 Fclose(fout);
854 goto jcantfout;
856 Fclose(fout);
858 /* If we have to serve file addressees, open reader */
859 if (xcnt != 0 && (fin = Fdopen(image, "r")) == NULL) {
860 perror(tr(149, "Failed to open a duplicate of "
861 "the temporary image"));
862 jcantfout: *senderror = TRU1;
863 (void)close(image);
864 image = -1;
865 goto jcant;
868 /* From now on use xcnt as a counter for pipecnt */
869 xcnt = 0;
873 * Now either copy "image" to the desired file
874 * or give it as the standard input to the desired
875 * program as appropriate.
878 if (np->n_flags & NAME_ADDRSPEC_ISPIPE) {
879 int pid;
880 sigset_t nset;
882 sigemptyset(&nset);
883 sigaddset(&nset, SIGHUP);
884 sigaddset(&nset, SIGINT);
885 sigaddset(&nset, SIGQUIT);
886 pid = start_command(sh, &nset,
887 fda[xcnt++], -1, "-c", np->n_name + 1, NULL);
888 if (pid < 0) {
889 fprintf(stderr, tr(281,
890 "Message piping to <%s> failed\n"),
891 np->n_name);
892 *senderror = TRU1;
893 goto jcant;
895 free_child(pid);
896 } else {
897 char *fname = file_expand(np->n_name);
898 if (fname == NULL) {
899 *senderror = TRU1;
900 goto jcant;
902 if ((fout = Zopen(fname, "a", NULL)) == NULL) {
903 fprintf(stderr, tr(282,
904 "Message writing to <%s> failed: %s\n"),
905 fname, strerror(errno));
906 *senderror = TRU1;
907 goto jcant;
909 rewind(fin);
910 while ((i = getc(fin)) != EOF)
911 putc(i, fout);
912 if (ferror(fout)) {
913 fprintf(stderr, tr(282,
914 "Message writing to <%s> failed: %s\n"),
915 fname, tr(283, "write error"));
916 *senderror = TRU1;
918 Fclose(fout);
920 jcant:
922 * In days of old we removed the entry from the
923 * the list; now for sake of header expansion
924 * we leave it in and mark it as deleted.
926 np->n_type |= GDEL;
927 np = np->n_flink;
928 if (image < 0)
929 goto jdelall;
931 jleave:
932 if (fin != NULL)
933 Fclose(fin);
934 for (i = 0; i < pipecnt; ++i)
935 (void)close(fda[i]);
936 if (image >= 0) {
937 close(image);
938 image = -1;
940 return (names);
942 jdelall:
943 while (np != NULL) {
944 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
945 != 0)
946 np->n_type |= GDEL;
947 np = np->n_flink;
949 goto jleave;
952 struct grouphead *
953 findgroup(char *name)
955 struct grouphead *gh;
957 for (gh = groups[hash(name)]; gh != NULL; gh = gh->g_link)
958 if (*gh->g_name == *name && strcmp(gh->g_name, name) == 0)
959 return(gh);
960 return(NULL);
963 void
964 printgroup(char *name)
966 struct grouphead *gh;
967 struct group *gp;
969 if ((gh = findgroup(name)) == NULL) {
970 fprintf(stderr, tr(202, "\"%s\": no such alias\n"), name);
971 return;
973 printf("%s\t", gh->g_name);
974 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link)
975 printf(" %s", gp->ge_name);
976 putchar('\n');
979 void
980 remove_group(const char *name)
982 struct grouphead *gh, *gp = NULL;
983 int h = hash(name);
985 for (gh = groups[h]; gh != NULL; gh = gh->g_link) {
986 if (*gh->g_name == *name && strcmp(gh->g_name, name) == 0) {
987 _remove_grouplist(gh);
988 free(gh->g_name);
989 if (gp != NULL)
990 gp->g_link = gh->g_link;
991 else
992 groups[h] = NULL;
993 free(gh);
994 break;
996 gp = gh;