(root)/
man-db-2.12.0/
libdb/
db_store.c
       1  /*
       2   * db_store.c: dbstore(), database storage routine.
       3   *
       4   * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.)
       5   * Copyright (C) 2001, 2002 Colin Watson.
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Library General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2 of the License, or (at your option) any later version.
      11   *
      12   * This library is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Library General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Library General Public
      18   * License along with this library; if not, write to the Free Software
      19   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      20   *
      21   * Mon Aug  8 20:35:30 BST 1994  Wilf. (G.Wilford@ee.surrey.ac.uk)
      22   */
      23  
      24  #ifdef HAVE_CONFIG_H
      25  #  include "config.h"
      26  #endif /* HAVE_CONFIG_H */
      27  
      28  #include <assert.h>
      29  #include <stdbool.h>
      30  #include <stdio.h>
      31  #include <string.h>
      32  #include <stdlib.h>
      33  #include <unistd.h>
      34  
      35  #include "attribute.h"
      36  #include "error.h"
      37  #include "gl_array_list.h"
      38  #include "gl_xlist.h"
      39  #include "timespec.h"
      40  #include "xalloc.h"
      41  #include "xvasprintf.h"
      42  
      43  #include "manconfig.h"
      44  
      45  #include "debug.h"
      46  #include "filenames.h"
      47  #include "glcontainers.h"
      48  
      49  #include "mydbm.h"
      50  #include "db_storage.h"
      51  
      52  /* compare_ids(a,b) is negative if id 'a' is preferred to id 'b', i.e. if
      53   * 'a' is a more canonical database entry than 'b'; positive if 'b' is
      54   * preferred to 'a'; and zero if they are equivalent. This usually goes in
      55   * comparison order, but there's a special exception when FAVOUR_STRAYCATS
      56   * is set.
      57   *
      58   * If promote_links is true, consider SO_MAN equivalent to ULT_MAN. This is
      59   * appropriate when sorting candidate pages for display.
      60   */
      61  int ATTRIBUTE_CONST compare_ids (char a, char b, bool promote_links)
      62  {
      63  #ifdef FAVOUR_STRAYCATS
      64  	if (a == WHATIS_MAN && b == STRAY_CAT)
      65  		return 1;
      66  	else if (a == STRAY_CAT && b == WHATIS_MAN)
      67  		return -1;
      68  #endif
      69  
      70  	if (promote_links) {
      71  		if ((a == ULT_MAN && b == SO_MAN) ||
      72  		    (a == SO_MAN && b == ULT_MAN))
      73  			return 0;
      74  	}
      75  
      76  	if (a < b)
      77  		return -1;
      78  	else if (a > b)
      79  		return 1;
      80  	else
      81  		return 0;
      82  }
      83  
      84  enum replace_action {
      85  	REPLACE_YES = 0,
      86  	REPLACE_NO,
      87  	REPLACE_FAIL
      88  };
      89  
      90  /* The do_we_replace logic. Decide, for some existing key, whether it should
      91   * be replaced with some new contents. Check that names and section
      92   * extensions match before calling this.
      93   */
      94  static int replace_if_necessary (MYDBM_FILE dbf,
      95  				 struct mandata *newdata,
      96  				 struct mandata *olddata,
      97  				 datum newkey, datum newcont)
      98  {
      99  	enum replace_action action;
     100  
     101  	/* It's OK to replace ULT_MAN with SO_MAN if the mtime is newer. It
     102  	 * isn't OK to replace a real page (either ULT_MAN or SO_MAN) with a
     103  	 * whatis reference; if the real page really went away then
     104  	 * purge_missing will catch that in time, but a real page that still
     105  	 * exists should always take precedence.
     106  	 *
     107  	 * Tie-break whatis references by lexicographical sort of
     108  	 * the pointed-to page names, which isn't great but at least
     109  	 * gives us something reproducible.
     110  	 *
     111  	 * TODO: name fields should be collated with the requested name
     112  	 */
     113  	if (compare_ids (newdata->id, olddata->id, false) < 0) {
     114  		debug ("replace_if_necessary: stronger ID; replacing\n");
     115  		action = REPLACE_YES;
     116  	} else if (compare_ids (newdata->id, olddata->id, true) <= 0 &&
     117  		   timespec_cmp (newdata->mtime, olddata->mtime) > 0) {
     118  		debug ("replace_if_necessary: newer mtime; replacing\n");
     119  		action = REPLACE_YES;
     120  	} else if (compare_ids (newdata->id, olddata->id, true) <= 0 &&
     121  		   timespec_cmp (newdata->mtime, olddata->mtime) < 0) {
     122  		debug ("replace_if_necessary: older mtime; not replacing\n");
     123  		action = REPLACE_NO;
     124  	} else if (compare_ids (newdata->id, olddata->id, false) > 0) {
     125  		debug ("replace_if_necessary: weaker ID; not replacing\n");
     126  		action = REPLACE_NO;
     127  	} else if (newdata->pointer && olddata->pointer &&
     128  		   strcmp (newdata->pointer, olddata->pointer) < 0) {
     129  		debug ("replace_if_necessary: pointer '%s' < '%s'; "
     130  		       "replacing\n", newdata->pointer, olddata->pointer);
     131  		action = REPLACE_YES;
     132  	} else if (newdata->pointer && olddata->pointer &&
     133  		   strcmp (newdata->pointer, olddata->pointer) > 0) {
     134  		debug ("replace_if_necessary: pointer '%s' > '%s'; "
     135  		       "not replacing\n", newdata->pointer, olddata->pointer);
     136  		action = REPLACE_NO;
     137  	} else if (!STREQ (dash_if_unset (newdata->comp),
     138  			  olddata->comp)) {
     139  		debug ("replace_if_necessary: differing compression "
     140  		       "extensions (%s != %s); failing\n",
     141  		       dash_if_unset (newdata->comp), olddata->comp);
     142  		action = REPLACE_FAIL;
     143  	} else {
     144  		debug ("replace_if_necessary: match; not replacing\n");
     145  		action = REPLACE_NO;
     146  	}
     147  
     148  	switch (action) {
     149  		case REPLACE_YES:
     150  			if (MYDBM_REPLACE (dbf, newkey, newcont))
     151  				gripe_replace_key (dbf, MYDBM_DPTR (newkey));
     152  			return 0;
     153  		case REPLACE_NO:
     154  			/* Insert if missing, but ignore failures. */
     155  			MYDBM_INSERT (dbf, newkey, newcont);
     156  			return 0;
     157  		default:
     158  			return 1;
     159  	}
     160  }
     161  
     162  /* The complement of split_content */
     163  static datum make_content (struct mandata *in)
     164  {
     165  	datum cont;
     166  	static const char dash[] = "-";
     167  	char *value;
     168  
     169  	memset (&cont, 0, sizeof cont);
     170  
     171  	if (!in->pointer)
     172  		in->pointer = xstrdup (dash);
     173  	if (!in->comp)
     174  		in->comp = xstrdup (dash);
     175  	if (!in->filter)
     176  		in->filter = xstrdup (dash);
     177  	if (!in->whatis)
     178  		in->whatis = xstrdup (dash + 1);
     179  
     180  	value = xasprintf (
     181  		"%s\t%s\t%s\t%ld\t%ld\t%c\t%s\t%s\t%s\t%s",
     182  		dash_if_unset (in->name),
     183  		in->ext,
     184  		in->sec,
     185  		(long) in->mtime.tv_sec,
     186  		(long) in->mtime.tv_nsec,
     187  		in->id,
     188  		in->pointer,
     189  		in->filter,
     190  		in->comp,
     191  		in->whatis);
     192  	assert (value);
     193  	MYDBM_SET (cont, value);
     194  
     195  #ifdef NDBM
     196  	/* limit of 4096 bytes of data using ndbm */
     197  	if (MYDBM_DSIZE (cont) > 4095) {
     198  		MYDBM_DPTR (cont)[4095] = '\0';
     199  		MYDBM_DSIZE (cont) = 4096;
     200  	}
     201  #endif
     202  	return cont;
     203  }
     204  
     205  /* The complement of list_extensions. */
     206  static char *make_extensions_reference (gl_list_t refs)
     207  {
     208  	struct name_ext *ref;
     209  	size_t len = 0;
     210  	char *data, *cur;
     211  
     212  	GL_LIST_FOREACH (refs, ref)
     213  		len += strlen (ref->name) + strlen (ref->ext) + 2;
     214  
     215  	cur = data = xmalloc (len + 1);
     216  	GL_LIST_FOREACH (refs, ref) {
     217  		*cur++ = '\t';
     218  		cur = stpcpy (cur, ref->name);
     219  		*cur++ = '\t';
     220  		cur = stpcpy (cur, ref->ext);
     221  	}
     222  
     223  	return data;
     224  }
     225  
     226  /*
     227   Any one of three situations can occur when storing some data.
     228  
     229   1) no simple key is found.
     230   	store as singular reference.
     231   2) simple key already exists, content starts with a '\t'.
     232   	Already multiple reference. Add our new item in multiple format
     233   	and update the simple key content, to point to our new one also.
     234   3) simple key already exists, content does not start with a '\t'.
     235   	First we have to reformat the simple key into a multi key for the
     236   	old item, and insert. Then we have to insert the new data as a
     237   	multi key. Lastly we must create the simple key and do a replace
     238   	on it.
     239  
     240   Use precedence algorithm on inserts. If we already have a key assigned
     241   to the new value, check priority of page using id. If new page is higher
     242   (lower value), replace old with new, otherwise ignore new page.
     243  
     244   If we have two ULT_MAN pages competing for the same key, we must have
     245   more than one of foo.sec, foo.sec.comp1, foo.sec.comp2. OR we have a
     246   replacement page. If the mtimes differ, throw out the old struct and
     247   replace it with the new, if the comp exts differ, oops, this is bad,
     248   keep one and return appropriate error code.
     249  
     250   If we have two WHATIS_MAN pages or a WHATIS_MAN and a SO_MAN page
     251   competing for the same key, don't worry. This will happen a lot and is
     252   not a problem.
     253  
     254   return errorcode or 0 on success.
     255  */
     256  int dbstore (MYDBM_FILE dbf, struct mandata *in, const char *base)
     257  {
     258  	datum oldkey, oldcont;
     259  	gl_list_t refs;
     260  	struct name_ext *ref;
     261  	char *value;
     262  	int ret = 0;
     263  
     264  	memset (&oldkey, 0, sizeof oldkey);
     265  	memset (&oldcont, 0, sizeof oldcont);
     266  
     267  	/* create a simple key */
     268  	MYDBM_SET (oldkey, name_to_key (base));
     269   	if (!*base) {
     270  		dbprintf (in);
     271   		return 2;
     272   	}
     273  
     274  	if (in->name) {
     275  		error (0, 0, "in->name (%s) should not be set when calling "
     276  			     "dbstore()!\n",
     277  		       in->name);
     278  		free (in->name);
     279  		in->name = NULL;
     280  	}
     281  
     282  	/* get the content for the simple key */
     283  
     284  	oldcont = MYDBM_FETCH (dbf, oldkey);
     285  
     286  	if (MYDBM_DPTR (oldcont) == NULL) { 		/* situation (1) */
     287  		if (!STREQ (base, MYDBM_DPTR (oldkey)))
     288  			in->name = xstrdup (base);
     289  		oldcont = make_content (in);
     290  		if (MYDBM_REPLACE (dbf, oldkey, oldcont))
     291  			gripe_replace_key (dbf, MYDBM_DPTR (oldkey));
     292  		MYDBM_FREE_DPTR (oldcont);
     293  		free (in->name);
     294  		in->name = NULL;
     295  	} else if (*MYDBM_DPTR (oldcont) == '\t') { 	/* situation (2) */
     296  		datum newkey, newcont;
     297  
     298  		memset (&newkey, 0, sizeof newkey);
     299  		memset (&newcont, 0, sizeof newcont);
     300  
     301  		newkey = make_multi_key (base, in->ext);
     302  		newcont = make_content (in);
     303  
     304  		/* Try to insert the new multi data */
     305  
     306  		if (MYDBM_INSERT (dbf, newkey, newcont)) {
     307  			datum cont;
     308  			struct mandata *info;
     309  
     310  			MYDBM_FREE_DPTR (oldcont);
     311  			cont = MYDBM_FETCH (dbf, newkey);
     312  			info = split_content (dbf, MYDBM_DPTR (cont));
     313  			ret = replace_if_necessary (dbf, in, info,
     314  						    newkey, newcont);
     315  			MYDBM_FREE_DPTR (cont);
     316  			free_mandata_struct (info);
     317  			MYDBM_FREE_DPTR (newkey);
     318  			MYDBM_FREE_DPTR (newcont);
     319  			MYDBM_FREE_DPTR (oldkey);
     320  
     321  			return ret;
     322  		}
     323  
     324  		/* Now lets add some info to the simple key's cont. */
     325  
     326  		/* This next bit needs to be done first as we'll wipe out
     327  		   MYDBM_DPTR (oldcont) otherwise (for NDBM only!) */
     328  
     329  		MYDBM_FREE_DPTR (newkey);
     330  		MYDBM_FREE_DPTR (newcont);
     331  
     332  		refs = list_extensions (MYDBM_DPTR (oldcont) + 1);
     333  		ref = XMALLOC (struct name_ext);
     334  		/* Not copied. */
     335  		ref->name = base;
     336  		ref->ext = in->ext;
     337  		gl_sortedlist_add (refs, name_ext_compare, ref);
     338  		value = make_extensions_reference (refs);
     339  		gl_list_free (refs);
     340  
     341  		MYDBM_SET (newcont, value);
     342  		MYDBM_FREE_DPTR (oldcont);
     343  
     344  		/* Try to replace the old simple data with the new stuff */
     345  
     346  		if (MYDBM_REPLACE (dbf, oldkey, newcont))
     347  			gripe_replace_key (dbf, MYDBM_DPTR (oldkey));
     348  
     349  		MYDBM_FREE_DPTR (newcont);
     350  	} else { 				/* situation (3) */
     351  		datum newkey, newcont, lastkey, lastcont;
     352  		struct mandata *old;
     353  		char *old_name;
     354  
     355  		memset (&newkey, 0, sizeof newkey);
     356  		memset (&newcont, 0, sizeof newcont);
     357  		memset (&lastkey, 0, sizeof lastkey);
     358  		memset (&lastcont, 0, sizeof lastcont);
     359  
     360  		/* Extract the old singular reference */
     361  
     362  		old = split_content (dbf, MYDBM_DPTR (oldcont));
     363  
     364  		/* Create multi keys for both old
     365  		   and new items, create new content */
     366  
     367  		if (old->name)
     368  			old_name = xstrdup (old->name);
     369  		else
     370  			old_name = xstrdup (MYDBM_DPTR (oldkey));
     371  
     372  		lastkey = make_multi_key (old_name, old->ext);
     373  
     374  		/* Check against identical multi keys before inserting
     375  		   into db */
     376  
     377  		if (STREQ (old_name, base) && STREQ (old->ext, in->ext)) {
     378  			if (!STREQ (base, MYDBM_DPTR (oldkey)))
     379  				in->name = xstrdup (base);
     380  			newcont = make_content (in);
     381  			ret = replace_if_necessary (dbf, in, old,
     382  						    oldkey, newcont);
     383  			MYDBM_FREE_DPTR (oldcont);
     384  			free_mandata_struct (old);
     385  			MYDBM_FREE_DPTR (newcont);
     386  			MYDBM_FREE_DPTR (lastkey);
     387  			MYDBM_FREE_DPTR (oldkey);
     388  			free (old_name);
     389  			free (in->name);
     390  			in->name = NULL;
     391  
     392  			return ret;
     393  		}
     394  
     395  		/* Multi keys use the proper case, and so don't need a name
     396  		 * field.
     397  		 */
     398  		if (old->name) {
     399  			free (old->name);
     400  			old->name = NULL;
     401  		}
     402  
     403  		lastcont = make_content (old);
     404  
     405  		/* We always replace here; if the multi key already exists
     406  		 * in the database, then that indicates some kind of
     407  		 * database corruption, but our new multi key is almost
     408  		 * certainly better.
     409  		 */
     410  		if (MYDBM_REPLACE (dbf, lastkey, lastcont))
     411  			gripe_replace_key (dbf, MYDBM_DPTR (lastkey));
     412  
     413  		MYDBM_FREE_DPTR (lastkey);
     414  		MYDBM_FREE_DPTR (lastcont);
     415  
     416  		newkey = make_multi_key (base, in->ext);
     417  		newcont = make_content (in);
     418  
     419  		ret = replace_if_necessary (dbf, in, old, newkey, newcont);
     420  
     421  		MYDBM_FREE_DPTR (newkey);
     422  		MYDBM_FREE_DPTR (newcont);
     423  
     424  		/* Now build a simple reference to the above two items */
     425  
     426  		refs = gl_list_create_empty (GL_ARRAY_LIST, name_ext_equals,
     427  					     NULL, plain_free, true);
     428  		ref = XMALLOC (struct name_ext);
     429  		/* Not copied. */
     430  		ref->name = old_name;
     431  		ref->ext = old->ext;
     432  		gl_sortedlist_add (refs, name_ext_compare, ref);
     433  		ref = XMALLOC (struct name_ext);
     434  		/* Not copied. */
     435  		ref->name = base;
     436  		ref->ext = in->ext;
     437  		gl_sortedlist_add (refs, name_ext_compare, ref);
     438  		value = make_extensions_reference (refs);
     439  		gl_list_free (refs);
     440  
     441  		MYDBM_SET (newcont, value);
     442  
     443  		if (MYDBM_REPLACE (dbf, oldkey, newcont))
     444  			gripe_replace_key (dbf, MYDBM_DPTR (oldkey));
     445  
     446  		MYDBM_FREE_DPTR (oldcont);
     447  		free_mandata_struct (old);
     448  		MYDBM_FREE_DPTR (newcont);
     449  		free (old_name);
     450  	}
     451  
     452  	MYDBM_FREE_DPTR (oldkey);
     453  	return ret;
     454  }