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