]> git.cameronkatri.com Git - mandoc.git/blob - man.c
c27ddaf8002c4e527f5984907d2f21be24a31858
[mandoc.git] / man.c
1 /* $Id: man.c,v 1.27 2009/07/07 09:35:40 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 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 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 #include <assert.h>
18 #include <ctype.h>
19 #include <stdarg.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23
24 #include "libman.h"
25
26 const char *const __man_merrnames[WERRMAX] = {
27 "invalid character", /* WNPRINT */
28 "system: malloc error", /* WNMEM */
29 "invalid manual section", /* WMSEC */
30 "invalid date format", /* WDATE */
31 "scope of prior line violated", /* WLNSCOPE */
32 "trailing whitespace", /* WTSPACE */
33 "unterminated quoted parameter", /* WTQUOTE */
34 "document has no body", /* WNODATA */
35 "document has no title/section", /* WNOTITLE */
36 "invalid escape sequence", /* WESCAPE */
37 };
38
39 const char *const __man_macronames[MAN_MAX] = {
40 "br", "TH", "SH", "SS",
41 "TP", "LP", "PP", "P",
42 "IP", "HP", "SM", "SB",
43 "BI", "IB", "BR", "RB",
44 "R", "B", "I", "IR",
45 "RI", "na", "i"
46 };
47
48 const char * const *man_macronames = __man_macronames;
49
50 static struct man_node *man_node_alloc(int, int,
51 enum man_type, int);
52 static int man_node_append(struct man *,
53 struct man_node *);
54 static int man_ptext(struct man *, int, char *);
55 static int man_pmacro(struct man *, int, char *);
56 static void man_free1(struct man *);
57 static int man_alloc1(struct man *);
58
59
60 const struct man_node *
61 man_node(const struct man *m)
62 {
63
64 return(MAN_HALT & m->flags ? NULL : m->first);
65 }
66
67
68 const struct man_meta *
69 man_meta(const struct man *m)
70 {
71
72 return(MAN_HALT & m->flags ? NULL : &m->meta);
73 }
74
75
76 int
77 man_reset(struct man *man)
78 {
79
80 man_free1(man);
81 return(man_alloc1(man));
82 }
83
84
85 void
86 man_free(struct man *man)
87 {
88
89 man_free1(man);
90
91 if (man->htab)
92 man_hash_free(man->htab);
93 free(man);
94 }
95
96
97 struct man *
98 man_alloc(void *data, int pflags, const struct man_cb *cb)
99 {
100 struct man *p;
101
102 if (NULL == (p = calloc(1, sizeof(struct man))))
103 return(NULL);
104
105 if ( ! man_alloc1(p)) {
106 free(p);
107 return(NULL);
108 }
109
110 p->data = data;
111 p->pflags = pflags;
112 (void)memcpy(&p->cb, cb, sizeof(struct man_cb));
113
114 if (NULL == (p->htab = man_hash_alloc())) {
115 free(p);
116 return(NULL);
117 }
118 return(p);
119 }
120
121
122 int
123 man_endparse(struct man *m)
124 {
125
126 if (MAN_HALT & m->flags)
127 return(0);
128 else if (man_macroend(m))
129 return(1);
130 m->flags |= MAN_HALT;
131 return(0);
132 }
133
134
135 int
136 man_parseln(struct man *m, int ln, char *buf)
137 {
138
139 return('.' == *buf ?
140 man_pmacro(m, ln, buf) :
141 man_ptext(m, ln, buf));
142 }
143
144
145 static void
146 man_free1(struct man *man)
147 {
148
149 if (man->first)
150 man_node_freelist(man->first);
151 if (man->meta.title)
152 free(man->meta.title);
153 if (man->meta.source)
154 free(man->meta.source);
155 if (man->meta.vol)
156 free(man->meta.vol);
157 }
158
159
160 static int
161 man_alloc1(struct man *m)
162 {
163
164 bzero(&m->meta, sizeof(struct man_meta));
165 m->flags = 0;
166 m->last = calloc(1, sizeof(struct man_node));
167 if (NULL == m->last)
168 return(0);
169 m->first = m->last;
170 m->last->type = MAN_ROOT;
171 m->next = MAN_NEXT_CHILD;
172 return(1);
173 }
174
175
176 static int
177 man_node_append(struct man *man, struct man_node *p)
178 {
179
180 assert(man->last);
181 assert(man->first);
182 assert(MAN_ROOT != p->type);
183
184 switch (man->next) {
185 case (MAN_NEXT_SIBLING):
186 man->last->next = p;
187 p->prev = man->last;
188 p->parent = man->last->parent;
189 break;
190 case (MAN_NEXT_CHILD):
191 man->last->child = p;
192 p->parent = man->last;
193 break;
194 default:
195 abort();
196 /* NOTREACHED */
197 }
198
199 p->parent->nchild++;
200
201 man->last = p;
202
203 switch (p->type) {
204 case (MAN_TEXT):
205 if ( ! man_valid_post(man))
206 return(0);
207 if ( ! man_action_post(man))
208 return(0);
209 break;
210 default:
211 break;
212 }
213
214 return(1);
215 }
216
217
218 static struct man_node *
219 man_node_alloc(int line, int pos, enum man_type type, int tok)
220 {
221 struct man_node *p;
222
223 p = calloc(1, sizeof(struct man_node));
224 if (NULL == p)
225 return(NULL);
226
227 p->line = line;
228 p->pos = pos;
229 p->type = type;
230 p->tok = tok;
231 return(p);
232 }
233
234
235 int
236 man_elem_alloc(struct man *man, int line, int pos, int tok)
237 {
238 struct man_node *p;
239
240 p = man_node_alloc(line, pos, MAN_ELEM, tok);
241 if (NULL == p)
242 return(0);
243 return(man_node_append(man, p));
244 }
245
246
247 int
248 man_word_alloc(struct man *man,
249 int line, int pos, const char *word)
250 {
251 struct man_node *p;
252
253 p = man_node_alloc(line, pos, MAN_TEXT, -1);
254 if (NULL == p)
255 return(0);
256 if (NULL == (p->string = strdup(word)))
257 return(0);
258 return(man_node_append(man, p));
259 }
260
261
262 void
263 man_node_free(struct man_node *p)
264 {
265
266 if (p->string)
267 free(p->string);
268 if (p->parent)
269 p->parent->nchild--;
270 free(p);
271 }
272
273
274 void
275 man_node_freelist(struct man_node *p)
276 {
277
278 if (p->child)
279 man_node_freelist(p->child);
280 if (p->next)
281 man_node_freelist(p->next);
282
283 assert(0 == p->nchild);
284 man_node_free(p);
285 }
286
287
288 static int
289 man_ptext(struct man *m, int line, char *buf)
290 {
291
292 if ( ! man_word_alloc(m, line, 0, buf))
293 return(0);
294 m->next = MAN_NEXT_SIBLING;
295
296 /*
297 * If this is one of the zany NLINE macros that consumes the
298 * next line of input as being influenced, then close out the
299 * existing macro "scope" and continue processing.
300 */
301
302 if ( ! (MAN_NLINE & m->flags))
303 return(1);
304
305 m->flags &= ~MAN_NLINE;
306 m->last = m->last->parent;
307
308 assert(MAN_ROOT != m->last->type);
309 if ( ! man_valid_post(m))
310 return(0);
311 if ( ! man_action_post(m))
312 return(0);
313
314 return(1);
315 }
316
317
318 int
319 man_pmacro(struct man *m, int ln, char *buf)
320 {
321 int i, j, c, ppos, fl;
322 char mac[5];
323 struct man_node *n;
324
325 /* Comments and empties are quickly ignored. */
326
327 n = m->last;
328 fl = MAN_NLINE & m->flags;
329
330 if (0 == buf[1])
331 goto out;
332
333 i = 1;
334
335 if (' ' == buf[i]) {
336 i++;
337 while (buf[i] && ' ' == buf[i])
338 i++;
339 if (0 == buf[i])
340 goto out;
341 }
342
343 ppos = i;
344
345 /* Copy the first word into a nil-terminated buffer. */
346
347 for (j = 0; j < 4; j++, i++) {
348 if (0 == (mac[j] = buf[i]))
349 break;
350 else if (' ' == buf[i])
351 break;
352 }
353
354 mac[j] = 0;
355
356 if (j == 4 || j < 1) {
357 if ( ! (MAN_IGN_MACRO & m->pflags)) {
358 (void)man_verr(m, ln, ppos,
359 "ill-formed macro: %s", mac);
360 goto err;
361 }
362 if ( ! man_vwarn(m, ln, ppos,
363 "ill-formed macro: %s", mac))
364 goto err;
365 return(1);
366 }
367
368 if (MAN_MAX == (c = man_hash_find(m->htab, mac))) {
369 if ( ! (MAN_IGN_MACRO & m->pflags)) {
370 (void)man_verr(m, ln, ppos,
371 "unknown macro: %s", mac);
372 goto err;
373 }
374 if ( ! man_vwarn(m, ln, ppos,
375 "unknown macro: %s", mac))
376 goto err;
377 return(1);
378 }
379
380 /* The macro is sane. Jump to the next word. */
381
382 while (buf[i] && ' ' == buf[i])
383 i++;
384
385 /* Begin recursive parse sequence. */
386
387 if ( ! man_macro(m, c, ln, ppos, &i, buf))
388 goto err;
389
390 out:
391 if (fl) {
392 /*
393 * A NLINE macro has been immediately followed with
394 * another. Close out the preceding macro's scope, and
395 * continue.
396 */
397 assert(MAN_ROOT != m->last->type);
398 assert(m->last->parent);
399 assert(MAN_ROOT != m->last->parent->type);
400
401 if (n != m->last)
402 m->last = m->last->parent;
403
404 if ( ! man_valid_post(m))
405 return(0);
406 if ( ! man_action_post(m))
407 return(0);
408 m->next = MAN_NEXT_SIBLING;
409 m->flags &= ~MAN_NLINE;
410 }
411
412 return(1);
413
414 err: /* Error out. */
415
416 m->flags |= MAN_HALT;
417 return(0);
418 }
419
420
421 int
422 man_verr(struct man *man, int ln, int pos, const char *fmt, ...)
423 {
424 char buf[256];
425 va_list ap;
426
427 if (NULL == man->cb.man_err)
428 return(0);
429
430 va_start(ap, fmt);
431 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
432 va_end(ap);
433 return((*man->cb.man_err)(man->data, ln, pos, buf));
434 }
435
436
437 int
438 man_vwarn(struct man *man, int ln, int pos, const char *fmt, ...)
439 {
440 char buf[256];
441 va_list ap;
442
443 if (NULL == man->cb.man_warn)
444 return(0);
445
446 va_start(ap, fmt);
447 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
448 va_end(ap);
449 return((*man->cb.man_warn)(man->data, ln, pos, buf));
450 }
451
452
453 int
454 man_err(struct man *m, int line, int pos, int iserr, enum merr type)
455 {
456 const char *p;
457
458 p = __man_merrnames[(int)type];
459 assert(p);
460
461 if (iserr)
462 return(man_verr(m, line, pos, p));
463
464 return(man_vwarn(m, line, pos, p));
465 }