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