11219Skas # 21219Skas 31219Skas #include "rcv.h" 41219Skas #include <sys/stat.h> 51219Skas #include <sgtty.h> 61219Skas #include <ctype.h> 71219Skas 81219Skas /* 91219Skas * Mail -- a mail program 101219Skas * 111219Skas * Auxiliary functions. 121219Skas */ 131219Skas 14*3195Skas static char *SccsId = "@(#)aux.c 1.6 03/11/81"; 151219Skas 161219Skas /* 171219Skas * Return a pointer to a dynamic copy of the argument. 181219Skas */ 191219Skas 201219Skas char * 211219Skas savestr(str) 221219Skas char *str; 231219Skas { 241219Skas register char *cp, *cp2, *top; 251219Skas 261219Skas for (cp = str; *cp; cp++) 271219Skas ; 281219Skas top = salloc(cp-str + 1); 291219Skas if (top == NOSTR) 301219Skas return(NOSTR); 311219Skas for (cp = str, cp2 = top; *cp; cp++) 321219Skas *cp2++ = *cp; 331219Skas *cp2 = 0; 341219Skas return(top); 351219Skas } 361219Skas 371219Skas /* 381219Skas * Copy the name from the passed header line into the passed 391219Skas * name buffer. Null pad the name buffer. 401219Skas */ 411219Skas 421219Skas copyname(linebuf, nbuf) 431219Skas char *linebuf, *nbuf; 441219Skas { 451219Skas register char *cp, *cp2; 461219Skas 471219Skas for (cp = linebuf + 5, cp2 = nbuf; *cp != ' ' && cp2-nbuf < 8; cp++) 481219Skas *cp2++ = *cp; 491219Skas while (cp2-nbuf < 8) 501219Skas *cp2++ = 0; 511219Skas } 521219Skas 531219Skas /* 541219Skas * Announce a fatal error and die. 551219Skas */ 561219Skas 571219Skas panic(str) 581219Skas char *str; 591219Skas { 601219Skas prs("panic: "); 611219Skas prs(str); 621219Skas prs("\n"); 631219Skas exit(1); 641219Skas } 651219Skas 661219Skas /* 671219Skas * Catch stdio errors and report them more nicely. 681219Skas */ 691219Skas 701219Skas _error(str) 711219Skas char *str; 721219Skas { 731219Skas prs("Stdio Error: "); 741219Skas prs(str); 751219Skas prs("\n"); 761219Skas abort(); 771219Skas } 781219Skas 791219Skas /* 801219Skas * Print a string on diagnostic output. 811219Skas */ 821219Skas 831219Skas prs(str) 841219Skas char *str; 851219Skas { 861219Skas register char *s; 871219Skas 881219Skas for (s = str; *s; s++) 891219Skas ; 901219Skas write(2, str, s-str); 911219Skas } 921219Skas 931219Skas /* 941219Skas * Touch the named message by setting its MTOUCH flag. 951219Skas * Touched messages have the effect of not being sent 961219Skas * back to the system mailbox on exit. 971219Skas */ 981219Skas 991219Skas touch(mesg) 1001219Skas { 1011479Skas register struct message *mp; 1021479Skas 1031479Skas if (mesg < 1 || mesg > msgCount) 1041479Skas return; 1051479Skas mp = &message[mesg-1]; 1061479Skas mp->m_flag |= MTOUCH; 1071479Skas if ((mp->m_flag & MREAD) == 0) 1081479Skas mp->m_flag |= MREAD|MSTATUS; 1091219Skas } 1101219Skas 1111219Skas /* 1121219Skas * Test to see if the passed file name is a directory. 1131219Skas * Return true if it is. 1141219Skas */ 1151219Skas 1161219Skas isdir(name) 1171219Skas char name[]; 1181219Skas { 1191219Skas struct stat sbuf; 1201219Skas 1211219Skas if (stat(name, &sbuf) < 0) 1221219Skas return(0); 1231219Skas return((sbuf.st_mode & S_IFMT) == S_IFDIR); 1241219Skas } 1251219Skas 1261219Skas /* 1271219Skas * Compute the size in characters of the passed message 1281219Skas */ 1291219Skas 1301219Skas unsigned int 1311219Skas msize(messp) 1321219Skas struct message *messp; 1331219Skas { 1341219Skas register struct message *mp; 1351219Skas 1361219Skas mp = messp; 1371219Skas return(mp->m_size); 1381219Skas } 1391219Skas 1401219Skas /* 1411219Skas * Count the number of arguments in the given string raw list. 1421219Skas */ 1431219Skas 1441219Skas argcount(argv) 1451219Skas char **argv; 1461219Skas { 1471219Skas register char **ap; 1481219Skas 1491219Skas for (ap = argv; *ap != NOSTR; ap++) 1501219Skas ; 1511219Skas return(ap-argv); 1521219Skas } 1531219Skas 1541219Skas /* 1551219Skas * Given a file address, determine the 1561219Skas * block number it represents. 1571219Skas */ 1581219Skas 1591219Skas blockof(off) 1601219Skas off_t off; 1611219Skas { 1621219Skas off_t a; 1631219Skas 1641219Skas a = off >> 9; 1651219Skas a &= 077777; 1661219Skas return((int) a); 1671219Skas } 1681219Skas 1691219Skas /* 1701219Skas * Take a file address, and determine 1711219Skas * its offset in the current block. 1721219Skas */ 1731219Skas 1741219Skas offsetof(off) 1751219Skas off_t off; 1761219Skas { 1771219Skas off_t a; 1781219Skas 1791219Skas a = off & 0777; 1801219Skas return((int) a); 1811219Skas } 1821219Skas 1831219Skas /* 1841219Skas * Determine if the passed file is actually a tty, via a call to 1851219Skas * gtty. This is not totally reliable, but . . . 1861219Skas */ 1871219Skas 1881219Skas isatty(f) 1891219Skas { 1901219Skas struct sgttyb buf; 1911219Skas 1921219Skas if (gtty(f, &buf) < 0) 1931219Skas return(0); 1941219Skas return(1); 1951219Skas } 1961219Skas 1971219Skas /* 1981219Skas * Return the desired header line from the passed message 1991219Skas * pointer (or NOSTR if the desired header field is not available). 2001219Skas */ 2011219Skas 2021219Skas char * 2031219Skas hfield(field, mp) 2041219Skas char field[]; 2051219Skas struct message *mp; 2061219Skas { 2071219Skas register FILE *ibuf; 2081219Skas char linebuf[LINESIZE]; 2091219Skas register int lc; 2101219Skas 2111219Skas ibuf = setinput(mp); 2121219Skas if ((lc = mp->m_lines) <= 0) 2131219Skas return(NOSTR); 2141219Skas if (readline(ibuf, linebuf) < 0) 2151219Skas return(NOSTR); 2161219Skas lc--; 2171219Skas do { 2181219Skas lc = gethfield(ibuf, linebuf, lc); 2191219Skas if (lc == -1) 2201219Skas return(NOSTR); 2211219Skas if (ishfield(linebuf, field)) 2221219Skas return(savestr(hcontents(linebuf))); 2231219Skas } while (lc > 0); 2241219Skas return(NOSTR); 2251219Skas } 2261219Skas 2271219Skas /* 2281219Skas * Return the next header field found in the given message. 2291219Skas * Return > 0 if something found, <= 0 elsewise. 2301219Skas * Must deal with \ continuations & other such fraud. 2311219Skas */ 2321219Skas 2331219Skas gethfield(f, linebuf, rem) 2341219Skas register FILE *f; 2351219Skas char linebuf[]; 2361219Skas register int rem; 2371219Skas { 2381219Skas char line2[LINESIZE]; 2391219Skas long loc; 2401219Skas register char *cp, *cp2; 2411219Skas register int c; 2421219Skas 2431219Skas 2441219Skas for (;;) { 2451219Skas if (rem <= 0) 2461219Skas return(-1); 2471219Skas if (readline(f, linebuf) < 0) 2481219Skas return(-1); 2491219Skas rem--; 2501219Skas if (strlen(linebuf) == 0) 2511219Skas return(-1); 2521219Skas if (isspace(linebuf[0])) 2531219Skas continue; 2541219Skas if (linebuf[0] == '>') 2551219Skas continue; 2561219Skas cp = index(linebuf, ':'); 2571219Skas if (cp == NOSTR) 2581219Skas continue; 2591219Skas for (cp2 = linebuf; cp2 < cp; cp2++) 2601219Skas if (isdigit(*cp2)) 2611219Skas continue; 2621219Skas 2631219Skas /* 2641219Skas * I guess we got a headline. 2651219Skas * Handle wraparounding 2661219Skas */ 2671219Skas 2681219Skas for (;;) { 2691219Skas if (rem <= 0) 2701219Skas break; 2711219Skas #ifdef CANTELL 2721219Skas loc = ftell(f); 2731219Skas if (readline(f, line2) < 0) 2741219Skas break; 2751219Skas rem--; 2761219Skas if (!isspace(line2[0])) { 2771219Skas fseek(f, loc, 0); 2781219Skas rem++; 2791219Skas break; 2801219Skas } 2811219Skas #else 2821219Skas c = getc(f); 2831219Skas ungetc(c, f); 2841219Skas if (!isspace(c) || c == '\n') 2851219Skas break; 2861219Skas if (readline(f, line2) < 0) 2871219Skas break; 2881219Skas rem--; 2891219Skas #endif 2901219Skas cp2 = line2; 2911219Skas for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) 2921219Skas ; 2931219Skas if (strlen(linebuf) + strlen(cp2) >= LINESIZE-2) 2941219Skas break; 2951219Skas cp = &linebuf[strlen(linebuf)]; 2961219Skas while (cp > linebuf && 2971219Skas (isspace(cp[-1]) || cp[-1] == '\\')) 2981219Skas cp--; 2991219Skas *cp++ = ' '; 3001219Skas for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) 3011219Skas ; 3021219Skas strcpy(cp, cp2); 3031219Skas } 3041219Skas if ((c = strlen(linebuf)) > 0) { 3051219Skas cp = &linebuf[c-1]; 3061219Skas while (cp > linebuf && isspace(*cp)) 3071219Skas cp--; 3081219Skas *++cp = 0; 3091219Skas } 3101219Skas return(rem); 3111219Skas } 3121219Skas /* NOTREACHED */ 3131219Skas } 3141219Skas 3151219Skas /* 3161219Skas * Check whether the passed line is a header line of 3171219Skas * the desired breed. 3181219Skas */ 3191219Skas 3201219Skas ishfield(linebuf, field) 3211219Skas char linebuf[], field[]; 3221219Skas { 3231219Skas register char *cp; 3241219Skas register int c; 3251219Skas 3261219Skas if ((cp = index(linebuf, ':')) == NOSTR) 3271219Skas return(0); 3281219Skas if (cp == linebuf) 3291219Skas return(0); 3301219Skas cp--; 3311219Skas while (cp > linebuf && isspace(*cp)) 3321219Skas cp--; 3331219Skas c = *++cp; 3341219Skas *cp = 0; 3351219Skas if (icequal(linebuf ,field)) { 3361219Skas *cp = c; 3371219Skas return(1); 3381219Skas } 3391219Skas *cp = c; 3401219Skas return(0); 3411219Skas } 3421219Skas 3431219Skas /* 3441219Skas * Extract the non label information from the given header field 3451219Skas * and return it. 3461219Skas */ 3471219Skas 3481219Skas char * 3491219Skas hcontents(hfield) 3501219Skas char hfield[]; 3511219Skas { 3521219Skas register char *cp; 3531219Skas 3541219Skas if ((cp = index(hfield, ':')) == NOSTR) 3551219Skas return(NOSTR); 3561219Skas cp++; 3571219Skas while (*cp && isspace(*cp)) 3581219Skas cp++; 3591219Skas return(cp); 3601219Skas } 3611219Skas 3621219Skas /* 3631219Skas * Compare two strings, ignoring case. 3641219Skas */ 3651219Skas 3661219Skas icequal(s1, s2) 3671219Skas register char *s1, *s2; 3681219Skas { 3691219Skas 3701219Skas while (raise(*s1++) == raise(*s2)) 3711219Skas if (*s2++ == 0) 3721219Skas return(1); 3731219Skas return(0); 3741219Skas } 3751219Skas 3761219Skas /* 3771219Skas * The following code deals with input stacking to do source 3781219Skas * commands. All but the current file pointer are saved on 3791219Skas * the stack. 3801219Skas */ 3811219Skas 3821219Skas static int ssp = -1; /* Top of file stack */ 3831519Skas struct sstack { 3841519Skas FILE *s_file; /* File we were in. */ 3851519Skas int s_cond; /* Saved state of conditionals */ 3861519Skas } sstack[_NFILE]; 3871219Skas 3881219Skas /* 3891219Skas * Pushdown current input file and switch to a new one. 3901219Skas * Set the global flag "sourcing" so that others will realize 3911219Skas * that they are no longer reading from a tty (in all probability). 3921219Skas */ 3931219Skas 3941219Skas source(name) 3951219Skas char name[]; 3961219Skas { 3971219Skas register FILE *fi; 3981219Skas 3991219Skas if ((fi = fopen(name, "r")) == NULL) { 4001219Skas perror(name); 4011219Skas return(1); 4021219Skas } 4031219Skas if (ssp >= _NFILE-2) { 4041219Skas printf("Too much \"sourcing\" going on.\n"); 4051219Skas fclose(fi); 4061219Skas return(1); 4071219Skas } 4081519Skas sstack[++ssp].s_file = input; 4091519Skas sstack[ssp].s_cond = cond; 4101519Skas cond = CANY; 4111219Skas input = fi; 4121219Skas sourcing++; 4131219Skas return(0); 4141219Skas } 4151219Skas 4161219Skas /* 4171219Skas * Source a file, but do nothing if the file cannot be opened. 4181219Skas */ 4191219Skas 4201219Skas source1(name) 4211219Skas char name[]; 4221219Skas { 4231219Skas register int f; 4241219Skas 4251219Skas if ((f = open(name, 0)) < 0) 4261219Skas return(0); 4271219Skas close(f); 4281219Skas source(name); 4291219Skas } 4301219Skas 4311219Skas /* 4321219Skas * Pop the current input back to the previous level. 4331219Skas * Update the "sourcing" flag as appropriate. 4341219Skas */ 4351219Skas 4361219Skas unstack() 4371219Skas { 4381219Skas if (ssp < 0) { 4391219Skas printf("\"Source\" stack over-pop.\n"); 4401219Skas sourcing = 0; 4411219Skas return(1); 4421219Skas } 4431219Skas fclose(input); 4441519Skas if (cond != CANY) 4451519Skas printf("Unmatched \"if\"\n"); 4461519Skas cond = sstack[ssp].s_cond; 4471519Skas input = sstack[ssp--].s_file; 4481219Skas if (ssp < 0) 4491219Skas sourcing = 0; 4501219Skas return(0); 4511219Skas } 4521219Skas 4531219Skas /* 4541219Skas * Touch the indicated file. 4551219Skas * This is nifty for the shell. 4561219Skas * If we have the utime() system call, this is better served 4571219Skas * by using that, since it will work for empty files. 4581219Skas * On non-utime systems, we must sleep a second, then read. 4591219Skas */ 4601219Skas 4611219Skas alter(name) 4621219Skas char name[]; 4631219Skas { 4641219Skas #ifdef UTIME 4651219Skas struct stat statb; 4661219Skas long time(); 4671219Skas time_t time_p[2]; 4681219Skas #else 4691219Skas register int pid, f; 4701219Skas char w; 4711219Skas #endif UTIME 4721219Skas 4731219Skas #ifdef UTIME 4741219Skas if (stat(name, &statb) < 0) 4751219Skas return; 4761219Skas time_p[0] = time((long *) 0) + 1; 4771219Skas time_p[1] = statb.st_mtime; 4781219Skas utime(name, time_p); 4791219Skas #else 4801219Skas if ((pid = fork()) != 0) 4811219Skas return; 4821219Skas clrbuf(stdout); 4831219Skas clrbuf(stderr); 4841219Skas clrbuf(stdin); 4851219Skas sleep(1); 4861219Skas if ((f = open(name, 0)) < 0) 4871219Skas exit(1); 4881219Skas read(f, &w, 1); 4891219Skas exit(0); 4901219Skas #endif 4911219Skas } 4921219Skas 4931219Skas /* 4941219Skas * Examine the passed line buffer and 4951219Skas * return true if it is all blanks and tabs. 4961219Skas */ 4971219Skas 4981219Skas blankline(linebuf) 4991219Skas char linebuf[]; 5001219Skas { 5011219Skas register char *cp; 5021219Skas 5031219Skas for (cp = linebuf; *cp; cp++) 5041219Skas if (!any(*cp, " \t")) 5051219Skas return(0); 5061219Skas return(1); 5071219Skas } 5081219Skas 5091219Skas /* 510*3195Skas * Get sender's name from this message. If the message has 511*3195Skas * a bunch of arpanet stuff in it, we may have to skin the name 512*3195Skas * before returning it. 513*3195Skas */ 514*3195Skas char * 515*3195Skas nameof(mp, reptype) 516*3195Skas register struct message *mp; 517*3195Skas { 518*3195Skas 519*3195Skas return(skin(name1(mp, reptype))); 520*3195Skas } 521*3195Skas 522*3195Skas /* 523*3195Skas * Skin an arpa net address according to the RFC 733 interpretation 524*3195Skas * of "host-phrase." 525*3195Skas */ 526*3195Skas char * 527*3195Skas skin(name) 528*3195Skas char *name; 529*3195Skas { 530*3195Skas register int c; 531*3195Skas register char *cp, *cp2; 532*3195Skas int gotlt, lastsp; 533*3195Skas char nbuf[BUFSIZ]; 534*3195Skas 535*3195Skas if (name == NOSTR) 536*3195Skas return(NOSTR); 537*3195Skas if (index(name, '(') == NOSTR && index(name, '<') == NOSTR) 538*3195Skas return(name); 539*3195Skas gotlt = 0; 540*3195Skas lastsp = 0; 541*3195Skas for (cp = name, cp2 = nbuf, c = *cp++; *cp; c = *cp++) { 542*3195Skas switch (c) { 543*3195Skas case '(': 544*3195Skas while (*cp != ')' && *cp != 0) 545*3195Skas cp++; 546*3195Skas if (*cp) 547*3195Skas cp++; 548*3195Skas break; 549*3195Skas 550*3195Skas case ' ': 551*3195Skas lastsp = 1; 552*3195Skas break; 553*3195Skas 554*3195Skas case '<': 555*3195Skas cp2 = nbuf; 556*3195Skas gotlt++; 557*3195Skas lastsp = 0; 558*3195Skas break; 559*3195Skas 560*3195Skas case '>': 561*3195Skas if (gotlt) 562*3195Skas goto done; 563*3195Skas 564*3195Skas /* Fall into . . . */ 565*3195Skas 566*3195Skas default: 567*3195Skas if (lastsp) { 568*3195Skas lastsp = 0; 569*3195Skas *cp2++ = ' '; 570*3195Skas } 571*3195Skas *cp2++ = c; 572*3195Skas break; 573*3195Skas } 574*3195Skas } 575*3195Skas done: 576*3195Skas *cp2 = 0; 577*3195Skas 578*3195Skas return(savestr(nbuf)); 579*3195Skas } 580*3195Skas 581*3195Skas /* 5821219Skas * Fetch the sender's name from the passed message. 583*3195Skas * Reptype can be 584*3195Skas * 0 -- get sender's name for display purposes 585*3195Skas * 1 -- get sender's name for reply 586*3195Skas * 2 -- get sender's name for Reply 5871219Skas */ 5881219Skas 5891219Skas char * 590*3195Skas name1(mp, reptype) 5911219Skas register struct message *mp; 5921219Skas { 5931219Skas char namebuf[LINESIZE]; 5941219Skas char linebuf[LINESIZE]; 5951219Skas register char *cp, *cp2; 5961219Skas register FILE *ibuf; 5971219Skas int first = 1; 5981219Skas 599*3195Skas #ifndef DELIVERMAIL 600*3195Skas if ((cp = hfield("from", mp)) != NOSTR) 601*3195Skas return(cp); 602*3195Skas if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 603*3195Skas return(cp); 604*3195Skas #endif 6051219Skas ibuf = setinput(mp); 6061219Skas copy("", namebuf); 6071219Skas if (readline(ibuf, linebuf) <= 0) 6081219Skas return(savestr(namebuf)); 6091219Skas newname: 6101219Skas for (cp = linebuf; *cp != ' '; cp++) 6111219Skas ; 6121219Skas while (any(*cp, " \t")) 6131219Skas cp++; 6141219Skas for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") && 6151219Skas cp2-namebuf < LINESIZE-1; *cp2++ = *cp++) 6161219Skas ; 6171219Skas *cp2 = '\0'; 6181219Skas if (readline(ibuf, linebuf) <= 0) 6191219Skas return(savestr(namebuf)); 6201219Skas if ((cp = index(linebuf, 'F')) == NULL) 6211219Skas return(savestr(namebuf)); 6221219Skas if (strncmp(cp, "From", 4) != 0) 6231219Skas return(savestr(namebuf)); 6241219Skas while ((cp = index(cp, 'r')) != NULL) { 6251219Skas if (strncmp(cp, "remote", 6) == 0) { 6261219Skas if ((cp = index(cp, 'f')) == NULL) 6271219Skas break; 6281219Skas if (strncmp(cp, "from", 4) != 0) 6291219Skas break; 6301219Skas if ((cp = index(cp, ' ')) == NULL) 6311219Skas break; 6321219Skas cp++; 6331219Skas if (first) { 6341219Skas copy(cp, namebuf); 6351219Skas first = 0; 6361219Skas } else 6371219Skas strcpy(rindex(namebuf, '!')+1, cp); 6381219Skas strcat(namebuf, "!"); 6391219Skas goto newname; 6401219Skas } 6411219Skas cp++; 6421219Skas } 6431219Skas return(savestr(namebuf)); 6441219Skas } 6451219Skas 6461219Skas /* 6471219Skas * Find the rightmost pointer to an instance of the 6481219Skas * character in the string and return it. 6491219Skas */ 6501219Skas 6511219Skas char * 6521219Skas rindex(str, c) 6531219Skas char str[]; 6541219Skas register int c; 6551219Skas { 6561219Skas register char *cp, *cp2; 6571219Skas 6581219Skas for (cp = str, cp2 = NOSTR; *cp; cp++) 6591219Skas if (c == *cp) 6601219Skas cp2 = cp; 6611219Skas return(cp2); 6621219Skas } 6631219Skas 6641219Skas /* 6651219Skas * See if the string is a number. 6661219Skas */ 6671219Skas 6681219Skas numeric(str) 6691219Skas char str[]; 6701219Skas { 6711219Skas register char *cp = str; 6721219Skas 6731219Skas while (*cp) 6741219Skas if (!isdigit(*cp++)) 6751219Skas return(0); 6761219Skas return(1); 6771219Skas } 6781219Skas 6791219Skas /* 6801219Skas * Are any of the characters in the two strings the same? 6811219Skas */ 6821219Skas 6831219Skas anyof(s1, s2) 6841219Skas register char *s1, *s2; 6851219Skas { 6861219Skas register int c; 6871219Skas 6881219Skas while (c = *s1++) 6891219Skas if (any(c, s2)) 6901219Skas return(1); 6911219Skas return(0); 6921219Skas } 6931219Skas 6941219Skas /* 6951219Skas * Determine the leftmost index of the character 6961219Skas * in the string. 6971219Skas */ 6981219Skas 6991219Skas char * 7001219Skas index(str, ch) 7011219Skas char *str; 7021219Skas { 7031219Skas register char *cp; 7041219Skas register int c; 7051219Skas 7061219Skas for (c = ch, cp = str; *cp; cp++) 7071219Skas if (*cp == c) 7081219Skas return(cp); 7091219Skas return(NOSTR); 7101219Skas } 7111219Skas 7121219Skas /* 7131219Skas * String compare two strings of bounded length. 7141219Skas */ 7151219Skas 7161219Skas strncmp(as1, as2, an) 7171219Skas char *as1, *as2; 7181219Skas { 7191219Skas register char *s1, *s2; 7201219Skas register int n; 7211219Skas 7221219Skas s1 = as1; 7231219Skas s2 = as2; 7241219Skas n = an; 7251219Skas while (--n >= 0 && *s1 == *s2++) 7261219Skas if (*s1++ == '\0') 7271219Skas return(0); 7281219Skas return(n<0 ? 0 : *s1 - *--s2); 7291219Skas } 7301219Skas 731