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