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

Annotation of mandoc/eqn.c, Revision 1.12

1.12    ! kristaps    1: /*     $Id: eqn.c,v 1.11 2011/07/18 13:35:07 kristaps Exp $ */
1.1       kristaps    2: /*
                      3:  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      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.
                     16:  */
                     17: #ifdef HAVE_CONFIG_H
                     18: #include "config.h"
                     19: #endif
                     20:
                     21: #include <assert.h>
                     22: #include <stdio.h>
                     23: #include <stdlib.h>
                     24: #include <string.h>
                     25: #include <time.h>
                     26:
                     27: #include "mandoc.h"
                     28: #include "libmandoc.h"
                     29: #include "libroff.h"
                     30:
1.11      kristaps   31: #define        EQN_NEST_MAX     128 /* maximum nesting of defines */
1.12    ! kristaps   32: #define        EQN_MSG(t, x)    mandoc_msg((t), (x)->parse, (x)->eqn.ln, (x)->eqn.pos, NULL)
1.8       kristaps   33:
                     34: struct eqnpart {
                     35:        const char      *name;
                     36:        size_t           sz;
1.12    ! kristaps   37:        int             (*fp)(struct eqn_node *);
1.8       kristaps   38: };
                     39:
                     40: enum   eqnpartt {
                     41:        EQN_DEFINE = 0,
                     42:        EQN_SET,
                     43:        EQN_UNDEF,
                     44:        EQN__MAX
                     45: };
                     46:
1.12    ! kristaps   47: static struct eqn_def  *eqn_def_find(struct eqn_node *,
        !            48:                                const char *, size_t);
        !            49: static int              eqn_do_define(struct eqn_node *);
        !            50: static int              eqn_do_ign2(struct eqn_node *);
        !            51: static int              eqn_do_undef(struct eqn_node *);
        !            52: static const char      *eqn_nexttok(struct eqn_node *, size_t *);
        !            53: static const char      *eqn_next(struct eqn_node *, char, size_t *);
        !            54: static int              eqn_box(struct eqn_node *);
1.6       kristaps   55:
1.8       kristaps   56: static const struct eqnpart eqnparts[EQN__MAX] = {
                     57:        { "define", 6, eqn_do_define }, /* EQN_DEFINE */
1.11      kristaps   58:        { "set", 3, eqn_do_ign2 }, /* EQN_SET */
1.8       kristaps   59:        { "undef", 5, eqn_do_undef }, /* EQN_UNDEF */
                     60: };
                     61:
1.1       kristaps   62: /* ARGSUSED */
                     63: enum rofferr
1.6       kristaps   64: eqn_read(struct eqn_node **epp, int ln,
                     65:                const char *p, int pos, int *offs)
1.1       kristaps   66: {
1.8       kristaps   67:        size_t           sz;
                     68:        struct eqn_node *ep;
1.12    ! kristaps   69:        enum rofferr     er;
        !            70:
        !            71:        ep = *epp;
        !            72:
        !            73:        /*
        !            74:         * If we're the terminating mark, unset our equation status and
        !            75:         * validate the full equation.
        !            76:         */
1.1       kristaps   77:
                     78:        if (0 == strcmp(p, ".EN")) {
1.12    ! kristaps   79:                er = eqn_end(ep);
1.1       kristaps   80:                *epp = NULL;
1.12    ! kristaps   81:                return(er);
1.1       kristaps   82:        }
                     83:
1.12    ! kristaps   84:        /*
        !            85:         * Build up the full string, replacing all newlines with regular
        !            86:         * whitespace.
        !            87:         */
1.6       kristaps   88:
1.12    ! kristaps   89:        sz = strlen(p + pos) + 1;
        !            90:        ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1);
1.6       kristaps   91:
1.12    ! kristaps   92:        /* First invocation: nil terminate the string. */
1.8       kristaps   93:
1.12    ! kristaps   94:        if (0 == ep->sz)
        !            95:                *ep->data = '\0';
1.8       kristaps   96:
1.12    ! kristaps   97:        ep->sz += sz;
        !            98:        strlcat(ep->data, p + pos, ep->sz + 1);
        !            99:        strlcat(ep->data, " ", ep->sz + 1);
1.11      kristaps  100:        return(ROFF_IGN);
                    101: }
                    102:
1.1       kristaps  103: struct eqn_node *
1.5       kristaps  104: eqn_alloc(int pos, int line, struct mparse *parse)
1.1       kristaps  105: {
                    106:        struct eqn_node *p;
                    107:
                    108:        p = mandoc_calloc(1, sizeof(struct eqn_node));
1.5       kristaps  109:        p->parse = parse;
1.12    ! kristaps  110:        p->eqn.ln = line;
1.2       kristaps  111:        p->eqn.pos = pos;
1.1       kristaps  112:
                    113:        return(p);
                    114: }
                    115:
1.12    ! kristaps  116: enum rofferr
        !           117: eqn_end(struct eqn_node *ep)
        !           118: {
        !           119:        int              c;
        !           120:
        !           121:        /*
        !           122:         * Validate the expression.
        !           123:         * Use the grammar found in the literature.
        !           124:         */
        !           125:
        !           126:        if (0 == ep->sz)
        !           127:                return(ROFF_IGN);
        !           128:
        !           129:        while (1 == (c = eqn_box(ep)))
        !           130:                /* Keep parsing. */ ;
        !           131:
        !           132:        return(c < 0 ? ROFF_IGN : ROFF_EQN);
        !           133: }
        !           134:
        !           135: static int
        !           136: eqn_box(struct eqn_node *ep)
1.1       kristaps  137: {
1.12    ! kristaps  138:        size_t           sz;
        !           139:        const char      *start;
        !           140:        int              i;
        !           141:
        !           142:        if (NULL == (start = eqn_nexttok(ep, &sz)))
        !           143:                return(0);
        !           144:
        !           145:        for (i = 0; i < (int)EQN__MAX; i++) {
        !           146:                if (eqnparts[i].sz != sz)
        !           147:                        continue;
        !           148:                if (strncmp(eqnparts[i].name, start, sz))
        !           149:                        continue;
        !           150:                if ( ! (*eqnparts[i].fp)(ep))
        !           151:                        return(-1);
        !           152:
        !           153:                return(1);
        !           154:        }
        !           155:
        !           156:        ep->eqn.data = mandoc_realloc
        !           157:                (ep->eqn.data, ep->eqn.sz + sz + 1);
        !           158:
        !           159:        if (0 == ep->eqn.sz)
        !           160:                *ep->eqn.data = '\0';
1.1       kristaps  161:
1.12    ! kristaps  162:        ep->eqn.sz += sz;
        !           163:        strlcat(ep->eqn.data, start, ep->eqn.sz + 1);
        !           164:        return(1);
1.1       kristaps  165: }
                    166:
                    167: void
                    168: eqn_free(struct eqn_node *p)
                    169: {
1.6       kristaps  170:        int              i;
1.1       kristaps  171:
                    172:        free(p->eqn.data);
1.6       kristaps  173:
                    174:        for (i = 0; i < (int)p->defsz; i++) {
                    175:                free(p->defs[i].key);
                    176:                free(p->defs[i].val);
                    177:        }
                    178:
1.12    ! kristaps  179:        free(p->data);
1.6       kristaps  180:        free(p->defs);
1.1       kristaps  181:        free(p);
1.6       kristaps  182: }
                    183:
                    184: static const char *
1.12    ! kristaps  185: eqn_nexttok(struct eqn_node *ep, size_t *sz)
        !           186: {
        !           187:
        !           188:        return(eqn_next(ep, '"', sz));
        !           189: }
        !           190:
        !           191: static const char *
        !           192: eqn_next(struct eqn_node *ep, char quote, size_t *sz)
1.6       kristaps  193: {
1.12    ! kristaps  194:        char            *start, *next;
        !           195:        int              q, diff, lim;
        !           196:        size_t           sv, ssz;
        !           197:        struct eqn_def  *def;
        !           198:
        !           199:        if (NULL == sz)
        !           200:                sz = &ssz;
1.6       kristaps  201:
1.12    ! kristaps  202:        start = &ep->data[(int)ep->cur];
1.6       kristaps  203:        q = 0;
                    204:
                    205:        if ('\0' == *start)
                    206:                return(NULL);
                    207:
1.12    ! kristaps  208:        if (quote == *start) {
        !           209:                ep->cur++;
1.6       kristaps  210:                q = 1;
                    211:        }
                    212:
1.12    ! kristaps  213:        lim = 0;
1.6       kristaps  214:
1.12    ! kristaps  215:        sv = ep->cur;
        !           216: again:
        !           217:        if (lim >= EQN_NEST_MAX) {
        !           218:                EQN_MSG(MANDOCERR_EQNNEST, ep);
        !           219:                return(NULL);
        !           220:        }
        !           221:
        !           222:        ep->cur = sv;
        !           223:        start = &ep->data[(int)ep->cur];
        !           224:        next = q ? strchr(start, quote) : strchr(start, ' ');
        !           225:
        !           226:        if (NULL != next) {
        !           227:                *sz = (size_t)(next - start);
        !           228:                ep->cur += *sz;
1.6       kristaps  229:                if (q)
1.12    ! kristaps  230:                        ep->cur++;
        !           231:                while (' ' == ep->data[(int)ep->cur])
        !           232:                        ep->cur++;
1.6       kristaps  233:        } else {
                    234:                if (q)
1.12    ! kristaps  235:                        EQN_MSG(MANDOCERR_BADQUOTE, ep);
        !           236:                next = strchr(start, '\0');
        !           237:                *sz = (size_t)(next - start);
        !           238:                ep->cur += *sz;
        !           239:        }
        !           240:
        !           241:        if (NULL != (def = eqn_def_find(ep, start, *sz))) {
        !           242:                diff = def->valsz - *sz;
        !           243:
        !           244:                if (def->valsz > *sz) {
        !           245:                        ep->sz += diff;
        !           246:                        ep->data = mandoc_realloc(ep->data, ep->sz + 1);
        !           247:                        ep->data[ep->sz] = '\0';
        !           248:                        start = &ep->data[(int)sv];
        !           249:                }
        !           250:
        !           251:                diff = def->valsz - *sz;
        !           252:                memmove(start + *sz + diff, start + *sz,
        !           253:                                (strlen(start) - *sz) + 1);
        !           254:                memcpy(start, def->val, def->valsz);
        !           255:                goto again;
1.6       kristaps  256:        }
                    257:
                    258:        return(start);
1.8       kristaps  259: }
                    260:
                    261: static int
1.12    ! kristaps  262: eqn_do_ign2(struct eqn_node *ep)
1.8       kristaps  263: {
                    264:        const char      *start;
                    265:
1.12    ! kristaps  266:        if (NULL == (start = eqn_nexttok(ep, NULL)))
        !           267:                EQN_MSG(MANDOCERR_EQNARGS, ep);
        !           268:        else if (NULL == (start = eqn_nexttok(ep, NULL)))
        !           269:                EQN_MSG(MANDOCERR_EQNARGS, ep);
        !           270:        else
        !           271:                return(1);
1.8       kristaps  272:
1.12    ! kristaps  273:        return(0);
1.8       kristaps  274: }
                    275:
                    276: static int
1.12    ! kristaps  277: eqn_do_define(struct eqn_node *ep)
1.8       kristaps  278: {
                    279:        const char      *start;
                    280:        size_t           sz;
1.12    ! kristaps  281:        struct eqn_def  *def;
1.8       kristaps  282:        int              i;
                    283:
1.12    ! kristaps  284:        if (NULL == (start = eqn_nexttok(ep, &sz))) {
        !           285:                EQN_MSG(MANDOCERR_EQNARGS, ep);
1.8       kristaps  286:                return(0);
                    287:        }
                    288:
                    289:        /*
                    290:         * Search for a key that already exists.
1.12    ! kristaps  291:         * Create a new key if none is found.
1.8       kristaps  292:         */
                    293:
1.12    ! kristaps  294:        if (NULL == (def = eqn_def_find(ep, start, sz))) {
1.8       kristaps  295:                /* Find holes in string array. */
                    296:                for (i = 0; i < (int)ep->defsz; i++)
                    297:                        if (0 == ep->defs[i].keysz)
                    298:                                break;
                    299:
                    300:                if (i == (int)ep->defsz) {
                    301:                        ep->defsz++;
                    302:                        ep->defs = mandoc_realloc
                    303:                                (ep->defs, ep->defsz *
                    304:                                 sizeof(struct eqn_def));
1.9       kristaps  305:                        ep->defs[i].key = ep->defs[i].val = NULL;
1.8       kristaps  306:                }
                    307:
                    308:                ep->defs[i].keysz = sz;
                    309:                ep->defs[i].key = mandoc_realloc
                    310:                        (ep->defs[i].key, sz + 1);
                    311:
                    312:                memcpy(ep->defs[i].key, start, sz);
                    313:                ep->defs[i].key[(int)sz] = '\0';
1.12    ! kristaps  314:                def = &ep->defs[i];
1.8       kristaps  315:        }
                    316:
1.12    ! kristaps  317:        start = eqn_next(ep, ep->data[(int)ep->cur], &sz);
1.8       kristaps  318:
1.12    ! kristaps  319:        if (NULL == start) {
        !           320:                EQN_MSG(MANDOCERR_EQNARGS, ep);
1.8       kristaps  321:                return(0);
                    322:        }
                    323:
1.12    ! kristaps  324:        def->valsz = sz;
        !           325:        def->val = mandoc_realloc(ep->defs[i].val, sz + 1);
        !           326:        memcpy(def->val, start, sz);
        !           327:        def->val[(int)sz] = '\0';
        !           328:        return(1);
1.8       kristaps  329: }
                    330:
                    331: static int
1.12    ! kristaps  332: eqn_do_undef(struct eqn_node *ep)
1.8       kristaps  333: {
                    334:        const char      *start;
1.12    ! kristaps  335:        struct eqn_def  *def;
1.8       kristaps  336:        size_t           sz;
                    337:
1.12    ! kristaps  338:        if (NULL == (start = eqn_nexttok(ep, &sz))) {
        !           339:                EQN_MSG(MANDOCERR_EQNARGS, ep);
        !           340:                return(0);
        !           341:        } else if (NULL != (def = eqn_def_find(ep, start, sz)))
        !           342:                def->keysz = 0;
1.8       kristaps  343:
1.12    ! kristaps  344:        return(1);
        !           345: }
        !           346:
        !           347: static struct eqn_def *
        !           348: eqn_def_find(struct eqn_node *ep, const char *key, size_t sz)
        !           349: {
        !           350:        int              i;
1.8       kristaps  351:
1.12    ! kristaps  352:        for (i = 0; i < (int)ep->defsz; i++)
        !           353:                if (ep->defs[i].keysz && ep->defs[i].keysz == sz &&
        !           354:                                0 == strncmp(ep->defs[i].key, key, sz))
        !           355:                        return(&ep->defs[i]);
1.8       kristaps  356:
1.12    ! kristaps  357:        return(NULL);
1.1       kristaps  358: }

CVSweb