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 2014 Joyent, Inc.
28 * Copyright 1999 Sun Microsystems, Inc. All rights reserved.
29 * Use is subject to license terms.
32 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
33 /* All Rights Reserved */
40 * mailx -- a modified version of a University of California at Berkeley
46 static int getln(char *line
, int max
, FILE *f
);
47 static int linecount(char *lp
, long size
);
50 * Set up the input pointers while copying the mail file into
55 setptr(register FILE *ibuf
)
57 int n
, newline
= 1, blankline
= 1;
58 int StartNewMsg
= TRUE
;
62 int cflg
= 0; /* found Content-length in header */
67 char linebuf
[LINESIZE
];
68 int inhead
, newmail
, Odot
;
77 (struct message
*)calloc(space
, sizeof (struct message
));
78 if (message
== NULL
) {
79 fprintf(stderr
, gettext(
80 "calloc: insufficient memory for %d messages\n"),
93 * Set default flags. When reading from
94 * a folder, assume the message has been
103 while ((n
= getln(linebuf
, sizeof (linebuf
), ibuf
)) > 0) {
108 hdr
= inhead
&& (headerp(linebuf
) ||
109 (linebuf
[0] == ' ' || linebuf
[0] == '\t'));
110 if (!hdr
&& cflg
) { /* nonheader, Content-length seen */
111 if (clen
> 0 && clen
< n
) { /* read too much */
113 * NB: this only can happen if there is a
114 * small content that is NOT \n terminated
115 * and has no leading blank line, i.e., never.
117 if (fwrite(linebuf
, 1, (int)clen
, otf
) !=
122 l
+= linecount(linebuf
, clen
);
127 /* shift line to the left, copy null as well */
128 memcpy(linebuf
, linebuf
+clen
, n
+1);
130 message
[msgCount
-1].m_clen
= clen
+ 1;
135 /* here, clen == 0 or clen >= n */
136 if (n
== 1 && linebuf
[0] == '\n') {
137 /* leading empty line */
143 message
[msgCount
-1].m_clen
= clen
;
145 if (fwrite(linebuf
, 1, n
, otf
) != n
) {
149 l
+= linecount(linebuf
, n
);
155 n
= clen
< sizeof (linebuf
) ?
156 (int)clen
: (int)sizeof (linebuf
);
157 if ((n
= fread(linebuf
, 1, n
, ibuf
)) <= 0) {
158 fprintf(stderr
, gettext(
159 "%s:\tYour mailfile was found to be corrupted.\n"),
161 fprintf(stderr
, gettext(
162 "\t(Unexpected end-of-file).\n"));
163 fprintf(stderr
, gettext(
164 "\tMessage #%d may be truncated.\n\n"),
168 clen
= 0; /* stop the loop */
171 /* All done, go to top for next message */
178 /* Look for a From line that starts a new message */
179 if (blankline
&& linebuf
[0] == 'F' && is_headline(linebuf
)) {
180 if (msgCount
> 0 && !newmail
) {
181 message
[msgCount
-1].m_size
= s
;
182 message
[msgCount
-1].m_lines
= l
;
183 message
[msgCount
-1].m_flag
= flag
;
185 if (msgCount
>= space
) {
187 * Limit the speed at which the
188 * allocated space grows.
195 Odot
= dot
- &(message
[0]);
196 message
= (struct message
*)
197 reallocarray(message
, space
,
198 sizeof (struct message
));
199 if (message
== NULL
) {
200 perror("realloc failed");
201 fprintf(stderr
, gettext(
202 "realloc: insufficient memory for %d messages\n"),
206 dot
= &message
[Odot
];
208 message
[msgCount
].m_offset
= offset
;
209 message
[msgCount
].m_text
= TRUE
;
210 message
[msgCount
].m_clen
= 0;
225 /* if didn't get a header line, we're no longer in the header */
232 * Look for Status: line. Do quick check for second character,
233 * many headers start with "S" but few have "t" as second char.
235 if ((linebuf
[1] == 't' || linebuf
[1] == 'T') &&
236 ishfield(linebuf
, "status")) {
237 cp
= hcontents(linebuf
);
245 * Look for Content-Length and Content-Type headers. Like
246 * above, do a quick check for the "-", which is rare.
248 if (linebuf
[7] == '-') {
249 if (ishfield(linebuf
, "content-length")) {
251 clen
= atol(hcontents(linebuf
));
254 } else if (ishfield(linebuf
, "content-type")) {
258 cp
= hcontents(linebuf
);
260 while (!isspace(*cp
))
263 if (icequal(word
, "binary"))
264 message
[msgCount
-1].m_text
= FALSE
;
270 if (fwrite(linebuf
, 1, n
, otf
) != n
) {
284 if (linebuf
[n
-1] == '\n') {
285 blankline
= newline
&& n
== 1;
288 /* Blank line. Skip StartNewMsg check below */
294 if (StartNewMsg
&& !ToldUser
) {
295 fprintf(stderr
, gettext(
296 "%s:\tYour mailfile was found to be corrupted\n"),
299 gettext("\t(Content-length mismatch).\n"));
300 fprintf(stderr
, gettext(
301 "\tMessage #%d may be truncated,\n"), msgCount
);
302 fprintf(stderr
, gettext(
303 "\twith another message concatenated to it.\n\n"));
315 message
[msgCount
-1].m_size
= s
;
316 message
[msgCount
-1].m_lines
= l
;
317 message
[msgCount
-1].m_flag
= flag
;
324 * Compute the content length of a message and set it into m_clen.
328 setclen(register struct message
*mp
)
339 nread
= getln(line
, sizeof (line
), ibuf
);
342 * First line is the From line, so no headers
343 * there to worry about.
350 * If line is blank, we've reached end of headers.
355 * If this line is a continuation
356 * of a previous header field, keep going.
358 if (isspace(line
[0]))
361 * If we are no longer looking at real
362 * header lines, we're done.
363 * This happens in uucp style mail where
364 * there are no headers at all.
366 if (!headerp(line
)) {
377 getln(char *line
, int max
, FILE *f
)
380 register char *cp
, *ecp
;
384 while (cp
< ecp
&& (c
= getc(f
)) != EOF
)
385 if ((*cp
++ = (char)c
) == '\n')
392 * Read up a line from the specified input into the line
393 * buffer. Return the number of characters read. Do not
394 * include the newline at the end.
398 readline(FILE *ibuf
, char *linebuf
)
406 for (cp
= linebuf
; c
!= '\n' && c
!= EOF
; c
= getc(ibuf
)) {
410 gettext("mailx: NUL changed to @\n"));
415 if (cp
- linebuf
< LINESIZE
-2)
419 if (c
== EOF
&& cp
== linebuf
)
421 return (cp
- linebuf
+ 1);
425 * linecount - determine the number of lines in a printable file.
429 linecount(char *lp
, long size
)
431 register char *cp
, *ecp
;
444 * Return a file buffer all ready to read up the
445 * passed message pointer.
449 setinput(register struct message
*mp
)
452 if (fseek(itf
, mp
->m_offset
, 0) < 0) {
454 panic("temporary file seek");
461 * Delete a file, but only if the file is a plain file.
465 removefile(char name
[])
470 if (stat(name
, &statb
) < 0)
472 return (0); /* it's already gone, no error */
475 if ((statb
.st_mode
& S_IFMT
) != S_IFREG
) {
479 return (unlink(name
));
483 * Terminate an editing session by attempting to write out the user's
484 * file from the temporary. Save any new stuff appended to the file.
488 int noremove
/* don't allow the file to be removed, trunc instead */
491 register int gotcha
, c
;
492 register struct message
*mp
;
493 FILE *obuf
, *ibuf
, *tbuf
= 0, *readstat
;
495 char tempname
[STSIZ
], *id
;
501 if (Tflag
!= NOSTR
) {
502 if ((readstat
= fopen(Tflag
, "w")) == NULL
)
505 for (mp
= &message
[0], gotcha
= 0; mp
< &message
[msgCount
]; mp
++) {
506 if (mp
->m_flag
& MNEW
) {
508 mp
->m_flag
|= MSTATUS
;
510 if (mp
->m_flag
& (MODIFY
|MDELETED
|MSTATUS
))
512 if (Tflag
!= NOSTR
&& (mp
->m_flag
& (MREAD
|MDELETED
)) != 0) {
513 if ((id
= hfield("article-id", mp
, addone
)) != NOSTR
)
514 fprintf(readstat
, "%s\n", id
);
519 if (!gotcha
|| Tflag
!= NOSTR
)
521 if ((ibuf
= fopen(editfile
, "r+")) == NULL
) {
527 if (fstat(fileno(ibuf
), &statb
) >= 0 && statb
.st_size
> mailsize
) {
528 nstrcpy(tempname
, STSIZ
, "/tmp/mboxXXXXXX");
529 if ((tmpfd
= mkstemp(tempname
)) == -1) {
535 if ((obuf
= fdopen(tmpfd
, "w")) == NULL
) {
538 removefile(tempname
);
543 fseek(ibuf
, mailsize
, 0);
544 while ((c
= getc(ibuf
)) != EOF
)
547 if ((tbuf
= fopen(tempname
, "r")) == NULL
) {
550 removefile(tempname
);
554 removefile(tempname
);
556 if ((obuf
= fopen(editfile
, "r+")) == NULL
) {
557 if ((obuf
= fopen(editfile
, "w")) == NULL
) {
566 printf("\"%s\" ", editfile
);
569 for (mp
= &message
[0]; mp
< &message
[msgCount
]; mp
++) {
570 if ((mp
->m_flag
& MDELETED
) != 0)
573 if (msend(mp
, obuf
, 0, fputs
) < 0) {
583 gotcha
= (c
== 0 && tbuf
== NULL
);
585 while ((c
= getc(tbuf
)) != EOF
)
597 if (gotcha
&& !noremove
&& (value("keep") == NOSTR
)) {
598 removefile(editfile
);
599 printf(gettext("removed.\n"));
602 printf(gettext("updated.\n"));
614 static int sigdepth
= 0; /* depth of holdsigs() */
616 static int omask
= 0;
618 static sigset_t mask
, omask
;
622 * Hold signals SIGHUP - SIGQUIT.
628 if (sigdepth
++ == 0) {
630 omask
= sigblock(sigmask(SIGHUP
) |
631 sigmask(SIGINT
)|sigmask(SIGQUIT
));
634 sigaddset(&mask
, SIGHUP
);
635 sigaddset(&mask
, SIGINT
);
636 sigaddset(&mask
, SIGQUIT
);
637 sigprocmask(SIG_BLOCK
, &mask
, &omask
);
648 * Release signals SIGHUP - SIGQUIT
658 sigprocmask(SIG_SETMASK
, &omask
, NULL
);
667 #if !defined(OLD_BSD_SIGS) && !defined(VMUNIX)
669 (*sigset(int sig
, void (*act
)(int)))(int)
671 struct sigaction sa
, osa
;
673 sa
.sa_handler
= (void (*)())act
;
674 sigemptyset(&sa
.sa_mask
);
675 sa
.sa_flags
= SA_RESTART
;
676 if (sigaction(sig
, &sa
, &osa
) < 0)
677 return ((void (*)(int))-1);
678 return ((void (*)(int))osa
.sa_handler
);
683 * Flush the standard output.
694 * Determine the size of the file possessed by
705 if (fstat(f
, &sbuf
) < 0)
707 return (sbuf
.st_size
);
711 * Check for either a stdio recognized error, or
712 * a possibly delayed write error (in case it's
713 * an NFS file, for instance).
719 return (ferror(iob
) || fsync(fileno(iob
)) < 0);
723 * Take a file name, possibly with shell meta characters
724 * in it and expand it by using wordexp().
725 * Return the file name as a dynamic string.
726 * If the name cannot be expanded (for whatever reason)
734 char foldbuf
[BUFSIZ
];
737 wordexp_t wrdexp_buf
;
739 if (debug
) fprintf(stderr
, "expand(%s)=", name
);
740 cp
= strchr(name
, '\0') - 1; /* pointer to last char of name */
742 /* strip off trailing blanks */
743 while (cp
> name
&& isspace(*cp
))
745 l
= *++cp
; /* save char */
747 name
= savestr(name
);
748 *cp
= (char)l
; /* restore char */
750 if (name
[0] == '+' && getfold(foldbuf
) >= 0) {
751 snprintf(xname
, sizeof (xname
), "%s/%s", foldbuf
, name
+ 1);
752 cp
= safeexpand(savestr(xname
));
753 if (debug
) fprintf(stderr
, "%s\n", cp
);
756 if (!anyof(name
, "~{[*?$`'\"\\")) {
757 if (debug
) fprintf(stderr
, "%s\n", name
);
760 if (wordexp(name
, &wrdexp_buf
, WRDE_NOCMD
) != 0) {
761 fprintf(stderr
, gettext("Syntax error in \"%s\"\n"), name
);
765 if (wrdexp_buf
.we_wordc
> 1) {
766 fprintf(stderr
, gettext("\"%s\": Ambiguous\n"), name
);
770 if (debug
) fprintf(stderr
, "%s\n", wrdexp_buf
.we_wordv
[0]);
771 return (savestr(wrdexp_buf
.we_wordv
[0]));
775 * Take a file name, possibly with shell meta characters
776 * in it and expand it by using "sh -c echo filename"
777 * Return the file name as a dynamic string.
778 * If the name cannot be expanded (for whatever reason)
779 * return the original file name.
783 safeexpand(char name
[])
785 char *t
= expand(name
);
786 return (t
) ? t
: savestr(name
);
790 * Determine the current folder directory name.
797 if ((folder
= value("folder")) == NOSTR
|| *folder
== '\0')
800 * If name looks like a folder name, don't try
801 * to expand it, to prevent infinite recursion.
803 if (*folder
!= '+' && (folder
= expand(folder
)) == NOSTR
||
806 if (*folder
== '/') {
807 nstrcpy(name
, BUFSIZ
, folder
);
809 snprintf(name
, BUFSIZ
, "%s/%s", homedir
, folder
);
814 * A nicer version of Fdopen, which allows us to fclose
815 * without losing the open file.
819 Fdopen(int fildes
, char *mode
)
828 return (fdopen(f
, mode
));
832 * return the filename associated with "s". This function always
833 * returns a non-null string (no error checking is done on the receiving end)
836 Getf(register char *s
)
839 static char defbuf
[PATHSIZE
];
841 if (((cp
= value(s
)) != 0) && *cp
) {
842 return (safeexpand(cp
));
843 } else if (strcmp(s
, "MBOX") == 0) {
844 snprintf(defbuf
, sizeof (defbuf
), "%s/%s", Getf("HOME"),
847 } else if (strcmp(s
, "DEAD") == 0) {
848 snprintf(defbuf
, sizeof (defbuf
), "%s/%s", Getf("HOME"),
851 } else if (strcmp(s
, "MAILRC") == 0) {
852 snprintf(defbuf
, sizeof (defbuf
), "%s/%s", Getf("HOME"),
855 } else if (strcmp(s
, "HOME") == 0) {
856 /* no recursion allowed! */
859 return ("DEAD"); /* "cannot happen" */