int repo_lookup_deparray(Repo *repo, Id entry, Id keyname, Queue *q, Id marker) { int r = repo_lookup_idarray(repo, entry, keyname, q); if (r && marker) { int i; if (marker < 0) { marker = -marker; for (i = 0; i < q->count; i++) if (q->elements[i] == marker) { queue_truncate(q, i); return r; } } else { for (i = 0; i < q->count; i++) if (q->elements[i] == marker) { queue_deleten(q, 0, i + 1); return r; } } } return r; }
void transaction_all_obs_pkgs(Transaction *trans, Id p, Queue *pkgs) { Pool *pool = trans->pool; Solvable *s = pool->solvables + p; Queue *ti = &trans->transaction_info; Id q; int i; queue_empty(pkgs); if (p <= 0 || !s->repo) return; if (s->repo == pool->installed) { q = trans->transaction_installed[p - pool->installed->start]; if (!q) return; if (q > 0) { /* only a single obsoleting package */ queue_push(pkgs, q); return; } /* find which packages obsolete us */ for (i = 0; i < ti->count; i += 2) if (ti->elements[i + 1] == p) queue_push2(pkgs, p, ti->elements[i]); /* sort obsoleters */ if (pkgs->count > 2) solv_sort(pkgs->elements, pkgs->count / 2, 2 * sizeof(Id), obsq_sortcmp, pool); for (i = 0; i < pkgs->count; i += 2) pkgs->elements[i / 2] = pkgs->elements[i + 1]; queue_truncate(pkgs, pkgs->count / 2); } else { /* find the packages we obsolete */ for (i = 0; i < ti->count; i += 2) { if (ti->elements[i] == p) queue_push(pkgs, ti->elements[i + 1]); else if (pkgs->count) break; } } }
int pool_findfileconflicts(Pool *pool, Queue *pkgs, int cutoff, Queue *conflicts, int flags, void *(*handle_cb)(Pool *, Id, void *) , void *handle_cbdata) { int i, j, cflmapn, idxmapset; struct cbdata cbdata; unsigned int now, start; void *handle; Repo *installed = pool->installed; Id p; int obsoleteusescolors = pool_get_flag(pool, POOL_FLAG_OBSOLETEUSESCOLORS); int hdrfetches; queue_empty(conflicts); if (!pkgs->count) return 0; now = start = solv_timems(0); POOL_DEBUG(SOLV_DEBUG_STATS, "searching for file conflicts\n"); POOL_DEBUG(SOLV_DEBUG_STATS, "packages: %d, cutoff %d\n", pkgs->count, cutoff); memset(&cbdata, 0, sizeof(cbdata)); cbdata.aliases = flags & FINDFILECONFLICTS_CHECK_DIRALIASING; cbdata.pool = pool; if (cbdata.aliases && (flags & FINDFILECONFLICTS_USE_ROOTDIR) != 0) { cbdata.rootdir = pool_get_rootdir(pool); if (cbdata.rootdir && !strcmp(cbdata.rootdir, "/")) cbdata.rootdir = 0; if (cbdata.rootdir) cbdata.rootdirl = strlen(cbdata.rootdir); if (!cbdata.rootdir) cbdata.usestat = 1; } queue_init(&cbdata.lookat); queue_init(&cbdata.lookat_dir); map_init(&cbdata.idxmap, pkgs->count); if (cutoff <= 0) cutoff = pkgs->count; /* avarage file list size: 200 files per package */ /* avarage dir count: 20 dirs per package */ /* first pass: scan dirs */ if (!cbdata.aliases) { hdrfetches = 0; cflmapn = (cutoff + 3) * 64; while ((cflmapn & (cflmapn - 1)) != 0) cflmapn = cflmapn & (cflmapn - 1); cbdata.dirmap = solv_calloc(cflmapn, 2 * sizeof(Id)); cbdata.dirmapn = cflmapn - 1; /* make it a mask */ cbdata.create = 1; idxmapset = 0; for (i = 0; i < pkgs->count; i++) { if (i == cutoff) cbdata.create = 0; cbdata.idx = i; p = pkgs->elements[i]; if ((flags & FINDFILECONFLICTS_USE_SOLVABLEFILELIST) != 0 && installed) { if (p >= installed->start && p < installed->end && pool->solvables[p].repo == installed) { iterate_solvable_dirs(pool, p, finddirs_cb, &cbdata); if (MAPTST(&cbdata.idxmap, i)) idxmapset++; continue; } } handle = (*handle_cb)(pool, p, handle_cbdata); if (!handle) continue; hdrfetches++; rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_ONLYDIRS, finddirs_cb, &cbdata); if (MAPTST(&cbdata.idxmap, i)) idxmapset++; } POOL_DEBUG(SOLV_DEBUG_STATS, "dirmap size: %d, used %d\n", cbdata.dirmapn + 1, cbdata.dirmapused); POOL_DEBUG(SOLV_DEBUG_STATS, "dirmap memory usage: %d K\n", (cbdata.dirmapn + 1) * 2 * (int)sizeof(Id) / 1024); POOL_DEBUG(SOLV_DEBUG_STATS, "header fetches: %d\n", hdrfetches); POOL_DEBUG(SOLV_DEBUG_STATS, "dirmap creation took %d ms\n", solv_timems(now)); POOL_DEBUG(SOLV_DEBUG_STATS, "dir conflicts found: %d, idxmap %d of %d\n", cbdata.dirconflicts, idxmapset, pkgs->count); } /* second pass: scan files */ now = solv_timems(0); cflmapn = (cutoff + 3) * 128; while ((cflmapn & (cflmapn - 1)) != 0) cflmapn = cflmapn & (cflmapn - 1); cbdata.cflmap = solv_calloc(cflmapn, 2 * sizeof(Id)); cbdata.cflmapn = cflmapn - 1; /* make it a mask */ cbdata.create = 1; hdrfetches = 0; for (i = 0; i < pkgs->count; i++) { if (i == cutoff) cbdata.create = 0; if (!cbdata.aliases && !MAPTST(&cbdata.idxmap, i)) continue; cbdata.idx = i; p = pkgs->elements[i]; if (!cbdata.create && (flags & FINDFILECONFLICTS_USE_SOLVABLEFILELIST) != 0 && installed) { if (p >= installed->start && p < installed->end && pool->solvables[p].repo == installed) if (!precheck_solvable_files(&cbdata, pool, p)) continue; } /* can't use FINDFILECONFLICTS_USE_SOLVABLEFILELIST because we have to know if * the file is a directory or not */ handle = (*handle_cb)(pool, p, handle_cbdata); if (!handle) continue; hdrfetches++; cbdata.lastdiridx = -1; rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_NOGHOSTS, cbdata.aliases ? findfileconflicts_basename_cb : findfileconflicts_cb, &cbdata); } POOL_DEBUG(SOLV_DEBUG_STATS, "filemap size: %d, used %d\n", cbdata.cflmapn + 1, cbdata.cflmapused); POOL_DEBUG(SOLV_DEBUG_STATS, "filemap memory usage: %d K\n", (cbdata.cflmapn + 1) * 2 * (int)sizeof(Id) / 1024); POOL_DEBUG(SOLV_DEBUG_STATS, "header fetches: %d\n", hdrfetches); POOL_DEBUG(SOLV_DEBUG_STATS, "filemap creation took %d ms\n", solv_timems(now)); POOL_DEBUG(SOLV_DEBUG_STATS, "lookat_dir size: %d\n", cbdata.lookat_dir.count); queue_free(&cbdata.lookat_dir); /* we need another pass for aliases */ if (cbdata.aliases) { now = solv_timems(0); /* make sure the first offset is not zero */ addfilesspace(&cbdata, 1); cflmapn = (cutoff + 3) * 16; while ((cflmapn & (cflmapn - 1)) != 0) cflmapn = cflmapn & (cflmapn - 1); cbdata.normap = solv_calloc(cflmapn, 2 * sizeof(Id)); cbdata.normapn = cflmapn - 1; /* make it a mask */ if (cbdata.usestat) { cbdata.statmap = solv_calloc(cflmapn, 2 * sizeof(Id)); cbdata.statmapn = cflmapn - 1; /* make it a mask */ } cbdata.create = 0; hdrfetches = 0; for (i = 0; i < pkgs->count; i++) { if (!MAPTST(&cbdata.idxmap, i)) continue; p = pkgs->elements[i]; cbdata.idx = i; /* can't use FINDFILECONFLICTS_USE_SOLVABLEFILELIST because we have to know if * the file is a directory or not */ handle = (*handle_cb)(pool, p, handle_cbdata); if (!handle) continue; hdrfetches++; cbdata.lastdiridx = -1; rpm_iterate_filelist(handle, RPM_ITERATE_FILELIST_NOGHOSTS, findfileconflicts_alias_cb, &cbdata); } POOL_DEBUG(SOLV_DEBUG_STATS, "normap size: %d, used %d\n", cbdata.normapn + 1, cbdata.normapused); POOL_DEBUG(SOLV_DEBUG_STATS, "normap memory usage: %d K\n", (cbdata.normapn + 1) * 2 * (int)sizeof(Id) / 1024); POOL_DEBUG(SOLV_DEBUG_STATS, "header fetches: %d\n", hdrfetches); POOL_DEBUG(SOLV_DEBUG_STATS, "stats made: %d\n", cbdata.statsmade); if (cbdata.usestat) { POOL_DEBUG(SOLV_DEBUG_STATS, "statmap size: %d, used %d\n", cbdata.statmapn + 1, cbdata.statmapused); POOL_DEBUG(SOLV_DEBUG_STATS, "statmap memory usage: %d K\n", (cbdata.statmapn + 1) * 2 * (int)sizeof(Id) / 1024); } cbdata.statmap = solv_free(cbdata.statmap); cbdata.statmapn = 0; cbdata.canonspace = solv_free(cbdata.canonspace); cbdata.canonspacen = 0; POOL_DEBUG(SOLV_DEBUG_STATS, "alias processing took %d ms\n", solv_timems(now)); } cbdata.dirmap = solv_free(cbdata.dirmap); cbdata.dirmapn = 0; cbdata.dirmapused = 0; cbdata.cflmap = solv_free(cbdata.cflmap); cbdata.cflmapn = 0; cbdata.cflmapused = 0; map_free(&cbdata.idxmap); /* sort and unify/prune */ now = solv_timems(0); POOL_DEBUG(SOLV_DEBUG_STATS, "raw candidates: %d, pruning\n", cbdata.lookat.count / 4); solv_sort(cbdata.lookat.elements, cbdata.lookat.count / 4, sizeof(Id) * 4, &lookat_hx_cmp, pool); for (i = j = 0; i < cbdata.lookat.count; ) { int first = 1; Id hx = cbdata.lookat.elements[i]; Id idx = cbdata.lookat.elements[i + 1]; Id dhx = cbdata.lookat.elements[i + 2]; Id dirid = cbdata.lookat.elements[i + 3]; i += 4; for (; i < cbdata.lookat.count && hx == cbdata.lookat.elements[i] && (dirid == cbdata.lookat.elements[i + 3] || dirid == -cbdata.lookat.elements[i + 3]); i += 4) { if (idx == cbdata.lookat.elements[i + 1] && dhx == cbdata.lookat.elements[i + 2]) continue; /* ignore duplicates */ if (first) { if (dirid < 0) continue; /* all have a neg dirid */ cbdata.lookat.elements[j++] = hx; cbdata.lookat.elements[j++] = idx; cbdata.lookat.elements[j++] = dhx; cbdata.lookat.elements[j++] = dirid; first = 0; } idx = cbdata.lookat.elements[i + 1]; dhx = cbdata.lookat.elements[i + 2]; cbdata.lookat.elements[j++] = hx; cbdata.lookat.elements[j++] = idx; cbdata.lookat.elements[j++] = dhx; cbdata.lookat.elements[j++] = dirid; } } queue_truncate(&cbdata.lookat, j); POOL_DEBUG(SOLV_DEBUG_STATS, "candidates now: %d\n", cbdata.lookat.count / 4); POOL_DEBUG(SOLV_DEBUG_STATS, "pruning took %d ms\n", solv_timems(now)); /* third pass: collect file info for all files that match a hx */ now = solv_timems(0); solv_sort(cbdata.lookat.elements, cbdata.lookat.count / 4, sizeof(Id) * 4, &lookat_idx_cmp, pool); queue_init(&cbdata.files); hdrfetches = 0; for (i = 0; i < cbdata.lookat.count; i += 4) { Id idx = cbdata.lookat.elements[i + 1]; int iterflags = RPM_ITERATE_FILELIST_WITHMD5 | RPM_ITERATE_FILELIST_NOGHOSTS; if (obsoleteusescolors) iterflags |= RPM_ITERATE_FILELIST_WITHCOL; p = pkgs->elements[idx]; handle = (*handle_cb)(pool, p, handle_cbdata); if (handle) hdrfetches++; for (;; i += 4) { int fstart = cbdata.files.count; queue_push(&cbdata.files, idx); queue_push(&cbdata.files, 0); cbdata.idx = idx; cbdata.hx = cbdata.lookat.elements[i]; cbdata.dirhash = cbdata.lookat.elements[i + 2]; cbdata.dirid = cbdata.lookat.elements[i + 3]; cbdata.lastdiridx = -1; if (handle) rpm_iterate_filelist(handle, iterflags, findfileconflicts2_cb, &cbdata); cbdata.files.elements[fstart + 1] = cbdata.files.count; cbdata.lookat.elements[i + 1] = fstart; if (i + 4 >= cbdata.lookat.count || cbdata.lookat.elements[i + 4 + 1] != idx) break; } } POOL_DEBUG(SOLV_DEBUG_STATS, "header fetches: %d\n", hdrfetches); POOL_DEBUG(SOLV_DEBUG_STATS, "file info fetching took %d ms\n", solv_timems(now)); cbdata.normap = solv_free(cbdata.normap); cbdata.normapn = 0; /* forth pass: for each hx we have, compare all matching files against all other matching files */ now = solv_timems(0); solv_sort(cbdata.lookat.elements, cbdata.lookat.count / 4, sizeof(Id) * 4, &lookat_hx_cmp, pool); for (i = 0; i < cbdata.lookat.count - 4; i += 4) { Id hx = cbdata.lookat.elements[i]; Id pstart = cbdata.lookat.elements[i + 1]; Id dirid = cbdata.lookat.elements[i + 3]; Id pidx = cbdata.files.elements[pstart]; Id pend = cbdata.files.elements[pstart + 1]; if (cbdata.lookat.elements[i + 4] != hx) continue; /* no package left with that hx */ for (j = i + 4; j < cbdata.lookat.count && cbdata.lookat.elements[j] == hx && cbdata.lookat.elements[j + 3] == dirid; j += 4) { Id qstart = cbdata.lookat.elements[j + 1]; Id qidx = cbdata.files.elements[qstart]; Id qend = cbdata.files.elements[qstart + 1]; int ii, jj; if (pidx >= cutoff && qidx >= cutoff) continue; /* no conflicts between packages with idx >= cutoff */ for (ii = pstart + 2; ii < pend; ii++) for (jj = qstart + 2; jj < qend; jj++) { char *fsi = (char *)cbdata.filesspace + cbdata.files.elements[ii]; char *fsj = (char *)cbdata.filesspace + cbdata.files.elements[jj]; if (cbdata.aliases) { /* compare just the basenames, the dirs match because of the dirid */ char *bsi = strrchr(fsi + 34, '/'); char *bsj = strrchr(fsj + 34, '/'); if (!bsi || !bsj) continue; if (strcmp(bsi, bsj)) continue; /* different base names */ } else { if (strcmp(fsi + 34, fsj + 34)) continue; /* different file names */ } if (!strcmp(fsi, fsj)) continue; /* file digests match, no conflict */ if (obsoleteusescolors && fsi[33] && fsj[33] && (fsi[33] & fsj[33]) == 0) continue; /* colors do not conflict */ queue_push(conflicts, pool_str2id(pool, fsi + 34, 1)); queue_push(conflicts, pkgs->elements[pidx]); queue_push(conflicts, pool_str2id(pool, fsi, 1)); queue_push(conflicts, pool_str2id(pool, fsj + 34, 1)); queue_push(conflicts, pkgs->elements[qidx]); queue_push(conflicts, pool_str2id(pool, fsj, 1)); } } } POOL_DEBUG(SOLV_DEBUG_STATS, "filespace size: %d K\n", cbdata.filesspacen / 1024); POOL_DEBUG(SOLV_DEBUG_STATS, "candidate check took %d ms\n", solv_timems(now)); cbdata.filesspace = solv_free(cbdata.filesspace); cbdata.filesspacen = 0; queue_free(&cbdata.lookat); queue_free(&cbdata.files); if (conflicts->count > 6) solv_sort(conflicts->elements, conflicts->count / 6, 6 * sizeof(Id), conflicts_cmp, pool); POOL_DEBUG(SOLV_DEBUG_STATS, "found %d file conflicts\n", conflicts->count / 6); POOL_DEBUG(SOLV_DEBUG_STATS, "file conflict detection took %d ms\n", solv_timems(start)); return conflicts->count / 6; }
/* * returns: * 0: no blocks * 1: matches all * -1: at least one block */ static int normalize_dep(Pool *pool, Id dep, Queue *bq, int flags) { int bqcnt = bq->count; int bqcnt2; int todnf = flags & CPLXDEPS_TODNF ? 1 : 0; Id p, dp; #ifdef CPLXDEBUG printf("normalize_dep %s todnf:%d\n", pool_dep2str(pool, dep), todnf); #endif if (pool_is_complex_dep(pool, dep)) { Reldep *rd = GETRELDEP(pool, dep); if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_COND) { int rdflags = rd->flags; Id name = rd->name; Id evr = rd->evr; int r, mode; if (rdflags == REL_COND) { /* check for relly complex ELSE case */ if (ISRELDEP(evr)) { Reldep *rd2 = GETRELDEP(pool, evr); if (rd2->flags == REL_ELSE) { int r2; /* really complex case */ if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_AND_1) { /* A OR ~B */ rdflags = REL_COND; evr = rd2->name; } else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_AND_2) { /* C OR B */ rdflags = REL_OR; name = rd2->evr; evr = rd2->name; } else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_OR_1) { /* A AND B */ rdflags = REL_AND; evr = rd2->name; } else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_OR_2) { /* A AND C */ rdflags = REL_AND; evr = rd2->evr; } else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_OR_3) { /* C AND ~B */ rdflags = REL_ELSE; name = rd2->evr; evr = rd2->name; } else if (!todnf) { /* we want AND: A IF (B ELSE C) -> (A OR ~B) AND (C OR B) */ r = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_AND_1); if (r == 0 && (flags & CPLXDEPS_DONTFIX) == 0) return 0; r2 = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_AND_2); if (r2 == 0 && (flags & CPLXDEPS_DONTFIX) == 0) { queue_truncate(bq, bqcnt); return 0; } if (r == -1 || r2 == -1) return -1; return r == 1 || r2 == 1 ? 1 : 0; } else { int r2, r3; /* we want OR: A IF (B ELSE C) -> (A AND B) OR (A AND C) OR (~B AND C) */ r = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_OR_1); if (r == 1) return 1; r2 = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_OR_2); if (r2 == 1) { queue_truncate(bq, bqcnt); return 1; } r3 = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_OR_3); if (r3 == 1) { queue_truncate(bq, bqcnt); return 1; } if (r == -1 || r2 == -1 || r3 == -1) return -1; return 0; } } } } mode = rdflags == REL_AND || rdflags == REL_ELSE ? 0 : 1; /* get blocks of first argument */ r = normalize_dep(pool, name, bq, flags); if (r == 0) { if (rdflags == REL_ELSE) return 0; if (rdflags == REL_AND && (flags & CPLXDEPS_DONTFIX) == 0) return 0; if (rdflags == REL_COND) { r = normalize_dep(pool, evr, bq, (flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX); return invert_depblocks(pool, bq, bqcnt, r); /* invert block for COND */ } return normalize_dep(pool, evr, bq, flags); } if (r == 1) { if (rdflags == REL_ELSE) { r = normalize_dep(pool, evr, bq, (flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX); return invert_depblocks(pool, bq, bqcnt, r); /* invert block for ELSE */ } if (rdflags == REL_OR || rdflags == REL_COND) return 1; return normalize_dep(pool, evr, bq, flags); } /* get blocks of second argument */ bqcnt2 = bq->count; /* COND is OR with NEG on evr block, so we invert the todnf flag in that case */ r = normalize_dep(pool, evr, bq, rdflags == REL_COND || rdflags == REL_ELSE ? ((flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX) : flags); if (rdflags == REL_COND || rdflags == REL_ELSE) r = invert_depblocks(pool, bq, bqcnt2, r); /* invert 2nd block */ if (r == 0) { if (rdflags == REL_OR) return -1; if (rdflags == REL_AND && (flags & CPLXDEPS_DONTFIX) != 0) return -1; queue_truncate(bq, bqcnt); return 0; } if (r == 1) { if (rdflags == REL_COND || rdflags == REL_OR) { queue_truncate(bq, bqcnt); return 1; } return -1; } if (mode == todnf) { /* simple case: just join em. nothing more to do here. */ #ifdef CPLXDEBUG printf("SIMPLE JOIN %d %d %d\n", bqcnt, bqcnt2, bq->count); #endif return -1; } else { /* complex case: mix em */ int i, j, bqcnt3; #ifdef CPLXDEBUG printf("COMPLEX JOIN %d %d %d\n", bqcnt, bqcnt2, bq->count); #endif bqcnt2 = expand_simpledeps(pool, bq, bqcnt, bqcnt2); bqcnt3 = bq->count; for (i = bqcnt; i < bqcnt2; i++) { for (j = bqcnt2; j < bqcnt3; j++) { int a, b; int bqcnt4 = bq->count; int k = i; /* mix i block with j block, both blocks are sorted */ while (bq->elements[k] && bq->elements[j]) { if (bq->elements[k] < bq->elements[j]) queue_push(bq, bq->elements[k++]); else { if (bq->elements[k] == bq->elements[j]) k++; queue_push(bq, bq->elements[j++]); } } while (bq->elements[j]) queue_push(bq, bq->elements[j++]); while (bq->elements[k]) queue_push(bq, bq->elements[k++]); /* block is finished, check for A + -A */ for (a = bqcnt4, b = bq->count - 1; a < b; ) { if (-bq->elements[a] == bq->elements[b]) break; if (-bq->elements[a] > bq->elements[b]) a++; else b--; } if (a < b) queue_truncate(bq, bqcnt4); /* ignore this block */ else queue_push(bq, 0); /* finish block */ } /* advance to next block */ while (bq->elements[i]) i++; } i = -1; if (bqcnt3 == bq->count) /* ignored all blocks? */ i = todnf ? 0 : 1; queue_deleten(bq, bqcnt, bqcnt3 - bqcnt); return i; } } } /* fallback case: just use package list */ dp = pool_whatprovides(pool, dep); if (dp <= 2 || !pool->whatprovidesdata[dp]) return dp == 2 ? 1 : 0; if (pool->whatprovidesdata[dp] == SYSTEMSOLVABLE) return 1; bqcnt = bq->count; if ((flags & CPLXDEPS_NAME) != 0) { while ((p = pool->whatprovidesdata[dp++]) != 0) { if (!pool_match_nevr(pool, pool->solvables + p, dep)) continue; queue_push(bq, p); if (todnf) queue_push(bq, 0); } } else if (todnf) { while ((p = pool->whatprovidesdata[dp++]) != 0) queue_push2(bq, p, 0); } else queue_push2(bq, pool->nsolvables, dp); /* not yet expanded marker + offset */ if (bq->count == bqcnt) return 0; /* no provider */ if (!todnf) queue_push(bq, 0); /* finish block */ return -1; }