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