2 * man.c: The manual pager
4 * Copyright (C) 1990, 1991 John W. Eaton.
5 * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.)
6 * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
7 * 2011, 2012 Colin Watson.
9 * This file is part of man-db.
11 * man-db is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * man-db is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with man-db; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 * Department of Chemical Engineering
28 * The University of Texas at Austin
31 * Mostly written/re-written by Wilf, some routines by Markus Armbruster.
33 * CJW: Various robustness, security, and internationalization fixes.
34 * Improved HTML support (originally written by Fabrizio Polacco).
35 * Rewrite of page location routines for improved maintainability and
41 #endif /* HAVE_CONFIG_H */
66 #include <sys/types.h>
74 #include "stat-time.h"
76 #include "xvasprintf.h"
81 #define _(String) gettext (String)
82 #define N_(String) gettext_noop (String)
84 #include "manconfig.h"
88 #include "hashtable.h"
90 #include "pathsearch.h"
91 #include "linelength.h"
92 #include "decompress.h"
95 #include "encodings.h"
96 #include "orderfiles.h"
100 #include "db_storage.h"
102 #include "filenames.h"
103 #include "globbing.h"
107 #include "manconv_client.h"
112 #endif /* MAN_OWNER */
114 /* the default preprocessor sequence */
115 #ifndef DEFAULT_MANROFFSEQ
116 # define DEFAULT_MANROFFSEQ ""
119 /* placeholder for the manual page name in the less prompt string */
120 #define MAN_PN "$MAN_PN"
122 /* Some systems lack these */
124 # define STDIN_FILENO 0
126 #ifndef STDOUT_FILENO
127 # define STDOUT_FILENO 1
129 #ifndef STDERR_FILENO
130 # define STDERR_FILENO 2
135 /* external formatter programs, one for use without -t, and one with -t */
136 #define NFMT_PROG "mandb_nfmt"
137 #define TFMT_PROG "mandb_tfmt"
138 #undef ALT_EXT_FORMAT /* allow external formatters located in cat hierarchy */
140 static int global_manpath = -1; /* global or user manual page hierarchy? */
141 static int skip; /* page exists but has been skipped */
143 #if defined _AIX || defined __sgi
148 const char *req_name;
153 struct mandata *source;
154 int add_index; /* for sort stabilisation */
155 struct candidate *next;
158 #define CANDIDATE_FILESYSTEM 0
159 #define CANDIDATE_DATABASE 1
161 static void gripe_system (pipeline *p, int status)
163 error (CHILD_FAIL, 0, _("command exited with status %d: %s"),
164 status, pipeline_tostring (p));
173 OPT_NO_JUSTIFICATION,
179 struct string_llist {
181 struct string_llist *next;
185 static char *manpathlist[MAXDIRS];
189 char *database = NULL;
190 extern const char *extension; /* for globbing.c */
191 extern char *user_config_file; /* defined in manp.c */
192 extern int disable_cache;
193 extern int min_cat_width, max_cat_width, cat_width;
194 man_sandbox *sandbox;
197 static const char *alt_system_name;
198 static const char **section_list;
199 static const char *section;
200 static char *colon_sep_section_list;
201 static const char *preprocessors;
202 static const char *pager;
203 static const char *locale;
204 static char *internal_locale, *multiple_locale;
205 static const char *prompt_string;
207 static const char *std_sections[] = STD_SECTIONS;
209 static const char *external;
210 static struct hashtable *db_hash = NULL;
213 static const char *roff_device = NULL;
214 static const char *want_encoding = NULL;
215 #ifdef NROFF_WARNINGS
216 static const char default_roff_warnings[] = "mac";
217 static struct string_llist *roff_warnings = NULL;
218 #endif /* NROFF_WARNINGS */
219 static int global_apropos;
220 static int print_where, print_where_cat;
222 static int local_man_file;
225 static int match_case;
226 static int regex_opt;
228 static int names_only;
229 static int ult_flags = SO_LINK | SOFT_LINK | HARD_LINK;
230 static const char *recode = NULL;
231 static int no_hyphenation;
232 static int no_justification;
233 static int subpages = 1;
235 static int ascii; /* insert tr in the output pipe */
236 static int save_cat; /* security breach? Can we save the cat? */
238 static int first_arg;
240 static int found_a_stray; /* found a straycat */
243 static char *tmp_cat_file; /* for open_cat_stream(), close_cat_stream() */
244 static int created_tmp_cat; /* dto. */
246 static int tmp_cat_fd;
247 static struct timespec man_modtime; /* modtime of man page, for
248 * commit_tmp_cat() */
250 # ifdef TROFF_IS_GROFF
252 static const char *gxditview;
254 static const char *html_pager;
255 # endif /* TROFF_IS_GROFF */
257 const char *argp_program_version = "man " PACKAGE_VERSION;
258 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
259 error_t argp_err_exit_status = FAIL;
261 static const char args_doc[] = N_("[SECTION] PAGE...");
263 # ifdef NROFF_WARNINGS
264 # define ONLY_NROFF_WARNINGS 0
266 # define ONLY_NROFF_WARNINGS OPTION_HIDDEN
269 # ifdef TROFF_IS_GROFF
270 # define ONLY_TROFF_IS_GROFF 0
272 # define ONLY_TROFF_IS_GROFF OPTION_HIDDEN
275 /* Please keep these options in the same order as in parse_opt below. */
276 static struct argp_option options[] = {
277 { "config-file", 'C', N_("FILE"), 0, N_("use this user configuration file") },
278 { "debug", 'd', 0, 0, N_("emit debugging messages") },
279 { "default", 'D', 0, 0, N_("reset all options to their default values") },
280 { "warnings", OPT_WARNINGS, N_("WARNINGS"), ONLY_NROFF_WARNINGS | OPTION_ARG_OPTIONAL,
281 N_("enable warnings from groff") },
283 { 0, 0, 0, 0, N_("Main modes of operation:"), 10 },
284 { "whatis", 'f', 0, 0, N_("equivalent to whatis") },
285 { "apropos", 'k', 0, 0, N_("equivalent to apropos") },
286 { "global-apropos", 'K', 0, 0, N_("search for text in all pages") },
287 { "where", 'w', 0, 0, N_("print physical location of man page(s)") },
288 { "path", 0, 0, OPTION_ALIAS },
289 { "location", 0, 0, OPTION_ALIAS },
290 { "where-cat", 'W', 0, 0, N_("print physical location of cat file(s)") },
291 { "location-cat", 0, 0, OPTION_ALIAS },
292 { "local-file", 'l', 0, 0, N_("interpret PAGE argument(s) as local filename(s)") },
293 { "catman", 'c', 0, 0, N_("used by catman to reformat out of date cat pages"), 11 },
294 { "recode", 'R', N_("ENCODING"), 0, N_("output source page encoded in ENCODING") },
296 { 0, 0, 0, 0, N_("Finding manual pages:"), 20 },
297 { "locale", 'L', N_("LOCALE"), 0, N_("define the locale for this particular man search") },
298 { "systems", 'm', N_("SYSTEM"), 0, N_("use manual pages from other systems") },
299 { "manpath", 'M', N_("PATH"), 0, N_("set search path for manual pages to PATH") },
300 { "sections", 'S', N_("LIST"), 0, N_("use colon separated section list"), 21 },
301 { 0, 's', 0, OPTION_ALIAS },
302 { "extension", 'e', N_("EXTENSION"),
303 0, N_("limit search to extension type EXTENSION"), 22 },
304 { "ignore-case", 'i', 0, 0, N_("look for pages case-insensitively (default)"), 23 },
305 { "match-case", 'I', 0, 0, N_("look for pages case-sensitively") },
306 { "regex", OPT_REGEX, 0, 0, N_("show all pages matching regex"), 24 },
307 { "wildcard", OPT_WILDCARD, 0, 0, N_("show all pages matching wildcard") },
308 { "names-only", OPT_NAMES, 0, 0, N_("make --regex and --wildcard match page names only, not "
309 "descriptions"), 25 },
310 { "all", 'a', 0, 0, N_("find all matching manual pages"), 26 },
311 { "update", 'u', 0, 0, N_("force a cache consistency check") },
313 OPT_NO_SUBPAGES, 0, 0, N_("don't try subpages, e.g. 'man foo bar' => 'man foo-bar'"), 27 },
315 { 0, 0, 0, 0, N_("Controlling formatted output:"), 30 },
316 { "pager", 'P', N_("PAGER"), 0, N_("use program PAGER to display output") },
317 { "prompt", 'r', N_("STRING"), 0, N_("provide the `less' pager with a prompt") },
318 { "ascii", '7', 0, 0, N_("display ASCII translation of certain latin1 chars"), 31 },
319 { "encoding", 'E', N_("ENCODING"), 0, N_("use selected output encoding") },
321 OPT_NO_HYPHENATION, 0, 0, N_("turn off hyphenation") },
322 { "nh", 0, 0, OPTION_ALIAS },
323 { "no-justification",
324 OPT_NO_JUSTIFICATION, 0, 0, N_("turn off justification") },
325 { "nj", 0, 0, OPTION_ALIAS },
326 { "preprocessor", 'p', N_("STRING"), 0, N_("STRING indicates which preprocessors to run:\n"
327 "e - [n]eqn, p - pic, t - tbl,\n"
328 "g - grap, r - refer, v - vgrind") },
330 { "troff", 't', 0, 0, N_("use %s to format pages"), 32 },
331 { "troff-device", 'T', N_("DEVICE"), OPTION_ARG_OPTIONAL,
332 N_("use %s with selected device") },
333 { "html", 'H', N_("BROWSER"), ONLY_TROFF_IS_GROFF | OPTION_ARG_OPTIONAL,
334 N_("use %s or BROWSER to display HTML output"), 33 },
335 { "gxditview", 'X', N_("RESOLUTION"),
336 ONLY_TROFF_IS_GROFF | OPTION_ARG_OPTIONAL,
337 N_("use groff and display through gxditview (X11):\n"
338 "-X = -TX75, -X100 = -TX100, -X100-12 = -TX100-12") },
339 { "ditroff", 'Z', 0, ONLY_TROFF_IS_GROFF, N_("use groff and force it to produce ditroff") },
340 #endif /* HAS_TROFF */
342 { 0, 'h', 0, OPTION_HIDDEN, 0 }, /* compatibility for --help */
346 static void init_html_pager (void)
348 html_pager = getenv ("BROWSER");
350 html_pager = WEB_BROWSER;
353 static error_t parse_opt (int key, char *arg, struct argp_state *state)
355 static int apropos, whatis; /* retain values between calls */
357 /* Please keep these keys in the same order as in options above. */
360 user_config_file = arg;
366 /* discard all preset options */
367 local_man_file = findall = update = catman =
368 debug_level = troff = global_apropos =
369 print_where = print_where_cat =
371 regex_opt = wildcard = names_only =
372 no_hyphenation = no_justification = 0;
373 #ifdef TROFF_IS_GROFF
379 roff_device = want_encoding = extension = pager =
380 locale = alt_system_name = external =
381 preprocessors = NULL;
382 colon_sep_section_list = manp = NULL;
386 #ifdef NROFF_WARNINGS
389 (arg ? arg : default_roff_warnings);
392 for (warning = strtok (s, ","); warning;
393 warning = strtok (NULL, ",")) {
394 struct string_llist *new;
395 new = xmalloc (sizeof *new);
396 new->name = xstrdup (warning);
397 new->next = roff_warnings;
403 #endif /* NROFF_WARNINGS */
431 ult_flags &= ~SO_LINK;
438 alt_system_name = arg;
446 colon_sep_section_list = arg;
474 case OPT_NO_SUBPAGES:
489 if (is_roff_device (want_encoding))
490 roff_device = want_encoding;
492 case OPT_NO_HYPHENATION:
495 case OPT_NO_JUSTIFICATION:
496 no_justification = 1;
506 /* Traditional nroff knows -T; troff does not (gets
507 * ignored). All incarnations of groff know it. Why
510 roff_device = (arg ? arg : "ps");
514 # ifdef TROFF_IS_GROFF
519 roff_device = "html";
520 # endif /* TROFF_IS_GROFF */
523 # ifdef TROFF_IS_GROFF
525 gxditview = (arg ? arg : "75");
526 # endif /* TROFF_IS_GROFF */
529 # ifdef TROFF_IS_GROFF
532 # endif /* TROFF_IS_GROFF */
534 #endif /* HAS_TROFF */
537 argp_state_help (state, state->out_stream,
540 case ARGP_KEY_SUCCESS:
541 /* check for incompatible options */
542 if (troff + whatis + apropos + catman +
543 (print_where || print_where_cat) > 1) {
544 char *badopts = xasprintf
546 troff ? "-[tTZH] " : "",
548 apropos ? "-k " : "",
550 print_where ? "-w " : "",
551 print_where_cat ? "-W " : "");
553 _("%s: incompatible options"),
556 if (regex_opt + wildcard > 1) {
557 char *badopts = xasprintf
559 regex_opt ? "--regex " : "",
560 wildcard ? "--wildcard " : "");
562 _("%s: incompatible options"),
567 return ARGP_ERR_UNKNOWN;
570 static char *help_filter (int key, const char *text,
571 void *input ATTRIBUTE_UNUSED)
574 # ifdef TROFF_IS_GROFF
575 static const char formatter[] = "groff";
578 static const char formatter[] = "troff";
579 # endif /* TROFF_IS_GROFF */
580 #endif /* HAS_TROFF */
586 return xasprintf (text, formatter);
587 # ifdef TROFF_IS_GROFF
589 browser = html_pager;
591 if (STRNEQ (browser, "exec ", 5))
593 return xasprintf (text, browser);
594 # endif /* TROFF_IS_GROFF */
595 #endif /* HAS_TROFF */
597 return (char *) text;
601 static struct argp argp = { options, parse_opt, args_doc, 0, 0, help_filter };
604 * changed these messages from stdout to stderr,
605 * (Fabrizio Polacco) Fri, 14 Feb 1997 01:30:07 +0200
607 static void gripe_no_name (const char *sect)
610 fprintf (stderr, _("No manual entry for %s\n"), sect);
612 _("(Alternatively, what manual page do you want from "
616 fputs (_("What manual page do you want?\n"), stderr);
621 /* In case we're set-id, double-check that our standard file descriptors are
622 * open in a standard way. See:
624 * http://austingroupbugs.net/view.php?id=173
626 static void check_standard_fds (void)
630 /* We can't even write an error message in this case, so check it
633 flags = fcntl (2, F_GETFL);
636 mode = flags & O_ACCMODE;
637 if (mode != O_WRONLY && mode != O_RDWR)
640 flags = fcntl (0, F_GETFL);
642 fprintf (stderr, "stdin not open!\n");
645 mode = flags & O_ACCMODE;
646 if (mode != O_RDONLY && mode != O_RDWR) {
647 fprintf (stderr, "stdin not open for reading!\n");
651 flags = fcntl (1, F_GETFL);
653 fprintf (stderr, "stdout not open!\n");
656 mode = flags & O_ACCMODE;
657 if (mode != O_WRONLY && mode != O_RDWR) {
658 fprintf (stderr, "stdout not open for reading!\n");
663 static struct termios tms;
664 static int tms_set = 0;
665 static pid_t tms_pid = 0;
667 static void set_term (void)
669 if (tms_set && getpid () == tms_pid)
670 tcsetattr (STDIN_FILENO, TCSANOW, &tms);
673 static void get_term (void)
675 if (isatty (STDOUT_FILENO)) {
676 debug ("is a tty\n");
677 tcgetattr (STDIN_FILENO, &tms);
679 /* Work around pipecmd_exec calling exit(3) rather
680 * than _exit(2), which means our atexit-registered
681 * functions are called at the end of each child
682 * process created using pipecmd_new_function and
683 * friends. It would probably be good to fix this
684 * in libpipeline at some point, but it would
685 * require care to avoid breaking compatibility.
693 #if defined(TROFF_IS_GROFF) || defined(HEIRLOOM_NROFF)
694 static int get_roff_line_length (void)
696 int line_length = cat_width ? cat_width : get_line_length ();
698 /* groff >= 1.18 defaults to 78. */
699 if ((!troff || ditroff) && line_length != 80) {
700 int length = line_length * 39 / 40;
701 if (length > line_length - 2)
702 return line_length - 2;
709 #ifdef HEIRLOOM_NROFF
710 static void heirloom_line_length (void *data)
712 printf (".ll %sn\n", (const char *) data);
713 /* TODO: This fails to do anything useful. Why? */
714 printf (".lt %sn\n", (const char *) data);
717 #endif /* HEIRLOOM_NROFF */
719 static pipecmd *add_roff_line_length (pipecmd *cmd, int *save_cat_p)
725 int line_length = get_line_length ();
726 debug ("Terminal width %d\n", line_length);
727 if (line_length >= min_cat_width &&
728 line_length <= max_cat_width)
729 debug ("Terminal width %d within cat page range "
731 line_length, min_cat_width, max_cat_width);
733 debug ("Terminal width %d not within cat page range "
735 line_length, min_cat_width, max_cat_width);
740 length = get_roff_line_length ();
742 #ifdef HEIRLOOM_NROFF
746 #endif /* HEIRLOOM_NROFF */
748 debug ("Using %d-character lines\n", length);
749 #if defined(TROFF_IS_GROFF)
750 pipecmd_argf (cmd, "-rLL=%dn", length);
751 pipecmd_argf (cmd, "-rLT=%dn", length);
752 #elif defined(HEIRLOOM_NROFF)
753 name = xasprintf ("echo .ll %dn && echo .lt %dn",
755 lldata = xasprintf ("%d", length);
756 llcmd = pipecmd_new_function (name, heirloom_line_length, free,
758 ret = pipecmd_new_sequence ("line-length", llcmd,
759 pipecmd_new_passthrough (), NULL);
761 #endif /* HEIRLOOM_NROFF */
766 #endif /* TROFF_IS_GROFF || HEIRLOOM_NROFF */
768 static void gripe_no_man (const char *name, const char *sec)
770 /* On AIX and IRIX, fall back to the vendor supplied browser. */
771 #if defined _AIX || defined __sgi
776 vendor_man = pipecmd_new ("/usr/bin/man");
777 for (i = 1; i < argc; ++i)
778 pipecmd_arg (vendor_man, global_argv[i]);
779 pipecmd_unsetenv (vendor_man, "MANPATH");
780 pipecmd_exec (vendor_man);
785 fprintf (stderr, _("No manual entry for %s in section %s\n"),
788 fprintf (stderr, _("No manual entry for %s\n"), name);
791 if (getenv ("MAN_TEST_DISABLE_UNDOCUMENTED") == NULL &&
792 pathsearch_executable (name))
794 _("See '%s' for help when manual pages are not "
795 "available.\n"), UNDOC_COMMAND);
799 /* fire up the appropriate external program */
800 static void do_extern (int argc, char *argv[])
805 cmd = pipecmd_new (external);
806 /* Please keep these in the same order as they are in whatis.c. */
808 pipecmd_arg (cmd, "-d");
809 if (local_man_file) /* actually apropos/whatis --long */
810 pipecmd_arg (cmd, "-l");
811 if (colon_sep_section_list)
812 pipecmd_args (cmd, "-s", colon_sep_section_list, NULL);
814 pipecmd_args (cmd, "-m", alt_system_name, NULL);
816 pipecmd_args (cmd, "-M", manp, NULL);
818 pipecmd_args (cmd, "-L", locale, NULL);
819 if (user_config_file)
820 pipecmd_args (cmd, "-C", user_config_file, NULL);
821 while (first_arg < argc)
822 pipecmd_arg (cmd, argv[first_arg++]);
823 p = pipeline_new_commands (cmd, NULL);
825 /* privs are already dropped */
826 exit (pipeline_run (p));
829 /* lookup $MANOPT and if available, put in *argv[] format for argp */
830 static char **manopt_to_env (int *argc)
832 char *manopt, *manopt_copy, *opt_start, **argv;
834 manopt = getenv ("MANOPT");
835 if (manopt == NULL || *manopt == '\0')
838 opt_start = manopt = manopt_copy = xstrdup (manopt);
840 /* allocate space for the program name */
842 argv = XNMALLOC (*argc + 3, char *);
843 argv[(*argc)++] = base_name (program_name);
845 /* for each [ \t]+ delimited string, allocate an array space and fill
846 it in. An escaped space is treated specially */
851 if (manopt != opt_start) {
853 argv = xnrealloc (argv, *argc + 3,
855 argv[(*argc)++] = xstrdup (opt_start);
857 while (CTYPE (isspace, *(manopt + 1)))
859 opt_start = manopt + 1;
862 if (*(manopt + 1) == ' ')
872 argv[(*argc)++] = xstrdup (opt_start);
879 /* Return char array with 'less' special chars escaped. Uses static storage. */
880 static const char *escape_less (const char *string)
882 static char *escaped_string;
885 /* 2*strlen will always be long enough to hold the escaped string */
886 ptr = escaped_string = xrealloc (escaped_string,
887 2 * strlen (string) + 1);
890 if (*string == '?' ||
901 return escaped_string;
904 #if defined(MAN_DB_CREATES) || defined(MAN_DB_UPDATES)
905 /* Run mandb to ensure databases are up to date. Only used with -u.
906 * Returns the exit status of mandb.
908 * If filename is non-NULL, uses mandb's -f option to update a single file.
910 static int run_mandb (int create, const char *manpath, const char *filename)
912 pipeline *mandb_pl = pipeline_new ();
913 pipecmd *mandb_cmd = pipecmd_new ("mandb");
916 pipecmd_arg (mandb_cmd, "-d");
918 pipecmd_arg (mandb_cmd, "-q");
920 if (user_config_file)
921 pipecmd_args (mandb_cmd, "-C", user_config_file, NULL);
924 pipecmd_args (mandb_cmd, "-f", filename, NULL);
926 pipecmd_arg (mandb_cmd, "-c");
927 pipecmd_setenv (mandb_cmd, "MAN_MUST_CREATE", "1");
929 pipecmd_arg (mandb_cmd, "-p");
932 pipecmd_arg (mandb_cmd, manpath);
934 pipeline_command (mandb_pl, mandb_cmd);
937 debug ("running mandb: ");
938 pipeline_dump (mandb_pl, stderr);
941 return pipeline_run (mandb_pl);
943 #endif /* MAN_DB_CREATES || MAN_DB_UPDATES */
946 static char *locale_manpath (const char *manpath)
951 if (multiple_locale && *multiple_locale) {
952 if (internal_locale && *internal_locale)
953 all_locales = xasprintf ("%s:%s", multiple_locale,
956 all_locales = xstrdup (multiple_locale);
958 if (internal_locale && *internal_locale)
959 all_locales = xstrdup (internal_locale);
964 new_manpath = add_nls_manpaths (manpath, all_locales);
971 * Check to see if the argument is a valid section number.
972 * If the name matches one of
973 * the sections listed in section_list, we'll assume that it's a section.
974 * The list of sections in config.h simply allows us to specify oddly
975 * named directories like .../man3f. Yuk.
977 static const char *is_section (const char *name)
981 for (vs = section_list; *vs; vs++) {
982 if (STREQ (*vs, name))
984 /* allow e.g. 3perl but disallow 8139too and libfoo */
985 if (strlen (*vs) == 1 && CTYPE (isdigit, **vs) &&
986 strlen (name) > 1 && !CTYPE (isdigit, name[1]) &&
987 STRNEQ (*vs, name, 1))
993 /* Snarf pre-processors from file, return string or NULL on failure */
994 static char *get_preprocessors_from_file (pipeline *decomp, int prefixes)
997 const size_t block = 4096;
1000 size_t previous_len = 0;
1005 /* Prefixes are inserted into the stream by man itself, and we must
1006 * skip over them to find any preprocessors line that exists. Each
1007 * one ends with an .lf macro.
1009 for (i = 0; ; ++i) {
1010 size_t len = block * (i + 1);
1011 const char *buffer, *scan, *end;
1014 scan = buffer = pipeline_peek (decomp, &len);
1015 if (!buffer || len == 0)
1018 for (j = 0; j < prefixes; ++j) {
1019 scan = memmem (scan, len - (scan - buffer),
1020 "\n.lf ", strlen ("\n.lf "));
1024 scan = memchr (scan, '\n', len - (scan - buffer));
1032 end = memchr (scan, '\n', len - (scan - buffer));
1033 if (!end && len == previous_len)
1034 /* end of file, no newline found */
1035 end = buffer + len - 1;
1037 line = xstrndup (scan, end - scan + 1);
1045 if (!strncmp (line, PP_COOKIE, 4)) {
1046 const char *newline = strchr (line, '\n');
1048 return xstrndup (line + 4, newline - (line + 4));
1050 return xstrdup (line + 4);
1057 /* Determine pre-processors, set save_cat and return string */
1058 static char *get_preprocessors (pipeline *decomp, const char *dbfilters,
1062 const char *pp_source;
1065 /* try in order: database, command line, file, environment, default */
1066 /* command line overrides the database, but database empty overrides default */
1067 if (dbfilters && (dbfilters[0] != '-') && !preprocessors) {
1068 pp_string = xstrdup (dbfilters);
1069 pp_source = "database";
1071 } else if (preprocessors) {
1072 pp_string = xstrdup (preprocessors);
1073 pp_source = "command line";
1075 } else if ((pp_string = get_preprocessors_from_file (decomp,
1079 } else if ((env = getenv ("MANROFFSEQ"))) {
1080 pp_string = xstrdup (env);
1081 pp_source = "environment";
1083 } else if (!dbfilters) {
1084 pp_string = xstrdup (DEFAULT_MANROFFSEQ);
1085 pp_source = "default";
1088 pp_string = xstrdup ("");
1089 pp_source = "no filters";
1093 debug ("pre-processors `%s' from %s\n", pp_string, pp_source);
1097 static const char *my_locale_charset (void)
1099 if (want_encoding && !is_roff_device (want_encoding))
1100 return want_encoding;
1102 return get_locale_charset ();
1105 static void add_col (pipeline *p, const char *locale_charset, ...)
1109 char *col_locale = NULL;
1111 cmd = pipecmd_new (COL);
1112 va_start (argv, locale_charset);
1113 pipecmd_argv (cmd, argv);
1115 pipecmd_pre_exec (cmd, sandbox_load, sandbox_free, sandbox);
1118 col_locale = find_charset_locale (locale_charset);
1120 pipecmd_setenv (cmd, "LC_CTYPE", col_locale);
1124 pipeline_command (p, cmd);
1127 /* Return pipeline to format file to stdout. */
1128 static pipeline *make_roff_command (const char *dir, const char *file,
1129 pipeline *decomp, const char *pp_string,
1130 char **result_encoding)
1132 const char *roff_opt;
1133 char *fmt_prog = NULL;
1134 pipeline *p = pipeline_new ();
1136 char *page_encoding = NULL;
1137 const char *output_encoding = NULL;
1138 const char *locale_charset = NULL;
1140 *result_encoding = xstrdup ("UTF-8"); /* optimistic default */
1142 roff_opt = getenv ("MANROFFOPT");
1146 if (dir && !recode) {
1147 #ifdef ALT_EXT_FORMAT
1148 char *catpath = get_catpath
1149 (dir, global_manpath ? SYSTEM_CAT : USER_CAT);
1151 /* If we have an alternate catpath, look for an external
1155 fmt_prog = appendstr (catpath, "/",
1156 troff ? TFMT_PROG : NFMT_PROG,
1158 if (!CAN_ACCESS (fmt_prog, X_OK)) {
1163 #endif /* ALT_EXT_FORMAT */
1165 /* If the page is in a proper manual page hierarchy (as
1166 * opposed to being read using --local-file or similar),
1167 * look for an external formatter there.
1170 fmt_prog = appendstr (NULL, dir, "/",
1171 troff ? TFMT_PROG : NFMT_PROG,
1173 if (!CAN_ACCESS (fmt_prog, X_OK)) {
1181 debug ("External formatter %s\n", fmt_prog);
1184 /* we don't have an external formatter script */
1185 const char *source_encoding, *roff_encoding;
1186 const char *groff_preconv;
1189 struct zsoelim_stdin_data *zsoelim_data;
1191 zsoelim_data = zsoelim_stdin_data_new (dir,
1193 cmd = pipecmd_new_function (ZSOELIM, &zsoelim_stdin,
1194 zsoelim_stdin_data_free,
1196 pipecmd_pre_exec (cmd, sandbox_load, sandbox_free,
1198 pipeline_command (p, cmd);
1201 page_encoding = check_preprocessor_encoding (decomp);
1203 page_encoding = get_page_encoding (lang);
1204 if (page_encoding && !STREQ (page_encoding, "UTF-8"))
1205 source_encoding = page_encoding;
1207 source_encoding = get_source_encoding (lang);
1208 debug ("page_encoding = %s\n", page_encoding);
1209 debug ("source_encoding = %s\n", source_encoding);
1211 /* Load the roff_device value dependent on the language dir
1215 #define STRC(s, otherwise) ((s) ? (s) : (otherwise))
1217 locale_charset = my_locale_charset ();
1218 debug ("locale_charset = %s\n",
1219 STRC (locale_charset, "NULL"));
1221 /* Pick the default device for this locale if there
1222 * wasn't one selected explicitly.
1226 get_default_device (locale_charset,
1228 #ifdef HEIRLOOM_NROFF
1229 /* In Heirloom, if LC_CTYPE is a UTF-8
1230 * locale, then -Tlocale will be equivalent
1231 * to -Tutf8 except that it will do a
1232 * slightly better job of rendering some
1233 * special characters.
1235 if (STREQ (roff_device, "utf8")) {
1236 const char *real_locale_charset =
1237 get_locale_charset ();
1238 if (real_locale_charset &&
1239 STREQ (real_locale_charset,
1241 roff_device = "locale";
1243 #endif /* HEIRLOOM_NROFF */
1244 debug ("roff_device (locale) = %s\n",
1245 STRC (roff_device, "NULL"));
1249 roff_encoding = get_roff_encoding (roff_device,
1251 debug ("roff_encoding = %s\n", roff_encoding);
1253 /* We may need to recode:
1254 * from page_encoding to roff_encoding on input;
1255 * from output_encoding to locale_charset on output
1257 * If we have preconv, then use it to recode the
1258 * input to a safe escaped form.
1259 * The --recode option overrides everything else.
1261 groff_preconv = get_groff_preconv ();
1263 add_manconv (p, page_encoding, recode);
1264 else if (groff_preconv) {
1265 pipecmd *preconv_cmd;
1266 add_manconv (p, page_encoding, "UTF-8");
1267 preconv_cmd = pipecmd_new_args
1268 (groff_preconv, "-e", "UTF-8", NULL);
1269 pipecmd_pre_exec (preconv_cmd, sandbox_load,
1270 sandbox_free, sandbox);
1271 pipeline_command (p, preconv_cmd);
1272 } else if (roff_encoding)
1273 add_manconv (p, page_encoding, roff_encoding);
1275 add_manconv (p, page_encoding, page_encoding);
1277 if (!troff && !recode) {
1278 output_encoding = get_output_encoding (roff_device);
1279 if (!output_encoding)
1280 output_encoding = source_encoding;
1281 debug ("output_encoding = %s\n", output_encoding);
1282 free (*result_encoding);
1283 *result_encoding = xstrdup (output_encoding);
1285 if (!getenv ("LESSCHARSET")) {
1286 const char *less_charset =
1287 get_less_charset (locale_charset);
1288 debug ("less_charset = %s\n", less_charset);
1289 setenv ("LESSCHARSET", less_charset, 1);
1292 if (!getenv ("JLESSCHARSET")) {
1293 const char *jless_charset =
1294 get_jless_charset (locale_charset);
1295 if (jless_charset) {
1296 debug ("jless_charset = %s\n",
1298 setenv ("JLESSCHARSET",
1307 else if (!fmt_prog) {
1310 #endif /* GNU_NROFF */
1313 #ifdef NROFF_WARNINGS
1314 struct string_llist *cur;
1315 #endif /* NROFF_WARNINGS */
1316 int wants_dev = 0; /* filter wants a dev argument */
1317 int wants_post = 0; /* postprocessor arguments */
1320 /* set cmd according to *pp_string, on
1321 errors leave cmd as NULL */
1322 switch (*pp_string) {
1325 cmd = pipecmd_new_argstr
1326 (get_def ("eqn", EQN));
1328 cmd = pipecmd_new_argstr
1329 (get_def ("neqn", NEQN));
1333 cmd = pipecmd_new_argstr
1334 (get_def ("grap", GRAP));
1337 cmd = pipecmd_new_argstr
1338 (get_def ("pic", PIC));
1341 cmd = pipecmd_new_argstr
1342 (get_def ("tbl", TBL));
1345 #endif /* GNU_NROFF */
1348 cmd = pipecmd_new_argstr
1349 (get_def ("vgrind", VGRIND));
1352 cmd = pipecmd_new_argstr
1353 (get_def ("refer", REFER));
1358 /* done with preprocessors, now add roff */
1360 cmd = pipecmd_new_argstr
1361 (get_def ("troff", TROFF));
1364 cmd = pipecmd_new_argstr
1365 (get_def ("nroff", NROFF));
1367 #ifdef TROFF_IS_GROFF
1368 if (troff && ditroff)
1369 pipecmd_arg (cmd, "-Z");
1370 #endif /* TROFF_IS_GROFF */
1372 #if defined(TROFF_IS_GROFF) || defined(HEIRLOOM_NROFF)
1374 pipecmd *seq = add_roff_line_length
1377 pipeline_command (p, seq);
1379 #endif /* TROFF_IS_GROFF || HEIRLOOM_NROFF */
1381 #ifdef NROFF_WARNINGS
1382 for (cur = roff_warnings; cur;
1384 pipecmd_argf (cmd, "-w%s", cur->name);
1385 #endif /* NROFF_WARNINGS */
1387 #ifdef HEIRLOOM_NROFF
1388 if (running_setuid ())
1389 pipecmd_unsetenv (cmd, "TROFFMACS");
1390 #endif /* HEIRLOOM_NROFF */
1392 pipecmd_argstr (cmd, roff_opt);
1400 assert (*pp_string); /* didn't fail on roff */
1402 _("ignoring unknown preprocessor `%c'"),
1410 "-T%s", roff_device);
1411 #ifdef TROFF_IS_GROFF
1413 pipecmd_argf (cmd, "-TX%s", gxditview);
1414 #endif /* TROFF_IS_GROFF */
1418 #ifdef TROFF_IS_GROFF
1420 pipecmd_arg (cmd, "-X");
1421 #endif /* TROFF_IS_GROFF */
1423 if (roff_device && STREQ (roff_device, "ps"))
1424 /* Tell grops to guess the page
1427 pipecmd_arg (cmd, "-P-g");
1430 pipecmd_pre_exec (cmd, sandbox_load_permissive,
1431 sandbox_free, sandbox);
1432 pipeline_command (p, cmd);
1434 if (*pp_string == ' ' || *pp_string == '-')
1436 } while (*pp_string++);
1438 if (!troff && *COL) {
1439 const char *man_keep_formatting =
1440 getenv ("MAN_KEEP_FORMATTING");
1441 if ((!man_keep_formatting || !*man_keep_formatting) &&
1442 !isatty (STDOUT_FILENO))
1443 /* we'll run col later, but prepare for it */
1444 setenv ("GROFF_NO_SGR", "1", 1);
1447 else if (using_tbl && !troff && *COL)
1448 add_col (p, locale_charset, NULL);
1449 #endif /* GNU_NROFF */
1452 /* use external formatter script, it takes arguments
1453 input file, preprocessor string, and (optional)
1455 cmd = pipecmd_new_args (fmt_prog, file, pp_string, NULL);
1457 pipecmd_arg (cmd, roff_device);
1458 pipeline_command (p, cmd);
1461 free (page_encoding);
1465 #ifdef TROFF_IS_GROFF
1466 /* Return pipeline to run a browser on a given file, observing
1467 * http://www.tuxedo.org/~esr/BROWSER/.
1469 * (Actually, I really implement
1470 * https://www.dwheeler.com/browse/secure_browser.html, but it's
1471 * backward-compatible.)
1473 * TODO: Is there any way to use the pipeline library better here?
1475 static pipeline *make_browser (const char *pattern, const char *file)
1479 char *browser = xmalloc (1);
1480 int found_percent_s = 0;
1486 percent = strchr (pattern, '%');
1488 size_t len = strlen (browser);
1489 browser = xrealloc (browser, len + 1 + (percent - pattern));
1490 strncat (browser, pattern, percent - pattern);
1491 switch (*(percent + 1)) {
1494 browser = appendstr (browser, "%", NULL);
1497 browser = appendstr (browser, ":", NULL);
1500 esc_file = escape_shell (file);
1501 browser = appendstr (browser, esc_file, NULL);
1503 found_percent_s = 1;
1506 len = strlen (browser); /* cannot be NULL */
1507 browser = xrealloc (browser, len + 3);
1508 strncat (browser, percent, 2);
1512 pattern = percent + 2;
1514 pattern = percent + 1;
1515 percent = strchr (pattern, '%');
1517 browser = appendstr (browser, pattern, NULL);
1518 if (!found_percent_s) {
1519 esc_file = escape_shell (file);
1520 browser = appendstr (browser, " ", esc_file, NULL);
1524 cmd = pipecmd_new_args ("/bin/sh", "-c", browser, NULL);
1525 pipecmd_pre_exec (cmd, drop_privs, NULL, NULL);
1526 p = pipeline_new_commands (cmd, NULL);
1527 pipeline_ignore_signals (p, 1);
1532 #endif /* TROFF_IS_GROFF */
1534 static void setenv_less (pipecmd *cmd, const char *title)
1536 const char *esc_title;
1537 char *less_opts, *man_pn;
1539 esc_title = escape_less (title);
1540 less_opts = xasprintf (LESS_OPTS, prompt_string, prompt_string);
1541 less_opts = appendstr (less_opts, less, NULL);
1542 man_pn = strstr (less_opts, MAN_PN);
1545 xmalloc (strlen (less_opts) - strlen (MAN_PN) +
1546 strlen (esc_title) + 1);
1547 strncpy (subst_opts, less_opts, man_pn - less_opts);
1548 subst_opts[man_pn - less_opts] = '\0';
1549 strcat (subst_opts, esc_title);
1550 strcat (subst_opts, man_pn + strlen (MAN_PN));
1552 less_opts = subst_opts;
1553 man_pn = strstr (less_opts, MAN_PN);
1556 debug ("Setting LESS to %s\n", less_opts);
1557 pipecmd_setenv (cmd, "LESS", less_opts);
1559 debug ("Setting MAN_PN to %s\n", esc_title);
1560 pipecmd_setenv (cmd, "MAN_PN", esc_title);
1565 static void add_output_iconv (pipeline *p,
1566 const char *source, const char *target)
1568 debug ("add_output_iconv: source %s, target %s\n", source, target);
1569 if (source && target && !STREQ (source, target)) {
1570 char *target_translit = xasprintf ("%s//TRANSLIT", target);
1572 iconv_cmd = pipecmd_new_args
1573 ("iconv", "-c", "-f", source, "-t", target_translit,
1575 pipecmd_pre_exec (iconv_cmd, sandbox_load, sandbox_free,
1577 pipeline_command (p, iconv_cmd);
1578 free (target_translit);
1582 /* Pipeline command to squeeze multiple blank lines into one.
1585 static void squeeze_blank_lines (void *data ATTRIBUTE_UNUSED)
1590 while (getline (&line, &len, stdin) != -1) {
1591 int in_blank_line = 1;
1592 int got_blank_line = 0;
1594 while (in_blank_line) {
1596 for (p = line; *p; ++p) {
1597 if (!CTYPE (isspace, *p)) {
1603 if (in_blank_line) {
1608 if (getline (&line, &len, stdin) == -1)
1613 if (got_blank_line && putchar ('\n') < 0)
1616 if (!in_blank_line && fputs (line, stdout) < 0)
1628 /* Return pipeline to display file provided on stdin.
1630 * TODO: htmlout case is pretty weird now. I'd like the intelligence to be
1631 * somewhere other than format_display.
1633 static pipeline *make_display_command (const char *encoding, const char *title)
1635 pipeline *p = pipeline_new ();
1636 const char *locale_charset = NULL;
1637 pipecmd *pager_cmd = NULL;
1639 locale_charset = my_locale_charset ();
1641 if (!troff && (!want_encoding || !is_roff_device (want_encoding)))
1642 add_output_iconv (p, encoding, locale_charset);
1644 if (!troff && *COL) {
1645 /* get rid of special characters if not writing to a
1648 const char *man_keep_formatting =
1649 getenv ("MAN_KEEP_FORMATTING");
1650 if ((!man_keep_formatting || !*man_keep_formatting) &&
1651 !isatty (STDOUT_FILENO))
1652 add_col (p, locale_charset, "-b", "-p", "-x", NULL);
1655 /* emulate pager -s, the sed code is just for information */
1658 const char *name = "sed -e '/^[[:space:]]*$/{ N; /^[[:space:]]*\\n[[:space:]]*$/D; }'";
1659 cmd = pipecmd_new_function (name, &squeeze_blank_lines, NULL, NULL);
1660 pipeline_command (p, cmd);
1663 if (isatty (STDOUT_FILENO)) {
1666 tr_cmd = pipecmd_new_argstr
1667 (get_def_user ("tr", TR TR_SET1 TR_SET2));
1668 pipecmd_pre_exec (tr_cmd, sandbox_load, sandbox_free,
1670 pipeline_command (p, tr_cmd);
1671 pager_cmd = pipecmd_new_argstr (pager);
1673 #ifdef TROFF_IS_GROFF
1675 /* format_display deals with html_pager */
1677 pager_cmd = pipecmd_new_argstr (pager);
1681 setenv_less (pager_cmd, title);
1682 pipeline_command (p, pager_cmd);
1684 pipeline_ignore_signals (p, 1);
1686 if (!pipeline_get_ncommands (p))
1687 /* Always return at least a dummy pipeline. */
1688 pipeline_command (p, pipecmd_new_passthrough ());
1693 /* return a (malloced) temporary name in cat_file's directory */
1694 static char *tmp_cat_filename (const char *cat_file)
1699 name = xstrdup ("/dev/null");
1700 tmp_cat_fd = open (name, O_WRONLY);
1703 name = xstrdup (cat_file);
1704 slash = strrchr (name, '/');
1706 *(slash + 1) = '\0';
1709 name = appendstr (name, "catXXXXXX", NULL);
1710 tmp_cat_fd = mkstemp (name);
1713 if (tmp_cat_fd == -1) {
1721 /* If delete unlink tmp_cat, else commit tmp_cat to cat_file.
1722 Return non-zero on error.
1724 static int commit_tmp_cat (const char *cat_file, const char *tmp_cat,
1730 if (!delete && global_manpath && euid == 0) {
1732 debug ("fixing temporary cat's ownership\n");
1735 struct passwd *man_owner = get_man_owner ();
1736 status = chown (tmp_cat, man_owner->pw_uid,
1739 error (0, errno, _("can't chown %s"), tmp_cat);
1742 #endif /* MAN_OWNER */
1744 if (!delete && !status) {
1746 debug ("fixing temporary cat's mode\n");
1749 status = chmod (tmp_cat, CATMODE);
1751 error (0, errno, _("can't chmod %s"), tmp_cat);
1755 if (!delete && !status) {
1757 debug ("renaming temporary cat to %s\n", cat_file);
1760 status = rename (tmp_cat, cat_file);
1762 error (0, errno, _("can't rename %s to %s"),
1767 if (!delete && !status) {
1769 debug ("setting modtime on cat file %s\n", cat_file);
1772 struct timespec times[2];
1774 times[0].tv_sec = 0;
1775 times[0].tv_nsec = UTIME_NOW;
1776 times[1] = man_modtime;
1777 status = utimens (cat_file, times);
1779 error (0, errno, _("can't set times on %s"),
1784 if (delete || status) {
1786 debug ("unlinking temporary cat\n");
1787 else if (unlink (tmp_cat))
1788 error (0, errno, _("can't unlink %s"), tmp_cat);
1794 /* TODO: This should all be refactored after work on the decompression
1795 * library is complete.
1797 static void discard_stderr (pipeline *p)
1801 for (i = 0; i < pipeline_get_ncommands (p); ++i)
1802 pipecmd_discard_err (pipeline_get_command (p, i), 1);
1805 static void maybe_discard_stderr (pipeline *p)
1807 const char *man_keep_stderr = getenv ("MAN_KEEP_STDERR");
1808 if ((!man_keep_stderr || !*man_keep_stderr) && isatty (STDOUT_FILENO))
1812 static void chdir_commands (pipeline *p, const char *dir)
1816 for (i = 0; i < pipeline_get_ncommands (p); ++i)
1817 pipecmd_chdir (pipeline_get_command (p, i), dir);
1822 /* Return pipeline to write formatted manual page to for saving as cat file. */
1823 static pipeline *open_cat_stream (const char *cat_file, const char *encoding)
1830 created_tmp_cat = 0;
1832 debug ("creating temporary cat for %s\n", cat_file);
1834 tmp_cat_file = tmp_cat_filename (cat_file);
1836 created_tmp_cat = 1;
1838 if (!debug_level && (errno == EACCES || errno == EROFS)) {
1839 /* No permission to write to the cat file. Oh well,
1840 * return NULL and let the caller sort it out.
1842 debug ("can't write to temporary cat for %s\n",
1846 error (FATAL, errno,
1847 _("can't create temporary cat for %s"),
1852 push_cleanup ((cleanup_fun) unlink, tmp_cat_file, 1);
1854 cat_p = pipeline_new ();
1855 add_output_iconv (cat_p, encoding, "UTF-8");
1857 /* fork the compressor */
1858 comp_cmd = pipecmd_new_argstr (get_def ("compressor", COMPRESSOR));
1859 pipecmd_nice (comp_cmd, 10);
1860 pipecmd_pre_exec (comp_cmd, sandbox_load, sandbox_free, sandbox);
1861 pipeline_command (cat_p, comp_cmd);
1863 /* pipeline_start will close tmp_cat_fd */
1864 pipeline_want_out (cat_p, tmp_cat_fd);
1869 /* Close the cat page stream, return non-zero on error.
1870 If delete don't update the cat file.
1872 static int close_cat_stream (pipeline *cat_p, const char *cat_file,
1877 status = pipeline_wait (cat_p);
1878 debug ("cat-saver exited with status %d\n", status);
1880 pipeline_free (cat_p);
1882 if (created_tmp_cat) {
1883 status |= commit_tmp_cat (cat_file, tmp_cat_file,
1886 pop_cleanup ((cleanup_fun) unlink, tmp_cat_file);
1888 free (tmp_cat_file);
1893 * format a manual page with format_cmd, display it with disp_cmd, and
1894 * save it to cat_file
1896 static int format_display_and_save (pipeline *decomp,
1897 pipeline *format_cmd,
1899 const char *cat_file, const char *encoding)
1901 pipeline *sav_p = open_cat_stream (cat_file, encoding);
1905 drop_effective_privs ();
1907 maybe_discard_stderr (format_cmd);
1909 pipeline_connect (decomp, format_cmd, NULL);
1911 pipeline_connect (format_cmd, disp_cmd, sav_p, NULL);
1912 pipeline_pump (decomp, format_cmd, disp_cmd, sav_p, NULL);
1914 pipeline_connect (format_cmd, disp_cmd, NULL);
1915 pipeline_pump (decomp, format_cmd, disp_cmd, NULL);
1919 regain_effective_privs ();
1921 pipeline_wait (decomp);
1922 instat = pipeline_wait (format_cmd);
1924 close_cat_stream (sav_p, cat_file, instat);
1925 pipeline_wait (disp_cmd);
1928 #endif /* MAN_CATS */
1930 /* Format a manual page with format_cmd and display it with disp_cmd.
1931 * Handle temporary file creation if necessary.
1932 * TODO: merge with format_display_and_save
1934 static void format_display (pipeline *decomp,
1935 pipeline *format_cmd, pipeline *disp_cmd,
1936 const char *man_file)
1938 int format_status = 0, disp_status = 0;
1939 #ifdef TROFF_IS_GROFF
1940 char *htmldir = NULL, *htmlfile = NULL;
1941 #endif /* TROFF_IS_GROFF */
1944 maybe_discard_stderr (format_cmd);
1946 drop_effective_privs ();
1948 #ifdef TROFF_IS_GROFF
1949 if (format_cmd && htmlout) {
1950 char *man_base, *man_ext;
1953 htmldir = create_tempdir ("hman");
1955 error (FATAL, errno,
1956 _("can't create temporary directory"));
1957 chdir_commands (format_cmd, htmldir);
1958 chdir_commands (disp_cmd, htmldir);
1959 man_base = base_name (man_file);
1960 man_ext = strchr (man_base, '.');
1963 htmlfile = xasprintf ("%s/%s.html", htmldir, man_base);
1965 htmlfd = open (htmlfile, O_CREAT | O_EXCL | O_WRONLY, 0644);
1967 error (FATAL, errno, _("can't open temporary file %s"),
1969 pipeline_want_out (format_cmd, htmlfd);
1970 pipeline_connect (decomp, format_cmd, NULL);
1971 pipeline_pump (decomp, format_cmd, NULL);
1972 pipeline_wait (decomp);
1973 format_status = pipeline_wait (format_cmd);
1975 #endif /* TROFF_IS_GROFF */
1977 pipeline_connect (decomp, format_cmd, NULL);
1978 pipeline_connect (format_cmd, disp_cmd, NULL);
1979 pipeline_pump (decomp, format_cmd, disp_cmd, NULL);
1980 pipeline_wait (decomp);
1981 format_status = pipeline_wait (format_cmd);
1982 disp_status = pipeline_wait (disp_cmd);
1984 pipeline_connect (decomp, disp_cmd, NULL);
1985 pipeline_pump (decomp, disp_cmd, NULL);
1986 pipeline_wait (decomp);
1987 disp_status = pipeline_wait (disp_cmd);
1990 #ifdef TROFF_IS_GROFF
1991 if (format_cmd && htmlout) {
1992 char *browser_list, *candidate;
1994 if (format_status) {
1995 if (remove_directory (htmldir, 0) == -1)
1997 _("can't remove directory %s"),
2001 gripe_system (format_cmd, format_status);
2004 browser_list = xstrdup (html_pager);
2005 for (candidate = strtok (browser_list, ":"); candidate;
2006 candidate = strtok (NULL, ":")) {
2008 debug ("Trying browser: %s\n", candidate);
2009 browser = make_browser (candidate, htmlfile);
2010 disp_status = pipeline_run (browser);
2015 if (html_pager && *html_pager)
2016 error (CHILD_FAIL, 0,
2017 "couldn't execute any browser from %s",
2020 error (CHILD_FAIL, 0,
2021 "no browser configured, so cannot show "
2024 free (browser_list);
2025 if (remove_directory (htmldir, 0) == -1)
2026 error (0, errno, _("can't remove directory %s"),
2031 #endif /* TROFF_IS_GROFF */
2033 if (format_status && format_status != (SIGPIPE + 0x80) * 256)
2034 gripe_system (format_cmd, format_status);
2035 if (disp_status && disp_status != (SIGPIPE + 0x80) * 256)
2036 gripe_system (disp_cmd, disp_status);
2039 regain_effective_privs ();
2042 /* "Display" a page in catman mode, which amounts to saving it. */
2043 /* TODO: merge with format_display_and_save? */
2044 static void display_catman (const char *cat_file, pipeline *decomp,
2045 pipeline *format_cmd, const char *encoding)
2047 char *tmpcat = tmp_cat_filename (cat_file);
2050 #endif /* COMP_CAT */
2053 add_output_iconv (format_cmd, encoding, "UTF-8");
2056 comp_cmd = pipecmd_new_argstr (get_def ("compressor", COMPRESSOR));
2057 pipecmd_pre_exec (comp_cmd, sandbox_load, sandbox_free, sandbox);
2058 pipeline_command (format_cmd, comp_cmd);
2059 #endif /* COMP_CAT */
2061 maybe_discard_stderr (format_cmd);
2062 pipeline_want_out (format_cmd, tmp_cat_fd);
2064 push_cleanup ((cleanup_fun) unlink, tmpcat, 1);
2066 /* save the cat as real user
2067 * (1) required for user man hierarchy
2068 * (2) else depending on ruid's privs is ok, effectively disables
2069 * catman for non-root.
2071 drop_effective_privs ();
2072 pipeline_connect (decomp, format_cmd, NULL);
2073 pipeline_pump (decomp, format_cmd, NULL);
2074 pipeline_wait (decomp);
2075 status = pipeline_wait (format_cmd);
2076 regain_effective_privs ();
2078 gripe_system (format_cmd, status);
2081 commit_tmp_cat (cat_file, tmpcat, status);
2082 pop_cleanup ((cleanup_fun) unlink, tmpcat);
2086 static void disable_hyphenation (void *data ATTRIBUTE_UNUSED)
2094 static void disable_justification (void *data ATTRIBUTE_UNUSED)
2102 #ifdef TROFF_IS_GROFF
2103 static void locale_macros (void *data)
2105 const char *macro_lang = data;
2106 const char *hyphen_lang = STREQ (lang, "en") ? "us" : macro_lang;
2108 debug ("Macro language %s; hyphenation language %s\n",
2109 macro_lang, hyphen_lang);
2112 /* If we're using groff >= 1.20.2 (for the 'file' warning
2115 ".if \\n[.g] \\{\\\n"
2116 ". ds Ystring \\n[.Y]\n"
2117 ". while (\\B'\\*[Ystring]' = 0) .chop Ystring\n"
2118 ". if ((\\n[.x] > 1) :"
2119 " ((\\n[.x] == 1) & (\\n[.y] > 20)) :"
2120 " ((\\n[.x] == 1) & (\\n[.y] == 20) & (\\*[Ystring] >= 2))) "
2122 /* disable warnings of category 'file' */
2123 ". warn (\\n[.warn] -"
2124 " (\\n[.warn] / 1048576 %% 2 * 1048576))\n"
2125 /* and load the appropriate per-locale macros */
2130 /* set the hyphenation language anyway, to make sure groff
2131 * only hyphenates languages it knows about
2134 ".lf 1\n", macro_lang, hyphen_lang);
2136 #endif /* TROFF_IS_GROFF */
2138 /* allow user to skip a page or quit after viewing desired page
2142 static int do_prompt (const char *name)
2148 if (!isatty (STDOUT_FILENO) || !isatty (STDIN_FILENO))
2149 return 0; /* noninteractive */
2150 tty = fopen ("/dev/tty", "r+");
2156 "[ view (return) | skip (Ctrl-D) | quit (Ctrl-C) ]\n"),
2180 * optionally chdir to dir, if necessary update cat_file from man_file
2181 * and display it. if man_file is NULL cat_file is a stray cat. If
2182 * !save_cat or cat_file is NULL we must not save the formatted cat.
2183 * If man_file is "" this is a special case -- we expect the man page
2184 * on standard input.
2186 static int display (const char *dir, const char *man_file,
2187 const char *cat_file, const char *title,
2188 const char *dbfilters)
2193 pipeline *format_cmd; /* command to format man_file to stdout */
2194 char *formatted_encoding = NULL;
2195 int display_to_stdout;
2196 pipeline *decomp = NULL;
2197 int decomp_errno = 0;
2199 /* define format_cmd */
2201 pipecmd *seq = pipecmd_new_sequence ("decompressor", NULL);
2204 decomp = decompress_open (man_file);
2206 decomp = decompress_fdopen (dup (STDIN_FILENO));
2208 if (!recode && no_hyphenation) {
2209 pipecmd *hcmd = pipecmd_new_function (
2210 "echo .nh && echo .de hy && echo ..",
2211 disable_hyphenation, NULL, NULL);
2212 pipecmd_sequence_command (seq, hcmd);
2216 if (!recode && no_justification) {
2217 pipecmd *jcmd = pipecmd_new_function (
2218 "echo .na && echo .de ad && echo ..",
2219 disable_justification, NULL, NULL);
2220 pipecmd_sequence_command (seq, jcmd);
2224 #ifdef TROFF_IS_GROFF
2225 /* This only works with preconv, since the per-locale macros
2226 * may change the assumed input encoding.
2228 if (!recode && *man_file && get_groff_preconv ()) {
2229 char *page_lang = lang_dir (man_file);
2231 if (page_lang && *page_lang &&
2232 !STREQ (page_lang, "C")) {
2233 struct locale_bits bits;
2237 unpack_locale_bits (page_lang, &bits);
2238 name = xasprintf ("echo .mso %s.tmac",
2240 lcmd = pipecmd_new_function (
2241 name, locale_macros, free,
2242 xstrdup (bits.language));
2243 pipecmd_sequence_command (seq, lcmd);
2246 free_locale_bits (&bits);
2250 #endif /* TROFF_IS_GROFF */
2253 assert (pipeline_get_ncommands (decomp) <= 1);
2254 if (pipeline_get_ncommands (decomp)) {
2255 pipecmd_sequence_command
2257 pipeline_get_command (decomp, 0));
2258 pipeline_set_command (decomp, 0, seq);
2260 pipecmd_sequence_command
2261 (seq, pipecmd_new_passthrough ());
2262 pipeline_command (decomp, seq);
2271 pipeline_start (decomp);
2272 pp_string = get_preprocessors (decomp, dbfilters, prefixes);
2273 format_cmd = make_roff_command (dir, man_file, decomp,
2275 &formatted_encoding);
2277 chdir_commands (format_cmd, dir);
2278 debug ("formatted_encoding = %s\n", formatted_encoding);
2282 decomp_errno = errno;
2285 /* Get modification time, for commit_tmp_cat(). */
2286 if (man_file && *man_file) {
2288 if (stat (man_file, &stb)) {
2289 man_modtime.tv_sec = 0;
2290 man_modtime.tv_nsec = 0;
2292 man_modtime = get_stat_mtime (&stb);
2295 display_to_stdout = troff;
2296 #ifdef TROFF_IS_GROFF
2298 display_to_stdout = 0;
2301 display_to_stdout = 1;
2303 if (display_to_stdout) {
2304 /* If we're reading stdin via '-l -', man_file is "". See
2309 assert (!format_cmd); /* no need to free it */
2310 error (0, decomp_errno, _("can't open %s"), man_file);
2313 if (*man_file == '\0')
2316 found = CAN_ACCESS (man_file, R_OK);
2319 if (prompt && do_prompt (title)) {
2320 pipeline_free (format_cmd);
2321 pipeline_free (decomp);
2322 free (formatted_encoding);
2325 drop_effective_privs ();
2326 pipeline_connect (decomp, format_cmd, NULL);
2327 pipeline_pump (decomp, format_cmd, NULL);
2328 pipeline_wait (decomp);
2329 status = pipeline_wait (format_cmd);
2330 regain_effective_privs ();
2332 gripe_system (format_cmd, status);
2338 /* The caller should already have checked for any
2339 * FSSTND-style (same hierarchy) cat page that may be
2340 * present, and we don't expect to have to update the cat
2341 * page in that case. If by some chance we do have to update
2342 * it, then there's no harm trying; open_cat_stream() will
2343 * refuse gracefully if the file isn't writeable.
2346 /* In theory we might be able to get away with saving cats
2347 * for want_encoding, but it does change the roff device so
2348 * perhaps that's best avoided.
2351 #ifdef TROFF_IS_GROFF
2363 } else if (!cat_file) {
2367 } else if (format && save_cat) {
2371 status = is_changed (man_file, cat_file);
2372 format = (status == -2) || ((status & 1) == 1);
2374 /* don't save if we haven't a cat directory */
2375 cat_dir = xstrdup (cat_file);
2376 tmp = strrchr (cat_dir, '/');
2379 save_cat = is_directory (cat_dir) == 1;
2381 debug ("cat dir %s does not exist\n", cat_dir);
2385 if (format && (!format_cmd || !decomp)) {
2387 /* format_cmd is NULL iff decomp is NULL; no need to
2388 * free either of them.
2390 assert (!format_cmd);
2392 error (0, decomp_errno, _("can't open %s"), man_file);
2396 /* if we're trying to read stdin via '-l -' then man_file
2397 * will be "" which access() obviously barfs on, but all is
2398 * well because the format_cmd will have been created to
2399 * expect input via stdin. So we special-case this to avoid
2400 * the bogus access() check.
2402 if (format == 1 && *man_file == '\0')
2406 (format ? man_file : cat_file, R_OK);
2408 debug ("format: %d, save_cat: %d, found: %d\n",
2409 format, save_cat, found);
2412 pipeline_free (format_cmd);
2413 pipeline_free (decomp);
2417 if (print_where || print_where_cat) {
2419 if (print_where && man_file) {
2420 printf ("%s", man_file);
2423 if (print_where_cat && cat_file && !format) {
2426 printf ("%s", cat_file);
2431 } else if (catman) {
2435 _("\ncannot write to "
2436 "%s in catman mode"),
2439 display_catman (cat_file, decomp,
2441 formatted_encoding);
2443 } else if (format) {
2444 /* no cat or out of date */
2447 if (prompt && do_prompt (title)) {
2448 pipeline_free (format_cmd);
2449 pipeline_free (decomp);
2450 free (formatted_encoding);
2457 disp_cmd = make_display_command (formatted_encoding,
2463 assert (disp_cmd); /* not htmlout for now */
2464 format_display_and_save (decomp,
2468 formatted_encoding);
2470 #endif /* MAN_CATS */
2471 /* don't save cat */
2472 format_display (decomp, format_cmd, disp_cmd,
2475 pipeline_free (disp_cmd);
2478 /* display preformatted cat */
2480 pipeline *decomp_cat;
2482 if (prompt && do_prompt (title)) {
2483 pipeline_free (format_cmd);
2484 pipeline_free (decomp);
2488 decomp_cat = decompress_open (cat_file);
2490 error (0, errno, _("can't open %s"), cat_file);
2491 pipeline_free (format_cmd);
2492 pipeline_free (decomp);
2495 disp_cmd = make_display_command ("UTF-8", title);
2496 format_display (decomp_cat, NULL, disp_cmd, man_file);
2497 pipeline_free (disp_cmd);
2498 pipeline_free (decomp_cat);
2502 free (formatted_encoding);
2504 pipeline_free (format_cmd);
2505 pipeline_free (decomp);
2513 static void gripe_converting_name (const char *name) ATTRIBUTE_NORETURN;
2514 static void gripe_converting_name (const char *name)
2516 error (FATAL, 0, _("Can't convert %s to cat name"), name);
2517 abort (); /* error should have exited; help compilers prove noreturn */
2520 /* Convert the trailing part of 'name' to be a cat page path by altering its
2521 * extension appropriately. If fsstnd is set, also try converting the
2522 * containing directory name from "man1" to "cat1" etc., returning NULL if
2523 * that doesn't work.
2525 * fsstnd should only be set if name is the original path of a man page
2526 * found in a man hierarchy, not something like a symlink target or a file
2527 * named with 'man -l'. Otherwise, a symlink to "/home/manuel/foo.1.gz"
2528 * would be converted to "/home/catuel/foo.1.gz", which would be bad.
2530 static char *convert_name (const char *name, int fsstnd)
2532 char *to_name, *t1 = NULL;
2535 struct compression *comp;
2536 #endif /* COMP_SRC */
2540 comp = comp_info (name, 1);
2542 namestem = comp->stem;
2544 #endif /* COMP_SRC */
2545 namestem = xstrdup (name);
2548 /* TODO: BSD layout requires .0. */
2549 to_name = xasprintf ("%s.%s", namestem, COMPRESS_EXT);
2550 #else /* !COMP_CAT */
2551 to_name = xstrdup (namestem);
2552 #endif /* COMP_CAT */
2556 t1 = strrchr (to_name, '/');
2558 gripe_converting_name (name);
2561 t2 = strrchr (to_name, '/');
2563 gripe_converting_name (name);
2567 if (STRNEQ (t2, "man", 3)) {
2568 /* If the second-last component starts with "man",
2569 * replace "man" with "cat".
2575 debug ("couldn't convert %s to FSSTND cat file\n",
2581 debug ("converted %s to %s\n", name, to_name);
2586 static char *find_cat_file (const char *path, const char *original,
2587 const char *man_file)
2589 size_t path_len = strlen (path);
2590 char *cat_file, *cat_path;
2593 /* Try the FSSTND way first, namely a cat page in the same hierarchy
2594 * as the original path to the man page. We don't create these
2595 * unless no alternate cat hierarchy is available, but will use them
2596 * if they happen to exist already and have the same timestamp as
2597 * the corresponding man page. (In practice I'm betting that this
2598 * means we'll hardly ever use them at all except for user
2599 * hierarchies; but compatibility, eh?)
2601 cat_file = convert_name (original, 1);
2603 status = is_changed (original, cat_file);
2604 if (status != -2 && (!(status & 1)) == 1) {
2605 debug ("found valid FSSTND cat file %s\n", cat_file);
2611 /* Otherwise, find the cat page we actually want to use or create,
2612 * taking any alternate cat hierarchy into account. If the original
2613 * path and man_file differ (i.e. original was a symlink or .so
2614 * link), try the link target and then the source.
2616 if (!STREQ (man_file, original)) {
2617 global_manpath = is_global_mandir (man_file);
2618 cat_path = get_catpath
2619 (man_file, global_manpath ? SYSTEM_CAT : USER_CAT);
2622 cat_file = convert_name (cat_path, 0);
2624 } else if (STRNEQ (man_file, path, path_len) &&
2625 man_file[path_len] == '/')
2626 cat_file = convert_name (man_file, 1);
2631 char *cat_dir = xstrdup (cat_file);
2632 char *tmp = strrchr (cat_dir, '/');
2635 if (is_directory (cat_dir)) {
2636 debug ("will try cat file %s\n", cat_file);
2640 debug ("cat dir %s does not exist\n", cat_dir);
2643 debug ("no cat path for %s\n", man_file);
2646 global_manpath = is_global_mandir (original);
2647 cat_path = get_catpath
2648 (original, global_manpath ? SYSTEM_CAT : USER_CAT);
2651 cat_file = convert_name (cat_path, 0);
2654 cat_file = convert_name (original, 1);
2657 debug ("will try cat file %s\n", cat_file);
2659 debug ("no cat path for %s\n", original);
2664 static int get_ult_flags (char from_db, char id)
2668 else if (id == ULT_MAN)
2669 /* Checking .so links is expensive, as we have to open the
2670 * file. Therefore, if the database lists it as ULT_MAN,
2671 * that's good enough for us and we won't recheck that. This
2672 * does mean that if a page changes from ULT_MAN to SO_MAN
2673 * then you might get duplicates until mandb is next run,
2674 * but that isn't a big deal.
2676 return ult_flags & ~SO_LINK;
2681 /* Is this candidate substantially a duplicate of a previous one?
2682 * Returns non-zero if so, otherwise zero.
2684 static int duplicate_candidates (struct candidate *left,
2685 struct candidate *right)
2687 const char *slash1, *slash2;
2688 struct locale_bits bits1, bits2;
2691 if (left->ult && right->ult && STREQ (left->ult, right->ult))
2692 return 1; /* same ultimate source file */
2694 if (!STREQ (left->source->name, right->source->name) ||
2695 !STREQ (left->source->sec, right->source->sec) ||
2696 !STREQ (left->source->ext, right->source->ext))
2697 return 0; /* different name/section/extension */
2699 if (STREQ (left->path, right->path))
2700 return 1; /* same path */
2702 /* Figure out if we've had a sufficiently similar candidate for this
2705 slash1 = strrchr (left->path, '/');
2706 slash2 = strrchr (right->path, '/');
2707 if (!slash1 || !slash2 ||
2708 !STRNEQ (left->path, right->path,
2709 MAX (slash1 - left->path, slash2 - right->path)))
2710 return 0; /* different path base */
2712 unpack_locale_bits (++slash1, &bits1);
2713 unpack_locale_bits (++slash2, &bits2);
2715 if (!STREQ (bits1.language, bits2.language) ||
2716 !STREQ (bits1.territory, bits2.territory) ||
2717 !STREQ (bits1.modifier, bits2.modifier))
2718 ret = 0; /* different language/territory/modifier */
2720 /* Everything seems to be the same; we can find nothing to
2721 * choose between them.
2725 free_locale_bits (&bits1);
2726 free_locale_bits (&bits2);
2730 static int compare_candidates (const struct candidate *left,
2731 const struct candidate *right)
2733 const struct mandata *lsource = left->source, *rsource = right->source;
2734 int sec_left = 0, sec_right = 0;
2736 const char *slash1, *slash2;
2738 /* If one candidate matches the requested name exactly, sort it
2739 * first. This makes --ignore-case behave more sensibly.
2741 /* name is never NULL here, see add_candidate() */
2742 if (STREQ (lsource->name, left->req_name)) {
2743 if (!STREQ (rsource->name, right->req_name))
2746 if (STREQ (rsource->name, right->req_name))
2750 /* Compare pure sections first, then ids, then extensions.
2751 * Rationale: whatis refs get the same section and extension as
2752 * their source, but may be supplanted by a real page with a
2753 * slightly different extension, possibly in another hierarchy (!);
2754 * see Debian bug #204249 for the gory details.
2756 * Any extension spelt out in full in section_list effectively
2757 * becomes a pure section; this allows extensions to be selectively
2758 * moved out of order with respect to their parent sections.
2760 if (strcmp (lsource->ext, rsource->ext)) {
2763 /* If the user asked for an explicit section, sort exact
2767 if (STREQ (lsource->ext, section)) {
2768 if (!STREQ (rsource->ext, section))
2771 if (STREQ (rsource->ext, section))
2776 /* Find out whether lsource->ext is ahead of rsource->ext in
2779 for (sp = section_list; *sp; ++sp) {
2782 if (!sec_left && **sp == *(lsource->ext))
2783 sec_left = sp - section_list + 1;
2784 if (!sec_right && **sp == *(rsource->ext))
2785 sec_right = sp - section_list + 1;
2786 } else if (STREQ (*sp, lsource->ext)) {
2787 sec_left = sp - section_list + 1;
2788 } else if (STREQ (*sp, rsource->ext)) {
2789 sec_right = sp - section_list + 1;
2791 /* Keep looking for a more specific match */
2793 if (sec_left != sec_right)
2794 return sec_left - sec_right;
2796 cmp = strcmp (lsource->sec, rsource->sec);
2801 /* ULT_MAN comes first, etc. Consider SO_MAN equivalent to ULT_MAN. */
2802 cmp = compare_ids (lsource->id, rsource->id, 1);
2806 /* The order in section_list has already been compared above. For
2807 * everything not mentioned explicitly there, we just compare
2810 cmp = strcmp (lsource->ext, rsource->ext);
2814 /* Try comparing based on language. We used to prefer to display a
2815 * page in the user's preferred language than a page from a better
2816 * section, but that attracted objections, so now we prefer to get
2817 * the section right. See Debian bug #519547.
2819 slash1 = strrchr (left->path, '/');
2820 slash2 = strrchr (right->path, '/');
2821 if (slash1 && slash2) {
2822 char *locale_copy, *p;
2823 struct locale_bits bits1, bits2, lbits;
2824 const char *codeset1, *codeset2;
2826 unpack_locale_bits (++slash1, &bits1);
2827 unpack_locale_bits (++slash2, &bits2);
2829 /* We need the current locale as well. */
2830 locale_copy = xstrdup (internal_locale);
2831 p = strchr (locale_copy, ':');
2834 unpack_locale_bits (locale_copy, &lbits);
2837 #define COMPARE_LOCALE_ELEMENTS(elt) do { \
2838 /* For different elements, prefer one that matches the locale if
2842 if (STREQ (lbits.elt, bits1.elt)) { \
2843 if (!STREQ (lbits.elt, bits2.elt)) { \
2848 if (STREQ (lbits.elt, bits2.elt)) { \
2854 cmp = strcmp (bits1.territory, bits2.territory); \
2856 /* No help from locale; might as well sort lexically. */ \
2860 COMPARE_LOCALE_ELEMENTS (language);
2861 COMPARE_LOCALE_ELEMENTS (territory);
2862 COMPARE_LOCALE_ELEMENTS (modifier);
2864 #undef COMPARE_LOCALE_ELEMENTS
2866 /* Prefer UTF-8 if available. Otherwise, consider them
2869 codeset1 = get_canonical_charset_name (bits1.codeset);
2870 codeset2 = get_canonical_charset_name (bits2.codeset);
2871 if (STREQ (codeset1, "UTF-8")) {
2872 if (!STREQ (codeset2, "UTF-8")) {
2877 if (STREQ (codeset2, "UTF-8")) {
2884 free_locale_bits (&lbits);
2885 free_locale_bits (&bits1);
2886 free_locale_bits (&bits2);
2891 /* Explicitly stabilise the sort as a last resort, so that manpath
2892 * ordering (e.g. language-specific hierarchies) works.
2894 if (left->add_index < right->add_index)
2896 else if (left->add_index > right->add_index)
2904 static int compare_candidates_qsort (const void *l, const void *r)
2906 const struct candidate *left = *(const struct candidate **)l;
2907 const struct candidate *right = *(const struct candidate **)r;
2909 return compare_candidates (left, right);
2912 static void free_candidate (struct candidate *candidate)
2915 free (candidate->ult);
2919 /* Add an entry to the list of candidates. */
2920 static int add_candidate (struct candidate **head, char from_db, char cat,
2921 const char *req_name, const char *path,
2922 const char *ult, struct mandata *source)
2924 struct candidate *search, *prev, *insert, *candp;
2925 static int add_index = 0;
2931 if (*source->pointer != '-')
2932 name = source->pointer;
2933 else if (source->name)
2934 name = source->name;
2938 filename = make_filename (path, name, source, cat ? "cat" : "man");
2941 ult = ult_src (filename, path, NULL,
2942 get_ult_flags (from_db, source->id), NULL);
2946 debug ("candidate: %d %d %s %s %s %c %s %s %s\n",
2947 from_db, cat, req_name, path, ult,
2948 source->id, source->name ? source->name : "-",
2949 source->sec, source->ext);
2952 source->name = xstrdup (req_name);
2954 candp = XMALLOC (struct candidate);
2955 candp->req_name = req_name;
2956 candp->from_db = from_db;
2959 candp->ult = ult ? xstrdup (ult) : NULL;
2960 candp->source = source;
2961 candp->add_index = add_index++;
2964 /* insert will be NULL (insert at start) or a pointer to the element
2965 * after which this element should be inserted.
2970 /* This search produces quadratic-time behaviour, although in
2971 * practice it doesn't seem to be too bad at the moment since the
2972 * run-time is dominated by calls to ult_src. In future it might be
2973 * worth optimising this; the reason I haven't done this yet is that
2974 * it involves quite a bit of tedious bookkeeping. A practical
2975 * approach would be to keep two hashes, one that's just a set to
2976 * keep track of whether candp->ult has been seen already, and one
2977 * that keeps a list of candidates for each candp->name that could
2978 * then be quickly checked by brute force.
2981 int dupcand = duplicate_candidates (candp, search);
2983 debug ("search: %d %d %s %s %s %c %s %s %s "
2985 search->from_db, search->cat, search->req_name,
2986 search->path, search->ult, search->source->id,
2987 search->source->name ? search->source->name : "-",
2988 search->source->sec, search->source->ext, dupcand);
2990 /* Check for duplicates. */
2992 int cmp = compare_candidates (candp, search);
2995 debug ("other duplicate is at least as "
2997 free_candidate (candp);
3000 debug ("this duplicate is better; removing "
3003 prev->next = search->next;
3004 free_candidate (search);
3005 search = prev->next;
3007 *head = search->next;
3008 free_candidate (search);
3017 search = search->next;
3021 /* Insert the new candidate at the end of the list (having had to go
3022 * through them all looking for duplicates anyway); we'll sort it
3027 candp->next = insert ? insert->next : *head;
3029 insert->next = candp;
3036 /* Sort the entire list of candidates. */
3037 static void sort_candidates (struct candidate **candidates)
3039 struct candidate *cand, **allcands;
3040 size_t count = 0, i;
3042 for (cand = *candidates; cand; cand = cand->next)
3048 allcands = XNMALLOC (count, struct candidate *);
3050 for (cand = *candidates; cand; cand = cand->next) {
3052 allcands[i++] = cand;
3054 assert (i == count);
3056 qsort (allcands, count, sizeof *allcands, compare_candidates_qsort);
3058 *candidates = cand = allcands[0];
3059 for (i = 1; i < count; ++i) {
3060 cand->next = allcands[i];
3069 * See if the preformatted man page or the source exists in the given
3072 static int try_section (const char *path, const char *sec, const char *name,
3073 struct candidate **cand_head)
3076 char **names = NULL, **np;
3077 size_t names_len = 0;
3079 int lff_opts = (match_case ? LFF_MATCHCASE : 0) |
3080 (regex_opt ? LFF_REGEX : 0) |
3081 (wildcard ? LFF_WILDCARD : 0);
3083 debug ("trying section %s with globbing\n", sec);
3085 #ifndef NROFF_MISSING /* #ifdef NROFF */
3087 * Look for man page source files.
3090 names = look_for_file (path, sec, name, 0, lff_opts);
3094 * See if there's a preformatted page around that
3097 #endif /* NROFF_MISSING */
3102 if (!troff && !want_encoding && !recode) {
3103 names = look_for_file (path, sec, name, 1, lff_opts);
3108 for (np = names; np && *np; np++)
3110 order_files (path, names, names_len);
3112 for (np = names; np && *np; np++) {
3113 struct mandata *info = infoalloc ();
3114 char *info_buffer = filename_info (*np, info, name);
3119 free_mandata_struct (info);
3122 info->addr = info_buffer;
3124 /* What kind of page is this? Since it's a real file, it
3125 * must be either ULT_MAN or SO_MAN. ult_src() can tell us
3128 ult = ult_src (*np, path, NULL, ult_flags, NULL);
3130 /* already warned */
3131 debug ("try_section(): bad link %s\n", *np);
3134 free_mandata_struct (info);
3137 if (STREQ (ult, *np))
3142 f = add_candidate (cand_head, CANDIDATE_FILESYSTEM,
3143 cat, name, path, ult, info);
3145 /* Free info and info_buffer if they weren't added to the
3151 free_mandata_struct (info);
3153 /* Don't free info and info_buffer here. */
3159 static int display_filesystem (struct candidate *candp)
3161 char *filename = make_filename (candp->path, NULL, candp->source,
3162 candp->cat ? "cat" : "man");
3168 /* source->name is never NULL thanks to add_candidate() */
3169 title = xasprintf ("%s(%s)", candp->source->name, candp->source->ext);
3172 if (troff || want_encoding || recode)
3174 found = display (candp->path, NULL, filename, title, NULL);
3176 const char *man_file;
3179 man_file = ult_src (filename, candp->path, NULL, ult_flags,
3181 if (man_file == NULL)
3184 debug ("found ultimate source file %s\n", man_file);
3185 lang = lang_dir (man_file);
3187 cat_file = find_cat_file (candp->path, filename, man_file);
3188 found = display (candp->path, man_file, cat_file, title, NULL);
3200 #ifdef MAN_DB_UPDATES
3201 /* wrapper to dbdelete which deals with opening/closing the db */
3202 static void dbdelete_wrapper (const char *page, struct mandata *info)
3207 dbf = MYDBM_RWOPEN (database);
3209 if (dbdelete (dbf, page, info) == 1)
3210 debug ("%s(%s) not in db!\n", page, info->ext);
3215 #endif /* MAN_DB_UPDATES */
3217 /* This started out life as try_section, but a lot of that routine is
3218 redundant wrt the db cache. */
3219 static int display_database (struct candidate *candp)
3225 struct mandata *in = candp->source;
3227 debug ("trying a db located file.\n");
3230 /* if the pointer holds some data, this is a reference to the
3231 real page, use that instead. */
3232 if (*in->pointer != '-')
3237 name = candp->req_name;
3239 if (in->id == WHATIS_MAN || in->id == WHATIS_CAT)
3240 debug (_("%s: relying on whatis refs is deprecated\n"), name);
3242 title = xasprintf ("%s(%s)",
3243 in->name ? in->name : candp->req_name, in->ext);
3245 #ifndef NROFF_MISSING /* #ifdef NROFF */
3247 * Look for man page source files.
3250 if (in->id < STRAY_CAT) { /* There should be a src page */
3251 file = make_filename (candp->path, name, in, "man");
3253 const char *man_file;
3256 man_file = ult_src (file, candp->path, NULL,
3257 get_ult_flags (1, in->id), NULL);
3258 if (man_file == NULL) {
3260 return found; /* zero */
3263 debug ("found ultimate source file %s\n", man_file);
3264 lang = lang_dir (man_file);
3266 cat_file = find_cat_file (candp->path, file, man_file);
3267 found += display (candp->path, man_file, cat_file,
3273 } /* else {drop through to the bottom and return 0 anyway} */
3276 #endif /* NROFF_MISSING */
3278 if (in->id <= WHATIS_CAT) {
3279 /* The db says we have a stray cat or whatis ref */
3286 /* show this page but force an update later to make sure
3287 we haven't just added the new page */
3290 /* If explicitly asked for troff or a different encoding,
3291 * don't show a stray cat.
3293 if (troff || want_encoding || recode) {
3298 file = make_filename (candp->path, name, in, "cat");
3301 catpath = get_catpath (candp->path,
3302 global_manpath ? SYSTEM_CAT
3305 if (catpath && strcmp (catpath, candp->path) != 0) {
3306 file = make_filename (catpath, name,
3310 /* don't delete here,
3311 return==0 will do that */
3313 return found; /* zero */
3318 return found; /* zero */
3322 found += display (candp->path, NULL, file, title, in->filter);
3329 /* test for existence, if fail: call dbdelete_wrapper, else return amount */
3330 static int display_database_check (struct candidate *candp)
3332 int exists = display_database (candp);
3334 #ifdef MAN_DB_UPDATES
3335 if (!exists && !skip) {
3336 debug ("dbdelete_wrapper (%s, %p)\n",
3337 candp->req_name, candp->source);
3338 dbdelete_wrapper (candp->req_name, candp->source);
3340 #endif /* MAN_DB_UPDATES */
3345 static void db_hashtable_free (void *defn)
3347 free_mandata_struct (defn);
3350 #ifdef MAN_DB_UPDATES
3351 static int maybe_update_file (const char *manpath, const char *name,
3352 struct mandata *info)
3354 const char *real_name;
3357 struct timespec file_mtime;
3363 /* If the pointer holds some data, then we need to look at that
3364 * name in the filesystem instead.
3366 if (!STRNEQ (info->pointer, "-", 1))
3367 real_name = info->pointer;
3368 else if (info->name)
3369 real_name = info->name;
3373 file = make_filename (manpath, real_name, info, "man");
3376 if (lstat (file, &buf) != 0)
3378 file_mtime = get_stat_mtime (&buf);
3379 if (timespec_cmp (file_mtime, info->mtime) == 0)
3382 debug ("%s needs to be recached: %ld.%09ld %ld.%09ld\n",
3384 (long) info->mtime.tv_sec, (long) info->mtime.tv_nsec,
3385 (long) file_mtime.tv_sec, (long) file_mtime.tv_nsec);
3386 status = run_mandb (0, manpath, file);
3388 error (0, 0, _("mandb command failed with exit status %d"),
3394 #endif /* MAN_DB_UPDATES */
3396 /* Special return values from try_db(). */
3398 #define TRY_DATABASE_OPEN_FAILED -1
3400 #ifdef MAN_DB_CREATES
3401 #define TRY_DATABASE_CREATED -2
3402 #endif /* MAN_DB_CREATES */
3404 #ifdef MAN_DB_UPDATES
3405 #define TRY_DATABASE_UPDATED -3
3406 #endif /* MAN_DB_UPDATES */
3408 /* Look for a page in the database. If db not accessible, return -1,
3409 otherwise return number of pages found. */
3410 static int try_db (const char *manpath, const char *sec, const char *name,
3411 struct candidate **cand_head)
3413 struct mandata *loc, *data;
3416 #ifdef MAN_DB_UPDATES
3417 int found_stale = 0;
3418 #endif /* MAN_DB_UPDATES */
3420 /* find out where our db for this manpath should be */
3422 catpath = get_catpath (manpath, global_manpath ? SYSTEM_CAT : USER_CAT);
3425 database = mkdbname (catpath);
3428 database = mkdbname (manpath);
3431 db_hash = hashtable_create (&db_hashtable_free);
3433 /* Have we looked here already? */
3434 data = hashtable_lookup (db_hash, manpath, strlen (manpath));
3439 dbf = MYDBM_RDOPEN (database);
3440 if (dbf && dbver_rd (dbf)) {
3445 debug ("Succeeded in opening %s O_RDONLY\n", database);
3447 /* if section is set, only return those that match,
3448 otherwise NULL retrieves all available */
3449 if (regex_opt || wildcard)
3450 data = dblookup_pattern
3451 (dbf, name, section, match_case,
3452 regex_opt, !names_only);
3454 data = dblookup_all (dbf, name, section,
3456 hashtable_install (db_hash, manpath, strlen (manpath),
3460 #ifdef MAN_DB_CREATES
3461 } else if (!global_manpath) {
3463 debug ("Failed to open %s O_RDONLY\n", database);
3464 if (run_mandb (1, manpath, NULL)) {
3465 data = infoalloc ();
3468 hashtable_install (db_hash,
3469 manpath, strlen (manpath),
3471 return TRY_DATABASE_OPEN_FAILED;
3473 return TRY_DATABASE_CREATED;
3474 #endif /* MAN_DB_CREATES */
3476 debug ("Failed to open %s O_RDONLY\n", database);
3477 data = infoalloc ();
3478 data->next = (struct mandata *) NULL;
3480 hashtable_install (db_hash, manpath, strlen (manpath),
3482 return TRY_DATABASE_OPEN_FAILED;
3486 /* if we already know that there is nothing here, get on with it */
3490 /* We already tried (and failed) to open this db before */
3492 return TRY_DATABASE_OPEN_FAILED;
3494 #ifdef MAN_DB_UPDATES
3495 /* Check that all the entries found are up to date. If not, the
3496 * caller should try again.
3498 for (loc = data; loc; loc = loc->next)
3499 if (STREQ (sec, loc->sec) &&
3500 (!extension || STREQ (extension, loc->ext)
3501 || STREQ (extension, loc->ext + strlen (sec))))
3502 if (maybe_update_file (manpath, name, loc))
3506 hashtable_remove (db_hash, manpath, strlen (manpath));
3507 return TRY_DATABASE_UPDATED;
3509 #endif /* MAN_DB_UPDATES */
3511 /* cycle through the mandata structures (there's usually only
3512 1 or 2) and see what we have w.r.t. the current section */
3513 for (loc = data; loc; loc = loc->next)
3514 if (STREQ (sec, loc->sec) &&
3515 (!extension || STREQ (extension, loc->ext)
3516 || STREQ (extension, loc->ext + strlen (sec))))
3517 found += add_candidate (cand_head, CANDIDATE_DATABASE,
3518 0, name, manpath, NULL, loc);
3523 /* Try to locate the page under the specified manpath, in the desired section,
3524 * with the supplied name. Glob if necessary. Initially search the filesystem;
3525 * if that fails, try finding it via a db cache access. */
3526 static int locate_page (const char *manpath, const char *sec, const char *name,
3527 struct candidate **candidates)
3531 /* sort out whether we want to treat this hierarchy as
3532 global or user. Differences:
3534 global: if setuid, use privs; don't create db.
3535 user : if setuid, drop privs; allow db creation. */
3537 global_manpath = is_global_mandir (manpath);
3538 if (!global_manpath)
3539 drop_effective_privs ();
3541 debug ("searching in %s, section %s\n", manpath, sec);
3543 found = try_section (manpath, sec, name, candidates);
3545 if ((!found || findall) && !global_apropos) {
3546 db_ok = try_db (manpath, sec, name, candidates);
3548 #ifdef MAN_DB_CREATES
3549 if (db_ok == TRY_DATABASE_CREATED)
3550 /* we created a db in the last call */
3551 db_ok = try_db (manpath, sec, name, candidates);
3552 #endif /* MAN_DB_CREATES */
3554 #ifdef MAN_DB_UPDATES
3555 if (db_ok == TRY_DATABASE_UPDATED)
3556 /* We found some outdated entries and rebuilt the
3557 * database in the last call. If this keeps
3558 * happening, though, give up and punt to the
3561 db_ok = try_db (manpath, sec, name, candidates);
3562 #endif /* MAN_DB_UPDATES */
3564 if (db_ok > 0) /* we found/opened a db and found something */
3568 if (!global_manpath)
3569 regain_effective_privs ();
3574 static int display_pages (struct candidate *candidates)
3576 struct candidate *candp;
3579 for (candp = candidates; candp; candp = candp->next) {
3580 global_manpath = is_global_mandir (candp->path);
3581 if (!global_manpath)
3582 drop_effective_privs ();
3584 switch (candp->from_db) {
3585 case CANDIDATE_FILESYSTEM:
3586 found += display_filesystem (candp);
3588 case CANDIDATE_DATABASE:
3589 found += display_database_check (candp);
3593 _("internal error: candidate type %d "
3594 "out of range"), candp->from_db);
3597 if (!global_manpath)
3598 regain_effective_privs ();
3600 if (found && !findall)
3608 * Search for text in all manual pages.
3610 * This is not a real full-text search, but a brute-force on-demand search.
3611 * The idea, name, and approach originate in the 'man' package, added (I
3612 * believe) by Andries Brouwer, although the implementation is new for
3613 * man-db and much faster due to running in-process.
3615 * Conceptually, this really belongs in whatis.c, as part of apropos.
3616 * However, the implementation in 'man' offers pages for immediate display
3617 * on request rather than simply listing them, which is currently awkward to
3618 * do in apropos. If we ever add support to apropos/whatis for either
3619 * calling back to man or displaying pages directly, we should revisit this.
3621 static int grep (const char *file, const char *string, const regex_t *search)
3628 /* pipeline_start makes file open failures unconditionally fatal.
3629 * Here, we'd rather just ignore any such files.
3631 if (stat (file, &st) < 0)
3634 decomp = decompress_open (file);
3637 pipeline_start (decomp);
3638 while ((line = pipeline_readline (decomp)) != NULL) {
3640 if (regexec (search, line,
3641 0, (regmatch_t *) 0, 0) == 0) {
3647 strstr (line, string) :
3648 strcasestr (line, string)) {
3655 pipeline_free (decomp);
3659 static int do_global_apropos_section (const char *path, const char *sec,
3664 size_t names_len = 0;
3667 global_manpath = is_global_mandir (path);
3668 if (!global_manpath)
3669 drop_effective_privs ();
3671 debug ("searching in %s, section %s\n", path, sec);
3673 names = look_for_file (path, sec, "*", 0, LFF_WILDCARD);
3675 xregcomp (&search, name,
3676 REG_EXTENDED | REG_NOSUB |
3677 (match_case ? 0 : REG_ICASE));
3679 memset (&search, 0, sizeof search);
3681 for (np = names; np && *np; ++np)
3683 order_files (path, names, names_len);
3685 for (np = names; np && *np; ++np) {
3686 struct mandata *info;
3689 const char *man_file;
3690 char *cat_file = NULL;
3692 if (!grep (*np, name, &search))
3695 info = infoalloc ();
3696 info_buffer = filename_info (*np, info, NULL);
3699 info->addr = info_buffer;
3701 title = xasprintf ("%s(%s)", strchr (info_buffer, '\0') + 1,
3703 man_file = ult_src (*np, path, NULL, ult_flags, NULL);
3706 lang = lang_dir (man_file);
3707 cat_file = find_cat_file (path, *np, man_file);
3708 if (display (path, man_file, cat_file, title, NULL))
3716 free_mandata_struct (info);
3722 if (!global_manpath)
3723 regain_effective_privs ();
3728 static int do_global_apropos (const char *name, int *found)
3730 const char **my_section_list;
3735 my_section_list = XNMALLOC (2, const char *);
3736 my_section_list[0] = section;
3737 my_section_list[1] = NULL;
3739 my_section_list = section_list;
3741 for (sp = my_section_list; *sp; sp++)
3742 for (mp = manpathlist; *mp; mp++)
3743 *found += do_global_apropos_section (*mp, *sp, name);
3746 free (my_section_list);
3748 return *found ? OK : NOT_FOUND;
3751 /* Each of local_man_loop and man sometimes calls the other. */
3752 static int man (const char *name, int *found);
3754 /* man issued with `-l' option */
3755 static int local_man_loop (const char *argv)
3757 int exit_status = OK;
3758 int local_mf = local_man_file;
3760 drop_effective_privs ();
3762 if (strcmp (argv, "-") == 0)
3763 display (NULL, "", NULL, "(stdin)", NULL);
3767 /* Check that the file exists and isn't e.g. a directory */
3768 if (stat (argv, &st)) {
3769 error (0, errno, "%s", argv);
3773 if (S_ISDIR (st.st_mode)) {
3774 error (0, EISDIR, "%s", argv);
3778 if (S_ISCHR (st.st_mode) || S_ISBLK (st.st_mode)) {
3779 /* EINVAL is about the best I can do. */
3780 error (0, EINVAL, "%s", argv);
3784 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
3785 /* Perhaps an executable. If its directory is on
3786 * $PATH, then we want to look up the corresponding
3787 * manual page in the appropriate hierarchy rather
3788 * than displaying the executable.
3790 char *argv_dir = dir_name (argv);
3793 if (directory_on_path (argv_dir)) {
3794 char *argv_base = base_name (argv);
3795 char *new_manp, *nm;
3796 char **old_manpathlist, **mp;
3798 debug ("recalculating manpath for executable "
3799 "in %s\n", argv_dir);
3801 new_manp = get_manpath_from_path (argv_dir, 0);
3802 if (!new_manp || !*new_manp) {
3803 debug ("no useful manpath for "
3805 goto executable_out;
3807 nm = locale_manpath (new_manp);
3811 old_manpathlist = XNMALLOC (MAXDIRS, char *);
3812 memcpy (old_manpathlist, manpathlist,
3813 MAXDIRS * sizeof (*manpathlist));
3814 create_pathlist (new_manp, manpathlist);
3816 man (argv_base, &found);
3818 for (mp = manpathlist; *mp; ++mp)
3820 memcpy (manpathlist, old_manpathlist,
3821 MAXDIRS * sizeof (*manpathlist));
3822 free (old_manpathlist);
3833 if (exit_status == OK) {
3834 char *argv_base = base_name (argv);
3837 argv_abs = xstrdup (argv);
3839 argv_abs = xgetcwd ();
3841 argv_abs = appendstr (argv_abs, "/",
3844 argv_abs = xstrdup (argv);
3846 lang = lang_dir (argv_abs);
3848 if (!display (NULL, argv, NULL, argv_base, NULL)) {
3850 error (0, errno, "%s", argv);
3851 exit_status = NOT_FOUND;
3858 local_man_file = local_mf;
3859 regain_effective_privs ();
3864 * Splits a "name[.section]" into { "name", "section" }.
3865 * Section would be NULL if not present.
3866 * The caller is responsible for freeing *ret_name and *ret_section.
3868 static void split_page_name (const char *page_name,
3874 dot = strrchr (page_name, '.');
3876 if (dot && is_section (dot + 1)) {
3877 *ret_name = xstrndup (page_name, dot - page_name);
3878 *ret_section = xstrdup (dot + 1);
3880 *ret_name = xstrdup (page_name);
3881 *ret_section = NULL;
3885 static void locate_page_in_manpath (const char *page_section,
3886 const char *page_name,
3887 struct candidate **candidates,
3892 for (mp = manpathlist; *mp; mp++)
3893 *found += locate_page (*mp, page_section, page_name, candidates);
3897 * Search for manual pages.
3899 * If preformatted manual pages are supported, look for the formatted
3900 * file first, then the man page source file. If they both exist and
3901 * the man page source file is newer, or only the source file exists,
3902 * try to reformat it and write the results in the cat directory. If
3903 * it is not possible to write the cat file, simply format and display
3906 * If preformatted pages are not supported, or the troff option is
3907 * being used, only look for the man page source file.
3910 static int man (const char *name, int *found)
3912 char *page_name, *page_section;
3913 struct candidate *candidates = NULL, *cand, *candnext;
3918 if (strchr (name, '/')) {
3919 int status = local_man_loop (name);
3926 locate_page_in_manpath (section, name, &candidates, found);
3930 for (sp = section_list; *sp; sp++) {
3931 locate_page_in_manpath (*sp, name, &candidates, found);
3935 split_page_name (name, &page_name, &page_section);
3937 if (!*found && page_section)
3938 locate_page_in_manpath (page_section, page_name, &candidates,
3942 free (page_section);
3944 sort_candidates (&candidates);
3947 *found = display_pages (candidates);
3949 for (cand = candidates; cand; cand = candnext) {
3950 candnext = cand->next;
3951 free_candidate (cand);
3954 return *found ? OK : NOT_FOUND;
3958 static const char **get_section_list (void)
3961 const char **config_sections;
3962 const char **sections = NULL;
3965 /* Section list from configuration file, or STD_SECTIONS if it's
3968 config_sections = get_sections ();
3969 if (!*config_sections) {
3970 free (config_sections);
3971 config_sections = std_sections;
3974 if (colon_sep_section_list == NULL)
3975 colon_sep_section_list = getenv ("MANSECT");
3976 if (colon_sep_section_list == NULL || *colon_sep_section_list == '\0')
3977 return config_sections;
3979 /* Although this is documented as colon-separated, at least Solaris
3980 * man's -s option takes a comma-separated list, so we accept that
3981 * too for compatibility.
3983 for (sec = strtok (colon_sep_section_list, ":,"); sec;
3984 sec = strtok (NULL, ":,")) {
3985 sections = xnrealloc (sections, i + 2, sizeof *sections);
3986 sections[i++] = sec;
3994 return config_sections;
3999 * Returns the first token of a libpipeline/sh-style command. See SUSv4TC2:
4000 * 2.2 Shell Command Language: Quoting.
4002 * Free the returned value.
4005 * sh_lang_first_word ("echo 3") returns "echo"
4006 * sh_lang_first_word ("'e ho' 3") returns "e ho"
4007 * sh_lang_first_word ("e\\cho 3") returns "echo"
4008 * sh_lang_first_word ("e\\\ncho 3") returns "echo"
4009 * sh_lang_first_word ("\"echo t\" 3") returns "echo t"
4010 * sh_lang_first_word ("\"ech\\o t\" 3") returns "ech\\o t"
4011 * sh_lang_first_word ("\"ech\\\\o t\" 3") returns "ech\\o t"
4012 * sh_lang_first_word ("\"ech\\\no t\" 3") returns "echo t"
4013 * sh_lang_first_word ("\"ech\\$ t\" 3") returns "ech$ t"
4014 * sh_lang_first_word ("\"ech\\` t\" 3") returns "ech` t"
4015 * sh_lang_first_word ("e\"ch\"o 3") returns "echo"
4016 * sh_lang_first_word ("e'ch'o 3") returns "echo"
4018 static char *sh_lang_first_word (const char *cmd)
4021 char *ret = xmalloc (strlen (cmd) + 1);
4023 for (i = 0; cmd[i] != '\0'; i++) {
4024 if (cmd[i] == '\\') {
4025 /* Escape Character (Backslash) */
4031 } else if (cmd[i] == '\'') {
4034 while (cmd[i] != '\0' && cmd[i] != '\'')
4035 ret[o++] = cmd[i++];
4036 } else if (cmd[i] == '"') {
4039 while (cmd[i] != '\0' && cmd[i] != '"') {
4040 if (cmd[i] == '\\') {
4041 if (cmd[i + 1] == '$' ||
4042 cmd[i + 1] == '`' ||
4043 cmd[i + 1] == '"' ||
4045 ret[o++] = cmd[++i];
4046 else if (cmd[i + 1] == '\n')
4055 } else if (cmd[i] == '\t' || cmd[i] == ' ' || cmd[i] == '\n' ||
4067 int main (int argc, char *argv[])
4069 int argc_env, exit_status = OK;
4073 set_program_name (argv[0]);
4075 check_standard_fds ();
4078 pipeline_install_post_fork (pop_all_cleanups);
4079 sandbox = sandbox_init ();
4084 internal_locale = setlocale (LC_MESSAGES, NULL);
4085 /* Use LANGUAGE only when LC_MESSAGES locale category is
4086 * neither "C" nor "POSIX". */
4087 if (internal_locale && strcmp (internal_locale, "C") &&
4088 strcmp (internal_locale, "POSIX"))
4089 multiple_locale = getenv ("LANGUAGE");
4090 internal_locale = xstrdup (internal_locale ? internal_locale : "C");
4092 /* export argv, it might be needed when invoking the vendor supplied browser */
4093 #if defined _AIX || defined __sgi
4097 #ifdef TROFF_IS_GROFF
4098 /* used in --help, so initialise early */
4101 #endif /* TROFF_IS_GROFF */
4103 /* First of all, find out if $MANOPT is set. If so, put it in
4104 *argv[] format for argp to play with. */
4105 argv_env = manopt_to_env (&argc_env);
4107 if (argp_parse (&argp, argc_env, argv_env, ARGP_NO_ARGS, 0, 0))
4110 /* parse the actual program args */
4111 if (argp_parse (&argp, argc, argv, ARGP_NO_ARGS, &first_arg, 0))
4114 /* record who we are and drop effective privs for later use */
4117 read_config_file (local_man_file || user_config_file);
4119 /* if the user wants whatis or apropos, give it to them... */
4121 do_extern (argc, argv);
4123 get_term (); /* stores terminal settings */
4125 debug ("real user = %d; effective user = %d\n", ruid, euid);
4126 #endif /* MAN_OWNER */
4128 /* close this locale and reinitialise if a new locale was
4129 issued as an argument or in $MANOPT */
4131 free (internal_locale);
4132 internal_locale = setlocale (LC_ALL, locale);
4133 if (internal_locale)
4134 internal_locale = xstrdup (internal_locale);
4136 internal_locale = xstrdup (locale);
4138 debug ("main(): locale = %s, internal_locale = %s\n",
4139 locale, internal_locale);
4140 if (internal_locale) {
4141 setenv ("LANGUAGE", internal_locale, 1);
4143 multiple_locale = NULL;
4147 #ifdef TROFF_IS_GROFF
4150 #endif /* TROFF_IS_GROFF */
4153 pager = getenv ("MANPAGER");
4155 pager = getenv ("PAGER");
4157 pager = get_def_user ("pager", NULL);
4158 if (pager == NULL) {
4159 char *pager_program = sh_lang_first_word (PAGER);
4160 if (pathsearch_executable (pager_program))
4164 free (pager_program);
4167 pager = get_def_user ("cat", CAT);
4169 if (prompt_string == NULL)
4170 prompt_string = getenv ("MANLESS");
4172 if (prompt_string == NULL)
4174 prompt_string = LESS_PROMPT;
4177 " Manual page " MAN_PN
4178 " ?ltline %lt?L/%L.:byte %bB?s/%s..?e (END):"
4180 "(press h for help or q to quit)");
4183 /* Restore and save $LESS in $MAN_ORIG_LESS so that recursive uses
4184 * of man work as expected.
4186 less = getenv ("MAN_ORIG_LESS");
4188 less = getenv ("LESS");
4189 setenv ("MAN_ORIG_LESS", less ? less : "", 1);
4191 debug ("\nusing %s as pager\n", pager);
4193 if (first_arg == argc) {
4195 manp = get_manpath ("");
4196 printf ("%s\n", manp);
4199 free (internal_locale);
4200 gripe_no_name (NULL);
4204 section_list = get_section_list ();
4207 char *mp = get_manpath (alt_system_name);
4208 manp = locale_manpath (mp);
4211 free (get_manpath (NULL));
4213 debug ("manpath search path (with duplicates) = %s\n", manp);
4215 create_pathlist (manp, manpathlist);
4217 /* man issued with `-l' option */
4218 if (local_man_file) {
4219 while (first_arg < argc) {
4220 exit_status = local_man_loop (argv[first_arg]);
4223 free (internal_locale);
4227 /* finished manpath processing, regain privs */
4228 regain_effective_privs ();
4230 #ifdef MAN_DB_UPDATES
4231 /* If `-u', do it now. */
4233 int status = run_mandb (0, NULL, NULL);
4236 _("mandb command failed with exit status %d"),
4239 #endif /* MAN_DB_UPDATES */
4241 while (first_arg < argc) {
4244 static int maybe_section = 0;
4245 const char *nextarg = argv[first_arg++];
4248 * See if this argument is a valid section name. If not,
4249 * is_section returns NULL.
4252 tmp = is_section (nextarg);
4255 debug ("\nsection: %s\n", section);
4260 if (maybe_section) {
4261 if (first_arg < argc)
4262 /* e.g. 'man 3perl Shell' */
4263 nextarg = argv[first_arg++];
4265 /* e.g. 'man 9wm' */
4267 /* ... but leave maybe_section set so we can
4268 * tell later that this happened.
4272 /* this is where we actually start looking for the man page */
4275 status = do_global_apropos (nextarg, &found);
4277 int found_subpage = 0;
4278 if (subpages && first_arg < argc) {
4279 char *subname = xasprintf (
4280 "%s-%s", nextarg, argv[first_arg]);
4281 status = man (subname, &found);
4288 if (!found_subpage && subpages && first_arg < argc) {
4289 char *subname = xasprintf (
4290 "%s_%s", nextarg, argv[first_arg]);
4291 status = man (subname, &found);
4299 status = man (nextarg, &found);
4302 /* clean out the cache of database lookups for each man page */
4303 hashtable_free (db_hash);
4306 if (section && maybe_section) {
4307 if (status != OK && !catman) {
4308 /* Maybe the section wasn't a section after
4309 * all? e.g. 'man 9wm fvwm'.
4311 int found_subpage = 0;
4312 debug ("\nRetrying section %s as name\n",
4317 char *subname = xasprintf (
4318 "%s-%s", tmp, nextarg);
4319 status = man (subname, &found);
4327 status = man (tmp, &found);
4328 hashtable_free (db_hash);
4330 /* ... but don't gripe about it if it doesn't
4334 /* It was a name after all, so arrange
4335 * to try the next page again with a
4341 /* No go, it really was a section. */
4346 if (status != OK && !catman) {
4348 exit_status = status;
4349 if (exit_status == NOT_FOUND) {
4350 if (!section && maybe_section &&
4351 CTYPE (isdigit, nextarg[0]))
4352 gripe_no_name (nextarg);
4354 gripe_no_man (nextarg, section);
4358 debug ("\nFound %d man pages\n", found);
4360 printf ("%s", nextarg);
4362 printf ("(%s)", section);
4363 if (first_arg != argc)
4364 fputs (", ", stdout);
4366 fputs (".\n", stdout);
4372 chkr_garbage_detector ();
4374 hashtable_free (db_hash);
4377 drop_effective_privs ();
4380 free_pathlist (manpathlist);
4381 free (internal_locale);