122444Sdist /* 222444Sdist * Copyright (c) 1980 Regents of the University of California. 333499Sbostic * All rights reserved. 433499Sbostic * 533499Sbostic * Redistribution and use in source and binary forms are permitted 634905Sbostic * provided that the above copyright notice and this paragraph are 734905Sbostic * duplicated in all such forms and that any documentation, 834905Sbostic * advertising materials, and other materials related to such 934905Sbostic * distribution and use acknowledge that the software was developed 1034905Sbostic * by the University of California, Berkeley. The name of the 1134905Sbostic * University may not be used to endorse or promote products derived 1234905Sbostic * from this software without specific prior written permission. 1334905Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434905Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534905Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1622444Sdist */ 1722444Sdist 1834905Sbostic #ifndef lint 19*34966Sedward static char sccsid[] = "@(#)aux.c 5.13 (Berkeley) 07/07/88"; 2034905Sbostic #endif /* not lint */ 211219Skas 221219Skas #include "rcv.h" 231219Skas #include <sys/stat.h> 241219Skas 251219Skas /* 261219Skas * Mail -- a mail program 271219Skas * 281219Skas * Auxiliary functions. 291219Skas */ 301219Skas 311219Skas /* 321219Skas * Return a pointer to a dynamic copy of the argument. 331219Skas */ 341219Skas 351219Skas char * 361219Skas savestr(str) 371219Skas char *str; 381219Skas { 391219Skas register char *cp, *cp2, *top; 401219Skas 411219Skas for (cp = str; *cp; cp++) 421219Skas ; 431219Skas top = salloc(cp-str + 1); 441219Skas if (top == NOSTR) 451219Skas return(NOSTR); 461219Skas for (cp = str, cp2 = top; *cp; cp++) 471219Skas *cp2++ = *cp; 481219Skas *cp2 = 0; 491219Skas return(top); 501219Skas } 511219Skas 521219Skas /* 531219Skas * Announce a fatal error and die. 541219Skas */ 551219Skas 5631142Sedward /*VARARGS1*/ 5731142Sedward panic(fmt, a, b) 5831142Sedward char *fmt; 591219Skas { 6031142Sedward fprintf(stderr, "panic: "); 6131142Sedward fprintf(stderr, fmt, a, b); 6231142Sedward putc('\n', stderr); 631219Skas exit(1); 641219Skas } 651219Skas 661219Skas /* 671219Skas * Touch the named message by setting its MTOUCH flag. 681219Skas * Touched messages have the effect of not being sent 691219Skas * back to the system mailbox on exit. 701219Skas */ 711219Skas 721219Skas touch(mesg) 731219Skas { 741479Skas register struct message *mp; 751479Skas 761479Skas if (mesg < 1 || mesg > msgCount) 771479Skas return; 781479Skas mp = &message[mesg-1]; 791479Skas mp->m_flag |= MTOUCH; 801479Skas if ((mp->m_flag & MREAD) == 0) 811479Skas mp->m_flag |= MREAD|MSTATUS; 821219Skas } 831219Skas 841219Skas /* 851219Skas * Test to see if the passed file name is a directory. 861219Skas * Return true if it is. 871219Skas */ 881219Skas 891219Skas isdir(name) 901219Skas char name[]; 911219Skas { 921219Skas struct stat sbuf; 931219Skas 941219Skas if (stat(name, &sbuf) < 0) 951219Skas return(0); 961219Skas return((sbuf.st_mode & S_IFMT) == S_IFDIR); 971219Skas } 981219Skas 991219Skas /* 1001219Skas * Count the number of arguments in the given string raw list. 1011219Skas */ 1021219Skas 1031219Skas argcount(argv) 1041219Skas char **argv; 1051219Skas { 1061219Skas register char **ap; 1071219Skas 10831142Sedward for (ap = argv; *ap++ != NOSTR;) 1091219Skas ; 11031142Sedward return ap - argv - 1; 1111219Skas } 1121219Skas 1131219Skas /* 1141219Skas * Return the desired header line from the passed message 1151219Skas * pointer (or NOSTR if the desired header field is not available). 1161219Skas */ 1171219Skas 1181219Skas char * 1191219Skas hfield(field, mp) 1201219Skas char field[]; 1211219Skas struct message *mp; 1221219Skas { 1231219Skas register FILE *ibuf; 1241219Skas char linebuf[LINESIZE]; 1251219Skas register int lc; 12631142Sedward register char *hfield; 12731142Sedward char *colon; 1281219Skas 1291219Skas ibuf = setinput(mp); 13031142Sedward if ((lc = mp->m_lines - 1) < 0) 13131142Sedward return NOSTR; 1321219Skas if (readline(ibuf, linebuf) < 0) 13331142Sedward return NOSTR; 13431142Sedward while (lc > 0) { 13531142Sedward if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) 13631142Sedward return NOSTR; 13731142Sedward if (hfield = ishfield(linebuf, colon, field)) 13831142Sedward return savestr(hfield); 13931142Sedward } 14031142Sedward return NOSTR; 1411219Skas } 1421219Skas 1431219Skas /* 1441219Skas * Return the next header field found in the given message. 14531142Sedward * Return >= 0 if something found, < 0 elsewise. 14631142Sedward * "colon" is set to point to the colon in the header. 1471219Skas * Must deal with \ continuations & other such fraud. 1481219Skas */ 1491219Skas 15031142Sedward gethfield(f, linebuf, rem, colon) 1511219Skas register FILE *f; 1521219Skas char linebuf[]; 1531219Skas register int rem; 15431142Sedward char **colon; 1551219Skas { 1561219Skas char line2[LINESIZE]; 1571219Skas register char *cp, *cp2; 1581219Skas register int c; 1591219Skas 1601219Skas for (;;) { 16131142Sedward if (--rem < 0) 16231142Sedward return -1; 16331142Sedward if ((c = readline(f, linebuf)) <= 0) 16431142Sedward return -1; 16531142Sedward for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':'; 16631142Sedward cp++) 16731142Sedward ; 16831142Sedward if (*cp != ':' || cp == linebuf) 1691219Skas continue; 1701219Skas /* 1711219Skas * I guess we got a headline. 1721219Skas * Handle wraparounding 1731219Skas */ 17431142Sedward *colon = cp; 17531142Sedward cp = linebuf + c; 1761219Skas for (;;) { 17731142Sedward while (--cp >= linebuf && (*cp == ' ' || *cp == '\t')) 17831142Sedward ; 17931142Sedward cp++; 1801219Skas if (rem <= 0) 1811219Skas break; 18231142Sedward ungetc(c = getc(f), f); 18331142Sedward if (c != ' ' && c != '\t') 1841219Skas break; 18531142Sedward if ((c = readline(f, line2)) < 0) 1861219Skas break; 1871219Skas rem--; 18831142Sedward for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++) 1891219Skas ; 19031142Sedward c -= cp2 - line2; 19131142Sedward if (cp + c >= linebuf + LINESIZE - 2) 1921219Skas break; 1931219Skas *cp++ = ' '; 19431142Sedward bcopy(cp2, cp, c); 19531142Sedward cp += c; 1961219Skas } 19731142Sedward *cp = 0; 19831142Sedward return rem; 1991219Skas } 2001219Skas /* NOTREACHED */ 2011219Skas } 2021219Skas 2031219Skas /* 2041219Skas * Check whether the passed line is a header line of 20531142Sedward * the desired breed. Return the field body, or 0. 2061219Skas */ 2071219Skas 20831142Sedward char* 20931142Sedward ishfield(linebuf, colon, field) 2101219Skas char linebuf[], field[]; 21131142Sedward char *colon; 2121219Skas { 21331142Sedward register char *cp = colon; 2141219Skas 2151219Skas *cp = 0; 21631142Sedward if (!icequal(linebuf, field)) { 21731142Sedward *cp = ':'; 21831142Sedward return 0; 2191219Skas } 22031142Sedward *cp = ':'; 22131142Sedward for (cp++; *cp == ' ' || *cp == '\t'; cp++) 22231142Sedward ; 22331142Sedward return cp; 2241219Skas } 2251219Skas 2261219Skas /* 2271219Skas * Compare two strings, ignoring case. 2281219Skas */ 2291219Skas 2301219Skas icequal(s1, s2) 2311219Skas register char *s1, *s2; 2321219Skas { 23331142Sedward register c1, c2; 2341219Skas 23531142Sedward for (;;) { 23631142Sedward if ((c1 = (unsigned char)*s1++) != 23731142Sedward (c2 = (unsigned char)*s2++)) { 23831142Sedward if (isupper(c1)) 23931142Sedward c1 = tolower(c1); 24031142Sedward if (c1 != c2) 24131142Sedward return 0; 24231142Sedward } 24331142Sedward if (c1 == 0) 24431142Sedward return 1; 24531142Sedward } 24631142Sedward /*NOTREACHED*/ 2471219Skas } 2481219Skas 2491219Skas /* 2507571Skurt * Copy a string, lowercasing it as we go. 2517571Skurt */ 2527571Skurt istrcpy(dest, src) 25331142Sedward register char *dest, *src; 2547571Skurt { 2557571Skurt 2567571Skurt do { 25731142Sedward if (isupper(*src)) 25831142Sedward *dest++ = tolower(*src); 25931142Sedward else 26031142Sedward *dest++ = *src; 26131142Sedward } while (*src++ != 0); 2627571Skurt } 2637571Skurt 2647571Skurt /* 2651219Skas * The following code deals with input stacking to do source 2661219Skas * commands. All but the current file pointer are saved on 2671219Skas * the stack. 2681219Skas */ 2691219Skas 27034965Sedward static int ssp; /* Top of file stack */ 2711519Skas struct sstack { 2721519Skas FILE *s_file; /* File we were in. */ 2731519Skas int s_cond; /* Saved state of conditionals */ 2745785Skurt int s_loading; /* Loading .mailrc, etc. */ 27518661Sserge } sstack[NOFILE]; 2761219Skas 2771219Skas /* 2781219Skas * Pushdown current input file and switch to a new one. 2791219Skas * Set the global flag "sourcing" so that others will realize 2801219Skas * that they are no longer reading from a tty (in all probability). 2811219Skas */ 282*34966Sedward source(arglist) 283*34966Sedward char **arglist; 2841219Skas { 285*34966Sedward FILE *fi; 286*34966Sedward char *cp; 2871219Skas 288*34966Sedward if ((cp = expand(*arglist)) == NOSTR) 2891219Skas return(1); 2903914Skurt if ((fi = fopen(cp, "r")) == NULL) { 2913914Skurt perror(cp); 2923914Skurt return(1); 2931219Skas } 29434965Sedward if (ssp >= NOFILE - 1) { 2951219Skas printf("Too much \"sourcing\" going on.\n"); 2961219Skas fclose(fi); 2971219Skas return(1); 2981219Skas } 29934965Sedward sstack[ssp].s_file = input; 3001519Skas sstack[ssp].s_cond = cond; 3015785Skurt sstack[ssp].s_loading = loading; 30234965Sedward ssp++; 3035785Skurt loading = 0; 3041519Skas cond = CANY; 3051219Skas input = fi; 3061219Skas sourcing++; 3071219Skas return(0); 3081219Skas } 3091219Skas 3101219Skas /* 3111219Skas * Pop the current input back to the previous level. 3121219Skas * Update the "sourcing" flag as appropriate. 3131219Skas */ 3141219Skas 3151219Skas unstack() 3161219Skas { 31734965Sedward if (ssp <= 0) { 3181219Skas printf("\"Source\" stack over-pop.\n"); 3191219Skas sourcing = 0; 3201219Skas return(1); 3211219Skas } 3221219Skas fclose(input); 3231519Skas if (cond != CANY) 3241519Skas printf("Unmatched \"if\"\n"); 32534965Sedward ssp--; 3261519Skas cond = sstack[ssp].s_cond; 3275785Skurt loading = sstack[ssp].s_loading; 32834965Sedward input = sstack[ssp].s_file; 32934965Sedward if (ssp == 0) 3305785Skurt sourcing = loading; 3311219Skas return(0); 3321219Skas } 3331219Skas 3341219Skas /* 3351219Skas * Touch the indicated file. 3361219Skas * This is nifty for the shell. 3371219Skas */ 3381219Skas 3391219Skas alter(name) 3401219Skas char name[]; 3411219Skas { 3421219Skas struct stat statb; 3431219Skas long time(); 3441219Skas time_t time_p[2]; 3451219Skas 3461219Skas if (stat(name, &statb) < 0) 3471219Skas return; 3481219Skas time_p[0] = time((long *) 0) + 1; 3491219Skas time_p[1] = statb.st_mtime; 3501219Skas utime(name, time_p); 3511219Skas } 3521219Skas 3531219Skas /* 3541219Skas * Examine the passed line buffer and 3551219Skas * return true if it is all blanks and tabs. 3561219Skas */ 3571219Skas 3581219Skas blankline(linebuf) 3591219Skas char linebuf[]; 3601219Skas { 3611219Skas register char *cp; 3621219Skas 3631219Skas for (cp = linebuf; *cp; cp++) 36418661Sserge if (*cp != ' ' && *cp != '\t') 3651219Skas return(0); 3661219Skas return(1); 3671219Skas } 3681219Skas 3691219Skas /* 3703195Skas * Get sender's name from this message. If the message has 3713195Skas * a bunch of arpanet stuff in it, we may have to skin the name 3723195Skas * before returning it. 3733195Skas */ 3743195Skas char * 3753195Skas nameof(mp, reptype) 3763195Skas register struct message *mp; 3773195Skas { 3785237Skurt register char *cp, *cp2; 3793195Skas 3805237Skurt cp = skin(name1(mp, reptype)); 3815237Skurt if (reptype != 0 || charcount(cp, '!') < 2) 3825237Skurt return(cp); 3835237Skurt cp2 = rindex(cp, '!'); 3845237Skurt cp2--; 3855237Skurt while (cp2 > cp && *cp2 != '!') 3865237Skurt cp2--; 3875237Skurt if (*cp2 == '!') 3885237Skurt return(cp2 + 1); 3895237Skurt return(cp); 3903195Skas } 3913195Skas 3923195Skas /* 39325912Smckusick * Skin an arpa net address according to the RFC 822 interpretation 3943195Skas * of "host-phrase." 3953195Skas */ 3963195Skas char * 3973195Skas skin(name) 3983195Skas char *name; 3993195Skas { 4003195Skas register int c; 4013195Skas register char *cp, *cp2; 40225912Smckusick char *bufend; 4033195Skas int gotlt, lastsp; 4043195Skas char nbuf[BUFSIZ]; 40518661Sserge int nesting; 4063195Skas 4073195Skas if (name == NOSTR) 4083195Skas return(NOSTR); 40912819Sleres if (index(name, '(') == NOSTR && index(name, '<') == NOSTR 41031142Sedward && index(name, ' ') == NOSTR) 4113195Skas return(name); 4123195Skas gotlt = 0; 4133195Skas lastsp = 0; 41425912Smckusick bufend = nbuf; 41525912Smckusick for (cp = name, cp2 = bufend; c = *cp++; ) { 4163195Skas switch (c) { 4173195Skas case '(': 41825912Smckusick /* 41925912Smckusick * Start of a "comment". 42025912Smckusick * Ignore it. 42125912Smckusick */ 42218661Sserge nesting = 1; 42325912Smckusick while ((c = *cp) != 0) { 42425912Smckusick cp++; 42525912Smckusick switch (c) { 42625912Smckusick case '\\': 42725912Smckusick if (*cp == 0) 42825912Smckusick goto outcm; 42925912Smckusick cp++; 43025912Smckusick break; 43118661Sserge case '(': 43218661Sserge nesting++; 43318661Sserge break; 43418661Sserge 43518661Sserge case ')': 43618661Sserge --nesting; 43718661Sserge break; 43818661Sserge } 43918661Sserge 44018661Sserge if (nesting <= 0) 44118661Sserge break; 44218661Sserge } 44325912Smckusick outcm: 44412819Sleres lastsp = 0; 4453195Skas break; 4463195Skas 44725912Smckusick case '"': 44825912Smckusick /* 44925912Smckusick * Start of a "quoted-string". 45025912Smckusick * Copy it in its entirety. 45125912Smckusick */ 45225912Smckusick while ((c = *cp) != 0) { 45325912Smckusick cp++; 45425912Smckusick switch (c) { 45525912Smckusick case '\\': 45625912Smckusick if ((c = *cp) == 0) 45725912Smckusick goto outqs; 45825912Smckusick cp++; 45925912Smckusick break; 46025912Smckusick case '"': 46125912Smckusick goto outqs; 46225912Smckusick } 46325912Smckusick *cp2++ = c; 46425912Smckusick } 46525912Smckusick outqs: 46625912Smckusick lastsp = 0; 46725912Smckusick break; 46825912Smckusick 4693195Skas case ' ': 47012819Sleres if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 47112819Sleres cp += 3, *cp2++ = '@'; 47212819Sleres else 47312819Sleres if (cp[0] == '@' && cp[1] == ' ') 47412819Sleres cp += 2, *cp2++ = '@'; 47512819Sleres else 47612819Sleres lastsp = 1; 4773195Skas break; 4783195Skas 4793195Skas case '<': 48025912Smckusick cp2 = bufend; 4813195Skas gotlt++; 4823195Skas lastsp = 0; 4833195Skas break; 4843195Skas 4853195Skas case '>': 48625912Smckusick if (gotlt) { 48725912Smckusick gotlt = 0; 48825912Smckusick while (*cp != ',' && *cp != 0) 48925912Smckusick cp++; 49025912Smckusick if (*cp == 0 ) 49125912Smckusick goto done; 49225912Smckusick *cp2++ = ','; 49325912Smckusick *cp2++ = ' '; 49425912Smckusick bufend = cp2; 49525912Smckusick break; 49625912Smckusick } 4973195Skas 4983195Skas /* Fall into . . . */ 4993195Skas 5003195Skas default: 5013195Skas if (lastsp) { 5023195Skas lastsp = 0; 5033195Skas *cp2++ = ' '; 5043195Skas } 5053195Skas *cp2++ = c; 5063195Skas break; 5073195Skas } 5083195Skas } 5093195Skas done: 5103195Skas *cp2 = 0; 5113195Skas 5123195Skas return(savestr(nbuf)); 5133195Skas } 5143195Skas 5153195Skas /* 5161219Skas * Fetch the sender's name from the passed message. 5173195Skas * Reptype can be 5183195Skas * 0 -- get sender's name for display purposes 5193195Skas * 1 -- get sender's name for reply 5203195Skas * 2 -- get sender's name for Reply 5211219Skas */ 5221219Skas 5231219Skas char * 5243195Skas name1(mp, reptype) 5251219Skas register struct message *mp; 5261219Skas { 5271219Skas char namebuf[LINESIZE]; 5281219Skas char linebuf[LINESIZE]; 5291219Skas register char *cp, *cp2; 5301219Skas register FILE *ibuf; 5311219Skas int first = 1; 5321219Skas 5333195Skas if ((cp = hfield("from", mp)) != NOSTR) 53431142Sedward return cp; 5353195Skas if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 53631142Sedward return cp; 5371219Skas ibuf = setinput(mp); 53831142Sedward namebuf[0] = 0; 53931142Sedward if (readline(ibuf, linebuf) < 0) 5401219Skas return(savestr(namebuf)); 5411219Skas newname: 54231142Sedward for (cp = linebuf; *cp && *cp != ' '; cp++) 5431219Skas ; 54431142Sedward for (; *cp == ' ' || *cp == '\t'; cp++) 5451219Skas ; 54631142Sedward for (cp2 = &namebuf[strlen(namebuf)]; 54731142Sedward *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) 54831142Sedward *cp2++ = *cp++; 5491219Skas *cp2 = '\0'; 55031142Sedward if (readline(ibuf, linebuf) < 0) 5511219Skas return(savestr(namebuf)); 5521219Skas if ((cp = index(linebuf, 'F')) == NULL) 5531219Skas return(savestr(namebuf)); 5541219Skas if (strncmp(cp, "From", 4) != 0) 5551219Skas return(savestr(namebuf)); 5561219Skas while ((cp = index(cp, 'r')) != NULL) { 5571219Skas if (strncmp(cp, "remote", 6) == 0) { 5581219Skas if ((cp = index(cp, 'f')) == NULL) 5591219Skas break; 5601219Skas if (strncmp(cp, "from", 4) != 0) 5611219Skas break; 5621219Skas if ((cp = index(cp, ' ')) == NULL) 5631219Skas break; 5641219Skas cp++; 5651219Skas if (first) { 56631142Sedward strcpy(namebuf, cp); 5671219Skas first = 0; 5681219Skas } else 5691219Skas strcpy(rindex(namebuf, '!')+1, cp); 5701219Skas strcat(namebuf, "!"); 5711219Skas goto newname; 5721219Skas } 5731219Skas cp++; 5741219Skas } 5751219Skas return(savestr(namebuf)); 5761219Skas } 5771219Skas 5781219Skas /* 5795237Skurt * Count the occurances of c in str 5805237Skurt */ 5815237Skurt charcount(str, c) 5825237Skurt char *str; 5835237Skurt { 5845237Skurt register char *cp; 5855237Skurt register int i; 5865237Skurt 5875237Skurt for (i = 0, cp = str; *cp; cp++) 5885237Skurt if (*cp == c) 5895237Skurt i++; 5905237Skurt return(i); 5915237Skurt } 5925237Skurt 5935237Skurt /* 59431142Sedward * Are any of the characters in the two strings the same? 5951219Skas */ 5961219Skas 59731142Sedward anyof(s1, s2) 59831142Sedward register char *s1, *s2; 5991219Skas { 6001219Skas 60131142Sedward while (*s1) 60231142Sedward if (index(s2, *s1++)) 60331142Sedward return 1; 60431142Sedward return 0; 6051219Skas } 6061219Skas 6071219Skas /* 60831142Sedward * Convert c to upper case 6091219Skas */ 6101219Skas 61131142Sedward raise(c) 61231142Sedward register c; 61331142Sedward { 61431142Sedward 61531142Sedward if (islower(c)) 61631142Sedward return toupper(c); 61731142Sedward return c; 61831142Sedward } 61931142Sedward 62031142Sedward /* 62131142Sedward * Copy s1 to s2, return pointer to null in s2. 62231142Sedward */ 62331142Sedward 62431142Sedward char * 62531142Sedward copy(s1, s2) 6261219Skas register char *s1, *s2; 6271219Skas { 6281219Skas 62931142Sedward while (*s2++ = *s1++) 63031142Sedward ; 63131142Sedward return s2 - 1; 6321219Skas } 6331219Skas 6341219Skas /* 6357571Skurt * See if the given header field is supposed to be ignored. 6367571Skurt */ 63734692Sedward isign(field, ignore) 6387571Skurt char *field; 63934692Sedward struct ignoretab ignore[2]; 6407571Skurt { 6417571Skurt char realfld[BUFSIZ]; 6427571Skurt 64318661Sserge /* 64418661Sserge * Lower-case the string, so that "Status" and "status" 64518661Sserge * will hash to the same place. 64618661Sserge */ 6477571Skurt istrcpy(realfld, field); 64834692Sedward if (ignore[1].i_count > 0) 64934692Sedward return (!member(realfld, ignore + 1)); 65018661Sserge else 65118661Sserge return (member(realfld, ignore)); 6527571Skurt } 65318661Sserge 65418661Sserge member(realfield, table) 65518661Sserge register char *realfield; 65634692Sedward struct ignoretab *table; 65718661Sserge { 65818661Sserge register struct ignore *igp; 65918661Sserge 66034692Sedward for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link) 66131142Sedward if (*igp->i_field == *realfield && 66231142Sedward equal(igp->i_field, realfield)) 66318661Sserge return (1); 66418661Sserge return (0); 66518661Sserge } 666