/* 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. */ static int replace_if_necessary (struct mandata *newdata, struct mandata *olddata, datum newkey, datum newcont) { /* 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. */ if (compare_ids (newdata->id, olddata->id, 1) <= 0 && newdata->_st_mtime > olddata->_st_mtime) { debug ("replace_if_necessary(): newer mtime; replacing\n"); if (MYDBM_REPLACE (dbf, newkey, newcont)) gripe_replace_key (MYDBM_DPTR (newkey)); return 0; } if (compare_ids (newdata->id, olddata->id, 0) < 0) { if (MYDBM_REPLACE (dbf, newkey, newcont)) gripe_replace_key (MYDBM_DPTR (newkey)); return 0; } /* 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 */ } } debug ("ignoring differing ids: %s\n", MYDBM_DPTR (newkey)); return 0; }
/* scan for the page, print any matches */ static void do_apropos (MYDBM_FILE dbf, const char * const *pages, int num_pages, int *found) { datum key, cont; char **lowpages; int *found_here; int (*combine) (int, int *); int i; #ifndef BTREE datum nextkey; #else /* BTREE */ int end; #endif /* !BTREE */ lowpages = XNMALLOC (num_pages, char *); for (i = 0; i < num_pages; ++i) { lowpages[i] = lower (pages[i]); debug ("lower(%s) = \"%s\"\n", pages[i], lowpages[i]); } found_here = XNMALLOC (num_pages, int); combine = require_all ? all_set : any_set; #ifndef BTREE key = MYDBM_FIRSTKEY (dbf); while (MYDBM_DPTR (key)) { cont = MYDBM_FETCH (dbf, key); #else /* BTREE */ end = btree_nextkeydata (dbf, &key, &cont); while (!end) { #endif /* !BTREE */ char *tab; struct mandata info; memset (&info, 0, sizeof (info)); /* bug#4372, NULL pointer dereference in MYDBM_DPTR (cont), * fix by [email protected] (J.H.M.Dassen), thanx Ray. * cjwatson: In that case, complain and exit, otherwise we * might loop (bug #95052). */ if (!MYDBM_DPTR (cont)) { debug ("key was %s\n", MYDBM_DPTR (key)); error (FATAL, 0, _("Database %s corrupted; rebuild with " "mandb --create"), database); } if (*MYDBM_DPTR (key) == '$') goto nextpage; if (*MYDBM_DPTR (cont) == '\t') goto nextpage; /* a real page */ split_content (MYDBM_DPTR (cont), &info); /* If there are sections given, does any of them match * either the section or extension of this page? */ if (sections) { char * const *section; int matched = 0; for (section = sections; *section; ++section) { if (STREQ (*section, info.sec) || STREQ (*section, info.ext)) { matched = 1; break; } } if (!matched) goto nextpage; } tab = strrchr (MYDBM_DPTR (key), '\t'); if (tab) *tab = '\0'; memset (found_here, 0, num_pages * sizeof (*found_here)); if (am_apropos) { char *whatis; parse_name ((const char **) lowpages, num_pages, MYDBM_DPTR (key), found, found_here); whatis = info.whatis ? xstrdup (info.whatis) : NULL; if (!combine (num_pages, found_here) && whatis) parse_whatis (pages, lowpages, num_pages, whatis, found, found_here); free (whatis); } else parse_name (pages, num_pages, MYDBM_DPTR (key), found, found_here); if (combine (num_pages, found_here)) display (dbf, &info, MYDBM_DPTR (key)); if (tab) *tab = '\t'; nextpage: #ifndef BTREE nextkey = MYDBM_NEXTKEY (dbf, key); MYDBM_FREE_DPTR (cont); MYDBM_FREE_DPTR (key); key = nextkey; #else /* BTREE */ MYDBM_FREE_DPTR (cont); MYDBM_FREE_DPTR (key); end = btree_nextkeydata (dbf, &key, &cont); #endif /* !BTREE */ info.addr = NULL; /* == MYDBM_DPTR (cont), freed above */ free_mandata_elements (&info); } for (i = 0; i < num_pages; ++i) free (lowpages[i]); free (lowpages); } /* loop through the man paths, searching for a match */ static int search (const char * const *pages, int num_pages) { int *found = XCALLOC (num_pages, int); char *catpath, **mp; int any_found, i; for (mp = manpathlist; *mp; mp++) { MYDBM_FILE dbf; catpath = get_catpath (*mp, SYSTEM_CAT | USER_CAT); if (catpath) { database = mkdbname (catpath); free (catpath); } else database = mkdbname (*mp); debug ("path=%s\n", *mp); dbf = MYDBM_RDOPEN (database); if (dbf && dbver_rd (dbf)) { MYDBM_CLOSE (dbf); dbf = NULL; } if (!dbf) { use_grep (pages, num_pages, *mp, found); continue; } if (am_apropos) do_apropos (dbf, pages, num_pages, found); else { if (regex_opt || wildcard) do_apropos (dbf, pages, num_pages, found); else do_whatis (dbf, pages, num_pages, *mp, found); } free (database); database = NULL; MYDBM_CLOSE (dbf); } chkr_garbage_detector (); any_found = 0; for (i = 0; i < num_pages; ++i) { if (found[i]) any_found = 1; else fprintf (stderr, _("%s: nothing appropriate.\n"), pages[i]); } free (found); return any_found; }
int dbdelete (MYDBM_FILE dbf, const char *name, struct mandata *info) { datum key, cont; memset (&key, 0, sizeof key); memset (&cont, 0, sizeof cont); /* get entry for info */ debug ("Attempting delete of %s(%s) entry.\n", name, info->ext); MYDBM_SET (key, name_to_key (name)); cont = MYDBM_FETCH (dbf, key); if (!MYDBM_DPTR (cont)) { /* 0 entries */ MYDBM_FREE_DPTR (key); return NO_ENTRY; } else if (*MYDBM_DPTR (cont) != '\t') { /* 1 entry */ MYDBM_DELETE (dbf, key); MYDBM_FREE_DPTR (cont); } else { /* 2+ entries */ char **names, **ext; char *multi_content = NULL; datum multi_key; int refs, i, j; /* Extract all of the extensions associated with this key */ refs = list_extensions (MYDBM_DPTR (cont) + 1, &names, &ext); for (i = 0; i < refs; ++i) if (STREQ (names[i], name) && STREQ (ext[i], info->ext)) break; if (i >= refs) { free (names); free (ext); MYDBM_FREE_DPTR (cont); MYDBM_FREE_DPTR (key); return NO_ENTRY; } multi_key = make_multi_key (names[i], ext[i]); if (!MYDBM_EXISTS (dbf, multi_key)) { error (0, 0, _( "multi key %s does not exist"), MYDBM_DPTR (multi_key)); gripe_corrupt_data (); } MYDBM_DELETE (dbf, multi_key); MYDBM_FREE_DPTR (multi_key); /* refs *may* be 1 if all manual pages with this name have been deleted. In this case, we'll have to remove the key too */ if (refs == 1) { free (names); free (ext); MYDBM_FREE_DPTR (cont); MYDBM_DELETE (dbf, key); MYDBM_FREE_DPTR (key); return 0; } /* create our new multi content */ for (j = 0; j < refs; ++j) if (i != j) multi_content = appendstr (multi_content, "\t", names[j], "\t", ext[j], NULL); MYDBM_FREE_DPTR (cont); /* if refs = 2 do something else. Doesn't really matter as the gdbm db file does not shrink any after a deletion anyway */ MYDBM_SET (cont, multi_content); if (MYDBM_REPLACE (dbf, key, cont)) gripe_replace_key (MYDBM_DPTR (key)); free (names); free (ext); } MYDBM_FREE_DPTR (key); return 0; }
int dbstore (struct mandata *in, const char *base) { datum oldkey, oldcont; memset (&oldkey, 0, sizeof oldkey); memset (&oldcont, 0, sizeof oldcont); /* create a simple key */ MYDBM_SET (oldkey, name_to_key (base)); if (!*base) { dbprintf (in); return 2; } if (in->name) { error (0, 0, "in->name (%s) should not be set when calling " "dbstore()!\n", in->name); free (in->name); in->name = NULL; } /* get the content for the simple key */ oldcont = MYDBM_FETCH (dbf, oldkey); if (MYDBM_DPTR (oldcont) == NULL) { /* situation (1) */ if (!STREQ (base, MYDBM_DPTR (oldkey))) in->name = xstrdup (base); oldcont = make_content (in); if (MYDBM_REPLACE (dbf, oldkey, oldcont)) gripe_replace_key (MYDBM_DPTR (oldkey)); free (MYDBM_DPTR (oldcont)); free (in->name); in->name = NULL; } else if (*MYDBM_DPTR (oldcont) == '\t') { /* situation (2) */ datum newkey, newcont; memset (&newkey, 0, sizeof newkey); memset (&newcont, 0, sizeof newcont); newkey = make_multi_key (base, in->ext); newcont = make_content (in); /* Try to insert the new multi data */ if (MYDBM_INSERT (dbf, newkey, newcont)) { datum cont; struct mandata info; int ret; MYDBM_FREE (MYDBM_DPTR (oldcont)); cont = MYDBM_FETCH (dbf, newkey); split_content (MYDBM_DPTR (cont), &info); ret = replace_if_necessary (in, &info, newkey, newcont); /* MYDBM_FREE (MYDBM_DPTR (cont)); */ free_mandata_elements (&info); free (MYDBM_DPTR (newkey)); free (MYDBM_DPTR (newcont)); free (MYDBM_DPTR (oldkey)); return ret; } /* Now lets add some info to the simple key's cont. */ /* This next bit needs to be done first as we'll wipe out MYDBM_DPTR (oldcont) otherwise (for NDBM only!) */ free (MYDBM_DPTR (newkey)); free (MYDBM_DPTR (newcont)); MYDBM_SET (newcont, xasprintf ( "%s\t%s\t%s", MYDBM_DPTR (oldcont), base, in->ext)); MYDBM_FREE (MYDBM_DPTR (oldcont)); /* Try to replace the old simple data with the new stuff */ if (MYDBM_REPLACE (dbf, oldkey, newcont)) gripe_replace_key (MYDBM_DPTR (oldkey)); free (MYDBM_DPTR (newcont)); } else { /* situation (3) */ datum newkey, newcont, lastkey, lastcont; struct mandata old; char *old_name; memset (&newkey, 0, sizeof newkey); memset (&newcont, 0, sizeof newcont); memset (&lastkey, 0, sizeof lastkey); memset (&lastcont, 0, sizeof lastcont); /* Extract the old singular reference */ split_content (MYDBM_DPTR (oldcont), &old); /* Create multi keys for both old and new items, create new content */ if (old.name) old_name = xstrdup (old.name); else old_name = xstrdup (MYDBM_DPTR (oldkey)); 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 (base, MYDBM_DPTR (oldkey))) in->name = xstrdup (base); newcont = make_content (in); ret = replace_if_necessary (in, &old, oldkey, newcont); /* MYDBM_FREE (MYDBM_DPTR (oldcont)); */ free_mandata_elements (&old); free (MYDBM_DPTR (newcont)); free (MYDBM_DPTR (lastkey)); free (MYDBM_DPTR (oldkey)); free (old_name); free (in->name); in->name = NULL; return ret; } /* Multi keys use the proper case, and so don't need a name * field. */ if (old.name) { free (old.name); old.name = NULL; } lastcont = make_content (&old); /* We always replace here; if the multi key already exists * in the database, then that indicates some kind of * database corruption, but our new multi key is almost * certainly better. */ if (MYDBM_REPLACE (dbf, lastkey, lastcont)) gripe_replace_key (MYDBM_DPTR (lastkey)); free (MYDBM_DPTR (lastkey)); free (MYDBM_DPTR (lastcont)); newkey = make_multi_key (base, in->ext); newcont = make_content (in); if (MYDBM_REPLACE (dbf, newkey, newcont)) gripe_replace_key (MYDBM_DPTR (newkey)); free (MYDBM_DPTR (newkey)); free (MYDBM_DPTR (newcont)); /* Now build a simple reference to the above two items */ MYDBM_SET (newcont, xasprintf ( "\t%s\t%s\t%s\t%s", old_name, old.ext, base, in->ext)); if (MYDBM_REPLACE (dbf, oldkey, newcont)) gripe_replace_key (MYDBM_DPTR (oldkey)); /* MYDBM_FREE (MYDBM_DPTR (oldcont)); */ free_mandata_elements (&old); free (MYDBM_DPTR (newcont)); free (old_name); } free (MYDBM_DPTR (oldkey)); return 0; }