1 # 2 3 #include "rcv.h" 4 #include <sys/stat.h> 5 #include <ctype.h> 6 7 /* 8 * Mail -- a mail program 9 * 10 * Auxiliary functions. 11 */ 12 13 static char *SccsId = "@(#)aux.c 2.4 02/13/82"; 14 15 /* 16 * Return a pointer to a dynamic copy of the argument. 17 */ 18 19 char * 20 savestr(str) 21 char *str; 22 { 23 register char *cp, *cp2, *top; 24 25 for (cp = str; *cp; cp++) 26 ; 27 top = salloc(cp-str + 1); 28 if (top == NOSTR) 29 return(NOSTR); 30 for (cp = str, cp2 = top; *cp; cp++) 31 *cp2++ = *cp; 32 *cp2 = 0; 33 return(top); 34 } 35 36 /* 37 * Copy the name from the passed header line into the passed 38 * name buffer. Null pad the name buffer. 39 */ 40 41 copyname(linebuf, nbuf) 42 char *linebuf, *nbuf; 43 { 44 register char *cp, *cp2; 45 46 for (cp = linebuf + 5, cp2 = nbuf; *cp != ' ' && cp2-nbuf < 8; cp++) 47 *cp2++ = *cp; 48 while (cp2-nbuf < 8) 49 *cp2++ = 0; 50 } 51 52 /* 53 * Announce a fatal error and die. 54 */ 55 56 panic(str) 57 char *str; 58 { 59 prs("panic: "); 60 prs(str); 61 prs("\n"); 62 exit(1); 63 } 64 65 /* 66 * Catch stdio errors and report them more nicely. 67 */ 68 69 _error(str) 70 char *str; 71 { 72 prs("Stdio Error: "); 73 prs(str); 74 prs("\n"); 75 abort(); 76 } 77 78 /* 79 * Print a string on diagnostic output. 80 */ 81 82 prs(str) 83 char *str; 84 { 85 register char *s; 86 87 for (s = str; *s; s++) 88 ; 89 write(2, str, s-str); 90 } 91 92 /* 93 * Touch the named message by setting its MTOUCH flag. 94 * Touched messages have the effect of not being sent 95 * back to the system mailbox on exit. 96 */ 97 98 touch(mesg) 99 { 100 register struct message *mp; 101 102 if (mesg < 1 || mesg > msgCount) 103 return; 104 mp = &message[mesg-1]; 105 mp->m_flag |= MTOUCH; 106 if ((mp->m_flag & MREAD) == 0) 107 mp->m_flag |= MREAD|MSTATUS; 108 } 109 110 /* 111 * Test to see if the passed file name is a directory. 112 * Return true if it is. 113 */ 114 115 isdir(name) 116 char name[]; 117 { 118 struct stat sbuf; 119 120 if (stat(name, &sbuf) < 0) 121 return(0); 122 return((sbuf.st_mode & S_IFMT) == S_IFDIR); 123 } 124 125 /* 126 * Compute the size in characters of the passed message 127 */ 128 129 unsigned int 130 msize(messp) 131 struct message *messp; 132 { 133 register struct message *mp; 134 135 mp = messp; 136 return(mp->m_size); 137 } 138 139 /* 140 * Count the number of arguments in the given string raw list. 141 */ 142 143 argcount(argv) 144 char **argv; 145 { 146 register char **ap; 147 148 for (ap = argv; *ap != NOSTR; ap++) 149 ; 150 return(ap-argv); 151 } 152 153 /* 154 * Given a file address, determine the 155 * block number it represents. 156 */ 157 158 blockof(off) 159 off_t off; 160 { 161 off_t a; 162 163 a = off >> 9; 164 a &= 077777; 165 return((int) a); 166 } 167 168 /* 169 * Take a file address, and determine 170 * its offset in the current block. 171 */ 172 173 offsetof(off) 174 off_t off; 175 { 176 off_t a; 177 178 a = off & 0777; 179 return((int) a); 180 } 181 182 /* 183 * Determine if the passed file is actually a tty, via a call to 184 * gtty. This is not totally reliable, but . . . 185 */ 186 187 isatty(f) 188 { 189 struct sgttyb buf; 190 191 if (gtty(f, &buf) < 0) 192 return(0); 193 return(1); 194 } 195 196 /* 197 * Return the desired header line from the passed message 198 * pointer (or NOSTR if the desired header field is not available). 199 */ 200 201 char * 202 hfield(field, mp) 203 char field[]; 204 struct message *mp; 205 { 206 register FILE *ibuf; 207 char linebuf[LINESIZE]; 208 register int lc; 209 210 ibuf = setinput(mp); 211 if ((lc = mp->m_lines) <= 0) 212 return(NOSTR); 213 if (readline(ibuf, linebuf) < 0) 214 return(NOSTR); 215 lc--; 216 do { 217 lc = gethfield(ibuf, linebuf, lc); 218 if (lc == -1) 219 return(NOSTR); 220 if (ishfield(linebuf, field)) 221 return(savestr(hcontents(linebuf))); 222 } while (lc > 0); 223 return(NOSTR); 224 } 225 226 /* 227 * Return the next header field found in the given message. 228 * Return > 0 if something found, <= 0 elsewise. 229 * Must deal with \ continuations & other such fraud. 230 */ 231 232 gethfield(f, linebuf, rem) 233 register FILE *f; 234 char linebuf[]; 235 register int rem; 236 { 237 char line2[LINESIZE]; 238 long loc; 239 register char *cp, *cp2; 240 register int c; 241 242 243 for (;;) { 244 if (rem <= 0) 245 return(-1); 246 if (readline(f, linebuf) < 0) 247 return(-1); 248 rem--; 249 if (strlen(linebuf) == 0) 250 return(-1); 251 if (isspace(linebuf[0])) 252 continue; 253 if (linebuf[0] == '>') 254 continue; 255 cp = index(linebuf, ':'); 256 if (cp == NOSTR) 257 continue; 258 for (cp2 = linebuf; cp2 < cp; cp2++) 259 if (isdigit(*cp2)) 260 continue; 261 262 /* 263 * I guess we got a headline. 264 * Handle wraparounding 265 */ 266 267 for (;;) { 268 if (rem <= 0) 269 break; 270 #ifdef CANTELL 271 loc = ftell(f); 272 if (readline(f, line2) < 0) 273 break; 274 rem--; 275 if (!isspace(line2[0])) { 276 fseek(f, loc, 0); 277 rem++; 278 break; 279 } 280 #else 281 c = getc(f); 282 ungetc(c, f); 283 if (!isspace(c) || c == '\n') 284 break; 285 if (readline(f, line2) < 0) 286 break; 287 rem--; 288 #endif 289 cp2 = line2; 290 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) 291 ; 292 if (strlen(linebuf) + strlen(cp2) >= LINESIZE-2) 293 break; 294 cp = &linebuf[strlen(linebuf)]; 295 while (cp > linebuf && 296 (isspace(cp[-1]) || cp[-1] == '\\')) 297 cp--; 298 *cp++ = ' '; 299 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) 300 ; 301 strcpy(cp, cp2); 302 } 303 if ((c = strlen(linebuf)) > 0) { 304 cp = &linebuf[c-1]; 305 while (cp > linebuf && isspace(*cp)) 306 cp--; 307 *++cp = 0; 308 } 309 return(rem); 310 } 311 /* NOTREACHED */ 312 } 313 314 /* 315 * Check whether the passed line is a header line of 316 * the desired breed. 317 */ 318 319 ishfield(linebuf, field) 320 char linebuf[], field[]; 321 { 322 register char *cp; 323 register int c; 324 325 if ((cp = index(linebuf, ':')) == NOSTR) 326 return(0); 327 if (cp == linebuf) 328 return(0); 329 cp--; 330 while (cp > linebuf && isspace(*cp)) 331 cp--; 332 c = *++cp; 333 *cp = 0; 334 if (icequal(linebuf ,field)) { 335 *cp = c; 336 return(1); 337 } 338 *cp = c; 339 return(0); 340 } 341 342 /* 343 * Extract the non label information from the given header field 344 * and return it. 345 */ 346 347 char * 348 hcontents(hfield) 349 char hfield[]; 350 { 351 register char *cp; 352 353 if ((cp = index(hfield, ':')) == NOSTR) 354 return(NOSTR); 355 cp++; 356 while (*cp && isspace(*cp)) 357 cp++; 358 return(cp); 359 } 360 361 /* 362 * Compare two strings, ignoring case. 363 */ 364 365 icequal(s1, s2) 366 register char *s1, *s2; 367 { 368 369 while (raise(*s1++) == raise(*s2)) 370 if (*s2++ == 0) 371 return(1); 372 return(0); 373 } 374 375 /* 376 * The following code deals with input stacking to do source 377 * commands. All but the current file pointer are saved on 378 * the stack. 379 */ 380 381 static int ssp = -1; /* Top of file stack */ 382 struct sstack { 383 FILE *s_file; /* File we were in. */ 384 int s_cond; /* Saved state of conditionals */ 385 int s_loading; /* Loading .mailrc, etc. */ 386 } sstack[_NFILE]; 387 388 /* 389 * Pushdown current input file and switch to a new one. 390 * Set the global flag "sourcing" so that others will realize 391 * that they are no longer reading from a tty (in all probability). 392 */ 393 394 source(name) 395 char name[]; 396 { 397 register FILE *fi; 398 register char *cp; 399 400 if ((cp = expand(name)) == NOSTR) 401 return(1); 402 if ((fi = fopen(cp, "r")) == NULL) { 403 perror(cp); 404 return(1); 405 } 406 if (ssp >= _NFILE-2) { 407 printf("Too much \"sourcing\" going on.\n"); 408 fclose(fi); 409 return(1); 410 } 411 sstack[++ssp].s_file = input; 412 sstack[ssp].s_cond = cond; 413 sstack[ssp].s_loading = loading; 414 loading = 0; 415 cond = CANY; 416 input = fi; 417 sourcing++; 418 return(0); 419 } 420 421 /* 422 * Source a file, but do nothing if the file cannot be opened. 423 */ 424 425 source1(name) 426 char name[]; 427 { 428 register int f; 429 430 if ((f = open(name, 0)) < 0) 431 return(0); 432 close(f); 433 source(name); 434 } 435 436 /* 437 * Pop the current input back to the previous level. 438 * Update the "sourcing" flag as appropriate. 439 */ 440 441 unstack() 442 { 443 if (ssp < 0) { 444 printf("\"Source\" stack over-pop.\n"); 445 sourcing = 0; 446 return(1); 447 } 448 fclose(input); 449 if (cond != CANY) 450 printf("Unmatched \"if\"\n"); 451 cond = sstack[ssp].s_cond; 452 loading = sstack[ssp].s_loading; 453 input = sstack[ssp--].s_file; 454 if (ssp < 0) 455 sourcing = loading; 456 return(0); 457 } 458 459 /* 460 * Touch the indicated file. 461 * This is nifty for the shell. 462 * If we have the utime() system call, this is better served 463 * by using that, since it will work for empty files. 464 * On non-utime systems, we must sleep a second, then read. 465 */ 466 467 alter(name) 468 char name[]; 469 { 470 #ifdef UTIME 471 struct stat statb; 472 long time(); 473 time_t time_p[2]; 474 #else 475 register int pid, f; 476 char w; 477 #endif UTIME 478 479 #ifdef UTIME 480 if (stat(name, &statb) < 0) 481 return; 482 time_p[0] = time((long *) 0) + 1; 483 time_p[1] = statb.st_mtime; 484 utime(name, time_p); 485 #else 486 sleep(1); 487 if ((f = open(name, 0)) < 0) 488 return; 489 read(f, &w, 1); 490 exit(0); 491 #endif 492 } 493 494 /* 495 * Examine the passed line buffer and 496 * return true if it is all blanks and tabs. 497 */ 498 499 blankline(linebuf) 500 char linebuf[]; 501 { 502 register char *cp; 503 504 for (cp = linebuf; *cp; cp++) 505 if (!any(*cp, " \t")) 506 return(0); 507 return(1); 508 } 509 510 /* 511 * Get sender's name from this message. If the message has 512 * a bunch of arpanet stuff in it, we may have to skin the name 513 * before returning it. 514 */ 515 char * 516 nameof(mp, reptype) 517 register struct message *mp; 518 { 519 register char *cp, *cp2; 520 521 cp = skin(name1(mp, reptype)); 522 if (reptype != 0 || charcount(cp, '!') < 2) 523 return(cp); 524 cp2 = rindex(cp, '!'); 525 cp2--; 526 while (cp2 > cp && *cp2 != '!') 527 cp2--; 528 if (*cp2 == '!') 529 return(cp2 + 1); 530 return(cp); 531 } 532 533 /* 534 * Skin an arpa net address according to the RFC 733 interpretation 535 * of "host-phrase." 536 */ 537 char * 538 skin(name) 539 char *name; 540 { 541 register int c; 542 register char *cp, *cp2; 543 int gotlt, lastsp; 544 char nbuf[BUFSIZ]; 545 546 if (name == NOSTR) 547 return(NOSTR); 548 if (index(name, '(') == NOSTR && index(name, '<') == NOSTR) 549 return(name); 550 gotlt = 0; 551 lastsp = 0; 552 for (cp = name, cp2 = nbuf, c = *cp++; *cp; c = *cp++) { 553 switch (c) { 554 case '(': 555 while (*cp != ')' && *cp != 0) 556 cp++; 557 if (*cp) 558 cp++; 559 break; 560 561 case ' ': 562 lastsp = 1; 563 break; 564 565 case '<': 566 cp2 = nbuf; 567 gotlt++; 568 lastsp = 0; 569 break; 570 571 case '>': 572 if (gotlt) 573 goto done; 574 575 /* Fall into . . . */ 576 577 default: 578 if (lastsp) { 579 lastsp = 0; 580 *cp2++ = ' '; 581 } 582 *cp2++ = c; 583 break; 584 } 585 } 586 done: 587 *cp2 = 0; 588 589 return(savestr(nbuf)); 590 } 591 592 /* 593 * Fetch the sender's name from the passed message. 594 * Reptype can be 595 * 0 -- get sender's name for display purposes 596 * 1 -- get sender's name for reply 597 * 2 -- get sender's name for Reply 598 */ 599 600 char * 601 name1(mp, reptype) 602 register struct message *mp; 603 { 604 char namebuf[LINESIZE]; 605 char linebuf[LINESIZE]; 606 register char *cp, *cp2; 607 register FILE *ibuf; 608 int first = 1; 609 610 #ifndef DELIVERMAIL 611 if ((cp = hfield("from", mp)) != NOSTR) 612 return(cp); 613 if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 614 return(cp); 615 #endif 616 ibuf = setinput(mp); 617 copy("", namebuf); 618 if (readline(ibuf, linebuf) <= 0) 619 return(savestr(namebuf)); 620 newname: 621 for (cp = linebuf; *cp != ' '; cp++) 622 ; 623 while (any(*cp, " \t")) 624 cp++; 625 for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") && 626 cp2-namebuf < LINESIZE-1; *cp2++ = *cp++) 627 ; 628 *cp2 = '\0'; 629 if (readline(ibuf, linebuf) <= 0) 630 return(savestr(namebuf)); 631 if ((cp = index(linebuf, 'F')) == NULL) 632 return(savestr(namebuf)); 633 if (strncmp(cp, "From", 4) != 0) 634 return(savestr(namebuf)); 635 while ((cp = index(cp, 'r')) != NULL) { 636 if (strncmp(cp, "remote", 6) == 0) { 637 if ((cp = index(cp, 'f')) == NULL) 638 break; 639 if (strncmp(cp, "from", 4) != 0) 640 break; 641 if ((cp = index(cp, ' ')) == NULL) 642 break; 643 cp++; 644 if (first) { 645 copy(cp, namebuf); 646 first = 0; 647 } else 648 strcpy(rindex(namebuf, '!')+1, cp); 649 strcat(namebuf, "!"); 650 goto newname; 651 } 652 cp++; 653 } 654 return(savestr(namebuf)); 655 } 656 657 /* 658 * Count the occurances of c in str 659 */ 660 charcount(str, c) 661 char *str; 662 { 663 register char *cp; 664 register int i; 665 666 for (i = 0, cp = str; *cp; cp++) 667 if (*cp == c) 668 i++; 669 return(i); 670 } 671 672 /* 673 * Find the rightmost pointer to an instance of the 674 * character in the string and return it. 675 */ 676 char * 677 rindex(str, c) 678 char str[]; 679 register int c; 680 { 681 register char *cp, *cp2; 682 683 for (cp = str, cp2 = NOSTR; *cp; cp++) 684 if (c == *cp) 685 cp2 = cp; 686 return(cp2); 687 } 688 689 /* 690 * See if the string is a number. 691 */ 692 693 numeric(str) 694 char str[]; 695 { 696 register char *cp = str; 697 698 while (*cp) 699 if (!isdigit(*cp++)) 700 return(0); 701 return(1); 702 } 703 704 /* 705 * Are any of the characters in the two strings the same? 706 */ 707 708 anyof(s1, s2) 709 register char *s1, *s2; 710 { 711 register int c; 712 713 while (c = *s1++) 714 if (any(c, s2)) 715 return(1); 716 return(0); 717 } 718 719 /* 720 * Determine the leftmost index of the character 721 * in the string. 722 */ 723 724 char * 725 index(str, ch) 726 char *str; 727 { 728 register char *cp; 729 register int c; 730 731 for (c = ch, cp = str; *cp; cp++) 732 if (*cp == c) 733 return(cp); 734 return(NOSTR); 735 } 736 737 /* 738 * String compare two strings of bounded length. 739 */ 740 741 strncmp(as1, as2, an) 742 char *as1, *as2; 743 { 744 register char *s1, *s2; 745 register int n; 746 747 s1 = as1; 748 s2 = as2; 749 n = an; 750 while (--n >= 0 && *s1 == *s2++) 751 if (*s1++ == '\0') 752 return(0); 753 return(n<0 ? 0 : *s1 - *--s2); 754 } 755 756