xref: /dflybsd-src/contrib/mdocml/mdoc.c (revision 1e4d43f9c96723e4e55543d240f182e1aac9a4c2)
1*99db7d0eSSascha Wildner /* $Id: mdoc.c,v 1.275 2020/04/06 10:16:17 schwarze Exp $ */
280387638SSascha Wildner /*
3*99db7d0eSSascha Wildner  * Copyright (c) 2010, 2012-2018, 2020 Ingo Schwarze <schwarze@openbsd.org>
460e1e752SSascha Wildner  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
580387638SSascha Wildner  *
680387638SSascha Wildner  * Permission to use, copy, modify, and distribute this software for any
780387638SSascha Wildner  * purpose with or without fee is hereby granted, provided that the above
880387638SSascha Wildner  * copyright notice and this permission notice appear in all copies.
980387638SSascha Wildner  *
1054ba9607SSascha Wildner  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1180387638SSascha Wildner  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1254ba9607SSascha Wildner  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1380387638SSascha Wildner  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1480387638SSascha Wildner  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1580387638SSascha Wildner  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1680387638SSascha Wildner  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*99db7d0eSSascha Wildner  *
18*99db7d0eSSascha Wildner  * Top level and utility functions of the mdoc(7) parser for mandoc(1).
1980387638SSascha Wildner  */
2080387638SSascha Wildner #include "config.h"
2180387638SSascha Wildner 
2280387638SSascha Wildner #include <sys/types.h>
2380387638SSascha Wildner 
2480387638SSascha Wildner #include <assert.h>
25070c62a6SFranco Fichtner #include <ctype.h>
2680387638SSascha Wildner #include <stdarg.h>
2780387638SSascha Wildner #include <stdio.h>
2880387638SSascha Wildner #include <stdlib.h>
2980387638SSascha Wildner #include <string.h>
3080387638SSascha Wildner #include <time.h>
3180387638SSascha Wildner 
32070c62a6SFranco Fichtner #include "mandoc_aux.h"
3354ba9607SSascha Wildner #include "mandoc.h"
3454ba9607SSascha Wildner #include "roff.h"
3554ba9607SSascha Wildner #include "mdoc.h"
3680387638SSascha Wildner #include "libmandoc.h"
3754ba9607SSascha Wildner #include "roff_int.h"
3854ba9607SSascha Wildner #include "libmdoc.h"
3980387638SSascha Wildner 
4080387638SSascha Wildner const	char *const __mdoc_argnames[MDOC_ARG_MAX] = {
4180387638SSascha Wildner 	"split",		"nosplit",		"ragged",
4280387638SSascha Wildner 	"unfilled",		"literal",		"file",
4380387638SSascha Wildner 	"offset",		"bullet",		"dash",
4480387638SSascha Wildner 	"hyphen",		"item",			"enum",
4580387638SSascha Wildner 	"tag",			"diag",			"hang",
4680387638SSascha Wildner 	"ohang",		"inset",		"column",
4780387638SSascha Wildner 	"width",		"compact",		"std",
4880387638SSascha Wildner 	"filled",		"words",		"emphasis",
4980387638SSascha Wildner 	"symbolic",		"nested",		"centered"
5080387638SSascha Wildner };
5180387638SSascha Wildner const	char * const *mdoc_argnames = __mdoc_argnames;
5280387638SSascha Wildner 
5354ba9607SSascha Wildner static	int		  mdoc_ptext(struct roff_man *, int, char *, int);
5454ba9607SSascha Wildner static	int		  mdoc_pmacro(struct roff_man *, int, char *, int);
5580387638SSascha Wildner 
56070c62a6SFranco Fichtner 
5780387638SSascha Wildner /*
5880387638SSascha Wildner  * Main parse routine.  Parses a single line -- really just hands off to
5980387638SSascha Wildner  * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
6080387638SSascha Wildner  */
6180387638SSascha Wildner int
mdoc_parseln(struct roff_man * mdoc,int ln,char * buf,int offs)6254ba9607SSascha Wildner mdoc_parseln(struct roff_man *mdoc, int ln, char *buf, int offs)
6380387638SSascha Wildner {
6480387638SSascha Wildner 
6554ba9607SSascha Wildner 	if (mdoc->last->type != ROFFT_EQN || ln > mdoc->last->line)
66f88b6c16SFranco Fichtner 		mdoc->flags |= MDOC_NEWLINE;
6780387638SSascha Wildner 
6880387638SSascha Wildner 	/*
6980387638SSascha Wildner 	 * Let the roff nS register switch SYNOPSIS mode early,
7080387638SSascha Wildner 	 * such that the parser knows at all times
7180387638SSascha Wildner 	 * whether this mode is on or off.
7280387638SSascha Wildner 	 * Note that this mode is also switched by the Sh macro.
7380387638SSascha Wildner 	 */
747888c61dSFranco Fichtner 	if (roff_getreg(mdoc->roff, "nS"))
75f88b6c16SFranco Fichtner 		mdoc->flags |= MDOC_SYNOPSIS;
7680387638SSascha Wildner 	else
77f88b6c16SFranco Fichtner 		mdoc->flags &= ~MDOC_SYNOPSIS;
7880387638SSascha Wildner 
7954ba9607SSascha Wildner 	return roff_getcontrol(mdoc->roff, buf, &offs) ?
80f88b6c16SFranco Fichtner 	    mdoc_pmacro(mdoc, ln, buf, offs) :
8154ba9607SSascha Wildner 	    mdoc_ptext(mdoc, ln, buf, offs);
8280387638SSascha Wildner }
8380387638SSascha Wildner 
8454ba9607SSascha Wildner void
mdoc_tail_alloc(struct roff_man * mdoc,int line,int pos,enum roff_tok tok)8554ba9607SSascha Wildner mdoc_tail_alloc(struct roff_man *mdoc, int line, int pos, enum roff_tok tok)
8680387638SSascha Wildner {
8754ba9607SSascha Wildner 	struct roff_node *p;
8880387638SSascha Wildner 
8954ba9607SSascha Wildner 	p = roff_node_alloc(mdoc, line, pos, ROFFT_TAIL, tok);
9054ba9607SSascha Wildner 	roff_node_append(mdoc, p);
9154ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_CHILD;
9280387638SSascha Wildner }
9380387638SSascha Wildner 
9454ba9607SSascha Wildner struct roff_node *
mdoc_endbody_alloc(struct roff_man * mdoc,int line,int pos,enum roff_tok tok,struct roff_node * body)9554ba9607SSascha Wildner mdoc_endbody_alloc(struct roff_man *mdoc, int line, int pos,
9654ba9607SSascha Wildner     enum roff_tok tok, struct roff_node *body)
9780387638SSascha Wildner {
9854ba9607SSascha Wildner 	struct roff_node *p;
9980387638SSascha Wildner 
10054ba9607SSascha Wildner 	body->flags |= NODE_ENDED;
10154ba9607SSascha Wildner 	body->parent->flags |= NODE_ENDED;
10254ba9607SSascha Wildner 	p = roff_node_alloc(mdoc, line, pos, ROFFT_BODY, tok);
10354ba9607SSascha Wildner 	p->body = body;
104f88b6c16SFranco Fichtner 	p->norm = body->norm;
10554ba9607SSascha Wildner 	p->end = ENDBODY_SPACE;
10654ba9607SSascha Wildner 	roff_node_append(mdoc, p);
10754ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_SIBLING;
10854ba9607SSascha Wildner 	return p;
10980387638SSascha Wildner }
11080387638SSascha Wildner 
11154ba9607SSascha Wildner struct roff_node *
mdoc_block_alloc(struct roff_man * mdoc,int line,int pos,enum roff_tok tok,struct mdoc_arg * args)11254ba9607SSascha Wildner mdoc_block_alloc(struct roff_man *mdoc, int line, int pos,
11354ba9607SSascha Wildner     enum roff_tok tok, struct mdoc_arg *args)
11480387638SSascha Wildner {
11554ba9607SSascha Wildner 	struct roff_node *p;
11680387638SSascha Wildner 
11754ba9607SSascha Wildner 	p = roff_node_alloc(mdoc, line, pos, ROFFT_BLOCK, tok);
11880387638SSascha Wildner 	p->args = args;
11980387638SSascha Wildner 	if (p->args)
12080387638SSascha Wildner 		(args->refcnt)++;
12180387638SSascha Wildner 
12280387638SSascha Wildner 	switch (tok) {
123070c62a6SFranco Fichtner 	case MDOC_Bd:
124070c62a6SFranco Fichtner 	case MDOC_Bf:
125070c62a6SFranco Fichtner 	case MDOC_Bl:
126070c62a6SFranco Fichtner 	case MDOC_En:
127070c62a6SFranco Fichtner 	case MDOC_Rs:
12880387638SSascha Wildner 		p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
12980387638SSascha Wildner 		break;
13080387638SSascha Wildner 	default:
13180387638SSascha Wildner 		break;
13280387638SSascha Wildner 	}
13354ba9607SSascha Wildner 	roff_node_append(mdoc, p);
13454ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_CHILD;
13554ba9607SSascha Wildner 	return p;
13680387638SSascha Wildner }
13780387638SSascha Wildner 
13854ba9607SSascha Wildner void
mdoc_elem_alloc(struct roff_man * mdoc,int line,int pos,enum roff_tok tok,struct mdoc_arg * args)13954ba9607SSascha Wildner mdoc_elem_alloc(struct roff_man *mdoc, int line, int pos,
14054ba9607SSascha Wildner      enum roff_tok tok, struct mdoc_arg *args)
14180387638SSascha Wildner {
14254ba9607SSascha Wildner 	struct roff_node *p;
14380387638SSascha Wildner 
14454ba9607SSascha Wildner 	p = roff_node_alloc(mdoc, line, pos, ROFFT_ELEM, tok);
14580387638SSascha Wildner 	p->args = args;
14680387638SSascha Wildner 	if (p->args)
14780387638SSascha Wildner 		(args->refcnt)++;
14880387638SSascha Wildner 
14980387638SSascha Wildner 	switch (tok) {
150070c62a6SFranco Fichtner 	case MDOC_An:
15180387638SSascha Wildner 		p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
15280387638SSascha Wildner 		break;
15380387638SSascha Wildner 	default:
15480387638SSascha Wildner 		break;
15580387638SSascha Wildner 	}
15654ba9607SSascha Wildner 	roff_node_append(mdoc, p);
15754ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_CHILD;
15880387638SSascha Wildner }
15980387638SSascha Wildner 
16080387638SSascha Wildner /*
16180387638SSascha Wildner  * Parse free-form text, that is, a line that does not begin with the
16280387638SSascha Wildner  * control character.
16380387638SSascha Wildner  */
16480387638SSascha Wildner static int
mdoc_ptext(struct roff_man * mdoc,int line,char * buf,int offs)16554ba9607SSascha Wildner mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs)
16680387638SSascha Wildner {
16754ba9607SSascha Wildner 	struct roff_node *n;
16854ba9607SSascha Wildner 	const char	 *cp, *sp;
16980387638SSascha Wildner 	char		 *c, *ws, *end;
17080387638SSascha Wildner 
171f88b6c16SFranco Fichtner 	n = mdoc->last;
17280387638SSascha Wildner 
17380387638SSascha Wildner 	/*
17454ba9607SSascha Wildner 	 * If a column list contains plain text, assume an implicit item
17554ba9607SSascha Wildner 	 * macro.  This can happen one or more times at the beginning
17654ba9607SSascha Wildner 	 * of such a list, intermixed with non-It mdoc macros and with
17754ba9607SSascha Wildner 	 * nodes generated on the roff level, for example by tbl.
17880387638SSascha Wildner 	 */
17980387638SSascha Wildner 
18054ba9607SSascha Wildner 	if ((n->tok == MDOC_Bl && n->type == ROFFT_BODY &&
18154ba9607SSascha Wildner 	     n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) ||
18254ba9607SSascha Wildner 	    (n->parent != NULL && n->parent->tok == MDOC_Bl &&
18354ba9607SSascha Wildner 	     n->parent->norm->Bl.type == LIST_column)) {
184f88b6c16SFranco Fichtner 		mdoc->flags |= MDOC_FREECOL;
18554ba9607SSascha Wildner 		(*mdoc_macro(MDOC_It)->fp)(mdoc, MDOC_It,
18654ba9607SSascha Wildner 		    line, offs, &offs, buf);
18754ba9607SSascha Wildner 		return 1;
18880387638SSascha Wildner 	}
18980387638SSascha Wildner 
19080387638SSascha Wildner 	/*
19180387638SSascha Wildner 	 * Search for the beginning of unescaped trailing whitespace (ws)
19280387638SSascha Wildner 	 * and for the first character not to be output (end).
19380387638SSascha Wildner 	 */
19480387638SSascha Wildner 
19580387638SSascha Wildner 	/* FIXME: replace with strcspn(). */
19680387638SSascha Wildner 	ws = NULL;
19780387638SSascha Wildner 	for (c = end = buf + offs; *c; c++) {
19880387638SSascha Wildner 		switch (*c) {
19980387638SSascha Wildner 		case ' ':
20080387638SSascha Wildner 			if (NULL == ws)
20180387638SSascha Wildner 				ws = c;
20280387638SSascha Wildner 			continue;
20380387638SSascha Wildner 		case '\t':
20480387638SSascha Wildner 			/*
20580387638SSascha Wildner 			 * Always warn about trailing tabs,
20680387638SSascha Wildner 			 * even outside literal context,
20780387638SSascha Wildner 			 * where they should be put on the next line.
20880387638SSascha Wildner 			 */
20980387638SSascha Wildner 			if (NULL == ws)
21080387638SSascha Wildner 				ws = c;
21180387638SSascha Wildner 			/*
21280387638SSascha Wildner 			 * Strip trailing tabs in literal context only;
21380387638SSascha Wildner 			 * outside, they affect the next line.
21480387638SSascha Wildner 			 */
21554ba9607SSascha Wildner 			if (mdoc->flags & ROFF_NOFILL)
21680387638SSascha Wildner 				continue;
21780387638SSascha Wildner 			break;
21880387638SSascha Wildner 		case '\\':
21980387638SSascha Wildner 			/* Skip the escaped character, too, if any. */
22080387638SSascha Wildner 			if (c[1])
22180387638SSascha Wildner 				c++;
22280387638SSascha Wildner 			/* FALLTHROUGH */
22380387638SSascha Wildner 		default:
22480387638SSascha Wildner 			ws = NULL;
22580387638SSascha Wildner 			break;
22680387638SSascha Wildner 		}
22780387638SSascha Wildner 		end = c + 1;
22880387638SSascha Wildner 	}
22980387638SSascha Wildner 	*end = '\0';
23080387638SSascha Wildner 
23180387638SSascha Wildner 	if (ws)
23254ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_SPACE_EOL, line, (int)(ws - buf), NULL);
23380387638SSascha Wildner 
23480387638SSascha Wildner 	/*
23554ba9607SSascha Wildner 	 * Blank lines are allowed in no-fill mode
23654ba9607SSascha Wildner 	 * and cancel preceding \c,
23754ba9607SSascha Wildner 	 * but add a single vertical space elsewhere.
23880387638SSascha Wildner 	 */
23980387638SSascha Wildner 
24054ba9607SSascha Wildner 	if (buf[offs] == '\0' && (mdoc->flags & ROFF_NOFILL) == 0) {
24154ba9607SSascha Wildner 		switch (mdoc->last->type) {
24254ba9607SSascha Wildner 		case ROFFT_TEXT:
24354ba9607SSascha Wildner 			sp = mdoc->last->string;
24454ba9607SSascha Wildner 			cp = end = strchr(sp, '\0') - 2;
24554ba9607SSascha Wildner 			if (cp < sp || cp[0] != '\\' || cp[1] != 'c')
24654ba9607SSascha Wildner 				break;
24754ba9607SSascha Wildner 			while (cp > sp && cp[-1] == '\\')
24854ba9607SSascha Wildner 				cp--;
24954ba9607SSascha Wildner 			if ((end - cp) % 2)
25054ba9607SSascha Wildner 				break;
25154ba9607SSascha Wildner 			*end = '\0';
25254ba9607SSascha Wildner 			return 1;
25354ba9607SSascha Wildner 		default:
25454ba9607SSascha Wildner 			break;
25554ba9607SSascha Wildner 		}
25654ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_FI_BLANK, line, (int)(c - buf), NULL);
25754ba9607SSascha Wildner 		roff_elem_alloc(mdoc, line, offs, ROFF_sp);
25854ba9607SSascha Wildner 		mdoc->last->flags |= NODE_VALID | NODE_ENDED;
25954ba9607SSascha Wildner 		mdoc->next = ROFF_NEXT_SIBLING;
26054ba9607SSascha Wildner 		return 1;
26180387638SSascha Wildner 	}
26280387638SSascha Wildner 
26354ba9607SSascha Wildner 	roff_word_alloc(mdoc, line, offs, buf+offs);
26480387638SSascha Wildner 
26554ba9607SSascha Wildner 	if (mdoc->flags & ROFF_NOFILL)
26654ba9607SSascha Wildner 		return 1;
26780387638SSascha Wildner 
26880387638SSascha Wildner 	/*
26980387638SSascha Wildner 	 * End-of-sentence check.  If the last character is an unescaped
27080387638SSascha Wildner 	 * EOS character, then flag the node as being the end of a
27180387638SSascha Wildner 	 * sentence.  The front-end will know how to interpret this.
27280387638SSascha Wildner 	 */
27380387638SSascha Wildner 
27480387638SSascha Wildner 	assert(buf < end);
27580387638SSascha Wildner 
276070c62a6SFranco Fichtner 	if (mandoc_eos(buf+offs, (size_t)(end-buf-offs)))
27754ba9607SSascha Wildner 		mdoc->last->flags |= NODE_EOS;
27880387638SSascha Wildner 
27954ba9607SSascha Wildner 	for (c = buf + offs; c != NULL; c = strchr(c + 1, '.')) {
28054ba9607SSascha Wildner 		if (c - buf < offs + 2)
28154ba9607SSascha Wildner 			continue;
28254ba9607SSascha Wildner 		if (end - c < 3)
28354ba9607SSascha Wildner 			break;
28454ba9607SSascha Wildner 		if (c[1] != ' ' ||
28554ba9607SSascha Wildner 		    isalnum((unsigned char)c[-2]) == 0 ||
28654ba9607SSascha Wildner 		    isalnum((unsigned char)c[-1]) == 0 ||
28754ba9607SSascha Wildner 		    (c[-2] == 'n' && c[-1] == 'c') ||
28854ba9607SSascha Wildner 		    (c[-2] == 'v' && c[-1] == 's'))
28954ba9607SSascha Wildner 			continue;
29054ba9607SSascha Wildner 		c += 2;
29154ba9607SSascha Wildner 		if (*c == ' ')
29254ba9607SSascha Wildner 			c++;
29354ba9607SSascha Wildner 		if (*c == ' ')
29454ba9607SSascha Wildner 			c++;
29554ba9607SSascha Wildner 		if (isupper((unsigned char)(*c)))
29654ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_EOS, line, (int)(c - buf), NULL);
29754ba9607SSascha Wildner 	}
29854ba9607SSascha Wildner 
29954ba9607SSascha Wildner 	return 1;
30080387638SSascha Wildner }
30180387638SSascha Wildner 
30280387638SSascha Wildner /*
30380387638SSascha Wildner  * Parse a macro line, that is, a line beginning with the control
30480387638SSascha Wildner  * character.
30580387638SSascha Wildner  */
30680387638SSascha Wildner static int
mdoc_pmacro(struct roff_man * mdoc,int ln,char * buf,int offs)30754ba9607SSascha Wildner mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs)
30880387638SSascha Wildner {
30954ba9607SSascha Wildner 	struct roff_node *n;
31054ba9607SSascha Wildner 	const char	 *cp;
31154ba9607SSascha Wildner 	size_t		  sz;
31254ba9607SSascha Wildner 	enum roff_tok	  tok;
31354ba9607SSascha Wildner 	int		  sv;
31480387638SSascha Wildner 
31554ba9607SSascha Wildner 	/* Determine the line macro. */
31680387638SSascha Wildner 
31760e1e752SSascha Wildner 	sv = offs;
31854ba9607SSascha Wildner 	tok = TOKEN_NONE;
31954ba9607SSascha Wildner 	for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++)
32054ba9607SSascha Wildner 		offs++;
32154ba9607SSascha Wildner 	if (sz == 2 || sz == 3)
32254ba9607SSascha Wildner 		tok = roffhash_find(mdoc->mdocmac, buf + sv, sz);
32354ba9607SSascha Wildner 	if (tok == TOKEN_NONE) {
32454ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_MACRO, ln, sv, "%s", buf + sv - 1);
32554ba9607SSascha Wildner 		return 1;
32680387638SSascha Wildner 	}
32780387638SSascha Wildner 
32854ba9607SSascha Wildner 	/* Skip a leading escape sequence or tab. */
32980387638SSascha Wildner 
33054ba9607SSascha Wildner 	switch (buf[offs]) {
33154ba9607SSascha Wildner 	case '\\':
33254ba9607SSascha Wildner 		cp = buf + offs + 1;
33354ba9607SSascha Wildner 		mandoc_escape(&cp, NULL, NULL);
33454ba9607SSascha Wildner 		offs = cp - buf;
33554ba9607SSascha Wildner 		break;
33654ba9607SSascha Wildner 	case '\t':
33760e1e752SSascha Wildner 		offs++;
33854ba9607SSascha Wildner 		break;
33954ba9607SSascha Wildner 	default:
34054ba9607SSascha Wildner 		break;
34154ba9607SSascha Wildner 	}
34280387638SSascha Wildner 
34380387638SSascha Wildner 	/* Jump to the next non-whitespace word. */
34480387638SSascha Wildner 
34554ba9607SSascha Wildner 	while (buf[offs] == ' ')
34660e1e752SSascha Wildner 		offs++;
34780387638SSascha Wildner 
34880387638SSascha Wildner 	/*
34980387638SSascha Wildner 	 * Trailing whitespace.  Note that tabs are allowed to be passed
35080387638SSascha Wildner 	 * into the parser as "text", so we only warn about spaces here.
35180387638SSascha Wildner 	 */
35280387638SSascha Wildner 
35360e1e752SSascha Wildner 	if ('\0' == buf[offs] && ' ' == buf[offs - 1])
35454ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL);
35580387638SSascha Wildner 
35680387638SSascha Wildner 	/*
357*99db7d0eSSascha Wildner 	 * If an initial or transparent macro or a list invocation,
358*99db7d0eSSascha Wildner 	 * divert directly into macro processing.
35980387638SSascha Wildner 	 */
36080387638SSascha Wildner 
361f88b6c16SFranco Fichtner 	n = mdoc->last;
362*99db7d0eSSascha Wildner 	if (n == NULL || tok == MDOC_It || tok == MDOC_El ||
363*99db7d0eSSascha Wildner 	    roff_tok_transparent(tok)) {
36454ba9607SSascha Wildner 		(*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf);
36554ba9607SSascha Wildner 		return 1;
36680387638SSascha Wildner 	}
36780387638SSascha Wildner 
36880387638SSascha Wildner 	/*
36954ba9607SSascha Wildner 	 * If a column list contains a non-It macro, assume an implicit
37054ba9607SSascha Wildner 	 * item macro.  This can happen one or more times at the
37154ba9607SSascha Wildner 	 * beginning of such a list, intermixed with text lines and
37254ba9607SSascha Wildner 	 * with nodes generated on the roff level, for example by tbl.
37380387638SSascha Wildner 	 */
37480387638SSascha Wildner 
37554ba9607SSascha Wildner 	if ((n->tok == MDOC_Bl && n->type == ROFFT_BODY &&
37654ba9607SSascha Wildner 	     n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) ||
37754ba9607SSascha Wildner 	    (n->parent != NULL && n->parent->tok == MDOC_Bl &&
37854ba9607SSascha Wildner 	     n->parent->norm->Bl.type == LIST_column)) {
379f88b6c16SFranco Fichtner 		mdoc->flags |= MDOC_FREECOL;
38054ba9607SSascha Wildner 		(*mdoc_macro(MDOC_It)->fp)(mdoc, MDOC_It, ln, sv, &sv, buf);
38154ba9607SSascha Wildner 		return 1;
38280387638SSascha Wildner 	}
38380387638SSascha Wildner 
38480387638SSascha Wildner 	/* Normal processing of a macro. */
38580387638SSascha Wildner 
38654ba9607SSascha Wildner 	(*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf);
387070c62a6SFranco Fichtner 
388070c62a6SFranco Fichtner 	/* In quick mode (for mandocdb), abort after the NAME section. */
389070c62a6SFranco Fichtner 
390070c62a6SFranco Fichtner 	if (mdoc->quick && MDOC_Sh == tok &&
391070c62a6SFranco Fichtner 	    SEC_NAME != mdoc->last->sec)
39254ba9607SSascha Wildner 		return 2;
39380387638SSascha Wildner 
39454ba9607SSascha Wildner 	return 1;
39580387638SSascha Wildner }
39680387638SSascha Wildner 
39760e1e752SSascha Wildner enum mdelim
mdoc_isdelim(const char * p)39860e1e752SSascha Wildner mdoc_isdelim(const char *p)
39960e1e752SSascha Wildner {
40080387638SSascha Wildner 
40160e1e752SSascha Wildner 	if ('\0' == p[0])
40254ba9607SSascha Wildner 		return DELIM_NONE;
40360e1e752SSascha Wildner 
40460e1e752SSascha Wildner 	if ('\0' == p[1])
40560e1e752SSascha Wildner 		switch (p[0]) {
406070c62a6SFranco Fichtner 		case '(':
407070c62a6SFranco Fichtner 		case '[':
40854ba9607SSascha Wildner 			return DELIM_OPEN;
409070c62a6SFranco Fichtner 		case '|':
41054ba9607SSascha Wildner 			return DELIM_MIDDLE;
411070c62a6SFranco Fichtner 		case '.':
412070c62a6SFranco Fichtner 		case ',':
413070c62a6SFranco Fichtner 		case ';':
414070c62a6SFranco Fichtner 		case ':':
415070c62a6SFranco Fichtner 		case '?':
416070c62a6SFranco Fichtner 		case '!':
417070c62a6SFranco Fichtner 		case ')':
418070c62a6SFranco Fichtner 		case ']':
41954ba9607SSascha Wildner 			return DELIM_CLOSE;
42060e1e752SSascha Wildner 		default:
42154ba9607SSascha Wildner 			return DELIM_NONE;
42260e1e752SSascha Wildner 		}
42360e1e752SSascha Wildner 
42460e1e752SSascha Wildner 	if ('\\' != p[0])
42554ba9607SSascha Wildner 		return DELIM_NONE;
42660e1e752SSascha Wildner 
42760e1e752SSascha Wildner 	if (0 == strcmp(p + 1, "."))
42854ba9607SSascha Wildner 		return DELIM_CLOSE;
429f88b6c16SFranco Fichtner 	if (0 == strcmp(p + 1, "fR|\\fP"))
43054ba9607SSascha Wildner 		return DELIM_MIDDLE;
43160e1e752SSascha Wildner 
43254ba9607SSascha Wildner 	return DELIM_NONE;
433070c62a6SFranco Fichtner }
434