1 /* $NetBSD: names.c,v 1.26 2007/10/23 14:58:45 christos Exp $ */
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)names.c 8.1 (Berkeley) 6/6/93";
37 __RCSID("$NetBSD: names.c,v 1.26 2007/10/23 14:58:45 christos Exp $");
42 * Mail -- a mail program
51 * Allocate a single element of a name list,
52 * initialize its name field to the passed
56 nalloc(char str
[], int ntype
)
60 np
= salloc(sizeof(*np
));
64 np
->n_name
= savestr(str
);
69 * Find the tail of a list and return it.
72 tailof(struct name
*name
)
79 while (np
->n_flink
!= NULL
)
85 * Grab a single word (liberal word)
86 * Throw away things between ()'s, and take anything between <>.
89 yankword(char *ap
, char wbuf
[])
100 while (*cp
!= '\0') {
112 } else if (is_WSP(*cp
) || *cp
== ',')
118 for (cp2
= wbuf
; *cp
&& (*cp2
++ = *cp
++) != '>'; /*EMPTY*/)
121 for (cp2
= wbuf
; *cp
&& !strchr(" \t,(", *cp
); *cp2
++ = *cp
++)
128 * Extract a list of names from a line,
129 * and make a list of names from it.
130 * Return the list or NULL if none found.
133 extract(char line
[], int ntype
)
136 struct name
*begin
, *np
, *t
;
139 if (line
== NULL
|| *line
== '\0')
144 while ((cp
= yankword(cp
, nbuf
)) != NULL
) {
145 t
= nalloc(nbuf
, ntype
);
156 /* XXX - is this really sufficient? */
157 static int need_quotes(char *str
)
159 return strpbrk(str
, " \t") != NULL
;
163 * Turn a list of names into a string of the same names.
166 detract(struct name
*np
, int ntype
)
174 quote
= ntype
& GSMOPTS
;
175 comma
= ntype
& GCOMMA
;
181 (void)fprintf(stderr
, "detract asked to insert commas\n");
182 for (p
= np
; p
!= NULL
; p
= p
->n_flink
) {
183 if (ntype
&& (p
->n_type
& GMASK
) != ntype
)
185 s
+= strlen(p
->n_name
) + 1;
188 if (quote
&& need_quotes(p
->n_name
))
196 for (p
= np
; p
!= NULL
; p
= p
->n_flink
) {
198 if (ntype
&& (p
->n_type
& GMASK
) != ntype
)
200 do_quotes
= (quote
&& need_quotes(p
->n_name
));
203 cp
= copy(p
->n_name
, cp
);
204 if (comma
&& p
->n_flink
!= NULL
)
211 if (comma
&& *--cp
== ',')
217 * Determine if the passed address is a local "send to file" address.
218 * If any of the network metacharacters precedes any slashes, it can't
219 * be a filename. We cheat with .'s to allow path names like ./...
222 isfileaddr(char *name
)
228 for (cp
= name
; *cp
; cp
++) {
229 if (*cp
== '!' || *cp
== '%' || *cp
== '@')
238 * For each recipient in the passed name list with a /
239 * in the name, append the message to the end of the named file
240 * and remove him from the recipient list.
242 * Recipients whose name begins with | are piped through the given
243 * program and removed.
246 outof(struct name
*names
, FILE *fo
, struct header
*hp
)
249 struct name
*np
, *begin
;
255 char tempname
[PATHSIZE
];
262 if (!isfileaddr(np
->n_name
) && np
->n_name
[0] != '|') {
266 ispipe
= np
->n_name
[0] == '|';
268 fname
= np
->n_name
+1;
270 fname
= expand(np
->n_name
);
273 * See if we have copied the complete message out yet.
278 (void)snprintf(tempname
, sizeof(tempname
),
279 "%s/mail.ReXXXXXXXXXXXX", tmpdir
);
280 if ((fd
= mkstemp(tempname
)) == -1 ||
281 (fout
= Fdopen(fd
, "a")) == NULL
) {
284 warn("%s", tempname
);
288 image
= open(tempname
, O_RDWR
);
289 (void)unlink(tempname
);
291 warn("%s", tempname
);
296 (void)fcntl(image
, F_SETFD
, FD_CLOEXEC
);
297 (void)fprintf(fout
, "From %s %s", myname
, date
);
299 (void)puthead(hp
, fout
, GTO
|GSUBJECT
|GCC
|GMISC
|GMIME
|GNL
);
301 (void)puthead(hp
, fout
, GTO
|GSUBJECT
|GCC
|GMISC
|GNL
);
303 while ((c
= getc(fo
)) != EOF
)
306 (void)putc('\n', fout
);
309 warn("%s", tempname
);
318 * Now either copy "image" to the desired file
319 * or give it as the standard input to the desired
320 * program as appropriate.
325 const char *shellcmd
;
330 * We can't really reuse the same image file,
331 * because multiple piped recipients will
332 * share the same lseek location and trample
335 if ((shellcmd
= value(ENAME_SHELL
)) == NULL
)
336 shellcmd
= _PATH_CSHELL
;
337 (void)sigemptyset(&nset
);
338 (void)sigaddset(&nset
, SIGHUP
);
339 (void)sigaddset(&nset
, SIGINT
);
340 (void)sigaddset(&nset
, SIGQUIT
);
341 pid
= start_command(shellcmd
, &nset
,
342 image
, -1, "-c", fname
, NULL
);
350 if ((fout
= Fopen(fname
, "a")) == NULL
) {
355 if ((f
= dup(image
)) < 0) {
359 fin
= Fdopen(f
, "r");
361 (void)fprintf(stderr
, "Can't reopen image\n");
367 while ((c
= getc(fin
)) != EOF
)
381 * In days of old we removed the entry from the
382 * the list; now for sake of header expansion
383 * we leave it in and mark it as deleted.
396 * Put another node onto a list of names and return
400 put(struct name
*list
, struct name
*node
)
402 node
->n_flink
= list
;
403 node
->n_blink
= NULL
;
405 list
->n_blink
= node
;
410 * Recursively expand a group name. We limit the expansion to some
411 * fixed level to keep things from going haywire.
412 * Direct recursion is not expanded for convenience.
415 gexpand(struct name
*nlist
, struct grouphead
*gh
, int metoo
, int ntype
)
418 struct grouphead
*ngh
;
423 if (depth
> MAXEXP
) {
424 (void)printf("Expanding alias to depth larger than %d\n", MAXEXP
);
428 for (gp
= gh
->g_list
; gp
!= NULL
; gp
= gp
->ge_link
) {
432 if (strcmp(cp
, gh
->g_name
) == 0)
434 if ((ngh
= findgroup(cp
)) != NULL
) {
435 nlist
= gexpand(nlist
, ngh
, metoo
, ntype
);
439 np
= nalloc(cp
, ntype
);
441 * At this point should allow to expand
442 * to self if only person in group
444 if (gp
== gh
->g_list
&& gp
->ge_link
== NULL
)
446 if (!metoo
&& strcmp(cp
, myname
) == 0)
449 nlist
= put(nlist
, np
);
456 * Map all of the aliased users in the invoker's mailrc
457 * file and insert them into the list.
458 * Changed after all these months of service to recursively
459 * expand names (2/14/80).
463 usermap(struct name
*names
)
465 struct name
*new, *np
, *cp
;
466 struct grouphead
*gh
;
471 metoo
= (value(ENAME_METOO
) != NULL
);
473 if (np
->n_name
[0] == '\\') {
479 gh
= findgroup(np
->n_name
);
482 new = gexpand(new, gh
, metoo
, np
->n_type
);
491 * Concatenate the two passed name lists, return the result.
494 cat(struct name
*n1
, struct name
*n2
)
509 * Determine the number of undeleted elements in
510 * a name list and return it.
513 count(struct name
*np
)
517 for (c
= 0; np
!= NULL
; np
= np
->n_flink
)
518 if ((np
->n_type
& GDEL
) == 0)
524 * Unpack the name list onto a vector of strings.
525 * Return an error if the name list won't fit.
528 unpack(struct name
*np
)
530 const char **ap
, **begin
;
532 int t
, extra
, metoo
, verbose
;
535 if ((t
= count(n
)) == 0)
536 errx(1, "No names to unpack");
538 * Compute the number of extra arguments we will need.
539 * We need at least two extra -- one for "mail" and one for
540 * the terminating 0 pointer. Additional spots may be needed
541 * to pass along -f to the host mailer.
545 metoo
= value(ENAME_METOO
) != NULL
;
548 verbose
= value(ENAME_VERBOSE
) != NULL
;
551 begin
= salloc((t
+ extra
) * sizeof(*begin
));
559 for (/*EMPTY*/; n
!= NULL
; n
= n
->n_flink
)
560 if ((n
->n_type
& GDEL
) == 0)
567 * Remove all of the duplicates from the passed name list by
568 * insertion sorting them, then checking for dups.
569 * Return the head of the new list.
572 elide(struct name
*names
)
574 struct name
*np
, *t
, *new;
587 while (strcasecmp(t
->n_name
, np
->n_name
) < 0) {
588 if (t
->n_flink
== NULL
)
594 * If we ran out of t's, put the new entry after
595 * the current value of t.
598 if (strcasecmp(t
->n_name
, np
->n_name
) < 0) {
608 * Otherwise, put the new entry in front of the
609 * current t. If at the front of the list,
610 * the new guy becomes the new head of the list.
624 * The normal case -- we are inserting into the
625 * middle of the list.
631 x
->n_blink
= t
->n_blink
;
632 t
->n_blink
->n_flink
= x
;
637 * Now the list headed up by new is sorted.
638 * Go through it and remove duplicates.
644 while (t
->n_flink
!= NULL
&&
645 strcasecmp(np
->n_name
, t
->n_flink
->n_name
) == 0)
647 if (t
== np
|| t
== NULL
) {
653 * Now t points to the last entry with the same name
654 * as np. Make np point beyond t.
657 np
->n_flink
= t
->n_flink
;
658 if (t
->n_flink
!= NULL
)
659 t
->n_flink
->n_blink
= np
;
666 * Delete the given name from a namelist.
669 delname(struct name
*np
, char name
[])
673 for (p
= np
; p
!= NULL
; p
= p
->n_flink
)
674 if (strcasecmp(p
->n_name
, name
) == 0) {
675 if (p
->n_blink
== NULL
) {
676 if (p
->n_flink
!= NULL
)
677 p
->n_flink
->n_blink
= NULL
;
681 if (p
->n_flink
== NULL
) {
682 if (p
->n_blink
!= NULL
)
683 p
->n_blink
->n_flink
= NULL
;
686 p
->n_blink
->n_flink
= p
->n_flink
;
687 p
->n_flink
->n_blink
= p
->n_blink
;
693 * Pretty print a name list
694 * Uncomment it if you need it.
705 (void)fprintf(stderr
, "%s(%d) ", np
->n_name
, np
->n_type
);
708 (void)fprintf(stderr
, "\n");