122444Sdist /* 222444Sdist * Copyright (c) 1980 Regents of the University of California. 333499Sbostic * All rights reserved. 433499Sbostic * 542741Sbostic * %sccs.include.redist.c% 622444Sdist */ 722444Sdist 834905Sbostic #ifndef lint 9*43865Sedward static char sccsid[] = "@(#)aux.c 5.20 (Berkeley) 06/25/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); 48*43865Sedward fflush(stdout); 49*43865Sedward abort(); 501219Skas } 511219Skas 521219Skas /* 531219Skas * Touch the named message by setting its MTOUCH flag. 541219Skas * Touched messages have the effect of not being sent 551219Skas * back to the system mailbox on exit. 561219Skas */ 5734987Sedward touch(mp) 5834987Sedward register struct message *mp; 591219Skas { 601479Skas 611479Skas mp->m_flag |= MTOUCH; 621479Skas if ((mp->m_flag & MREAD) == 0) 631479Skas mp->m_flag |= MREAD|MSTATUS; 641219Skas } 651219Skas 661219Skas /* 671219Skas * Test to see if the passed file name is a directory. 681219Skas * Return true if it is. 691219Skas */ 701219Skas isdir(name) 711219Skas char name[]; 721219Skas { 731219Skas struct stat sbuf; 741219Skas 751219Skas if (stat(name, &sbuf) < 0) 761219Skas return(0); 771219Skas return((sbuf.st_mode & S_IFMT) == S_IFDIR); 781219Skas } 791219Skas 801219Skas /* 811219Skas * Count the number of arguments in the given string raw list. 821219Skas */ 831219Skas argcount(argv) 841219Skas char **argv; 851219Skas { 861219Skas register char **ap; 871219Skas 8831142Sedward for (ap = argv; *ap++ != NOSTR;) 891219Skas ; 9031142Sedward return ap - argv - 1; 911219Skas } 921219Skas 931219Skas /* 941219Skas * Return the desired header line from the passed message 951219Skas * pointer (or NOSTR if the desired header field is not available). 961219Skas */ 971219Skas char * 981219Skas hfield(field, mp) 991219Skas char field[]; 1001219Skas struct message *mp; 1011219Skas { 1021219Skas register FILE *ibuf; 1031219Skas char linebuf[LINESIZE]; 1041219Skas register int lc; 10531142Sedward register char *hfield; 10631142Sedward char *colon; 1071219Skas 1081219Skas ibuf = setinput(mp); 10931142Sedward if ((lc = mp->m_lines - 1) < 0) 11031142Sedward return NOSTR; 11136478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 11231142Sedward return NOSTR; 11331142Sedward while (lc > 0) { 11431142Sedward if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) 11531142Sedward return NOSTR; 11631142Sedward if (hfield = ishfield(linebuf, colon, field)) 11731142Sedward return savestr(hfield); 11831142Sedward } 11931142Sedward return NOSTR; 1201219Skas } 1211219Skas 1221219Skas /* 1231219Skas * Return the next header field found in the given message. 12431142Sedward * Return >= 0 if something found, < 0 elsewise. 12531142Sedward * "colon" is set to point to the colon in the header. 1261219Skas * Must deal with \ continuations & other such fraud. 1271219Skas */ 12831142Sedward gethfield(f, linebuf, rem, colon) 1291219Skas register FILE *f; 1301219Skas char linebuf[]; 1311219Skas register int rem; 13231142Sedward char **colon; 1331219Skas { 1341219Skas char line2[LINESIZE]; 1351219Skas register char *cp, *cp2; 1361219Skas register int c; 1371219Skas 1381219Skas for (;;) { 13931142Sedward if (--rem < 0) 14031142Sedward return -1; 14136478Sedward if ((c = readline(f, linebuf, LINESIZE)) <= 0) 14231142Sedward return -1; 14331142Sedward for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':'; 14431142Sedward cp++) 14531142Sedward ; 14631142Sedward if (*cp != ':' || cp == linebuf) 1471219Skas continue; 1481219Skas /* 1491219Skas * I guess we got a headline. 1501219Skas * Handle wraparounding 1511219Skas */ 15231142Sedward *colon = cp; 15331142Sedward cp = linebuf + c; 1541219Skas for (;;) { 15531142Sedward while (--cp >= linebuf && (*cp == ' ' || *cp == '\t')) 15631142Sedward ; 15731142Sedward cp++; 1581219Skas if (rem <= 0) 1591219Skas break; 16031142Sedward ungetc(c = getc(f), f); 16131142Sedward if (c != ' ' && c != '\t') 1621219Skas break; 16336478Sedward if ((c = readline(f, line2, LINESIZE)) < 0) 1641219Skas break; 1651219Skas rem--; 16631142Sedward for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++) 1671219Skas ; 16831142Sedward c -= cp2 - line2; 16931142Sedward if (cp + c >= linebuf + LINESIZE - 2) 1701219Skas break; 1711219Skas *cp++ = ' '; 17231142Sedward bcopy(cp2, cp, c); 17331142Sedward cp += c; 1741219Skas } 17531142Sedward *cp = 0; 17631142Sedward return rem; 1771219Skas } 1781219Skas /* NOTREACHED */ 1791219Skas } 1801219Skas 1811219Skas /* 1821219Skas * Check whether the passed line is a header line of 18331142Sedward * the desired breed. Return the field body, or 0. 1841219Skas */ 1851219Skas 18631142Sedward char* 18731142Sedward ishfield(linebuf, colon, field) 1881219Skas char linebuf[], field[]; 18931142Sedward char *colon; 1901219Skas { 19131142Sedward register char *cp = colon; 1921219Skas 1931219Skas *cp = 0; 19434987Sedward if (strcasecmp(linebuf, field) != 0) { 19531142Sedward *cp = ':'; 19631142Sedward return 0; 1971219Skas } 19831142Sedward *cp = ':'; 19931142Sedward for (cp++; *cp == ' ' || *cp == '\t'; cp++) 20031142Sedward ; 20131142Sedward return cp; 2021219Skas } 2031219Skas 2041219Skas /* 2057571Skurt * Copy a string, lowercasing it as we go. 2067571Skurt */ 2077571Skurt istrcpy(dest, src) 20831142Sedward register char *dest, *src; 2097571Skurt { 2107571Skurt 2117571Skurt do { 21231142Sedward if (isupper(*src)) 21331142Sedward *dest++ = tolower(*src); 21431142Sedward else 21531142Sedward *dest++ = *src; 21631142Sedward } while (*src++ != 0); 2177571Skurt } 2187571Skurt 2197571Skurt /* 2201219Skas * The following code deals with input stacking to do source 2211219Skas * commands. All but the current file pointer are saved on 2221219Skas * the stack. 2231219Skas */ 2241219Skas 22534965Sedward static int ssp; /* Top of file stack */ 2261519Skas struct sstack { 2271519Skas FILE *s_file; /* File we were in. */ 2281519Skas int s_cond; /* Saved state of conditionals */ 2295785Skurt int s_loading; /* Loading .mailrc, etc. */ 23018661Sserge } sstack[NOFILE]; 2311219Skas 2321219Skas /* 2331219Skas * Pushdown current input file and switch to a new one. 2341219Skas * Set the global flag "sourcing" so that others will realize 2351219Skas * that they are no longer reading from a tty (in all probability). 2361219Skas */ 23734966Sedward source(arglist) 23834966Sedward char **arglist; 2391219Skas { 24034966Sedward FILE *fi; 24134966Sedward char *cp; 2421219Skas 24334966Sedward if ((cp = expand(*arglist)) == NOSTR) 2441219Skas return(1); 245*43865Sedward if ((fi = Fopen(cp, "r")) == NULL) { 2463914Skurt perror(cp); 2473914Skurt return(1); 2481219Skas } 24934965Sedward if (ssp >= NOFILE - 1) { 2501219Skas printf("Too much \"sourcing\" going on.\n"); 251*43865Sedward Fclose(fi); 2521219Skas return(1); 2531219Skas } 25434965Sedward sstack[ssp].s_file = input; 2551519Skas sstack[ssp].s_cond = cond; 2565785Skurt sstack[ssp].s_loading = loading; 25734965Sedward ssp++; 2585785Skurt loading = 0; 2591519Skas cond = CANY; 2601219Skas input = fi; 2611219Skas sourcing++; 2621219Skas return(0); 2631219Skas } 2641219Skas 2651219Skas /* 2661219Skas * Pop the current input back to the previous level. 2671219Skas * Update the "sourcing" flag as appropriate. 2681219Skas */ 2691219Skas unstack() 2701219Skas { 27134965Sedward if (ssp <= 0) { 2721219Skas printf("\"Source\" stack over-pop.\n"); 2731219Skas sourcing = 0; 2741219Skas return(1); 2751219Skas } 276*43865Sedward Fclose(input); 2771519Skas if (cond != CANY) 2781519Skas printf("Unmatched \"if\"\n"); 27934965Sedward ssp--; 2801519Skas cond = sstack[ssp].s_cond; 2815785Skurt loading = sstack[ssp].s_loading; 28234965Sedward input = sstack[ssp].s_file; 28334965Sedward if (ssp == 0) 2845785Skurt sourcing = loading; 2851219Skas return(0); 2861219Skas } 2871219Skas 2881219Skas /* 2891219Skas * Touch the indicated file. 2901219Skas * This is nifty for the shell. 2911219Skas */ 2921219Skas alter(name) 29338078Sbostic char *name; 2941219Skas { 29538078Sbostic struct stat sb; 29638078Sbostic struct timeval tv[2]; 29738078Sbostic time_t time(); 2981219Skas 29938078Sbostic if (stat(name, &sb)) 3001219Skas return; 30138078Sbostic tv[0].tv_sec = time((time_t *)0) + 1; 30238078Sbostic tv[1].tv_sec = sb.st_mtime; 30338078Sbostic tv[0].tv_usec = tv[1].tv_usec = 0; 30438078Sbostic (void)utimes(name, tv); 3051219Skas } 3061219Skas 3071219Skas /* 3081219Skas * Examine the passed line buffer and 3091219Skas * return true if it is all blanks and tabs. 3101219Skas */ 3111219Skas blankline(linebuf) 3121219Skas char linebuf[]; 3131219Skas { 3141219Skas register char *cp; 3151219Skas 3161219Skas for (cp = linebuf; *cp; cp++) 31718661Sserge if (*cp != ' ' && *cp != '\t') 3181219Skas return(0); 3191219Skas return(1); 3201219Skas } 3211219Skas 3221219Skas /* 3233195Skas * Get sender's name from this message. If the message has 3243195Skas * a bunch of arpanet stuff in it, we may have to skin the name 3253195Skas * before returning it. 3263195Skas */ 3273195Skas char * 3283195Skas nameof(mp, reptype) 3293195Skas register struct message *mp; 3303195Skas { 3315237Skurt register char *cp, *cp2; 3323195Skas 3335237Skurt cp = skin(name1(mp, reptype)); 3345237Skurt if (reptype != 0 || charcount(cp, '!') < 2) 3355237Skurt return(cp); 3365237Skurt cp2 = rindex(cp, '!'); 3375237Skurt cp2--; 3385237Skurt while (cp2 > cp && *cp2 != '!') 3395237Skurt cp2--; 3405237Skurt if (*cp2 == '!') 3415237Skurt return(cp2 + 1); 3425237Skurt return(cp); 3433195Skas } 3443195Skas 3453195Skas /* 34639763Sedward * Start of a "comment". 34739763Sedward * Ignore it. 34839763Sedward */ 34939763Sedward char * 35039763Sedward skip_comment(cp) 35139763Sedward register char *cp; 35239763Sedward { 35339763Sedward register nesting = 1; 35439763Sedward 35539763Sedward for (; nesting > 0 && *cp; cp++) { 35639763Sedward switch (*cp) { 35739763Sedward case '\\': 35839763Sedward if (cp[1]) 35939763Sedward cp++; 36039763Sedward break; 36139763Sedward case '(': 36239763Sedward nesting++; 36339763Sedward break; 36439763Sedward case ')': 36539763Sedward nesting--; 36639763Sedward break; 36739763Sedward } 36839763Sedward } 36939763Sedward return cp; 37039763Sedward } 37139763Sedward 37239763Sedward /* 37325912Smckusick * Skin an arpa net address according to the RFC 822 interpretation 3743195Skas * of "host-phrase." 3753195Skas */ 3763195Skas char * 3773195Skas skin(name) 3783195Skas char *name; 3793195Skas { 3803195Skas register int c; 3813195Skas register char *cp, *cp2; 38225912Smckusick char *bufend; 3833195Skas int gotlt, lastsp; 3843195Skas char nbuf[BUFSIZ]; 3853195Skas 3863195Skas if (name == NOSTR) 3873195Skas return(NOSTR); 38812819Sleres if (index(name, '(') == NOSTR && index(name, '<') == NOSTR 38931142Sedward && index(name, ' ') == NOSTR) 3903195Skas return(name); 3913195Skas gotlt = 0; 3923195Skas lastsp = 0; 39325912Smckusick bufend = nbuf; 39425912Smckusick for (cp = name, cp2 = bufend; c = *cp++; ) { 3953195Skas switch (c) { 3963195Skas case '(': 39739763Sedward cp = skip_comment(cp); 39812819Sleres lastsp = 0; 3993195Skas break; 4003195Skas 40125912Smckusick case '"': 40225912Smckusick /* 40325912Smckusick * Start of a "quoted-string". 40425912Smckusick * Copy it in its entirety. 40525912Smckusick */ 40639763Sedward while (c = *cp) { 40725912Smckusick cp++; 40839763Sedward if (c == '"') 40939763Sedward break; 41039763Sedward if (c != '\\') 41139763Sedward *cp2++ = c; 41239763Sedward else if (c = *cp) { 41339763Sedward *cp2++ = c; 41425912Smckusick cp++; 41525912Smckusick } 41625912Smckusick } 41725912Smckusick lastsp = 0; 41825912Smckusick break; 41925912Smckusick 4203195Skas case ' ': 42112819Sleres if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 42212819Sleres cp += 3, *cp2++ = '@'; 42312819Sleres else 42412819Sleres if (cp[0] == '@' && cp[1] == ' ') 42512819Sleres cp += 2, *cp2++ = '@'; 42612819Sleres else 42712819Sleres lastsp = 1; 4283195Skas break; 4293195Skas 4303195Skas case '<': 43125912Smckusick cp2 = bufend; 4323195Skas gotlt++; 4333195Skas lastsp = 0; 4343195Skas break; 4353195Skas 4363195Skas case '>': 43725912Smckusick if (gotlt) { 43825912Smckusick gotlt = 0; 43939763Sedward while ((c = *cp) && c != ',') { 44025912Smckusick cp++; 44139763Sedward if (c == '(') 44239763Sedward cp = skip_comment(cp); 44339763Sedward else if (c == '"') 44439763Sedward while (c = *cp) { 44539763Sedward cp++; 44639763Sedward if (c == '"') 44739763Sedward break; 44839763Sedward if (c == '\\' && *cp) 44939763Sedward cp++; 45039763Sedward } 45139763Sedward } 45239763Sedward lastsp = 0; 45325912Smckusick break; 45425912Smckusick } 4553195Skas /* Fall into . . . */ 4563195Skas 4573195Skas default: 4583195Skas if (lastsp) { 4593195Skas lastsp = 0; 4603195Skas *cp2++ = ' '; 4613195Skas } 4623195Skas *cp2++ = c; 46339763Sedward if (c == ',' && !gotlt) { 46439763Sedward *cp2++ = ' '; 46539763Sedward for (; *cp == ' '; cp++) 46639763Sedward ; 46739763Sedward lastsp = 0; 46839763Sedward bufend = cp2; 46939763Sedward } 4703195Skas } 4713195Skas } 4723195Skas *cp2 = 0; 4733195Skas 4743195Skas return(savestr(nbuf)); 4753195Skas } 4763195Skas 4773195Skas /* 4781219Skas * Fetch the sender's name from the passed message. 4793195Skas * Reptype can be 4803195Skas * 0 -- get sender's name for display purposes 4813195Skas * 1 -- get sender's name for reply 4823195Skas * 2 -- get sender's name for Reply 4831219Skas */ 4841219Skas char * 4853195Skas name1(mp, reptype) 4861219Skas register struct message *mp; 4871219Skas { 4881219Skas char namebuf[LINESIZE]; 4891219Skas char linebuf[LINESIZE]; 4901219Skas register char *cp, *cp2; 4911219Skas register FILE *ibuf; 4921219Skas int first = 1; 4931219Skas 4943195Skas if ((cp = hfield("from", mp)) != NOSTR) 49531142Sedward return cp; 4963195Skas if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 49731142Sedward return cp; 4981219Skas ibuf = setinput(mp); 49931142Sedward namebuf[0] = 0; 50036478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 5011219Skas return(savestr(namebuf)); 5021219Skas newname: 50331142Sedward for (cp = linebuf; *cp && *cp != ' '; cp++) 5041219Skas ; 50531142Sedward for (; *cp == ' ' || *cp == '\t'; cp++) 5061219Skas ; 50731142Sedward for (cp2 = &namebuf[strlen(namebuf)]; 50831142Sedward *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) 50931142Sedward *cp2++ = *cp++; 5101219Skas *cp2 = '\0'; 51136478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 5121219Skas return(savestr(namebuf)); 5131219Skas if ((cp = index(linebuf, 'F')) == NULL) 5141219Skas return(savestr(namebuf)); 5151219Skas if (strncmp(cp, "From", 4) != 0) 5161219Skas return(savestr(namebuf)); 5171219Skas while ((cp = index(cp, 'r')) != NULL) { 5181219Skas if (strncmp(cp, "remote", 6) == 0) { 5191219Skas if ((cp = index(cp, 'f')) == NULL) 5201219Skas break; 5211219Skas if (strncmp(cp, "from", 4) != 0) 5221219Skas break; 5231219Skas if ((cp = index(cp, ' ')) == NULL) 5241219Skas break; 5251219Skas cp++; 5261219Skas if (first) { 52731142Sedward strcpy(namebuf, cp); 5281219Skas first = 0; 5291219Skas } else 5301219Skas strcpy(rindex(namebuf, '!')+1, cp); 5311219Skas strcat(namebuf, "!"); 5321219Skas goto newname; 5331219Skas } 5341219Skas cp++; 5351219Skas } 5361219Skas return(savestr(namebuf)); 5371219Skas } 5381219Skas 5391219Skas /* 5405237Skurt * Count the occurances of c in str 5415237Skurt */ 5425237Skurt charcount(str, c) 5435237Skurt char *str; 5445237Skurt { 5455237Skurt register char *cp; 5465237Skurt register int i; 5475237Skurt 5485237Skurt for (i = 0, cp = str; *cp; cp++) 5495237Skurt if (*cp == c) 5505237Skurt i++; 5515237Skurt return(i); 5525237Skurt } 5535237Skurt 5545237Skurt /* 55531142Sedward * Are any of the characters in the two strings the same? 5561219Skas */ 55731142Sedward anyof(s1, s2) 55831142Sedward register char *s1, *s2; 5591219Skas { 5601219Skas 56131142Sedward while (*s1) 56231142Sedward if (index(s2, *s1++)) 56331142Sedward return 1; 56431142Sedward return 0; 5651219Skas } 5661219Skas 5671219Skas /* 56831142Sedward * Convert c to upper case 5691219Skas */ 57031142Sedward raise(c) 57131142Sedward register c; 57231142Sedward { 57331142Sedward 57431142Sedward if (islower(c)) 57531142Sedward return toupper(c); 57631142Sedward return c; 57731142Sedward } 57831142Sedward 57931142Sedward /* 58031142Sedward * Copy s1 to s2, return pointer to null in s2. 58131142Sedward */ 58231142Sedward char * 58331142Sedward copy(s1, s2) 5841219Skas register char *s1, *s2; 5851219Skas { 5861219Skas 58731142Sedward while (*s2++ = *s1++) 58831142Sedward ; 58931142Sedward return s2 - 1; 5901219Skas } 5911219Skas 5921219Skas /* 5937571Skurt * See if the given header field is supposed to be ignored. 5947571Skurt */ 59534692Sedward isign(field, ignore) 5967571Skurt char *field; 59734692Sedward struct ignoretab ignore[2]; 5987571Skurt { 5997571Skurt char realfld[BUFSIZ]; 6007571Skurt 60134969Sedward if (ignore == ignoreall) 60234969Sedward return 1; 60318661Sserge /* 60418661Sserge * Lower-case the string, so that "Status" and "status" 60518661Sserge * will hash to the same place. 60618661Sserge */ 6077571Skurt istrcpy(realfld, field); 60834692Sedward if (ignore[1].i_count > 0) 60934692Sedward return (!member(realfld, ignore + 1)); 61018661Sserge else 61118661Sserge return (member(realfld, ignore)); 6127571Skurt } 61318661Sserge 61418661Sserge member(realfield, table) 61518661Sserge register char *realfield; 61634692Sedward struct ignoretab *table; 61718661Sserge { 61818661Sserge register struct ignore *igp; 61918661Sserge 62034692Sedward for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link) 62131142Sedward if (*igp->i_field == *realfield && 62231142Sedward equal(igp->i_field, realfield)) 62318661Sserge return (1); 62418661Sserge return (0); 62518661Sserge } 626