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

Annotation of mandoc/mdocml.c, Revision 1.43

1.43    ! kristaps    1: /* $Id: mdocml.c,v 1.42 2009/01/14 11:58:24 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.21      kristaps   33: #define        MD_LINE_SZ      (256)
1.2       kristaps   34:
1.21      kristaps   35: struct md_parse {
                     36:        int              warn;
                     37: #define        MD_WARN_ALL     (1 << 0)
                     38: #define        MD_WARN_ERR     (1 << 1)
                     39:        int              dbg;
                     40:        struct mdoc     *mdoc;
                     41:        char            *buf;
                     42:        u_long           bufsz;
                     43:        char            *name;
                     44:        int              fd;
                     45: };
1.17      kristaps   46:
1.9       kristaps   47: static void             usage(void);
                     48:
1.21      kristaps   49: static int              parse_begin(struct md_parse *);
                     50: static int              parse_leave(struct md_parse *, int);
                     51: static int              io_begin(struct md_parse *);
                     52: static int              io_leave(struct md_parse *, int);
                     53: static int              buf_begin(struct md_parse *);
                     54: static int              buf_leave(struct md_parse *, int);
                     55:
                     56: static int              msg_err(void *, int, int, enum mdoc_err);
                     57: static int              msg_warn(void *, int, int, enum mdoc_warn);
1.37      kristaps   58: static void             msg_msg(void *, int, int, const char *);
1.1       kristaps   59:
1.19      kristaps   60: #ifdef __linux__
                     61: extern int              getsubopt(char **, char *const *, char **);
                     62: #endif
                     63:
1.1       kristaps   64: int
                     65: main(int argc, char *argv[])
                     66: {
                     67:        int              c;
1.21      kristaps   68:        struct md_parse  parser;
                     69:        char            *opts, *v;
1.18      kristaps   70: #define ALL             0
                     71: #define ERROR           1
                     72:        char            *toks[] = { "all", "error", NULL };
1.1       kristaps   73:
                     74:        extern char     *optarg;
                     75:        extern int       optind;
                     76:
1.21      kristaps   77:        (void)memset(&parser, 0, sizeof(struct md_parse));
1.17      kristaps   78:
1.21      kristaps   79:        while (-1 != (c = getopt(argc, argv, "vW:")))
1.1       kristaps   80:                switch (c) {
1.13      kristaps   81:                case ('v'):
1.21      kristaps   82:                        parser.dbg++;
1.13      kristaps   83:                        break;
                     84:                case ('W'):
1.18      kristaps   85:                        opts = optarg;
                     86:                        while (*opts)
                     87:                                switch (getsubopt(&opts, toks, &v)) {
                     88:                                case (ALL):
1.21      kristaps   89:                                        parser.warn |= MD_WARN_ALL;
1.18      kristaps   90:                                        break;
                     91:                                case (ERROR):
1.21      kristaps   92:                                        parser.warn |= MD_WARN_ERR;
1.18      kristaps   93:                                        break;
                     94:                                default:
                     95:                                        usage();
                     96:                                        return(1);
                     97:                                }
1.13      kristaps   98:                        break;
1.1       kristaps   99:                default:
                    100:                        usage();
                    101:                        return(1);
                    102:                }
                    103:
                    104:        argv += optind;
1.4       kristaps  105:        argc -= optind;
1.1       kristaps  106:
1.21      kristaps  107:        parser.name = "-";
1.4       kristaps  108:        if (1 == argc)
1.21      kristaps  109:                parser.name = *argv++;
                    110:
                    111:        if ( ! io_begin(&parser))
                    112:                return(EXIT_FAILURE);
1.1       kristaps  113:
1.21      kristaps  114:        return(EXIT_SUCCESS);
1.1       kristaps  115: }
                    116:
                    117:
                    118: static int
1.21      kristaps  119: io_leave(struct md_parse *p, int code)
1.1       kristaps  120: {
                    121:
1.21      kristaps  122:        if (-1 == p->fd || STDIN_FILENO == p->fd)
                    123:                return(code);
                    124:
                    125:        if (-1 == close(p->fd)) {
                    126:                warn("%s", p->name);
                    127:                code = 0;
1.4       kristaps  128:        }
1.21      kristaps  129:        return(code);
                    130: }
                    131:
                    132:
                    133: static int
                    134: io_begin(struct md_parse *p)
                    135: {
                    136:
                    137:        p->fd = STDIN_FILENO;
                    138:        if (0 != strncmp(p->name, "-", 1))
                    139:                if (-1 == (p->fd = open(p->name, O_RDONLY, 0))) {
                    140:                        warn("%s", p->name);
                    141:                        return(io_leave(p, 0));
                    142:                }
1.1       kristaps  143:
1.21      kristaps  144:        return(io_leave(p, buf_begin(p)));
1.1       kristaps  145: }
                    146:
                    147:
                    148: static int
1.21      kristaps  149: buf_leave(struct md_parse *p, int code)
1.1       kristaps  150: {
1.4       kristaps  151:
1.21      kristaps  152:        if (p->buf)
                    153:                free(p->buf);
                    154:        return(code);
                    155: }
1.1       kristaps  156:
                    157:
1.21      kristaps  158: static int
                    159: buf_begin(struct md_parse *p)
                    160: {
                    161:        struct stat      st;
1.1       kristaps  162:
1.21      kristaps  163:        if (-1 == fstat(p->fd, &st)) {
                    164:                warn("%s", p->name);
                    165:                return(1);
                    166:        }
                    167:
                    168:        p->bufsz = MAX(st.st_blksize, BUFSIZ);
                    169:
                    170:        if (NULL == (p->buf = malloc(p->bufsz))) {
                    171:                warn("malloc");
                    172:                return(buf_leave(p, 0));
                    173:        }
                    174:
                    175:        return(buf_leave(p, parse_begin(p)));
                    176: }
                    177:
                    178:
                    179: static void
                    180: print_node(const struct mdoc_node *n, int indent)
                    181: {
1.26      kristaps  182:        const char       *p, *t;
                    183:        int               i, j;
                    184:        size_t            argc, sz;
                    185:        char            **params;
                    186:        struct mdoc_arg  *argv;
1.24      kristaps  187:
                    188:        argv = NULL;
                    189:        argc = 0;
1.26      kristaps  190:        params = NULL;
                    191:        sz = 0;
1.21      kristaps  192:
1.42      kristaps  193:        t = mdoc_type2a(n->type);
                    194:
1.21      kristaps  195:        switch (n->type) {
                    196:        case (MDOC_TEXT):
                    197:                assert(NULL == n->child);
1.25      kristaps  198:                p = n->data.text.string;
1.21      kristaps  199:                break;
                    200:        case (MDOC_BODY):
1.40      kristaps  201:                p = mdoc_macronames[n->tok];
1.21      kristaps  202:                break;
                    203:        case (MDOC_HEAD):
1.40      kristaps  204:                p = mdoc_macronames[n->tok];
1.21      kristaps  205:                break;
1.34      kristaps  206:        case (MDOC_TAIL):
1.40      kristaps  207:                p = mdoc_macronames[n->tok];
1.34      kristaps  208:                break;
1.21      kristaps  209:        case (MDOC_ELEM):
1.40      kristaps  210:                p = mdoc_macronames[n->tok];
1.24      kristaps  211:                argv = n->data.elem.argv;
                    212:                argc = n->data.elem.argc;
1.21      kristaps  213:                break;
                    214:        case (MDOC_BLOCK):
1.40      kristaps  215:                p = mdoc_macronames[n->tok];
1.24      kristaps  216:                argv = n->data.block.argv;
                    217:                argc = n->data.block.argc;
1.21      kristaps  218:                break;
1.38      kristaps  219:        case (MDOC_ROOT):
                    220:                p = "root";
                    221:                break;
1.22      kristaps  222:        default:
                    223:                abort();
                    224:                /* NOTREACHED */
1.21      kristaps  225:        }
                    226:
                    227:        for (i = 0; i < indent; i++)
                    228:                (void)printf("    ");
1.24      kristaps  229:        (void)printf("%s (%s)", p, t);
                    230:
                    231:        for (i = 0; i < (int)argc; i++) {
                    232:                (void)printf(" -%s", mdoc_argnames[argv[i].arg]);
1.42      kristaps  233:                if (argv[i].sz > 0)
1.41      kristaps  234:                        (void)printf(" [");
1.24      kristaps  235:                for (j = 0; j < (int)argv[i].sz; j++)
1.41      kristaps  236:                        (void)printf(" [%s]", argv[i].value[j]);
1.42      kristaps  237:                if (argv[i].sz > 0)
1.41      kristaps  238:                        (void)printf(" ]");
1.24      kristaps  239:        }
                    240:
1.26      kristaps  241:        for (i = 0; i < (int)sz; i++)
1.41      kristaps  242:                (void)printf(" [%s]", params[i]);
1.26      kristaps  243:
1.39      kristaps  244:        (void)printf(" %d:%d\n", n->line, n->pos);
1.21      kristaps  245:
                    246:        if (n->child)
                    247:                print_node(n->child, indent + 1);
                    248:        if (n->next)
                    249:                print_node(n->next, indent);
                    250: }
1.1       kristaps  251:
                    252:
1.21      kristaps  253: static int
                    254: parse_leave(struct md_parse *p, int code)
                    255: {
                    256:        const struct mdoc_node *n;
                    257:
1.36      kristaps  258:        if (NULL == p->mdoc)
                    259:                return(code);
                    260:
                    261:        if ( ! mdoc_endparse(p->mdoc))
                    262:                code = 0;
                    263:        if ((n = mdoc_result(p->mdoc)))
                    264:                print_node(n, 0);
                    265:
1.38      kristaps  266:        mdoc_free(p->mdoc);
                    267:
1.21      kristaps  268:        return(code);
                    269: }
                    270:
                    271:
                    272: static int
                    273: parse_begin(struct md_parse *p)
                    274: {
                    275:        ssize_t          sz, i;
                    276:        size_t           pos;
                    277:        char             line[256], sv[256];
                    278:        struct mdoc_cb   cb;
1.43    ! kristaps  279:        int              lnn;
1.21      kristaps  280:
                    281:        cb.mdoc_err = msg_err;
                    282:        cb.mdoc_warn = msg_warn;
                    283:        cb.mdoc_msg = msg_msg;
                    284:
                    285:        if (NULL == (p->mdoc = mdoc_alloc(p, &cb)))
                    286:                return(parse_leave(p, 0));
                    287:
1.43    ! kristaps  288:        for (lnn = 1, pos = 0; ; ) {
1.21      kristaps  289:                if (-1 == (sz = read(p->fd, p->buf, p->bufsz))) {
                    290:                        warn("%s", p->name);
                    291:                        return(parse_leave(p, 0));
                    292:                } else if (0 == sz)
                    293:                        break;
                    294:
                    295:                for (i = 0; i < sz; i++) {
                    296:                        if ('\n' != p->buf[i]) {
                    297:                                if (pos < sizeof(line)) {
1.23      kristaps  298:                                        sv[(int)pos] = p->buf[(int)i];
                    299:                                        line[(int)pos++] =
                    300:                                                p->buf[(int)i];
1.21      kristaps  301:                                        continue;
                    302:                                }
                    303:                                warnx("%s: line %d too long",
1.43    ! kristaps  304:                                                p->name, lnn);
1.21      kristaps  305:                                return(parse_leave(p, 0));
                    306:                        }
                    307:
                    308:                        line[(int)pos] = sv[(int)pos] = 0;
1.43    ! kristaps  309:                        if ( ! mdoc_parseln(p->mdoc, lnn, line))
1.21      kristaps  310:                                return(parse_leave(p, 0));
1.1       kristaps  311:
1.43    ! kristaps  312:                        lnn++;
1.21      kristaps  313:                        pos = 0;
1.4       kristaps  314:                }
1.21      kristaps  315:        }
1.1       kristaps  316:
1.21      kristaps  317:        return(parse_leave(p, 1));
1.4       kristaps  318: }
1.1       kristaps  319:
                    320:
1.4       kristaps  321: static int
1.37      kristaps  322: msg_err(void *arg, int line, int col, enum mdoc_err type)
1.21      kristaps  323: {
1.37      kristaps  324:        char             *lit;
1.21      kristaps  325:        struct md_parse  *p;
                    326:
                    327:        p = (struct md_parse *)arg;
                    328:
1.37      kristaps  329:        lit = NULL;
1.21      kristaps  330:
                    331:        switch (type) {
1.35      kristaps  332:        case (ERR_SYNTAX_NOTEXT):
                    333:                lit = "syntax: context-free text disallowed";
                    334:                break;
1.21      kristaps  335:        case (ERR_SYNTAX_QUOTE):
1.24      kristaps  336:                lit = "syntax: disallowed argument quotation";
                    337:                break;
                    338:        case (ERR_SYNTAX_UNQUOTE):
1.21      kristaps  339:                lit = "syntax: unterminated quotation";
                    340:                break;
                    341:        case (ERR_SYNTAX_WS):
                    342:                lit = "syntax: whitespace in argument";
                    343:                break;
1.25      kristaps  344:        case (ERR_SYNTAX_ARGFORM):
1.37      kristaps  345:                lit = "syntax: macro arguments malformed";
1.23      kristaps  346:                break;
1.28      kristaps  347:        case (ERR_SYNTAX_NOPUNCT):
1.37      kristaps  348:                lit = "syntax: macro doesn't understand punctuation";
1.28      kristaps  349:                break;
1.25      kristaps  350:        case (ERR_SYNTAX_ARG):
1.37      kristaps  351:                lit = "syntax: unknown argument for macro";
1.24      kristaps  352:                break;
1.21      kristaps  353:        case (ERR_SCOPE_BREAK):
1.42      kristaps  354:                lit = "scope: macro breaks prior scope";
1.24      kristaps  355:                break;
                    356:        case (ERR_SCOPE_NOCTX):
1.37      kristaps  357:                lit = "scope: closure macro has no context";
1.21      kristaps  358:                break;
1.25      kristaps  359:        case (ERR_SCOPE_NONEST):
1.37      kristaps  360:                lit = "scope: macro may not be nested in the current context";
1.25      kristaps  361:                break;
1.21      kristaps  362:        case (ERR_MACRO_NOTSUP):
1.35      kristaps  363:                lit = "macro not supported";
1.21      kristaps  364:                break;
                    365:        case (ERR_MACRO_NOTCALL):
1.37      kristaps  366:                lit = "macro not callable";
1.21      kristaps  367:                break;
1.23      kristaps  368:        case (ERR_SEC_PROLOGUE):
1.37      kristaps  369:                lit = "macro cannot be called in the prologue";
1.23      kristaps  370:                break;
                    371:        case (ERR_SEC_NPROLOGUE):
1.37      kristaps  372:                lit = "macro called outside of prologue";
1.23      kristaps  373:                break;
1.28      kristaps  374:        case (ERR_ARGS_EQ0):
1.37      kristaps  375:                lit = "macro expects zero arguments";
1.28      kristaps  376:                break;
1.29      kristaps  377:        case (ERR_ARGS_EQ1):
1.37      kristaps  378:                lit = "macro expects one argument";
1.29      kristaps  379:                break;
1.21      kristaps  380:        case (ERR_ARGS_GE1):
1.37      kristaps  381:                lit = "macro expects one or more arguments";
1.21      kristaps  382:                break;
1.28      kristaps  383:        case (ERR_ARGS_LE2):
1.37      kristaps  384:                lit = "macro expects two or fewer arguments";
1.28      kristaps  385:                break;
1.36      kristaps  386:        case (ERR_ARGS_LE8):
1.37      kristaps  387:                lit = "macro expects eight or fewer arguments";
1.36      kristaps  388:                break;
1.23      kristaps  389:        case (ERR_ARGS_MANY):
1.37      kristaps  390:                lit = "macro has too many arguments";
1.23      kristaps  391:                break;
                    392:        case (ERR_SEC_PROLOGUE_OO):
1.37      kristaps  393:                lit = "prologue macro is out-of-order";
1.23      kristaps  394:                break;
                    395:        case (ERR_SEC_PROLOGUE_REP):
1.37      kristaps  396:                lit = "prologue macro repeated";
1.23      kristaps  397:                break;
                    398:        case (ERR_SEC_NAME):
                    399:                lit = "`NAME' section must be first";
                    400:                break;
1.24      kristaps  401:        case (ERR_SYNTAX_ARGVAL):
                    402:                lit = "syntax: expected value for macro argument";
                    403:                break;
1.25      kristaps  404:        case (ERR_SYNTAX_ARGBAD):
1.39      kristaps  405:                lit = "syntax: invalid value(s) for macro argument";
                    406:                break;
                    407:        case (ERR_SYNTAX_ARGMISS):
                    408:                lit = "syntax: missing required argument(s) for macro";
1.25      kristaps  409:                break;
1.24      kristaps  410:        case (ERR_SYNTAX_ARGMANY):
                    411:                lit = "syntax: too many values for macro argument";
                    412:                break;
1.39      kristaps  413:        case (ERR_SYNTAX_CHILDBAD):
                    414:                lit = "syntax: invalid child for parent macro";
                    415:                break;
1.40      kristaps  416:        case (ERR_SYNTAX_PARENTBAD):
                    417:                lit = "syntax: invalid parent for macro";
                    418:                break;
1.32      kristaps  419:        case (ERR_SYNTAX_CHILDHEAD):
                    420:                lit = "syntax: expected only block-header section";
                    421:                break;
                    422:        case (ERR_SYNTAX_CHILDBODY):
                    423:                lit = "syntax: expected only a block-body section";
                    424:                break;
                    425:        case (ERR_SYNTAX_EMPTYHEAD):
                    426:                lit = "syntax: block-header section may not be empty";
                    427:                break;
                    428:        case (ERR_SYNTAX_EMPTYBODY):
                    429:                lit = "syntax: block-body section may not be empty";
1.31      kristaps  430:                break;
1.21      kristaps  431:        default:
                    432:                abort();
                    433:                /* NOTREACHED */
                    434:        }
                    435:
1.39      kristaps  436:        (void)fprintf(stderr, "%s:%d: error: %s (column %d)\n",
                    437:                        p->name, line, lit, col);
1.21      kristaps  438:        return(0);
                    439: }
                    440:
                    441:
                    442: static void
1.37      kristaps  443: msg_msg(void *arg, int line, int col, const char *msg)
1.4       kristaps  444: {
1.21      kristaps  445:        struct md_parse  *p;
                    446:
                    447:        p = (struct md_parse *)arg;
                    448:
                    449:        if (p->dbg < 2)
                    450:                return;
                    451:
1.39      kristaps  452:        (void)printf("%s:%d: %s (column %d)\n",
                    453:                        p->name, line, msg, col);
1.1       kristaps  454: }
                    455:
                    456:
                    457: static int
1.37      kristaps  458: msg_warn(void *arg, int line, int col, enum mdoc_warn type)
1.1       kristaps  459: {
1.37      kristaps  460:        char             *lit;
1.21      kristaps  461:        struct md_parse  *p;
                    462:        extern char      *__progname;
1.1       kristaps  463:
1.21      kristaps  464:        p = (struct md_parse *)arg;
1.1       kristaps  465:
1.21      kristaps  466:        if ( ! (p->warn & MD_WARN_ALL))
1.1       kristaps  467:                return(1);
1.21      kristaps  468:
1.37      kristaps  469:        lit = NULL;
1.21      kristaps  470:
                    471:        switch (type) {
                    472:        case (WARN_SYNTAX_WS_EOLN):
                    473:                lit = "syntax: whitespace at end-of-line";
                    474:                break;
1.25      kristaps  475:        case (WARN_SYNTAX_QUOTED):
                    476:                lit = "syntax: quotation mark starting string";
                    477:                break;
1.21      kristaps  478:        case (WARN_SYNTAX_MACLIKE):
                    479:                lit = "syntax: macro-like argument";
                    480:                break;
1.24      kristaps  481:        case (WARN_SYNTAX_ARGLIKE):
                    482:                lit = "syntax: argument-like value";
                    483:                break;
1.32      kristaps  484:        case (WARN_SYNTAX_EMPTYBODY):
1.40      kristaps  485:                lit = "syntax: macro suggests non-empty block-body section";
                    486:                break;
                    487:        case (WARN_SYNTAX_EMPTYHEAD):
                    488:                lit = "syntax: macro suggests non-empty block-head section";
                    489:                break;
                    490:        case (WARN_SYNTAX_NOBODY):
                    491:                lit = "syntax: macro suggests empty block-body section";
1.32      kristaps  492:                break;
1.23      kristaps  493:        case (WARN_SEC_OO):
                    494:                lit = "section is out of conventional order";
                    495:                break;
1.38      kristaps  496:        case (WARN_SEC_REP):
                    497:                lit = "section repeated";
                    498:                break;
1.21      kristaps  499:        case (WARN_ARGS_GE1):
1.37      kristaps  500:                lit = "macro suggests one or more arguments";
1.21      kristaps  501:                break;
1.25      kristaps  502:        case (WARN_ARGS_EQ0):
1.37      kristaps  503:                lit = "macro suggests zero arguments";
1.25      kristaps  504:                break;
                    505:        case (WARN_IGN_AFTER_BLK):
1.37      kristaps  506:                lit = "ignore: macro ignored after block macro";
1.25      kristaps  507:                break;
1.30      kristaps  508:        case (WARN_IGN_OBSOLETE):
1.37      kristaps  509:                lit = "ignore: macro is obsolete";
1.30      kristaps  510:                break;
1.25      kristaps  511:        case (WARN_IGN_BEFORE_BLK):
1.37      kristaps  512:                lit = "ignore: macro before block macro ignored";
1.25      kristaps  513:                break;
1.27      kristaps  514:        case (WARN_COMPAT_TROFF):
1.37      kristaps  515:                lit = "compat: macro behaves differently in troff and nroff";
1.27      kristaps  516:                break;
1.21      kristaps  517:        default:
                    518:                abort();
                    519:                /* NOTREACHED */
1.1       kristaps  520:        }
                    521:
1.21      kristaps  522:
1.39      kristaps  523:        (void)fprintf(stderr, "%s:%d: warning: %s (column %d)\n",
                    524:                        p->name, line, lit, col);
1.21      kristaps  525:
                    526:        if (p->warn & MD_WARN_ERR) {
                    527:                (void)fprintf(stderr, "%s: considering warnings as "
                    528:                                "errors\n", __progname);
                    529:                return(0);
1.1       kristaps  530:        }
                    531:
1.21      kristaps  532:        return(1);
1.1       kristaps  533: }
                    534:
                    535:
                    536: static void
                    537: usage(void)
                    538: {
                    539:        extern char     *__progname;
                    540:
1.21      kristaps  541:        (void)fprintf(stderr, "usage: %s [-v] [-Wwarn...] [infile]\n",
1.19      kristaps  542:                        __progname);
1.1       kristaps  543: }
1.18      kristaps  544:

CVSweb