diff options
Diffstat (limited to '')
-rw-r--r-- | src/whatis.c | 1009 |
1 files changed, 1009 insertions, 0 deletions
diff --git a/src/whatis.c b/src/whatis.c new file mode 100644 index 0000000..ee43f10 --- /dev/null +++ b/src/whatis.c @@ -0,0 +1,1009 @@ +/* + * whatis.c: search the index or whatis database(s) for words. + * + * 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 + * + * routines for whatis and apropos programs. Whatis looks up the + * keyword for the description, apropos searches the entire database + * for word matches. + * + * Mon Aug 8 20:35:30 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk) + * + * CJW: Add more safety in the face of corrupted databases. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> + +#include "gettext.h" +#include <locale.h> +#define _(String) gettext (String) +#define N_(String) gettext_noop (String) + +#ifdef HAVE_ICONV +# include <iconv.h> +#endif /* HAVE_ICONV */ + +#include <sys/types.h> +#include <sys/stat.h> +#include "regex.h" + +#include "argp.h" +#include "dirname.h" +#include "fnmatch.h" +#include "progname.h" +#include "xvasprintf.h" + +#include "manconfig.h" + +#include "cleanup.h" +#include "error.h" +#include "pipeline.h" +#include "pathsearch.h" +#include "linelength.h" +#include "hashtable.h" +#include "lower.h" +#include "wordfnmatch.h" +#include "xregcomp.h" +#include "encodings.h" +#include "sandbox.h" + +#include "mydbm.h" +#include "db_storage.h" + +#include "manp.h" + +static char *manpathlist[MAXDIRS]; + +extern char *user_config_file; +static char **keywords; +static int num_keywords; + +int am_apropos; +char *database; +int quiet = 1; +man_sandbox *sandbox; + +#ifdef HAVE_ICONV +iconv_t conv_to_locale; +#endif /* HAVE_ICONV */ + +static regex_t *preg; +static int regex_opt; +static int exact; + +static int wildcard; + +static int require_all; + +static int long_output; + +static char **sections; + +static char *manp = NULL; +static const char *alt_systems = ""; +static const char *locale = NULL; +static char *multiple_locale = NULL, *internal_locale; + +static struct hashtable *display_seen = NULL; + +const char *argp_program_version; /* initialised in main */ +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +error_t argp_err_exit_status = FAIL; + +static const char args_doc[] = N_("KEYWORD..."); +static const char apropos_doc[] = "\v" N_("The --regex option is enabled by default."); + +static struct argp_option options[] = { + { "debug", 'd', 0, 0, N_("emit debugging messages") }, + { "verbose", 'v', 0, 0, N_("print verbose warning messages") }, + { "regex", 'r', 0, 0, N_("interpret each keyword as a regex"), 10 }, + { "exact", 'e', 0, 0, N_("search each keyword for exact match") }, /* apropos only */ + { "wildcard", 'w', 0, 0, N_("the keyword(s) contain wildcards") }, + { "and", 'a', 0, 0, N_("require all keywords to match"), 20 }, /* apropos only */ + { "long", 'l', 0, 0, N_("do not trim output to terminal width"), 30 }, + { "sections", 's', N_("LIST"), 0, N_("search only these sections (colon-separated)"), 40 }, + { "section", 0, 0, OPTION_ALIAS }, + { "systems", 'm', N_("SYSTEM"), 0, N_("use manual pages from other systems") }, + { "manpath", 'M', N_("PATH"), 0, N_("set search path for manual pages to PATH") }, + { "locale", 'L', N_("LOCALE"), 0, N_("define the locale for this search") }, + { "config-file", 'C', N_("FILE"), 0, N_("use this user configuration file") }, + { "whatis", 'f', 0, OPTION_HIDDEN, 0 }, + { "apropos", 'k', 0, OPTION_HIDDEN, 0 }, + { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */ + { 0 } +}; + +static char **split_sections (const char *sections_str) +{ + int i = 0; + char *str = xstrdup (sections_str); + const char *section; + char **out = NULL; + + /* Although this is documented as colon-separated, at least Solaris + * man's -s option takes a comma-separated list, so we accept that + * too for compatibility. + */ + for (section = strtok (str, ":,"); section; + section = strtok (NULL, ":,")) { + out = xnrealloc (out, i + 2, sizeof *out); + out[i++] = xstrdup (section); + } + if (i) + out[i] = NULL; + + free (str); + return out; +} + +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) { + case 'd': + debug_level = 1; + return 0; + case 'v': + quiet = 0; + return 0; + case 'r': + regex_opt = 1; + return 0; + case 'e': + /* Only makes sense for apropos, but has + * historically been accepted by whatis anyway. + */ + regex_opt = 0; + exact = 1; + return 0; + case 'w': + regex_opt = 0; + wildcard = 1; + return 0; + case 'a': + if (am_apropos) + require_all = 1; + else + argp_usage (state); + return 0; + case 'l': + long_output = 1; + return 0; + case 's': + sections = split_sections (arg); + return 0; + case 'm': + alt_systems = arg; + return 0; + case 'M': + manp = xstrdup (arg); + return 0; + case 'L': + locale = arg; + return 0; + case 'C': + user_config_file = arg; + return 0; + case 'f': + /* helpful override if program name detection fails */ + am_apropos = 0; + return 0; + case 'k': + /* helpful override if program name detection fails */ + am_apropos = 1; + return 0; + case 'h': + argp_state_help (state, state->out_stream, + ARGP_HELP_STD_HELP & + ~ARGP_HELP_PRE_DOC); + break; + case ARGP_KEY_ARGS: + keywords = state->argv + state->next; + num_keywords = state->argc - state->next; + return 0; + case ARGP_KEY_NO_ARGS: + /* Make sure that we have a keyword! */ + printf (_("%s what?\n"), program_name); + exit (FAIL); + case ARGP_KEY_SUCCESS: + if (am_apropos && !exact && !wildcard) + regex_opt = 1; + return 0; + } + return ARGP_ERR_UNKNOWN; +} + +static char *help_filter (int key, const char *text, + void *input ATTRIBUTE_UNUSED) +{ + switch (key) { + case ARGP_KEY_HELP_PRE_DOC: + /* We have no pre-options help text, but the input + * text may contain header junk due to gettext (""). + */ + return NULL; + default: + return (char *) text; + } +} + +static struct argp apropos_argp = { options, parse_opt, args_doc, apropos_doc, + 0, help_filter }; +static struct argp whatis_argp = { options, parse_opt, args_doc }; + +static char *locale_manpath (const char *manpath) +{ + char *all_locales; + char *new_manpath; + + if (multiple_locale && *multiple_locale) { + if (internal_locale && *internal_locale) + all_locales = xasprintf ("%s:%s", multiple_locale, + internal_locale); + else + all_locales = xstrdup (multiple_locale); + } else { + if (internal_locale && *internal_locale) + all_locales = xstrdup (internal_locale); + else + all_locales = NULL; + } + + new_manpath = add_nls_manpaths (manpath, all_locales); + free (all_locales); + + return new_manpath; +} + +#ifdef HAVE_ICONV +static char *simple_convert (iconv_t conv, char *string) +{ + if (conv != (iconv_t) -1) { + size_t string_conv_alloc = strlen (string) + 1; + char *string_conv = xmalloc (string_conv_alloc); + for (;;) { + char *inptr = string, *outptr = string_conv; + size_t inleft = strlen (string); + size_t outleft = string_conv_alloc - 1; + if (iconv (conv, (ICONV_CONST char **) &inptr, &inleft, + &outptr, &outleft) == (size_t) -1 && + errno == E2BIG) { + string_conv_alloc <<= 1; + string_conv = xrealloc (string_conv, + string_conv_alloc); + } else { + /* Either we succeeded, or we've done our + * best; go ahead and print what we've got. + */ + string_conv[string_conv_alloc - 1 - outleft] = + '\0'; + break; + } + } + return string_conv; + } else + return xstrdup (string); +} +#else /* !HAVE_ICONV */ +# define simple_convert(conv, string) xstrdup (string) +#endif /* HAVE_ICONV */ + +/* Do the old thing, if we cannot find the relevant database. + * This invokes grep once per argument; we can't do much about this because + * we need to know which arguments failed. The only way to speed this up + * would be to implement grep internally, but it hardly seems worth it for a + * legacy mechanism. + */ +static void use_grep (const char * const *pages, int num_pages, char *manpath, + int *found) +{ + char *whatis_file = xasprintf ("%s/whatis", manpath); + + if (CAN_ACCESS (whatis_file, R_OK)) { + const char *flags; + int i; + + if (am_apropos) { + if (regex_opt) + flags = get_def_user ( + "apropos_regex_grep_flags", + APROPOS_REGEX_GREP_FLAGS); + else + flags = get_def_user ("apropos_grep_flags", + APROPOS_GREP_FLAGS); + } else + flags = get_def_user ("whatis_grep_flags", + WHATIS_GREP_FLAGS); + + for (i = 0; i < num_pages; ++i) { + pipeline *grep_pl; + pipecmd *grep_cmd; + char *anchored_page; + + if (am_apropos) + anchored_page = xstrdup (pages[i]); + else + anchored_page = xasprintf ("^%s", pages[i]); + + grep_cmd = pipecmd_new_argstr (get_def_user ("grep", + GREP)); + pipecmd_argstr (grep_cmd, flags); + pipecmd_args (grep_cmd, anchored_page, whatis_file, + (void *) 0); + pipecmd_pre_exec (grep_cmd, sandbox_load, sandbox_free, + sandbox); + grep_pl = pipeline_new_commands (grep_cmd, (void *) 0); + + if (pipeline_run (grep_pl) == 0) + found[i] = 1; + + free (anchored_page); + } + } else + debug ("warning: can't read the fallback whatis text database " + "%s/whatis\n", manpath); + + free (whatis_file); +} + +static struct mandata *resolve_pointers (MYDBM_FILE dbf, struct mandata *info, + const char *page) +{ + int rounds; + const char *newpage; + + if (*(info->pointer) == '-' || + ((!info->name || STREQ (info->name, page)) && + STREQ (info->pointer, page))) + return info; + + /* Now we have to work through pointers. The limit of 10 is fairly + * arbitrary: it's just there to avoid an infinite loop. + */ + newpage = info->pointer; + info = dblookup_exact (dbf, newpage, info->ext, 1); + for (rounds = 0; rounds < 10; rounds++) { + struct mandata *newinfo; + + /* If the pointer lookup fails, do nothing. */ + if (!info) + return NULL; + + if (*(info->pointer) == '-' || + ((!info->name || STREQ (info->name, newpage)) && + STREQ (info->pointer, newpage))) + return info; + + newinfo = dblookup_exact (dbf, info->pointer, info->ext, 1); + free_mandata_struct (info); + info = newinfo; + } + + if (!quiet) + error (0, 0, _("warning: %s contains a pointer loop"), page); + return NULL; +} + +/* fill_in_whatis() is really a ../libdb/db_lookup.c routine but whatis.c + is the only file that actually requires access to the whatis text... */ + +/* Take mandata struct (earlier returned from a dblookup()) and return + the relative whatis */ +static char *get_whatis (struct mandata *info, const char *page) +{ + if (!info) + return xstrdup (_("(unknown subject)")); + + /* See if we need to fill in the whatis here. */ + if (info->whatis != NULL && *(info->whatis)) + return xstrdup (info->whatis); + if (!quiet && *(info->pointer) != '-') + error (0, 0, _("warning: %s contains a pointer loop"), + page); + return xstrdup (_("(unknown subject)")); +} + +/* print out any matches found */ +static void display (MYDBM_FILE dbf, struct mandata *info, const char *page) +{ + struct mandata *newinfo; + char *string, *whatis, *string_conv; + const char *page_name; + char *key; + int line_len, rest; + + newinfo = resolve_pointers (dbf, info, page); + whatis = get_whatis (newinfo, page); + if (newinfo == NULL) + newinfo = info; + + dbprintf (newinfo); + + if (newinfo->name) + page_name = newinfo->name; + else + page_name = page; + + key = xasprintf ("%s (%s)", page_name, newinfo->ext); + if (hashtable_lookup_structure (display_seen, key, strlen (key))) + goto out; + hashtable_install (display_seen, key, strlen (key), NULL); + + line_len = get_line_length (); + + if (!long_output && strlen (page_name) > (size_t) (line_len / 2)) + string = xasprintf ("%.*s...", line_len / 2 - 3, page_name); + else + string = xstrdup (page_name); + string = appendstr (string, " (", newinfo->ext, ")", (void *) 0); + if (!STREQ (newinfo->pointer, "-") && !STREQ (newinfo->pointer, page)) + string = appendstr (string, " [", newinfo->pointer, "]", + (void *) 0); + + if (strlen (string) < (size_t) 20) { + int i; + string = xrealloc (string, 21); + for (i = strlen (string); i < 20; ++i) + string[i] = ' '; + string[i] = '\0'; + } + string = appendstr (string, " - ", (void *) 0); + + rest = line_len - strlen (string); + if (!long_output && strlen (whatis) > (size_t) rest) { + whatis[rest - 3] = '\0'; + string = appendstr (string, whatis, "...\n", (void *) 0); + } else + string = appendstr (string, whatis, "\n", (void *) 0); + + string_conv = simple_convert (conv_to_locale, string); + fputs (string_conv, stdout); + + free (string_conv); + free (string); + +out: + free (key); + free (whatis); + if (newinfo != info) + free_mandata_struct (newinfo); +} + +/* lookup the page and display the results */ +static int do_whatis_section (MYDBM_FILE dbf, + const char *page, const char *section) +{ + struct mandata *info; + int count = 0; + + info = dblookup_all (dbf, page, section, 0); + while (info) { + struct mandata *pinfo; + + display (dbf, info, page); + count++; + pinfo = info->next; /* go on to next structure */ + free_mandata_elements (info); + free (info); + info = pinfo; + } + return count; +} + +static int suitable_manpath (const char *manpath, const char *page_dir) +{ + char *page_manp, *pm; + char *page_manpathlist[MAXDIRS], **mp; + int ret; + + page_manp = get_manpath_from_path (page_dir, 0); + if (!page_manp || !*page_manp) { + free (page_manp); + return 0; + } + pm = locale_manpath (page_manp); + free (page_manp); + page_manp = pm; + create_pathlist (page_manp, page_manpathlist); + + ret = 0; + for (mp = page_manpathlist; *mp; ++mp) { + if (STREQ (*mp, manpath)) { + ret = 1; + break; + } + } + + for (mp = page_manpathlist; *mp; ++mp) + free (*mp); + free (page_manp); + return ret; +} + +static void do_whatis (MYDBM_FILE dbf, + const char * const *pages, int num_pages, + const char *manpath, int *found) +{ + int i; + + for (i = 0; i < num_pages; ++i) { + char *page = xstrdup (pages[i]); + struct stat st; + + if (strchr (page, '/') && stat (page, &st) == 0 && + !S_ISDIR (st.st_mode) && + st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + /* Perhaps an executable. If its directory is on + * $PATH, then we only want to process this page for + * matching manual hierarchies. + */ + char *page_dir = dir_name (page); + + if (directory_on_path (page_dir)) { + if (suitable_manpath (manpath, page_dir)) { + char *old_page = page; + page = base_name (old_page); + free (old_page); + } else { + debug ("%s not on manpath for %s\n", + manpath, page); + free (page_dir); + free (page); + continue; + } + } + free (page_dir); + } + + if (sections) { + char * const *section; + + for (section = sections; *section; ++section) { + if (do_whatis_section (dbf, page, *section)) + found[i] = 1; + } + } else { + if (do_whatis_section (dbf, page, NULL)) + found[i] = 1; + } + + free (page); + } +} + +static int any_set (int num_pages, int *found_here) +{ + int i; + + for (i = 0; i < num_pages; ++i) + if (found_here[i]) + return 1; + return 0; +} + +static int all_set (int num_pages, int *found_here) +{ + int i; + + for (i = 0; i < num_pages; ++i) + if (!found_here[i]) + return 0; + return 1; +} + +static void parse_name (const char * const *pages, int num_pages, + const char *dbname, int *found, int *found_here) +{ + int i; + + if (regex_opt) { + for (i = 0; i < num_pages; ++i) { + if (regexec (&preg[i], dbname, 0, + (regmatch_t *) 0, 0) == 0) + found[i] = found_here[i] = 1; + } + return; + } + + if (am_apropos && !wildcard) { + char *lowdbname = lower (dbname); + + for (i = 0; i < num_pages; ++i) { + if (STREQ (lowdbname, pages[i])) + found[i] = found_here[i] = 1; + } + free (lowdbname); + return; + } + + for (i = 0; i < num_pages; ++i) { + if (fnmatch (pages[i], dbname, 0) == 0) + found[i] = found_here[i] = 1; + } +} + +/* return 1 on word match */ +static int match (const char *lowpage, const char *whatis) +{ + char *lowwhatis = lower (whatis); + size_t len = strlen (lowpage); + char *p, *begin; + + begin = lowwhatis; + + /* check for string match, then see if it is a _word_ */ + while (lowwhatis && (p = strstr (lowwhatis, lowpage))) { + char *left = p - 1; + char *right = p + len; + + if ((p == begin || (!CTYPE (islower, *left) && *left != '_')) && + (!*right || (!CTYPE (islower, *right) && *right != '_'))) { + free (begin); + return 1; + } + lowwhatis = p + 1; + } + + free (begin); + return 0; +} + +static void parse_whatis (const char * const *pages, char * const *lowpages, + int num_pages, const char *whatis, + int *found, int *found_here) +{ + int i; + + if (regex_opt) { + for (i = 0; i < num_pages; ++i) { + if (regexec (&preg[i], whatis, 0, + (regmatch_t *) 0, 0) == 0) + found[i] = found_here[i] = 1; + } + return; + } + + if (wildcard) { + for (i = 0; i < num_pages; ++i) { + if (exact) { + if (fnmatch (pages[i], whatis, 0) == 0) + found[i] = found_here[i] = 1; + } else { + if (word_fnmatch (pages[i], whatis)) + found[i] = found_here[i] = 1; + } + } + return; + } + + for (i = 0; i < num_pages; ++i) { + if (match (lowpages[i], whatis)) + found[i] = found_here[i] = 1; + } +} + +/* cjwatson: Optimized functions don't seem to be correct in some + * circumstances; disabled for now. + */ +#undef BTREE + +/* 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 dassen@wi.leidenuniv.nl (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); + } + + free (found_here); + + 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 main (int argc, char *argv[]) +{ + char *program_base_name; +#ifdef HAVE_ICONV + char *locale_charset; +#endif + int status = OK; + + set_program_name (argv[0]); + program_base_name = base_name (program_name); + if (STREQ (program_base_name, APROPOS_NAME)) { + am_apropos = 1; + argp_program_version = "apropos " PACKAGE_VERSION; + } else { + struct argp_option *optionp; + am_apropos = 0; + argp_program_version = "whatis " PACKAGE_VERSION; + for (optionp = (struct argp_option *) whatis_argp.options; + optionp->name || optionp->key || optionp->arg || + optionp->flags || optionp->doc || optionp->group; + ++optionp) { + if (!optionp->name) + continue; + if (STREQ (optionp->name, "exact") || + STREQ (optionp->name, "and")) + optionp->flags |= OPTION_HIDDEN; + } + } + free (program_base_name); + + init_debug (); + pipeline_install_post_fork (pop_all_cleanups); + sandbox = sandbox_init (); + init_locale (); + + internal_locale = setlocale (LC_MESSAGES, NULL); + /* Use LANGUAGE only when LC_MESSAGES locale category is + * neither "C" nor "POSIX". */ + if (internal_locale && strcmp (internal_locale, "C") && + strcmp (internal_locale, "POSIX")) + multiple_locale = getenv ("LANGUAGE"); + internal_locale = xstrdup (internal_locale ? internal_locale : "C"); + + if (argp_parse (am_apropos ? &apropos_argp : &whatis_argp, argc, argv, + 0, 0, 0)) + exit (FAIL); + + read_config_file (user_config_file != NULL); + + /* close this locale and reinitialise if a new locale was + issued as an argument or in $MANOPT */ + if (locale) { + free (internal_locale); + internal_locale = setlocale (LC_ALL, locale); + if (internal_locale) + internal_locale = xstrdup (internal_locale); + else + internal_locale = xstrdup (locale); + + debug ("main(): locale = %s, internal_locale = %s\n", + locale, internal_locale); + if (internal_locale) { + setenv ("LANGUAGE", internal_locale, 1); + locale_changed (); + multiple_locale = NULL; + } + } + + /* sort out the internal manpath */ + if (manp == NULL) + manp = locale_manpath (get_manpath (alt_systems)); + else + free (get_manpath (NULL)); + + create_pathlist (manp, manpathlist); + + display_seen = hashtable_create (&null_hashtable_free); + +#ifdef HAVE_ICONV + locale_charset = xasprintf ("%s//IGNORE", get_locale_charset ()); + conv_to_locale = iconv_open (locale_charset, "UTF-8"); + free (locale_charset); +#endif /* HAVE_ICONV */ + + if (regex_opt) { + int i; + preg = XNMALLOC (num_keywords, regex_t); + for (i = 0; i < num_keywords; ++i) + xregcomp (&preg[i], keywords[i], + REG_EXTENDED | REG_NOSUB | REG_ICASE); + } + + if (!search ((const char **) keywords, num_keywords)) + status = NOT_FOUND; + + if (regex_opt) { + int i; + for (i = 0; i < num_keywords; ++i) + regfree (&preg[i]); + free (preg); + } + +#ifdef HAVE_ICONV + if (conv_to_locale != (iconv_t) -1) + iconv_close (conv_to_locale); +#endif /* HAVE_ICONV */ + hashtable_free (display_seen); + free_pathlist (manpathlist); + free (manp); + free (internal_locale); + exit (status); +} |