1 /*
2 * CDDL HEADER START
3 *
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
7 * with the License.
8 *
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.
13 *
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]
19 *
20 * CDDL HEADER END
21 */
22
23 /*
24 * Copyright 1999 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
30
31 #pragma ident "%Z%%M% %I% %E% SMI"
32
33 #include "rcv.h"
34 #include <locale.h>
35 #include <wordexp.h>
36
37 /*
38 * mailx -- a modified version of a University of California at Berkeley
39 * mail program
40 *
41 * File I/O.
42 */
43
44 static int getln(char *line, int max, FILE *f);
45 static int linecount(char *lp, long size);
46
47 /*
48 * Set up the input pointers while copying the mail file into
49 * /tmp.
50 */
51
52 void
setptr(register FILE * ibuf)53 setptr(register FILE *ibuf)
54 {
55 int n, newline = 1, blankline = 1;
56 int StartNewMsg = TRUE;
57 int ToldUser = FALSE;
58 long clen = -1L;
59 int hdr = 0;
60 int cflg = 0; /* found Content-length in header */
61 register char *cp;
62 register int l;
63 register long s;
64 off_t offset;
65 char linebuf[LINESIZE];
66 int inhead, newmail, Odot;
67 short flag;
68
69 if (!space) {
70 msgCount = 0;
71 offset = 0;
72 space = 32;
73 newmail = 0;
74 message =
75 (struct message *)calloc(space, sizeof (struct message));
76 if (message == NULL) {
77 fprintf(stderr, gettext(
78 "calloc: insufficient memory for %d messages\n"),
79 space);
80 exit(1);
81 /* NOTREACHED */
82 }
83 dot = message;
84 } else {
85 newmail = 1;
86 offset = fsize(otf);
87 }
88 s = 0L;
89 l = 0;
90 /*
91 * Set default flags. When reading from
92 * a folder, assume the message has been
93 * previously read.
94 */
95 if (edit)
96 flag = MUSED|MREAD;
97 else
98 flag = MUSED|MNEW;
99
100 inhead = 0;
101 while ((n = getln(linebuf, sizeof (linebuf), ibuf)) > 0) {
102 if (!newline) {
103 goto putout;
104 }
105 top:
106 hdr = inhead && (headerp(linebuf) ||
107 (linebuf[0] == ' ' || linebuf[0] == '\t'));
108 if (!hdr && cflg) { /* nonheader, Content-length seen */
109 if (clen > 0 && clen < n) { /* read too much */
110 /*
111 * NB: this only can happen if there is a
112 * small content that is NOT \n terminated
113 * and has no leading blank line, i.e., never.
114 */
115 if (fwrite(linebuf, 1, (int)clen, otf) !=
116 clen) {
117 fclose(ibuf);
118 fflush(otf);
119 } else {
120 l += linecount(linebuf, clen);
121 }
122 offset += clen;
123 s += clen;
124 n -= (int)clen;
125 /* shift line to the left, copy null as well */
126 memcpy(linebuf, linebuf+clen, n+1);
127 cflg = 0;
128 message[msgCount-1].m_clen = clen + 1;
129 blankline = 1;
130 StartNewMsg = TRUE;
131 goto top;
132 }
133 /* here, clen == 0 or clen >= n */
134 if (n == 1 && linebuf[0] == '\n') {
135 /* leading empty line */
136 clen++; /* cheat */
137 inhead = 0;
138 }
139 offset += clen;
140 s += (long)clen;
141 message[msgCount-1].m_clen = clen;
142 for (;;) {
143 if (fwrite(linebuf, 1, n, otf) != n) {
144 fclose(ibuf);
145 fflush(otf);
146 } else {
147 l += linecount(linebuf, n);
148 }
149 clen -= n;
150 if (clen <= 0) {
151 break;
152 }
153 n = clen < sizeof (linebuf) ?
154 (int)clen : (int)sizeof (linebuf);
155 if ((n = fread(linebuf, 1, n, ibuf)) <= 0) {
156 fprintf(stderr, gettext(
157 "%s:\tYour mailfile was found to be corrupted.\n"),
158 progname);
159 fprintf(stderr, gettext(
160 "\t(Unexpected end-of-file).\n"));
161 fprintf(stderr, gettext(
162 "\tMessage #%d may be truncated.\n\n"),
163 msgCount);
164 offset -= clen;
165 s -= clen;
166 clen = 0; /* stop the loop */
167 }
168 }
169 /* All done, go to top for next message */
170 cflg = 0;
171 blankline = 1;
172 StartNewMsg = TRUE;
173 continue;
174 }
175
176 /* Look for a From line that starts a new message */
177 if (blankline && linebuf[0] == 'F' && ishead(linebuf)) {
178 if (msgCount > 0 && !newmail) {
179 message[msgCount-1].m_size = s;
180 message[msgCount-1].m_lines = l;
181 message[msgCount-1].m_flag = flag;
182 }
183 if (msgCount >= space) {
184 /*
185 * Limit the speed at which the
186 * allocated space grows.
187 */
188 if (space < 512)
189 space = space*2;
190 else
191 space += 512;
192 errno = 0;
193 Odot = dot - &(message[0]);
194 message = (struct message *)
195 realloc(message,
196 space*(sizeof (struct message)));
197 if (message == NULL) {
198 perror("realloc failed");
199 fprintf(stderr, gettext(
200 "realloc: insufficient memory for %d messages\n"),
201 space);
202 exit(1);
203 }
204 dot = &message[Odot];
205 }
206 message[msgCount].m_offset = offset;
207 message[msgCount].m_text = TRUE;
208 message[msgCount].m_clen = 0;
209 newmail = 0;
210 msgCount++;
211 if (edit)
212 flag = MUSED|MREAD;
213 else
214 flag = MUSED|MNEW;
215 inhead = 1;
216 s = 0L;
217 l = 0;
218 StartNewMsg = FALSE;
219 ToldUser = FALSE;
220 goto putout;
221 }
222
223 /* if didn't get a header line, we're no longer in the header */
224 if (!hdr)
225 inhead = 0;
226 if (!inhead)
227 goto putout;
228
229 /*
230 * Look for Status: line. Do quick check for second character,
231 * many headers start with "S" but few have "t" as second char.
232 */
233 if ((linebuf[1] == 't' || linebuf[1] == 'T') &&
234 ishfield(linebuf, "status")) {
235 cp = hcontents(linebuf);
236 flag = MUSED|MNEW;
237 if (strchr(cp, 'R'))
238 flag |= MREAD;
239 if (strchr(cp, 'O'))
240 flag &= ~MNEW;
241 }
242 /*
243 * Look for Content-Length and Content-Type headers. Like
244 * above, do a quick check for the "-", which is rare.
245 */
246 if (linebuf[7] == '-') {
247 if (ishfield(linebuf, "content-length")) {
248 if (!cflg) {
249 clen = atol(hcontents(linebuf));
250 cflg = clen >= 0;
251 }
252 } else if (ishfield(linebuf, "content-type")) {
253 char word[LINESIZE];
254 char *cp2;
255
256 cp = hcontents(linebuf);
257 cp2 = word;
258 while (!isspace(*cp))
259 *cp2++ = *cp++;
260 *cp2 = '\0';
261 if (icequal(word, "binary"))
262 message[msgCount-1].m_text = FALSE;
263 }
264 }
265 putout:
266 offset += n;
267 s += (long)n;
268 if (fwrite(linebuf, 1, n, otf) != n) {
269 fclose(ibuf);
270 fflush(otf);
271 } else {
272 l++;
273 }
274 if (ferror(otf)) {
275 perror("/tmp");
276 exit(1);
277 }
278 if (msgCount == 0) {
279 fclose(ibuf);
280 fflush(otf);
281 }
282 if (linebuf[n-1] == '\n') {
283 blankline = newline && n == 1;
284 newline = 1;
285 if (n == 1) {
286 /* Blank line. Skip StartNewMsg check below */
287 continue;
288 }
289 } else {
290 newline = 0;
291 }
292 if (StartNewMsg && !ToldUser) {
293 fprintf(stderr, gettext(
294 "%s:\tYour mailfile was found to be corrupted\n"),
295 progname);
296 fprintf(stderr,
297 gettext("\t(Content-length mismatch).\n"));
298 fprintf(stderr, gettext(
299 "\tMessage #%d may be truncated,\n"), msgCount);
300 fprintf(stderr, gettext(
301 "\twith another message concatenated to it.\n\n"));
302 ToldUser = TRUE;
303 }
304 }
305
306 if (n == 0) {
307 fflush(otf);
308 if (fferror(otf)) {
309 perror("/tmp");
310 exit(1);
311 }
312 if (msgCount) {
313 message[msgCount-1].m_size = s;
314 message[msgCount-1].m_lines = l;
315 message[msgCount-1].m_flag = flag;
316 }
317 fclose(ibuf);
318 }
319 }
320
321 /*
322 * Compute the content length of a message and set it into m_clen.
323 */
324
325 void
setclen(register struct message * mp)326 setclen(register struct message *mp)
327 {
328 long c;
329 FILE *ibuf;
330 char line[LINESIZE];
331 int fline, nread;
332
333 ibuf = setinput(mp);
334 c = mp->m_size;
335 fline = 1;
336 while (c > 0L) {
337 nread = getln(line, sizeof (line), ibuf);
338 c -= nread;
339 /*
340 * First line is the From line, so no headers
341 * there to worry about.
342 */
343 if (fline) {
344 fline = 0;
345 continue;
346 }
347 /*
348 * If line is blank, we've reached end of headers.
349 */
350 if (line[0] == '\n')
351 break;
352 /*
353 * If this line is a continuation
354 * of a previous header field, keep going.
355 */
356 if (isspace(line[0]))
357 continue;
358 /*
359 * If we are no longer looking at real
360 * header lines, we're done.
361 * This happens in uucp style mail where
362 * there are no headers at all.
363 */
364 if (!headerp(line)) {
365 c += nread;
366 break;
367 }
368 }
369 if (c == 0)
370 c = 1;
371 mp->m_clen = c;
372 }
373
374 static int
getln(char * line,int max,FILE * f)375 getln(char *line, int max, FILE *f)
376 {
377 register int c;
378 register char *cp, *ecp;
379
380 cp = line;
381 ecp = cp + max - 1;
382 while (cp < ecp && (c = getc(f)) != EOF)
383 if ((*cp++ = (char)c) == '\n')
384 break;
385 *cp = '\0';
386 return (cp - line);
387 }
388
389 /*
390 * Read up a line from the specified input into the line
391 * buffer. Return the number of characters read. Do not
392 * include the newline at the end.
393 */
394
395 int
readline(FILE * ibuf,char * linebuf)396 readline(FILE *ibuf, char *linebuf)
397 {
398 register char *cp;
399 register int c;
400 int seennulls = 0;
401
402 clearerr(ibuf);
403 c = getc(ibuf);
404 for (cp = linebuf; c != '\n' && c != EOF; c = getc(ibuf)) {
405 if (c == 0) {
406 if (!seennulls) {
407 fprintf(stderr,
408 gettext("mailx: NUL changed to @\n"));
409 seennulls++;
410 }
411 c = '@';
412 }
413 if (cp - linebuf < LINESIZE-2)
414 *cp++ = (char)c;
415 }
416 *cp = 0;
417 if (c == EOF && cp == linebuf)
418 return (0);
419 return (cp - linebuf + 1);
420 }
421
422 /*
423 * linecount - determine the number of lines in a printable file.
424 */
425
426 static int
linecount(char * lp,long size)427 linecount(char *lp, long size)
428 {
429 register char *cp, *ecp;
430 register int count;
431
432 count = 0;
433 cp = lp;
434 ecp = cp + size;
435 while (cp < ecp)
436 if (*cp++ == '\n')
437 count++;
438 return (count);
439 }
440
441 /*
442 * Return a file buffer all ready to read up the
443 * passed message pointer.
444 */
445
446 FILE *
setinput(register struct message * mp)447 setinput(register struct message *mp)
448 {
449 fflush(otf);
450 if (fseek(itf, mp->m_offset, 0) < 0) {
451 perror("fseek");
452 panic("temporary file seek");
453 }
454 return (itf);
455 }
456
457
458 /*
459 * Delete a file, but only if the file is a plain file.
460 */
461
462 int
removefile(char name[])463 removefile(char name[])
464 {
465 struct stat statb;
466 extern int errno;
467
468 if (stat(name, &statb) < 0)
469 if (errno == ENOENT)
470 return (0); /* it's already gone, no error */
471 else
472 return (-1);
473 if ((statb.st_mode & S_IFMT) != S_IFREG) {
474 errno = EISDIR;
475 return (-1);
476 }
477 return (unlink(name));
478 }
479
480 /*
481 * Terminate an editing session by attempting to write out the user's
482 * file from the temporary. Save any new stuff appended to the file.
483 */
484 int
edstop(int noremove)485 edstop(
486 int noremove /* don't allow the file to be removed, trunc instead */
487 )
488 {
489 register int gotcha, c;
490 register struct message *mp;
491 FILE *obuf, *ibuf, *tbuf = 0, *readstat;
492 struct stat statb;
493 char tempname[STSIZ], *id;
494 int tmpfd = -1;
495
496 if (readonly)
497 return (0);
498 holdsigs();
499 if (Tflag != NOSTR) {
500 if ((readstat = fopen(Tflag, "w")) == NULL)
501 Tflag = NOSTR;
502 }
503 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
504 if (mp->m_flag & MNEW) {
505 mp->m_flag &= ~MNEW;
506 mp->m_flag |= MSTATUS;
507 }
508 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
509 gotcha++;
510 if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
511 if ((id = hfield("article-id", mp, addone)) != NOSTR)
512 fprintf(readstat, "%s\n", id);
513 }
514 }
515 if (Tflag != NOSTR)
516 fclose(readstat);
517 if (!gotcha || Tflag != NOSTR)
518 goto done;
519 if ((ibuf = fopen(editfile, "r+")) == NULL) {
520 perror(editfile);
521 relsesigs();
522 longjmp(srbuf, 1);
523 }
524 lock(ibuf, "r+", 1);
525 if (fstat(fileno(ibuf), &statb) >= 0 && statb.st_size > mailsize) {
526 nstrcpy(tempname, STSIZ, "/tmp/mboxXXXXXX");
527 if ((tmpfd = mkstemp(tempname)) == -1) {
528 perror(tempname);
529 fclose(ibuf);
530 relsesigs();
531 longjmp(srbuf, 1);
532 }
533 if ((obuf = fdopen(tmpfd, "w")) == NULL) {
534 perror(tempname);
535 fclose(ibuf);
536 removefile(tempname);
537 relsesigs();
538 (void) close(tmpfd);
539 longjmp(srbuf, 1);
540 }
541 fseek(ibuf, mailsize, 0);
542 while ((c = getc(ibuf)) != EOF)
543 putc(c, obuf);
544 fclose(obuf);
545 if ((tbuf = fopen(tempname, "r")) == NULL) {
546 perror(tempname);
547 fclose(ibuf);
548 removefile(tempname);
549 relsesigs();
550 longjmp(srbuf, 1);
551 }
552 removefile(tempname);
553 }
554 if ((obuf = fopen(editfile, "r+")) == NULL) {
555 if ((obuf = fopen(editfile, "w")) == NULL) {
556 perror(editfile);
557 fclose(ibuf);
558 if (tbuf)
559 fclose(tbuf);
560 relsesigs();
561 longjmp(srbuf, 1);
562 }
563 }
564 printf("\"%s\" ", editfile);
565 flush();
566 c = 0;
567 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
568 if ((mp->m_flag & MDELETED) != 0)
569 continue;
570 c++;
571 if (msend(mp, obuf, 0, fputs) < 0) {
572 perror(editfile);
573 fclose(ibuf);
574 fclose(obuf);
575 if (tbuf)
576 fclose(tbuf);
577 relsesigs();
578 longjmp(srbuf, 1);
579 }
580 }
581 gotcha = (c == 0 && tbuf == NULL);
582 if (tbuf != NULL) {
583 while ((c = getc(tbuf)) != EOF)
584 putc(c, obuf);
585 fclose(tbuf);
586 }
587 fflush(obuf);
588 if (fferror(obuf)) {
589 perror(editfile);
590 fclose(ibuf);
591 fclose(obuf);
592 relsesigs();
593 longjmp(srbuf, 1);
594 }
595 if (gotcha && !noremove && (value("keep") == NOSTR)) {
596 removefile(editfile);
597 printf(gettext("removed.\n"));
598 }
599 else
600 printf(gettext("updated.\n"));
601 fclose(ibuf);
602 trunc(obuf);
603 fclose(obuf);
604 flush();
605
606 done:
607 relsesigs();
608 return (1);
609 }
610
611 #ifndef OLD_BSD_SIGS
612 static int sigdepth = 0; /* depth of holdsigs() */
613 #ifdef VMUNIX
614 static int omask = 0;
615 #else
616 static sigset_t mask, omask;
617 #endif
618 #endif
619 /*
620 * Hold signals SIGHUP - SIGQUIT.
621 */
622 void
holdsigs(void)623 holdsigs(void)
624 {
625 #ifndef OLD_BSD_SIGS
626 if (sigdepth++ == 0) {
627 #ifdef VMUNIX
628 omask = sigblock(sigmask(SIGHUP) |
629 sigmask(SIGINT)|sigmask(SIGQUIT));
630 #else
631 sigemptyset(&mask);
632 sigaddset(&mask, SIGHUP);
633 sigaddset(&mask, SIGINT);
634 sigaddset(&mask, SIGQUIT);
635 sigprocmask(SIG_BLOCK, &mask, &omask);
636 #endif
637 }
638 #else
639 sighold(SIGHUP);
640 sighold(SIGINT);
641 sighold(SIGQUIT);
642 #endif
643 }
644
645 /*
646 * Release signals SIGHUP - SIGQUIT
647 */
648 void
relsesigs(void)649 relsesigs(void)
650 {
651 #ifndef OLD_BSD_SIGS
652 if (--sigdepth == 0)
653 #ifdef VMUNIX
654 sigsetmask(omask);
655 #else
656 sigprocmask(SIG_SETMASK, &omask, NULL);
657 #endif
658 #else
659 sigrelse(SIGHUP);
660 sigrelse(SIGINT);
661 sigrelse(SIGQUIT);
662 #endif
663 }
664
665 #if !defined(OLD_BSD_SIGS) && !defined(VMUNIX)
666 void
sigset(int sig,void (* act)(int))667 (*sigset(int sig, void (*act)(int)))(int)
668 {
669 struct sigaction sa, osa;
670
671 sa.sa_handler = (void (*)())act;
672 sigemptyset(&sa.sa_mask);
673 sa.sa_flags = SA_RESTART;
674 if (sigaction(sig, &sa, &osa) < 0)
675 return ((void (*)(int))-1);
676 return ((void (*)(int))osa.sa_handler);
677 }
678 #endif
679
680 /*
681 * Flush the standard output.
682 */
683
684 void
flush(void)685 flush(void)
686 {
687 fflush(stdout);
688 fflush(stderr);
689 }
690
691 /*
692 * Determine the size of the file possessed by
693 * the passed buffer.
694 */
695
696 off_t
fsize(FILE * iob)697 fsize(FILE *iob)
698 {
699 register int f;
700 struct stat sbuf;
701
702 f = fileno(iob);
703 if (fstat(f, &sbuf) < 0)
704 return (0);
705 return (sbuf.st_size);
706 }
707
708 /*
709 * Check for either a stdio recognized error, or
710 * a possibly delayed write error (in case it's
711 * an NFS file, for instance).
712 */
713
714 int
fferror(FILE * iob)715 fferror(FILE *iob)
716 {
717 return (ferror(iob) || fsync(fileno(iob)) < 0);
718 }
719
720 /*
721 * Take a file name, possibly with shell meta characters
722 * in it and expand it by using wordexp().
723 * Return the file name as a dynamic string.
724 * If the name cannot be expanded (for whatever reason)
725 * return NULL.
726 */
727
728 char *
expand(char * name)729 expand(char *name)
730 {
731 char xname[BUFSIZ];
732 char foldbuf[BUFSIZ];
733 register char *cp;
734 register int l;
735 wordexp_t wrdexp_buf;
736
737 if (debug) fprintf(stderr, "expand(%s)=", name);
738 cp = strchr(name, '\0') - 1; /* pointer to last char of name */
739 if (isspace(*cp)) {
740 /* strip off trailing blanks */
741 while (cp > name && isspace(*cp))
742 cp--;
743 l = *++cp; /* save char */
744 *cp = '\0';
745 name = savestr(name);
746 *cp = (char)l; /* restore char */
747 }
748 if (name[0] == '+' && getfold(foldbuf) >= 0) {
749 snprintf(xname, sizeof (xname), "%s/%s", foldbuf, name + 1);
750 cp = safeexpand(savestr(xname));
751 if (debug) fprintf(stderr, "%s\n", cp);
752 return (cp);
753 }
754 if (!anyof(name, "~{[*?$`'\"\\")) {
755 if (debug) fprintf(stderr, "%s\n", name);
756 return (name);
757 }
758 if (wordexp(name, &wrdexp_buf, 0) != 0) {
759 fprintf(stderr, gettext("Syntax error in \"%s\"\n"), name);
760 fflush(stderr);
761 return (NOSTR);
762 }
763 if (wrdexp_buf.we_wordc > 1) {
764 fprintf(stderr, gettext("\"%s\": Ambiguous\n"), name);
765 fflush(stderr);
766 return (NOSTR);
767 }
768 if (debug) fprintf(stderr, "%s\n", wrdexp_buf.we_wordv[0]);
769 return (savestr(wrdexp_buf.we_wordv[0]));
770 }
771
772 /*
773 * Take a file name, possibly with shell meta characters
774 * in it and expand it by using "sh -c echo filename"
775 * Return the file name as a dynamic string.
776 * If the name cannot be expanded (for whatever reason)
777 * return the original file name.
778 */
779
780 char *
safeexpand(char name[])781 safeexpand(char name[])
782 {
783 char *t = expand(name);
784 return (t) ? t : savestr(name);
785 }
786
787 /*
788 * Determine the current folder directory name.
789 */
790 int
getfold(char * name)791 getfold(char *name)
792 {
793 char *folder;
794
795 if ((folder = value("folder")) == NOSTR || *folder == '\0')
796 return (-1);
797 /*
798 * If name looks like a folder name, don't try
799 * to expand it, to prevent infinite recursion.
800 */
801 if (*folder != '+' && (folder = expand(folder)) == NOSTR ||
802 *folder == '\0')
803 return (-1);
804 if (*folder == '/') {
805 nstrcpy(name, BUFSIZ, folder);
806 } else
807 snprintf(name, BUFSIZ, "%s/%s", homedir, folder);
808 return (0);
809 }
810
811 /*
812 * A nicer version of Fdopen, which allows us to fclose
813 * without losing the open file.
814 */
815
816 FILE *
Fdopen(int fildes,char * mode)817 Fdopen(int fildes, char *mode)
818 {
819 register int f;
820
821 f = dup(fildes);
822 if (f < 0) {
823 perror("dup");
824 return (NULL);
825 }
826 return (fdopen(f, mode));
827 }
828
829 /*
830 * return the filename associated with "s". This function always
831 * returns a non-null string (no error checking is done on the receiving end)
832 */
833 char *
Getf(register char * s)834 Getf(register char *s)
835 {
836 register char *cp;
837 static char defbuf[PATHSIZE];
838
839 if (((cp = value(s)) != 0) && *cp) {
840 return (safeexpand(cp));
841 } else if (strcmp(s, "MBOX") == 0) {
842 snprintf(defbuf, sizeof (defbuf), "%s/%s", Getf("HOME"),
843 "mbox");
844 return (defbuf);
845 } else if (strcmp(s, "DEAD") == 0) {
846 snprintf(defbuf, sizeof (defbuf), "%s/%s", Getf("HOME"),
847 "dead.letter");
848 return (defbuf);
849 } else if (strcmp(s, "MAILRC") == 0) {
850 snprintf(defbuf, sizeof (defbuf), "%s/%s", Getf("HOME"),
851 ".mailrc");
852 return (defbuf);
853 } else if (strcmp(s, "HOME") == 0) {
854 /* no recursion allowed! */
855 return (".");
856 }
857 return ("DEAD"); /* "cannot happen" */
858 }
859