colour_table_create(): auto-enable $TERMs named "color" (Gavin Troy)
[s-mailx.git] / names.c
blob6bb24f9dce7c5c2aaf7842de73a45d3708d7b3f5
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 ui32_t
449 count_nonlocal(struct name const *np)
451 ui32_t c;
452 NYD_ENTER;
454 for (c = 0; np != NULL; np = np->n_flink)
455 if (!(np->n_type & GDEL) && !(np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE))
456 ++c;
457 NYD_LEAVE;
458 return c;
461 FL struct name *
462 extract(char const *line, enum gfield ntype)
464 struct name *rv;
465 NYD_ENTER;
467 rv = _extract1(line, ntype, " \t,", 0);
468 NYD_LEAVE;
469 return rv;
472 FL struct name *
473 lextract(char const *line, enum gfield ntype)
475 struct name *rv;
476 NYD_ENTER;
478 rv = ((line != NULL && strpbrk(line, ",\"\\(<|"))
479 ? _extract1(line, ntype, ",", 1) : extract(line, ntype));
480 NYD_LEAVE;
481 return rv;
484 FL char *
485 detract(struct name *np, enum gfield ntype)
487 char *topp, *cp;
488 struct name *p;
489 int comma, s;
490 NYD_ENTER;
492 topp = NULL;
493 if (np == NULL)
494 goto jleave;
496 comma = ntype & GCOMMA;
497 ntype &= ~GCOMMA;
498 s = 0;
499 if ((options & OPT_DEBUG) && comma)
500 fprintf(stderr, tr(145, "detract asked to insert commas\n"));
501 for (p = np; p != NULL; p = p->n_flink) {
502 if (ntype && (p->n_type & GMASK) != ntype)
503 continue;
504 s += strlen(p->n_fullname) +1;
505 if (comma)
506 s++;
508 if (s == 0)
509 goto jleave;
511 s += 2;
512 topp = salloc(s);
513 cp = topp;
514 for (p = np; p != NULL; p = p->n_flink) {
515 if (ntype && (p->n_type & GMASK) != ntype)
516 continue;
517 cp = sstpcpy(cp, p->n_fullname);
518 if (comma && p->n_flink != NULL)
519 *cp++ = ',';
520 *cp++ = ' ';
522 *--cp = 0;
523 if (comma && *--cp == ',')
524 *cp = 0;
525 jleave:
526 NYD_LEAVE;
527 return topp;
530 FL struct name *
531 grab_names(char const *field, struct name *np, int comma, enum gfield gflags)
533 struct name *nq;
534 NYD_ENTER;
536 jloop:
537 np = lextract(readstr_input(field, detract(np, comma)), gflags);
538 for (nq = np; nq != NULL; nq = nq->n_flink)
539 if (is_addr_invalid(nq, 1))
540 goto jloop;
541 NYD_LEAVE;
542 return np;
545 FL struct name *
546 checkaddrs(struct name *np)
548 struct name *n;
549 NYD_ENTER;
551 for (n = np; n != NULL;) {
552 if (is_addr_invalid(n, 1)) {
553 if (n->n_blink)
554 n->n_blink->n_flink = n->n_flink;
555 if (n->n_flink)
556 n->n_flink->n_blink = n->n_blink;
557 if (n == np)
558 np = n->n_flink;
560 n = n->n_flink;
562 NYD_LEAVE;
563 return np;
566 FL struct name *
567 usermap(struct name *names, bool_t force_metoo)
569 struct name *new, *np, *cp;
570 struct grouphead *gh;
571 int metoo;
572 NYD_ENTER;
574 new = NULL;
575 np = names;
576 metoo = (force_metoo || ok_blook(metoo));
577 while (np != NULL) {
578 assert(!(np->n_type & GDEL)); /* TODO legacy */
579 if (is_fileorpipe_addr(np) || np->n_name[0] == '\\') {
580 cp = np->n_flink;
581 new = put(new, np);
582 np = cp;
583 continue;
585 gh = findgroup(np->n_name);
586 cp = np->n_flink;
587 if (gh != NULL)
588 new = _gexpand(0, new, gh, metoo, np->n_type);
589 else
590 new = put(new, np);
591 np = cp;
593 NYD_LEAVE;
594 return new;
597 FL struct name *
598 elide(struct name *names)
600 struct name *np, *t, *newn, *x;
601 NYD_ENTER;
603 newn = NULL;
604 if (names == NULL)
605 goto jleave;
607 /* Throw away all deleted nodes (XXX merge with plain sort below?) */
608 for (np = NULL; names != NULL; names = names->n_flink)
609 if (!(names->n_type & GDEL)) {
610 names->n_blink = np;
611 if (np)
612 np->n_flink = names;
613 else
614 newn = names;
615 np = names;
617 if (newn == NULL)
618 goto jleave;
620 np = newn->n_flink;
621 if (np != NULL)
622 np->n_blink = NULL;
623 newn->n_flink = NULL;
625 while (np != NULL) {
626 int cmpres;
628 t = newn;
629 while ((cmpres = asccasecmp(t->n_name, np->n_name)) < 0) {
630 if (t->n_flink == NULL)
631 break;
632 t = t->n_flink;
635 /* If we ran out of t's, put new entry after the current value of t */
636 if (cmpres < 0) {
637 t->n_flink = np;
638 np->n_blink = t;
639 t = np;
640 np = np->n_flink;
641 t->n_flink = NULL;
642 continue;
645 /* Otherwise, put the new entry in front of the current t. If at the
646 * front of the list, the new guy becomes the new head of the list */
647 if (t == newn) {
648 t = np;
649 np = np->n_flink;
650 t->n_flink = newn;
651 newn->n_blink = t;
652 t->n_blink = NULL;
653 newn = t;
654 continue;
657 /* The normal case -- we are inserting into the middle of the list */
658 x = np;
659 np = np->n_flink;
660 x->n_flink = t;
661 x->n_blink = t->n_blink;
662 t->n_blink->n_flink = x;
663 t->n_blink = x;
666 /* Now the list headed up by new is sorted. Remove duplicates */
667 np = newn;
668 while (np != NULL) {
669 t = np;
670 while (t->n_flink != NULL && !asccasecmp(np->n_name, t->n_flink->n_name))
671 t = t->n_flink;
672 if (t == np) {
673 np = np->n_flink;
674 continue;
677 /* Now t points to the last entry with the same name as np.
678 * Make np point beyond t */
679 np->n_flink = t->n_flink;
680 if (t->n_flink != NULL)
681 t->n_flink->n_blink = np;
682 np = np->n_flink;
684 jleave:
685 NYD_LEAVE;
686 return newn;
689 FL struct name *
690 delete_alternates(struct name *np)
692 struct name *xp;
693 char **ap;
694 NYD_ENTER;
696 np = delname(np, myname);
697 if (altnames)
698 for (ap = altnames; *ap != '\0'; ++ap)
699 np = delname(np, *ap);
701 if ((xp = lextract(ok_vlook(from), GEXTRA | GSKIN)) != NULL)
702 while (xp != NULL) {
703 np = delname(np, xp->n_name);
704 xp = xp->n_flink;
707 if ((xp = lextract(ok_vlook(replyto), GEXTRA | GSKIN)) != NULL)
708 while (xp != NULL) {
709 np = delname(np, xp->n_name);
710 xp = xp->n_flink;
713 if ((xp = extract(ok_vlook(sender), GEXTRA | GSKIN)) != NULL)
714 while (xp != NULL) {
715 np = delname(np, xp->n_name);
716 xp = xp->n_flink;
718 NYD_LEAVE;
719 return np;
722 FL int
723 is_myname(char const *name)
725 int rv = 1;
726 struct name *xp;
727 char **ap;
728 NYD_ENTER;
730 if (_same_name(myname, name))
731 goto jleave;
732 if (altnames)
733 for (ap = altnames; *ap != NULL; ++ap)
734 if (_same_name(*ap, name))
735 goto jleave;
737 if ((xp = lextract(ok_vlook(from), GEXTRA | GSKIN)) != NULL)
738 while (xp != NULL) {
739 if (_same_name(xp->n_name, name))
740 goto jleave;
741 xp = xp->n_flink;
744 if ((xp = lextract(ok_vlook(replyto), GEXTRA | GSKIN)) != NULL)
745 while (xp != NULL) {
746 if (_same_name(xp->n_name, name))
747 goto jleave;
748 xp = xp->n_flink;
751 if ((xp = extract(ok_vlook(sender), GEXTRA | GSKIN)) != NULL)
752 while (xp != NULL) {
753 if (_same_name(xp->n_name, name))
754 goto jleave;
755 xp = xp->n_flink;
757 rv = 0;
758 jleave:
759 NYD_LEAVE;
760 return rv;
763 FL struct name *
764 outof(struct name *names, FILE *fo, bool_t *senderror)
766 ui32_t pipecnt, xcnt, i;
767 int *fda;
768 char const *sh;
769 struct name *np;
770 FILE *fin = NULL, *fout;
771 NYD_ENTER;
773 /* Look through all recipients and do a quick return if no file or pipe
774 * addressee is found */
775 fda = NULL; /* Silence cc */
776 for (pipecnt = xcnt = 0, np = names; np != NULL; np = np->n_flink)
777 switch (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) {
778 case NAME_ADDRSPEC_ISFILE:
779 ++xcnt;
780 break;
781 case NAME_ADDRSPEC_ISPIPE:
782 ++pipecnt;
783 break;
785 if (pipecnt == 0 && xcnt == 0)
786 goto jleave;
788 /* Otherwise create an array of file descriptors for each found pipe
789 * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
790 * each pipe subprocess needs its very own file descriptor, and we need
791 * to deal with that.
792 * To make our life a bit easier let's just use the auto-reclaimed
793 * string storage */
794 if (pipecnt == 0) {
795 fda = NULL;
796 sh = NULL;
797 } else {
798 fda = salloc(sizeof(int) * pipecnt);
799 for (i = 0; i < pipecnt; ++i)
800 fda[i] = -1;
801 if ((sh = ok_vlook(SHELL)) == NULL)
802 sh = XSHELL;
805 for (np = names; np != NULL;) {
806 if (!(np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE)) {
807 np = np->n_flink;
808 continue;
811 /* See if we have copied the complete message out yet. If not, do so */
812 if (image < 0) {
813 int c;
814 char *tempEdit;
816 if ((fout = Ftmp(&tempEdit, "outof",
817 OF_WRONLY | OF_HOLDSIGS | OF_REGISTER, 0600)) == NULL) {
818 perror(tr(146, "Creation of temporary image"));
819 *senderror = TRU1;
820 goto jcant;
822 if ((image = open(tempEdit, O_RDWR | O_CLOEXEC)) >= 0) {
823 _SET_CLOEXEC(image);
824 for (i = 0; i < pipecnt; ++i) {
825 int fd = open(tempEdit, O_RDONLY | O_CLOEXEC);
826 if (fd < 0) {
827 close(image);
828 image = -1;
829 pipecnt = i;
830 break;
832 fda[i] = fd;
833 _SET_CLOEXEC(fd);
836 Ftmp_release(&tempEdit);
838 if (image < 0) {
839 perror(tr(147, "Creating descriptor duplicate of temporary image"));
840 *senderror = TRU1;
841 Fclose(fout);
842 goto jcant;
845 fprintf(fout, "From %s %s", myname, time_current.tc_ctime);
846 c = EOF;
847 while (i = c, (c = getc(fo)) != EOF)
848 putc(c, fout);
849 rewind(fo);
850 if ((int)i != '\n')
851 putc('\n', fout);
852 putc('\n', fout);
853 fflush(fout);
854 if (ferror(fout)) {
855 perror(tr(148, "Finalizing write of temporary 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,
864 "Failed to open a duplicate of the temporary image"));
865 jcantfout:
866 *senderror = TRU1;
867 close(image);
868 image = -1;
869 goto jcant;
872 /* From now on use xcnt as a counter for pipecnt */
873 xcnt = 0;
876 /* Now either copy "image" to the desired file or give it as the standard
877 * input to the desired 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, fda[xcnt++], -1, "-c",
887 np->n_name + 1, NULL, NULL);
888 if (pid < 0) {
889 fprintf(stderr, tr(281, "Message piping to <%s> failed\n"),
890 np->n_name);
891 *senderror = TRU1;
892 goto jcant;
894 free_child(pid);
895 } else {
896 char c, *fname = file_expand(np->n_name);
897 if (fname == NULL) {
898 *senderror = TRU1;
899 goto jcant;
902 if ((fout = Zopen(fname, "a", NULL)) == NULL) {
903 fprintf(stderr, tr(282, "Message writing to <%s> failed: %s\n"),
904 fname, strerror(errno));
905 *senderror = TRU1;
906 goto jcant;
908 rewind(fin);
909 while ((c = getc(fin)) != EOF)
910 putc(c, fout);
911 if (ferror(fout)) {
912 fprintf(stderr, tr(282, "Message writing to <%s> failed: %s\n"),
913 fname, tr(283, "write error"));
914 *senderror = TRU1;
916 Fclose(fout);
918 jcant:
919 /* In days of old we removed the entry from the the list; now for sake of
920 * header expansion we leave it in and mark it as deleted */
921 np->n_type |= GDEL;
922 np = np->n_flink;
923 if (image < 0)
924 goto jdelall;
926 jleave:
927 if (fin != NULL)
928 Fclose(fin);
929 for (i = 0; i < pipecnt; ++i)
930 close(fda[i]);
931 if (image >= 0) {
932 close(image);
933 image = -1;
935 NYD_LEAVE;
936 return names;
938 jdelall:
939 while (np != NULL) {
940 if (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
941 np->n_type |= GDEL;
942 np = np->n_flink;
944 goto jleave;
947 FL struct grouphead *
948 findgroup(char *name)
950 struct grouphead *gh;
951 NYD_ENTER;
953 for (gh = groups[hash(name)]; gh != NULL; gh = gh->g_link)
954 if (*gh->g_name == *name && !strcmp(gh->g_name, name))
955 break;
956 NYD_LEAVE;
957 return gh;
960 FL void
961 printgroup(char *name)
963 struct grouphead *gh;
964 struct group *gp;
965 NYD_ENTER;
967 if ((gh = findgroup(name)) == NULL) {
968 fprintf(stderr, tr(202, "\"%s\": no such alias\n"), name);
969 goto jleave;
972 printf("%s\t", gh->g_name);
973 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link)
974 printf(" %s", gp->ge_name);
975 putchar('\n');
976 jleave:
977 NYD_LEAVE;
980 FL void
981 remove_group(char const *name)
983 ui32_t h;
984 struct grouphead *gh, *gp;
985 NYD_ENTER;
987 h = hash(name);
989 for (gp = NULL, gh = groups[h]; gh != NULL; gh = gh->g_link) {
990 if (*gh->g_name == *name && !strcmp(gh->g_name, name)) {
991 _remove_grouplist(gh);
992 free(gh->g_name);
993 if (gp != NULL)
994 gp->g_link = gh->g_link;
995 else
996 groups[h] = NULL;
997 free(gh);
998 break;
1000 gp = gh;
1002 NYD_LEAVE;
1005 #ifdef _OUR_CLOEXEC
1006 # undef O_CLOEXEC
1007 # undef _OUR_CLOEXEC
1008 #endif
1009 #undef _SET_CLOEXEC
1011 /* vim:set fenc=utf-8:s-it-mode */