summaryrefslogtreecommitdiff
path: root/libdb/db_store.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdb/db_store.c')
-rw-r--r--libdb/db_store.c202
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;
}