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 2002 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
45 * mailx -- a modified version of a University of California at Berkeley
48 * Still more user commands.
51 static int bangexp(char *str
);
52 static int diction(const void *a
, const void *b
);
53 static char *getfilename(char *name
, int *aedit
);
54 static int resp1(int *msgvec
, int useauthor
);
55 static int Resp1(int *msgvec
, int useauthor
);
56 static char *reedit(char *subj
);
57 static int shell1(char *str
);
58 static void sort(char **list
);
59 static char *replyto(struct message
*mp
, char **f
);
60 static int reply2sender(void);
62 static char prevfile
[PATHSIZE
];
63 static char origprevfile
[PATHSIZE
];
64 static char lastbang
[BUFSIZ
];
67 * Process a shell escape by saving signals, ignoring signals,
88 nstrcpy(cmd
, sizeof (cmd
), str
);
91 if ((Shell
= value("SHELL")) == NOSTR
|| *Shell
=='\0')
93 for (t
= SIGINT
; t
<= SIGQUIT
; t
++)
94 sig
[t
-SIGINT
] = sigset(t
, SIG_IGN
);
99 for (t
= SIGINT
; t
<= SIGQUIT
; t
++)
100 if (sig
[t
-SIGINT
] != SIG_IGN
)
102 execlp(Shell
, Shell
, "-c", cmd
, (char *)0);
110 for (t
= SIGINT
; t
<= SIGQUIT
; t
++)
111 sigset(t
, sig
[t
-SIGINT
]);
116 * Fork an interactive shell.
132 if ((Shell
= value("SHELL")) == NOSTR
|| *Shell
=='\0')
134 for (t
= SIGINT
; t
<= SIGQUIT
; t
++)
135 sig
[t
-SIGINT
] = sigset(t
, SIG_IGN
);
140 for (t
= SIGINT
; t
<= SIGQUIT
; t
++)
141 if (sig
[t
-SIGINT
] != SIG_IGN
)
143 execlp(Shell
, Shell
, (char *)0);
151 for (t
= SIGINT
; t
<= SIGQUIT
; t
++)
152 sigset(t
, sig
[t
-SIGINT
]);
158 * Expand the shell escape by expanding unescaped !'s into the
159 * last issued command where possible.
164 char bangbuf
[BUFSIZ
];
165 register char *cp
, *cp2
;
168 int bangit
= (value("bang")!=NOSTR
);
174 if (*cp
=='!' && bangit
) {
175 if (n
< (int)strlen(lastbang
)) {
177 printf(gettext("Command buffer overflow\n"));
181 strcpy(cp2
, lastbang
);
182 cp2
+= strlen(lastbang
);
183 n
-= strlen(lastbang
);
187 if (*cp
== '\\' && cp
[1] == '!') {
200 printf("!%s\n", bangbuf
);
203 nstrcpy(str
, BUFSIZ
, bangbuf
);
204 nstrcpy(lastbang
, sizeof (lastbang
), bangbuf
);
209 * Print out a nice help message from some file or another.
218 if ((f
= fopen(HELPFILE
, "r")) == NULL
) {
219 printf(gettext("No help just now.\n"));
222 while ((c
= getc(f
)) != EOF
)
229 * Change user's working directory.
236 char cwd
[PATHSIZE
], file
[PATHSIZE
];
237 static char efile
[PATHSIZE
];
239 for (cp
= str
; *cp
== ' '; cp
++)
244 if ((cp
= expand(cp
)) == NOSTR
)
246 if (editfile
!= NOSTR
&& (*editfile
!= '/' || mailname
[0] != '/')) {
247 if (getcwd(cwd
, (int)sizeof (cwd
)) == 0) {
249 gettext("Can't get current directory: %s\n"), cwd
);
258 * Convert previously relative names to absolute names.
260 if (editfile
!= NOSTR
&& *editfile
!= '/') {
261 snprintf(file
, sizeof (file
), "%s/%s", cwd
, editfile
);
262 nstrcpy(efile
, sizeof (efile
), file
);
265 if (mailname
[0] != '/') {
266 snprintf(file
, sizeof (file
), "%s/%s", cwd
, mailname
);
267 nstrcpy(mailname
, PATHSIZE
, file
);
273 * Two versions of reply. Reply to all names in message or reply
274 * to only sender of message, depending on setting of "replyall".
281 return(resp1(msgvec
, 0));
283 return(Resp1(msgvec
, 0));
287 followup(int *msgvec
)
290 return(resp1(msgvec
, 1));
292 return(Resp1(msgvec
, 1));
296 replyall(int *msgvec
)
298 return(resp1(msgvec
, 0));
302 resp1(int *msgvec
, int useauthor
)
305 char *cp
, *buf
, *rcv
, *skin_rcv
, *reply2
, **ap
, *returnaddr
;
308 char mylocalname
[BUFSIZ
], mydomname
[BUFSIZ
];
310 if (msgvec
[1] != 0) {
312 "Sorry, can't reply to multiple messages at once\n"));
315 snprintf(mydomname
, sizeof (mydomname
), "%s@%s", myname
, domain
);
316 snprintf(mylocalname
, sizeof (mylocalname
), "%s@%s", myname
, host
);
317 returnaddr
= value("returnaddr");
319 mp
= &message
[msgvec
[0] - 1];
321 reply2
= replyto(mp
, &rcv
);
322 cp
= skin(hfield("to", mp
, addto
));
324 buf
= (char *)salloc(strlen(reply2
) + strlen(cp
) + 2);
330 np
= elide(extract(buf
, GTO
));
332 /* rcv = netrename(rcv); */
335 * Delete my name from the reply list,
336 * and with it, all my alternate names.
338 skin_rcv
= skin(rcv
);
340 np
= delname(np
, myname
);
341 np
= delname(np
, mylocalname
);
342 np
= delname(np
, mydomname
);
343 if (returnaddr
&& *returnaddr
)
344 np
= delname(np
, returnaddr
);
346 for (ap
= altnames
; *ap
; ap
++)
347 np
= delname(np
, *ap
);
357 head
.h_subject
= hfield("subject", mp
, addone
);
358 if (head
.h_subject
== NOSTR
)
359 head
.h_subject
= hfield("subj", mp
, addone
);
360 head
.h_subject
= reedit(head
.h_subject
);
362 cp
= skin(hfield("cc", mp
, addto
));
364 np
= elide(extract(cp
, GCC
));
366 np
= delname(np
, myname
);
367 np
= delname(np
, mylocalname
);
368 np
= delname(np
, mydomname
);
369 if (returnaddr
&& *returnaddr
)
370 np
= delname(np
, returnaddr
);
371 np
= delname(np
, skin_rcv
);
373 for (ap
= altnames
; *ap
; ap
++)
374 np
= delname(np
, *ap
);
375 head
.h_cc
= detract(np
, 0);
378 head
.h_defopt
= NOSTR
;
379 head
.h_others
= NOSTRPTR
;
380 mail1(&head
, useauthor
, useauthor
? rcv
: NOSTR
);
385 getrecf(char *buf
, char *recfile
, int useauthor
, int sz_recfile
)
387 register char *bp
, *cp
;
388 register char *recf
= recfile
;
389 register int folderize
;
392 folderize
= (value("outfolder")!=NOSTR
&& getfold(fldr
) == 0);
397 if (debug
) fprintf(stderr
, "buf='%s'\n", buf
);
398 for (bp
=skin(buf
), cp
=recf
; *bp
&& !any(*bp
, ", "); bp
++) {
404 if (cp
>= &recfile
[sz_recfile
- 1]) {
405 printf(gettext("File name buffer overflow\n"));
412 /* now strip off any Internet host names */
413 if ((cp
= strchr(recf
, '%')) == NOSTR
)
414 cp
= strchr(recf
, '@');
418 if (cp
= value("record")) {
420 if (folderize
&& *cp
!='+' && *cp
!='/'
421 && *safeexpand(cp
)!='/') {
425 nstrcpy(recf
, sz
, cp
);
429 if (debug
) fprintf(stderr
, "recfile='%s'\n", recfile
);
433 * Modify the subject we are replying to to begin with Re: if
434 * it does not already.
441 register char *newsubj
;
445 strncpy(sbuf
, subj
, 3);
447 if (icequal(sbuf
, "re:"))
449 newsubj
= (char *)salloc((unsigned)(strlen(subj
) + 5));
450 sprintf(newsubj
, "Re: %s", subj
);
455 * Preserve the named messages, so that they will be sent
456 * back to the system mailbox.
460 preserve(int *msgvec
)
462 register struct message
*mp
;
463 register int *ip
, mesg
;
466 printf(gettext("Cannot \"preserve\" in edit mode\n"));
469 for (ip
= msgvec
; *ip
!= 0; ip
++) {
471 mp
= &message
[mesg
-1];
472 mp
->m_flag
|= MPRESERVE
;
480 * Mark all given messages as unread.
487 for (ip
= msgvec
; *ip
!= 0; ip
++) {
488 dot
= &message
[*ip
-1];
489 dot
->m_flag
&= ~(MREAD
|MTOUCH
);
490 dot
->m_flag
|= MSTATUS
;
496 * Print the size of each message.
502 register struct message
*mp
;
503 register int *ip
, mesg
;
505 for (ip
= msgvec
; *ip
!= 0; ip
++) {
507 mp
= &message
[mesg
-1];
509 printf("%d: %ld\n", mesg
, mp
->m_size
);
515 * Quit quickly. If we are sourcing, just pop the input level
516 * by returning an error.
525 close(creat(Tflag
, TEMPPERM
));
528 exit(e
? e
: rpterr
);
530 return (0); /* shut up lint and CC */
534 * Set or display a variable value. Syntax is similar to that
541 register struct var
*vp
;
542 register char *cp
, *cp2
;
543 char varbuf
[BUFSIZ
], **ap
, **p
;
546 if (argcount(arglist
) == 0) {
547 for (h
= 0, s
= 1; h
< HSHSIZE
; h
++)
548 for (vp
= variables
[h
]; vp
!= NOVAR
; vp
= vp
->v_link
)
550 ap
= (char **) salloc(s
* sizeof *ap
);
551 for (h
= 0, p
= ap
; h
< HSHSIZE
; h
++)
552 for (vp
= variables
[h
]; vp
!= NOVAR
; vp
= vp
->v_link
)
556 for (p
= ap
; *p
!= NOSTR
; p
++)
557 if (((cp
= value(*p
)) != 0) && *cp
)
558 printf("%s=\"%s\"\n", *p
, cp
);
564 for (ap
= arglist
; *ap
!= NOSTR
; ap
++) {
567 while (*cp
!= '=' && *cp
!= '\0')
574 if (equal(varbuf
, "")) {
575 printf(gettext("Non-null variable name required\n"));
585 * Unset a bunch of variable values.
589 unset(char **arglist
)
595 for (ap
= arglist
; *ap
!= NOSTR
; ap
++)
596 errs
+= deassign(*ap
);
601 * Add users to a group.
607 register struct grouphead
*gh
;
608 register struct mgroup
*gp
;
611 char **ap
, *gname
, **p
;
613 if (argcount(argv
) == 0) {
614 for (h
= 0, s
= 1; h
< HSHSIZE
; h
++)
615 for (gh
= groups
[h
]; gh
!= NOGRP
; gh
= gh
->g_link
)
617 ap
= (char **) salloc(s
* sizeof *ap
);
618 for (h
= 0, p
= ap
; h
< HSHSIZE
; h
++)
619 for (gh
= groups
[h
]; gh
!= NOGRP
; gh
= gh
->g_link
)
623 for (p
= ap
; *p
!= NOSTR
; p
++)
627 if (argcount(argv
) == 1) {
633 if ((gh
= findgroup(gname
)) == NOGRP
) {
634 if ((gh
= (struct grouphead
*)
635 calloc(sizeof (*gh
), 1)) == NULL
) {
636 panic("Failed to allocate memory for group");
638 gh
->g_name
= vcopy(gname
);
640 gh
->g_link
= groups
[h
];
645 * Insert names from the command list into the group.
646 * Who cares if there are duplicates? They get tossed
650 for (ap
= argv
+1; *ap
!= NOSTR
; ap
++) {
651 if ((gp
= (struct mgroup
*)
652 calloc(sizeof (*gp
), 1)) == NULL
) {
653 panic("Failed to allocate memory for group");
655 gp
->ge_name
= vcopy(*ap
);
656 gp
->ge_link
= gh
->g_list
;
663 * Remove users from a group.
669 register struct grouphead
*gh
, **ghp
;
670 register struct mgroup
*gp
, *gpnext
;
674 if (argcount(argv
) == 0) {
675 printf("Must specify alias or group to remove\n");
680 * Remove names on the command list from the group list.
683 for (ap
= argv
; *ap
!= NOSTR
; ap
++) {
686 for (ghp
= &groups
[h
]; *ghp
!= NOGRP
; ghp
= &((*ghp
)->g_link
)) {
688 if (equal(gh
->g_name
, gname
)) {
689 /* remove from list */
691 /* free each member of gorup */
692 for (gp
= gh
->g_list
; gp
!= NOGE
; gp
= gpnext
) {
693 gpnext
= gp
->ge_link
;
707 * Sort the passed string vecotor into ascending dictionary
716 for (ap
= list
; *ap
!= NOSTR
; ap
++)
720 qsort((char *) list
, (unsigned) (ap
-list
), sizeof *list
, diction
);
724 * Do a dictionary order comparison of the arguments from
728 diction(const void *a
, const void *b
)
730 return(strcmp(*(char **)a
, *(char **)b
));
734 * The do nothing command for comments.
749 * Print out the current edit file, if we are editing.
750 * Otherwise, print the name of the person who's mail
759 if (argv
[0] == NOSTR
) {
760 mdot
= newfileinfo(1);
761 dot
= &message
[mdot
- 1];
766 * Acker's! Must switch to the new file.
767 * We use a funny interpretation --
768 * # -- gets the previous file
769 * % -- gets the invoker's post office box
770 * %user -- gets someone else's post office box
771 * & -- gets invoker's mbox file
772 * string -- reads the given file
775 cp
= getfilename(argv
[0], &editing
);
778 if (setfile(cp
, editing
)) {
779 nstrcpy(origname
, PATHSIZE
, origprevfile
);
782 mdot
= newfileinfo(1);
783 dot
= &message
[mdot
- 1];
788 * Evaluate the string given as a new mailbox name.
789 * Ultimately, we want this to support a number of meta characters.
791 * % -- for my system mail box
792 * %user -- for user's system mail box
793 * # -- for previous file
794 * & -- get's invoker's mbox file
795 * file name -- for any other file
799 getfilename(char *name
, int *aedit
)
802 char savename
[BUFSIZ
];
803 char oldmailname
[BUFSIZ
];
807 * Assume we will be in "edit file" mode, until
814 nstrcpy(prevfile
, sizeof (prevfile
), editfile
);
815 nstrcpy(origprevfile
, sizeof (origprevfile
), origname
);
817 nstrcpy(oldmailname
, sizeof (oldmailname
), mailname
);
819 cp
= savestr(mailname
);
820 nstrcpy(origname
, PATHSIZE
, cp
);
821 nstrcpy(mailname
, PATHSIZE
, oldmailname
);
824 nstrcpy(oldmailname
, sizeof (oldmailname
), mailname
);
826 cp
= savestr(mailname
);
827 nstrcpy(mailname
, PATHSIZE
, oldmailname
);
828 nstrcpy(origname
, PATHSIZE
, cp
);
834 if (prevfile
[0] == 0) {
835 printf(gettext("No previous file\n"));
838 cp
= savestr(prevfile
);
839 nstrcpy(prevfile
, sizeof (prevfile
), editfile
);
840 nstrcpy(tmp
, sizeof (tmp
), origname
);
841 nstrcpy(origname
, PATHSIZE
, origprevfile
);
842 nstrcpy(origprevfile
, sizeof (origprevfile
), tmp
);
846 nstrcpy(prevfile
, sizeof (prevfile
), editfile
);
847 nstrcpy(origprevfile
, sizeof (origprevfile
), origname
);
850 nstrcpy(origname
, PATHSIZE
, cp
);
853 /* Fall into . . . */
857 nstrcpy(prevfile
, sizeof (prevfile
), editfile
);
858 nstrcpy(origprevfile
, sizeof (origprevfile
), origname
);
859 cp
= safeexpand(name
);
860 nstrcpy(origname
, PATHSIZE
, cp
);
862 name
= getcwd(NOSTR
, PATHSIZE
);
863 nstrcat(name
, PATHSIZE
, "/");
864 nstrcat(name
, PATHSIZE
, cp
);
872 * Expand file names like echo
876 echo(register char **argv
)
881 while (*argv
!= NOSTR
) {
883 if ((cp
= expand(cp
)) != NOSTR
) {
896 * Reply to a series of messages by simply mailing to the senders
897 * and not messing around with the To: and Cc: lists as in normal
905 return(Resp1(msgvec
, 0));
907 return(resp1(msgvec
, 0));
911 Followup(int *msgvec
)
914 return(Resp1(msgvec
, 1));
916 return(resp1(msgvec
, 1));
920 replysender(int *msgvec
)
922 return(Resp1(msgvec
, 0));
926 Resp1(int *msgvec
, int useauthor
)
931 register char *cp
, *cp2
, *subject
;
933 for (s
= 0, ap
= msgvec
; *ap
!= 0; ap
++) {
934 mp
= &message
[*ap
- 1];
936 cp
= replyto(mp
, NOSTRPTR
);
941 cp
= (char *)salloc(s
+ 2);
943 for (ap
= msgvec
; *ap
!= 0; ap
++) {
944 mp
= &message
[*ap
- 1];
945 cp2
= replyto(mp
, NOSTRPTR
);
950 mp
= &message
[msgvec
[0] - 1];
951 subject
= hfield("subject", mp
, addone
);
953 if (subject
== NOSTR
)
954 subject
= hfield("subj", mp
, addone
);
955 head
.h_subject
= reedit(subject
);
956 if (subject
!= NOSTR
)
960 head
.h_defopt
= NOSTR
;
961 head
.h_others
= NOSTRPTR
;
962 mail1(&head
, useauthor
, NOSTR
);
967 * Conditional commands. These allow one to parameterize one's
968 * .mailrc and do some things if sending, others if receiving.
977 printf(gettext("Illegal nested \"if\"\n"));
996 printf(gettext("Unrecognized if-keyword: \"%s\"\n"), cp
);
1003 * Implement 'else'. This is pretty simple -- we just
1004 * flip over the conditional flag.
1013 printf(gettext("\"Else\" without matching \"if\"\n"));
1033 printf(gettext("invalid condition encountered\n"));
1041 * End of if statement. Just set cond back to anything.
1049 printf(gettext("\"Endif\" without matching \"if\"\n"));
1057 * Set the list of alternate names.
1060 alternates(char **namelist
)
1063 register char **ap
, **ap2
, *cp
;
1065 c
= argcount(namelist
) + 1;
1069 for (ap
= altnames
; *ap
; ap
++)
1075 free((char *)altnames
);
1076 if ((altnames
= (char **)
1077 calloc((unsigned)c
, sizeof (char *))) == NULL
)
1078 panic("Failed to allocate memory");
1079 for (ap
= namelist
, ap2
= altnames
; *ap
; ap
++, ap2
++) {
1081 calloc((unsigned)strlen(*ap
) + 1, sizeof (char))) == NULL
)
1082 panic("Failed to allocate memory");
1091 * Figure out who to reply to.
1092 * Return the real sender in *f.
1095 replyto(struct message
*mp
, char **f
)
1099 if ((rf
= skin(hfield("from", mp
, addto
)))==NOSTR
)
1100 rf
= skin(addto(NOSTR
, nameof(mp
)));
1101 if ((r
= skin(hfield("reply-to", mp
, addto
)))==NOSTR
)
1109 * reply2sender - determine whether a "reply" command should reply to the
1110 * sender of the messages, or to all the recipients of the
1113 * With the advent of POSIX.2 compliance, this has become
1114 * a bit more complicated, and so should be done in one
1115 * place, for all to use.
1121 register int rep
= (value("replyall") != NOSTR
);
1122 register int flp
= (value("flipr") != NOSTR
);
1124 return((rep
&& !flp
)|| (!rep
&& flp
));