diff options
Diffstat (limited to 'libdb/db_store.c')
-rw-r--r-- | libdb/db_store.c | 202 |
1 files changed, 139 insertions, 63 deletions
diff --git a/libdb/db_store.c b/libdb/db_store.c index 213ae63e..12b3b9f1 100644 --- a/libdb/db_store.c +++ b/libdb/db_store.c @@ -26,6 +26,7 @@ #endif /* HAVE_CONFIG_H */ #include <assert.h> +#include <stdbool.h> #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -33,6 +34,8 @@ #include "attribute.h" #include "error.h" +#include "gl_array_list.h" +#include "gl_xlist.h" #include "timespec.h" #include "xalloc.h" #include "xvasprintf.h" @@ -40,6 +43,8 @@ #include "manconfig.h" #include "debug.h" +#include "filenames.h" +#include "glcontainers.h" #include "mydbm.h" #include "db_storage.h" @@ -53,7 +58,7 @@ * If promote_links is true, consider SO_MAN equivalent to ULT_MAN. This is * appropriate when sorting candidate pages for display. */ -int ATTRIBUTE_CONST compare_ids (char a, char b, int promote_links) +int ATTRIBUTE_CONST compare_ids (char a, char b, bool promote_links) { #ifdef FAVOUR_STRAYCATS if (a == WHATIS_MAN && b == STRAY_CAT) @@ -76,6 +81,12 @@ int ATTRIBUTE_CONST compare_ids (char a, char b, int promote_links) return 0; } +enum replace_action { + REPLACE_YES = 0, + REPLACE_NO, + REPLACE_FAIL +}; + /* The do_we_replace logic. Decide, for some existing key, whether it should * be replaced with some new contents. Check that names and section * extensions match before calling this. @@ -85,40 +96,67 @@ static int replace_if_necessary (MYDBM_FILE dbf, struct mandata *olddata, datum newkey, datum newcont) { + enum replace_action action; + /* It's OK to replace ULT_MAN with SO_MAN if the mtime is newer. It * isn't OK to replace a real page (either ULT_MAN or SO_MAN) with a * whatis reference; if the real page really went away then * purge_missing will catch that in time, but a real page that still * exists should always take precedence. + * + * Tie-break whatis references by lexicographical sort of + * the pointed-to page names, which isn't great but at least + * gives us something reproducible. + * + * TODO: name fields should be collated with the requested name */ - if (compare_ids (newdata->id, olddata->id, 1) <= 0 && - timespec_cmp (newdata->mtime, olddata->mtime) > 0) { - debug ("replace_if_necessary(): newer mtime; replacing\n"); - if (MYDBM_REPLACE (dbf, newkey, newcont)) - gripe_replace_key (dbf, MYDBM_DPTR (newkey)); - return 0; - } - - if (compare_ids (newdata->id, olddata->id, 0) < 0) { - if (MYDBM_REPLACE (dbf, newkey, newcont)) - gripe_replace_key (dbf, MYDBM_DPTR (newkey)); - return 0; + if (compare_ids (newdata->id, olddata->id, false) < 0) { + debug ("replace_if_necessary: stronger ID; replacing\n"); + action = REPLACE_YES; + } else if (compare_ids (newdata->id, olddata->id, true) <= 0 && + timespec_cmp (newdata->mtime, olddata->mtime) > 0) { + debug ("replace_if_necessary: newer mtime; replacing\n"); + action = REPLACE_YES; + } else if (compare_ids (newdata->id, olddata->id, true) <= 0 && + timespec_cmp (newdata->mtime, olddata->mtime) < 0) { + debug ("replace_if_necessary: older mtime; not replacing\n"); + action = REPLACE_NO; + } else if (compare_ids (newdata->id, olddata->id, false) > 0) { + debug ("replace_if_necessary: weaker ID; not replacing\n"); + action = REPLACE_NO; + } else if (newdata->pointer && olddata->pointer && + strcmp (newdata->pointer, olddata->pointer) < 0) { + debug ("replace_if_necessary: pointer '%s' < '%s'; " + "replacing\n", newdata->pointer, olddata->pointer); + action = REPLACE_YES; + } else if (newdata->pointer && olddata->pointer && + strcmp (newdata->pointer, olddata->pointer) > 0) { + debug ("replace_if_necessary: pointer '%s' > '%s'; " + "not replacing\n", newdata->pointer, olddata->pointer); + action = REPLACE_NO; + } else if (!STREQ (dash_if_unset (newdata->comp), + olddata->comp)) { + debug ("replace_if_necessary: differing compression " + "extensions (%s != %s); failing\n", + dash_if_unset (newdata->comp), olddata->comp); + action = REPLACE_FAIL; + } else { + debug ("replace_if_necessary: match; not replacing\n"); + action = REPLACE_NO; } - /* TODO: name fields should be collated with the requested name */ - - if (newdata->id == olddata->id) { - if (STREQ (dash_if_unset (newdata->comp), olddata->comp)) - return 0; /* same file */ - else { - debug ("ignoring differing compression " - "extensions: %s\n", MYDBM_DPTR (newkey)); - return 1; /* differing exts */ - } + switch (action) { + case REPLACE_YES: + if (MYDBM_REPLACE (dbf, newkey, newcont)) + gripe_replace_key (dbf, MYDBM_DPTR (newkey)); + return 0; + case REPLACE_NO: + /* Insert if missing, but ignore failures. */ + MYDBM_INSERT (dbf, newkey, newcont); + return 0; + default: + return 1; } - - debug ("ignoring differing ids: %s\n", MYDBM_DPTR (newkey)); - return 0; } /* The complement of split_content */ @@ -131,13 +169,13 @@ static datum make_content (struct mandata *in) memset (&cont, 0, sizeof cont); if (!in->pointer) - in->pointer = dash; - if (!in->filter) - in->filter = dash; + in->pointer = xstrdup (dash); if (!in->comp) - in->comp = dash; + in->comp = xstrdup (dash); + if (!in->filter) + in->filter = xstrdup (dash); if (!in->whatis) - in->whatis = dash + 1; + in->whatis = xstrdup (dash + 1); value = xasprintf ( "%s\t%s\t%s\t%ld\t%ld\t%c\t%s\t%s\t%s\t%s", @@ -164,6 +202,27 @@ static datum make_content (struct mandata *in) return cont; } +/* The complement of list_extensions. */ +static char *make_extensions_reference (gl_list_t refs) +{ + struct name_ext *ref; + size_t len = 0; + char *data, *cur; + + GL_LIST_FOREACH (refs, ref) + len += strlen (ref->name) + strlen (ref->ext) + 2; + + cur = data = xmalloc (len + 1); + GL_LIST_FOREACH (refs, ref) { + *cur++ = '\t'; + cur = stpcpy (cur, ref->name); + *cur++ = '\t'; + cur = stpcpy (cur, ref->ext); + } + + return data; +} + /* Any one of three situations can occur when storing some data. @@ -197,7 +256,10 @@ static datum make_content (struct mandata *in) int dbstore (MYDBM_FILE dbf, struct mandata *in, const char *base) { datum oldkey, oldcont; + gl_list_t refs; + struct name_ext *ref; char *value; + int ret = 0; memset (&oldkey, 0, sizeof oldkey); memset (&oldcont, 0, sizeof oldcont); @@ -243,16 +305,15 @@ int dbstore (MYDBM_FILE dbf, struct mandata *in, const char *base) if (MYDBM_INSERT (dbf, newkey, newcont)) { datum cont; - struct mandata info; - int ret; + struct mandata *info; MYDBM_FREE_DPTR (oldcont); cont = MYDBM_FETCH (dbf, newkey); - split_content (dbf, MYDBM_DPTR (cont), &info); - ret = replace_if_necessary (dbf, in, &info, + info = split_content (dbf, MYDBM_DPTR (cont)); + ret = replace_if_necessary (dbf, in, info, newkey, newcont); - /* MYDBM_FREE_DPTR (cont); */ - free_mandata_elements (&info); + MYDBM_FREE_DPTR (cont); + free_mandata_struct (info); MYDBM_FREE_DPTR (newkey); MYDBM_FREE_DPTR (newcont); MYDBM_FREE_DPTR (oldkey); @@ -268,9 +329,15 @@ int dbstore (MYDBM_FILE dbf, struct mandata *in, const char *base) MYDBM_FREE_DPTR (newkey); MYDBM_FREE_DPTR (newcont); - value = xasprintf ( - "%s\t%s\t%s", MYDBM_DPTR (oldcont), base, in->ext); - assert (value); + refs = list_extensions (MYDBM_DPTR (oldcont) + 1); + ref = XMALLOC (struct name_ext); + /* Not copied. */ + ref->name = base; + ref->ext = in->ext; + gl_sortedlist_add (refs, name_ext_compare, ref); + value = make_extensions_reference (refs); + gl_list_free (refs); + MYDBM_SET (newcont, value); MYDBM_FREE_DPTR (oldcont); @@ -282,7 +349,7 @@ int dbstore (MYDBM_FILE dbf, struct mandata *in, const char *base) MYDBM_FREE_DPTR (newcont); } else { /* situation (3) */ datum newkey, newcont, lastkey, lastcont; - struct mandata old; + struct mandata *old; char *old_name; memset (&newkey, 0, sizeof newkey); @@ -292,31 +359,29 @@ int dbstore (MYDBM_FILE dbf, struct mandata *in, const char *base) /* Extract the old singular reference */ - split_content (dbf, MYDBM_DPTR (oldcont), &old); + old = split_content (dbf, MYDBM_DPTR (oldcont)); /* Create multi keys for both old and new items, create new content */ - if (old.name) - old_name = xstrdup (old.name); + if (old->name) + old_name = xstrdup (old->name); else old_name = xstrdup (MYDBM_DPTR (oldkey)); - lastkey = make_multi_key (old_name, old.ext); + lastkey = make_multi_key (old_name, old->ext); /* Check against identical multi keys before inserting into db */ - if (STREQ (old_name, base) && STREQ (old.ext, in->ext)) { - int ret; - + if (STREQ (old_name, base) && STREQ (old->ext, in->ext)) { if (!STREQ (base, MYDBM_DPTR (oldkey))) in->name = xstrdup (base); newcont = make_content (in); - ret = replace_if_necessary (dbf, in, &old, + ret = replace_if_necessary (dbf, in, old, oldkey, newcont); - /* MYDBM_FREE_DPTR (oldcont); */ - free_mandata_elements (&old); + MYDBM_FREE_DPTR (oldcont); + free_mandata_struct (old); MYDBM_FREE_DPTR (newcont); MYDBM_FREE_DPTR (lastkey); MYDBM_FREE_DPTR (oldkey); @@ -330,12 +395,12 @@ int dbstore (MYDBM_FILE dbf, struct mandata *in, const char *base) /* Multi keys use the proper case, and so don't need a name * field. */ - if (old.name) { - free (old.name); - old.name = NULL; + if (old->name) { + free (old->name); + old->name = NULL; } - lastcont = make_content (&old); + lastcont = make_content (old); /* We always replace here; if the multi key already exists * in the database, then that indicates some kind of @@ -351,28 +416,39 @@ int dbstore (MYDBM_FILE dbf, struct mandata *in, const char *base) newkey = make_multi_key (base, in->ext); newcont = make_content (in); - if (MYDBM_REPLACE (dbf, newkey, newcont)) - gripe_replace_key (dbf, MYDBM_DPTR (newkey)); + ret = replace_if_necessary (dbf, in, old, newkey, newcont); MYDBM_FREE_DPTR (newkey); MYDBM_FREE_DPTR (newcont); /* Now build a simple reference to the above two items */ - value = xasprintf ( - "\t%s\t%s\t%s\t%s", old_name, old.ext, base, in->ext); - assert (value); + refs = gl_list_create_empty (GL_ARRAY_LIST, name_ext_equals, + NULL, plain_free, true); + ref = XMALLOC (struct name_ext); + /* Not copied. */ + ref->name = old_name; + ref->ext = old->ext; + gl_sortedlist_add (refs, name_ext_compare, ref); + ref = XMALLOC (struct name_ext); + /* Not copied. */ + ref->name = base; + ref->ext = in->ext; + gl_sortedlist_add (refs, name_ext_compare, ref); + value = make_extensions_reference (refs); + gl_list_free (refs); + MYDBM_SET (newcont, value); if (MYDBM_REPLACE (dbf, oldkey, newcont)) gripe_replace_key (dbf, MYDBM_DPTR (oldkey)); - /* MYDBM_FREE_DPTR (oldcont); */ - free_mandata_elements (&old); + MYDBM_FREE_DPTR (oldcont); + free_mandata_struct (old); MYDBM_FREE_DPTR (newcont); free (old_name); } MYDBM_FREE_DPTR (oldkey); - return 0; + return ret; } |