122444Sdist /* 222444Sdist * Copyright (c) 1980 Regents of the University of California. 333499Sbostic * All rights reserved. 433499Sbostic * 5*42741Sbostic * %sccs.include.redist.c% 622444Sdist */ 722444Sdist 834905Sbostic #ifndef lint 9*42741Sbostic static char sccsid[] = "@(#)aux.c 5.19 (Berkeley) 06/01/90"; 1034905Sbostic #endif /* not lint */ 111219Skas 121219Skas #include "rcv.h" 131219Skas #include <sys/stat.h> 1438078Sbostic #include <sys/time.h> 151219Skas 161219Skas /* 171219Skas * Mail -- a mail program 181219Skas * 191219Skas * Auxiliary functions. 201219Skas */ 211219Skas 221219Skas /* 231219Skas * Return a pointer to a dynamic copy of the argument. 241219Skas */ 251219Skas char * 261219Skas savestr(str) 271219Skas char *str; 281219Skas { 2934987Sedward char *new; 3034987Sedward int size = strlen(str) + 1; 311219Skas 3234987Sedward if ((new = salloc(size)) != NOSTR) 3334987Sedward bcopy(str, new, size); 3434987Sedward return new; 351219Skas } 361219Skas 371219Skas /* 381219Skas * Announce a fatal error and die. 391219Skas */ 401219Skas 4131142Sedward /*VARARGS1*/ 4231142Sedward panic(fmt, a, b) 4331142Sedward char *fmt; 441219Skas { 4531142Sedward fprintf(stderr, "panic: "); 4631142Sedward fprintf(stderr, fmt, a, b); 4731142Sedward putc('\n', stderr); 481219Skas exit(1); 491219Skas } 501219Skas 511219Skas /* 521219Skas * Touch the named message by setting its MTOUCH flag. 531219Skas * Touched messages have the effect of not being sent 541219Skas * back to the system mailbox on exit. 551219Skas */ 5634987Sedward touch(mp) 5734987Sedward register struct message *mp; 581219Skas { 591479Skas 601479Skas mp->m_flag |= MTOUCH; 611479Skas if ((mp->m_flag & MREAD) == 0) 621479Skas mp->m_flag |= MREAD|MSTATUS; 631219Skas } 641219Skas 651219Skas /* 661219Skas * Test to see if the passed file name is a directory. 671219Skas * Return true if it is. 681219Skas */ 691219Skas isdir(name) 701219Skas char name[]; 711219Skas { 721219Skas struct stat sbuf; 731219Skas 741219Skas if (stat(name, &sbuf) < 0) 751219Skas return(0); 761219Skas return((sbuf.st_mode & S_IFMT) == S_IFDIR); 771219Skas } 781219Skas 791219Skas /* 801219Skas * Count the number of arguments in the given string raw list. 811219Skas */ 821219Skas argcount(argv) 831219Skas char **argv; 841219Skas { 851219Skas register char **ap; 861219Skas 8731142Sedward for (ap = argv; *ap++ != NOSTR;) 881219Skas ; 8931142Sedward return ap - argv - 1; 901219Skas } 911219Skas 921219Skas /* 931219Skas * Return the desired header line from the passed message 941219Skas * pointer (or NOSTR if the desired header field is not available). 951219Skas */ 961219Skas char * 971219Skas hfield(field, mp) 981219Skas char field[]; 991219Skas struct message *mp; 1001219Skas { 1011219Skas register FILE *ibuf; 1021219Skas char linebuf[LINESIZE]; 1031219Skas register int lc; 10431142Sedward register char *hfield; 10531142Sedward char *colon; 1061219Skas 1071219Skas ibuf = setinput(mp); 10831142Sedward if ((lc = mp->m_lines - 1) < 0) 10931142Sedward return NOSTR; 11036478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 11131142Sedward return NOSTR; 11231142Sedward while (lc > 0) { 11331142Sedward if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) 11431142Sedward return NOSTR; 11531142Sedward if (hfield = ishfield(linebuf, colon, field)) 11631142Sedward return savestr(hfield); 11731142Sedward } 11831142Sedward return NOSTR; 1191219Skas } 1201219Skas 1211219Skas /* 1221219Skas * Return the next header field found in the given message. 12331142Sedward * Return >= 0 if something found, < 0 elsewise. 12431142Sedward * "colon" is set to point to the colon in the header. 1251219Skas * Must deal with \ continuations & other such fraud. 1261219Skas */ 12731142Sedward gethfield(f, linebuf, rem, colon) 1281219Skas register FILE *f; 1291219Skas char linebuf[]; 1301219Skas register int rem; 13131142Sedward char **colon; 1321219Skas { 1331219Skas char line2[LINESIZE]; 1341219Skas register char *cp, *cp2; 1351219Skas register int c; 1361219Skas 1371219Skas for (;;) { 13831142Sedward if (--rem < 0) 13931142Sedward return -1; 14036478Sedward if ((c = readline(f, linebuf, LINESIZE)) <= 0) 14131142Sedward return -1; 14231142Sedward for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':'; 14331142Sedward cp++) 14431142Sedward ; 14531142Sedward if (*cp != ':' || cp == linebuf) 1461219Skas continue; 1471219Skas /* 1481219Skas * I guess we got a headline. 1491219Skas * Handle wraparounding 1501219Skas */ 15131142Sedward *colon = cp; 15231142Sedward cp = linebuf + c; 1531219Skas for (;;) { 15431142Sedward while (--cp >= linebuf && (*cp == ' ' || *cp == '\t')) 15531142Sedward ; 15631142Sedward cp++; 1571219Skas if (rem <= 0) 1581219Skas break; 15931142Sedward ungetc(c = getc(f), f); 16031142Sedward if (c != ' ' && c != '\t') 1611219Skas break; 16236478Sedward if ((c = readline(f, line2, LINESIZE)) < 0) 1631219Skas break; 1641219Skas rem--; 16531142Sedward for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++) 1661219Skas ; 16731142Sedward c -= cp2 - line2; 16831142Sedward if (cp + c >= linebuf + LINESIZE - 2) 1691219Skas break; 1701219Skas *cp++ = ' '; 17131142Sedward bcopy(cp2, cp, c); 17231142Sedward cp += c; 1731219Skas } 17431142Sedward *cp = 0; 17531142Sedward return rem; 1761219Skas } 1771219Skas /* NOTREACHED */ 1781219Skas } 1791219Skas 1801219Skas /* 1811219Skas * Check whether the passed line is a header line of 18231142Sedward * the desired breed. Return the field body, or 0. 1831219Skas */ 1841219Skas 18531142Sedward char* 18631142Sedward ishfield(linebuf, colon, field) 1871219Skas char linebuf[], field[]; 18831142Sedward char *colon; 1891219Skas { 19031142Sedward register char *cp = colon; 1911219Skas 1921219Skas *cp = 0; 19334987Sedward if (strcasecmp(linebuf, field) != 0) { 19431142Sedward *cp = ':'; 19531142Sedward return 0; 1961219Skas } 19731142Sedward *cp = ':'; 19831142Sedward for (cp++; *cp == ' ' || *cp == '\t'; cp++) 19931142Sedward ; 20031142Sedward return cp; 2011219Skas } 2021219Skas 2031219Skas /* 2047571Skurt * Copy a string, lowercasing it as we go. 2057571Skurt */ 2067571Skurt istrcpy(dest, src) 20731142Sedward register char *dest, *src; 2087571Skurt { 2097571Skurt 2107571Skurt do { 21131142Sedward if (isupper(*src)) 21231142Sedward *dest++ = tolower(*src); 21331142Sedward else 21431142Sedward *dest++ = *src; 21531142Sedward } while (*src++ != 0); 2167571Skurt } 2177571Skurt 2187571Skurt /* 2191219Skas * The following code deals with input stacking to do source 2201219Skas * commands. All but the current file pointer are saved on 2211219Skas * the stack. 2221219Skas */ 2231219Skas 22434965Sedward static int ssp; /* Top of file stack */ 2251519Skas struct sstack { 2261519Skas FILE *s_file; /* File we were in. */ 2271519Skas int s_cond; /* Saved state of conditionals */ 2285785Skurt int s_loading; /* Loading .mailrc, etc. */ 22918661Sserge } sstack[NOFILE]; 2301219Skas 2311219Skas /* 2321219Skas * Pushdown current input file and switch to a new one. 2331219Skas * Set the global flag "sourcing" so that others will realize 2341219Skas * that they are no longer reading from a tty (in all probability). 2351219Skas */ 23634966Sedward source(arglist) 23734966Sedward char **arglist; 2381219Skas { 23934966Sedward FILE *fi; 24034966Sedward char *cp; 2411219Skas 24234966Sedward if ((cp = expand(*arglist)) == NOSTR) 2431219Skas return(1); 2443914Skurt if ((fi = fopen(cp, "r")) == NULL) { 2453914Skurt perror(cp); 2463914Skurt return(1); 2471219Skas } 24834965Sedward if (ssp >= NOFILE - 1) { 2491219Skas printf("Too much \"sourcing\" going on.\n"); 2501219Skas fclose(fi); 2511219Skas return(1); 2521219Skas } 25334965Sedward sstack[ssp].s_file = input; 2541519Skas sstack[ssp].s_cond = cond; 2555785Skurt sstack[ssp].s_loading = loading; 25634965Sedward ssp++; 2575785Skurt loading = 0; 2581519Skas cond = CANY; 2591219Skas input = fi; 2601219Skas sourcing++; 2611219Skas return(0); 2621219Skas } 2631219Skas 2641219Skas /* 2651219Skas * Pop the current input back to the previous level. 2661219Skas * Update the "sourcing" flag as appropriate. 2671219Skas */ 2681219Skas unstack() 2691219Skas { 27034965Sedward if (ssp <= 0) { 2711219Skas printf("\"Source\" stack over-pop.\n"); 2721219Skas sourcing = 0; 2731219Skas return(1); 2741219Skas } 2751219Skas fclose(input); 2761519Skas if (cond != CANY) 2771519Skas printf("Unmatched \"if\"\n"); 27834965Sedward ssp--; 2791519Skas cond = sstack[ssp].s_cond; 2805785Skurt loading = sstack[ssp].s_loading; 28134965Sedward input = sstack[ssp].s_file; 28234965Sedward if (ssp == 0) 2835785Skurt sourcing = loading; 2841219Skas return(0); 2851219Skas } 2861219Skas 2871219Skas /* 2881219Skas * Touch the indicated file. 2891219Skas * This is nifty for the shell. 2901219Skas */ 2911219Skas alter(name) 29238078Sbostic char *name; 2931219Skas { 29438078Sbostic struct stat sb; 29538078Sbostic struct timeval tv[2]; 29638078Sbostic time_t time(); 2971219Skas 29838078Sbostic if (stat(name, &sb)) 2991219Skas return; 30038078Sbostic tv[0].tv_sec = time((time_t *)0) + 1; 30138078Sbostic tv[1].tv_sec = sb.st_mtime; 30238078Sbostic tv[0].tv_usec = tv[1].tv_usec = 0; 30338078Sbostic (void)utimes(name, tv); 3041219Skas } 3051219Skas 3061219Skas /* 3071219Skas * Examine the passed line buffer and 3081219Skas * return true if it is all blanks and tabs. 3091219Skas */ 3101219Skas blankline(linebuf) 3111219Skas char linebuf[]; 3121219Skas { 3131219Skas register char *cp; 3141219Skas 3151219Skas for (cp = linebuf; *cp; cp++) 31618661Sserge if (*cp != ' ' && *cp != '\t') 3171219Skas return(0); 3181219Skas return(1); 3191219Skas } 3201219Skas 3211219Skas /* 3223195Skas * Get sender's name from this message. If the message has 3233195Skas * a bunch of arpanet stuff in it, we may have to skin the name 3243195Skas * before returning it. 3253195Skas */ 3263195Skas char * 3273195Skas nameof(mp, reptype) 3283195Skas register struct message *mp; 3293195Skas { 3305237Skurt register char *cp, *cp2; 3313195Skas 3325237Skurt cp = skin(name1(mp, reptype)); 3335237Skurt if (reptype != 0 || charcount(cp, '!') < 2) 3345237Skurt return(cp); 3355237Skurt cp2 = rindex(cp, '!'); 3365237Skurt cp2--; 3375237Skurt while (cp2 > cp && *cp2 != '!') 3385237Skurt cp2--; 3395237Skurt if (*cp2 == '!') 3405237Skurt return(cp2 + 1); 3415237Skurt return(cp); 3423195Skas } 3433195Skas 3443195Skas /* 34539763Sedward * Start of a "comment". 34639763Sedward * Ignore it. 34739763Sedward */ 34839763Sedward char * 34939763Sedward skip_comment(cp) 35039763Sedward register char *cp; 35139763Sedward { 35239763Sedward register nesting = 1; 35339763Sedward 35439763Sedward for (; nesting > 0 && *cp; cp++) { 35539763Sedward switch (*cp) { 35639763Sedward case '\\': 35739763Sedward if (cp[1]) 35839763Sedward cp++; 35939763Sedward break; 36039763Sedward case '(': 36139763Sedward nesting++; 36239763Sedward break; 36339763Sedward case ')': 36439763Sedward nesting--; 36539763Sedward break; 36639763Sedward } 36739763Sedward } 36839763Sedward return cp; 36939763Sedward } 37039763Sedward 37139763Sedward /* 37225912Smckusick * Skin an arpa net address according to the RFC 822 interpretation 3733195Skas * of "host-phrase." 3743195Skas */ 3753195Skas char * 3763195Skas skin(name) 3773195Skas char *name; 3783195Skas { 3793195Skas register int c; 3803195Skas register char *cp, *cp2; 38125912Smckusick char *bufend; 3823195Skas int gotlt, lastsp; 3833195Skas char nbuf[BUFSIZ]; 3843195Skas 3853195Skas if (name == NOSTR) 3863195Skas return(NOSTR); 38712819Sleres if (index(name, '(') == NOSTR && index(name, '<') == NOSTR 38831142Sedward && index(name, ' ') == NOSTR) 3893195Skas return(name); 3903195Skas gotlt = 0; 3913195Skas lastsp = 0; 39225912Smckusick bufend = nbuf; 39325912Smckusick for (cp = name, cp2 = bufend; c = *cp++; ) { 3943195Skas switch (c) { 3953195Skas case '(': 39639763Sedward cp = skip_comment(cp); 39712819Sleres lastsp = 0; 3983195Skas break; 3993195Skas 40025912Smckusick case '"': 40125912Smckusick /* 40225912Smckusick * Start of a "quoted-string". 40325912Smckusick * Copy it in its entirety. 40425912Smckusick */ 40539763Sedward while (c = *cp) { 40625912Smckusick cp++; 40739763Sedward if (c == '"') 40839763Sedward break; 40939763Sedward if (c != '\\') 41039763Sedward *cp2++ = c; 41139763Sedward else if (c = *cp) { 41239763Sedward *cp2++ = c; 41325912Smckusick cp++; 41425912Smckusick } 41525912Smckusick } 41625912Smckusick lastsp = 0; 41725912Smckusick break; 41825912Smckusick 4193195Skas case ' ': 42012819Sleres if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 42112819Sleres cp += 3, *cp2++ = '@'; 42212819Sleres else 42312819Sleres if (cp[0] == '@' && cp[1] == ' ') 42412819Sleres cp += 2, *cp2++ = '@'; 42512819Sleres else 42612819Sleres lastsp = 1; 4273195Skas break; 4283195Skas 4293195Skas case '<': 43025912Smckusick cp2 = bufend; 4313195Skas gotlt++; 4323195Skas lastsp = 0; 4333195Skas break; 4343195Skas 4353195Skas case '>': 43625912Smckusick if (gotlt) { 43725912Smckusick gotlt = 0; 43839763Sedward while ((c = *cp) && c != ',') { 43925912Smckusick cp++; 44039763Sedward if (c == '(') 44139763Sedward cp = skip_comment(cp); 44239763Sedward else if (c == '"') 44339763Sedward while (c = *cp) { 44439763Sedward cp++; 44539763Sedward if (c == '"') 44639763Sedward break; 44739763Sedward if (c == '\\' && *cp) 44839763Sedward cp++; 44939763Sedward } 45039763Sedward } 45139763Sedward lastsp = 0; 45225912Smckusick break; 45325912Smckusick } 4543195Skas /* Fall into . . . */ 4553195Skas 4563195Skas default: 4573195Skas if (lastsp) { 4583195Skas lastsp = 0; 4593195Skas *cp2++ = ' '; 4603195Skas } 4613195Skas *cp2++ = c; 46239763Sedward if (c == ',' && !gotlt) { 46339763Sedward *cp2++ = ' '; 46439763Sedward for (; *cp == ' '; cp++) 46539763Sedward ; 46639763Sedward lastsp = 0; 46739763Sedward bufend = cp2; 46839763Sedward } 4693195Skas } 4703195Skas } 4713195Skas *cp2 = 0; 4723195Skas 4733195Skas return(savestr(nbuf)); 4743195Skas } 4753195Skas 4763195Skas /* 4771219Skas * Fetch the sender's name from the passed message. 4783195Skas * Reptype can be 4793195Skas * 0 -- get sender's name for display purposes 4803195Skas * 1 -- get sender's name for reply 4813195Skas * 2 -- get sender's name for Reply 4821219Skas */ 4831219Skas char * 4843195Skas name1(mp, reptype) 4851219Skas register struct message *mp; 4861219Skas { 4871219Skas char namebuf[LINESIZE]; 4881219Skas char linebuf[LINESIZE]; 4891219Skas register char *cp, *cp2; 4901219Skas register FILE *ibuf; 4911219Skas int first = 1; 4921219Skas 4933195Skas if ((cp = hfield("from", mp)) != NOSTR) 49431142Sedward return cp; 4953195Skas if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 49631142Sedward return cp; 4971219Skas ibuf = setinput(mp); 49831142Sedward namebuf[0] = 0; 49936478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 5001219Skas return(savestr(namebuf)); 5011219Skas newname: 50231142Sedward for (cp = linebuf; *cp && *cp != ' '; cp++) 5031219Skas ; 50431142Sedward for (; *cp == ' ' || *cp == '\t'; cp++) 5051219Skas ; 50631142Sedward for (cp2 = &namebuf[strlen(namebuf)]; 50731142Sedward *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) 50831142Sedward *cp2++ = *cp++; 5091219Skas *cp2 = '\0'; 51036478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 5111219Skas return(savestr(namebuf)); 5121219Skas if ((cp = index(linebuf, 'F')) == NULL) 5131219Skas return(savestr(namebuf)); 5141219Skas if (strncmp(cp, "From", 4) != 0) 5151219Skas return(savestr(namebuf)); 5161219Skas while ((cp = index(cp, 'r')) != NULL) { 5171219Skas if (strncmp(cp, "remote", 6) == 0) { 5181219Skas if ((cp = index(cp, 'f')) == NULL) 5191219Skas break; 5201219Skas if (strncmp(cp, "from", 4) != 0) 5211219Skas break; 5221219Skas if ((cp = index(cp, ' ')) == NULL) 5231219Skas break; 5241219Skas cp++; 5251219Skas if (first) { 52631142Sedward strcpy(namebuf, cp); 5271219Skas first = 0; 5281219Skas } else 5291219Skas strcpy(rindex(namebuf, '!')+1, cp); 5301219Skas strcat(namebuf, "!"); 5311219Skas goto newname; 5321219Skas } 5331219Skas cp++; 5341219Skas } 5351219Skas return(savestr(namebuf)); 5361219Skas } 5371219Skas 5381219Skas /* 5395237Skurt * Count the occurances of c in str 5405237Skurt */ 5415237Skurt charcount(str, c) 5425237Skurt char *str; 5435237Skurt { 5445237Skurt register char *cp; 5455237Skurt register int i; 5465237Skurt 5475237Skurt for (i = 0, cp = str; *cp; cp++) 5485237Skurt if (*cp == c) 5495237Skurt i++; 5505237Skurt return(i); 5515237Skurt } 5525237Skurt 5535237Skurt /* 55431142Sedward * Are any of the characters in the two strings the same? 5551219Skas */ 55631142Sedward anyof(s1, s2) 55731142Sedward register char *s1, *s2; 5581219Skas { 5591219Skas 56031142Sedward while (*s1) 56131142Sedward if (index(s2, *s1++)) 56231142Sedward return 1; 56331142Sedward return 0; 5641219Skas } 5651219Skas 5661219Skas /* 56731142Sedward * Convert c to upper case 5681219Skas */ 56931142Sedward raise(c) 57031142Sedward register c; 57131142Sedward { 57231142Sedward 57331142Sedward if (islower(c)) 57431142Sedward return toupper(c); 57531142Sedward return c; 57631142Sedward } 57731142Sedward 57831142Sedward /* 57931142Sedward * Copy s1 to s2, return pointer to null in s2. 58031142Sedward */ 58131142Sedward char * 58231142Sedward copy(s1, s2) 5831219Skas register char *s1, *s2; 5841219Skas { 5851219Skas 58631142Sedward while (*s2++ = *s1++) 58731142Sedward ; 58831142Sedward return s2 - 1; 5891219Skas } 5901219Skas 5911219Skas /* 5927571Skurt * See if the given header field is supposed to be ignored. 5937571Skurt */ 59434692Sedward isign(field, ignore) 5957571Skurt char *field; 59634692Sedward struct ignoretab ignore[2]; 5977571Skurt { 5987571Skurt char realfld[BUFSIZ]; 5997571Skurt 60034969Sedward if (ignore == ignoreall) 60134969Sedward return 1; 60218661Sserge /* 60318661Sserge * Lower-case the string, so that "Status" and "status" 60418661Sserge * will hash to the same place. 60518661Sserge */ 6067571Skurt istrcpy(realfld, field); 60734692Sedward if (ignore[1].i_count > 0) 60834692Sedward return (!member(realfld, ignore + 1)); 60918661Sserge else 61018661Sserge return (member(realfld, ignore)); 6117571Skurt } 61218661Sserge 61318661Sserge member(realfield, table) 61418661Sserge register char *realfield; 61534692Sedward struct ignoretab *table; 61618661Sserge { 61718661Sserge register struct ignore *igp; 61818661Sserge 61934692Sedward for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link) 62031142Sedward if (*igp->i_field == *realfield && 62131142Sedward equal(igp->i_field, realfield)) 62218661Sserge return (1); 62318661Sserge return (0); 62418661Sserge } 625