1 /*
2 * manp.c: Manpath calculations
3 *
4 * Copyright (C) 1990, 1991 John W. Eaton.
5 * Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.)
6 * Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011,
7 * 2012 Colin Watson.
8 *
9 * This file is part of man-db.
10 *
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.
15 *
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.
20 *
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
24 *
25 * John W. Eaton
26 * jwe@che.utexas.edu
27 * Department of Chemical Engineering
28 * The University of Texas at Austin
29 * Austin, Texas 78712
30 *
31 * unpack_locale_bits is derived from _nl_explode_name in libintl:
32 * Copyright (C) 1995-1998, 2000-2001, 2003, 2005 Free Software Foundation,
33 * Inc.
34 * Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
35 * This was originally LGPL v2 or later, but I (Colin Watson) hereby
36 * exercise my option under section 3 of LGPL v2 to distribute it under the
37 * GPL v2 or later as above.
38 *
39 * Wed May 4 15:44:47 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk): changes
40 * to get_dirlist() and manpath().
41 *
42 * This whole code segment is unfriendly and could do with a complete
43 * overhaul.
44 */
45
46 #ifdef HAVE_CONFIG_H
47 # include "config.h"
48 #endif /* HAVE_CONFIG_H */
49
50 #include <stdbool.h>
51 #include <stdio.h>
52 #include <ctype.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <assert.h>
56 #include <errno.h>
57 #include <dirent.h>
58 #include <stdbool.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62
63 #include "attribute.h"
64 #include "canonicalize.h"
65 #include "error.h"
66 #include "gl_array_list.h"
67 #include "gl_linkedhash_list.h"
68 #include "gl_xlist.h"
69 #include "xalloc.h"
70 #include "xgetcwd.h"
71 #include "xstrndup.h"
72 #include "xvasprintf.h"
73
74 #include "gettext.h"
75 #define _(String) gettext (String)
76
77 #include "manconfig.h"
78
79 #include "appendstr.h"
80 #include "cleanup.h"
81 #include "debug.h"
82 #include "fatal.h"
83 #include "glcontainers.h"
84 #include "security.h"
85 #include "util.h"
86
87 #include "manp.h"
88 #include "globbing.h"
89
90 enum config_flag {
91 MANDATORY,
92 MANPATH_MAP,
93 MANDB_MAP,
94 MANDB_MAP_USER,
95 DEFINE,
96 DEFINE_USER,
97 SECTION,
98 SECTION_USER
99 };
100
101 struct config_item {
102 char *key;
103 char *cont;
104 enum config_flag flag;
105 };
106
107 static gl_list_t config;
108
109 char *user_config_file = NULL;
110 bool disable_cache;
111 int min_cat_width = 80, max_cat_width = 80, cat_width = 0;
112
113 static void add_man_subdirs (gl_list_t list, const char *p);
114 static char *fsstnd (const char *path);
115 static char *def_path (enum config_flag flag);
116 static void add_dir_to_list (gl_list_t list, const char *dir);
117 static void add_dir_to_path_list (gl_list_t list, const char *p);
118
119
120 static void config_item_free (const void *elt)
121 {
122 /* gl_list declares the argument as const, but there doesn't seem to
123 * be a good reason for this.
124 */
125 struct config_item *item = (struct config_item *) elt;
126 free (item->key);
127 free (item->cont);
128 free (item);
129 }
130
131 static void add_config (const char *key, const char *cont,
132 enum config_flag flag)
133 {
134 struct config_item *item = XMALLOC (struct config_item);
135 item->key = xstrdup (key);
136 item->cont = xstrdup (cont);
137 item->flag = flag;
138 gl_list_add_last (config, item);
139 }
140
141 static const char * ATTRIBUTE_PURE get_config (const char *key,
142 enum config_flag flag)
143 {
144 const struct config_item *item;
145 char *cont = NULL;
146
147 GL_LIST_FOREACH (config, item)
148 if (flag == item->flag && STREQ (key, item->key)) {
149 cont = item->cont;
150 break;
151 }
152
153 return cont;
154 }
155
156 /* Must not return DEFINEs set in ~/.manpath. This is used to fetch
157 * definitions used in raised-privilege code; if in doubt, be conservative!
158 *
159 * If not setuid, this is identical to get_def_user.
160 */
161 const char * ATTRIBUTE_PURE get_def (const char *thing, const char *def)
162 {
163 const char *config_def;
164
165 if (!running_setuid ())
166 return get_def_user (thing, def);
167
168 config_def = get_config (thing, DEFINE);
169 return config_def ? config_def : def;
170 }
171
172 const char * ATTRIBUTE_PURE get_def_user (const char *thing, const char *def)
173 {
174 const char *config_def = get_config (thing, DEFINE_USER);
175 if (!config_def)
176 config_def = get_config (thing, DEFINE);
177 return config_def ? config_def : def;
178 }
179
180 static void add_sections (char *sections, bool user)
181 {
182 char *section_list = xstrdup (sections);
183 char *sect;
184 bool first = true;
185
186 debug (" Added sections: ");
187 for (sect = strtok (section_list, " "); sect;
188 sect = strtok (NULL, " ")) {
189 add_config (sect, "", user ? SECTION_USER : SECTION);
190 if (!first)
191 debug (", ");
192 debug ("`%s'", sect);
193 first = false;
194 }
195 debug (".\n");
196 free (section_list);
197 }
198
199 gl_list_t get_sections (void)
200 {
201 const struct config_item *item;
202 int length_user = 0, length = 0;
203 gl_list_t sections;
204 enum config_flag flag;
205
206 GL_LIST_FOREACH (config, item) {
207 if (item->flag == SECTION_USER)
208 length_user++;
209 else if (item->flag == SECTION)
210 length++;
211 }
212 sections = new_string_list (GL_ARRAY_LIST, true);
213 if (length_user)
214 flag = SECTION_USER;
215 else
216 flag = SECTION;
217 GL_LIST_FOREACH (config, item)
218 if (item->flag == flag)
219 gl_list_add_last (sections, xstrdup (item->key));
220 return sections;
221 }
222
223 static void add_def (const char *thing, const char *config_def, bool user)
224 {
225 add_config (thing, config_def, user ? DEFINE_USER : DEFINE);
226
227 debug (" Defined `%s' as `%s'.\n", thing, config_def);
228 }
229
230 static void add_manpath_map (const char *path, const char *mandir)
231 {
232 if (!path || !mandir)
233 return;
234
235 add_config (path, mandir, MANPATH_MAP);
236
237 debug (" Path `%s' mapped to mandir `%s'.\n", path, mandir);
238 }
239
240 static void add_mandb_map (const char *mandir, const char *catdir, bool user)
241 {
242 char *tmpcatdir;
243
244 if (!mandir)
245 return;
246
247 if (STREQ (catdir, "FSSTND"))
248 tmpcatdir = fsstnd (mandir);
249 else
250 tmpcatdir = xstrdup (catdir);
251
252 if (!tmpcatdir)
253 return;
254
255 add_config (mandir, tmpcatdir, user ? MANDB_MAP_USER : MANDB_MAP);
256
257 debug (" %s mandir `%s', catdir `%s'.\n",
258 user ? "User" : "Global", mandir, tmpcatdir);
259
260 free (tmpcatdir);
261 }
262
263 static void add_mandatory (const char *mandir)
264 {
265 if (!mandir)
266 return;
267
268 add_config (mandir, "", MANDATORY);
269
270 debug (" Mandatory mandir `%s'.\n", mandir);
271 }
272
273 /* accept (NULL or oldpath) and new path component. return new path */
274 static char *pathappend (char *oldpath, const char *appendage)
275 {
276 assert ((!oldpath || *oldpath) && appendage);
277 /* Remove duplicates */
278 if (oldpath) {
279 char *oldpathtok = xstrdup (oldpath), *tok;
280 char *app_dedup = xstrdup (appendage);
281 char *oldpathtok_ptr = oldpathtok;
282 for (tok = strsep (&oldpathtok_ptr, ":"); tok;
283 tok = strsep (&oldpathtok_ptr, ":")) {
284 char *search;
285 if (!*tok) /* ignore empty fields */
286 continue;
287 search = strstr (app_dedup, tok);
288 while (search) {
289 char *terminator = search + strlen (tok);
290 if (search > app_dedup && search[-1] != ':')
291 /* Ignore suffix matches. */
292 ;
293 else if (!*terminator) {
294 /* End of the string, so chop here. */
295 *search = 0;
296 while (search > app_dedup &&
297 *--search == ':')
298 *search = 0;
299 break;
300 } else if (*terminator == ':') {
301 char *newapp;
302 *search = 0;
303 newapp = xasprintf ("%s%s", app_dedup,
304 terminator + 1);
305 assert (newapp);
306 free (app_dedup);
307 app_dedup = newapp;
308 }
309 search = strstr (terminator, tok);
310 }
311 }
312 free (oldpathtok);
313 if (!STREQ (appendage, app_dedup))
314 debug ("%s:%s reduced to %s%s%s\n",
315 oldpath, appendage,
316 oldpath, *app_dedup ? ":" : "", app_dedup);
317 if (*app_dedup)
318 oldpath = appendstr (oldpath, ":", app_dedup,
319 (void *) 0);
320 free (app_dedup);
321 return oldpath;
322 } else
323 return xstrdup (appendage);
324 }
325
326 static void gripe_reading_mp_config (const char *file)
327 {
328 error (FAIL, 0,
329 _("can't make sense of the manpath configuration file %s"),
330 file);
331 }
332
333 static void gripe_stat_file (const char *file)
334 {
335 debug_error (_("warning: %s"), file);
336 }
337
338 static void gripe_not_directory (const char *dir)
339 {
340 if (!quiet)
341 error (0, 0, _("warning: %s isn't a directory"), dir);
342 }
343
344 /* accept a manpath list, separated with ':', return the associated
345 catpath list */
346 char *cat_manpath (char *manp)
347 {
348 char *catp = NULL;
349 const char *path, *catdir;
350
351 for (path = strsep (&manp, ":"); path; path = strsep (&manp, ":")) {
352 catdir = get_config (path, MANDB_MAP_USER);
353 if (!catdir)
354 catdir = get_config (path, MANDB_MAP);
355 catp = catdir ? pathappend (catp, catdir)
356 : pathappend (catp, path);
357 }
358
359 return catp;
360 }
361
362 /* Unpack a glibc-style locale into its component parts.
363 *
364 * This function was inspired by _nl_explode_name in libintl; I've rewritten
365 * it here with extensive modifications in order not to require libintl or
366 * glibc internals, because this API is more convenient for man-db, and to
367 * be consistent with surrounding style. I also dropped the normalised
368 * codeset handling, which we don't need here.
369 */
370 void unpack_locale_bits (const char *locale, struct locale_bits *bits)
371 {
372 const char *p, *start;
373
374 bits->language = NULL;
375 bits->territory = NULL;
376 bits->codeset = NULL;
377 bits->modifier = NULL;
378
379 /* Now we determine the single parts of the locale name. First look
380 * for the language. Termination symbols are '_', '.', and '@'.
381 */
382 p = locale;
383 while (*p && *p != '_' && *p != '.' && *p != '@')
384 ++p;
385 if (p == locale) {
386 /* This does not make sense: language has to be specified.
387 * Use this entry as it is without exploding. Perhaps it is
388 * an alias.
389 */
390 bits->language = xstrdup (locale);
391 goto out;
392 }
393 bits->language = xstrndup (locale, p - locale);
394
395 if (*p == '_') {
396 /* Next is the territory. */
397 start = ++p;
398 while (*p && *p != '.' && *p != '@')
399 ++p;
400 bits->territory = xstrndup (start, p - start);
401 }
402
403 if (*p == '.') {
404 /* Next is the codeset. */
405 start = ++p;
406 while (*p && *p != '@')
407 ++p;
408 bits->codeset = xstrndup (start, p - start);
409 }
410
411 if (*p == '@')
412 /* Next is the modifier. */
413 bits->modifier = xstrdup (++p);
414
415 out:
416 if (!bits->territory)
417 bits->territory = xstrdup ("");
418 if (!bits->codeset)
419 bits->codeset = xstrdup ("");
420 if (!bits->modifier)
421 bits->modifier = xstrdup ("");
422 }
423
424 /* Free the contents of a locale_bits structure populated by
425 * unpack_locale_bits. Does not free the pointer argument.
426 */
427 void free_locale_bits (struct locale_bits *bits)
428 {
429 free (bits->language);
430 free (bits->territory);
431 free (bits->codeset);
432 free (bits->modifier);
433 }
434
435
436 static char *get_nls_manpath (const char *manpathlist, const char *locale)
437 {
438 struct locale_bits lbits;
439 char *manpath = NULL;
440 char *manpathlist_copy, *path, *manpathlist_ptr;
441
442 unpack_locale_bits (locale, &lbits);
443 if (STREQ (lbits.language, "C") || STREQ (lbits.language, "POSIX")) {
444 free_locale_bits (&lbits);
445 return xstrdup (manpathlist);
446 }
447
448 manpathlist_copy = xstrdup (manpathlist);
449 manpathlist_ptr = manpathlist_copy;
450 for (path = strsep (&manpathlist_ptr, ":"); path;
451 path = strsep (&manpathlist_ptr, ":")) {
452 DIR *mandir = opendir (path);
453 struct dirent *mandirent;
454
455 if (!mandir)
456 continue;
457
458 while ((mandirent = readdir (mandir)) != NULL) {
459 const char *name;
460 struct locale_bits mbits;
461 char *fullpath;
462
463 name = mandirent->d_name;
464 if (STREQ (name, ".") || STREQ (name, ".."))
465 continue;
466 if (STRNEQ (name, "man", 3))
467 continue;
468 fullpath = xasprintf ("%s/%s", path, name);
469 if (is_directory (fullpath) != 1) {
470 free (fullpath);
471 continue;
472 }
473
474 unpack_locale_bits (name, &mbits);
475 if (STREQ (lbits.language, mbits.language) &&
476 (!*mbits.territory ||
477 STREQ (lbits.territory, mbits.territory)) &&
478 (!*mbits.modifier ||
479 STREQ (lbits.modifier, mbits.modifier)))
480 manpath = pathappend (manpath, fullpath);
481 free_locale_bits (&mbits);
482 free (fullpath);
483 }
484
485 if (STREQ (lbits.language, "en"))
486 /* For English, we look in the subdirectories as
487 * above just in case there's something like
488 * en_GB.UTF-8, but it's more probable that English
489 * manual pages reside at the top level.
490 */
491 manpath = pathappend (manpath, path);
492
493 closedir (mandir);
494 }
495 free (manpathlist_copy);
496
497 free_locale_bits (&lbits);
498 return manpath;
499 }
500
501 char *add_nls_manpaths (const char *manpathlist, const char *locales)
502 {
503 char *manpath = NULL;
504 char *locales_copy, *tok, *locales_ptr;
505 char *locale_manpath;
506
507 debug ("add_nls_manpaths(): processing %s\n", manpathlist);
508
509 if (locales == NULL || *locales == '\0')
510 return xstrdup (manpathlist);
511
512 /* For each locale, we iterate over the manpath and find appropriate
513 * locale directories for each item. We then concatenate the results
514 * for all locales. In other words, LANGUAGE=fr:de and
515 * manpath=/usr/share/man:/usr/local/share/man could result in
516 * something like this list:
517 *
518 * /usr/share/man/fr
519 * /usr/local/share/man/fr
520 * /usr/share/man/de
521 * /usr/local/share/man/de
522 * /usr/share/man
523 * /usr/local/share/man
524 *
525 * This assumes that it's more important to have documentation in
526 * the preferred language than to have documentation for the correct
527 * object (in the case where there are different versions of a
528 * program in different hierarchies, for example). It is not
529 * entirely obvious that this is the right assumption, but on the
530 * other hand the other choice is not entirely obvious either. We
531 * tie-break on "we've always done it this way", and people can use
532 * 'man -a' or whatever in the occasional case where we get it
533 * wrong.
534 *
535 * We go to no special effort to de-duplicate directories here.
536 * create_pathlist will sort it out later; note that it preserves
537 * order in that it keeps the first of any duplicate set in its
538 * original position.
539 */
540
541 locales_copy = xstrdup (locales);
542 locales_ptr = locales_copy;
543 for (tok = strsep (&locales_ptr, ":"); tok;
544 tok = strsep (&locales_ptr, ":")) {
545 if (!*tok) /* ignore empty fields */
546 continue;
547 debug ("checking for locale %s\n", tok);
548
549 locale_manpath = get_nls_manpath (manpathlist, tok);
550 if (locale_manpath) {
551 if (manpath)
552 manpath = appendstr (manpath, ":",
553 locale_manpath,
554 (void *) 0);
555 else
556 manpath = xstrdup (locale_manpath);
557 free (locale_manpath);
558 }
559 }
560 free (locales_copy);
561
562 /* Always try untranslated pages as a last resort. */
563 locale_manpath = get_nls_manpath (manpathlist, "C");
564 if (locale_manpath) {
565 if (manpath)
566 manpath = appendstr (manpath, ":",
567 locale_manpath, (void *) 0);
568 else
569 manpath = xstrdup (locale_manpath);
570 free (locale_manpath);
571 }
572
573 return manpath;
574 }
575
576 static char *add_system_manpath (const char *systems, const char *manpathlist)
577 {
578 char *one_system;
579 char *manpath = NULL;
580 char *tmpsystems;
581
582 if (!systems)
583 systems = getenv ("SYSTEM");
584
585 if (!systems || !*systems)
586 return xstrdup (manpathlist);
587
588 /* Avoid breaking the environment. */
589 tmpsystems = xstrdup (systems);
590
591 /* For each systems component */
592
593 for (one_system = strtok (tmpsystems, ",:"); one_system;
594 one_system = strtok (NULL, ",:")) {
595
596 /* For each manpathlist component */
597
598 if (!STREQ (one_system, "man")) {
599 const char *next, *path;
600 char *newdir = NULL;
601 for (path = manpathlist; path; path = next) {
602 int status;
603 char *element;
604
605 next = strchr (path, ':');
606 if (next) {
607 element = xstrndup (path, next - path);
608 ++next;
609 } else
610 element = xstrdup (path);
611 newdir = appendstr (newdir, element, "/",
612 one_system, (void *) 0);
613 free (element);
614
615 status = is_directory (newdir);
616
617 if (status == 0)
618 gripe_not_directory (newdir);
619 else if (status == 1) {
620 debug ("adding %s to manpathlist\n",
621 newdir);
622 manpath = pathappend (manpath, newdir);
623 } else
624 debug_error ("can't stat %s", newdir);
625 /* reset newdir */
626 *newdir = '\0';
627 }
628 free (newdir);
629 } else
630 manpath = pathappend (manpath, manpathlist);
631 }
632 free (tmpsystems);
633
634 /*
635 * Thu, 21 Nov 1996 22:24:19 +0200 fpolacco@debian.org
636 * bug#5534 (man fails if env var SYSTEM is defined)
637 * with error [man: internal manpath equates to NULL]
638 * the reason: is_directory (newdir); returns -1
639 */
640 if (!manpath) {
641 debug ("add_system_manpath(): "
642 "internal manpath equates to NULL\n");
643 return xstrdup (manpathlist);
644 }
645 return manpath;
646 }
647
648 /*
649 * Always add system and locale directories to pathlist.
650 * If the environment variable MANPATH is set, return it.
651 * If the environment variable PATH is set and has a nonzero length,
652 * try to determine the corresponding manpath, otherwise, return the
653 * default manpath.
654 *
655 * The man_db.config file is used to map system wide /bin directories
656 * to top level man page directories.
657 *
658 * For directories which are in the user's path but not in the
659 * man_db.config file, see if there is a subdirectory `man' or `MAN'.
660 * If so, add that directory to the path. Example: user has
661 * $HOME/bin in his path and the directory $HOME/bin/man exists -- the
662 * directory $HOME/bin/man will be added to the manpath.
663 */
664 static char *guess_manpath (const char *systems)
665 {
666 const char *path = getenv ("PATH");
667 char *manpathlist, *manpath;
668
669 if (path == NULL || getenv ("MAN_TEST_DISABLE_PATH")) {
670 /* Things aren't going to work well, but hey... */
671 if (path == NULL && !quiet)
672 error (0, 0, _("warning: $PATH not set"));
673
674 manpathlist = def_path (MANDATORY);
675 } else {
676 if (strlen (path) == 0) {
677 /* Things aren't going to work well here either... */
678 if (!quiet)
679 error (0, 0, _("warning: empty $PATH"));
680
681 return add_system_manpath (systems,
682 def_path (MANDATORY));
683 }
684
685 manpathlist = get_manpath_from_path (path, true);
686 }
687 manpath = add_system_manpath (systems, manpathlist);
688 free (manpathlist);
689 return manpath;
690 }
691
692 char *get_manpath (const char *systems)
693 {
694 char *manpathlist;
695
696 /* need to read config file even if MANPATH set, for mandb(8) */
697 read_config_file (false);
698
699 manpathlist = getenv ("MANPATH");
700 if (manpathlist && *manpathlist) {
701 char *system1, *system2, *guessed;
702 char *pos;
703 /* This must be it. */
704 if (manpathlist[0] == ':') {
705 if (!quiet)
706 error (0, 0,
707 _("warning: $MANPATH set, "
708 "prepending %s"),
709 CONFIG_FILE);
710 system1 = add_system_manpath (systems, manpathlist);
711 guessed = guess_manpath (systems);
712 manpathlist = xasprintf ("%s%s", guessed, system1);
713 free (guessed);
714 free (system1);
715 } else if (manpathlist[strlen (manpathlist) - 1] == ':') {
716 if (!quiet)
717 error (0, 0,
718 _("warning: $MANPATH set, "
719 "appending %s"),
720 CONFIG_FILE);
721 system1 = add_system_manpath (systems, manpathlist);
722 guessed = guess_manpath (systems);
723 manpathlist = xasprintf ("%s%s", system1, guessed);
724 free (guessed);
725 free (system1);
726 } else if ((pos = strstr (manpathlist,"::"))) {
727 *(pos++) = '\0';
728 if (!quiet)
729 error (0, 0,
730 _("warning: $MANPATH set, "
731 "inserting %s"),
732 CONFIG_FILE);
733 system1 = add_system_manpath (systems, manpathlist);
734 guessed = guess_manpath (systems);
735 system2 = add_system_manpath (systems, pos);
736 manpathlist = xasprintf ("%s:%s%s", system1, guessed,
737 system2);
738 free (system2);
739 free (guessed);
740 free (system1);
741 } else {
742 if (!quiet)
743 error (0, 0,
744 _("warning: $MANPATH set, ignoring %s"),
745 CONFIG_FILE);
746 manpathlist = add_system_manpath (systems,
747 manpathlist);
748 }
749 } else
750 manpathlist = guess_manpath (systems);
751
752 return manpathlist;
753 }
754
755 /* Parse the manpath.config file, extracting appropriate information. */
756 static void add_to_dirlist (FILE *config_file, bool user)
757 {
758 char *bp;
759 char *buf = NULL;
760 size_t n = 0;
761 char key[512], cont[512];
762 int val;
763 int c;
764
765 while (getline (&buf, &n, config_file) >= 0) {
766 bp = buf;
767
768 while (CTYPE (isspace, *bp))
769 bp++;
770
771 /* TODO: would like a (limited) replacement for sscanf()
772 * here that allocates its own memory. At that point check
773 * everything that sprintf()s manpath et al!
774 */
775 if (*bp == '#' || *bp == '\0')
776 goto next;
777 else if (strncmp (bp, "NOCACHE", 7) == 0)
778 disable_cache = true;
779 else if (strncmp (bp, "NO", 2) == 0)
780 goto next; /* match any word starting with NO */
781 else if (sscanf (bp, "MANBIN %*s") == 1)
782 goto next;
783 else if (sscanf (bp, "MANDATORY_MANPATH %511s", key) == 1)
784 add_mandatory (key);
785 else if (sscanf (bp, "MANPATH_MAP %511s %511s",
786 key, cont) == 2)
787 add_manpath_map (key, cont);
788 else if ((c = sscanf (bp, "MANDB_MAP %511s %511s",
789 key, cont)) > 0)
790 add_mandb_map (key, c == 2 ? cont : key, user);
791 else if ((c = sscanf (bp, "DEFINE %511s %511[^\n]",
792 key, cont)) > 0)
793 add_def (key, c == 2 ? cont : "", user);
794 else if (sscanf (bp, "SECTION %511[^\n]", cont) == 1)
795 add_sections (cont, user);
796 else if (sscanf (bp, "SECTIONS %511[^\n]", cont) == 1)
797 /* Since I keep getting it wrong ... */
798 add_sections (cont, user);
799 else if (sscanf (bp, "MINCATWIDTH %d", &val) == 1)
800 min_cat_width = val;
801 else if (sscanf (bp, "MAXCATWIDTH %d", &val) == 1)
802 max_cat_width = val;
803 else if (sscanf (bp, "CATWIDTH %d", &val) == 1)
804 cat_width = val;
805 else {
806 error (0, 0, _("can't parse directory list `%s'"), bp);
807 gripe_reading_mp_config (CONFIG_FILE);
808 }
809
810 next:
811 free (buf);
812 buf = NULL;
813 }
814
815 free (buf);
816 }
817
818 static void free_config_file (void *unused MAYBE_UNUSED)
819 {
820 gl_list_free (config);
821 }
822
823 void read_config_file (bool optional)
824 {
825 static bool done = false;
826 char *dotmanpath = NULL;
827 FILE *config_file;
828
829 if (done)
830 return;
831
832 config = gl_list_create_empty (GL_ARRAY_LIST, NULL, NULL,
833 config_item_free, true);
834 push_cleanup (free_config_file, NULL, 0);
835
836 if (user_config_file)
837 dotmanpath = xstrdup (user_config_file);
838 else {
839 char *home = getenv ("HOME");
840 if (home)
841 dotmanpath = xasprintf ("%s/.manpath", home);
842 }
843 if (dotmanpath) {
844 config_file = fopen (dotmanpath, "r");
845 if (config_file != NULL) {
846 debug ("From the config file %s:\n", dotmanpath);
847 add_to_dirlist (config_file, true);
848 fclose (config_file);
849 }
850 free (dotmanpath);
851 }
852
853 if (getenv ("MAN_TEST_DISABLE_SYSTEM_CONFIG") == NULL) {
854 config_file = fopen (CONFIG_FILE, "r");
855 if (config_file == NULL) {
856 if (optional)
857 debug ("can't open %s; continuing anyway\n",
858 CONFIG_FILE);
859 else
860 error (FAIL, 0,
861 _("can't open the manpath "
862 "configuration file %s"),
863 CONFIG_FILE);
864 } else {
865 debug ("From the config file %s:\n", CONFIG_FILE);
866
867 add_to_dirlist (config_file, false);
868 fclose (config_file);
869 }
870 }
871
872 done = true;
873 }
874
875
876 /*
877 * Construct the default manpath. This picks up mandatory manpaths
878 * only.
879 */
880 static char *def_path (enum config_flag flag)
881 {
882 char *manpath = NULL;
883 const struct config_item *item;
884
885 GL_LIST_FOREACH (config, item)
886 if (item->flag == flag) {
887 gl_list_t expanded_dirs;
888 const char *expanded_dir;
889
890 expanded_dirs = expand_path (item->key);
891 GL_LIST_FOREACH (expanded_dirs, expanded_dir) {
892 int status = is_directory (expanded_dir);
893
894 if (status < 0)
895 gripe_stat_file (expanded_dir);
896 else if (status == 0 && !quiet)
897 error (0, 0,
898 _("warning: mandatory "
899 "directory %s doesn't exist"),
900 expanded_dir);
901 else if (status == 1)
902 manpath = pathappend (manpath,
903 expanded_dir);
904 }
905 gl_list_free (expanded_dirs);
906 }
907
908 /* If we have complete config file failure... */
909 if (!manpath)
910 return xstrdup ("/usr/man");
911
912 return manpath;
913 }
914
915 /*
916 * For each directory in the user's path, see if it is one of the
917 * directories listed in the man_db.config file. If so, and it is
918 * not already in the manpath, add it. If the directory is not listed
919 * in the man_db.config file, see if there is a subdirectory `../man' or
920 * `man', or, for FHS-compliance, `../share/man' or `share/man'. If so,
921 * and it is not already in the manpath, add it.
922 * Example: user has $HOME/bin in his path and the directory
923 * $HOME/man exists -- the directory $HOME/man will be added
924 * to the manpath.
925 */
926 char *get_manpath_from_path (const char *path, bool mandatory)
927 {
928 gl_list_t tmplist;
929 const struct config_item *config_item;
930 int len;
931 char *tmppath;
932 char *p;
933 char *end;
934 char *manpathlist;
935 char *item;
936
937 tmplist = new_string_list (GL_LINKEDHASH_LIST, false);
938 tmppath = xstrdup (path);
939
940 for (end = p = tmppath; end; p = end + 1) {
941 bool manpath_map_found = false;
942
943 end = strchr (p, ':');
944 if (end)
945 *end = '\0';
946
947 /* don't do this for current dir ("." or empty entry in PATH) */
948 if (*p == '\0' || strcmp (p, ".") == 0)
949 continue;
950
951 debug ("path directory %s ", p);
952
953 /* If the directory we're working on has MANPATH_MAP entries
954 * in the config file, add them to the list.
955 */
956 GL_LIST_FOREACH (config, config_item) {
957 if (MANPATH_MAP != config_item->flag ||
958 !STREQ (p, config_item->key))
959 continue;
960 if (!manpath_map_found)
961 debug ("is in the config file\n");
962 manpath_map_found = true;
963 add_dir_to_list (tmplist, config_item->cont);
964 }
965
966 /* The directory we're working on isn't in the config file.
967 See if it has ../man, man, ../share/man, or share/man
968 subdirectories. If so, and they haven't been added to
969 the list, do. */
970
971 if (!manpath_map_found) {
972 debug ("is not in the config file\n");
973 add_man_subdirs (tmplist, p);
974 }
975 }
976
977 free (tmppath);
978
979 if (mandatory) {
980 debug ("adding mandatory man directories\n");
981
982 GL_LIST_FOREACH (config, config_item) {
983 if (config_item->flag == MANDATORY)
984 add_dir_to_list (tmplist, config_item->key);
985 }
986 }
987
988 len = 0;
989 GL_LIST_FOREACH (tmplist, item)
990 len += strlen (item) + 1;
991
992 if (!len)
993 /* No path elements in configuration file or with
994 * appropriate subdirectories.
995 */
996 return xstrdup ("");
997
998 manpathlist = xmalloc (len);
999 *manpathlist = '\0';
1000
1001 p = manpathlist;
1002 GL_LIST_FOREACH (tmplist, item) {
1003 len = strlen (item);
1004 memcpy (p, item, len);
1005 p += len;
1006 *p++ = ':';
1007 }
1008
1009 p[-1] = '\0';
1010
1011 gl_list_free (tmplist);
1012
1013 return manpathlist;
1014 }
1015
1016 /* Add a directory to the manpath list if it isn't already there. */
1017 static void add_expanded_dir_to_list (gl_list_t list, const char *dir)
1018 {
1019 int status;
1020
1021 if (gl_list_search (list, dir))
1022 return;
1023
1024 /* Not found -- add it. */
1025
1026 status = is_directory (dir);
1027
1028 if (status < 0)
1029 gripe_stat_file (dir);
1030 else if (status == 0)
1031 gripe_not_directory (dir);
1032 else if (status == 1) {
1033 debug (" adding %s to manpath\n", dir);
1034 gl_list_add_last (list, xstrdup (dir));
1035 }
1036 }
1037
1038 /*
1039 * Add a directory to the manpath list if it isn't already there, expanding
1040 * wildcards.
1041 */
1042 static void add_dir_to_list (gl_list_t list, const char *dir)
1043 {
1044 gl_list_t expanded_dirs;
1045 const char *expanded_dir;
1046
1047 expanded_dirs = expand_path (dir);
1048 GL_LIST_FOREACH (expanded_dirs, expanded_dir)
1049 add_expanded_dir_to_list (list, expanded_dir);
1050 gl_list_free (expanded_dirs);
1051 }
1052
1053 /* path does not exist in config file: check to see if path/../man,
1054 path/man, path/../share/man, or path/share/man exist, and add them to the
1055 list if they do. */
1056 static void add_man_subdirs (gl_list_t list, const char *path)
1057 {
1058 char *newpath;
1059 char *trimmed_path = xstrdup (path);
1060
1061 /* don't assume anything about path, especially that it ends in
1062 "bin" or even has a '/' in it! */
1063
1064 char *subdir = strrchr (trimmed_path, '/');
1065
1066 /* Trailing slash or root directory. Remove the trailing slash and
1067 try again. If root directory, subdir will be null, so we don't
1068 cause a segfault. If a path element is '/', we will correctly add
1069 /man and /share/man manpaths. */
1070 if (subdir && strncmp (subdir, "/", 2) == 0) {
1071 subdir[0] = '\0';
1072 subdir = strrchr (trimmed_path, '/');
1073 }
1074 if (subdir) {
1075 newpath = xasprintf ("%.*s/man",
1076 (int) (subdir - trimmed_path),
1077 trimmed_path);
1078 if (is_directory (newpath) == 1)
1079 add_dir_to_list (list, newpath);
1080 free (newpath);
1081 }
1082
1083 newpath = xasprintf ("%s/man", trimmed_path);
1084 if (is_directory (newpath) == 1)
1085 add_dir_to_list (list, newpath);
1086 free (newpath);
1087
1088 if (subdir) {
1089 newpath = xasprintf ("%.*s/share/man",
1090 (int) (subdir - trimmed_path),
1091 trimmed_path);
1092 if (is_directory (newpath) == 1)
1093 add_dir_to_list (list, newpath);
1094 free (newpath);
1095 }
1096
1097 newpath = xasprintf ("%s/share/man", trimmed_path);
1098 if (is_directory (newpath) == 1)
1099 add_dir_to_list (list, newpath);
1100 free (newpath);
1101
1102 free (trimmed_path);
1103 }
1104
1105 struct canonicalized_path {
1106 char *path;
1107 char *canon_path;
1108 };
1109
1110 static struct canonicalized_path *canonicalized_path_new (const char *path)
1111 {
1112 char *canon_path;
1113 struct canonicalized_path *cp = NULL;
1114
1115 canon_path = canonicalize_file_name (path);
1116 if (canon_path) {
1117 cp = XMALLOC (struct canonicalized_path);
1118 cp->path = xstrdup (path);
1119 cp->canon_path = canon_path; /* steal memory */
1120 }
1121 return cp;
1122 }
1123
1124 static bool ATTRIBUTE_PURE canonicalized_path_equals (const void *elt1,
1125 const void *elt2)
1126 {
1127 const struct canonicalized_path *cp1 = elt1, *cp2 = elt2;
1128 return string_equals (cp1->canon_path, cp2->canon_path);
1129 }
1130
1131 static size_t ATTRIBUTE_PURE canonicalized_path_hash (const void *elt)
1132 {
1133 const struct canonicalized_path *cp = elt;
1134 return string_hash (cp->canon_path);
1135 }
1136
1137 static void canonicalized_path_free (const void *elt)
1138 {
1139 /* gl_list declares the argument as const, but there doesn't seem to
1140 * be a good reason for this.
1141 */
1142 struct canonicalized_path *cp = (struct canonicalized_path *) elt;
1143 free (cp->path);
1144 free (cp->canon_path);
1145 free (cp);
1146 }
1147
1148 static void add_dir_to_path_list (gl_list_t list, const char *p)
1149 {
1150 gl_list_t expanded_dirs;
1151 char *expanded_dir;
1152
1153 expanded_dirs = expand_path (p);
1154 GL_LIST_FOREACH (expanded_dirs, expanded_dir) {
1155 int status = is_directory (expanded_dir);
1156
1157 if (status < 0)
1158 gripe_stat_file (expanded_dir);
1159 else if (status == 0)
1160 gripe_not_directory (expanded_dir);
1161 else {
1162 char *path;
1163 struct canonicalized_path *cp;
1164
1165 /* deal with relative paths */
1166 if (*expanded_dir != '/') {
1167 char *cwd = xgetcwd ();
1168 if (!cwd)
1169 fatal (errno,
1170 _("can't determine current directory"));
1171 path = appendstr (cwd, "/", expanded_dir,
1172 (void *) 0);
1173 } else
1174 path = xstrdup (expanded_dir);
1175
1176 cp = canonicalized_path_new (path);
1177 if (cp && !gl_list_search (list, cp)) {
1178 debug ("adding %s to manpathlist\n", path);
1179 gl_list_add_last (list, cp);
1180 } else if (cp)
1181 canonicalized_path_free (cp);
1182 free (path);
1183 }
1184 }
1185 gl_list_free (expanded_dirs);
1186 }
1187
1188 gl_list_t create_pathlist (const char *manp)
1189 {
1190 gl_list_t canonicalized_list, list;
1191 const char *p, *end;
1192 const struct canonicalized_path *cp;
1193
1194 /* Expand the manpath into a list of (path, canonicalized path)
1195 * pairs for easier handling. add_dir_to_path_list only adds items
1196 * if they do not have the same canonicalized path as an existing
1197 * item, thereby eliminating duplicates due to symlinks.
1198 * For each entry, add corresponding OVERRIDE_DIR if configured.
1199 */
1200
1201 canonicalized_list = gl_list_create_empty
1202 (GL_LINKEDHASH_LIST, canonicalized_path_equals,
1203 canonicalized_path_hash, canonicalized_path_free, false);
1204 for (p = manp;; p = end + 1) {
1205 char *element;
1206
1207 end = strchr (p, ':');
1208 element = end ? xstrndup (p, end - p) : xstrdup (p);
1209
1210 if (*OVERRIDE_DIR) {
1211 char *element_override = xasprintf
1212 ("%s/%s", element, OVERRIDE_DIR);
1213 add_dir_to_path_list
1214 (canonicalized_list, element_override);
1215 free (element_override);
1216 }
1217
1218 add_dir_to_path_list (canonicalized_list, element);
1219 free (element);
1220
1221 if (!end)
1222 break;
1223 }
1224
1225 list = new_string_list (GL_ARRAY_LIST, false);
1226 GL_LIST_FOREACH (canonicalized_list, cp)
1227 gl_list_add_last (list, xstrdup (cp->path));
1228
1229 if (debug_level) {
1230 debug ("final search path = ");
1231 GL_LIST_FOREACH (list, p) {
1232 if (!gl_list_previous_node (list, list_node))
1233 debug ("%s", p);
1234 else
1235 debug (":%s", p);
1236 }
1237 debug ("\n");
1238 }
1239
1240 gl_list_free (canonicalized_list);
1241 return list;
1242 }
1243
1244 void free_pathlist (gl_list_t list)
1245 {
1246 gl_list_free (list);
1247 }
1248
1249 /* Routine to get list of named system and user manpaths (in reverse order). */
1250 char *get_mandb_manpath (void)
1251 {
1252 char *manpath = NULL;
1253 const struct config_item *item;
1254
1255 GL_LIST_FOREACH (config, item)
1256 if (item->flag == MANDB_MAP || item->flag == MANDB_MAP_USER)
1257 manpath = pathappend (manpath, item->key);
1258
1259 return manpath;
1260 }
1261
1262 /* Take manpath or manfile path as the first argument, and the type of
1263 * catpaths we want as the other (system catpaths, user catpaths, or both).
1264 * Return catdir mapping or NULL if it isn't a global/user mandir (as
1265 * appropriate).
1266 *
1267 * This routine would seem to work correctly for nls subdirs and would
1268 * specify the (correct) consistent catpath even if not defined in the
1269 * config file.
1270 *
1271 * Do not return user catpaths when cattype == 0! This is used to decide
1272 * whether to drop privileges. When cattype != 0 it's OK to return global
1273 * catpaths.
1274 */
1275 char *get_catpath (const char *name, int cattype)
1276 {
1277 const struct config_item *item;
1278 char *ret = NULL;
1279
1280 GL_LIST_FOREACH (config, item)
1281 if (((cattype & SYSTEM_CAT) && item->flag == MANDB_MAP) ||
1282 ((cattype & USER_CAT) && item->flag == MANDB_MAP_USER)) {
1283 size_t manlen = strlen (item->key);
1284 if (STRNEQ (name, item->key, manlen)) {
1285 const char *suffix;
1286 char *infix;
1287 char *catpath = xstrdup (item->cont);
1288
1289 /* For NLS subdirectories (e.g.
1290 * /usr/share/man/de -> /var/cache/man/de),
1291 * we need to find the second-last slash, as
1292 * long as this strictly follows the key.
1293 */
1294 suffix = strrchr (name, '/');
1295 if (!suffix) {
1296 ret = appendstr (catpath,
1297 name + manlen,
1298 (void *) 0);
1299 break;
1300 }
1301
1302 while (suffix > name + manlen)
1303 if (*--suffix == '/')
1304 break;
1305 if (suffix < name + manlen)
1306 suffix = name + manlen;
1307 if (*suffix == '/')
1308 ++suffix;
1309 infix = xstrndup (name + manlen,
1310 suffix - (name + manlen));
1311 catpath = appendstr (catpath, infix,
1312 (void *) 0);
1313 free (infix);
1314 if (STRNEQ (suffix, "man", 3)) {
1315 suffix += 3;
1316 catpath = appendstr (catpath, "cat",
1317 (void *) 0);
1318 }
1319 catpath = appendstr (catpath, suffix,
1320 (void *) 0);
1321 ret = catpath;
1322 break;
1323 }
1324 }
1325
1326 return ret;
1327 }
1328
1329 /* Check to see if the supplied man directory is a system-wide mandir.
1330 * Obviously, user directories must not be included here.
1331 */
1332 bool ATTRIBUTE_PURE is_global_mandir (const char *dir)
1333 {
1334 const struct config_item *item;
1335 bool ret = false;
1336
1337 GL_LIST_FOREACH (config, item)
1338 if (item->flag == MANDB_MAP &&
1339 STRNEQ (dir, item->key, strlen (item->key))) {
1340 ret = true;
1341 break;
1342 }
1343
1344 return ret;
1345 }
1346
1347 /* Accept a manpath (not a full pathname to a file) and return an FSSTND
1348 equivalent catpath */
1349 static char *fsstnd (const char *path)
1350 {
1351 char *manpath;
1352 char *catpath;
1353 char *element;
1354
1355 if (strncmp (path, MAN_ROOT, sizeof MAN_ROOT - 1) != 0) {
1356 if (!quiet)
1357 error (0, 0, _("warning: %s does not begin with %s"),
1358 path, MAN_ROOT);
1359 return xstrdup (path);
1360 }
1361 /* get rid of initial "/usr" */
1362 path += sizeof MAN_ROOT - 1;
1363 manpath = xstrdup (path);
1364 catpath = xmalloc (strlen (path) + sizeof CAT_ROOT - 3);
1365
1366 /* start with CAT_ROOT */
1367 (void) strcpy (catpath, CAT_ROOT);
1368
1369 /* split up path into elements and deal with accordingly */
1370 for (element = strtok (manpath, "/"); element;
1371 element = strtok (NULL, "/")) {
1372 if (strncmp (element, "man", 3) == 0) {
1373 if (*(element + 3)) {
1374 *element = 'c';
1375 *(element + 2) = 't';
1376 } else
1377 continue;
1378 }
1379 (void) strcat (catpath, "/");
1380 (void) strcat (catpath, element);
1381 }
1382 free (manpath);
1383 return catpath;
1384 }