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.10 6/28/83"; 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: && index(name, ' ') == NOSTR) 551: return(name); 552: gotlt = 0; 553: lastsp = 0; 554: for (cp = name, cp2 = nbuf; c = *cp++; ) { 555: switch (c) { 556: case '(': 557: while (*cp != ')' && *cp != 0) 558: cp++; 559: if (*cp) 560: cp++; 561: lastsp = 0; 562: break; 563: 564: case ' ': 565: if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 566: cp += 3, *cp2++ = '@'; 567: else 568: if (cp[0] == '@' && cp[1] == ' ') 569: cp += 2, *cp2++ = '@'; 570: else 571: lastsp = 1; 572: break; 573: 574: case '<': 575: cp2 = nbuf; 576: gotlt++; 577: lastsp = 0; 578: break; 579: 580: case '>': 581: if (gotlt) 582: goto done; 583: 584: /* Fall into . . . */ 585: 586: default: 587: if (lastsp) { 588: lastsp = 0; 589: *cp2++ = ' '; 590: } 591: *cp2++ = c; 592: break; 593: } 594: } 595: done: 596: *cp2 = 0; 597: 598: return(savestr(nbuf)); 599: } 600: 601: /* 602: * Fetch the sender's name from the passed message. 603: * Reptype can be 604: * 0 -- get sender's name for display purposes 605: * 1 -- get sender's name for reply 606: * 2 -- get sender's name for Reply 607: */ 608: 609: char * 610: name1(mp, reptype) 611: register struct message *mp; 612: { 613: char namebuf[LINESIZE]; 614: char linebuf[LINESIZE]; 615: register char *cp, *cp2; 616: register FILE *ibuf; 617: int first = 1; 618: 619: #ifndef SENDMAIL 620: if ((cp = hfield("from", mp)) != NOSTR) 621: return(cp); 622: if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 623: return(cp); 624: #endif 625: ibuf = setinput(mp); 626: copy("", namebuf); 627: if (readline(ibuf, linebuf) <= 0) 628: return(savestr(namebuf)); 629: newname: 630: for (cp = linebuf; *cp != ' '; cp++) 631: ; 632: while (any(*cp, " \t")) 633: cp++; 634: for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") && 635: cp2-namebuf < LINESIZE-1; *cp2++ = *cp++) 636: ; 637: *cp2 = '\0'; 638: if (readline(ibuf, linebuf) <= 0) 639: return(savestr(namebuf)); 640: if ((cp = index(linebuf, 'F')) == NULL) 641: return(savestr(namebuf)); 642: if (strncmp(cp, "From", 4) != 0) 643: return(savestr(namebuf)); 644: while ((cp = index(cp, 'r')) != NULL) { 645: if (strncmp(cp, "remote", 6) == 0) { 646: if ((cp = index(cp, 'f')) == NULL) 647: break; 648: if (strncmp(cp, "from", 4) != 0) 649: break; 650: if ((cp = index(cp, ' ')) == NULL) 651: break; 652: cp++; 653: if (first) { 654: copy(cp, namebuf); 655: first = 0; 656: } else 657: strcpy(rindex(namebuf, '!')+1, cp); 658: strcat(namebuf, "!"); 659: goto newname; 660: } 661: cp++; 662: } 663: return(savestr(namebuf)); 664: } 665: 666: /* 667: * Count the occurances of c in str 668: */ 669: charcount(str, c) 670: char *str; 671: { 672: register char *cp; 673: register int i; 674: 675: for (i = 0, cp = str; *cp; cp++) 676: if (*cp == c) 677: i++; 678: return(i); 679: } 680: 681: /* 682: * Find the rightmost pointer to an instance of the 683: * character in the string and return it. 684: */ 685: char * 686: rindex(str, c) 687: char str[]; 688: register int c; 689: { 690: register char *cp, *cp2; 691: 692: for (cp = str, cp2 = NOSTR; *cp; cp++) 693: if (c == *cp) 694: cp2 = cp; 695: return(cp2); 696: } 697: 698: /* 699: * See if the string is a number. 700: */ 701: 702: numeric(str) 703: char str[]; 704: { 705: register char *cp = str; 706: 707: while (*cp) 708: if (!isdigit(*cp++)) 709: return(0); 710: return(1); 711: } 712: 713: /* 714: * Are any of the characters in the two strings the same? 715: */ 716: 717: anyof(s1, s2) 718: register char *s1, *s2; 719: { 720: register int c; 721: 722: while (c = *s1++) 723: if (any(c, s2)) 724: return(1); 725: return(0); 726: } 727: 728: /* 729: * Determine the leftmost index of the character 730: * in the string. 731: */ 732: 733: char * 734: index(str, ch) 735: char *str; 736: { 737: register char *cp; 738: register int c; 739: 740: for (c = ch, cp = str; *cp; cp++) 741: if (*cp == c) 742: return(cp); 743: return(NOSTR); 744: } 745: 746: /* 747: * String compare two strings of bounded length. 748: */ 749: 750: strncmp(as1, as2, an) 751: char *as1, *as2; 752: { 753: register char *s1, *s2; 754: register int n; 755: 756: s1 = as1; 757: s2 = as2; 758: n = an; 759: while (--n >= 0 && *s1 == *s2++) 760: if (*s1++ == '\0') 761: return(0); 762: return(n<0 ? 0 : *s1 - *--s2); 763: } 764: 765: /* 766: * See if the given header field is supposed to be ignored. 767: */ 768: isign(field) 769: char *field; 770: { 771: char realfld[BUFSIZ]; 772: register int h; 773: register struct ignore *igp; 774: 775: istrcpy(realfld, field); 776: h = hash(realfld); 777: for (igp = ignore[h]; igp != 0; igp = igp->i_link) 778: if (strcmp(igp->i_field, realfld) == 0) 779: return(1); 780: return(0); 781: }