diff options
Diffstat (limited to 'src/ult_src.c')
-rw-r--r-- | src/ult_src.c | 389 |
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; } |