int test_base_bitset(const char* param) { rand_seed((uint32_t)time(NULL)); // bit create int size = param ? atoi(param) : rand() % 1024; bit_t* bit = bit_create(size); if (!bit) { fprintf(stderr, "bit create fail\n"); return -1; } // bit set int test_size = (size >> 1); for (int i = 0; i < test_size; ++ i) { bit_set(bit, i); } // bit is set for (int i = 0; i < size; ++ i) { if (i < test_size && bit_isset(bit, i)) { fprintf(stderr, "bit-set error\n"); bit_release(bit); return -1; } if (i >= test_size && bit_isset(bit, i) == 0) { fprintf(stderr, "bit-set error\n"); bit_release(bit); return -1; } } // bit count if (bit_count(bit) != test_size) { fprintf(stderr, "bit-count = %d error\n", bit_count(bit)); bit_release(bit); return -1; } // bit reset -> bit count for (int i = 0; i < test_size; ++ i) { bit_reset(bit, i); } if (bit_count(bit) != 0) { fprintf(stderr, "bit-count error\n"); bit_release(bit); return -1; } bit_release(bit); return 0; }
isc_boolean_t dns_nsec_typepresent(dns_rdata_t *nsec, dns_rdatatype_t type) { dns_rdata_nsec_t nsecstruct; isc_result_t result; isc_boolean_t present; unsigned int i, len, window; REQUIRE(nsec != NULL); REQUIRE(nsec->type == dns_rdatatype_nsec); /* This should never fail */ result = dns_rdata_tostruct(nsec, &nsecstruct, NULL); INSIST(result == ISC_R_SUCCESS); present = ISC_FALSE; for (i = 0; i < nsecstruct.len; i += len) { INSIST(i + 2 <= nsecstruct.len); window = nsecstruct.typebits[i]; len = nsecstruct.typebits[i + 1]; INSIST(len > 0 && len <= 32); i += 2; INSIST(i + len <= nsecstruct.len); if (window * 256 > type) break; if ((window + 1) * 256 <= type) continue; if (type < (window * 256) + len * 8) present = ISC_TF(bit_isset(&nsecstruct.typebits[i], type % 256)); break; } dns_rdata_freestruct(&nsec); return (present); }
static int nextbit(bitmap_t map, int bit) /* Return the next bit set in 'map' from 'bit' onwards, cyclic. */ { for (;;) { bit= (bit+1) & 63; if (bit_isset(map, bit)) break; } return bit; }
void tab_parse(char *file, char *user) /* Parse a crontab file and add its data to the tables. Handle errors by * yourself. Table is owned by 'user' if non-null. */ { crontab_t **atab, *tab; cronjob_t **ajob, *job; int fd; struct stat st; char *p, *q; size_t n; ssize_t r; int ok, wc; for (atab= &crontabs; (tab= *atab) != nil; atab= &tab->next) { if (strcmp(file, tab->file) == 0) break; } /* Try to open the file. */ if ((fd= open(file, O_RDONLY)) < 0 || fstat(fd, &st) < 0) { if (errno != ENOENT) { log(LOG_ERR, "%s: %s\n", file, strerror(errno)); } if (fd != -1) close(fd); return; } /* Forget it if the file is awfully big. */ if (st.st_size > TAB_MAX) { log(LOG_ERR, "%s: %lu bytes is bigger than my %lu limit\n", file, (unsigned long) st.st_size, (unsigned long) TAB_MAX); return; } /* If the file is the same as before then don't bother. */ if (tab != nil && st.st_mtime == tab->mtime) { close(fd); tab->current= 1; return; } /* Create a new table structure. */ tab= allocate(sizeof(*tab)); tab->file= allocate((strlen(file) + 1) * sizeof(tab->file[0])); strcpy(tab->file, file); tab->user= nil; if (user != nil) { tab->user= allocate((strlen(user) + 1) * sizeof(tab->user[0])); strcpy(tab->user, user); } tab->data= allocate((st.st_size + 1) * sizeof(tab->data[0])); tab->jobs= nil; tab->mtime= st.st_mtime; tab->current= 0; tab->next= *atab; *atab= tab; /* Pull a new table in core. */ n= 0; while (n < st.st_size) { if ((r = read(fd, tab->data + n, st.st_size - n)) < 0) { log(LOG_CRIT, "%s: %s", file, strerror(errno)); close(fd); return; } if (r == 0) break; n+= r; } close(fd); tab->data[n]= 0; if (strlen(tab->data) < n) { log(LOG_ERR, "%s contains a null character\n", file); return; } /* Parse the file. */ ajob= &tab->jobs; p= tab->data; ok= 1; while (ok && *p != 0) { q= get_token(&p); if (*q == '#' || q == p) { /* Comment or empty. */ while (*p != 0 && *p++ != '\n') {} continue; } /* One new job coming up. */ *ajob= job= allocate(sizeof(*job)); *(ajob= &job->next)= nil; job->tab= tab; if (!range_parse(file, q, job->min, 0, 59, &wc)) { ok= 0; break; } q= get_token(&p); if (!range_parse(file, q, job->hour, 0, 23, &wc)) { ok= 0; break; } q= get_token(&p); if (!range_parse(file, q, job->mday, 1, 31, &wc)) { ok= 0; break; } job->do_mday= !wc; q= get_token(&p); if (!range_parse(file, q, job->mon, 1, 12, &wc)) { ok= 0; break; } job->do_mday |= !wc; q= get_token(&p); if (!range_parse(file, q, job->wday, 0, 7, &wc)) { ok= 0; break; } job->do_wday= !wc; /* 7 is Sunday, but 0 is a common mistake because it is in the * tm_wday range. We allow and even prefer it internally. */ if (bit_isset(job->wday, 7)) { bit_clr(job->wday, 7); bit_set(job->wday, 0); } /* The month range is 1-12, but tm_mon likes 0-11. */ job->mon[0] >>= 1; if (bit_isset(job->mon, 8)) bit_set(job->mon, 7); job->mon[1] >>= 1; /* Scan for options. */ job->user= nil; while (q= get_token(&p), *q == '-') { q++; if (q[0] == '-' && q+1 == p) { /* -- */ q= get_token(&p); break; } while (q < p) switch (*q++) { case 'u': if (q == p) q= get_token(&p); if (q == p) goto usage; memmove(q-1, q, p-q); /* gross... */ p[-1]= 0; job->user= q-1; q= p; break; default: usage: log(LOG_ERR, "%s: bad option -%c, good options are: -u username\n", file, q[-1]); ok= 0; goto endtab; } } /* A crontab owned by a user can only do things as that user. */ if (tab->user != nil) job->user= tab->user; /* Inspect the first character of the command. */ job->cmd= q; if (q == p || *q == '#') { /* Rest of the line is empty, i.e. the commands are on * the following lines indented by one tab. */ while (*p != 0 && *p++ != '\n') {} if (*p++ != '\t') { log(LOG_ERR, "%s: contains an empty command\n", file); ok= 0; goto endtab; } while (*p != 0) { if ((*q = *p++) == '\n') { if (*p != '\t') break; p++; } q++; } } else { /* The command is on this line. Alas we must now be * backwards compatible and change %'s to newlines. */ p= q; while (*p != 0) { if ((*q = *p++) == '\n') break; if (*q == '%') *q= '\n'; q++; } } *q= 0; job->rtime= now; job->late= 0; /* It is on time. */ job->atjob= 0; /* True cron job. */ job->pid= IDLE_PID; /* Not running yet. */ tab_reschedule(job); /* Compute next time to run. */ } endtab: if (ok) tab->current= 1; }
void tab_reschedule(cronjob_t *job) /* Reschedule one job. Compute the next time to run the job in job->rtime. */ { struct tm prevtm, nexttm, tmptm; time_t nodst_rtime, dst_rtime; /* AT jobs are only run once. */ if (job->atjob) { job->rtime= NEVER; return; } /* Was the job scheduled late last time? */ if (job->late) job->rtime= now; prevtm= *localtime(&job->rtime); prevtm.tm_sec= 0; nexttm= prevtm; nexttm.tm_min++; /* Minimal increment */ for (;;) { if (nexttm.tm_min > 59) { nexttm.tm_min= 0; nexttm.tm_hour++; } if (nexttm.tm_hour > 23) { nexttm.tm_min= 0; nexttm.tm_hour= 0; nexttm.tm_mday++; } if (nexttm.tm_mday > 31) { nexttm.tm_hour= nexttm.tm_min= 0; nexttm.tm_mday= 1; nexttm.tm_mon++; } if (nexttm.tm_mon >= 12) { nexttm.tm_hour= nexttm.tm_min= 0; nexttm.tm_mday= 1; nexttm.tm_mon= 0; nexttm.tm_year++; } /* Verify tm_year. A crontab entry cannot restrict tm_year * directly. However, certain dates (such as Feb, 29th) do * not occur every year. We limit the difference between * nexttm.tm_year and prevtm.tm_year to detect impossible dates * (e.g, Feb, 31st). In theory every date occurs within a * period of 4 years. However, some years at the end of a * century are not leap years (e.g, the year 2100). An extra * factor of 2 should be enough. */ if (nexttm.tm_year-prevtm.tm_year > 2*4) { job->rtime= NEVER; return; /* Impossible combination */ } if (!job->do_wday) { /* Verify the mon and mday fields. If do_wday and * do_mday are both true we have to merge both * schedules. This is done after the call to mktime. */ if (!bit_isset(job->mon, nexttm.tm_mon)) { /* Clear other fields */ nexttm.tm_mday= 1; nexttm.tm_hour= nexttm.tm_min= 0; nexttm.tm_mon++; continue; } /* Verify mday */ if (!bit_isset(job->mday, nexttm.tm_mday)) { /* Clear other fields */ nexttm.tm_hour= nexttm.tm_min= 0; nexttm.tm_mday++; continue; } } /* Verify hour */ if (!bit_isset(job->hour, nexttm.tm_hour)) { /* Clear tm_min field */ nexttm.tm_min= 0; nexttm.tm_hour++; continue; } /* Verify min */ if (!bit_isset(job->min, nexttm.tm_min)) { nexttm.tm_min++; continue; } /* Verify that we don't have any problem with DST. Try * tm_isdst=0 first. */ tmptm= nexttm; tmptm.tm_isdst= 0; #if 0 fprintf(stderr, "tab_reschedule: trying %04d-%02d-%02d %02d:%02d:%02d isdst=0\n", 1900+nexttm.tm_year, nexttm.tm_mon+1, nexttm.tm_mday, nexttm.tm_hour, nexttm.tm_min, nexttm.tm_sec); #endif nodst_rtime= job->rtime= mktime(&tmptm); if (job->rtime == -1) { /* This should not happen. */ log(LOG_ERR, "mktime failed for %04d-%02d-%02d %02d:%02d:%02d", 1900+nexttm.tm_year, nexttm.tm_mon+1, nexttm.tm_mday, nexttm.tm_hour, nexttm.tm_min, nexttm.tm_sec); job->rtime= NEVER; return; } if (tmptm.tm_hour != nexttm.tm_hour || tmptm.tm_min != nexttm.tm_min) { assert(tmptm.tm_isdst); tmptm= nexttm; tmptm.tm_isdst= 1; #if 0 fprintf(stderr, "tab_reschedule: trying %04d-%02d-%02d %02d:%02d:%02d isdst=1\n", 1900+nexttm.tm_year, nexttm.tm_mon+1, nexttm.tm_mday, nexttm.tm_hour, nexttm.tm_min, nexttm.tm_sec); #endif dst_rtime= job->rtime= mktime(&tmptm); if (job->rtime == -1) { /* This should not happen. */ log(LOG_ERR, "mktime failed for %04d-%02d-%02d %02d:%02d:%02d\n", 1900+nexttm.tm_year, nexttm.tm_mon+1, nexttm.tm_mday, nexttm.tm_hour, nexttm.tm_min, nexttm.tm_sec); job->rtime= NEVER; return; } if (tmptm.tm_hour != nexttm.tm_hour || tmptm.tm_min != nexttm.tm_min) { assert(!tmptm.tm_isdst); /* We have a problem. This time neither * exists with DST nor without DST. * Use the latest time, which should be * nodst_rtime. */ assert(nodst_rtime > dst_rtime); job->rtime= nodst_rtime; #if 0 fprintf(stderr, "During DST trans. %04d-%02d-%02d %02d:%02d:%02d\n", 1900+nexttm.tm_year, nexttm.tm_mon+1, nexttm.tm_mday, nexttm.tm_hour, nexttm.tm_min, nexttm.tm_sec); #endif } } /* Verify this the combination (tm_year, tm_mon, tm_mday). */ if (tmptm.tm_mday != nexttm.tm_mday || tmptm.tm_mon != nexttm.tm_mon || tmptm.tm_year != nexttm.tm_year) { /* Wrong day */ #if 0 fprintf(stderr, "Wrong day\n"); #endif nexttm.tm_hour= nexttm.tm_min= 0; nexttm.tm_mday++; continue; } /* Check tm_wday */ if (job->do_wday && bit_isset(job->wday, tmptm.tm_wday)) { /* OK, wday matched */ break; } /* Check tm_mday */ if (job->do_mday && bit_isset(job->mon, tmptm.tm_mon) && bit_isset(job->mday, tmptm.tm_mday)) { /* OK, mon and mday matched */ break; } if (!job->do_wday && !job->do_mday) { /* No need to match wday and mday */ break; } /* Wrong day */ #if 0 fprintf(stderr, "Wrong mon+mday or wday\n"); #endif nexttm.tm_hour= nexttm.tm_min= 0; nexttm.tm_mday++; } #if 0 fprintf(stderr, "Using %04d-%02d-%02d %02d:%02d:%02d \n", 1900+nexttm.tm_year, nexttm.tm_mon+1, nexttm.tm_mday, nexttm.tm_hour, nexttm.tm_min, nexttm.tm_sec); tmptm= *localtime(&job->rtime); fprintf(stderr, "Act. %04d-%02d-%02d %02d:%02d:%02d isdst=%d\n", 1900+tmptm.tm_year, tmptm.tm_mon+1, tmptm.tm_mday, tmptm.tm_hour, tmptm.tm_min, tmptm.tm_sec, tmptm.tm_isdst); #endif /* Is job issuing lagging behind with the progress of time? */ job->late= (job->rtime < now); /* The result is in job->rtime. */ if (job->rtime < next) next= job->rtime; }
isc_result_t dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node, dns_name_t *target, unsigned char *buffer, dns_rdata_t *rdata) { isc_result_t result; dns_rdataset_t rdataset; isc_region_t r; unsigned int i, window; int octet; unsigned char *nsec_bits, *bm; unsigned int max_type; dns_rdatasetiter_t *rdsiter; memset(buffer, 0, DNS_NSEC_BUFFERSIZE); dns_name_toregion(target, &r); memcpy(buffer, r.base, r.length); r.base = buffer; /* * Use the end of the space for a raw bitmap leaving enough * space for the window identifiers and length octets. */ bm = r.base + r.length + 512; nsec_bits = r.base + r.length; set_bit(bm, dns_rdatatype_rrsig, 1); set_bit(bm, dns_rdatatype_nsec, 1); max_type = dns_rdatatype_nsec; dns_rdataset_init(&rdataset); rdsiter = NULL; result = dns_db_allrdatasets(db, node, version, 0, &rdsiter); if (result != ISC_R_SUCCESS) return (result); for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; result = dns_rdatasetiter_next(rdsiter)) { dns_rdatasetiter_current(rdsiter, &rdataset); if (rdataset.type != dns_rdatatype_nsec && rdataset.type != dns_rdatatype_nsec3 && rdataset.type != dns_rdatatype_rrsig) { if (rdataset.type > max_type) max_type = rdataset.type; set_bit(bm, rdataset.type, 1); } dns_rdataset_disassociate(&rdataset); } /* * At zone cuts, deny the existence of glue in the parent zone. */ if (bit_isset(bm, dns_rdatatype_ns) && ! bit_isset(bm, dns_rdatatype_soa)) { for (i = 0; i <= max_type; i++) { if (bit_isset(bm, i) && ! dns_rdatatype_iszonecutauth((dns_rdatatype_t)i)) set_bit(bm, i, 0); } } dns_rdatasetiter_destroy(&rdsiter); if (result != ISC_R_NOMORE) return (result); for (window = 0; window < 256; window++) { if (window * 256 > max_type) break; for (octet = 31; octet >= 0; octet--) if (bm[window * 32 + octet] != 0) break; if (octet < 0) continue; nsec_bits[0] = window; nsec_bits[1] = octet + 1; /* * Note: potential overlapping move. */ memmove(&nsec_bits[2], &bm[window * 32], octet + 1); nsec_bits += 3 + octet; } r.length = nsec_bits - r.base; INSIST(r.length <= DNS_NSEC_BUFFERSIZE); dns_rdata_fromregion(rdata, dns_db_class(db), dns_rdatatype_nsec, &r); return (ISC_R_SUCCESS); }
isc_result_t dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node, unsigned int hashalg, unsigned int flags, unsigned int iterations, const unsigned char *salt, size_t salt_length, const unsigned char *nexthash, size_t hash_length, unsigned char *buffer, dns_rdata_t *rdata) { isc_result_t result; dns_rdataset_t rdataset; isc_region_t r; unsigned int i, window; int octet; isc_boolean_t found; isc_boolean_t found_ns; isc_boolean_t need_rrsig; unsigned char *nsec_bits, *bm; unsigned int max_type; dns_rdatasetiter_t *rdsiter; unsigned char *p; REQUIRE(salt_length < 256U); REQUIRE(hash_length < 256U); REQUIRE(flags <= 0xffU); REQUIRE(hashalg <= 0xffU); REQUIRE(iterations <= 0xffffU); switch (hashalg) { case dns_hash_sha1: REQUIRE(hash_length == ISC_SHA1_DIGESTLENGTH); break; } memset(buffer, 0, DNS_NSEC3_BUFFERSIZE); p = buffer; *p++ = hashalg; *p++ = flags; *p++ = iterations >> 8; *p++ = iterations; *p++ = salt_length; memcpy(p, salt, salt_length); p += salt_length; *p++ = hash_length; memcpy(p, nexthash, hash_length); p += hash_length; r.length = p - buffer; r.base = buffer; /* * Use the end of the space for a raw bitmap leaving enough * space for the window identifiers and length octets. */ bm = r.base + r.length + 512; nsec_bits = r.base + r.length; max_type = 0; if (node == NULL) goto collapse_bitmap; dns_rdataset_init(&rdataset); rdsiter = NULL; result = dns_db_allrdatasets(db, node, version, 0, &rdsiter); if (result != ISC_R_SUCCESS) return (result); found = found_ns = need_rrsig = ISC_FALSE; for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; result = dns_rdatasetiter_next(rdsiter)) { dns_rdatasetiter_current(rdsiter, &rdataset); if (rdataset.type != dns_rdatatype_nsec && rdataset.type != dns_rdatatype_nsec3 && rdataset.type != dns_rdatatype_rrsig) { if (rdataset.type > max_type) max_type = rdataset.type; set_bit(bm, rdataset.type, 1); /* * Work out if we need to set the RRSIG bit for * this node. We set the RRSIG bit if either of * the following conditions are met: * 1) We have a SOA or DS then we need to set * the RRSIG bit as both always will be signed. * 2) We set the RRSIG bit if we don't have * a NS record but do have other data. */ if (rdataset.type == dns_rdatatype_soa || rdataset.type == dns_rdatatype_ds) need_rrsig = ISC_TRUE; else if (rdataset.type == dns_rdatatype_ns) found_ns = ISC_TRUE; else found = ISC_TRUE; } dns_rdataset_disassociate(&rdataset); } if ((found && !found_ns) || need_rrsig) { if (dns_rdatatype_rrsig > max_type) max_type = dns_rdatatype_rrsig; set_bit(bm, dns_rdatatype_rrsig, 1); } /* * At zone cuts, deny the existence of glue in the parent zone. */ if (bit_isset(bm, dns_rdatatype_ns) && ! bit_isset(bm, dns_rdatatype_soa)) { for (i = 0; i <= max_type; i++) { if (bit_isset(bm, i) && ! dns_rdatatype_iszonecutauth((dns_rdatatype_t)i)) set_bit(bm, i, 0); } } dns_rdatasetiter_destroy(&rdsiter); if (result != ISC_R_NOMORE) return (result); collapse_bitmap: for (window = 0; window < 256; window++) { if (window * 256 > max_type) break; for (octet = 31; octet >= 0; octet--) if (bm[window * 32 + octet] != 0) break; if (octet < 0) continue; nsec_bits[0] = window; nsec_bits[1] = octet + 1; /* * Note: potentially overlapping move. */ memmove(&nsec_bits[2], &bm[window * 32], octet + 1); nsec_bits += 3 + octet; } r.length = nsec_bits - r.base; INSIST(r.length <= DNS_NSEC3_BUFFERSIZE); dns_rdata_fromregion(rdata, dns_db_class(db), dns_rdatatype_nsec3, &r); return (ISC_R_SUCCESS); }
static int hash_dir(const char *dirname) { struct bucket_info *bi, *nextbi; struct entry_info *ei, *nextei; struct dirent *de; struct stat st; unsigned char idmask[MAX_COLLISIONS / 8]; int i, n, nextid, buflen, ret = -1; const char *pathsep; char *buf; DIR *d; if (access(dirname, R_OK|W_OK|X_OK) != 0) { fprintf(stderr, "ERROR: Access denied '%s'\n", dirname); return -1; } buflen = strlen(dirname); pathsep = (buflen && dirname[buflen-1] == '/') ? "" : "/"; buflen += NAME_MAX + 2; buf = malloc(buflen); if (buf == NULL) goto err; if (do_verbose) printf("Doing %s\n", dirname); d = opendir(dirname); if (!d) goto err; while ((de = readdir(d)) != NULL) { if (snprintf(buf, buflen, "%s%s%s", dirname, pathsep, de->d_name) >= buflen) continue; if (lstat(buf, &st) < 0) continue; if (S_ISLNK(st.st_mode) && handle_symlink(de->d_name, buf) == 0) continue; handle_certificate(de->d_name, buf); } closedir(d); for (i = 0; i < countof(hash_table); i++) { for (bi = hash_table[i]; bi; bi = nextbi) { nextbi = bi->next; DEBUG("Type %d, hash %08x, num entries %d:\n", bi->type, bi->hash, bi->num_needed); nextid = 0; memset(idmask, 0, (bi->num_needed+7)/8); for (ei = bi->first_entry; ei; ei = ei->next) if (ei->old_id < bi->num_needed) bit_set(idmask, ei->old_id); for (ei = bi->first_entry; ei; ei = nextei) { nextei = ei->next; DEBUG("\t(old_id %d, need_symlink %d) Cert %s\n", ei->old_id, ei->need_symlink, ei->filename); if (ei->old_id < bi->num_needed) { /* Link exists, and is used as-is */ snprintf(buf, buflen, "%08x.%s%d", bi->hash, symlink_extensions[bi->type], ei->old_id); if (do_verbose) printf("link %s -> %s\n", ei->filename, buf); } else if (ei->need_symlink) { /* New link needed (it may replace something) */ while (bit_isset(idmask, nextid)) nextid++; snprintf(buf, buflen, "%s%s%n%08x.%s%d", dirname, pathsep, &n, bi->hash, symlink_extensions[bi->type], nextid); if (do_verbose) printf("link %s -> %s\n", ei->filename, &buf[n]); unlink(buf); symlink(ei->filename, buf); } else if (do_remove_links) { /* Link to be deleted */ snprintf(buf, buflen, "%s%s%n%08x.%s%d", dirname, pathsep, &n, bi->hash, symlink_extensions[bi->type], ei->old_id); if (do_verbose) printf("unlink %s\n", &buf[n]); unlink(buf); } free(ei->filename); free(ei); } free(bi); } hash_table[i] = NULL; } ret = 0; err: free(buf); return ret; }
int str_match(const char *str, const char *format) { const char *s = str; const char *fmt = format; unsigned int flag = 0; int width = 0; bit_t bit; char c; const char *s0; while (*fmt) { if (*fmt == '%') { fmt++; while (*fmt >= '0' && *fmt <= '9') width = width * 10 + *fmt++ - '0'; switch(*fmt) { case 'd': if (width) { while (*s && isdigit(*s) && width-- > 0) s++; } else { while (*s && isdigit(*s)) s++; } fmt++; break; case '[': fmt++; bit_zero(&bit); if (*fmt == '^') { fmt++; flag |= STATUS_CARET; } while (*fmt && *fmt != ']') { if (fmt[1] == '-') { for (c = *fmt; c < fmt[2]; c++) { bit_set(&bit, c); } if (c == fmt[2]) fmt += 2; } else { bit_set(&bit, *fmt); fmt++; } } if (flag & STATUS_CARET) { bit_rev(&bit); /* clear STATUS_CARET */ flag &= ~STATUS_CARET; } if (width) { while (*s && bit_isset(&bit, *s) && width-- > 0) { s++; } } else { while (*s && bit_isset(&bit, *s)) { s++; } } if (*fmt == ']') fmt++; break; case 'f': if (width) { while (*s && isdigit(*s) && width > 0) s++, width--; if (*s == '.' && width > 0) s++, width--; while (*s && isdigit(*s) && width > 0) s++, width--; } else { while (*s && isdigit(*s)) s++; if (*s == '.') s++; while (*s && isdigit(*s)) s++; } fmt++; break; case '{': fmt++; flag &= ~STATUS_OR; while(*fmt && *fmt != '|' && *fmt != '}') { s0 = s; /* match */ while (*fmt && *fmt != '|' && *fmt != '}' && (*s0 == *fmt)) s0++, fmt++; /* match ok*/ if (*fmt == '|' || *fmt == '}') { flag |= STATUS_OR; while (*fmt && *fmt != '}') fmt++; s = s0; } else { /* match no*/ while (*fmt && *fmt != '|' && *fmt != '}') fmt++; if (*fmt == '|' || *fmt == '}') fmt++; } } if (!(flag & STATUS_OR)) return 0; if (*fmt == '}') fmt++; break; default: fmt++; break; } width = 0; } else { /*printf("%d:%d:%c:%c\n", *fmt, *s, *fmt, *s);*/ if (!*fmt && !*s) return 1; if (*fmt != *s) { /* %?*/ if (*fmt && fmt[1] == '%' && fmt[2] == '?') { fmt++; continue; } else return 0; } fmt++; s++; } } return !*fmt && !*s; }