=================================================================== RCS file: /cvs/mandoc/main.c,v retrieving revision 1.2 retrieving revision 1.8 diff -u -p -r1.2 -r1.8 --- mandoc/main.c 2009/03/19 16:18:36 1.2 +++ mandoc/main.c 2009/03/22 19:01:11 1.8 @@ -1,4 +1,4 @@ -/* $Id: main.c,v 1.2 2009/03/19 16:18:36 kristaps Exp $ */ +/* $Id: main.c,v 1.8 2009/03/22 19:01:11 kristaps Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons * @@ -28,10 +28,26 @@ #include "mdoc.h" +#ifdef __linux__ +extern int getsubopt(char **, char * const *, char **); +# ifndef __dead +# define __dead __attribute__((__noreturn__)) +# endif +#endif + +struct buf { + char *buf; + size_t sz; +}; + +struct curparse { + const char *file; + int wflags; #define WARN_WALL 0x03 /* All-warnings mask. */ #define WARN_WCOMPAT (1 << 0) /* Compatibility warnings. */ #define WARN_WSYNTAX (1 << 1) /* Syntax warnings. */ #define WARN_WERR (1 << 2) /* Warnings->errors. */ +}; enum outt { OUTT_ASCII, @@ -61,28 +77,30 @@ static int woptions(int *, char *); static int merr(void *, int, int, const char *); static int mwarn(void *, int, int, enum mdoc_warn, const char *); -static int file(char **, size_t *, char **, size_t *, +static int file(struct buf *, struct buf *, const char *, struct mdoc *); -static int fdesc(char **, size_t *, char **, size_t *, +static int fdesc(struct buf *, struct buf *, const char *, int, struct mdoc *); int main(int argc, char *argv[]) { - int c, rc, fflags, wflags; + int c, rc, fflags; struct mdoc_cb cb; struct mdoc *mdoc; - char *buf, *line; - size_t bufsz, linesz; void *outdata; enum outt outtype; + struct buf ln, blk; out_run outrun; out_free outfree; + struct curparse curp; - fflags = wflags = 0; + fflags = 0; outtype = OUTT_ASCII; + bzero(&curp, sizeof(struct curparse)); + /* LINTED */ while (-1 != (c = getopt(argc, argv, "f:VW:T:"))) switch (c) { @@ -95,7 +113,7 @@ main(int argc, char *argv[]) return(0); break; case ('W'): - if ( ! woptions(&wflags, optarg)) + if ( ! woptions(&curp.wflags, optarg)) return(0); break; case ('V'): @@ -153,28 +171,42 @@ main(int argc, char *argv[]) cb.mdoc_err = merr; cb.mdoc_warn = mwarn; - buf = line = NULL; - bufsz = linesz = 0; + bzero(&ln, sizeof(struct buf)); + bzero(&blk, sizeof(struct buf)); - mdoc = mdoc_alloc(&wflags, fflags, &cb); + mdoc = mdoc_alloc(&curp, fflags, &cb); - while (*argv) { - if ( ! file(&line, &linesz, &buf, &bufsz, *argv, mdoc)) - break; - if (outrun && ! (*outrun)(outdata, mdoc)) - break; + /* + * Loop around available files. + */ - /* Reset the parser for another file. */ - mdoc_reset(mdoc); - argv++; + if (NULL == *argv) { + curp.file = ""; + c = fdesc(&blk, &ln, "stdin", STDIN_FILENO, mdoc); + rc = 0; + if (c && NULL == outrun) + rc = 1; + else if (c && outrun && (*outrun)(outdata, mdoc)) + rc = 1; + } else { + while (*argv) { + curp.file = *argv; + c = file(&blk, &ln, *argv, mdoc); + if ( ! c) + break; + if (outrun && ! (*outrun)(outdata, mdoc)) + break; + /* Reset the parser for another file. */ + mdoc_reset(mdoc); + argv++; + } + rc = NULL == *argv; } - rc = NULL == *argv; - - if (buf) - free(buf); - if (line) - free(line); + if (blk.buf) + free(blk.buf); + if (ln.buf) + free(ln.buf); if (outfree) (*outfree)(outdata); @@ -205,7 +237,7 @@ usage(void) static int -file(char **ln, size_t *lnsz, char **buf, size_t *bufsz, +file(struct buf *blk, struct buf *ln, const char *file, struct mdoc *mdoc) { int fd, c; @@ -215,7 +247,7 @@ file(char **ln, size_t *lnsz, char **buf, size_t *bufs return(0); } - c = fdesc(ln, lnsz, buf, bufsz, file, fd, mdoc); + c = fdesc(blk, ln, file, fd, mdoc); if (-1 == close(fd)) warn("%s", file); @@ -225,68 +257,129 @@ file(char **ln, size_t *lnsz, char **buf, size_t *bufs static int -fdesc(char **lnp, size_t *lnsz, char **bufp, size_t *bufsz, +fdesc(struct buf *blk, struct buf *ln, const char *f, int fd, struct mdoc *mdoc) { size_t sz; ssize_t ssz; struct stat st; int j, i, pos, lnn; - char *ln, *buf; +#ifdef STRIP_XO + int macro, xo, xeoln; +#endif - buf = *bufp; - ln = *lnp; - /* * Two buffers: ln and buf. buf is the input buffer, optimised * for each file's block size. ln is a line buffer. Both * growable, hence passed in by ptr-ptr. */ - if (-1 == fstat(fd, &st)) { + sz = BUFSIZ; + + if (-1 == fstat(fd, &st)) warnx("%s", f); - sz = BUFSIZ; - } else - sz = (unsigned)BUFSIZ > st.st_blksize ? - (size_t)BUFSIZ : st.st_blksize; + else if ((size_t)st.st_blksize > sz) + sz = st.st_blksize; - if (sz > *bufsz) { - if (NULL == (buf = realloc(buf, sz))) + if (sz > blk->sz) { + blk->buf = realloc(blk->buf, sz); + if (NULL == blk->buf) err(1, "realloc"); - *bufp = buf; - *bufsz = sz; + blk->sz = sz; } /* * Fill buf with file blocksize and parse newlines into ln. */ +#ifdef STRIP_XO + macro = xo = xeoln = 0; +#endif for (lnn = 1, pos = 0; ; ) { - if (-1 == (ssz = read(fd, buf, sz))) { + if (-1 == (ssz = read(fd, blk->buf, sz))) { warn("%s", f); return(0); } else if (0 == ssz) break; for (i = 0; i < (int)ssz; i++) { - if (pos >= (int)*lnsz) { - *lnsz += 256; /* Step-size. */ - ln = realloc(ln, *lnsz); - if (NULL == ln) + if (pos >= (int)ln->sz) { + ln->sz += 256; /* Step-size. */ + ln->buf = realloc(ln->buf, ln->sz); + if (NULL == ln->buf) err(1, "realloc"); - *lnp = ln; } - if ('\n' != buf[i]) { - ln[pos++] = buf[i]; + if ('\n' != blk->buf[i]) { + /* + * Ugly of uglies. Here we handle the + * dreaded `Xo/Xc' scoping. Cover the + * eyes of any nearby children. This + * makes `Xo/Xc' enclosures look like + * one huge line. + */ +#ifdef STRIP_XO + /* + * First, note whether we're in a macro + * line. + */ + if (0 == pos && '.' == blk->buf[i]) + macro = 1; + + /* + * If we're in an `Xo' context and just + * nixed a newline, remove the control + * character for new macro lines: + * they're going to show up as all part + * of the same line. + */ + if (xo && xeoln && '.' == blk->buf[i]) { + xeoln = 0; + continue; + } + xeoln = 0; + + /* + * If we've parsed `Xo', enter an xo + * context. `Xo' must be in a parsable + * state. This is the ugly part. IT IS + * NOT SMART ENOUGH TO HANDLE ESCAPED + * WHITESPACE. + */ + if (macro && pos && 'o' == blk->buf[i]) { + if (xo && 'X' == ln->buf[pos - 1]) { + if (' ' == ln->buf[pos - 2]) + xo++; + } else if ('X' == ln->buf[pos - 1]) { + if (2 == pos && '.' == ln->buf[pos - 2]) + xo++; + else if (' ' == ln->buf[pos - 2]) + xo++; + } + } + + /* + * If we're parsed `Xc', leave an xo + * context if one's already pending. + * `Xc' must be in a parsable state. + * THIS IS NOT SMART ENOUGH TO HANDLE + * ESCAPED WHITESPACE. + */ + if (macro && pos && 'c' == blk->buf[i]) + if (xo && 'X' == ln->buf[pos - 1]) + if (' ' == ln->buf[pos - 2]) + xo--; +#endif /* STRIP_XO */ + + ln->buf[pos++] = blk->buf[i]; continue; } /* Check for CPP-escaped newline. */ - if (pos > 0 && '\\' == ln[pos - 1]) { + if (pos > 0 && '\\' == ln->buf[pos - 1]) { for (j = pos - 1; j >= 0; j--) - if ('\\' != ln[j]) + if ('\\' != ln->buf[j]) break; if ( ! ((pos - j) % 2)) { @@ -296,11 +389,25 @@ fdesc(char **lnp, size_t *lnsz, char **bufp, size_t *b } } - ln[pos] = 0; - if ( ! mdoc_parseln(mdoc, lnn, ln)) +#ifdef STRIP_XO + /* + * If we're in an xo context, put a space in + * place of the newline and continue parsing. + * Mark that we just did a newline. + */ + if (xo) { + xeoln = 1; + ln->buf[pos++] = ' '; + lnn++; + continue; + } +#endif /* STRIP_XO */ + + ln->buf[pos] = 0; + if ( ! mdoc_parseln(mdoc, lnn, ln->buf)) return(0); lnn++; - pos = 0; + macro = pos = 0; } } @@ -409,8 +516,12 @@ woptions(int *wflags, char *arg) static int merr(void *arg, int line, int col, const char *msg) { + struct curparse *curp; - warnx("error: %s (line %d, column %d)", msg, line, col); + curp = (struct curparse *)arg; + + warnx("%s:%d: error: %s (column %d)", + curp->file, line, msg, col); return(0); } @@ -419,30 +530,30 @@ static int mwarn(void *arg, int line, int col, enum mdoc_warn type, const char *msg) { - int flags; + struct curparse *curp; char *wtype; - flags = *(int *)arg; + curp = (struct curparse *)arg; wtype = NULL; switch (type) { case (WARN_COMPAT): wtype = "compat"; - if (flags & WARN_WCOMPAT) + if (curp->wflags & WARN_WCOMPAT) break; return(1); case (WARN_SYNTAX): wtype = "syntax"; - if (flags & WARN_WSYNTAX) + if (curp->wflags & WARN_WSYNTAX) break; return(1); } assert(wtype); - warnx("%s warning: %s (line %d, column %d)", - wtype, msg, line, col); + warnx("%s:%d: %s warning: %s (column %d)", + curp->file, line, wtype, msg, col); - if ( ! (flags & WARN_WERR)) + if ( ! (curp->wflags & WARN_WERR)) return(1); warnx("%s: considering warnings as errors",