1*1219Skas # 2*1219Skas 3*1219Skas #include "rcv.h" 4*1219Skas #include <sys/stat.h> 5*1219Skas #include <sgtty.h> 6*1219Skas #include <ctype.h> 7*1219Skas 8*1219Skas /* 9*1219Skas * Mail -- a mail program 10*1219Skas * 11*1219Skas * Auxiliary functions. 12*1219Skas */ 13*1219Skas 14*1219Skas static char *SccsId = "@(#)aux.c 1.1 10/08/80"; 15*1219Skas 16*1219Skas /* 17*1219Skas * Return a pointer to a dynamic copy of the argument. 18*1219Skas */ 19*1219Skas 20*1219Skas char * 21*1219Skas savestr(str) 22*1219Skas char *str; 23*1219Skas { 24*1219Skas register char *cp, *cp2, *top; 25*1219Skas 26*1219Skas for (cp = str; *cp; cp++) 27*1219Skas ; 28*1219Skas top = salloc(cp-str + 1); 29*1219Skas if (top == NOSTR) 30*1219Skas return(NOSTR); 31*1219Skas for (cp = str, cp2 = top; *cp; cp++) 32*1219Skas *cp2++ = *cp; 33*1219Skas *cp2 = 0; 34*1219Skas return(top); 35*1219Skas } 36*1219Skas 37*1219Skas /* 38*1219Skas * Copy the name from the passed header line into the passed 39*1219Skas * name buffer. Null pad the name buffer. 40*1219Skas */ 41*1219Skas 42*1219Skas copyname(linebuf, nbuf) 43*1219Skas char *linebuf, *nbuf; 44*1219Skas { 45*1219Skas register char *cp, *cp2; 46*1219Skas 47*1219Skas for (cp = linebuf + 5, cp2 = nbuf; *cp != ' ' && cp2-nbuf < 8; cp++) 48*1219Skas *cp2++ = *cp; 49*1219Skas while (cp2-nbuf < 8) 50*1219Skas *cp2++ = 0; 51*1219Skas } 52*1219Skas 53*1219Skas /* 54*1219Skas * Announce a fatal error and die. 55*1219Skas */ 56*1219Skas 57*1219Skas panic(str) 58*1219Skas char *str; 59*1219Skas { 60*1219Skas prs("panic: "); 61*1219Skas prs(str); 62*1219Skas prs("\n"); 63*1219Skas exit(1); 64*1219Skas } 65*1219Skas 66*1219Skas /* 67*1219Skas * Catch stdio errors and report them more nicely. 68*1219Skas */ 69*1219Skas 70*1219Skas _error(str) 71*1219Skas char *str; 72*1219Skas { 73*1219Skas prs("Stdio Error: "); 74*1219Skas prs(str); 75*1219Skas prs("\n"); 76*1219Skas abort(); 77*1219Skas } 78*1219Skas 79*1219Skas /* 80*1219Skas * Print a string on diagnostic output. 81*1219Skas */ 82*1219Skas 83*1219Skas prs(str) 84*1219Skas char *str; 85*1219Skas { 86*1219Skas register char *s; 87*1219Skas 88*1219Skas for (s = str; *s; s++) 89*1219Skas ; 90*1219Skas write(2, str, s-str); 91*1219Skas } 92*1219Skas 93*1219Skas /* 94*1219Skas * Touch the named message by setting its MTOUCH flag. 95*1219Skas * Touched messages have the effect of not being sent 96*1219Skas * back to the system mailbox on exit. 97*1219Skas */ 98*1219Skas 99*1219Skas touch(mesg) 100*1219Skas { 101*1219Skas if (mesg >= 1 && mesg <= msgCount) 102*1219Skas message[mesg-1].m_flag |= MTOUCH; 103*1219Skas } 104*1219Skas 105*1219Skas /* 106*1219Skas * Test to see if the passed file name is a directory. 107*1219Skas * Return true if it is. 108*1219Skas */ 109*1219Skas 110*1219Skas isdir(name) 111*1219Skas char name[]; 112*1219Skas { 113*1219Skas struct stat sbuf; 114*1219Skas 115*1219Skas if (stat(name, &sbuf) < 0) 116*1219Skas return(0); 117*1219Skas return((sbuf.st_mode & S_IFMT) == S_IFDIR); 118*1219Skas } 119*1219Skas 120*1219Skas /* 121*1219Skas * Compute the size in characters of the passed message 122*1219Skas */ 123*1219Skas 124*1219Skas unsigned int 125*1219Skas msize(messp) 126*1219Skas struct message *messp; 127*1219Skas { 128*1219Skas register struct message *mp; 129*1219Skas 130*1219Skas mp = messp; 131*1219Skas return(mp->m_size); 132*1219Skas } 133*1219Skas 134*1219Skas /* 135*1219Skas * Count the number of arguments in the given string raw list. 136*1219Skas */ 137*1219Skas 138*1219Skas argcount(argv) 139*1219Skas char **argv; 140*1219Skas { 141*1219Skas register char **ap; 142*1219Skas 143*1219Skas for (ap = argv; *ap != NOSTR; ap++) 144*1219Skas ; 145*1219Skas return(ap-argv); 146*1219Skas } 147*1219Skas 148*1219Skas /* 149*1219Skas * Given a file address, determine the 150*1219Skas * block number it represents. 151*1219Skas */ 152*1219Skas 153*1219Skas blockof(off) 154*1219Skas off_t off; 155*1219Skas { 156*1219Skas off_t a; 157*1219Skas 158*1219Skas a = off >> 9; 159*1219Skas a &= 077777; 160*1219Skas return((int) a); 161*1219Skas } 162*1219Skas 163*1219Skas /* 164*1219Skas * Take a file address, and determine 165*1219Skas * its offset in the current block. 166*1219Skas */ 167*1219Skas 168*1219Skas offsetof(off) 169*1219Skas off_t off; 170*1219Skas { 171*1219Skas off_t a; 172*1219Skas 173*1219Skas a = off & 0777; 174*1219Skas return((int) a); 175*1219Skas } 176*1219Skas 177*1219Skas /* 178*1219Skas * Determine if the passed file is actually a tty, via a call to 179*1219Skas * gtty. This is not totally reliable, but . . . 180*1219Skas */ 181*1219Skas 182*1219Skas isatty(f) 183*1219Skas { 184*1219Skas struct sgttyb buf; 185*1219Skas 186*1219Skas if (gtty(f, &buf) < 0) 187*1219Skas return(0); 188*1219Skas return(1); 189*1219Skas } 190*1219Skas 191*1219Skas /* 192*1219Skas * Return the desired header line from the passed message 193*1219Skas * pointer (or NOSTR if the desired header field is not available). 194*1219Skas */ 195*1219Skas 196*1219Skas char * 197*1219Skas hfield(field, mp) 198*1219Skas char field[]; 199*1219Skas struct message *mp; 200*1219Skas { 201*1219Skas register FILE *ibuf; 202*1219Skas char linebuf[LINESIZE]; 203*1219Skas register int lc; 204*1219Skas 205*1219Skas ibuf = setinput(mp); 206*1219Skas if ((lc = mp->m_lines) <= 0) 207*1219Skas return(NOSTR); 208*1219Skas if (readline(ibuf, linebuf) < 0) 209*1219Skas return(NOSTR); 210*1219Skas lc--; 211*1219Skas do { 212*1219Skas lc = gethfield(ibuf, linebuf, lc); 213*1219Skas if (lc == -1) 214*1219Skas return(NOSTR); 215*1219Skas if (ishfield(linebuf, field)) 216*1219Skas return(savestr(hcontents(linebuf))); 217*1219Skas } while (lc > 0); 218*1219Skas return(NOSTR); 219*1219Skas } 220*1219Skas 221*1219Skas /* 222*1219Skas * Return the next header field found in the given message. 223*1219Skas * Return > 0 if something found, <= 0 elsewise. 224*1219Skas * Must deal with \ continuations & other such fraud. 225*1219Skas */ 226*1219Skas 227*1219Skas gethfield(f, linebuf, rem) 228*1219Skas register FILE *f; 229*1219Skas char linebuf[]; 230*1219Skas register int rem; 231*1219Skas { 232*1219Skas char line2[LINESIZE]; 233*1219Skas long loc; 234*1219Skas register char *cp, *cp2; 235*1219Skas register int c; 236*1219Skas 237*1219Skas 238*1219Skas for (;;) { 239*1219Skas if (rem <= 0) 240*1219Skas return(-1); 241*1219Skas if (readline(f, linebuf) < 0) 242*1219Skas return(-1); 243*1219Skas rem--; 244*1219Skas if (strlen(linebuf) == 0) 245*1219Skas return(-1); 246*1219Skas if (isspace(linebuf[0])) 247*1219Skas continue; 248*1219Skas if (linebuf[0] == '>') 249*1219Skas continue; 250*1219Skas cp = index(linebuf, ':'); 251*1219Skas if (cp == NOSTR) 252*1219Skas continue; 253*1219Skas for (cp2 = linebuf; cp2 < cp; cp2++) 254*1219Skas if (isdigit(*cp2)) 255*1219Skas continue; 256*1219Skas 257*1219Skas /* 258*1219Skas * I guess we got a headline. 259*1219Skas * Handle wraparounding 260*1219Skas */ 261*1219Skas 262*1219Skas for (;;) { 263*1219Skas if (rem <= 0) 264*1219Skas break; 265*1219Skas #ifdef CANTELL 266*1219Skas loc = ftell(f); 267*1219Skas if (readline(f, line2) < 0) 268*1219Skas break; 269*1219Skas rem--; 270*1219Skas if (!isspace(line2[0])) { 271*1219Skas fseek(f, loc, 0); 272*1219Skas rem++; 273*1219Skas break; 274*1219Skas } 275*1219Skas #else 276*1219Skas c = getc(f); 277*1219Skas ungetc(c, f); 278*1219Skas if (!isspace(c) || c == '\n') 279*1219Skas break; 280*1219Skas if (readline(f, line2) < 0) 281*1219Skas break; 282*1219Skas rem--; 283*1219Skas #endif 284*1219Skas cp2 = line2; 285*1219Skas for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) 286*1219Skas ; 287*1219Skas if (strlen(linebuf) + strlen(cp2) >= LINESIZE-2) 288*1219Skas break; 289*1219Skas cp = &linebuf[strlen(linebuf)]; 290*1219Skas while (cp > linebuf && 291*1219Skas (isspace(cp[-1]) || cp[-1] == '\\')) 292*1219Skas cp--; 293*1219Skas *cp++ = ' '; 294*1219Skas for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) 295*1219Skas ; 296*1219Skas strcpy(cp, cp2); 297*1219Skas } 298*1219Skas if ((c = strlen(linebuf)) > 0) { 299*1219Skas cp = &linebuf[c-1]; 300*1219Skas while (cp > linebuf && isspace(*cp)) 301*1219Skas cp--; 302*1219Skas *++cp = 0; 303*1219Skas } 304*1219Skas return(rem); 305*1219Skas } 306*1219Skas /* NOTREACHED */ 307*1219Skas } 308*1219Skas 309*1219Skas /* 310*1219Skas * Check whether the passed line is a header line of 311*1219Skas * the desired breed. 312*1219Skas */ 313*1219Skas 314*1219Skas ishfield(linebuf, field) 315*1219Skas char linebuf[], field[]; 316*1219Skas { 317*1219Skas register char *cp; 318*1219Skas register int c; 319*1219Skas 320*1219Skas if ((cp = index(linebuf, ':')) == NOSTR) 321*1219Skas return(0); 322*1219Skas if (cp == linebuf) 323*1219Skas return(0); 324*1219Skas cp--; 325*1219Skas while (cp > linebuf && isspace(*cp)) 326*1219Skas cp--; 327*1219Skas c = *++cp; 328*1219Skas *cp = 0; 329*1219Skas if (icequal(linebuf ,field)) { 330*1219Skas *cp = c; 331*1219Skas return(1); 332*1219Skas } 333*1219Skas *cp = c; 334*1219Skas return(0); 335*1219Skas } 336*1219Skas 337*1219Skas /* 338*1219Skas * Extract the non label information from the given header field 339*1219Skas * and return it. 340*1219Skas */ 341*1219Skas 342*1219Skas char * 343*1219Skas hcontents(hfield) 344*1219Skas char hfield[]; 345*1219Skas { 346*1219Skas register char *cp; 347*1219Skas 348*1219Skas if ((cp = index(hfield, ':')) == NOSTR) 349*1219Skas return(NOSTR); 350*1219Skas cp++; 351*1219Skas while (*cp && isspace(*cp)) 352*1219Skas cp++; 353*1219Skas return(cp); 354*1219Skas } 355*1219Skas 356*1219Skas /* 357*1219Skas * Compare two strings, ignoring case. 358*1219Skas */ 359*1219Skas 360*1219Skas icequal(s1, s2) 361*1219Skas register char *s1, *s2; 362*1219Skas { 363*1219Skas 364*1219Skas while (raise(*s1++) == raise(*s2)) 365*1219Skas if (*s2++ == 0) 366*1219Skas return(1); 367*1219Skas return(0); 368*1219Skas } 369*1219Skas 370*1219Skas /* 371*1219Skas * The following code deals with input stacking to do source 372*1219Skas * commands. All but the current file pointer are saved on 373*1219Skas * the stack. 374*1219Skas */ 375*1219Skas 376*1219Skas static int ssp = -1; /* Top of file stack */ 377*1219Skas static FILE *sstack[_NFILE]; /* Saved input files */ 378*1219Skas 379*1219Skas /* 380*1219Skas * Pushdown current input file and switch to a new one. 381*1219Skas * Set the global flag "sourcing" so that others will realize 382*1219Skas * that they are no longer reading from a tty (in all probability). 383*1219Skas */ 384*1219Skas 385*1219Skas source(name) 386*1219Skas char name[]; 387*1219Skas { 388*1219Skas register FILE *fi; 389*1219Skas 390*1219Skas if ((fi = fopen(name, "r")) == NULL) { 391*1219Skas perror(name); 392*1219Skas return(1); 393*1219Skas } 394*1219Skas if (ssp >= _NFILE-2) { 395*1219Skas printf("Too much \"sourcing\" going on.\n"); 396*1219Skas fclose(fi); 397*1219Skas return(1); 398*1219Skas } 399*1219Skas sstack[++ssp] = input; 400*1219Skas input = fi; 401*1219Skas sourcing++; 402*1219Skas return(0); 403*1219Skas } 404*1219Skas 405*1219Skas /* 406*1219Skas * Source a file, but do nothing if the file cannot be opened. 407*1219Skas */ 408*1219Skas 409*1219Skas source1(name) 410*1219Skas char name[]; 411*1219Skas { 412*1219Skas register int f; 413*1219Skas 414*1219Skas if ((f = open(name, 0)) < 0) 415*1219Skas return(0); 416*1219Skas close(f); 417*1219Skas source(name); 418*1219Skas } 419*1219Skas 420*1219Skas /* 421*1219Skas * Pop the current input back to the previous level. 422*1219Skas * Update the "sourcing" flag as appropriate. 423*1219Skas */ 424*1219Skas 425*1219Skas unstack() 426*1219Skas { 427*1219Skas if (ssp < 0) { 428*1219Skas printf("\"Source\" stack over-pop.\n"); 429*1219Skas sourcing = 0; 430*1219Skas return(1); 431*1219Skas } 432*1219Skas fclose(input); 433*1219Skas input = sstack[ssp--]; 434*1219Skas if (ssp < 0) 435*1219Skas sourcing = 0; 436*1219Skas return(0); 437*1219Skas } 438*1219Skas 439*1219Skas /* 440*1219Skas * Touch the indicated file. 441*1219Skas * This is nifty for the shell. 442*1219Skas * If we have the utime() system call, this is better served 443*1219Skas * by using that, since it will work for empty files. 444*1219Skas * On non-utime systems, we must sleep a second, then read. 445*1219Skas */ 446*1219Skas 447*1219Skas alter(name) 448*1219Skas char name[]; 449*1219Skas { 450*1219Skas #ifdef UTIME 451*1219Skas struct stat statb; 452*1219Skas long time(); 453*1219Skas time_t time_p[2]; 454*1219Skas #else 455*1219Skas register int pid, f; 456*1219Skas char w; 457*1219Skas #endif UTIME 458*1219Skas 459*1219Skas #ifdef UTIME 460*1219Skas if (stat(name, &statb) < 0) 461*1219Skas return; 462*1219Skas time_p[0] = time((long *) 0) + 1; 463*1219Skas time_p[1] = statb.st_mtime; 464*1219Skas utime(name, time_p); 465*1219Skas #else 466*1219Skas if ((pid = fork()) != 0) 467*1219Skas return; 468*1219Skas clrbuf(stdout); 469*1219Skas clrbuf(stderr); 470*1219Skas clrbuf(stdin); 471*1219Skas sleep(1); 472*1219Skas if ((f = open(name, 0)) < 0) 473*1219Skas exit(1); 474*1219Skas read(f, &w, 1); 475*1219Skas exit(0); 476*1219Skas #endif 477*1219Skas } 478*1219Skas 479*1219Skas /* 480*1219Skas * Examine the passed line buffer and 481*1219Skas * return true if it is all blanks and tabs. 482*1219Skas */ 483*1219Skas 484*1219Skas blankline(linebuf) 485*1219Skas char linebuf[]; 486*1219Skas { 487*1219Skas register char *cp; 488*1219Skas 489*1219Skas for (cp = linebuf; *cp; cp++) 490*1219Skas if (!any(*cp, " \t")) 491*1219Skas return(0); 492*1219Skas return(1); 493*1219Skas } 494*1219Skas 495*1219Skas /* 496*1219Skas * Fetch the sender's name from the passed message. 497*1219Skas */ 498*1219Skas 499*1219Skas char * 500*1219Skas nameof(mp) 501*1219Skas register struct message *mp; 502*1219Skas { 503*1219Skas char namebuf[LINESIZE]; 504*1219Skas char linebuf[LINESIZE]; 505*1219Skas register char *cp, *cp2; 506*1219Skas register FILE *ibuf; 507*1219Skas int first = 1; 508*1219Skas 509*1219Skas if ((cp = hfield("reply-to", mp)) != NOSTR) { 510*1219Skas strcpy(namebuf, cp); 511*1219Skas return(namebuf); 512*1219Skas } 513*1219Skas ibuf = setinput(mp); 514*1219Skas copy("", namebuf); 515*1219Skas if (readline(ibuf, linebuf) <= 0) 516*1219Skas return(savestr(namebuf)); 517*1219Skas newname: 518*1219Skas for (cp = linebuf; *cp != ' '; cp++) 519*1219Skas ; 520*1219Skas while (any(*cp, " \t")) 521*1219Skas cp++; 522*1219Skas for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") && 523*1219Skas cp2-namebuf < LINESIZE-1; *cp2++ = *cp++) 524*1219Skas ; 525*1219Skas *cp2 = '\0'; 526*1219Skas if (readline(ibuf, linebuf) <= 0) 527*1219Skas return(savestr(namebuf)); 528*1219Skas if ((cp = index(linebuf, 'F')) == NULL) 529*1219Skas return(savestr(namebuf)); 530*1219Skas if (strncmp(cp, "From", 4) != 0) 531*1219Skas return(savestr(namebuf)); 532*1219Skas while ((cp = index(cp, 'r')) != NULL) { 533*1219Skas if (strncmp(cp, "remote", 6) == 0) { 534*1219Skas if ((cp = index(cp, 'f')) == NULL) 535*1219Skas break; 536*1219Skas if (strncmp(cp, "from", 4) != 0) 537*1219Skas break; 538*1219Skas if ((cp = index(cp, ' ')) == NULL) 539*1219Skas break; 540*1219Skas cp++; 541*1219Skas if (first) { 542*1219Skas copy(cp, namebuf); 543*1219Skas first = 0; 544*1219Skas } else 545*1219Skas strcpy(rindex(namebuf, '!')+1, cp); 546*1219Skas strcat(namebuf, "!"); 547*1219Skas goto newname; 548*1219Skas } 549*1219Skas cp++; 550*1219Skas } 551*1219Skas return(savestr(namebuf)); 552*1219Skas } 553*1219Skas 554*1219Skas /* 555*1219Skas * Find the rightmost pointer to an instance of the 556*1219Skas * character in the string and return it. 557*1219Skas */ 558*1219Skas 559*1219Skas char * 560*1219Skas rindex(str, c) 561*1219Skas char str[]; 562*1219Skas register int c; 563*1219Skas { 564*1219Skas register char *cp, *cp2; 565*1219Skas 566*1219Skas for (cp = str, cp2 = NOSTR; *cp; cp++) 567*1219Skas if (c == *cp) 568*1219Skas cp2 = cp; 569*1219Skas return(cp2); 570*1219Skas } 571*1219Skas 572*1219Skas /* 573*1219Skas * See if the string is a number. 574*1219Skas */ 575*1219Skas 576*1219Skas numeric(str) 577*1219Skas char str[]; 578*1219Skas { 579*1219Skas register char *cp = str; 580*1219Skas 581*1219Skas while (*cp) 582*1219Skas if (!isdigit(*cp++)) 583*1219Skas return(0); 584*1219Skas return(1); 585*1219Skas } 586*1219Skas 587*1219Skas /* 588*1219Skas * Are any of the characters in the two strings the same? 589*1219Skas */ 590*1219Skas 591*1219Skas anyof(s1, s2) 592*1219Skas register char *s1, *s2; 593*1219Skas { 594*1219Skas register int c; 595*1219Skas 596*1219Skas while (c = *s1++) 597*1219Skas if (any(c, s2)) 598*1219Skas return(1); 599*1219Skas return(0); 600*1219Skas } 601*1219Skas 602*1219Skas /* 603*1219Skas * Determine the leftmost index of the character 604*1219Skas * in the string. 605*1219Skas */ 606*1219Skas 607*1219Skas char * 608*1219Skas index(str, ch) 609*1219Skas char *str; 610*1219Skas { 611*1219Skas register char *cp; 612*1219Skas register int c; 613*1219Skas 614*1219Skas for (c = ch, cp = str; *cp; cp++) 615*1219Skas if (*cp == c) 616*1219Skas return(cp); 617*1219Skas return(NOSTR); 618*1219Skas } 619*1219Skas 620*1219Skas /* 621*1219Skas * String compare two strings of bounded length. 622*1219Skas */ 623*1219Skas 624*1219Skas strncmp(as1, as2, an) 625*1219Skas char *as1, *as2; 626*1219Skas { 627*1219Skas register char *s1, *s2; 628*1219Skas register int n; 629*1219Skas 630*1219Skas s1 = as1; 631*1219Skas s2 = as2; 632*1219Skas n = an; 633*1219Skas while (--n >= 0 && *s1 == *s2++) 634*1219Skas if (*s1++ == '\0') 635*1219Skas return(0); 636*1219Skas return(n<0 ? 0 : *s1 - *--s2); 637*1219Skas } 638*1219Skas 639