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 1.6 03/11/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 399 if ((fi = fopen(name, "r")) == NULL) { 400 perror(name); 401 return(1); 402 } 403 if (ssp >= _NFILE-2) { 404 printf("Too much \"sourcing\" going on.\n"); 405 fclose(fi); 406 return(1); 407 } 408 sstack[++ssp].s_file = input; 409 sstack[ssp].s_cond = cond; 410 cond = CANY; 411 input = fi; 412 sourcing++; 413 return(0); 414 } 415 416 /* 417 * Source a file, but do nothing if the file cannot be opened. 418 */ 419 420 source1(name) 421 char name[]; 422 { 423 register int f; 424 425 if ((f = open(name, 0)) < 0) 426 return(0); 427 close(f); 428 source(name); 429 } 430 431 /* 432 * Pop the current input back to the previous level. 433 * Update the "sourcing" flag as appropriate. 434 */ 435 436 unstack() 437 { 438 if (ssp < 0) { 439 printf("\"Source\" stack over-pop.\n"); 440 sourcing = 0; 441 return(1); 442 } 443 fclose(input); 444 if (cond != CANY) 445 printf("Unmatched \"if\"\n"); 446 cond = sstack[ssp].s_cond; 447 input = sstack[ssp--].s_file; 448 if (ssp < 0) 449 sourcing = 0; 450 return(0); 451 } 452 453 /* 454 * Touch the indicated file. 455 * This is nifty for the shell. 456 * If we have the utime() system call, this is better served 457 * by using that, since it will work for empty files. 458 * On non-utime systems, we must sleep a second, then read. 459 */ 460 461 alter(name) 462 char name[]; 463 { 464 #ifdef UTIME 465 struct stat statb; 466 long time(); 467 time_t time_p[2]; 468 #else 469 register int pid, f; 470 char w; 471 #endif UTIME 472 473 #ifdef UTIME 474 if (stat(name, &statb) < 0) 475 return; 476 time_p[0] = time((long *) 0) + 1; 477 time_p[1] = statb.st_mtime; 478 utime(name, time_p); 479 #else 480 if ((pid = fork()) != 0) 481 return; 482 clrbuf(stdout); 483 clrbuf(stderr); 484 clrbuf(stdin); 485 sleep(1); 486 if ((f = open(name, 0)) < 0) 487 exit(1); 488 read(f, &w, 1); 489 exit(0); 490 #endif 491 } 492 493 /* 494 * Examine the passed line buffer and 495 * return true if it is all blanks and tabs. 496 */ 497 498 blankline(linebuf) 499 char linebuf[]; 500 { 501 register char *cp; 502 503 for (cp = linebuf; *cp; cp++) 504 if (!any(*cp, " \t")) 505 return(0); 506 return(1); 507 } 508 509 /* 510 * Get sender's name from this message. If the message has 511 * a bunch of arpanet stuff in it, we may have to skin the name 512 * before returning it. 513 */ 514 char * 515 nameof(mp, reptype) 516 register struct message *mp; 517 { 518 519 return(skin(name1(mp, reptype))); 520 } 521 522 /* 523 * Skin an arpa net address according to the RFC 733 interpretation 524 * of "host-phrase." 525 */ 526 char * 527 skin(name) 528 char *name; 529 { 530 register int c; 531 register char *cp, *cp2; 532 int gotlt, lastsp; 533 char nbuf[BUFSIZ]; 534 535 if (name == NOSTR) 536 return(NOSTR); 537 if (index(name, '(') == NOSTR && index(name, '<') == NOSTR) 538 return(name); 539 gotlt = 0; 540 lastsp = 0; 541 for (cp = name, cp2 = nbuf, c = *cp++; *cp; c = *cp++) { 542 switch (c) { 543 case '(': 544 while (*cp != ')' && *cp != 0) 545 cp++; 546 if (*cp) 547 cp++; 548 break; 549 550 case ' ': 551 lastsp = 1; 552 break; 553 554 case '<': 555 cp2 = nbuf; 556 gotlt++; 557 lastsp = 0; 558 break; 559 560 case '>': 561 if (gotlt) 562 goto done; 563 564 /* Fall into . . . */ 565 566 default: 567 if (lastsp) { 568 lastsp = 0; 569 *cp2++ = ' '; 570 } 571 *cp2++ = c; 572 break; 573 } 574 } 575 done: 576 *cp2 = 0; 577 578 return(savestr(nbuf)); 579 } 580 581 /* 582 * Fetch the sender's name from the passed message. 583 * Reptype can be 584 * 0 -- get sender's name for display purposes 585 * 1 -- get sender's name for reply 586 * 2 -- get sender's name for Reply 587 */ 588 589 char * 590 name1(mp, reptype) 591 register struct message *mp; 592 { 593 char namebuf[LINESIZE]; 594 char linebuf[LINESIZE]; 595 register char *cp, *cp2; 596 register FILE *ibuf; 597 int first = 1; 598 599 #ifndef DELIVERMAIL 600 if ((cp = hfield("from", mp)) != NOSTR) 601 return(cp); 602 if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 603 return(cp); 604 #endif 605 ibuf = setinput(mp); 606 copy("", namebuf); 607 if (readline(ibuf, linebuf) <= 0) 608 return(savestr(namebuf)); 609 newname: 610 for (cp = linebuf; *cp != ' '; cp++) 611 ; 612 while (any(*cp, " \t")) 613 cp++; 614 for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") && 615 cp2-namebuf < LINESIZE-1; *cp2++ = *cp++) 616 ; 617 *cp2 = '\0'; 618 if (readline(ibuf, linebuf) <= 0) 619 return(savestr(namebuf)); 620 if ((cp = index(linebuf, 'F')) == NULL) 621 return(savestr(namebuf)); 622 if (strncmp(cp, "From", 4) != 0) 623 return(savestr(namebuf)); 624 while ((cp = index(cp, 'r')) != NULL) { 625 if (strncmp(cp, "remote", 6) == 0) { 626 if ((cp = index(cp, 'f')) == NULL) 627 break; 628 if (strncmp(cp, "from", 4) != 0) 629 break; 630 if ((cp = index(cp, ' ')) == NULL) 631 break; 632 cp++; 633 if (first) { 634 copy(cp, namebuf); 635 first = 0; 636 } else 637 strcpy(rindex(namebuf, '!')+1, cp); 638 strcat(namebuf, "!"); 639 goto newname; 640 } 641 cp++; 642 } 643 return(savestr(namebuf)); 644 } 645 646 /* 647 * Find the rightmost pointer to an instance of the 648 * character in the string and return it. 649 */ 650 651 char * 652 rindex(str, c) 653 char str[]; 654 register int c; 655 { 656 register char *cp, *cp2; 657 658 for (cp = str, cp2 = NOSTR; *cp; cp++) 659 if (c == *cp) 660 cp2 = cp; 661 return(cp2); 662 } 663 664 /* 665 * See if the string is a number. 666 */ 667 668 numeric(str) 669 char str[]; 670 { 671 register char *cp = str; 672 673 while (*cp) 674 if (!isdigit(*cp++)) 675 return(0); 676 return(1); 677 } 678 679 /* 680 * Are any of the characters in the two strings the same? 681 */ 682 683 anyof(s1, s2) 684 register char *s1, *s2; 685 { 686 register int c; 687 688 while (c = *s1++) 689 if (any(c, s2)) 690 return(1); 691 return(0); 692 } 693 694 /* 695 * Determine the leftmost index of the character 696 * in the string. 697 */ 698 699 char * 700 index(str, ch) 701 char *str; 702 { 703 register char *cp; 704 register int c; 705 706 for (c = ch, cp = str; *cp; cp++) 707 if (*cp == c) 708 return(cp); 709 return(NOSTR); 710 } 711 712 /* 713 * String compare two strings of bounded length. 714 */ 715 716 strncmp(as1, as2, an) 717 char *as1, *as2; 718 { 719 register char *s1, *s2; 720 register int n; 721 722 s1 = as1; 723 s2 = as2; 724 n = an; 725 while (--n >= 0 && *s1 == *s2++) 726 if (*s1++ == '\0') 727 return(0); 728 return(n<0 ? 0 : *s1 - *--s2); 729 } 730 731