]> git.cameronkatri.com Git - mandoc.git/blobdiff - mandocdb.c
fix the section number in the <title> element for preformatted pages;
[mandoc.git] / mandocdb.c
index 6ac2f1a8caa13526ed4e2364b5591029fc2daca8..0ef661b2d57c79894c22eaa890a529c8ab680c29 100644 (file)
@@ -1,7 +1,7 @@
-/*     $Id: mandocdb.c,v 1.264 2020/01/25 22:59:22 schwarze Exp $ */
+/* $Id: mandocdb.c,v 1.268 2021/08/07 13:02:10 schwarze Exp $ */
 /*
- * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2011-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2016 Ed Maste <emaste@freebsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -15,6 +15,8 @@
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Implementation of the makewhatis(8) program.
  */
 #include "config.h"
 
@@ -118,7 +120,7 @@ struct      mdoc_handler {
 int             mandocdb(int, char *[]);
 
 static void     dbadd(struct dba *, struct mpage *);
-static void     dbadd_mlink(const struct mlink *mlink);
+static void     dbadd_mlink(const struct mlink *);
 static void     dbprune(struct dba *);
 static void     dbwrite(struct dba *);
 static void     filescan(const char *);
@@ -163,6 +165,9 @@ static      void     putkey(const struct mpage *, char *, uint64_t);
 static void     putkeys(const struct mpage *, char *, size_t, uint64_t);
 static void     putmdockey(const struct mpage *,
                        const struct roff_node *, uint64_t, int);
+#ifdef READ_ALLOWED_PATH
+static int      read_allowed(const char *);
+#endif
 static int      render_string(char **, size_t *);
 static void     say(const char *, const char *, ...)
                        __attribute__((__format__ (__printf__, 2, 3)));
@@ -610,8 +615,8 @@ treescan(void)
                                continue;
                        }
                        if (strncmp(buf, basedir, basedir_len) != 0
-#ifdef HOMEBREWDIR
-                           && strncmp(buf, HOMEBREWDIR, strlen(HOMEBREWDIR))
+#ifdef READ_ALLOWED_PATH
+                           && !read_allowed(buf)
 #endif
                        ) {
                                if (warnings) say("",
@@ -778,17 +783,17 @@ treescan(void)
  * See treescan() for the fts(3) version of this.
  */
 static void
-filescan(const char *file)
+filescan(const char *infile)
 {
-       char             buf[PATH_MAX];
        struct stat      st;
        struct mlink    *mlink;
-       char            *p, *start;
+       char            *linkfile, *p, *realdir, *start, *usefile;
+       size_t           realdir_len;
 
        assert(use_all);
 
-       if (strncmp(file, "./", 2) == 0)
-               file += 2;
+       if (strncmp(infile, "./", 2) == 0)
+               infile += 2;
 
        /*
         * We have to do lstat(2) before realpath(3) loses
@@ -797,13 +802,13 @@ filescan(const char *file)
         * we want to use the orginal file name, while for
         * regular files, we want to use the real path.
         */
-       if (lstat(file, &st) == -1) {
+       if (lstat(infile, &st) == -1) {
                exitcode = (int)MANDOCLEVEL_BADARG;
-               say(file, "&lstat");
+               say(infile, "&lstat");
                return;
-       } else if ((st.st_mode & (S_IFREG | S_IFLNK)) == 0) {
+       } else if (S_ISREG(st.st_mode) == 0 && S_ISLNK(st.st_mode) == 0) {
                exitcode = (int)MANDOCLEVEL_BADARG;
-               say(file, "Not a regular file");
+               say(infile, "Not a regular file");
                return;
        }
 
@@ -811,23 +816,24 @@ filescan(const char *file)
         * We have to resolve the file name to the real path
         * in any case for the base directory check.
         */
-       if (realpath(file, buf) == NULL) {
+       if ((usefile = realpath(infile, NULL)) == NULL) {
                exitcode = (int)MANDOCLEVEL_BADARG;
-               say(file, "&realpath");
+               say(infile, "&realpath");
                return;
        }
 
        if (op == OP_TEST)
-               start = buf;
-       else if (strncmp(buf, basedir, basedir_len) == 0)
-               start = buf + basedir_len;
-#ifdef HOMEBREWDIR
-       else if (strncmp(buf, HOMEBREWDIR, strlen(HOMEBREWDIR)) == 0)
-               start = buf;
+               start = usefile;
+       else if (strncmp(usefile, basedir, basedir_len) == 0)
+               start = usefile + basedir_len;
+#ifdef READ_ALLOWED_PATH
+       else if (read_allowed(usefile))
+               start = usefile;
 #endif
        else {
                exitcode = (int)MANDOCLEVEL_BADARG;
-               say("", "%s: outside base directory", buf);
+               say("", "%s: outside base directory", infile);
+               free(usefile);
                return;
        }
 
@@ -835,25 +841,72 @@ filescan(const char *file)
         * Now we are sure the file is inside our tree.
         * If it is a symbolic link, ignore the real path
         * and use the original name.
-        * This implies passing stuff like "cat1/../man1/foo.1"
-        * on the command line won't work.  So don't do that.
-        * Note the stat(2) can still fail if the link target
-        * doesn't exist.
         */
-       if (st.st_mode & S_IFLNK) {
-               if (stat(buf, &st) == -1) {
+       do {
+               if (S_ISLNK(st.st_mode) == 0)
+                       break;
+
+               /*
+                * Some implementations of realpath(3) may succeed
+                * even if the target of the link does not exist,
+                * so check again for extra safety.
+                */
+               if (stat(usefile, &st) == -1) {
                        exitcode = (int)MANDOCLEVEL_BADARG;
-                       say(file, "&stat");
+                       say(infile, "&stat");
+                       free(usefile);
                        return;
                }
-               if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) {
-                       say(file, "Filename too long");
-                       return;
+               linkfile = mandoc_strdup(infile);
+               if (op == OP_TEST) {
+                       free(usefile);
+                       start = usefile = linkfile;
+                       break;
+               }
+               if (strncmp(infile, basedir, basedir_len) == 0) {
+                       free(usefile);
+                       usefile = linkfile;
+                       start = usefile + basedir_len;
+                       break;
                }
-               start = buf;
-               if (op != OP_TEST && strncmp(buf, basedir, basedir_len) == 0)
-                       start += basedir_len;
-       }
+
+               /*
+                * This symbolic link points into the basedir
+                * from the outside.  Let's see whether any of
+                * the parent directories resolve to the basedir.
+                */
+               p = strchr(linkfile, '\0');
+               do {
+                       while (*--p != '/')
+                               continue;
+                       *p = '\0';
+                       if ((realdir = realpath(linkfile, NULL)) == NULL) {
+                               exitcode = (int)MANDOCLEVEL_BADARG;
+                               say(infile, "&realpath");
+                               free(linkfile);
+                               free(usefile);
+                               return;
+                       }
+                       realdir_len = strlen(realdir) + 1;
+                       free(realdir);
+                       *p = '/';
+               } while (realdir_len > basedir_len);
+
+               /*
+                * If one of the directories resolves to the basedir,
+                * use the rest of the original name.
+                * Otherwise, the best we can do
+                * is to use the filename pointed to.
+                */
+               if (realdir_len == basedir_len) {
+                       free(usefile);
+                       usefile = linkfile;
+                       start = p + 1;
+               } else {
+                       free(linkfile);
+                       start = usefile + basedir_len;
+               }
+       } while (/* CONSTCOND */ 0);
 
        mlink = mandoc_calloc(1, sizeof(struct mlink));
        mlink->dform = FORM_NONE;
@@ -861,6 +914,7 @@ filescan(const char *file)
            sizeof(mlink->file)) {
                say(start, "Filename too long");
                free(mlink);
+               free(usefile);
                return;
        }
 
@@ -869,13 +923,13 @@ filescan(const char *file)
         * but outside our tree, guess the base directory.
         */
 
-       if (op == OP_TEST || (start == buf && *start == '/')) {
-               if (strncmp(buf, "man/", 4) == 0)
-                       start = buf + 4;
-               else if ((start = strstr(buf, "/man/")) != NULL)
+       if (op == OP_TEST || (start == usefile && *start == '/')) {
+               if (strncmp(usefile, "man/", 4) == 0)
+                       start = usefile + 4;
+               else if ((start = strstr(usefile, "/man/")) != NULL)
                        start += 5;
                else
-                       start = buf;
+                       start = usefile;
        }
 
        /*
@@ -925,6 +979,7 @@ filescan(const char *file)
                *p = '\0';
        }
        mlink_add(mlink, &st);
+       free(usefile);
 }
 
 static void
@@ -2329,6 +2384,25 @@ set_basedir(const char *targetdir, int report_baddir)
        return 1;
 }
 
+#ifdef READ_ALLOWED_PATH
+static int
+read_allowed(const char *candidate)
+{
+       const char      *cp;
+       size_t           len;
+
+       for (cp = READ_ALLOWED_PATH;; cp += len) {
+               while (*cp == ':')
+                       cp++;
+               if (*cp == '\0')
+                       return 0;
+               len = strcspn(cp, ":");
+               if (strncmp(candidate, cp, len) == 0)
+                       return 1;
+       }
+}
+#endif
+
 static void
 say(const char *file, const char *format, ...)
 {