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.9 (Berkeley) 06/16/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 */ 332 333 alter(name) 334 char name[]; 335 { 336 struct stat statb; 337 long time(); 338 time_t time_p[2]; 339 340 if (stat(name, &statb) < 0) 341 return; 342 time_p[0] = time((long *) 0) + 1; 343 time_p[1] = statb.st_mtime; 344 utime(name, time_p); 345 } 346 347 /* 348 * Examine the passed line buffer and 349 * return true if it is all blanks and tabs. 350 */ 351 352 blankline(linebuf) 353 char linebuf[]; 354 { 355 register char *cp; 356 357 for (cp = linebuf; *cp; cp++) 358 if (*cp != ' ' && *cp != '\t') 359 return(0); 360 return(1); 361 } 362 363 /* 364 * Get sender's name from this message. If the message has 365 * a bunch of arpanet stuff in it, we may have to skin the name 366 * before returning it. 367 */ 368 char * 369 nameof(mp, reptype) 370 register struct message *mp; 371 { 372 register char *cp, *cp2; 373 374 cp = skin(name1(mp, reptype)); 375 if (reptype != 0 || charcount(cp, '!') < 2) 376 return(cp); 377 cp2 = rindex(cp, '!'); 378 cp2--; 379 while (cp2 > cp && *cp2 != '!') 380 cp2--; 381 if (*cp2 == '!') 382 return(cp2 + 1); 383 return(cp); 384 } 385 386 /* 387 * Skin an arpa net address according to the RFC 822 interpretation 388 * of "host-phrase." 389 */ 390 char * 391 skin(name) 392 char *name; 393 { 394 register int c; 395 register char *cp, *cp2; 396 char *bufend; 397 int gotlt, lastsp; 398 char nbuf[BUFSIZ]; 399 int nesting; 400 401 if (name == NOSTR) 402 return(NOSTR); 403 if (index(name, '(') == NOSTR && index(name, '<') == NOSTR 404 && index(name, ' ') == NOSTR) 405 return(name); 406 gotlt = 0; 407 lastsp = 0; 408 bufend = nbuf; 409 for (cp = name, cp2 = bufend; c = *cp++; ) { 410 switch (c) { 411 case '(': 412 /* 413 * Start of a "comment". 414 * Ignore it. 415 */ 416 nesting = 1; 417 while ((c = *cp) != 0) { 418 cp++; 419 switch (c) { 420 case '\\': 421 if (*cp == 0) 422 goto outcm; 423 cp++; 424 break; 425 case '(': 426 nesting++; 427 break; 428 429 case ')': 430 --nesting; 431 break; 432 } 433 434 if (nesting <= 0) 435 break; 436 } 437 outcm: 438 lastsp = 0; 439 break; 440 441 case '"': 442 /* 443 * Start of a "quoted-string". 444 * Copy it in its entirety. 445 */ 446 while ((c = *cp) != 0) { 447 cp++; 448 switch (c) { 449 case '\\': 450 if ((c = *cp) == 0) 451 goto outqs; 452 cp++; 453 break; 454 case '"': 455 goto outqs; 456 } 457 *cp2++ = c; 458 } 459 outqs: 460 lastsp = 0; 461 break; 462 463 case ' ': 464 if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 465 cp += 3, *cp2++ = '@'; 466 else 467 if (cp[0] == '@' && cp[1] == ' ') 468 cp += 2, *cp2++ = '@'; 469 else 470 lastsp = 1; 471 break; 472 473 case '<': 474 cp2 = bufend; 475 gotlt++; 476 lastsp = 0; 477 break; 478 479 case '>': 480 if (gotlt) { 481 gotlt = 0; 482 while (*cp != ',' && *cp != 0) 483 cp++; 484 if (*cp == 0 ) 485 goto done; 486 *cp2++ = ','; 487 *cp2++ = ' '; 488 bufend = cp2; 489 break; 490 } 491 492 /* Fall into . . . */ 493 494 default: 495 if (lastsp) { 496 lastsp = 0; 497 *cp2++ = ' '; 498 } 499 *cp2++ = c; 500 break; 501 } 502 } 503 done: 504 *cp2 = 0; 505 506 return(savestr(nbuf)); 507 } 508 509 /* 510 * Fetch the sender's name from the passed message. 511 * Reptype can be 512 * 0 -- get sender's name for display purposes 513 * 1 -- get sender's name for reply 514 * 2 -- get sender's name for Reply 515 */ 516 517 char * 518 name1(mp, reptype) 519 register struct message *mp; 520 { 521 char namebuf[LINESIZE]; 522 char linebuf[LINESIZE]; 523 register char *cp, *cp2; 524 register FILE *ibuf; 525 int first = 1; 526 527 if ((cp = hfield("from", mp)) != NOSTR) 528 return cp; 529 if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 530 return cp; 531 ibuf = setinput(mp); 532 namebuf[0] = 0; 533 if (readline(ibuf, linebuf) < 0) 534 return(savestr(namebuf)); 535 newname: 536 for (cp = linebuf; *cp && *cp != ' '; cp++) 537 ; 538 for (; *cp == ' ' || *cp == '\t'; cp++) 539 ; 540 for (cp2 = &namebuf[strlen(namebuf)]; 541 *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) 542 *cp2++ = *cp++; 543 *cp2 = '\0'; 544 if (readline(ibuf, linebuf) < 0) 545 return(savestr(namebuf)); 546 if ((cp = index(linebuf, 'F')) == NULL) 547 return(savestr(namebuf)); 548 if (strncmp(cp, "From", 4) != 0) 549 return(savestr(namebuf)); 550 while ((cp = index(cp, 'r')) != NULL) { 551 if (strncmp(cp, "remote", 6) == 0) { 552 if ((cp = index(cp, 'f')) == NULL) 553 break; 554 if (strncmp(cp, "from", 4) != 0) 555 break; 556 if ((cp = index(cp, ' ')) == NULL) 557 break; 558 cp++; 559 if (first) { 560 strcpy(namebuf, cp); 561 first = 0; 562 } else 563 strcpy(rindex(namebuf, '!')+1, cp); 564 strcat(namebuf, "!"); 565 goto newname; 566 } 567 cp++; 568 } 569 return(savestr(namebuf)); 570 } 571 572 /* 573 * Count the occurances of c in str 574 */ 575 charcount(str, c) 576 char *str; 577 { 578 register char *cp; 579 register int i; 580 581 for (i = 0, cp = str; *cp; cp++) 582 if (*cp == c) 583 i++; 584 return(i); 585 } 586 587 /* 588 * Are any of the characters in the two strings the same? 589 */ 590 591 anyof(s1, s2) 592 register char *s1, *s2; 593 { 594 595 while (*s1) 596 if (index(s2, *s1++)) 597 return 1; 598 return 0; 599 } 600 601 /* 602 * Convert c to upper case 603 */ 604 605 raise(c) 606 register c; 607 { 608 609 if (islower(c)) 610 return toupper(c); 611 return c; 612 } 613 614 /* 615 * Copy s1 to s2, return pointer to null in s2. 616 */ 617 618 char * 619 copy(s1, s2) 620 register char *s1, *s2; 621 { 622 623 while (*s2++ = *s1++) 624 ; 625 return s2 - 1; 626 } 627 628 /* 629 * Add a single character onto a string. 630 */ 631 632 stradd(str, c) 633 register char *str; 634 { 635 636 while (*str++) 637 ; 638 str[-1] = c; 639 *str = 0; 640 } 641 642 /* 643 * See if the given header field is supposed to be ignored. 644 */ 645 isign(field, ignore) 646 char *field; 647 struct ignoretab ignore[2]; 648 { 649 char realfld[BUFSIZ]; 650 651 /* 652 * Lower-case the string, so that "Status" and "status" 653 * will hash to the same place. 654 */ 655 istrcpy(realfld, field); 656 if (ignore[1].i_count > 0) 657 return (!member(realfld, ignore + 1)); 658 else 659 return (member(realfld, ignore)); 660 } 661 662 member(realfield, table) 663 register char *realfield; 664 struct ignoretab *table; 665 { 666 register struct ignore *igp; 667 668 for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link) 669 if (*igp->i_field == *realfield && 670 equal(igp->i_field, realfield)) 671 return (1); 672 return (0); 673 } 674