chiark / gitweb /
check server runs after upgrade
[disorder] / server / trackdb.c
index 4be5f25802187cc422dc4ae8fa38f03af895bb4f..2318bd9ed41d5ed954f41c1a3b6688e3ef9c9f2c 100644 (file)
@@ -44,7 +44,6 @@
 #include "configuration.h"
 #include "syscalls.h"
 #include "wstat.h"
-#include "words.h"
 #include "printf.h"
 #include "filepart.h"
 #include "trackname.h"
@@ -53,6 +52,8 @@
 #include "cache.h"
 #include "eventlog.h"
 #include "hash.h"
+#include "unicode.h"
+#include "unidata.h"
 
 #define RESCAN "disorder-rescan"
 #define DEADLOCK "disorder-deadlock"
@@ -284,8 +285,10 @@ static DB *open_db(const char *path,
   return db;
 }
 
-/* open track databases */
-void trackdb_open(void) {
+/** @brief Open track databases
+ * @param dbupgrade Non-0 to allow non-current database versions
+ */
+void trackdb_open(int dbupgrade) {
   int newdb, err;
 
   /* sanity checks */
@@ -300,18 +303,31 @@ void trackdb_open(void) {
 
     s = trackdb_get_global("_dbversion");
     oldversion = s ? atol(s) : 1;
-    if(oldversion != config->dbversion) {
+    if(oldversion > config->dbversion) {
+      /* Database is from the future */
+      fatal(0, "this version of DisOrder is too old for database version %ld",
+            oldversion);
+    }
+    if(oldversion < config->dbversion && !dbupgrade) {
       /* 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);
     }
+    if(oldversion == config->dbversion && dbupgrade) {
+      /* This doesn't make any sense */
+      fatal(0, "database is already at current version");
+    }
     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;
   } else {
+    if(dbupgrade) {
+      /* Cannot upgrade a new database */
+      fatal(0, "cannot upgrade a database that does not exist");
+    }
     /* This is a brand new database */
     newdb = 1;
   }
@@ -326,8 +342,8 @@ 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) {
+  if(newdb && !dbupgrade) {
+    /* Stash the database version */
     char buf[32];
 
     snprintf(buf, sizeof buf, "%ld", config->dbversion);
@@ -573,24 +589,50 @@ static int is_display_pref(const char *name) {
   return !strncmp(name, prefix, (sizeof prefix) - 1);
 }
 
+/** @brief Word_Break property tailor that treats underscores as spaces */
+static int tailor_underscore_Word_Break_Other(uint32_t c) {
+  switch(c) {
+  default:
+    return -1;
+  case 0x005F: /* LOW LINE (SPACING UNDERSCORE) */
+    return unicode_Word_Break_Other;
+  }
+}
+
+/** @brief Normalize and split a string using a given tailoring */
+static void word_split(struct vector *v,
+                       const char *s,
+                       unicode_property_tailor *pt) {
+  size_t nw, nt32, i;
+  uint32_t *t32, **w32;
+
+  /* Convert to UTF-32 */
+  if(!(t32 = utf8_to_utf32(s, strlen(s), &nt32)))
+    return;
+  /* Erase case distinctions */
+  if(!(t32 = utf32_casefold_compat(t32, nt32, &nt32)))
+    return;
+  /* 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 */
+  for(i = 0; i < nw; ++i)
+    vector_append(v, utf32_to_utf8(w32[i], utf32_len(w32[i]), 0));
+}
+
 /* compute the words of a track name */
 static char **track_to_words(const char *track,
                              const struct kvp *p) {
   struct vector v;
-  char **w;
-  int nw;
   const char *rootless = track_rootless(track);
 
   if(!rootless)
     rootless = track;                   /* bodge */
   vector_init(&v);
-  if((w = words(casefold(strip_extension(rootless)), &nw)))
-    vector_append_many(&v, w, nw);
-
+  rootless = strip_extension(rootless);
+  word_split(&v, strip_extension(rootless), tailor_underscore_Word_Break_Other);
   for(; p; p = p->next)
     if(is_display_pref(p->name))
-      if((w = words(casefold(p->value), &nw)))
-        vector_append_many(&v, w, nw);
+      word_split(&v, p->value, 0);
   vector_terminate(&v);
   return dedupe(v.vec, v.nvec);
 }
@@ -1739,7 +1781,7 @@ char **trackdb_search(char **wordlist, int nwordlist, int *ntracks) {
   /* casefold all the words */
   w = xmalloc(nwordlist * sizeof (char *));
   for(n = 0; n < nwordlist; ++n) {
-    w[n] = casefold(wordlist[n]);
+    w[n] = utf8_casefold_compat(wordlist[n], strlen(wordlist[n]), 0);
     if(checktag(w[n])) ++ntags;         /* count up tags */
   }
   /* find the longest non-stopword */