Annotation of mandoc/eqn.c, Revision 1.17
1.17 ! kristaps 1: /* $Id: eqn.c,v 1.16 2011/07/21 13:18:24 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:
1.17 ! kristaps 34: struct eqnstr {
1.8 kristaps 35: const char *name;
36: size_t sz;
37: };
38:
1.17 ! kristaps 39: struct eqnpart {
! 40: struct eqnstr str;
! 41: int (*fp)(struct eqn_node *);
1.16 kristaps 42: };
43:
1.8 kristaps 44: enum eqnpartt {
45: EQN_DEFINE = 0,
46: EQN_SET,
47: EQN_UNDEF,
48: EQN__MAX
49: };
50:
1.13 kristaps 51: static void eqn_box_free(struct eqn_box *);
1.12 kristaps 52: static struct eqn_def *eqn_def_find(struct eqn_node *,
53: const char *, size_t);
54: static int eqn_do_define(struct eqn_node *);
1.14 kristaps 55: static int eqn_do_set(struct eqn_node *);
1.12 kristaps 56: static int eqn_do_undef(struct eqn_node *);
57: static const char *eqn_nexttok(struct eqn_node *, size_t *);
1.14 kristaps 58: static const char *eqn_nextrawtok(struct eqn_node *, size_t *);
59: static const char *eqn_next(struct eqn_node *,
60: char, size_t *, int);
1.15 kristaps 61: static int eqn_box(struct eqn_node *,
62: struct eqn_box *, struct eqn_box **);
1.6 kristaps 63:
1.8 kristaps 64: static const struct eqnpart eqnparts[EQN__MAX] = {
1.17 ! kristaps 65: { { "define", 6 }, eqn_do_define }, /* EQN_DEFINE */
! 66: { { "set", 3 }, eqn_do_set }, /* EQN_SET */
! 67: { { "undef", 5 }, eqn_do_undef }, /* EQN_UNDEF */
1.8 kristaps 68: };
69:
1.17 ! kristaps 70: static const struct eqnstr eqnmarks[EQNMARK__MAX] = {
1.16 kristaps 71: { "", 0 }, /* EQNMARK_NONE */
72: { "dot", 3 }, /* EQNMARK_DOT */
73: { "dotdot", 6 }, /* EQNMARK_DOTDOT */
74: { "hat", 3 }, /* EQNMARK_HAT */
75: { "tilde", 5 }, /* EQNMARK_TILDE */
76: { "vec", 3 }, /* EQNMARK_VEC */
77: { "dyad", 4 }, /* EQNMARK_DYAD */
78: { "bar", 3 }, /* EQNMARK_BAR */
79: { "under", 5 }, /* EQNMARK_UNDER */
80: };
81:
1.17 ! kristaps 82: static const struct eqnstr eqnfonts[EQNFONT__MAX] = {
! 83: { "roman", 5 },
! 84: { "bold", 4 },
! 85: { "italic", 6 },
! 86: };
! 87:
1.1 kristaps 88: /* ARGSUSED */
89: enum rofferr
1.6 kristaps 90: eqn_read(struct eqn_node **epp, int ln,
91: const char *p, int pos, int *offs)
1.1 kristaps 92: {
1.8 kristaps 93: size_t sz;
94: struct eqn_node *ep;
1.12 kristaps 95: enum rofferr er;
96:
97: ep = *epp;
98:
99: /*
100: * If we're the terminating mark, unset our equation status and
101: * validate the full equation.
102: */
1.1 kristaps 103:
104: if (0 == strcmp(p, ".EN")) {
1.12 kristaps 105: er = eqn_end(ep);
1.1 kristaps 106: *epp = NULL;
1.12 kristaps 107: return(er);
1.1 kristaps 108: }
109:
1.12 kristaps 110: /*
111: * Build up the full string, replacing all newlines with regular
112: * whitespace.
113: */
1.6 kristaps 114:
1.12 kristaps 115: sz = strlen(p + pos) + 1;
116: ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1);
1.6 kristaps 117:
1.12 kristaps 118: /* First invocation: nil terminate the string. */
1.8 kristaps 119:
1.12 kristaps 120: if (0 == ep->sz)
121: *ep->data = '\0';
1.8 kristaps 122:
1.12 kristaps 123: ep->sz += sz;
124: strlcat(ep->data, p + pos, ep->sz + 1);
125: strlcat(ep->data, " ", ep->sz + 1);
1.11 kristaps 126: return(ROFF_IGN);
127: }
128:
1.1 kristaps 129: struct eqn_node *
1.5 kristaps 130: eqn_alloc(int pos, int line, struct mparse *parse)
1.1 kristaps 131: {
132: struct eqn_node *p;
133:
134: p = mandoc_calloc(1, sizeof(struct eqn_node));
1.5 kristaps 135: p->parse = parse;
1.12 kristaps 136: p->eqn.ln = line;
1.2 kristaps 137: p->eqn.pos = pos;
1.1 kristaps 138:
139: return(p);
140: }
141:
1.12 kristaps 142: enum rofferr
143: eqn_end(struct eqn_node *ep)
144: {
1.15 kristaps 145: struct eqn_box *root, *last;
146: int c;
1.13 kristaps 147:
148: ep->eqn.root = root =
149: mandoc_calloc(1, sizeof(struct eqn_box));
150: root->type = EQN_ROOT;
151:
152: if (0 == ep->sz)
153: return(ROFF_IGN);
1.12 kristaps 154:
155: /*
1.15 kristaps 156: * Run the parser.
157: * If we return before reaching the end of our input, our scope
158: * is still open somewhere.
159: * If we return alright but don't have a symmetric scoping, then
160: * something's not right either.
161: * Otherwise, return the equation.
1.12 kristaps 162: */
163:
1.16 kristaps 164: if (0 == (c = eqn_box(ep, root, &last))) {
165: if (last != root) {
166: EQN_MSG(MANDOCERR_EQNSCOPE, ep);
167: c = 0;
168: }
169: } else if (c > 0)
1.15 kristaps 170: EQN_MSG(MANDOCERR_EQNNSCOPE, ep);
171:
1.16 kristaps 172: return(0 == c ? ROFF_EQN : ROFF_IGN);
1.12 kristaps 173: }
174:
175: static int
1.15 kristaps 176: eqn_box(struct eqn_node *ep, struct eqn_box *last, struct eqn_box **sv)
1.1 kristaps 177: {
1.12 kristaps 178: size_t sz;
179: const char *start;
1.15 kristaps 180: int c, i, nextc;
1.17 ! kristaps 181: enum eqn_fontt font;
1.13 kristaps 182: struct eqn_box *bp;
1.12 kristaps 183:
1.15 kristaps 184: /*
185: * Mark our last level of subexpression.
186: * Also mark whether that the next node should be a
187: * subexpression node.
188: */
189:
190: *sv = last;
1.13 kristaps 191: nextc = 1;
1.17 ! kristaps 192: font = EQNFONT_NONE;
! 193:
1.13 kristaps 194: again:
1.12 kristaps 195: if (NULL == (start = eqn_nexttok(ep, &sz)))
196: return(0);
197:
1.17 ! kristaps 198: for (i = 0; i < (int)EQNFONT__MAX; i++) {
! 199: if (eqnfonts[i].sz != sz)
! 200: continue;
! 201: if (strncmp(eqnfonts[i].name, start, sz))
! 202: continue;
! 203: font = (enum eqn_fontt)i;
! 204: goto again;
! 205: }
! 206:
1.12 kristaps 207: for (i = 0; i < (int)EQN__MAX; i++) {
1.17 ! kristaps 208: if (eqnparts[i].str.sz != sz)
1.12 kristaps 209: continue;
1.17 ! kristaps 210: if (strncmp(eqnparts[i].str.name, start, sz))
1.12 kristaps 211: continue;
212: if ( ! (*eqnparts[i].fp)(ep))
213: return(-1);
1.13 kristaps 214: goto again;
1.12 kristaps 215: }
1.16 kristaps 216:
217: for (i = 0; i < (int)EQNMARK__MAX; i++) {
218: if (eqnmarks[i].sz != sz)
219: continue;
220: if (strncmp(eqnmarks[i].name, start, sz))
221: continue;
222: last->mark = (enum eqn_markt)i;
223: goto again;
224: }
1.12 kristaps 225:
1.15 kristaps 226: /* Exit this [hopefully] subexpression. */
227:
228: if (sz == 1 && 0 == strncmp("}", start, 1))
229: return(1);
230:
1.13 kristaps 231: bp = mandoc_calloc(1, sizeof(struct eqn_box));
1.17 ! kristaps 232: bp->font = font;
! 233: font = EQNFONT_NONE;
! 234:
1.13 kristaps 235: if (nextc)
236: last->child = bp;
237: else
238: last->next = bp;
1.1 kristaps 239:
1.15 kristaps 240: last = bp;
241:
242: /*
243: * See if we're to open a new subexpression.
244: * If so, mark our node as such and descend.
245: */
246:
247: if (sz == 1 && 0 == strncmp("{", start, 1)) {
248: bp->type = EQN_SUBEXPR;
249: c = eqn_box(ep, bp, sv);
250:
251: nextc = 0;
252: goto again;
253: }
254:
255: /* A regular text node. */
256:
257: bp->type = EQN_TEXT;
1.13 kristaps 258: bp->text = mandoc_malloc(sz + 1);
259: *bp->text = '\0';
260: strlcat(bp->text, start, sz + 1);
261:
262: nextc = 0;
263: goto again;
1.1 kristaps 264: }
265:
266: void
267: eqn_free(struct eqn_node *p)
268: {
1.6 kristaps 269: int i;
1.1 kristaps 270:
1.13 kristaps 271: eqn_box_free(p->eqn.root);
1.6 kristaps 272:
273: for (i = 0; i < (int)p->defsz; i++) {
274: free(p->defs[i].key);
275: free(p->defs[i].val);
276: }
277:
1.12 kristaps 278: free(p->data);
1.6 kristaps 279: free(p->defs);
1.1 kristaps 280: free(p);
1.6 kristaps 281: }
282:
1.13 kristaps 283: static void
284: eqn_box_free(struct eqn_box *bp)
285: {
286:
287: if (bp->child)
288: eqn_box_free(bp->child);
289: if (bp->next)
290: eqn_box_free(bp->next);
291:
292: free(bp->text);
293: free(bp);
294: }
295:
1.6 kristaps 296: static const char *
1.14 kristaps 297: eqn_nextrawtok(struct eqn_node *ep, size_t *sz)
298: {
299:
300: return(eqn_next(ep, '"', sz, 0));
301: }
302:
303: static const char *
1.12 kristaps 304: eqn_nexttok(struct eqn_node *ep, size_t *sz)
305: {
306:
1.14 kristaps 307: return(eqn_next(ep, '"', sz, 1));
1.12 kristaps 308: }
309:
310: static const char *
1.14 kristaps 311: eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl)
1.6 kristaps 312: {
1.12 kristaps 313: char *start, *next;
314: int q, diff, lim;
315: size_t sv, ssz;
316: struct eqn_def *def;
317:
318: if (NULL == sz)
319: sz = &ssz;
1.6 kristaps 320:
1.13 kristaps 321: lim = 0;
322: sv = ep->cur;
323: again:
324: /* Prevent self-definitions. */
325:
326: if (lim >= EQN_NEST_MAX) {
327: EQN_MSG(MANDOCERR_EQNNEST, ep);
328: return(NULL);
329: }
330:
331: ep->cur = sv;
1.12 kristaps 332: start = &ep->data[(int)ep->cur];
1.6 kristaps 333: q = 0;
334:
335: if ('\0' == *start)
336: return(NULL);
337:
1.12 kristaps 338: if (quote == *start) {
339: ep->cur++;
1.6 kristaps 340: q = 1;
341: }
342:
1.12 kristaps 343: start = &ep->data[(int)ep->cur];
344: next = q ? strchr(start, quote) : strchr(start, ' ');
345:
346: if (NULL != next) {
347: *sz = (size_t)(next - start);
348: ep->cur += *sz;
1.6 kristaps 349: if (q)
1.12 kristaps 350: ep->cur++;
351: while (' ' == ep->data[(int)ep->cur])
352: ep->cur++;
1.6 kristaps 353: } else {
354: if (q)
1.12 kristaps 355: EQN_MSG(MANDOCERR_BADQUOTE, ep);
356: next = strchr(start, '\0');
357: *sz = (size_t)(next - start);
358: ep->cur += *sz;
359: }
360:
1.13 kristaps 361: /* Quotes aren't expanded for values. */
362:
1.14 kristaps 363: if (q || ! repl)
1.13 kristaps 364: return(start);
365:
1.12 kristaps 366: if (NULL != (def = eqn_def_find(ep, start, *sz))) {
367: diff = def->valsz - *sz;
368:
369: if (def->valsz > *sz) {
370: ep->sz += diff;
371: ep->data = mandoc_realloc(ep->data, ep->sz + 1);
372: ep->data[ep->sz] = '\0';
373: start = &ep->data[(int)sv];
374: }
375:
376: diff = def->valsz - *sz;
377: memmove(start + *sz + diff, start + *sz,
378: (strlen(start) - *sz) + 1);
379: memcpy(start, def->val, def->valsz);
380: goto again;
1.6 kristaps 381: }
382:
383: return(start);
1.8 kristaps 384: }
385:
386: static int
1.14 kristaps 387: eqn_do_set(struct eqn_node *ep)
1.8 kristaps 388: {
389: const char *start;
390:
1.14 kristaps 391: if (NULL == (start = eqn_nextrawtok(ep, NULL)))
1.12 kristaps 392: EQN_MSG(MANDOCERR_EQNARGS, ep);
1.14 kristaps 393: else if (NULL == (start = eqn_nextrawtok(ep, NULL)))
1.12 kristaps 394: EQN_MSG(MANDOCERR_EQNARGS, ep);
395: else
396: return(1);
1.8 kristaps 397:
1.12 kristaps 398: return(0);
1.8 kristaps 399: }
400:
401: static int
1.12 kristaps 402: eqn_do_define(struct eqn_node *ep)
1.8 kristaps 403: {
404: const char *start;
405: size_t sz;
1.12 kristaps 406: struct eqn_def *def;
1.8 kristaps 407: int i;
408:
1.14 kristaps 409: if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
1.12 kristaps 410: EQN_MSG(MANDOCERR_EQNARGS, ep);
1.8 kristaps 411: return(0);
412: }
413:
414: /*
415: * Search for a key that already exists.
1.12 kristaps 416: * Create a new key if none is found.
1.8 kristaps 417: */
418:
1.12 kristaps 419: if (NULL == (def = eqn_def_find(ep, start, sz))) {
1.8 kristaps 420: /* Find holes in string array. */
421: for (i = 0; i < (int)ep->defsz; i++)
422: if (0 == ep->defs[i].keysz)
423: break;
424:
425: if (i == (int)ep->defsz) {
426: ep->defsz++;
427: ep->defs = mandoc_realloc
428: (ep->defs, ep->defsz *
429: sizeof(struct eqn_def));
1.9 kristaps 430: ep->defs[i].key = ep->defs[i].val = NULL;
1.8 kristaps 431: }
432:
433: ep->defs[i].keysz = sz;
434: ep->defs[i].key = mandoc_realloc
435: (ep->defs[i].key, sz + 1);
436:
437: memcpy(ep->defs[i].key, start, sz);
438: ep->defs[i].key[(int)sz] = '\0';
1.12 kristaps 439: def = &ep->defs[i];
1.8 kristaps 440: }
441:
1.14 kristaps 442: start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0);
1.8 kristaps 443:
1.12 kristaps 444: if (NULL == start) {
445: EQN_MSG(MANDOCERR_EQNARGS, ep);
1.8 kristaps 446: return(0);
447: }
448:
1.12 kristaps 449: def->valsz = sz;
1.13 kristaps 450: def->val = mandoc_realloc(def->val, sz + 1);
1.12 kristaps 451: memcpy(def->val, start, sz);
452: def->val[(int)sz] = '\0';
453: return(1);
1.8 kristaps 454: }
455:
456: static int
1.12 kristaps 457: eqn_do_undef(struct eqn_node *ep)
1.8 kristaps 458: {
459: const char *start;
1.12 kristaps 460: struct eqn_def *def;
1.8 kristaps 461: size_t sz;
462:
1.14 kristaps 463: if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
1.12 kristaps 464: EQN_MSG(MANDOCERR_EQNARGS, ep);
465: return(0);
466: } else if (NULL != (def = eqn_def_find(ep, start, sz)))
467: def->keysz = 0;
1.8 kristaps 468:
1.12 kristaps 469: return(1);
470: }
471:
472: static struct eqn_def *
473: eqn_def_find(struct eqn_node *ep, const char *key, size_t sz)
474: {
475: int i;
1.8 kristaps 476:
1.12 kristaps 477: for (i = 0; i < (int)ep->defsz; i++)
478: if (ep->defs[i].keysz && ep->defs[i].keysz == sz &&
479: 0 == strncmp(ep->defs[i].key, key, sz))
480: return(&ep->defs[i]);
1.8 kristaps 481:
1.12 kristaps 482: return(NULL);
1.1 kristaps 483: }
CVSweb