4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright (c) 2016 by Delphix. All rights reserved.
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 * Lexical processing of commands.
54 static void contin(int);
56 static int isprefix(char *as1
, char *as2
);
57 static const struct cmd
*lex(char word
[]);
58 static int Passeren(void);
59 static void setmsize(int sz
);
62 * Set up editing on the given file name.
63 * If isedit is true, we are considered to be editing the file,
64 * otherwise we are reading our mail which has signficance for
69 setfile(char *name
, int isedit
)
74 static char efile
[PATHSIZE
];
81 if (!isedit
&& issysmbox
)
83 if (stat(name
, &stbuf
) < 0 && errno
== EOVERFLOW
) {
84 fprintf(stderr
, gettext("mailbox %s is too large to"
85 " accept incoming mail\n"), name
);
88 if ((ibuf
= fopen(name
, "r")) == NULL
) {
91 int filethere
= (access(name
, 0) == 0);
94 goto doexit
; /* no mail, return error */
95 if (isedit
|| filethere
)
98 char *f
= strrchr(name
, '/');
100 fprintf(stderr
, gettext("No mail.\n"));
102 fprintf(stderr
, gettext("No mail for %s\n"),
107 fstat(fileno(ibuf
), &stbuf
);
108 if (stbuf
.st_size
== 0L || (stbuf
.st_mode
&S_IFMT
) != S_IFREG
) {
110 goto doexit
; /* no mail, return error */
112 if (stbuf
.st_size
== 0L)
113 fprintf(stderr
, gettext("%s: empty file\n"),
117 gettext("%s: not a regular file\n"), name
);
119 if (strrchr(name
, '/') == NOSTR
)
120 fprintf(stderr
, gettext("No mail.\n"));
122 fprintf(stderr
, gettext("No mail for %s\n"),
123 strrchr(name
, '/') + 1);
129 if (fgets(fortest
, sizeof (fortest
), ibuf
) == NULL
) {
130 perror(gettext("mailx: Unable to read from mail file"));
134 fseek(ibuf
, (long)(BUFSIZ
+1), 0); /* flush input buffer */
136 if (strncmp(fortest
, "Forward to ", 11) == 0) {
138 goto doexit
; /* no mail, return error */
139 fprintf(stderr
, gettext("Your mail is being forwarded to %s"),
146 goto doexit
; /* there is mail, return success */
150 * Looks like all will be well. Must hold signals
151 * while we are reading the new file, else we will ruin
152 * the message[] data structure.
153 * Copy the messages into /tmp and set pointers.
159 * Now that we know we can switch to the new file
160 * it's safe to close out the current file.
162 * If we're switching to the file we are currently
163 * editing, don't allow it to be removed as a side
164 * effect of closing it out.
167 edstop(strcmp(editfile
, name
) == 0);
169 quit(strcmp(editfile
, name
) == 0);
180 if (!isedit
&& issysmbox
&& !Hflag
)
181 readonly
= Passeren() == -1;
183 fstat(fileno(ibuf
), &stbuf
);
184 utimep
->actime
= stbuf
.st_atime
;
185 utimep
->modtime
= stbuf
.st_mtime
;
188 if ((i
= open(name
, O_WRONLY
)) < 0)
194 nstrcpy(efile
, PATHSIZE
, name
);
197 if (name
!= mailname
)
198 nstrcpy(mailname
, PATHSIZE
, name
);
200 mailsize
= fsize(ibuf
);
201 if ((fd
= open(tempMesg
, O_RDWR
|O_CREAT
|O_EXCL
, 0600)) < 0 ||
202 (otf
= fdopen(fd
, "w")) == NULL
) {
204 if (!edit
&& issysmbox
)
208 if ((itf
= fopen(tempMesg
, "r")) == NULL
) {
210 if (!edit
&& issysmbox
)
214 removefile(tempMesg
);
223 if (!isedit
&& issysmbox
)
228 if (!isedit
&& issysmbox
)
230 exit(exrc
? exrc
: rpterr
);
234 /* global to semaphores */
235 static char semfn
[128];
236 static FILE *semfp
= NULL
;
239 * return -1 if file is already being read, 0 otherwise
246 if ((home
= getenv("HOME")) == NULL
)
248 snprintf(semfn
, sizeof (semfn
), "%s%s", home
, "/.Maillock");
249 if ((semfp
= fopen(semfn
, "w")) == NULL
) {
251 gettext("WARNING: Can't open mail lock file (%s).\n"), semfn
);
253 gettext("\t Assuming you are not already reading mail.\n"));
256 if (lock(semfp
, "w", 0) < 0) {
257 if (errno
== ENOLCK
) {
259 gettext("WARNING: Unable to acquire mail lock, no record locks available.\n"));
261 gettext("\t Assuming you are not already reading mail.\n"));
265 gettext("WARNING: You are already reading mail.\n"));
267 gettext("\t This instance of mail is read only.\n"));
285 * Interpret user commands one by one. If standard input is not a tty,
290 static int shudprompt
;
297 char linebuf
[LINESIZE
];
303 sigset(SIGCONT
, SIG_DFL
);
305 if (rcvmode
&& !sourcing
) {
306 if (sigset(SIGINT
, SIG_IGN
) != SIG_IGN
)
307 sigset(SIGINT
, stop
);
308 if (sigset(SIGHUP
, SIG_IGN
) != SIG_IGN
)
309 sigset(SIGHUP
, hangup
);
315 * Print the prompt, if needed. Clear out
316 * string space, and flush the output.
319 if (!rcvmode
&& !sourcing
)
323 if ((shudprompt
= (intty
&& !sourcing
)) != 0) {
324 if (prompt
== NOSTR
) {
325 if ((int)value("bsdcompat"))
331 sigset(SIGCONT
, contin
);
333 if (intty
&& value("autoinc") &&
334 stat(editfile
, &minfo
) >= 0 &&
335 minfo
.st_size
> mailsize
) {
338 OmsgCount
= msgCount
;
341 if (!edit
&& issysmbox
)
343 if ((ibuf
= fopen(editfile
, "r")) == NULL
) {
345 gettext("Can't reopen %s\n"),
347 if (!edit
&& issysmbox
)
352 if (edit
|| !issysmbox
)
354 fseek(ibuf
, mailsize
, 0);
355 mailsize
= fsize(ibuf
);
359 if (!edit
&& issysmbox
)
361 if (msgCount
-OmsgCount
> 0) {
363 "New mail has arrived.\n"));
364 if (msgCount
- OmsgCount
== 1)
366 "Loaded 1 new message\n"));
369 "Loaded %d new messages\n"),
371 if (value("header") != NOSTR
)
372 for (i
= OmsgCount
+1;
373 i
<= msgCount
; i
++) {
380 printf("%s", prompt
);
386 * Read a line of commands from the current input
387 * and handle end of file specially.
393 if (readline(input
, line
) <= 0) {
402 if (value("ignoreeof") != NOSTR
&& shudprompt
) {
403 if (++eofloop
< 25) {
405 "Use \"quit\" to quit.\n"));
411 if ((n
= strlen(line
)) == 0)
417 if (n
> LINESIZE
- (int)strlen(linebuf
) - 1)
419 strcat(linebuf
, line
);
421 n
= LINESIZE
- strlen(linebuf
) - 1;
422 if ((int)strlen(line
) > n
) {
424 "Line plus continuation line too long:\n\t%s\n\nplus\n\t%s\n"),
434 strncat(linebuf
, line
, n
);
436 sigset(SIGCONT
, SIG_DFL
);
438 if (execute(linebuf
, 0))
445 * Execute a single command. If the command executed
446 * is "quit," then return non-zero so that the caller
447 * will know to return back to main, if it cares.
448 * Contxt is non-zero if called while composing mail.
452 execute(char linebuf
[], int contxt
)
455 char *arglist
[MAXARGC
];
456 const struct cmd
*com
;
457 register char *cp
, *cp2
;
462 * Strip the white space away from the beginning
463 * of the command, then scan out a word, which
464 * consists of anything except digits and white space.
466 * Handle |, ! and # differently to get the correct
467 * lexical conventions.
471 while (any(*cp
, " \t"))
477 while (*cp
&& !any(*cp
, " \t0123456789$^.:/-+*'\""))
482 * Look up the command; if not found, complain.
483 * Normally, a blank command would map to the
484 * first command in the table; while sourcing,
485 * however, we ignore blank lines to eliminate
489 if (sourcing
&& equal(word
, ""))
493 fprintf(stderr
, gettext("Unknown command: \"%s\"\n"), word
);
506 * See if we should execute the command -- if a conditional
507 * we always execute it, otherwise, check the state of cond.
510 if ((com
->c_argtype
& F
) == 0)
511 if (cond
== CRCV
&& !rcvmode
|| cond
== CSEND
&& rcvmode
||
512 cond
== CTTY
&& !intty
|| cond
== CNOTTY
&& intty
)
516 * Special case so that quit causes a return to
517 * main, who will call the quit code directly.
518 * If we are in a source file, just unstack.
521 if (com
->c_func
== (int (*)(void *))edstop
) {
532 * Process the arguments to the command, depending
533 * on the type it expects. Default to an error.
534 * If we are sourcing an interactive command, it's
538 if (!rcvmode
&& (com
->c_argtype
& M
) == 0) {
540 gettext("May not execute \"%s\" while sending\n"),
548 if (sourcing
&& com
->c_argtype
& I
) {
550 gettext("May not execute \"%s\" while sourcing\n"),
558 if (readonly
&& com
->c_argtype
& W
) {
559 fprintf(stderr
, gettext(
560 "May not execute \"%s\" -- message file is read only\n"),
568 if (contxt
&& com
->c_argtype
& R
) {
569 fprintf(stderr
, gettext("Cannot recursively invoke \"%s\"\n"),
574 switch (com
->c_argtype
& ~(F
|P
|I
|M
|T
|W
|R
)) {
577 * A message list defaulting to nearest forward
582 gettext("Illegal use of \"message list\"\n"));
585 if ((c
= getmsglist(cp
, msgvec
, com
->c_msgflag
)) < 0)
591 *msgvec
= first(com
->c_msgflag
,
595 if (*msgvec
== NULL
) {
596 fprintf(stderr
, gettext("No applicable messages\n"));
599 e
= (*com
->c_func
)(msgvec
);
604 * A message operand with no defaults, but no error
605 * if none exists. There will be an error if the
606 * msgvec pointer is of zero value.
610 gettext("Illegal use of \"message operand\"\n"));
613 if (getmessage(cp
, msgvec
, com
->c_msgflag
) < 0)
615 e
= (*com
->c_func
)(msgvec
);
620 * Just the straight string, with
621 * leading blanks removed.
623 while (any(*cp
, " \t"))
625 e
= (*com
->c_func
)(cp
);
630 * A vector of strings, in shell style.
632 if ((c
= getrawlist(cp
, arglist
,
633 sizeof (arglist
) / sizeof (*arglist
))) < 0)
635 if (c
< com
->c_minargs
) {
637 gettext("%s requires at least %d arg(s)\n"),
638 com
->c_name
, com
->c_minargs
);
641 if (c
> com
->c_maxargs
) {
643 gettext("%s takes no more than %d arg(s)\n"),
644 com
->c_name
, com
->c_maxargs
);
647 e
= (*com
->c_func
)(arglist
);
652 * Just the constant zero, for exiting,
655 e
= (*com
->c_func
)(0);
659 panic("Unknown argtype");
663 * Exit the current source file on
671 if (com
->c_func
== (int (*)(void *))edstop
)
673 if (value("autoprint") != NOSTR
&& com
->c_argtype
& P
)
674 if ((dot
->m_flag
& MDELETED
) == 0) {
675 muvec
[0] = dot
- &message
[0] + 1;
679 if (!sourcing
&& (com
->c_argtype
& T
) == 0)
686 * When we wake up after ^Z, reprint the prompt.
697 printf("%s", prompt
);
703 * Branch here on hangup signal and simulate quit.
725 if (value("exit") != NOSTR
)
734 * Set the size of the message vector used to construct argument
735 * lists to message list functions.
742 if (msgvec
!= (int *)0)
745 sz
= 1; /* need at least one cell for terminating 0 */
746 if ((msgvec
= (int *)
747 calloc((unsigned)(sz
+ 1), sizeof (*msgvec
))) == NULL
)
748 panic("Failed to allocate memory for message vector");
752 * Find the correct command in the command table corresponding
753 * to the passed command "word"
756 static const struct cmd
*
759 register const struct cmd
*cp
;
761 for (cp
= &cmdtab
[0]; cp
->c_name
!= NOSTR
; cp
++)
762 if (isprefix(word
, cp
->c_name
))
768 * Determine if as1 is a valid prefix of as2.
771 isprefix(char *as1
, char *as2
)
773 register char *s1
, *s2
;
780 return (*--s1
== '\0');
784 * The following gets called on receipt of a rubout. This is
785 * to abort printout of a command, mainly.
786 * Dispatching here when command() is inactive crashes rcv.
787 * Close all open files except 0, 1, 2, and the temporary.
788 * The special call to getuserid() is needed so it won't get
789 * annoyed about losing its open file.
790 * Also, unstack all source files.
793 static int inithdr
; /* am printing startup headers */
806 (void) getuserid((char *)0);
807 for (head
= fplist
; head
!= (NODE
*)NULL
; head
= head
->next
) {
808 if (head
->fp
== stdin
|| head
->fp
== stdout
)
810 if (head
->fp
== itf
|| head
->fp
== otf
)
812 if (head
->fp
== stderr
)
814 if (head
->fp
== semfp
)
816 if (head
->fp
== pipef
) {
828 fprintf(stderr
, gettext("Interrupt\n"));
838 * Announce the presence of the current mailx version,
839 * give the message count, and print a header listing.
842 #define GREETING "%s Type ? for help.\n"
848 extern const char *const version
;
850 if (!Hflag
&& value("quiet") == NOSTR
)
851 printf(gettext(GREETING
), version
);
852 mdot
= newfileinfo(1);
855 dot
= &message
[mdot
- 1];
856 if (msgCount
> 0 && !noheader
) {
864 * Announce information about the file we are editing.
865 * Return a likely place to set dot.
868 newfileinfo(int start
)
870 register struct message
*mp
;
871 register int u
, n
, mdot
, d
, s
;
872 char fname
[BUFSIZ
], zname
[BUFSIZ
], *ename
;
875 return (1); /* fake it--return message 1 */
876 for (mp
= &message
[start
- 1]; mp
< &message
[msgCount
]; mp
++)
877 if ((mp
->m_flag
& (MNEW
|MREAD
)) == MNEW
)
879 if (mp
>= &message
[msgCount
])
880 for (mp
= &message
[start
- 1]; mp
< &message
[msgCount
]; mp
++)
881 if ((mp
->m_flag
& MREAD
) == 0)
883 if (mp
< &message
[msgCount
])
884 mdot
= mp
- &message
[0] + 1;
888 for (mp
= &message
[start
- 1]; mp
< &message
[msgCount
]; mp
++) {
889 if (mp
->m_flag
& MNEW
)
891 if ((mp
->m_flag
& MREAD
) == 0)
893 if (mp
->m_flag
& MDELETED
)
895 if (mp
->m_flag
& MSAVED
)
899 if (getfold(fname
) >= 0) {
900 nstrcat(fname
, sizeof (fname
), "/");
901 if (strncmp(fname
, editfile
, strlen(fname
)) == 0) {
902 snprintf(zname
, sizeof (zname
),
903 "+%s", editfile
+ strlen(fname
));
907 printf("\"%s\": ", ename
);
909 printf(gettext("1 message"));
911 printf(gettext("%d messages"), msgCount
);
913 printf(gettext(" %d new"), n
);
915 printf(gettext(" %d unread"), u
);
917 printf(gettext(" %d deleted"), d
);
919 printf(gettext(" %d saved"), s
);
921 printf(gettext(" [Read only]"));
927 * Print the current version number.
938 printf("%s\n", version
);
943 * Load a file of user definitions.
948 register FILE *in
, *oldin
;
950 if ((in
= fopen(name
, "r")) == NULL
)
964 * Incorporate any new mail into the current session.
966 * XXX - Since autoinc works on "edited" files as well as the
967 * system mailbox, this probably ought to as well.
976 int firstnewmsg
= msgCount
+ 1;
979 fprintf(stderr
, gettext("Not in system mailbox\n"));
982 if (((ibuf
= fopen(mailname
, "r")) == NULL
) ||
983 (fstat(fileno(ibuf
), &stbuf
) < 0) || stbuf
.st_size
== 0L ||
984 stbuf
.st_size
== mailsize
|| (stbuf
.st_mode
&S_IFMT
) != S_IFREG
) {
985 if (strrchr(mailname
, '/') == NOSTR
)
986 fprintf(stderr
, gettext("No new mail.\n"));
988 fprintf(stderr
, gettext("No new mail for %s\n"),
989 strrchr(mailname
, '/')+1);
999 fseek(ibuf
, mailsize
, 0);
1000 mailsize
= fsize(ibuf
);
1007 mdot
= newfileinfo(firstnewmsg
);
1008 dot
= &message
[mdot
- 1];