diff options
Diffstat (limited to '')
-rw-r--r-- | src/mandb.c | 954 |
1 files changed, 954 insertions, 0 deletions
diff --git a/src/mandb.c b/src/mandb.c new file mode 100644 index 0000000..90325d9 --- /dev/null +++ b/src/mandb.c @@ -0,0 +1,954 @@ +/* + * mandb.c: used to create and initialise global man database. + * + * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.) + * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011, + * 2012 Colin Watson. + * + * This file is part of man-db. + * + * man-db is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * man-db is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with man-db; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Tue Apr 26 12:56:44 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + * + * CJW: Security fixes. Make --test work. Purge old database entries. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> /* for chmod() */ +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> +#include <signal.h> + +#ifdef MAN_OWNER +# include <pwd.h> +#endif /* MAN_OWNER */ + +#include "argp.h" +#include "dirname.h" +#include "progname.h" +#include "stat-time.h" +#include "timespec.h" +#include "utimens.h" +#include "xgetcwd.h" +#include "xvasprintf.h" + +#include "gettext.h" +#define _(String) gettext (String) +#define N_(String) gettext_noop (String) + +#include "manconfig.h" + +#include "error.h" +#include "cleanup.h" +#include "hashtable.h" +#include "pipeline.h" +#include "sandbox.h" +#include "security.h" + +#include "mydbm.h" + +#include "check_mandirs.h" +#include "filenames.h" +#include "manp.h" + +int quiet = 1; +extern int opt_test; /* don't update db */ +char *manp; +char *database = NULL; +extern char *extension; /* for globbing.c */ +extern int force_rescan; /* for check_mandirs.c */ +static char *single_filename = NULL; +extern char *user_config_file; /* for manp.c */ +#ifdef MAN_OWNER +struct passwd *man_owner; +#endif +man_sandbox *sandbox; + +static int purged = 0; +static int strays = 0; + +static int check_for_strays = 1; +static int purge = 1; +static int user; +static int create; +static const char *arg_manp; + +struct tried_catdirs_entry { + char *manpath; + int seen; +}; + +const char *argp_program_version = "mandb " PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static const char args_doc[] = N_("[MANPATH]"); + +static struct argp_option options[] = { + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { "quiet", 'q', 0, 0, N_("work quietly, except for 'bogus' warning") }, + { "no-straycats", 's', 0, 0, N_("don't look for or add stray cats to the dbs") }, + { "no-purge", 'p', 0, 0, N_("don't purge obsolete entries from the dbs") }, + { "user-db", 'u', 0, 0, N_("produce user databases only") }, + { "create", 'c', 0, 0, N_("create dbs from scratch, rather than updating") }, + { "test", 't', 0, 0, N_("check manual pages for correctness") }, + { "filename", 'f', N_("FILENAME"), 0, N_("update just the entry for this filename") }, + { "config-file", 'C', N_("FILE"), 0, N_("use this user configuration file") }, + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + static int quiet_temp = 0; + + switch (key) { + case 'd': + debug_level = 1; + return 0; + case 'q': + ++quiet_temp; + return 0; + case 's': + check_for_strays = 0; + return 0; + case 'p': + purge = 0; + return 0; + case 'u': + user = 1; + return 0; + case 'c': + create = 1; + purge = 0; + return 0; + case 't': + opt_test = 1; + return 0; + case 'f': + single_filename = arg; + create = 0; + purge = 0; + check_for_strays = 0; + return 0; + case 'C': + user_config_file = arg; + return 0; + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP); + break; + case ARGP_KEY_ARG: + if (arg_manp) + argp_usage (state); + arg_manp = arg; + return 0; + case ARGP_KEY_SUCCESS: + if (opt_test && !debug_level) + quiet = 1; + else if (quiet_temp == 1) + quiet = 2; + else + quiet = quiet_temp; + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static struct argp argp = { options, parse_opt, args_doc }; + +struct dbpaths { +#ifdef NDBM +# ifdef BERKELEY_DB + char *dbfile; + char *tmpdbfile; +# else /* !BERKELEY_DB NDBM */ + char *dirfile; + char *pagfile; + char *tmpdirfile; + char *tmppagfile; +# endif /* BERKELEY_DB */ +#else /* !NDBM */ + char *xfile; + char *xtmpfile; +#endif /* NDBM */ +}; + +#ifdef MAN_OWNER +extern uid_t ruid; +extern uid_t euid; +#endif /* MAN_OWNER */ + +static char *manpathlist[MAXDIRS]; + +extern int pages; + +/* remove() with error checking */ +static void check_remove (const char *path) +{ + if (remove (path) == -1 && errno != ENOENT) + error (0, errno, _("can't remove %s"), path); +} + +/* rename() with error checking */ +static void check_rename (const char *from, const char *to) +{ + if (rename (from, to) == -1 && errno != ENOENT) { + error (0, errno, _("can't rename %s to %s"), from, to); + check_remove (from); + } +} + +/* chmod() with error checking */ +static void check_chmod (const char *path, mode_t mode) +{ + if (chmod (path, mode) == -1) { + error (0, errno, _("can't chmod %s"), path); + check_remove (path); + } +} + +/* CPhipps 2000/02/24 - Copy a file. */ +static int xcopy (const char *from, const char *to) +{ + FILE *ifp, *ofp; + struct stat st; + struct timespec times[2]; + static const size_t buf_size = 32 * 1024; + char *buf; + int ret = 0; + + ifp = fopen (from, "r"); + if (!ifp) { + ret = -errno; + if (errno == ENOENT) + return 0; + error (0, errno, "fopen %s", from); + return ret; + } + + if (fstat (fileno (ifp), &st) >= 0) { + times[0] = get_stat_atime (&st); + times[1] = get_stat_mtime (&st); + } else { + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_sec = 0; + times[1].tv_nsec = UTIME_OMIT; + } + + ofp = fopen (to, "w"); + if (!ofp) { + ret = -errno; + error (0, errno, "fopen %s", to); + fclose (ifp); + return ret; + } + + buf = xmalloc (buf_size); + while (!feof (ifp) && !ferror (ifp)) { + size_t in = fread (buf, 1, buf_size, ifp); + if (in > 0) { + if (fwrite (buf, 1, in, ofp) == 0 && ferror (ofp)) { + ret = -errno; + error (0, errno, _("can't write to %s"), to); + break; + } + } else if (ferror (ifp)) { + ret = -errno; + error (0, errno, _("can't read from %s"), from); + break; + } + } + free (buf); + + fclose (ifp); + fclose (ofp); + + if (ret < 0) + check_remove (to); + else { + check_chmod (to, DBMODE); + utimens (to, times); + } + + return ret; +} + +/* rename and chmod the database */ +static void finish_up (struct dbpaths *dbpaths) +{ +#ifdef NDBM +# ifdef BERKELEY_DB + check_rename (dbpaths->tmpdbfile, dbpaths->dbfile); + check_chmod (dbpaths->dbfile, DBMODE); + free (dbpaths->tmpdbfile); + dbpaths->tmpdbfile = NULL; +# else /* not BERKELEY_DB */ + check_rename (dbpaths->tmpdirfile, dbpaths->dirfile); + check_chmod (dbpaths->dirfile, DBMODE); + check_rename (dbpaths->tmppagfile, dbpaths->pagfile); + check_chmod (dbpaths->pagfile, DBMODE); + free (dbpaths->tmpdirfile); + free (dbpaths->tmppagfile); + dbpaths->tmpdirfile = dbpaths->tmppagfile = NULL; +# endif /* BERKELEY_DB */ +#else /* not NDBM */ + check_rename (dbpaths->xtmpfile, dbpaths->xfile); + check_chmod (dbpaths->xfile, DBMODE); + free (dbpaths->xtmpfile); + dbpaths->xtmpfile = NULL; +#endif /* NDBM */ +} + +#ifdef MAN_OWNER +/* change the owner of global man databases */ +static void do_chown (struct dbpaths *dbpaths) +{ +# ifdef NDBM +# ifdef BERKELEY_DB + chown_if_possible (dbpaths->dbfile); +# else /* not BERKELEY_DB */ + chown_if_possible (dbpaths->dirfile); + chown_if_possible (dbpaths->pagfile); +# endif /* BERKELEY_DB */ +# else /* not NDBM */ + chown_if_possible (dbpaths->xfile); +# endif /* NDBM */ +} +#endif /* MAN_OWNER */ + +/* Update a single file in an existing database. */ +static int update_one_file (const char *manpath, const char *filename) +{ + MYDBM_FILE dbf; + + dbf = MYDBM_RWOPEN (database); + if (dbf) { + struct mandata info; + char *manpage; + + memset (&info, 0, sizeof (struct mandata)); + manpage = filename_info (filename, &info, ""); + if (info.name) { + dbdelete (dbf, info.name, &info); + purge_pointers (dbf, info.name); + free (info.name); + } + free (manpage); + + test_manfile (dbf, filename, manpath); + } + MYDBM_CLOSE (dbf); + + return 1; +} + +/* dont actually create any dbs, just do an update */ +static int update_db_wrapper (const char *manpath, const char *catpath) +{ + int amount; + + if (single_filename) + return update_one_file (manpath, single_filename); + + amount = update_db (manpath, catpath); + if (amount != EOF) + return amount; + + return create_db (manpath, catpath); +} + +/* remove incomplete databases */ +static void cleanup_sigsafe (void *arg) +{ + struct dbpaths *dbpaths = arg; + +#ifdef NDBM +# ifdef BERKELEY_DB + if (dbpaths->tmpdbfile) + unlink (dbpaths->tmpdbfile); +# else /* !BERKELEY_DB NDBM */ + if (dbpaths->tmpdirfile) + unlink (dbpaths->tmpdirfile); + if (dbpaths->tmppagfile) + unlink (dbpaths->tmppagfile); +# endif /* BERKELEY_DB NDBM */ +#else /* !NDBM */ + if (dbpaths->xtmpfile) + unlink (dbpaths->xtmpfile); +#endif /* NDBM */ +} + +/* free database names */ +static void cleanup (void *arg) +{ + struct dbpaths *dbpaths = arg; + +#ifdef NDBM +# ifdef BERKELEY_DB + free (dbpaths->dbfile); + free (dbpaths->tmpdbfile); + dbpaths->dbfile = dbpaths->tmpdbfile = NULL; +# else /* !BERKELEY_DB NDBM */ + free (dbpaths->dirfile); + free (dbpaths->pagfile); + free (dbpaths->tmpdirfile); + free (dbpaths->tmppagfile); + dbpaths->dirfile = dbpaths->pagfile = NULL; + dbpaths->tmpdirfile = dbpaths->tmppagfile = NULL; +# endif /* BERKELEY_DB NDBM */ +#else /* !NDBM */ + free (dbpaths->xfile); + free (dbpaths->xtmpfile); + dbpaths->xfile = dbpaths->xtmpfile = NULL; +#endif /* NDBM */ + free (dbpaths); +} + +#define CACHEDIR_TAG \ + "Signature: 8a477f597d28d172789f06886806bc55\n" \ + "# This file is a cache directory tag created by man-db.\n" \ + "# For information about cache directory tags, see:\n" \ + "#\thttp://www.brynosaurus.com/cachedir/\n" + +/* sort out the database names */ +static int mandb (struct dbpaths *dbpaths, + const char *catpath, const char *manpath, int global_manpath) +{ + int ret, amount; + char *dbname; + int should_create; + + dbname = mkdbname (catpath); + database = xasprintf ("%s/%d", catpath, getpid ()); + + if (!quiet) + printf (_("Processing manual pages under %s...\n"), manpath); + + if (!STREQ (catpath, manpath)) { + char *cachedir_tag; + int fd; + int cachedir_tag_exists = 0; + + cachedir_tag = xasprintf ("%s/CACHEDIR.TAG", catpath); + fd = open (cachedir_tag, O_RDONLY); + if (fd < 0) { + FILE *cachedir_tag_file; + + if (errno != ENOENT) + check_remove (cachedir_tag); + cachedir_tag_file = fopen (cachedir_tag, "w"); + if (cachedir_tag_file) { + cachedir_tag_exists = 1; + fputs (CACHEDIR_TAG, cachedir_tag_file); + fclose (cachedir_tag_file); + } + } else { + cachedir_tag_exists = 1; + close (fd); + } + if (cachedir_tag_exists) { + if (global_manpath) + chown_if_possible (cachedir_tag); + check_chmod (cachedir_tag, DBMODE); + } + free (cachedir_tag); + } + + should_create = (create || force_rescan || opt_test); + +#ifdef NDBM +# ifdef BERKELEY_DB + dbpaths->dbfile = xasprintf ("%s.db", dbname); + free (dbname); + dbpaths->tmpdbfile = xasprintf ("%s.db", database); + if (!should_create) { + if (xcopy (dbpaths->dbfile, dbpaths->tmpdbfile) < 0) + should_create = 1; + } + if (should_create) { + check_remove (dbpaths->tmpdbfile); + ret = create_db (manpath, catpath); + if (ret < 0) + return ret; + amount = ret; + } else { + ret = update_db_wrapper (manpath, catpath); + if (ret < 0) + return ret; + amount = ret; + } +# else /* !BERKELEY_DB NDBM */ + dbpaths->dirfile = xasprintf ("%s.dir", dbname); + dbpaths->pagfile = xasprintf ("%s.pag", dbname); + free (dbname); + dbpaths->tmpdirfile = xasprintf ("%s.dir", database); + dbpaths->tmppagfile = xasprintf ("%s.pag", database); + if (!should_create) { + if (xcopy (dbpaths->dirfile, dbpaths->tmpdirfile) < 0 || + xcopy (dbpaths->pagfile, dbpaths->tmppagfile) < 0) + should_create = 1; + } + if (should_create) { + check_remove (dbpaths->tmpdirfile); + check_remove (dbpaths->tmppagfile); + ret = create_db (manpath, catpath); + if (ret < 0) + return ret; + amount = ret; + } else { + ret = update_db_wrapper (manpath, catpath); + if (ret < 0) + return ret; + amount = ret; + } +# endif /* BERKELEY_DB NDBM */ +#else /* !NDBM */ + dbpaths->xfile = dbname; /* steal memory */ + dbpaths->xtmpfile = xstrdup (database); + if (!should_create) { + if (xcopy (dbpaths->xfile, dbpaths->xtmpfile) < 0) + should_create = 1; + } + if (should_create) { + check_remove (dbpaths->xtmpfile); + ret = create_db (manpath, catpath); + if (ret < 0) + return ret; + amount = ret; + } else { + ret = update_db_wrapper (manpath, catpath); + if (ret < 0) + return ret; + amount = ret; + } +#endif /* NDBM */ + + return amount; +} + +static int process_manpath (const char *manpath, int global_manpath, + struct hashtable *tried_catdirs) +{ + char *catpath; + struct tried_catdirs_entry *tried; + struct stat st; + int run_mandb = 0; + struct dbpaths *dbpaths = NULL; + int amount = 0; + + if (global_manpath) { /* system db */ + catpath = get_catpath (manpath, SYSTEM_CAT); + assert (catpath); + } else { /* user db */ + catpath = get_catpath (manpath, USER_CAT); + if (!catpath) + catpath = xstrdup (manpath); + } + tried = XMALLOC (struct tried_catdirs_entry); + tried->manpath = xstrdup (manpath); + tried->seen = 0; + hashtable_install (tried_catdirs, catpath, strlen (catpath), tried); + + if (stat (manpath, &st) < 0 || !S_ISDIR (st.st_mode)) + goto out; + tried->seen = 1; + + if (single_filename) { + /* The file might be in a per-locale subdirectory that we + * aren't processing right now. + */ + char *manpath_prefix = xasprintf ("%s/man", manpath); + if (STRNEQ (manpath_prefix, single_filename, + strlen (manpath_prefix))) + run_mandb = 1; + free (manpath_prefix); + } else + run_mandb = 1; + + force_rescan = 0; + if (purge) { + database = mkdbname (catpath); + purged += purge_missing (manpath, catpath, run_mandb); + free (database); + database = NULL; + } + + dbpaths = XZALLOC (struct dbpaths); + push_cleanup (cleanup, dbpaths, 0); + push_cleanup (cleanup_sigsafe, dbpaths, 1); + if (run_mandb) { + int ret = mandb (dbpaths, catpath, manpath, global_manpath); + if (ret < 0) { + amount = ret; + goto out; + } + amount += ret; + } + + if (!opt_test && amount) + finish_up (dbpaths); +#ifdef MAN_OWNER + if (global_manpath) + do_chown (dbpaths); +#endif /* MAN_OWNER */ + +out: + if (dbpaths) { + cleanup_sigsafe (dbpaths); + pop_cleanup (cleanup_sigsafe, dbpaths); + cleanup (dbpaths); + pop_cleanup (cleanup, dbpaths); + } + + free (database); + database = NULL; + + if (check_for_strays && amount > 0) { + database = mkdbname (catpath); + strays += straycats (manpath); + free (database); + database = NULL; + } + + free (catpath); + + return amount; +} + +static int is_lang_dir (const char *base) +{ + return strlen (base) >= 2 && + base[0] >= 'a' && base[0] <= 'z' && + base[1] >= 'a' && base[1] <= 'z' && + (!base[2] || base[2] < 'a' || base[2] > 'z'); +} + +static void tried_catdirs_free (void *defn) +{ + struct tried_catdirs_entry *tried = defn; + + free (tried->manpath); + free (tried); +} + +static void purge_catdir (const struct hashtable *tried_catdirs, + const char *path) +{ + struct stat st; + + if (stat (path, &st) == 0 && S_ISDIR (st.st_mode) && + !hashtable_lookup (tried_catdirs, path, strlen (path))) { + if (!quiet) + printf (_("Removing obsolete cat directory %s...\n"), + path); + remove_directory (path, 1); + } +} + +static void purge_catsubdirs (const char *manpath, const char *catpath) +{ + DIR *dir; + struct dirent *ent; + struct stat st; + + dir = opendir (catpath); + if (!dir) + return; + while ((ent = readdir (dir)) != NULL) { + char *mandir, *catdir; + + if (!STRNEQ (ent->d_name, "cat", 3)) + continue; + + mandir = xasprintf ("%s/man%s", manpath, ent->d_name + 3); + catdir = xasprintf ("%s/%s", catpath, ent->d_name); + + if (stat (mandir, &st) != 0 && errno == ENOENT) { + if (!quiet) + printf (_("Removing obsolete cat directory " + "%s...\n"), catdir); + remove_directory (catdir, 1); + } + + free (catdir); + free (mandir); + } + closedir (dir); +} + +/* Remove catdirs whose corresponding mandirs no longer exist. For safety, + * in case people set catdirs to silly locations, we only do this for the + * cat* and NLS subdirectories of catdirs, but not for the top-level catdir + * itself (which might contain other data, or which might be difficult for + * mandb to recreate with the proper permissions). + * + * We need to be careful here to avoid removing catdirs just because we + * happened not to inspect the corresponding mandir this time round. If a + * mandir was inspected and turned out not to exist, then its catdir is + * clearly fair game for removal of NLS subdirectories. These must match + * the usual NLS pattern (two lower-case letters followed by nothing or a + * non-letter). + */ +static void purge_catdirs (const struct hashtable *tried_catdirs) +{ + struct hashtable_iter *iter = NULL; + const struct nlist *elt; + + while ((elt = hashtable_iterate (tried_catdirs, &iter)) != NULL) { + const char *path = elt->name; + struct tried_catdirs_entry *tried = elt->defn; + char *base; + DIR *dir; + struct dirent *subdirent; + + base = base_name (path); + if (is_lang_dir (base)) { + /* expect to check this as a subdirectory later */ + free (base); + continue; + } + free (base); + + purge_catsubdirs (tried->manpath, path); + + dir = opendir (path); + if (!dir) + continue; + while ((subdirent = readdir (dir)) != NULL) { + char *subdirpath; + + if (STREQ (subdirent->d_name, ".") || + STREQ (subdirent->d_name, "..")) + continue; + if (STRNEQ (subdirent->d_name, "cat", 3)) + continue; + if (!is_lang_dir (subdirent->d_name)) + continue; + + subdirpath = xasprintf ("%s/%s", path, + subdirent->d_name); + + tried = hashtable_lookup (tried_catdirs, subdirpath, + strlen (subdirpath)); + if (tried && tried->seen) { + debug ("Seen mandir for %s; not deleting\n", + subdirpath); + /* However, we may still need to purge cat* + * subdirectories. + */ + purge_catsubdirs (tried->manpath, subdirpath); + } else + purge_catdir (tried_catdirs, subdirpath); + + free (subdirpath); + } + closedir (dir); + } +} + +int main (int argc, char *argv[]) +{ + char *sys_manp; + int amount = 0; + char **mp; + struct hashtable *tried_catdirs; +#ifdef SIGPIPE + struct sigaction sa; +#endif /* SIGPIPE */ + +#ifdef __profile__ + char *cwd; +#endif /* __profile__ */ + + set_program_name (argv[0]); + + init_debug (); + pipeline_install_post_fork (pop_all_cleanups); + sandbox = sandbox_init (); + init_locale (); + +#ifdef SIGPIPE + /* Reset SIGPIPE to its default disposition. Too many broken pieces + * of software (Python << 3.2, gnome-session, etc.) spawn child + * processes with SIGPIPE ignored, and this produces noise in cron + * mail. + */ + memset (&sa, 0, sizeof sa); + sa.sa_handler = SIG_DFL; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + sigaction (SIGPIPE, &sa, NULL); +#endif /* SIGPIPE */ + + if (argp_parse (&argp, argc, argv, 0, 0, 0)) + exit (FAIL); + +#ifdef __profile__ + cwd = xgetcwd (); + if (!cwd) { + cwd = xmalloc (1); + cwd[0] = '\0'; + } +#endif /* __profile__ */ + + /* record who we are and drop effective privs for later use */ + init_security (); + +#ifdef MAN_OWNER + man_owner = get_man_owner (); + if (!user && euid != 0 && euid != man_owner->pw_uid) + user = 1; +#endif /* MAN_OWNER */ + + read_config_file (user); + + /* This is required for get_catpath(), regardless */ + manp = get_manpath (NULL); /* also calls read_config_file() */ + + /* pick up the system manpath or use the supplied one */ + if (arg_manp) { + free (manp); + manp = xstrdup (arg_manp); + } else if (!user) { + sys_manp = get_mandb_manpath (); + if (sys_manp) { + free (manp); + manp = sys_manp; + } else + error (0, 0, + _("warning: no MANDB_MAP directives in %s, " + "using your manpath"), + CONFIG_FILE); + } + + debug ("manpath=%s\n", manp); + + /* get the manpath as an array of pointers */ + create_pathlist (manp, manpathlist); + + /* finished manpath processing, regain privs */ + regain_effective_privs (); + + tried_catdirs = hashtable_create (tried_catdirs_free); + + for (mp = manpathlist; *mp; mp++) { + int global_manpath = is_global_mandir (*mp); + int ret; + DIR *dir; + struct dirent *subdirent; + + if (global_manpath) { /* system db */ + if (user) + continue; + } else { /* user db */ + drop_effective_privs (); + } + + ret = process_manpath (*mp, global_manpath, tried_catdirs); + if (ret < 0) + exit (FATAL); + amount += ret; + + dir = opendir (*mp); + if (!dir) { + error (0, errno, _("can't search directory %s"), *mp); + goto next_manpath; + } + + while ((subdirent = readdir (dir)) != NULL) { + char *subdirpath; + + /* Look for per-locale subdirectories. */ + if (STREQ (subdirent->d_name, ".") || + STREQ (subdirent->d_name, "..")) + continue; + if (STRNEQ (subdirent->d_name, "man", 3)) + continue; + + subdirpath = xasprintf ("%s/%s", *mp, + subdirent->d_name); + ret = process_manpath (subdirpath, global_manpath, + tried_catdirs); + if (ret < 0) + exit (FATAL); + amount += ret; + free (subdirpath); + } + + closedir (dir); + +next_manpath: + if (!global_manpath) + regain_effective_privs (); + + chkr_garbage_detector (); + } + + purge_catdirs (tried_catdirs); + hashtable_free (tried_catdirs); + + if (!quiet) { + printf (ngettext ("%d man subdirectory contained newer " + "manual pages.\n", + "%d man subdirectories contained newer " + "manual pages.\n", amount), + amount); + printf (ngettext ("%d manual page was added.\n", + "%d manual pages were added.\n", pages), + pages); + if (check_for_strays) + printf (ngettext ("%d stray cat was added.\n", + "%d stray cats were added.\n", + strays), + strays); + if (purge) + printf (ngettext ("%d old database entry was " + "purged.\n", + "%d old database entries were " + "purged.\n", purged), + purged); + } + +#ifdef __profile__ + /* For profiling */ + if (cwd[0]) + chdir (cwd); +#endif /* __profile__ */ + + free_pathlist (manpathlist); + free (manp); + if (create && !amount) { + const char *must_create; + if (!quiet) + fprintf (stderr, _("No databases created.")); + must_create = getenv ("MAN_MUST_CREATE"); + if (must_create && STREQ (must_create, "1")) + exit (FAIL); + } + exit (OK); +} |