]> git.cameronkatri.com Git - mandoc.git/blob - eqn.c
Fix lost allocation.
[mandoc.git] / eqn.c
1 /* $Id: eqn.c,v 1.10 2011/07/17 14:15:11 kristaps Exp $ */
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
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,
53 const char **, size_t *);
54
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
61 /* ARGSUSED */
62 enum rofferr
63 eqn_read(struct eqn_node **epp, int ln,
64 const char *p, int pos, int *offs)
65 {
66 size_t sz;
67 struct eqn_node *ep;
68 struct mparse *mp;
69 const char *start, *end;
70 int i, c;
71
72 if (0 == strcmp(p, ".EN")) {
73 *epp = NULL;
74 return(ROFF_EQN);
75 }
76
77 ep = *epp;
78 mp = ep->parse;
79 end = p + pos;
80
81 if (NULL == (start = eqn_nexttok(mp, ln, pos, &end, &sz)))
82 return(ROFF_IGN);
83
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 }
103
104 end = p + pos;
105 while (NULL != (start = eqn_nexttok(mp, ln, pos, &end, &sz))) {
106 if (0 == sz)
107 continue;
108
109 for (i = 0; i < (int)ep->defsz; i++) {
110 if (0 == ep->defs[i].keysz)
111 continue;
112 if (ep->defs[i].keysz != sz)
113 continue;
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;
119 }
120
121 ep->eqn.data = mandoc_realloc
122 (ep->eqn.data, ep->eqn.sz + sz + 1);
123
124 if (0 == ep->eqn.sz)
125 *ep->eqn.data = '\0';
126
127 ep->eqn.sz += sz;
128 strlcat(ep->eqn.data, start, ep->eqn.sz + 1);
129 }
130
131 return(ROFF_IGN);
132 }
133
134 struct eqn_node *
135 eqn_alloc(int pos, int line, struct mparse *parse)
136 {
137 struct eqn_node *p;
138
139 p = mandoc_calloc(1, sizeof(struct eqn_node));
140 p->parse = parse;
141 p->eqn.line = line;
142 p->eqn.pos = pos;
143
144 return(p);
145 }
146
147 /* ARGSUSED */
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 {
158 int i;
159
160 free(p->eqn.data);
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);
168 free(p);
169 }
170
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 */
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 {
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 */
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);
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 ep->defs[i].key = ep->defs[i].val = NULL;
287 }
288
289 ep->defs[i].keysz = sz;
290 ep->defs[i].key = mandoc_realloc
291 (ep->defs[i].key, sz + 1);
292
293 memcpy(ep->defs[i].key, start, sz);
294 ep->defs[i].key[(int)sz] = '\0';
295 }
296
297 start = eqn_nexttok(mp, ln, pos, end, &sz);
298
299 if (NULL == start || 0 == sz) {
300 ep->defs[i].keysz = 0;
301 mandoc_msg(MANDOCERR_EQNARGS, mp, ln, pos, NULL);
302 return(0);
303 }
304
305 ep->defs[i].valsz = sz;
306 ep->defs[i].val = mandoc_realloc
307 (ep->defs[i].val, sz + 1);
308 memcpy(ep->defs[i].val, start, sz);
309 ep->defs[i].val[(int)sz] = '\0';
310
311 return(sz ? 1 : 0);
312 }
313
314 static int
315 eqn_do_undef(struct eqn_node *ep, int ln, int pos, const char **end)
316 {
317 const char *start;
318 struct mparse *mp;
319 size_t sz;
320 int i;
321
322 mp = ep->parse;
323
324 start = eqn_nexttok(mp, ln, pos, end, &sz);
325 if (NULL == start || 0 == sz) {
326 mandoc_msg(MANDOCERR_EQNARGS, mp, ln, pos, NULL);
327 return(0);
328 }
329
330 for (i = 0; i < (int)ep->defsz; i++) {
331 if (0 == ep->defs[i].keysz || ep->defs[i].keysz != sz)
332 continue;
333 if (strncmp(ep->defs[i].key, start, sz))
334 continue;
335 ep->defs[i].keysz = 0;
336 break;
337 }
338
339 return(1);
340 }