[BACK]Return to eqn.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mandoc

Annotation of src/usr.bin/mandoc/eqn.c, Revision 1.4

1.4     ! schwarze    1: /*     $Id: eqn.c,v 1.3 2011/04/24 16:22:02 schwarze Exp $ */
1.1       schwarze    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>
1.4     ! schwarze   22: #include <limits.h>
        !            23: #include <stdio.h>
1.1       schwarze   24: #include <stdlib.h>
                     25: #include <string.h>
                     26:
                     27: #include "mandoc.h"
                     28: #include "libmandoc.h"
                     29: #include "libroff.h"
                     30:
1.4     ! schwarze   31: #define        EQN_NEST_MAX     128 /* maximum nesting of defines */
        !            32: #define        EQN_MSG(t, x)    mandoc_msg((t), (x)->parse, (x)->eqn.ln, (x)->eqn.pos, NULL)
        !            33:
        !            34: enum   eqn_rest {
        !            35:        EQN_DESCOPE,
        !            36:        EQN_ERR,
        !            37:        EQN_OK,
        !            38:        EQN_EOF
        !            39: };
        !            40:
        !            41: enum   eqn_symt {
        !            42:        EQNSYM_alpha,
        !            43:        EQNSYM_beta,
        !            44:        EQNSYM_chi,
        !            45:        EQNSYM_delta,
        !            46:        EQNSYM_epsilon,
        !            47:        EQNSYM_eta,
        !            48:        EQNSYM_gamma,
        !            49:        EQNSYM_iota,
        !            50:        EQNSYM_kappa,
        !            51:        EQNSYM_lambda,
        !            52:        EQNSYM_mu,
        !            53:        EQNSYM_nu,
        !            54:        EQNSYM_omega,
        !            55:        EQNSYM_omicron,
        !            56:        EQNSYM_phi,
        !            57:        EQNSYM_pi,
        !            58:        EQNSYM_ps,
        !            59:        EQNSYM_rho,
        !            60:        EQNSYM_sigma,
        !            61:        EQNSYM_tau,
        !            62:        EQNSYM_theta,
        !            63:        EQNSYM_upsilon,
        !            64:        EQNSYM_xi,
        !            65:        EQNSYM_zeta,
        !            66:        EQNSYM_DELTA,
        !            67:        EQNSYM_GAMMA,
        !            68:        EQNSYM_LAMBDA,
        !            69:        EQNSYM_OMEGA,
        !            70:        EQNSYM_PHI,
        !            71:        EQNSYM_PI,
        !            72:        EQNSYM_PSI,
        !            73:        EQNSYM_SIGMA,
        !            74:        EQNSYM_THETA,
        !            75:        EQNSYM_UPSILON,
        !            76:        EQNSYM_XI,
        !            77:        EQNSYM_inter,
        !            78:        EQNSYM_union,
        !            79:        EQNSYM_prod,
        !            80:        EQNSYM_int,
        !            81:        EQNSYM_sum,
        !            82:        EQNSYM_grad,
        !            83:        EQNSYM_del,
        !            84:        EQNSYM_times,
        !            85:        EQNSYM_cdot,
        !            86:        EQNSYM_nothing,
        !            87:        EQNSYM_approx,
        !            88:        EQNSYM_prime,
        !            89:        EQNSYM_half,
        !            90:        EQNSYM_partial,
        !            91:        EQNSYM_inf,
        !            92:        EQNSYM_muchgreat,
        !            93:        EQNSYM_muchless,
        !            94:        EQNSYM_larrow,
        !            95:        EQNSYM_rarrow,
        !            96:        EQNSYM_pm,
        !            97:        EQNSYM_nequal,
        !            98:        EQNSYM_equiv,
        !            99:        EQNSYM_lessequal,
        !           100:        EQNSYM_moreequal,
        !           101:        EQNSYM__MAX
        !           102: };
        !           103:
        !           104: enum   eqnpartt {
        !           105:        EQN_DEFINE = 0,
        !           106:        EQN_NDEFINE,
        !           107:        EQN_TDEFINE,
        !           108:        EQN_SET,
        !           109:        EQN_UNDEF,
        !           110:        EQN_GFONT,
        !           111:        EQN_GSIZE,
        !           112:        EQN_BACK,
        !           113:        EQN_FWD,
        !           114:        EQN_UP,
        !           115:        EQN_DOWN,
        !           116:        EQN__MAX
        !           117: };
        !           118:
        !           119: struct eqnstr {
        !           120:        const char      *name;
        !           121:        size_t           sz;
        !           122: };
        !           123:
        !           124: #define        STRNEQ(p1, sz1, p2, sz2) \
        !           125:        ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
        !           126: #define        EQNSTREQ(x, p, sz) \
        !           127:        STRNEQ((x)->name, (x)->sz, (p), (sz))
        !           128:
        !           129: struct eqnpart {
        !           130:        struct eqnstr    str;
        !           131:        int             (*fp)(struct eqn_node *);
        !           132: };
        !           133:
        !           134: struct eqnsym {
        !           135:        struct eqnstr    str;
        !           136:        const char      *sym;
        !           137: };
        !           138:
        !           139:
        !           140: static enum eqn_rest    eqn_box(struct eqn_node *, struct eqn_box *);
        !           141: static struct eqn_box  *eqn_box_alloc(struct eqn_node *,
        !           142:                                struct eqn_box *);
        !           143: static void             eqn_box_free(struct eqn_box *);
        !           144: static struct eqn_def  *eqn_def_find(struct eqn_node *,
        !           145:                                const char *, size_t);
        !           146: static int              eqn_do_gfont(struct eqn_node *);
        !           147: static int              eqn_do_gsize(struct eqn_node *);
        !           148: static int              eqn_do_define(struct eqn_node *);
        !           149: static int              eqn_do_ign1(struct eqn_node *);
        !           150: static int              eqn_do_ign2(struct eqn_node *);
        !           151: static int              eqn_do_tdefine(struct eqn_node *);
        !           152: static int              eqn_do_undef(struct eqn_node *);
        !           153: static enum eqn_rest    eqn_eqn(struct eqn_node *, struct eqn_box *);
        !           154: static enum eqn_rest    eqn_list(struct eqn_node *, struct eqn_box *);
        !           155: static enum eqn_rest    eqn_matrix(struct eqn_node *, struct eqn_box *);
        !           156: static const char      *eqn_nexttok(struct eqn_node *, size_t *);
        !           157: static const char      *eqn_nextrawtok(struct eqn_node *, size_t *);
        !           158: static const char      *eqn_next(struct eqn_node *,
        !           159:                                char, size_t *, int);
        !           160: static void             eqn_rewind(struct eqn_node *);
        !           161:
        !           162: static const struct eqnpart eqnparts[EQN__MAX] = {
        !           163:        { { "define", 6 }, eqn_do_define }, /* EQN_DEFINE */
        !           164:        { { "ndefine", 7 }, eqn_do_define }, /* EQN_NDEFINE */
        !           165:        { { "tdefine", 7 }, eqn_do_tdefine }, /* EQN_TDEFINE */
        !           166:        { { "set", 3 }, eqn_do_ign2 }, /* EQN_SET */
        !           167:        { { "undef", 5 }, eqn_do_undef }, /* EQN_UNDEF */
        !           168:        { { "gfont", 5 }, eqn_do_gfont }, /* EQN_GFONT */
        !           169:        { { "gsize", 5 }, eqn_do_gsize }, /* EQN_GSIZE */
        !           170:        { { "back", 4 }, eqn_do_ign1 }, /* EQN_BACK */
        !           171:        { { "fwd", 3 }, eqn_do_ign1 }, /* EQN_FWD */
        !           172:        { { "up", 2 }, eqn_do_ign1 }, /* EQN_UP */
        !           173:        { { "down", 4 }, eqn_do_ign1 }, /* EQN_DOWN */
        !           174: };
        !           175:
        !           176: static const struct eqnstr eqnmarks[EQNMARK__MAX] = {
        !           177:        { "", 0 }, /* EQNMARK_NONE */
        !           178:        { "dot", 3 }, /* EQNMARK_DOT */
        !           179:        { "dotdot", 6 }, /* EQNMARK_DOTDOT */
        !           180:        { "hat", 3 }, /* EQNMARK_HAT */
        !           181:        { "tilde", 5 }, /* EQNMARK_TILDE */
        !           182:        { "vec", 3 }, /* EQNMARK_VEC */
        !           183:        { "dyad", 4 }, /* EQNMARK_DYAD */
        !           184:        { "bar", 3 }, /* EQNMARK_BAR */
        !           185:        { "under", 5 }, /* EQNMARK_UNDER */
        !           186: };
        !           187:
        !           188: static const struct eqnstr eqnfonts[EQNFONT__MAX] = {
        !           189:        { "", 0 }, /* EQNFONT_NONE */
        !           190:        { "roman", 5 }, /* EQNFONT_ROMAN */
        !           191:        { "bold", 4 }, /* EQNFONT_BOLD */
        !           192:        { "fat", 3 }, /* EQNFONT_FAT */
        !           193:        { "italic", 6 }, /* EQNFONT_ITALIC */
        !           194: };
        !           195:
        !           196: static const struct eqnstr eqnposs[EQNPOS__MAX] = {
        !           197:        { "", 0 }, /* EQNPOS_NONE */
        !           198:        { "over", 4 }, /* EQNPOS_OVER */
        !           199:        { "sup", 3 }, /* EQNPOS_SUP */
        !           200:        { "sub", 3 }, /* EQNPOS_SUB */
        !           201:        { "to", 2 }, /* EQNPOS_TO */
        !           202:        { "from", 4 }, /* EQNPOS_FROM */
        !           203: };
        !           204:
        !           205: static const struct eqnstr eqnpiles[EQNPILE__MAX] = {
        !           206:        { "", 0 }, /* EQNPILE_NONE */
        !           207:        { "pile", 4 }, /* EQNPILE_PILE */
        !           208:        { "cpile", 5 }, /* EQNPILE_CPILE */
        !           209:        { "rpile", 5 }, /* EQNPILE_RPILE */
        !           210:        { "lpile", 5 }, /* EQNPILE_LPILE */
        !           211:        { "col", 3 }, /* EQNPILE_COL */
        !           212:        { "ccol", 4 }, /* EQNPILE_CCOL */
        !           213:        { "rcol", 4 }, /* EQNPILE_RCOL */
        !           214:        { "lcol", 4 }, /* EQNPILE_LCOL */
        !           215: };
        !           216:
        !           217: static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
        !           218:        { { "alpha", 5 }, "*a" }, /* EQNSYM_alpha */
        !           219:        { { "beta", 4 }, "*b" }, /* EQNSYM_beta */
        !           220:        { { "chi", 3 }, "*x" }, /* EQNSYM_chi */
        !           221:        { { "delta", 5 }, "*d" }, /* EQNSYM_delta */
        !           222:        { { "epsilon", 7 }, "*e" }, /* EQNSYM_epsilon */
        !           223:        { { "eta", 3 }, "*y" }, /* EQNSYM_eta */
        !           224:        { { "gamma", 5 }, "*g" }, /* EQNSYM_gamma */
        !           225:        { { "iota", 4 }, "*i" }, /* EQNSYM_iota */
        !           226:        { { "kappa", 5 }, "*k" }, /* EQNSYM_kappa */
        !           227:        { { "lambda", 6 }, "*l" }, /* EQNSYM_lambda */
        !           228:        { { "mu", 2 }, "*m" }, /* EQNSYM_mu */
        !           229:        { { "nu", 2 }, "*n" }, /* EQNSYM_nu */
        !           230:        { { "omega", 5 }, "*w" }, /* EQNSYM_omega */
        !           231:        { { "omicron", 7 }, "*o" }, /* EQNSYM_omicron */
        !           232:        { { "phi", 3 }, "*f" }, /* EQNSYM_phi */
        !           233:        { { "pi", 2 }, "*p" }, /* EQNSYM_pi */
        !           234:        { { "psi", 2 }, "*q" }, /* EQNSYM_psi */
        !           235:        { { "rho", 3 }, "*r" }, /* EQNSYM_rho */
        !           236:        { { "sigma", 5 }, "*s" }, /* EQNSYM_sigma */
        !           237:        { { "tau", 3 }, "*t" }, /* EQNSYM_tau */
        !           238:        { { "theta", 5 }, "*h" }, /* EQNSYM_theta */
        !           239:        { { "upsilon", 7 }, "*u" }, /* EQNSYM_upsilon */
        !           240:        { { "xi", 2 }, "*c" }, /* EQNSYM_xi */
        !           241:        { { "zeta", 4 }, "*z" }, /* EQNSYM_zeta */
        !           242:        { { "DELTA", 5 }, "*D" }, /* EQNSYM_DELTA */
        !           243:        { { "GAMMA", 5 }, "*G" }, /* EQNSYM_GAMMA */
        !           244:        { { "LAMBDA", 6 }, "*L" }, /* EQNSYM_LAMBDA */
        !           245:        { { "OMEGA", 5 }, "*W" }, /* EQNSYM_OMEGA */
        !           246:        { { "PHI", 3 }, "*F" }, /* EQNSYM_PHI */
        !           247:        { { "PI", 2 }, "*P" }, /* EQNSYM_PI */
        !           248:        { { "PSI", 3 }, "*Q" }, /* EQNSYM_PSI */
        !           249:        { { "SIGMA", 5 }, "*S" }, /* EQNSYM_SIGMA */
        !           250:        { { "THETA", 5 }, "*H" }, /* EQNSYM_THETA */
        !           251:        { { "UPSILON", 7 }, "*U" }, /* EQNSYM_UPSILON */
        !           252:        { { "XI", 2 }, "*C" }, /* EQNSYM_XI */
        !           253:        { { "inter", 5 }, "ca" }, /* EQNSYM_inter */
        !           254:        { { "union", 5 }, "cu" }, /* EQNSYM_union */
        !           255:        { { "prod", 4 }, "product" }, /* EQNSYM_prod */
        !           256:        { { "int", 3 }, "integral" }, /* EQNSYM_int */
        !           257:        { { "sum", 3 }, "sum" }, /* EQNSYM_sum */
        !           258:        { { "grad", 4 }, "gr" }, /* EQNSYM_grad */
        !           259:        { { "del", 3 }, "gr" }, /* EQNSYM_del */
        !           260:        { { "times", 5 }, "mu" }, /* EQNSYM_times */
        !           261:        { { "cdot", 4 }, "pc" }, /* EQNSYM_cdot */
        !           262:        { { "nothing", 7 }, "&" }, /* EQNSYM_nothing */
        !           263:        { { "approx", 6 }, "~~" }, /* EQNSYM_approx */
        !           264:        { { "prime", 5 }, "aq" }, /* EQNSYM_prime */
        !           265:        { { "half", 4 }, "12" }, /* EQNSYM_half */
        !           266:        { { "partial", 7 }, "pd" }, /* EQNSYM_partial */
        !           267:        { { "inf", 3 }, "if" }, /* EQNSYM_inf */
        !           268:        { { ">>", 2 }, ">>" }, /* EQNSYM_muchgreat */
        !           269:        { { "<<", 2 }, "<<" }, /* EQNSYM_muchless */
        !           270:        { { "<-", 2 }, "<-" }, /* EQNSYM_larrow */
        !           271:        { { "->", 2 }, "->" }, /* EQNSYM_rarrow */
        !           272:        { { "+-", 2 }, "+-" }, /* EQNSYM_pm */
        !           273:        { { "!=", 2 }, "!=" }, /* EQNSYM_nequal */
        !           274:        { { "==", 2 }, "==" }, /* EQNSYM_equiv */
        !           275:        { { "<=", 2 }, "<=" }, /* EQNSYM_lessequal */
        !           276:        { { ">=", 2 }, ">=" }, /* EQNSYM_moreequal */
        !           277: };
        !           278:
1.1       schwarze  279: /* ARGSUSED */
                    280: enum rofferr
1.4     ! schwarze  281: eqn_read(struct eqn_node **epp, int ln,
        !           282:                const char *p, int pos, int *offs)
1.1       schwarze  283: {
                    284:        size_t           sz;
                    285:        struct eqn_node *ep;
1.4     ! schwarze  286:        enum rofferr     er;
1.1       schwarze  287:
                    288:        ep = *epp;
                    289:
1.4     ! schwarze  290:        /*
        !           291:         * If we're the terminating mark, unset our equation status and
        !           292:         * validate the full equation.
        !           293:         */
        !           294:
        !           295:        if (0 == strncmp(p, ".EN", 3)) {
        !           296:                er = eqn_end(epp);
        !           297:                p += 3;
        !           298:                while (' ' == *p || '\t' == *p)
        !           299:                        p++;
        !           300:                if ('\0' == *p)
        !           301:                        return(er);
        !           302:                mandoc_msg(MANDOCERR_ARGSLOST, ep->parse, ln, pos, NULL);
        !           303:                return(er);
        !           304:        }
1.1       schwarze  305:
1.4     ! schwarze  306:        /*
        !           307:         * Build up the full string, replacing all newlines with regular
        !           308:         * whitespace.
        !           309:         */
        !           310:
        !           311:        sz = strlen(p + pos) + 1;
        !           312:        ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1);
        !           313:
        !           314:        /* First invocation: nil terminate the string. */
        !           315:
        !           316:        if (0 == ep->sz)
        !           317:                *ep->data = '\0';
        !           318:
        !           319:        ep->sz += sz;
        !           320:        strlcat(ep->data, p + pos, ep->sz + 1);
        !           321:        strlcat(ep->data, " ", ep->sz + 1);
1.1       schwarze  322:        return(ROFF_IGN);
                    323: }
                    324:
                    325: struct eqn_node *
1.4     ! schwarze  326: eqn_alloc(const char *name, int pos, int line, struct mparse *parse)
1.1       schwarze  327: {
                    328:        struct eqn_node *p;
1.4     ! schwarze  329:        size_t           sz;
        !           330:        const char      *end;
1.1       schwarze  331:
                    332:        p = mandoc_calloc(1, sizeof(struct eqn_node));
1.4     ! schwarze  333:
        !           334:        if (name && '\0' != *name) {
        !           335:                sz = strlen(name);
        !           336:                assert(sz);
        !           337:                do {
        !           338:                        sz--;
        !           339:                        end = name + (int)sz;
        !           340:                } while (' ' == *end || '\t' == *end);
        !           341:                p->eqn.name = mandoc_strndup(name, sz + 1);
        !           342:        }
        !           343:
        !           344:        p->parse = parse;
        !           345:        p->eqn.ln = line;
1.1       schwarze  346:        p->eqn.pos = pos;
1.4     ! schwarze  347:        p->gsize = EQN_DEFSIZE;
1.1       schwarze  348:
                    349:        return(p);
                    350: }
                    351:
1.4     ! schwarze  352: enum rofferr
        !           353: eqn_end(struct eqn_node **epp)
        !           354: {
        !           355:        struct eqn_node *ep;
        !           356:        struct eqn_box  *root;
        !           357:        enum eqn_rest    c;
        !           358:
        !           359:        ep = *epp;
        !           360:        *epp = NULL;
        !           361:
        !           362:        ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box));
        !           363:
        !           364:        root = ep->eqn.root;
        !           365:        root->type = EQN_ROOT;
        !           366:
        !           367:        if (0 == ep->sz)
        !           368:                return(ROFF_IGN);
        !           369:
        !           370:        if (EQN_DESCOPE == (c = eqn_eqn(ep, root))) {
        !           371:                EQN_MSG(MANDOCERR_EQNNSCOPE, ep);
        !           372:                c = EQN_ERR;
        !           373:        }
        !           374:
        !           375:        return(EQN_EOF == c ? ROFF_EQN : ROFF_IGN);
        !           376: }
        !           377:
        !           378: static enum eqn_rest
        !           379: eqn_eqn(struct eqn_node *ep, struct eqn_box *last)
        !           380: {
        !           381:        struct eqn_box  *bp;
        !           382:        enum eqn_rest    c;
        !           383:
        !           384:        bp = eqn_box_alloc(ep, last);
        !           385:        bp->type = EQN_SUBEXPR;
        !           386:
        !           387:        while (EQN_OK == (c = eqn_box(ep, bp)))
        !           388:                /* Spin! */ ;
        !           389:
        !           390:        return(c);
        !           391: }
        !           392:
        !           393: static enum eqn_rest
        !           394: eqn_matrix(struct eqn_node *ep, struct eqn_box *last)
        !           395: {
        !           396:        struct eqn_box  *bp;
        !           397:        const char      *start;
        !           398:        size_t           sz;
        !           399:        enum eqn_rest    c;
        !           400:
        !           401:        bp = eqn_box_alloc(ep, last);
        !           402:        bp->type = EQN_MATRIX;
        !           403:
        !           404:        if (NULL == (start = eqn_nexttok(ep, &sz))) {
        !           405:                EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           406:                return(EQN_ERR);
        !           407:        }
        !           408:        if ( ! STRNEQ(start, sz, "{", 1)) {
        !           409:                EQN_MSG(MANDOCERR_EQNSYNT, ep);
        !           410:                return(EQN_ERR);
        !           411:        }
        !           412:
        !           413:        while (EQN_OK == (c = eqn_box(ep, bp)))
        !           414:                switch (bp->last->pile) {
        !           415:                case (EQNPILE_LCOL):
        !           416:                        /* FALLTHROUGH */
        !           417:                case (EQNPILE_CCOL):
        !           418:                        /* FALLTHROUGH */
        !           419:                case (EQNPILE_RCOL):
        !           420:                        continue;
        !           421:                default:
        !           422:                        EQN_MSG(MANDOCERR_EQNSYNT, ep);
        !           423:                        return(EQN_ERR);
        !           424:                };
        !           425:
        !           426:        if (EQN_DESCOPE != c) {
        !           427:                if (EQN_EOF == c)
        !           428:                        EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           429:                return(EQN_ERR);
        !           430:        }
        !           431:
        !           432:        eqn_rewind(ep);
        !           433:        start = eqn_nexttok(ep, &sz);
        !           434:        assert(start);
        !           435:        if (STRNEQ(start, sz, "}", 1))
        !           436:                return(EQN_OK);
        !           437:
        !           438:        EQN_MSG(MANDOCERR_EQNBADSCOPE, ep);
        !           439:        return(EQN_ERR);
        !           440: }
        !           441:
        !           442: static enum eqn_rest
        !           443: eqn_list(struct eqn_node *ep, struct eqn_box *last)
        !           444: {
        !           445:        struct eqn_box  *bp;
        !           446:        const char      *start;
        !           447:        size_t           sz;
        !           448:        enum eqn_rest    c;
        !           449:
        !           450:        bp = eqn_box_alloc(ep, last);
        !           451:        bp->type = EQN_LIST;
        !           452:
        !           453:        if (NULL == (start = eqn_nexttok(ep, &sz))) {
        !           454:                EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           455:                return(EQN_ERR);
        !           456:        }
        !           457:        if ( ! STRNEQ(start, sz, "{", 1)) {
        !           458:                EQN_MSG(MANDOCERR_EQNSYNT, ep);
        !           459:                return(EQN_ERR);
        !           460:        }
        !           461:
        !           462:        while (EQN_DESCOPE == (c = eqn_eqn(ep, bp))) {
        !           463:                eqn_rewind(ep);
        !           464:                start = eqn_nexttok(ep, &sz);
        !           465:                assert(start);
        !           466:                if ( ! STRNEQ(start, sz, "above", 5))
        !           467:                        break;
        !           468:        }
        !           469:
        !           470:        if (EQN_DESCOPE != c) {
        !           471:                if (EQN_ERR != c)
        !           472:                        EQN_MSG(MANDOCERR_EQNSCOPE, ep);
        !           473:                return(EQN_ERR);
        !           474:        }
        !           475:
        !           476:        eqn_rewind(ep);
        !           477:        start = eqn_nexttok(ep, &sz);
        !           478:        assert(start);
        !           479:        if (STRNEQ(start, sz, "}", 1))
        !           480:                return(EQN_OK);
        !           481:
        !           482:        EQN_MSG(MANDOCERR_EQNBADSCOPE, ep);
        !           483:        return(EQN_ERR);
        !           484: }
        !           485:
        !           486: static enum eqn_rest
        !           487: eqn_box(struct eqn_node *ep, struct eqn_box *last)
1.1       schwarze  488: {
1.4     ! schwarze  489:        size_t           sz;
        !           490:        const char      *start;
        !           491:        char            *left;
        !           492:        char             sym[64];
        !           493:        enum eqn_rest    c;
        !           494:        int              i, size;
        !           495:        struct eqn_box  *bp;
        !           496:
        !           497:        if (NULL == (start = eqn_nexttok(ep, &sz)))
        !           498:                return(EQN_EOF);
        !           499:
        !           500:        if (STRNEQ(start, sz, "}", 1))
        !           501:                return(EQN_DESCOPE);
        !           502:        else if (STRNEQ(start, sz, "right", 5))
        !           503:                return(EQN_DESCOPE);
        !           504:        else if (STRNEQ(start, sz, "above", 5))
        !           505:                return(EQN_DESCOPE);
        !           506:        else if (STRNEQ(start, sz, "mark", 4))
        !           507:                return(EQN_OK);
        !           508:        else if (STRNEQ(start, sz, "lineup", 6))
        !           509:                return(EQN_OK);
        !           510:
        !           511:        for (i = 0; i < (int)EQN__MAX; i++) {
        !           512:                if ( ! EQNSTREQ(&eqnparts[i].str, start, sz))
        !           513:                        continue;
        !           514:                return((*eqnparts[i].fp)(ep) ?
        !           515:                                EQN_OK : EQN_ERR);
        !           516:        }
        !           517:
        !           518:        if (STRNEQ(start, sz, "{", 1)) {
        !           519:                if (EQN_DESCOPE != (c = eqn_eqn(ep, last))) {
        !           520:                        if (EQN_ERR != c)
        !           521:                                EQN_MSG(MANDOCERR_EQNSCOPE, ep);
        !           522:                        return(EQN_ERR);
        !           523:                }
        !           524:                eqn_rewind(ep);
        !           525:                start = eqn_nexttok(ep, &sz);
        !           526:                assert(start);
        !           527:                if (STRNEQ(start, sz, "}", 1))
        !           528:                        return(EQN_OK);
        !           529:                EQN_MSG(MANDOCERR_EQNBADSCOPE, ep);
        !           530:                return(EQN_ERR);
        !           531:        }
        !           532:
        !           533:        for (i = 0; i < (int)EQNPILE__MAX; i++) {
        !           534:                if ( ! EQNSTREQ(&eqnpiles[i], start, sz))
        !           535:                        continue;
        !           536:                if (EQN_OK == (c = eqn_list(ep, last)))
        !           537:                        last->last->pile = (enum eqn_pilet)i;
        !           538:                return(c);
        !           539:        }
        !           540:
        !           541:        if (STRNEQ(start, sz, "matrix", 6))
        !           542:                return(eqn_matrix(ep, last));
        !           543:
        !           544:        if (STRNEQ(start, sz, "left", 4)) {
        !           545:                if (NULL == (start = eqn_nexttok(ep, &sz))) {
        !           546:                        EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           547:                        return(EQN_ERR);
        !           548:                }
        !           549:                left = mandoc_strndup(start, sz);
        !           550:                c = eqn_eqn(ep, last);
        !           551:                if (last->last)
        !           552:                        last->last->left = left;
        !           553:                else
        !           554:                        free(left);
        !           555:                if (EQN_DESCOPE != c)
        !           556:                        return(c);
        !           557:                assert(last->last);
        !           558:                eqn_rewind(ep);
        !           559:                start = eqn_nexttok(ep, &sz);
        !           560:                assert(start);
        !           561:                if ( ! STRNEQ(start, sz, "right", 5))
        !           562:                        return(EQN_DESCOPE);
        !           563:                if (NULL == (start = eqn_nexttok(ep, &sz))) {
        !           564:                        EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           565:                        return(EQN_ERR);
        !           566:                }
        !           567:                last->last->right = mandoc_strndup(start, sz);
        !           568:                return(EQN_OK);
        !           569:        }
        !           570:
        !           571:        for (i = 0; i < (int)EQNPOS__MAX; i++) {
        !           572:                if ( ! EQNSTREQ(&eqnposs[i], start, sz))
        !           573:                        continue;
        !           574:                if (NULL == last->last) {
        !           575:                        EQN_MSG(MANDOCERR_EQNSYNT, ep);
        !           576:                        return(EQN_ERR);
        !           577:                }
        !           578:                last->last->pos = (enum eqn_post)i;
        !           579:                if (EQN_EOF == (c = eqn_box(ep, last))) {
        !           580:                        EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           581:                        return(EQN_ERR);
        !           582:                }
        !           583:                return(c);
        !           584:        }
        !           585:
        !           586:        for (i = 0; i < (int)EQNMARK__MAX; i++) {
        !           587:                if ( ! EQNSTREQ(&eqnmarks[i], start, sz))
        !           588:                        continue;
        !           589:                if (NULL == last->last) {
        !           590:                        EQN_MSG(MANDOCERR_EQNSYNT, ep);
        !           591:                        return(EQN_ERR);
        !           592:                }
        !           593:                last->last->mark = (enum eqn_markt)i;
        !           594:                if (EQN_EOF == (c = eqn_box(ep, last))) {
        !           595:                        EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           596:                        return(EQN_ERR);
        !           597:                }
        !           598:                return(c);
        !           599:        }
        !           600:
        !           601:        for (i = 0; i < (int)EQNFONT__MAX; i++) {
        !           602:                if ( ! EQNSTREQ(&eqnfonts[i], start, sz))
        !           603:                        continue;
        !           604:                if (EQN_EOF == (c = eqn_box(ep, last))) {
        !           605:                        EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           606:                        return(EQN_ERR);
        !           607:                } else if (EQN_OK == c)
        !           608:                        last->last->font = (enum eqn_fontt)i;
        !           609:                return(c);
        !           610:        }
1.1       schwarze  611:
1.4     ! schwarze  612:        if (STRNEQ(start, sz, "size", 4)) {
        !           613:                if (NULL == (start = eqn_nexttok(ep, &sz))) {
        !           614:                        EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           615:                        return(EQN_ERR);
        !           616:                }
        !           617:                size = mandoc_strntoi(start, sz, 10);
        !           618:                if (EQN_EOF == (c = eqn_box(ep, last))) {
        !           619:                        EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           620:                        return(EQN_ERR);
        !           621:                } else if (EQN_OK != c)
        !           622:                        return(c);
        !           623:                last->last->size = size;
        !           624:        }
        !           625:
        !           626:        bp = eqn_box_alloc(ep, last);
        !           627:        bp->type = EQN_TEXT;
        !           628:        for (i = 0; i < (int)EQNSYM__MAX; i++)
        !           629:                if (EQNSTREQ(&eqnsyms[i].str, start, sz)) {
        !           630:                        sym[63] = '\0';
        !           631:                        snprintf(sym, 62, "\\[%s]", eqnsyms[i].sym);
        !           632:                        bp->text = mandoc_strdup(sym);
        !           633:                        return(EQN_OK);
        !           634:                }
        !           635:
        !           636:        bp->text = mandoc_strndup(start, sz);
        !           637:        return(EQN_OK);
1.1       schwarze  638: }
                    639:
                    640: void
                    641: eqn_free(struct eqn_node *p)
                    642: {
1.4     ! schwarze  643:        int              i;
        !           644:
        !           645:        eqn_box_free(p->eqn.root);
        !           646:
        !           647:        for (i = 0; i < (int)p->defsz; i++) {
        !           648:                free(p->defs[i].key);
        !           649:                free(p->defs[i].val);
        !           650:        }
1.1       schwarze  651:
1.4     ! schwarze  652:        free(p->eqn.name);
        !           653:        free(p->data);
        !           654:        free(p->defs);
1.1       schwarze  655:        free(p);
1.4     ! schwarze  656: }
        !           657:
        !           658: static struct eqn_box *
        !           659: eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
        !           660: {
        !           661:        struct eqn_box  *bp;
        !           662:
        !           663:        bp = mandoc_calloc(1, sizeof(struct eqn_box));
        !           664:        bp->parent = parent;
        !           665:        bp->size = ep->gsize;
        !           666:
        !           667:        if (NULL == parent->first)
        !           668:                parent->first = bp;
        !           669:        else
        !           670:                parent->last->next = bp;
        !           671:
        !           672:        parent->last = bp;
        !           673:        return(bp);
        !           674: }
        !           675:
        !           676: static void
        !           677: eqn_box_free(struct eqn_box *bp)
        !           678: {
        !           679:
        !           680:        if (bp->first)
        !           681:                eqn_box_free(bp->first);
        !           682:        if (bp->next)
        !           683:                eqn_box_free(bp->next);
        !           684:
        !           685:        free(bp->text);
        !           686:        free(bp->left);
        !           687:        free(bp->right);
        !           688:        free(bp);
        !           689: }
        !           690:
        !           691: static const char *
        !           692: eqn_nextrawtok(struct eqn_node *ep, size_t *sz)
        !           693: {
        !           694:
        !           695:        return(eqn_next(ep, '"', sz, 0));
        !           696: }
        !           697:
        !           698: static const char *
        !           699: eqn_nexttok(struct eqn_node *ep, size_t *sz)
        !           700: {
        !           701:
        !           702:        return(eqn_next(ep, '"', sz, 1));
        !           703: }
        !           704:
        !           705: static void
        !           706: eqn_rewind(struct eqn_node *ep)
        !           707: {
        !           708:
        !           709:        ep->cur = ep->rew;
        !           710: }
        !           711:
        !           712: static const char *
        !           713: eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl)
        !           714: {
        !           715:        char            *start, *next;
        !           716:        int              q, diff, lim;
        !           717:        size_t           ssz, dummy;
        !           718:        struct eqn_def  *def;
        !           719:
        !           720:        if (NULL == sz)
        !           721:                sz = &dummy;
        !           722:
        !           723:        lim = 0;
        !           724:        ep->rew = ep->cur;
        !           725: again:
        !           726:        /* Prevent self-definitions. */
        !           727:
        !           728:        if (lim >= EQN_NEST_MAX) {
        !           729:                EQN_MSG(MANDOCERR_ROFFLOOP, ep);
        !           730:                return(NULL);
        !           731:        }
        !           732:
        !           733:        ep->cur = ep->rew;
        !           734:        start = &ep->data[(int)ep->cur];
        !           735:        q = 0;
        !           736:
        !           737:        if ('\0' == *start)
        !           738:                return(NULL);
        !           739:
        !           740:        if (quote == *start) {
        !           741:                ep->cur++;
        !           742:                q = 1;
        !           743:        }
        !           744:
        !           745:        start = &ep->data[(int)ep->cur];
        !           746:
        !           747:        if ( ! q) {
        !           748:                if ('{' == *start || '}' == *start)
        !           749:                        ssz = 1;
        !           750:                else
        !           751:                        ssz = strcspn(start + 1, " ^~\"{}\t") + 1;
        !           752:                next = start + (int)ssz;
        !           753:                if ('\0' == *next)
        !           754:                        next = NULL;
        !           755:        } else
        !           756:                next = strchr(start, quote);
        !           757:
        !           758:        if (NULL != next) {
        !           759:                *sz = (size_t)(next - start);
        !           760:                ep->cur += *sz;
        !           761:                if (q)
        !           762:                        ep->cur++;
        !           763:                while (' ' == ep->data[(int)ep->cur] ||
        !           764:                                '\t' == ep->data[(int)ep->cur] ||
        !           765:                                '^' == ep->data[(int)ep->cur] ||
        !           766:                                '~' == ep->data[(int)ep->cur])
        !           767:                        ep->cur++;
        !           768:        } else {
        !           769:                if (q)
        !           770:                        EQN_MSG(MANDOCERR_BADQUOTE, ep);
        !           771:                next = strchr(start, '\0');
        !           772:                *sz = (size_t)(next - start);
        !           773:                ep->cur += *sz;
        !           774:        }
        !           775:
        !           776:        /* Quotes aren't expanded for values. */
        !           777:
        !           778:        if (q || ! repl)
        !           779:                return(start);
        !           780:
        !           781:        if (NULL != (def = eqn_def_find(ep, start, *sz))) {
        !           782:                diff = def->valsz - *sz;
        !           783:
        !           784:                if (def->valsz > *sz) {
        !           785:                        ep->sz += diff;
        !           786:                        ep->data = mandoc_realloc(ep->data, ep->sz + 1);
        !           787:                        ep->data[ep->sz] = '\0';
        !           788:                        start = &ep->data[(int)ep->rew];
        !           789:                }
        !           790:
        !           791:                diff = def->valsz - *sz;
        !           792:                memmove(start + *sz + diff, start + *sz,
        !           793:                                (strlen(start) - *sz) + 1);
        !           794:                memcpy(start, def->val, def->valsz);
        !           795:                goto again;
        !           796:        }
        !           797:
        !           798:        return(start);
        !           799: }
        !           800:
        !           801: static int
        !           802: eqn_do_ign1(struct eqn_node *ep)
        !           803: {
        !           804:
        !           805:        if (NULL == eqn_nextrawtok(ep, NULL))
        !           806:                EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           807:        else
        !           808:                return(1);
        !           809:
        !           810:        return(0);
        !           811: }
        !           812:
        !           813: static int
        !           814: eqn_do_ign2(struct eqn_node *ep)
        !           815: {
        !           816:
        !           817:        if (NULL == eqn_nextrawtok(ep, NULL))
        !           818:                EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           819:        else if (NULL == eqn_nextrawtok(ep, NULL))
        !           820:                EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           821:        else
        !           822:                return(1);
        !           823:
        !           824:        return(0);
        !           825: }
        !           826:
        !           827: static int
        !           828: eqn_do_tdefine(struct eqn_node *ep)
        !           829: {
        !           830:
        !           831:        if (NULL == eqn_nextrawtok(ep, NULL))
        !           832:                EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           833:        else if (NULL == eqn_next(ep, ep->data[(int)ep->cur], NULL, 0))
        !           834:                EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           835:        else
        !           836:                return(1);
        !           837:
        !           838:        return(0);
        !           839: }
        !           840:
        !           841: static int
        !           842: eqn_do_define(struct eqn_node *ep)
        !           843: {
        !           844:        const char      *start;
        !           845:        size_t           sz;
        !           846:        struct eqn_def  *def;
        !           847:        int              i;
        !           848:
        !           849:        if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
        !           850:                EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           851:                return(0);
        !           852:        }
        !           853:
        !           854:        /*
        !           855:         * Search for a key that already exists.
        !           856:         * Create a new key if none is found.
        !           857:         */
        !           858:
        !           859:        if (NULL == (def = eqn_def_find(ep, start, sz))) {
        !           860:                /* Find holes in string array. */
        !           861:                for (i = 0; i < (int)ep->defsz; i++)
        !           862:                        if (0 == ep->defs[i].keysz)
        !           863:                                break;
        !           864:
        !           865:                if (i == (int)ep->defsz) {
        !           866:                        ep->defsz++;
        !           867:                        ep->defs = mandoc_realloc
        !           868:                                (ep->defs, ep->defsz *
        !           869:                                 sizeof(struct eqn_def));
        !           870:                        ep->defs[i].key = ep->defs[i].val = NULL;
        !           871:                }
        !           872:
        !           873:                ep->defs[i].keysz = sz;
        !           874:                ep->defs[i].key = mandoc_realloc
        !           875:                        (ep->defs[i].key, sz + 1);
        !           876:
        !           877:                memcpy(ep->defs[i].key, start, sz);
        !           878:                ep->defs[i].key[(int)sz] = '\0';
        !           879:                def = &ep->defs[i];
        !           880:        }
        !           881:
        !           882:        start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0);
        !           883:
        !           884:        if (NULL == start) {
        !           885:                EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           886:                return(0);
        !           887:        }
        !           888:
        !           889:        def->valsz = sz;
        !           890:        def->val = mandoc_realloc(def->val, sz + 1);
        !           891:        memcpy(def->val, start, sz);
        !           892:        def->val[(int)sz] = '\0';
        !           893:        return(1);
        !           894: }
        !           895:
        !           896: static int
        !           897: eqn_do_gfont(struct eqn_node *ep)
        !           898: {
        !           899:
        !           900:        if (NULL == eqn_nextrawtok(ep, NULL)) {
        !           901:                EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           902:                return(0);
        !           903:        }
        !           904:        return(1);
        !           905: }
        !           906:
        !           907: static int
        !           908: eqn_do_gsize(struct eqn_node *ep)
        !           909: {
        !           910:        const char      *start;
        !           911:        size_t           sz;
        !           912:
        !           913:        if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
        !           914:                EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           915:                return(0);
        !           916:        }
        !           917:        ep->gsize = mandoc_strntoi(start, sz, 10);
        !           918:        return(1);
        !           919: }
        !           920:
        !           921: static int
        !           922: eqn_do_undef(struct eqn_node *ep)
        !           923: {
        !           924:        const char      *start;
        !           925:        struct eqn_def  *def;
        !           926:        size_t           sz;
        !           927:
        !           928:        if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
        !           929:                EQN_MSG(MANDOCERR_EQNEOF, ep);
        !           930:                return(0);
        !           931:        } else if (NULL != (def = eqn_def_find(ep, start, sz)))
        !           932:                def->keysz = 0;
        !           933:
        !           934:        return(1);
        !           935: }
        !           936:
        !           937: static struct eqn_def *
        !           938: eqn_def_find(struct eqn_node *ep, const char *key, size_t sz)
        !           939: {
        !           940:        int              i;
        !           941:
        !           942:        for (i = 0; i < (int)ep->defsz; i++)
        !           943:                if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
        !           944:                                        ep->defs[i].keysz, key, sz))
        !           945:                        return(&ep->defs[i]);
        !           946:
        !           947:        return(NULL);
1.1       schwarze  948: }