summaryrefslogtreecommitdiff
path: root/src/ult_src.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ult_src.c')
-rw-r--r--src/ult_src.c389
1 files changed, 228 insertions, 161 deletions
diff --git a/src/ult_src.c b/src/ult_src.c
index 69afb712..08413d20 100644
--- a/src/ult_src.c
+++ b/src/ult_src.c
@@ -33,6 +33,7 @@
#endif /* HAVE_CONFIG_H */
#include <string.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
@@ -46,7 +47,10 @@
#include "canonicalize.h"
#include "dirname.h"
#include "error.h"
+#include "gl_array_list.h"
+#include "gl_hash_map.h"
#include "gl_xlist.h"
+#include "gl_xmap.h"
#include "xalloc.h"
#include "xstrndup.h"
#include "xvasprintf.h"
@@ -56,13 +60,24 @@
#include "manconfig.h"
+#include "compression.h"
#include "debug.h"
+#include "glcontainers.h"
-#include "compression.h"
#include "decompress.h"
-#include "globbing.h"
#include "ult_src.h"
+void gripe_canonicalize_failed (const char *path)
+{
+ if (quiet < 2) {
+ if (errno == ENOENT)
+ error (0, 0, _("warning: %s is a dangling symlink"),
+ path);
+ else
+ error (0, errno, _("can't resolve %s"), path);
+ }
+}
+
/* Find minimum value hard link filename for given file and inode.
* Returns a newly allocated string.
*/
@@ -122,15 +137,7 @@ static char *ult_softlink (const char *fullpath)
resolved_path = canonicalize_file_name (fullpath);
if (!resolved_path) {
/* discard the unresolved path */
- if (quiet < 2) {
- if (errno == ENOENT)
- error (0, 0,
- _("warning: %s is a dangling symlink"),
- fullpath);
- else
- error (0, errno, _("can't resolve %s"),
- fullpath);
- }
+ gripe_canonicalize_failed (fullpath);
return NULL;
}
@@ -139,6 +146,33 @@ static char *ult_softlink (const char *fullpath)
return resolved_path;
}
+static char *find_include_directive (char *path)
+{
+ decompress *decomp;
+ const char *buffer;
+ char *directive;
+
+ decomp = decompress_open (path, DECOMPRESS_ALLOW_INPROCESS);
+ if (!decomp) {
+ if (quiet < 2)
+ error (0, errno, _("can't open %s"), path);
+ return NULL;
+ }
+ decompress_start (decomp);
+
+ /* make sure that we skip over any comments */
+ do {
+ buffer = decompress_readline (decomp);
+ } while (buffer && STRNEQ (buffer, ".\\\"", 3));
+
+ directive = xstrdup (buffer ? buffer : "");
+
+ decompress_wait (decomp);
+ decompress_free (decomp);
+
+ return directive;
+}
+
/* Test 'buffer' to see if it contains a .so include. If so and it's not an
* absolute filename, return newly allocated string whose contents are the
* include.
@@ -183,203 +217,236 @@ static char *test_for_include (const char *buffer)
static char *find_include (const char *name, const char *path,
const char *include)
{
- char *ret;
- char *dirname;
- char *temp_file;
+ char *target;
+ struct compression *comp;
/* Restore the original path from before ult_softlink() etc., in
* case it went outside the mantree.
*/
- ret = xasprintf ("%s/%s", path, include);
- assert (ret);
+ target = xasprintf ("%s/%s", path, include);
+ assert (target);
/* If the original path from above doesn't exist, try to create new
* path as if the "include" was relative to the current man page.
*/
- if (CAN_ACCESS (ret, F_OK))
- return ret;
-
- dirname = dir_name (name);
- temp_file = xasprintf ("%s/%s", dirname, include);
- assert (temp_file);
- free (dirname);
-
- if (CAN_ACCESS (temp_file, F_OK)) {
- /* Just plain include. */
- free (ret);
- ret = canonicalize_file_name (temp_file);
- } else {
- /* Try globbing - the file suffix might be missing. */
- char *temp_file_asterisk = xasprintf ("%s*", temp_file);
- gl_list_t candidate_files = expand_path (temp_file_asterisk);
-
- free (temp_file_asterisk);
- if (gl_list_size (candidate_files)) {
- const char *candidate_file = gl_list_get_at
- (candidate_files, 0);
- if (CAN_ACCESS (candidate_file, F_OK)) {
- free (ret);
- ret = canonicalize_file_name (candidate_file);
+ if (!CAN_ACCESS (target, F_OK)) {
+ comp = comp_file (target);
+ free (target);
+ if (comp) {
+ target = comp->stem;
+ comp->stem = NULL; /* steal memory */
+ } else
+ target = NULL;
+ }
+
+ if (!target) {
+ char *dirname = dir_name (name);
+ char *temp_file = xasprintf ("%s/%s", dirname, include);
+ assert (temp_file);
+ free (dirname);
+
+ if (CAN_ACCESS (temp_file, F_OK))
+ /* Just plain include. */
+ target = xstrdup (temp_file);
+ else {
+ comp = comp_file (temp_file);
+ if (comp) {
+ target = comp->stem;
+ comp->stem = NULL; /* steal memory */
}
}
- gl_list_free (candidate_files);
+ free (temp_file);
}
- free (temp_file);
- return ret;
+ if (target) {
+ char *canonicalized = canonicalize_file_name (target);
+ if (canonicalized)
+ return canonicalized;
+ else {
+ gripe_canonicalize_failed (target);
+ free (target);
+ return NULL;
+ }
+ } else {
+ if (quiet < 2)
+ error (0, 0, _("can't resolve %s"), include);
+ return NULL;
+ }
}
+struct ult_key {
+ char *name;
+ int flags;
+};
+
+static struct ult_key *ult_key_new (const char *name, int flags)
+{
+ struct ult_key *ukey = XMALLOC (struct ult_key);
+ ukey->name = xstrdup (name);
+ ukey->flags = flags;
+ return ukey;
+}
+
+static bool ATTRIBUTE_PURE ult_key_equals (const void *key1, const void *key2)
+{
+ struct ult_key *ukey1 = (struct ult_key *) key1;
+ struct ult_key *ukey2 = (struct ult_key *) key2;
+ return ukey1->flags == ukey2->flags &&
+ STREQ (ukey1->name, ukey2->name);
+}
+
+static size_t ATTRIBUTE_PURE ult_key_hash (const void *key)
+{
+ struct ult_key *ukey = (struct ult_key *) key;
+ return string_hash (ukey->name) ^ (size_t) ukey->flags;
+}
+
+static void ult_key_free (const void *key)
+{
+ struct ult_key *ukey = (struct ult_key *) key;
+ free (ukey->name);
+ free (ukey);
+}
+
+static struct ult_value *ult_value_new (void)
+{
+ struct ult_value *uvalue = XMALLOC (struct ult_value);
+ uvalue->path = NULL;
+ uvalue->trace = new_string_list (GL_ARRAY_LIST, true);
+ return uvalue;
+}
+
+static void ult_value_free (const void *value)
+{
+ struct ult_value *uvalue = (struct ult_value *) value;
+ if (uvalue) {
+ free (uvalue->path);
+ gl_list_free (uvalue->trace);
+ free (uvalue);
+ }
+}
+
+gl_map_t ult_cache = NULL;
+
/*
- * recursive function which finds the ultimate source file by following
- * any ".so filename" directives in the first line of the man pages.
- * Also (optionally) traces symlinks and hard links(!).
+ * Find the ultimate source file by following any ".so filename" directives
+ * in the first line of the man pages. Also (optionally) trace symlinks and
+ * hard links(!).
*
* name is full pathname, path is the MANPATH directory (/usr/man)
* flags is a combination of SO_LINK | SOFT_LINK | HARD_LINK
*/
-const char *ult_src (const char *name, const char *path,
- struct stat *buf, int flags, gl_list_t trace)
+const struct ult_value *ult_src (const char *name, const char *path,
+ struct stat *buf, int flags)
{
- static char *base; /* must be static */
- static short recurse; /* must be static */
+ char *base = xstrdup (name);
+ struct ult_key *key;
+ const struct ult_value *existing;
+ struct ult_value *value;
+ struct stat new_buf;
+
+ if (!ult_cache)
+ ult_cache = gl_map_create_empty (GL_HASH_MAP,
+ ult_key_equals, ult_key_hash,
+ ult_key_free, ult_value_free);
+ key = ult_key_new (name, flags);
+ if (gl_map_search (ult_cache, key, (const void **) &existing)) {
+ ult_key_free (key);
+ return existing;
+ }
+ value = ult_value_new ();
- /* initialise the function */
+ debug ("ult_src: File %s in mantree %s\n", name, path);
- if (trace)
- gl_list_add_last (trace, xstrdup (name));
+ gl_list_add_last (value->trace, xstrdup (name));
/* as ult_softlink() & ult_hardlink() do all of their respective
* resolving in one call, only need to sort them out once
*/
- if (recurse == 0) {
- struct stat new_buf;
- free (base);
- base = xstrdup (name);
-
- debug ("\nult_src: File %s in mantree %s\n", name, path);
-
- /* If we don't have a buf, allocate and assign one */
- if (!buf && ((flags & SOFT_LINK) || (flags & HARD_LINK))) {
- buf = &new_buf;
- if (lstat (base, buf) == -1) {
- if (quiet < 2)
- error (0, errno, _("can't resolve %s"),
- base);
- return NULL;
- }
- }
-
- /* Permit semi local (inter-tree) soft links */
- if (flags & SOFT_LINK) {
- assert (buf); /* initialised above */
- if (S_ISLNK (buf->st_mode)) {
- /* Is a symlink, resolve it. */
- char *softlink = ult_softlink (base);
- if (softlink) {
- free (base);
- base = softlink;
- } else
- return NULL;
- }
- }
-
- /* Only deal with local (inter-dir) HARD links */
- if (flags & HARD_LINK) {
- assert (buf); /* initialised above */
- if (buf->st_nlink > 1) {
- /* Has HARD links, find least value */
- char *hardlink = ult_hardlink (base,
- buf->st_ino);
- if (hardlink) {
- free (base);
- base = hardlink;
- }
- }
+ /* If we don't have a buf, allocate and assign one */
+ if (!buf && ((flags & SOFT_LINK) || (flags & HARD_LINK))) {
+ buf = &new_buf;
+ if (lstat (base, buf) == -1) {
+ if (quiet < 2)
+ error (0, errno, _("can't resolve %s"), base);
+ goto err;
}
}
- /* keep a check on recursion level */
- else if (recurse == 10) {
- if (quiet < 2)
- error (0, 0, _("%s is self referencing"), name);
- return NULL;
+ /* Permit semi local (inter-tree) soft links */
+ if (flags & SOFT_LINK) {
+ assert (buf); /* initialised above */
+ if (S_ISLNK (buf->st_mode)) {
+ /* Is a symlink, resolve it. */
+ char *softlink = ult_softlink (base);
+ if (softlink) {
+ free (base);
+ base = softlink;
+ } else
+ goto err;
+ }
}
- if (flags & SO_LINK) {
- const char *buffer;
- char *decomp_base;
- decompress *decomp;
- char *include;
- struct stat st;
-
- if (stat (base, &st) < 0) {
- struct compression *comp = comp_file (base);
-
- if (comp) {
+ /* Only deal with local (inter-dir) HARD links */
+ if (flags & HARD_LINK) {
+ assert (buf); /* initialised above */
+ if (buf->st_nlink > 1) {
+ /* Has HARD links, find least value */
+ char *hardlink = ult_hardlink (base,
+ buf->st_ino);
+ if (hardlink) {
free (base);
- base = comp->stem;
- comp->stem = NULL; /* steal memory */
- } else {
- if (quiet < 2)
- error (0, errno, _("can't open %s"),
- base);
- return NULL;
+ base = hardlink;
}
}
+ }
- /* base may change for recursive calls to ult_src, but
- * decompress_open doesn't keep its own copy.
- */
- decomp_base = xstrdup (base);
- decomp = decompress_open (decomp_base,
- DECOMPRESS_ALLOW_INPROCESS);
- if (!decomp) {
- if (quiet < 2)
- error (0, errno, _("can't open %s"), base);
- free (decomp_base);
- return NULL;
- }
- decompress_start (decomp);
+ if (flags & SO_LINK) {
+ int i;
+ for (i = 0; i < 10; ++i) {
+ char *directive, *include;
- /* make sure that we skip over any comments */
- do {
- buffer = decompress_readline (decomp);
- } while (buffer && STRNEQ (buffer, ".\\\"", 3));
+ directive = find_include_directive (base);
+ if (!directive)
+ goto err;
- include = test_for_include (buffer);
- if (include) {
- char *new_name;
- const char *ult;
+ include = test_for_include (directive);
+ free (directive);
+ if (!include)
+ break;
free (base);
base = find_include (name, path, include);
free (include);
+ if (!base)
+ goto err;
debug ("ult_src: points to %s\n", base);
- recurse++;
- /* Take a copy; it's unwise to pass base directly to
- * a recursive call, as it may be freed.
- */
- new_name = xstrdup (base);
- ult = ult_src (new_name, path, NULL, flags, trace);
- free (new_name);
- recurse--;
-
- decompress_wait (decomp);
- decompress_free (decomp);
- free (decomp_base);
- return ult;
+ gl_list_add_last (value->trace, xstrdup (base));
+ }
+ if (i == 10) {
+ if (quiet < 2)
+ error (0, 0, _("%s is self referencing"),
+ name);
+ goto err;
}
-
- decompress_wait (decomp);
- decompress_free (decomp);
- free (decomp_base);
}
/* We have the ultimate source */
- if (trace)
- gl_list_add_last (trace, xstrdup (base));
- return base;
+ value->path = xstrdup (base);
+ gl_list_add_last (value->trace, xstrdup (base));
+ gl_map_put (ult_cache, key, value);
+ free (base);
+ return value;
+
+err:
+ /* The cache is short-lived and only within a single process, so
+ * negative caching is fine.
+ */
+ ult_value_free (value);
+ gl_map_put (ult_cache, key, NULL);
+ free (base);
+ return NULL;
}