nail.1, mk-mk.in: include the VERSION in the manual
[s-mailx.git] / names.c
blob7c80274b24dce9babe318bdcd5b9c42f5307b1ff
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 "rcv.h"
42 #include <sys/stat.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <unistd.h>
47 #include "extern.h"
49 /* Same name, taking care for *allnet*? */
50 static int same_name(char const *n1, char const *n2);
51 /* Delete the given name from a namelist */
52 static struct name * delname(struct name *np, char const *name);
53 /* Put another node onto a list of names and return the list */
54 static struct name * put(struct name *list, struct name *node);
55 /* Grab a single name (liberal name) */
56 static char const * yankname(char const *ap, char *wbuf,
57 char const *separators, int keepcomms);
58 /* Extraction multiplexer that splits an input line to names */
59 static struct name * extract1(char const *line, enum gfield ntype,
60 char const *separators, int keepcomms);
61 /* Recursively expand a group name. We limit the expansion to some fixed level
62 * to keep things from going haywire. Direct recursion is not expanded for
63 * convenience */
64 static struct name * gexpand(struct name *nlist, struct grouphead *gh,
65 int metoo, int ntype);
67 static void _remove_grouplist(struct grouphead *gh);
69 static int
70 same_name(char const *n1, char const *n2)
72 int ret = 0;
73 char c1, c2;
75 if (value("allnet") != NULL) {
76 do {
77 c1 = *n1++;
78 c2 = *n2++;
79 c1 = lowerconv(c1);
80 c2 = lowerconv(c2);
81 if (c1 != c2)
82 goto jleave;
83 } while (c1 != '\0' && c2 != '\0' && c1 != '@' && c2 != '@');
84 ret = 1;
85 } else
86 ret = (asccasecmp(n1, n2) == 0);
87 jleave:
88 return (ret);
91 static struct name *
92 delname(struct name *np, char const *name)
94 struct name *p;
96 for (p = np; p != NULL; p = p->n_flink)
97 if (same_name(p->n_name, name)) {
98 if (p->n_blink == NULL) {
99 if (p->n_flink != NULL)
100 p->n_flink->n_blink = NULL;
101 np = p->n_flink;
102 continue;
104 if (p->n_flink == NULL) {
105 if (p->n_blink != NULL)
106 p->n_blink->n_flink = NULL;
107 continue;
109 p->n_blink->n_flink = p->n_flink;
110 p->n_flink->n_blink = p->n_blink;
112 return (np);
115 static struct name *
116 put(struct name *list, struct name *node)
118 node->n_flink = list;
119 node->n_blink = NULL;
120 if (list != NULL)
121 list->n_blink = node;
122 return (node);
125 static char const *
126 yankname(char const *ap, char *wbuf, char const *separators, int keepcomms)
128 char const *cp;
129 char *wp, c, inquote, lc, lastsp;
131 *(wp = wbuf) = '\0';
133 /* Skip over intermediate list trash, as in ".org> , <xy@zz.org>" */
134 for (c = *ap; blankchar(c) || c == ','; c = *++ap)
136 if (c == '\0') {
137 cp = NULL;
138 goto jleave;
142 * Parse a full name: TODO RFC 5322
143 * - Keep everything in quotes, liberal handle *quoted-pair*s therein
144 * - Skip entire (nested) comments
145 * - In non-quote, non-comment, join adjacent space to a single SP
146 * - Understand separators only in non-quote, non-comment context,
147 * and only if not part of a *quoted-pair* (XXX too liberal)
149 cp = ap;
150 for (inquote = lc = lastsp = 0;; lc = c, ++cp) {
151 c = *cp;
152 if (c == '\0')
153 break;
154 if (c == '\\') {
155 lastsp = 0;
156 continue;
158 if (c == '"') {
159 if (lc != '\\')
160 inquote = ! inquote;
161 #if 0 /* TODO when doing real RFC 5322 parsers - why have i done this? */
162 else
163 --wp;
164 #endif
165 goto jwpwc;
167 if (inquote || lc == '\\') {
168 jwpwc: *wp++ = c;
169 lastsp = 0;
170 continue;
172 if (c == '(') {
173 ap = cp;
174 cp = skip_comment(cp + 1);
175 if (keepcomms)
176 while (ap < cp)
177 *wp++ = *ap++;
178 --cp;
179 lastsp = 0;
180 continue;
182 if (strchr(separators, c) != NULL)
183 break;
185 lc = lastsp;
186 lastsp = blankchar(c);
187 if (! lastsp || ! lc)
188 *wp++ = c;
190 if (blankchar(lc))
191 --wp;
193 *wp = '\0';
194 jleave:
195 return (cp);
198 static struct name *
199 extract1(char const *line, enum gfield ntype, char const *separators,
200 int keepcomms)
202 struct name *topp, *np, *t;
203 char const *cp;
204 char *nbuf;
206 topp = NULL;
207 if (line == NULL || *line == '\0')
208 goto jleave;
210 np = NULL;
211 cp = line;
212 nbuf = ac_alloc(strlen(line) + 1);
213 while ((cp = yankname(cp, nbuf, separators, keepcomms)) != NULL) {
214 t = nalloc(nbuf, ntype);
215 if (topp == NULL)
216 topp = t;
217 else
218 np->n_flink = t;
219 t->n_blink = np;
220 np = t;
222 ac_free(nbuf);
224 jleave:
225 return topp;
228 static struct name *
229 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype)
231 struct group *gp;
232 struct grouphead *ngh;
233 struct name *np;
234 static int depth;
235 char *cp;
237 if (depth > MAXEXP) {
238 printf(tr(150, "Expanding alias to depth larger than %d\n"),
239 MAXEXP);
240 goto jleave;
242 depth++;
244 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) {
245 cp = gp->ge_name;
246 if (*cp == '\\')
247 goto quote;
248 if (strcmp(cp, gh->g_name) == 0)
249 goto quote;
250 if ((ngh = findgroup(cp)) != NULL) {
251 /* For S-nail(1), the "group" may *be* the sender in
252 * that a name maps to a full address specification */
253 if (! metoo && ngh->g_list->ge_link == NULL &&
254 same_name(cp, myname))
255 continue;
256 nlist = gexpand(nlist, ngh, metoo, ntype);
257 continue;
259 quote:
260 np = nalloc(cp, ntype|GFULL);
262 * At this point should allow to expand
263 * to self if only person in group
265 if (gp == gh->g_list && gp->ge_link == NULL)
266 goto skip;
267 if (! metoo && same_name(cp, myname))
268 np->n_type |= GDEL;
269 skip:
270 nlist = put(nlist, np);
272 --depth;
273 jleave:
274 return (nlist);
277 static void
278 _remove_grouplist(struct grouphead *gh)
280 struct group *gp, *gq;
282 if ((gp = gh->g_list) != NULL) {
283 for (; gp; gp = gq) {
284 gq = gp->ge_link;
285 free(gp->ge_name);
286 free(gp);
292 * Allocate a single element of a name list, initialize its name field to the
293 * passed name and return it.
295 struct name *
296 nalloc(char *str, enum gfield ntype)
298 struct addrguts ag;
299 struct str in, out;
300 struct name *np;
302 np = (struct name*)salloc(sizeof *np);
303 np->n_flink = NULL;
304 np->n_blink = NULL;
305 np->n_type = ntype;
306 np->n_flags = 0;
308 (void)addrspec_with_guts((ntype & (GFULL|GSKIN|GREF)) != 0, str, &ag);
309 if ((ag.ag_n_flags & NAME_NAME_SALLOC) == 0) {
310 ag.ag_n_flags |= NAME_NAME_SALLOC;
311 ag.ag_skinned = savestrbuf(ag.ag_skinned, ag.ag_slen);
313 np->n_fullname = np->n_name = ag.ag_skinned;
314 np->n_flags = ag.ag_n_flags;
316 if (ntype & GFULL) {
317 if (ag.ag_ilen == ag.ag_slen
318 #ifdef HAVE_IDNA
319 && (ag.ag_n_flags & NAME_IDNA) == 0
320 #endif
322 goto jleave;
323 if (ag.ag_n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
324 goto jleave;
325 #ifdef HAVE_IDNA
326 if ((ag.ag_n_flags & NAME_IDNA) == 0) {
327 #endif
328 in.s = str;
329 in.l = ag.ag_ilen;
330 #ifdef HAVE_IDNA
331 } else {
333 * The domain name was IDNA and has been converted.
334 * We also have to ensure that the domain name in
335 * .n_fullname is replaced with the converted version,
336 * since MIME doesn't perform encoding of addresses.
338 size_t l = ag.ag_iaddr_start,
339 lsuff = ag.ag_ilen - ag.ag_iaddr_aend;
340 in.s = ac_alloc(l + ag.ag_slen + lsuff + 1);
341 memcpy(in.s, str, l);
342 memcpy(in.s + l, ag.ag_skinned, ag.ag_slen);
343 l += ag.ag_slen;
344 memcpy(in.s + l, str + ag.ag_iaddr_aend, lsuff);
345 l += lsuff;
346 in.s[l] = '\0';
347 in.l = l;
349 #endif
350 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
351 np->n_fullname = savestr(out.s);
352 free(out.s);
353 #ifdef HAVE_IDNA
354 if (ag.ag_n_flags & NAME_IDNA)
355 ac_free(in.s);
356 #endif
357 np->n_flags |= NAME_FULLNAME_SALLOC;
358 } else if (ntype & GREF) { /* TODO LEGACY */
359 /* TODO Unfortunately we had to skin GREFerences i.e. the
360 * TODO surrounding angle brackets have been stripped away.
361 * TODO Necessarily since otherwise the plain address check
362 * TODO fails due to them; insert them back so that valid
363 * TODO headers will be created */
364 np->n_fullname = np->n_name = str = salloc(ag.ag_slen + 2 + 1);
365 *(str++) = '<';
366 memcpy(str, ag.ag_skinned, ag.ag_slen);
367 str += ag.ag_slen;
368 *(str++) = '>';
369 *str = '\0';
371 jleave:
372 return (np);
375 struct name *
376 ndup(struct name *np, enum gfield ntype)
378 struct name *nnp;
380 if ((ntype & (GFULL|GSKIN)) && (np->n_flags & NAME_SKINNED) == 0) {
381 nnp = nalloc(np->n_name, ntype);
382 goto jleave;
385 nnp = (struct name*)salloc(sizeof *np);
386 nnp->n_flink = nnp->n_blink = NULL;
387 nnp->n_type = ntype;
388 nnp->n_flags = (np->n_flags &
389 ~(NAME_NAME_SALLOC | NAME_FULLNAME_SALLOC)) |
390 NAME_NAME_SALLOC;
391 nnp->n_name = savestr(np->n_name);
392 if (np->n_name == np->n_fullname || (ntype & (GFULL|GSKIN)) == 0)
393 nnp->n_fullname = nnp->n_name;
394 else {
395 nnp->n_flags |= NAME_FULLNAME_SALLOC;
396 nnp->n_fullname = savestr(np->n_fullname);
398 jleave:
399 return (nnp);
403 * Concatenate the two passed name lists, return the result.
405 struct name *
406 cat(struct name *n1, struct name *n2)
408 struct name *tail;
410 if (n1 == NULL)
411 return (n2);
412 if (n2 == NULL)
413 return (n1);
415 tail = n1;
416 while (tail->n_flink != NULL)
417 tail = tail->n_flink;
418 tail->n_flink = n2;
419 n2->n_blink = tail;
420 return (n1);
424 * Determine the number of undeleted elements in
425 * a name list and return it.
428 count(struct name const*np)
430 int c;
432 for (c = 0; np != NULL; np = np->n_flink)
433 if ((np->n_type & GDEL) == 0)
434 c++;
435 return (c);
439 * Extract a list of names from a line,
440 * and make a list of names from it.
441 * Return the list or NULL if none found.
443 struct name *
444 extract(char const *line, enum gfield ntype)
446 return extract1(line, ntype, " \t,", 0);
449 struct name *
450 lextract(char const *line, enum gfield ntype)
452 return ((line && strpbrk(line, ",\"\\(<|")) ?
453 extract1(line, ntype, ",", 1) : extract(line, ntype));
457 * Turn a list of names into a string of the same names.
459 char *
460 detract(struct name *np, enum gfield ntype)
462 char *topp, *cp;
463 struct name *p;
464 int comma, s;
466 topp = NULL;
467 if (np == NULL)
468 goto jleave;
470 comma = ntype & GCOMMA;
471 ntype &= ~GCOMMA;
472 s = 0;
473 if ((options & OPT_DEBUG) && comma)
474 fprintf(stderr, tr(145, "detract asked to insert commas\n"));
475 for (p = np; p != NULL; p = p->n_flink) {
476 if (ntype && (p->n_type & GMASK) != ntype)
477 continue;
478 s += strlen(p->n_fullname) + 1;
479 if (comma)
480 s++;
482 if (s == 0)
483 goto jleave;
485 s += 2;
486 topp = salloc(s);
487 cp = topp;
488 for (p = np; p != NULL; p = p->n_flink) {
489 if (ntype && (p->n_type & GMASK) != ntype)
490 continue;
491 cp = sstpcpy(cp, p->n_fullname);
492 if (comma && p->n_flink != NULL)
493 *cp++ = ',';
494 *cp++ = ' ';
496 *--cp = 0;
497 if (comma && *--cp == ',')
498 *cp = 0;
499 jleave:
500 return topp;
503 struct name *
504 grab_names(const char *field, struct name *np, int comma, enum gfield gflags)
506 struct name *nq;
507 jloop:
508 np = lextract(readstr_input(field, detract(np, comma)), gflags);
509 for (nq = np; nq != NULL; nq = nq->n_flink)
510 if (is_addr_invalid(nq, 1))
511 goto jloop;
512 return np;
516 * Check all addresses in np and delete invalid ones.
518 struct name *
519 checkaddrs(struct name *np)
521 struct name *n;
523 for (n = np; n != NULL;) {
524 if (is_addr_invalid(n, 1)) {
525 if (n->n_blink)
526 n->n_blink->n_flink = n->n_flink;
527 if (n->n_flink)
528 n->n_flink->n_blink = n->n_blink;
529 if (n == np)
530 np = n->n_flink;
532 n = n->n_flink;
534 return (np);
538 * Map all of the aliased users in the invoker's mailrc
539 * file and insert them into the list.
540 * Changed after all these months of service to recursively
541 * expand names (2/14/80).
543 struct name *
544 usermap(struct name *names, bool_t force_metoo)
546 struct name *new, *np, *cp;
547 struct grouphead *gh;
548 int metoo;
550 new = NULL;
551 np = names;
552 metoo = (force_metoo || value("metoo") != NULL);
553 while (np != NULL) {
554 assert((np->n_type & GDEL) == 0); /* TODO legacy */
555 if (is_fileorpipe_addr(np) || np->n_name[0] == '\\') {
556 cp = np->n_flink;
557 new = put(new, np);
558 np = cp;
559 continue;
561 gh = findgroup(np->n_name);
562 cp = np->n_flink;
563 if (gh != NULL)
564 new = gexpand(new, gh, metoo, np->n_type);
565 else
566 new = put(new, np);
567 np = cp;
569 return new;
573 * Remove all of the duplicates from the passed name list by
574 * insertion sorting them, then checking for dups.
575 * Return the head of the new list.
577 struct name *
578 elide(struct name *names)
580 struct name *np, *t, *newn, *x;
582 if (names == NULL)
583 return (NULL);
584 /* Throw away all deleted nodes (XXX merge with plain sort below?) */
585 for (newn = np = NULL; names != NULL; names = names->n_flink)
586 if ((names->n_type & GDEL) == 0) {
587 names->n_blink = np;
588 if (np)
589 np->n_flink = names;
590 else
591 newn = names;
592 np = names;
594 if (newn == NULL)
595 return (NULL);
597 np = newn->n_flink;
598 if (np != NULL)
599 np->n_blink = NULL;
600 newn->n_flink = NULL;
602 while (np != NULL) {
603 t = newn;
604 while (asccasecmp(t->n_name, np->n_name) < 0) {
605 if (t->n_flink == NULL)
606 break;
607 t = t->n_flink;
611 * If we ran out of t's, put the new entry after
612 * the current value of t.
615 if (asccasecmp(t->n_name, np->n_name) < 0) {
616 t->n_flink = np;
617 np->n_blink = t;
618 t = np;
619 np = np->n_flink;
620 t->n_flink = NULL;
621 continue;
625 * Otherwise, put the new entry in front of the
626 * current t. If at the front of the list,
627 * the new guy becomes the new head of the list.
630 if (t == newn) {
631 t = np;
632 np = np->n_flink;
633 t->n_flink = newn;
634 newn->n_blink = t;
635 t->n_blink = NULL;
636 newn = t;
637 continue;
641 * The normal case -- we are inserting into the
642 * 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;
654 * Now the list headed up by new is sorted.
655 * Go through it and remove duplicates.
658 np = newn;
659 while (np != NULL) {
660 t = np;
661 while (t->n_flink != NULL &&
662 asccasecmp(np->n_name, t->n_flink->n_name) == 0)
663 t = t->n_flink;
664 if (t == np) {
665 np = np->n_flink;
666 continue;
670 * Now t points to the last entry with the same name
671 * as np. Make np point beyond t.
674 np->n_flink = t->n_flink;
675 if (t->n_flink != NULL)
676 t->n_flink->n_blink = np;
677 np = np->n_flink;
679 return (newn);
682 struct name *
683 delete_alternates(struct name *np)
685 struct name *xp;
686 char **ap;
688 np = delname(np, myname);
689 if (altnames)
690 for (ap = altnames; *ap; ap++)
691 np = delname(np, *ap);
692 if ((xp = lextract(value("from"), GEXTRA|GSKIN)) != NULL)
693 while (xp) {
694 np = delname(np, xp->n_name);
695 xp = xp->n_flink;
697 if ((xp = lextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
698 while (xp) {
699 np = delname(np, xp->n_name);
700 xp = xp->n_flink;
702 if ((xp = extract(value("sender"), GEXTRA|GSKIN)) != NULL)
703 while (xp) {
704 np = delname(np, xp->n_name);
705 xp = xp->n_flink;
707 return (np);
711 is_myname(char const *name)
713 int ret = 1;
714 struct name *xp;
715 char **ap;
717 if (same_name(myname, name))
718 goto jleave;
719 if (altnames)
720 for (ap = altnames; *ap; ap++)
721 if (same_name(*ap, name))
722 goto jleave;
723 if ((xp = lextract(value("from"), GEXTRA|GSKIN)) != NULL)
724 while (xp) {
725 if (same_name(xp->n_name, name))
726 goto jleave;
727 xp = xp->n_flink;
729 if ((xp = lextract(value("replyto"), GEXTRA|GSKIN)) != NULL)
730 while (xp) {
731 if (same_name(xp->n_name, name))
732 goto jleave;
733 xp = xp->n_flink;
735 if ((xp = extract(value("sender"), GEXTRA|GSKIN)) != NULL)
736 while (xp) {
737 if (same_name(xp->n_name, name))
738 goto jleave;
739 xp = xp->n_flink;
741 ret = 0;
742 jleave:
743 return (ret);
747 * For each recipient in the passed name list with a /
748 * in the name, append the message to the end of the named file
749 * and remove him from the recipient list.
751 * Recipients whose name begins with | are piped through the given
752 * program and removed.
754 struct name *
755 outof(struct name *names, FILE *fo, struct header *hp, bool_t *senderror)
757 int pipecnt, xcnt, *fda, i;
758 char const *sh;
759 struct name *np;
760 FILE *fin = NULL, *fout;
761 (void)hp;
764 * Look through all recipients and do a quick return if no file or pipe
765 * addressee is found.
767 fda = NULL; /* Silence cc */
768 for (pipecnt = xcnt = 0, np = names; np != NULL; np = np->n_flink)
769 switch (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) {
770 case NAME_ADDRSPEC_ISFILE:
771 ++xcnt;
772 break;
773 case NAME_ADDRSPEC_ISPIPE:
774 ++pipecnt;
775 break;
777 if (pipecnt == 0 && xcnt == 0)
778 goto jleave;
781 * Otherwise create an array of file descriptors for each found pipe
782 * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
783 * each pipe subprocess needs its very own file descriptor, and we need
784 * to deal with that.
785 * To make our life a bit easier let's just use the auto-reclaimed
786 * string storage.
788 if (pipecnt == 0) {
789 fda = NULL;
790 sh = NULL;
791 } else {
792 fda = (int*)salloc(sizeof(int) * pipecnt);
793 for (i = 0; i < pipecnt; ++i)
794 fda[i] = -1;
795 if ((sh = value("SHELL")) == NULL)
796 sh = SHELL;
799 for (np = names; np != NULL;) {
800 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
801 == 0) {
802 np = np->n_flink;
803 continue;
807 * See if we have copied the complete message out yet.
808 * If not, do so.
810 if (image < 0) {
811 int c;
812 char *tempEdit;
814 /* XXX tempEdit unlink racy - block signals, at least */
815 if ((fout = Ftemp(&tempEdit, "Re", "w", 0600, 1))
816 == NULL) {
817 perror(tr(146, "Creation of temporary image"));
818 *senderror = TRU1;
819 goto jcant;
821 image = open(tempEdit, O_RDWR);
822 if (image >= 0)
823 for (i = 0; i < pipecnt; ++i) {
824 int fd = open(tempEdit, O_RDONLY);
825 if (fd < 0) {
826 (void)close(image);
827 image = -1;
828 pipecnt = i;
829 break;
831 fda[i] = fd;
832 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
834 unlink(tempEdit);
835 Ftfree(&tempEdit);
836 if (image < 0) {
837 perror(tr(147, "Creating descriptor duplicate "
838 "of temporary image"));
839 *senderror = TRU1;
840 Fclose(fout);
841 goto jcant;
843 (void)fcntl(image, F_SETFD, FD_CLOEXEC);
845 fprintf(fout, "From %s %s",
846 myname, time_current.tc_ctime);
847 c = EOF;
848 while (i = c, (c = getc(fo)) != EOF)
849 putc(c, fout);
850 rewind(fo);
851 if (i != '\n')
852 putc('\n', fout);
853 putc('\n', fout);
854 fflush(fout);
855 if (ferror(fout)) {
856 perror(tr(148, "Finalizing write of temporary "
857 "image"));
858 Fclose(fout);
859 goto jcantfout;
861 Fclose(fout);
863 /* If we have to serve file addressees, open reader */
864 if (xcnt != 0 && (fin = Fdopen(image, "r")) == NULL) {
865 perror(tr(149, "Failed to open a duplicate of "
866 "the temporary image"));
867 jcantfout: *senderror = TRU1;
868 (void)close(image);
869 image = -1;
870 goto jcant;
873 /* From now on use xcnt as a counter for pipecnt */
874 xcnt = 0;
878 * Now either copy "image" to the desired file
879 * or give it as the standard input to the desired
880 * program as appropriate.
883 if (np->n_flags & NAME_ADDRSPEC_ISPIPE) {
884 int pid;
885 sigset_t nset;
887 sigemptyset(&nset);
888 sigaddset(&nset, SIGHUP);
889 sigaddset(&nset, SIGINT);
890 sigaddset(&nset, SIGQUIT);
891 pid = start_command(sh, &nset,
892 fda[xcnt++], -1, "-c", np->n_name + 1, NULL);
893 if (pid < 0) {
894 fprintf(stderr, tr(281,
895 "Message piping to <%s> failed\n"),
896 np->n_name);
897 *senderror = TRU1;
898 goto jcant;
900 free_child(pid);
901 } else {
902 char *fname = file_expand(np->n_name);
903 if (fname == NULL) {
904 *senderror = TRU1;
905 goto jcant;
907 if ((fout = Zopen(fname, "a", NULL)) == NULL) {
908 fprintf(stderr, tr(282,
909 "Message writing to <%s> failed: %s\n"),
910 fname, strerror(errno));
911 *senderror = TRU1;
912 goto jcant;
914 rewind(fin);
915 while ((i = getc(fin)) != EOF)
916 putc(i, fout);
917 if (ferror(fout)) {
918 fprintf(stderr, tr(282,
919 "Message writing to <%s> failed: %s\n"),
920 fname, tr(283, "write error"));
921 *senderror = TRU1;
923 Fclose(fout);
925 jcant:
927 * In days of old we removed the entry from the
928 * the list; now for sake of header expansion
929 * we leave it in and mark it as deleted.
931 np->n_type |= GDEL;
932 np = np->n_flink;
933 if (image < 0)
934 goto jdelall;
936 jleave:
937 if (fin != NULL)
938 Fclose(fin);
939 for (i = 0; i < pipecnt; ++i)
940 (void)close(fda[i]);
941 if (image >= 0) {
942 close(image);
943 image = -1;
945 return (names);
947 jdelall:
948 while (np != NULL) {
949 if ((np->n_flags & (NAME_ADDRSPEC_ISFILE|NAME_ADDRSPEC_ISPIPE))
950 != 0)
951 np->n_type |= GDEL;
952 np = np->n_flink;
954 goto jleave;
957 struct grouphead *
958 findgroup(char *name)
960 struct grouphead *gh;
962 for (gh = groups[hash(name)]; gh != NULL; gh = gh->g_link)
963 if (*gh->g_name == *name && strcmp(gh->g_name, name) == 0)
964 return(gh);
965 return(NULL);
968 void
969 printgroup(char *name)
971 struct grouphead *gh;
972 struct group *gp;
974 if ((gh = findgroup(name)) == NULL) {
975 fprintf(stderr, tr(202, "\"%s\": no such alias\n"), name);
976 return;
978 printf("%s\t", gh->g_name);
979 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link)
980 printf(" %s", gp->ge_name);
981 putchar('\n');
984 void
985 remove_group(const char *name)
987 struct grouphead *gh, *gp = NULL;
988 int h = hash(name);
990 for (gh = groups[h]; gh != NULL; gh = gh->g_link) {
991 if (*gh->g_name == *name && strcmp(gh->g_name, name) == 0) {
992 _remove_grouplist(gh);
993 free(gh->g_name);
994 if (gp != NULL)
995 gp->g_link = gh->g_link;
996 else
997 groups[h] = NULL;
998 free(gh);
999 break;
1001 gp = gh;