[BACK]Return to mdocml.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Annotation of mandoc/mdocml.c, Revision 1.54

1.54    ! kristaps    1:        /* $Id: mdocml.c,v 1.53 2009/02/20 23:35:36 kristaps Exp $ */
1.1       kristaps    2: /*
                      3:  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the
                      7:  * above copyright notice and this permission notice appear in all
                      8:  * copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
                     11:  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
                     12:  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
                     13:  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
                     14:  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
                     15:  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
                     16:  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
                     17:  * PERFORMANCE OF THIS SOFTWARE.
                     18:  */
1.21      kristaps   19: #include <sys/stat.h>
1.1       kristaps   20: #include <sys/param.h>
                     21:
                     22: #include <assert.h>
1.21      kristaps   23: #include <fcntl.h>
1.1       kristaps   24: #include <err.h>
                     25: #include <getopt.h>
                     26: #include <stdio.h>
                     27: #include <stdlib.h>
                     28: #include <string.h>
                     29: #include <unistd.h>
                     30:
1.21      kristaps   31: #include "mdoc.h"
1.1       kristaps   32:
1.44      kristaps   33: #define        MD_LINE_SZ      (256)           /* Max input line size. */
1.2       kristaps   34:
1.54    ! kristaps   35: typedef        int             (*md_print)(const struct mdoc_node *,
        !            36:                                const struct mdoc_meta *);
        !            37:
1.21      kristaps   38: struct md_parse {
1.47      kristaps   39:        int               warn;         /* Warning flags. */
                     40: #define        MD_WARN_SYNTAX   (1 << 0)       /* Show syntax warnings. */
                     41: #define        MD_WARN_COMPAT   (1 << 1)       /* Show compat warnings. */
                     42: #define        MD_WARN_ALL      (0x03)         /* Show all warnings. */
                     43: #define        MD_WARN_ERR      (1 << 2)       /* Make warnings->errors. */
                     44:        int               dbg;          /* Debug level. */
                     45:        struct mdoc      *mdoc;         /* Active parser. */
                     46:        char             *buf;          /* Input buffer. */
                     47:        u_long            bufsz;        /* Input buffer size. */
1.51      kristaps   48:        char             *in;           /* Input file name. */
                     49:        int               fdin;         /* Input file desc. */
1.54    ! kristaps   50:        md_print          fp;
1.21      kristaps   51: };
1.17      kristaps   52:
1.47      kristaps   53: extern char             *__progname;
                     54:
1.51      kristaps   55: static void              usage(void);
1.44      kristaps   56:
1.51      kristaps   57: static int               parse_opts(struct md_parse *, int, char *[]);
                     58: static int               parse_subopts(struct md_parse *, char *);
1.9       kristaps   59:
1.47      kristaps   60: static int               parse_begin(struct md_parse *);
                     61: static int               parse_leave(struct md_parse *, int);
                     62: static int               io_begin(struct md_parse *);
                     63: static int               io_leave(struct md_parse *, int);
                     64: static int               buf_begin(struct md_parse *);
                     65: static int               buf_leave(struct md_parse *, int);
                     66:
                     67: static void              msg_msg(void *, int, int, const char *);
                     68: static int               msg_err(void *, int, int, const char *);
                     69: static int               msg_warn(void *, int, int,
1.44      kristaps   70:                                enum mdoc_warn, const char *);
1.1       kristaps   71:
1.19      kristaps   72: #ifdef __linux__
1.47      kristaps   73: extern int               getsubopt(char **, char *const *, char **);
1.19      kristaps   74: #endif
                     75:
1.1       kristaps   76: int
                     77: main(int argc, char *argv[])
                     78: {
1.21      kristaps   79:        struct md_parse  parser;
1.47      kristaps   80:
1.21      kristaps   81:        (void)memset(&parser, 0, sizeof(struct md_parse));
1.17      kristaps   82:
1.51      kristaps   83:        if ( ! parse_opts(&parser, argc, argv))
                     84:                return(EXIT_FAILURE);
1.21      kristaps   85:        if ( ! io_begin(&parser))
                     86:                return(EXIT_FAILURE);
1.1       kristaps   87:
1.21      kristaps   88:        return(EXIT_SUCCESS);
1.1       kristaps   89: }
                     90:
                     91:
                     92: static int
1.21      kristaps   93: io_leave(struct md_parse *p, int code)
1.1       kristaps   94: {
                     95:
1.51      kristaps   96:        if (-1 == p->fdin || STDIN_FILENO == p->fdin)
1.21      kristaps   97:                return(code);
                     98:
1.51      kristaps   99:        if (-1 == close(p->fdin)) {
                    100:                warn("%s", p->in);
1.21      kristaps  101:                code = 0;
1.4       kristaps  102:        }
1.21      kristaps  103:        return(code);
                    104: }
                    105:
                    106:
                    107: static int
1.51      kristaps  108: parse_subopts(struct md_parse *p, char *arg)
                    109: {
                    110:        char            *v;
                    111:        char            *toks[] = { "all", "compat",
                    112:                                "syntax", "error", NULL };
                    113:
                    114:        /*
                    115:         * Future -Wxxx levels and so on should be here.  For now we
                    116:         * only recognise syntax and compat warnings as categories,
                    117:         * beyond the usually "all" and "error" (make warn error out).
                    118:         */
                    119:
                    120:        while (*arg)
                    121:                switch (getsubopt(&arg, toks, &v)) {
                    122:                case (0):
                    123:                        p->warn |= MD_WARN_ALL;
                    124:                        break;
                    125:                case (1):
                    126:                        p->warn |= MD_WARN_COMPAT;
                    127:                        break;
                    128:                case (2):
                    129:                        p->warn |= MD_WARN_SYNTAX;
                    130:                        break;
                    131:                case (3):
                    132:                        p->warn |= MD_WARN_ERR;
                    133:                        break;
                    134:                default:
                    135:                        usage();
                    136:                        return(0);
                    137:                }
                    138:
                    139:        return(1);
                    140: }
                    141:
                    142:
                    143: static int
                    144: parse_opts(struct md_parse *p, int argc, char *argv[])
                    145: {
                    146:        int              c;
                    147:
                    148:        extern char     *optarg;
                    149:        extern int       optind;
                    150:
1.54    ! kristaps  151:        extern int termprint(const struct mdoc_node *,
        !           152:                        const struct mdoc_meta *);
        !           153:        extern int treeprint(const struct mdoc_node *,
        !           154:                        const struct mdoc_meta *);
        !           155:
1.51      kristaps  156:        p->in = "-";
                    157:
1.54    ! kristaps  158:        while (-1 != (c = getopt(argc, argv, "f:vW:")))
1.51      kristaps  159:                switch (c) {
1.54    ! kristaps  160:                case ('f'):
        !           161:                        if (0 == strcmp(optarg, "tree")) {
        !           162:                                p->fp = treeprint;
        !           163:                                break;
        !           164:                        } else if (0 == strcmp(optarg, "term")) {
        !           165:                                p->fp = termprint;
        !           166:                                break;
        !           167:                        }
        !           168:                        warnx("unknown filter: %s", optarg);
        !           169:                        return(0);
1.51      kristaps  170:                case ('v'):
                    171:                        p->dbg++;
                    172:                        break;
                    173:                case ('W'):
                    174:                        if ( ! parse_subopts(p, optarg))
                    175:                                return(0);
                    176:                        break;
                    177:                default:
                    178:                        usage();
                    179:                        return(0);
                    180:                }
                    181:
                    182:        argv += optind;
                    183:        if (0 == (argc -= optind))
                    184:                return(1);
                    185:
                    186:        p->in = *argv++;
                    187:        return(1);
                    188: }
                    189:
                    190:
                    191: static int
1.21      kristaps  192: io_begin(struct md_parse *p)
                    193: {
                    194:
1.51      kristaps  195:        p->fdin = STDIN_FILENO;
                    196:        if (0 != strncmp(p->in, "-", 1))
                    197:                if (-1 == (p->fdin = open(p->in, O_RDONLY, 0))) {
                    198:                        warn("%s", p->in);
1.21      kristaps  199:                        return(io_leave(p, 0));
                    200:                }
1.1       kristaps  201:
1.21      kristaps  202:        return(io_leave(p, buf_begin(p)));
1.1       kristaps  203: }
                    204:
                    205:
                    206: static int
1.21      kristaps  207: buf_leave(struct md_parse *p, int code)
1.1       kristaps  208: {
1.4       kristaps  209:
1.21      kristaps  210:        if (p->buf)
                    211:                free(p->buf);
                    212:        return(code);
                    213: }
1.1       kristaps  214:
                    215:
1.21      kristaps  216: static int
                    217: buf_begin(struct md_parse *p)
                    218: {
                    219:        struct stat      st;
1.1       kristaps  220:
1.51      kristaps  221:        if (-1 == fstat(p->fdin, &st)) {
                    222:                warn("%s", p->in);
                    223:                return(0);
1.21      kristaps  224:        }
                    225:
1.51      kristaps  226:        /*
                    227:         * Try to intuit the fastest way of sucking down buffered data
                    228:         * by using either the block buffer size or the hard-coded one.
                    229:         * This is inspired by bin/cat.c.
                    230:         */
                    231:
1.21      kristaps  232:        p->bufsz = MAX(st.st_blksize, BUFSIZ);
                    233:
                    234:        if (NULL == (p->buf = malloc(p->bufsz))) {
                    235:                warn("malloc");
                    236:                return(buf_leave(p, 0));
                    237:        }
                    238:
                    239:        return(buf_leave(p, parse_begin(p)));
                    240: }
                    241:
                    242:
                    243: static int
                    244: parse_leave(struct md_parse *p, int code)
                    245: {
1.54    ! kristaps  246:        md_print         fp;
1.21      kristaps  247:
1.36      kristaps  248:        if (NULL == p->mdoc)
                    249:                return(code);
                    250:
                    251:        if ( ! mdoc_endparse(p->mdoc))
                    252:                code = 0;
1.51      kristaps  253:
1.54    ! kristaps  254:        if (code && (fp = p->fp)) {
        !           255:                if ( ! (*fp)(mdoc_node(p->mdoc), mdoc_meta(p->mdoc)))
        !           256:                        code = 0;
        !           257:        }
1.36      kristaps  258:
1.38      kristaps  259:        mdoc_free(p->mdoc);
1.21      kristaps  260:        return(code);
                    261: }
                    262:
                    263:
                    264: static int
                    265: parse_begin(struct md_parse *p)
                    266: {
                    267:        ssize_t          sz, i;
                    268:        size_t           pos;
1.44      kristaps  269:        char             line[MD_LINE_SZ];
1.21      kristaps  270:        struct mdoc_cb   cb;
1.43      kristaps  271:        int              lnn;
1.21      kristaps  272:
                    273:        cb.mdoc_err = msg_err;
                    274:        cb.mdoc_warn = msg_warn;
                    275:        cb.mdoc_msg = msg_msg;
                    276:
                    277:        if (NULL == (p->mdoc = mdoc_alloc(p, &cb)))
                    278:                return(parse_leave(p, 0));
                    279:
1.51      kristaps  280:        /*
                    281:         * This is a little more complicated than fgets.  TODO: have
                    282:         * some benchmarks that show it's faster (note that I want to
                    283:         * check many, many manuals simultaneously, so speed is
                    284:         * important).  Fill a buffer (sized to the block size) with a
                    285:         * single read, then parse \n-terminated lines into a line
                    286:         * buffer, which is passed to the parser.  Hard-code the line
                    287:         * buffer to a particular size -- a reasonable assumption.
                    288:         */
                    289:
1.43      kristaps  290:        for (lnn = 1, pos = 0; ; ) {
1.51      kristaps  291:                if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) {
                    292:                        warn("%s", p->in);
1.21      kristaps  293:                        return(parse_leave(p, 0));
                    294:                } else if (0 == sz)
                    295:                        break;
                    296:
                    297:                for (i = 0; i < sz; i++) {
                    298:                        if ('\n' != p->buf[i]) {
                    299:                                if (pos < sizeof(line)) {
1.44      kristaps  300:                                        line[(int)pos++] = p->buf[(int)i];
1.21      kristaps  301:                                        continue;
                    302:                                }
1.51      kristaps  303:                                warnx("%s: line %d too long", p->in, lnn);
1.21      kristaps  304:                                return(parse_leave(p, 0));
                    305:                        }
                    306:
1.44      kristaps  307:                        line[(int)pos] = 0;
1.43      kristaps  308:                        if ( ! mdoc_parseln(p->mdoc, lnn, line))
1.21      kristaps  309:                                return(parse_leave(p, 0));
1.1       kristaps  310:
1.43      kristaps  311:                        lnn++;
1.21      kristaps  312:                        pos = 0;
1.4       kristaps  313:                }
1.21      kristaps  314:        }
1.1       kristaps  315:
1.21      kristaps  316:        return(parse_leave(p, 1));
1.4       kristaps  317: }
1.1       kristaps  318:
                    319:
1.4       kristaps  320: static int
1.44      kristaps  321: msg_err(void *arg, int line, int col, const char *msg)
1.21      kristaps  322: {
                    323:        struct md_parse  *p;
                    324:
                    325:        p = (struct md_parse *)arg;
                    326:
1.51      kristaps  327:        warnx("%s:%d: error: %s (column %d)",
                    328:                        p->in, line, msg, col);
1.21      kristaps  329:        return(0);
                    330: }
                    331:
                    332:
                    333: static void
1.37      kristaps  334: msg_msg(void *arg, int line, int col, const char *msg)
1.4       kristaps  335: {
1.21      kristaps  336:        struct md_parse  *p;
                    337:
                    338:        p = (struct md_parse *)arg;
                    339:
1.44      kristaps  340:        if (0 == p->dbg)
1.21      kristaps  341:                return;
                    342:
1.51      kristaps  343:        warnx("%s:%d: debug: %s (column %d)",
                    344:                        p->in, line, msg, col);
1.1       kristaps  345: }
                    346:
                    347:
                    348: static int
1.44      kristaps  349: msg_warn(void *arg, int line, int col,
                    350:                enum mdoc_warn type, const char *msg)
1.1       kristaps  351: {
1.21      kristaps  352:        struct md_parse  *p;
1.1       kristaps  353:
1.21      kristaps  354:        p = (struct md_parse *)arg;
1.1       kristaps  355:
1.44      kristaps  356:        switch (type) {
                    357:        case (WARN_COMPAT):
                    358:                if (p->warn & MD_WARN_COMPAT)
                    359:                        break;
                    360:                return(1);
                    361:        case (WARN_SYNTAX):
                    362:                if (p->warn & MD_WARN_SYNTAX)
                    363:                        break;
1.1       kristaps  364:                return(1);
                    365:        }
                    366:
1.51      kristaps  367:        warnx("%s:%d: warning: %s (column %d)",
                    368:                        p->in, line, msg, col);
1.21      kristaps  369:
1.44      kristaps  370:        if ( ! (p->warn & MD_WARN_ERR))
                    371:                return(1);
1.21      kristaps  372:
1.51      kristaps  373:        warnx("%s: considering warnings as errors", __progname);
1.44      kristaps  374:        return(0);
1.1       kristaps  375: }
                    376:
                    377:
                    378: static void
                    379: usage(void)
                    380: {
                    381:
1.54    ! kristaps  382:        warnx("usage: %s [-ffilter] [-v] [-Wwarn...] [infile]",
        !           383:                        __progname);
1.1       kristaps  384: }
1.18      kristaps  385:

CVSweb