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