# /home/anton/tmp/robsd/src-mandoc-unveil.diff commit 2c121f0b31528c38600fc5372c293f4f5a988792 Author: Anton Lindqvist Date: Tue May 5 05:59:25 2026 +0200 Revert "oops, fix a one-byte mishap in the previous commit" This reverts commit e5e15d037d1c725b2e05764caca68b88e048bad6. diff --git usr.bin/mandoc/main.c usr.bin/mandoc/main.c index 50c28e9721bb..a4a5067143f6 100644 --- usr.bin/mandoc/main.c +++ usr.bin/mandoc/main.c @@ -1,7 +1,6 @@ -/* $OpenBSD: main.c,v 1.270 2026/04/17 17:30:50 schwarze Exp $ */ +/* $OpenBSD: main.c,v 1.268 2026/02/23 18:58:30 deraadt Exp $ */ /* - * Copyright (c) 2010-2012, 2014-2021, 2025, 2026 - * Ingo Schwarze + * Copyright (c) 2010-2012,2014-2021,2025 Ingo Schwarze * Copyright (c) 2008-2012 Kristaps Dzonsons * Copyright (c) 2010 Joerg Sonnenberger * @@ -32,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -85,8 +83,6 @@ enum outt { struct outstate { struct tag_files *tag_files; /* Tagging state variables. */ void *outdata; /* data for output */ - char **argv; /* Pager and arguments. */ - int argc; /* Number of pager arguments. */ int use_pager; int wstop; /* stop after a file with a warning */ int had_output; /* Some output was generated. */ @@ -115,7 +111,6 @@ static void process_onefile(struct mparse *, struct manpage *, struct outstate *, struct manconf *); static void run_pager(struct outstate *, char *); static pid_t spawn_pager(struct outstate *, char *); -static int unveil_pager(struct outstate *); static void usage(enum argmode) __attribute__((__noreturn__)); static int woptions(char *, enum mandoc_os *, int *); @@ -361,6 +356,39 @@ main(int argc, char *argv[]) isatty(STDOUT_FILENO) == 0)) outst.use_pager = 0; + if (unveil("/tmp", "rwc") == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); + return mandoc_msg_getrc(); + } + if (conf.output.outfilename) { + if (unveil(".", "rwc") == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); + return mandoc_msg_getrc(); + } + if (unveil(conf.output.outfilename, "rwc") == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); + return mandoc_msg_getrc(); + } + } + if (conf.output.tagfilename) { + if (unveil(".", "rwc") == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); + return mandoc_msg_getrc(); + } + if (unveil(conf.output.tagfilename, "rwc") == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); + return mandoc_msg_getrc(); + } + } + if (unveil("/", "rx") == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); + return mandoc_msg_getrc(); + } + if (pledge("stdio rpath wpath cpath tty proc exec", NULL) == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); + return mandoc_msg_getrc(); + } + if (outst.use_pager && (conf.output.width == 0 || conf.output.indent == 0) && ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && @@ -371,106 +399,13 @@ main(int argc, char *argv[]) conf.output.indent = 3; } - /* - * Parse the configuration file before the first unveil(2) - * because it requires realpath(3) on the manpath directories. - * Operating-system specific .Xr validation additionally - * requires a separate base system manpath. - */ - - manconf_parse(&conf, conf_file, defpaths, auxpaths); - if (mandoc_msg_getmin() < MANDOCERR_STYLE) - manpath_base(&conf.basepath); - - /* - * Figure out access to the pager executable first, before any - * other unveil(2), because this requires testing with access(2). - */ - if (outst.use_pager && - conf.output.outfilename == NULL && - conf.output.tagfilename == NULL && - unveil_pager(&outst) == -1) - return mandoc_msg_getrc(); - - /* - * Restrict file system access. If using mandoc.db(5), - * allow reading from all user-specified manpaths. - * For operating-system specific .Xr validation, allow - * reading from the base system directories even when - * those aren't included in the user-specified manpath. - */ - - if ((search.argmode != ARG_FILE || - mandoc_msg_getmin() < MANDOCERR_WARNING) && - manpath_unveil(&conf.manpath, 0) == -1) - return mandoc_msg_getrc(); - if (mandoc_msg_getmin() < MANDOCERR_STYLE && - manpath_unveil(&conf.basepath, 0) == -1) - return mandoc_msg_getrc(); - - /* - * For man -l and mandoc, allow read access to the whole file system. - * Do not attempt to unveil command line arguments individually - * because having many of them is a legitimate use case. - */ - - if (search.argmode == ARG_FILE) { - if (unveil("/", "r") == -1) { - mandoc_msg(MANDOCERR_UNVEIL, 0, 0, "/: %s", - strerror(errno)); - return mandoc_msg_getrc(); - } - } - - /* - * For the -T locale and -T utf8 output formats, - * setlocale(3) needs to read locale configuration files. - */ - - if (outst.outtype == OUTT_LOCALE || outst.outtype == OUTT_UTF8) { - if (unveil(_PATH_LOCALE, "r") == -1) { - mandoc_msg(MANDOCERR_UNVEIL, 0, 0, "%s: %s", - _PATH_LOCALE, strerror(errno)); - return mandoc_msg_getrc(); - } - } - - /* Set up output files, including temporary ones. */ - - if (outst.use_pager) { - if (conf.output.outfilename == NULL || - conf.output.tagfilename == NULL) { - if (unveil("/tmp", "rwc") == -1) { - mandoc_msg(MANDOCERR_UNVEIL, 0, 0, "%s: %s", - "/tmp", strerror(errno)); - return mandoc_msg_getrc(); - } - } - if (conf.output.outfilename != NULL) { - if (unveil(conf.output.outfilename, "wc") == -1) { - mandoc_msg(MANDOCERR_UNVEIL, 0, 0, "%s: %s", - conf.output.outfilename, strerror(errno)); - return mandoc_msg_getrc(); - } - } - if (conf.output.tagfilename != NULL) { - if (unveil(conf.output.tagfilename, "wc") == -1) { - mandoc_msg(MANDOCERR_UNVEIL, 0, 0, "%s: %s", - conf.output.tagfilename, strerror(errno)); - return mandoc_msg_getrc(); - } - } - outst.tag_files = term_tag_init(conf.output.outfilename, - outst.outtype == OUTT_HTML ? ".html" : "", - conf.output.tagfilename); - } - - /* Restrict access to system calls. */ - - if (outst.use_pager == 0) /* Needed to read manuals. */ + if (outst.use_pager == 0) c = pledge("stdio rpath", NULL); - else /* Needed for spawn_pager() and term_tag_unlink(). */ - c = pledge("stdio rpath cpath tty proc exec", NULL); + else if (conf.output.outfilename != NULL || + conf.output.tagfilename != NULL) + c = pledge("stdio rpath wpath cpath", NULL); + else + c = pledge("stdio rpath wpath cpath tty proc exec", NULL); if (c == -1) { mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); return mandoc_msg_getrc(); @@ -522,6 +457,12 @@ main(int argc, char *argv[]) conf.output.tag = tagarg == NULL ? *argv : tagarg + 1; } + /* Read the configuration file. */ + + if (search.argmode != ARG_FILE || + mandoc_msg_getmin() == MANDOCERR_STYLE) + manconf_parse(&conf, conf_file, defpaths, auxpaths); + /* man(1): Resolve each name individually. */ if (search.argmode == ARG_NAME) { @@ -706,8 +647,7 @@ out: if (outst.tag_files != NULL) { if (term_tag_close() != -1 && conf.output.outfilename == NULL && - conf.output.tagfilename == NULL && - ressz > 0) + conf.output.tagfilename == NULL) run_pager(&outst, conf.output.tag); term_tag_unlink(); } else if (outst.had_output && outst.outtype != OUTT_LINT) @@ -940,6 +880,19 @@ process_onefile(struct mparse *mp, struct manpage *resp, } else fd = STDIN_FILENO; + if (outst->use_pager) { + outst->use_pager = 0; + outst->tag_files = term_tag_init(conf->output.outfilename, + outst->outtype == OUTT_HTML ? ".html" : "", + conf->output.tagfilename); + if ((conf->output.outfilename != NULL || + conf->output.tagfilename != NULL) && + pledge("stdio rpath wpath cpath", NULL) == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, + "%s", strerror(errno)); + exit(mandoc_msg_getrc()); + } + } if (outst->had_output && outst->outtype <= OUTT_UTF8) { if (outst->outdata == NULL) outdata_alloc(outst, &conf->output); @@ -970,6 +923,7 @@ static void parse(struct mparse *mp, int fd, const char *file, struct outstate *outst, struct manconf *conf) { + static struct manpaths basepaths; struct roff_meta *meta; assert(fd >= 0); @@ -1054,7 +1008,9 @@ parse(struct mparse *mp, int fd, const char *file, conf->output.tag_found = 1; if (mandoc_msg_getmin() < MANDOCERR_STYLE) { - check_xr(&conf->basepath); + if (basepaths.sz == 0) + manpath_base(&basepaths); + check_xr(&basepaths); } else if (mandoc_msg_getmin() < MANDOCERR_WARNING) check_xr(&conf->manpath); } @@ -1236,85 +1192,6 @@ woptions(char *arg, enum mandoc_os *os_e, int *wstop) return 0; } -static int -unveil_pager(struct outstate *outst) -{ - const char *cp, *pager; - char *cmd, *dir, *path; - size_t len; - int i; - - pager = getenv("MANPAGER"); - if (pager == NULL || *pager == '\0') - pager = getenv("PAGER"); - if (pager == NULL || *pager == '\0') - pager = "less"; - - /* Count the arguments on the pager command line. */ - - outst->argc = 0; - cp = pager; - while (*cp != '\0') { - len = strcspn(cp, " "); - outst->argc++; - cp += len; - while (*cp == ' ') - cp++; - } - - /* - * Allocate the pager command line. - * The six additional pointers are: - * -T tagfilename -t tag_target outfilename NULL - */ - - outst->argv = mandoc_calloc(outst->argc + 6, sizeof(char *)); - i = 0; - cp = pager; - while (*cp != '\0') { - len = strcspn(cp, " "); - outst->argv[i++] = mandoc_strndup(cp, len); - cp += len; - while (*cp == ' ') - cp++; - } - outst->argv[i] = NULL; - assert(i == outst->argc); - - /* Use the PATH to find the pager executable. */ - - if (outst->argv[0][0] != '/') { - if ((path = getenv("PATH")) == NULL || *path == '\0') { - mandoc_msg(MANDOCERR_PATH, 0, 0, - "cannot search for \"%s\" in empty PATH", - outst->argv[0]); - return -1; - } - path = mandoc_strdup(path); - for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) { - mandoc_asprintf(&cmd, "%s/%s", dir, outst->argv[0]); - if (access(cmd, X_OK) == 0) { - free(outst->argv[0]); - outst->argv[0] = cmd; - break; - } - free(cmd); - } - free(path); - } - if (outst->argv[0][0] != '/') { - mandoc_msg(MANDOCERR_PATH, 0, 0, - "pager \"%s\" not found in PATH", outst->argv[0]); - return -1; - } - if (unveil(outst->argv[0], "x") == -1) { - mandoc_msg(MANDOCERR_UNVEIL, 0, 0, "%s: %s", - outst->argv[0], strerror(errno)); - return -1; - } - return 0; -} - /* * Wait until moved to the foreground, * then fork the pager and wait for the user to close it. @@ -1377,44 +1254,63 @@ static pid_t spawn_pager(struct outstate *outst, char *tag_target) { const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ +#define MAX_PAGER_ARGS 16 + char *argv[MAX_PAGER_ARGS]; + const char *pager; char *cp; + size_t wordlen; size_t cmdlen; - int use_ofn; + int argc, use_ofn; pid_t pager_pid; assert(outst->tag_files->ofd == -1); assert(outst->tag_files->tfs == NULL); + pager = getenv("MANPAGER"); + if (pager == NULL || *pager == '\0') + pager = getenv("PAGER"); + if (pager == NULL || *pager == '\0') + pager = "less"; + + /* + * Parse the pager command into words. + * Intentionally do not do anything fancy here. + */ + + argc = 0; + while (*pager != '\0' && argc + 5 < MAX_PAGER_ARGS) { + wordlen = strcspn(pager, " "); + argv[argc++] = mandoc_strndup(pager, wordlen); + pager += wordlen; + while (*pager == ' ') + pager++; + } + /* For more(1) and less(1), use the tag file. */ use_ofn = 1; if (*outst->tag_files->tfn != '\0' && - (cmdlen = strlen(outst->argv[0])) >= 4) { - cp = outst->argv[0] + cmdlen - 4; + (cmdlen = strlen(argv[0])) >= 4) { + cp = argv[0] + cmdlen - 4; if (strcmp(cp, "less") == 0 || strcmp(cp, "more") == 0) { - outst->argv[outst->argc++] = mandoc_strdup("-T"); - outst->argv[outst->argc++] = - mandoc_strdup(outst->tag_files->tfn); + argv[argc++] = mandoc_strdup("-T"); + argv[argc++] = mandoc_strdup(outst->tag_files->tfn); if (tag_target != NULL) { - outst->argv[outst->argc++] = - mandoc_strdup("-t"); - outst->argv[outst->argc++] = - mandoc_strdup(tag_target); + argv[argc++] = mandoc_strdup("-t"); + argv[argc++] = mandoc_strdup(tag_target); use_ofn = 0; } } } if (use_ofn) { if (outst->outtype == OUTT_HTML && tag_target != NULL) - mandoc_asprintf(&outst->argv[outst->argc], - "file://%s#%s", outst->tag_files->ofn, - tag_target); + mandoc_asprintf(&argv[argc], "file://%s#%s", + outst->tag_files->ofn, tag_target); else - outst->argv[outst->argc] = - mandoc_strdup(outst->tag_files->ofn); - outst->argc++; + argv[argc] = mandoc_strdup(outst->tag_files->ofn); + argc++; } - outst->argv[outst->argc] = NULL; + argv[argc] = NULL; switch (pager_pid = fork()) { case -1: @@ -1423,13 +1319,11 @@ spawn_pager(struct outstate *outst, char *tag_target) case 0: break; default: - while (outst->argc > 0) - free(outst->argv[--outst->argc]); + while (argc > 0) + free(argv[--argc]); (void)setpgid(pager_pid, 0); (void)tcsetpgrp(STDOUT_FILENO, pager_pid); - - /* Needed for term_tag_unlink(). */ - if (pledge("stdio cpath tty proc", NULL) == -1) { + if (pledge("stdio rpath wpath cpath tty proc", NULL) == -1) { mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); exit(mandoc_msg_getrc()); @@ -1446,8 +1340,7 @@ spawn_pager(struct outstate *outst, char *tag_target) while (tcgetpgrp(STDOUT_FILENO) != getpid()) nanosleep(&timeout, NULL); - execv(outst->argv[0], outst->argv); - mandoc_msg(MANDOCERR_EXEC, 0, 0, "%s: %s", - outst->argv[0], strerror(errno)); + execvp(argv[0], argv); + mandoc_msg(MANDOCERR_EXEC, 0, 0, "%s: %s", argv[0], strerror(errno)); _exit(mandoc_msg_getrc()); } diff --git usr.bin/mandoc/manconf.h usr.bin/mandoc/manconf.h index 633c71aa06b5..5073121b1248 100644 --- usr.bin/mandoc/manconf.h +++ usr.bin/mandoc/manconf.h @@ -1,7 +1,6 @@ -/* $OpenBSD: manconf.h,v 1.10 2026/04/17 15:30:27 schwarze Exp $ */ +/* $OpenBSD: manconf.h,v 1.9 2020/07/21 15:08:49 schwarze Exp $ */ /* - * Copyright (c) 2011, 2015, 2017, 2018, 2020, 2026 - * Ingo Schwarze + * Copyright (c) 2011,2015,2017,2018,2020 Ingo Schwarze * Copyright (c) 2011 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any @@ -50,7 +49,6 @@ struct manoutput { struct manconf { struct manoutput output; struct manpaths manpath; - struct manpaths basepath; }; @@ -58,4 +56,3 @@ void manconf_parse(struct manconf *, const char *, char *, char *); int manconf_output(struct manoutput *, const char *, int); void manconf_free(struct manconf *); void manpath_base(struct manpaths *); -int manpath_unveil(struct manpaths *, int); diff --git usr.bin/mandoc/mandoc.h usr.bin/mandoc/mandoc.h index 00ec8ad36a84..1349fc033de2 100644 --- usr.bin/mandoc/mandoc.h +++ usr.bin/mandoc/mandoc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: mandoc.h,v 1.226 2026/04/17 15:30:27 schwarze Exp $ */ +/* $OpenBSD: mandoc.h,v 1.225 2025/01/05 18:03:51 schwarze Exp $ */ /* * Copyright (c) 2012-2022, 2025 Ingo Schwarze * Copyright (c) 2010, 2011, 2014 Kristaps Dzonsons @@ -285,10 +285,8 @@ enum mandocerr { MANDOCERR_GZDOPEN, MANDOCERR_MKSTEMP, MANDOCERR_OPEN, - MANDOCERR_PATH, MANDOCERR_PLEDGE, MANDOCERR_READ, - MANDOCERR_UNVEIL, MANDOCERR_WAIT, MANDOCERR_WRITE, diff --git usr.bin/mandoc/mandoc_msg.c usr.bin/mandoc/mandoc_msg.c index c3f72d8dce04..08dc63cf7ad5 100644 --- usr.bin/mandoc/mandoc_msg.c +++ usr.bin/mandoc/mandoc_msg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mandoc_msg.c,v 1.20 2026/04/17 15:30:27 schwarze Exp $ */ +/* $OpenBSD: mandoc_msg.c,v 1.19 2025/01/05 18:03:51 schwarze Exp $ */ /* * Copyright (c) 2014-2022, 2025 Ingo Schwarze * Copyright (c) 2010, 2011 Kristaps Dzonsons @@ -281,10 +281,8 @@ static const char *const type_message[MANDOCERR_MAX] = { "gzdopen", "mkstemp", "open", - "PATH", "pledge", "read", - "unveil", "wait", "write", }; diff --git usr.bin/mandoc/manpath.c usr.bin/mandoc/manpath.c index 8dded24b587e..f1fb585673b0 100644 --- usr.bin/mandoc/manpath.c +++ usr.bin/mandoc/manpath.c @@ -1,7 +1,6 @@ -/* $OpenBSD: manpath.c,v 1.33 2026/04/17 15:30:27 schwarze Exp $ */ +/* $OpenBSD: manpath.c,v 1.32 2025/06/26 17:21:02 schwarze Exp $ */ /* - * Copyright (c) 2011, 2014, 2015, 2017-2021, 2026 - * Ingo Schwarze + * Copyright (c) 2011,2014,2015,2017-2021 Ingo Schwarze * Copyright (c) 2011 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any @@ -25,7 +24,6 @@ #include #include #include -#include #include "mandoc_aux.h" #include "mandoc.h" @@ -89,40 +87,6 @@ manpath_base(struct manpaths *dirs) manpath_parseline(dirs, path_base, '\0'); } -int -manpath_unveil(struct manpaths *dirs, int writeable) -{ - char *file; - size_t i; - int len; - - for (i = 0; i < dirs->sz; i++) { - if (unveil(dirs->paths[i], "r") == -1) { - mandoc_msg(MANDOCERR_UNVEIL, 0, 0, "%s: %s", - dirs->paths[i], strerror(errno)); - return -1; - } - if (writeable == 0) - continue; - len = mandoc_asprintf(&file, "%s/mandoc.db~", dirs->paths[i]); - if (unveil(file, "rwc") == -1) { - mandoc_msg(MANDOCERR_UNVEIL, 0, 0, "%s: %s", - file, strerror(errno)); - free(file); - return -1; - } - file[len - 1] = '\0'; - if (unveil(file, "rwc") == -1) { - mandoc_msg(MANDOCERR_UNVEIL, 0, 0, "%s: %s", - file, strerror(errno)); - free(file); - return -1; - } - free(file); - } - return 0; -} - /* * Parse a FULL pathname from a colon-separated list of arrays. */ @@ -166,13 +130,9 @@ manpath_add(struct manpaths *dirs, const char *dir, char option) return; fail: - if (option == '\0') - return; - if (option == 'm' && (strcmp(dir, "andoc") == 0 || - strcmp(dir, "an") == 0 || strcmp(dir, "doc") == 0)) - return; - mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-%c %s: %s", - option, dir, strerror(errno)); + if (option != '\0') + mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, + "-%c %s: %s", option, dir, strerror(errno)); } void diff --git usr.bin/mandoc/mansearch.c usr.bin/mandoc/mansearch.c index 90835cc466b6..fa2019b033be 100644 --- usr.bin/mandoc/mansearch.c +++ usr.bin/mandoc/mansearch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mansearch.c,v 1.68 2026/04/17 14:07:48 schwarze Exp $ */ +/* $OpenBSD: mansearch.c,v 1.67 2022/12/26 19:16:02 jmc Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons * Copyright (c) 2013-2018 Ingo Schwarze @@ -83,6 +83,7 @@ mansearch(const struct mansearch *search, int argc, char *argv[], struct manpage **res, size_t *sz) { + char buf[PATH_MAX]; struct dbm_res *rp; struct expr *e; struct dbm_page *page; @@ -90,7 +91,7 @@ mansearch(const struct mansearch *search, struct ohash *htab; size_t cur, i, maxres, outkey; unsigned int slot; - int argi, im; + int argi, chdir_status, getcwd_status, im; argi = 0; if ((e = exprcomp(search, argc, argv, &argi)) == NULL) { @@ -111,6 +112,20 @@ mansearch(const struct mansearch *search, break; } + /* + * Remember the original working directory, if possible. + * This will be needed if the second or a later directory + * is given as a relative path. + * Do not error out if the current directory is not + * searchable: Maybe it won't be needed after all. + */ + + if (getcwd(buf, PATH_MAX) == NULL) { + getcwd_status = 0; + (void)strlcpy(buf, strerror(errno), sizeof(buf)); + } else + getcwd_status = 1; + /* * Loop over the directories (containing databases) for us to * search. @@ -119,11 +134,23 @@ mansearch(const struct mansearch *search, * scan it for our match expression. */ + chdir_status = 0; for (i = 0; i < paths->sz; i++) { + if (chdir_status && paths->paths[i][0] != '/') { + if ( ! getcwd_status) { + warnx("%s: getcwd: %s", paths->paths[i], buf); + continue; + } else if (chdir(buf) == -1) { + warn("%s", buf); + continue; + } + } if (chdir(paths->paths[i]) == -1) { warn("%s", paths->paths[i]); continue; } + chdir_status = 1; + if (dbm_open(MANDOC_DB) == -1) { if (errno != ENOENT) warn("%s/%s", paths->paths[i], MANDOC_DB); @@ -157,7 +184,8 @@ mansearch(const struct mansearch *search, mpage = *res + cur; mandoc_asprintf(&mpage->file, "%s/%s", paths->paths[i], page->file + 1); - if (access(page->file + 1, R_OK) == -1) { + if (access(chdir_status ? page->file + 1 : + mpage->file, R_OK) == -1) { warn("%s", mpage->file); warnx("outdated mandoc.db contains " "bogus %s entry, run makewhatis %s", @@ -191,6 +219,8 @@ mansearch(const struct mansearch *search, } if (res != NULL && cur > 1) qsort(*res, cur, sizeof(struct manpage), manpage_compare); + if (chdir_status && getcwd_status && chdir(buf) == -1) + warn("%s", buf); exprfree(e); *sz = cur; return res != NULL || cur;