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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 * 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 * 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 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 * 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 * 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