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

Annotation of mandoc/roff.c, Revision 1.75

1.75    ! kristaps    1: /*     $Id: roff.c,v 1.74 2010/05/16 10:59:36 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))
        !            31: #ifdef ROFF_DEBUG
        !            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,
        !            42:        ROFF_ccond,
1.74      kristaps   43: #if 0
                     44:        ROFF_am,
                     45:        ROFF_ami,
1.67      kristaps   46:        ROFF_de,
                     47:        ROFF_dei,
                     48:        ROFF_ig,
                     49:        ROFF_close,
1.74      kristaps   50: #endif
1.67      kristaps   51:        ROFF_MAX
                     52: };
                     53:
                     54: struct roff {
                     55:        struct roffnode *last; /* leaf of stack */
                     56:        mandocmsg        msg; /* err/warn/fatal messages */
                     57:        void            *data; /* privdata for messages */
                     58: };
                     59:
                     60: struct roffnode {
                     61:        enum rofft       tok; /* type of node */
                     62:        struct roffnode *parent; /* up one in stack */
1.74      kristaps   63:        char            *end; /* end-token: custom */
1.67      kristaps   64:        int              line; /* parse line */
                     65:        int              col; /* parse col */
1.75    ! kristaps   66:        int              endspan;
1.67      kristaps   67: };
                     68:
                     69: #define        ROFF_ARGS        struct roff *r, /* parse ctx */ \
1.72      kristaps   70:                         enum rofft tok, /* tok of macro */ \
1.67      kristaps   71:                         char **bufp, /* input buffer */ \
                     72:                         size_t *szp, /* size of input buffer */ \
                     73:                         int ln, /* parse line */ \
1.75    ! kristaps   74:                         int ppos, /* original pos in buffer */ \
        !            75:                         int pos, /* current pos in buffer */ \
1.74      kristaps   76:                         int *offs /* reset offset of buffer data */
1.67      kristaps   77:
                     78: typedef        enum rofferr (*roffproc)(ROFF_ARGS);
                     79:
                     80: struct roffmac {
                     81:        const char      *name; /* macro name */
1.75    ! kristaps   82:        roffproc         proc;
1.67      kristaps   83: };
                     84:
1.75    ! kristaps   85: static enum rofferr     roff_if(ROFF_ARGS);
        !            86: static enum rofferr     roff_ccond(ROFF_ARGS);
1.74      kristaps   87: #if 0
1.67      kristaps   88: static enum rofferr     roff_new_close(ROFF_ARGS);
                     89: static enum rofferr     roff_new_ig(ROFF_ARGS);
                     90: static enum rofferr     roff_sub_ig(ROFF_ARGS);
1.74      kristaps   91: #endif
1.67      kristaps   92:
                     93: const  struct roffmac   roffs[ROFF_MAX] = {
1.75    ! kristaps   94:        { "if", roff_if },
        !            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.67      kristaps  101:        { "ig", roff_sub_ig, roff_new_ig },
                    102:        { ".", NULL, roff_new_close },
1.74      kristaps  103: #endif
1.67      kristaps  104: };
                    105:
                    106: static void             roff_free1(struct roff *);
                    107: static enum rofft       roff_hash_find(const char *);
                    108: static int              roffnode_push(struct roff *,
                    109:                                enum rofft, int, int);
                    110: static void             roffnode_pop(struct roff *);
                    111: static enum rofft       roff_parse(const char *, int *);
                    112:
                    113:
                    114: /*
                    115:  * Look up a roff token by its name.  Returns ROFF_MAX if no macro by
                    116:  * the nil-terminated string name could be found.
                    117:  */
                    118: static enum rofft
                    119: roff_hash_find(const char *p)
                    120: {
                    121:        int              i;
                    122:
                    123:        /* FIXME: make this be fast and efficient. */
                    124:
                    125:        for (i = 0; i < (int)ROFF_MAX; i++)
                    126:                if (0 == strcmp(roffs[i].name, p))
                    127:                        return((enum rofft)i);
                    128:
                    129:        return(ROFF_MAX);
                    130: }
                    131:
                    132:
                    133: /*
                    134:  * Pop the current node off of the stack of roff instructions currently
                    135:  * pending.
                    136:  */
                    137: static void
                    138: roffnode_pop(struct roff *r)
                    139: {
                    140:        struct roffnode *p;
                    141:
1.75    ! kristaps  142:        assert(r->last);
        !           143:        p = r->last;
        !           144:        r->last = r->last->parent;
1.74      kristaps  145:        if (p->end)
                    146:                free(p->end);
1.67      kristaps  147:        free(p);
                    148: }
                    149:
                    150:
                    151: /*
                    152:  * Push a roff node onto the instruction stack.  This must later be
                    153:  * removed with roffnode_pop().
                    154:  */
                    155: static int
                    156: roffnode_push(struct roff *r, enum rofft tok, int line, int col)
                    157: {
                    158:        struct roffnode *p;
                    159:
                    160:        if (NULL == (p = calloc(1, sizeof(struct roffnode)))) {
                    161:                (*r->msg)(MANDOCERR_MEM, r->data, line, col, NULL);
                    162:                return(0);
                    163:        }
                    164:
                    165:        p->tok = tok;
                    166:        p->parent = r->last;
                    167:        p->line = line;
                    168:        p->col = col;
                    169:
                    170:        r->last = p;
                    171:        return(1);
                    172: }
                    173:
                    174:
                    175: static void
                    176: roff_free1(struct roff *r)
                    177: {
                    178:
                    179:        while (r->last)
                    180:                roffnode_pop(r);
                    181: }
                    182:
                    183:
                    184: void
                    185: roff_reset(struct roff *r)
                    186: {
                    187:
                    188:        roff_free1(r);
                    189: }
                    190:
                    191:
                    192: void
                    193: roff_free(struct roff *r)
                    194: {
                    195:
                    196:        roff_free1(r);
                    197:        free(r);
                    198: }
                    199:
                    200:
                    201: struct roff *
                    202: roff_alloc(const mandocmsg msg, void *data)
                    203: {
                    204:        struct roff     *r;
                    205:
                    206:        if (NULL == (r = calloc(1, sizeof(struct roff)))) {
                    207:                (*msg)(MANDOCERR_MEM, data, 0, 0, NULL);
                    208:                return(0);
                    209:        }
                    210:
                    211:        r->msg = msg;
                    212:        r->data = data;
                    213:        return(r);
                    214: }
                    215:
                    216:
                    217: enum rofferr
1.74      kristaps  218: roff_parseln(struct roff *r, int ln,
                    219:                char **bufp, size_t *szp, int pos, int *offs)
1.67      kristaps  220: {
                    221:        enum rofft       t;
1.75    ! kristaps  222:        int              ppos;
1.74      kristaps  223:
                    224:        /* Return when in free text without a context. */
                    225:
1.75    ! kristaps  226:        if (r->last && ! ROFF_CTL((*bufp)[pos])) {
        !           227:                /* XXX: this assumes we're just discarding. */
        !           228:                while (r->last) {
        !           229:                        if (r->last->endspan-- < 0)
        !           230:                                break;
        !           231:                        ROFF_MDEBUG(r, "closing implicit scope");
        !           232:                        roffnode_pop(r);
        !           233:                }
        !           234:                return(ROFF_IGN);
        !           235:        } else if ( ! ROFF_CTL((*bufp)[pos]))
1.67      kristaps  236:                return(ROFF_CONT);
                    237:
                    238:        /* There's nothing on the stack: make us anew. */
                    239:
1.75    ! kristaps  240:        ppos = pos;
1.74      kristaps  241:        if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
1.67      kristaps  242:                return(ROFF_CONT);
                    243:
1.75    ! kristaps  244:        assert(roffs[t].proc);
        !           245:        return((*roffs[t].proc)(r, t, bufp, szp, ln, ppos, pos, offs));
1.74      kristaps  246: }
                    247:
                    248:
                    249: int
                    250: roff_endparse(struct roff *r)
                    251: {
                    252:
                    253:        if (NULL == r->last)
                    254:                return(1);
                    255:        return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data, r->last->line,
                    256:                                r->last->col, NULL));
1.67      kristaps  257: }
                    258:
                    259:
                    260: /*
                    261:  * Parse a roff node's type from the input buffer.  This must be in the
                    262:  * form of ".foo xxx" in the usual way.
                    263:  */
                    264: static enum rofft
                    265: roff_parse(const char *buf, int *pos)
                    266: {
                    267:        int              j;
                    268:        char             mac[5];
                    269:        enum rofft       t;
                    270:
1.75    ! kristaps  271:        assert(ROFF_CTL(buf[*pos]));
        !           272:        (*pos)++;
1.67      kristaps  273:
                    274:        while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
                    275:                (*pos)++;
                    276:
                    277:        if ('\0' == buf[*pos])
                    278:                return(ROFF_MAX);
                    279:
                    280:        for (j = 0; j < 4; j++, (*pos)++)
                    281:                if ('\0' == (mac[j] = buf[*pos]))
                    282:                        break;
                    283:                else if (' ' == buf[*pos])
                    284:                        break;
                    285:
                    286:        if (j == 4 || j < 1)
                    287:                return(ROFF_MAX);
                    288:
                    289:        mac[j] = '\0';
                    290:
                    291:        if (ROFF_MAX == (t = roff_hash_find(mac)))
                    292:                return(t);
                    293:
                    294:        while (buf[*pos] && ' ' == buf[*pos])
                    295:                (*pos)++;
                    296:
                    297:        return(t);
                    298: }
                    299:
                    300:
1.74      kristaps  301: #if 0
1.67      kristaps  302: /* ARGSUSED */
                    303: static enum rofferr
                    304: roff_sub_ig(ROFF_ARGS)
                    305: {
1.71      kristaps  306:        int              i, j;
1.67      kristaps  307:
                    308:        /* Ignore free-text lines. */
                    309:
1.74      kristaps  310:        if ('.' != (*bufp)[ppos] && '\'' != (*bufp)[ppos])
1.67      kristaps  311:                return(ROFF_IGN);
                    312:
1.71      kristaps  313:        if (r->last->end) {
                    314:                i = ppos + 1;
1.72      kristaps  315:
1.71      kristaps  316:                while ((*bufp)[i] && ' ' == (*bufp)[i])
                    317:                        i++;
                    318:
                    319:                for (j = 0; r->last->end[j]; i++, j++)
                    320:                        if ((*bufp)[i] != r->last->end[j])
                    321:                                return(ROFF_IGN);
                    322:
                    323:                if (r->last->end[j])
                    324:                        return(ROFF_IGN);
                    325:                if ((*bufp)[i] && ' ' != (*bufp)[i])
                    326:                        return(ROFF_IGN);
                    327:
                    328:                while (' ' == (*bufp)[i])
                    329:                        i++;
1.72      kristaps  330:
1.71      kristaps  331:        } else if (ROFF_close != roff_parse(*bufp, &i))
                    332:                return(ROFF_IGN);
                    333:
                    334:        roffnode_pop(r);
                    335:
                    336:        if ('\0' == (*bufp)[i])
1.67      kristaps  337:                return(ROFF_IGN);
1.71      kristaps  338:        if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, i, NULL))
                    339:                return(ROFF_ERR);
1.67      kristaps  340:
                    341:        return(ROFF_IGN);
                    342: }
                    343:
                    344:
                    345: /* ARGSUSED */
                    346: static enum rofferr
                    347: roff_new_close(ROFF_ARGS)
                    348: {
                    349:
                    350:        if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                    351:                return(ROFF_ERR);
1.72      kristaps  352:
1.69      kristaps  353:        return(ROFF_IGN);
1.67      kristaps  354: }
1.75    ! kristaps  355: #endif
1.67      kristaps  356:
                    357:
1.75    ! kristaps  358: /* ARGSUSED */
1.74      kristaps  359: static enum rofferr
1.75    ! kristaps  360: roff_ccond(ROFF_ARGS)
1.74      kristaps  361: {
                    362:
1.75    ! kristaps  363:        if (NULL == r->last || ROFF_if != r->last->tok || r->last->endspan > -1) {
        !           364:                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
        !           365:                        return(ROFF_ERR);
        !           366:                return(ROFF_IGN);
        !           367:        }
        !           368:
        !           369:        ROFF_MDEBUG(r, "closing explicit scope");
        !           370:        roffnode_pop(r);
        !           371:
        !           372:        while (r->last) {
        !           373:                if (--r->last->endspan < 0)
        !           374:                        break;
1.74      kristaps  375:
1.75    ! kristaps  376:                ROFF_MDEBUG(r, "closing implicit scope");
1.74      kristaps  377:                roffnode_pop(r);
1.75    ! kristaps  378:        }
1.74      kristaps  379:
                    380:        return(ROFF_IGN);
                    381: }
                    382:
                    383:
1.75    ! kristaps  384: /* ARGSUSED */
1.74      kristaps  385: static enum rofferr
1.75    ! kristaps  386: roff_if(ROFF_ARGS)
1.74      kristaps  387: {
                    388:
                    389:        /*
                    390:         * Read ahead past the conditional.
                    391:         * FIXME: this does not work, as conditionals don't end on
                    392:         * whitespace, but are parsed according to a formal grammar.
                    393:         * It's good enough for now, however.
                    394:         */
                    395:
                    396:        if ( ! roffnode_push(r, tok, ln, ppos))
                    397:                return(ROFF_ERR);
                    398:
1.75    ! kristaps  399:        while ((*bufp)[pos] && ' ' != (*bufp)[pos])
        !           400:                pos++;
        !           401:        while (' ' == (*bufp)[pos])
        !           402:                pos++;
1.74      kristaps  403:
                    404:        /* Don't evaluate: just assume NO. */
                    405:
1.75    ! kristaps  406:        r->last->endspan = 1;
        !           407:
        !           408:        if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
        !           409:                ROFF_MDEBUG(r, "opening explicit scope");
        !           410:                r->last->endspan = -1;
        !           411:                pos += 2;
        !           412:        } else
        !           413:                ROFF_MDEBUG(r, "opening implicit scope");
1.74      kristaps  414:
1.75    ! kristaps  415:        if ('\0' == (*bufp)[pos])
        !           416:                return(ROFF_IGN);
1.74      kristaps  417:
1.75    ! kristaps  418:        *offs = pos;
        !           419:        return(ROFF_RERUN);
1.74      kristaps  420: }
                    421:
                    422:
1.75    ! kristaps  423: #if 0
1.67      kristaps  424: static enum rofferr
                    425: roff_new_ig(ROFF_ARGS)
                    426: {
1.71      kristaps  427:        int              i;
1.67      kristaps  428:
1.72      kristaps  429:        if ( ! roffnode_push(r, tok, ln, ppos))
1.71      kristaps  430:                return(ROFF_ERR);
1.74      kristaps  431:
                    432:        /*
                    433:         * Other macros (not `ig') using this routine have additional
                    434:         * crap here that we discard.
                    435:         */
1.71      kristaps  436:
1.72      kristaps  437:        if (ROFF_ig != tok) {
                    438:                while ((*bufp)[ppos] && ' ' != (*bufp)[ppos])
                    439:                        ppos++;
                    440:                while (' ' == (*bufp)[ppos])
                    441:                        ppos++;
                    442:        }
                    443:
1.71      kristaps  444:        i = (int)ppos;
1.72      kristaps  445:
1.71      kristaps  446:        while ((*bufp)[i] && ' ' != (*bufp)[i])
                    447:                i++;
                    448:
                    449:        if (i == (int)ppos)
                    450:                return(ROFF_IGN);
1.72      kristaps  451:
1.71      kristaps  452:        if ((*bufp)[i])
                    453:                if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, i, NULL))
                    454:                        return(ROFF_ERR);
                    455:
                    456:        /*
1.72      kristaps  457:         * If the macro has arguments, the first argument (up to the
                    458:         * next whitespace) is interpreted as an argument marking the
                    459:         * macro close.  Thus, `.ig foo' will close at `.foo'.
1.71      kristaps  460:         *
                    461:         * NOTE: the closing macro `.foo' in the above case is not
                    462:         * allowed to have leading spaces with old groff!  Thus `.foo'
                    463:         * != `. foo'.  Oh yeah, everything after the `.foo' is lost.
                    464:         * Merry fucking Christmas.
                    465:         */
                    466:
1.73      kristaps  467:        r->last->end = malloc((size_t)(i - ppos) + 1);
1.71      kristaps  468:        if (NULL == r->last->end) {
                    469:                (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
                    470:                return(ROFF_ERR);
                    471:        }
                    472:
1.73      kristaps  473:        memcpy(r->last->end, &(*bufp)[ppos], (size_t)(i - ppos));
                    474:        r->last->end[i - ppos] = '\0';
1.71      kristaps  475:
                    476:        return(ROFF_IGN);
1.67      kristaps  477: }
1.74      kristaps  478: #endif

CVSweb