]> git.cameronkatri.com Git - mandoc.git/blobdiff - read.c
Fix a few lies; polish wording and formatting while here.
[mandoc.git] / read.c
diff --git a/read.c b/read.c
index 89b2c28d9052bc8cf5fd143ebfcfee37e7a4dba8..d78a0b0d6dee2f406767662659abb47ad4cf9a31 100644 (file)
--- a/read.c
+++ b/read.c
@@ -1,7 +1,8 @@
-/*     $Id: read.c,v 1.33 2012/11/19 17:57:23 schwarze Exp $ */
+/*     $Id: read.c,v 1.46 2014/03/23 11:25:26 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2012 Joerg Sonnenberger <joerg@netbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -26,6 +27,7 @@
 
 #include <assert.h>
 #include <ctype.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <unistd.h>
 
 #include "mandoc.h"
+#include "mandoc_aux.h"
 #include "libmandoc.h"
 #include "mdoc.h"
 #include "man.h"
 #include "main.h"
 
-#ifndef MAP_FILE
-#define        MAP_FILE        0
-#endif
-
 #define        REPARSE_LIMIT   1000
 
 struct buf {
@@ -55,15 +54,15 @@ struct      mparse {
        enum mandoclevel  file_status; /* status of current parse */
        enum mandoclevel  wlevel; /* ignore messages below this */
        int               line; /* line number in the file */
-       enum mparset      inttype; /* which parser to use */
+       int               options; /* parser options */
        struct man       *pman; /* persistent man parser */
        struct mdoc      *pmdoc; /* persistent mdoc parser */
        struct man       *man; /* man parser */
        struct mdoc      *mdoc; /* mdoc parser */
        struct roff      *roff; /* roff parser (!NULL) */
+       char             *sodest; /* filename pointed to by .so */
        int               reparse_count; /* finite interp. stack */
        mandocmsg         mmsg; /* warning/error message handler */
-       void             *arg; /* argument to mmsg */
        const char       *file; 
        struct buf       *secondary;
        char             *defos; /* default operating system */
@@ -71,10 +70,12 @@ struct      mparse {
 
 static void      resize_buf(struct buf *, size_t);
 static void      mparse_buf_r(struct mparse *, struct buf, int);
-static void      mparse_readfd_r(struct mparse *, int, const char *, int);
 static void      pset(const char *, int, struct mparse *);
-static int       read_whole_file(const char *, int, struct buf *, int *);
+static int       read_whole_file(struct mparse *, const char *, int,
+                               struct buf *, int *);
 static void      mparse_end(struct mparse *);
+static void      mparse_parse_buffer(struct mparse *, struct buf,
+                       const char *);
 
 static const enum mandocerr    mandoclimits[MANDOCLEVEL_MAX] = {
        MANDOCERR_OK,
@@ -109,7 +110,7 @@ static      const char * const      mandocerrs[MANDOCERR_MAX] = {
        "bad NAME section contents",
        "sections out of conventional order",
        "duplicate section name",
-       "section not in conventional manual section",
+       "section header suited to sections 2, 3, and 9 only",
 
        /* related to macros and nesting */
        "skipping obsolete macro",
@@ -180,6 +181,7 @@ static      const char * const      mandocerrs[MANDOCERR_MAX] = {
        "skipping unknown macro",
        "NOT IMPLEMENTED, please use groff: skipping request",
        "argument count wrong",
+       "skipping column outside column list",
        "skipping end of block that is not open",
        "missing end of block",
        "scope open on exit",
@@ -187,12 +189,14 @@ static    const char * const      mandocerrs[MANDOCERR_MAX] = {
        "macro requires line argument(s)",
        "macro requires body argument(s)",
        "macro requires argument(s)",
+       "request requires a numeric argument",
        "missing list type",
        "line argument(s) will be lost",
        "body argument(s) will be lost",
 
        "generic fatal error",
 
+       "input too large",
        "not a manual",
        "column syntax is inconsistent",
        "NOT IMPLEMENTED: .Bd -file",
@@ -203,6 +207,11 @@ static     const char * const      mandocerrs[MANDOCERR_MAX] = {
        "no document body",
        "no document prologue",
        "static buffer exhausted",
+
+       /* system errors */
+       "cannot open file",
+       "cannot stat file",
+       "cannot read file",
 };
 
 static const char * const      mandoclevels[MANDOCLEVEL_MAX] = {
@@ -247,35 +256,36 @@ pset(const char *buf, int pos, struct mparse *curp)
                        return;
        }
 
-       switch (curp->inttype) {
-       case (MPARSE_MDOC):
+       if (MPARSE_MDOC & curp->options) {
                if (NULL == curp->pmdoc) 
-                       curp->pmdoc = mdoc_alloc(curp->roff, curp,
-                                       curp->defos);
+                       curp->pmdoc = mdoc_alloc(
+                           curp->roff, curp, curp->defos,
+                           MPARSE_QUICK & curp->options ? 1 : 0);
                assert(curp->pmdoc);
                curp->mdoc = curp->pmdoc;
                return;
-       case (MPARSE_MAN):
+       } else if (MPARSE_MAN & curp->options) {
                if (NULL == curp->pman) 
-                       curp->pman = man_alloc(curp->roff, curp);
+                       curp->pman = man_alloc(curp->roff, curp,
+                           MPARSE_QUICK & curp->options ? 1 : 0);
                assert(curp->pman);
                curp->man = curp->pman;
                return;
-       default:
-               break;
        }
 
        if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3))  {
                if (NULL == curp->pmdoc) 
-                       curp->pmdoc = mdoc_alloc(curp->roff, curp,
-                                       curp->defos);
+                       curp->pmdoc = mdoc_alloc(
+                           curp->roff, curp, curp->defos,
+                           MPARSE_QUICK & curp->options ? 1 : 0);
                assert(curp->pmdoc);
                curp->mdoc = curp->pmdoc;
                return;
        } 
 
        if (NULL == curp->pman) 
-               curp->pman = man_alloc(curp->roff, curp);
+               curp->pman = man_alloc(curp->roff, curp,
+                   MPARSE_QUICK & curp->options ? 1 : 0);
        assert(curp->pman);
        curp->man = curp->pman;
 }
@@ -327,6 +337,15 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
                                break;
                        }
 
+                       /*
+                        * Make sure we have space for at least
+                        * one backslash and one other character
+                        * and the trailing NUL byte.
+                        */
+
+                       if (pos + 2 >= (int)ln.sz)
+                               resize_buf(&ln, 256);
+
                        /* 
                         * Warn about bogus characters.  If you're using
                         * non-ASCII encoding, you're screwing your
@@ -343,8 +362,6 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
                                mandoc_msg(MANDOCERR_BADCHAR, curp,
                                                curp->line, pos, NULL);
                                i++;
-                               if (pos >= (int)ln.sz)
-                                       resize_buf(&ln, 256);
                                ln.buf[pos++] = '?';
                                continue;
                        }
@@ -352,8 +369,6 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
                        /* Trailing backslash = a plain char. */
 
                        if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
-                               if (pos >= (int)ln.sz)
-                                       resize_buf(&ln, 256);
                                ln.buf[pos++] = blk.buf[i++];
                                continue;
                        }
@@ -395,10 +410,20 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
                                break;
                        }
 
-                       /* Some other escape sequence, copy & cont. */
+                       /* Catch escaped bogus characters. */
 
-                       if (pos + 1 >= (int)ln.sz)
-                               resize_buf(&ln, 256);
+                       c = (unsigned char) blk.buf[i+1];
+
+                       if ( ! (isascii(c) && 
+                                       (isgraph(c) || isblank(c)))) {
+                               mandoc_msg(MANDOCERR_BADCHAR, curp,
+                                               curp->line, pos, NULL);
+                               i += 2;
+                               ln.buf[pos++] = '?';
+                               continue;
+                       }
+
+                       /* Some other escape sequence, copy & cont. */
 
                        ln.buf[pos++] = blk.buf[i++];
                        ln.buf[pos++] = blk.buf[i++];
@@ -467,6 +492,12 @@ rerun:
                        assert(MANDOCLEVEL_FATAL <= curp->file_status);
                        break;
                case (ROFF_SO):
+                       if (0 == (MPARSE_SO & curp->options) &&
+                           (i >= (int)blk.sz || '\0' == blk.buf[i])) {
+                               curp->sodest = mandoc_strdup(ln.buf + of);
+                               free(ln.buf);
+                               return;
+                       }
                        /*
                         * We remove `so' clauses from our lookaside
                         * buffer because we're going to descend into
@@ -474,7 +505,7 @@ rerun:
                         */
                        if (curp->secondary) 
                                curp->secondary->sz -= pos + 1;
-                       mparse_readfd_r(curp, -1, ln.buf + of, 1);
+                       mparse_readfd(curp, -1, ln.buf + of);
                        if (MANDOCLEVEL_FATAL <= curp->file_status)
                                break;
                        pos = 0;
@@ -538,7 +569,8 @@ rerun:
                if (0 == rc) {
                        assert(MANDOCLEVEL_FATAL <= curp->file_status);
                        break;
-               }
+               } else if (2 == rc)
+                       break;
 
                /* Temporary buffers typically are not full. */
 
@@ -554,7 +586,8 @@ rerun:
 }
 
 static int
-read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
+read_whole_file(struct mparse *curp, const char *file, int fd,
+               struct buf *fb, int *with_mmap)
 {
        size_t           off;
        ssize_t          ssz;
@@ -562,7 +595,10 @@ read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
 #ifdef HAVE_MMAP
        struct stat      st;
        if (-1 == fstat(fd, &st)) {
-               perror(file);
+               curp->file_status = MANDOCLEVEL_SYSERR;
+               if (curp->mmsg)
+                       (*curp->mmsg)(MANDOCERR_SYSSTAT, curp->file_status,
+                           file, 0, 0, strerror(errno));
                return(0);
        }
 
@@ -575,13 +611,15 @@ read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
 
        if (S_ISREG(st.st_mode)) {
                if (st.st_size >= (1U << 31)) {
-                       fprintf(stderr, "%s: input too large\n", file);
+                       curp->file_status = MANDOCLEVEL_FATAL;
+                       if (curp->mmsg)
+                               (*curp->mmsg)(MANDOCERR_TOOLARGE,
+                                   curp->file_status, file, 0, 0, NULL);
                        return(0);
                }
                *with_mmap = 1;
                fb->sz = (size_t)st.st_size;
-               fb->buf = mmap(NULL, fb->sz, PROT_READ, 
-                               MAP_FILE|MAP_SHARED, fd, 0);
+               fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0);
                if (fb->buf != MAP_FAILED)
                        return(1);
        }
@@ -599,7 +637,11 @@ read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
        for (;;) {
                if (off == fb->sz) {
                        if (fb->sz == (1U << 31)) {
-                               fprintf(stderr, "%s: input too large\n", file);
+                               curp->file_status = MANDOCLEVEL_FATAL;
+                               if (curp->mmsg)
+                                       (*curp->mmsg)(MANDOCERR_TOOLARGE,
+                                           curp->file_status,
+                                           file, 0, 0, NULL);
                                break;
                        }
                        resize_buf(fb, 65536);
@@ -610,7 +652,11 @@ read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
                        return(1);
                }
                if (ssz == -1) {
-                       perror(file);
+                       curp->file_status = MANDOCLEVEL_SYSERR;
+                       if (curp->mmsg)
+                               (*curp->mmsg)(MANDOCERR_SYSREAD,
+                                   curp->file_status, file, 0, 0,
+                                   strerror(errno));
                        break;
                }
                off += (size_t)ssz;
@@ -638,7 +684,7 @@ mparse_end(struct mparse *curp)
                return;
        }
 
-       if ( ! (curp->man || curp->mdoc)) {
+       if ( ! (curp->mdoc || curp->man || curp->sodest)) {
                mandoc_msg(MANDOCERR_NOTMANUAL, curp, 1, 0, NULL);
                curp->file_status = MANDOCLEVEL_FATAL;
                return;
@@ -648,19 +694,25 @@ mparse_end(struct mparse *curp)
 }
 
 static void
-mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file,
-               int re)
+mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file)
 {
        const char      *svfile;
+       static int       recursion_depth;
+
+       if (64 < recursion_depth) {
+               mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, 0, NULL);
+               return;
+       }
 
        /* Line number is per-file. */
        svfile = curp->file;
        curp->file = file;
        curp->line = 1;
+       recursion_depth++;
 
        mparse_buf_r(curp, blk, 1);
 
-       if (0 == re && MANDOCLEVEL_FATAL > curp->file_status)
+       if (0 == --recursion_depth && MANDOCLEVEL_FATAL > curp->file_status)
                mparse_end(curp);
 
        curp->file = svfile;
@@ -675,22 +727,25 @@ mparse_readmem(struct mparse *curp, const void *buf, size_t len,
        blk.buf = UNCONST(buf);
        blk.sz = len;
 
-       mparse_parse_buffer(curp, blk, file, 0);
+       mparse_parse_buffer(curp, blk, file);
        return(curp->file_status);
 }
 
-static void
-mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re)
+enum mandoclevel
+mparse_readfd(struct mparse *curp, int fd, const char *file)
 {
        struct buf       blk;
        int              with_mmap;
 
-       if (-1 == fd)
-               if (-1 == (fd = open(file, O_RDONLY, 0))) {
-                       perror(file);
-                       curp->file_status = MANDOCLEVEL_SYSERR;
-                       return;
-               }
+       if (-1 == fd && -1 == (fd = open(file, O_RDONLY, 0))) {
+               curp->file_status = MANDOCLEVEL_SYSERR;
+               if (curp->mmsg)
+                       (*curp->mmsg)(MANDOCERR_SYSOPEN,
+                           curp->file_status,
+                           file, 0, 0, strerror(errno));
+               goto out;
+       }
+
        /*
         * Run for each opened file; may be called more than once for
         * each full parse sequence if the opened file is nested (i.e.,
@@ -698,12 +753,10 @@ mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re)
         * the parse phase for the file.
         */
 
-       if ( ! read_whole_file(file, fd, &blk, &with_mmap)) {
-               curp->file_status = MANDOCLEVEL_SYSERR;
-               return;
-       }
+       if ( ! read_whole_file(curp, file, fd, &blk, &with_mmap))
+               goto out;
 
-       mparse_parse_buffer(curp, blk, file, re);
+       mparse_parse_buffer(curp, blk, file);
 
 #ifdef HAVE_MMAP
        if (with_mmap)
@@ -714,19 +767,13 @@ mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re)
 
        if (STDIN_FILENO != fd && -1 == close(fd))
                perror(file);
-}
-
-enum mandoclevel
-mparse_readfd(struct mparse *curp, int fd, const char *file)
-{
-
-       mparse_readfd_r(curp, fd, file, 0);
+out:
        return(curp->file_status);
 }
 
 struct mparse *
-mparse_alloc(enum mparset inttype, enum mandoclevel wlevel,
-               mandocmsg mmsg, void *arg, char *defos)
+mparse_alloc(int options, enum mandoclevel wlevel,
+               mandocmsg mmsg, char *defos)
 {
        struct mparse   *curp;
 
@@ -734,13 +781,12 @@ mparse_alloc(enum mparset inttype, enum mandoclevel wlevel,
 
        curp = mandoc_calloc(1, sizeof(struct mparse));
 
+       curp->options = options;
        curp->wlevel = wlevel;
        curp->mmsg = mmsg;
-       curp->arg = arg;
-       curp->inttype = inttype;
        curp->defos = defos;
 
-       curp->roff = roff_alloc(inttype, curp);
+       curp->roff = roff_alloc(curp, options);
        return(curp);
 }
 
@@ -760,6 +806,9 @@ mparse_reset(struct mparse *curp)
        curp->file_status = MANDOCLEVEL_OK;
        curp->mdoc = NULL;
        curp->man = NULL;
+
+       free(curp->sodest);
+       curp->sodest = NULL;
 }
 
 void
@@ -776,13 +825,20 @@ mparse_free(struct mparse *curp)
                free(curp->secondary->buf);
 
        free(curp->secondary);
+       free(curp->sodest);
        free(curp);
 }
 
 void
-mparse_result(struct mparse *curp, struct mdoc **mdoc, struct man **man)
+mparse_result(struct mparse *curp,
+       struct mdoc **mdoc, struct man **man, char **sodest)
 {
 
+       if (sodest && NULL != (*sodest = curp->sodest)) {
+               *mdoc = NULL;
+               *man = NULL;
+               return;
+       }
        if (mdoc)
                *mdoc = curp->mdoc;
        if (man)