xref: /openbsd-src/usr.bin/mandoc/man.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /* $Id: man.c,v 1.1 2009/04/06 20:30:40 kristaps Exp $ */
2 /*
3  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
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 <stdarg.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include "libman.h"
27 
28 const	char *const __man_macronames[MAN_MAX] = {
29 	"\\\"",		"TH",		"SH",		"SS",
30 	"TP", 		"LP",		"PP",		"P",
31 	"IP",		"HP",		"SM",		"SB",
32 	"BI",		"IB",		"BR",		"RB",
33 	"R",		"B",		"I",		"IR",
34 	"RI",		"br",		"na",		"i"
35 	};
36 
37 const	char * const *man_macronames = __man_macronames;
38 
39 static	struct man_node	*man_node_alloc(int, int,
40 				enum man_type, int);
41 static	int		 man_node_append(struct man *,
42 				struct man_node *);
43 static	int		 man_ptext(struct man *, int, char *);
44 static	int		 man_pmacro(struct man *, int, char *);
45 static	void		 man_free1(struct man *);
46 static	int		 man_alloc1(struct man *);
47 
48 
49 const struct man_node *
50 man_node(const struct man *m)
51 {
52 
53 	return(MAN_HALT & m->flags ? NULL : m->first);
54 }
55 
56 
57 const struct man_meta *
58 man_meta(const struct man *m)
59 {
60 
61 	return(MAN_HALT & m->flags ? NULL : &m->meta);
62 }
63 
64 
65 int
66 man_reset(struct man *man)
67 {
68 
69 	man_free1(man);
70 	return(man_alloc1(man));
71 }
72 
73 
74 void
75 man_free(struct man *man)
76 {
77 
78 	man_free1(man);
79 
80 	if (man->htab)
81 		man_hash_free(man->htab);
82 	free(man);
83 }
84 
85 
86 struct man *
87 man_alloc(void *data, int pflags, const struct man_cb *cb)
88 {
89 	struct man	*p;
90 
91 	if (NULL == (p = calloc(1, sizeof(struct man))))
92 		return(NULL);
93 
94 	if ( ! man_alloc1(p)) {
95 		free(p);
96 		return(NULL);
97 	}
98 
99 	p->data = data;
100 	p->pflags = pflags;
101 	(void)memcpy(&p->cb, cb, sizeof(struct man_cb));
102 
103 	if (NULL == (p->htab = man_hash_alloc())) {
104 		free(p);
105 		return(NULL);
106 	}
107 	return(p);
108 }
109 
110 
111 int
112 man_endparse(struct man *m)
113 {
114 
115 	if (MAN_HALT & m->flags)
116 		return(0);
117 	else if (man_macroend(m))
118 		return(1);
119 	m->flags |= MAN_HALT;
120 	return(0);
121 }
122 
123 
124 int
125 man_parseln(struct man *m, int ln, char *buf)
126 {
127 
128 	return('.' == *buf ?
129 			man_pmacro(m, ln, buf) :
130 			man_ptext(m, ln, buf));
131 }
132 
133 
134 static void
135 man_free1(struct man *man)
136 {
137 
138 	if (man->first)
139 		man_node_freelist(man->first);
140 	if (man->meta.title)
141 		free(man->meta.title);
142 	if (man->meta.source)
143 		free(man->meta.source);
144 	if (man->meta.vol)
145 		free(man->meta.vol);
146 }
147 
148 
149 static int
150 man_alloc1(struct man *m)
151 {
152 
153 	bzero(&m->meta, sizeof(struct man_meta));
154 	m->flags = 0;
155 	m->last = calloc(1, sizeof(struct man_node));
156 	if (NULL == m->last)
157 		return(0);
158 	m->first = m->last;
159 	m->last->type = MAN_ROOT;
160 	m->next = MAN_NEXT_CHILD;
161 	return(1);
162 }
163 
164 
165 static int
166 man_node_append(struct man *man, struct man_node *p)
167 {
168 
169 	assert(man->last);
170 	assert(man->first);
171 	assert(MAN_ROOT != p->type);
172 
173 	switch (man->next) {
174 	case (MAN_NEXT_SIBLING):
175 		man->last->next = p;
176 		p->prev = man->last;
177 		p->parent = man->last->parent;
178 		break;
179 	case (MAN_NEXT_CHILD):
180 		man->last->child = p;
181 		p->parent = man->last;
182 		break;
183 	default:
184 		abort();
185 		/* NOTREACHED */
186 	}
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 	free(p);
256 }
257 
258 
259 void
260 man_node_freelist(struct man_node *p)
261 {
262 
263 	if (p->child)
264 		man_node_freelist(p->child);
265 	if (p->next)
266 		man_node_freelist(p->next);
267 
268 	man_node_free(p);
269 }
270 
271 
272 static int
273 man_ptext(struct man *m, int line, char *buf)
274 {
275 
276 	if ( ! man_word_alloc(m, line, 0, buf))
277 		return(0);
278 	m->next = MAN_NEXT_SIBLING;
279 
280 	/*
281 	 * If this is one of the zany NLINE macros that consumes the
282 	 * next line of input as being influenced, then close out the
283 	 * existing macro "scope" and continue processing.
284 	 */
285 
286 	if ( ! (MAN_NLINE & m->flags))
287 		return(1);
288 
289 	m->flags &= ~MAN_NLINE;
290 	m->last = m->last->parent;
291 
292 	assert(MAN_ROOT != m->last->type);
293 	if ( ! man_valid_post(m))
294 		return(0);
295 	if ( ! man_action_post(m))
296 		return(0);
297 
298 	return(1);
299 }
300 
301 
302 int
303 man_pmacro(struct man *m, int ln, char *buf)
304 {
305 	int		  i, j, c, ppos, fl;
306 	char		  mac[5];
307 	struct man_node	 *n;
308 
309 	/* Comments and empties are quickly ignored. */
310 
311 	n = m->last;
312 	fl = MAN_NLINE & m->flags;
313 
314 	if (0 == buf[1])
315 		goto out;
316 
317 	i = 1;
318 
319 	if (' ' == buf[i]) {
320 		i++;
321 		while (buf[i] && ' ' == buf[i])
322 			i++;
323 		if (0 == buf[i])
324 			goto out;
325 	}
326 
327 	ppos = i;
328 
329 	if (buf[i] && '\\' == buf[i])
330 		if (buf[i + 1] && '\"' == buf[i + 1])
331 			goto out;
332 
333 	/* Copy the first word into a nil-terminated buffer. */
334 
335 	for (j = 0; j < 4; j++, i++) {
336 		if (0 == (mac[j] = buf[i]))
337 			break;
338 		else if (' ' == buf[i])
339 			break;
340 	}
341 
342 	mac[j] = 0;
343 
344 	if (j == 4 || j < 1) {
345 		if ( ! (MAN_IGN_MACRO & m->pflags)) {
346 			(void)man_verr(m, ln, ppos,
347 				"ill-formed macro: %s", mac);
348 			goto err;
349 		}
350 		if ( ! man_vwarn(m, ln, ppos,
351 				"ill-formed macro: %s", mac))
352 			goto err;
353 		return(1);
354 	}
355 
356 	if (MAN_MAX == (c = man_hash_find(m->htab, mac))) {
357 		if ( ! (MAN_IGN_MACRO & m->pflags)) {
358 			(void)man_verr(m, ln, ppos,
359 				"unknown macro: %s", mac);
360 			goto err;
361 		}
362 		if ( ! man_vwarn(m, ln, ppos,
363 				"unknown macro: %s", mac))
364 			goto err;
365 		return(1);
366 	}
367 
368 	/* The macro is sane.  Jump to the next word. */
369 
370 	while (buf[i] && ' ' == buf[i])
371 		i++;
372 
373 	/* Begin recursive parse sequence. */
374 
375 	if ( ! man_macro(m, c, ln, ppos, &i, buf))
376 		goto err;
377 
378 out:
379 	if (fl) {
380 		/*
381 		 * A NLINE macro has been immediately followed with
382 		 * another.  Close out the preceeding macro's scope, and
383 		 * continue.
384 		 */
385 		assert(MAN_ROOT != m->last->type);
386 		assert(m->last->parent);
387 		assert(MAN_ROOT != m->last->parent->type);
388 
389 		if (n != m->last)
390 			m->last = m->last->parent;
391 
392 		if ( ! man_valid_post(m))
393 			return(0);
394 		if ( ! man_action_post(m))
395 			return(0);
396 		m->next = MAN_NEXT_SIBLING;
397 		m->flags &= ~MAN_NLINE;
398 	}
399 
400 	return(1);
401 
402 err:	/* Error out. */
403 
404 	m->flags |= MAN_HALT;
405 	return(0);
406 }
407 
408 
409 int
410 man_verr(struct man *man, int ln, int pos, const char *fmt, ...)
411 {
412 	char		 buf[256];
413 	va_list		 ap;
414 
415 	if (NULL == man->cb.man_err)
416 		return(0);
417 
418 	va_start(ap, fmt);
419 	(void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
420 	va_end(ap);
421 	return((*man->cb.man_err)(man->data, ln, pos, buf));
422 }
423 
424 
425 int
426 man_vwarn(struct man *man, int ln, int pos, const char *fmt, ...)
427 {
428 	char		 buf[256];
429 	va_list		 ap;
430 
431 	if (NULL == man->cb.man_warn)
432 		return(0);
433 
434 	va_start(ap, fmt);
435 	(void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
436 	va_end(ap);
437 	return((*man->cb.man_warn)(man->data, ln, pos, buf));
438 }
439 
440 
441