1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)util.c 8.36 (Berkeley) 04/12/94"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 # include <sysexits.h> 15 /* 16 ** STRIPQUOTES -- Strip quotes & quote bits from a string. 17 ** 18 ** Runs through a string and strips off unquoted quote 19 ** characters and quote bits. This is done in place. 20 ** 21 ** Parameters: 22 ** s -- the string to strip. 23 ** 24 ** Returns: 25 ** none. 26 ** 27 ** Side Effects: 28 ** none. 29 ** 30 ** Called By: 31 ** deliver 32 */ 33 34 stripquotes(s) 35 char *s; 36 { 37 register char *p; 38 register char *q; 39 register char c; 40 41 if (s == NULL) 42 return; 43 44 p = q = s; 45 do 46 { 47 c = *p++; 48 if (c == '\\') 49 c = *p++; 50 else if (c == '"') 51 continue; 52 *q++ = c; 53 } while (c != '\0'); 54 } 55 /* 56 ** XALLOC -- Allocate memory and bitch wildly on failure. 57 ** 58 ** THIS IS A CLUDGE. This should be made to give a proper 59 ** error -- but after all, what can we do? 60 ** 61 ** Parameters: 62 ** sz -- size of area to allocate. 63 ** 64 ** Returns: 65 ** pointer to data region. 66 ** 67 ** Side Effects: 68 ** Memory is allocated. 69 */ 70 71 char * 72 xalloc(sz) 73 register int sz; 74 { 75 register char *p; 76 77 /* some systems can't handle size zero mallocs */ 78 if (sz <= 0) 79 sz = 1; 80 81 p = malloc((unsigned) sz); 82 if (p == NULL) 83 { 84 syserr("Out of memory!!"); 85 abort(); 86 /* exit(EX_UNAVAILABLE); */ 87 } 88 return (p); 89 } 90 /* 91 ** COPYPLIST -- copy list of pointers. 92 ** 93 ** This routine is the equivalent of newstr for lists of 94 ** pointers. 95 ** 96 ** Parameters: 97 ** list -- list of pointers to copy. 98 ** Must be NULL terminated. 99 ** copycont -- if TRUE, copy the contents of the vector 100 ** (which must be a string) also. 101 ** 102 ** Returns: 103 ** a copy of 'list'. 104 ** 105 ** Side Effects: 106 ** none. 107 */ 108 109 char ** 110 copyplist(list, copycont) 111 char **list; 112 bool copycont; 113 { 114 register char **vp; 115 register char **newvp; 116 117 for (vp = list; *vp != NULL; vp++) 118 continue; 119 120 vp++; 121 122 newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); 123 bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); 124 125 if (copycont) 126 { 127 for (vp = newvp; *vp != NULL; vp++) 128 *vp = newstr(*vp); 129 } 130 131 return (newvp); 132 } 133 /* 134 ** COPYQUEUE -- copy address queue. 135 ** 136 ** This routine is the equivalent of newstr for address queues 137 ** addresses marked with QDONTSEND aren't copied 138 ** 139 ** Parameters: 140 ** addr -- list of address structures to copy. 141 ** 142 ** Returns: 143 ** a copy of 'addr'. 144 ** 145 ** Side Effects: 146 ** none. 147 */ 148 149 ADDRESS * 150 copyqueue(addr) 151 ADDRESS *addr; 152 { 153 register ADDRESS *newaddr; 154 ADDRESS *ret; 155 register ADDRESS **tail = &ret; 156 157 while (addr != NULL) 158 { 159 if (!bitset(QDONTSEND, addr->q_flags)) 160 { 161 newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS)); 162 STRUCTCOPY(*addr, *newaddr); 163 *tail = newaddr; 164 tail = &newaddr->q_next; 165 } 166 addr = addr->q_next; 167 } 168 *tail = NULL; 169 170 return ret; 171 } 172 /* 173 ** PRINTAV -- print argument vector. 174 ** 175 ** Parameters: 176 ** av -- argument vector. 177 ** 178 ** Returns: 179 ** none. 180 ** 181 ** Side Effects: 182 ** prints av. 183 */ 184 185 printav(av) 186 register char **av; 187 { 188 while (*av != NULL) 189 { 190 if (tTd(0, 44)) 191 printf("\n\t%08x=", *av); 192 else 193 (void) putchar(' '); 194 xputs(*av++); 195 } 196 (void) putchar('\n'); 197 } 198 /* 199 ** LOWER -- turn letter into lower case. 200 ** 201 ** Parameters: 202 ** c -- character to turn into lower case. 203 ** 204 ** Returns: 205 ** c, in lower case. 206 ** 207 ** Side Effects: 208 ** none. 209 */ 210 211 char 212 lower(c) 213 register char c; 214 { 215 return((isascii(c) && isupper(c)) ? tolower(c) : c); 216 } 217 /* 218 ** XPUTS -- put string doing control escapes. 219 ** 220 ** Parameters: 221 ** s -- string to put. 222 ** 223 ** Returns: 224 ** none. 225 ** 226 ** Side Effects: 227 ** output to stdout 228 */ 229 230 xputs(s) 231 register char *s; 232 { 233 register int c; 234 register struct metamac *mp; 235 extern struct metamac MetaMacros[]; 236 237 if (s == NULL) 238 { 239 printf("<null>"); 240 return; 241 } 242 while ((c = (*s++ & 0377)) != '\0') 243 { 244 if (!isascii(c)) 245 { 246 if (c == MATCHREPL || c == MACROEXPAND) 247 { 248 putchar('$'); 249 continue; 250 } 251 for (mp = MetaMacros; mp->metaname != '\0'; mp++) 252 { 253 if ((mp->metaval & 0377) == c) 254 { 255 printf("$%c", mp->metaname); 256 break; 257 } 258 } 259 if (mp->metaname != '\0') 260 continue; 261 (void) putchar('\\'); 262 c &= 0177; 263 } 264 if (isprint(c)) 265 { 266 putchar(c); 267 continue; 268 } 269 270 /* wasn't a meta-macro -- find another way to print it */ 271 switch (c) 272 { 273 case '\0': 274 continue; 275 276 case '\n': 277 c = 'n'; 278 break; 279 280 case '\r': 281 c = 'r'; 282 break; 283 284 case '\t': 285 c = 't'; 286 break; 287 288 default: 289 (void) putchar('^'); 290 (void) putchar(c ^ 0100); 291 continue; 292 } 293 } 294 (void) fflush(stdout); 295 } 296 /* 297 ** MAKELOWER -- Translate a line into lower case 298 ** 299 ** Parameters: 300 ** p -- the string to translate. If NULL, return is 301 ** immediate. 302 ** 303 ** Returns: 304 ** none. 305 ** 306 ** Side Effects: 307 ** String pointed to by p is translated to lower case. 308 ** 309 ** Called By: 310 ** parse 311 */ 312 313 makelower(p) 314 register char *p; 315 { 316 register char c; 317 318 if (p == NULL) 319 return; 320 for (; (c = *p) != '\0'; p++) 321 if (isascii(c) && isupper(c)) 322 *p = tolower(c); 323 } 324 /* 325 ** BUILDFNAME -- build full name from gecos style entry. 326 ** 327 ** This routine interprets the strange entry that would appear 328 ** in the GECOS field of the password file. 329 ** 330 ** Parameters: 331 ** p -- name to build. 332 ** login -- the login name of this user (for &). 333 ** buf -- place to put the result. 334 ** 335 ** Returns: 336 ** none. 337 ** 338 ** Side Effects: 339 ** none. 340 */ 341 342 buildfname(gecos, login, buf) 343 register char *gecos; 344 char *login; 345 char *buf; 346 { 347 register char *p; 348 register char *bp = buf; 349 int l; 350 351 if (*gecos == '*') 352 gecos++; 353 354 /* find length of final string */ 355 l = 0; 356 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 357 { 358 if (*p == '&') 359 l += strlen(login); 360 else 361 l++; 362 } 363 364 /* now fill in buf */ 365 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 366 { 367 if (*p == '&') 368 { 369 (void) strcpy(bp, login); 370 *bp = toupper(*bp); 371 while (*bp != '\0') 372 bp++; 373 } 374 else 375 *bp++ = *p; 376 } 377 *bp = '\0'; 378 } 379 /* 380 ** SAFEFILE -- return true if a file exists and is safe for a user. 381 ** 382 ** Parameters: 383 ** fn -- filename to check. 384 ** uid -- user id to compare against. 385 ** gid -- group id to compare against. 386 ** uname -- user name to compare against (used for group 387 ** sets). 388 ** flags -- modifiers: 389 ** SFF_MUSTOWN -- "uid" must own this file. 390 ** SFF_NOSLINK -- file cannot be a symbolic link. 391 ** mode -- mode bits that must match. 392 ** 393 ** Returns: 394 ** 0 if fn exists, is owned by uid, and matches mode. 395 ** An errno otherwise. The actual errno is cleared. 396 ** 397 ** Side Effects: 398 ** none. 399 */ 400 401 #include <grp.h> 402 403 #ifndef S_IXOTH 404 # define S_IXOTH (S_IEXEC >> 6) 405 #endif 406 407 #ifndef S_IXGRP 408 # define S_IXGRP (S_IEXEC >> 3) 409 #endif 410 411 #ifndef S_IXUSR 412 # define S_IXUSR (S_IEXEC) 413 #endif 414 415 int 416 safefile(fn, uid, gid, uname, flags, mode) 417 char *fn; 418 uid_t uid; 419 gid_t gid; 420 char *uname; 421 int flags; 422 int mode; 423 { 424 register char *p; 425 register struct group *gr = NULL; 426 struct stat stbuf; 427 428 if (tTd(54, 4)) 429 printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n", 430 fn, uid, gid, flags, mode); 431 errno = 0; 432 433 for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/') 434 { 435 *p = '\0'; 436 if (stat(fn, &stbuf) < 0) 437 break; 438 if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 439 { 440 if (bitset(S_IXOTH, stbuf.st_mode)) 441 continue; 442 break; 443 } 444 if (stbuf.st_uid == uid && bitset(S_IXUSR, stbuf.st_mode)) 445 continue; 446 if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode)) 447 continue; 448 #ifndef NO_GROUP_SET 449 if (uname != NULL && 450 ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 451 (gr = getgrgid(stbuf.st_gid)) != NULL)) 452 { 453 register char **gp; 454 455 for (gp = gr->gr_mem; *gp != NULL; gp++) 456 if (strcmp(*gp, uname) == 0) 457 break; 458 if (*gp != NULL && bitset(S_IXGRP, stbuf.st_mode)) 459 continue; 460 } 461 #endif 462 if (!bitset(S_IXOTH, stbuf.st_mode)) 463 break; 464 } 465 if (p != NULL) 466 { 467 int ret = errno; 468 469 if (ret == 0) 470 ret = EACCES; 471 if (tTd(54, 4)) 472 printf("\t[dir %s] %s\n", fn, errstring(ret)); 473 *p = '/'; 474 return ret; 475 } 476 477 #ifdef HASLSTAT 478 if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, &stbuf) 479 : stat(fn, &stbuf)) < 0) 480 #else 481 if (stat(fn, &stbuf) < 0) 482 #endif 483 { 484 int ret = errno; 485 486 if (tTd(54, 4)) 487 printf("\t%s\n", errstring(ret)); 488 489 errno = 0; 490 return ret; 491 } 492 493 #ifdef S_ISLNK 494 if (bitset(SFF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode)) 495 { 496 if (tTd(54, 4)) 497 printf("\t[slink mode %o]\tEPERM\n", stbuf.st_mode); 498 return EPERM; 499 } 500 #endif 501 502 if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 503 mode >>= 6; 504 else if (stbuf.st_uid != uid) 505 { 506 mode >>= 3; 507 if (stbuf.st_gid == gid) 508 ; 509 #ifndef NO_GROUP_SET 510 else if (uname != NULL && 511 ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 512 (gr = getgrgid(stbuf.st_gid)) != NULL)) 513 { 514 register char **gp; 515 516 for (gp = gr->gr_mem; *gp != NULL; gp++) 517 if (strcmp(*gp, uname) == 0) 518 break; 519 if (*gp == NULL) 520 mode >>= 3; 521 } 522 #endif 523 else 524 mode >>= 3; 525 } 526 if (tTd(54, 4)) 527 printf("\t[uid %d, stat %o, mode %o] ", 528 stbuf.st_uid, stbuf.st_mode, mode); 529 if ((stbuf.st_uid == uid || stbuf.st_uid == 0 || 530 !bitset(SFF_MUSTOWN, flags)) && 531 (stbuf.st_mode & mode) == mode) 532 { 533 if (tTd(54, 4)) 534 printf("\tOK\n"); 535 return 0; 536 } 537 if (tTd(54, 4)) 538 printf("\tEACCES\n"); 539 return EACCES; 540 } 541 /* 542 ** FIXCRLF -- fix <CR><LF> in line. 543 ** 544 ** Looks for the <CR><LF> combination and turns it into the 545 ** UNIX canonical <NL> character. It only takes one line, 546 ** i.e., it is assumed that the first <NL> found is the end 547 ** of the line. 548 ** 549 ** Parameters: 550 ** line -- the line to fix. 551 ** stripnl -- if true, strip the newline also. 552 ** 553 ** Returns: 554 ** none. 555 ** 556 ** Side Effects: 557 ** line is changed in place. 558 */ 559 560 fixcrlf(line, stripnl) 561 char *line; 562 bool stripnl; 563 { 564 register char *p; 565 566 p = strchr(line, '\n'); 567 if (p == NULL) 568 return; 569 if (p > line && p[-1] == '\r') 570 p--; 571 if (!stripnl) 572 *p++ = '\n'; 573 *p = '\0'; 574 } 575 /* 576 ** DFOPEN -- determined file open 577 ** 578 ** This routine has the semantics of fopen, except that it will 579 ** keep trying a few times to make this happen. The idea is that 580 ** on very loaded systems, we may run out of resources (inodes, 581 ** whatever), so this tries to get around it. 582 */ 583 584 #ifndef O_ACCMODE 585 # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) 586 #endif 587 588 struct omodes 589 { 590 int mask; 591 int mode; 592 char *farg; 593 } OpenModes[] = 594 { 595 O_ACCMODE, O_RDONLY, "r", 596 O_ACCMODE|O_APPEND, O_WRONLY, "w", 597 O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a", 598 O_TRUNC, 0, "w+", 599 O_APPEND, O_APPEND, "a+", 600 0, 0, "r+", 601 }; 602 603 FILE * 604 dfopen(filename, omode, cmode) 605 char *filename; 606 int omode; 607 int cmode; 608 { 609 register int tries; 610 int fd; 611 register struct omodes *om; 612 struct stat st; 613 614 for (om = OpenModes; om->mask != 0; om++) 615 if ((omode & om->mask) == om->mode) 616 break; 617 618 for (tries = 0; tries < 10; tries++) 619 { 620 sleep((unsigned) (10 * tries)); 621 errno = 0; 622 fd = open(filename, omode, cmode); 623 if (fd >= 0) 624 break; 625 switch (errno) 626 { 627 case ENFILE: /* system file table full */ 628 case EINTR: /* interrupted syscall */ 629 #ifdef ETXTBSY 630 case ETXTBSY: /* Apollo: net file locked */ 631 #endif 632 continue; 633 } 634 break; 635 } 636 if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode)) 637 { 638 int locktype; 639 640 /* lock the file to avoid accidental conflicts */ 641 if ((omode & O_ACCMODE) != O_RDONLY) 642 locktype = LOCK_EX; 643 else 644 locktype = LOCK_SH; 645 (void) lockfile(fd, filename, NULL, locktype); 646 errno = 0; 647 } 648 if (fd < 0) 649 return NULL; 650 else 651 return fdopen(fd, om->farg); 652 } 653 /* 654 ** PUTLINE -- put a line like fputs obeying SMTP conventions 655 ** 656 ** This routine always guarantees outputing a newline (or CRLF, 657 ** as appropriate) at the end of the string. 658 ** 659 ** Parameters: 660 ** l -- line to put. 661 ** mci -- the mailer connection information. 662 ** 663 ** Returns: 664 ** none 665 ** 666 ** Side Effects: 667 ** output of l to fp. 668 */ 669 670 putline(l, mci) 671 register char *l; 672 register MCI *mci; 673 { 674 register char *p; 675 register char svchar; 676 int slop = 0; 677 678 /* strip out 0200 bits -- these can look like TELNET protocol */ 679 if (bitset(MCIF_7BIT, mci->mci_flags)) 680 { 681 for (p = l; (svchar = *p) != '\0'; ++p) 682 if (bitset(0200, svchar)) 683 *p = svchar &~ 0200; 684 } 685 686 do 687 { 688 /* find the end of the line */ 689 p = strchr(l, '\n'); 690 if (p == NULL) 691 p = &l[strlen(l)]; 692 693 if (TrafficLogFile != NULL) 694 fprintf(TrafficLogFile, "%05d >>> ", getpid()); 695 696 /* check for line overflow */ 697 while (mci->mci_mailer->m_linelimit > 0 && 698 (p - l + slop) > mci->mci_mailer->m_linelimit) 699 { 700 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 701 702 svchar = *q; 703 *q = '\0'; 704 if (l[0] == '.' && slop == 0 && 705 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 706 { 707 (void) putc('.', mci->mci_out); 708 if (TrafficLogFile != NULL) 709 (void) putc('.', TrafficLogFile); 710 } 711 fputs(l, mci->mci_out); 712 (void) putc('!', mci->mci_out); 713 fputs(mci->mci_mailer->m_eol, mci->mci_out); 714 (void) putc(' ', mci->mci_out); 715 if (TrafficLogFile != NULL) 716 fprintf(TrafficLogFile, "%s!\n%05d >>> ", 717 l, getpid()); 718 *q = svchar; 719 l = q; 720 slop = 1; 721 } 722 723 /* output last part */ 724 if (l[0] == '.' && slop == 0 && 725 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 726 { 727 (void) putc('.', mci->mci_out); 728 if (TrafficLogFile != NULL) 729 (void) putc('.', TrafficLogFile); 730 } 731 if (TrafficLogFile != NULL) 732 fprintf(TrafficLogFile, "%.*s\n", p - l, l); 733 for ( ; l < p; ++l) 734 (void) putc(*l, mci->mci_out); 735 fputs(mci->mci_mailer->m_eol, mci->mci_out); 736 if (*l == '\n') 737 ++l; 738 } while (l[0] != '\0'); 739 } 740 /* 741 ** XUNLINK -- unlink a file, doing logging as appropriate. 742 ** 743 ** Parameters: 744 ** f -- name of file to unlink. 745 ** 746 ** Returns: 747 ** none. 748 ** 749 ** Side Effects: 750 ** f is unlinked. 751 */ 752 753 xunlink(f) 754 char *f; 755 { 756 register int i; 757 758 # ifdef LOG 759 if (LogLevel > 98) 760 syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f); 761 # endif /* LOG */ 762 763 i = unlink(f); 764 # ifdef LOG 765 if (i < 0 && LogLevel > 97) 766 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 767 # endif /* LOG */ 768 } 769 /* 770 ** XFCLOSE -- close a file, doing logging as appropriate. 771 ** 772 ** Parameters: 773 ** fp -- file pointer for the file to close 774 ** a, b -- miscellaneous crud to print for debugging 775 ** 776 ** Returns: 777 ** none. 778 ** 779 ** Side Effects: 780 ** fp is closed. 781 */ 782 783 xfclose(fp, a, b) 784 FILE *fp; 785 char *a, *b; 786 { 787 if (tTd(53, 99)) 788 printf("xfclose(%x) %s %s\n", fp, a, b); 789 #ifdef XDEBUG 790 if (fileno(fp) == 1) 791 syserr("xfclose(%s %s): fd = 1", a, b); 792 #endif 793 if (fclose(fp) < 0 && tTd(53, 99)) 794 printf("xfclose FAILURE: %s\n", errstring(errno)); 795 } 796 /* 797 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 798 ** 799 ** Parameters: 800 ** buf -- place to put the input line. 801 ** siz -- size of buf. 802 ** fp -- file to read from. 803 ** timeout -- the timeout before error occurs. 804 ** during -- what we are trying to read (for error messages). 805 ** 806 ** Returns: 807 ** NULL on error (including timeout). This will also leave 808 ** buf containing a null string. 809 ** buf otherwise. 810 ** 811 ** Side Effects: 812 ** none. 813 */ 814 815 static jmp_buf CtxReadTimeout; 816 static int readtimeout(); 817 818 char * 819 sfgets(buf, siz, fp, timeout, during) 820 char *buf; 821 int siz; 822 FILE *fp; 823 time_t timeout; 824 char *during; 825 { 826 register EVENT *ev = NULL; 827 register char *p; 828 829 if (fp == NULL) 830 { 831 buf[0] = '\0'; 832 return NULL; 833 } 834 835 /* set the timeout */ 836 if (timeout != 0) 837 { 838 if (setjmp(CtxReadTimeout) != 0) 839 { 840 # ifdef LOG 841 syslog(LOG_NOTICE, 842 "timeout waiting for input from %s during %s\n", 843 CurHostName? CurHostName: "local", during); 844 # endif 845 errno = 0; 846 usrerr("451 timeout waiting for input during %s", 847 during); 848 buf[0] = '\0'; 849 #ifdef XDEBUG 850 checkfd012(during); 851 #endif 852 return (NULL); 853 } 854 ev = setevent(timeout, readtimeout, 0); 855 } 856 857 /* try to read */ 858 p = NULL; 859 while (!feof(fp) && !ferror(fp)) 860 { 861 errno = 0; 862 p = fgets(buf, siz, fp); 863 if (p != NULL || errno != EINTR) 864 break; 865 clearerr(fp); 866 } 867 868 /* clear the event if it has not sprung */ 869 clrevent(ev); 870 871 /* clean up the books and exit */ 872 LineNumber++; 873 if (p == NULL) 874 { 875 buf[0] = '\0'; 876 if (TrafficLogFile != NULL) 877 fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid()); 878 return (NULL); 879 } 880 if (TrafficLogFile != NULL) 881 fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf); 882 if (SevenBit) 883 for (p = buf; *p != '\0'; p++) 884 *p &= ~0200; 885 return (buf); 886 } 887 888 static 889 readtimeout() 890 { 891 longjmp(CtxReadTimeout, 1); 892 } 893 /* 894 ** FGETFOLDED -- like fgets, but know about folded lines. 895 ** 896 ** Parameters: 897 ** buf -- place to put result. 898 ** n -- bytes available. 899 ** f -- file to read from. 900 ** 901 ** Returns: 902 ** input line(s) on success, NULL on error or EOF. 903 ** This will normally be buf -- unless the line is too 904 ** long, when it will be xalloc()ed. 905 ** 906 ** Side Effects: 907 ** buf gets lines from f, with continuation lines (lines 908 ** with leading white space) appended. CRLF's are mapped 909 ** into single newlines. Any trailing NL is stripped. 910 */ 911 912 char * 913 fgetfolded(buf, n, f) 914 char *buf; 915 register int n; 916 FILE *f; 917 { 918 register char *p = buf; 919 char *bp = buf; 920 register int i; 921 922 n--; 923 while ((i = getc(f)) != EOF) 924 { 925 if (i == '\r') 926 { 927 i = getc(f); 928 if (i != '\n') 929 { 930 if (i != EOF) 931 (void) ungetc(i, f); 932 i = '\r'; 933 } 934 } 935 if (--n <= 0) 936 { 937 /* allocate new space */ 938 char *nbp; 939 int nn; 940 941 nn = (p - bp); 942 if (nn < MEMCHUNKSIZE) 943 nn *= 2; 944 else 945 nn += MEMCHUNKSIZE; 946 nbp = xalloc(nn); 947 bcopy(bp, nbp, p - bp); 948 p = &nbp[p - bp]; 949 if (bp != buf) 950 free(bp); 951 bp = nbp; 952 n = nn - (p - bp); 953 } 954 *p++ = i; 955 if (i == '\n') 956 { 957 LineNumber++; 958 i = getc(f); 959 if (i != EOF) 960 (void) ungetc(i, f); 961 if (i != ' ' && i != '\t') 962 break; 963 } 964 } 965 if (p == bp) 966 return (NULL); 967 *--p = '\0'; 968 return (bp); 969 } 970 /* 971 ** CURTIME -- return current time. 972 ** 973 ** Parameters: 974 ** none. 975 ** 976 ** Returns: 977 ** the current time. 978 ** 979 ** Side Effects: 980 ** none. 981 */ 982 983 time_t 984 curtime() 985 { 986 auto time_t t; 987 988 (void) time(&t); 989 return (t); 990 } 991 /* 992 ** ATOBOOL -- convert a string representation to boolean. 993 ** 994 ** Defaults to "TRUE" 995 ** 996 ** Parameters: 997 ** s -- string to convert. Takes "tTyY" as true, 998 ** others as false. 999 ** 1000 ** Returns: 1001 ** A boolean representation of the string. 1002 ** 1003 ** Side Effects: 1004 ** none. 1005 */ 1006 1007 bool 1008 atobool(s) 1009 register char *s; 1010 { 1011 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1012 return (TRUE); 1013 return (FALSE); 1014 } 1015 /* 1016 ** ATOOCT -- convert a string representation to octal. 1017 ** 1018 ** Parameters: 1019 ** s -- string to convert. 1020 ** 1021 ** Returns: 1022 ** An integer representing the string interpreted as an 1023 ** octal number. 1024 ** 1025 ** Side Effects: 1026 ** none. 1027 */ 1028 1029 atooct(s) 1030 register char *s; 1031 { 1032 register int i = 0; 1033 1034 while (*s >= '0' && *s <= '7') 1035 i = (i << 3) | (*s++ - '0'); 1036 return (i); 1037 } 1038 /* 1039 ** WAITFOR -- wait for a particular process id. 1040 ** 1041 ** Parameters: 1042 ** pid -- process id to wait for. 1043 ** 1044 ** Returns: 1045 ** status of pid. 1046 ** -1 if pid never shows up. 1047 ** 1048 ** Side Effects: 1049 ** none. 1050 */ 1051 1052 int 1053 waitfor(pid) 1054 int pid; 1055 { 1056 #ifdef WAITUNION 1057 union wait st; 1058 #else 1059 auto int st; 1060 #endif 1061 int i; 1062 1063 do 1064 { 1065 errno = 0; 1066 i = wait(&st); 1067 } while ((i >= 0 || errno == EINTR) && i != pid); 1068 if (i < 0) 1069 return -1; 1070 #ifdef WAITUNION 1071 return st.w_status; 1072 #else 1073 return st; 1074 #endif 1075 } 1076 /* 1077 ** BITINTERSECT -- tell if two bitmaps intersect 1078 ** 1079 ** Parameters: 1080 ** a, b -- the bitmaps in question 1081 ** 1082 ** Returns: 1083 ** TRUE if they have a non-null intersection 1084 ** FALSE otherwise 1085 ** 1086 ** Side Effects: 1087 ** none. 1088 */ 1089 1090 bool 1091 bitintersect(a, b) 1092 BITMAP a; 1093 BITMAP b; 1094 { 1095 int i; 1096 1097 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1098 if ((a[i] & b[i]) != 0) 1099 return (TRUE); 1100 return (FALSE); 1101 } 1102 /* 1103 ** BITZEROP -- tell if a bitmap is all zero 1104 ** 1105 ** Parameters: 1106 ** map -- the bit map to check 1107 ** 1108 ** Returns: 1109 ** TRUE if map is all zero. 1110 ** FALSE if there are any bits set in map. 1111 ** 1112 ** Side Effects: 1113 ** none. 1114 */ 1115 1116 bool 1117 bitzerop(map) 1118 BITMAP map; 1119 { 1120 int i; 1121 1122 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1123 if (map[i] != 0) 1124 return (FALSE); 1125 return (TRUE); 1126 } 1127 /* 1128 ** STRCONTAINEDIN -- tell if one string is contained in another 1129 ** 1130 ** Parameters: 1131 ** a -- possible substring. 1132 ** b -- possible superstring. 1133 ** 1134 ** Returns: 1135 ** TRUE if a is contained in b. 1136 ** FALSE otherwise. 1137 */ 1138 1139 bool 1140 strcontainedin(a, b) 1141 register char *a; 1142 register char *b; 1143 { 1144 int la; 1145 int lb; 1146 int c; 1147 1148 la = strlen(a); 1149 lb = strlen(b); 1150 c = *a; 1151 if (isascii(c) && isupper(c)) 1152 c = tolower(c); 1153 for (; lb-- >= la; b++) 1154 { 1155 if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) 1156 continue; 1157 if (strncasecmp(a, b, la) == 0) 1158 return TRUE; 1159 } 1160 return FALSE; 1161 } 1162 /* 1163 ** CHECKFD012 -- check low numbered file descriptors 1164 ** 1165 ** File descriptors 0, 1, and 2 should be open at all times. 1166 ** This routine verifies that, and fixes it if not true. 1167 ** 1168 ** Parameters: 1169 ** where -- a tag printed if the assertion failed 1170 ** 1171 ** Returns: 1172 ** none 1173 */ 1174 1175 checkfd012(where) 1176 char *where; 1177 { 1178 #ifdef XDEBUG 1179 register int i; 1180 struct stat stbuf; 1181 1182 for (i = 0; i < 3; i++) 1183 { 1184 if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP) 1185 { 1186 /* oops.... */ 1187 int fd; 1188 1189 syserr("%s: fd %d not open", where, i); 1190 fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666); 1191 if (fd != i) 1192 { 1193 (void) dup2(fd, i); 1194 (void) close(fd); 1195 } 1196 } 1197 } 1198 #endif /* XDEBUG */ 1199 } 1200 /* 1201 ** PRINTOPENFDS -- print the open file descriptors (for debugging) 1202 ** 1203 ** Parameters: 1204 ** logit -- if set, send output to syslog; otherwise 1205 ** print for debugging. 1206 ** 1207 ** Returns: 1208 ** none. 1209 */ 1210 1211 #include <netdb.h> 1212 #include <arpa/inet.h> 1213 1214 printopenfds(logit) 1215 bool logit; 1216 { 1217 register int fd; 1218 extern int DtableSize; 1219 1220 for (fd = 0; fd < DtableSize; fd++) 1221 dumpfd(fd, FALSE, logit); 1222 } 1223 /* 1224 ** DUMPFD -- dump a file descriptor 1225 ** 1226 ** Parameters: 1227 ** fd -- the file descriptor to dump. 1228 ** printclosed -- if set, print a notification even if 1229 ** it is closed; otherwise print nothing. 1230 ** logit -- if set, send output to syslog instead of stdout. 1231 */ 1232 1233 dumpfd(fd, printclosed, logit) 1234 int fd; 1235 bool printclosed; 1236 bool logit; 1237 { 1238 register struct hostent *hp; 1239 register char *p; 1240 char *fmtstr; 1241 struct sockaddr_in sin; 1242 auto int slen; 1243 struct stat st; 1244 char buf[200]; 1245 1246 p = buf; 1247 sprintf(p, "%3d: ", fd); 1248 p += strlen(p); 1249 1250 if (fstat(fd, &st) < 0) 1251 { 1252 if (printclosed || errno != EBADF) 1253 { 1254 sprintf(p, "CANNOT STAT (%s)", errstring(errno)); 1255 goto printit; 1256 } 1257 return; 1258 } 1259 1260 slen = fcntl(fd, F_GETFL, NULL); 1261 if (slen != -1) 1262 { 1263 sprintf(p, "fl=0x%x, ", slen); 1264 p += strlen(p); 1265 } 1266 1267 sprintf(p, "mode=%o: ", st.st_mode); 1268 p += strlen(p); 1269 switch (st.st_mode & S_IFMT) 1270 { 1271 #ifdef S_IFSOCK 1272 case S_IFSOCK: 1273 sprintf(p, "SOCK "); 1274 p += strlen(p); 1275 slen = sizeof sin; 1276 if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0) 1277 sprintf(p, "(badsock)"); 1278 else 1279 { 1280 hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET); 1281 sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr) 1282 : hp->h_name, ntohs(sin.sin_port)); 1283 } 1284 p += strlen(p); 1285 sprintf(p, "->"); 1286 p += strlen(p); 1287 slen = sizeof sin; 1288 if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0) 1289 sprintf(p, "(badsock)"); 1290 else 1291 { 1292 hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET); 1293 sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr) 1294 : hp->h_name, ntohs(sin.sin_port)); 1295 } 1296 break; 1297 #endif 1298 1299 case S_IFCHR: 1300 sprintf(p, "CHR: "); 1301 p += strlen(p); 1302 goto defprint; 1303 1304 case S_IFBLK: 1305 sprintf(p, "BLK: "); 1306 p += strlen(p); 1307 goto defprint; 1308 1309 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1310 case S_IFIFO: 1311 sprintf(p, "FIFO: "); 1312 p += strlen(p); 1313 goto defprint; 1314 #endif 1315 1316 #ifdef S_IFDIR 1317 case S_IFDIR: 1318 sprintf(p, "DIR: "); 1319 p += strlen(p); 1320 goto defprint; 1321 #endif 1322 1323 #ifdef S_IFLNK 1324 case S_IFLNK: 1325 sprintf(p, "LNK: "); 1326 p += strlen(p); 1327 goto defprint; 1328 #endif 1329 1330 default: 1331 defprint: 1332 if (sizeof st.st_size > 4) 1333 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd", 1334 else 1335 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld", 1336 sprintf(p, fmtstr, 1337 major(st.st_dev), minor(st.st_dev), st.st_ino, 1338 st.st_nlink, st.st_uid, st.st_gid, st.st_size); 1339 break; 1340 } 1341 1342 printit: 1343 #ifdef LOG 1344 if (logit) 1345 syslog(LOG_DEBUG, "%s", buf); 1346 else 1347 #endif 1348 printf("%s\n", buf); 1349 } 1350 /* 1351 ** SHORTENSTRING -- return short version of a string 1352 ** 1353 ** If the string is already short, just return it. If it is too 1354 ** long, return the head and tail of the string. 1355 ** 1356 ** Parameters: 1357 ** s -- the string to shorten. 1358 ** m -- the max length of the string. 1359 ** 1360 ** Returns: 1361 ** Either s or a short version of s. 1362 */ 1363 1364 #ifndef MAXSHORTSTR 1365 # define MAXSHORTSTR 203 1366 #endif 1367 1368 char * 1369 shortenstring(s, m) 1370 register char *s; 1371 int m; 1372 { 1373 int l; 1374 static char buf[MAXSHORTSTR + 1]; 1375 1376 l = strlen(s); 1377 if (l < m) 1378 return s; 1379 if (m > MAXSHORTSTR) 1380 m = MAXSHORTSTR; 1381 else if (m < 10) 1382 { 1383 if (m < 5) 1384 { 1385 strncpy(buf, s, m); 1386 buf[m] = '\0'; 1387 return buf; 1388 } 1389 strncpy(buf, s, m - 3); 1390 strcpy(buf + m - 3, "..."); 1391 return buf; 1392 } 1393 m = (m - 3) / 2; 1394 strncpy(buf, s, m); 1395 strcpy(buf + m, "..."); 1396 strcpy(buf + m + 3, s + l - m); 1397 return buf; 1398 } 1399