|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1983 Eric P. Allman ! 3: * Copyright (c) 1988 Regents of the University of California. ! 4: * All rights reserved. ! 5: * ! 6: * Redistribution and use in source and binary forms are permitted provided ! 7: * that: (1) source distributions retain this entire copyright notice and ! 8: * comment, and (2) distributions including binaries display the following ! 9: * acknowledgement: ``This product includes software developed by the ! 10: * University of California, Berkeley and its contributors'' in the ! 11: * documentation or other materials provided with the distribution and in ! 12: * all advertising materials mentioning features or use of this software. ! 13: * Neither the name of the University nor the names of its contributors may ! 14: * be used to endorse or promote products derived from this software without ! 15: * specific prior written permission. ! 16: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED ! 17: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF ! 18: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 19: */ ! 20: ! 21: #ifndef lint ! 22: static char sccsid[] = "@(#)util.c 5.18 (Berkeley) 6/1/90"; ! 23: #endif /* not lint */ ! 24: ! 25: # include <stdio.h> ! 26: # include <sys/types.h> ! 27: # include <sys/stat.h> ! 28: # include <sysexits.h> ! 29: # include <errno.h> ! 30: # include "sendmail.h" ! 31: ! 32: /* ! 33: ** STRIPQUOTES -- Strip quotes & quote bits from a string. ! 34: ** ! 35: ** Runs through a string and strips off unquoted quote ! 36: ** characters and quote bits. This is done in place. ! 37: ** ! 38: ** Parameters: ! 39: ** s -- the string to strip. ! 40: ** qf -- if set, remove actual `` " '' characters ! 41: ** as well as the quote bits. ! 42: ** ! 43: ** Returns: ! 44: ** none. ! 45: ** ! 46: ** Side Effects: ! 47: ** none. ! 48: ** ! 49: ** Called By: ! 50: ** deliver ! 51: */ ! 52: ! 53: stripquotes(s, qf) ! 54: char *s; ! 55: bool qf; ! 56: { ! 57: register char *p; ! 58: register char *q; ! 59: register char c; ! 60: ! 61: if (s == NULL) ! 62: return; ! 63: ! 64: for (p = q = s; (c = *p++) != '\0'; ) ! 65: { ! 66: if (c != '"' || !qf) ! 67: *q++ = c & 0177; ! 68: } ! 69: *q = '\0'; ! 70: } ! 71: /* ! 72: ** QSTRLEN -- give me the string length assuming 0200 bits add a char ! 73: ** ! 74: ** Parameters: ! 75: ** s -- the string to measure. ! 76: ** ! 77: ** Reurns: ! 78: ** The length of s, including space for backslash escapes. ! 79: ** ! 80: ** Side Effects: ! 81: ** none. ! 82: */ ! 83: ! 84: qstrlen(s) ! 85: register char *s; ! 86: { ! 87: register int l = 0; ! 88: register char c; ! 89: ! 90: while ((c = *s++) != '\0') ! 91: { ! 92: if (bitset(0200, c)) ! 93: l++; ! 94: l++; ! 95: } ! 96: return (l); ! 97: } ! 98: /* ! 99: ** CAPITALIZE -- return a copy of a string, properly capitalized. ! 100: ** ! 101: ** Parameters: ! 102: ** s -- the string to capitalize. ! 103: ** ! 104: ** Returns: ! 105: ** a pointer to a properly capitalized string. ! 106: ** ! 107: ** Side Effects: ! 108: ** none. ! 109: */ ! 110: ! 111: char * ! 112: capitalize(s) ! 113: register char *s; ! 114: { ! 115: static char buf[50]; ! 116: register char *p; ! 117: ! 118: p = buf; ! 119: ! 120: for (;;) ! 121: { ! 122: while (!isalpha(*s) && *s != '\0') ! 123: *p++ = *s++; ! 124: if (*s == '\0') ! 125: break; ! 126: *p++ = toupper(*s); ! 127: s++; ! 128: while (isalpha(*s)) ! 129: *p++ = *s++; ! 130: } ! 131: ! 132: *p = '\0'; ! 133: return (buf); ! 134: } ! 135: /* ! 136: ** XALLOC -- Allocate memory and bitch wildly on failure. ! 137: ** ! 138: ** THIS IS A CLUDGE. This should be made to give a proper ! 139: ** error -- but after all, what can we do? ! 140: ** ! 141: ** Parameters: ! 142: ** sz -- size of area to allocate. ! 143: ** ! 144: ** Returns: ! 145: ** pointer to data region. ! 146: ** ! 147: ** Side Effects: ! 148: ** Memory is allocated. ! 149: */ ! 150: ! 151: char * ! 152: xalloc(sz) ! 153: register int sz; ! 154: { ! 155: register char *p; ! 156: extern char *malloc(); ! 157: ! 158: p = malloc((unsigned) sz); ! 159: if (p == NULL) ! 160: { ! 161: syserr("Out of memory!!"); ! 162: abort(); ! 163: /* exit(EX_UNAVAILABLE); */ ! 164: } ! 165: return (p); ! 166: } ! 167: /* ! 168: ** COPYPLIST -- copy list of pointers. ! 169: ** ! 170: ** This routine is the equivalent of newstr for lists of ! 171: ** pointers. ! 172: ** ! 173: ** Parameters: ! 174: ** list -- list of pointers to copy. ! 175: ** Must be NULL terminated. ! 176: ** copycont -- if TRUE, copy the contents of the vector ! 177: ** (which must be a string) also. ! 178: ** ! 179: ** Returns: ! 180: ** a copy of 'list'. ! 181: ** ! 182: ** Side Effects: ! 183: ** none. ! 184: */ ! 185: ! 186: char ** ! 187: copyplist(list, copycont) ! 188: char **list; ! 189: bool copycont; ! 190: { ! 191: register char **vp; ! 192: register char **newvp; ! 193: ! 194: for (vp = list; *vp != NULL; vp++) ! 195: continue; ! 196: ! 197: vp++; ! 198: ! 199: newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); ! 200: bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); ! 201: ! 202: if (copycont) ! 203: { ! 204: for (vp = newvp; *vp != NULL; vp++) ! 205: *vp = newstr(*vp); ! 206: } ! 207: ! 208: return (newvp); ! 209: } ! 210: /* ! 211: ** PRINTAV -- print argument vector. ! 212: ** ! 213: ** Parameters: ! 214: ** av -- argument vector. ! 215: ** ! 216: ** Returns: ! 217: ** none. ! 218: ** ! 219: ** Side Effects: ! 220: ** prints av. ! 221: */ ! 222: ! 223: printav(av) ! 224: register char **av; ! 225: { ! 226: while (*av != NULL) ! 227: { ! 228: if (tTd(0, 44)) ! 229: printf("\n\t%08x=", *av); ! 230: else ! 231: (void) putchar(' '); ! 232: xputs(*av++); ! 233: } ! 234: (void) putchar('\n'); ! 235: } ! 236: /* ! 237: ** LOWER -- turn letter into lower case. ! 238: ** ! 239: ** Parameters: ! 240: ** c -- character to turn into lower case. ! 241: ** ! 242: ** Returns: ! 243: ** c, in lower case. ! 244: ** ! 245: ** Side Effects: ! 246: ** none. ! 247: */ ! 248: ! 249: char ! 250: lower(c) ! 251: register char c; ! 252: { ! 253: return(isascii(c) && isupper(c) ? tolower(c) : c); ! 254: } ! 255: /* ! 256: ** XPUTS -- put string doing control escapes. ! 257: ** ! 258: ** Parameters: ! 259: ** s -- string to put. ! 260: ** ! 261: ** Returns: ! 262: ** none. ! 263: ** ! 264: ** Side Effects: ! 265: ** output to stdout ! 266: */ ! 267: ! 268: xputs(s) ! 269: register char *s; ! 270: { ! 271: register char c; ! 272: ! 273: if (s == NULL) ! 274: { ! 275: printf("<null>"); ! 276: return; ! 277: } ! 278: (void) putchar('"'); ! 279: while ((c = *s++) != '\0') ! 280: { ! 281: if (!isascii(c)) ! 282: { ! 283: (void) putchar('\\'); ! 284: c &= 0177; ! 285: } ! 286: if (c < 040 || c >= 0177) ! 287: { ! 288: (void) putchar('^'); ! 289: c ^= 0100; ! 290: } ! 291: (void) putchar(c); ! 292: } ! 293: (void) putchar('"'); ! 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(p, login, buf) ! 343: register char *p; ! 344: char *login; ! 345: char *buf; ! 346: { ! 347: register char *bp = buf; ! 348: ! 349: if (*p == '*') ! 350: p++; ! 351: while (*p != '\0' && *p != ',' && *p != ';' && *p != '%') ! 352: { ! 353: if (*p == '&') ! 354: { ! 355: (void) strcpy(bp, login); ! 356: *bp = toupper(*bp); ! 357: while (*bp != '\0') ! 358: bp++; ! 359: p++; ! 360: } ! 361: else ! 362: *bp++ = *p++; ! 363: } ! 364: *bp = '\0'; ! 365: } ! 366: /* ! 367: ** SAFEFILE -- return true if a file exists and is safe for a user. ! 368: ** ! 369: ** Parameters: ! 370: ** fn -- filename to check. ! 371: ** uid -- uid to compare against. ! 372: ** mode -- mode bits that must match. ! 373: ** ! 374: ** Returns: ! 375: ** TRUE if fn exists, is owned by uid, and matches mode. ! 376: ** FALSE otherwise. ! 377: ** ! 378: ** Side Effects: ! 379: ** none. ! 380: */ ! 381: ! 382: bool ! 383: safefile(fn, uid, mode) ! 384: char *fn; ! 385: int uid; ! 386: int mode; ! 387: { ! 388: struct stat stbuf; ! 389: ! 390: if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid && ! 391: (stbuf.st_mode & mode) == mode) ! 392: return (TRUE); ! 393: errno = 0; ! 394: return (FALSE); ! 395: } ! 396: /* ! 397: ** FIXCRLF -- fix <CR><LF> in line. ! 398: ** ! 399: ** Looks for the <CR><LF> combination and turns it into the ! 400: ** UNIX canonical <NL> character. It only takes one line, ! 401: ** i.e., it is assumed that the first <NL> found is the end ! 402: ** of the line. ! 403: ** ! 404: ** Parameters: ! 405: ** line -- the line to fix. ! 406: ** stripnl -- if true, strip the newline also. ! 407: ** ! 408: ** Returns: ! 409: ** none. ! 410: ** ! 411: ** Side Effects: ! 412: ** line is changed in place. ! 413: */ ! 414: ! 415: fixcrlf(line, stripnl) ! 416: char *line; ! 417: bool stripnl; ! 418: { ! 419: register char *p; ! 420: ! 421: p = index(line, '\n'); ! 422: if (p == NULL) ! 423: return; ! 424: if (p > line && p[-1] == '\r') ! 425: p--; ! 426: if (!stripnl) ! 427: *p++ = '\n'; ! 428: *p = '\0'; ! 429: } ! 430: /* ! 431: ** DFOPEN -- determined file open ! 432: ** ! 433: ** This routine has the semantics of fopen, except that it will ! 434: ** keep trying a few times to make this happen. The idea is that ! 435: ** on very loaded systems, we may run out of resources (inodes, ! 436: ** whatever), so this tries to get around it. ! 437: */ ! 438: ! 439: FILE * ! 440: dfopen(filename, mode) ! 441: char *filename; ! 442: char *mode; ! 443: { ! 444: register int tries; ! 445: register FILE *fp; ! 446: ! 447: for (tries = 0; tries < 10; tries++) ! 448: { ! 449: sleep((unsigned) (10 * tries)); ! 450: errno = 0; ! 451: fp = fopen(filename, mode); ! 452: if (fp != NULL) ! 453: break; ! 454: if (errno != ENFILE && errno != EINTR) ! 455: break; ! 456: } ! 457: errno = 0; ! 458: return (fp); ! 459: } ! 460: /* ! 461: ** PUTLINE -- put a line like fputs obeying SMTP conventions ! 462: ** ! 463: ** This routine always guarantees outputing a newline (or CRLF, ! 464: ** as appropriate) at the end of the string. ! 465: ** ! 466: ** Parameters: ! 467: ** l -- line to put. ! 468: ** fp -- file to put it onto. ! 469: ** m -- the mailer used to control output. ! 470: ** ! 471: ** Returns: ! 472: ** none ! 473: ** ! 474: ** Side Effects: ! 475: ** output of l to fp. ! 476: */ ! 477: ! 478: # define SMTPLINELIM 990 /* maximum line length */ ! 479: ! 480: putline(l, fp, m) ! 481: register char *l; ! 482: FILE *fp; ! 483: MAILER *m; ! 484: { ! 485: register char *p; ! 486: char svchar; ! 487: ! 488: /* strip out 0200 bits -- these can look like TELNET protocol */ ! 489: if (bitnset(M_LIMITS, m->m_flags)) ! 490: { ! 491: p = l; ! 492: while ((*p++ &= ~0200) != 0) ! 493: continue; ! 494: } ! 495: ! 496: do ! 497: { ! 498: /* find the end of the line */ ! 499: p = index(l, '\n'); ! 500: if (p == NULL) ! 501: p = &l[strlen(l)]; ! 502: ! 503: /* check for line overflow */ ! 504: while ((p - l) > SMTPLINELIM && bitnset(M_LIMITS, m->m_flags)) ! 505: { ! 506: register char *q = &l[SMTPLINELIM - 1]; ! 507: ! 508: svchar = *q; ! 509: *q = '\0'; ! 510: if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) ! 511: (void) putc('.', fp); ! 512: fputs(l, fp); ! 513: (void) putc('!', fp); ! 514: fputs(m->m_eol, fp); ! 515: *q = svchar; ! 516: l = q; ! 517: } ! 518: ! 519: /* output last part */ ! 520: svchar = *p; ! 521: *p = '\0'; ! 522: if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) ! 523: (void) putc('.', fp); ! 524: fputs(l, fp); ! 525: fputs(m->m_eol, fp); ! 526: *p = svchar; ! 527: l = p; ! 528: if (*l == '\n') ! 529: l++; ! 530: } while (l[0] != '\0'); ! 531: } ! 532: /* ! 533: ** XUNLINK -- unlink a file, doing logging as appropriate. ! 534: ** ! 535: ** Parameters: ! 536: ** f -- name of file to unlink. ! 537: ** ! 538: ** Returns: ! 539: ** none. ! 540: ** ! 541: ** Side Effects: ! 542: ** f is unlinked. ! 543: */ ! 544: ! 545: xunlink(f) ! 546: char *f; ! 547: { ! 548: register int i; ! 549: ! 550: # ifdef LOG ! 551: if (LogLevel > 20) ! 552: syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f); ! 553: # endif LOG ! 554: ! 555: i = unlink(f); ! 556: # ifdef LOG ! 557: if (i < 0 && LogLevel > 21) ! 558: syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); ! 559: # endif LOG ! 560: } ! 561: /* ! 562: ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. ! 563: ** ! 564: ** Parameters: ! 565: ** buf -- place to put the input line. ! 566: ** siz -- size of buf. ! 567: ** fp -- file to read from. ! 568: ** ! 569: ** Returns: ! 570: ** NULL on error (including timeout). This will also leave ! 571: ** buf containing a null string. ! 572: ** buf otherwise. ! 573: ** ! 574: ** Side Effects: ! 575: ** none. ! 576: */ ! 577: ! 578: static jmp_buf CtxReadTimeout; ! 579: ! 580: char * ! 581: sfgets(buf, siz, fp) ! 582: char *buf; ! 583: int siz; ! 584: FILE *fp; ! 585: { ! 586: register EVENT *ev = NULL; ! 587: register char *p; ! 588: extern readtimeout(); ! 589: ! 590: /* set the timeout */ ! 591: if (ReadTimeout != 0) ! 592: { ! 593: if (setjmp(CtxReadTimeout) != 0) ! 594: { ! 595: # ifdef LOG ! 596: syslog(LOG_NOTICE, ! 597: "timeout waiting for input from %s\n", ! 598: RealHostName? RealHostName: "local"); ! 599: # endif ! 600: errno = 0; ! 601: usrerr("451 timeout waiting for input"); ! 602: buf[0] = '\0'; ! 603: return (NULL); ! 604: } ! 605: ev = setevent((time_t) ReadTimeout, readtimeout, 0); ! 606: } ! 607: ! 608: /* try to read */ ! 609: p = NULL; ! 610: while (p == NULL && !feof(fp) && !ferror(fp)) ! 611: { ! 612: errno = 0; ! 613: p = fgets(buf, siz, fp); ! 614: if (errno == EINTR) ! 615: clearerr(fp); ! 616: } ! 617: ! 618: /* clear the event if it has not sprung */ ! 619: clrevent(ev); ! 620: ! 621: /* clean up the books and exit */ ! 622: LineNumber++; ! 623: if (p == NULL) ! 624: { ! 625: buf[0] = '\0'; ! 626: return (NULL); ! 627: } ! 628: for (p = buf; *p != '\0'; p++) ! 629: *p &= ~0200; ! 630: return (buf); ! 631: } ! 632: ! 633: static ! 634: readtimeout() ! 635: { ! 636: longjmp(CtxReadTimeout, 1); ! 637: } ! 638: /* ! 639: ** FGETFOLDED -- like fgets, but know about folded lines. ! 640: ** ! 641: ** Parameters: ! 642: ** buf -- place to put result. ! 643: ** n -- bytes available. ! 644: ** f -- file to read from. ! 645: ** ! 646: ** Returns: ! 647: ** buf on success, NULL on error or EOF. ! 648: ** ! 649: ** Side Effects: ! 650: ** buf gets lines from f, with continuation lines (lines ! 651: ** with leading white space) appended. CRLF's are mapped ! 652: ** into single newlines. Any trailing NL is stripped. ! 653: */ ! 654: ! 655: char * ! 656: fgetfolded(buf, n, f) ! 657: char *buf; ! 658: register int n; ! 659: FILE *f; ! 660: { ! 661: register char *p = buf; ! 662: register int i; ! 663: ! 664: n--; ! 665: while ((i = getc(f)) != EOF) ! 666: { ! 667: if (i == '\r') ! 668: { ! 669: i = getc(f); ! 670: if (i != '\n') ! 671: { ! 672: if (i != EOF) ! 673: (void) ungetc(i, f); ! 674: i = '\r'; ! 675: } ! 676: } ! 677: if (--n > 0) ! 678: *p++ = i; ! 679: if (i == '\n') ! 680: { ! 681: LineNumber++; ! 682: i = getc(f); ! 683: if (i != EOF) ! 684: (void) ungetc(i, f); ! 685: if (i != ' ' && i != '\t') ! 686: { ! 687: *--p = '\0'; ! 688: return (buf); ! 689: } ! 690: } ! 691: } ! 692: return (NULL); ! 693: } ! 694: /* ! 695: ** CURTIME -- return current time. ! 696: ** ! 697: ** Parameters: ! 698: ** none. ! 699: ** ! 700: ** Returns: ! 701: ** the current time. ! 702: ** ! 703: ** Side Effects: ! 704: ** none. ! 705: */ ! 706: ! 707: time_t ! 708: curtime() ! 709: { ! 710: auto time_t t; ! 711: ! 712: (void) time(&t); ! 713: return (t); ! 714: } ! 715: /* ! 716: ** ATOBOOL -- convert a string representation to boolean. ! 717: ** ! 718: ** Defaults to "TRUE" ! 719: ** ! 720: ** Parameters: ! 721: ** s -- string to convert. Takes "tTyY" as true, ! 722: ** others as false. ! 723: ** ! 724: ** Returns: ! 725: ** A boolean representation of the string. ! 726: ** ! 727: ** Side Effects: ! 728: ** none. ! 729: */ ! 730: ! 731: bool ! 732: atobool(s) ! 733: register char *s; ! 734: { ! 735: if (*s == '\0' || index("tTyY", *s) != NULL) ! 736: return (TRUE); ! 737: return (FALSE); ! 738: } ! 739: /* ! 740: ** ATOOCT -- convert a string representation to octal. ! 741: ** ! 742: ** Parameters: ! 743: ** s -- string to convert. ! 744: ** ! 745: ** Returns: ! 746: ** An integer representing the string interpreted as an ! 747: ** octal number. ! 748: ** ! 749: ** Side Effects: ! 750: ** none. ! 751: */ ! 752: ! 753: atooct(s) ! 754: register char *s; ! 755: { ! 756: register int i = 0; ! 757: ! 758: while (*s >= '0' && *s <= '7') ! 759: i = (i << 3) | (*s++ - '0'); ! 760: return (i); ! 761: } ! 762: /* ! 763: ** WAITFOR -- wait for a particular process id. ! 764: ** ! 765: ** Parameters: ! 766: ** pid -- process id to wait for. ! 767: ** ! 768: ** Returns: ! 769: ** status of pid. ! 770: ** -1 if pid never shows up. ! 771: ** ! 772: ** Side Effects: ! 773: ** none. ! 774: */ ! 775: ! 776: waitfor(pid) ! 777: int pid; ! 778: { ! 779: auto int st; ! 780: int i; ! 781: ! 782: do ! 783: { ! 784: errno = 0; ! 785: i = wait(&st); ! 786: } while ((i >= 0 || errno == EINTR) && i != pid); ! 787: if (i < 0) ! 788: st = -1; ! 789: return (st); ! 790: } ! 791: /* ! 792: ** BITINTERSECT -- tell if two bitmaps intersect ! 793: ** ! 794: ** Parameters: ! 795: ** a, b -- the bitmaps in question ! 796: ** ! 797: ** Returns: ! 798: ** TRUE if they have a non-null intersection ! 799: ** FALSE otherwise ! 800: ** ! 801: ** Side Effects: ! 802: ** none. ! 803: */ ! 804: ! 805: bool ! 806: bitintersect(a, b) ! 807: BITMAP a; ! 808: BITMAP b; ! 809: { ! 810: int i; ! 811: ! 812: for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) ! 813: if ((a[i] & b[i]) != 0) ! 814: return (TRUE); ! 815: return (FALSE); ! 816: } ! 817: /* ! 818: ** BITZEROP -- tell if a bitmap is all zero ! 819: ** ! 820: ** Parameters: ! 821: ** map -- the bit map to check ! 822: ** ! 823: ** Returns: ! 824: ** TRUE if map is all zero. ! 825: ** FALSE if there are any bits set in map. ! 826: ** ! 827: ** Side Effects: ! 828: ** none. ! 829: */ ! 830: ! 831: bool ! 832: bitzerop(map) ! 833: BITMAP map; ! 834: { ! 835: int i; ! 836: ! 837: for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) ! 838: if (map[i] != 0) ! 839: return (FALSE); ! 840: return (TRUE); ! 841: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.