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
45 * mailx -- a modified version of a University of California at Berkeley
48 * Lexical processing of commands.
52 static void contin(int);
54 static int isprefix(char *as1
, char *as2
);
55 static const struct cmd
*lex(char word
[]);
56 static int Passeren(void);
57 static void setmsize(int sz
);
60 * Set up editing on the given file name.
61 * If isedit is true, we are considered to be editing the file,
62 * otherwise we are reading our mail which has signficance for
67 setfile(char *name
, int isedit
)
72 static char efile
[PATHSIZE
];
79 if (!isedit
&& issysmbox
)
81 if (stat(name
, &stbuf
) < 0 && errno
== EOVERFLOW
) {
82 fprintf(stderr
, gettext("mailbox %s is too large to"
83 " accept incoming mail\n"), name
);
86 if ((ibuf
= fopen(name
, "r")) == NULL
) {
89 int filethere
= (access(name
, 0) == 0);
92 goto doexit
; /* no mail, return error */
93 if (isedit
|| filethere
)
96 char *f
= strrchr(name
, '/');
98 fprintf(stderr
, gettext("No mail.\n"));
100 fprintf(stderr
, gettext("No mail for %s\n"),
105 fstat(fileno(ibuf
), &stbuf
);
106 if (stbuf
.st_size
== 0L || (stbuf
.st_mode
&S_IFMT
) != S_IFREG
) {
108 goto doexit
; /* no mail, return error */
110 if (stbuf
.st_size
== 0L)
111 fprintf(stderr
, gettext("%s: empty file\n"),
115 gettext("%s: not a regular file\n"), name
);
117 if (strrchr(name
, '/') == NOSTR
)
118 fprintf(stderr
, gettext("No mail.\n"));
120 fprintf(stderr
, gettext("No mail for %s\n"),
121 strrchr(name
, '/') + 1);
127 if (fgets(fortest
, sizeof (fortest
), ibuf
) == NULL
) {
128 perror(gettext("mailx: Unable to read from mail file"));
132 fseek(ibuf
, (long)(BUFSIZ
+1), 0); /* flush input buffer */
134 if (strncmp(fortest
, "Forward to ", 11) == 0) {
136 goto doexit
; /* no mail, return error */
137 fprintf(stderr
, gettext("Your mail is being forwarded to %s"),
144 goto doexit
; /* there is mail, return success */
148 * Looks like all will be well. Must hold signals
149 * while we are reading the new file, else we will ruin
150 * the message[] data structure.
151 * Copy the messages into /tmp and set pointers.
157 * Now that we know we can switch to the new file
158 * it's safe to close out the current file.
160 * If we're switching to the file we are currently
161 * editing, don't allow it to be removed as a side
162 * effect of closing it out.
165 edstop(strcmp(editfile
, name
) == 0);
167 quit(strcmp(editfile
, name
) == 0);
178 if (!isedit
&& issysmbox
&& !Hflag
)
179 readonly
= Passeren() == -1;
181 fstat(fileno(ibuf
), &stbuf
);
182 utimep
->actime
= stbuf
.st_atime
;
183 utimep
->modtime
= stbuf
.st_mtime
;
186 if ((i
= open(name
, O_WRONLY
)) < 0)
192 nstrcpy(efile
, PATHSIZE
, name
);
195 if (name
!= mailname
)
196 nstrcpy(mailname
, PATHSIZE
, name
);
198 mailsize
= fsize(ibuf
);
199 if ((fd
= open(tempMesg
, O_RDWR
|O_CREAT
|O_EXCL
, 0600)) < 0 ||
200 (otf
= fdopen(fd
, "w")) == NULL
) {
202 if (!edit
&& issysmbox
)
206 if ((itf
= fopen(tempMesg
, "r")) == NULL
) {
208 if (!edit
&& issysmbox
)
212 removefile(tempMesg
);
221 if (!isedit
&& issysmbox
)
226 if (!isedit
&& issysmbox
)
228 exit(exrc
? exrc
: rpterr
);
232 /* global to semaphores */
233 static char semfn
[128];
234 static FILE *semfp
= NULL
;
237 * return -1 if file is already being read, 0 otherwise
244 if ((home
= getenv("HOME")) == NULL
)
246 snprintf(semfn
, sizeof (semfn
), "%s%s", home
, "/.Maillock");
247 if ((semfp
= fopen(semfn
, "w")) == NULL
) {
249 gettext("WARNING: Can't open mail lock file (%s).\n"), semfn
);
251 gettext("\t Assuming you are not already reading mail.\n"));
254 if (lock(semfp
, "w", 0) < 0) {
255 if (errno
== ENOLCK
) {
257 gettext("WARNING: Unable to acquire mail lock, no record locks available.\n"));
259 gettext("\t Assuming you are not already reading mail.\n"));
263 gettext("WARNING: You are already reading mail.\n"));
265 gettext("\t This instance of mail is read only.\n"));
283 * Interpret user commands one by one. If standard input is not a tty,
288 static int shudprompt
;
295 char linebuf
[LINESIZE
];
301 sigset(SIGCONT
, SIG_DFL
);
303 if (rcvmode
&& !sourcing
) {
304 if (sigset(SIGINT
, SIG_IGN
) != SIG_IGN
)
305 sigset(SIGINT
, stop
);
306 if (sigset(SIGHUP
, SIG_IGN
) != SIG_IGN
)
307 sigset(SIGHUP
, hangup
);
313 * Print the prompt, if needed. Clear out
314 * string space, and flush the output.
317 if (!rcvmode
&& !sourcing
)
321 if ((shudprompt
= (intty
&& !sourcing
)) != 0) {
322 if (prompt
== NOSTR
) {
323 if ((int)value("bsdcompat"))
329 sigset(SIGCONT
, contin
);
331 if (intty
&& value("autoinc") &&
332 stat(editfile
, &minfo
) >= 0 &&
333 minfo
.st_size
> mailsize
) {
336 OmsgCount
= msgCount
;
339 if (!edit
&& issysmbox
)
341 if ((ibuf
= fopen(editfile
, "r")) == NULL
) {
343 gettext("Can't reopen %s\n"),
345 if (!edit
&& issysmbox
)
350 if (edit
|| !issysmbox
)
352 fseek(ibuf
, mailsize
, 0);
353 mailsize
= fsize(ibuf
);
357 if (!edit
&& issysmbox
)
359 if (msgCount
-OmsgCount
> 0) {
361 "New mail has arrived.\n"));
362 if (msgCount
- OmsgCount
== 1)
364 "Loaded 1 new message\n"));
367 "Loaded %d new messages\n"),
369 if (value("header") != NOSTR
)
370 for (i
= OmsgCount
+1;
371 i
<= msgCount
; i
++) {
378 printf("%s", prompt
);
384 * Read a line of commands from the current input
385 * and handle end of file specially.
391 if (readline(input
, line
) <= 0) {
400 if (value("ignoreeof") != NOSTR
&& shudprompt
) {
401 if (++eofloop
< 25) {
403 "Use \"quit\" to quit.\n"));
409 if ((n
= strlen(line
)) == 0)
415 if (n
> LINESIZE
- (int)strlen(linebuf
) - 1)
417 strcat(linebuf
, line
);
419 n
= LINESIZE
- strlen(linebuf
) - 1;
420 if ((int)strlen(line
) > n
) {
422 "Line plus continuation line too long:\n\t%s\n\nplus\n\t%s\n"),
432 strncat(linebuf
, line
, n
);
434 sigset(SIGCONT
, SIG_DFL
);
436 if (execute(linebuf
, 0))
443 * Execute a single command. If the command executed
444 * is "quit," then return non-zero so that the caller
445 * will know to return back to main, if it cares.
446 * Contxt is non-zero if called while composing mail.
450 execute(char linebuf
[], int contxt
)
453 char *arglist
[MAXARGC
];
454 const struct cmd
*com
;
455 register char *cp
, *cp2
;
460 * Strip the white space away from the beginning
461 * of the command, then scan out a word, which
462 * consists of anything except digits and white space.
464 * Handle |, ! and # differently to get the correct
465 * lexical conventions.
469 while (any(*cp
, " \t"))
475 while (*cp
&& !any(*cp
, " \t0123456789$^.:/-+*'\""))
480 * Look up the command; if not found, complain.
481 * Normally, a blank command would map to the
482 * first command in the table; while sourcing,
483 * however, we ignore blank lines to eliminate
487 if (sourcing
&& equal(word
, ""))
491 fprintf(stderr
, gettext("Unknown command: \"%s\"\n"), word
);
504 * See if we should execute the command -- if a conditional
505 * we always execute it, otherwise, check the state of cond.
508 if ((com
->c_argtype
& F
) == 0)
509 if (cond
== CRCV
&& !rcvmode
|| cond
== CSEND
&& rcvmode
||
510 cond
== CTTY
&& !intty
|| cond
== CNOTTY
&& intty
)
514 * Special case so that quit causes a return to
515 * main, who will call the quit code directly.
516 * If we are in a source file, just unstack.
519 if (com
->c_func
== (int (*)(void *))edstop
) {
530 * Process the arguments to the command, depending
531 * on the type it expects. Default to an error.
532 * If we are sourcing an interactive command, it's
536 if (!rcvmode
&& (com
->c_argtype
& M
) == 0) {
538 gettext("May not execute \"%s\" while sending\n"),
546 if (sourcing
&& com
->c_argtype
& I
) {
548 gettext("May not execute \"%s\" while sourcing\n"),
556 if (readonly
&& com
->c_argtype
& W
) {
557 fprintf(stderr
, gettext(
558 "May not execute \"%s\" -- message file is read only\n"),
566 if (contxt
&& com
->c_argtype
& R
) {
567 fprintf(stderr
, gettext("Cannot recursively invoke \"%s\"\n"),
572 switch (com
->c_argtype
& ~(F
|P
|I
|M
|T
|W
|R
)) {
575 * A message list defaulting to nearest forward
580 gettext("Illegal use of \"message list\"\n"));
583 if ((c
= getmsglist(cp
, msgvec
, com
->c_msgflag
)) < 0)
589 *msgvec
= first(com
->c_msgflag
,
594 fprintf(stderr
, gettext("No applicable messages\n"));
597 e
= (*com
->c_func
)(msgvec
);
602 * A message operand with no defaults, but no error
603 * if none exists. There will be an error if the
604 * msgvec pointer is of zero value.
608 gettext("Illegal use of \"message operand\"\n"));
611 if (getmessage(cp
, msgvec
, com
->c_msgflag
) < 0)
613 e
= (*com
->c_func
)(msgvec
);
618 * Just the straight string, with
619 * leading blanks removed.
621 while (any(*cp
, " \t"))
623 e
= (*com
->c_func
)(cp
);
628 * A vector of strings, in shell style.
630 if ((c
= getrawlist(cp
, arglist
,
631 sizeof (arglist
) / sizeof (*arglist
))) < 0)
633 if (c
< com
->c_minargs
) {
635 gettext("%s requires at least %d arg(s)\n"),
636 com
->c_name
, com
->c_minargs
);
639 if (c
> com
->c_maxargs
) {
641 gettext("%s takes no more than %d arg(s)\n"),
642 com
->c_name
, com
->c_maxargs
);
645 e
= (*com
->c_func
)(arglist
);
650 * Just the constant zero, for exiting,
653 e
= (*com
->c_func
)(0);
657 panic("Unknown argtype");
661 * Exit the current source file on
669 if (com
->c_func
== (int (*)(void *))edstop
)
671 if (value("autoprint") != NOSTR
&& com
->c_argtype
& P
)
672 if ((dot
->m_flag
& MDELETED
) == 0) {
673 muvec
[0] = dot
- &message
[0] + 1;
677 if (!sourcing
&& (com
->c_argtype
& T
) == 0)
684 * When we wake up after ^Z, reprint the prompt.
695 printf("%s", prompt
);
701 * Branch here on hangup signal and simulate quit.
723 if (value("exit") != NOSTR
)
732 * Set the size of the message vector used to construct argument
733 * lists to message list functions.
743 sz
= 1; /* need at least one cell for terminating 0 */
744 if ((msgvec
= (int *)
745 calloc((unsigned)(sz
+ 1), sizeof (*msgvec
))) == NULL
)
746 panic("Failed to allocate memory for message vector");
750 * Find the correct command in the command table corresponding
751 * to the passed command "word"
754 static const struct cmd
*
757 register const struct cmd
*cp
;
759 for (cp
= &cmdtab
[0]; cp
->c_name
!= NOSTR
; cp
++)
760 if (isprefix(word
, cp
->c_name
))
766 * Determine if as1 is a valid prefix of as2.
769 isprefix(char *as1
, char *as2
)
771 register char *s1
, *s2
;
778 return (*--s1
== '\0');
782 * The following gets called on receipt of a rubout. This is
783 * to abort printout of a command, mainly.
784 * Dispatching here when command() is inactive crashes rcv.
785 * Close all open files except 0, 1, 2, and the temporary.
786 * The special call to getuserid() is needed so it won't get
787 * annoyed about losing its open file.
788 * Also, unstack all source files.
791 static int inithdr
; /* am printing startup headers */
804 (void) getuserid((char *)0);
805 for (head
= fplist
; head
!= (NODE
*)NULL
; head
= head
->next
) {
806 if (head
->fp
== stdin
|| head
->fp
== stdout
)
808 if (head
->fp
== itf
|| head
->fp
== otf
)
810 if (head
->fp
== stderr
)
812 if (head
->fp
== semfp
)
814 if (head
->fp
== pipef
) {
826 fprintf(stderr
, gettext("Interrupt\n"));
836 * Announce the presence of the current mailx version,
837 * give the message count, and print a header listing.
840 #define GREETING "%s Type ? for help.\n"
846 extern const char *const version
;
848 if (!Hflag
&& value("quiet") == NOSTR
)
849 printf(gettext(GREETING
), version
);
850 mdot
= newfileinfo(1);
853 dot
= &message
[mdot
- 1];
854 if (msgCount
> 0 && !noheader
) {
862 * Announce information about the file we are editing.
863 * Return a likely place to set dot.
866 newfileinfo(int start
)
868 register struct message
*mp
;
869 register int u
, n
, mdot
, d
, s
;
870 char fname
[BUFSIZ
], zname
[BUFSIZ
], *ename
;
873 return (1); /* fake it--return message 1 */
874 for (mp
= &message
[start
- 1]; mp
< &message
[msgCount
]; mp
++)
875 if ((mp
->m_flag
& (MNEW
|MREAD
)) == MNEW
)
877 if (mp
>= &message
[msgCount
])
878 for (mp
= &message
[start
- 1]; mp
< &message
[msgCount
]; mp
++)
879 if ((mp
->m_flag
& MREAD
) == 0)
881 if (mp
< &message
[msgCount
])
882 mdot
= mp
- &message
[0] + 1;
886 for (mp
= &message
[start
- 1]; mp
< &message
[msgCount
]; mp
++) {
887 if (mp
->m_flag
& MNEW
)
889 if ((mp
->m_flag
& MREAD
) == 0)
891 if (mp
->m_flag
& MDELETED
)
893 if (mp
->m_flag
& MSAVED
)
897 if (getfold(fname
) >= 0) {
898 nstrcat(fname
, sizeof (fname
), "/");
899 if (strncmp(fname
, editfile
, strlen(fname
)) == 0) {
900 snprintf(zname
, sizeof (zname
),
901 "+%s", editfile
+ strlen(fname
));
905 printf("\"%s\": ", ename
);
907 printf(gettext("1 message"));
909 printf(gettext("%d messages"), msgCount
);
911 printf(gettext(" %d new"), n
);
913 printf(gettext(" %d unread"), u
);
915 printf(gettext(" %d deleted"), d
);
917 printf(gettext(" %d saved"), s
);
919 printf(gettext(" [Read only]"));
925 * Print the current version number.
936 printf("%s\n", version
);
941 * Load a file of user definitions.
946 register FILE *in
, *oldin
;
948 if ((in
= fopen(name
, "r")) == NULL
)
962 * Incorporate any new mail into the current session.
964 * XXX - Since autoinc works on "edited" files as well as the
965 * system mailbox, this probably ought to as well.
974 int firstnewmsg
= msgCount
+ 1;
977 fprintf(stderr
, gettext("Not in system mailbox\n"));
980 if (((ibuf
= fopen(mailname
, "r")) == NULL
) ||
981 (fstat(fileno(ibuf
), &stbuf
) < 0) || stbuf
.st_size
== 0L ||
982 stbuf
.st_size
== mailsize
|| (stbuf
.st_mode
&S_IFMT
) != S_IFREG
) {
983 if (strrchr(mailname
, '/') == NOSTR
)
984 fprintf(stderr
, gettext("No new mail.\n"));
986 fprintf(stderr
, gettext("No new mail for %s\n"),
987 strrchr(mailname
, '/')+1);
997 fseek(ibuf
, mailsize
, 0);
998 mailsize
= fsize(ibuf
);
1005 mdot
= newfileinfo(firstnewmsg
);
1006 dot
= &message
[mdot
- 1];