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