util.c revision 18532
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}; 299#define SSTACK_SIZE 64 /* XXX was NOFILE. */ 300static struct sstack sstack[SSTACK_SIZE]; 301 302/* 303 * Pushdown current input file and switch to a new one. 304 * Set the global flag "sourcing" so that others will realize 305 * that they are no longer reading from a tty (in all probability). 306 */ 307int 308source(arglist) 309 char **arglist; 310{ 311 FILE *fi; 312 char *cp; 313 314 if ((cp = expand(*arglist)) == NOSTR) 315 return(1); 316 if ((fi = Fopen(cp, "r")) == NULL) { 317 perror(cp); 318 return(1); 319 } 320 if (ssp >= SSTACK_SIZE - 1) { 321 printf("Too much \"sourcing\" going on.\n"); 322 Fclose(fi); 323 return(1); 324 } 325 sstack[ssp].s_file = input; 326 sstack[ssp].s_cond = cond; 327 sstack[ssp].s_loading = loading; 328 ssp++; 329 loading = 0; 330 cond = CANY; 331 input = fi; 332 sourcing++; 333 return(0); 334} 335 336/* 337 * Pop the current input back to the previous level. 338 * Update the "sourcing" flag as appropriate. 339 */ 340int 341unstack() 342{ 343 if (ssp <= 0) { 344 printf("\"Source\" stack over-pop.\n"); 345 sourcing = 0; 346 return(1); 347 } 348 Fclose(input); 349 if (cond != CANY) 350 printf("Unmatched \"if\"\n"); 351 ssp--; 352 cond = sstack[ssp].s_cond; 353 loading = sstack[ssp].s_loading; 354 input = sstack[ssp].s_file; 355 if (ssp == 0) 356 sourcing = loading; 357 return(0); 358} 359 360/* 361 * Touch the indicated file. 362 * This is nifty for the shell. 363 */ 364void 365alter(name) 366 char *name; 367{ 368 struct stat sb; 369 struct timeval tv[2]; 370 time_t time(); 371 372 if (stat(name, &sb)) 373 return; 374 tv[0].tv_sec = time((time_t *)0) + 1; 375 tv[1].tv_sec = sb.st_mtime; 376 tv[0].tv_usec = tv[1].tv_usec = 0; 377 (void)utimes(name, tv); 378} 379 380/* 381 * Examine the passed line buffer and 382 * return true if it is all blanks and tabs. 383 */ 384int 385blankline(linebuf) 386 char linebuf[]; 387{ 388 register char *cp; 389 390 for (cp = linebuf; *cp; cp++) 391 if (*cp != ' ' && *cp != '\t') 392 return(0); 393 return(1); 394} 395 396/* 397 * Get sender's name from this message. If the message has 398 * a bunch of arpanet stuff in it, we may have to skin the name 399 * before returning it. 400 */ 401char * 402nameof(mp, reptype) 403 register struct message *mp; 404 int reptype; 405{ 406 register char *cp, *cp2; 407 408 cp = skin(name1(mp, reptype)); 409 if (reptype != 0 || charcount(cp, '!') < 2) 410 return(cp); 411 cp2 = rindex(cp, '!'); 412 cp2--; 413 while (cp2 > cp && *cp2 != '!') 414 cp2--; 415 if (*cp2 == '!') 416 return(cp2 + 1); 417 return(cp); 418} 419 420/* 421 * Start of a "comment". 422 * Ignore it. 423 */ 424char * 425skip_comment(cp) 426 register char *cp; 427{ 428 register nesting = 1; 429 430 for (; nesting > 0 && *cp; cp++) { 431 switch (*cp) { 432 case '\\': 433 if (cp[1]) 434 cp++; 435 break; 436 case '(': 437 nesting++; 438 break; 439 case ')': 440 nesting--; 441 break; 442 } 443 } 444 return cp; 445} 446 447/* 448 * Skin an arpa net address according to the RFC 822 interpretation 449 * of "host-phrase." 450 */ 451char * 452skin(name) 453 char *name; 454{ 455 register int c; 456 register char *cp, *cp2; 457 char *bufend; 458 int gotlt, lastsp; 459 char nbuf[BUFSIZ]; 460 461 if (name == NOSTR) 462 return(NOSTR); 463 if (index(name, '(') == NOSTR && index(name, '<') == NOSTR 464 && index(name, ' ') == NOSTR) 465 return(name); 466 gotlt = 0; 467 lastsp = 0; 468 bufend = nbuf; 469 for (cp = name, cp2 = bufend; c = *cp++; ) { 470 switch (c) { 471 case '(': 472 cp = skip_comment(cp); 473 lastsp = 0; 474 break; 475 476 case '"': 477 /* 478 * Start of a "quoted-string". 479 * Copy it in its entirety. 480 */ 481 while (c = *cp) { 482 cp++; 483 if (c == '"') 484 break; 485 if (c != '\\') 486 *cp2++ = c; 487 else if (c = *cp) { 488 *cp2++ = c; 489 cp++; 490 } 491 } 492 lastsp = 0; 493 break; 494 495 case ' ': 496 if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 497 cp += 3, *cp2++ = '@'; 498 else 499 if (cp[0] == '@' && cp[1] == ' ') 500 cp += 2, *cp2++ = '@'; 501 else 502 lastsp = 1; 503 break; 504 505 case '<': 506 cp2 = bufend; 507 gotlt++; 508 lastsp = 0; 509 break; 510 511 case '>': 512 if (gotlt) { 513 gotlt = 0; 514 while ((c = *cp) && c != ',') { 515 cp++; 516 if (c == '(') 517 cp = skip_comment(cp); 518 else if (c == '"') 519 while (c = *cp) { 520 cp++; 521 if (c == '"') 522 break; 523 if (c == '\\' && *cp) 524 cp++; 525 } 526 } 527 lastsp = 0; 528 break; 529 } 530 /* Fall into . . . */ 531 532 default: 533 if (lastsp) { 534 lastsp = 0; 535 *cp2++ = ' '; 536 } 537 *cp2++ = c; 538 if (c == ',' && !gotlt) { 539 *cp2++ = ' '; 540 for (; *cp == ' '; cp++) 541 ; 542 lastsp = 0; 543 bufend = cp2; 544 } 545 } 546 } 547 *cp2 = 0; 548 549 return(savestr(nbuf)); 550} 551 552/* 553 * Fetch the sender's name from the passed message. 554 * Reptype can be 555 * 0 -- get sender's name for display purposes 556 * 1 -- get sender's name for reply 557 * 2 -- get sender's name for Reply 558 */ 559char * 560name1(mp, reptype) 561 register struct message *mp; 562 int reptype; 563{ 564 char namebuf[LINESIZE]; 565 char linebuf[LINESIZE]; 566 register char *cp, *cp2; 567 register FILE *ibuf; 568 int first = 1; 569 570 if ((cp = hfield("from", mp)) != NOSTR) 571 return cp; 572 if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 573 return cp; 574 ibuf = setinput(mp); 575 namebuf[0] = 0; 576 if (readline(ibuf, linebuf, LINESIZE) < 0) 577 return(savestr(namebuf)); 578newname: 579 for (cp = linebuf; *cp && *cp != ' '; cp++) 580 ; 581 for (; *cp == ' ' || *cp == '\t'; cp++) 582 ; 583 for (cp2 = &namebuf[strlen(namebuf)]; 584 *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) 585 *cp2++ = *cp++; 586 *cp2 = '\0'; 587 if (readline(ibuf, linebuf, LINESIZE) < 0) 588 return(savestr(namebuf)); 589 if ((cp = index(linebuf, 'F')) == NULL) 590 return(savestr(namebuf)); 591 if (strncmp(cp, "From", 4) != 0) 592 return(savestr(namebuf)); 593 while ((cp = index(cp, 'r')) != NULL) { 594 if (strncmp(cp, "remote", 6) == 0) { 595 if ((cp = index(cp, 'f')) == NULL) 596 break; 597 if (strncmp(cp, "from", 4) != 0) 598 break; 599 if ((cp = index(cp, ' ')) == NULL) 600 break; 601 cp++; 602 if (first) { 603 strcpy(namebuf, cp); 604 first = 0; 605 } else 606 strcpy(rindex(namebuf, '!')+1, cp); 607 strcat(namebuf, "!"); 608 goto newname; 609 } 610 cp++; 611 } 612 return(savestr(namebuf)); 613} 614 615/* 616 * Count the occurances of c in str 617 */ 618int 619charcount(str, c) 620 char *str; 621 int c; 622{ 623 register char *cp; 624 register int i; 625 626 for (i = 0, cp = str; *cp; cp++) 627 if (*cp == c) 628 i++; 629 return(i); 630} 631 632/* 633 * Are any of the characters in the two strings the same? 634 */ 635int 636anyof(s1, s2) 637 register char *s1, *s2; 638{ 639 640 while (*s1) 641 if (index(s2, *s1++)) 642 return 1; 643 return 0; 644} 645 646/* 647 * Convert c to upper case 648 */ 649int 650raise(c) 651 register int c; 652{ 653 654 if (islower(c)) 655 return toupper(c); 656 return c; 657} 658 659/* 660 * Copy s1 to s2, return pointer to null in s2. 661 */ 662char * 663copy(s1, s2) 664 register char *s1, *s2; 665{ 666 667 while (*s2++ = *s1++) 668 ; 669 return s2 - 1; 670} 671 672/* 673 * See if the given header field is supposed to be ignored. 674 */ 675int 676isign(field, ignore) 677 char *field; 678 struct ignoretab ignore[2]; 679{ 680 char realfld[BUFSIZ]; 681 682 if (ignore == ignoreall) 683 return 1; 684 /* 685 * Lower-case the string, so that "Status" and "status" 686 * will hash to the same place. 687 */ 688 istrcpy(realfld, field); 689 if (ignore[1].i_count > 0) 690 return (!member(realfld, ignore + 1)); 691 else 692 return (member(realfld, ignore)); 693} 694 695int 696member(realfield, table) 697 register char *realfield; 698 struct ignoretab *table; 699{ 700 register struct ignore *igp; 701 702 for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link) 703 if (*igp->i_field == *realfield && 704 equal(igp->i_field, realfield)) 705 return (1); 706 return (0); 707} 708