=================================================================== RCS file: /cvs/mandoc/eqn.c,v retrieving revision 1.11 retrieving revision 1.22 diff -u -p -r1.11 -r1.22 --- mandoc/eqn.c 2011/07/18 13:35:07 1.11 +++ mandoc/eqn.c 2011/07/22 00:16:37 1.22 @@ -1,4 +1,4 @@ -/* $Id: eqn.c,v 1.11 2011/07/18 13:35:07 kristaps Exp $ */ +/* $Id: eqn.c,v 1.22 2011/07/22 00:16:37 kristaps Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons * @@ -19,6 +19,7 @@ #endif #include +#include #include #include #include @@ -29,18 +30,25 @@ #include "libroff.h" #define EQN_NEST_MAX 128 /* maximum nesting of defines */ +#define EQN_MSG(t, x) mandoc_msg((t), (x)->parse, (x)->eqn.ln, (x)->eqn.pos, NULL) -#define EQN_ARGS struct eqn_node *ep, \ - int ln, \ - int pos, \ - const char **end +enum eqn_rest { + EQN_DESCOPE, + EQN_ERR, + EQN_OK, + EQN_EOF +}; -struct eqnpart { +struct eqnstr { const char *name; size_t sz; - int (*fp)(EQN_ARGS); }; +struct eqnpart { + struct eqnstr str; + int (*fp)(struct eqn_node *); +}; + enum eqnpartt { EQN_DEFINE = 0, EQN_SET, @@ -48,21 +56,62 @@ enum eqnpartt { EQN__MAX }; -static void eqn_append(struct eqn_node *, - struct mparse *, int, - int, const char *, int); -static int eqn_do_define(EQN_ARGS); -static int eqn_do_ign2(EQN_ARGS); -static int eqn_do_undef(EQN_ARGS); -static const char *eqn_nexttok(struct mparse *, int, int, - const char **, size_t *); +static struct eqn_box *eqn_box_alloc(struct eqn_box *); +static void eqn_box_free(struct eqn_box *); +static struct eqn_def *eqn_def_find(struct eqn_node *, + const char *, size_t); +static int eqn_do_define(struct eqn_node *); +static int eqn_do_set(struct eqn_node *); +static int eqn_do_undef(struct eqn_node *); +static const char *eqn_nexttok(struct eqn_node *, size_t *); +static const char *eqn_nextrawtok(struct eqn_node *, size_t *); +static const char *eqn_next(struct eqn_node *, + char, size_t *, int); +static void eqn_rewind(struct eqn_node *); +static enum eqn_rest eqn_eqn(struct eqn_node *, struct eqn_box *); +static enum eqn_rest eqn_box(struct eqn_node *, struct eqn_box *); static const struct eqnpart eqnparts[EQN__MAX] = { - { "define", 6, eqn_do_define }, /* EQN_DEFINE */ - { "set", 3, eqn_do_ign2 }, /* EQN_SET */ - { "undef", 5, eqn_do_undef }, /* EQN_UNDEF */ + { { "define", 6 }, eqn_do_define }, /* EQN_DEFINE */ + { { "set", 3 }, eqn_do_set }, /* EQN_SET */ + { { "undef", 5 }, eqn_do_undef }, /* EQN_UNDEF */ }; +static const struct eqnstr eqnmarks[EQNMARK__MAX] = { + { "", 0 }, /* EQNMARK_NONE */ + { "dot", 3 }, /* EQNMARK_DOT */ + { "dotdot", 6 }, /* EQNMARK_DOTDOT */ + { "hat", 3 }, /* EQNMARK_HAT */ + { "tilde", 5 }, /* EQNMARK_TILDE */ + { "vec", 3 }, /* EQNMARK_VEC */ + { "dyad", 4 }, /* EQNMARK_DYAD */ + { "bar", 3 }, /* EQNMARK_BAR */ + { "under", 5 }, /* EQNMARK_UNDER */ +}; + +static const struct eqnstr eqnfonts[EQNFONT__MAX] = { + { "", 0 }, /* EQNFONT_NONE */ + { "roman", 5 }, /* EQNFONT_ROMAN */ + { "bold", 4 }, /* EQNFONT_BOLD */ + { "italic", 6 }, /* EQNFONT_ITALIC */ +}; + +static const struct eqnstr eqnposs[EQNPOS__MAX] = { + { "", 0 }, /* EQNPOS_NONE */ + { "over", 4 }, /* EQNPOS_OVER */ + { "sup", 3 }, /* EQNPOS_SUP */ + { "sub", 3 }, /* EQNPOS_SUB */ + { "to", 2 }, /* EQNPOS_TO */ + { "from", 4 }, /* EQNPOS_FROM */ +}; + +static const struct eqnstr eqnpiles[EQNPILE__MAX] = { + { "", 0 }, /* EQNPILE_NONE */ + { "cpile", 5 }, /* EQNPILE_CPILE */ + { "rpile", 5 }, /* EQNPILE_RPILE */ + { "lpile", 5 }, /* EQNPILE_LPILE */ +}; + /* ARGSUSED */ enum rofferr eqn_read(struct eqn_node **epp, int ln, @@ -70,108 +119,262 @@ eqn_read(struct eqn_node **epp, int ln, { size_t sz; struct eqn_node *ep; - struct mparse *mp; - const char *start, *end; - int i, c; + enum rofferr er; + ep = *epp; + + /* + * If we're the terminating mark, unset our equation status and + * validate the full equation. + */ + if (0 == strcmp(p, ".EN")) { + er = eqn_end(ep); *epp = NULL; - return(ROFF_EQN); + return(er); } - ep = *epp; - mp = ep->parse; - end = p + pos; + /* + * Build up the full string, replacing all newlines with regular + * whitespace. + */ - if (NULL == (start = eqn_nexttok(mp, ln, pos, &end, &sz))) + sz = strlen(p + pos) + 1; + ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1); + + /* First invocation: nil terminate the string. */ + + if (0 == ep->sz) + *ep->data = '\0'; + + ep->sz += sz; + strlcat(ep->data, p + pos, ep->sz + 1); + strlcat(ep->data, " ", ep->sz + 1); + return(ROFF_IGN); +} + +struct eqn_node * +eqn_alloc(int pos, int line, struct mparse *parse) +{ + struct eqn_node *p; + + p = mandoc_calloc(1, sizeof(struct eqn_node)); + p->parse = parse; + p->eqn.ln = line; + p->eqn.pos = pos; + + return(p); +} + +enum rofferr +eqn_end(struct eqn_node *ep) +{ + struct eqn_box *root; + enum eqn_rest c; + + ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box)); + + root = ep->eqn.root; + root->type = EQN_ROOT; + + if (0 == ep->sz) return(ROFF_IGN); - for (i = 0; i < (int)EQN__MAX; i++) { - if (eqnparts[i].sz != sz) - continue; - if (strncmp(eqnparts[i].name, start, sz)) - continue; + if (EQN_DESCOPE == (c = eqn_eqn(ep, root))) { + EQN_MSG(MANDOCERR_EQNNSCOPE, ep); + c = EQN_ERR; + } - if ((c = (*eqnparts[i].fp)(ep, ln, pos, &end)) < 0) - return(ROFF_ERR); - else if (0 == c || '\0' == *end) - return(ROFF_IGN); + return(EQN_EOF == c ? ROFF_EQN : ROFF_IGN); +} - /* - * Re-calculate offset and rerun, if trailing text. - * This allows multiple definitions (say) on each line. - */ +static enum eqn_rest +eqn_eqn(struct eqn_node *ep, struct eqn_box *last) +{ + struct eqn_box *bp; + enum eqn_rest c; - *offs = end - (p + pos); - return(ROFF_RERUN); - } + bp = eqn_box_alloc(last); + bp->type = EQN_SUBEXPR; - eqn_append(ep, mp, ln, pos, p + pos, 0); - return(ROFF_IGN); + while (EQN_OK == (c = eqn_box(ep, bp))) + /* Spin! */ ; + + return(c); } -static void -eqn_append(struct eqn_node *ep, struct mparse *mp, - int ln, int pos, const char *end, int re) +static enum eqn_rest +eqn_box(struct eqn_node *ep, struct eqn_box *last) { - const char *start; size_t sz; - int i; - - if (re >= EQN_NEST_MAX) { - mandoc_msg(MANDOCERR_BADQUOTE, mp, ln, pos, NULL); - return; - } + const char *start; + char *left; + enum eqn_rest c; + int i, size; + struct eqn_box *bp; - while (NULL != (start = eqn_nexttok(mp, ln, pos, &end, &sz))) { - if (0 == sz) + if (NULL == (start = eqn_nexttok(ep, &sz))) + return(EQN_EOF); + + if (1 == sz && 0 == strncmp("}", start, 1)) + return(EQN_DESCOPE); + else if (5 == sz && 0 == strncmp("right", start, 5)) + return(EQN_DESCOPE); + else if (5 == sz && 0 == strncmp("above", start, 5)) + return(EQN_DESCOPE); + + for (i = 0; i < (int)EQN__MAX; i++) { + if (eqnparts[i].str.sz != sz) continue; - for (i = 0; i < (int)ep->defsz; i++) { - if (0 == ep->defs[i].keysz) - continue; - if (ep->defs[i].keysz != sz) - continue; - if (strncmp(ep->defs[i].key, start, sz)) - continue; - start = ep->defs[i].val; - sz = ep->defs[i].valsz; + if (strncmp(eqnparts[i].str.name, start, sz)) + continue; + return((*eqnparts[i].fp)(ep) ? EQN_OK : EQN_ERR); + } - eqn_append(ep, mp, ln, pos, start, re + 1); - break; + if (1 == sz && 0 == strncmp("{", start, 1)) { + if (EQN_DESCOPE != (c = eqn_eqn(ep, last))) { + if (EQN_ERR != c) + EQN_MSG(MANDOCERR_EQNSCOPE, ep); + return(EQN_ERR); } - if (i < (int)ep->defsz) + eqn_rewind(ep); + start = eqn_nexttok(ep, &sz); + assert(start); + if (1 == sz && 0 == strncmp("}", start, 1)) + return(EQN_OK); + EQN_MSG(MANDOCERR_EQNBADSCOPE, ep); + return(EQN_ERR); + } + + for (i = 0; i < (int)EQNPILE__MAX; i++) { + if (eqnpiles[i].sz != sz) continue; + if (strncmp(eqnpiles[i].name, start, sz)) + continue; + if (NULL == (start = eqn_nexttok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + if (1 != sz || strncmp("{", start, 1)) { + EQN_MSG(MANDOCERR_EQNSYNT, ep); + return(EQN_ERR); + } - ep->eqn.data = mandoc_realloc - (ep->eqn.data, ep->eqn.sz + sz + 1); + while (EQN_DESCOPE == (c = eqn_eqn(ep, last))) { + assert(last->last); + last->last->pile = (enum eqn_pilet)i; + eqn_rewind(ep); + start = eqn_nexttok(ep, &sz); + assert(start); + if (5 != sz || strncmp("above", start, 5)) + break; + last->last->above = 1; + } - if (0 == ep->eqn.sz) - *ep->eqn.data = '\0'; + if (EQN_DESCOPE != c) { + if (EQN_ERR != c) + EQN_MSG(MANDOCERR_EQNSCOPE, ep); + return(EQN_ERR); + } - ep->eqn.sz += sz; - strlcat(ep->eqn.data, start, ep->eqn.sz + 1); + eqn_rewind(ep); + start = eqn_nexttok(ep, &sz); + assert(start); + if (1 == sz && 0 == strncmp("}", start, 1)) + return(EQN_OK); + + EQN_MSG(MANDOCERR_EQNBADSCOPE, ep); + return(EQN_ERR); } -} -struct eqn_node * -eqn_alloc(int pos, int line, struct mparse *parse) -{ - struct eqn_node *p; + if (4 == sz && 0 == strncmp("left", start, 4)) { + if (NULL == (start = eqn_nexttok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + left = mandoc_strndup(start, sz); + if (EQN_DESCOPE != (c = eqn_eqn(ep, last))) + return(c); + assert(last->last); + last->last->left = left; + eqn_rewind(ep); + start = eqn_nexttok(ep, &sz); + assert(start); + if (5 != sz || strncmp("right", start, 5)) + return(EQN_DESCOPE); + if (NULL == (start = eqn_nexttok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + last->last->right = mandoc_strndup(start, sz); + return(EQN_OK); + } - p = mandoc_calloc(1, sizeof(struct eqn_node)); - p->parse = parse; - p->eqn.line = line; - p->eqn.pos = pos; + for (i = 0; i < (int)EQNPOS__MAX; i++) { + if (eqnposs[i].sz != sz) + continue; + if (strncmp(eqnposs[i].name, start, sz)) + continue; + if (NULL == last->last) { + EQN_MSG(MANDOCERR_EQNSYNT, ep); + return(EQN_ERR); + } + last->last->pos = (enum eqn_post)i; + if (EQN_EOF == (c = eqn_box(ep, last))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + return(c); + } - return(p); -} + for (i = 0; i < (int)EQNMARK__MAX; i++) { + if (eqnmarks[i].sz != sz) + continue; + if (strncmp(eqnmarks[i].name, start, sz)) + continue; + if (NULL == last->last) { + EQN_MSG(MANDOCERR_EQNSYNT, ep); + return(EQN_ERR); + } + last->last->mark = (enum eqn_markt)i; + if (EQN_EOF == (c = eqn_box(ep, last))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + return(c); + } -/* ARGSUSED */ -void -eqn_end(struct eqn_node *e) -{ + for (i = 0; i < (int)EQNFONT__MAX; i++) { + if (eqnfonts[i].sz != sz) + continue; + if (strncmp(eqnfonts[i].name, start, sz)) + continue; + if (EQN_EOF == (c = eqn_box(ep, last))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } else if (EQN_OK == c) + last->last->font = (enum eqn_fontt)i; + return(c); + } - /* Nothing to do. */ + if (4 == sz && 0 == strncmp("size", start, 4)) { + if (NULL == (start = eqn_nexttok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + size = mandoc_strntoi(start, sz, 10); + if (EQN_EOF == (c = eqn_box(ep, last))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } else if (EQN_OK != c) + return(c); + last->last->size = size; + } + + bp = eqn_box_alloc(last); + bp->type = EQN_TEXT; + bp->text = mandoc_strndup(start, sz); + return(EQN_OK); } void @@ -179,122 +382,194 @@ eqn_free(struct eqn_node *p) { int i; - free(p->eqn.data); + eqn_box_free(p->eqn.root); for (i = 0; i < (int)p->defsz; i++) { free(p->defs[i].key); free(p->defs[i].val); } + free(p->data); free(p->defs); free(p); } -/* - * Return the current equation token setting "next" on the next one, - * setting the token size in "sz". - * This does the Right Thing for quoted strings, too. - * Returns NULL if no more tokens exist. - */ +static struct eqn_box * +eqn_box_alloc(struct eqn_box *parent) +{ + struct eqn_box *bp; + + bp = mandoc_calloc(1, sizeof(struct eqn_box)); + bp->parent = parent; + bp->size = EQN_DEFSIZE; + + if (NULL == parent->first) + parent->first = bp; + else + parent->last->next = bp; + + parent->last = bp; + return(bp); +} + +static void +eqn_box_free(struct eqn_box *bp) +{ + + if (bp->first) + eqn_box_free(bp->first); + if (bp->next) + eqn_box_free(bp->next); + + free(bp->text); + free(bp->left); + free(bp->right); + free(bp); +} + static const char * -eqn_nexttok(struct mparse *mp, int ln, int pos, - const char **next, size_t *sz) +eqn_nextrawtok(struct eqn_node *ep, size_t *sz) { - const char *start; - int q; - start = *next; + return(eqn_next(ep, '"', sz, 0)); +} + +static const char * +eqn_nexttok(struct eqn_node *ep, size_t *sz) +{ + + return(eqn_next(ep, '"', sz, 1)); +} + +static void +eqn_rewind(struct eqn_node *ep) +{ + + ep->cur = ep->rew; +} + +static const char * +eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl) +{ + char *start, *next; + int q, diff, lim; + size_t ssz, dummy; + struct eqn_def *def; + + if (NULL == sz) + sz = &dummy; + + lim = 0; + ep->rew = ep->cur; +again: + /* Prevent self-definitions. */ + + if (lim >= EQN_NEST_MAX) { + EQN_MSG(MANDOCERR_EQNNEST, ep); + return(NULL); + } + + ep->cur = ep->rew; + start = &ep->data[(int)ep->cur]; q = 0; if ('\0' == *start) return(NULL); - if ('"' == *start) { - start++; + if (quote == *start) { + ep->cur++; q = 1; } - *next = q ? strchr(start, '"') : strchr(start, ' '); + start = &ep->data[(int)ep->cur]; - if (NULL != *next) { - *sz = (size_t)(*next - start); + if ( ! q) { + if ('{' == *start || '}' == *start) + ssz = 1; + else + ssz = strcspn(start + 1, " ~\"{}\t") + 1; + next = start + (int)ssz; + if ('\0' == *next) + next = NULL; + } else + next = strchr(start, quote); + + if (NULL != next) { + *sz = (size_t)(next - start); + ep->cur += *sz; if (q) - (*next)++; - while (' ' == **next) - (*next)++; + ep->cur++; + while (' ' == ep->data[(int)ep->cur] || + '\t' == ep->data[(int)ep->cur] || + '~' == ep->data[(int)ep->cur]) + ep->cur++; } else { - /* - * XXX: groff gets confused by this and doesn't always - * do the "right thing" (just terminate it and warn - * about it). - */ if (q) - mandoc_msg(MANDOCERR_BADQUOTE, - mp, ln, pos, NULL); - *next = strchr(start, '\0'); - *sz = (size_t)(*next - start); + EQN_MSG(MANDOCERR_BADQUOTE, ep); + next = strchr(start, '\0'); + *sz = (size_t)(next - start); + ep->cur += *sz; } + /* Quotes aren't expanded for values. */ + + if (q || ! repl) + return(start); + + if (NULL != (def = eqn_def_find(ep, start, *sz))) { + diff = def->valsz - *sz; + + if (def->valsz > *sz) { + ep->sz += diff; + ep->data = mandoc_realloc(ep->data, ep->sz + 1); + ep->data[ep->sz] = '\0'; + start = &ep->data[(int)ep->rew]; + } + + diff = def->valsz - *sz; + memmove(start + *sz + diff, start + *sz, + (strlen(start) - *sz) + 1); + memcpy(start, def->val, def->valsz); + goto again; + } + return(start); } static int -eqn_do_ign2(struct eqn_node *ep, int ln, int pos, const char **end) +eqn_do_set(struct eqn_node *ep) { const char *start; - struct mparse *mp; - size_t sz; - mp = ep->parse; + if (NULL == (start = eqn_nextrawtok(ep, NULL))) + EQN_MSG(MANDOCERR_EQNARGS, ep); + else if (NULL == (start = eqn_nextrawtok(ep, NULL))) + EQN_MSG(MANDOCERR_EQNARGS, ep); + else + return(1); - start = eqn_nexttok(ep->parse, ln, pos, end, &sz); - if (NULL == start || 0 == sz) { - mandoc_msg(MANDOCERR_EQNARGS, mp, ln, pos, NULL); - return(0); - } - - start = eqn_nexttok(ep->parse, ln, pos, end, &sz); - if (NULL == start || 0 == sz) { - mandoc_msg(MANDOCERR_EQNARGS, mp, ln, pos, NULL); - return(0); - } - - return(1); + return(0); } static int -eqn_do_define(struct eqn_node *ep, int ln, int pos, const char **end) +eqn_do_define(struct eqn_node *ep) { const char *start; - struct mparse *mp; size_t sz; + struct eqn_def *def; int i; - mp = ep->parse; - - start = eqn_nexttok(mp, ln, pos, end, &sz); - if (NULL == start || 0 == sz) { - mandoc_msg(MANDOCERR_EQNARGS, mp, ln, pos, NULL); + if (NULL == (start = eqn_nextrawtok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNARGS, ep); return(0); } - /* TODO: merge this code with roff_getstr(). */ - /* * Search for a key that already exists. - * Note that the string array can have "holes" (null key). + * Create a new key if none is found. */ - for (i = 0; i < (int)ep->defsz; i++) { - if (0 == ep->defs[i].keysz || ep->defs[i].keysz != sz) - continue; - if (0 == strncmp(ep->defs[i].key, start, sz)) - break; - } - - /* Create a new key. */ - - if (i == (int)ep->defsz) { + if (NULL == (def = eqn_def_find(ep, start, sz))) { /* Find holes in string array. */ for (i = 0; i < (int)ep->defsz; i++) if (0 == ep->defs[i].keysz) @@ -314,49 +589,48 @@ eqn_do_define(struct eqn_node *ep, int ln, int pos, co memcpy(ep->defs[i].key, start, sz); ep->defs[i].key[(int)sz] = '\0'; + def = &ep->defs[i]; } - start = eqn_nexttok(mp, ln, pos, end, &sz); + start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0); - if (NULL == start || 0 == sz) { - ep->defs[i].keysz = 0; - mandoc_msg(MANDOCERR_EQNARGS, mp, ln, pos, NULL); + if (NULL == start) { + EQN_MSG(MANDOCERR_EQNARGS, ep); return(0); } - ep->defs[i].valsz = sz; - ep->defs[i].val = mandoc_realloc - (ep->defs[i].val, sz + 1); - memcpy(ep->defs[i].val, start, sz); - ep->defs[i].val[(int)sz] = '\0'; - - return(sz ? 1 : 0); + def->valsz = sz; + def->val = mandoc_realloc(def->val, sz + 1); + memcpy(def->val, start, sz); + def->val[(int)sz] = '\0'; + return(1); } static int -eqn_do_undef(struct eqn_node *ep, int ln, int pos, const char **end) +eqn_do_undef(struct eqn_node *ep) { const char *start; - struct mparse *mp; + struct eqn_def *def; size_t sz; - int i; - mp = ep->parse; - - start = eqn_nexttok(mp, ln, pos, end, &sz); - if (NULL == start || 0 == sz) { - mandoc_msg(MANDOCERR_EQNARGS, mp, ln, pos, NULL); + if (NULL == (start = eqn_nextrawtok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNARGS, ep); return(0); - } + } else if (NULL != (def = eqn_def_find(ep, start, sz))) + def->keysz = 0; - for (i = 0; i < (int)ep->defsz; i++) { - if (0 == ep->defs[i].keysz || ep->defs[i].keysz != sz) - continue; - if (strncmp(ep->defs[i].key, start, sz)) - continue; - ep->defs[i].keysz = 0; - break; - } - return(1); +} + +static struct eqn_def * +eqn_def_find(struct eqn_node *ep, const char *key, size_t sz) +{ + int i; + + for (i = 0; i < (int)ep->defsz; i++) + if (ep->defs[i].keysz && ep->defs[i].keysz == sz && + 0 == strncmp(ep->defs[i].key, key, sz)) + return(&ep->defs[i]); + + return(NULL); }