X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/fbcfb25715ddbb522c8efac3faec1eca4811683d..9f28e855aad11d25c0a69a7e59825c07bdefcafe:/server/trackdb.c diff --git a/server/trackdb.c b/server/trackdb.c index b4e0de9..bc526ca 100644 --- a/server/trackdb.c +++ b/server/trackdb.c @@ -17,6 +17,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ +/** @file server/trackdb.c + * @brief Track database */ #include #include "types.h" @@ -34,6 +36,7 @@ #include #include #include +#include #include "event.h" #include "mem.h" @@ -144,9 +147,17 @@ static int compare(DB attribute((unused)) *db_, return compare_path_raw(a->data, a->size, b->data, b->size); } -/* open environment */ -void trackdb_init(int recover) { +/** @brief Open database environment + * @param flags Flags word + * + * Flags should be one of: + * - @ref TRACKDB_NO_RECOVER + * - @ref TRACKDB_NORMAL_RECOVER + * - @ref TRACKDB_FATAL_RECOVER + */ +void trackdb_init(int flags) { int err; + const int recover = flags & TRACKDB_RECOVER_MASK; static int recover_type[] = { 0, DB_RECOVER, DB_RECOVER_FATAL }; /* sanity checks */ @@ -207,7 +218,8 @@ static pid_t subprogram(ev_source *ev, const char *prog, /* If we're in the background then trap subprocess stdout/stderr */ if(!(pid = xfork())) { exitfn = _exit; - ev_signal_atfork(ev); + if(ev) + ev_signal_atfork(ev); signal(SIGPIPE, SIG_DFL); if(outputfd != -1) { xdup2(outputfd, 1); @@ -285,9 +297,17 @@ static DB *open_db(const char *path, return db; } -/* open track databases */ -void trackdb_open(void) { +/** @brief Open track databases + * @param Flags flags word + * + * @p flags should be one of: + * - @p TRACKDB_NO_UPGRADE, if no upgrade should be attempted + * - @p TRACKDB_CAN_UPGRADE, if an upgrade may be attempted + * - @p TRACKDB_OPEN_FOR_UPGRADE, if this is disorder-dbupgrade + */ +void trackdb_open(int flags) { int newdb, err; + pid_t pid; /* sanity checks */ assert(opened == 0); @@ -300,19 +320,51 @@ void trackdb_open(void) { long oldversion; s = trackdb_get_global("_dbversion"); - oldversion = s ? atol(s) : 1; - if(oldversion != config->dbversion) { - /* This database needs upgrading. This isn't implemented yet so we just - * fail. */ - fatal(0, "database needs upgrading from %ld to %ld", - oldversion, config->dbversion); - } - newdb = 0; /* Close the database again, we'll open it property below */ if((err = trackdb_globaldb->close(trackdb_globaldb, 0))) fatal(0, "error closing global.db: %s", db_strerror(err)); trackdb_globaldb = 0; + /* Convert version string to an integer */ + oldversion = s ? atol(s) : 1; + if(oldversion > config->dbversion) { + /* Database is from the future; we never allow this. */ + fatal(0, "this version of DisOrder is too old for database version %ld", + oldversion); + } + if(oldversion < config->dbversion) { + /* Database version is out of date */ + switch(flags & TRACKDB_UPGRADE_MASK) { + case TRACKDB_NO_UPGRADE: + /* This database needs upgrading but this is not permitted */ + fatal(0, "database needs upgrading from %ld to %ld", + oldversion, config->dbversion); + case TRACKDB_CAN_UPGRADE: + /* This database needs upgrading */ + info("invoking disorder-dbupgrade to upgrade from %ld to %ld", + oldversion, config->dbversion); + pid = subprogram(0, "disorder-dbupgrade", -1); + while(waitpid(pid, &err, 0) == -1 && errno == EINTR) + ; + if(err) + fatal(0, "disorder-dbupgrade %s", wstat(err)); + info("disorder-dbupgrade succeeded"); + break; + case TRACKDB_OPEN_FOR_UPGRADE: + break; + default: + abort(); + } + } + if(oldversion == config->dbversion && (flags & TRACKDB_OPEN_FOR_UPGRADE)) { + /* This doesn't make any sense */ + fatal(0, "database is already at current version"); + } + newdb = 0; } else { + if(flags & TRACKDB_OPEN_FOR_UPGRADE) { + /* Cannot upgrade a new database */ + fatal(0, "cannot upgrade a database that does not exist"); + } /* This is a brand new database */ newdb = 1; } @@ -327,10 +379,11 @@ void trackdb_open(void) { trackdb_globaldb = open_db("global.db", 0, DB_HASH, DB_CREATE, 0666); trackdb_noticeddb = open_db("noticed.db", DB_DUPSORT, DB_BTREE, DB_CREATE, 0666); - /* Stash the database version */ if(newdb) { + /* Stash the database version */ char buf[32]; + assert(!(flags & TRACKDB_OPEN_FOR_UPGRADE)); snprintf(buf, sizeof buf, "%ld", config->dbversion); trackdb_set_global("_dbversion", buf, 0); } @@ -584,6 +637,22 @@ static int tailor_underscore_Word_Break_Other(uint32_t c) { } } +/** @brief Remove all combining characters in-place + * @param s Pointer to start of string + * @param ns Length of string + * @return New, possiblby reduced, length + */ +static size_t remove_combining_chars(uint32_t *s, size_t ns) { + uint32_t *start = s, *t = s, *end = s + ns; + + while(s < end) { + const uint32_t c = *s++; + if(!utf32_combining_class(c)) + *t++ = c; + } + return t - start; +} + /** @brief Normalize and split a string using a given tailoring */ static void word_split(struct vector *v, const char *s, @@ -597,6 +666,8 @@ static void word_split(struct vector *v, /* Erase case distinctions */ if(!(t32 = utf32_casefold_compat(t32, nt32, &nt32))) return; + /* Drop combining characters */ + nt32 = remove_combining_chars(t32, nt32); /* Split into words, treating _ as a space */ w32 = utf32_word_split(t32, nt32, &nw, pt); /* Convert words back to UTF-8 and append to result */ @@ -1763,11 +1834,20 @@ char **trackdb_search(char **wordlist, int nwordlist, int *ntracks) { const char *dbname; *ntracks = 0; /* for early returns */ - /* casefold all the words */ + /* normalize all the words */ w = xmalloc(nwordlist * sizeof (char *)); for(n = 0; n < nwordlist; ++n) { + uint32_t *w32; + size_t nw32; + w[n] = utf8_casefold_compat(wordlist[n], strlen(wordlist[n]), 0); if(checktag(w[n])) ++ntags; /* count up tags */ + /* Strip out combining characters (AFTER checking whether it's a tag) */ + if(!(w32 = utf8_to_utf32(w[n], strlen(w[n]), &nw32))) + return 0; + nw32 = remove_combining_chars(w32, nw32); + if(!(w[n] = utf32_to_utf8(w32, nw32, 0))) + return 0; } /* find the longest non-stopword */ for(n = 0; n < nwordlist; ++n) @@ -1928,9 +2008,9 @@ static int reap_rescan(ev_source attribute((unused)) *ev, void attribute((unused)) *u) { if(pid == rescan_pid) rescan_pid = -1; if(status) - error(0, "disorderd-rescan: %s", wstat(status)); + error(0, RESCAN": %s", wstat(status)); else - D(("disorderd-rescan terminate: %s", wstat(status))); + D((RESCAN" terminated: %s", wstat(status))); /* Our cache of file lookups is out of date now */ cache_clean(&cache_files_type); eventlog("rescanned", (char *)0); @@ -1938,14 +2018,22 @@ static int reap_rescan(ev_source attribute((unused)) *ev, } void trackdb_rescan(ev_source *ev) { + int w; + if(rescan_pid != -1) { error(0, "rescan already underway"); return; } rescan_pid = subprogram(ev, RESCAN, -1); - ev_child(ev, rescan_pid, 0, reap_rescan, 0); - D(("started rescanner")); - + if(ev) { + ev_child(ev, rescan_pid, 0, reap_rescan, 0); + D(("started rescanner")); + } else { + /* This is the first rescan, we block until it is complete */ + while(waitpid(rescan_pid, &w, 0) < 0 && errno == EINTR) + ; + reap_rescan(0, rescan_pid, w, 0, 0); + } } int trackdb_rescan_cancel(void) {