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