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

Annotation of mandoc/roff.c, Revision 1.77

1.77    ! kristaps    1: /*     $Id: roff.c,v 1.76 2010/05/16 14:47:19 kristaps Exp $ */
1.1       kristaps    2: /*
1.67      kristaps    3:  * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
1.1       kristaps    4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
1.66      kristaps    6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
1.1       kristaps    8:  *
1.66      kristaps    9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1       kristaps   16:  */
1.66      kristaps   17: #ifdef HAVE_CONFIG_H
                     18: #include "config.h"
                     19: #endif
1.30      kristaps   20:
1.67      kristaps   21: #include <assert.h>
1.1       kristaps   22: #include <stdlib.h>
1.67      kristaps   23: #include <string.h>
1.75      kristaps   24: #include <stdio.h>
1.1       kristaps   25:
1.67      kristaps   26: #include "mandoc.h"
1.43      kristaps   27: #include "roff.h"
1.33      kristaps   28:
1.75      kristaps   29: #define        ROFF_CTL(c) \
                     30:        ('.' == (c) || '\'' == (c))
1.76      kristaps   31: #if    0
1.75      kristaps   32: #define        ROFF_MDEBUG(p, str) \
                     33:        fprintf(stderr, "%s: %s (%d:%d)\n", (str), \
                     34:                roffs[(p)->last->tok].name, \
                     35:                (p)->last->line, (p)->last->col)
                     36: #else
                     37: #define        ROFF_MDEBUG(p, str) while (/* CONSTCOND */ 0)
                     38: #endif
                     39:
1.67      kristaps   40: enum   rofft {
1.75      kristaps   41:        ROFF_if,
1.76      kristaps   42:        ROFF_ig,
                     43:        ROFF_cblock,
1.75      kristaps   44:        ROFF_ccond,
1.74      kristaps   45: #if 0
                     46:        ROFF_am,
                     47:        ROFF_ami,
1.67      kristaps   48:        ROFF_de,
                     49:        ROFF_dei,
                     50:        ROFF_close,
1.74      kristaps   51: #endif
1.67      kristaps   52:        ROFF_MAX
                     53: };
                     54:
                     55: struct roff {
                     56:        struct roffnode *last; /* leaf of stack */
                     57:        mandocmsg        msg; /* err/warn/fatal messages */
                     58:        void            *data; /* privdata for messages */
                     59: };
                     60:
                     61: struct roffnode {
                     62:        enum rofft       tok; /* type of node */
                     63:        struct roffnode *parent; /* up one in stack */
1.74      kristaps   64:        char            *end; /* end-token: custom */
1.67      kristaps   65:        int              line; /* parse line */
                     66:        int              col; /* parse col */
1.75      kristaps   67:        int              endspan;
1.67      kristaps   68: };
                     69:
                     70: #define        ROFF_ARGS        struct roff *r, /* parse ctx */ \
1.72      kristaps   71:                         enum rofft tok, /* tok of macro */ \
1.67      kristaps   72:                         char **bufp, /* input buffer */ \
                     73:                         size_t *szp, /* size of input buffer */ \
                     74:                         int ln, /* parse line */ \
1.75      kristaps   75:                         int ppos, /* original pos in buffer */ \
                     76:                         int pos, /* current pos in buffer */ \
1.74      kristaps   77:                         int *offs /* reset offset of buffer data */
1.67      kristaps   78:
                     79: typedef        enum rofferr (*roffproc)(ROFF_ARGS);
                     80:
                     81: struct roffmac {
                     82:        const char      *name; /* macro name */
1.75      kristaps   83:        roffproc         proc;
1.67      kristaps   84: };
                     85:
1.75      kristaps   86: static enum rofferr     roff_if(ROFF_ARGS);
1.76      kristaps   87: static enum rofferr     roff_ig(ROFF_ARGS);
                     88: static enum rofferr     roff_cblock(ROFF_ARGS);
1.75      kristaps   89: static enum rofferr     roff_ccond(ROFF_ARGS);
1.67      kristaps   90:
                     91: const  struct roffmac   roffs[ROFF_MAX] = {
1.75      kristaps   92:        { "if", roff_if },
1.76      kristaps   93:        { "ig", roff_ig },
                     94:        { ".", roff_cblock },
1.75      kristaps   95:        { "\\}", roff_ccond },
1.74      kristaps   96: #if 0
                     97:        { "am", roff_sub_ig, roff_new_ig },
                     98:        { "ami", roff_sub_ig, roff_new_ig },
1.72      kristaps   99:        { "de", roff_sub_ig, roff_new_ig },
                    100:        { "dei", roff_sub_ig, roff_new_ig },
1.74      kristaps  101: #endif
1.67      kristaps  102: };
                    103:
                    104: static void             roff_free1(struct roff *);
                    105: static enum rofft       roff_hash_find(const char *);
1.76      kristaps  106: static void             roffnode_cleanscope(struct roff *);
1.67      kristaps  107: static int              roffnode_push(struct roff *,
                    108:                                enum rofft, int, int);
                    109: static void             roffnode_pop(struct roff *);
                    110: static enum rofft       roff_parse(const char *, int *);
                    111:
                    112:
                    113: /*
                    114:  * Look up a roff token by its name.  Returns ROFF_MAX if no macro by
                    115:  * the nil-terminated string name could be found.
                    116:  */
                    117: static enum rofft
                    118: roff_hash_find(const char *p)
                    119: {
                    120:        int              i;
                    121:
                    122:        /* FIXME: make this be fast and efficient. */
                    123:
                    124:        for (i = 0; i < (int)ROFF_MAX; i++)
                    125:                if (0 == strcmp(roffs[i].name, p))
                    126:                        return((enum rofft)i);
                    127:
                    128:        return(ROFF_MAX);
                    129: }
                    130:
                    131:
                    132: /*
                    133:  * Pop the current node off of the stack of roff instructions currently
                    134:  * pending.
                    135:  */
                    136: static void
                    137: roffnode_pop(struct roff *r)
                    138: {
                    139:        struct roffnode *p;
                    140:
1.75      kristaps  141:        assert(r->last);
                    142:        p = r->last;
                    143:        r->last = r->last->parent;
1.74      kristaps  144:        if (p->end)
                    145:                free(p->end);
1.67      kristaps  146:        free(p);
                    147: }
                    148:
                    149:
                    150: /*
                    151:  * Push a roff node onto the instruction stack.  This must later be
                    152:  * removed with roffnode_pop().
                    153:  */
                    154: static int
                    155: roffnode_push(struct roff *r, enum rofft tok, int line, int col)
                    156: {
                    157:        struct roffnode *p;
                    158:
                    159:        if (NULL == (p = calloc(1, sizeof(struct roffnode)))) {
                    160:                (*r->msg)(MANDOCERR_MEM, r->data, line, col, NULL);
                    161:                return(0);
                    162:        }
                    163:
                    164:        p->tok = tok;
                    165:        p->parent = r->last;
                    166:        p->line = line;
                    167:        p->col = col;
                    168:
                    169:        r->last = p;
                    170:        return(1);
                    171: }
                    172:
                    173:
                    174: static void
                    175: roff_free1(struct roff *r)
                    176: {
                    177:
                    178:        while (r->last)
                    179:                roffnode_pop(r);
                    180: }
                    181:
                    182:
                    183: void
                    184: roff_reset(struct roff *r)
                    185: {
                    186:
                    187:        roff_free1(r);
                    188: }
                    189:
                    190:
                    191: void
                    192: roff_free(struct roff *r)
                    193: {
                    194:
                    195:        roff_free1(r);
                    196:        free(r);
                    197: }
                    198:
                    199:
                    200: struct roff *
                    201: roff_alloc(const mandocmsg msg, void *data)
                    202: {
                    203:        struct roff     *r;
                    204:
                    205:        if (NULL == (r = calloc(1, sizeof(struct roff)))) {
                    206:                (*msg)(MANDOCERR_MEM, data, 0, 0, NULL);
                    207:                return(0);
                    208:        }
                    209:
                    210:        r->msg = msg;
                    211:        r->data = data;
                    212:        return(r);
                    213: }
                    214:
                    215:
                    216: enum rofferr
1.74      kristaps  217: roff_parseln(struct roff *r, int ln,
                    218:                char **bufp, size_t *szp, int pos, int *offs)
1.67      kristaps  219: {
                    220:        enum rofft       t;
1.75      kristaps  221:        int              ppos;
1.74      kristaps  222:
1.75      kristaps  223:        if (r->last && ! ROFF_CTL((*bufp)[pos])) {
1.76      kristaps  224:                if (ROFF_ig == r->last->tok)
                    225:                        return(ROFF_IGN);
                    226:                roffnode_cleanscope(r);
                    227:                /* FIXME: this assumes we're discarding! */
1.75      kristaps  228:                return(ROFF_IGN);
                    229:        } else if ( ! ROFF_CTL((*bufp)[pos]))
1.67      kristaps  230:                return(ROFF_CONT);
                    231:
                    232:        /* There's nothing on the stack: make us anew. */
                    233:
1.75      kristaps  234:        ppos = pos;
1.76      kristaps  235:        if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) {
                    236:                if (r->last && ROFF_ig == r->last->tok)
                    237:                        return(ROFF_IGN);
1.67      kristaps  238:                return(ROFF_CONT);
1.76      kristaps  239:        }
1.67      kristaps  240:
1.75      kristaps  241:        assert(roffs[t].proc);
                    242:        return((*roffs[t].proc)(r, t, bufp, szp, ln, ppos, pos, offs));
1.74      kristaps  243: }
                    244:
                    245:
                    246: int
                    247: roff_endparse(struct roff *r)
                    248: {
                    249:
                    250:        if (NULL == r->last)
                    251:                return(1);
                    252:        return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data, r->last->line,
                    253:                                r->last->col, NULL));
1.67      kristaps  254: }
                    255:
                    256:
                    257: /*
                    258:  * Parse a roff node's type from the input buffer.  This must be in the
                    259:  * form of ".foo xxx" in the usual way.
                    260:  */
                    261: static enum rofft
                    262: roff_parse(const char *buf, int *pos)
                    263: {
                    264:        int              j;
                    265:        char             mac[5];
                    266:        enum rofft       t;
                    267:
1.75      kristaps  268:        assert(ROFF_CTL(buf[*pos]));
                    269:        (*pos)++;
1.67      kristaps  270:
                    271:        while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
                    272:                (*pos)++;
                    273:
                    274:        if ('\0' == buf[*pos])
                    275:                return(ROFF_MAX);
                    276:
                    277:        for (j = 0; j < 4; j++, (*pos)++)
                    278:                if ('\0' == (mac[j] = buf[*pos]))
                    279:                        break;
                    280:                else if (' ' == buf[*pos])
                    281:                        break;
                    282:
                    283:        if (j == 4 || j < 1)
                    284:                return(ROFF_MAX);
                    285:
                    286:        mac[j] = '\0';
                    287:
                    288:        if (ROFF_MAX == (t = roff_hash_find(mac)))
                    289:                return(t);
                    290:
                    291:        while (buf[*pos] && ' ' == buf[*pos])
                    292:                (*pos)++;
                    293:
                    294:        return(t);
                    295: }
                    296:
                    297:
                    298: /* ARGSUSED */
                    299: static enum rofferr
1.76      kristaps  300: roff_cblock(ROFF_ARGS)
1.67      kristaps  301: {
                    302:
1.76      kristaps  303:        if (NULL == r->last) {
                    304:                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                    305:                        return(ROFF_ERR);
                    306:                return(ROFF_IGN);
                    307:        }
1.67      kristaps  308:
1.76      kristaps  309:        if (ROFF_ig != r->last->tok) {
                    310:                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                    311:                        return(ROFF_ERR);
1.67      kristaps  312:                return(ROFF_IGN);
1.76      kristaps  313:        }
1.67      kristaps  314:
1.76      kristaps  315:        if ((*bufp)[pos])
                    316:                if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
                    317:                        return(ROFF_ERR);
1.71      kristaps  318:
1.76      kristaps  319:        ROFF_MDEBUG(r, "closing ignore block");
1.71      kristaps  320:        roffnode_pop(r);
1.76      kristaps  321:        roffnode_cleanscope(r);
                    322:        return(ROFF_IGN);
1.71      kristaps  323:
1.67      kristaps  324: }
                    325:
                    326:
1.76      kristaps  327: static void
                    328: roffnode_cleanscope(struct roff *r)
1.67      kristaps  329: {
                    330:
1.76      kristaps  331:        while (r->last) {
                    332:                if (--r->last->endspan < 0)
                    333:                        break;
                    334:                ROFF_MDEBUG(r, "closing implicit scope");
                    335:                roffnode_pop(r);
                    336:        }
1.67      kristaps  337: }
                    338:
                    339:
1.75      kristaps  340: /* ARGSUSED */
1.74      kristaps  341: static enum rofferr
1.75      kristaps  342: roff_ccond(ROFF_ARGS)
1.74      kristaps  343: {
                    344:
1.76      kristaps  345:        if (NULL == r->last) {
                    346:                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                    347:                        return(ROFF_ERR);
                    348:                return(ROFF_IGN);
                    349:        }
                    350:
                    351:        if (ROFF_if != r->last->tok) {
1.75      kristaps  352:                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                    353:                        return(ROFF_ERR);
                    354:                return(ROFF_IGN);
                    355:        }
                    356:
1.76      kristaps  357:        if (r->last->endspan > -1) {
                    358:                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                    359:                        return(ROFF_ERR);
                    360:                return(ROFF_IGN);
                    361:        }
                    362:
                    363:        if ((*bufp)[pos])
                    364:                if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
                    365:                        return(ROFF_ERR);
                    366:
1.75      kristaps  367:        ROFF_MDEBUG(r, "closing explicit scope");
                    368:        roffnode_pop(r);
1.76      kristaps  369:        roffnode_cleanscope(r);
                    370:        return(ROFF_IGN);
                    371: }
                    372:
1.75      kristaps  373:
1.76      kristaps  374: /* ARGSUSED */
                    375: static enum rofferr
                    376: roff_ig(ROFF_ARGS)
                    377: {
                    378:
                    379:        if ( ! roffnode_push(r, tok, ln, ppos))
                    380:                return(ROFF_ERR);
                    381:
                    382:        ROFF_MDEBUG(r, "opening ignore block");
1.74      kristaps  383:
1.77    ! kristaps  384:        if ((*bufp)[pos])
        !           385:                if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
        !           386:                        return(ROFF_ERR);
1.74      kristaps  387:
                    388:        return(ROFF_IGN);
                    389: }
                    390:
                    391:
1.75      kristaps  392: /* ARGSUSED */
1.74      kristaps  393: static enum rofferr
1.75      kristaps  394: roff_if(ROFF_ARGS)
1.74      kristaps  395: {
1.77    ! kristaps  396:        int              sv;
1.74      kristaps  397:
                    398:        /*
                    399:         * Read ahead past the conditional.
                    400:         * FIXME: this does not work, as conditionals don't end on
                    401:         * whitespace, but are parsed according to a formal grammar.
                    402:         * It's good enough for now, however.
                    403:         */
                    404:
1.75      kristaps  405:        while ((*bufp)[pos] && ' ' != (*bufp)[pos])
                    406:                pos++;
1.77    ! kristaps  407:
        !           408:        sv = pos;
1.75      kristaps  409:        while (' ' == (*bufp)[pos])
                    410:                pos++;
1.74      kristaps  411:
1.77    ! kristaps  412:        /*
        !           413:         * Roff is weird.  If we have just white-space after the
        !           414:         * conditional, it's considered the BODY and we exit without
        !           415:         * really doing anything.  Warn about this.  It's probably
        !           416:         * wrong.
        !           417:         */
        !           418:
        !           419:        if ('\0' == (*bufp)[pos] && sv != pos) {
        !           420:                if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
        !           421:                        return(ROFF_ERR);
        !           422:                return(ROFF_IGN);
        !           423:        }
        !           424:
        !           425:        if ( ! roffnode_push(r, tok, ln, ppos))
        !           426:                return(ROFF_ERR);
        !           427:
1.74      kristaps  428:        /* Don't evaluate: just assume NO. */
                    429:
1.75      kristaps  430:        r->last->endspan = 1;
                    431:
                    432:        if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
                    433:                ROFF_MDEBUG(r, "opening explicit scope");
                    434:                r->last->endspan = -1;
                    435:                pos += 2;
                    436:        } else
                    437:                ROFF_MDEBUG(r, "opening implicit scope");
1.74      kristaps  438:
1.77    ! kristaps  439:        /*
        !           440:         * If there are no arguments on the line, the next-line scope is
        !           441:         * assumed.
        !           442:         */
        !           443:
1.75      kristaps  444:        if ('\0' == (*bufp)[pos])
                    445:                return(ROFF_IGN);
1.77    ! kristaps  446:
        !           447:        /* Otherwise re-run the roff parser after recalculating. */
1.74      kristaps  448:
1.75      kristaps  449:        *offs = pos;
                    450:        return(ROFF_RERUN);
1.74      kristaps  451: }

CVSweb