]> git.cameronkatri.com Git - mandoc.git/blobdiff - main.c
Move check for closing punctuation into its own function. This will
[mandoc.git] / main.c
diff --git a/main.c b/main.c
index 4c7db9c8eaf847757276f0e66721427e6f0b09f0..496024e1c3ea94c5c10cb0b176ff830776a9b2aa 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,7 +1,7 @@
-/*     $Id: main.c,v 1.114 2010/12/01 17:00:05 kristaps Exp $ */
+/*     $Id: main.c,v 1.151 2011/03/16 15:28:35 kristaps Exp $ */
 /*
- * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -41,7 +41,7 @@
 #define        MAP_FILE        0
 #endif
 
-#define        UNCONST(a)      ((void *)(uintptr_t)(const void *)(a))
+#define        REPARSE_LIMIT   1000
 
 /* FIXME: Intel's compiler?  LLVM?  pcc?  */
 
@@ -77,11 +77,13 @@ enum        outt {
 };
 
 struct curparse {
-       const char       *file;         /* Current parse. */
-       int               fd;           /* Current parse. */
-       int               line;         /* Line number in the file. */
-       enum mandoclevel  wlevel;       /* Ignore messages below this. */
-       int               wstop;        /* Stop after a file with a warning. */
+       enum mandoclevel  exit_status;  /* status of all file parses */
+       const char       *file;         /* current file-name */
+       enum mandoclevel  file_status;  /* error status of current parse */
+       int               fd;           /* current file-descriptor */
+       int               line;         /* line number in the file */
+       enum mandoclevel  wlevel;       /* ignore messages below this */
+       int               wstop;        /* stop after a file with a warning */
        enum intt         inttype;      /* which parser to use */
        struct man       *pman;         /* persistent man parser */
        struct mdoc      *pmdoc;        /* persistent mdoc parser */
@@ -89,6 +91,7 @@ struct        curparse {
        struct mdoc      *mdoc;         /* mdoc parser */
        struct roff      *roff;         /* roff parser (!NULL) */
        struct regset     regs;         /* roff registers */
+       int               reparse_count; /* finite interpolation stack */
        enum outt         outtype;      /* which output to use */
        out_mdoc          outmdoc;      /* mdoc output ptr */
        out_man           outman;       /* man output ptr */
@@ -122,71 +125,98 @@ static    const char * const      mandocerrs[MANDOCERR_MAX] = {
 
        "generic warning",
 
+       /* related to the prologue */
+       "no title in document",
+       "document title should be all caps",
+       "unknown manual section",
+       "date missing, using today's date",
+       "cannot parse date, using it verbatim",
+       "prologue macros out of order",
+       "duplicate prologue macro",
+       "macro not allowed in prologue",
+       "macro not allowed in body",
+
+       /* related to document structure */
        ".so is fragile, better use ln(1)",
-       "text should be uppercase",
+       "NAME section must come first",
+       "bad NAME section contents",
+       "manual name not yet set",
        "sections out of conventional order",
-       "section name repeats",
-       "out of order prologue",
-       "repeated prologue entry",
-       "list type must come first",
-       "tab in non-literal context",
-       "bad escape sequence",
-       "unterminated quoted string",
-       "argument requires the width argument",
-       "superfluous width argument",
-       "bad date argument",
-       "bad width argument",
-       "unknown manual section",
+       "duplicate section name",
        "section not in conventional manual section",
-       "end of line whitespace",
-       "blocks badly nested",
 
-       "generic error",
+       /* related to macros and nesting */
+       "skipping obsolete macro",
+       "skipping paragraph macro",
+       "skipping no-space macro",
+       "blocks badly nested",
+       "child violates parent syntax",
+       "nested displays are not portable",
+       "already in literal mode",
 
-       "NAME section must come first",
+       /* related to missing macro arguments */
+       "skipping empty macro",
+       "argument count wrong",
+       "missing display type",
+       "list type must come first",
+       "tag lists require a width argument",
+       "missing font type",
+       "skipping end of block that is not open",
+
+       /* related to bad macro arguments */
+       "skipping argument",
+       "duplicate argument",
+       "duplicate display type",
+       "duplicate list type",
+       "unknown AT&T UNIX version",
        "bad Boolean value",
-       "child violates parent syntax",
-       "bad AT&T symbol",
-       "bad standard",
-       "list type repeated",
-       "display type repeated",
-       "argument repeated",
-       "ignoring argument",
-       "manual name not yet set",
-       "obsolete macro ignored",
-       "empty macro ignored",
-       "macro not allowed in body",
-       "macro not allowed in prologue",
-       "bad character",
-       "bad NAME section contents",
-       "no blank lines",
-       "no text in this context",
+       "unknown font",
+       "unknown standard specifier",
+       "bad width argument",
+
+       /* related to plain text */
+       "blank line in non-literal context",
+       "tab in non-literal context",
+       "end of line whitespace",
        "bad comment style",
-       "unknown macro will be lost",
-       "NOT IMPLEMENTED: skipping request",
+       "unknown escape sequence",
+       "unterminated quoted string",
+       
+       "generic error",
+
+       /* related to tables */
+       "bad table syntax",
+       "bad table option",
+       "bad table layout",
+       "no table layout cells specified",
+       "no table data cells specified",
+       "ignore data in cell",
+       "data block still open",
+       "ignoring extra data cells",
+
+       "input stack limit exceeded, infinite loop?",
+       "skipping bad character",
+       "escaped character not allowed in a name",
+       "skipping text before the first section header",
+       "skipping unknown macro",
+       "NOT IMPLEMENTED, please use groff: skipping request",
        "line scope broken",
        "argument count wrong",
-       "request scope close w/none open",
-       "scope already open",
+       "skipping end of block that is not open",
+       "missing end of block",
        "scope open on exit",
+       "uname(3) system call failed",
        "macro requires line argument(s)",
        "macro requires body argument(s)",
        "macro requires argument(s)",
-       "no title in document",
        "missing list type",
-       "missing display type",
-       "missing font type",
        "line argument(s) will be lost",
        "body argument(s) will be lost",
-       "paragraph macro ignored",
 
        "generic fatal error",
 
        "column syntax is inconsistent",
-       "displays may not be nested",
-       "unsupported display type",
-       "blocks badly nested",
-       "no such block is open",
+       "NOT IMPLEMENTED: .Bd -file",
        "line scope broken, syntax violated",
        "argument count wrong, violates syntax",
        "child violates parent syntax",
@@ -194,7 +224,6 @@ static      const char * const      mandocerrs[MANDOCERR_MAX] = {
        "NOT IMPLEMENTED: .so with absolute path or \"..\"",
        "no document body",
        "no document prologue",
-       "utsname system call failed",
        "static buffer exhausted",
 };
 
@@ -213,7 +242,6 @@ static      void              version(void) __attribute__((noreturn));
 static int               woptions(struct curparse *, char *);
 
 static const char       *progname;
-static enum mandoclevel  exit_status = MANDOCLEVEL_OK;
 
 int
 main(int argc, char *argv[])
@@ -232,6 +260,7 @@ main(int argc, char *argv[])
        curp.inttype = INTT_AUTO;
        curp.outtype = OUTT_ASCII;
        curp.wlevel  = MANDOCLEVEL_FATAL;
+       curp.exit_status = MANDOCLEVEL_OK;
 
        /* LINTED */
        while (-1 != (c = getopt(argc, argv, "m:O:T:VW:")))
@@ -272,7 +301,7 @@ main(int argc, char *argv[])
 
        while (*argv) {
                ffile(*argv, &curp);
-               if (MANDOCLEVEL_OK != exit_status && curp.wstop)
+               if (MANDOCLEVEL_OK != curp.exit_status && curp.wstop)
                        break;
                ++argv;
        }
@@ -286,7 +315,7 @@ main(int argc, char *argv[])
        if (curp.roff)
                roff_free(curp.roff);
 
-       return((int)exit_status);
+       return((int)curp.exit_status);
 }
 
 
@@ -331,7 +360,7 @@ ffile(const char *file, struct curparse *curp)
 
        if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
                perror(curp->file);
-               exit_status = MANDOCLEVEL_SYSERR;
+               curp->exit_status = MANDOCLEVEL_SYSERR;
                return;
        }
 
@@ -349,7 +378,7 @@ pfile(const char *file, struct curparse *curp)
 
        if (-1 == (fd = open(file, O_RDONLY, 0))) {
                perror(file);
-               exit_status = MANDOCLEVEL_SYSERR;
+               curp->file_status = MANDOCLEVEL_SYSERR;
                return(0);
        }
 
@@ -367,7 +396,7 @@ pfile(const char *file, struct curparse *curp)
        if (-1 == close(fd))
                perror(file);
 
-       return(MANDOCLEVEL_FATAL > exit_status ? 1 : 0);
+       return(MANDOCLEVEL_FATAL > curp->file_status ? 1 : 0);
 }
 
 
@@ -375,7 +404,7 @@ static void
 resize_buf(struct buf *buf, size_t initial)
 {
 
-       buf->sz = buf->sz ? 2 * buf->sz : initial;
+       buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial;
        buf->buf = realloc(buf->buf, buf->sz);
        if (NULL == buf->buf) {
                perror(NULL);
@@ -471,6 +500,7 @@ fdesc(struct curparse *curp)
 
        curp->mdoc = NULL;
        curp->man = NULL;
+       curp->file_status = MANDOCLEVEL_OK;
 
        /* Make sure the mandotory roff parser is initialised. */
 
@@ -483,41 +513,38 @@ fdesc(struct curparse *curp)
 
        pdesc(curp);
 
-       if (MANDOCLEVEL_FATAL <= exit_status)
+       if (MANDOCLEVEL_FATAL <= curp->file_status)
                goto cleanup;
 
        /* NOTE a parser may not have been assigned, yet. */
 
        if ( ! (curp->man || curp->mdoc)) {
                fprintf(stderr, "%s: Not a manual\n", curp->file);
-               exit_status = MANDOCLEVEL_FATAL;
+               curp->file_status = MANDOCLEVEL_FATAL;
                goto cleanup;
        }
 
        /* Clean up the parse routine ASTs. */
 
        if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) {
-               assert(MANDOCLEVEL_FATAL <= exit_status);
+               assert(MANDOCLEVEL_FATAL <= curp->file_status);
                goto cleanup;
        }
 
        if (curp->man && ! man_endparse(curp->man)) {
-               assert(MANDOCLEVEL_FATAL <= exit_status);
+               assert(MANDOCLEVEL_FATAL <= curp->file_status);
                goto cleanup;
        }
 
        assert(curp->roff);
-       if ( ! roff_endparse(curp->roff)) {
-               assert(MANDOCLEVEL_FATAL <= exit_status);
-               goto cleanup;
-       }
+       roff_endparse(curp->roff);
 
        /*
         * With -Wstop and warnings or errors of at least
         * the requested level, do not produce output.
         */
 
-       if (MANDOCLEVEL_OK != exit_status && curp->wstop)
+       if (MANDOCLEVEL_OK != curp->file_status && curp->wstop)
                goto cleanup;
 
        /* If unset, allocate output dev now (if applicable). */
@@ -592,6 +619,9 @@ fdesc(struct curparse *curp)
        assert(curp->roff);
        roff_reset(curp->roff);
 
+       if (curp->exit_status < curp->file_status)
+               curp->exit_status = curp->file_status;
+
        return;
 }
 
@@ -609,7 +639,7 @@ pdesc(struct curparse *curp)
         */
 
        if ( ! read_whole_file(curp, &blk, &with_mmap)) {
-               exit_status = MANDOCLEVEL_SYSERR;
+               curp->file_status = MANDOCLEVEL_SYSERR;
                return;
        }
 
@@ -625,9 +655,15 @@ pdesc(struct curparse *curp)
                free(blk.buf);
 }
 
+/*
+ * Main parse routine for an opened file.  This is called for each
+ * opened file and simply loops around the full input file, possibly
+ * nesting (i.e., with `so').
+ */
 static void
 parsebuf(struct curparse *curp, struct buf blk, int start)
 {
+       const struct tbl_span   *span;
        struct buf       ln;
        enum rofferr     rr;
        int              i, of, rc;
@@ -635,12 +671,6 @@ parsebuf(struct curparse *curp, struct buf blk, int start)
        int              lnn; /* line number in the real file */
        unsigned char    c;
 
-       /*
-        * Main parse routine for an opened file.  This is called for
-        * each opened file and simply loops around the full input file,
-        * possibly nesting (i.e., with `so').
-        */
-
        memset(&ln, 0, sizeof(struct buf));
 
        lnn = curp->line; 
@@ -650,10 +680,22 @@ parsebuf(struct curparse *curp, struct buf blk, int start)
                if (0 == pos && '\0' == blk.buf[i])
                        break;
 
-               if (start)
+               if (start) {
                        curp->line = lnn;
+                       curp->reparse_count = 0;
+               }
 
                while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) {
+
+                       /*
+                        * When finding an unescaped newline character,
+                        * leave the character loop to process the line.
+                        * Skip a preceding carriage return, if any.
+                        */
+
+                       if ('\r' == blk.buf[i] && i + 1 < (int)blk.sz &&
+                           '\n' == blk.buf[i + 1])
+                               ++i;
                        if ('\n' == blk.buf[i]) {
                                ++i;
                                ++lnn;
@@ -688,11 +730,18 @@ parsebuf(struct curparse *curp, struct buf blk, int start)
                                continue;
                        }
 
-                       /* Found escape & at least one other char. */
+                       /*
+                        * Found escape and at least one other character.
+                        * When it's a newline character, skip it.
+                        * When there is a carriage return in between,
+                        * skip that one as well.
+                        */
 
+                       if ('\r' == blk.buf[i + 1] && i + 2 < (int)blk.sz &&
+                           '\n' == blk.buf[i + 2])
+                               ++i;
                        if ('\n' == blk.buf[i + 1]) {
                                i += 2;
-                               /* Escaped newlines are skipped over */
                                ++lnn;
                                continue;
                        }
@@ -750,11 +799,15 @@ rerun:
 
                switch (rr) {
                case (ROFF_REPARSE):
-                       parsebuf(curp, ln, 0);
+                       if (REPARSE_LIMIT >= ++curp->reparse_count)
+                               parsebuf(curp, ln, 0);
+                       else
+                               mmsg(MANDOCERR_ROFFLOOP, curp, 
+                                   curp->line, pos, NULL);
                        pos = 0;
                        continue;
                case (ROFF_APPEND):
-                       pos = strlen(ln.buf);
+                       pos = (int)strlen(ln.buf);
                        continue;
                case (ROFF_RERUN):
                        goto rerun;
@@ -762,7 +815,7 @@ rerun:
                        pos = 0;
                        continue;
                case (ROFF_ERR):
-                       assert(MANDOCLEVEL_FATAL <= exit_status);
+                       assert(MANDOCLEVEL_FATAL <= curp->file_status);
                        break;
                case (ROFF_SO):
                        if (pfile(ln.buf + of, curp)) {
@@ -770,10 +823,18 @@ rerun:
                                continue;
                        } else
                                break;
-               case (ROFF_CONT):
+               default:
                        break;
                }
 
+               /*
+                * If we encounter errors in the recursive parsebuf()
+                * call, make sure we don't continue parsing.
+                */
+
+               if (MANDOCLEVEL_FATAL <= curp->file_status)
+                       break;
+
                /*
                 * If input parsers have not been allocated, do so now.
                 * We keep these instanced betwen parsers, but set them
@@ -788,19 +849,39 @@ rerun:
                 * Lastly, push down into the parsers themselves.  One
                 * of these will have already been set in the pset()
                 * routine.
+                * If libroff returns ROFF_TBL, then add it to the
+                * currently open parse.  Since we only get here if
+                * there does exist data (see tbl_data.c), we're
+                * guaranteed that something's been allocated.
+                * Do the same for ROFF_EQN.
                 */
 
-               if (curp->man || curp->mdoc) {
+               rc = -1;
+
+               if (ROFF_TBL == rr)
+                       while (NULL != (span = roff_span(curp->roff))) {
+                               rc = curp->man ?
+                                       man_addspan(curp->man, span) :
+                                       mdoc_addspan(curp->mdoc, span);
+                               if (0 == rc)
+                                       break;
+                       }
+               else if (ROFF_EQN == rr)
+                       rc = curp->mdoc ? 
+                               mdoc_addeqn(curp->mdoc, 
+                                       roff_eqn(curp->roff)) :
+                               man_addeqn(curp->man,
+                                       roff_eqn(curp->roff));
+               else if (curp->man || curp->mdoc)
                        rc = curp->man ?
                                man_parseln(curp->man, 
                                        curp->line, ln.buf, of) :
                                mdoc_parseln(curp->mdoc, 
                                        curp->line, ln.buf, of);
 
-                       if ( ! rc) {
-                               assert(MANDOCLEVEL_FATAL <= exit_status);
-                               break;
-                       }
+               if (0 == rc) {
+                       assert(MANDOCLEVEL_FATAL <= curp->file_status);
+                       break;
                }
 
                /* Temporary buffers typically are not full. */
@@ -980,8 +1061,8 @@ mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)
                fprintf(stderr, ": %s", msg);
        fputc('\n', stderr);
 
-       if (exit_status < level)
-               exit_status = level;
+       if (cp->file_status < level)
+               cp->file_status = level;
        
        return(level < MANDOCLEVEL_FATAL);
 }