u_char * getStatPtr( oid *name, /* IN - name of var, OUT - name matched */ size_t *namelen, /* IN -number of sub-ids in name, OUT - subid-is in matched name */ u_char *type, /* OUT - type of matched variable */ size_t *len, /* OUT - length of matched variable */ u_short *acl, /* OUT - access control list */ int exact, /* IN - TRUE if exact match wanted */ WriteMethod **write_method, struct snmp_pdu *pdu, /* IN - relevant auth info re PDU */ int *noSuchObject) { struct subtree *tp; oid save[MAX_OID_LEN]; size_t savelen = 0; u_char result_type; u_short result_acl; u_char *search_return=NULL; found = FALSE; if (!exact){ memcpy(save, name, *namelen * sizeof(oid)); savelen = *namelen; } *write_method = NULL; DEBUGMSGTL(("snmp_vars", "Looking for: ")); DEBUGMSGOID(("snmp_vars", name, *namelen)); DEBUGMSG(("snmp_vars"," ...\n")); tp = find_subtree(name, *namelen, NULL); while ( search_return == NULL && tp != NULL ) { DEBUGMSGTL(("snmp_vars", "Trying tree: ")); DEBUGMSGOID(("snmp_vars", tp->name, tp->namelen)); DEBUGMSG(("snmp_vars"," ...\n")); search_return = search_subtree_vars( tp, name, namelen, &result_type, len, &result_acl, exact, write_method, pdu, noSuchObject); if ( search_return != NULL || exact ) break; tp = tp->next; } if ( tp == NULL ) { if (!search_return && !exact){ memcpy(name, save, savelen * sizeof(oid)); *namelen = savelen; } if (found) *noSuchObject = FALSE; else *noSuchObject = TRUE; return NULL; } *type = result_type; *acl = result_acl; return search_return; }
static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path) { if (!it) return NULL; while (*path) { const char *slash; struct cache_tree_sub *sub; slash = strchr(path, '/'); if (!slash) slash = path + strlen(path); /* between path and slash is the name of the * subtree to look for. */ sub = find_subtree(it, path, slash - path, 0); if (!sub) return NULL; it = sub->cache_tree; if (slash) while (*slash && *slash == '/') slash++; if (!slash || !*slash) return it; /* prefix ended with slashes */ path = slash; } return it; }
void cache_tree_invalidate_path(struct cache_tree *it, const char *path) { /* a/b/c * ==> invalidate self * ==> find "a", have it invalidate "b/c" * a * ==> invalidate self * ==> if "a" exists as a subtree, remove it. */ const char *slash; int namelen; struct cache_tree_sub *down; #if DEBUG fprintf(stderr, "cache-tree invalidate <%s>\n", path); #endif if (!it) return; slash = strchr(path, '/'); it->entry_count = -1; if (!slash) { int pos; namelen = strlen(path); pos = subtree_pos(it, path, namelen); if (0 <= pos) { cache_tree_free(&it->down[pos]->cache_tree); free(it->down[pos]); /* 0 1 2 3 4 5 * ^ ^subtree_nr = 6 * pos * move 4 and 5 up one place (2 entries) * 2 = 6 - 3 - 1 = subtree_nr - pos - 1 */ memmove(it->down+pos, it->down+pos+1, sizeof(struct cache_tree_sub *) * (it->subtree_nr - pos - 1)); it->subtree_nr--; } return; } namelen = slash - path; down = find_subtree(it, path, namelen, 0); if (down) cache_tree_invalidate_path(down->cache_tree, slash + 1); }
static int do_invalidate_path(struct cache_tree *it, const char *path) { /* a/b/c * ==> invalidate self * ==> find "a", have it invalidate "b/c" * a * ==> invalidate self * ==> if "a" exists as a subtree, remove it. */ const char *slash; int namelen; struct cache_tree_sub *down; #if DEBUG_CACHE_TREE fprintf(stderr, "cache-tree invalidate <%s>\n", path); #endif if (!it) return 0; slash = strchrnul(path, '/'); namelen = slash - path; it->entry_count = -1; if (!*slash) { int pos; pos = subtree_pos(it, path, namelen); if (0 <= pos) { cache_tree_free(&it->down[pos]->cache_tree); free(it->down[pos]); /* 0 1 2 3 4 5 * ^ ^subtree_nr = 6 * pos * move 4 and 5 up one place (2 entries) * 2 = 6 - 3 - 1 = subtree_nr - pos - 1 */ MOVE_ARRAY(it->down + pos, it->down + pos + 1, it->subtree_nr - pos - 1); it->subtree_nr--; } return 1; } down = find_subtree(it, path, namelen, 0); if (down) do_invalidate_path(down->cache_tree, slash + 1); return 1; }
static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *path) { if (!it) return NULL; while (*path) { const char *slash; struct cache_tree_sub *sub; slash = strchrnul(path, '/'); /* * Between path and slash is the name of the subtree * to look for. */ sub = find_subtree(it, path, slash - path, 0); if (!sub) return NULL; it = sub->cache_tree; path = slash; while (*path == '/') path++; } return it; }
struct cache_tree_sub *cache_tree_sub(struct cache_tree *it, const char *path) { int pathlen = strlen(path); return find_subtree(it, path, pathlen, 1); }
static int update_one(struct cache_tree *it, struct cache_entry **cache, int entries, const char *base, int baselen, int missing_ok, int dryrun) { struct strbuf buffer; int i; if (0 <= it->entry_count && has_sha1_file(it->sha1)) return it->entry_count; /* * We first scan for subtrees and update them; we start by * marking existing subtrees -- the ones that are unmarked * should not be in the result. */ for (i = 0; i < it->subtree_nr; i++) it->down[i]->used = 0; /* * Find the subtrees and update them. */ for (i = 0; i < entries; i++) { struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; int pathlen, sublen, subcnt; path = ce->name; pathlen = ce_namelen(ce); if (pathlen <= baselen || memcmp(base, path, baselen)) break; /* at the end of this level */ slash = strchr(path + baselen, '/'); if (!slash) continue; /* * a/bbb/c (base = a/, slash = /c) * ==> * path+baselen = bbb/c, sublen = 3 */ sublen = slash - (path + baselen); sub = find_subtree(it, path + baselen, sublen, 1); if (!sub->cache_tree) sub->cache_tree = cache_tree(); subcnt = update_one(sub->cache_tree, cache + i, entries - i, path, baselen + sublen + 1, missing_ok, dryrun); if (subcnt < 0) return subcnt; i += subcnt - 1; sub->used = 1; } discard_unused_subtrees(it); /* * Then write out the tree object for this level. */ strbuf_init(&buffer, 8192); for (i = 0; i < entries; i++) { struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; int pathlen, entlen; const unsigned char *sha1; unsigned mode; path = ce->name; pathlen = ce_namelen(ce); if (pathlen <= baselen || memcmp(base, path, baselen)) break; /* at the end of this level */ slash = strchr(path + baselen, '/'); if (slash) { entlen = slash - (path + baselen); sub = find_subtree(it, path + baselen, entlen, 0); if (!sub) die("cache-tree.c: '%.*s' in '%s' not found", entlen, path + baselen, path); i += sub->cache_tree->entry_count - 1; sha1 = sub->cache_tree->sha1; mode = S_IFDIR; } else { sha1 = ce->sha1; mode = ce->ce_mode; entlen = pathlen - baselen; } if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) return error("invalid object %06o %s for '%.*s'", mode, sha1_to_hex(sha1), entlen+baselen, path); if (ce->ce_flags & CE_REMOVE) continue; /* entry being removed */ strbuf_grow(&buffer, entlen + 100); strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0'); strbuf_add(&buffer, sha1, 20); #if DEBUG fprintf(stderr, "cache-tree update-one %o %.*s\n", mode, entlen, path + baselen); #endif } if (dryrun) hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1); else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) { strbuf_release(&buffer); return -1; } strbuf_release(&buffer); it->entry_count = i; #if DEBUG fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n", it->entry_count, it->subtree_nr, sha1_to_hex(it->sha1)); #endif return i; }
int unregister_mib_range( oid *name, size_t len, int priority, int range_subid, oid range_ubound) { struct subtree *list, *myptr; struct subtree *prev, *child; /* loop through children */ struct register_parameters reg_parms; list = find_subtree( name, len, subtrees ); if ( list == NULL ) return MIB_NO_SUCH_REGISTRATION; for ( child=list, prev=NULL; child != NULL; prev=child, child=child->children ) { if (( snmp_oid_compare( child->name, child->namelen, name, len) == 0 ) && ( child->priority == priority )) break; /* found it */ } if ( child == NULL ) return MIB_NO_SUCH_REGISTRATION; unload_subtree( child, prev ); myptr = child; /* remember this for later */ /* * Now handle any occurances in the following subtrees, * as a result of splitting this range. Due to the * nature of the way such splits work, the first * subtree 'slice' that doesn't refer to the given * name marks the end of the original region. * * This should also serve to register ranges. */ for ( list = myptr->next ; list != NULL ; list=list->next ) { for ( child=list, prev=NULL; child != NULL; prev=child, child=child->children ) { if (( snmp_oid_compare( child->name, child->namelen, name, len) == 0 ) && ( child->priority == priority )) { unload_subtree( child, prev ); free_subtree( child ); break; } } if ( child == NULL ) /* Didn't find the given name */ break; } free_subtree( myptr ); reg_parms.name = name; reg_parms.namelen = len; reg_parms.priority = priority; reg_parms.range_subid = range_subid; reg_parms.range_ubound = range_ubound; snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, SNMPD_CALLBACK_UNREGISTER_OID, ®_parms); return MIB_UNREGISTERED_OK; }
int load_subtree( struct subtree *new_sub ) { struct subtree *tree1, *tree2, *new2; struct subtree *prev, *next; int res; if ( new_sub == NULL ) return MIB_REGISTERED_OK; /* Degenerate case */ /* * Find the subtree that contains the start of * the new subtree (if any)... */ tree1 = find_subtree( new_sub->start, new_sub->start_len, NULL ); /* * ...and the subtree that follows the new one * (NULL implies this is the final region covered) */ if ( tree1 == NULL ) tree2 = find_subtree_next( new_sub->start, new_sub->start_len, NULL ); else tree2 = tree1->next; /* * Handle new subtrees that start in virgin territory. */ if ( tree1 == NULL ) { new2 = NULL; /* Is there any overlap with later subtrees ? */ if ( tree2 && snmp_oid_compare( new_sub->end, new_sub->end_len, tree2->start, tree2->start_len ) > 0 ) new2 = split_subtree( new_sub, tree2->start, tree2->start_len ); /* * Link the new subtree (less any overlapping region) * with the list of existing registrations */ if ( tree2 ) { new_sub->prev = tree2->prev; tree2->prev = new_sub; } else new_sub->prev = find_subtree_previous( new_sub->start, new_sub->start_len, NULL ); if ( new_sub->prev ) new_sub->prev->next = new_sub; else subtrees = new_sub; new_sub->next = tree2; /* * If there was any overlap, * recurse to merge in the overlapping region * (including anything that may follow the overlap) */ if ( new2 ) return load_subtree( new2 ); } else { /* * If the new subtree starts *within* an existing registration * (rather than at the same point as it), then split the * existing subtree at this point. */ if ( snmp_oid_compare( new_sub->start, new_sub->start_len, tree1->start, tree1->start_len) != 0 ) tree1 = split_subtree( tree1, new_sub->start, new_sub->start_len); if ( tree1 == NULL ) return MIB_REGISTRATION_FAILED; /* Now consider the end of this existing subtree: * If it matches the new subtree precisely, * simply merge the new one into the list of children * If it includes the whole of the new subtree, * split it at the appropriate point, and merge again * * If the new subtree extends beyond this existing region, * split it, and recurse to merge the two parts. */ switch ( snmp_oid_compare( new_sub->end, new_sub->end_len, tree1->end, tree1->end_len)) { case -1: /* Existing subtree contains new one */ (void) split_subtree( tree1, new_sub->end, new_sub->end_len); /* Fall Through */ case 0: /* The two trees match precisely */ /* * Note: This is the only point where the original * registration OID ("name") is used */ prev = NULL; next = tree1; while ( next && next->namelen > new_sub->namelen ) { prev = next; next = next->children; } while ( next && next->namelen == new_sub->namelen && next->priority < new_sub->priority ) { prev = next; next = next->children; } if ( next && next->namelen == new_sub->namelen && next->priority == new_sub->priority ) return MIB_DUPLICATE_REGISTRATION; if ( prev ) { new_sub->children = next; prev->children = new_sub; new_sub->prev = prev->prev; new_sub->next = prev->next; } else { new_sub->children = next; new_sub->prev = next->prev; new_sub->next = next->next; for ( next = new_sub->next ; next != NULL ; next = next->children ) next->prev = new_sub; for ( prev = new_sub->prev ; prev != NULL ; prev = prev->children ) prev->next = new_sub; } break; case 1: /* New subtree contains the existing one */ new2 = split_subtree( new_sub, tree1->end, tree1->end_len); res = load_subtree( new_sub ); if ( res != MIB_REGISTERED_OK ) return res; return load_subtree( new2 ); } } return 0; }
static void verify_one(struct repository *r, struct index_state *istate, struct cache_tree *it, struct strbuf *path) { int i, pos, len = path->len; struct strbuf tree_buf = STRBUF_INIT; struct object_id new_oid; for (i = 0; i < it->subtree_nr; i++) { strbuf_addf(path, "%s/", it->down[i]->name); verify_one(r, istate, it->down[i]->cache_tree, path); strbuf_setlen(path, len); } if (it->entry_count < 0 || /* no verification on tests (t7003) that replace trees */ lookup_replace_object(r, &it->oid) != &it->oid) return; if (path->len) { pos = index_name_pos(istate, path->buf, path->len); pos = -pos - 1; } else { pos = 0; } i = 0; while (i < it->entry_count) { struct cache_entry *ce = istate->cache[pos + i]; const char *slash; struct cache_tree_sub *sub = NULL; const struct object_id *oid; const char *name; unsigned mode; int entlen; if (ce->ce_flags & (CE_STAGEMASK | CE_INTENT_TO_ADD | CE_REMOVE)) BUG("%s with flags 0x%x should not be in cache-tree", ce->name, ce->ce_flags); name = ce->name + path->len; slash = strchr(name, '/'); if (slash) { entlen = slash - name; sub = find_subtree(it, ce->name + path->len, entlen, 0); if (!sub || sub->cache_tree->entry_count < 0) BUG("bad subtree '%.*s'", entlen, name); oid = &sub->cache_tree->oid; mode = S_IFDIR; i += sub->cache_tree->entry_count; } else { oid = &ce->oid; mode = ce->ce_mode; entlen = ce_namelen(ce) - path->len; i++; } strbuf_addf(&tree_buf, "%o %.*s%c", mode, entlen, name, '\0'); strbuf_add(&tree_buf, oid->hash, the_hash_algo->rawsz); } hash_object_file(tree_buf.buf, tree_buf.len, tree_type, &new_oid); if (!oideq(&new_oid, &it->oid)) BUG("cache-tree for path %.*s does not match. " "Expected %s got %s", len, path->buf, oid_to_hex(&new_oid), oid_to_hex(&it->oid)); strbuf_setlen(path, len); strbuf_release(&tree_buf); }
static int update_one(struct cache_tree *it, struct cache_entry **cache, int entries, const char *base, int baselen, int *skip_count, int flags) { struct strbuf buffer; int missing_ok = flags & WRITE_TREE_MISSING_OK; int dryrun = flags & WRITE_TREE_DRY_RUN; int repair = flags & WRITE_TREE_REPAIR; int to_invalidate = 0; int i; assert(!(dryrun && repair)); *skip_count = 0; if (0 <= it->entry_count && has_sha1_file(it->oid.hash)) return it->entry_count; /* * We first scan for subtrees and update them; we start by * marking existing subtrees -- the ones that are unmarked * should not be in the result. */ for (i = 0; i < it->subtree_nr; i++) it->down[i]->used = 0; /* * Find the subtrees and update them. */ i = 0; while (i < entries) { const struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; int pathlen, sublen, subcnt, subskip; path = ce->name; pathlen = ce_namelen(ce); if (pathlen <= baselen || memcmp(base, path, baselen)) break; /* at the end of this level */ slash = strchr(path + baselen, '/'); if (!slash) { i++; continue; } /* * a/bbb/c (base = a/, slash = /c) * ==> * path+baselen = bbb/c, sublen = 3 */ sublen = slash - (path + baselen); sub = find_subtree(it, path + baselen, sublen, 1); if (!sub->cache_tree) sub->cache_tree = cache_tree(); subcnt = update_one(sub->cache_tree, cache + i, entries - i, path, baselen + sublen + 1, &subskip, flags); if (subcnt < 0) return subcnt; if (!subcnt) die("index cache-tree records empty sub-tree"); i += subcnt; sub->count = subcnt; /* to be used in the next loop */ *skip_count += subskip; sub->used = 1; } discard_unused_subtrees(it); /* * Then write out the tree object for this level. */ strbuf_init(&buffer, 8192); i = 0; while (i < entries) { const struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub = NULL; const char *path, *slash; int pathlen, entlen; const struct object_id *oid; unsigned mode; int expected_missing = 0; int contains_ita = 0; int ce_missing_ok; path = ce->name; pathlen = ce_namelen(ce); if (pathlen <= baselen || memcmp(base, path, baselen)) break; /* at the end of this level */ slash = strchr(path + baselen, '/'); if (slash) { entlen = slash - (path + baselen); sub = find_subtree(it, path + baselen, entlen, 0); if (!sub) die("cache-tree.c: '%.*s' in '%s' not found", entlen, path + baselen, path); i += sub->count; oid = &sub->cache_tree->oid; mode = S_IFDIR; contains_ita = sub->cache_tree->entry_count < 0; if (contains_ita) { to_invalidate = 1; expected_missing = 1; } } else { oid = &ce->oid; mode = ce->ce_mode; entlen = pathlen - baselen; i++; } ce_missing_ok = mode == S_IFGITLINK || missing_ok || (repository_format_partial_clone && ce_skip_worktree(ce)); if (is_null_oid(oid) || (!ce_missing_ok && !has_object_file(oid))) { strbuf_release(&buffer); if (expected_missing) return -1; return error("invalid object %06o %s for '%.*s'", mode, oid_to_hex(oid), entlen+baselen, path); } /* * CE_REMOVE entries are removed before the index is * written to disk. Skip them to remain consistent * with the future on-disk index. */ if (ce->ce_flags & CE_REMOVE) { *skip_count = *skip_count + 1; continue; } /* * CE_INTENT_TO_ADD entries exist on on-disk index but * they are not part of generated trees. Invalidate up * to root to force cache-tree users to read elsewhere. */ if (!sub && ce_intent_to_add(ce)) { to_invalidate = 1; continue; } /* * "sub" can be an empty tree if all subentries are i-t-a. */ if (contains_ita && is_empty_tree_oid(oid)) continue; strbuf_grow(&buffer, entlen + 100); strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0'); strbuf_add(&buffer, oid->hash, the_hash_algo->rawsz); #if DEBUG fprintf(stderr, "cache-tree update-one %o %.*s\n", mode, entlen, path + baselen); #endif } if (repair) { struct object_id oid; hash_object_file(buffer.buf, buffer.len, tree_type, &oid); if (has_object_file(&oid)) oidcpy(&it->oid, &oid); else to_invalidate = 1; } else if (dryrun) { hash_object_file(buffer.buf, buffer.len, tree_type, &it->oid); } else if (write_object_file(buffer.buf, buffer.len, tree_type, &it->oid)) { strbuf_release(&buffer); return -1; } strbuf_release(&buffer); it->entry_count = to_invalidate ? -1 : i - *skip_count; #if DEBUG fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n", it->entry_count, it->subtree_nr, oid_to_hex(&it->oid)); #endif return i; }
static int update_one(struct cache_tree *it, const struct cache_entry * const *cache, int entries, const char *base, int baselen, int *skip_count, int flags) { struct strbuf buffer; int missing_ok = flags & WRITE_TREE_MISSING_OK; int dryrun = flags & WRITE_TREE_DRY_RUN; int to_invalidate = 0; int i; *skip_count = 0; if (0 <= it->entry_count && has_sha1_file(it->sha1)) return it->entry_count; /* * We first scan for subtrees and update them; we start by * marking existing subtrees -- the ones that are unmarked * should not be in the result. */ for (i = 0; i < it->subtree_nr; i++) it->down[i]->used = 0; /* * Find the subtrees and update them. */ i = 0; while (i < entries) { const struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; int pathlen, sublen, subcnt, subskip; path = ce->name; pathlen = ce_namelen(ce); if (pathlen <= baselen || memcmp(base, path, baselen)) break; /* at the end of this level */ slash = strchr(path + baselen, '/'); if (!slash) { i++; continue; } /* * a/bbb/c (base = a/, slash = /c) * ==> * path+baselen = bbb/c, sublen = 3 */ sublen = slash - (path + baselen); sub = find_subtree(it, path + baselen, sublen, 1); if (!sub->cache_tree) sub->cache_tree = cache_tree(); subcnt = update_one(sub->cache_tree, cache + i, entries - i, path, baselen + sublen + 1, &subskip, flags); if (subcnt < 0) return subcnt; i += subcnt; sub->count = subcnt; /* to be used in the next loop */ *skip_count += subskip; sub->used = 1; } discard_unused_subtrees(it); /* * Then write out the tree object for this level. */ strbuf_init(&buffer, 8192); i = 0; while (i < entries) { const struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; int pathlen, entlen; const unsigned char *sha1; unsigned mode; path = ce->name; pathlen = ce_namelen(ce); if (pathlen <= baselen || memcmp(base, path, baselen)) break; /* at the end of this level */ slash = strchr(path + baselen, '/'); if (slash) { entlen = slash - (path + baselen); sub = find_subtree(it, path + baselen, entlen, 0); if (!sub) die("cache-tree.c: '%.*s' in '%s' not found", entlen, path + baselen, path); i += sub->count; sha1 = sub->cache_tree->sha1; mode = S_IFDIR; if (sub->cache_tree->entry_count < 0) to_invalidate = 1; } else { sha1 = ce->sha1; mode = ce->ce_mode; entlen = pathlen - baselen; i++; } if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) { strbuf_release(&buffer); return error("invalid object %06o %s for '%.*s'", mode, sha1_to_hex(sha1), entlen+baselen, path); } /* * CE_REMOVE entries are removed before the index is * written to disk. Skip them to remain consistent * with the future on-disk index. */ if (ce->ce_flags & CE_REMOVE) { *skip_count = *skip_count + 1; continue; } /* * CE_INTENT_TO_ADD entries exist on on-disk index but * they are not part of generated trees. Invalidate up * to root to force cache-tree users to read elsewhere. */ if (ce->ce_flags & CE_INTENT_TO_ADD) { to_invalidate = 1; continue; } strbuf_grow(&buffer, entlen + 100); strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0'); strbuf_add(&buffer, sha1, 20); #if DEBUG fprintf(stderr, "cache-tree update-one %o %.*s\n", mode, entlen, path + baselen); #endif } if (dryrun) hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1); else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) { strbuf_release(&buffer); return -1; } strbuf_release(&buffer); it->entry_count = to_invalidate ? -1 : i - *skip_count; #if DEBUG fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n", it->entry_count, it->subtree_nr, sha1_to_hex(it->sha1)); #endif return i; }