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