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

Annotation of mandoc/roff.c, Revision 1.76

1.76    ! kristaps    1: /*     $Id: roff.c,v 1.75 2010/05/16 13:49:23 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.76    ! kristaps  384:        /* FIXME: warn about end of line. */
1.74      kristaps  385:
                    386:        return(ROFF_IGN);
                    387: }
                    388:
                    389:
1.75      kristaps  390: /* ARGSUSED */
1.74      kristaps  391: static enum rofferr
1.75      kristaps  392: roff_if(ROFF_ARGS)
1.74      kristaps  393: {
                    394:
                    395:        /*
                    396:         * Read ahead past the conditional.
                    397:         * FIXME: this does not work, as conditionals don't end on
                    398:         * whitespace, but are parsed according to a formal grammar.
                    399:         * It's good enough for now, however.
                    400:         */
                    401:
                    402:        if ( ! roffnode_push(r, tok, ln, ppos))
                    403:                return(ROFF_ERR);
                    404:
1.75      kristaps  405:        while ((*bufp)[pos] && ' ' != (*bufp)[pos])
                    406:                pos++;
                    407:        while (' ' == (*bufp)[pos])
                    408:                pos++;
1.74      kristaps  409:
                    410:        /* Don't evaluate: just assume NO. */
                    411:
1.75      kristaps  412:        r->last->endspan = 1;
                    413:
                    414:        if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
                    415:                ROFF_MDEBUG(r, "opening explicit scope");
                    416:                r->last->endspan = -1;
                    417:                pos += 2;
                    418:        } else
                    419:                ROFF_MDEBUG(r, "opening implicit scope");
1.74      kristaps  420:
1.75      kristaps  421:        if ('\0' == (*bufp)[pos])
                    422:                return(ROFF_IGN);
1.74      kristaps  423:
1.75      kristaps  424:        *offs = pos;
                    425:        return(ROFF_RERUN);
1.74      kristaps  426: }

CVSweb