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