#include "filepart.h"
#include "words.h"
-const char *find_track_root(const char *track) {
+const struct collection *find_track_collection(const char *track) {
int n;
size_t l, tl = strlen(track);
&& track[l] == '/')
break;
}
- if(n >= config->collection.n) {
- error(0, "found track in no collection '%s'", track);
+ if(n < config->collection.n)
+ return &config->collection.s[n];
+ else
return 0;
- }
- return config->collection.s[n].root;
+}
+
+const char *find_track_root(const char *track) {
+ const struct collection *c = find_track_collection(track);
+ if(c)
+ return c->root;
+ error(0, "found track in no collection '%s'", track);
+ return 0;
}
const char *track_rootless(const char *track) {
#ifndef TRACKNAME_H
#define TRACKNAME_H
+const struct collection *find_track_collection(const char *track);
+/* find the collection for @track@ */
+
const char *find_track_root(const char *track);
/* find the collection root for @track@ */
#include "printf.h"
#include "trackdb.h"
#include "trackdb-int.h"
+#include "trackname.h"
static DB_TXN *global_tid;
struct recheck_state {
const struct collection *c;
- long nobsolete, nlength;
+ long nobsolete, nnocollection, nlength;
};
/* called for each non-alias track */
if(aborted()) return EINTR;
D(("rechecking %s", track));
+ /* if we're not checking a specific collection, find the right collection */
+ if(!c) {
+ if(!(c = find_track_collection(track))) {
+ D(("obsoleting %s", track));
+ if((err = trackdb_obsolete(track, tid))) return err;
+ ++cs->nnocollection;
+ return 0;
+ }
+ }
/* see if the track has evaporated */
if(check(c->module, c->root, path) == 0) {
D(("obsoleting %s", track));
static void recheck_collection(const struct collection *c) {
struct recheck_state cs;
- info("rechecking %s", c->root);
+ if(c)
+ info("rechecking %s", c->root);
+ else
+ info("rechecking all tracks");
for(;;) {
checkabort();
global_tid = trackdb_begin_transaction();
memset(&cs, 0, sizeof cs);
cs.c = c;
- if(trackdb_scan(c->root, recheck_callback, &cs, global_tid)) goto fail;
+ if(trackdb_scan(c ? c->root : 0, recheck_callback, &cs, global_tid))
+ goto fail;
break;
fail:
/* Maybe we need to shut down */
/* Let anything else that is going on get out of the way. */
sleep(10);
checkabort();
- info("resuming recheck of %s", c->root);
+ if(c)
+ info("resuming recheck of %s", c->root);
+ else
+ info("resuming global recheck");
}
trackdb_commit_transaction(global_tid);
global_tid = 0;
- info("rechecked %s, %ld obsoleted, %ld lengths calculated",
- c->root, cs.nobsolete, cs.nlength);
+ if(c)
+ info("rechecked %s, %ld obsoleted, %ld lengths calculated",
+ c->root, cs.nobsolete, cs.nlength);
+ else
+ info("rechecked all tracks, %ld no collection, %ld obsoleted, %ld lengths calculated",
+ cs.nnocollection, cs.nobsolete, cs.nlength);
}
/* rescan/recheck a collection by name */
trackdb_init(0);
trackdb_open();
if(optind == argc) {
+ /* Rescan all collections */
do_all(rescan_collection);
- do_all(recheck_collection);
+ /* Check that every track still exists */
+ recheck_collection(0);
}
else {
+ /* Rescan specified collections */
for(n = optind; n < argc; ++n)
do_directory(argv[n], rescan_collection);
+ /* Check specified collections for tracks that have gone */
for(n = optind; n < argc; ++n)
do_directory(argv[n], recheck_collection);
}
DB_TXN *tid),
void *u,
DB_TXN *tid);
-/* Call CALLBACK for each non-alias track below ROOT. Return 0 or
- * DB_LOCK_DEADLOCK. CALLBACK should return 0 on success or EINTR to cancel
- * the scan. */
+/* Call CALLBACK for each non-alias track below ROOT (or all tracks if ROOT is
+ * 0). Return 0 or DB_LOCK_DEADLOCK. CALLBACK should return 0 on success or
+ * EINTR to cancel the scan. */
/* fill KEY in with S, returns KEY */
static inline DBT *make_key(DBT *key, const char *s) {
DB_TXN *tid) {
DBC *cursor;
DBT k, d;
- size_t root_len = strlen(root);
- int err;
+ const size_t root_len = root ? strlen(root) : 0;
+ int err, cberr;
struct kvp *data;
+ const char *track;
cursor = trackdb_opencursor(trackdb_tracksdb, tid);
- err = cursor->c_get(cursor, make_key(&k, root), prepare_data(&d),
- DB_SET_RANGE);
+ if(root)
+ err = cursor->c_get(cursor, make_key(&k, root), prepare_data(&d),
+ DB_SET_RANGE);
+ else {
+ memset(&k, 0, sizeof k);
+ err = cursor->c_get(cursor, &k, prepare_data(&d),
+ DB_FIRST);
+ }
while(!err) {
- if(k.size > root_len
- && !strncmp(k.data, root, root_len)
- && ((char *)k.data)[root_len] == '/') {
+ if(!root
+ || (k.size > root_len
+ && !strncmp(k.data, root, root_len)
+ && ((char *)k.data)[root_len] == '/')) {
data = kvp_urldecode(d.data, d.size);
- if(kvp_get(data, "_path"))
- if((err = callback(xstrndup(k.data, k.size), data, u, tid)))
+ if(kvp_get(data, "_path")) {
+ track = xstrndup(k.data, k.size);
+ /* Advance to the next track before the callback so that the callback
+ * may safely delete the track */
+ err = cursor->c_get(cursor, &k, &d, DB_NEXT);
+ if((cberr = callback(track, data, u, tid))) {
+ err = cberr;
break;
- err = cursor->c_get(cursor, &k, &d, DB_NEXT);
+ }
+ } else
+ err = cursor->c_get(cursor, &k, &d, DB_NEXT);
} else
break;
}