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