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