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