Annotation of mandoc/eqn.c, Revision 1.8
1.8 ! kristaps 1: /* $Id: eqn.c,v 1.7 2011/07/17 12:52:54 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.8 ! kristaps 31: #define EQN_ARGS struct eqn_node *ep, \
! 32: int ln, \
! 33: int pos, \
! 34: const char **end
! 35:
! 36: struct eqnpart {
! 37: const char *name;
! 38: size_t sz;
! 39: int (*fp)(EQN_ARGS);
! 40: };
! 41:
! 42: enum eqnpartt {
! 43: EQN_DEFINE = 0,
! 44: EQN_SET,
! 45: EQN_UNDEF,
! 46: EQN__MAX
! 47: };
! 48:
! 49: static int eqn_do_define(EQN_ARGS);
! 50: static int eqn_do_set(EQN_ARGS);
! 51: static int eqn_do_undef(EQN_ARGS);
! 52: static const char *eqn_nexttok(struct mparse *, int, int,
1.6 kristaps 53: const char **, size_t *);
54:
1.8 ! kristaps 55: static const struct eqnpart eqnparts[EQN__MAX] = {
! 56: { "define", 6, eqn_do_define }, /* EQN_DEFINE */
! 57: { "set", 3, eqn_do_set }, /* EQN_SET */
! 58: { "undef", 5, eqn_do_undef }, /* EQN_UNDEF */
! 59: };
! 60:
1.1 kristaps 61: /* ARGSUSED */
62: enum rofferr
1.6 kristaps 63: eqn_read(struct eqn_node **epp, int ln,
64: const char *p, int pos, int *offs)
1.1 kristaps 65: {
1.8 ! kristaps 66: size_t sz;
! 67: struct eqn_node *ep;
! 68: struct mparse *mp;
! 69: const char *start, *end;
! 70: int i, c;
1.1 kristaps 71:
72: if (0 == strcmp(p, ".EN")) {
73: *epp = NULL;
74: return(ROFF_EQN);
75: }
76:
77: ep = *epp;
1.8 ! kristaps 78: mp = ep->parse;
1.6 kristaps 79: end = p + pos;
80:
1.8 ! kristaps 81: if (NULL == (start = eqn_nexttok(mp, ln, pos, &end, &sz)))
1.6 kristaps 82: return(ROFF_IGN);
83:
1.8 ! kristaps 84: for (i = 0; i < (int)EQN__MAX; i++) {
! 85: if (eqnparts[i].sz != sz)
! 86: continue;
! 87: if (strncmp(eqnparts[i].name, start, sz))
! 88: continue;
! 89:
! 90: if ((c = (*eqnparts[i].fp)(ep, ln, pos, &end)) < 0)
! 91: return(ROFF_ERR);
! 92: else if (0 == c || '\0' == *end)
! 93: return(ROFF_IGN);
! 94:
! 95: /*
! 96: * Re-calculate offset and rerun, if trailing text.
! 97: * This allows multiple definitions (say) on each line.
! 98: */
! 99:
! 100: *offs = end - (p + pos);
! 101: return(ROFF_RERUN);
! 102: }
1.7 kristaps 103:
1.8 ! kristaps 104: end = p + pos;
! 105: while (NULL != (start = eqn_nexttok(mp, ln, pos, &end, &sz))) {
! 106: if (0 == sz)
! 107: continue;
1.7 kristaps 108:
1.8 ! kristaps 109: for (i = 0; i < (int)ep->defsz; i++) {
! 110: if (0 == ep->defs[i].keysz)
! 111: continue;
1.6 kristaps 112: if (ep->defs[i].keysz != sz)
113: continue;
1.8 ! kristaps 114: if (strncmp(ep->defs[i].key, start, sz))
! 115: continue;
! 116: start = ep->defs[i].val;
! 117: sz = ep->defs[i].valsz;
! 118: break;
1.6 kristaps 119: }
120:
1.8 ! kristaps 121: ep->eqn.data = mandoc_realloc
! 122: (ep->eqn.data, ep->eqn.sz + sz + 1);
1.6 kristaps 123:
1.8 ! kristaps 124: if (0 == ep->eqn.sz)
! 125: *ep->eqn.data = '\0';
1.6 kristaps 126:
1.8 ! kristaps 127: ep->eqn.sz += sz;
! 128: strlcat(ep->eqn.data, start, ep->eqn.sz + 1);
! 129: }
1.6 kristaps 130:
1.1 kristaps 131: return(ROFF_IGN);
132: }
133:
134: struct eqn_node *
1.5 kristaps 135: eqn_alloc(int pos, int line, struct mparse *parse)
1.1 kristaps 136: {
137: struct eqn_node *p;
138:
139: p = mandoc_calloc(1, sizeof(struct eqn_node));
1.5 kristaps 140: p->parse = parse;
1.2 kristaps 141: p->eqn.line = line;
142: p->eqn.pos = pos;
1.1 kristaps 143:
144: return(p);
145: }
146:
1.3 kristaps 147: /* ARGSUSED */
1.1 kristaps 148: void
149: eqn_end(struct eqn_node *e)
150: {
151:
152: /* Nothing to do. */
153: }
154:
155: void
156: eqn_free(struct eqn_node *p)
157: {
1.6 kristaps 158: int i;
1.1 kristaps 159:
160: free(p->eqn.data);
1.6 kristaps 161:
162: for (i = 0; i < (int)p->defsz; i++) {
163: free(p->defs[i].key);
164: free(p->defs[i].val);
165: }
166:
167: free(p->defs);
1.1 kristaps 168: free(p);
1.6 kristaps 169: }
170:
1.7 kristaps 171: /*
172: * Return the current equation token setting "next" on the next one,
173: * setting the token size in "sz".
174: * This does the Right Thing for quoted strings, too.
175: * Returns NULL if no more tokens exist.
176: */
1.6 kristaps 177: static const char *
178: eqn_nexttok(struct mparse *mp, int ln, int pos,
179: const char **next, size_t *sz)
180: {
181: const char *start;
182: int q;
183:
184: start = *next;
185: q = 0;
186:
187: if ('\0' == *start)
188: return(NULL);
189:
190: if ('"' == *start) {
191: start++;
192: q = 1;
193: }
194:
195: *next = q ? strchr(start, '"') : strchr(start, ' ');
196:
197: if (NULL != *next) {
198: *sz = (size_t)(*next - start);
199: if (q)
200: (*next)++;
201: while (' ' == **next)
202: (*next)++;
203: } else {
1.7 kristaps 204: /*
205: * XXX: groff gets confused by this and doesn't always
206: * do the "right thing" (just terminate it and warn
207: * about it).
208: */
1.6 kristaps 209: if (q)
210: mandoc_msg(MANDOCERR_BADQUOTE,
211: mp, ln, pos, NULL);
212: *next = strchr(start, '\0');
213: *sz = (size_t)(*next - start);
214: }
215:
216: return(start);
1.8 ! kristaps 217: }
! 218:
! 219: static int
! 220: eqn_do_set(struct eqn_node *ep, int ln, int pos, const char **end)
! 221: {
! 222: const char *start;
! 223: struct mparse *mp;
! 224: size_t sz;
! 225:
! 226: mp = ep->parse;
! 227:
! 228: start = eqn_nexttok(ep->parse, ln, pos, end, &sz);
! 229: if (NULL == start || 0 == sz) {
! 230: mandoc_msg(MANDOCERR_EQNARGS, mp, ln, pos, NULL);
! 231: return(0);
! 232: }
! 233:
! 234: start = eqn_nexttok(ep->parse, ln, pos, end, &sz);
! 235: if (NULL == start || 0 == sz) {
! 236: mandoc_msg(MANDOCERR_EQNARGS, mp, ln, pos, NULL);
! 237: return(0);
! 238: }
! 239:
! 240: return(1);
! 241: }
! 242:
! 243: static int
! 244: eqn_do_define(struct eqn_node *ep, int ln, int pos, const char **end)
! 245: {
! 246: const char *start;
! 247: struct mparse *mp;
! 248: size_t sz;
! 249: int i;
! 250:
! 251: mp = ep->parse;
! 252:
! 253: start = eqn_nexttok(mp, ln, pos, end, &sz);
! 254: if (NULL == start || 0 == sz) {
! 255: mandoc_msg(MANDOCERR_EQNARGS, mp, ln, pos, NULL);
! 256: return(0);
! 257: }
! 258:
! 259: /* TODO: merge this code with roff_getstr(). */
! 260:
! 261: /*
! 262: * Search for a key that already exists.
! 263: * Note that the string array can have "holes" (null key).
! 264: */
! 265:
! 266: for (i = 0; i < (int)ep->defsz; i++) {
! 267: if (0 == ep->defs[i].keysz || ep->defs[i].keysz != sz)
! 268: continue;
! 269: if (0 == strncmp(ep->defs[i].key, start, sz))
! 270: break;
! 271: }
! 272:
! 273: /* Create a new key. */
! 274:
! 275: if (i == (int)ep->defsz) {
! 276: /* Find holes in string array. */
! 277: for (i = 0; i < (int)ep->defsz; i++)
! 278: if (0 == ep->defs[i].keysz)
! 279: break;
! 280:
! 281: if (i == (int)ep->defsz) {
! 282: ep->defsz++;
! 283: ep->defs = mandoc_realloc
! 284: (ep->defs, ep->defsz *
! 285: sizeof(struct eqn_def));
! 286: }
! 287:
! 288: ep->defs[i].keysz = sz;
! 289: ep->defs[i].key = mandoc_realloc
! 290: (ep->defs[i].key, sz + 1);
! 291:
! 292: memcpy(ep->defs[i].key, start, sz);
! 293: ep->defs[i].key[(int)sz] = '\0';
! 294: ep->defs[i].val = NULL;
! 295: ep->defs[i].valsz = 0;
! 296: }
! 297:
! 298: start = eqn_nexttok(mp, ln, pos, end, &sz);
! 299:
! 300: if (NULL == start || 0 == sz) {
! 301: ep->defs[i].keysz = 0;
! 302: mandoc_msg(MANDOCERR_EQNARGS, mp, ln, pos, NULL);
! 303: return(0);
! 304: }
! 305:
! 306: ep->defs[i].valsz = sz;
! 307: ep->defs[i].val = mandoc_realloc
! 308: (ep->defs[i].val, sz + 1);
! 309: memcpy(ep->defs[i].val, start, sz);
! 310: ep->defs[i].val[(int)sz] = '\0';
! 311:
! 312: return(sz ? 1 : 0);
! 313: }
! 314:
! 315: static int
! 316: eqn_do_undef(struct eqn_node *ep, int ln, int pos, const char **end)
! 317: {
! 318: const char *start;
! 319: struct mparse *mp;
! 320: size_t sz;
! 321: int i;
! 322:
! 323: mp = ep->parse;
! 324:
! 325: start = eqn_nexttok(mp, ln, pos, end, &sz);
! 326: if (NULL == start || 0 == sz) {
! 327: mandoc_msg(MANDOCERR_EQNARGS, mp, ln, pos, NULL);
! 328: return(0);
! 329: }
! 330:
! 331: for (i = 0; i < (int)ep->defsz; i++) {
! 332: if (0 == ep->defs[i].keysz || ep->defs[i].keysz != sz)
! 333: continue;
! 334: if (strncmp(ep->defs[i].key, start, sz))
! 335: continue;
! 336: ep->defs[i].keysz = 0;
! 337: break;
! 338: }
! 339:
! 340: return(1);
1.1 kristaps 341: }
CVSweb