1 /* 2 * Copyright (c) 1980 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 */ 12 13 #ifdef notdef 14 static char sccsid[] = "@(#)aux.c 5.7 (Berkeley) 02/18/88"; 15 #endif /* notdef */ 16 17 #include "rcv.h" 18 #include <sys/stat.h> 19 20 /* 21 * Mail -- a mail program 22 * 23 * Auxiliary functions. 24 */ 25 26 /* 27 * Return a pointer to a dynamic copy of the argument. 28 */ 29 30 char * 31 savestr(str) 32 char *str; 33 { 34 register char *cp, *cp2, *top; 35 36 for (cp = str; *cp; cp++) 37 ; 38 top = salloc(cp-str + 1); 39 if (top == NOSTR) 40 return(NOSTR); 41 for (cp = str, cp2 = top; *cp; cp++) 42 *cp2++ = *cp; 43 *cp2 = 0; 44 return(top); 45 } 46 47 /* 48 * Announce a fatal error and die. 49 */ 50 51 /*VARARGS1*/ 52 panic(fmt, a, b) 53 char *fmt; 54 { 55 fprintf(stderr, "panic: "); 56 fprintf(stderr, fmt, a, b); 57 putc('\n', stderr); 58 exit(1); 59 } 60 61 /* 62 * Touch the named message by setting its MTOUCH flag. 63 * Touched messages have the effect of not being sent 64 * back to the system mailbox on exit. 65 */ 66 67 touch(mesg) 68 { 69 register struct message *mp; 70 71 if (mesg < 1 || mesg > msgCount) 72 return; 73 mp = &message[mesg-1]; 74 mp->m_flag |= MTOUCH; 75 if ((mp->m_flag & MREAD) == 0) 76 mp->m_flag |= MREAD|MSTATUS; 77 } 78 79 /* 80 * Test to see if the passed file name is a directory. 81 * Return true if it is. 82 */ 83 84 isdir(name) 85 char name[]; 86 { 87 struct stat sbuf; 88 89 if (stat(name, &sbuf) < 0) 90 return(0); 91 return((sbuf.st_mode & S_IFMT) == S_IFDIR); 92 } 93 94 /* 95 * Count the number of arguments in the given string raw list. 96 */ 97 98 argcount(argv) 99 char **argv; 100 { 101 register char **ap; 102 103 for (ap = argv; *ap++ != NOSTR;) 104 ; 105 return ap - argv - 1; 106 } 107 108 /* 109 * Return the desired header line from the passed message 110 * pointer (or NOSTR if the desired header field is not available). 111 */ 112 113 char * 114 hfield(field, mp) 115 char field[]; 116 struct message *mp; 117 { 118 register FILE *ibuf; 119 char linebuf[LINESIZE]; 120 register int lc; 121 register char *hfield; 122 char *colon; 123 124 ibuf = setinput(mp); 125 if ((lc = mp->m_lines - 1) < 0) 126 return NOSTR; 127 if (readline(ibuf, linebuf) < 0) 128 return NOSTR; 129 while (lc > 0) { 130 if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) 131 return NOSTR; 132 if (hfield = ishfield(linebuf, colon, field)) 133 return savestr(hfield); 134 } 135 return NOSTR; 136 } 137 138 /* 139 * Return the next header field found in the given message. 140 * Return >= 0 if something found, < 0 elsewise. 141 * "colon" is set to point to the colon in the header. 142 * Must deal with \ continuations & other such fraud. 143 */ 144 145 gethfield(f, linebuf, rem, colon) 146 register FILE *f; 147 char linebuf[]; 148 register int rem; 149 char **colon; 150 { 151 char line2[LINESIZE]; 152 register char *cp, *cp2; 153 register int c; 154 155 for (;;) { 156 if (--rem < 0) 157 return -1; 158 if ((c = readline(f, linebuf)) <= 0) 159 return -1; 160 for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':'; 161 cp++) 162 ; 163 if (*cp != ':' || cp == linebuf) 164 continue; 165 /* 166 * I guess we got a headline. 167 * Handle wraparounding 168 */ 169 *colon = cp; 170 cp = linebuf + c; 171 for (;;) { 172 while (--cp >= linebuf && (*cp == ' ' || *cp == '\t')) 173 ; 174 cp++; 175 if (rem <= 0) 176 break; 177 ungetc(c = getc(f), f); 178 if (c != ' ' && c != '\t') 179 break; 180 if ((c = readline(f, line2)) < 0) 181 break; 182 rem--; 183 for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++) 184 ; 185 c -= cp2 - line2; 186 if (cp + c >= linebuf + LINESIZE - 2) 187 break; 188 *cp++ = ' '; 189 bcopy(cp2, cp, c); 190 cp += c; 191 } 192 *cp = 0; 193 return rem; 194 } 195 /* NOTREACHED */ 196 } 197 198 /* 199 * Check whether the passed line is a header line of 200 * the desired breed. Return the field body, or 0. 201 */ 202 203 char* 204 ishfield(linebuf, colon, field) 205 char linebuf[], field[]; 206 char *colon; 207 { 208 register char *cp = colon; 209 210 *cp = 0; 211 if (!icequal(linebuf, field)) { 212 *cp = ':'; 213 return 0; 214 } 215 *cp = ':'; 216 for (cp++; *cp == ' ' || *cp == '\t'; cp++) 217 ; 218 return cp; 219 } 220 221 /* 222 * Compare two strings, ignoring case. 223 */ 224 225 icequal(s1, s2) 226 register char *s1, *s2; 227 { 228 register c1, c2; 229 230 for (;;) { 231 if ((c1 = (unsigned char)*s1++) != 232 (c2 = (unsigned char)*s2++)) { 233 if (isupper(c1)) 234 c1 = tolower(c1); 235 if (c1 != c2) 236 return 0; 237 } 238 if (c1 == 0) 239 return 1; 240 } 241 /*NOTREACHED*/ 242 } 243 244 /* 245 * Copy a string, lowercasing it as we go. 246 */ 247 istrcpy(dest, src) 248 register char *dest, *src; 249 { 250 251 do { 252 if (isupper(*src)) 253 *dest++ = tolower(*src); 254 else 255 *dest++ = *src; 256 } while (*src++ != 0); 257 } 258 259 /* 260 * The following code deals with input stacking to do source 261 * commands. All but the current file pointer are saved on 262 * the stack. 263 */ 264 265 static int ssp = -1; /* Top of file stack */ 266 struct sstack { 267 FILE *s_file; /* File we were in. */ 268 int s_cond; /* Saved state of conditionals */ 269 int s_loading; /* Loading .mailrc, etc. */ 270 } sstack[NOFILE]; 271 272 /* 273 * Pushdown current input file and switch to a new one. 274 * Set the global flag "sourcing" so that others will realize 275 * that they are no longer reading from a tty (in all probability). 276 */ 277 278 source(name) 279 char name[]; 280 { 281 register FILE *fi; 282 register char *cp; 283 284 if ((cp = expand(name)) == NOSTR) 285 return(1); 286 if ((fi = fopen(cp, "r")) == NULL) { 287 perror(cp); 288 return(1); 289 } 290 if (ssp >= NOFILE - 2) { 291 printf("Too much \"sourcing\" going on.\n"); 292 fclose(fi); 293 return(1); 294 } 295 sstack[++ssp].s_file = input; 296 sstack[ssp].s_cond = cond; 297 sstack[ssp].s_loading = loading; 298 loading = 0; 299 cond = CANY; 300 input = fi; 301 sourcing++; 302 return(0); 303 } 304 305 /* 306 * Pop the current input back to the previous level. 307 * Update the "sourcing" flag as appropriate. 308 */ 309 310 unstack() 311 { 312 if (ssp < 0) { 313 printf("\"Source\" stack over-pop.\n"); 314 sourcing = 0; 315 return(1); 316 } 317 fclose(input); 318 if (cond != CANY) 319 printf("Unmatched \"if\"\n"); 320 cond = sstack[ssp].s_cond; 321 loading = sstack[ssp].s_loading; 322 input = sstack[ssp--].s_file; 323 if (ssp < 0) 324 sourcing = loading; 325 return(0); 326 } 327 328 /* 329 * Touch the indicated file. 330 * This is nifty for the shell. 331 * If we have the utime() system call, this is better served 332 * by using that, since it will work for empty files. 333 * On non-utime systems, we must sleep a second, then read. 334 */ 335 336 alter(name) 337 char name[]; 338 { 339 #ifdef UTIME 340 struct stat statb; 341 long time(); 342 time_t time_p[2]; 343 #else 344 register int pid, f; 345 char w; 346 #endif UTIME 347 348 #ifdef UTIME 349 if (stat(name, &statb) < 0) 350 return; 351 time_p[0] = time((long *) 0) + 1; 352 time_p[1] = statb.st_mtime; 353 utime(name, time_p); 354 #else 355 sleep(1); 356 if ((f = open(name, 0)) < 0) 357 return; 358 read(f, &w, 1); 359 exit(0); 360 #endif 361 } 362 363 /* 364 * Examine the passed line buffer and 365 * return true if it is all blanks and tabs. 366 */ 367 368 blankline(linebuf) 369 char linebuf[]; 370 { 371 register char *cp; 372 373 for (cp = linebuf; *cp; cp++) 374 if (*cp != ' ' && *cp != '\t') 375 return(0); 376 return(1); 377 } 378 379 /* 380 * Get sender's name from this message. If the message has 381 * a bunch of arpanet stuff in it, we may have to skin the name 382 * before returning it. 383 */ 384 char * 385 nameof(mp, reptype) 386 register struct message *mp; 387 { 388 register char *cp, *cp2; 389 390 cp = skin(name1(mp, reptype)); 391 if (reptype != 0 || charcount(cp, '!') < 2) 392 return(cp); 393 cp2 = rindex(cp, '!'); 394 cp2--; 395 while (cp2 > cp && *cp2 != '!') 396 cp2--; 397 if (*cp2 == '!') 398 return(cp2 + 1); 399 return(cp); 400 } 401 402 /* 403 * Skin an arpa net address according to the RFC 822 interpretation 404 * of "host-phrase." 405 */ 406 char * 407 skin(name) 408 char *name; 409 { 410 register int c; 411 register char *cp, *cp2; 412 char *bufend; 413 int gotlt, lastsp; 414 char nbuf[BUFSIZ]; 415 int nesting; 416 417 if (name == NOSTR) 418 return(NOSTR); 419 if (index(name, '(') == NOSTR && index(name, '<') == NOSTR 420 && index(name, ' ') == NOSTR) 421 return(name); 422 gotlt = 0; 423 lastsp = 0; 424 bufend = nbuf; 425 for (cp = name, cp2 = bufend; c = *cp++; ) { 426 switch (c) { 427 case '(': 428 /* 429 * Start of a "comment". 430 * Ignore it. 431 */ 432 nesting = 1; 433 while ((c = *cp) != 0) { 434 cp++; 435 switch (c) { 436 case '\\': 437 if (*cp == 0) 438 goto outcm; 439 cp++; 440 break; 441 case '(': 442 nesting++; 443 break; 444 445 case ')': 446 --nesting; 447 break; 448 } 449 450 if (nesting <= 0) 451 break; 452 } 453 outcm: 454 lastsp = 0; 455 break; 456 457 case '"': 458 /* 459 * Start of a "quoted-string". 460 * Copy it in its entirety. 461 */ 462 while ((c = *cp) != 0) { 463 cp++; 464 switch (c) { 465 case '\\': 466 if ((c = *cp) == 0) 467 goto outqs; 468 cp++; 469 break; 470 case '"': 471 goto outqs; 472 } 473 *cp2++ = c; 474 } 475 outqs: 476 lastsp = 0; 477 break; 478 479 case ' ': 480 if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 481 cp += 3, *cp2++ = '@'; 482 else 483 if (cp[0] == '@' && cp[1] == ' ') 484 cp += 2, *cp2++ = '@'; 485 else 486 lastsp = 1; 487 break; 488 489 case '<': 490 cp2 = bufend; 491 gotlt++; 492 lastsp = 0; 493 break; 494 495 case '>': 496 if (gotlt) { 497 gotlt = 0; 498 while (*cp != ',' && *cp != 0) 499 cp++; 500 if (*cp == 0 ) 501 goto done; 502 *cp2++ = ','; 503 *cp2++ = ' '; 504 bufend = cp2; 505 break; 506 } 507 508 /* Fall into . . . */ 509 510 default: 511 if (lastsp) { 512 lastsp = 0; 513 *cp2++ = ' '; 514 } 515 *cp2++ = c; 516 break; 517 } 518 } 519 done: 520 *cp2 = 0; 521 522 return(savestr(nbuf)); 523 } 524 525 /* 526 * Fetch the sender's name from the passed message. 527 * Reptype can be 528 * 0 -- get sender's name for display purposes 529 * 1 -- get sender's name for reply 530 * 2 -- get sender's name for Reply 531 */ 532 533 char * 534 name1(mp, reptype) 535 register struct message *mp; 536 { 537 char namebuf[LINESIZE]; 538 char linebuf[LINESIZE]; 539 register char *cp, *cp2; 540 register FILE *ibuf; 541 int first = 1; 542 543 if ((cp = hfield("from", mp)) != NOSTR) 544 return cp; 545 if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 546 return cp; 547 ibuf = setinput(mp); 548 namebuf[0] = 0; 549 if (readline(ibuf, linebuf) < 0) 550 return(savestr(namebuf)); 551 newname: 552 for (cp = linebuf; *cp && *cp != ' '; cp++) 553 ; 554 for (; *cp == ' ' || *cp == '\t'; cp++) 555 ; 556 for (cp2 = &namebuf[strlen(namebuf)]; 557 *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) 558 *cp2++ = *cp++; 559 *cp2 = '\0'; 560 if (readline(ibuf, linebuf) < 0) 561 return(savestr(namebuf)); 562 if ((cp = index(linebuf, 'F')) == NULL) 563 return(savestr(namebuf)); 564 if (strncmp(cp, "From", 4) != 0) 565 return(savestr(namebuf)); 566 while ((cp = index(cp, 'r')) != NULL) { 567 if (strncmp(cp, "remote", 6) == 0) { 568 if ((cp = index(cp, 'f')) == NULL) 569 break; 570 if (strncmp(cp, "from", 4) != 0) 571 break; 572 if ((cp = index(cp, ' ')) == NULL) 573 break; 574 cp++; 575 if (first) { 576 strcpy(namebuf, cp); 577 first = 0; 578 } else 579 strcpy(rindex(namebuf, '!')+1, cp); 580 strcat(namebuf, "!"); 581 goto newname; 582 } 583 cp++; 584 } 585 return(savestr(namebuf)); 586 } 587 588 /* 589 * Count the occurances of c in str 590 */ 591 charcount(str, c) 592 char *str; 593 { 594 register char *cp; 595 register int i; 596 597 for (i = 0, cp = str; *cp; cp++) 598 if (*cp == c) 599 i++; 600 return(i); 601 } 602 603 /* 604 * Are any of the characters in the two strings the same? 605 */ 606 607 anyof(s1, s2) 608 register char *s1, *s2; 609 { 610 611 while (*s1) 612 if (index(s2, *s1++)) 613 return 1; 614 return 0; 615 } 616 617 /* 618 * Convert c to upper case 619 */ 620 621 raise(c) 622 register c; 623 { 624 625 if (islower(c)) 626 return toupper(c); 627 return c; 628 } 629 630 /* 631 * Copy s1 to s2, return pointer to null in s2. 632 */ 633 634 char * 635 copy(s1, s2) 636 register char *s1, *s2; 637 { 638 639 while (*s2++ = *s1++) 640 ; 641 return s2 - 1; 642 } 643 644 /* 645 * Add a single character onto a string. 646 */ 647 648 stradd(str, c) 649 register char *str; 650 { 651 652 while (*str++) 653 ; 654 str[-1] = c; 655 *str = 0; 656 } 657 658 /* 659 * See if the given header field is supposed to be ignored. 660 */ 661 isign(field) 662 char *field; 663 { 664 char realfld[BUFSIZ]; 665 666 /* 667 * Lower-case the string, so that "Status" and "status" 668 * will hash to the same place. 669 */ 670 istrcpy(realfld, field); 671 if (nretained > 0) 672 return (!member(realfld, retain)); 673 else 674 return (member(realfld, ignore)); 675 } 676 677 member(realfield, table) 678 register char *realfield; 679 struct ignore **table; 680 { 681 register struct ignore *igp; 682 683 for (igp = table[hash(realfield)]; igp != 0; igp = igp->i_link) 684 if (*igp->i_field == *realfield && 685 equal(igp->i_field, realfield)) 686 return (1); 687 return (0); 688 } 689