]> git.cameronkatri.com Git - mandoc.git/blob - mdoc.c
7525bc4107b1fea2da23e866471745fbbb5af9c1
[mandoc.git] / mdoc.c
1 /* $Id: mdoc.c,v 1.57 2009/03/08 20:57:35 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
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
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19 #include <assert.h>
20 #include <ctype.h>
21 #include <err.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "private.h"
28
29 /*
30 * Main caller in the libmdoc library. This begins the parsing routine,
31 * handles allocation of data, and so forth. Most of the "work" is done
32 * in macro.c and validate.c.
33 */
34
35 static struct mdoc_node *mdoc_node_alloc(const struct mdoc *);
36 static int mdoc_node_append(struct mdoc *,
37 struct mdoc_node *);
38
39 static int parsetext(struct mdoc *, int, char *);
40 static int parsemacro(struct mdoc *, int, char *);
41
42
43 const char *const __mdoc_macronames[MDOC_MAX] = {
44 "\\\"", "Dd", "Dt", "Os",
45 "Sh", "Ss", "Pp", "D1",
46 "Dl", "Bd", "Ed", "Bl",
47 "El", "It", "Ad", "An",
48 "Ar", "Cd", "Cm", "Dv",
49 "Er", "Ev", "Ex", "Fa",
50 "Fd", "Fl", "Fn", "Ft",
51 "Ic", "In", "Li", "Nd",
52 "Nm", "Op", "Ot", "Pa",
53 "Rv", "St", "Va", "Vt",
54 /* LINTED */
55 "Xr", "\%A", "\%B", "\%D",
56 /* LINTED */
57 "\%I", "\%J", "\%N", "\%O",
58 /* LINTED */
59 "\%P", "\%R", "\%T", "\%V",
60 "Ac", "Ao", "Aq", "At",
61 "Bc", "Bf", "Bo", "Bq",
62 "Bsx", "Bx", "Db", "Dc",
63 "Do", "Dq", "Ec", "Ef",
64 "Em", "Eo", "Fx", "Ms",
65 "No", "Ns", "Nx", "Ox",
66 "Pc", "Pf", "Po", "Pq",
67 "Qc", "Ql", "Qo", "Qq",
68 "Re", "Rs", "Sc", "So",
69 "Sq", "Sm", "Sx", "Sy",
70 "Tn", "Ux", "Xc", "Xo",
71 "Fo", "Fc", "Oo", "Oc",
72 "Bk", "Ek", "Bt", "Hf",
73 "Fr", "Ud", "Lb", "Ap",
74 "Lp"
75 };
76
77 const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
78 "split", "nosplit", "ragged",
79 "unfilled", "literal", "file",
80 "offset", "bullet", "dash",
81 "hyphen", "item", "enum",
82 "tag", "diag", "hang",
83 "ohang", "inset", "column",
84 "width", "compact", "std",
85 "filled", "words", "emphasis",
86 "symbolic"
87 };
88
89 const char * const *mdoc_macronames = __mdoc_macronames;
90 const char * const *mdoc_argnames = __mdoc_argnames;
91
92
93 const struct mdoc_node *
94 mdoc_node(const struct mdoc *mdoc)
95 {
96
97 return(mdoc->first);
98 }
99
100
101 const struct mdoc_meta *
102 mdoc_meta(const struct mdoc *mdoc)
103 {
104
105 return(&mdoc->meta);
106 }
107
108
109 void
110 mdoc_free(struct mdoc *mdoc)
111 {
112
113 if (mdoc->first)
114 mdoc_node_freelist(mdoc->first);
115 if (mdoc->htab)
116 mdoc_tokhash_free(mdoc->htab);
117 if (mdoc->meta.title)
118 free(mdoc->meta.title);
119 if (mdoc->meta.os)
120 free(mdoc->meta.os);
121 if (mdoc->meta.name)
122 free(mdoc->meta.name);
123 if (mdoc->meta.arch)
124 free(mdoc->meta.arch);
125 if (mdoc->meta.vol)
126 free(mdoc->meta.vol);
127
128 free(mdoc);
129 }
130
131
132 struct mdoc *
133 mdoc_alloc(void *data, int pflags, const struct mdoc_cb *cb)
134 {
135 struct mdoc *p;
136
137 p = xcalloc(1, sizeof(struct mdoc));
138
139 p->data = data;
140 if (cb)
141 (void)memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
142
143 p->last = xcalloc(1, sizeof(struct mdoc_node));
144 p->last->type = MDOC_ROOT;
145 p->first = p->last;
146 p->pflags = pflags;
147 p->next = MDOC_NEXT_CHILD;
148 p->htab = mdoc_tokhash_alloc();
149
150 return(p);
151 }
152
153
154 int
155 mdoc_endparse(struct mdoc *mdoc)
156 {
157
158 if (MDOC_HALT & mdoc->flags)
159 return(0);
160 if (NULL == mdoc->first)
161 return(1);
162
163 assert(mdoc->last);
164 if ( ! macro_end(mdoc)) {
165 mdoc->flags |= MDOC_HALT;
166 return(0);
167 }
168 return(1);
169 }
170
171
172 /*
173 * Main parse routine. Parses a single line -- really just hands off to
174 * the macro or text parser.
175 */
176 int
177 mdoc_parseln(struct mdoc *m, int ln, char *buf)
178 {
179
180 /* If in error-mode, then we parse no more. */
181
182 if (MDOC_HALT & m->flags)
183 return(0);
184
185 return('.' == *buf ? parsemacro(m, ln, buf) :
186 parsetext(m, ln, buf));
187 }
188
189
190 void
191 mdoc_vmsg(struct mdoc *mdoc, int ln, int pos, const char *fmt, ...)
192 {
193 char buf[256];
194 va_list ap;
195
196 if (NULL == mdoc->cb.mdoc_msg)
197 return;
198
199 va_start(ap, fmt);
200 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
201 va_end(ap);
202 (*mdoc->cb.mdoc_msg)(mdoc->data, ln, pos, buf);
203 }
204
205
206 int
207 mdoc_verr(struct mdoc *mdoc, int ln, int pos,
208 const char *fmt, ...)
209 {
210 char buf[256];
211 va_list ap;
212
213 if (NULL == mdoc->cb.mdoc_err)
214 return(0);
215
216 va_start(ap, fmt);
217 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
218 va_end(ap);
219 return((*mdoc->cb.mdoc_err)(mdoc->data, ln, pos, buf));
220 }
221
222
223 int
224 mdoc_vwarn(struct mdoc *mdoc, int ln, int pos,
225 enum mdoc_warn type, const char *fmt, ...)
226 {
227 char buf[256];
228 va_list ap;
229
230 if (NULL == mdoc->cb.mdoc_warn)
231 return(0);
232
233 va_start(ap, fmt);
234 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
235 va_end(ap);
236 return((*mdoc->cb.mdoc_warn)(mdoc->data, ln, pos, type, buf));
237 }
238
239
240 int
241 mdoc_macro(struct mdoc *m, int tok,
242 int ln, int pp, int *pos, char *buf)
243 {
244
245 /* FIXME - these should happen during validation. */
246
247 if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
248 SEC_PROLOGUE != m->lastnamed)
249 return(mdoc_perr(m, ln, pp,
250 "disallowed in document body"));
251
252 if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
253 SEC_PROLOGUE == m->lastnamed)
254 return(mdoc_perr(m, ln, pp,
255 "disallowed in prologue"));
256
257 if (1 != pp && ! (MDOC_CALLABLE & mdoc_macros[tok].flags))
258 return(mdoc_perr(m, ln, pp, "not callable"));
259
260 return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf));
261 }
262
263
264 static int
265 mdoc_node_append(struct mdoc *mdoc, struct mdoc_node *p)
266 {
267
268 assert(mdoc->last);
269 assert(mdoc->first);
270 assert(MDOC_ROOT != p->type);
271
272 switch (mdoc->next) {
273 case (MDOC_NEXT_SIBLING):
274 mdoc->last->next = p;
275 p->prev = mdoc->last;
276 p->parent = mdoc->last->parent;
277 break;
278 case (MDOC_NEXT_CHILD):
279 mdoc->last->child = p;
280 p->parent = mdoc->last;
281 break;
282 default:
283 abort();
284 /* NOTREACHED */
285 }
286
287 if ( ! mdoc_valid_pre(mdoc, p))
288 return(0);
289
290 switch (p->type) {
291 case (MDOC_HEAD):
292 assert(MDOC_BLOCK == p->parent->type);
293 p->parent->head = p;
294 break;
295 case (MDOC_TAIL):
296 assert(MDOC_BLOCK == p->parent->type);
297 p->parent->tail = p;
298 break;
299 case (MDOC_BODY):
300 assert(MDOC_BLOCK == p->parent->type);
301 p->parent->body = p;
302 break;
303 default:
304 break;
305 }
306
307 mdoc->last = p;
308 return(1);
309 }
310
311
312 static struct mdoc_node *
313 mdoc_node_alloc(const struct mdoc *mdoc)
314 {
315 struct mdoc_node *p;
316
317 p = xcalloc(1, sizeof(struct mdoc_node));
318 p->sec = mdoc->lastsec;
319
320 return(p);
321 }
322
323
324 int
325 mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, int tok)
326 {
327 struct mdoc_node *p;
328
329 assert(mdoc->first);
330 assert(mdoc->last);
331
332 p = mdoc_node_alloc(mdoc);
333
334 p->line = line;
335 p->pos = pos;
336 p->type = MDOC_TAIL;
337 p->tok = tok;
338
339 return(mdoc_node_append(mdoc, p));
340 }
341
342
343 int
344 mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, int tok)
345 {
346 struct mdoc_node *p;
347
348 assert(mdoc->first);
349 assert(mdoc->last);
350
351 p = mdoc_node_alloc(mdoc);
352
353 p->line = line;
354 p->pos = pos;
355 p->type = MDOC_HEAD;
356 p->tok = tok;
357
358 return(mdoc_node_append(mdoc, p));
359 }
360
361
362 int
363 mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, int tok)
364 {
365 struct mdoc_node *p;
366
367 assert(mdoc->first);
368 assert(mdoc->last);
369
370 p = mdoc_node_alloc(mdoc);
371
372 p->line = line;
373 p->pos = pos;
374 p->type = MDOC_BODY;
375 p->tok = tok;
376
377 return(mdoc_node_append(mdoc, p));
378 }
379
380
381 int
382 mdoc_root_alloc(struct mdoc *mdoc)
383 {
384 struct mdoc_node *p;
385
386 p = mdoc_node_alloc(mdoc);
387
388 p->type = MDOC_ROOT;
389
390 return(mdoc_node_append(mdoc, p));
391 }
392
393
394 int
395 mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
396 int tok, struct mdoc_arg *args)
397 {
398 struct mdoc_node *p;
399
400 p = mdoc_node_alloc(mdoc);
401
402 p->pos = pos;
403 p->line = line;
404 p->type = MDOC_BLOCK;
405 p->tok = tok;
406 p->args = args;
407
408 if (args)
409 (args->refcnt)++;
410
411 return(mdoc_node_append(mdoc, p));
412 }
413
414
415 int
416 mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos,
417 int tok, struct mdoc_arg *args)
418 {
419 struct mdoc_node *p;
420
421 p = mdoc_node_alloc(mdoc);
422
423 p->line = line;
424 p->pos = pos;
425 p->type = MDOC_ELEM;
426 p->tok = tok;
427 p->args = args;
428
429 if (args)
430 (args->refcnt)++;
431
432 return(mdoc_node_append(mdoc, p));
433 }
434
435
436 int
437 mdoc_word_alloc(struct mdoc *mdoc,
438 int line, int pos, const char *word)
439 {
440 struct mdoc_node *p;
441
442 p = mdoc_node_alloc(mdoc);
443
444 p->line = line;
445 p->pos = pos;
446 p->type = MDOC_TEXT;
447 p->string = xstrdup(word);
448
449 return(mdoc_node_append(mdoc, p));
450 }
451
452
453 void
454 mdoc_node_free(struct mdoc_node *p)
455 {
456
457 if (p->string)
458 free(p->string);
459 if (p->args)
460 mdoc_argv_free(p->args);
461 free(p);
462 }
463
464
465 void
466 mdoc_node_freelist(struct mdoc_node *p)
467 {
468
469 if (p->child)
470 mdoc_node_freelist(p->child);
471 if (p->next)
472 mdoc_node_freelist(p->next);
473
474 mdoc_node_free(p);
475 }
476
477
478 /*
479 * Parse free-form text, that is, a line that does not begin with the
480 * control character.
481 */
482 static int
483 parsetext(struct mdoc *mdoc, int line, char *buf)
484 {
485
486 if (SEC_PROLOGUE == mdoc->lastnamed)
487 return(mdoc_perr(mdoc, line, 0,
488 "text disallowed in prologue"));
489
490 if ( ! mdoc_word_alloc(mdoc, line, 0, buf))
491 return(0);
492
493 mdoc->next = MDOC_NEXT_SIBLING;
494 return(1);
495 }
496
497
498 /*
499 * Parse a macro line, that is, a line beginning with the control
500 * character.
501 */
502 int
503 parsemacro(struct mdoc *m, int ln, char *buf)
504 {
505 int i, c;
506 char mac[5];
507
508 /* Comments are quickly ignored. */
509
510 if (buf[1] && '\\' == buf[1])
511 if (buf[2] && '\"' == buf[2])
512 return(1);
513
514 /* Copy the first word into a nil-terminated buffer. */
515
516 for (i = 1; i < 5; i++) {
517 if (0 == (mac[i - 1] = buf[i]))
518 break;
519 else if (isspace((unsigned char)buf[i]))
520 break;
521 }
522
523 /* FIXME: be able to skip unknown macro lines! */
524
525 mac[i - 1] = 0;
526
527 if (i == 5 || i <= 2) {
528 (void)mdoc_perr(m, ln, 1, "unknown macro: %s%s",
529 mac, i == 5 ? "..." : "");
530 goto err;
531 }
532
533 if (MDOC_MAX == (c = mdoc_tokhash_find(m->htab, mac))) {
534 (void)mdoc_perr(m, ln, 1, "unknown macro: %s", mac);
535 goto err;
536 }
537
538 /* The macro is sane. Jump to the next word. */
539
540 while (buf[i] && isspace((unsigned char)buf[i]))
541 i++;
542
543 /* Begin recursive parse sequence. */
544
545 if ( ! mdoc_macro(m, c, ln, 1, &i, buf))
546 goto err;
547
548 return(1);
549
550 err: /* Error out. */
551
552 m->flags |= MDOC_HALT;
553 return(0);
554 }