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