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

Annotation of mandoc/roff.c, Revision 1.74

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

CVSweb