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