4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
24 * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
32 * University Copyright- Copyright (c) 1982, 1986, 1988
33 * The Regents of the University of California
36 * University Acknowledgment- Portions of this document are derived from
37 * software developed by the University of California, Berkeley, and its
42 * mailx -- a modified version of a University of California at Berkeley
51 static struct name
*nalloc(char str
[]);
52 static int isfileaddr(char *name
);
53 static int lengthof(struct name
*name
);
54 static struct name
*gexpand(struct name
*nlist
, struct grouphead
*gh
,
55 int metoo
, int arg_ntype
);
56 static char *norm(register char *user
, register char *ubuf
,
58 static struct name
*put(struct name
*list
, struct name
*node
);
61 * Allocate a single element of a name list,
62 * initialize its name field to the passed
69 register struct name
*np
;
71 np
= (struct name
*)salloc(sizeof (*np
));
75 np
->n_full
= savestr(str
);
76 np
->n_name
= skin(np
->n_full
);
81 * Find the tail of a list and return it.
85 tailof(struct name
*name
)
87 register struct name
*np
;
92 while (np
->n_flink
!= NIL
)
98 * Extract a list of names from a line,
99 * and make a list of names from it.
100 * Return the list or NIL if none found.
104 extract(char line
[], int arg_ntype
)
106 short ntype
= (short)arg_ntype
;
108 register struct name
*top
, *np
, *t
;
109 char nbuf
[BUFSIZ
], abuf
[BUFSIZ
];
112 if (line
== NOSTR
|| strlen(line
) == 0)
114 comma
= docomma(line
);
118 while ((cp
= yankword(cp
, nbuf
, sizeof (nbuf
), comma
)) != NOSTR
) {
119 if (np
!= NIL
&& equal(nbuf
, "at")) {
120 nstrcpy(abuf
, sizeof (abuf
), nbuf
);
121 if ((cp
= yankword(cp
, nbuf
, sizeof (nbuf
),
123 nstrcpy(nbuf
, sizeof (nbuf
), abuf
);
126 snprintf(abuf
, sizeof (abuf
), "%s@%s", np
->n_name
,
128 np
->n_name
= savestr(abuf
);
145 * Turn a list of names into a string of the same names.
149 detract(register struct name
*np
, int ntype
)
152 register char *cp
, *top
;
153 register struct name
*p
;
158 for (p
= np
; p
!= NIL
; p
= p
->n_flink
) {
159 if ((ntype
&& (p
->n_type
& GMASK
) != ntype
) ||
162 s
+= strlen(p
->n_full
) + 2;
166 top
= (char *)salloc((unsigned)(++s
));
168 for (p
= np
; p
!= NIL
; p
= p
->n_flink
) {
169 if ((ntype
&& (p
->n_type
& GMASK
) != ntype
) ||
172 cp
= copy(p
->n_full
, cp
);
181 outpre(struct name
*to
)
183 register struct name
*np
;
185 for (np
= to
; np
; np
= np
->n_flink
)
186 if (isfileaddr(np
->n_name
))
192 * For each recipient in the passed name list with a /
193 * in the name, append the message to the end of the named file
194 * and remove him from the recipient list.
196 * Recipients whose name begins with | are piped through the given
197 * program and removed.
201 outof(struct name
*names
, FILE *fo
)
204 register struct name
*np
;
206 char *date
, *fname
, *shell
;
215 for (np
= names
; np
!= NIL
; np
= np
->n_flink
) {
216 if (!isfileaddr(np
->n_name
) && np
->n_name
[0] != '|')
219 ispipe
= np
->n_name
[0] == '|';
221 fname
= np
->n_name
+1;
223 fname
= safeexpand(np
->n_name
);
226 * See if we have copied the complete message out yet.
231 fd
= open(tempEdit
, O_CREAT
|O_EXCL
|O_APPEND
|O_WRONLY
,
233 if ((fd
< 0) && (errno
== EEXIST
)) {
234 if ((fd
= open(tempEdit
, O_APPEND
|O_WRONLY
,
241 if ((fout
= fdopen(fd
, "a")) == NULL
) {
246 image
= open(tempEdit
, O_RDWR
);
256 fprintf(fout
, "From %s %s", myname
, date
);
257 while ((c
= getc(fo
)) != EOF
)
268 * Now either copy "image" to the desired file
269 * or give it as the standard input to the desired
270 * program as appropriate.
278 sigset(SIGHUP
, SIG_IGN
);
279 sigset(SIGINT
, SIG_IGN
);
280 sigset(SIGQUIT
, SIG_IGN
);
285 if ((shell
= value("SHELL")) == NOSTR
||
288 (void) execlp(shell
, shell
, "-c", fname
,
300 if ((fout
= fopen(fname
, "a")) == NULL
) {
305 fin
= Fdopen(image
, "r");
308 gettext("Can't reopen image\n"));
315 putc(getc(fin
), fout
);
316 while (fgets(line
, sizeof (line
), fin
)) {
317 if (strncmp(line
, "From ", 5) == 0)
322 while ((c
= getc(fin
)) != EOF
)
328 senderr
++, perror(fname
);
334 * In days of old we removed the entry from the
335 * the list; now for sake of header expansion
336 * we leave it in and mark it as deleted.
341 register struct name
*t
, *x
;
369 * Determine if the passed address is a local "send to file" address.
370 * If any of the network metacharacters precedes any slashes, it can't
371 * be a filename. We cheat with .'s to allow path names like ./...
372 * If "fcc" has been unset, then short-circuit those tests, but not
376 isfileaddr(char *name
)
379 char *fcc
= value("fcc");
387 for (cp
= name
; *cp
; cp
++) {
390 if (any(*cp
, metanet
))
399 * Map all of the aliased users in the invoker's mailrc
400 * file and insert them into the list.
401 * Changed after all these months of service to recursively
402 * expand names (2/14/80).
406 usermap(struct name
*names
)
408 register struct name
*newnames
, *np
, *cp
;
409 struct grouphead
*gh
;
414 metoo
= (value("metoo") != NOSTR
);
416 if (np
->n_name
[0] == '\\') {
418 newnames
= put(newnames
, np
);
422 gh
= findgroup(np
->n_name
);
425 newnames
= gexpand(newnames
, gh
, metoo
, np
->n_type
);
427 newnames
= put(newnames
, np
);
434 * Recursively expand a group name. We limit the expansion to some
435 * fixed level to keep things from going haywire.
436 * Direct recursion is not expanded for convenience.
440 gexpand(struct name
*nlist
, struct grouphead
*gh
, int metoo
, int arg_ntype
)
442 short ntype
= (short)arg_ntype
;
444 struct grouphead
*ngh
;
449 if (depth
> MAXEXP
) {
450 printf(gettext("Expanding alias to depth larger than %d\n"),
455 for (gp
= gh
->g_list
; gp
!= NOGE
; gp
= gp
->ge_link
) {
459 if (strcmp(cp
, gh
->g_name
) == 0)
461 if ((ngh
= findgroup(cp
)) != NOGRP
) {
462 nlist
= gexpand(nlist
, ngh
, metoo
, ntype
);
469 * At this point should allow to expand
470 * to self if only person in group
472 if (gp
== gh
->g_list
&& gp
->ge_link
== NOGE
)
474 if (!metoo
&& samebody(myname
, gp
->ge_name
, FALSE
))
477 nlist
= put(nlist
, np
);
484 * Normalize a network name for comparison purposes.
487 norm(register char *user
, register char *ubuf
, int nbangs
)
492 while (*user
++ == '!')
495 if (!strchr(user
, '!')) {
496 snprintf(ubuf
, BUFSIZ
, "%s!%s", host
, user
);
501 cp
= user
+ strlen(user
);
503 while (cp
> user
&& *--cp
!= '!')
505 user
= (cp
> user
) ? ++cp
: cp
;
507 * Now strip off all Internet-type
510 if ((cp
= strchr(user
, '%')) == NOSTR
)
511 cp
= strchr(user
, '@');
514 strncpy(ubuf
, user
, cp
- user
);
515 ubuf
[cp
- user
] = '\0';
525 * Implement allnet options.
528 samebody(register char *user
, register char *addr
, int fuzzy
)
530 char ubuf
[BUFSIZ
], abuf
[BUFSIZ
];
531 char *allnet
= value("allnet");
532 int nbangs
= allnet
? (strcmp(allnet
, "uucp") == 0) ? 2 : 1 : 0;
534 if (fuzzy
&& value("fuzzymatch")) {
537 (void) strlcpy(ubuf
, user
, BUFSIZ
);
538 for (i
= 0; ubuf
[i
]; i
++)
539 ubuf
[i
] = tolower(ubuf
[i
]);
540 (void) strlcpy(abuf
, addr
, BUFSIZ
);
541 for (i
= 0; abuf
[i
]; i
++)
542 abuf
[i
] = tolower(abuf
[i
]);
543 return (strstr(abuf
, ubuf
) != NOSTR
);
545 user
= norm(user
, ubuf
, nbangs
);
546 addr
= norm(addr
, abuf
, nbangs
);
547 return (strcmp(user
, addr
) == 0);
551 * Compute the length of the passed name list and
555 lengthof(struct name
*name
)
557 register struct name
*np
;
560 for (c
= 0, np
= name
; np
!= NIL
; c
++, np
= np
->n_flink
)
566 * Concatenate the two passed name lists, return the result.
570 cat(struct name
*n1
, struct name
*n2
)
572 register struct name
*tail
;
585 * Unpack the name list onto a vector of strings.
586 * Return an error if the name list won't fit.
590 unpack(struct name
*np
)
592 register char **ap
, **top
;
593 register struct name
*n
;
595 int t
, extra
, metoo
, verbose
;
598 if ((t
= lengthof(n
)) == 0)
599 panic("No names to unpack");
602 * Compute the number of extra arguments we will need.
603 * We need at least 2 extra -- one for "mail" and one for
604 * the terminating 0 pointer.
605 * Additional spots may be needed to pass along -r and -f to
614 metoo
= value("metoo") != NOSTR
;
617 verbose
= value("verbose") != NOSTR
;
622 top
= (char **)salloc((t
+ extra
) * sizeof (char *));
625 if (rflag
!= NOSTR
) {
636 snprintf(hbuf
, sizeof (hbuf
), "%d", hflag
);
637 *ap
++ = savestr(hbuf
);
640 if (n
->n_type
& GDEL
) {
652 * See if the user named himself as a destination
653 * for outgoing mail. If so, set the global flag
654 * selfsent so that we avoid removing his mailbox.
658 mechk(struct name
*names
)
660 register struct name
*np
;
662 for (np
= names
; np
!= NIL
; np
= np
->n_flink
)
663 if ((np
->n_type
& GDEL
) == 0 &&
664 samebody(np
->n_name
, myname
, FALSE
)) {
671 * Remove all of the duplicates from the passed name list by
672 * insertion sorting them, then checking for dups.
673 * Return the head of the new list.
677 elide(struct name
*names
)
679 register struct name
*np
, *t
, *newnames
;
689 newnames
->n_flink
= NIL
;
692 while (strcmp(t
->n_name
, np
->n_name
) < 0) {
693 if (t
->n_flink
== NIL
)
699 * If we ran out of t's, put the new entry after
700 * the current value of t.
703 if (strcmp(t
->n_name
, np
->n_name
) < 0) {
713 * Otherwise, put the new entry in front of the
714 * current t. If at the front of the list,
715 * the new guy becomes the new head of the list.
721 t
->n_flink
= newnames
;
722 newnames
->n_blink
= t
;
729 * The normal case -- we are inserting into the
730 * middle of the list.
736 x
->n_blink
= t
->n_blink
;
737 t
->n_blink
->n_flink
= x
;
742 * Now the list headed up by new is sorted.
743 * Go through it and remove duplicates.
744 * Remember the best "type" among all the
745 * duplicates of a name.
754 while (t
->n_flink
!= NIL
&&
755 strcmp(np
->n_name
, t
->n_flink
->n_name
) == 0) {
757 /* "To" before "Cc" before "Bcc" */
758 if (t
->n_type
< type
)
761 if (t
== np
|| t
== NIL
) {
767 * Now t points to the last entry with the same name
768 * as np. Make np point beyond t.
771 np
->n_flink
= t
->n_flink
;
772 if (t
->n_flink
!= NIL
)
773 t
->n_flink
->n_blink
= np
;
781 * Put another node onto a list of names and return
786 put(struct name
*list
, struct name
*node
)
788 node
->n_flink
= list
;
791 list
->n_blink
= node
;
797 * Delete the given name from a namelist.
800 delname(register struct name
*np
, char name
[])
802 register struct name
*p
;
804 for (p
= np
; p
!= NIL
; p
= p
->n_flink
)
805 if (samebody(name
, p
->n_name
, FALSE
)) {
806 if (p
->n_blink
== NIL
) {
807 if (p
->n_flink
!= NIL
)
808 p
->n_flink
->n_blink
= NIL
;
812 if (p
->n_flink
== NIL
) {
813 if (p
->n_blink
!= NIL
)
814 p
->n_blink
->n_flink
= NIL
;
817 p
->n_blink
->n_flink
= p
->n_flink
;
818 p
->n_flink
->n_blink
= p
->n_blink
;
824 * Call the given routine on each element of the name
825 * list, replacing said value if need be.
829 mapf(register struct name
*np
, char *from
)
831 register struct name
*p
;
833 if (debug
) fprintf(stderr
, "mapf %lx, %s\n", (long)np
, from
);
834 for (p
= np
; p
!= NIL
; p
= p
->n_flink
)
835 if ((p
->n_type
& GDEL
) == 0) {
836 p
->n_name
= netmap(p
->n_name
, from
);
837 p
->n_full
= splice(p
->n_name
, p
->n_full
);
839 if (debug
) fprintf(stderr
, "mapf %s done\n", from
);