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

Annotation of mandoc/roff.c, Revision 1.73

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

CVSweb