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
41 #pragma ident "%Z%%M% %I% %E% SMI"
47 * mailx -- a modified version of a University of California at Berkeley
50 * Still more user commands.
53 static int bangexp(char *str
);
54 static int diction(const void *a
, const void *b
);
55 static char *getfilename(char *name
, int *aedit
);
56 static int resp1(int *msgvec
, int useauthor
);
57 static int Resp1(int *msgvec
, int useauthor
);
58 static char *reedit(char *subj
);
59 static int shell1(char *str
);
60 static void sort(char **list
);
61 static char *replyto(struct message
*mp
, char **f
);
62 static int reply2sender(void);
64 static char prevfile
[PATHSIZE
];
65 static char origprevfile
[PATHSIZE
];
66 static char lastbang
[BUFSIZ
];
69 * Process a shell escape by saving signals, ignoring signals,
90 nstrcpy(cmd
, sizeof (cmd
), str
);
93 if ((Shell
= value("SHELL")) == NOSTR
|| *Shell
=='\0')
95 for (t
= SIGINT
; t
<= SIGQUIT
; t
++)
96 sig
[t
-SIGINT
] = sigset(t
, SIG_IGN
);
101 for (t
= SIGINT
; t
<= SIGQUIT
; t
++)
102 if (sig
[t
-SIGINT
] != SIG_IGN
)
104 execlp(Shell
, Shell
, "-c", cmd
, (char *)0);
112 for (t
= SIGINT
; t
<= SIGQUIT
; t
++)
113 sigset(t
, sig
[t
-SIGINT
]);
118 * Fork an interactive shell.
134 if ((Shell
= value("SHELL")) == NOSTR
|| *Shell
=='\0')
136 for (t
= SIGINT
; t
<= SIGQUIT
; t
++)
137 sig
[t
-SIGINT
] = sigset(t
, SIG_IGN
);
142 for (t
= SIGINT
; t
<= SIGQUIT
; t
++)
143 if (sig
[t
-SIGINT
] != SIG_IGN
)
145 execlp(Shell
, Shell
, (char *)0);
153 for (t
= SIGINT
; t
<= SIGQUIT
; t
++)
154 sigset(t
, sig
[t
-SIGINT
]);
160 * Expand the shell escape by expanding unescaped !'s into the
161 * last issued command where possible.
166 char bangbuf
[BUFSIZ
];
167 register char *cp
, *cp2
;
170 int bangit
= (value("bang")!=NOSTR
);
176 if (*cp
=='!' && bangit
) {
177 if (n
< (int)strlen(lastbang
)) {
179 printf(gettext("Command buffer overflow\n"));
183 strcpy(cp2
, lastbang
);
184 cp2
+= strlen(lastbang
);
185 n
-= strlen(lastbang
);
189 if (*cp
== '\\' && cp
[1] == '!') {
202 printf("!%s\n", bangbuf
);
205 nstrcpy(str
, BUFSIZ
, bangbuf
);
206 nstrcpy(lastbang
, sizeof (lastbang
), bangbuf
);
211 * Print out a nice help message from some file or another.
220 if ((f
= fopen(HELPFILE
, "r")) == NULL
) {
221 printf(gettext("No help just now.\n"));
224 while ((c
= getc(f
)) != EOF
)
231 * Change user's working directory.
238 char cwd
[PATHSIZE
], file
[PATHSIZE
];
239 static char efile
[PATHSIZE
];
241 for (cp
= str
; *cp
== ' '; cp
++)
246 if ((cp
= expand(cp
)) == NOSTR
)
248 if (editfile
!= NOSTR
&& (*editfile
!= '/' || mailname
[0] != '/')) {
249 if (getcwd(cwd
, (int)sizeof (cwd
)) == 0) {
251 gettext("Can't get current directory: %s\n"), cwd
);
260 * Convert previously relative names to absolute names.
262 if (editfile
!= NOSTR
&& *editfile
!= '/') {
263 snprintf(file
, sizeof (file
), "%s/%s", cwd
, editfile
);
264 nstrcpy(efile
, sizeof (efile
), file
);
267 if (mailname
[0] != '/') {
268 snprintf(file
, sizeof (file
), "%s/%s", cwd
, mailname
);
269 nstrcpy(mailname
, PATHSIZE
, file
);
275 * Two versions of reply. Reply to all names in message or reply
276 * to only sender of message, depending on setting of "replyall".
283 return(resp1(msgvec
, 0));
285 return(Resp1(msgvec
, 0));
289 followup(int *msgvec
)
292 return(resp1(msgvec
, 1));
294 return(Resp1(msgvec
, 1));
298 replyall(int *msgvec
)
300 return(resp1(msgvec
, 0));
304 resp1(int *msgvec
, int useauthor
)
307 char *cp
, *buf
, *rcv
, *skin_rcv
, *reply2
, **ap
, *returnaddr
;
310 char mylocalname
[BUFSIZ
], mydomname
[BUFSIZ
];
312 if (msgvec
[1] != 0) {
314 "Sorry, can't reply to multiple messages at once\n"));
317 snprintf(mydomname
, sizeof (mydomname
), "%s@%s", myname
, domain
);
318 snprintf(mylocalname
, sizeof (mylocalname
), "%s@%s", myname
, host
);
319 returnaddr
= value("returnaddr");
321 mp
= &message
[msgvec
[0] - 1];
323 reply2
= replyto(mp
, &rcv
);
324 cp
= skin(hfield("to", mp
, addto
));
326 buf
= (char *)salloc(strlen(reply2
) + strlen(cp
) + 2);
332 np
= elide(extract(buf
, GTO
));
334 /* rcv = netrename(rcv); */
337 * Delete my name from the reply list,
338 * and with it, all my alternate names.
340 skin_rcv
= skin(rcv
);
342 np
= delname(np
, myname
);
343 np
= delname(np
, mylocalname
);
344 np
= delname(np
, mydomname
);
345 if (returnaddr
&& *returnaddr
)
346 np
= delname(np
, returnaddr
);
348 for (ap
= altnames
; *ap
; ap
++)
349 np
= delname(np
, *ap
);
359 head
.h_subject
= hfield("subject", mp
, addone
);
360 if (head
.h_subject
== NOSTR
)
361 head
.h_subject
= hfield("subj", mp
, addone
);
362 head
.h_subject
= reedit(head
.h_subject
);
364 cp
= skin(hfield("cc", mp
, addto
));
366 np
= elide(extract(cp
, GCC
));
368 np
= delname(np
, myname
);
369 np
= delname(np
, mylocalname
);
370 np
= delname(np
, mydomname
);
371 if (returnaddr
&& *returnaddr
)
372 np
= delname(np
, returnaddr
);
373 np
= delname(np
, skin_rcv
);
375 for (ap
= altnames
; *ap
; ap
++)
376 np
= delname(np
, *ap
);
377 head
.h_cc
= detract(np
, 0);
380 head
.h_defopt
= NOSTR
;
381 head
.h_others
= NOSTRPTR
;
382 mail1(&head
, useauthor
, useauthor
? rcv
: NOSTR
);
387 getrecf(char *buf
, char *recfile
, int useauthor
, int sz_recfile
)
389 register char *bp
, *cp
;
390 register char *recf
= recfile
;
391 register int folderize
;
394 folderize
= (value("outfolder")!=NOSTR
&& getfold(fldr
) == 0);
399 if (debug
) fprintf(stderr
, "buf='%s'\n", buf
);
400 for (bp
=skin(buf
), cp
=recf
; *bp
&& !any(*bp
, ", "); bp
++) {
406 if (cp
>= &recfile
[sz_recfile
- 1]) {
407 printf(gettext("File name buffer overflow\n"));
414 /* now strip off any Internet host names */
415 if ((cp
= strchr(recf
, '%')) == NOSTR
)
416 cp
= strchr(recf
, '@');
420 if (cp
= value("record")) {
422 if (folderize
&& *cp
!='+' && *cp
!='/'
423 && *safeexpand(cp
)!='/') {
427 nstrcpy(recf
, sz
, cp
);
431 if (debug
) fprintf(stderr
, "recfile='%s'\n", recfile
);
435 * Modify the subject we are replying to to begin with Re: if
436 * it does not already.
443 register char *newsubj
;
447 strncpy(sbuf
, subj
, 3);
449 if (icequal(sbuf
, "re:"))
451 newsubj
= (char *)salloc((unsigned)(strlen(subj
) + 5));
452 sprintf(newsubj
, "Re: %s", subj
);
457 * Preserve the named messages, so that they will be sent
458 * back to the system mailbox.
462 preserve(int *msgvec
)
464 register struct message
*mp
;
465 register int *ip
, mesg
;
468 printf(gettext("Cannot \"preserve\" in edit mode\n"));
471 for (ip
= msgvec
; *ip
!= NULL
; ip
++) {
473 mp
= &message
[mesg
-1];
474 mp
->m_flag
|= MPRESERVE
;
482 * Mark all given messages as unread.
489 for (ip
= msgvec
; *ip
!= NULL
; ip
++) {
490 dot
= &message
[*ip
-1];
491 dot
->m_flag
&= ~(MREAD
|MTOUCH
);
492 dot
->m_flag
|= MSTATUS
;
498 * Print the size of each message.
504 register struct message
*mp
;
505 register int *ip
, mesg
;
507 for (ip
= msgvec
; *ip
!= NULL
; ip
++) {
509 mp
= &message
[mesg
-1];
511 printf("%d: %ld\n", mesg
, mp
->m_size
);
517 * Quit quickly. If we are sourcing, just pop the input level
518 * by returning an error.
527 close(creat(Tflag
, TEMPPERM
));
530 exit(e
? e
: rpterr
);
532 return (0); /* shut up lint and CC */
536 * Set or display a variable value. Syntax is similar to that
543 register struct var
*vp
;
544 register char *cp
, *cp2
;
545 char varbuf
[BUFSIZ
], **ap
, **p
;
548 if (argcount(arglist
) == 0) {
549 for (h
= 0, s
= 1; h
< HSHSIZE
; h
++)
550 for (vp
= variables
[h
]; vp
!= NOVAR
; vp
= vp
->v_link
)
552 ap
= (char **) salloc(s
* sizeof *ap
);
553 for (h
= 0, p
= ap
; h
< HSHSIZE
; h
++)
554 for (vp
= variables
[h
]; vp
!= NOVAR
; vp
= vp
->v_link
)
558 for (p
= ap
; *p
!= NOSTR
; p
++)
559 if (((cp
= value(*p
)) != 0) && *cp
)
560 printf("%s=\"%s\"\n", *p
, cp
);
566 for (ap
= arglist
; *ap
!= NOSTR
; ap
++) {
569 while (*cp
!= '=' && *cp
!= '\0')
576 if (equal(varbuf
, "")) {
577 printf(gettext("Non-null variable name required\n"));
587 * Unset a bunch of variable values.
591 unset(char **arglist
)
597 for (ap
= arglist
; *ap
!= NOSTR
; ap
++)
598 errs
+= deassign(*ap
);
603 * Add users to a group.
609 register struct grouphead
*gh
;
610 register struct mgroup
*gp
;
613 char **ap
, *gname
, **p
;
615 if (argcount(argv
) == 0) {
616 for (h
= 0, s
= 1; h
< HSHSIZE
; h
++)
617 for (gh
= groups
[h
]; gh
!= NOGRP
; gh
= gh
->g_link
)
619 ap
= (char **) salloc(s
* sizeof *ap
);
620 for (h
= 0, p
= ap
; h
< HSHSIZE
; h
++)
621 for (gh
= groups
[h
]; gh
!= NOGRP
; gh
= gh
->g_link
)
625 for (p
= ap
; *p
!= NOSTR
; p
++)
629 if (argcount(argv
) == 1) {
635 if ((gh
= findgroup(gname
)) == NOGRP
) {
636 if ((gh
= (struct grouphead
*)
637 calloc(sizeof (*gh
), 1)) == NULL
) {
638 panic("Failed to allocate memory for group");
640 gh
->g_name
= vcopy(gname
);
642 gh
->g_link
= groups
[h
];
647 * Insert names from the command list into the group.
648 * Who cares if there are duplicates? They get tossed
652 for (ap
= argv
+1; *ap
!= NOSTR
; ap
++) {
653 if ((gp
= (struct mgroup
*)
654 calloc(sizeof (*gp
), 1)) == NULL
) {
655 panic("Failed to allocate memory for group");
657 gp
->ge_name
= vcopy(*ap
);
658 gp
->ge_link
= gh
->g_list
;
665 * Remove users from a group.
671 register struct grouphead
*gh
, **ghp
;
672 register struct mgroup
*gp
, *gpnext
;
676 if (argcount(argv
) == 0) {
677 printf("Must specify alias or group to remove\n");
682 * Remove names on the command list from the group list.
685 for (ap
= argv
; *ap
!= NOSTR
; ap
++) {
688 for (ghp
= &groups
[h
]; *ghp
!= NOGRP
; ghp
= &((*ghp
)->g_link
)) {
690 if (equal(gh
->g_name
, gname
)) {
691 /* remove from list */
693 /* free each member of gorup */
694 for (gp
= gh
->g_list
; gp
!= NOGE
; gp
= gpnext
) {
695 gpnext
= gp
->ge_link
;
709 * Sort the passed string vecotor into ascending dictionary
718 for (ap
= list
; *ap
!= NOSTR
; ap
++)
722 qsort((char *) list
, (unsigned) (ap
-list
), sizeof *list
, diction
);
726 * Do a dictionary order comparison of the arguments from
730 diction(const void *a
, const void *b
)
732 return(strcmp(*(char **)a
, *(char **)b
));
736 * The do nothing command for comments.
751 * Print out the current edit file, if we are editing.
752 * Otherwise, print the name of the person who's mail
761 if (argv
[0] == NOSTR
) {
762 mdot
= newfileinfo(1);
763 dot
= &message
[mdot
- 1];
768 * Acker's! Must switch to the new file.
769 * We use a funny interpretation --
770 * # -- gets the previous file
771 * % -- gets the invoker's post office box
772 * %user -- gets someone else's post office box
773 * & -- gets invoker's mbox file
774 * string -- reads the given file
777 cp
= getfilename(argv
[0], &editing
);
780 if (setfile(cp
, editing
)) {
781 nstrcpy(origname
, PATHSIZE
, origprevfile
);
784 mdot
= newfileinfo(1);
785 dot
= &message
[mdot
- 1];
790 * Evaluate the string given as a new mailbox name.
791 * Ultimately, we want this to support a number of meta characters.
793 * % -- for my system mail box
794 * %user -- for user's system mail box
795 * # -- for previous file
796 * & -- get's invoker's mbox file
797 * file name -- for any other file
801 getfilename(char *name
, int *aedit
)
804 char savename
[BUFSIZ
];
805 char oldmailname
[BUFSIZ
];
809 * Assume we will be in "edit file" mode, until
816 nstrcpy(prevfile
, sizeof (prevfile
), editfile
);
817 nstrcpy(origprevfile
, sizeof (origprevfile
), origname
);
819 nstrcpy(oldmailname
, sizeof (oldmailname
), mailname
);
821 cp
= savestr(mailname
);
822 nstrcpy(origname
, PATHSIZE
, cp
);
823 nstrcpy(mailname
, PATHSIZE
, oldmailname
);
826 nstrcpy(oldmailname
, sizeof (oldmailname
), mailname
);
828 cp
= savestr(mailname
);
829 nstrcpy(mailname
, PATHSIZE
, oldmailname
);
830 nstrcpy(origname
, PATHSIZE
, cp
);
836 if (prevfile
[0] == 0) {
837 printf(gettext("No previous file\n"));
840 cp
= savestr(prevfile
);
841 nstrcpy(prevfile
, sizeof (prevfile
), editfile
);
842 nstrcpy(tmp
, sizeof (tmp
), origname
);
843 nstrcpy(origname
, PATHSIZE
, origprevfile
);
844 nstrcpy(origprevfile
, sizeof (origprevfile
), tmp
);
848 nstrcpy(prevfile
, sizeof (prevfile
), editfile
);
849 nstrcpy(origprevfile
, sizeof (origprevfile
), origname
);
852 nstrcpy(origname
, PATHSIZE
, cp
);
855 /* Fall into . . . */
859 nstrcpy(prevfile
, sizeof (prevfile
), editfile
);
860 nstrcpy(origprevfile
, sizeof (origprevfile
), origname
);
861 cp
= safeexpand(name
);
862 nstrcpy(origname
, PATHSIZE
, cp
);
864 name
= getcwd(NOSTR
, PATHSIZE
);
865 nstrcat(name
, PATHSIZE
, "/");
866 nstrcat(name
, PATHSIZE
, cp
);
874 * Expand file names like echo
878 echo(register char **argv
)
883 while (*argv
!= NOSTR
) {
885 if ((cp
= expand(cp
)) != NOSTR
) {
898 * Reply to a series of messages by simply mailing to the senders
899 * and not messing around with the To: and Cc: lists as in normal
907 return(Resp1(msgvec
, 0));
909 return(resp1(msgvec
, 0));
913 Followup(int *msgvec
)
916 return(Resp1(msgvec
, 1));
918 return(resp1(msgvec
, 1));
922 replysender(int *msgvec
)
924 return(Resp1(msgvec
, 0));
928 Resp1(int *msgvec
, int useauthor
)
933 register char *cp
, *cp2
, *subject
;
935 for (s
= 0, ap
= msgvec
; *ap
!= 0; ap
++) {
936 mp
= &message
[*ap
- 1];
938 cp
= replyto(mp
, NOSTRPTR
);
943 cp
= (char *)salloc(s
+ 2);
945 for (ap
= msgvec
; *ap
!= 0; ap
++) {
946 mp
= &message
[*ap
- 1];
947 cp2
= replyto(mp
, NOSTRPTR
);
952 mp
= &message
[msgvec
[0] - 1];
953 subject
= hfield("subject", mp
, addone
);
955 if (subject
== NOSTR
)
956 subject
= hfield("subj", mp
, addone
);
957 head
.h_subject
= reedit(subject
);
958 if (subject
!= NOSTR
)
962 head
.h_defopt
= NOSTR
;
963 head
.h_others
= NOSTRPTR
;
964 mail1(&head
, useauthor
, NOSTR
);
969 * Conditional commands. These allow one to parameterize one's
970 * .mailrc and do some things if sending, others if receiving.
979 printf(gettext("Illegal nested \"if\"\n"));
998 printf(gettext("Unrecognized if-keyword: \"%s\"\n"), cp
);
1005 * Implement 'else'. This is pretty simple -- we just
1006 * flip over the conditional flag.
1015 printf(gettext("\"Else\" without matching \"if\"\n"));
1035 printf(gettext("invalid condition encountered\n"));
1043 * End of if statement. Just set cond back to anything.
1051 printf(gettext("\"Endif\" without matching \"if\"\n"));
1059 * Set the list of alternate names.
1062 alternates(char **namelist
)
1065 register char **ap
, **ap2
, *cp
;
1067 c
= argcount(namelist
) + 1;
1071 for (ap
= altnames
; *ap
; ap
++)
1077 free((char *)altnames
);
1078 if ((altnames
= (char **)
1079 calloc((unsigned)c
, sizeof (char *))) == NULL
)
1080 panic("Failed to allocate memory");
1081 for (ap
= namelist
, ap2
= altnames
; *ap
; ap
++, ap2
++) {
1083 calloc((unsigned)strlen(*ap
) + 1, sizeof (char))) == NULL
)
1084 panic("Failed to allocate memory");
1093 * Figure out who to reply to.
1094 * Return the real sender in *f.
1097 replyto(struct message
*mp
, char **f
)
1101 if ((rf
= skin(hfield("from", mp
, addto
)))==NOSTR
)
1102 rf
= skin(addto(NOSTR
, nameof(mp
)));
1103 if ((r
= skin(hfield("reply-to", mp
, addto
)))==NOSTR
)
1111 * reply2sender - determine whether a "reply" command should reply to the
1112 * sender of the messages, or to all the recipients of the
1115 * With the advent of POSIX.2 compliance, this has become
1116 * a bit more complicated, and so should be done in one
1117 * place, for all to use.
1123 register int rep
= (value("replyall") != NOSTR
);
1124 register int flp
= (value("flipr") != NOSTR
);
1126 return((rep
&& !flp
)|| (!rep
&& flp
));