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