mdoc.c revision 1.37
1/*	$Id: mdoc.c,v 1.37 2010/04/02 12:39:47 schwarze 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 <sys/types.h>
18
19#include <assert.h>
20#include <ctype.h>
21#include <stdarg.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include "libmdoc.h"
27#include "libmandoc.h"
28
29const	char *const __mdoc_merrnames[MERRMAX] = {
30	"trailing whitespace", /* ETAILWS */
31	"unexpected quoted parameter", /* EQUOTPARM */
32	"unterminated quoted parameter", /* EQUOTTERM */
33	"argument parameter suggested", /* EARGVAL */
34	"macro disallowed in prologue", /* EBODYPROL */
35	"macro disallowed in body", /* EPROLBODY */
36	"text disallowed in prologue", /* ETEXTPROL */
37	"blank line disallowed", /* ENOBLANK */
38	"text parameter too long", /* ETOOLONG */
39	"invalid escape sequence", /* EESCAPE */
40	"invalid character", /* EPRINT */
41	"document has no body", /* ENODAT */
42	"document has no prologue", /* ENOPROLOGUE */
43	"expected line arguments", /* ELINE */
44	"invalid AT&T argument", /* EATT */
45	"default name not yet set", /* ENAME */
46	"missing list type", /* ELISTTYPE */
47	"missing display type", /* EDISPTYPE */
48	"too many display types", /* EMULTIDISP */
49	"too many list types", /* EMULTILIST */
50	"NAME section must be first", /* ESECNAME */
51	"badly-formed NAME section", /* ENAMESECINC */
52	"argument repeated", /* EARGREP */
53	"expected boolean parameter", /* EBOOL */
54	"inconsistent column syntax", /* ECOLMIS */
55	"nested display invalid", /* ENESTDISP */
56	"width argument missing", /* EMISSWIDTH */
57	"invalid section for this manual section", /* EWRONGMSEC */
58	"section out of conventional order", /* ESECOOO */
59	"section repeated", /* ESECREP */
60	"invalid standard argument", /* EBADSTAND */
61	"multi-line arguments discouraged", /* ENOMULTILINE */
62	"multi-line arguments suggested", /* EMULTILINE */
63	"line arguments discouraged", /* ENOLINE */
64	"prologue macro out of conventional order", /* EPROLOOO */
65	"prologue macro repeated", /* EPROLREP */
66	"invalid manual section", /* EBADMSEC */
67	"invalid section", /* EBADSEC */
68	"invalid font mode", /* EFONT */
69	"invalid date syntax", /* EBADDATE */
70	"invalid number format", /* ENUMFMT */
71	"superfluous width argument", /* ENOWIDTH */
72	"system: utsname error", /* EUTSNAME */
73	"obsolete macro", /* EOBS */
74	"end-of-line scope violation", /* EIMPBRK */
75	"empty macro ignored", /* EIGNE */
76	"unclosed explicit scope", /* EOPEN */
77	"unterminated quoted phrase", /* EQUOTPHR */
78	"closure macro without prior context", /* ENOCTX */
79	"no description found for library", /* ELIB */
80	"bad child for parent context", /* EBADCHILD */
81	"list arguments preceding type", /* ENOTYPE */
82};
83
84const	char *const __mdoc_macronames[MDOC_MAX] = {
85	"Ap",		"Dd",		"Dt",		"Os",
86	"Sh",		"Ss",		"Pp",		"D1",
87	"Dl",		"Bd",		"Ed",		"Bl",
88	"El",		"It",		"Ad",		"An",
89	"Ar",		"Cd",		"Cm",		"Dv",
90	"Er",		"Ev",		"Ex",		"Fa",
91	"Fd",		"Fl",		"Fn",		"Ft",
92	"Ic",		"In",		"Li",		"Nd",
93	"Nm",		"Op",		"Ot",		"Pa",
94	"Rv",		"St",		"Va",		"Vt",
95	/* LINTED */
96	"Xr",		"%A",		"%B",		"%D",
97	/* LINTED */
98	"%I",		"%J",		"%N",		"%O",
99	/* LINTED */
100	"%P",		"%R",		"%T",		"%V",
101	"Ac",		"Ao",		"Aq",		"At",
102	"Bc",		"Bf",		"Bo",		"Bq",
103	"Bsx",		"Bx",		"Db",		"Dc",
104	"Do",		"Dq",		"Ec",		"Ef",
105	"Em",		"Eo",		"Fx",		"Ms",
106	"No",		"Ns",		"Nx",		"Ox",
107	"Pc",		"Pf",		"Po",		"Pq",
108	"Qc",		"Ql",		"Qo",		"Qq",
109	"Re",		"Rs",		"Sc",		"So",
110	"Sq",		"Sm",		"Sx",		"Sy",
111	"Tn",		"Ux",		"Xc",		"Xo",
112	"Fo",		"Fc",		"Oo",		"Oc",
113	"Bk",		"Ek",		"Bt",		"Hf",
114	"Fr",		"Ud",		"Lb",		"Lp",
115	"Lk",		"Mt",		"Brq",		"Bro",
116	/* LINTED */
117	"Brc",		"%C",		"Es",		"En",
118	/* LINTED */
119	"Dx",		"%Q",		"br",		"sp",
120	/* LINTED */
121	"%U",		"eos"
122	};
123
124const	char *const __mdoc_argnames[MDOC_ARG_MAX] = {
125	"split",		"nosplit",		"ragged",
126	"unfilled",		"literal",		"file",
127	"offset",		"bullet",		"dash",
128	"hyphen",		"item",			"enum",
129	"tag",			"diag",			"hang",
130	"ohang",		"inset",		"column",
131	"width",		"compact",		"std",
132	"filled",		"words",		"emphasis",
133	"symbolic",		"nested",		"centered"
134	};
135
136const	char * const *mdoc_macronames = __mdoc_macronames;
137const	char * const *mdoc_argnames = __mdoc_argnames;
138
139static	void		  mdoc_free1(struct mdoc *);
140static	void		  mdoc_alloc1(struct mdoc *);
141static	struct mdoc_node *node_alloc(struct mdoc *, int, int,
142				enum mdoct, enum mdoc_type);
143static	int		  node_append(struct mdoc *,
144				struct mdoc_node *);
145static	int		  parsetext(struct mdoc *, int, char *);
146static	int		  parsemacro(struct mdoc *, int, char *);
147static	int		  macrowarn(struct mdoc *, int, const char *);
148static	int		  pstring(struct mdoc *, int, int,
149				const char *, size_t);
150
151const struct mdoc_node *
152mdoc_node(const struct mdoc *m)
153{
154
155	return(MDOC_HALT & m->flags ? NULL : m->first);
156}
157
158
159const struct mdoc_meta *
160mdoc_meta(const struct mdoc *m)
161{
162
163	return(MDOC_HALT & m->flags ? NULL : &m->meta);
164}
165
166
167/*
168 * Frees volatile resources (parse tree, meta-data, fields).
169 */
170static void
171mdoc_free1(struct mdoc *mdoc)
172{
173
174	if (mdoc->first)
175		mdoc_node_freelist(mdoc->first);
176	if (mdoc->meta.title)
177		free(mdoc->meta.title);
178	if (mdoc->meta.os)
179		free(mdoc->meta.os);
180	if (mdoc->meta.name)
181		free(mdoc->meta.name);
182	if (mdoc->meta.arch)
183		free(mdoc->meta.arch);
184	if (mdoc->meta.vol)
185		free(mdoc->meta.vol);
186}
187
188
189/*
190 * Allocate all volatile resources (parse tree, meta-data, fields).
191 */
192static void
193mdoc_alloc1(struct mdoc *mdoc)
194{
195
196	memset(&mdoc->meta, 0, sizeof(struct mdoc_meta));
197	mdoc->flags = 0;
198	mdoc->lastnamed = mdoc->lastsec = SEC_NONE;
199	mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node));
200	mdoc->first = mdoc->last;
201	mdoc->last->type = MDOC_ROOT;
202	mdoc->next = MDOC_NEXT_CHILD;
203}
204
205
206/*
207 * Free up volatile resources (see mdoc_free1()) then re-initialises the
208 * data with mdoc_alloc1().  After invocation, parse data has been reset
209 * and the parser is ready for re-invocation on a new tree; however,
210 * cross-parse non-volatile data is kept intact.
211 */
212void
213mdoc_reset(struct mdoc *mdoc)
214{
215
216	mdoc_free1(mdoc);
217	mdoc_alloc1(mdoc);
218}
219
220
221/*
222 * Completely free up all volatile and non-volatile parse resources.
223 * After invocation, the pointer is no longer usable.
224 */
225void
226mdoc_free(struct mdoc *mdoc)
227{
228
229	mdoc_free1(mdoc);
230	free(mdoc);
231}
232
233
234/*
235 * Allocate volatile and non-volatile parse resources.
236 */
237struct mdoc *
238mdoc_alloc(void *data, int pflags, const struct mdoc_cb *cb)
239{
240	struct mdoc	*p;
241
242	p = mandoc_calloc(1, sizeof(struct mdoc));
243
244	if (cb)
245		memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
246
247	p->data = data;
248	p->pflags = pflags;
249
250	mdoc_hash_init();
251	mdoc_alloc1(p);
252	return(p);
253}
254
255
256/*
257 * Climb back up the parse tree, validating open scopes.  Mostly calls
258 * through to macro_end() in macro.c.
259 */
260int
261mdoc_endparse(struct mdoc *m)
262{
263
264	if (MDOC_HALT & m->flags)
265		return(0);
266	else if (mdoc_macroend(m))
267		return(1);
268	m->flags |= MDOC_HALT;
269	return(0);
270}
271
272
273/*
274 * Main parse routine.  Parses a single line -- really just hands off to
275 * the macro (parsemacro()) or text parser (parsetext()).
276 */
277int
278mdoc_parseln(struct mdoc *m, int ln, char *buf)
279{
280
281	if (MDOC_HALT & m->flags)
282		return(0);
283
284	return('.' == *buf ? parsemacro(m, ln, buf) :
285			parsetext(m, ln, buf));
286}
287
288
289int
290mdoc_verr(struct mdoc *mdoc, int ln, int pos,
291		const char *fmt, ...)
292{
293	char		 buf[256];
294	va_list		 ap;
295
296	if (NULL == mdoc->cb.mdoc_err)
297		return(0);
298
299	va_start(ap, fmt);
300	(void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
301	va_end(ap);
302
303	return((*mdoc->cb.mdoc_err)(mdoc->data, ln, pos, buf));
304}
305
306
307int
308mdoc_vwarn(struct mdoc *mdoc, int ln, int pos, const char *fmt, ...)
309{
310	char		 buf[256];
311	va_list		 ap;
312
313	if (NULL == mdoc->cb.mdoc_warn)
314		return(0);
315
316	va_start(ap, fmt);
317	(void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
318	va_end(ap);
319
320	return((*mdoc->cb.mdoc_warn)(mdoc->data, ln, pos, buf));
321}
322
323
324int
325mdoc_err(struct mdoc *m, int line, int pos, int iserr, enum merr type)
326{
327	const char	*p;
328
329	p = __mdoc_merrnames[(int)type];
330	assert(p);
331
332	if (iserr)
333		return(mdoc_verr(m, line, pos, p));
334
335	return(mdoc_vwarn(m, line, pos, p));
336}
337
338
339int
340mdoc_macro(struct mdoc *m, enum mdoct tok,
341		int ln, int pp, int *pos, char *buf)
342{
343
344	assert(tok < MDOC_MAX);
345	/*
346	 * If we're in the prologue, deny "body" macros.  Similarly, if
347	 * we're in the body, deny prologue calls.
348	 */
349	if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
350			MDOC_PBODY & m->flags)
351		return(mdoc_perr(m, ln, pp, EPROLBODY));
352	if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
353			! (MDOC_PBODY & m->flags))
354		return(mdoc_perr(m, ln, pp, EBODYPROL));
355
356	return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf));
357}
358
359
360static int
361node_append(struct mdoc *mdoc, struct mdoc_node *p)
362{
363
364	assert(mdoc->last);
365	assert(mdoc->first);
366	assert(MDOC_ROOT != p->type);
367
368	switch (mdoc->next) {
369	case (MDOC_NEXT_SIBLING):
370		mdoc->last->next = p;
371		p->prev = mdoc->last;
372		p->parent = mdoc->last->parent;
373		break;
374	case (MDOC_NEXT_CHILD):
375		mdoc->last->child = p;
376		p->parent = mdoc->last;
377		break;
378	default:
379		abort();
380		/* NOTREACHED */
381	}
382
383	p->parent->nchild++;
384
385	if ( ! mdoc_valid_pre(mdoc, p))
386		return(0);
387	if ( ! mdoc_action_pre(mdoc, p))
388		return(0);
389
390	switch (p->type) {
391	case (MDOC_HEAD):
392		assert(MDOC_BLOCK == p->parent->type);
393		p->parent->head = p;
394		break;
395	case (MDOC_TAIL):
396		assert(MDOC_BLOCK == p->parent->type);
397		p->parent->tail = p;
398		break;
399	case (MDOC_BODY):
400		assert(MDOC_BLOCK == p->parent->type);
401		p->parent->body = p;
402		break;
403	default:
404		break;
405	}
406
407	mdoc->last = p;
408
409	switch (p->type) {
410	case (MDOC_TEXT):
411		if ( ! mdoc_valid_post(mdoc))
412			return(0);
413		if ( ! mdoc_action_post(mdoc))
414			return(0);
415		break;
416	default:
417		break;
418	}
419
420	return(1);
421}
422
423
424static struct mdoc_node *
425node_alloc(struct mdoc *m, int line, int pos,
426		enum mdoct tok, enum mdoc_type type)
427{
428	struct mdoc_node *p;
429
430	p = mandoc_calloc(1, sizeof(struct mdoc_node));
431	p->sec = m->lastsec;
432	p->line = line;
433	p->pos = pos;
434	p->tok = tok;
435	p->type = type;
436
437	return(p);
438}
439
440
441int
442mdoc_tail_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
443{
444	struct mdoc_node *p;
445
446	p = node_alloc(m, line, pos, tok, MDOC_TAIL);
447	if ( ! node_append(m, p))
448		return(0);
449	m->next = MDOC_NEXT_CHILD;
450	return(1);
451}
452
453
454int
455mdoc_head_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
456{
457	struct mdoc_node *p;
458
459	assert(m->first);
460	assert(m->last);
461
462	p = node_alloc(m, line, pos, tok, MDOC_HEAD);
463	if ( ! node_append(m, p))
464		return(0);
465	m->next = MDOC_NEXT_CHILD;
466	return(1);
467}
468
469
470int
471mdoc_body_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
472{
473	struct mdoc_node *p;
474
475	p = node_alloc(m, line, pos, tok, MDOC_BODY);
476	if ( ! node_append(m, p))
477		return(0);
478	m->next = MDOC_NEXT_CHILD;
479	return(1);
480}
481
482
483int
484mdoc_block_alloc(struct mdoc *m, int line, int pos,
485		enum mdoct tok, struct mdoc_arg *args)
486{
487	struct mdoc_node *p;
488
489	p = node_alloc(m, line, pos, tok, MDOC_BLOCK);
490	p->args = args;
491	if (p->args)
492		(args->refcnt)++;
493	if ( ! node_append(m, p))
494		return(0);
495	m->next = MDOC_NEXT_CHILD;
496	return(1);
497}
498
499
500int
501mdoc_elem_alloc(struct mdoc *m, int line, int pos,
502		enum mdoct tok, struct mdoc_arg *args)
503{
504	struct mdoc_node *p;
505
506	p = node_alloc(m, line, pos, tok, MDOC_ELEM);
507	p->args = args;
508	if (p->args)
509		(args->refcnt)++;
510	if ( ! node_append(m, p))
511		return(0);
512	m->next = MDOC_NEXT_CHILD;
513	return(1);
514}
515
516
517static int
518pstring(struct mdoc *m, int line, int pos, const char *p, size_t len)
519{
520	struct mdoc_node *n;
521	size_t		  sv;
522
523	n = node_alloc(m, line, pos, -1, MDOC_TEXT);
524	n->string = mandoc_malloc(len + 1);
525	sv = strlcpy(n->string, p, len + 1);
526
527	/* Prohibit truncation. */
528	assert(sv < len + 1);
529
530	if ( ! node_append(m, n))
531		return(0);
532	m->next = MDOC_NEXT_SIBLING;
533	return(1);
534}
535
536
537int
538mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p)
539{
540
541	return(pstring(m, line, pos, p, strlen(p)));
542}
543
544
545void
546mdoc_node_free(struct mdoc_node *p)
547{
548
549	if (p->parent)
550		p->parent->nchild--;
551	if (p->string)
552		free(p->string);
553	if (p->args)
554		mdoc_argv_free(p->args);
555	free(p);
556}
557
558
559void
560mdoc_node_freelist(struct mdoc_node *p)
561{
562
563	if (p->child)
564		mdoc_node_freelist(p->child);
565	if (p->next)
566		mdoc_node_freelist(p->next);
567
568	assert(0 == p->nchild);
569	mdoc_node_free(p);
570}
571
572
573/*
574 * Parse free-form text, that is, a line that does not begin with the
575 * control character.
576 */
577static int
578parsetext(struct mdoc *m, int line, char *buf)
579{
580	int		 i, j;
581	char		 sv;
582
583	if (SEC_NONE == m->lastnamed)
584		return(mdoc_perr(m, line, 0, ETEXTPROL));
585
586	/*
587	 * If in literal mode, then pass the buffer directly to the
588	 * back-end, as it should be preserved as a single term.
589	 */
590
591	if (MDOC_LITERAL & m->flags)
592		return(mdoc_word_alloc(m, line, 0, buf));
593
594	/* Disallow blank/white-space lines in non-literal mode. */
595
596	for (i = 0; ' ' == buf[i]; i++)
597		/* Skip leading whitespace. */ ;
598
599	if ('\0' == buf[i])
600		return(mdoc_perr(m, line, 0, ENOBLANK));
601
602	/*
603	 * Break apart a free-form line into tokens.  Spaces are
604	 * stripped out of the input.
605	 */
606
607	for (j = i; buf[i]; i++) {
608		if (' ' != buf[i])
609			continue;
610
611		/* Escaped whitespace. */
612		if (i && ' ' == buf[i] && '\\' == buf[i - 1])
613			continue;
614
615		sv = buf[i];
616		buf[i++] = '\0';
617
618		if ( ! pstring(m, line, j, &buf[j], (size_t)(i - j)))
619			return(0);
620
621		/* Trailing whitespace?  Check at overwritten byte. */
622
623		if (' ' == sv && '\0' == buf[i])
624			if ( ! mdoc_pwarn(m, line, i - 1, ETAILWS))
625				return(0);
626
627		for ( ; ' ' == buf[i]; i++)
628			/* Skip trailing whitespace. */ ;
629
630		j = i;
631
632		/* Trailing whitespace? */
633
634		if (' ' == buf[i - 1] && '\0' == buf[i])
635			if ( ! mdoc_pwarn(m, line, i - 1, ETAILWS))
636				return(0);
637
638		if ('\0' == buf[i])
639			break;
640	}
641
642	if (j != i && ! pstring(m, line, j, &buf[j], (size_t)(i - j)))
643		return(0);
644
645	/*
646	 * Mark the end of a sentence.  Only works when you respect
647	 * Jason's rule: "new sentence, new line".
648	 */
649	if ('.' == buf[i-1] || '!' == buf[i-1] || '?' == buf[i-1]) {
650		m->next = MDOC_NEXT_SIBLING;
651		if ( ! mdoc_elem_alloc(m, line, i, MDOC_eos, NULL))
652			return(0);
653	}
654
655	m->next = MDOC_NEXT_SIBLING;
656	return(1);
657}
658
659
660
661static int
662macrowarn(struct mdoc *m, int ln, const char *buf)
663{
664	if ( ! (MDOC_IGN_MACRO & m->pflags))
665		return(mdoc_verr(m, ln, 0,
666				"unknown macro: %s%s",
667				buf, strlen(buf) > 3 ? "..." : ""));
668	return(mdoc_vwarn(m, ln, 0, "unknown macro: %s%s",
669				buf, strlen(buf) > 3 ? "..." : ""));
670}
671
672
673/*
674 * Parse a macro line, that is, a line beginning with the control
675 * character.
676 */
677int
678parsemacro(struct mdoc *m, int ln, char *buf)
679{
680	int		  i, j, c;
681	char		  mac[5];
682	struct mdoc_node *n;
683	char		 *t;
684
685	/* Empty lines are ignored. */
686
687	if ('\0' == buf[1])
688		return(1);
689
690	i = 1;
691
692	/* Accept whitespace after the initial control char. */
693
694	if (' ' == buf[i]) {
695		i++;
696		while (buf[i] && ' ' == buf[i])
697			i++;
698		if ('\0' == buf[i])
699			return(1);
700	}
701
702	/* Copy the first word into a nil-terminated buffer. */
703
704	for (j = 0; j < 4; j++, i++) {
705		if ('\0' == (mac[j] = buf[i]))
706			break;
707		else if (' ' == buf[i])
708			break;
709
710		/* Check for invalid characters. */
711
712		if (isgraph((u_char)buf[i]))
713			continue;
714		return(mdoc_perr(m, ln, i, EPRINT));
715	}
716
717	mac[j] = 0;
718
719	if (j == 4 || j < 2) {
720		if ( ! macrowarn(m, ln, mac))
721			goto err;
722		return(1);
723	}
724
725	if (MDOC_MAX == (c = mdoc_hash_find(mac))) {
726		if ( ! macrowarn(m, ln, mac))
727			goto err;
728		return(1);
729	}
730
731	/* The macro is sane.  Jump to the next word. */
732
733	while (buf[i] && ' ' == buf[i])
734		i++;
735
736	/* Trailing whitespace? */
737
738	if ('\0' == buf[i] && ' ' == buf[i - 1])
739		if ( ! mdoc_pwarn(m, ln, i - 1, ETAILWS))
740			goto err;
741
742	/*
743	 * Begin recursive parse sequence.  Since we're at the start of
744	 * the line, we don't need to do callable/parseable checks.
745	 */
746	if ( ! mdoc_macro(m, c, ln, 1, &i, buf))
747		goto err;
748
749	/*
750	 * Mark the end of a sentence, but be careful not to insert
751	 * markers into reference blocks.
752	 */
753	n = m->last;
754	if (n->child)
755		n = n->child;
756	while (n->next)
757		n = n->next;
758	if (MDOC_TEXT == n->type && m->last->parent->tok != MDOC_Rs) {
759		t = n->string;
760		while (t[0] && t[1])
761			t++;
762		if ('.' == *t || '!' == *t || '?' == *t) {
763			if ( ! mdoc_elem_alloc(m, ln, i, MDOC_eos, NULL))
764				return(0);
765			m->next = MDOC_NEXT_SIBLING;
766		}
767	}
768
769	return(1);
770
771err:	/* Error out. */
772
773	m->flags |= MDOC_HALT;
774	return(0);
775}
776
777
778