122444Sdist /* 222444Sdist * Copyright (c) 1980 Regents of the University of California. 322444Sdist * All rights reserved. The Berkeley software License Agreement 422444Sdist * specifies the terms and conditions for redistribution. 522444Sdist */ 622444Sdist 714522Ssam #ifndef lint 8*31142Sedward static char *sccsid = "@(#)aux.c 5.6 (Berkeley) 05/18/87"; 922444Sdist #endif not lint 101219Skas 111219Skas #include "rcv.h" 121219Skas #include <sys/stat.h> 131219Skas 141219Skas /* 151219Skas * Mail -- a mail program 161219Skas * 171219Skas * Auxiliary functions. 181219Skas */ 191219Skas 201219Skas /* 211219Skas * Return a pointer to a dynamic copy of the argument. 221219Skas */ 231219Skas 241219Skas char * 251219Skas savestr(str) 261219Skas char *str; 271219Skas { 281219Skas register char *cp, *cp2, *top; 291219Skas 301219Skas for (cp = str; *cp; cp++) 311219Skas ; 321219Skas top = salloc(cp-str + 1); 331219Skas if (top == NOSTR) 341219Skas return(NOSTR); 351219Skas for (cp = str, cp2 = top; *cp; cp++) 361219Skas *cp2++ = *cp; 371219Skas *cp2 = 0; 381219Skas return(top); 391219Skas } 401219Skas 411219Skas /* 421219Skas * Announce a fatal error and die. 431219Skas */ 441219Skas 45*31142Sedward /*VARARGS1*/ 46*31142Sedward panic(fmt, a, b) 47*31142Sedward char *fmt; 481219Skas { 49*31142Sedward fprintf(stderr, "panic: "); 50*31142Sedward fprintf(stderr, fmt, a, b); 51*31142Sedward putc('\n', stderr); 521219Skas exit(1); 531219Skas } 541219Skas 551219Skas /* 561219Skas * Touch the named message by setting its MTOUCH flag. 571219Skas * Touched messages have the effect of not being sent 581219Skas * back to the system mailbox on exit. 591219Skas */ 601219Skas 611219Skas touch(mesg) 621219Skas { 631479Skas register struct message *mp; 641479Skas 651479Skas if (mesg < 1 || mesg > msgCount) 661479Skas return; 671479Skas mp = &message[mesg-1]; 681479Skas mp->m_flag |= MTOUCH; 691479Skas if ((mp->m_flag & MREAD) == 0) 701479Skas mp->m_flag |= MREAD|MSTATUS; 711219Skas } 721219Skas 731219Skas /* 741219Skas * Test to see if the passed file name is a directory. 751219Skas * Return true if it is. 761219Skas */ 771219Skas 781219Skas isdir(name) 791219Skas char name[]; 801219Skas { 811219Skas struct stat sbuf; 821219Skas 831219Skas if (stat(name, &sbuf) < 0) 841219Skas return(0); 851219Skas return((sbuf.st_mode & S_IFMT) == S_IFDIR); 861219Skas } 871219Skas 881219Skas /* 891219Skas * Count the number of arguments in the given string raw list. 901219Skas */ 911219Skas 921219Skas argcount(argv) 931219Skas char **argv; 941219Skas { 951219Skas register char **ap; 961219Skas 97*31142Sedward for (ap = argv; *ap++ != NOSTR;) 981219Skas ; 99*31142Sedward return ap - argv - 1; 1001219Skas } 1011219Skas 1021219Skas /* 1031219Skas * Return the desired header line from the passed message 1041219Skas * pointer (or NOSTR if the desired header field is not available). 1051219Skas */ 1061219Skas 1071219Skas char * 1081219Skas hfield(field, mp) 1091219Skas char field[]; 1101219Skas struct message *mp; 1111219Skas { 1121219Skas register FILE *ibuf; 1131219Skas char linebuf[LINESIZE]; 1141219Skas register int lc; 115*31142Sedward register char *hfield; 116*31142Sedward char *colon; 1171219Skas 1181219Skas ibuf = setinput(mp); 119*31142Sedward if ((lc = mp->m_lines - 1) < 0) 120*31142Sedward return NOSTR; 1211219Skas if (readline(ibuf, linebuf) < 0) 122*31142Sedward return NOSTR; 123*31142Sedward while (lc > 0) { 124*31142Sedward if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) 125*31142Sedward return NOSTR; 126*31142Sedward if (hfield = ishfield(linebuf, colon, field)) 127*31142Sedward return savestr(hfield); 128*31142Sedward } 129*31142Sedward return NOSTR; 1301219Skas } 1311219Skas 1321219Skas /* 1331219Skas * Return the next header field found in the given message. 134*31142Sedward * Return >= 0 if something found, < 0 elsewise. 135*31142Sedward * "colon" is set to point to the colon in the header. 1361219Skas * Must deal with \ continuations & other such fraud. 1371219Skas */ 1381219Skas 139*31142Sedward gethfield(f, linebuf, rem, colon) 1401219Skas register FILE *f; 1411219Skas char linebuf[]; 1421219Skas register int rem; 143*31142Sedward char **colon; 1441219Skas { 1451219Skas char line2[LINESIZE]; 1461219Skas register char *cp, *cp2; 1471219Skas register int c; 1481219Skas 1491219Skas for (;;) { 150*31142Sedward if (--rem < 0) 151*31142Sedward return -1; 152*31142Sedward if ((c = readline(f, linebuf)) <= 0) 153*31142Sedward return -1; 154*31142Sedward for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':'; 155*31142Sedward cp++) 156*31142Sedward ; 157*31142Sedward if (*cp != ':' || cp == linebuf) 1581219Skas continue; 1591219Skas /* 1601219Skas * I guess we got a headline. 1611219Skas * Handle wraparounding 1621219Skas */ 163*31142Sedward *colon = cp; 164*31142Sedward cp = linebuf + c; 1651219Skas for (;;) { 166*31142Sedward while (--cp >= linebuf && (*cp == ' ' || *cp == '\t')) 167*31142Sedward ; 168*31142Sedward cp++; 1691219Skas if (rem <= 0) 1701219Skas break; 171*31142Sedward ungetc(c = getc(f), f); 172*31142Sedward if (c != ' ' && c != '\t') 1731219Skas break; 174*31142Sedward if ((c = readline(f, line2)) < 0) 1751219Skas break; 1761219Skas rem--; 177*31142Sedward for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++) 1781219Skas ; 179*31142Sedward c -= cp2 - line2; 180*31142Sedward if (cp + c >= linebuf + LINESIZE - 2) 1811219Skas break; 1821219Skas *cp++ = ' '; 183*31142Sedward bcopy(cp2, cp, c); 184*31142Sedward cp += c; 1851219Skas } 186*31142Sedward *cp = 0; 187*31142Sedward return rem; 1881219Skas } 1891219Skas /* NOTREACHED */ 1901219Skas } 1911219Skas 1921219Skas /* 1931219Skas * Check whether the passed line is a header line of 194*31142Sedward * the desired breed. Return the field body, or 0. 1951219Skas */ 1961219Skas 197*31142Sedward char* 198*31142Sedward ishfield(linebuf, colon, field) 1991219Skas char linebuf[], field[]; 200*31142Sedward char *colon; 2011219Skas { 202*31142Sedward register char *cp = colon; 2031219Skas 2041219Skas *cp = 0; 205*31142Sedward if (!icequal(linebuf, field)) { 206*31142Sedward *cp = ':'; 207*31142Sedward return 0; 2081219Skas } 209*31142Sedward *cp = ':'; 210*31142Sedward for (cp++; *cp == ' ' || *cp == '\t'; cp++) 211*31142Sedward ; 212*31142Sedward return cp; 2131219Skas } 2141219Skas 2151219Skas /* 2161219Skas * Compare two strings, ignoring case. 2171219Skas */ 2181219Skas 2191219Skas icequal(s1, s2) 2201219Skas register char *s1, *s2; 2211219Skas { 222*31142Sedward register c1, c2; 2231219Skas 224*31142Sedward for (;;) { 225*31142Sedward if ((c1 = (unsigned char)*s1++) != 226*31142Sedward (c2 = (unsigned char)*s2++)) { 227*31142Sedward if (isupper(c1)) 228*31142Sedward c1 = tolower(c1); 229*31142Sedward if (c1 != c2) 230*31142Sedward return 0; 231*31142Sedward } 232*31142Sedward if (c1 == 0) 233*31142Sedward return 1; 234*31142Sedward } 235*31142Sedward /*NOTREACHED*/ 2361219Skas } 2371219Skas 2381219Skas /* 2397571Skurt * Copy a string, lowercasing it as we go. 2407571Skurt */ 2417571Skurt istrcpy(dest, src) 242*31142Sedward register char *dest, *src; 2437571Skurt { 2447571Skurt 2457571Skurt do { 246*31142Sedward if (isupper(*src)) 247*31142Sedward *dest++ = tolower(*src); 248*31142Sedward else 249*31142Sedward *dest++ = *src; 250*31142Sedward } while (*src++ != 0); 2517571Skurt } 2527571Skurt 2537571Skurt /* 2541219Skas * The following code deals with input stacking to do source 2551219Skas * commands. All but the current file pointer are saved on 2561219Skas * the stack. 2571219Skas */ 2581219Skas 2591219Skas static int ssp = -1; /* Top of file stack */ 2601519Skas struct sstack { 2611519Skas FILE *s_file; /* File we were in. */ 2621519Skas int s_cond; /* Saved state of conditionals */ 2635785Skurt int s_loading; /* Loading .mailrc, etc. */ 26418661Sserge } sstack[NOFILE]; 2651219Skas 2661219Skas /* 2671219Skas * Pushdown current input file and switch to a new one. 2681219Skas * Set the global flag "sourcing" so that others will realize 2691219Skas * that they are no longer reading from a tty (in all probability). 2701219Skas */ 2711219Skas 2721219Skas source(name) 2731219Skas char name[]; 2741219Skas { 2751219Skas register FILE *fi; 2763914Skurt register char *cp; 2771219Skas 2783914Skurt if ((cp = expand(name)) == NOSTR) 2791219Skas return(1); 2803914Skurt if ((fi = fopen(cp, "r")) == NULL) { 2813914Skurt perror(cp); 2823914Skurt return(1); 2831219Skas } 28418661Sserge if (ssp >= NOFILE - 2) { 2851219Skas printf("Too much \"sourcing\" going on.\n"); 2861219Skas fclose(fi); 2871219Skas return(1); 2881219Skas } 2891519Skas sstack[++ssp].s_file = input; 2901519Skas sstack[ssp].s_cond = cond; 2915785Skurt sstack[ssp].s_loading = loading; 2925785Skurt loading = 0; 2931519Skas cond = CANY; 2941219Skas input = fi; 2951219Skas sourcing++; 2961219Skas return(0); 2971219Skas } 2981219Skas 2991219Skas /* 3001219Skas * Pop the current input back to the previous level. 3011219Skas * Update the "sourcing" flag as appropriate. 3021219Skas */ 3031219Skas 3041219Skas unstack() 3051219Skas { 3061219Skas if (ssp < 0) { 3071219Skas printf("\"Source\" stack over-pop.\n"); 3081219Skas sourcing = 0; 3091219Skas return(1); 3101219Skas } 3111219Skas fclose(input); 3121519Skas if (cond != CANY) 3131519Skas printf("Unmatched \"if\"\n"); 3141519Skas cond = sstack[ssp].s_cond; 3155785Skurt loading = sstack[ssp].s_loading; 3161519Skas input = sstack[ssp--].s_file; 3171219Skas if (ssp < 0) 3185785Skurt sourcing = loading; 3191219Skas return(0); 3201219Skas } 3211219Skas 3221219Skas /* 3231219Skas * Touch the indicated file. 3241219Skas * This is nifty for the shell. 3251219Skas * If we have the utime() system call, this is better served 3261219Skas * by using that, since it will work for empty files. 3271219Skas * On non-utime systems, we must sleep a second, then read. 3281219Skas */ 3291219Skas 3301219Skas alter(name) 3311219Skas char name[]; 3321219Skas { 3331219Skas #ifdef UTIME 3341219Skas struct stat statb; 3351219Skas long time(); 3361219Skas time_t time_p[2]; 3371219Skas #else 3381219Skas register int pid, f; 3391219Skas char w; 3401219Skas #endif UTIME 3411219Skas 3421219Skas #ifdef UTIME 3431219Skas if (stat(name, &statb) < 0) 3441219Skas return; 3451219Skas time_p[0] = time((long *) 0) + 1; 3461219Skas time_p[1] = statb.st_mtime; 3471219Skas utime(name, time_p); 3481219Skas #else 3491219Skas sleep(1); 3501219Skas if ((f = open(name, 0)) < 0) 3514389Skurt return; 3521219Skas read(f, &w, 1); 3531219Skas exit(0); 3541219Skas #endif 3551219Skas } 3561219Skas 3571219Skas /* 3581219Skas * Examine the passed line buffer and 3591219Skas * return true if it is all blanks and tabs. 3601219Skas */ 3611219Skas 3621219Skas blankline(linebuf) 3631219Skas char linebuf[]; 3641219Skas { 3651219Skas register char *cp; 3661219Skas 3671219Skas for (cp = linebuf; *cp; cp++) 36818661Sserge if (*cp != ' ' && *cp != '\t') 3691219Skas return(0); 3701219Skas return(1); 3711219Skas } 3721219Skas 3731219Skas /* 3743195Skas * Get sender's name from this message. If the message has 3753195Skas * a bunch of arpanet stuff in it, we may have to skin the name 3763195Skas * before returning it. 3773195Skas */ 3783195Skas char * 3793195Skas nameof(mp, reptype) 3803195Skas register struct message *mp; 3813195Skas { 3825237Skurt register char *cp, *cp2; 3833195Skas 3845237Skurt cp = skin(name1(mp, reptype)); 3855237Skurt if (reptype != 0 || charcount(cp, '!') < 2) 3865237Skurt return(cp); 3875237Skurt cp2 = rindex(cp, '!'); 3885237Skurt cp2--; 3895237Skurt while (cp2 > cp && *cp2 != '!') 3905237Skurt cp2--; 3915237Skurt if (*cp2 == '!') 3925237Skurt return(cp2 + 1); 3935237Skurt return(cp); 3943195Skas } 3953195Skas 3963195Skas /* 39725912Smckusick * Skin an arpa net address according to the RFC 822 interpretation 3983195Skas * of "host-phrase." 3993195Skas */ 4003195Skas char * 4013195Skas skin(name) 4023195Skas char *name; 4033195Skas { 4043195Skas register int c; 4053195Skas register char *cp, *cp2; 40625912Smckusick char *bufend; 4073195Skas int gotlt, lastsp; 4083195Skas char nbuf[BUFSIZ]; 40918661Sserge int nesting; 4103195Skas 4113195Skas if (name == NOSTR) 4123195Skas return(NOSTR); 41312819Sleres if (index(name, '(') == NOSTR && index(name, '<') == NOSTR 414*31142Sedward && index(name, ' ') == NOSTR) 4153195Skas return(name); 4163195Skas gotlt = 0; 4173195Skas lastsp = 0; 41825912Smckusick bufend = nbuf; 41925912Smckusick for (cp = name, cp2 = bufend; c = *cp++; ) { 4203195Skas switch (c) { 4213195Skas case '(': 42225912Smckusick /* 42325912Smckusick * Start of a "comment". 42425912Smckusick * Ignore it. 42525912Smckusick */ 42618661Sserge nesting = 1; 42725912Smckusick while ((c = *cp) != 0) { 42825912Smckusick cp++; 42925912Smckusick switch (c) { 43025912Smckusick case '\\': 43125912Smckusick if (*cp == 0) 43225912Smckusick goto outcm; 43325912Smckusick cp++; 43425912Smckusick break; 43518661Sserge case '(': 43618661Sserge nesting++; 43718661Sserge break; 43818661Sserge 43918661Sserge case ')': 44018661Sserge --nesting; 44118661Sserge break; 44218661Sserge } 44318661Sserge 44418661Sserge if (nesting <= 0) 44518661Sserge break; 44618661Sserge } 44725912Smckusick outcm: 44812819Sleres lastsp = 0; 4493195Skas break; 4503195Skas 45125912Smckusick case '"': 45225912Smckusick /* 45325912Smckusick * Start of a "quoted-string". 45425912Smckusick * Copy it in its entirety. 45525912Smckusick */ 45625912Smckusick while ((c = *cp) != 0) { 45725912Smckusick cp++; 45825912Smckusick switch (c) { 45925912Smckusick case '\\': 46025912Smckusick if ((c = *cp) == 0) 46125912Smckusick goto outqs; 46225912Smckusick cp++; 46325912Smckusick break; 46425912Smckusick case '"': 46525912Smckusick goto outqs; 46625912Smckusick } 46725912Smckusick *cp2++ = c; 46825912Smckusick } 46925912Smckusick outqs: 47025912Smckusick lastsp = 0; 47125912Smckusick break; 47225912Smckusick 4733195Skas case ' ': 47412819Sleres if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 47512819Sleres cp += 3, *cp2++ = '@'; 47612819Sleres else 47712819Sleres if (cp[0] == '@' && cp[1] == ' ') 47812819Sleres cp += 2, *cp2++ = '@'; 47912819Sleres else 48012819Sleres lastsp = 1; 4813195Skas break; 4823195Skas 4833195Skas case '<': 48425912Smckusick cp2 = bufend; 4853195Skas gotlt++; 4863195Skas lastsp = 0; 4873195Skas break; 4883195Skas 4893195Skas case '>': 49025912Smckusick if (gotlt) { 49125912Smckusick gotlt = 0; 49225912Smckusick while (*cp != ',' && *cp != 0) 49325912Smckusick cp++; 49425912Smckusick if (*cp == 0 ) 49525912Smckusick goto done; 49625912Smckusick *cp2++ = ','; 49725912Smckusick *cp2++ = ' '; 49825912Smckusick bufend = cp2; 49925912Smckusick break; 50025912Smckusick } 5013195Skas 5023195Skas /* Fall into . . . */ 5033195Skas 5043195Skas default: 5053195Skas if (lastsp) { 5063195Skas lastsp = 0; 5073195Skas *cp2++ = ' '; 5083195Skas } 5093195Skas *cp2++ = c; 5103195Skas break; 5113195Skas } 5123195Skas } 5133195Skas done: 5143195Skas *cp2 = 0; 5153195Skas 5163195Skas return(savestr(nbuf)); 5173195Skas } 5183195Skas 5193195Skas /* 5201219Skas * Fetch the sender's name from the passed message. 5213195Skas * Reptype can be 5223195Skas * 0 -- get sender's name for display purposes 5233195Skas * 1 -- get sender's name for reply 5243195Skas * 2 -- get sender's name for Reply 5251219Skas */ 5261219Skas 5271219Skas char * 5283195Skas name1(mp, reptype) 5291219Skas register struct message *mp; 5301219Skas { 5311219Skas char namebuf[LINESIZE]; 5321219Skas char linebuf[LINESIZE]; 5331219Skas register char *cp, *cp2; 5341219Skas register FILE *ibuf; 5351219Skas int first = 1; 5361219Skas 5373195Skas if ((cp = hfield("from", mp)) != NOSTR) 538*31142Sedward return cp; 5393195Skas if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 540*31142Sedward return cp; 5411219Skas ibuf = setinput(mp); 542*31142Sedward namebuf[0] = 0; 543*31142Sedward if (readline(ibuf, linebuf) < 0) 5441219Skas return(savestr(namebuf)); 5451219Skas newname: 546*31142Sedward for (cp = linebuf; *cp && *cp != ' '; cp++) 5471219Skas ; 548*31142Sedward for (; *cp == ' ' || *cp == '\t'; cp++) 5491219Skas ; 550*31142Sedward for (cp2 = &namebuf[strlen(namebuf)]; 551*31142Sedward *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) 552*31142Sedward *cp2++ = *cp++; 5531219Skas *cp2 = '\0'; 554*31142Sedward if (readline(ibuf, linebuf) < 0) 5551219Skas return(savestr(namebuf)); 5561219Skas if ((cp = index(linebuf, 'F')) == NULL) 5571219Skas return(savestr(namebuf)); 5581219Skas if (strncmp(cp, "From", 4) != 0) 5591219Skas return(savestr(namebuf)); 5601219Skas while ((cp = index(cp, 'r')) != NULL) { 5611219Skas if (strncmp(cp, "remote", 6) == 0) { 5621219Skas if ((cp = index(cp, 'f')) == NULL) 5631219Skas break; 5641219Skas if (strncmp(cp, "from", 4) != 0) 5651219Skas break; 5661219Skas if ((cp = index(cp, ' ')) == NULL) 5671219Skas break; 5681219Skas cp++; 5691219Skas if (first) { 570*31142Sedward strcpy(namebuf, cp); 5711219Skas first = 0; 5721219Skas } else 5731219Skas strcpy(rindex(namebuf, '!')+1, cp); 5741219Skas strcat(namebuf, "!"); 5751219Skas goto newname; 5761219Skas } 5771219Skas cp++; 5781219Skas } 5791219Skas return(savestr(namebuf)); 5801219Skas } 5811219Skas 5821219Skas /* 5835237Skurt * Count the occurances of c in str 5845237Skurt */ 5855237Skurt charcount(str, c) 5865237Skurt char *str; 5875237Skurt { 5885237Skurt register char *cp; 5895237Skurt register int i; 5905237Skurt 5915237Skurt for (i = 0, cp = str; *cp; cp++) 5925237Skurt if (*cp == c) 5935237Skurt i++; 5945237Skurt return(i); 5955237Skurt } 5965237Skurt 5975237Skurt /* 598*31142Sedward * Are any of the characters in the two strings the same? 5991219Skas */ 6001219Skas 601*31142Sedward anyof(s1, s2) 602*31142Sedward register char *s1, *s2; 6031219Skas { 6041219Skas 605*31142Sedward while (*s1) 606*31142Sedward if (index(s2, *s1++)) 607*31142Sedward return 1; 608*31142Sedward return 0; 6091219Skas } 6101219Skas 6111219Skas /* 612*31142Sedward * Convert c to upper case 6131219Skas */ 6141219Skas 615*31142Sedward raise(c) 616*31142Sedward register c; 617*31142Sedward { 618*31142Sedward 619*31142Sedward if (islower(c)) 620*31142Sedward return toupper(c); 621*31142Sedward return c; 622*31142Sedward } 623*31142Sedward 624*31142Sedward /* 625*31142Sedward * Copy s1 to s2, return pointer to null in s2. 626*31142Sedward */ 627*31142Sedward 628*31142Sedward char * 629*31142Sedward copy(s1, s2) 6301219Skas register char *s1, *s2; 6311219Skas { 6321219Skas 633*31142Sedward while (*s2++ = *s1++) 634*31142Sedward ; 635*31142Sedward return s2 - 1; 6361219Skas } 6371219Skas 6381219Skas /* 639*31142Sedward * Add a single character onto a string. 640*31142Sedward */ 641*31142Sedward 642*31142Sedward stradd(str, c) 643*31142Sedward register char *str; 644*31142Sedward { 645*31142Sedward 646*31142Sedward while (*str++) 647*31142Sedward ; 648*31142Sedward str[-1] = c; 649*31142Sedward *str = 0; 650*31142Sedward } 651*31142Sedward 652*31142Sedward /* 6537571Skurt * See if the given header field is supposed to be ignored. 6547571Skurt */ 6557571Skurt isign(field) 6567571Skurt char *field; 6577571Skurt { 6587571Skurt char realfld[BUFSIZ]; 6597571Skurt 66018661Sserge /* 66118661Sserge * Lower-case the string, so that "Status" and "status" 66218661Sserge * will hash to the same place. 66318661Sserge */ 6647571Skurt istrcpy(realfld, field); 66518661Sserge if (nretained > 0) 66618661Sserge return (!member(realfld, retain)); 66718661Sserge else 66818661Sserge return (member(realfld, ignore)); 6697571Skurt } 67018661Sserge 67118661Sserge member(realfield, table) 67218661Sserge register char *realfield; 673*31142Sedward struct ignore **table; 67418661Sserge { 67518661Sserge register struct ignore *igp; 67618661Sserge 67718661Sserge for (igp = table[hash(realfield)]; igp != 0; igp = igp->i_link) 678*31142Sedward if (*igp->i_field == *realfield && 679*31142Sedward equal(igp->i_field, realfield)) 68018661Sserge return (1); 68118661Sserge return (0); 68218661Sserge } 683