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*1479Skas static char *SccsId = "@(#)aux.c 1.2 10/17/80"; 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 { 101*1479Skas register struct message *mp; 102*1479Skas 103*1479Skas if (mesg < 1 || mesg > msgCount) 104*1479Skas return; 105*1479Skas mp = &message[mesg-1]; 106*1479Skas mp->m_flag |= MTOUCH; 107*1479Skas if ((mp->m_flag & MREAD) == 0) 108*1479Skas 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 */ 3831219Skas static FILE *sstack[_NFILE]; /* Saved input files */ 3841219Skas 3851219Skas /* 3861219Skas * Pushdown current input file and switch to a new one. 3871219Skas * Set the global flag "sourcing" so that others will realize 3881219Skas * that they are no longer reading from a tty (in all probability). 3891219Skas */ 3901219Skas 3911219Skas source(name) 3921219Skas char name[]; 3931219Skas { 3941219Skas register FILE *fi; 3951219Skas 3961219Skas if ((fi = fopen(name, "r")) == NULL) { 3971219Skas perror(name); 3981219Skas return(1); 3991219Skas } 4001219Skas if (ssp >= _NFILE-2) { 4011219Skas printf("Too much \"sourcing\" going on.\n"); 4021219Skas fclose(fi); 4031219Skas return(1); 4041219Skas } 4051219Skas sstack[++ssp] = input; 4061219Skas input = fi; 4071219Skas sourcing++; 4081219Skas return(0); 4091219Skas } 4101219Skas 4111219Skas /* 4121219Skas * Source a file, but do nothing if the file cannot be opened. 4131219Skas */ 4141219Skas 4151219Skas source1(name) 4161219Skas char name[]; 4171219Skas { 4181219Skas register int f; 4191219Skas 4201219Skas if ((f = open(name, 0)) < 0) 4211219Skas return(0); 4221219Skas close(f); 4231219Skas source(name); 4241219Skas } 4251219Skas 4261219Skas /* 4271219Skas * Pop the current input back to the previous level. 4281219Skas * Update the "sourcing" flag as appropriate. 4291219Skas */ 4301219Skas 4311219Skas unstack() 4321219Skas { 4331219Skas if (ssp < 0) { 4341219Skas printf("\"Source\" stack over-pop.\n"); 4351219Skas sourcing = 0; 4361219Skas return(1); 4371219Skas } 4381219Skas fclose(input); 4391219Skas input = sstack[ssp--]; 4401219Skas if (ssp < 0) 4411219Skas sourcing = 0; 4421219Skas return(0); 4431219Skas } 4441219Skas 4451219Skas /* 4461219Skas * Touch the indicated file. 4471219Skas * This is nifty for the shell. 4481219Skas * If we have the utime() system call, this is better served 4491219Skas * by using that, since it will work for empty files. 4501219Skas * On non-utime systems, we must sleep a second, then read. 4511219Skas */ 4521219Skas 4531219Skas alter(name) 4541219Skas char name[]; 4551219Skas { 4561219Skas #ifdef UTIME 4571219Skas struct stat statb; 4581219Skas long time(); 4591219Skas time_t time_p[2]; 4601219Skas #else 4611219Skas register int pid, f; 4621219Skas char w; 4631219Skas #endif UTIME 4641219Skas 4651219Skas #ifdef UTIME 4661219Skas if (stat(name, &statb) < 0) 4671219Skas return; 4681219Skas time_p[0] = time((long *) 0) + 1; 4691219Skas time_p[1] = statb.st_mtime; 4701219Skas utime(name, time_p); 4711219Skas #else 4721219Skas if ((pid = fork()) != 0) 4731219Skas return; 4741219Skas clrbuf(stdout); 4751219Skas clrbuf(stderr); 4761219Skas clrbuf(stdin); 4771219Skas sleep(1); 4781219Skas if ((f = open(name, 0)) < 0) 4791219Skas exit(1); 4801219Skas read(f, &w, 1); 4811219Skas exit(0); 4821219Skas #endif 4831219Skas } 4841219Skas 4851219Skas /* 4861219Skas * Examine the passed line buffer and 4871219Skas * return true if it is all blanks and tabs. 4881219Skas */ 4891219Skas 4901219Skas blankline(linebuf) 4911219Skas char linebuf[]; 4921219Skas { 4931219Skas register char *cp; 4941219Skas 4951219Skas for (cp = linebuf; *cp; cp++) 4961219Skas if (!any(*cp, " \t")) 4971219Skas return(0); 4981219Skas return(1); 4991219Skas } 5001219Skas 5011219Skas /* 5021219Skas * Fetch the sender's name from the passed message. 5031219Skas */ 5041219Skas 5051219Skas char * 5061219Skas nameof(mp) 5071219Skas register struct message *mp; 5081219Skas { 5091219Skas char namebuf[LINESIZE]; 5101219Skas char linebuf[LINESIZE]; 5111219Skas register char *cp, *cp2; 5121219Skas register FILE *ibuf; 5131219Skas int first = 1; 5141219Skas 5151219Skas if ((cp = hfield("reply-to", mp)) != NOSTR) { 5161219Skas strcpy(namebuf, cp); 5171219Skas return(namebuf); 5181219Skas } 5191219Skas ibuf = setinput(mp); 5201219Skas copy("", namebuf); 5211219Skas if (readline(ibuf, linebuf) <= 0) 5221219Skas return(savestr(namebuf)); 5231219Skas newname: 5241219Skas for (cp = linebuf; *cp != ' '; cp++) 5251219Skas ; 5261219Skas while (any(*cp, " \t")) 5271219Skas cp++; 5281219Skas for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") && 5291219Skas cp2-namebuf < LINESIZE-1; *cp2++ = *cp++) 5301219Skas ; 5311219Skas *cp2 = '\0'; 5321219Skas if (readline(ibuf, linebuf) <= 0) 5331219Skas return(savestr(namebuf)); 5341219Skas if ((cp = index(linebuf, 'F')) == NULL) 5351219Skas return(savestr(namebuf)); 5361219Skas if (strncmp(cp, "From", 4) != 0) 5371219Skas return(savestr(namebuf)); 5381219Skas while ((cp = index(cp, 'r')) != NULL) { 5391219Skas if (strncmp(cp, "remote", 6) == 0) { 5401219Skas if ((cp = index(cp, 'f')) == NULL) 5411219Skas break; 5421219Skas if (strncmp(cp, "from", 4) != 0) 5431219Skas break; 5441219Skas if ((cp = index(cp, ' ')) == NULL) 5451219Skas break; 5461219Skas cp++; 5471219Skas if (first) { 5481219Skas copy(cp, namebuf); 5491219Skas first = 0; 5501219Skas } else 5511219Skas strcpy(rindex(namebuf, '!')+1, cp); 5521219Skas strcat(namebuf, "!"); 5531219Skas goto newname; 5541219Skas } 5551219Skas cp++; 5561219Skas } 5571219Skas return(savestr(namebuf)); 5581219Skas } 5591219Skas 5601219Skas /* 5611219Skas * Find the rightmost pointer to an instance of the 5621219Skas * character in the string and return it. 5631219Skas */ 5641219Skas 5651219Skas char * 5661219Skas rindex(str, c) 5671219Skas char str[]; 5681219Skas register int c; 5691219Skas { 5701219Skas register char *cp, *cp2; 5711219Skas 5721219Skas for (cp = str, cp2 = NOSTR; *cp; cp++) 5731219Skas if (c == *cp) 5741219Skas cp2 = cp; 5751219Skas return(cp2); 5761219Skas } 5771219Skas 5781219Skas /* 5791219Skas * See if the string is a number. 5801219Skas */ 5811219Skas 5821219Skas numeric(str) 5831219Skas char str[]; 5841219Skas { 5851219Skas register char *cp = str; 5861219Skas 5871219Skas while (*cp) 5881219Skas if (!isdigit(*cp++)) 5891219Skas return(0); 5901219Skas return(1); 5911219Skas } 5921219Skas 5931219Skas /* 5941219Skas * Are any of the characters in the two strings the same? 5951219Skas */ 5961219Skas 5971219Skas anyof(s1, s2) 5981219Skas register char *s1, *s2; 5991219Skas { 6001219Skas register int c; 6011219Skas 6021219Skas while (c = *s1++) 6031219Skas if (any(c, s2)) 6041219Skas return(1); 6051219Skas return(0); 6061219Skas } 6071219Skas 6081219Skas /* 6091219Skas * Determine the leftmost index of the character 6101219Skas * in the string. 6111219Skas */ 6121219Skas 6131219Skas char * 6141219Skas index(str, ch) 6151219Skas char *str; 6161219Skas { 6171219Skas register char *cp; 6181219Skas register int c; 6191219Skas 6201219Skas for (c = ch, cp = str; *cp; cp++) 6211219Skas if (*cp == c) 6221219Skas return(cp); 6231219Skas return(NOSTR); 6241219Skas } 6251219Skas 6261219Skas /* 6271219Skas * String compare two strings of bounded length. 6281219Skas */ 6291219Skas 6301219Skas strncmp(as1, as2, an) 6311219Skas char *as1, *as2; 6321219Skas { 6331219Skas register char *s1, *s2; 6341219Skas register int n; 6351219Skas 6361219Skas s1 = as1; 6371219Skas s2 = as2; 6381219Skas n = an; 6391219Skas while (--n >= 0 && *s1 == *s2++) 6401219Skas if (*s1++ == '\0') 6411219Skas return(0); 6421219Skas return(n<0 ? 0 : *s1 - *--s2); 6431219Skas } 6441219Skas 645