diff --git a/lib/Makefile.local b/lib/Makefile.local index a6400126..04418fa8 100644 --- a/lib/Makefile.local +++ b/lib/Makefile.local @@ -59,7 +59,8 @@ libnotmuch_cxx_srcs = \ $(dir)/config.cc \ $(dir)/regexp-fields.cc \ $(dir)/thread.cc \ - $(dir)/thread-fp.cc + $(dir)/thread-fp.cc \ + $(dir)/features.cc libnotmuch_modules := $(libnotmuch_c_srcs:.c=.o) $(libnotmuch_cxx_srcs:.cc=.o) diff --git a/lib/database-private.h b/lib/database-private.h index 041602cd..2d220811 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -32,6 +32,8 @@ #include "notmuch-private.h" +#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0])) + #ifdef SILENCE_XAPIAN_DEPRECATION_WARNINGS #define XAPIAN_DEPRECATED(D) D #endif @@ -263,4 +265,16 @@ _notmuch_database_find_doc_ids (notmuch_database_t *notmuch, const char *value, Xapian::PostingIterator *begin, Xapian::PostingIterator *end); + +#define NOTMUCH_DATABASE_VERSION 3 + +/* features.cc */ + +_notmuch_features +_notmuch_database_parse_features (const void *ctx, const char *features, unsigned int version, + char mode, char **incompat_out); + +char * +_notmuch_database_print_features (const void *ctx, unsigned int features); + #endif diff --git a/lib/database.cc b/lib/database.cc index 75189685..e08d43ca 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -39,8 +39,6 @@ using namespace std; -#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0])) - typedef struct { const char *name; const char *prefix; @@ -458,41 +456,6 @@ _notmuch_database_prefix (notmuch_database_t *notmuch, const char *name) return NULL; } -static const struct { - /* NOTMUCH_FEATURE_* value. */ - _notmuch_features value; - /* Feature name as it appears in the database. This name should - * be appropriate for displaying to the user if an older version - * of notmuch doesn't support this feature. */ - const char *name; - /* Compatibility flags when this feature is declared. */ - const char *flags; -} feature_names[] = { - { NOTMUCH_FEATURE_FILE_TERMS, - "multiple paths per message", "rw" }, - { NOTMUCH_FEATURE_DIRECTORY_DOCS, - "relative directory paths", "rw" }, - /* Header values are not required for reading a database because a - * reader can just refer to the message file. */ - { NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES, - "from/subject/message-ID in database", "w" }, - { NOTMUCH_FEATURE_BOOL_FOLDER, - "exact folder:/path: search", "rw" }, - { NOTMUCH_FEATURE_GHOSTS, - "mail documents for missing messages", "w" }, - /* Knowledge of the index mime-types are not required for reading - * a database because a reader will just be unable to query - * them. */ - { NOTMUCH_FEATURE_INDEXED_MIMETYPES, - "indexed MIME types", "w" }, - { NOTMUCH_FEATURE_LAST_MOD, - "modification tracking", "w" }, - /* Existing databases will work fine for all queries not involving - * 'body:' */ - { NOTMUCH_FEATURE_UNPREFIX_BODY_ONLY, - "index body and headers separately", "w" }, -}; - const char * notmuch_status_to_string (notmuch_status_t status) { @@ -817,83 +780,6 @@ _notmuch_database_new_revision (notmuch_database_t *notmuch) return new_revision; } -/* Parse a database features string from the given database version. - * Returns the feature bit set. - * - * For version < 3, this ignores the features string and returns a - * hard-coded set of features. - * - * If there are unrecognized features that are required to open the - * database in mode (which should be 'r' or 'w'), return a - * comma-separated list of unrecognized but required features in - * *incompat_out suitable for presenting to the user. *incompat_out - * will be allocated from ctx. - */ -static _notmuch_features -_parse_features (const void *ctx, const char *features, unsigned int version, - char mode, char **incompat_out) -{ - _notmuch_features res = static_cast<_notmuch_features>(0); - unsigned int namelen, i; - size_t llen = 0; - const char *flags; - - /* Prior to database version 3, features were implied by the - * version number. */ - if (version == 0) - return NOTMUCH_FEATURES_V0; - else if (version == 1) - return NOTMUCH_FEATURES_V1; - else if (version == 2) - return NOTMUCH_FEATURES_V2; - - /* Parse the features string */ - while ((features = strtok_len_c (features + llen, "\n", &llen)) != NULL) { - flags = strchr (features, '\t'); - if (! flags || flags > features + llen) - continue; - namelen = flags - features; - - for (i = 0; i < ARRAY_SIZE (feature_names); ++i) { - if (strlen (feature_names[i].name) == namelen && - strncmp (feature_names[i].name, features, namelen) == 0) { - res |= feature_names[i].value; - break; - } - } - - if (i == ARRAY_SIZE (feature_names) && incompat_out) { - /* Unrecognized feature */ - const char *have = strchr (flags, mode); - if (have && have < features + llen) { - /* This feature is required to access this database in - * 'mode', but we don't understand it. */ - if (! *incompat_out) - *incompat_out = talloc_strdup (ctx, ""); - *incompat_out = talloc_asprintf_append_buffer ( - *incompat_out, "%s%.*s", **incompat_out ? ", " : "", - namelen, features); - } - } - } - - return res; -} - -static char * -_print_features (const void *ctx, unsigned int features) -{ - unsigned int i; - char *res = talloc_strdup (ctx, ""); - - for (i = 0; i < ARRAY_SIZE (feature_names); ++i) - if (features & feature_names[i].value) - res = talloc_asprintf_append_buffer ( - res, "%s\t%s\n", feature_names[i].name, feature_names[i].flags); - - return res; -} - notmuch_status_t notmuch_database_open (const char *path, notmuch_database_mode_t mode, @@ -1012,7 +898,7 @@ notmuch_database_open_verbose (const char *path, /* Check features. */ incompat_features = NULL; - notmuch->features = _parse_features ( + notmuch->features = _notmuch_database_parse_features ( local, notmuch->xapian_db->get_metadata ("features").c_str (), version, mode == NOTMUCH_DATABASE_MODE_READ_WRITE ? 'w' : 'r', &incompat_features); @@ -1674,7 +1560,7 @@ notmuch_database_upgrade (notmuch_database_t *notmuch, } status = NOTMUCH_STATUS_SUCCESS; - db->set_metadata ("features", _print_features (local, notmuch->features)); + db->set_metadata ("features", _notmuch_database_print_features (local, notmuch->features)); db->set_metadata ("version", STRINGIFY (NOTMUCH_DATABASE_VERSION)); DONE: diff --git a/lib/features.cc b/lib/features.cc new file mode 100644 index 00000000..8def2461 --- /dev/null +++ b/lib/features.cc @@ -0,0 +1,114 @@ +#include "database-private.h" + +static const struct { + /* NOTMUCH_FEATURE_* value. */ + _notmuch_features value; + /* Feature name as it appears in the database. This name should + * be appropriate for displaying to the user if an older version + * of notmuch doesn't support this feature. */ + const char *name; + /* Compatibility flags when this feature is declared. */ + const char *flags; +} feature_names[] = { + { NOTMUCH_FEATURE_FILE_TERMS, + "multiple paths per message", "rw" }, + { NOTMUCH_FEATURE_DIRECTORY_DOCS, + "relative directory paths", "rw" }, + /* Header values are not required for reading a database because a + * reader can just refer to the message file. */ + { NOTMUCH_FEATURE_FROM_SUBJECT_ID_VALUES, + "from/subject/message-ID in database", "w" }, + { NOTMUCH_FEATURE_BOOL_FOLDER, + "exact folder:/path: search", "rw" }, + { NOTMUCH_FEATURE_GHOSTS, + "mail documents for missing messages", "w" }, + /* Knowledge of the index mime-types are not required for reading + * a database because a reader will just be unable to query + * them. */ + { NOTMUCH_FEATURE_INDEXED_MIMETYPES, + "indexed MIME types", "w" }, + { NOTMUCH_FEATURE_LAST_MOD, + "modification tracking", "w" }, + /* Existing databases will work fine for all queries not involving + * 'body:' */ + { NOTMUCH_FEATURE_UNPREFIX_BODY_ONLY, + "index body and headers separately", "w" }, +}; + +char * +_notmuch_database_print_features (const void *ctx, unsigned int features) +{ + unsigned int i; + char *res = talloc_strdup (ctx, ""); + + for (i = 0; i < ARRAY_SIZE (feature_names); ++i) + if (features & feature_names[i].value) + res = talloc_asprintf_append_buffer ( + res, "%s\t%s\n", feature_names[i].name, feature_names[i].flags); + + return res; +} + + +/* Parse a database features string from the given database version. + * Returns the feature bit set. + * + * For version < 3, this ignores the features string and returns a + * hard-coded set of features. + * + * If there are unrecognized features that are required to open the + * database in mode (which should be 'r' or 'w'), return a + * comma-separated list of unrecognized but required features in + * *incompat_out suitable for presenting to the user. *incompat_out + * will be allocated from ctx. + */ +_notmuch_features +_notmuch_database_parse_features (const void *ctx, const char *features, unsigned int version, + char mode, char **incompat_out) +{ + _notmuch_features res = static_cast<_notmuch_features>(0); + unsigned int namelen, i; + size_t llen = 0; + const char *flags; + + /* Prior to database version 3, features were implied by the + * version number. */ + if (version == 0) + return NOTMUCH_FEATURES_V0; + else if (version == 1) + return NOTMUCH_FEATURES_V1; + else if (version == 2) + return NOTMUCH_FEATURES_V2; + + /* Parse the features string */ + while ((features = strtok_len_c (features + llen, "\n", &llen)) != NULL) { + flags = strchr (features, '\t'); + if (! flags || flags > features + llen) + continue; + namelen = flags - features; + + for (i = 0; i < ARRAY_SIZE (feature_names); ++i) { + if (strlen (feature_names[i].name) == namelen && + strncmp (feature_names[i].name, features, namelen) == 0) { + res |= feature_names[i].value; + break; + } + } + + if (i == ARRAY_SIZE (feature_names) && incompat_out) { + /* Unrecognized feature */ + const char *have = strchr (flags, mode); + if (have && have < features + llen) { + /* This feature is required to access this database in + * 'mode', but we don't understand it. */ + if (! *incompat_out) + *incompat_out = talloc_strdup (ctx, ""); + *incompat_out = talloc_asprintf_append_buffer ( + *incompat_out, "%s%.*s", **incompat_out ? ", " : "", + namelen, features); + } + } + } + + return res; +}