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*54505Sbostic static char sccsid[] = "@(#)aux.c 5.21 (Berkeley) 06/26/92"; 1034905Sbostic #endif /* not lint */ 111219Skas 121219Skas #include "rcv.h" 13*54505Sbostic #include "extern.h" 141219Skas 151219Skas /* 161219Skas * Mail -- a mail program 171219Skas * 181219Skas * Auxiliary functions. 191219Skas */ 201219Skas 211219Skas /* 221219Skas * Return a pointer to a dynamic copy of the argument. 231219Skas */ 241219Skas char * 251219Skas savestr(str) 261219Skas char *str; 271219Skas { 2834987Sedward char *new; 2934987Sedward int size = strlen(str) + 1; 301219Skas 3134987Sedward if ((new = salloc(size)) != NOSTR) 3234987Sedward bcopy(str, new, size); 3334987Sedward return new; 341219Skas } 351219Skas 361219Skas /* 371219Skas * Announce a fatal error and die. 381219Skas */ 39*54505Sbostic #if __STDC__ 40*54505Sbostic #include <stdarg.h> 41*54505Sbostic #else 42*54505Sbostic #include <varargs.h> 43*54505Sbostic #endif 441219Skas 45*54505Sbostic void 46*54505Sbostic #if __STDC__ 47*54505Sbostic panic(const char *fmt, ...) 48*54505Sbostic #else 49*54505Sbostic panic(fmt, va_alist) 5031142Sedward char *fmt; 51*54505Sbostic va_dcl 52*54505Sbostic #endif 531219Skas { 54*54505Sbostic va_list ap; 55*54505Sbostic #if __STDC__ 56*54505Sbostic va_start(ap, fmt); 57*54505Sbostic #else 58*54505Sbostic va_start(ap); 59*54505Sbostic #endif 60*54505Sbostic (void)fprintf(stderr, "panic: "); 61*54505Sbostic vfprintf(stderr, fmt, ap); 62*54505Sbostic va_end(ap); 63*54505Sbostic (void)fprintf(stderr, "\n"); 64*54505Sbostic fflush(stderr); 6543865Sedward abort(); 661219Skas } 671219Skas 681219Skas /* 691219Skas * Touch the named message by setting its MTOUCH flag. 701219Skas * Touched messages have the effect of not being sent 711219Skas * back to the system mailbox on exit. 721219Skas */ 73*54505Sbostic void 7434987Sedward touch(mp) 7534987Sedward register struct message *mp; 761219Skas { 771479Skas 781479Skas mp->m_flag |= MTOUCH; 791479Skas if ((mp->m_flag & MREAD) == 0) 801479Skas mp->m_flag |= MREAD|MSTATUS; 811219Skas } 821219Skas 831219Skas /* 841219Skas * Test to see if the passed file name is a directory. 851219Skas * Return true if it is. 861219Skas */ 87*54505Sbostic int 881219Skas isdir(name) 891219Skas char name[]; 901219Skas { 911219Skas struct stat sbuf; 921219Skas 931219Skas if (stat(name, &sbuf) < 0) 941219Skas return(0); 951219Skas return((sbuf.st_mode & S_IFMT) == S_IFDIR); 961219Skas } 971219Skas 981219Skas /* 991219Skas * Count the number of arguments in the given string raw list. 1001219Skas */ 101*54505Sbostic int 1021219Skas argcount(argv) 1031219Skas char **argv; 1041219Skas { 1051219Skas register char **ap; 1061219Skas 10731142Sedward for (ap = argv; *ap++ != NOSTR;) 1081219Skas ; 10931142Sedward return ap - argv - 1; 1101219Skas } 1111219Skas 1121219Skas /* 1131219Skas * Return the desired header line from the passed message 1141219Skas * pointer (or NOSTR if the desired header field is not available). 1151219Skas */ 1161219Skas char * 1171219Skas hfield(field, mp) 1181219Skas char field[]; 1191219Skas struct message *mp; 1201219Skas { 1211219Skas register FILE *ibuf; 1221219Skas char linebuf[LINESIZE]; 1231219Skas register int lc; 12431142Sedward register char *hfield; 12531142Sedward char *colon; 1261219Skas 1271219Skas ibuf = setinput(mp); 12831142Sedward if ((lc = mp->m_lines - 1) < 0) 12931142Sedward return NOSTR; 13036478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 13131142Sedward return NOSTR; 13231142Sedward while (lc > 0) { 13331142Sedward if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) 13431142Sedward return NOSTR; 13531142Sedward if (hfield = ishfield(linebuf, colon, field)) 13631142Sedward return savestr(hfield); 13731142Sedward } 13831142Sedward return NOSTR; 1391219Skas } 1401219Skas 1411219Skas /* 1421219Skas * Return the next header field found in the given message. 14331142Sedward * Return >= 0 if something found, < 0 elsewise. 14431142Sedward * "colon" is set to point to the colon in the header. 1451219Skas * Must deal with \ continuations & other such fraud. 1461219Skas */ 147*54505Sbostic int 14831142Sedward gethfield(f, linebuf, rem, colon) 1491219Skas register FILE *f; 1501219Skas char linebuf[]; 1511219Skas register int rem; 15231142Sedward char **colon; 1531219Skas { 1541219Skas char line2[LINESIZE]; 1551219Skas register char *cp, *cp2; 1561219Skas register int c; 1571219Skas 1581219Skas for (;;) { 15931142Sedward if (--rem < 0) 16031142Sedward return -1; 16136478Sedward if ((c = readline(f, linebuf, LINESIZE)) <= 0) 16231142Sedward return -1; 16331142Sedward for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':'; 16431142Sedward cp++) 16531142Sedward ; 16631142Sedward if (*cp != ':' || cp == linebuf) 1671219Skas continue; 1681219Skas /* 1691219Skas * I guess we got a headline. 1701219Skas * Handle wraparounding 1711219Skas */ 17231142Sedward *colon = cp; 17331142Sedward cp = linebuf + c; 1741219Skas for (;;) { 17531142Sedward while (--cp >= linebuf && (*cp == ' ' || *cp == '\t')) 17631142Sedward ; 17731142Sedward cp++; 1781219Skas if (rem <= 0) 1791219Skas break; 18031142Sedward ungetc(c = getc(f), f); 18131142Sedward if (c != ' ' && c != '\t') 1821219Skas break; 18336478Sedward if ((c = readline(f, line2, LINESIZE)) < 0) 1841219Skas break; 1851219Skas rem--; 18631142Sedward for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++) 1871219Skas ; 18831142Sedward c -= cp2 - line2; 18931142Sedward if (cp + c >= linebuf + LINESIZE - 2) 1901219Skas break; 1911219Skas *cp++ = ' '; 19231142Sedward bcopy(cp2, cp, c); 19331142Sedward cp += c; 1941219Skas } 19531142Sedward *cp = 0; 19631142Sedward return rem; 1971219Skas } 1981219Skas /* NOTREACHED */ 1991219Skas } 2001219Skas 2011219Skas /* 2021219Skas * Check whether the passed line is a header line of 20331142Sedward * the desired breed. Return the field body, or 0. 2041219Skas */ 2051219Skas 20631142Sedward char* 20731142Sedward ishfield(linebuf, colon, field) 2081219Skas char linebuf[], field[]; 20931142Sedward char *colon; 2101219Skas { 21131142Sedward register char *cp = colon; 2121219Skas 2131219Skas *cp = 0; 21434987Sedward if (strcasecmp(linebuf, field) != 0) { 21531142Sedward *cp = ':'; 21631142Sedward return 0; 2171219Skas } 21831142Sedward *cp = ':'; 21931142Sedward for (cp++; *cp == ' ' || *cp == '\t'; cp++) 22031142Sedward ; 22131142Sedward return cp; 2221219Skas } 2231219Skas 2241219Skas /* 2257571Skurt * Copy a string, lowercasing it as we go. 2267571Skurt */ 227*54505Sbostic void 2287571Skurt istrcpy(dest, src) 22931142Sedward register char *dest, *src; 2307571Skurt { 2317571Skurt 2327571Skurt do { 23331142Sedward if (isupper(*src)) 23431142Sedward *dest++ = tolower(*src); 23531142Sedward else 23631142Sedward *dest++ = *src; 23731142Sedward } while (*src++ != 0); 2387571Skurt } 2397571Skurt 2407571Skurt /* 2411219Skas * The following code deals with input stacking to do source 2421219Skas * commands. All but the current file pointer are saved on 2431219Skas * the stack. 2441219Skas */ 2451219Skas 24634965Sedward static int ssp; /* Top of file stack */ 2471519Skas struct sstack { 2481519Skas FILE *s_file; /* File we were in. */ 2491519Skas int s_cond; /* Saved state of conditionals */ 2505785Skurt int s_loading; /* Loading .mailrc, etc. */ 25118661Sserge } sstack[NOFILE]; 2521219Skas 2531219Skas /* 2541219Skas * Pushdown current input file and switch to a new one. 2551219Skas * Set the global flag "sourcing" so that others will realize 2561219Skas * that they are no longer reading from a tty (in all probability). 2571219Skas */ 258*54505Sbostic int 25934966Sedward source(arglist) 26034966Sedward char **arglist; 2611219Skas { 26234966Sedward FILE *fi; 26334966Sedward char *cp; 2641219Skas 26534966Sedward if ((cp = expand(*arglist)) == NOSTR) 2661219Skas return(1); 26743865Sedward if ((fi = Fopen(cp, "r")) == NULL) { 2683914Skurt perror(cp); 2693914Skurt return(1); 2701219Skas } 27134965Sedward if (ssp >= NOFILE - 1) { 2721219Skas printf("Too much \"sourcing\" going on.\n"); 27343865Sedward Fclose(fi); 2741219Skas return(1); 2751219Skas } 27634965Sedward sstack[ssp].s_file = input; 2771519Skas sstack[ssp].s_cond = cond; 2785785Skurt sstack[ssp].s_loading = loading; 27934965Sedward ssp++; 2805785Skurt loading = 0; 2811519Skas cond = CANY; 2821219Skas input = fi; 2831219Skas sourcing++; 2841219Skas return(0); 2851219Skas } 2861219Skas 2871219Skas /* 2881219Skas * Pop the current input back to the previous level. 2891219Skas * Update the "sourcing" flag as appropriate. 2901219Skas */ 291*54505Sbostic int 2921219Skas unstack() 2931219Skas { 29434965Sedward if (ssp <= 0) { 2951219Skas printf("\"Source\" stack over-pop.\n"); 2961219Skas sourcing = 0; 2971219Skas return(1); 2981219Skas } 29943865Sedward Fclose(input); 3001519Skas if (cond != CANY) 3011519Skas printf("Unmatched \"if\"\n"); 30234965Sedward ssp--; 3031519Skas cond = sstack[ssp].s_cond; 3045785Skurt loading = sstack[ssp].s_loading; 30534965Sedward input = sstack[ssp].s_file; 30634965Sedward if (ssp == 0) 3075785Skurt sourcing = loading; 3081219Skas return(0); 3091219Skas } 3101219Skas 3111219Skas /* 3121219Skas * Touch the indicated file. 3131219Skas * This is nifty for the shell. 3141219Skas */ 315*54505Sbostic void 3161219Skas alter(name) 31738078Sbostic char *name; 3181219Skas { 31938078Sbostic struct stat sb; 32038078Sbostic struct timeval tv[2]; 32138078Sbostic time_t time(); 3221219Skas 32338078Sbostic if (stat(name, &sb)) 3241219Skas return; 32538078Sbostic tv[0].tv_sec = time((time_t *)0) + 1; 32638078Sbostic tv[1].tv_sec = sb.st_mtime; 32738078Sbostic tv[0].tv_usec = tv[1].tv_usec = 0; 32838078Sbostic (void)utimes(name, tv); 3291219Skas } 3301219Skas 3311219Skas /* 3321219Skas * Examine the passed line buffer and 3331219Skas * return true if it is all blanks and tabs. 3341219Skas */ 335*54505Sbostic int 3361219Skas blankline(linebuf) 3371219Skas char linebuf[]; 3381219Skas { 3391219Skas register char *cp; 3401219Skas 3411219Skas for (cp = linebuf; *cp; cp++) 34218661Sserge if (*cp != ' ' && *cp != '\t') 3431219Skas return(0); 3441219Skas return(1); 3451219Skas } 3461219Skas 3471219Skas /* 3483195Skas * Get sender's name from this message. If the message has 3493195Skas * a bunch of arpanet stuff in it, we may have to skin the name 3503195Skas * before returning it. 3513195Skas */ 3523195Skas char * 3533195Skas nameof(mp, reptype) 3543195Skas register struct message *mp; 355*54505Sbostic int reptype; 3563195Skas { 3575237Skurt register char *cp, *cp2; 3583195Skas 3595237Skurt cp = skin(name1(mp, reptype)); 3605237Skurt if (reptype != 0 || charcount(cp, '!') < 2) 3615237Skurt return(cp); 3625237Skurt cp2 = rindex(cp, '!'); 3635237Skurt cp2--; 3645237Skurt while (cp2 > cp && *cp2 != '!') 3655237Skurt cp2--; 3665237Skurt if (*cp2 == '!') 3675237Skurt return(cp2 + 1); 3685237Skurt return(cp); 3693195Skas } 3703195Skas 3713195Skas /* 37239763Sedward * Start of a "comment". 37339763Sedward * Ignore it. 37439763Sedward */ 37539763Sedward char * 37639763Sedward skip_comment(cp) 37739763Sedward register char *cp; 37839763Sedward { 37939763Sedward register nesting = 1; 38039763Sedward 38139763Sedward for (; nesting > 0 && *cp; cp++) { 38239763Sedward switch (*cp) { 38339763Sedward case '\\': 38439763Sedward if (cp[1]) 38539763Sedward cp++; 38639763Sedward break; 38739763Sedward case '(': 38839763Sedward nesting++; 38939763Sedward break; 39039763Sedward case ')': 39139763Sedward nesting--; 39239763Sedward break; 39339763Sedward } 39439763Sedward } 39539763Sedward return cp; 39639763Sedward } 39739763Sedward 39839763Sedward /* 39925912Smckusick * Skin an arpa net address according to the RFC 822 interpretation 4003195Skas * of "host-phrase." 4013195Skas */ 4023195Skas char * 4033195Skas skin(name) 4043195Skas char *name; 4053195Skas { 4063195Skas register int c; 4073195Skas register char *cp, *cp2; 40825912Smckusick char *bufend; 4093195Skas int gotlt, lastsp; 4103195Skas char nbuf[BUFSIZ]; 4113195Skas 4123195Skas if (name == NOSTR) 4133195Skas return(NOSTR); 41412819Sleres if (index(name, '(') == NOSTR && index(name, '<') == NOSTR 41531142Sedward && index(name, ' ') == NOSTR) 4163195Skas return(name); 4173195Skas gotlt = 0; 4183195Skas lastsp = 0; 41925912Smckusick bufend = nbuf; 42025912Smckusick for (cp = name, cp2 = bufend; c = *cp++; ) { 4213195Skas switch (c) { 4223195Skas case '(': 42339763Sedward cp = skip_comment(cp); 42412819Sleres lastsp = 0; 4253195Skas break; 4263195Skas 42725912Smckusick case '"': 42825912Smckusick /* 42925912Smckusick * Start of a "quoted-string". 43025912Smckusick * Copy it in its entirety. 43125912Smckusick */ 43239763Sedward while (c = *cp) { 43325912Smckusick cp++; 43439763Sedward if (c == '"') 43539763Sedward break; 43639763Sedward if (c != '\\') 43739763Sedward *cp2++ = c; 43839763Sedward else if (c = *cp) { 43939763Sedward *cp2++ = c; 44025912Smckusick cp++; 44125912Smckusick } 44225912Smckusick } 44325912Smckusick lastsp = 0; 44425912Smckusick break; 44525912Smckusick 4463195Skas case ' ': 44712819Sleres if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 44812819Sleres cp += 3, *cp2++ = '@'; 44912819Sleres else 45012819Sleres if (cp[0] == '@' && cp[1] == ' ') 45112819Sleres cp += 2, *cp2++ = '@'; 45212819Sleres else 45312819Sleres lastsp = 1; 4543195Skas break; 4553195Skas 4563195Skas case '<': 45725912Smckusick cp2 = bufend; 4583195Skas gotlt++; 4593195Skas lastsp = 0; 4603195Skas break; 4613195Skas 4623195Skas case '>': 46325912Smckusick if (gotlt) { 46425912Smckusick gotlt = 0; 46539763Sedward while ((c = *cp) && c != ',') { 46625912Smckusick cp++; 46739763Sedward if (c == '(') 46839763Sedward cp = skip_comment(cp); 46939763Sedward else if (c == '"') 47039763Sedward while (c = *cp) { 47139763Sedward cp++; 47239763Sedward if (c == '"') 47339763Sedward break; 47439763Sedward if (c == '\\' && *cp) 47539763Sedward cp++; 47639763Sedward } 47739763Sedward } 47839763Sedward lastsp = 0; 47925912Smckusick break; 48025912Smckusick } 4813195Skas /* Fall into . . . */ 4823195Skas 4833195Skas default: 4843195Skas if (lastsp) { 4853195Skas lastsp = 0; 4863195Skas *cp2++ = ' '; 4873195Skas } 4883195Skas *cp2++ = c; 48939763Sedward if (c == ',' && !gotlt) { 49039763Sedward *cp2++ = ' '; 49139763Sedward for (; *cp == ' '; cp++) 49239763Sedward ; 49339763Sedward lastsp = 0; 49439763Sedward bufend = cp2; 49539763Sedward } 4963195Skas } 4973195Skas } 4983195Skas *cp2 = 0; 4993195Skas 5003195Skas return(savestr(nbuf)); 5013195Skas } 5023195Skas 5033195Skas /* 5041219Skas * Fetch the sender's name from the passed message. 5053195Skas * Reptype can be 5063195Skas * 0 -- get sender's name for display purposes 5073195Skas * 1 -- get sender's name for reply 5083195Skas * 2 -- get sender's name for Reply 5091219Skas */ 5101219Skas char * 5113195Skas name1(mp, reptype) 5121219Skas register struct message *mp; 513*54505Sbostic int reptype; 5141219Skas { 5151219Skas char namebuf[LINESIZE]; 5161219Skas char linebuf[LINESIZE]; 5171219Skas register char *cp, *cp2; 5181219Skas register FILE *ibuf; 5191219Skas int first = 1; 5201219Skas 5213195Skas if ((cp = hfield("from", mp)) != NOSTR) 52231142Sedward return cp; 5233195Skas if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 52431142Sedward return cp; 5251219Skas ibuf = setinput(mp); 52631142Sedward namebuf[0] = 0; 52736478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 5281219Skas return(savestr(namebuf)); 5291219Skas newname: 53031142Sedward for (cp = linebuf; *cp && *cp != ' '; cp++) 5311219Skas ; 53231142Sedward for (; *cp == ' ' || *cp == '\t'; cp++) 5331219Skas ; 53431142Sedward for (cp2 = &namebuf[strlen(namebuf)]; 53531142Sedward *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) 53631142Sedward *cp2++ = *cp++; 5371219Skas *cp2 = '\0'; 53836478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 5391219Skas return(savestr(namebuf)); 5401219Skas if ((cp = index(linebuf, 'F')) == NULL) 5411219Skas return(savestr(namebuf)); 5421219Skas if (strncmp(cp, "From", 4) != 0) 5431219Skas return(savestr(namebuf)); 5441219Skas while ((cp = index(cp, 'r')) != NULL) { 5451219Skas if (strncmp(cp, "remote", 6) == 0) { 5461219Skas if ((cp = index(cp, 'f')) == NULL) 5471219Skas break; 5481219Skas if (strncmp(cp, "from", 4) != 0) 5491219Skas break; 5501219Skas if ((cp = index(cp, ' ')) == NULL) 5511219Skas break; 5521219Skas cp++; 5531219Skas if (first) { 55431142Sedward strcpy(namebuf, cp); 5551219Skas first = 0; 5561219Skas } else 5571219Skas strcpy(rindex(namebuf, '!')+1, cp); 5581219Skas strcat(namebuf, "!"); 5591219Skas goto newname; 5601219Skas } 5611219Skas cp++; 5621219Skas } 5631219Skas return(savestr(namebuf)); 5641219Skas } 5651219Skas 5661219Skas /* 5675237Skurt * Count the occurances of c in str 5685237Skurt */ 569*54505Sbostic int 5705237Skurt charcount(str, c) 5715237Skurt char *str; 572*54505Sbostic int c; 5735237Skurt { 5745237Skurt register char *cp; 5755237Skurt register int i; 5765237Skurt 5775237Skurt for (i = 0, cp = str; *cp; cp++) 5785237Skurt if (*cp == c) 5795237Skurt i++; 5805237Skurt return(i); 5815237Skurt } 5825237Skurt 5835237Skurt /* 58431142Sedward * Are any of the characters in the two strings the same? 5851219Skas */ 586*54505Sbostic int 58731142Sedward anyof(s1, s2) 58831142Sedward register char *s1, *s2; 5891219Skas { 5901219Skas 59131142Sedward while (*s1) 59231142Sedward if (index(s2, *s1++)) 59331142Sedward return 1; 59431142Sedward return 0; 5951219Skas } 5961219Skas 5971219Skas /* 59831142Sedward * Convert c to upper case 5991219Skas */ 600*54505Sbostic int 60131142Sedward raise(c) 602*54505Sbostic register int c; 60331142Sedward { 60431142Sedward 60531142Sedward if (islower(c)) 60631142Sedward return toupper(c); 60731142Sedward return c; 60831142Sedward } 60931142Sedward 61031142Sedward /* 61131142Sedward * Copy s1 to s2, return pointer to null in s2. 61231142Sedward */ 61331142Sedward char * 61431142Sedward copy(s1, s2) 6151219Skas register char *s1, *s2; 6161219Skas { 6171219Skas 61831142Sedward while (*s2++ = *s1++) 61931142Sedward ; 62031142Sedward return s2 - 1; 6211219Skas } 6221219Skas 6231219Skas /* 6247571Skurt * See if the given header field is supposed to be ignored. 6257571Skurt */ 626*54505Sbostic int 62734692Sedward isign(field, ignore) 6287571Skurt char *field; 62934692Sedward struct ignoretab ignore[2]; 6307571Skurt { 6317571Skurt char realfld[BUFSIZ]; 6327571Skurt 63334969Sedward if (ignore == ignoreall) 63434969Sedward return 1; 63518661Sserge /* 63618661Sserge * Lower-case the string, so that "Status" and "status" 63718661Sserge * will hash to the same place. 63818661Sserge */ 6397571Skurt istrcpy(realfld, field); 64034692Sedward if (ignore[1].i_count > 0) 64134692Sedward return (!member(realfld, ignore + 1)); 64218661Sserge else 64318661Sserge return (member(realfld, ignore)); 6447571Skurt } 64518661Sserge 646*54505Sbostic int 64718661Sserge member(realfield, table) 64818661Sserge register char *realfield; 64934692Sedward struct ignoretab *table; 65018661Sserge { 65118661Sserge register struct ignore *igp; 65218661Sserge 65334692Sedward for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link) 65431142Sedward if (*igp->i_field == *realfield && 65531142Sedward equal(igp->i_field, realfield)) 65618661Sserge return (1); 65718661Sserge return (0); 65818661Sserge } 659