1*c1c95addSBrooks Davis /* $Id: roff.c,v 1.400 2023/10/24 20:53:12 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
3*c1c95addSBrooks Davis * Copyright (c) 2010-2015, 2017-2023 Ingo Schwarze <schwarze@openbsd.org>
461d06d6bSBaptiste Daroussin * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
561d06d6bSBaptiste Daroussin *
661d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any
761d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above
861d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies.
961d06d6bSBaptiste Daroussin *
1061d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1161d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1261d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1361d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1461d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1561d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1661d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176d38604fSBaptiste Daroussin *
186d38604fSBaptiste Daroussin * Implementation of the roff(7) parser for mandoc(1).
1961d06d6bSBaptiste Daroussin */
2061d06d6bSBaptiste Daroussin #include "config.h"
2161d06d6bSBaptiste Daroussin
2261d06d6bSBaptiste Daroussin #include <sys/types.h>
2361d06d6bSBaptiste Daroussin
2461d06d6bSBaptiste Daroussin #include <assert.h>
2561d06d6bSBaptiste Daroussin #include <ctype.h>
2661d06d6bSBaptiste Daroussin #include <limits.h>
2761d06d6bSBaptiste Daroussin #include <stddef.h>
2861d06d6bSBaptiste Daroussin #include <stdint.h>
2961d06d6bSBaptiste Daroussin #include <stdio.h>
3061d06d6bSBaptiste Daroussin #include <stdlib.h>
3161d06d6bSBaptiste Daroussin #include <string.h>
3261d06d6bSBaptiste Daroussin
3361d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
3461d06d6bSBaptiste Daroussin #include "mandoc_ohash.h"
357295610fSBaptiste Daroussin #include "mandoc.h"
3661d06d6bSBaptiste Daroussin #include "roff.h"
377295610fSBaptiste Daroussin #include "mandoc_parse.h"
3861d06d6bSBaptiste Daroussin #include "libmandoc.h"
3961d06d6bSBaptiste Daroussin #include "roff_int.h"
407295610fSBaptiste Daroussin #include "tbl_parse.h"
417295610fSBaptiste Daroussin #include "eqn_parse.h"
427295610fSBaptiste Daroussin
4361d06d6bSBaptiste Daroussin /* Maximum number of string expansions per line, to break infinite loops. */
4461d06d6bSBaptiste Daroussin #define EXPAND_LIMIT 1000
4561d06d6bSBaptiste Daroussin
4661d06d6bSBaptiste Daroussin /* Types of definitions of macros and strings. */
4761d06d6bSBaptiste Daroussin #define ROFFDEF_USER (1 << 1) /* User-defined. */
4861d06d6bSBaptiste Daroussin #define ROFFDEF_PRE (1 << 2) /* Predefined. */
4961d06d6bSBaptiste Daroussin #define ROFFDEF_REN (1 << 3) /* Renamed standard macro. */
5061d06d6bSBaptiste Daroussin #define ROFFDEF_STD (1 << 4) /* mdoc(7) or man(7) macro. */
5161d06d6bSBaptiste Daroussin #define ROFFDEF_ANY (ROFFDEF_USER | ROFFDEF_PRE | \
5261d06d6bSBaptiste Daroussin ROFFDEF_REN | ROFFDEF_STD)
5361d06d6bSBaptiste Daroussin #define ROFFDEF_UNDEF (1 << 5) /* Completely undefined. */
5461d06d6bSBaptiste Daroussin
5561d06d6bSBaptiste Daroussin /* --- data types --------------------------------------------------------- */
5661d06d6bSBaptiste Daroussin
5761d06d6bSBaptiste Daroussin /*
5861d06d6bSBaptiste Daroussin * An incredibly-simple string buffer.
5961d06d6bSBaptiste Daroussin */
6061d06d6bSBaptiste Daroussin struct roffstr {
6161d06d6bSBaptiste Daroussin char *p; /* nil-terminated buffer */
6261d06d6bSBaptiste Daroussin size_t sz; /* saved strlen(p) */
6361d06d6bSBaptiste Daroussin };
6461d06d6bSBaptiste Daroussin
6561d06d6bSBaptiste Daroussin /*
6661d06d6bSBaptiste Daroussin * A key-value roffstr pair as part of a singly-linked list.
6761d06d6bSBaptiste Daroussin */
6861d06d6bSBaptiste Daroussin struct roffkv {
6961d06d6bSBaptiste Daroussin struct roffstr key;
7061d06d6bSBaptiste Daroussin struct roffstr val;
7161d06d6bSBaptiste Daroussin struct roffkv *next; /* next in list */
7261d06d6bSBaptiste Daroussin };
7361d06d6bSBaptiste Daroussin
7461d06d6bSBaptiste Daroussin /*
7561d06d6bSBaptiste Daroussin * A single number register as part of a singly-linked list.
7661d06d6bSBaptiste Daroussin */
7761d06d6bSBaptiste Daroussin struct roffreg {
7861d06d6bSBaptiste Daroussin struct roffstr key;
7961d06d6bSBaptiste Daroussin int val;
8061d06d6bSBaptiste Daroussin int step;
8161d06d6bSBaptiste Daroussin struct roffreg *next;
8261d06d6bSBaptiste Daroussin };
8361d06d6bSBaptiste Daroussin
8461d06d6bSBaptiste Daroussin /*
8561d06d6bSBaptiste Daroussin * Association of request and macro names with token IDs.
8661d06d6bSBaptiste Daroussin */
8761d06d6bSBaptiste Daroussin struct roffreq {
8861d06d6bSBaptiste Daroussin enum roff_tok tok;
8961d06d6bSBaptiste Daroussin char name[];
9061d06d6bSBaptiste Daroussin };
9161d06d6bSBaptiste Daroussin
927295610fSBaptiste Daroussin /*
937295610fSBaptiste Daroussin * A macro processing context.
947295610fSBaptiste Daroussin * More than one is needed when macro calls are nested.
957295610fSBaptiste Daroussin */
967295610fSBaptiste Daroussin struct mctx {
977295610fSBaptiste Daroussin char **argv;
987295610fSBaptiste Daroussin int argc;
997295610fSBaptiste Daroussin int argsz;
1007295610fSBaptiste Daroussin };
1017295610fSBaptiste Daroussin
10261d06d6bSBaptiste Daroussin struct roff {
10361d06d6bSBaptiste Daroussin struct roff_man *man; /* mdoc or man parser */
10461d06d6bSBaptiste Daroussin struct roffnode *last; /* leaf of stack */
1057295610fSBaptiste Daroussin struct mctx *mstack; /* stack of macro contexts */
10661d06d6bSBaptiste Daroussin int *rstack; /* stack of inverted `ie' values */
10761d06d6bSBaptiste Daroussin struct ohash *reqtab; /* request lookup table */
10861d06d6bSBaptiste Daroussin struct roffreg *regtab; /* number registers */
10961d06d6bSBaptiste Daroussin struct roffkv *strtab; /* user-defined strings & macros */
11061d06d6bSBaptiste Daroussin struct roffkv *rentab; /* renamed strings & macros */
11161d06d6bSBaptiste Daroussin struct roffkv *xmbtab; /* multi-byte trans table (`tr') */
11261d06d6bSBaptiste Daroussin struct roffstr *xtab; /* single-byte trans table (`tr') */
11361d06d6bSBaptiste Daroussin const char *current_string; /* value of last called user macro */
11461d06d6bSBaptiste Daroussin struct tbl_node *first_tbl; /* first table parsed */
11561d06d6bSBaptiste Daroussin struct tbl_node *last_tbl; /* last table parsed */
11661d06d6bSBaptiste Daroussin struct tbl_node *tbl; /* current table being parsed */
11761d06d6bSBaptiste Daroussin struct eqn_node *last_eqn; /* equation parser */
11861d06d6bSBaptiste Daroussin struct eqn_node *eqn; /* active equation parser */
11961d06d6bSBaptiste Daroussin int eqn_inline; /* current equation is inline */
12061d06d6bSBaptiste Daroussin int options; /* parse options */
1217295610fSBaptiste Daroussin int mstacksz; /* current size of mstack */
1227295610fSBaptiste Daroussin int mstackpos; /* position in mstack */
12361d06d6bSBaptiste Daroussin int rstacksz; /* current size limit of rstack */
12461d06d6bSBaptiste Daroussin int rstackpos; /* position in rstack */
12561d06d6bSBaptiste Daroussin int format; /* current file in mdoc or man format */
12661d06d6bSBaptiste Daroussin char control; /* control character */
12761d06d6bSBaptiste Daroussin char escape; /* escape character */
12861d06d6bSBaptiste Daroussin };
12961d06d6bSBaptiste Daroussin
13045a5aec3SBaptiste Daroussin /*
13145a5aec3SBaptiste Daroussin * A macro definition, condition, or ignored block.
13245a5aec3SBaptiste Daroussin */
13361d06d6bSBaptiste Daroussin struct roffnode {
13461d06d6bSBaptiste Daroussin enum roff_tok tok; /* type of node */
13561d06d6bSBaptiste Daroussin struct roffnode *parent; /* up one in stack */
13661d06d6bSBaptiste Daroussin int line; /* parse line */
13761d06d6bSBaptiste Daroussin int col; /* parse col */
13861d06d6bSBaptiste Daroussin char *name; /* node name, e.g. macro name */
13945a5aec3SBaptiste Daroussin char *end; /* custom end macro of the block */
14045a5aec3SBaptiste Daroussin int endspan; /* scope to: 1=eol 2=next line -1=\} */
14145a5aec3SBaptiste Daroussin int rule; /* content is: 1=evaluated 0=skipped */
14261d06d6bSBaptiste Daroussin };
14361d06d6bSBaptiste Daroussin
14461d06d6bSBaptiste Daroussin #define ROFF_ARGS struct roff *r, /* parse ctx */ \
14561d06d6bSBaptiste Daroussin enum roff_tok tok, /* tok of macro */ \
14661d06d6bSBaptiste Daroussin struct buf *buf, /* input buffer */ \
14761d06d6bSBaptiste Daroussin int ln, /* parse line */ \
14861d06d6bSBaptiste Daroussin int ppos, /* original pos in buffer */ \
14961d06d6bSBaptiste Daroussin int pos, /* current pos in buffer */ \
15061d06d6bSBaptiste Daroussin int *offs /* reset offset of buffer data */
15161d06d6bSBaptiste Daroussin
1527295610fSBaptiste Daroussin typedef int (*roffproc)(ROFF_ARGS);
15361d06d6bSBaptiste Daroussin
15461d06d6bSBaptiste Daroussin struct roffmac {
15561d06d6bSBaptiste Daroussin roffproc proc; /* process new macro */
15661d06d6bSBaptiste Daroussin roffproc text; /* process as child text of macro */
15761d06d6bSBaptiste Daroussin roffproc sub; /* process as child of macro */
15861d06d6bSBaptiste Daroussin int flags;
15961d06d6bSBaptiste Daroussin #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
16061d06d6bSBaptiste Daroussin };
16161d06d6bSBaptiste Daroussin
16261d06d6bSBaptiste Daroussin struct predef {
16361d06d6bSBaptiste Daroussin const char *name; /* predefined input name */
16461d06d6bSBaptiste Daroussin const char *str; /* replacement symbol */
16561d06d6bSBaptiste Daroussin };
16661d06d6bSBaptiste Daroussin
16761d06d6bSBaptiste Daroussin #define PREDEF(__name, __str) \
16861d06d6bSBaptiste Daroussin { (__name), (__str) },
16961d06d6bSBaptiste Daroussin
17061d06d6bSBaptiste Daroussin /* --- function prototypes ------------------------------------------------ */
17161d06d6bSBaptiste Daroussin
1727295610fSBaptiste Daroussin static int roffnode_cleanscope(struct roff *);
1737295610fSBaptiste Daroussin static int roffnode_pop(struct roff *);
17461d06d6bSBaptiste Daroussin static void roffnode_push(struct roff *, enum roff_tok,
17561d06d6bSBaptiste Daroussin const char *, int, int);
1767295610fSBaptiste Daroussin static void roff_addtbl(struct roff_man *, int, struct tbl_node *);
1777295610fSBaptiste Daroussin static int roff_als(ROFF_ARGS);
1787295610fSBaptiste Daroussin static int roff_block(ROFF_ARGS);
1797295610fSBaptiste Daroussin static int roff_block_text(ROFF_ARGS);
1807295610fSBaptiste Daroussin static int roff_block_sub(ROFF_ARGS);
18145a5aec3SBaptiste Daroussin static int roff_break(ROFF_ARGS);
1827295610fSBaptiste Daroussin static int roff_cblock(ROFF_ARGS);
1837295610fSBaptiste Daroussin static int roff_cc(ROFF_ARGS);
1847295610fSBaptiste Daroussin static int roff_ccond(struct roff *, int, int);
1857295610fSBaptiste Daroussin static int roff_char(ROFF_ARGS);
1867295610fSBaptiste Daroussin static int roff_cond(ROFF_ARGS);
1876d38604fSBaptiste Daroussin static int roff_cond_checkend(ROFF_ARGS);
1887295610fSBaptiste Daroussin static int roff_cond_text(ROFF_ARGS);
1897295610fSBaptiste Daroussin static int roff_cond_sub(ROFF_ARGS);
1907295610fSBaptiste Daroussin static int roff_ds(ROFF_ARGS);
1917295610fSBaptiste Daroussin static int roff_ec(ROFF_ARGS);
1927295610fSBaptiste Daroussin static int roff_eo(ROFF_ARGS);
1937295610fSBaptiste Daroussin static int roff_eqndelim(struct roff *, struct buf *, int);
1946d38604fSBaptiste Daroussin static int roff_evalcond(struct roff *, int, char *, int *);
19561d06d6bSBaptiste Daroussin static int roff_evalnum(struct roff *, int,
19661d06d6bSBaptiste Daroussin const char *, int *, int *, int);
19761d06d6bSBaptiste Daroussin static int roff_evalpar(struct roff *, int,
19861d06d6bSBaptiste Daroussin const char *, int *, int *, int);
19961d06d6bSBaptiste Daroussin static int roff_evalstrcond(const char *, int *);
2007295610fSBaptiste Daroussin static int roff_expand(struct roff *, struct buf *,
2017295610fSBaptiste Daroussin int, int, char);
202*c1c95addSBrooks Davis static void roff_expand_patch(struct buf *, int,
203*c1c95addSBrooks Davis const char *, int);
20461d06d6bSBaptiste Daroussin static void roff_free1(struct roff *);
20561d06d6bSBaptiste Daroussin static void roff_freereg(struct roffreg *);
20661d06d6bSBaptiste Daroussin static void roff_freestr(struct roffkv *);
20761d06d6bSBaptiste Daroussin static size_t roff_getname(struct roff *, char **, int, int);
20861d06d6bSBaptiste Daroussin static int roff_getnum(const char *, int *, int *, int);
20961d06d6bSBaptiste Daroussin static int roff_getop(const char *, int *, char *);
21061d06d6bSBaptiste Daroussin static int roff_getregn(struct roff *,
21161d06d6bSBaptiste Daroussin const char *, size_t, char);
21261d06d6bSBaptiste Daroussin static int roff_getregro(const struct roff *,
21361d06d6bSBaptiste Daroussin const char *name);
21461d06d6bSBaptiste Daroussin static const char *roff_getstrn(struct roff *,
21561d06d6bSBaptiste Daroussin const char *, size_t, int *);
21661d06d6bSBaptiste Daroussin static int roff_hasregn(const struct roff *,
21761d06d6bSBaptiste Daroussin const char *, size_t);
2187295610fSBaptiste Daroussin static int roff_insec(ROFF_ARGS);
2197295610fSBaptiste Daroussin static int roff_it(ROFF_ARGS);
2207295610fSBaptiste Daroussin static int roff_line_ignore(ROFF_ARGS);
22161d06d6bSBaptiste Daroussin static void roff_man_alloc1(struct roff_man *);
22261d06d6bSBaptiste Daroussin static void roff_man_free1(struct roff_man *);
2237295610fSBaptiste Daroussin static int roff_manyarg(ROFF_ARGS);
224*c1c95addSBrooks Davis static int roff_mc(ROFF_ARGS);
2257295610fSBaptiste Daroussin static int roff_noarg(ROFF_ARGS);
2267295610fSBaptiste Daroussin static int roff_nop(ROFF_ARGS);
2277295610fSBaptiste Daroussin static int roff_nr(ROFF_ARGS);
2287295610fSBaptiste Daroussin static int roff_onearg(ROFF_ARGS);
22961d06d6bSBaptiste Daroussin static enum roff_tok roff_parse(struct roff *, char *, int *,
23061d06d6bSBaptiste Daroussin int, int);
231*c1c95addSBrooks Davis static int roff_parse_comment(struct roff *, struct buf *,
232*c1c95addSBrooks Davis int, int, char);
2337295610fSBaptiste Daroussin static int roff_parsetext(struct roff *, struct buf *,
23461d06d6bSBaptiste Daroussin int, int *);
2357295610fSBaptiste Daroussin static int roff_renamed(ROFF_ARGS);
236*c1c95addSBrooks Davis static int roff_req_or_macro(ROFF_ARGS);
2377295610fSBaptiste Daroussin static int roff_return(ROFF_ARGS);
2387295610fSBaptiste Daroussin static int roff_rm(ROFF_ARGS);
2397295610fSBaptiste Daroussin static int roff_rn(ROFF_ARGS);
2407295610fSBaptiste Daroussin static int roff_rr(ROFF_ARGS);
24161d06d6bSBaptiste Daroussin static void roff_setregn(struct roff *, const char *,
24261d06d6bSBaptiste Daroussin size_t, int, char, int);
24361d06d6bSBaptiste Daroussin static void roff_setstr(struct roff *,
24461d06d6bSBaptiste Daroussin const char *, const char *, int);
24561d06d6bSBaptiste Daroussin static void roff_setstrn(struct roffkv **, const char *,
24661d06d6bSBaptiste Daroussin size_t, const char *, size_t, int);
2477295610fSBaptiste Daroussin static int roff_shift(ROFF_ARGS);
2487295610fSBaptiste Daroussin static int roff_so(ROFF_ARGS);
2497295610fSBaptiste Daroussin static int roff_tr(ROFF_ARGS);
2507295610fSBaptiste Daroussin static int roff_Dd(ROFF_ARGS);
2517295610fSBaptiste Daroussin static int roff_TE(ROFF_ARGS);
2527295610fSBaptiste Daroussin static int roff_TS(ROFF_ARGS);
2537295610fSBaptiste Daroussin static int roff_EQ(ROFF_ARGS);
2547295610fSBaptiste Daroussin static int roff_EN(ROFF_ARGS);
2557295610fSBaptiste Daroussin static int roff_T_(ROFF_ARGS);
2567295610fSBaptiste Daroussin static int roff_unsupp(ROFF_ARGS);
2577295610fSBaptiste Daroussin static int roff_userdef(ROFF_ARGS);
25861d06d6bSBaptiste Daroussin
25961d06d6bSBaptiste Daroussin /* --- constant data ------------------------------------------------------ */
26061d06d6bSBaptiste Daroussin
26161d06d6bSBaptiste Daroussin #define ROFFNUM_SCALE (1 << 0) /* Honour scaling in roff_getnum(). */
26261d06d6bSBaptiste Daroussin #define ROFFNUM_WHITE (1 << 1) /* Skip whitespace in roff_evalnum(). */
26361d06d6bSBaptiste Daroussin
26461d06d6bSBaptiste Daroussin const char *__roff_name[MAN_MAX + 1] = {
2657295610fSBaptiste Daroussin "br", "ce", "fi", "ft",
2667295610fSBaptiste Daroussin "ll", "mc", "nf",
2677295610fSBaptiste Daroussin "po", "rj", "sp",
26861d06d6bSBaptiste Daroussin "ta", "ti", NULL,
26961d06d6bSBaptiste Daroussin "ab", "ad", "af", "aln",
27061d06d6bSBaptiste Daroussin "als", "am", "am1", "ami",
27161d06d6bSBaptiste Daroussin "ami1", "as", "as1", "asciify",
27261d06d6bSBaptiste Daroussin "backtrace", "bd", "bleedat", "blm",
27361d06d6bSBaptiste Daroussin "box", "boxa", "bp", "BP",
27461d06d6bSBaptiste Daroussin "break", "breakchar", "brnl", "brp",
27561d06d6bSBaptiste Daroussin "brpnl", "c2", "cc",
27661d06d6bSBaptiste Daroussin "cf", "cflags", "ch", "char",
27761d06d6bSBaptiste Daroussin "chop", "class", "close", "CL",
27861d06d6bSBaptiste Daroussin "color", "composite", "continue", "cp",
27961d06d6bSBaptiste Daroussin "cropat", "cs", "cu", "da",
28061d06d6bSBaptiste Daroussin "dch", "Dd", "de", "de1",
28161d06d6bSBaptiste Daroussin "defcolor", "dei", "dei1", "device",
28261d06d6bSBaptiste Daroussin "devicem", "di", "do", "ds",
28361d06d6bSBaptiste Daroussin "ds1", "dwh", "dt", "ec",
28461d06d6bSBaptiste Daroussin "ecr", "ecs", "el", "em",
28561d06d6bSBaptiste Daroussin "EN", "eo", "EP", "EQ",
28661d06d6bSBaptiste Daroussin "errprint", "ev", "evc", "ex",
28761d06d6bSBaptiste Daroussin "fallback", "fam", "fc", "fchar",
28861d06d6bSBaptiste Daroussin "fcolor", "fdeferlig", "feature", "fkern",
28961d06d6bSBaptiste Daroussin "fl", "flig", "fp", "fps",
29061d06d6bSBaptiste Daroussin "fschar", "fspacewidth", "fspecial", "ftr",
29161d06d6bSBaptiste Daroussin "fzoom", "gcolor", "hc", "hcode",
29261d06d6bSBaptiste Daroussin "hidechar", "hla", "hlm", "hpf",
29361d06d6bSBaptiste Daroussin "hpfa", "hpfcode", "hw", "hy",
29461d06d6bSBaptiste Daroussin "hylang", "hylen", "hym", "hypp",
29561d06d6bSBaptiste Daroussin "hys", "ie", "if", "ig",
29661d06d6bSBaptiste Daroussin "index", "it", "itc", "IX",
29761d06d6bSBaptiste Daroussin "kern", "kernafter", "kernbefore", "kernpair",
29861d06d6bSBaptiste Daroussin "lc", "lc_ctype", "lds", "length",
29961d06d6bSBaptiste Daroussin "letadj", "lf", "lg", "lhang",
30061d06d6bSBaptiste Daroussin "linetabs", "lnr", "lnrf", "lpfx",
30161d06d6bSBaptiste Daroussin "ls", "lsm", "lt",
30261d06d6bSBaptiste Daroussin "mediasize", "minss", "mk", "mso",
30361d06d6bSBaptiste Daroussin "na", "ne", "nh", "nhychar",
30461d06d6bSBaptiste Daroussin "nm", "nn", "nop", "nr",
30561d06d6bSBaptiste Daroussin "nrf", "nroff", "ns", "nx",
30661d06d6bSBaptiste Daroussin "open", "opena", "os", "output",
30761d06d6bSBaptiste Daroussin "padj", "papersize", "pc", "pev",
30861d06d6bSBaptiste Daroussin "pi", "PI", "pl", "pm",
30961d06d6bSBaptiste Daroussin "pn", "pnr", "ps",
31061d06d6bSBaptiste Daroussin "psbb", "pshape", "pso", "ptr",
31161d06d6bSBaptiste Daroussin "pvs", "rchar", "rd", "recursionlimit",
31261d06d6bSBaptiste Daroussin "return", "rfschar", "rhang",
31361d06d6bSBaptiste Daroussin "rm", "rn", "rnn", "rr",
31461d06d6bSBaptiste Daroussin "rs", "rt", "schar", "sentchar",
31561d06d6bSBaptiste Daroussin "shc", "shift", "sizes", "so",
31661d06d6bSBaptiste Daroussin "spacewidth", "special", "spreadwarn", "ss",
31761d06d6bSBaptiste Daroussin "sty", "substring", "sv", "sy",
31861d06d6bSBaptiste Daroussin "T&", "tc", "TE",
31961d06d6bSBaptiste Daroussin "TH", "tkf", "tl",
32061d06d6bSBaptiste Daroussin "tm", "tm1", "tmc", "tr",
32161d06d6bSBaptiste Daroussin "track", "transchar", "trf", "trimat",
32261d06d6bSBaptiste Daroussin "trin", "trnt", "troff", "TS",
32361d06d6bSBaptiste Daroussin "uf", "ul", "unformat", "unwatch",
32461d06d6bSBaptiste Daroussin "unwatchn", "vpt", "vs", "warn",
32561d06d6bSBaptiste Daroussin "warnscale", "watch", "watchlength", "watchn",
32661d06d6bSBaptiste Daroussin "wh", "while", "write", "writec",
32761d06d6bSBaptiste Daroussin "writem", "xflag", ".", NULL,
32861d06d6bSBaptiste Daroussin NULL, "text",
32961d06d6bSBaptiste Daroussin "Dd", "Dt", "Os", "Sh",
33061d06d6bSBaptiste Daroussin "Ss", "Pp", "D1", "Dl",
33161d06d6bSBaptiste Daroussin "Bd", "Ed", "Bl", "El",
33261d06d6bSBaptiste Daroussin "It", "Ad", "An", "Ap",
33361d06d6bSBaptiste Daroussin "Ar", "Cd", "Cm", "Dv",
33461d06d6bSBaptiste Daroussin "Er", "Ev", "Ex", "Fa",
33561d06d6bSBaptiste Daroussin "Fd", "Fl", "Fn", "Ft",
33661d06d6bSBaptiste Daroussin "Ic", "In", "Li", "Nd",
33761d06d6bSBaptiste Daroussin "Nm", "Op", "Ot", "Pa",
33861d06d6bSBaptiste Daroussin "Rv", "St", "Va", "Vt",
33961d06d6bSBaptiste Daroussin "Xr", "%A", "%B", "%D",
34061d06d6bSBaptiste Daroussin "%I", "%J", "%N", "%O",
34161d06d6bSBaptiste Daroussin "%P", "%R", "%T", "%V",
34261d06d6bSBaptiste Daroussin "Ac", "Ao", "Aq", "At",
34361d06d6bSBaptiste Daroussin "Bc", "Bf", "Bo", "Bq",
34461d06d6bSBaptiste Daroussin "Bsx", "Bx", "Db", "Dc",
34561d06d6bSBaptiste Daroussin "Do", "Dq", "Ec", "Ef",
34661d06d6bSBaptiste Daroussin "Em", "Eo", "Fx", "Ms",
34761d06d6bSBaptiste Daroussin "No", "Ns", "Nx", "Ox",
34861d06d6bSBaptiste Daroussin "Pc", "Pf", "Po", "Pq",
34961d06d6bSBaptiste Daroussin "Qc", "Ql", "Qo", "Qq",
35061d06d6bSBaptiste Daroussin "Re", "Rs", "Sc", "So",
35161d06d6bSBaptiste Daroussin "Sq", "Sm", "Sx", "Sy",
35261d06d6bSBaptiste Daroussin "Tn", "Ux", "Xc", "Xo",
35361d06d6bSBaptiste Daroussin "Fo", "Fc", "Oo", "Oc",
35461d06d6bSBaptiste Daroussin "Bk", "Ek", "Bt", "Hf",
35561d06d6bSBaptiste Daroussin "Fr", "Ud", "Lb", "Lp",
35661d06d6bSBaptiste Daroussin "Lk", "Mt", "Brq", "Bro",
35761d06d6bSBaptiste Daroussin "Brc", "%C", "Es", "En",
35861d06d6bSBaptiste Daroussin "Dx", "%Q", "%U", "Ta",
3596d38604fSBaptiste Daroussin "Tg", NULL,
36061d06d6bSBaptiste Daroussin "TH", "SH", "SS", "TP",
3617295610fSBaptiste Daroussin "TQ",
36261d06d6bSBaptiste Daroussin "LP", "PP", "P", "IP",
36361d06d6bSBaptiste Daroussin "HP", "SM", "SB", "BI",
36461d06d6bSBaptiste Daroussin "IB", "BR", "RB", "R",
36561d06d6bSBaptiste Daroussin "B", "I", "IR", "RI",
36661d06d6bSBaptiste Daroussin "RE", "RS", "DT", "UC",
36761d06d6bSBaptiste Daroussin "PD", "AT", "in",
3687295610fSBaptiste Daroussin "SY", "YS", "OP",
3697295610fSBaptiste Daroussin "EX", "EE", "UR",
370*c1c95addSBrooks Davis "UE", "MT", "ME", "MR",
371*c1c95addSBrooks Davis NULL
37261d06d6bSBaptiste Daroussin };
37361d06d6bSBaptiste Daroussin const char *const *roff_name = __roff_name;
37461d06d6bSBaptiste Daroussin
37561d06d6bSBaptiste Daroussin static struct roffmac roffs[TOKEN_NONE] = {
3767295610fSBaptiste Daroussin { roff_noarg, NULL, NULL, 0 }, /* br */
37761d06d6bSBaptiste Daroussin { roff_onearg, NULL, NULL, 0 }, /* ce */
3787295610fSBaptiste Daroussin { roff_noarg, NULL, NULL, 0 }, /* fi */
37961d06d6bSBaptiste Daroussin { roff_onearg, NULL, NULL, 0 }, /* ft */
38061d06d6bSBaptiste Daroussin { roff_onearg, NULL, NULL, 0 }, /* ll */
381*c1c95addSBrooks Davis { roff_mc, NULL, NULL, 0 }, /* mc */
3827295610fSBaptiste Daroussin { roff_noarg, NULL, NULL, 0 }, /* nf */
38361d06d6bSBaptiste Daroussin { roff_onearg, NULL, NULL, 0 }, /* po */
38461d06d6bSBaptiste Daroussin { roff_onearg, NULL, NULL, 0 }, /* rj */
38561d06d6bSBaptiste Daroussin { roff_onearg, NULL, NULL, 0 }, /* sp */
38661d06d6bSBaptiste Daroussin { roff_manyarg, NULL, NULL, 0 }, /* ta */
38761d06d6bSBaptiste Daroussin { roff_onearg, NULL, NULL, 0 }, /* ti */
38861d06d6bSBaptiste Daroussin { NULL, NULL, NULL, 0 }, /* ROFF_MAX */
38961d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* ab */
39061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* ad */
39161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* af */
39261d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* aln */
39361d06d6bSBaptiste Daroussin { roff_als, NULL, NULL, 0 }, /* als */
39461d06d6bSBaptiste Daroussin { roff_block, roff_block_text, roff_block_sub, 0 }, /* am */
39561d06d6bSBaptiste Daroussin { roff_block, roff_block_text, roff_block_sub, 0 }, /* am1 */
39661d06d6bSBaptiste Daroussin { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami */
39761d06d6bSBaptiste Daroussin { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami1 */
39861d06d6bSBaptiste Daroussin { roff_ds, NULL, NULL, 0 }, /* as */
39961d06d6bSBaptiste Daroussin { roff_ds, NULL, NULL, 0 }, /* as1 */
40061d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* asciify */
40161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* backtrace */
40261d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* bd */
40361d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* bleedat */
40461d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* blm */
40561d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* box */
40661d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* boxa */
40761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* bp */
40861d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* BP */
40945a5aec3SBaptiste Daroussin { roff_break, NULL, NULL, 0 }, /* break */
41061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* breakchar */
41161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* brnl */
4127295610fSBaptiste Daroussin { roff_noarg, NULL, NULL, 0 }, /* brp */
41361d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* brpnl */
41461d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* c2 */
41561d06d6bSBaptiste Daroussin { roff_cc, NULL, NULL, 0 }, /* cc */
41661d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* cf */
41761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* cflags */
41861d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* ch */
4197295610fSBaptiste Daroussin { roff_char, NULL, NULL, 0 }, /* char */
42061d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* chop */
42161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* class */
42261d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* close */
42361d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* CL */
42461d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* color */
42561d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* composite */
42661d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* continue */
42761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* cp */
42861d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* cropat */
42961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* cs */
43061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* cu */
43161d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* da */
43261d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* dch */
43361d06d6bSBaptiste Daroussin { roff_Dd, NULL, NULL, 0 }, /* Dd */
43461d06d6bSBaptiste Daroussin { roff_block, roff_block_text, roff_block_sub, 0 }, /* de */
43561d06d6bSBaptiste Daroussin { roff_block, roff_block_text, roff_block_sub, 0 }, /* de1 */
43661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* defcolor */
43761d06d6bSBaptiste Daroussin { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei */
43861d06d6bSBaptiste Daroussin { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei1 */
43961d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* device */
44061d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* devicem */
44161d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* di */
44261d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* do */
44361d06d6bSBaptiste Daroussin { roff_ds, NULL, NULL, 0 }, /* ds */
44461d06d6bSBaptiste Daroussin { roff_ds, NULL, NULL, 0 }, /* ds1 */
44561d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* dwh */
44661d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* dt */
44761d06d6bSBaptiste Daroussin { roff_ec, NULL, NULL, 0 }, /* ec */
44861d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* ecr */
44961d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* ecs */
45061d06d6bSBaptiste Daroussin { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* el */
45161d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* em */
45261d06d6bSBaptiste Daroussin { roff_EN, NULL, NULL, 0 }, /* EN */
45361d06d6bSBaptiste Daroussin { roff_eo, NULL, NULL, 0 }, /* eo */
45461d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* EP */
45561d06d6bSBaptiste Daroussin { roff_EQ, NULL, NULL, 0 }, /* EQ */
45661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* errprint */
45761d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* ev */
45861d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* evc */
45961d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* ex */
46061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* fallback */
46161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* fam */
46261d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* fc */
46361d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* fchar */
46461d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* fcolor */
46561d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* fdeferlig */
46661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* feature */
46761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* fkern */
46861d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* fl */
46961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* flig */
47061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* fp */
47161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* fps */
47261d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* fschar */
47361d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* fspacewidth */
47461d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* fspecial */
47561d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* ftr */
47661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* fzoom */
47761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* gcolor */
47861d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hc */
47961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hcode */
48061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hidechar */
48161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hla */
48261d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hlm */
48361d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hpf */
48461d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hpfa */
48561d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hpfcode */
48661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hw */
48761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hy */
48861d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hylang */
48961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hylen */
49061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hym */
49161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hypp */
49261d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* hys */
49361d06d6bSBaptiste Daroussin { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* ie */
49461d06d6bSBaptiste Daroussin { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* if */
49561d06d6bSBaptiste Daroussin { roff_block, roff_block_text, roff_block_sub, 0 }, /* ig */
49661d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* index */
49761d06d6bSBaptiste Daroussin { roff_it, NULL, NULL, 0 }, /* it */
49861d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* itc */
49961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* IX */
50061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* kern */
50161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* kernafter */
50261d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* kernbefore */
50361d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* kernpair */
50461d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* lc */
50561d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* lc_ctype */
50661d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* lds */
50761d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* length */
50861d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* letadj */
50961d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* lf */
51061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* lg */
51161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* lhang */
51261d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* linetabs */
51361d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* lnr */
51461d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* lnrf */
51561d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* lpfx */
51661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* ls */
51761d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* lsm */
51861d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* lt */
51961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* mediasize */
52061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* minss */
52161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* mk */
52261d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* mso */
52361d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* na */
52461d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* ne */
52561d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* nh */
52661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* nhychar */
52761d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* nm */
52861d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* nn */
5297295610fSBaptiste Daroussin { roff_nop, NULL, NULL, 0 }, /* nop */
53061d06d6bSBaptiste Daroussin { roff_nr, NULL, NULL, 0 }, /* nr */
53161d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* nrf */
53261d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* nroff */
53361d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* ns */
53461d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* nx */
53561d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* open */
53661d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* opena */
53761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* os */
53861d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* output */
53961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* padj */
54061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* papersize */
54161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* pc */
54261d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* pev */
54361d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* pi */
54461d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* PI */
54561d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* pl */
54661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* pm */
54761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* pn */
54861d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* pnr */
54961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* ps */
55061d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* psbb */
55161d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* pshape */
55261d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* pso */
55361d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* ptr */
55461d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* pvs */
55561d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* rchar */
55661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* rd */
55761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* recursionlimit */
5587295610fSBaptiste Daroussin { roff_return, NULL, NULL, 0 }, /* return */
55961d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* rfschar */
56061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* rhang */
56161d06d6bSBaptiste Daroussin { roff_rm, NULL, NULL, 0 }, /* rm */
56261d06d6bSBaptiste Daroussin { roff_rn, NULL, NULL, 0 }, /* rn */
56361d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* rnn */
56461d06d6bSBaptiste Daroussin { roff_rr, NULL, NULL, 0 }, /* rr */
56561d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* rs */
56661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* rt */
56761d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* schar */
56861d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* sentchar */
56961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* shc */
5707295610fSBaptiste Daroussin { roff_shift, NULL, NULL, 0 }, /* shift */
57161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* sizes */
57261d06d6bSBaptiste Daroussin { roff_so, NULL, NULL, 0 }, /* so */
57361d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* spacewidth */
57461d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* special */
57561d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* spreadwarn */
57661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* ss */
57761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* sty */
57861d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* substring */
57961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* sv */
58061d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* sy */
58161d06d6bSBaptiste Daroussin { roff_T_, NULL, NULL, 0 }, /* T& */
58261d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* tc */
58361d06d6bSBaptiste Daroussin { roff_TE, NULL, NULL, 0 }, /* TE */
58461d06d6bSBaptiste Daroussin { roff_Dd, NULL, NULL, 0 }, /* TH */
58561d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* tkf */
58661d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* tl */
58761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* tm */
58861d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* tm1 */
58961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* tmc */
59061d06d6bSBaptiste Daroussin { roff_tr, NULL, NULL, 0 }, /* tr */
59161d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* track */
59261d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* transchar */
59361d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* trf */
59461d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* trimat */
59561d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* trin */
59661d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* trnt */
59761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* troff */
59861d06d6bSBaptiste Daroussin { roff_TS, NULL, NULL, 0 }, /* TS */
59961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* uf */
60061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* ul */
60161d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* unformat */
60261d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* unwatch */
60361d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* unwatchn */
60461d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* vpt */
60561d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* vs */
60661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* warn */
60761d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* warnscale */
60861d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* watch */
60961d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* watchlength */
61061d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* watchn */
61161d06d6bSBaptiste Daroussin { roff_unsupp, NULL, NULL, 0 }, /* wh */
6127295610fSBaptiste Daroussin { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /*while*/
61361d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* write */
61461d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* writec */
61561d06d6bSBaptiste Daroussin { roff_insec, NULL, NULL, 0 }, /* writem */
61661d06d6bSBaptiste Daroussin { roff_line_ignore, NULL, NULL, 0 }, /* xflag */
61761d06d6bSBaptiste Daroussin { roff_cblock, NULL, NULL, 0 }, /* . */
61861d06d6bSBaptiste Daroussin { roff_renamed, NULL, NULL, 0 },
61961d06d6bSBaptiste Daroussin { roff_userdef, NULL, NULL, 0 }
62061d06d6bSBaptiste Daroussin };
62161d06d6bSBaptiste Daroussin
62261d06d6bSBaptiste Daroussin /* Array of injected predefined strings. */
62361d06d6bSBaptiste Daroussin #define PREDEFS_MAX 38
62461d06d6bSBaptiste Daroussin static const struct predef predefs[PREDEFS_MAX] = {
62561d06d6bSBaptiste Daroussin #include "predefs.in"
62661d06d6bSBaptiste Daroussin };
62761d06d6bSBaptiste Daroussin
62861d06d6bSBaptiste Daroussin static int roffce_lines; /* number of input lines to center */
62961d06d6bSBaptiste Daroussin static struct roff_node *roffce_node; /* active request */
63061d06d6bSBaptiste Daroussin static int roffit_lines; /* number of lines to delay */
63161d06d6bSBaptiste Daroussin static char *roffit_macro; /* nil-terminated macro line */
63261d06d6bSBaptiste Daroussin
63361d06d6bSBaptiste Daroussin
63461d06d6bSBaptiste Daroussin /* --- request table ------------------------------------------------------ */
63561d06d6bSBaptiste Daroussin
63661d06d6bSBaptiste Daroussin struct ohash *
roffhash_alloc(enum roff_tok mintok,enum roff_tok maxtok)63761d06d6bSBaptiste Daroussin roffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok)
63861d06d6bSBaptiste Daroussin {
63961d06d6bSBaptiste Daroussin struct ohash *htab;
64061d06d6bSBaptiste Daroussin struct roffreq *req;
64161d06d6bSBaptiste Daroussin enum roff_tok tok;
64261d06d6bSBaptiste Daroussin size_t sz;
64361d06d6bSBaptiste Daroussin unsigned int slot;
64461d06d6bSBaptiste Daroussin
64561d06d6bSBaptiste Daroussin htab = mandoc_malloc(sizeof(*htab));
64661d06d6bSBaptiste Daroussin mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name));
64761d06d6bSBaptiste Daroussin
64861d06d6bSBaptiste Daroussin for (tok = mintok; tok < maxtok; tok++) {
64961d06d6bSBaptiste Daroussin if (roff_name[tok] == NULL)
65061d06d6bSBaptiste Daroussin continue;
65161d06d6bSBaptiste Daroussin sz = strlen(roff_name[tok]);
65261d06d6bSBaptiste Daroussin req = mandoc_malloc(sizeof(*req) + sz + 1);
65361d06d6bSBaptiste Daroussin req->tok = tok;
65461d06d6bSBaptiste Daroussin memcpy(req->name, roff_name[tok], sz + 1);
65561d06d6bSBaptiste Daroussin slot = ohash_qlookup(htab, req->name);
65661d06d6bSBaptiste Daroussin ohash_insert(htab, slot, req);
65761d06d6bSBaptiste Daroussin }
65861d06d6bSBaptiste Daroussin return htab;
65961d06d6bSBaptiste Daroussin }
66061d06d6bSBaptiste Daroussin
66161d06d6bSBaptiste Daroussin void
roffhash_free(struct ohash * htab)66261d06d6bSBaptiste Daroussin roffhash_free(struct ohash *htab)
66361d06d6bSBaptiste Daroussin {
66461d06d6bSBaptiste Daroussin struct roffreq *req;
66561d06d6bSBaptiste Daroussin unsigned int slot;
66661d06d6bSBaptiste Daroussin
66761d06d6bSBaptiste Daroussin if (htab == NULL)
66861d06d6bSBaptiste Daroussin return;
66961d06d6bSBaptiste Daroussin for (req = ohash_first(htab, &slot); req != NULL;
67061d06d6bSBaptiste Daroussin req = ohash_next(htab, &slot))
67161d06d6bSBaptiste Daroussin free(req);
67261d06d6bSBaptiste Daroussin ohash_delete(htab);
67361d06d6bSBaptiste Daroussin free(htab);
67461d06d6bSBaptiste Daroussin }
67561d06d6bSBaptiste Daroussin
67661d06d6bSBaptiste Daroussin enum roff_tok
roffhash_find(struct ohash * htab,const char * name,size_t sz)67761d06d6bSBaptiste Daroussin roffhash_find(struct ohash *htab, const char *name, size_t sz)
67861d06d6bSBaptiste Daroussin {
67961d06d6bSBaptiste Daroussin struct roffreq *req;
68061d06d6bSBaptiste Daroussin const char *end;
68161d06d6bSBaptiste Daroussin
68261d06d6bSBaptiste Daroussin if (sz) {
68361d06d6bSBaptiste Daroussin end = name + sz;
68461d06d6bSBaptiste Daroussin req = ohash_find(htab, ohash_qlookupi(htab, name, &end));
68561d06d6bSBaptiste Daroussin } else
68661d06d6bSBaptiste Daroussin req = ohash_find(htab, ohash_qlookup(htab, name));
68761d06d6bSBaptiste Daroussin return req == NULL ? TOKEN_NONE : req->tok;
68861d06d6bSBaptiste Daroussin }
68961d06d6bSBaptiste Daroussin
69061d06d6bSBaptiste Daroussin /* --- stack of request blocks -------------------------------------------- */
69161d06d6bSBaptiste Daroussin
69261d06d6bSBaptiste Daroussin /*
69361d06d6bSBaptiste Daroussin * Pop the current node off of the stack of roff instructions currently
69445a5aec3SBaptiste Daroussin * pending. Return 1 if it is a loop or 0 otherwise.
69561d06d6bSBaptiste Daroussin */
6967295610fSBaptiste Daroussin static int
roffnode_pop(struct roff * r)69761d06d6bSBaptiste Daroussin roffnode_pop(struct roff *r)
69861d06d6bSBaptiste Daroussin {
69961d06d6bSBaptiste Daroussin struct roffnode *p;
7007295610fSBaptiste Daroussin int inloop;
70161d06d6bSBaptiste Daroussin
70261d06d6bSBaptiste Daroussin p = r->last;
7037295610fSBaptiste Daroussin inloop = p->tok == ROFF_while;
7047295610fSBaptiste Daroussin r->last = p->parent;
70561d06d6bSBaptiste Daroussin free(p->name);
70661d06d6bSBaptiste Daroussin free(p->end);
70761d06d6bSBaptiste Daroussin free(p);
7087295610fSBaptiste Daroussin return inloop;
70961d06d6bSBaptiste Daroussin }
71061d06d6bSBaptiste Daroussin
71161d06d6bSBaptiste Daroussin /*
71261d06d6bSBaptiste Daroussin * Push a roff node onto the instruction stack. This must later be
71361d06d6bSBaptiste Daroussin * removed with roffnode_pop().
71461d06d6bSBaptiste Daroussin */
71561d06d6bSBaptiste Daroussin static void
roffnode_push(struct roff * r,enum roff_tok tok,const char * name,int line,int col)71661d06d6bSBaptiste Daroussin roffnode_push(struct roff *r, enum roff_tok tok, const char *name,
71761d06d6bSBaptiste Daroussin int line, int col)
71861d06d6bSBaptiste Daroussin {
71961d06d6bSBaptiste Daroussin struct roffnode *p;
72061d06d6bSBaptiste Daroussin
72161d06d6bSBaptiste Daroussin p = mandoc_calloc(1, sizeof(struct roffnode));
72261d06d6bSBaptiste Daroussin p->tok = tok;
72361d06d6bSBaptiste Daroussin if (name)
72461d06d6bSBaptiste Daroussin p->name = mandoc_strdup(name);
72561d06d6bSBaptiste Daroussin p->parent = r->last;
72661d06d6bSBaptiste Daroussin p->line = line;
72761d06d6bSBaptiste Daroussin p->col = col;
72861d06d6bSBaptiste Daroussin p->rule = p->parent ? p->parent->rule : 0;
72961d06d6bSBaptiste Daroussin
73061d06d6bSBaptiste Daroussin r->last = p;
73161d06d6bSBaptiste Daroussin }
73261d06d6bSBaptiste Daroussin
73361d06d6bSBaptiste Daroussin /* --- roff parser state data management ---------------------------------- */
73461d06d6bSBaptiste Daroussin
73561d06d6bSBaptiste Daroussin static void
roff_free1(struct roff * r)73661d06d6bSBaptiste Daroussin roff_free1(struct roff *r)
73761d06d6bSBaptiste Daroussin {
73861d06d6bSBaptiste Daroussin int i;
73961d06d6bSBaptiste Daroussin
7407295610fSBaptiste Daroussin tbl_free(r->first_tbl);
74161d06d6bSBaptiste Daroussin r->first_tbl = r->last_tbl = r->tbl = NULL;
74261d06d6bSBaptiste Daroussin
74361d06d6bSBaptiste Daroussin eqn_free(r->last_eqn);
74461d06d6bSBaptiste Daroussin r->last_eqn = r->eqn = NULL;
74561d06d6bSBaptiste Daroussin
7467295610fSBaptiste Daroussin while (r->mstackpos >= 0)
7477295610fSBaptiste Daroussin roff_userret(r);
7487295610fSBaptiste Daroussin
74961d06d6bSBaptiste Daroussin while (r->last)
75061d06d6bSBaptiste Daroussin roffnode_pop(r);
75161d06d6bSBaptiste Daroussin
75261d06d6bSBaptiste Daroussin free (r->rstack);
75361d06d6bSBaptiste Daroussin r->rstack = NULL;
75461d06d6bSBaptiste Daroussin r->rstacksz = 0;
75561d06d6bSBaptiste Daroussin r->rstackpos = -1;
75661d06d6bSBaptiste Daroussin
75761d06d6bSBaptiste Daroussin roff_freereg(r->regtab);
75861d06d6bSBaptiste Daroussin r->regtab = NULL;
75961d06d6bSBaptiste Daroussin
76061d06d6bSBaptiste Daroussin roff_freestr(r->strtab);
76161d06d6bSBaptiste Daroussin roff_freestr(r->rentab);
76261d06d6bSBaptiste Daroussin roff_freestr(r->xmbtab);
76361d06d6bSBaptiste Daroussin r->strtab = r->rentab = r->xmbtab = NULL;
76461d06d6bSBaptiste Daroussin
76561d06d6bSBaptiste Daroussin if (r->xtab)
76661d06d6bSBaptiste Daroussin for (i = 0; i < 128; i++)
76761d06d6bSBaptiste Daroussin free(r->xtab[i].p);
76861d06d6bSBaptiste Daroussin free(r->xtab);
76961d06d6bSBaptiste Daroussin r->xtab = NULL;
77061d06d6bSBaptiste Daroussin }
77161d06d6bSBaptiste Daroussin
77261d06d6bSBaptiste Daroussin void
roff_reset(struct roff * r)77361d06d6bSBaptiste Daroussin roff_reset(struct roff *r)
77461d06d6bSBaptiste Daroussin {
77561d06d6bSBaptiste Daroussin roff_free1(r);
7766d38604fSBaptiste Daroussin r->options |= MPARSE_COMMENT;
77761d06d6bSBaptiste Daroussin r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
77861d06d6bSBaptiste Daroussin r->control = '\0';
77961d06d6bSBaptiste Daroussin r->escape = '\\';
78061d06d6bSBaptiste Daroussin roffce_lines = 0;
78161d06d6bSBaptiste Daroussin roffce_node = NULL;
78261d06d6bSBaptiste Daroussin roffit_lines = 0;
78361d06d6bSBaptiste Daroussin roffit_macro = NULL;
78461d06d6bSBaptiste Daroussin }
78561d06d6bSBaptiste Daroussin
78661d06d6bSBaptiste Daroussin void
roff_free(struct roff * r)78761d06d6bSBaptiste Daroussin roff_free(struct roff *r)
78861d06d6bSBaptiste Daroussin {
7897295610fSBaptiste Daroussin int i;
7907295610fSBaptiste Daroussin
79161d06d6bSBaptiste Daroussin roff_free1(r);
7927295610fSBaptiste Daroussin for (i = 0; i < r->mstacksz; i++)
7937295610fSBaptiste Daroussin free(r->mstack[i].argv);
7947295610fSBaptiste Daroussin free(r->mstack);
79561d06d6bSBaptiste Daroussin roffhash_free(r->reqtab);
79661d06d6bSBaptiste Daroussin free(r);
79761d06d6bSBaptiste Daroussin }
79861d06d6bSBaptiste Daroussin
79961d06d6bSBaptiste Daroussin struct roff *
roff_alloc(int options)8007295610fSBaptiste Daroussin roff_alloc(int options)
80161d06d6bSBaptiste Daroussin {
80261d06d6bSBaptiste Daroussin struct roff *r;
80361d06d6bSBaptiste Daroussin
80461d06d6bSBaptiste Daroussin r = mandoc_calloc(1, sizeof(struct roff));
80561d06d6bSBaptiste Daroussin r->reqtab = roffhash_alloc(0, ROFF_RENAMED);
8066d38604fSBaptiste Daroussin r->options = options | MPARSE_COMMENT;
80761d06d6bSBaptiste Daroussin r->format = options & (MPARSE_MDOC | MPARSE_MAN);
8087295610fSBaptiste Daroussin r->mstackpos = -1;
80961d06d6bSBaptiste Daroussin r->rstackpos = -1;
81061d06d6bSBaptiste Daroussin r->escape = '\\';
81161d06d6bSBaptiste Daroussin return r;
81261d06d6bSBaptiste Daroussin }
81361d06d6bSBaptiste Daroussin
81461d06d6bSBaptiste Daroussin /* --- syntax tree state data management ---------------------------------- */
81561d06d6bSBaptiste Daroussin
81661d06d6bSBaptiste Daroussin static void
roff_man_free1(struct roff_man * man)81761d06d6bSBaptiste Daroussin roff_man_free1(struct roff_man *man)
81861d06d6bSBaptiste Daroussin {
8197295610fSBaptiste Daroussin if (man->meta.first != NULL)
8207295610fSBaptiste Daroussin roff_node_delete(man, man->meta.first);
82161d06d6bSBaptiste Daroussin free(man->meta.msec);
82261d06d6bSBaptiste Daroussin free(man->meta.vol);
82361d06d6bSBaptiste Daroussin free(man->meta.os);
82461d06d6bSBaptiste Daroussin free(man->meta.arch);
82561d06d6bSBaptiste Daroussin free(man->meta.title);
82661d06d6bSBaptiste Daroussin free(man->meta.name);
82761d06d6bSBaptiste Daroussin free(man->meta.date);
8287295610fSBaptiste Daroussin free(man->meta.sodest);
8297295610fSBaptiste Daroussin }
8307295610fSBaptiste Daroussin
8317295610fSBaptiste Daroussin void
roff_state_reset(struct roff_man * man)8327295610fSBaptiste Daroussin roff_state_reset(struct roff_man *man)
8337295610fSBaptiste Daroussin {
8347295610fSBaptiste Daroussin man->last = man->meta.first;
8357295610fSBaptiste Daroussin man->last_es = NULL;
8367295610fSBaptiste Daroussin man->flags = 0;
8377295610fSBaptiste Daroussin man->lastsec = man->lastnamed = SEC_NONE;
8387295610fSBaptiste Daroussin man->next = ROFF_NEXT_CHILD;
8397295610fSBaptiste Daroussin roff_setreg(man->roff, "nS", 0, '=');
84061d06d6bSBaptiste Daroussin }
84161d06d6bSBaptiste Daroussin
84261d06d6bSBaptiste Daroussin static void
roff_man_alloc1(struct roff_man * man)84361d06d6bSBaptiste Daroussin roff_man_alloc1(struct roff_man *man)
84461d06d6bSBaptiste Daroussin {
84561d06d6bSBaptiste Daroussin memset(&man->meta, 0, sizeof(man->meta));
8467295610fSBaptiste Daroussin man->meta.first = mandoc_calloc(1, sizeof(*man->meta.first));
8477295610fSBaptiste Daroussin man->meta.first->type = ROFFT_ROOT;
8487295610fSBaptiste Daroussin man->meta.macroset = MACROSET_NONE;
8497295610fSBaptiste Daroussin roff_state_reset(man);
85061d06d6bSBaptiste Daroussin }
85161d06d6bSBaptiste Daroussin
85261d06d6bSBaptiste Daroussin void
roff_man_reset(struct roff_man * man)85361d06d6bSBaptiste Daroussin roff_man_reset(struct roff_man *man)
85461d06d6bSBaptiste Daroussin {
85561d06d6bSBaptiste Daroussin roff_man_free1(man);
85661d06d6bSBaptiste Daroussin roff_man_alloc1(man);
85761d06d6bSBaptiste Daroussin }
85861d06d6bSBaptiste Daroussin
85961d06d6bSBaptiste Daroussin void
roff_man_free(struct roff_man * man)86061d06d6bSBaptiste Daroussin roff_man_free(struct roff_man *man)
86161d06d6bSBaptiste Daroussin {
86261d06d6bSBaptiste Daroussin roff_man_free1(man);
863*c1c95addSBrooks Davis free(man->os_r);
86461d06d6bSBaptiste Daroussin free(man);
86561d06d6bSBaptiste Daroussin }
86661d06d6bSBaptiste Daroussin
86761d06d6bSBaptiste Daroussin struct roff_man *
roff_man_alloc(struct roff * roff,const char * os_s,int quick)8687295610fSBaptiste Daroussin roff_man_alloc(struct roff *roff, const char *os_s, int quick)
86961d06d6bSBaptiste Daroussin {
87061d06d6bSBaptiste Daroussin struct roff_man *man;
87161d06d6bSBaptiste Daroussin
87261d06d6bSBaptiste Daroussin man = mandoc_calloc(1, sizeof(*man));
87361d06d6bSBaptiste Daroussin man->roff = roff;
87461d06d6bSBaptiste Daroussin man->os_s = os_s;
87561d06d6bSBaptiste Daroussin man->quick = quick;
87661d06d6bSBaptiste Daroussin roff_man_alloc1(man);
87761d06d6bSBaptiste Daroussin roff->man = man;
87861d06d6bSBaptiste Daroussin return man;
87961d06d6bSBaptiste Daroussin }
88061d06d6bSBaptiste Daroussin
88161d06d6bSBaptiste Daroussin /* --- syntax tree handling ----------------------------------------------- */
88261d06d6bSBaptiste Daroussin
88361d06d6bSBaptiste Daroussin struct roff_node *
roff_node_alloc(struct roff_man * man,int line,int pos,enum roff_type type,int tok)88461d06d6bSBaptiste Daroussin roff_node_alloc(struct roff_man *man, int line, int pos,
88561d06d6bSBaptiste Daroussin enum roff_type type, int tok)
88661d06d6bSBaptiste Daroussin {
88761d06d6bSBaptiste Daroussin struct roff_node *n;
88861d06d6bSBaptiste Daroussin
88961d06d6bSBaptiste Daroussin n = mandoc_calloc(1, sizeof(*n));
89061d06d6bSBaptiste Daroussin n->line = line;
89161d06d6bSBaptiste Daroussin n->pos = pos;
89261d06d6bSBaptiste Daroussin n->tok = tok;
89361d06d6bSBaptiste Daroussin n->type = type;
89461d06d6bSBaptiste Daroussin n->sec = man->lastsec;
89561d06d6bSBaptiste Daroussin
89661d06d6bSBaptiste Daroussin if (man->flags & MDOC_SYNOPSIS)
89761d06d6bSBaptiste Daroussin n->flags |= NODE_SYNPRETTY;
89861d06d6bSBaptiste Daroussin else
89961d06d6bSBaptiste Daroussin n->flags &= ~NODE_SYNPRETTY;
9007295610fSBaptiste Daroussin if ((man->flags & (ROFF_NOFILL | ROFF_NONOFILL)) == ROFF_NOFILL)
9017295610fSBaptiste Daroussin n->flags |= NODE_NOFILL;
9027295610fSBaptiste Daroussin else
9037295610fSBaptiste Daroussin n->flags &= ~NODE_NOFILL;
90461d06d6bSBaptiste Daroussin if (man->flags & MDOC_NEWLINE)
90561d06d6bSBaptiste Daroussin n->flags |= NODE_LINE;
90661d06d6bSBaptiste Daroussin man->flags &= ~MDOC_NEWLINE;
90761d06d6bSBaptiste Daroussin
90861d06d6bSBaptiste Daroussin return n;
90961d06d6bSBaptiste Daroussin }
91061d06d6bSBaptiste Daroussin
91161d06d6bSBaptiste Daroussin void
roff_node_append(struct roff_man * man,struct roff_node * n)91261d06d6bSBaptiste Daroussin roff_node_append(struct roff_man *man, struct roff_node *n)
91361d06d6bSBaptiste Daroussin {
91461d06d6bSBaptiste Daroussin
91561d06d6bSBaptiste Daroussin switch (man->next) {
91661d06d6bSBaptiste Daroussin case ROFF_NEXT_SIBLING:
91761d06d6bSBaptiste Daroussin if (man->last->next != NULL) {
91861d06d6bSBaptiste Daroussin n->next = man->last->next;
91961d06d6bSBaptiste Daroussin man->last->next->prev = n;
92061d06d6bSBaptiste Daroussin } else
92161d06d6bSBaptiste Daroussin man->last->parent->last = n;
92261d06d6bSBaptiste Daroussin man->last->next = n;
92361d06d6bSBaptiste Daroussin n->prev = man->last;
92461d06d6bSBaptiste Daroussin n->parent = man->last->parent;
92561d06d6bSBaptiste Daroussin break;
92661d06d6bSBaptiste Daroussin case ROFF_NEXT_CHILD:
92761d06d6bSBaptiste Daroussin if (man->last->child != NULL) {
92861d06d6bSBaptiste Daroussin n->next = man->last->child;
92961d06d6bSBaptiste Daroussin man->last->child->prev = n;
93061d06d6bSBaptiste Daroussin } else
93161d06d6bSBaptiste Daroussin man->last->last = n;
93261d06d6bSBaptiste Daroussin man->last->child = n;
93361d06d6bSBaptiste Daroussin n->parent = man->last;
93461d06d6bSBaptiste Daroussin break;
93561d06d6bSBaptiste Daroussin default:
93661d06d6bSBaptiste Daroussin abort();
93761d06d6bSBaptiste Daroussin }
93861d06d6bSBaptiste Daroussin man->last = n;
93961d06d6bSBaptiste Daroussin
94061d06d6bSBaptiste Daroussin switch (n->type) {
94161d06d6bSBaptiste Daroussin case ROFFT_HEAD:
94261d06d6bSBaptiste Daroussin n->parent->head = n;
94361d06d6bSBaptiste Daroussin break;
94461d06d6bSBaptiste Daroussin case ROFFT_BODY:
94561d06d6bSBaptiste Daroussin if (n->end != ENDBODY_NOT)
94661d06d6bSBaptiste Daroussin return;
94761d06d6bSBaptiste Daroussin n->parent->body = n;
94861d06d6bSBaptiste Daroussin break;
94961d06d6bSBaptiste Daroussin case ROFFT_TAIL:
95061d06d6bSBaptiste Daroussin n->parent->tail = n;
95161d06d6bSBaptiste Daroussin break;
95261d06d6bSBaptiste Daroussin default:
95361d06d6bSBaptiste Daroussin return;
95461d06d6bSBaptiste Daroussin }
95561d06d6bSBaptiste Daroussin
95661d06d6bSBaptiste Daroussin /*
95761d06d6bSBaptiste Daroussin * Copy over the normalised-data pointer of our parent. Not
95861d06d6bSBaptiste Daroussin * everybody has one, but copying a null pointer is fine.
95961d06d6bSBaptiste Daroussin */
96061d06d6bSBaptiste Daroussin
96161d06d6bSBaptiste Daroussin n->norm = n->parent->norm;
96261d06d6bSBaptiste Daroussin assert(n->parent->type == ROFFT_BLOCK);
96361d06d6bSBaptiste Daroussin }
96461d06d6bSBaptiste Daroussin
96561d06d6bSBaptiste Daroussin void
roff_word_alloc(struct roff_man * man,int line,int pos,const char * word)96661d06d6bSBaptiste Daroussin roff_word_alloc(struct roff_man *man, int line, int pos, const char *word)
96761d06d6bSBaptiste Daroussin {
96861d06d6bSBaptiste Daroussin struct roff_node *n;
96961d06d6bSBaptiste Daroussin
97061d06d6bSBaptiste Daroussin n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE);
97161d06d6bSBaptiste Daroussin n->string = roff_strdup(man->roff, word);
97261d06d6bSBaptiste Daroussin roff_node_append(man, n);
97361d06d6bSBaptiste Daroussin n->flags |= NODE_VALID | NODE_ENDED;
97461d06d6bSBaptiste Daroussin man->next = ROFF_NEXT_SIBLING;
97561d06d6bSBaptiste Daroussin }
97661d06d6bSBaptiste Daroussin
97761d06d6bSBaptiste Daroussin void
roff_word_append(struct roff_man * man,const char * word)97861d06d6bSBaptiste Daroussin roff_word_append(struct roff_man *man, const char *word)
97961d06d6bSBaptiste Daroussin {
98061d06d6bSBaptiste Daroussin struct roff_node *n;
98161d06d6bSBaptiste Daroussin char *addstr, *newstr;
98261d06d6bSBaptiste Daroussin
98361d06d6bSBaptiste Daroussin n = man->last;
98461d06d6bSBaptiste Daroussin addstr = roff_strdup(man->roff, word);
98561d06d6bSBaptiste Daroussin mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
98661d06d6bSBaptiste Daroussin free(addstr);
98761d06d6bSBaptiste Daroussin free(n->string);
98861d06d6bSBaptiste Daroussin n->string = newstr;
98961d06d6bSBaptiste Daroussin man->next = ROFF_NEXT_SIBLING;
99061d06d6bSBaptiste Daroussin }
99161d06d6bSBaptiste Daroussin
99261d06d6bSBaptiste Daroussin void
roff_elem_alloc(struct roff_man * man,int line,int pos,int tok)99361d06d6bSBaptiste Daroussin roff_elem_alloc(struct roff_man *man, int line, int pos, int tok)
99461d06d6bSBaptiste Daroussin {
99561d06d6bSBaptiste Daroussin struct roff_node *n;
99661d06d6bSBaptiste Daroussin
99761d06d6bSBaptiste Daroussin n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok);
99861d06d6bSBaptiste Daroussin roff_node_append(man, n);
99961d06d6bSBaptiste Daroussin man->next = ROFF_NEXT_CHILD;
100061d06d6bSBaptiste Daroussin }
100161d06d6bSBaptiste Daroussin
100261d06d6bSBaptiste Daroussin struct roff_node *
roff_block_alloc(struct roff_man * man,int line,int pos,int tok)100361d06d6bSBaptiste Daroussin roff_block_alloc(struct roff_man *man, int line, int pos, int tok)
100461d06d6bSBaptiste Daroussin {
100561d06d6bSBaptiste Daroussin struct roff_node *n;
100661d06d6bSBaptiste Daroussin
100761d06d6bSBaptiste Daroussin n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok);
100861d06d6bSBaptiste Daroussin roff_node_append(man, n);
100961d06d6bSBaptiste Daroussin man->next = ROFF_NEXT_CHILD;
101061d06d6bSBaptiste Daroussin return n;
101161d06d6bSBaptiste Daroussin }
101261d06d6bSBaptiste Daroussin
101361d06d6bSBaptiste Daroussin struct roff_node *
roff_head_alloc(struct roff_man * man,int line,int pos,int tok)101461d06d6bSBaptiste Daroussin roff_head_alloc(struct roff_man *man, int line, int pos, int tok)
101561d06d6bSBaptiste Daroussin {
101661d06d6bSBaptiste Daroussin struct roff_node *n;
101761d06d6bSBaptiste Daroussin
101861d06d6bSBaptiste Daroussin n = roff_node_alloc(man, line, pos, ROFFT_HEAD, tok);
101961d06d6bSBaptiste Daroussin roff_node_append(man, n);
102061d06d6bSBaptiste Daroussin man->next = ROFF_NEXT_CHILD;
102161d06d6bSBaptiste Daroussin return n;
102261d06d6bSBaptiste Daroussin }
102361d06d6bSBaptiste Daroussin
102461d06d6bSBaptiste Daroussin struct roff_node *
roff_body_alloc(struct roff_man * man,int line,int pos,int tok)102561d06d6bSBaptiste Daroussin roff_body_alloc(struct roff_man *man, int line, int pos, int tok)
102661d06d6bSBaptiste Daroussin {
102761d06d6bSBaptiste Daroussin struct roff_node *n;
102861d06d6bSBaptiste Daroussin
102961d06d6bSBaptiste Daroussin n = roff_node_alloc(man, line, pos, ROFFT_BODY, tok);
103061d06d6bSBaptiste Daroussin roff_node_append(man, n);
103161d06d6bSBaptiste Daroussin man->next = ROFF_NEXT_CHILD;
103261d06d6bSBaptiste Daroussin return n;
103361d06d6bSBaptiste Daroussin }
103461d06d6bSBaptiste Daroussin
103561d06d6bSBaptiste Daroussin static void
roff_addtbl(struct roff_man * man,int line,struct tbl_node * tbl)10367295610fSBaptiste Daroussin roff_addtbl(struct roff_man *man, int line, struct tbl_node *tbl)
103761d06d6bSBaptiste Daroussin {
103861d06d6bSBaptiste Daroussin struct roff_node *n;
10397295610fSBaptiste Daroussin struct tbl_span *span;
104061d06d6bSBaptiste Daroussin
10417295610fSBaptiste Daroussin if (man->meta.macroset == MACROSET_MAN)
104261d06d6bSBaptiste Daroussin man_breakscope(man, ROFF_TS);
104361d06d6bSBaptiste Daroussin while ((span = tbl_span(tbl)) != NULL) {
10447295610fSBaptiste Daroussin n = roff_node_alloc(man, line, 0, ROFFT_TBL, TOKEN_NONE);
104561d06d6bSBaptiste Daroussin n->span = span;
104661d06d6bSBaptiste Daroussin roff_node_append(man, n);
104761d06d6bSBaptiste Daroussin n->flags |= NODE_VALID | NODE_ENDED;
104861d06d6bSBaptiste Daroussin man->next = ROFF_NEXT_SIBLING;
104961d06d6bSBaptiste Daroussin }
105061d06d6bSBaptiste Daroussin }
105161d06d6bSBaptiste Daroussin
105261d06d6bSBaptiste Daroussin void
roff_node_unlink(struct roff_man * man,struct roff_node * n)105361d06d6bSBaptiste Daroussin roff_node_unlink(struct roff_man *man, struct roff_node *n)
105461d06d6bSBaptiste Daroussin {
105561d06d6bSBaptiste Daroussin
105661d06d6bSBaptiste Daroussin /* Adjust siblings. */
105761d06d6bSBaptiste Daroussin
105861d06d6bSBaptiste Daroussin if (n->prev)
105961d06d6bSBaptiste Daroussin n->prev->next = n->next;
106061d06d6bSBaptiste Daroussin if (n->next)
106161d06d6bSBaptiste Daroussin n->next->prev = n->prev;
106261d06d6bSBaptiste Daroussin
106361d06d6bSBaptiste Daroussin /* Adjust parent. */
106461d06d6bSBaptiste Daroussin
106561d06d6bSBaptiste Daroussin if (n->parent != NULL) {
106661d06d6bSBaptiste Daroussin if (n->parent->child == n)
106761d06d6bSBaptiste Daroussin n->parent->child = n->next;
106861d06d6bSBaptiste Daroussin if (n->parent->last == n)
106961d06d6bSBaptiste Daroussin n->parent->last = n->prev;
107061d06d6bSBaptiste Daroussin }
107161d06d6bSBaptiste Daroussin
107261d06d6bSBaptiste Daroussin /* Adjust parse point. */
107361d06d6bSBaptiste Daroussin
107461d06d6bSBaptiste Daroussin if (man == NULL)
107561d06d6bSBaptiste Daroussin return;
107661d06d6bSBaptiste Daroussin if (man->last == n) {
107761d06d6bSBaptiste Daroussin if (n->prev == NULL) {
107861d06d6bSBaptiste Daroussin man->last = n->parent;
107961d06d6bSBaptiste Daroussin man->next = ROFF_NEXT_CHILD;
108061d06d6bSBaptiste Daroussin } else {
108161d06d6bSBaptiste Daroussin man->last = n->prev;
108261d06d6bSBaptiste Daroussin man->next = ROFF_NEXT_SIBLING;
108361d06d6bSBaptiste Daroussin }
108461d06d6bSBaptiste Daroussin }
10857295610fSBaptiste Daroussin if (man->meta.first == n)
10867295610fSBaptiste Daroussin man->meta.first = NULL;
10877295610fSBaptiste Daroussin }
10887295610fSBaptiste Daroussin
10897295610fSBaptiste Daroussin void
roff_node_relink(struct roff_man * man,struct roff_node * n)10907295610fSBaptiste Daroussin roff_node_relink(struct roff_man *man, struct roff_node *n)
10917295610fSBaptiste Daroussin {
10927295610fSBaptiste Daroussin roff_node_unlink(man, n);
10937295610fSBaptiste Daroussin n->prev = n->next = NULL;
10947295610fSBaptiste Daroussin roff_node_append(man, n);
109561d06d6bSBaptiste Daroussin }
109661d06d6bSBaptiste Daroussin
109761d06d6bSBaptiste Daroussin void
roff_node_free(struct roff_node * n)109861d06d6bSBaptiste Daroussin roff_node_free(struct roff_node *n)
109961d06d6bSBaptiste Daroussin {
110061d06d6bSBaptiste Daroussin
110161d06d6bSBaptiste Daroussin if (n->args != NULL)
110261d06d6bSBaptiste Daroussin mdoc_argv_free(n->args);
110361d06d6bSBaptiste Daroussin if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM)
110461d06d6bSBaptiste Daroussin free(n->norm);
110561d06d6bSBaptiste Daroussin eqn_box_free(n->eqn);
110661d06d6bSBaptiste Daroussin free(n->string);
11076d38604fSBaptiste Daroussin free(n->tag);
110861d06d6bSBaptiste Daroussin free(n);
110961d06d6bSBaptiste Daroussin }
111061d06d6bSBaptiste Daroussin
111161d06d6bSBaptiste Daroussin void
roff_node_delete(struct roff_man * man,struct roff_node * n)111261d06d6bSBaptiste Daroussin roff_node_delete(struct roff_man *man, struct roff_node *n)
111361d06d6bSBaptiste Daroussin {
111461d06d6bSBaptiste Daroussin
111561d06d6bSBaptiste Daroussin while (n->child != NULL)
111661d06d6bSBaptiste Daroussin roff_node_delete(man, n->child);
111761d06d6bSBaptiste Daroussin roff_node_unlink(man, n);
111861d06d6bSBaptiste Daroussin roff_node_free(n);
111961d06d6bSBaptiste Daroussin }
112061d06d6bSBaptiste Daroussin
11216d38604fSBaptiste Daroussin int
roff_node_transparent(struct roff_node * n)11226d38604fSBaptiste Daroussin roff_node_transparent(struct roff_node *n)
11236d38604fSBaptiste Daroussin {
11246d38604fSBaptiste Daroussin if (n == NULL)
11256d38604fSBaptiste Daroussin return 0;
11266d38604fSBaptiste Daroussin if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
11276d38604fSBaptiste Daroussin return 1;
11286d38604fSBaptiste Daroussin return roff_tok_transparent(n->tok);
11296d38604fSBaptiste Daroussin }
11306d38604fSBaptiste Daroussin
11316d38604fSBaptiste Daroussin int
roff_tok_transparent(enum roff_tok tok)11326d38604fSBaptiste Daroussin roff_tok_transparent(enum roff_tok tok)
11336d38604fSBaptiste Daroussin {
11346d38604fSBaptiste Daroussin switch (tok) {
11356d38604fSBaptiste Daroussin case ROFF_ft:
11366d38604fSBaptiste Daroussin case ROFF_ll:
11376d38604fSBaptiste Daroussin case ROFF_mc:
11386d38604fSBaptiste Daroussin case ROFF_po:
11396d38604fSBaptiste Daroussin case ROFF_ta:
11406d38604fSBaptiste Daroussin case MDOC_Db:
11416d38604fSBaptiste Daroussin case MDOC_Es:
11426d38604fSBaptiste Daroussin case MDOC_Sm:
11436d38604fSBaptiste Daroussin case MDOC_Tg:
11446d38604fSBaptiste Daroussin case MAN_DT:
11456d38604fSBaptiste Daroussin case MAN_UC:
11466d38604fSBaptiste Daroussin case MAN_PD:
11476d38604fSBaptiste Daroussin case MAN_AT:
11486d38604fSBaptiste Daroussin return 1;
11496d38604fSBaptiste Daroussin default:
11506d38604fSBaptiste Daroussin return 0;
11516d38604fSBaptiste Daroussin }
11526d38604fSBaptiste Daroussin }
11536d38604fSBaptiste Daroussin
11546d38604fSBaptiste Daroussin struct roff_node *
roff_node_child(struct roff_node * n)11556d38604fSBaptiste Daroussin roff_node_child(struct roff_node *n)
11566d38604fSBaptiste Daroussin {
11576d38604fSBaptiste Daroussin for (n = n->child; roff_node_transparent(n); n = n->next)
11586d38604fSBaptiste Daroussin continue;
11596d38604fSBaptiste Daroussin return n;
11606d38604fSBaptiste Daroussin }
11616d38604fSBaptiste Daroussin
11626d38604fSBaptiste Daroussin struct roff_node *
roff_node_prev(struct roff_node * n)11636d38604fSBaptiste Daroussin roff_node_prev(struct roff_node *n)
11646d38604fSBaptiste Daroussin {
11656d38604fSBaptiste Daroussin do {
11666d38604fSBaptiste Daroussin n = n->prev;
11676d38604fSBaptiste Daroussin } while (roff_node_transparent(n));
11686d38604fSBaptiste Daroussin return n;
11696d38604fSBaptiste Daroussin }
11706d38604fSBaptiste Daroussin
11716d38604fSBaptiste Daroussin struct roff_node *
roff_node_next(struct roff_node * n)11726d38604fSBaptiste Daroussin roff_node_next(struct roff_node *n)
11736d38604fSBaptiste Daroussin {
11746d38604fSBaptiste Daroussin do {
11756d38604fSBaptiste Daroussin n = n->next;
11766d38604fSBaptiste Daroussin } while (roff_node_transparent(n));
11776d38604fSBaptiste Daroussin return n;
11786d38604fSBaptiste Daroussin }
11796d38604fSBaptiste Daroussin
118061d06d6bSBaptiste Daroussin void
deroff(char ** dest,const struct roff_node * n)118161d06d6bSBaptiste Daroussin deroff(char **dest, const struct roff_node *n)
118261d06d6bSBaptiste Daroussin {
118361d06d6bSBaptiste Daroussin char *cp;
118461d06d6bSBaptiste Daroussin size_t sz;
118561d06d6bSBaptiste Daroussin
11866d38604fSBaptiste Daroussin if (n->string == NULL) {
118761d06d6bSBaptiste Daroussin for (n = n->child; n != NULL; n = n->next)
118861d06d6bSBaptiste Daroussin deroff(dest, n);
118961d06d6bSBaptiste Daroussin return;
119061d06d6bSBaptiste Daroussin }
119161d06d6bSBaptiste Daroussin
119261d06d6bSBaptiste Daroussin /* Skip leading whitespace. */
119361d06d6bSBaptiste Daroussin
119461d06d6bSBaptiste Daroussin for (cp = n->string; *cp != '\0'; cp++) {
119561d06d6bSBaptiste Daroussin if (cp[0] == '\\' && cp[1] != '\0' &&
119661d06d6bSBaptiste Daroussin strchr(" %&0^|~", cp[1]) != NULL)
119761d06d6bSBaptiste Daroussin cp++;
119861d06d6bSBaptiste Daroussin else if ( ! isspace((unsigned char)*cp))
119961d06d6bSBaptiste Daroussin break;
120061d06d6bSBaptiste Daroussin }
120161d06d6bSBaptiste Daroussin
120261d06d6bSBaptiste Daroussin /* Skip trailing backslash. */
120361d06d6bSBaptiste Daroussin
120461d06d6bSBaptiste Daroussin sz = strlen(cp);
120561d06d6bSBaptiste Daroussin if (sz > 0 && cp[sz - 1] == '\\')
120661d06d6bSBaptiste Daroussin sz--;
120761d06d6bSBaptiste Daroussin
120861d06d6bSBaptiste Daroussin /* Skip trailing whitespace. */
120961d06d6bSBaptiste Daroussin
121061d06d6bSBaptiste Daroussin for (; sz; sz--)
121161d06d6bSBaptiste Daroussin if ( ! isspace((unsigned char)cp[sz-1]))
121261d06d6bSBaptiste Daroussin break;
121361d06d6bSBaptiste Daroussin
121461d06d6bSBaptiste Daroussin /* Skip empty strings. */
121561d06d6bSBaptiste Daroussin
121661d06d6bSBaptiste Daroussin if (sz == 0)
121761d06d6bSBaptiste Daroussin return;
121861d06d6bSBaptiste Daroussin
121961d06d6bSBaptiste Daroussin if (*dest == NULL) {
122061d06d6bSBaptiste Daroussin *dest = mandoc_strndup(cp, sz);
122161d06d6bSBaptiste Daroussin return;
122261d06d6bSBaptiste Daroussin }
122361d06d6bSBaptiste Daroussin
122461d06d6bSBaptiste Daroussin mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
122561d06d6bSBaptiste Daroussin free(*dest);
122661d06d6bSBaptiste Daroussin *dest = cp;
122761d06d6bSBaptiste Daroussin }
122861d06d6bSBaptiste Daroussin
122961d06d6bSBaptiste Daroussin /* --- main functions of the roff parser ---------------------------------- */
123061d06d6bSBaptiste Daroussin
123161d06d6bSBaptiste Daroussin /*
1232*c1c95addSBrooks Davis * Save comments preceding the title macro, for example in order to
1233*c1c95addSBrooks Davis * preserve Copyright and license headers in HTML output,
1234*c1c95addSBrooks Davis * provide diagnostics about RCS ids and trailing whitespace in comments,
1235*c1c95addSBrooks Davis * then discard comments including preceding whitespace.
1236*c1c95addSBrooks Davis * This function also handles input line continuation.
123761d06d6bSBaptiste Daroussin */
12387295610fSBaptiste Daroussin static int
roff_parse_comment(struct roff * r,struct buf * buf,int ln,int pos,char ec)1239*c1c95addSBrooks Davis roff_parse_comment(struct roff *r, struct buf *buf, int ln, int pos, char ec)
124061d06d6bSBaptiste Daroussin {
124161d06d6bSBaptiste Daroussin struct roff_node *n; /* used for header comments */
124261d06d6bSBaptiste Daroussin const char *start; /* start of the string to process */
1243*c1c95addSBrooks Davis const char *cp; /* for RCS id parsing */
124461d06d6bSBaptiste Daroussin char *stesc; /* start of an escape sequence ('\\') */
124561d06d6bSBaptiste Daroussin char *ep; /* end of comment string */
124661d06d6bSBaptiste Daroussin int rcsid; /* kind of RCS id seen */
124761d06d6bSBaptiste Daroussin
1248*c1c95addSBrooks Davis for (start = stesc = buf->buf + pos;; stesc++) {
1249*c1c95addSBrooks Davis /*
1250*c1c95addSBrooks Davis * XXX Ugly hack: Remove the newline character that
1251*c1c95addSBrooks Davis * mparse_buf_r() appended to mark the end of input
1252*c1c95addSBrooks Davis * if it is not preceded by an escape character.
1253*c1c95addSBrooks Davis */
1254*c1c95addSBrooks Davis if (stesc[0] == '\n') {
1255*c1c95addSBrooks Davis assert(stesc[1] == '\0');
1256*c1c95addSBrooks Davis stesc[0] = '\0';
1257*c1c95addSBrooks Davis }
125861d06d6bSBaptiste Daroussin
1259*c1c95addSBrooks Davis /* The line ends without continuation or comment. */
1260*c1c95addSBrooks Davis if (stesc[0] == '\0')
1261*c1c95addSBrooks Davis return ROFF_CONT;
1262*c1c95addSBrooks Davis
1263*c1c95addSBrooks Davis /* Unescaped byte: skip it. */
1264*c1c95addSBrooks Davis if (stesc[0] != ec)
126561d06d6bSBaptiste Daroussin continue;
1266*c1c95addSBrooks Davis
1267*c1c95addSBrooks Davis /*
1268*c1c95addSBrooks Davis * XXX Ugly hack: Do not attempt to append another line
1269*c1c95addSBrooks Davis * if the function mparse_buf_r() appended a newline
1270*c1c95addSBrooks Davis * character to indicate the end of input.
1271*c1c95addSBrooks Davis */
1272*c1c95addSBrooks Davis if (stesc[1] == '\n') {
1273*c1c95addSBrooks Davis assert(stesc[2] == '\0');
1274*c1c95addSBrooks Davis stesc[0] = '\0';
1275*c1c95addSBrooks Davis return ROFF_CONT;
1276*c1c95addSBrooks Davis }
1277*c1c95addSBrooks Davis
1278*c1c95addSBrooks Davis /*
1279*c1c95addSBrooks Davis * An escape character at the end of an input line
1280*c1c95addSBrooks Davis * requests line continuation.
1281*c1c95addSBrooks Davis */
1282*c1c95addSBrooks Davis if (stesc[1] == '\0') {
1283*c1c95addSBrooks Davis stesc[0] = '\0';
1284*c1c95addSBrooks Davis return ROFF_IGN | ROFF_APPEND;
1285*c1c95addSBrooks Davis }
1286*c1c95addSBrooks Davis
1287*c1c95addSBrooks Davis /* Found a comment: process it. */
1288*c1c95addSBrooks Davis if (stesc[1] == '"' || stesc[1] == '#')
1289*c1c95addSBrooks Davis break;
1290*c1c95addSBrooks Davis
1291*c1c95addSBrooks Davis /* Escaped escape character: skip them both. */
1292*c1c95addSBrooks Davis if (stesc[1] == ec)
129361d06d6bSBaptiste Daroussin stesc++;
1294*c1c95addSBrooks Davis }
129561d06d6bSBaptiste Daroussin
1296*c1c95addSBrooks Davis /* Look for an RCS id in the comment. */
129761d06d6bSBaptiste Daroussin
129861d06d6bSBaptiste Daroussin rcsid = 0;
1299*c1c95addSBrooks Davis if ((cp = strstr(stesc + 2, "$" "OpenBSD")) != NULL) {
130061d06d6bSBaptiste Daroussin rcsid = 1 << MANDOC_OS_OPENBSD;
130161d06d6bSBaptiste Daroussin cp += 8;
1302*c1c95addSBrooks Davis } else if ((cp = strstr(stesc + 2, "$" "NetBSD")) != NULL) {
130361d06d6bSBaptiste Daroussin rcsid = 1 << MANDOC_OS_NETBSD;
130461d06d6bSBaptiste Daroussin cp += 7;
130561d06d6bSBaptiste Daroussin }
1306*c1c95addSBrooks Davis if (cp != NULL && isalnum((unsigned char)*cp) == 0 &&
130761d06d6bSBaptiste Daroussin strchr(cp, '$') != NULL) {
130861d06d6bSBaptiste Daroussin if (r->man->meta.rcsids & rcsid)
13097295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_RCS_REP, ln,
1310*c1c95addSBrooks Davis (int)(stesc - buf->buf) + 2, "%s", stesc + 1);
131161d06d6bSBaptiste Daroussin r->man->meta.rcsids |= rcsid;
131261d06d6bSBaptiste Daroussin }
131361d06d6bSBaptiste Daroussin
1314*c1c95addSBrooks Davis /* Warn about trailing whitespace at the end of the comment. */
131561d06d6bSBaptiste Daroussin
1316*c1c95addSBrooks Davis ep = strchr(stesc + 2, '\0') - 1;
1317*c1c95addSBrooks Davis if (*ep == '\n')
1318*c1c95addSBrooks Davis *ep-- = '\0';
131961d06d6bSBaptiste Daroussin if (*ep == ' ' || *ep == '\t')
13207295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_SPACE_EOL,
13217295610fSBaptiste Daroussin ln, (int)(ep - buf->buf), NULL);
132261d06d6bSBaptiste Daroussin
1323*c1c95addSBrooks Davis /* Save comments preceding the title macro in the syntax tree. */
132461d06d6bSBaptiste Daroussin
1325*c1c95addSBrooks Davis if (r->options & MPARSE_COMMENT) {
132661d06d6bSBaptiste Daroussin while (*ep == ' ' || *ep == '\t')
132761d06d6bSBaptiste Daroussin ep--;
132861d06d6bSBaptiste Daroussin ep[1] = '\0';
1329*c1c95addSBrooks Davis n = roff_node_alloc(r->man, ln, stesc + 1 - buf->buf,
133061d06d6bSBaptiste Daroussin ROFFT_COMMENT, TOKEN_NONE);
133161d06d6bSBaptiste Daroussin n->string = mandoc_strdup(stesc + 2);
133261d06d6bSBaptiste Daroussin roff_node_append(r->man, n);
133361d06d6bSBaptiste Daroussin n->flags |= NODE_VALID | NODE_ENDED;
133461d06d6bSBaptiste Daroussin r->man->next = ROFF_NEXT_SIBLING;
133561d06d6bSBaptiste Daroussin }
133661d06d6bSBaptiste Daroussin
1337*c1c95addSBrooks Davis /* The comment requests line continuation. */
133861d06d6bSBaptiste Daroussin
13397295610fSBaptiste Daroussin if (stesc[1] == '#') {
13407295610fSBaptiste Daroussin *stesc = '\0';
13417295610fSBaptiste Daroussin return ROFF_IGN | ROFF_APPEND;
13427295610fSBaptiste Daroussin }
13437295610fSBaptiste Daroussin
1344*c1c95addSBrooks Davis /* Discard the comment including preceding whitespace. */
13457295610fSBaptiste Daroussin
13467295610fSBaptiste Daroussin while (stesc > start && stesc[-1] == ' ' &&
13477295610fSBaptiste Daroussin (stesc == start + 1 || stesc[-2] != '\\'))
134861d06d6bSBaptiste Daroussin stesc--;
134961d06d6bSBaptiste Daroussin *stesc = '\0';
135061d06d6bSBaptiste Daroussin return ROFF_CONT;
135161d06d6bSBaptiste Daroussin }
135261d06d6bSBaptiste Daroussin
1353*c1c95addSBrooks Davis /*
1354*c1c95addSBrooks Davis * In the current line, expand escape sequences that produce parsable
1355*c1c95addSBrooks Davis * input text. Also check the syntax of the remaining escape sequences,
1356*c1c95addSBrooks Davis * which typically produce output glyphs or change formatter state.
1357*c1c95addSBrooks Davis */
1358*c1c95addSBrooks Davis static int
roff_expand(struct roff * r,struct buf * buf,int ln,int pos,char ec)1359*c1c95addSBrooks Davis roff_expand(struct roff *r, struct buf *buf, int ln, int pos, char ec)
1360*c1c95addSBrooks Davis {
1361*c1c95addSBrooks Davis char ubuf[24]; /* buffer to print a number */
1362*c1c95addSBrooks Davis struct mctx *ctx; /* current macro call context */
1363*c1c95addSBrooks Davis const char *res; /* the string to be pasted */
1364*c1c95addSBrooks Davis const char *src; /* source for copying */
1365*c1c95addSBrooks Davis char *dst; /* destination for copying */
1366*c1c95addSBrooks Davis enum mandoc_esc subtype; /* return value from roff_escape */
1367*c1c95addSBrooks Davis int iesc; /* index of leading escape char */
1368*c1c95addSBrooks Davis int inam; /* index of the escape name */
1369*c1c95addSBrooks Davis int iarg; /* index beginning the argument */
1370*c1c95addSBrooks Davis int iendarg; /* index right after the argument */
1371*c1c95addSBrooks Davis int iend; /* index right after the sequence */
1372*c1c95addSBrooks Davis int isrc, idst; /* to reduce \\ and \. in names */
1373*c1c95addSBrooks Davis int deftype; /* type of definition to paste */
1374*c1c95addSBrooks Davis int argi; /* macro argument index */
1375*c1c95addSBrooks Davis int quote_args; /* true for \\$@, false for \\$* */
1376*c1c95addSBrooks Davis int asz; /* length of the replacement */
1377*c1c95addSBrooks Davis int rsz; /* length of the rest of the string */
1378*c1c95addSBrooks Davis int npos; /* position in numeric expression */
1379*c1c95addSBrooks Davis int expand_count; /* to avoid infinite loops */
1380*c1c95addSBrooks Davis
138161d06d6bSBaptiste Daroussin expand_count = 0;
1382*c1c95addSBrooks Davis while (buf->buf[pos] != '\0') {
138361d06d6bSBaptiste Daroussin
13847295610fSBaptiste Daroussin /*
1385*c1c95addSBrooks Davis * Skip plain ASCII characters.
13867295610fSBaptiste Daroussin * If we have a non-standard escape character,
1387*c1c95addSBrooks Davis * escape literal backslashes because all processing in
1388*c1c95addSBrooks Davis * subsequent functions uses the standard escaping rules.
13897295610fSBaptiste Daroussin */
139061d06d6bSBaptiste Daroussin
1391*c1c95addSBrooks Davis if (buf->buf[pos] != ec) {
1392*c1c95addSBrooks Davis if (buf->buf[pos] == '\\') {
1393*c1c95addSBrooks Davis roff_expand_patch(buf, pos, "\\e", pos + 1);
1394*c1c95addSBrooks Davis pos++;
139561d06d6bSBaptiste Daroussin }
1396*c1c95addSBrooks Davis pos++;
139761d06d6bSBaptiste Daroussin continue;
139861d06d6bSBaptiste Daroussin }
139961d06d6bSBaptiste Daroussin
1400*c1c95addSBrooks Davis /*
1401*c1c95addSBrooks Davis * Parse escape sequences,
1402*c1c95addSBrooks Davis * issue diagnostic messages when appropriate,
1403*c1c95addSBrooks Davis * and skip sequences that do not need expansion.
1404*c1c95addSBrooks Davis * If we have a non-standard escape character, translate
1405*c1c95addSBrooks Davis * it to backslashes and translate backslashes to \e.
1406*c1c95addSBrooks Davis */
140761d06d6bSBaptiste Daroussin
1408*c1c95addSBrooks Davis if (roff_escape(buf->buf, ln, pos, &iesc, &inam,
1409*c1c95addSBrooks Davis &iarg, &iendarg, &iend) != ESCAPE_EXPAND) {
1410*c1c95addSBrooks Davis while (pos < iend) {
1411*c1c95addSBrooks Davis if (buf->buf[pos] == ec) {
1412*c1c95addSBrooks Davis buf->buf[pos] = '\\';
1413*c1c95addSBrooks Davis if (pos + 1 < iend)
1414*c1c95addSBrooks Davis pos++;
1415*c1c95addSBrooks Davis } else if (buf->buf[pos] == '\\') {
1416*c1c95addSBrooks Davis roff_expand_patch(buf,
1417*c1c95addSBrooks Davis pos, "\\e", pos + 1);
1418*c1c95addSBrooks Davis pos++;
1419*c1c95addSBrooks Davis iend++;
1420*c1c95addSBrooks Davis }
1421*c1c95addSBrooks Davis pos++;
1422*c1c95addSBrooks Davis }
142361d06d6bSBaptiste Daroussin continue;
142461d06d6bSBaptiste Daroussin }
142561d06d6bSBaptiste Daroussin
1426*c1c95addSBrooks Davis /* Reduce \\ and \. in names. */
142761d06d6bSBaptiste Daroussin
1428*c1c95addSBrooks Davis if (buf->buf[inam] == '*' || buf->buf[inam] == 'n') {
1429*c1c95addSBrooks Davis isrc = idst = iarg;
1430*c1c95addSBrooks Davis while (isrc < iendarg) {
1431*c1c95addSBrooks Davis if (isrc + 1 < iendarg &&
1432*c1c95addSBrooks Davis buf->buf[isrc] == '\\' &&
1433*c1c95addSBrooks Davis (buf->buf[isrc + 1] == '\\' ||
1434*c1c95addSBrooks Davis buf->buf[isrc + 1] == '.'))
1435*c1c95addSBrooks Davis isrc++;
1436*c1c95addSBrooks Davis buf->buf[idst++] = buf->buf[isrc++];
1437*c1c95addSBrooks Davis }
1438*c1c95addSBrooks Davis iendarg -= isrc - idst;
1439*c1c95addSBrooks Davis }
1440*c1c95addSBrooks Davis
1441*c1c95addSBrooks Davis /* Handle expansion. */
1442*c1c95addSBrooks Davis
144361d06d6bSBaptiste Daroussin res = NULL;
1444*c1c95addSBrooks Davis switch (buf->buf[inam]) {
144561d06d6bSBaptiste Daroussin case '*':
1446*c1c95addSBrooks Davis if (iendarg == iarg)
1447*c1c95addSBrooks Davis break;
144861d06d6bSBaptiste Daroussin deftype = ROFFDEF_USER | ROFFDEF_PRE;
1449*c1c95addSBrooks Davis if ((res = roff_getstrn(r, buf->buf + iarg,
1450*c1c95addSBrooks Davis iendarg - iarg, &deftype)) != NULL)
1451*c1c95addSBrooks Davis break;
14527295610fSBaptiste Daroussin
14537295610fSBaptiste Daroussin /*
1454*c1c95addSBrooks Davis * If not overridden,
1455*c1c95addSBrooks Davis * let \*(.T through to the formatters.
14567295610fSBaptiste Daroussin */
14577295610fSBaptiste Daroussin
1458*c1c95addSBrooks Davis if (iendarg - iarg == 2 &&
1459*c1c95addSBrooks Davis buf->buf[iarg] == '.' &&
1460*c1c95addSBrooks Davis buf->buf[iarg + 1] == 'T') {
1461*c1c95addSBrooks Davis roff_setstrn(&r->strtab, ".T", 2, NULL, 0, 0);
1462*c1c95addSBrooks Davis pos = iend;
14637295610fSBaptiste Daroussin continue;
14647295610fSBaptiste Daroussin }
1465*c1c95addSBrooks Davis
1466*c1c95addSBrooks Davis mandoc_msg(MANDOCERR_STR_UNDEF, ln, iesc,
1467*c1c95addSBrooks Davis "%.*s", iendarg - iarg, buf->buf + iarg);
146861d06d6bSBaptiste Daroussin break;
1469*c1c95addSBrooks Davis
14707295610fSBaptiste Daroussin case '$':
14717295610fSBaptiste Daroussin if (r->mstackpos < 0) {
1472*c1c95addSBrooks Davis mandoc_msg(MANDOCERR_ARG_UNDEF, ln, iesc,
1473*c1c95addSBrooks Davis "%.*s", iend - iesc, buf->buf + iesc);
14747295610fSBaptiste Daroussin break;
14757295610fSBaptiste Daroussin }
14767295610fSBaptiste Daroussin ctx = r->mstack + r->mstackpos;
1477*c1c95addSBrooks Davis argi = buf->buf[iarg] - '1';
1478*c1c95addSBrooks Davis if (argi >= 0 && argi <= 8) {
1479*c1c95addSBrooks Davis if (argi < ctx->argc)
1480*c1c95addSBrooks Davis res = ctx->argv[argi];
14817295610fSBaptiste Daroussin break;
14827295610fSBaptiste Daroussin }
1483*c1c95addSBrooks Davis if (buf->buf[iarg] == '*')
14847295610fSBaptiste Daroussin quote_args = 0;
1485*c1c95addSBrooks Davis else if (buf->buf[iarg] == '@')
14867295610fSBaptiste Daroussin quote_args = 1;
14877295610fSBaptiste Daroussin else {
1488*c1c95addSBrooks Davis mandoc_msg(MANDOCERR_ARG_NONUM, ln, iesc,
1489*c1c95addSBrooks Davis "%.*s", iend - iesc, buf->buf + iesc);
14907295610fSBaptiste Daroussin break;
14917295610fSBaptiste Daroussin }
14927295610fSBaptiste Daroussin asz = 0;
1493*c1c95addSBrooks Davis for (argi = 0; argi < ctx->argc; argi++) {
1494*c1c95addSBrooks Davis if (argi)
14957295610fSBaptiste Daroussin asz++; /* blank */
14967295610fSBaptiste Daroussin if (quote_args)
14977295610fSBaptiste Daroussin asz += 2; /* quotes */
1498*c1c95addSBrooks Davis asz += strlen(ctx->argv[argi]);
14997295610fSBaptiste Daroussin }
1500*c1c95addSBrooks Davis if (asz != iend - iesc) {
1501*c1c95addSBrooks Davis rsz = buf->sz - iend;
1502*c1c95addSBrooks Davis if (asz < iend - iesc)
1503*c1c95addSBrooks Davis memmove(buf->buf + iesc + asz,
1504*c1c95addSBrooks Davis buf->buf + iend, rsz);
1505*c1c95addSBrooks Davis buf->sz = iesc + asz + rsz;
1506*c1c95addSBrooks Davis buf->buf = mandoc_realloc(buf->buf, buf->sz);
1507*c1c95addSBrooks Davis if (asz > iend - iesc)
1508*c1c95addSBrooks Davis memmove(buf->buf + iesc + asz,
1509*c1c95addSBrooks Davis buf->buf + iend, rsz);
15107295610fSBaptiste Daroussin }
1511*c1c95addSBrooks Davis dst = buf->buf + iesc;
1512*c1c95addSBrooks Davis for (argi = 0; argi < ctx->argc; argi++) {
1513*c1c95addSBrooks Davis if (argi)
1514*c1c95addSBrooks Davis *dst++ = ' ';
15157295610fSBaptiste Daroussin if (quote_args)
1516*c1c95addSBrooks Davis *dst++ = '"';
1517*c1c95addSBrooks Davis src = ctx->argv[argi];
1518*c1c95addSBrooks Davis while (*src != '\0')
1519*c1c95addSBrooks Davis *dst++ = *src++;
15207295610fSBaptiste Daroussin if (quote_args)
1521*c1c95addSBrooks Davis *dst++ = '"';
15227295610fSBaptiste Daroussin }
15237295610fSBaptiste Daroussin continue;
1524*c1c95addSBrooks Davis case 'A':
1525*c1c95addSBrooks Davis ubuf[0] = iendarg > iarg ? '1' : '0';
1526*c1c95addSBrooks Davis ubuf[1] = '\0';
1527*c1c95addSBrooks Davis res = ubuf;
1528*c1c95addSBrooks Davis break;
152961d06d6bSBaptiste Daroussin case 'B':
153061d06d6bSBaptiste Daroussin npos = 0;
1531*c1c95addSBrooks Davis ubuf[0] = iendarg > iarg && iend > iendarg &&
1532*c1c95addSBrooks Davis roff_evalnum(r, ln, buf->buf + iarg, &npos,
153361d06d6bSBaptiste Daroussin NULL, ROFFNUM_SCALE) &&
1534*c1c95addSBrooks Davis npos == iendarg - iarg ? '1' : '0';
153561d06d6bSBaptiste Daroussin ubuf[1] = '\0';
1536*c1c95addSBrooks Davis res = ubuf;
1537*c1c95addSBrooks Davis break;
1538*c1c95addSBrooks Davis case 'V':
1539*c1c95addSBrooks Davis mandoc_msg(MANDOCERR_UNSUPP, ln, iesc,
1540*c1c95addSBrooks Davis "%.*s", iend - iesc, buf->buf + iesc);
1541*c1c95addSBrooks Davis roff_expand_patch(buf, iendarg, "}", iend);
1542*c1c95addSBrooks Davis roff_expand_patch(buf, iesc, "${", iarg);
1543*c1c95addSBrooks Davis continue;
1544*c1c95addSBrooks Davis case 'g':
154561d06d6bSBaptiste Daroussin break;
154661d06d6bSBaptiste Daroussin case 'n':
1547*c1c95addSBrooks Davis if (iendarg > iarg)
154861d06d6bSBaptiste Daroussin (void)snprintf(ubuf, sizeof(ubuf), "%d",
1549*c1c95addSBrooks Davis roff_getregn(r, buf->buf + iarg,
1550*c1c95addSBrooks Davis iendarg - iarg, buf->buf[inam + 1]));
155161d06d6bSBaptiste Daroussin else
155261d06d6bSBaptiste Daroussin ubuf[0] = '\0';
1553*c1c95addSBrooks Davis res = ubuf;
155461d06d6bSBaptiste Daroussin break;
155561d06d6bSBaptiste Daroussin case 'w':
1556*c1c95addSBrooks Davis rsz = 0;
1557*c1c95addSBrooks Davis subtype = ESCAPE_UNDEF;
1558*c1c95addSBrooks Davis while (iarg < iendarg) {
1559*c1c95addSBrooks Davis asz = subtype == ESCAPE_SKIPCHAR ? 0 : 1;
1560*c1c95addSBrooks Davis if (buf->buf[iarg] != '\\') {
1561*c1c95addSBrooks Davis rsz += asz;
1562*c1c95addSBrooks Davis iarg++;
1563*c1c95addSBrooks Davis continue;
1564*c1c95addSBrooks Davis }
1565*c1c95addSBrooks Davis switch ((subtype = roff_escape(buf->buf, 0,
1566*c1c95addSBrooks Davis iarg, NULL, NULL, NULL, NULL, &iarg))) {
1567*c1c95addSBrooks Davis case ESCAPE_SPECIAL:
1568*c1c95addSBrooks Davis case ESCAPE_NUMBERED:
1569*c1c95addSBrooks Davis case ESCAPE_UNICODE:
1570*c1c95addSBrooks Davis case ESCAPE_OVERSTRIKE:
1571*c1c95addSBrooks Davis case ESCAPE_UNDEF:
1572*c1c95addSBrooks Davis break;
1573*c1c95addSBrooks Davis case ESCAPE_DEVICE:
1574*c1c95addSBrooks Davis asz *= 8;
1575*c1c95addSBrooks Davis break;
1576*c1c95addSBrooks Davis case ESCAPE_EXPAND:
1577*c1c95addSBrooks Davis abort();
1578*c1c95addSBrooks Davis default:
1579*c1c95addSBrooks Davis continue;
1580*c1c95addSBrooks Davis }
1581*c1c95addSBrooks Davis rsz += asz;
1582*c1c95addSBrooks Davis }
1583*c1c95addSBrooks Davis (void)snprintf(ubuf, sizeof(ubuf), "%d", rsz * 24);
1584*c1c95addSBrooks Davis res = ubuf;
1585*c1c95addSBrooks Davis break;
1586*c1c95addSBrooks Davis default:
158761d06d6bSBaptiste Daroussin break;
158861d06d6bSBaptiste Daroussin }
1589*c1c95addSBrooks Davis if (res == NULL)
159061d06d6bSBaptiste Daroussin res = "";
1591*c1c95addSBrooks Davis if (++expand_count > EXPAND_LIMIT ||
1592*c1c95addSBrooks Davis buf->sz + strlen(res) > SHRT_MAX) {
1593*c1c95addSBrooks Davis mandoc_msg(MANDOCERR_ROFFLOOP, ln, iesc, NULL);
159461d06d6bSBaptiste Daroussin return ROFF_IGN;
159561d06d6bSBaptiste Daroussin }
1596*c1c95addSBrooks Davis roff_expand_patch(buf, iesc, res, iend);
159761d06d6bSBaptiste Daroussin }
159861d06d6bSBaptiste Daroussin return ROFF_CONT;
159961d06d6bSBaptiste Daroussin }
160061d06d6bSBaptiste Daroussin
160161d06d6bSBaptiste Daroussin /*
1602*c1c95addSBrooks Davis * Replace the substring from the start position (inclusive)
1603*c1c95addSBrooks Davis * to end position (exclusive) with the repl(acement) string.
1604*c1c95addSBrooks Davis */
1605*c1c95addSBrooks Davis static void
roff_expand_patch(struct buf * buf,int start,const char * repl,int end)1606*c1c95addSBrooks Davis roff_expand_patch(struct buf *buf, int start, const char *repl, int end)
1607*c1c95addSBrooks Davis {
1608*c1c95addSBrooks Davis char *nbuf;
1609*c1c95addSBrooks Davis
1610*c1c95addSBrooks Davis buf->sz = mandoc_asprintf(&nbuf, "%.*s%s%s", start, buf->buf,
1611*c1c95addSBrooks Davis repl, buf->buf + end) + 1;
1612*c1c95addSBrooks Davis free(buf->buf);
1613*c1c95addSBrooks Davis buf->buf = nbuf;
1614*c1c95addSBrooks Davis }
1615*c1c95addSBrooks Davis
1616*c1c95addSBrooks Davis /*
16177295610fSBaptiste Daroussin * Parse a quoted or unquoted roff-style request or macro argument.
16187295610fSBaptiste Daroussin * Return a pointer to the parsed argument, which is either the original
16197295610fSBaptiste Daroussin * pointer or advanced by one byte in case the argument is quoted.
16207295610fSBaptiste Daroussin * NUL-terminate the argument in place.
16217295610fSBaptiste Daroussin * Collapse pairs of quotes inside quoted arguments.
16227295610fSBaptiste Daroussin * Advance the argument pointer to the next argument,
16237295610fSBaptiste Daroussin * or to the NUL byte terminating the argument line.
16247295610fSBaptiste Daroussin */
16257295610fSBaptiste Daroussin char *
roff_getarg(struct roff * r,char ** cpp,int ln,int * pos)16267295610fSBaptiste Daroussin roff_getarg(struct roff *r, char **cpp, int ln, int *pos)
16277295610fSBaptiste Daroussin {
16287295610fSBaptiste Daroussin struct buf buf;
16297295610fSBaptiste Daroussin char *cp, *start;
16307295610fSBaptiste Daroussin int newesc, pairs, quoted, white;
16317295610fSBaptiste Daroussin
16327295610fSBaptiste Daroussin /* Quoting can only start with a new word. */
16337295610fSBaptiste Daroussin start = *cpp;
16347295610fSBaptiste Daroussin quoted = 0;
16357295610fSBaptiste Daroussin if ('"' == *start) {
16367295610fSBaptiste Daroussin quoted = 1;
16377295610fSBaptiste Daroussin start++;
16387295610fSBaptiste Daroussin }
16397295610fSBaptiste Daroussin
16407295610fSBaptiste Daroussin newesc = pairs = white = 0;
16417295610fSBaptiste Daroussin for (cp = start; '\0' != *cp; cp++) {
16427295610fSBaptiste Daroussin
16437295610fSBaptiste Daroussin /*
16447295610fSBaptiste Daroussin * Move the following text left
16457295610fSBaptiste Daroussin * after quoted quotes and after "\\" and "\t".
16467295610fSBaptiste Daroussin */
16477295610fSBaptiste Daroussin if (pairs)
16487295610fSBaptiste Daroussin cp[-pairs] = cp[0];
16497295610fSBaptiste Daroussin
16507295610fSBaptiste Daroussin if ('\\' == cp[0]) {
16517295610fSBaptiste Daroussin /*
16527295610fSBaptiste Daroussin * In copy mode, translate double to single
16537295610fSBaptiste Daroussin * backslashes and backslash-t to literal tabs.
16547295610fSBaptiste Daroussin */
16557295610fSBaptiste Daroussin switch (cp[1]) {
16567295610fSBaptiste Daroussin case 'a':
16577295610fSBaptiste Daroussin case 't':
16587295610fSBaptiste Daroussin cp[-pairs] = '\t';
16597295610fSBaptiste Daroussin pairs++;
16607295610fSBaptiste Daroussin cp++;
16617295610fSBaptiste Daroussin break;
16627295610fSBaptiste Daroussin case '\\':
1663*c1c95addSBrooks Davis cp[-pairs] = '\\';
16647295610fSBaptiste Daroussin newesc = 1;
16657295610fSBaptiste Daroussin pairs++;
16667295610fSBaptiste Daroussin cp++;
16677295610fSBaptiste Daroussin break;
16687295610fSBaptiste Daroussin case ' ':
16697295610fSBaptiste Daroussin /* Skip escaped blanks. */
16707295610fSBaptiste Daroussin if (0 == quoted)
16717295610fSBaptiste Daroussin cp++;
16727295610fSBaptiste Daroussin break;
16737295610fSBaptiste Daroussin default:
16747295610fSBaptiste Daroussin break;
16757295610fSBaptiste Daroussin }
16767295610fSBaptiste Daroussin } else if (0 == quoted) {
16777295610fSBaptiste Daroussin if (' ' == cp[0]) {
16787295610fSBaptiste Daroussin /* Unescaped blanks end unquoted args. */
16797295610fSBaptiste Daroussin white = 1;
16807295610fSBaptiste Daroussin break;
16817295610fSBaptiste Daroussin }
16827295610fSBaptiste Daroussin } else if ('"' == cp[0]) {
16837295610fSBaptiste Daroussin if ('"' == cp[1]) {
16847295610fSBaptiste Daroussin /* Quoted quotes collapse. */
16857295610fSBaptiste Daroussin pairs++;
16867295610fSBaptiste Daroussin cp++;
16877295610fSBaptiste Daroussin } else {
16887295610fSBaptiste Daroussin /* Unquoted quotes end quoted args. */
16897295610fSBaptiste Daroussin quoted = 2;
16907295610fSBaptiste Daroussin break;
16917295610fSBaptiste Daroussin }
16927295610fSBaptiste Daroussin }
16937295610fSBaptiste Daroussin }
16947295610fSBaptiste Daroussin
16957295610fSBaptiste Daroussin /* Quoted argument without a closing quote. */
16967295610fSBaptiste Daroussin if (1 == quoted)
16977295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_QUOTE, ln, *pos, NULL);
16987295610fSBaptiste Daroussin
16997295610fSBaptiste Daroussin /* NUL-terminate this argument and move to the next one. */
17007295610fSBaptiste Daroussin if (pairs)
17017295610fSBaptiste Daroussin cp[-pairs] = '\0';
17027295610fSBaptiste Daroussin if ('\0' != *cp) {
17037295610fSBaptiste Daroussin *cp++ = '\0';
17047295610fSBaptiste Daroussin while (' ' == *cp)
17057295610fSBaptiste Daroussin cp++;
17067295610fSBaptiste Daroussin }
17077295610fSBaptiste Daroussin *pos += (int)(cp - start) + (quoted ? 1 : 0);
17087295610fSBaptiste Daroussin *cpp = cp;
17097295610fSBaptiste Daroussin
17107295610fSBaptiste Daroussin if ('\0' == *cp && (white || ' ' == cp[-1]))
17117295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_SPACE_EOL, ln, *pos, NULL);
17127295610fSBaptiste Daroussin
17137295610fSBaptiste Daroussin start = mandoc_strdup(start);
17147295610fSBaptiste Daroussin if (newesc == 0)
17157295610fSBaptiste Daroussin return start;
17167295610fSBaptiste Daroussin
17177295610fSBaptiste Daroussin buf.buf = start;
17187295610fSBaptiste Daroussin buf.sz = strlen(start) + 1;
17197295610fSBaptiste Daroussin buf.next = NULL;
1720*c1c95addSBrooks Davis if (roff_expand(r, &buf, ln, 0, '\\') == ROFF_IGN) {
17217295610fSBaptiste Daroussin free(buf.buf);
17227295610fSBaptiste Daroussin buf.buf = mandoc_strdup("");
17237295610fSBaptiste Daroussin }
17247295610fSBaptiste Daroussin return buf.buf;
17257295610fSBaptiste Daroussin }
17267295610fSBaptiste Daroussin
17277295610fSBaptiste Daroussin
17287295610fSBaptiste Daroussin /*
172961d06d6bSBaptiste Daroussin * Process text streams.
173061d06d6bSBaptiste Daroussin */
17317295610fSBaptiste Daroussin static int
roff_parsetext(struct roff * r,struct buf * buf,int pos,int * offs)173261d06d6bSBaptiste Daroussin roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs)
173361d06d6bSBaptiste Daroussin {
173461d06d6bSBaptiste Daroussin size_t sz;
173561d06d6bSBaptiste Daroussin const char *start;
173661d06d6bSBaptiste Daroussin char *p;
173761d06d6bSBaptiste Daroussin int isz;
173861d06d6bSBaptiste Daroussin enum mandoc_esc esc;
173961d06d6bSBaptiste Daroussin
174061d06d6bSBaptiste Daroussin /* Spring the input line trap. */
174161d06d6bSBaptiste Daroussin
174261d06d6bSBaptiste Daroussin if (roffit_lines == 1) {
174361d06d6bSBaptiste Daroussin isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro);
174461d06d6bSBaptiste Daroussin free(buf->buf);
174561d06d6bSBaptiste Daroussin buf->buf = p;
174661d06d6bSBaptiste Daroussin buf->sz = isz + 1;
174761d06d6bSBaptiste Daroussin *offs = 0;
174861d06d6bSBaptiste Daroussin free(roffit_macro);
174961d06d6bSBaptiste Daroussin roffit_lines = 0;
175061d06d6bSBaptiste Daroussin return ROFF_REPARSE;
175161d06d6bSBaptiste Daroussin } else if (roffit_lines > 1)
175261d06d6bSBaptiste Daroussin --roffit_lines;
175361d06d6bSBaptiste Daroussin
175461d06d6bSBaptiste Daroussin if (roffce_node != NULL && buf->buf[pos] != '\0') {
175561d06d6bSBaptiste Daroussin if (roffce_lines < 1) {
175661d06d6bSBaptiste Daroussin r->man->last = roffce_node;
175761d06d6bSBaptiste Daroussin r->man->next = ROFF_NEXT_SIBLING;
175861d06d6bSBaptiste Daroussin roffce_lines = 0;
175961d06d6bSBaptiste Daroussin roffce_node = NULL;
176061d06d6bSBaptiste Daroussin } else
176161d06d6bSBaptiste Daroussin roffce_lines--;
176261d06d6bSBaptiste Daroussin }
176361d06d6bSBaptiste Daroussin
176461d06d6bSBaptiste Daroussin /* Convert all breakable hyphens into ASCII_HYPH. */
176561d06d6bSBaptiste Daroussin
176661d06d6bSBaptiste Daroussin start = p = buf->buf + pos;
176761d06d6bSBaptiste Daroussin
176861d06d6bSBaptiste Daroussin while (*p != '\0') {
176961d06d6bSBaptiste Daroussin sz = strcspn(p, "-\\");
177061d06d6bSBaptiste Daroussin p += sz;
177161d06d6bSBaptiste Daroussin
177261d06d6bSBaptiste Daroussin if (*p == '\0')
177361d06d6bSBaptiste Daroussin break;
177461d06d6bSBaptiste Daroussin
177561d06d6bSBaptiste Daroussin if (*p == '\\') {
177661d06d6bSBaptiste Daroussin /* Skip over escapes. */
177761d06d6bSBaptiste Daroussin p++;
177861d06d6bSBaptiste Daroussin esc = mandoc_escape((const char **)&p, NULL, NULL);
177961d06d6bSBaptiste Daroussin if (esc == ESCAPE_ERROR)
178061d06d6bSBaptiste Daroussin break;
178161d06d6bSBaptiste Daroussin while (*p == '-')
178261d06d6bSBaptiste Daroussin p++;
178361d06d6bSBaptiste Daroussin continue;
178461d06d6bSBaptiste Daroussin } else if (p == start) {
178561d06d6bSBaptiste Daroussin p++;
178661d06d6bSBaptiste Daroussin continue;
178761d06d6bSBaptiste Daroussin }
178861d06d6bSBaptiste Daroussin
178961d06d6bSBaptiste Daroussin if (isalpha((unsigned char)p[-1]) &&
179061d06d6bSBaptiste Daroussin isalpha((unsigned char)p[1]))
179161d06d6bSBaptiste Daroussin *p = ASCII_HYPH;
179261d06d6bSBaptiste Daroussin p++;
179361d06d6bSBaptiste Daroussin }
179461d06d6bSBaptiste Daroussin return ROFF_CONT;
179561d06d6bSBaptiste Daroussin }
179661d06d6bSBaptiste Daroussin
17977295610fSBaptiste Daroussin int
roff_parseln(struct roff * r,int ln,struct buf * buf,int * offs,size_t len)17986d38604fSBaptiste Daroussin roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs, size_t len)
179961d06d6bSBaptiste Daroussin {
180061d06d6bSBaptiste Daroussin enum roff_tok t;
18017295610fSBaptiste Daroussin int e;
180261d06d6bSBaptiste Daroussin int pos; /* parse point */
180361d06d6bSBaptiste Daroussin int spos; /* saved parse point for messages */
180461d06d6bSBaptiste Daroussin int ppos; /* original offset in buf->buf */
180561d06d6bSBaptiste Daroussin int ctl; /* macro line (boolean) */
180661d06d6bSBaptiste Daroussin
180761d06d6bSBaptiste Daroussin ppos = pos = *offs;
180861d06d6bSBaptiste Daroussin
18096d38604fSBaptiste Daroussin if (len > 80 && r->tbl == NULL && r->eqn == NULL &&
18106d38604fSBaptiste Daroussin (r->man->flags & ROFF_NOFILL) == 0 &&
18116d38604fSBaptiste Daroussin strchr(" .\\", buf->buf[pos]) == NULL &&
18126d38604fSBaptiste Daroussin buf->buf[pos] != r->control &&
18136d38604fSBaptiste Daroussin strcspn(buf->buf, " ") < 80)
18146d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_TEXT_LONG, ln, (int)len - 1,
18156d38604fSBaptiste Daroussin "%.20s...", buf->buf + pos);
18166d38604fSBaptiste Daroussin
181761d06d6bSBaptiste Daroussin /* Handle in-line equation delimiters. */
181861d06d6bSBaptiste Daroussin
181961d06d6bSBaptiste Daroussin if (r->tbl == NULL &&
182061d06d6bSBaptiste Daroussin r->last_eqn != NULL && r->last_eqn->delim &&
182161d06d6bSBaptiste Daroussin (r->eqn == NULL || r->eqn_inline)) {
182261d06d6bSBaptiste Daroussin e = roff_eqndelim(r, buf, pos);
182361d06d6bSBaptiste Daroussin if (e == ROFF_REPARSE)
182461d06d6bSBaptiste Daroussin return e;
182561d06d6bSBaptiste Daroussin assert(e == ROFF_CONT);
182661d06d6bSBaptiste Daroussin }
182761d06d6bSBaptiste Daroussin
1828*c1c95addSBrooks Davis /* Handle comments and escape sequences. */
1829*c1c95addSBrooks Davis
1830*c1c95addSBrooks Davis e = roff_parse_comment(r, buf, ln, pos, r->escape);
1831*c1c95addSBrooks Davis if ((e & ROFF_MASK) == ROFF_IGN)
1832*c1c95addSBrooks Davis return e;
1833*c1c95addSBrooks Davis assert(e == ROFF_CONT);
183461d06d6bSBaptiste Daroussin
18357295610fSBaptiste Daroussin e = roff_expand(r, buf, ln, pos, r->escape);
18367295610fSBaptiste Daroussin if ((e & ROFF_MASK) == ROFF_IGN)
183761d06d6bSBaptiste Daroussin return e;
183861d06d6bSBaptiste Daroussin assert(e == ROFF_CONT);
183961d06d6bSBaptiste Daroussin
184061d06d6bSBaptiste Daroussin ctl = roff_getcontrol(r, buf->buf, &pos);
184161d06d6bSBaptiste Daroussin
184261d06d6bSBaptiste Daroussin /*
184361d06d6bSBaptiste Daroussin * First, if a scope is open and we're not a macro, pass the
184461d06d6bSBaptiste Daroussin * text through the macro's filter.
184561d06d6bSBaptiste Daroussin * Equations process all content themselves.
184661d06d6bSBaptiste Daroussin * Tables process almost all content themselves, but we want
184761d06d6bSBaptiste Daroussin * to warn about macros before passing it there.
184861d06d6bSBaptiste Daroussin */
184961d06d6bSBaptiste Daroussin
185061d06d6bSBaptiste Daroussin if (r->last != NULL && ! ctl) {
185161d06d6bSBaptiste Daroussin t = r->last->tok;
185261d06d6bSBaptiste Daroussin e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs);
18537295610fSBaptiste Daroussin if ((e & ROFF_MASK) == ROFF_IGN)
185461d06d6bSBaptiste Daroussin return e;
18557295610fSBaptiste Daroussin e &= ~ROFF_MASK;
18567295610fSBaptiste Daroussin } else
18577295610fSBaptiste Daroussin e = ROFF_IGN;
185861d06d6bSBaptiste Daroussin if (r->eqn != NULL && strncmp(buf->buf + ppos, ".EN", 3)) {
185961d06d6bSBaptiste Daroussin eqn_read(r->eqn, buf->buf + ppos);
18607295610fSBaptiste Daroussin return e;
186161d06d6bSBaptiste Daroussin }
186261d06d6bSBaptiste Daroussin if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) {
186361d06d6bSBaptiste Daroussin tbl_read(r->tbl, ln, buf->buf, ppos);
18647295610fSBaptiste Daroussin roff_addtbl(r->man, ln, r->tbl);
18657295610fSBaptiste Daroussin return e;
186661d06d6bSBaptiste Daroussin }
18676d38604fSBaptiste Daroussin if ( ! ctl) {
18686d38604fSBaptiste Daroussin r->options &= ~MPARSE_COMMENT;
18697295610fSBaptiste Daroussin return roff_parsetext(r, buf, pos, offs) | e;
18706d38604fSBaptiste Daroussin }
187161d06d6bSBaptiste Daroussin
187261d06d6bSBaptiste Daroussin /* Skip empty request lines. */
187361d06d6bSBaptiste Daroussin
187461d06d6bSBaptiste Daroussin if (buf->buf[pos] == '"') {
18757295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_COMMENT_BAD, ln, pos, NULL);
187661d06d6bSBaptiste Daroussin return ROFF_IGN;
187761d06d6bSBaptiste Daroussin } else if (buf->buf[pos] == '\0')
187861d06d6bSBaptiste Daroussin return ROFF_IGN;
187961d06d6bSBaptiste Daroussin
188061d06d6bSBaptiste Daroussin /*
188161d06d6bSBaptiste Daroussin * If a scope is open, go to the child handler for that macro,
188261d06d6bSBaptiste Daroussin * as it may want to preprocess before doing anything with it.
188361d06d6bSBaptiste Daroussin */
188461d06d6bSBaptiste Daroussin
188561d06d6bSBaptiste Daroussin if (r->last) {
188661d06d6bSBaptiste Daroussin t = r->last->tok;
188761d06d6bSBaptiste Daroussin return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs);
188861d06d6bSBaptiste Daroussin }
188961d06d6bSBaptiste Daroussin
18906d38604fSBaptiste Daroussin r->options &= ~MPARSE_COMMENT;
189161d06d6bSBaptiste Daroussin spos = pos;
189261d06d6bSBaptiste Daroussin t = roff_parse(r, buf->buf, &pos, ln, ppos);
1893*c1c95addSBrooks Davis return roff_req_or_macro(r, t, buf, ln, spos, pos, offs);
1894*c1c95addSBrooks Davis }
189561d06d6bSBaptiste Daroussin
1896*c1c95addSBrooks Davis /*
1897*c1c95addSBrooks Davis * Handle a new request or macro.
1898*c1c95addSBrooks Davis * May be called outside any scope or from inside a conditional scope.
1899*c1c95addSBrooks Davis */
1900*c1c95addSBrooks Davis static int
roff_req_or_macro(ROFF_ARGS)1901*c1c95addSBrooks Davis roff_req_or_macro(ROFF_ARGS) {
190261d06d6bSBaptiste Daroussin
1903*c1c95addSBrooks Davis /* For now, tables ignore most macros and some request. */
1904*c1c95addSBrooks Davis
1905*c1c95addSBrooks Davis if (r->tbl != NULL && (tok == TOKEN_NONE || tok == ROFF_TS ||
1906*c1c95addSBrooks Davis tok == ROFF_br || tok == ROFF_ce || tok == ROFF_rj ||
1907*c1c95addSBrooks Davis tok == ROFF_sp)) {
19087295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLMACRO,
1909*c1c95addSBrooks Davis ln, ppos, "%s", buf->buf + ppos);
1910*c1c95addSBrooks Davis if (tok != TOKEN_NONE)
191161d06d6bSBaptiste Daroussin return ROFF_IGN;
191261d06d6bSBaptiste Daroussin while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ')
191361d06d6bSBaptiste Daroussin pos++;
191461d06d6bSBaptiste Daroussin while (buf->buf[pos] == ' ')
191561d06d6bSBaptiste Daroussin pos++;
191661d06d6bSBaptiste Daroussin tbl_read(r->tbl, ln, buf->buf, pos);
19177295610fSBaptiste Daroussin roff_addtbl(r->man, ln, r->tbl);
191861d06d6bSBaptiste Daroussin return ROFF_IGN;
191961d06d6bSBaptiste Daroussin }
192061d06d6bSBaptiste Daroussin
192161d06d6bSBaptiste Daroussin /* For now, let high level macros abort .ce mode. */
192261d06d6bSBaptiste Daroussin
1923*c1c95addSBrooks Davis if (roffce_node != NULL &&
1924*c1c95addSBrooks Davis (tok == TOKEN_NONE || tok == ROFF_Dd || tok == ROFF_EQ ||
1925*c1c95addSBrooks Davis tok == ROFF_TH || tok == ROFF_TS)) {
192661d06d6bSBaptiste Daroussin r->man->last = roffce_node;
192761d06d6bSBaptiste Daroussin r->man->next = ROFF_NEXT_SIBLING;
192861d06d6bSBaptiste Daroussin roffce_lines = 0;
192961d06d6bSBaptiste Daroussin roffce_node = NULL;
193061d06d6bSBaptiste Daroussin }
193161d06d6bSBaptiste Daroussin
193261d06d6bSBaptiste Daroussin /*
193361d06d6bSBaptiste Daroussin * This is neither a roff request nor a user-defined macro.
193461d06d6bSBaptiste Daroussin * Let the standard macro set parsers handle it.
193561d06d6bSBaptiste Daroussin */
193661d06d6bSBaptiste Daroussin
1937*c1c95addSBrooks Davis if (tok == TOKEN_NONE)
193861d06d6bSBaptiste Daroussin return ROFF_CONT;
193961d06d6bSBaptiste Daroussin
1940*c1c95addSBrooks Davis /* Execute a roff request or a user-defined macro. */
194161d06d6bSBaptiste Daroussin
1942*c1c95addSBrooks Davis return (*roffs[tok].proc)(r, tok, buf, ln, ppos, pos, offs);
194361d06d6bSBaptiste Daroussin }
194461d06d6bSBaptiste Daroussin
19457295610fSBaptiste Daroussin /*
19467295610fSBaptiste Daroussin * Internal interface function to tell the roff parser that execution
19477295610fSBaptiste Daroussin * of the current macro ended. This is required because macro
19487295610fSBaptiste Daroussin * definitions usually do not end with a .return request.
19497295610fSBaptiste Daroussin */
19507295610fSBaptiste Daroussin void
roff_userret(struct roff * r)19517295610fSBaptiste Daroussin roff_userret(struct roff *r)
19527295610fSBaptiste Daroussin {
19537295610fSBaptiste Daroussin struct mctx *ctx;
19547295610fSBaptiste Daroussin int i;
19557295610fSBaptiste Daroussin
19567295610fSBaptiste Daroussin assert(r->mstackpos >= 0);
19577295610fSBaptiste Daroussin ctx = r->mstack + r->mstackpos;
19587295610fSBaptiste Daroussin for (i = 0; i < ctx->argc; i++)
19597295610fSBaptiste Daroussin free(ctx->argv[i]);
19607295610fSBaptiste Daroussin ctx->argc = 0;
19617295610fSBaptiste Daroussin r->mstackpos--;
19627295610fSBaptiste Daroussin }
19637295610fSBaptiste Daroussin
196461d06d6bSBaptiste Daroussin void
roff_endparse(struct roff * r)196561d06d6bSBaptiste Daroussin roff_endparse(struct roff *r)
196661d06d6bSBaptiste Daroussin {
196761d06d6bSBaptiste Daroussin if (r->last != NULL)
19687295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOEND, r->last->line,
19697295610fSBaptiste Daroussin r->last->col, "%s", roff_name[r->last->tok]);
197061d06d6bSBaptiste Daroussin
197161d06d6bSBaptiste Daroussin if (r->eqn != NULL) {
19727295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOEND,
197361d06d6bSBaptiste Daroussin r->eqn->node->line, r->eqn->node->pos, "EQ");
197461d06d6bSBaptiste Daroussin eqn_parse(r->eqn);
197561d06d6bSBaptiste Daroussin r->eqn = NULL;
197661d06d6bSBaptiste Daroussin }
197761d06d6bSBaptiste Daroussin
197861d06d6bSBaptiste Daroussin if (r->tbl != NULL) {
19797295610fSBaptiste Daroussin tbl_end(r->tbl, 1);
198061d06d6bSBaptiste Daroussin r->tbl = NULL;
198161d06d6bSBaptiste Daroussin }
198261d06d6bSBaptiste Daroussin }
198361d06d6bSBaptiste Daroussin
198461d06d6bSBaptiste Daroussin /*
1985*c1c95addSBrooks Davis * Parse the request or macro name at buf[*pos].
1986*c1c95addSBrooks Davis * Return ROFF_RENAMED, ROFF_USERDEF, or a ROFF_* token value.
1987*c1c95addSBrooks Davis * For empty, undefined, mdoc(7), and man(7) macros, return TOKEN_NONE.
1988*c1c95addSBrooks Davis * As a side effect, set r->current_string to the definition or to NULL.
198961d06d6bSBaptiste Daroussin */
199061d06d6bSBaptiste Daroussin static enum roff_tok
roff_parse(struct roff * r,char * buf,int * pos,int ln,int ppos)199161d06d6bSBaptiste Daroussin roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
199261d06d6bSBaptiste Daroussin {
199361d06d6bSBaptiste Daroussin char *cp;
199461d06d6bSBaptiste Daroussin const char *mac;
199561d06d6bSBaptiste Daroussin size_t maclen;
199661d06d6bSBaptiste Daroussin int deftype;
199761d06d6bSBaptiste Daroussin enum roff_tok t;
199861d06d6bSBaptiste Daroussin
199961d06d6bSBaptiste Daroussin cp = buf + *pos;
200061d06d6bSBaptiste Daroussin
200161d06d6bSBaptiste Daroussin if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
200261d06d6bSBaptiste Daroussin return TOKEN_NONE;
200361d06d6bSBaptiste Daroussin
200461d06d6bSBaptiste Daroussin mac = cp;
200561d06d6bSBaptiste Daroussin maclen = roff_getname(r, &cp, ln, ppos);
200661d06d6bSBaptiste Daroussin
200761d06d6bSBaptiste Daroussin deftype = ROFFDEF_USER | ROFFDEF_REN;
200861d06d6bSBaptiste Daroussin r->current_string = roff_getstrn(r, mac, maclen, &deftype);
200961d06d6bSBaptiste Daroussin switch (deftype) {
201061d06d6bSBaptiste Daroussin case ROFFDEF_USER:
201161d06d6bSBaptiste Daroussin t = ROFF_USERDEF;
201261d06d6bSBaptiste Daroussin break;
201361d06d6bSBaptiste Daroussin case ROFFDEF_REN:
201461d06d6bSBaptiste Daroussin t = ROFF_RENAMED;
201561d06d6bSBaptiste Daroussin break;
201661d06d6bSBaptiste Daroussin default:
201761d06d6bSBaptiste Daroussin t = roffhash_find(r->reqtab, mac, maclen);
201861d06d6bSBaptiste Daroussin break;
201961d06d6bSBaptiste Daroussin }
202061d06d6bSBaptiste Daroussin if (t != TOKEN_NONE)
202161d06d6bSBaptiste Daroussin *pos = cp - buf;
202261d06d6bSBaptiste Daroussin else if (deftype == ROFFDEF_UNDEF) {
202361d06d6bSBaptiste Daroussin /* Using an undefined macro defines it to be empty. */
202461d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, mac, maclen, "", 0, 0);
202561d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, mac, maclen, NULL, 0, 0);
202661d06d6bSBaptiste Daroussin }
202761d06d6bSBaptiste Daroussin return t;
202861d06d6bSBaptiste Daroussin }
202961d06d6bSBaptiste Daroussin
203061d06d6bSBaptiste Daroussin /* --- handling of request blocks ----------------------------------------- */
203161d06d6bSBaptiste Daroussin
20326d38604fSBaptiste Daroussin /*
20336d38604fSBaptiste Daroussin * Close a macro definition block or an "ignore" block.
20346d38604fSBaptiste Daroussin */
20357295610fSBaptiste Daroussin static int
roff_cblock(ROFF_ARGS)203661d06d6bSBaptiste Daroussin roff_cblock(ROFF_ARGS)
203761d06d6bSBaptiste Daroussin {
20386d38604fSBaptiste Daroussin int rr;
203961d06d6bSBaptiste Daroussin
204061d06d6bSBaptiste Daroussin if (r->last == NULL) {
20417295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "..");
204261d06d6bSBaptiste Daroussin return ROFF_IGN;
204361d06d6bSBaptiste Daroussin }
204461d06d6bSBaptiste Daroussin
204561d06d6bSBaptiste Daroussin switch (r->last->tok) {
204661d06d6bSBaptiste Daroussin case ROFF_am:
204761d06d6bSBaptiste Daroussin case ROFF_ami:
204861d06d6bSBaptiste Daroussin case ROFF_de:
204961d06d6bSBaptiste Daroussin case ROFF_dei:
205061d06d6bSBaptiste Daroussin case ROFF_ig:
205161d06d6bSBaptiste Daroussin break;
20526d38604fSBaptiste Daroussin case ROFF_am1:
20536d38604fSBaptiste Daroussin case ROFF_de1:
20546d38604fSBaptiste Daroussin /* Remapped in roff_block(). */
20556d38604fSBaptiste Daroussin abort();
205661d06d6bSBaptiste Daroussin default:
20577295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "..");
205861d06d6bSBaptiste Daroussin return ROFF_IGN;
205961d06d6bSBaptiste Daroussin }
206061d06d6bSBaptiste Daroussin
20616d38604fSBaptiste Daroussin roffnode_pop(r);
20626d38604fSBaptiste Daroussin roffnode_cleanscope(r);
20636d38604fSBaptiste Daroussin
20646d38604fSBaptiste Daroussin /*
20656d38604fSBaptiste Daroussin * If a conditional block with braces is still open,
20666d38604fSBaptiste Daroussin * check for "\}" block end markers.
20676d38604fSBaptiste Daroussin */
20686d38604fSBaptiste Daroussin
20696d38604fSBaptiste Daroussin if (r->last != NULL && r->last->endspan < 0) {
20706d38604fSBaptiste Daroussin rr = 1; /* If arguments follow "\}", warn about them. */
20716d38604fSBaptiste Daroussin roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
20726d38604fSBaptiste Daroussin }
20736d38604fSBaptiste Daroussin
207461d06d6bSBaptiste Daroussin if (buf->buf[pos] != '\0')
20757295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
207661d06d6bSBaptiste Daroussin ".. %s", buf->buf + pos);
207761d06d6bSBaptiste Daroussin
207861d06d6bSBaptiste Daroussin return ROFF_IGN;
207961d06d6bSBaptiste Daroussin }
208061d06d6bSBaptiste Daroussin
208145a5aec3SBaptiste Daroussin /*
208245a5aec3SBaptiste Daroussin * Pop all nodes ending at the end of the current input line.
208345a5aec3SBaptiste Daroussin * Return the number of loops ended.
208445a5aec3SBaptiste Daroussin */
20857295610fSBaptiste Daroussin static int
roffnode_cleanscope(struct roff * r)208661d06d6bSBaptiste Daroussin roffnode_cleanscope(struct roff *r)
208761d06d6bSBaptiste Daroussin {
20887295610fSBaptiste Daroussin int inloop;
208961d06d6bSBaptiste Daroussin
20907295610fSBaptiste Daroussin inloop = 0;
20916d38604fSBaptiste Daroussin while (r->last != NULL && r->last->endspan > 0) {
209261d06d6bSBaptiste Daroussin if (--r->last->endspan != 0)
209361d06d6bSBaptiste Daroussin break;
20947295610fSBaptiste Daroussin inloop += roffnode_pop(r);
209561d06d6bSBaptiste Daroussin }
20967295610fSBaptiste Daroussin return inloop;
209761d06d6bSBaptiste Daroussin }
209861d06d6bSBaptiste Daroussin
209945a5aec3SBaptiste Daroussin /*
21006d38604fSBaptiste Daroussin * Handle the closing "\}" of a conditional block.
210145a5aec3SBaptiste Daroussin * Apart from generating warnings, this only pops nodes.
210245a5aec3SBaptiste Daroussin * Return the number of loops ended.
210345a5aec3SBaptiste Daroussin */
21047295610fSBaptiste Daroussin static int
roff_ccond(struct roff * r,int ln,int ppos)210561d06d6bSBaptiste Daroussin roff_ccond(struct roff *r, int ln, int ppos)
210661d06d6bSBaptiste Daroussin {
210761d06d6bSBaptiste Daroussin if (NULL == r->last) {
21087295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
21097295610fSBaptiste Daroussin return 0;
211061d06d6bSBaptiste Daroussin }
211161d06d6bSBaptiste Daroussin
211261d06d6bSBaptiste Daroussin switch (r->last->tok) {
211361d06d6bSBaptiste Daroussin case ROFF_el:
211461d06d6bSBaptiste Daroussin case ROFF_ie:
211561d06d6bSBaptiste Daroussin case ROFF_if:
21167295610fSBaptiste Daroussin case ROFF_while:
211761d06d6bSBaptiste Daroussin break;
211861d06d6bSBaptiste Daroussin default:
21197295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
21207295610fSBaptiste Daroussin return 0;
212161d06d6bSBaptiste Daroussin }
212261d06d6bSBaptiste Daroussin
212361d06d6bSBaptiste Daroussin if (r->last->endspan > -1) {
21247295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}");
21257295610fSBaptiste Daroussin return 0;
212661d06d6bSBaptiste Daroussin }
212761d06d6bSBaptiste Daroussin
21287295610fSBaptiste Daroussin return roffnode_pop(r) + roffnode_cleanscope(r);
212961d06d6bSBaptiste Daroussin }
213061d06d6bSBaptiste Daroussin
21317295610fSBaptiste Daroussin static int
roff_block(ROFF_ARGS)213261d06d6bSBaptiste Daroussin roff_block(ROFF_ARGS)
213361d06d6bSBaptiste Daroussin {
213461d06d6bSBaptiste Daroussin const char *name, *value;
213561d06d6bSBaptiste Daroussin char *call, *cp, *iname, *rname;
213661d06d6bSBaptiste Daroussin size_t csz, namesz, rsz;
213761d06d6bSBaptiste Daroussin int deftype;
213861d06d6bSBaptiste Daroussin
213961d06d6bSBaptiste Daroussin /* Ignore groff compatibility mode for now. */
214061d06d6bSBaptiste Daroussin
214161d06d6bSBaptiste Daroussin if (tok == ROFF_de1)
214261d06d6bSBaptiste Daroussin tok = ROFF_de;
214361d06d6bSBaptiste Daroussin else if (tok == ROFF_dei1)
214461d06d6bSBaptiste Daroussin tok = ROFF_dei;
214561d06d6bSBaptiste Daroussin else if (tok == ROFF_am1)
214661d06d6bSBaptiste Daroussin tok = ROFF_am;
214761d06d6bSBaptiste Daroussin else if (tok == ROFF_ami1)
214861d06d6bSBaptiste Daroussin tok = ROFF_ami;
214961d06d6bSBaptiste Daroussin
215061d06d6bSBaptiste Daroussin /* Parse the macro name argument. */
215161d06d6bSBaptiste Daroussin
215261d06d6bSBaptiste Daroussin cp = buf->buf + pos;
215361d06d6bSBaptiste Daroussin if (tok == ROFF_ig) {
215461d06d6bSBaptiste Daroussin iname = NULL;
215561d06d6bSBaptiste Daroussin namesz = 0;
215661d06d6bSBaptiste Daroussin } else {
215761d06d6bSBaptiste Daroussin iname = cp;
215861d06d6bSBaptiste Daroussin namesz = roff_getname(r, &cp, ln, ppos);
215961d06d6bSBaptiste Daroussin iname[namesz] = '\0';
216061d06d6bSBaptiste Daroussin }
216161d06d6bSBaptiste Daroussin
216261d06d6bSBaptiste Daroussin /* Resolve the macro name argument if it is indirect. */
216361d06d6bSBaptiste Daroussin
216461d06d6bSBaptiste Daroussin if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
216561d06d6bSBaptiste Daroussin deftype = ROFFDEF_USER;
216661d06d6bSBaptiste Daroussin name = roff_getstrn(r, iname, namesz, &deftype);
216761d06d6bSBaptiste Daroussin if (name == NULL) {
21687295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_STR_UNDEF,
21697295610fSBaptiste Daroussin ln, (int)(iname - buf->buf),
217061d06d6bSBaptiste Daroussin "%.*s", (int)namesz, iname);
217161d06d6bSBaptiste Daroussin namesz = 0;
217261d06d6bSBaptiste Daroussin } else
217361d06d6bSBaptiste Daroussin namesz = strlen(name);
217461d06d6bSBaptiste Daroussin } else
217561d06d6bSBaptiste Daroussin name = iname;
217661d06d6bSBaptiste Daroussin
217761d06d6bSBaptiste Daroussin if (namesz == 0 && tok != ROFF_ig) {
21787295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_EMPTY,
21797295610fSBaptiste Daroussin ln, ppos, "%s", roff_name[tok]);
218061d06d6bSBaptiste Daroussin return ROFF_IGN;
218161d06d6bSBaptiste Daroussin }
218261d06d6bSBaptiste Daroussin
218361d06d6bSBaptiste Daroussin roffnode_push(r, tok, name, ln, ppos);
218461d06d6bSBaptiste Daroussin
218561d06d6bSBaptiste Daroussin /*
218661d06d6bSBaptiste Daroussin * At the beginning of a `de' macro, clear the existing string
218761d06d6bSBaptiste Daroussin * with the same name, if there is one. New content will be
218861d06d6bSBaptiste Daroussin * appended from roff_block_text() in multiline mode.
218961d06d6bSBaptiste Daroussin */
219061d06d6bSBaptiste Daroussin
219161d06d6bSBaptiste Daroussin if (tok == ROFF_de || tok == ROFF_dei) {
219261d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
219361d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
219461d06d6bSBaptiste Daroussin } else if (tok == ROFF_am || tok == ROFF_ami) {
219561d06d6bSBaptiste Daroussin deftype = ROFFDEF_ANY;
219661d06d6bSBaptiste Daroussin value = roff_getstrn(r, iname, namesz, &deftype);
219761d06d6bSBaptiste Daroussin switch (deftype) { /* Before appending, ... */
219861d06d6bSBaptiste Daroussin case ROFFDEF_PRE: /* copy predefined to user-defined. */
219961d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, name, namesz,
220061d06d6bSBaptiste Daroussin value, strlen(value), 0);
220161d06d6bSBaptiste Daroussin break;
220261d06d6bSBaptiste Daroussin case ROFFDEF_REN: /* call original standard macro. */
220361d06d6bSBaptiste Daroussin csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
220461d06d6bSBaptiste Daroussin (int)strlen(value), value);
220561d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
220661d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
220761d06d6bSBaptiste Daroussin free(call);
220861d06d6bSBaptiste Daroussin break;
220961d06d6bSBaptiste Daroussin case ROFFDEF_STD: /* rename and call standard macro. */
221061d06d6bSBaptiste Daroussin rsz = mandoc_asprintf(&rname, "__%s_renamed", name);
221161d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, rname, rsz, name, namesz, 0);
221261d06d6bSBaptiste Daroussin csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n",
221361d06d6bSBaptiste Daroussin (int)rsz, rname);
221461d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, name, namesz, call, csz, 0);
221561d06d6bSBaptiste Daroussin free(call);
221661d06d6bSBaptiste Daroussin free(rname);
221761d06d6bSBaptiste Daroussin break;
221861d06d6bSBaptiste Daroussin default:
221961d06d6bSBaptiste Daroussin break;
222061d06d6bSBaptiste Daroussin }
222161d06d6bSBaptiste Daroussin }
222261d06d6bSBaptiste Daroussin
222361d06d6bSBaptiste Daroussin if (*cp == '\0')
222461d06d6bSBaptiste Daroussin return ROFF_IGN;
222561d06d6bSBaptiste Daroussin
222661d06d6bSBaptiste Daroussin /* Get the custom end marker. */
222761d06d6bSBaptiste Daroussin
222861d06d6bSBaptiste Daroussin iname = cp;
222961d06d6bSBaptiste Daroussin namesz = roff_getname(r, &cp, ln, ppos);
223061d06d6bSBaptiste Daroussin
223161d06d6bSBaptiste Daroussin /* Resolve the end marker if it is indirect. */
223261d06d6bSBaptiste Daroussin
223361d06d6bSBaptiste Daroussin if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
223461d06d6bSBaptiste Daroussin deftype = ROFFDEF_USER;
223561d06d6bSBaptiste Daroussin name = roff_getstrn(r, iname, namesz, &deftype);
223661d06d6bSBaptiste Daroussin if (name == NULL) {
22377295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_STR_UNDEF,
22387295610fSBaptiste Daroussin ln, (int)(iname - buf->buf),
223961d06d6bSBaptiste Daroussin "%.*s", (int)namesz, iname);
224061d06d6bSBaptiste Daroussin namesz = 0;
224161d06d6bSBaptiste Daroussin } else
224261d06d6bSBaptiste Daroussin namesz = strlen(name);
224361d06d6bSBaptiste Daroussin } else
224461d06d6bSBaptiste Daroussin name = iname;
224561d06d6bSBaptiste Daroussin
224661d06d6bSBaptiste Daroussin if (namesz)
224761d06d6bSBaptiste Daroussin r->last->end = mandoc_strndup(name, namesz);
224861d06d6bSBaptiste Daroussin
224961d06d6bSBaptiste Daroussin if (*cp != '\0')
22507295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_EXCESS,
225161d06d6bSBaptiste Daroussin ln, pos, ".%s ... %s", roff_name[tok], cp);
225261d06d6bSBaptiste Daroussin
225361d06d6bSBaptiste Daroussin return ROFF_IGN;
225461d06d6bSBaptiste Daroussin }
225561d06d6bSBaptiste Daroussin
22567295610fSBaptiste Daroussin static int
roff_block_sub(ROFF_ARGS)225761d06d6bSBaptiste Daroussin roff_block_sub(ROFF_ARGS)
225861d06d6bSBaptiste Daroussin {
225961d06d6bSBaptiste Daroussin enum roff_tok t;
226061d06d6bSBaptiste Daroussin int i, j;
226161d06d6bSBaptiste Daroussin
226261d06d6bSBaptiste Daroussin /*
2263*c1c95addSBrooks Davis * If a custom end marker is a user-defined or predefined macro
2264*c1c95addSBrooks Davis * or a request, interpret it.
226561d06d6bSBaptiste Daroussin */
226661d06d6bSBaptiste Daroussin
226761d06d6bSBaptiste Daroussin if (r->last->end) {
226861d06d6bSBaptiste Daroussin for (i = pos, j = 0; r->last->end[j]; j++, i++)
226961d06d6bSBaptiste Daroussin if (buf->buf[i] != r->last->end[j])
227061d06d6bSBaptiste Daroussin break;
227161d06d6bSBaptiste Daroussin
227261d06d6bSBaptiste Daroussin if (r->last->end[j] == '\0' &&
227361d06d6bSBaptiste Daroussin (buf->buf[i] == '\0' ||
227461d06d6bSBaptiste Daroussin buf->buf[i] == ' ' ||
227561d06d6bSBaptiste Daroussin buf->buf[i] == '\t')) {
227661d06d6bSBaptiste Daroussin roffnode_pop(r);
227761d06d6bSBaptiste Daroussin roffnode_cleanscope(r);
227861d06d6bSBaptiste Daroussin
227961d06d6bSBaptiste Daroussin while (buf->buf[i] == ' ' || buf->buf[i] == '\t')
228061d06d6bSBaptiste Daroussin i++;
228161d06d6bSBaptiste Daroussin
228261d06d6bSBaptiste Daroussin pos = i;
228361d06d6bSBaptiste Daroussin if (roff_parse(r, buf->buf, &pos, ln, ppos) !=
228461d06d6bSBaptiste Daroussin TOKEN_NONE)
228561d06d6bSBaptiste Daroussin return ROFF_RERUN;
228661d06d6bSBaptiste Daroussin return ROFF_IGN;
228761d06d6bSBaptiste Daroussin }
228861d06d6bSBaptiste Daroussin }
228961d06d6bSBaptiste Daroussin
2290*c1c95addSBrooks Davis /* Handle the standard end marker. */
229161d06d6bSBaptiste Daroussin
229261d06d6bSBaptiste Daroussin t = roff_parse(r, buf->buf, &pos, ln, ppos);
2293*c1c95addSBrooks Davis if (t == ROFF_cblock)
2294*c1c95addSBrooks Davis return roff_cblock(r, t, buf, ln, ppos, pos, offs);
229561d06d6bSBaptiste Daroussin
2296*c1c95addSBrooks Davis /* Not an end marker, so append the line to the block. */
2297*c1c95addSBrooks Davis
229861d06d6bSBaptiste Daroussin if (tok != ROFF_ig)
229961d06d6bSBaptiste Daroussin roff_setstr(r, r->last->name, buf->buf + ppos, 2);
230061d06d6bSBaptiste Daroussin return ROFF_IGN;
230161d06d6bSBaptiste Daroussin }
230261d06d6bSBaptiste Daroussin
23037295610fSBaptiste Daroussin static int
roff_block_text(ROFF_ARGS)230461d06d6bSBaptiste Daroussin roff_block_text(ROFF_ARGS)
230561d06d6bSBaptiste Daroussin {
230661d06d6bSBaptiste Daroussin
230761d06d6bSBaptiste Daroussin if (tok != ROFF_ig)
230861d06d6bSBaptiste Daroussin roff_setstr(r, r->last->name, buf->buf + pos, 2);
230961d06d6bSBaptiste Daroussin
231061d06d6bSBaptiste Daroussin return ROFF_IGN;
231161d06d6bSBaptiste Daroussin }
231261d06d6bSBaptiste Daroussin
23136d38604fSBaptiste Daroussin /*
23146d38604fSBaptiste Daroussin * Check for a closing "\}" and handle it.
23156d38604fSBaptiste Daroussin * In this function, the final "int *offs" argument is used for
23166d38604fSBaptiste Daroussin * different purposes than elsewhere:
23176d38604fSBaptiste Daroussin * Input: *offs == 0: caller wants to discard arguments following \}
23186d38604fSBaptiste Daroussin * *offs == 1: caller wants to preserve text following \}
23196d38604fSBaptiste Daroussin * Output: *offs = 0: tell caller to discard input line
23206d38604fSBaptiste Daroussin * *offs = 1: tell caller to use input line
23216d38604fSBaptiste Daroussin */
23227295610fSBaptiste Daroussin static int
roff_cond_checkend(ROFF_ARGS)23236d38604fSBaptiste Daroussin roff_cond_checkend(ROFF_ARGS)
232461d06d6bSBaptiste Daroussin {
232561d06d6bSBaptiste Daroussin char *ep;
23267295610fSBaptiste Daroussin int endloop, irc, rr;
232761d06d6bSBaptiste Daroussin
23287295610fSBaptiste Daroussin irc = ROFF_IGN;
232961d06d6bSBaptiste Daroussin rr = r->last->rule;
23307295610fSBaptiste Daroussin endloop = tok != ROFF_while ? ROFF_IGN :
23317295610fSBaptiste Daroussin rr ? ROFF_LOOPCONT : ROFF_LOOPEXIT;
23327295610fSBaptiste Daroussin if (roffnode_cleanscope(r))
23337295610fSBaptiste Daroussin irc |= endloop;
233461d06d6bSBaptiste Daroussin
233561d06d6bSBaptiste Daroussin /*
23366d38604fSBaptiste Daroussin * If "\}" occurs on a macro line without a preceding macro or
23376d38604fSBaptiste Daroussin * a text line contains nothing else, drop the line completely.
233861d06d6bSBaptiste Daroussin */
233961d06d6bSBaptiste Daroussin
234061d06d6bSBaptiste Daroussin ep = buf->buf + pos;
23416d38604fSBaptiste Daroussin if (ep[0] == '\\' && ep[1] == '}' && (ep[2] == '\0' || *offs == 0))
234261d06d6bSBaptiste Daroussin rr = 0;
234361d06d6bSBaptiste Daroussin
23447295610fSBaptiste Daroussin /*
23456d38604fSBaptiste Daroussin * The closing delimiter "\}" rewinds the conditional scope
23467295610fSBaptiste Daroussin * but is otherwise ignored when interpreting the line.
23477295610fSBaptiste Daroussin */
234861d06d6bSBaptiste Daroussin
234961d06d6bSBaptiste Daroussin while ((ep = strchr(ep, '\\')) != NULL) {
235061d06d6bSBaptiste Daroussin switch (ep[1]) {
235161d06d6bSBaptiste Daroussin case '}':
23526d38604fSBaptiste Daroussin if (ep[2] == '\0')
23536d38604fSBaptiste Daroussin ep[0] = '\0';
23546d38604fSBaptiste Daroussin else if (rr)
23556d38604fSBaptiste Daroussin ep[1] = '&';
23566d38604fSBaptiste Daroussin else
235761d06d6bSBaptiste Daroussin memmove(ep, ep + 2, strlen(ep + 2) + 1);
23587295610fSBaptiste Daroussin if (roff_ccond(r, ln, ep - buf->buf))
23597295610fSBaptiste Daroussin irc |= endloop;
236061d06d6bSBaptiste Daroussin break;
236161d06d6bSBaptiste Daroussin case '\0':
236261d06d6bSBaptiste Daroussin ++ep;
236361d06d6bSBaptiste Daroussin break;
236461d06d6bSBaptiste Daroussin default:
236561d06d6bSBaptiste Daroussin ep += 2;
236661d06d6bSBaptiste Daroussin break;
236761d06d6bSBaptiste Daroussin }
236861d06d6bSBaptiste Daroussin }
23696d38604fSBaptiste Daroussin *offs = rr;
23706d38604fSBaptiste Daroussin return irc;
23716d38604fSBaptiste Daroussin }
23726d38604fSBaptiste Daroussin
23736d38604fSBaptiste Daroussin /*
23746d38604fSBaptiste Daroussin * Parse and process a request or macro line in conditional scope.
23756d38604fSBaptiste Daroussin */
23766d38604fSBaptiste Daroussin static int
roff_cond_sub(ROFF_ARGS)23776d38604fSBaptiste Daroussin roff_cond_sub(ROFF_ARGS)
23786d38604fSBaptiste Daroussin {
23796d38604fSBaptiste Daroussin struct roffnode *bl;
2380*c1c95addSBrooks Davis int irc, rr, spos;
23816d38604fSBaptiste Daroussin enum roff_tok t;
23826d38604fSBaptiste Daroussin
23836d38604fSBaptiste Daroussin rr = 0; /* If arguments follow "\}", skip them. */
23846d38604fSBaptiste Daroussin irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
2385*c1c95addSBrooks Davis spos = pos;
23866d38604fSBaptiste Daroussin t = roff_parse(r, buf->buf, &pos, ln, ppos);
23876d38604fSBaptiste Daroussin
238861d06d6bSBaptiste Daroussin /*
2389*c1c95addSBrooks Davis * Handle requests and macros if the conditional evaluated
2390*c1c95addSBrooks Davis * to true or if they are structurally required.
2391*c1c95addSBrooks Davis * The .break request is always handled specially.
239261d06d6bSBaptiste Daroussin */
239361d06d6bSBaptiste Daroussin
239445a5aec3SBaptiste Daroussin if (t == ROFF_break) {
239545a5aec3SBaptiste Daroussin if (irc & ROFF_LOOPMASK)
239645a5aec3SBaptiste Daroussin irc = ROFF_IGN | ROFF_LOOPEXIT;
239745a5aec3SBaptiste Daroussin else if (rr) {
239845a5aec3SBaptiste Daroussin for (bl = r->last; bl != NULL; bl = bl->parent) {
239945a5aec3SBaptiste Daroussin bl->rule = 0;
240045a5aec3SBaptiste Daroussin if (bl->tok == ROFF_while)
240145a5aec3SBaptiste Daroussin break;
240245a5aec3SBaptiste Daroussin }
240345a5aec3SBaptiste Daroussin }
2404*c1c95addSBrooks Davis } else if (rr || (t < TOKEN_NONE && roffs[t].flags & ROFFMAC_STRUCT)) {
2405*c1c95addSBrooks Davis irc |= roff_req_or_macro(r, t, buf, ln, spos, pos, offs);
2406*c1c95addSBrooks Davis if (irc & ROFF_WHILE)
2407*c1c95addSBrooks Davis irc &= ~(ROFF_LOOPCONT | ROFF_LOOPEXIT);
2408*c1c95addSBrooks Davis }
24097295610fSBaptiste Daroussin return irc;
241061d06d6bSBaptiste Daroussin }
241161d06d6bSBaptiste Daroussin
24126d38604fSBaptiste Daroussin /*
24136d38604fSBaptiste Daroussin * Parse and process a text line in conditional scope.
24146d38604fSBaptiste Daroussin */
24157295610fSBaptiste Daroussin static int
roff_cond_text(ROFF_ARGS)241661d06d6bSBaptiste Daroussin roff_cond_text(ROFF_ARGS)
241761d06d6bSBaptiste Daroussin {
24186d38604fSBaptiste Daroussin int irc, rr;
241961d06d6bSBaptiste Daroussin
24206d38604fSBaptiste Daroussin rr = 1; /* If arguments follow "\}", preserve them. */
24216d38604fSBaptiste Daroussin irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr);
24227295610fSBaptiste Daroussin if (rr)
24237295610fSBaptiste Daroussin irc |= ROFF_CONT;
24247295610fSBaptiste Daroussin return irc;
242561d06d6bSBaptiste Daroussin }
242661d06d6bSBaptiste Daroussin
242761d06d6bSBaptiste Daroussin /* --- handling of numeric and conditional expressions -------------------- */
242861d06d6bSBaptiste Daroussin
242961d06d6bSBaptiste Daroussin /*
243061d06d6bSBaptiste Daroussin * Parse a single signed integer number. Stop at the first non-digit.
243161d06d6bSBaptiste Daroussin * If there is at least one digit, return success and advance the
243261d06d6bSBaptiste Daroussin * parse point, else return failure and let the parse point unchanged.
243361d06d6bSBaptiste Daroussin * Ignore overflows, treat them just like the C language.
243461d06d6bSBaptiste Daroussin */
243561d06d6bSBaptiste Daroussin static int
roff_getnum(const char * v,int * pos,int * res,int flags)243661d06d6bSBaptiste Daroussin roff_getnum(const char *v, int *pos, int *res, int flags)
243761d06d6bSBaptiste Daroussin {
243861d06d6bSBaptiste Daroussin int myres, scaled, n, p;
243961d06d6bSBaptiste Daroussin
244061d06d6bSBaptiste Daroussin if (NULL == res)
244161d06d6bSBaptiste Daroussin res = &myres;
244261d06d6bSBaptiste Daroussin
244361d06d6bSBaptiste Daroussin p = *pos;
244461d06d6bSBaptiste Daroussin n = v[p] == '-';
244561d06d6bSBaptiste Daroussin if (n || v[p] == '+')
244661d06d6bSBaptiste Daroussin p++;
244761d06d6bSBaptiste Daroussin
244861d06d6bSBaptiste Daroussin if (flags & ROFFNUM_WHITE)
244961d06d6bSBaptiste Daroussin while (isspace((unsigned char)v[p]))
245061d06d6bSBaptiste Daroussin p++;
245161d06d6bSBaptiste Daroussin
245261d06d6bSBaptiste Daroussin for (*res = 0; isdigit((unsigned char)v[p]); p++)
245361d06d6bSBaptiste Daroussin *res = 10 * *res + v[p] - '0';
245461d06d6bSBaptiste Daroussin if (p == *pos + n)
245561d06d6bSBaptiste Daroussin return 0;
245661d06d6bSBaptiste Daroussin
245761d06d6bSBaptiste Daroussin if (n)
245861d06d6bSBaptiste Daroussin *res = -*res;
245961d06d6bSBaptiste Daroussin
246061d06d6bSBaptiste Daroussin /* Each number may be followed by one optional scaling unit. */
246161d06d6bSBaptiste Daroussin
246261d06d6bSBaptiste Daroussin switch (v[p]) {
246361d06d6bSBaptiste Daroussin case 'f':
246461d06d6bSBaptiste Daroussin scaled = *res * 65536;
246561d06d6bSBaptiste Daroussin break;
246661d06d6bSBaptiste Daroussin case 'i':
246761d06d6bSBaptiste Daroussin scaled = *res * 240;
246861d06d6bSBaptiste Daroussin break;
246961d06d6bSBaptiste Daroussin case 'c':
247061d06d6bSBaptiste Daroussin scaled = *res * 240 / 2.54;
247161d06d6bSBaptiste Daroussin break;
247261d06d6bSBaptiste Daroussin case 'v':
247361d06d6bSBaptiste Daroussin case 'P':
247461d06d6bSBaptiste Daroussin scaled = *res * 40;
247561d06d6bSBaptiste Daroussin break;
247661d06d6bSBaptiste Daroussin case 'm':
247761d06d6bSBaptiste Daroussin case 'n':
247861d06d6bSBaptiste Daroussin scaled = *res * 24;
247961d06d6bSBaptiste Daroussin break;
248061d06d6bSBaptiste Daroussin case 'p':
248161d06d6bSBaptiste Daroussin scaled = *res * 10 / 3;
248261d06d6bSBaptiste Daroussin break;
248361d06d6bSBaptiste Daroussin case 'u':
248461d06d6bSBaptiste Daroussin scaled = *res;
248561d06d6bSBaptiste Daroussin break;
248661d06d6bSBaptiste Daroussin case 'M':
248761d06d6bSBaptiste Daroussin scaled = *res * 6 / 25;
248861d06d6bSBaptiste Daroussin break;
248961d06d6bSBaptiste Daroussin default:
249061d06d6bSBaptiste Daroussin scaled = *res;
249161d06d6bSBaptiste Daroussin p--;
249261d06d6bSBaptiste Daroussin break;
249361d06d6bSBaptiste Daroussin }
249461d06d6bSBaptiste Daroussin if (flags & ROFFNUM_SCALE)
249561d06d6bSBaptiste Daroussin *res = scaled;
249661d06d6bSBaptiste Daroussin
249761d06d6bSBaptiste Daroussin *pos = p + 1;
249861d06d6bSBaptiste Daroussin return 1;
249961d06d6bSBaptiste Daroussin }
250061d06d6bSBaptiste Daroussin
250161d06d6bSBaptiste Daroussin /*
250261d06d6bSBaptiste Daroussin * Evaluate a string comparison condition.
250361d06d6bSBaptiste Daroussin * The first character is the delimiter.
250461d06d6bSBaptiste Daroussin * Succeed if the string up to its second occurrence
2505*c1c95addSBrooks Davis * matches the string up to its third occurrence.
250661d06d6bSBaptiste Daroussin * Advance the cursor after the third occurrence
250761d06d6bSBaptiste Daroussin * or lacking that, to the end of the line.
250861d06d6bSBaptiste Daroussin */
250961d06d6bSBaptiste Daroussin static int
roff_evalstrcond(const char * v,int * pos)251061d06d6bSBaptiste Daroussin roff_evalstrcond(const char *v, int *pos)
251161d06d6bSBaptiste Daroussin {
251261d06d6bSBaptiste Daroussin const char *s1, *s2, *s3;
251361d06d6bSBaptiste Daroussin int match;
251461d06d6bSBaptiste Daroussin
251561d06d6bSBaptiste Daroussin match = 0;
251661d06d6bSBaptiste Daroussin s1 = v + *pos; /* initial delimiter */
251761d06d6bSBaptiste Daroussin s2 = s1 + 1; /* for scanning the first string */
251861d06d6bSBaptiste Daroussin s3 = strchr(s2, *s1); /* for scanning the second string */
251961d06d6bSBaptiste Daroussin
252061d06d6bSBaptiste Daroussin if (NULL == s3) /* found no middle delimiter */
252161d06d6bSBaptiste Daroussin goto out;
252261d06d6bSBaptiste Daroussin
252361d06d6bSBaptiste Daroussin while ('\0' != *++s3) {
252461d06d6bSBaptiste Daroussin if (*s2 != *s3) { /* mismatch */
252561d06d6bSBaptiste Daroussin s3 = strchr(s3, *s1);
252661d06d6bSBaptiste Daroussin break;
252761d06d6bSBaptiste Daroussin }
252861d06d6bSBaptiste Daroussin if (*s3 == *s1) { /* found the final delimiter */
252961d06d6bSBaptiste Daroussin match = 1;
253061d06d6bSBaptiste Daroussin break;
253161d06d6bSBaptiste Daroussin }
253261d06d6bSBaptiste Daroussin s2++;
253361d06d6bSBaptiste Daroussin }
253461d06d6bSBaptiste Daroussin
253561d06d6bSBaptiste Daroussin out:
253661d06d6bSBaptiste Daroussin if (NULL == s3)
253761d06d6bSBaptiste Daroussin s3 = strchr(s2, '\0');
253861d06d6bSBaptiste Daroussin else if (*s3 != '\0')
253961d06d6bSBaptiste Daroussin s3++;
254061d06d6bSBaptiste Daroussin *pos = s3 - v;
254161d06d6bSBaptiste Daroussin return match;
254261d06d6bSBaptiste Daroussin }
254361d06d6bSBaptiste Daroussin
254461d06d6bSBaptiste Daroussin /*
254561d06d6bSBaptiste Daroussin * Evaluate an optionally negated single character, numerical,
254661d06d6bSBaptiste Daroussin * or string condition.
254761d06d6bSBaptiste Daroussin */
254861d06d6bSBaptiste Daroussin static int
roff_evalcond(struct roff * r,int ln,char * v,int * pos)254961d06d6bSBaptiste Daroussin roff_evalcond(struct roff *r, int ln, char *v, int *pos)
255061d06d6bSBaptiste Daroussin {
25517295610fSBaptiste Daroussin const char *start, *end;
255261d06d6bSBaptiste Daroussin char *cp, *name;
255361d06d6bSBaptiste Daroussin size_t sz;
25547295610fSBaptiste Daroussin int deftype, len, number, savepos, istrue, wanttrue;
255561d06d6bSBaptiste Daroussin
255661d06d6bSBaptiste Daroussin if ('!' == v[*pos]) {
255761d06d6bSBaptiste Daroussin wanttrue = 0;
255861d06d6bSBaptiste Daroussin (*pos)++;
255961d06d6bSBaptiste Daroussin } else
256061d06d6bSBaptiste Daroussin wanttrue = 1;
256161d06d6bSBaptiste Daroussin
256261d06d6bSBaptiste Daroussin switch (v[*pos]) {
256361d06d6bSBaptiste Daroussin case '\0':
256461d06d6bSBaptiste Daroussin return 0;
256561d06d6bSBaptiste Daroussin case 'n':
256661d06d6bSBaptiste Daroussin case 'o':
256761d06d6bSBaptiste Daroussin (*pos)++;
256861d06d6bSBaptiste Daroussin return wanttrue;
256961d06d6bSBaptiste Daroussin case 'e':
257061d06d6bSBaptiste Daroussin case 't':
257161d06d6bSBaptiste Daroussin case 'v':
257261d06d6bSBaptiste Daroussin (*pos)++;
257361d06d6bSBaptiste Daroussin return !wanttrue;
25747295610fSBaptiste Daroussin case 'c':
25757295610fSBaptiste Daroussin do {
25767295610fSBaptiste Daroussin (*pos)++;
25777295610fSBaptiste Daroussin } while (v[*pos] == ' ');
25787295610fSBaptiste Daroussin
25797295610fSBaptiste Daroussin /*
25807295610fSBaptiste Daroussin * Quirk for groff compatibility:
25817295610fSBaptiste Daroussin * The horizontal tab is neither available nor unavailable.
25827295610fSBaptiste Daroussin */
25837295610fSBaptiste Daroussin
25847295610fSBaptiste Daroussin if (v[*pos] == '\t') {
25857295610fSBaptiste Daroussin (*pos)++;
25867295610fSBaptiste Daroussin return 0;
25877295610fSBaptiste Daroussin }
25887295610fSBaptiste Daroussin
25897295610fSBaptiste Daroussin /* Printable ASCII characters are available. */
25907295610fSBaptiste Daroussin
25917295610fSBaptiste Daroussin if (v[*pos] != '\\') {
25927295610fSBaptiste Daroussin (*pos)++;
25937295610fSBaptiste Daroussin return wanttrue;
25947295610fSBaptiste Daroussin }
25957295610fSBaptiste Daroussin
25967295610fSBaptiste Daroussin end = v + ++*pos;
25977295610fSBaptiste Daroussin switch (mandoc_escape(&end, &start, &len)) {
25987295610fSBaptiste Daroussin case ESCAPE_SPECIAL:
25997295610fSBaptiste Daroussin istrue = mchars_spec2cp(start, len) != -1;
26007295610fSBaptiste Daroussin break;
26017295610fSBaptiste Daroussin case ESCAPE_UNICODE:
26027295610fSBaptiste Daroussin istrue = 1;
26037295610fSBaptiste Daroussin break;
26047295610fSBaptiste Daroussin case ESCAPE_NUMBERED:
26057295610fSBaptiste Daroussin istrue = mchars_num2char(start, len) != -1;
26067295610fSBaptiste Daroussin break;
26077295610fSBaptiste Daroussin default:
26087295610fSBaptiste Daroussin istrue = !wanttrue;
26097295610fSBaptiste Daroussin break;
26107295610fSBaptiste Daroussin }
26117295610fSBaptiste Daroussin *pos = end - v;
26127295610fSBaptiste Daroussin return istrue == wanttrue;
261361d06d6bSBaptiste Daroussin case 'd':
261461d06d6bSBaptiste Daroussin case 'r':
261561d06d6bSBaptiste Daroussin cp = v + *pos + 1;
261661d06d6bSBaptiste Daroussin while (*cp == ' ')
261761d06d6bSBaptiste Daroussin cp++;
261861d06d6bSBaptiste Daroussin name = cp;
261961d06d6bSBaptiste Daroussin sz = roff_getname(r, &cp, ln, cp - v);
262061d06d6bSBaptiste Daroussin if (sz == 0)
262161d06d6bSBaptiste Daroussin istrue = 0;
262261d06d6bSBaptiste Daroussin else if (v[*pos] == 'r')
262361d06d6bSBaptiste Daroussin istrue = roff_hasregn(r, name, sz);
262461d06d6bSBaptiste Daroussin else {
262561d06d6bSBaptiste Daroussin deftype = ROFFDEF_ANY;
262661d06d6bSBaptiste Daroussin roff_getstrn(r, name, sz, &deftype);
262761d06d6bSBaptiste Daroussin istrue = !!deftype;
262861d06d6bSBaptiste Daroussin }
26297295610fSBaptiste Daroussin *pos = (name + sz) - v;
263061d06d6bSBaptiste Daroussin return istrue == wanttrue;
263161d06d6bSBaptiste Daroussin default:
263261d06d6bSBaptiste Daroussin break;
263361d06d6bSBaptiste Daroussin }
263461d06d6bSBaptiste Daroussin
263561d06d6bSBaptiste Daroussin savepos = *pos;
263661d06d6bSBaptiste Daroussin if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE))
263761d06d6bSBaptiste Daroussin return (number > 0) == wanttrue;
263861d06d6bSBaptiste Daroussin else if (*pos == savepos)
263961d06d6bSBaptiste Daroussin return roff_evalstrcond(v, pos) == wanttrue;
264061d06d6bSBaptiste Daroussin else
264161d06d6bSBaptiste Daroussin return 0;
264261d06d6bSBaptiste Daroussin }
264361d06d6bSBaptiste Daroussin
26447295610fSBaptiste Daroussin static int
roff_line_ignore(ROFF_ARGS)264561d06d6bSBaptiste Daroussin roff_line_ignore(ROFF_ARGS)
264661d06d6bSBaptiste Daroussin {
264761d06d6bSBaptiste Daroussin
264861d06d6bSBaptiste Daroussin return ROFF_IGN;
264961d06d6bSBaptiste Daroussin }
265061d06d6bSBaptiste Daroussin
26517295610fSBaptiste Daroussin static int
roff_insec(ROFF_ARGS)265261d06d6bSBaptiste Daroussin roff_insec(ROFF_ARGS)
265361d06d6bSBaptiste Daroussin {
265461d06d6bSBaptiste Daroussin
26557295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_INSEC, ln, ppos, "%s", roff_name[tok]);
265661d06d6bSBaptiste Daroussin return ROFF_IGN;
265761d06d6bSBaptiste Daroussin }
265861d06d6bSBaptiste Daroussin
26597295610fSBaptiste Daroussin static int
roff_unsupp(ROFF_ARGS)266061d06d6bSBaptiste Daroussin roff_unsupp(ROFF_ARGS)
266161d06d6bSBaptiste Daroussin {
266261d06d6bSBaptiste Daroussin
26637295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_UNSUPP, ln, ppos, "%s", roff_name[tok]);
266461d06d6bSBaptiste Daroussin return ROFF_IGN;
266561d06d6bSBaptiste Daroussin }
266661d06d6bSBaptiste Daroussin
26677295610fSBaptiste Daroussin static int
roff_cond(ROFF_ARGS)266861d06d6bSBaptiste Daroussin roff_cond(ROFF_ARGS)
266961d06d6bSBaptiste Daroussin {
26707295610fSBaptiste Daroussin int irc;
267161d06d6bSBaptiste Daroussin
267261d06d6bSBaptiste Daroussin roffnode_push(r, tok, NULL, ln, ppos);
267361d06d6bSBaptiste Daroussin
267461d06d6bSBaptiste Daroussin /*
267561d06d6bSBaptiste Daroussin * An `.el' has no conditional body: it will consume the value
267661d06d6bSBaptiste Daroussin * of the current rstack entry set in prior `ie' calls or
267761d06d6bSBaptiste Daroussin * defaults to DENY.
267861d06d6bSBaptiste Daroussin *
267961d06d6bSBaptiste Daroussin * If we're not an `el', however, then evaluate the conditional.
268061d06d6bSBaptiste Daroussin */
268161d06d6bSBaptiste Daroussin
268261d06d6bSBaptiste Daroussin r->last->rule = tok == ROFF_el ?
268361d06d6bSBaptiste Daroussin (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
268461d06d6bSBaptiste Daroussin roff_evalcond(r, ln, buf->buf, &pos);
268561d06d6bSBaptiste Daroussin
268661d06d6bSBaptiste Daroussin /*
268761d06d6bSBaptiste Daroussin * An if-else will put the NEGATION of the current evaluated
268861d06d6bSBaptiste Daroussin * conditional into the stack of rules.
268961d06d6bSBaptiste Daroussin */
269061d06d6bSBaptiste Daroussin
269161d06d6bSBaptiste Daroussin if (tok == ROFF_ie) {
269261d06d6bSBaptiste Daroussin if (r->rstackpos + 1 == r->rstacksz) {
269361d06d6bSBaptiste Daroussin r->rstacksz += 16;
269461d06d6bSBaptiste Daroussin r->rstack = mandoc_reallocarray(r->rstack,
269561d06d6bSBaptiste Daroussin r->rstacksz, sizeof(int));
269661d06d6bSBaptiste Daroussin }
269761d06d6bSBaptiste Daroussin r->rstack[++r->rstackpos] = !r->last->rule;
269861d06d6bSBaptiste Daroussin }
269961d06d6bSBaptiste Daroussin
270061d06d6bSBaptiste Daroussin /* If the parent has false as its rule, then so do we. */
270161d06d6bSBaptiste Daroussin
270261d06d6bSBaptiste Daroussin if (r->last->parent && !r->last->parent->rule)
270361d06d6bSBaptiste Daroussin r->last->rule = 0;
270461d06d6bSBaptiste Daroussin
270561d06d6bSBaptiste Daroussin /*
270661d06d6bSBaptiste Daroussin * Determine scope.
270761d06d6bSBaptiste Daroussin * If there is nothing on the line after the conditional,
270861d06d6bSBaptiste Daroussin * not even whitespace, use next-line scope.
27097295610fSBaptiste Daroussin * Except that .while does not support next-line scope.
271061d06d6bSBaptiste Daroussin */
271161d06d6bSBaptiste Daroussin
27127295610fSBaptiste Daroussin if (buf->buf[pos] == '\0' && tok != ROFF_while) {
271361d06d6bSBaptiste Daroussin r->last->endspan = 2;
271461d06d6bSBaptiste Daroussin goto out;
271561d06d6bSBaptiste Daroussin }
271661d06d6bSBaptiste Daroussin
271761d06d6bSBaptiste Daroussin while (buf->buf[pos] == ' ')
271861d06d6bSBaptiste Daroussin pos++;
271961d06d6bSBaptiste Daroussin
272061d06d6bSBaptiste Daroussin /* An opening brace requests multiline scope. */
272161d06d6bSBaptiste Daroussin
272261d06d6bSBaptiste Daroussin if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
272361d06d6bSBaptiste Daroussin r->last->endspan = -1;
272461d06d6bSBaptiste Daroussin pos += 2;
272561d06d6bSBaptiste Daroussin while (buf->buf[pos] == ' ')
272661d06d6bSBaptiste Daroussin pos++;
272761d06d6bSBaptiste Daroussin goto out;
272861d06d6bSBaptiste Daroussin }
272961d06d6bSBaptiste Daroussin
273061d06d6bSBaptiste Daroussin /*
273161d06d6bSBaptiste Daroussin * Anything else following the conditional causes
273261d06d6bSBaptiste Daroussin * single-line scope. Warn if the scope contains
273361d06d6bSBaptiste Daroussin * nothing but trailing whitespace.
273461d06d6bSBaptiste Daroussin */
273561d06d6bSBaptiste Daroussin
273661d06d6bSBaptiste Daroussin if (buf->buf[pos] == '\0')
27377295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_COND_EMPTY,
27387295610fSBaptiste Daroussin ln, ppos, "%s", roff_name[tok]);
273961d06d6bSBaptiste Daroussin
274061d06d6bSBaptiste Daroussin r->last->endspan = 1;
274161d06d6bSBaptiste Daroussin
274261d06d6bSBaptiste Daroussin out:
274361d06d6bSBaptiste Daroussin *offs = pos;
27447295610fSBaptiste Daroussin irc = ROFF_RERUN;
27457295610fSBaptiste Daroussin if (tok == ROFF_while)
27467295610fSBaptiste Daroussin irc |= ROFF_WHILE;
27477295610fSBaptiste Daroussin return irc;
274861d06d6bSBaptiste Daroussin }
274961d06d6bSBaptiste Daroussin
27507295610fSBaptiste Daroussin static int
roff_ds(ROFF_ARGS)275161d06d6bSBaptiste Daroussin roff_ds(ROFF_ARGS)
275261d06d6bSBaptiste Daroussin {
275361d06d6bSBaptiste Daroussin char *string;
275461d06d6bSBaptiste Daroussin const char *name;
275561d06d6bSBaptiste Daroussin size_t namesz;
275661d06d6bSBaptiste Daroussin
275761d06d6bSBaptiste Daroussin /* Ignore groff compatibility mode for now. */
275861d06d6bSBaptiste Daroussin
275961d06d6bSBaptiste Daroussin if (tok == ROFF_ds1)
276061d06d6bSBaptiste Daroussin tok = ROFF_ds;
276161d06d6bSBaptiste Daroussin else if (tok == ROFF_as1)
276261d06d6bSBaptiste Daroussin tok = ROFF_as;
276361d06d6bSBaptiste Daroussin
276461d06d6bSBaptiste Daroussin /*
276561d06d6bSBaptiste Daroussin * The first word is the name of the string.
276661d06d6bSBaptiste Daroussin * If it is empty or terminated by an escape sequence,
276761d06d6bSBaptiste Daroussin * abort the `ds' request without defining anything.
276861d06d6bSBaptiste Daroussin */
276961d06d6bSBaptiste Daroussin
277061d06d6bSBaptiste Daroussin name = string = buf->buf + pos;
277161d06d6bSBaptiste Daroussin if (*name == '\0')
277261d06d6bSBaptiste Daroussin return ROFF_IGN;
277361d06d6bSBaptiste Daroussin
277461d06d6bSBaptiste Daroussin namesz = roff_getname(r, &string, ln, pos);
27757295610fSBaptiste Daroussin switch (name[namesz]) {
27767295610fSBaptiste Daroussin case '\\':
277761d06d6bSBaptiste Daroussin return ROFF_IGN;
27787295610fSBaptiste Daroussin case '\t':
27797295610fSBaptiste Daroussin string = buf->buf + pos + namesz;
27807295610fSBaptiste Daroussin break;
27817295610fSBaptiste Daroussin default:
27827295610fSBaptiste Daroussin break;
27837295610fSBaptiste Daroussin }
278461d06d6bSBaptiste Daroussin
278561d06d6bSBaptiste Daroussin /* Read past the initial double-quote, if any. */
278661d06d6bSBaptiste Daroussin if (*string == '"')
278761d06d6bSBaptiste Daroussin string++;
278861d06d6bSBaptiste Daroussin
278961d06d6bSBaptiste Daroussin /* The rest is the value. */
279061d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
279161d06d6bSBaptiste Daroussin ROFF_as == tok);
279261d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
279361d06d6bSBaptiste Daroussin return ROFF_IGN;
279461d06d6bSBaptiste Daroussin }
279561d06d6bSBaptiste Daroussin
279661d06d6bSBaptiste Daroussin /*
279761d06d6bSBaptiste Daroussin * Parse a single operator, one or two characters long.
279861d06d6bSBaptiste Daroussin * If the operator is recognized, return success and advance the
279961d06d6bSBaptiste Daroussin * parse point, else return failure and let the parse point unchanged.
280061d06d6bSBaptiste Daroussin */
280161d06d6bSBaptiste Daroussin static int
roff_getop(const char * v,int * pos,char * res)280261d06d6bSBaptiste Daroussin roff_getop(const char *v, int *pos, char *res)
280361d06d6bSBaptiste Daroussin {
280461d06d6bSBaptiste Daroussin
280561d06d6bSBaptiste Daroussin *res = v[*pos];
280661d06d6bSBaptiste Daroussin
280761d06d6bSBaptiste Daroussin switch (*res) {
280861d06d6bSBaptiste Daroussin case '+':
280961d06d6bSBaptiste Daroussin case '-':
281061d06d6bSBaptiste Daroussin case '*':
281161d06d6bSBaptiste Daroussin case '/':
281261d06d6bSBaptiste Daroussin case '%':
281361d06d6bSBaptiste Daroussin case '&':
281461d06d6bSBaptiste Daroussin case ':':
281561d06d6bSBaptiste Daroussin break;
281661d06d6bSBaptiste Daroussin case '<':
281761d06d6bSBaptiste Daroussin switch (v[*pos + 1]) {
281861d06d6bSBaptiste Daroussin case '=':
281961d06d6bSBaptiste Daroussin *res = 'l';
282061d06d6bSBaptiste Daroussin (*pos)++;
282161d06d6bSBaptiste Daroussin break;
282261d06d6bSBaptiste Daroussin case '>':
282361d06d6bSBaptiste Daroussin *res = '!';
282461d06d6bSBaptiste Daroussin (*pos)++;
282561d06d6bSBaptiste Daroussin break;
282661d06d6bSBaptiste Daroussin case '?':
282761d06d6bSBaptiste Daroussin *res = 'i';
282861d06d6bSBaptiste Daroussin (*pos)++;
282961d06d6bSBaptiste Daroussin break;
283061d06d6bSBaptiste Daroussin default:
283161d06d6bSBaptiste Daroussin break;
283261d06d6bSBaptiste Daroussin }
283361d06d6bSBaptiste Daroussin break;
283461d06d6bSBaptiste Daroussin case '>':
283561d06d6bSBaptiste Daroussin switch (v[*pos + 1]) {
283661d06d6bSBaptiste Daroussin case '=':
283761d06d6bSBaptiste Daroussin *res = 'g';
283861d06d6bSBaptiste Daroussin (*pos)++;
283961d06d6bSBaptiste Daroussin break;
284061d06d6bSBaptiste Daroussin case '?':
284161d06d6bSBaptiste Daroussin *res = 'a';
284261d06d6bSBaptiste Daroussin (*pos)++;
284361d06d6bSBaptiste Daroussin break;
284461d06d6bSBaptiste Daroussin default:
284561d06d6bSBaptiste Daroussin break;
284661d06d6bSBaptiste Daroussin }
284761d06d6bSBaptiste Daroussin break;
284861d06d6bSBaptiste Daroussin case '=':
284961d06d6bSBaptiste Daroussin if ('=' == v[*pos + 1])
285061d06d6bSBaptiste Daroussin (*pos)++;
285161d06d6bSBaptiste Daroussin break;
285261d06d6bSBaptiste Daroussin default:
285361d06d6bSBaptiste Daroussin return 0;
285461d06d6bSBaptiste Daroussin }
285561d06d6bSBaptiste Daroussin (*pos)++;
285661d06d6bSBaptiste Daroussin
285761d06d6bSBaptiste Daroussin return *res;
285861d06d6bSBaptiste Daroussin }
285961d06d6bSBaptiste Daroussin
286061d06d6bSBaptiste Daroussin /*
286161d06d6bSBaptiste Daroussin * Evaluate either a parenthesized numeric expression
286261d06d6bSBaptiste Daroussin * or a single signed integer number.
286361d06d6bSBaptiste Daroussin */
286461d06d6bSBaptiste Daroussin static int
roff_evalpar(struct roff * r,int ln,const char * v,int * pos,int * res,int flags)286561d06d6bSBaptiste Daroussin roff_evalpar(struct roff *r, int ln,
286661d06d6bSBaptiste Daroussin const char *v, int *pos, int *res, int flags)
286761d06d6bSBaptiste Daroussin {
286861d06d6bSBaptiste Daroussin
286961d06d6bSBaptiste Daroussin if ('(' != v[*pos])
287061d06d6bSBaptiste Daroussin return roff_getnum(v, pos, res, flags);
287161d06d6bSBaptiste Daroussin
287261d06d6bSBaptiste Daroussin (*pos)++;
287361d06d6bSBaptiste Daroussin if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE))
287461d06d6bSBaptiste Daroussin return 0;
287561d06d6bSBaptiste Daroussin
287661d06d6bSBaptiste Daroussin /*
287761d06d6bSBaptiste Daroussin * Omission of the closing parenthesis
287861d06d6bSBaptiste Daroussin * is an error in validation mode,
287961d06d6bSBaptiste Daroussin * but ignored in evaluation mode.
288061d06d6bSBaptiste Daroussin */
288161d06d6bSBaptiste Daroussin
288261d06d6bSBaptiste Daroussin if (')' == v[*pos])
288361d06d6bSBaptiste Daroussin (*pos)++;
288461d06d6bSBaptiste Daroussin else if (NULL == res)
288561d06d6bSBaptiste Daroussin return 0;
288661d06d6bSBaptiste Daroussin
288761d06d6bSBaptiste Daroussin return 1;
288861d06d6bSBaptiste Daroussin }
288961d06d6bSBaptiste Daroussin
289061d06d6bSBaptiste Daroussin /*
289161d06d6bSBaptiste Daroussin * Evaluate a complete numeric expression.
289261d06d6bSBaptiste Daroussin * Proceed left to right, there is no concept of precedence.
289361d06d6bSBaptiste Daroussin */
289461d06d6bSBaptiste Daroussin static int
roff_evalnum(struct roff * r,int ln,const char * v,int * pos,int * res,int flags)289561d06d6bSBaptiste Daroussin roff_evalnum(struct roff *r, int ln, const char *v,
289661d06d6bSBaptiste Daroussin int *pos, int *res, int flags)
289761d06d6bSBaptiste Daroussin {
289861d06d6bSBaptiste Daroussin int mypos, operand2;
289961d06d6bSBaptiste Daroussin char operator;
290061d06d6bSBaptiste Daroussin
290161d06d6bSBaptiste Daroussin if (NULL == pos) {
290261d06d6bSBaptiste Daroussin mypos = 0;
290361d06d6bSBaptiste Daroussin pos = &mypos;
290461d06d6bSBaptiste Daroussin }
290561d06d6bSBaptiste Daroussin
290661d06d6bSBaptiste Daroussin if (flags & ROFFNUM_WHITE)
290761d06d6bSBaptiste Daroussin while (isspace((unsigned char)v[*pos]))
290861d06d6bSBaptiste Daroussin (*pos)++;
290961d06d6bSBaptiste Daroussin
291061d06d6bSBaptiste Daroussin if ( ! roff_evalpar(r, ln, v, pos, res, flags))
291161d06d6bSBaptiste Daroussin return 0;
291261d06d6bSBaptiste Daroussin
291361d06d6bSBaptiste Daroussin while (1) {
291461d06d6bSBaptiste Daroussin if (flags & ROFFNUM_WHITE)
291561d06d6bSBaptiste Daroussin while (isspace((unsigned char)v[*pos]))
291661d06d6bSBaptiste Daroussin (*pos)++;
291761d06d6bSBaptiste Daroussin
291861d06d6bSBaptiste Daroussin if ( ! roff_getop(v, pos, &operator))
291961d06d6bSBaptiste Daroussin break;
292061d06d6bSBaptiste Daroussin
292161d06d6bSBaptiste Daroussin if (flags & ROFFNUM_WHITE)
292261d06d6bSBaptiste Daroussin while (isspace((unsigned char)v[*pos]))
292361d06d6bSBaptiste Daroussin (*pos)++;
292461d06d6bSBaptiste Daroussin
292561d06d6bSBaptiste Daroussin if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags))
292661d06d6bSBaptiste Daroussin return 0;
292761d06d6bSBaptiste Daroussin
292861d06d6bSBaptiste Daroussin if (flags & ROFFNUM_WHITE)
292961d06d6bSBaptiste Daroussin while (isspace((unsigned char)v[*pos]))
293061d06d6bSBaptiste Daroussin (*pos)++;
293161d06d6bSBaptiste Daroussin
293261d06d6bSBaptiste Daroussin if (NULL == res)
293361d06d6bSBaptiste Daroussin continue;
293461d06d6bSBaptiste Daroussin
293561d06d6bSBaptiste Daroussin switch (operator) {
293661d06d6bSBaptiste Daroussin case '+':
293761d06d6bSBaptiste Daroussin *res += operand2;
293861d06d6bSBaptiste Daroussin break;
293961d06d6bSBaptiste Daroussin case '-':
294061d06d6bSBaptiste Daroussin *res -= operand2;
294161d06d6bSBaptiste Daroussin break;
294261d06d6bSBaptiste Daroussin case '*':
294361d06d6bSBaptiste Daroussin *res *= operand2;
294461d06d6bSBaptiste Daroussin break;
294561d06d6bSBaptiste Daroussin case '/':
294661d06d6bSBaptiste Daroussin if (operand2 == 0) {
294761d06d6bSBaptiste Daroussin mandoc_msg(MANDOCERR_DIVZERO,
29487295610fSBaptiste Daroussin ln, *pos, "%s", v);
294961d06d6bSBaptiste Daroussin *res = 0;
295061d06d6bSBaptiste Daroussin break;
295161d06d6bSBaptiste Daroussin }
295261d06d6bSBaptiste Daroussin *res /= operand2;
295361d06d6bSBaptiste Daroussin break;
295461d06d6bSBaptiste Daroussin case '%':
295561d06d6bSBaptiste Daroussin if (operand2 == 0) {
295661d06d6bSBaptiste Daroussin mandoc_msg(MANDOCERR_DIVZERO,
29577295610fSBaptiste Daroussin ln, *pos, "%s", v);
295861d06d6bSBaptiste Daroussin *res = 0;
295961d06d6bSBaptiste Daroussin break;
296061d06d6bSBaptiste Daroussin }
296161d06d6bSBaptiste Daroussin *res %= operand2;
296261d06d6bSBaptiste Daroussin break;
296361d06d6bSBaptiste Daroussin case '<':
296461d06d6bSBaptiste Daroussin *res = *res < operand2;
296561d06d6bSBaptiste Daroussin break;
296661d06d6bSBaptiste Daroussin case '>':
296761d06d6bSBaptiste Daroussin *res = *res > operand2;
296861d06d6bSBaptiste Daroussin break;
296961d06d6bSBaptiste Daroussin case 'l':
297061d06d6bSBaptiste Daroussin *res = *res <= operand2;
297161d06d6bSBaptiste Daroussin break;
297261d06d6bSBaptiste Daroussin case 'g':
297361d06d6bSBaptiste Daroussin *res = *res >= operand2;
297461d06d6bSBaptiste Daroussin break;
297561d06d6bSBaptiste Daroussin case '=':
297661d06d6bSBaptiste Daroussin *res = *res == operand2;
297761d06d6bSBaptiste Daroussin break;
297861d06d6bSBaptiste Daroussin case '!':
297961d06d6bSBaptiste Daroussin *res = *res != operand2;
298061d06d6bSBaptiste Daroussin break;
298161d06d6bSBaptiste Daroussin case '&':
298261d06d6bSBaptiste Daroussin *res = *res && operand2;
298361d06d6bSBaptiste Daroussin break;
298461d06d6bSBaptiste Daroussin case ':':
298561d06d6bSBaptiste Daroussin *res = *res || operand2;
298661d06d6bSBaptiste Daroussin break;
298761d06d6bSBaptiste Daroussin case 'i':
298861d06d6bSBaptiste Daroussin if (operand2 < *res)
298961d06d6bSBaptiste Daroussin *res = operand2;
299061d06d6bSBaptiste Daroussin break;
299161d06d6bSBaptiste Daroussin case 'a':
299261d06d6bSBaptiste Daroussin if (operand2 > *res)
299361d06d6bSBaptiste Daroussin *res = operand2;
299461d06d6bSBaptiste Daroussin break;
299561d06d6bSBaptiste Daroussin default:
299661d06d6bSBaptiste Daroussin abort();
299761d06d6bSBaptiste Daroussin }
299861d06d6bSBaptiste Daroussin }
299961d06d6bSBaptiste Daroussin return 1;
300061d06d6bSBaptiste Daroussin }
300161d06d6bSBaptiste Daroussin
300261d06d6bSBaptiste Daroussin /* --- register management ------------------------------------------------ */
300361d06d6bSBaptiste Daroussin
300461d06d6bSBaptiste Daroussin void
roff_setreg(struct roff * r,const char * name,int val,char sign)300561d06d6bSBaptiste Daroussin roff_setreg(struct roff *r, const char *name, int val, char sign)
300661d06d6bSBaptiste Daroussin {
300761d06d6bSBaptiste Daroussin roff_setregn(r, name, strlen(name), val, sign, INT_MIN);
300861d06d6bSBaptiste Daroussin }
300961d06d6bSBaptiste Daroussin
301061d06d6bSBaptiste Daroussin static void
roff_setregn(struct roff * r,const char * name,size_t len,int val,char sign,int step)301161d06d6bSBaptiste Daroussin roff_setregn(struct roff *r, const char *name, size_t len,
301261d06d6bSBaptiste Daroussin int val, char sign, int step)
301361d06d6bSBaptiste Daroussin {
301461d06d6bSBaptiste Daroussin struct roffreg *reg;
301561d06d6bSBaptiste Daroussin
301661d06d6bSBaptiste Daroussin /* Search for an existing register with the same name. */
301761d06d6bSBaptiste Daroussin reg = r->regtab;
301861d06d6bSBaptiste Daroussin
301961d06d6bSBaptiste Daroussin while (reg != NULL && (reg->key.sz != len ||
302061d06d6bSBaptiste Daroussin strncmp(reg->key.p, name, len) != 0))
302161d06d6bSBaptiste Daroussin reg = reg->next;
302261d06d6bSBaptiste Daroussin
302361d06d6bSBaptiste Daroussin if (NULL == reg) {
302461d06d6bSBaptiste Daroussin /* Create a new register. */
302561d06d6bSBaptiste Daroussin reg = mandoc_malloc(sizeof(struct roffreg));
302661d06d6bSBaptiste Daroussin reg->key.p = mandoc_strndup(name, len);
302761d06d6bSBaptiste Daroussin reg->key.sz = len;
302861d06d6bSBaptiste Daroussin reg->val = 0;
302961d06d6bSBaptiste Daroussin reg->step = 0;
303061d06d6bSBaptiste Daroussin reg->next = r->regtab;
303161d06d6bSBaptiste Daroussin r->regtab = reg;
303261d06d6bSBaptiste Daroussin }
303361d06d6bSBaptiste Daroussin
303461d06d6bSBaptiste Daroussin if ('+' == sign)
303561d06d6bSBaptiste Daroussin reg->val += val;
303661d06d6bSBaptiste Daroussin else if ('-' == sign)
303761d06d6bSBaptiste Daroussin reg->val -= val;
303861d06d6bSBaptiste Daroussin else
303961d06d6bSBaptiste Daroussin reg->val = val;
304061d06d6bSBaptiste Daroussin if (step != INT_MIN)
304161d06d6bSBaptiste Daroussin reg->step = step;
304261d06d6bSBaptiste Daroussin }
304361d06d6bSBaptiste Daroussin
304461d06d6bSBaptiste Daroussin /*
304561d06d6bSBaptiste Daroussin * Handle some predefined read-only number registers.
304661d06d6bSBaptiste Daroussin * For now, return -1 if the requested register is not predefined;
304761d06d6bSBaptiste Daroussin * in case a predefined read-only register having the value -1
304861d06d6bSBaptiste Daroussin * were to turn up, another special value would have to be chosen.
304961d06d6bSBaptiste Daroussin */
305061d06d6bSBaptiste Daroussin static int
roff_getregro(const struct roff * r,const char * name)305161d06d6bSBaptiste Daroussin roff_getregro(const struct roff *r, const char *name)
305261d06d6bSBaptiste Daroussin {
305361d06d6bSBaptiste Daroussin
305461d06d6bSBaptiste Daroussin switch (*name) {
305561d06d6bSBaptiste Daroussin case '$': /* Number of arguments of the last macro evaluated. */
30567295610fSBaptiste Daroussin return r->mstackpos < 0 ? 0 : r->mstack[r->mstackpos].argc;
305761d06d6bSBaptiste Daroussin case 'A': /* ASCII approximation mode is always off. */
305861d06d6bSBaptiste Daroussin return 0;
305961d06d6bSBaptiste Daroussin case 'g': /* Groff compatibility mode is always on. */
306061d06d6bSBaptiste Daroussin return 1;
306161d06d6bSBaptiste Daroussin case 'H': /* Fixed horizontal resolution. */
306261d06d6bSBaptiste Daroussin return 24;
306361d06d6bSBaptiste Daroussin case 'j': /* Always adjust left margin only. */
306461d06d6bSBaptiste Daroussin return 0;
306561d06d6bSBaptiste Daroussin case 'T': /* Some output device is always defined. */
306661d06d6bSBaptiste Daroussin return 1;
306761d06d6bSBaptiste Daroussin case 'V': /* Fixed vertical resolution. */
306861d06d6bSBaptiste Daroussin return 40;
306961d06d6bSBaptiste Daroussin default:
307061d06d6bSBaptiste Daroussin return -1;
307161d06d6bSBaptiste Daroussin }
307261d06d6bSBaptiste Daroussin }
307361d06d6bSBaptiste Daroussin
307461d06d6bSBaptiste Daroussin int
roff_getreg(struct roff * r,const char * name)307561d06d6bSBaptiste Daroussin roff_getreg(struct roff *r, const char *name)
307661d06d6bSBaptiste Daroussin {
307761d06d6bSBaptiste Daroussin return roff_getregn(r, name, strlen(name), '\0');
307861d06d6bSBaptiste Daroussin }
307961d06d6bSBaptiste Daroussin
308061d06d6bSBaptiste Daroussin static int
roff_getregn(struct roff * r,const char * name,size_t len,char sign)308161d06d6bSBaptiste Daroussin roff_getregn(struct roff *r, const char *name, size_t len, char sign)
308261d06d6bSBaptiste Daroussin {
308361d06d6bSBaptiste Daroussin struct roffreg *reg;
308461d06d6bSBaptiste Daroussin int val;
308561d06d6bSBaptiste Daroussin
308661d06d6bSBaptiste Daroussin if ('.' == name[0] && 2 == len) {
308761d06d6bSBaptiste Daroussin val = roff_getregro(r, name + 1);
308861d06d6bSBaptiste Daroussin if (-1 != val)
308961d06d6bSBaptiste Daroussin return val;
309061d06d6bSBaptiste Daroussin }
309161d06d6bSBaptiste Daroussin
309261d06d6bSBaptiste Daroussin for (reg = r->regtab; reg; reg = reg->next) {
309361d06d6bSBaptiste Daroussin if (len == reg->key.sz &&
309461d06d6bSBaptiste Daroussin 0 == strncmp(name, reg->key.p, len)) {
309561d06d6bSBaptiste Daroussin switch (sign) {
309661d06d6bSBaptiste Daroussin case '+':
309761d06d6bSBaptiste Daroussin reg->val += reg->step;
309861d06d6bSBaptiste Daroussin break;
309961d06d6bSBaptiste Daroussin case '-':
310061d06d6bSBaptiste Daroussin reg->val -= reg->step;
310161d06d6bSBaptiste Daroussin break;
310261d06d6bSBaptiste Daroussin default:
310361d06d6bSBaptiste Daroussin break;
310461d06d6bSBaptiste Daroussin }
310561d06d6bSBaptiste Daroussin return reg->val;
310661d06d6bSBaptiste Daroussin }
310761d06d6bSBaptiste Daroussin }
310861d06d6bSBaptiste Daroussin
310961d06d6bSBaptiste Daroussin roff_setregn(r, name, len, 0, '\0', INT_MIN);
311061d06d6bSBaptiste Daroussin return 0;
311161d06d6bSBaptiste Daroussin }
311261d06d6bSBaptiste Daroussin
311361d06d6bSBaptiste Daroussin static int
roff_hasregn(const struct roff * r,const char * name,size_t len)311461d06d6bSBaptiste Daroussin roff_hasregn(const struct roff *r, const char *name, size_t len)
311561d06d6bSBaptiste Daroussin {
311661d06d6bSBaptiste Daroussin struct roffreg *reg;
311761d06d6bSBaptiste Daroussin int val;
311861d06d6bSBaptiste Daroussin
311961d06d6bSBaptiste Daroussin if ('.' == name[0] && 2 == len) {
312061d06d6bSBaptiste Daroussin val = roff_getregro(r, name + 1);
312161d06d6bSBaptiste Daroussin if (-1 != val)
312261d06d6bSBaptiste Daroussin return 1;
312361d06d6bSBaptiste Daroussin }
312461d06d6bSBaptiste Daroussin
312561d06d6bSBaptiste Daroussin for (reg = r->regtab; reg; reg = reg->next)
312661d06d6bSBaptiste Daroussin if (len == reg->key.sz &&
312761d06d6bSBaptiste Daroussin 0 == strncmp(name, reg->key.p, len))
312861d06d6bSBaptiste Daroussin return 1;
312961d06d6bSBaptiste Daroussin
313061d06d6bSBaptiste Daroussin return 0;
313161d06d6bSBaptiste Daroussin }
313261d06d6bSBaptiste Daroussin
313361d06d6bSBaptiste Daroussin static void
roff_freereg(struct roffreg * reg)313461d06d6bSBaptiste Daroussin roff_freereg(struct roffreg *reg)
313561d06d6bSBaptiste Daroussin {
313661d06d6bSBaptiste Daroussin struct roffreg *old_reg;
313761d06d6bSBaptiste Daroussin
313861d06d6bSBaptiste Daroussin while (NULL != reg) {
313961d06d6bSBaptiste Daroussin free(reg->key.p);
314061d06d6bSBaptiste Daroussin old_reg = reg;
314161d06d6bSBaptiste Daroussin reg = reg->next;
314261d06d6bSBaptiste Daroussin free(old_reg);
314361d06d6bSBaptiste Daroussin }
314461d06d6bSBaptiste Daroussin }
314561d06d6bSBaptiste Daroussin
31467295610fSBaptiste Daroussin static int
roff_nr(ROFF_ARGS)314761d06d6bSBaptiste Daroussin roff_nr(ROFF_ARGS)
314861d06d6bSBaptiste Daroussin {
314961d06d6bSBaptiste Daroussin char *key, *val, *step;
315061d06d6bSBaptiste Daroussin size_t keysz;
315161d06d6bSBaptiste Daroussin int iv, is, len;
315261d06d6bSBaptiste Daroussin char sign;
315361d06d6bSBaptiste Daroussin
315461d06d6bSBaptiste Daroussin key = val = buf->buf + pos;
315561d06d6bSBaptiste Daroussin if (*key == '\0')
315661d06d6bSBaptiste Daroussin return ROFF_IGN;
315761d06d6bSBaptiste Daroussin
315861d06d6bSBaptiste Daroussin keysz = roff_getname(r, &val, ln, pos);
31597295610fSBaptiste Daroussin if (key[keysz] == '\\' || key[keysz] == '\t')
316061d06d6bSBaptiste Daroussin return ROFF_IGN;
316161d06d6bSBaptiste Daroussin
316261d06d6bSBaptiste Daroussin sign = *val;
316361d06d6bSBaptiste Daroussin if (sign == '+' || sign == '-')
316461d06d6bSBaptiste Daroussin val++;
316561d06d6bSBaptiste Daroussin
316661d06d6bSBaptiste Daroussin len = 0;
316761d06d6bSBaptiste Daroussin if (roff_evalnum(r, ln, val, &len, &iv, ROFFNUM_SCALE) == 0)
316861d06d6bSBaptiste Daroussin return ROFF_IGN;
316961d06d6bSBaptiste Daroussin
317061d06d6bSBaptiste Daroussin step = val + len;
317161d06d6bSBaptiste Daroussin while (isspace((unsigned char)*step))
317261d06d6bSBaptiste Daroussin step++;
317361d06d6bSBaptiste Daroussin if (roff_evalnum(r, ln, step, NULL, &is, 0) == 0)
317461d06d6bSBaptiste Daroussin is = INT_MIN;
317561d06d6bSBaptiste Daroussin
317661d06d6bSBaptiste Daroussin roff_setregn(r, key, keysz, iv, sign, is);
317761d06d6bSBaptiste Daroussin return ROFF_IGN;
317861d06d6bSBaptiste Daroussin }
317961d06d6bSBaptiste Daroussin
31807295610fSBaptiste Daroussin static int
roff_rr(ROFF_ARGS)318161d06d6bSBaptiste Daroussin roff_rr(ROFF_ARGS)
318261d06d6bSBaptiste Daroussin {
318361d06d6bSBaptiste Daroussin struct roffreg *reg, **prev;
318461d06d6bSBaptiste Daroussin char *name, *cp;
318561d06d6bSBaptiste Daroussin size_t namesz;
318661d06d6bSBaptiste Daroussin
318761d06d6bSBaptiste Daroussin name = cp = buf->buf + pos;
318861d06d6bSBaptiste Daroussin if (*name == '\0')
318961d06d6bSBaptiste Daroussin return ROFF_IGN;
319061d06d6bSBaptiste Daroussin namesz = roff_getname(r, &cp, ln, pos);
319161d06d6bSBaptiste Daroussin name[namesz] = '\0';
319261d06d6bSBaptiste Daroussin
319361d06d6bSBaptiste Daroussin prev = &r->regtab;
319461d06d6bSBaptiste Daroussin while (1) {
319561d06d6bSBaptiste Daroussin reg = *prev;
319661d06d6bSBaptiste Daroussin if (reg == NULL || !strcmp(name, reg->key.p))
319761d06d6bSBaptiste Daroussin break;
319861d06d6bSBaptiste Daroussin prev = ®->next;
319961d06d6bSBaptiste Daroussin }
320061d06d6bSBaptiste Daroussin if (reg != NULL) {
320161d06d6bSBaptiste Daroussin *prev = reg->next;
320261d06d6bSBaptiste Daroussin free(reg->key.p);
320361d06d6bSBaptiste Daroussin free(reg);
320461d06d6bSBaptiste Daroussin }
320561d06d6bSBaptiste Daroussin return ROFF_IGN;
320661d06d6bSBaptiste Daroussin }
320761d06d6bSBaptiste Daroussin
320861d06d6bSBaptiste Daroussin /* --- handler functions for roff requests -------------------------------- */
320961d06d6bSBaptiste Daroussin
32107295610fSBaptiste Daroussin static int
roff_rm(ROFF_ARGS)321161d06d6bSBaptiste Daroussin roff_rm(ROFF_ARGS)
321261d06d6bSBaptiste Daroussin {
321361d06d6bSBaptiste Daroussin const char *name;
321461d06d6bSBaptiste Daroussin char *cp;
321561d06d6bSBaptiste Daroussin size_t namesz;
321661d06d6bSBaptiste Daroussin
321761d06d6bSBaptiste Daroussin cp = buf->buf + pos;
321861d06d6bSBaptiste Daroussin while (*cp != '\0') {
321961d06d6bSBaptiste Daroussin name = cp;
322061d06d6bSBaptiste Daroussin namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf));
322161d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
322261d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
32237295610fSBaptiste Daroussin if (name[namesz] == '\\' || name[namesz] == '\t')
322461d06d6bSBaptiste Daroussin break;
322561d06d6bSBaptiste Daroussin }
322661d06d6bSBaptiste Daroussin return ROFF_IGN;
322761d06d6bSBaptiste Daroussin }
322861d06d6bSBaptiste Daroussin
32297295610fSBaptiste Daroussin static int
roff_it(ROFF_ARGS)323061d06d6bSBaptiste Daroussin roff_it(ROFF_ARGS)
323161d06d6bSBaptiste Daroussin {
323261d06d6bSBaptiste Daroussin int iv;
323361d06d6bSBaptiste Daroussin
323461d06d6bSBaptiste Daroussin /* Parse the number of lines. */
323561d06d6bSBaptiste Daroussin
323661d06d6bSBaptiste Daroussin if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) {
32377295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_IT_NONUM,
32387295610fSBaptiste Daroussin ln, ppos, "%s", buf->buf + 1);
323961d06d6bSBaptiste Daroussin return ROFF_IGN;
324061d06d6bSBaptiste Daroussin }
324161d06d6bSBaptiste Daroussin
324261d06d6bSBaptiste Daroussin while (isspace((unsigned char)buf->buf[pos]))
324361d06d6bSBaptiste Daroussin pos++;
324461d06d6bSBaptiste Daroussin
324561d06d6bSBaptiste Daroussin /*
324661d06d6bSBaptiste Daroussin * Arm the input line trap.
324761d06d6bSBaptiste Daroussin * Special-casing "an-trap" is an ugly workaround to cope
324861d06d6bSBaptiste Daroussin * with DocBook stupidly fiddling with man(7) internals.
324961d06d6bSBaptiste Daroussin */
325061d06d6bSBaptiste Daroussin
325161d06d6bSBaptiste Daroussin roffit_lines = iv;
325261d06d6bSBaptiste Daroussin roffit_macro = mandoc_strdup(iv != 1 ||
325361d06d6bSBaptiste Daroussin strcmp(buf->buf + pos, "an-trap") ?
325461d06d6bSBaptiste Daroussin buf->buf + pos : "br");
325561d06d6bSBaptiste Daroussin return ROFF_IGN;
325661d06d6bSBaptiste Daroussin }
325761d06d6bSBaptiste Daroussin
32587295610fSBaptiste Daroussin static int
roff_Dd(ROFF_ARGS)325961d06d6bSBaptiste Daroussin roff_Dd(ROFF_ARGS)
326061d06d6bSBaptiste Daroussin {
326161d06d6bSBaptiste Daroussin int mask;
326261d06d6bSBaptiste Daroussin enum roff_tok t, te;
326361d06d6bSBaptiste Daroussin
326461d06d6bSBaptiste Daroussin switch (tok) {
326561d06d6bSBaptiste Daroussin case ROFF_Dd:
326661d06d6bSBaptiste Daroussin tok = MDOC_Dd;
326761d06d6bSBaptiste Daroussin te = MDOC_MAX;
326861d06d6bSBaptiste Daroussin if (r->format == 0)
326961d06d6bSBaptiste Daroussin r->format = MPARSE_MDOC;
327061d06d6bSBaptiste Daroussin mask = MPARSE_MDOC | MPARSE_QUICK;
327161d06d6bSBaptiste Daroussin break;
327261d06d6bSBaptiste Daroussin case ROFF_TH:
327361d06d6bSBaptiste Daroussin tok = MAN_TH;
327461d06d6bSBaptiste Daroussin te = MAN_MAX;
327561d06d6bSBaptiste Daroussin if (r->format == 0)
327661d06d6bSBaptiste Daroussin r->format = MPARSE_MAN;
327761d06d6bSBaptiste Daroussin mask = MPARSE_QUICK;
327861d06d6bSBaptiste Daroussin break;
327961d06d6bSBaptiste Daroussin default:
328061d06d6bSBaptiste Daroussin abort();
328161d06d6bSBaptiste Daroussin }
328261d06d6bSBaptiste Daroussin if ((r->options & mask) == 0)
328361d06d6bSBaptiste Daroussin for (t = tok; t < te; t++)
328461d06d6bSBaptiste Daroussin roff_setstr(r, roff_name[t], NULL, 0);
328561d06d6bSBaptiste Daroussin return ROFF_CONT;
328661d06d6bSBaptiste Daroussin }
328761d06d6bSBaptiste Daroussin
32887295610fSBaptiste Daroussin static int
roff_TE(ROFF_ARGS)328961d06d6bSBaptiste Daroussin roff_TE(ROFF_ARGS)
329061d06d6bSBaptiste Daroussin {
32917295610fSBaptiste Daroussin r->man->flags &= ~ROFF_NONOFILL;
329261d06d6bSBaptiste Daroussin if (r->tbl == NULL) {
32937295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "TE");
329461d06d6bSBaptiste Daroussin return ROFF_IGN;
329561d06d6bSBaptiste Daroussin }
32967295610fSBaptiste Daroussin if (tbl_end(r->tbl, 0) == 0) {
329761d06d6bSBaptiste Daroussin r->tbl = NULL;
329861d06d6bSBaptiste Daroussin free(buf->buf);
329961d06d6bSBaptiste Daroussin buf->buf = mandoc_strdup(".sp");
330061d06d6bSBaptiste Daroussin buf->sz = 4;
330161d06d6bSBaptiste Daroussin *offs = 0;
330261d06d6bSBaptiste Daroussin return ROFF_REPARSE;
330361d06d6bSBaptiste Daroussin }
330461d06d6bSBaptiste Daroussin r->tbl = NULL;
330561d06d6bSBaptiste Daroussin return ROFF_IGN;
330661d06d6bSBaptiste Daroussin }
330761d06d6bSBaptiste Daroussin
33087295610fSBaptiste Daroussin static int
roff_T_(ROFF_ARGS)330961d06d6bSBaptiste Daroussin roff_T_(ROFF_ARGS)
331061d06d6bSBaptiste Daroussin {
331161d06d6bSBaptiste Daroussin
331261d06d6bSBaptiste Daroussin if (NULL == r->tbl)
33137295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "T&");
331461d06d6bSBaptiste Daroussin else
331561d06d6bSBaptiste Daroussin tbl_restart(ln, ppos, r->tbl);
331661d06d6bSBaptiste Daroussin
331761d06d6bSBaptiste Daroussin return ROFF_IGN;
331861d06d6bSBaptiste Daroussin }
331961d06d6bSBaptiste Daroussin
332061d06d6bSBaptiste Daroussin /*
332161d06d6bSBaptiste Daroussin * Handle in-line equation delimiters.
332261d06d6bSBaptiste Daroussin */
33237295610fSBaptiste Daroussin static int
roff_eqndelim(struct roff * r,struct buf * buf,int pos)332461d06d6bSBaptiste Daroussin roff_eqndelim(struct roff *r, struct buf *buf, int pos)
332561d06d6bSBaptiste Daroussin {
332661d06d6bSBaptiste Daroussin char *cp1, *cp2;
332761d06d6bSBaptiste Daroussin const char *bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr;
332861d06d6bSBaptiste Daroussin
332961d06d6bSBaptiste Daroussin /*
333061d06d6bSBaptiste Daroussin * Outside equations, look for an opening delimiter.
333161d06d6bSBaptiste Daroussin * If we are inside an equation, we already know it is
333261d06d6bSBaptiste Daroussin * in-line, or this function wouldn't have been called;
333361d06d6bSBaptiste Daroussin * so look for a closing delimiter.
333461d06d6bSBaptiste Daroussin */
333561d06d6bSBaptiste Daroussin
333661d06d6bSBaptiste Daroussin cp1 = buf->buf + pos;
333761d06d6bSBaptiste Daroussin cp2 = strchr(cp1, r->eqn == NULL ?
333861d06d6bSBaptiste Daroussin r->last_eqn->odelim : r->last_eqn->cdelim);
333961d06d6bSBaptiste Daroussin if (cp2 == NULL)
334061d06d6bSBaptiste Daroussin return ROFF_CONT;
334161d06d6bSBaptiste Daroussin
334261d06d6bSBaptiste Daroussin *cp2++ = '\0';
334361d06d6bSBaptiste Daroussin bef_pr = bef_nl = aft_nl = aft_pr = "";
334461d06d6bSBaptiste Daroussin
334561d06d6bSBaptiste Daroussin /* Handle preceding text, protecting whitespace. */
334661d06d6bSBaptiste Daroussin
334761d06d6bSBaptiste Daroussin if (*buf->buf != '\0') {
334861d06d6bSBaptiste Daroussin if (r->eqn == NULL)
334961d06d6bSBaptiste Daroussin bef_pr = "\\&";
335061d06d6bSBaptiste Daroussin bef_nl = "\n";
335161d06d6bSBaptiste Daroussin }
335261d06d6bSBaptiste Daroussin
335361d06d6bSBaptiste Daroussin /*
335461d06d6bSBaptiste Daroussin * Prepare replacing the delimiter with an equation macro
335561d06d6bSBaptiste Daroussin * and drop leading white space from the equation.
335661d06d6bSBaptiste Daroussin */
335761d06d6bSBaptiste Daroussin
335861d06d6bSBaptiste Daroussin if (r->eqn == NULL) {
335961d06d6bSBaptiste Daroussin while (*cp2 == ' ')
336061d06d6bSBaptiste Daroussin cp2++;
336161d06d6bSBaptiste Daroussin mac = ".EQ";
336261d06d6bSBaptiste Daroussin } else
336361d06d6bSBaptiste Daroussin mac = ".EN";
336461d06d6bSBaptiste Daroussin
336561d06d6bSBaptiste Daroussin /* Handle following text, protecting whitespace. */
336661d06d6bSBaptiste Daroussin
336761d06d6bSBaptiste Daroussin if (*cp2 != '\0') {
336861d06d6bSBaptiste Daroussin aft_nl = "\n";
336961d06d6bSBaptiste Daroussin if (r->eqn != NULL)
337061d06d6bSBaptiste Daroussin aft_pr = "\\&";
337161d06d6bSBaptiste Daroussin }
337261d06d6bSBaptiste Daroussin
337361d06d6bSBaptiste Daroussin /* Do the actual replacement. */
337461d06d6bSBaptiste Daroussin
337561d06d6bSBaptiste Daroussin buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf,
337661d06d6bSBaptiste Daroussin bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1;
337761d06d6bSBaptiste Daroussin free(buf->buf);
337861d06d6bSBaptiste Daroussin buf->buf = cp1;
337961d06d6bSBaptiste Daroussin
338061d06d6bSBaptiste Daroussin /* Toggle the in-line state of the eqn subsystem. */
338161d06d6bSBaptiste Daroussin
338261d06d6bSBaptiste Daroussin r->eqn_inline = r->eqn == NULL;
338361d06d6bSBaptiste Daroussin return ROFF_REPARSE;
338461d06d6bSBaptiste Daroussin }
338561d06d6bSBaptiste Daroussin
33867295610fSBaptiste Daroussin static int
roff_EQ(ROFF_ARGS)338761d06d6bSBaptiste Daroussin roff_EQ(ROFF_ARGS)
338861d06d6bSBaptiste Daroussin {
338961d06d6bSBaptiste Daroussin struct roff_node *n;
339061d06d6bSBaptiste Daroussin
33917295610fSBaptiste Daroussin if (r->man->meta.macroset == MACROSET_MAN)
339261d06d6bSBaptiste Daroussin man_breakscope(r->man, ROFF_EQ);
339361d06d6bSBaptiste Daroussin n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE);
339461d06d6bSBaptiste Daroussin if (ln > r->man->last->line)
339561d06d6bSBaptiste Daroussin n->flags |= NODE_LINE;
33967295610fSBaptiste Daroussin n->eqn = eqn_box_new();
339761d06d6bSBaptiste Daroussin roff_node_append(r->man, n);
339861d06d6bSBaptiste Daroussin r->man->next = ROFF_NEXT_SIBLING;
339961d06d6bSBaptiste Daroussin
340061d06d6bSBaptiste Daroussin assert(r->eqn == NULL);
340161d06d6bSBaptiste Daroussin if (r->last_eqn == NULL)
34027295610fSBaptiste Daroussin r->last_eqn = eqn_alloc();
340361d06d6bSBaptiste Daroussin else
340461d06d6bSBaptiste Daroussin eqn_reset(r->last_eqn);
340561d06d6bSBaptiste Daroussin r->eqn = r->last_eqn;
340661d06d6bSBaptiste Daroussin r->eqn->node = n;
340761d06d6bSBaptiste Daroussin
340861d06d6bSBaptiste Daroussin if (buf->buf[pos] != '\0')
34097295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
341061d06d6bSBaptiste Daroussin ".EQ %s", buf->buf + pos);
341161d06d6bSBaptiste Daroussin
341261d06d6bSBaptiste Daroussin return ROFF_IGN;
341361d06d6bSBaptiste Daroussin }
341461d06d6bSBaptiste Daroussin
34157295610fSBaptiste Daroussin static int
roff_EN(ROFF_ARGS)341661d06d6bSBaptiste Daroussin roff_EN(ROFF_ARGS)
341761d06d6bSBaptiste Daroussin {
341861d06d6bSBaptiste Daroussin if (r->eqn != NULL) {
341961d06d6bSBaptiste Daroussin eqn_parse(r->eqn);
342061d06d6bSBaptiste Daroussin r->eqn = NULL;
342161d06d6bSBaptiste Daroussin } else
34227295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "EN");
342361d06d6bSBaptiste Daroussin if (buf->buf[pos] != '\0')
34247295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
342561d06d6bSBaptiste Daroussin "EN %s", buf->buf + pos);
342661d06d6bSBaptiste Daroussin return ROFF_IGN;
342761d06d6bSBaptiste Daroussin }
342861d06d6bSBaptiste Daroussin
34297295610fSBaptiste Daroussin static int
roff_TS(ROFF_ARGS)343061d06d6bSBaptiste Daroussin roff_TS(ROFF_ARGS)
343161d06d6bSBaptiste Daroussin {
343261d06d6bSBaptiste Daroussin if (r->tbl != NULL) {
34337295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_BROKEN, ln, ppos, "TS breaks TS");
34347295610fSBaptiste Daroussin tbl_end(r->tbl, 0);
343561d06d6bSBaptiste Daroussin }
34367295610fSBaptiste Daroussin r->man->flags |= ROFF_NONOFILL;
34377295610fSBaptiste Daroussin r->tbl = tbl_alloc(ppos, ln, r->last_tbl);
34387295610fSBaptiste Daroussin if (r->last_tbl == NULL)
343961d06d6bSBaptiste Daroussin r->first_tbl = r->tbl;
344061d06d6bSBaptiste Daroussin r->last_tbl = r->tbl;
344161d06d6bSBaptiste Daroussin return ROFF_IGN;
344261d06d6bSBaptiste Daroussin }
344361d06d6bSBaptiste Daroussin
34447295610fSBaptiste Daroussin static int
roff_noarg(ROFF_ARGS)34457295610fSBaptiste Daroussin roff_noarg(ROFF_ARGS)
34467295610fSBaptiste Daroussin {
34477295610fSBaptiste Daroussin if (r->man->flags & (MAN_BLINE | MAN_ELINE))
34487295610fSBaptiste Daroussin man_breakscope(r->man, tok);
34497295610fSBaptiste Daroussin if (tok == ROFF_brp)
34507295610fSBaptiste Daroussin tok = ROFF_br;
34517295610fSBaptiste Daroussin roff_elem_alloc(r->man, ln, ppos, tok);
34527295610fSBaptiste Daroussin if (buf->buf[pos] != '\0')
34537295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos,
34547295610fSBaptiste Daroussin "%s %s", roff_name[tok], buf->buf + pos);
34557295610fSBaptiste Daroussin if (tok == ROFF_nf)
34567295610fSBaptiste Daroussin r->man->flags |= ROFF_NOFILL;
34577295610fSBaptiste Daroussin else if (tok == ROFF_fi)
34587295610fSBaptiste Daroussin r->man->flags &= ~ROFF_NOFILL;
34597295610fSBaptiste Daroussin r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
34607295610fSBaptiste Daroussin r->man->next = ROFF_NEXT_SIBLING;
34617295610fSBaptiste Daroussin return ROFF_IGN;
34627295610fSBaptiste Daroussin }
34637295610fSBaptiste Daroussin
34647295610fSBaptiste Daroussin static int
roff_onearg(ROFF_ARGS)346561d06d6bSBaptiste Daroussin roff_onearg(ROFF_ARGS)
346661d06d6bSBaptiste Daroussin {
346761d06d6bSBaptiste Daroussin struct roff_node *n;
346861d06d6bSBaptiste Daroussin char *cp;
346961d06d6bSBaptiste Daroussin int npos;
347061d06d6bSBaptiste Daroussin
347161d06d6bSBaptiste Daroussin if (r->man->flags & (MAN_BLINE | MAN_ELINE) &&
347261d06d6bSBaptiste Daroussin (tok == ROFF_ce || tok == ROFF_rj || tok == ROFF_sp ||
347361d06d6bSBaptiste Daroussin tok == ROFF_ti))
347461d06d6bSBaptiste Daroussin man_breakscope(r->man, tok);
347561d06d6bSBaptiste Daroussin
347661d06d6bSBaptiste Daroussin if (roffce_node != NULL && (tok == ROFF_ce || tok == ROFF_rj)) {
347761d06d6bSBaptiste Daroussin r->man->last = roffce_node;
347861d06d6bSBaptiste Daroussin r->man->next = ROFF_NEXT_SIBLING;
347961d06d6bSBaptiste Daroussin }
348061d06d6bSBaptiste Daroussin
348161d06d6bSBaptiste Daroussin roff_elem_alloc(r->man, ln, ppos, tok);
348261d06d6bSBaptiste Daroussin n = r->man->last;
348361d06d6bSBaptiste Daroussin
348461d06d6bSBaptiste Daroussin cp = buf->buf + pos;
348561d06d6bSBaptiste Daroussin if (*cp != '\0') {
348661d06d6bSBaptiste Daroussin while (*cp != '\0' && *cp != ' ')
348761d06d6bSBaptiste Daroussin cp++;
348861d06d6bSBaptiste Daroussin while (*cp == ' ')
348961d06d6bSBaptiste Daroussin *cp++ = '\0';
349061d06d6bSBaptiste Daroussin if (*cp != '\0')
34917295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_EXCESS,
34927295610fSBaptiste Daroussin ln, (int)(cp - buf->buf),
349361d06d6bSBaptiste Daroussin "%s ... %s", roff_name[tok], cp);
349461d06d6bSBaptiste Daroussin roff_word_alloc(r->man, ln, pos, buf->buf + pos);
349561d06d6bSBaptiste Daroussin }
349661d06d6bSBaptiste Daroussin
349761d06d6bSBaptiste Daroussin if (tok == ROFF_ce || tok == ROFF_rj) {
349861d06d6bSBaptiste Daroussin if (r->man->last->type == ROFFT_ELEM) {
349961d06d6bSBaptiste Daroussin roff_word_alloc(r->man, ln, pos, "1");
350061d06d6bSBaptiste Daroussin r->man->last->flags |= NODE_NOSRC;
350161d06d6bSBaptiste Daroussin }
350261d06d6bSBaptiste Daroussin npos = 0;
350361d06d6bSBaptiste Daroussin if (roff_evalnum(r, ln, r->man->last->string, &npos,
350461d06d6bSBaptiste Daroussin &roffce_lines, 0) == 0) {
35057295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_CE_NONUM,
35067295610fSBaptiste Daroussin ln, pos, "ce %s", buf->buf + pos);
350761d06d6bSBaptiste Daroussin roffce_lines = 1;
350861d06d6bSBaptiste Daroussin }
350961d06d6bSBaptiste Daroussin if (roffce_lines < 1) {
351061d06d6bSBaptiste Daroussin r->man->last = r->man->last->parent;
351161d06d6bSBaptiste Daroussin roffce_node = NULL;
351261d06d6bSBaptiste Daroussin roffce_lines = 0;
351361d06d6bSBaptiste Daroussin } else
351461d06d6bSBaptiste Daroussin roffce_node = r->man->last->parent;
351561d06d6bSBaptiste Daroussin } else {
351661d06d6bSBaptiste Daroussin n->flags |= NODE_VALID | NODE_ENDED;
351761d06d6bSBaptiste Daroussin r->man->last = n;
351861d06d6bSBaptiste Daroussin }
351961d06d6bSBaptiste Daroussin n->flags |= NODE_LINE;
352061d06d6bSBaptiste Daroussin r->man->next = ROFF_NEXT_SIBLING;
352161d06d6bSBaptiste Daroussin return ROFF_IGN;
352261d06d6bSBaptiste Daroussin }
352361d06d6bSBaptiste Daroussin
35247295610fSBaptiste Daroussin static int
roff_manyarg(ROFF_ARGS)352561d06d6bSBaptiste Daroussin roff_manyarg(ROFF_ARGS)
352661d06d6bSBaptiste Daroussin {
352761d06d6bSBaptiste Daroussin struct roff_node *n;
352861d06d6bSBaptiste Daroussin char *sp, *ep;
352961d06d6bSBaptiste Daroussin
353061d06d6bSBaptiste Daroussin roff_elem_alloc(r->man, ln, ppos, tok);
353161d06d6bSBaptiste Daroussin n = r->man->last;
353261d06d6bSBaptiste Daroussin
353361d06d6bSBaptiste Daroussin for (sp = ep = buf->buf + pos; *sp != '\0'; sp = ep) {
353461d06d6bSBaptiste Daroussin while (*ep != '\0' && *ep != ' ')
353561d06d6bSBaptiste Daroussin ep++;
353661d06d6bSBaptiste Daroussin while (*ep == ' ')
353761d06d6bSBaptiste Daroussin *ep++ = '\0';
353861d06d6bSBaptiste Daroussin roff_word_alloc(r->man, ln, sp - buf->buf, sp);
353961d06d6bSBaptiste Daroussin }
354061d06d6bSBaptiste Daroussin
354161d06d6bSBaptiste Daroussin n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
354261d06d6bSBaptiste Daroussin r->man->last = n;
354361d06d6bSBaptiste Daroussin r->man->next = ROFF_NEXT_SIBLING;
354461d06d6bSBaptiste Daroussin return ROFF_IGN;
354561d06d6bSBaptiste Daroussin }
354661d06d6bSBaptiste Daroussin
35477295610fSBaptiste Daroussin static int
roff_als(ROFF_ARGS)354861d06d6bSBaptiste Daroussin roff_als(ROFF_ARGS)
354961d06d6bSBaptiste Daroussin {
355061d06d6bSBaptiste Daroussin char *oldn, *newn, *end, *value;
355161d06d6bSBaptiste Daroussin size_t oldsz, newsz, valsz;
355261d06d6bSBaptiste Daroussin
355361d06d6bSBaptiste Daroussin newn = oldn = buf->buf + pos;
355461d06d6bSBaptiste Daroussin if (*newn == '\0')
355561d06d6bSBaptiste Daroussin return ROFF_IGN;
355661d06d6bSBaptiste Daroussin
355761d06d6bSBaptiste Daroussin newsz = roff_getname(r, &oldn, ln, pos);
35587295610fSBaptiste Daroussin if (newn[newsz] == '\\' || newn[newsz] == '\t' || *oldn == '\0')
355961d06d6bSBaptiste Daroussin return ROFF_IGN;
356061d06d6bSBaptiste Daroussin
356161d06d6bSBaptiste Daroussin end = oldn;
356261d06d6bSBaptiste Daroussin oldsz = roff_getname(r, &end, ln, oldn - buf->buf);
356361d06d6bSBaptiste Daroussin if (oldsz == 0)
356461d06d6bSBaptiste Daroussin return ROFF_IGN;
356561d06d6bSBaptiste Daroussin
35667295610fSBaptiste Daroussin valsz = mandoc_asprintf(&value, ".%.*s \\$@\\\"\n",
356761d06d6bSBaptiste Daroussin (int)oldsz, oldn);
356861d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, newn, newsz, value, valsz, 0);
356961d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
357061d06d6bSBaptiste Daroussin free(value);
357161d06d6bSBaptiste Daroussin return ROFF_IGN;
357261d06d6bSBaptiste Daroussin }
357361d06d6bSBaptiste Daroussin
357445a5aec3SBaptiste Daroussin /*
357545a5aec3SBaptiste Daroussin * The .break request only makes sense inside conditionals,
357645a5aec3SBaptiste Daroussin * and that case is already handled in roff_cond_sub().
357745a5aec3SBaptiste Daroussin */
357845a5aec3SBaptiste Daroussin static int
roff_break(ROFF_ARGS)357945a5aec3SBaptiste Daroussin roff_break(ROFF_ARGS)
358045a5aec3SBaptiste Daroussin {
358145a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, pos, "break");
358245a5aec3SBaptiste Daroussin return ROFF_IGN;
358345a5aec3SBaptiste Daroussin }
358445a5aec3SBaptiste Daroussin
35857295610fSBaptiste Daroussin static int
roff_cc(ROFF_ARGS)358661d06d6bSBaptiste Daroussin roff_cc(ROFF_ARGS)
358761d06d6bSBaptiste Daroussin {
358861d06d6bSBaptiste Daroussin const char *p;
358961d06d6bSBaptiste Daroussin
359061d06d6bSBaptiste Daroussin p = buf->buf + pos;
359161d06d6bSBaptiste Daroussin
359261d06d6bSBaptiste Daroussin if (*p == '\0' || (r->control = *p++) == '.')
359361d06d6bSBaptiste Daroussin r->control = '\0';
359461d06d6bSBaptiste Daroussin
359561d06d6bSBaptiste Daroussin if (*p != '\0')
35967295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_EXCESS,
359761d06d6bSBaptiste Daroussin ln, p - buf->buf, "cc ... %s", p);
359861d06d6bSBaptiste Daroussin
359961d06d6bSBaptiste Daroussin return ROFF_IGN;
360061d06d6bSBaptiste Daroussin }
360161d06d6bSBaptiste Daroussin
36027295610fSBaptiste Daroussin static int
roff_char(ROFF_ARGS)36037295610fSBaptiste Daroussin roff_char(ROFF_ARGS)
36047295610fSBaptiste Daroussin {
36057295610fSBaptiste Daroussin const char *p, *kp, *vp;
36067295610fSBaptiste Daroussin size_t ksz, vsz;
36077295610fSBaptiste Daroussin int font;
36087295610fSBaptiste Daroussin
36097295610fSBaptiste Daroussin /* Parse the character to be replaced. */
36107295610fSBaptiste Daroussin
36117295610fSBaptiste Daroussin kp = buf->buf + pos;
36127295610fSBaptiste Daroussin p = kp + 1;
36137295610fSBaptiste Daroussin if (*kp == '\0' || (*kp == '\\' &&
36147295610fSBaptiste Daroussin mandoc_escape(&p, NULL, NULL) != ESCAPE_SPECIAL) ||
36157295610fSBaptiste Daroussin (*p != ' ' && *p != '\0')) {
36167295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_CHAR_ARG, ln, pos, "char %s", kp);
36177295610fSBaptiste Daroussin return ROFF_IGN;
36187295610fSBaptiste Daroussin }
36197295610fSBaptiste Daroussin ksz = p - kp;
36207295610fSBaptiste Daroussin while (*p == ' ')
36217295610fSBaptiste Daroussin p++;
36227295610fSBaptiste Daroussin
36237295610fSBaptiste Daroussin /*
36247295610fSBaptiste Daroussin * If the replacement string contains a font escape sequence,
36257295610fSBaptiste Daroussin * we have to restore the font at the end.
36267295610fSBaptiste Daroussin */
36277295610fSBaptiste Daroussin
36287295610fSBaptiste Daroussin vp = p;
36297295610fSBaptiste Daroussin vsz = strlen(p);
36307295610fSBaptiste Daroussin font = 0;
36317295610fSBaptiste Daroussin while (*p != '\0') {
36327295610fSBaptiste Daroussin if (*p++ != '\\')
36337295610fSBaptiste Daroussin continue;
36347295610fSBaptiste Daroussin switch (mandoc_escape(&p, NULL, NULL)) {
36357295610fSBaptiste Daroussin case ESCAPE_FONT:
36367295610fSBaptiste Daroussin case ESCAPE_FONTROMAN:
36377295610fSBaptiste Daroussin case ESCAPE_FONTITALIC:
36387295610fSBaptiste Daroussin case ESCAPE_FONTBOLD:
36397295610fSBaptiste Daroussin case ESCAPE_FONTBI:
36406d38604fSBaptiste Daroussin case ESCAPE_FONTCR:
36416d38604fSBaptiste Daroussin case ESCAPE_FONTCB:
36426d38604fSBaptiste Daroussin case ESCAPE_FONTCI:
36437295610fSBaptiste Daroussin case ESCAPE_FONTPREV:
36447295610fSBaptiste Daroussin font++;
36457295610fSBaptiste Daroussin break;
36467295610fSBaptiste Daroussin default:
36477295610fSBaptiste Daroussin break;
36487295610fSBaptiste Daroussin }
36497295610fSBaptiste Daroussin }
36507295610fSBaptiste Daroussin if (font > 1)
36517295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_CHAR_FONT,
36527295610fSBaptiste Daroussin ln, (int)(vp - buf->buf), "%s", vp);
36537295610fSBaptiste Daroussin
36547295610fSBaptiste Daroussin /*
36557295610fSBaptiste Daroussin * Approximate the effect of .char using the .tr tables.
36567295610fSBaptiste Daroussin * XXX In groff, .char and .tr interact differently.
36577295610fSBaptiste Daroussin */
36587295610fSBaptiste Daroussin
36597295610fSBaptiste Daroussin if (ksz == 1) {
36607295610fSBaptiste Daroussin if (r->xtab == NULL)
36617295610fSBaptiste Daroussin r->xtab = mandoc_calloc(128, sizeof(*r->xtab));
36627295610fSBaptiste Daroussin assert((unsigned int)*kp < 128);
36637295610fSBaptiste Daroussin free(r->xtab[(int)*kp].p);
36647295610fSBaptiste Daroussin r->xtab[(int)*kp].sz = mandoc_asprintf(&r->xtab[(int)*kp].p,
36657295610fSBaptiste Daroussin "%s%s", vp, font ? "\fP" : "");
36667295610fSBaptiste Daroussin } else {
36677295610fSBaptiste Daroussin roff_setstrn(&r->xmbtab, kp, ksz, vp, vsz, 0);
36687295610fSBaptiste Daroussin if (font)
36697295610fSBaptiste Daroussin roff_setstrn(&r->xmbtab, kp, ksz, "\\fP", 3, 1);
36707295610fSBaptiste Daroussin }
36717295610fSBaptiste Daroussin return ROFF_IGN;
36727295610fSBaptiste Daroussin }
36737295610fSBaptiste Daroussin
36747295610fSBaptiste Daroussin static int
roff_ec(ROFF_ARGS)367561d06d6bSBaptiste Daroussin roff_ec(ROFF_ARGS)
367661d06d6bSBaptiste Daroussin {
367761d06d6bSBaptiste Daroussin const char *p;
367861d06d6bSBaptiste Daroussin
367961d06d6bSBaptiste Daroussin p = buf->buf + pos;
368061d06d6bSBaptiste Daroussin if (*p == '\0')
368161d06d6bSBaptiste Daroussin r->escape = '\\';
368261d06d6bSBaptiste Daroussin else {
368361d06d6bSBaptiste Daroussin r->escape = *p;
368461d06d6bSBaptiste Daroussin if (*++p != '\0')
36857295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_EXCESS, ln,
36867295610fSBaptiste Daroussin (int)(p - buf->buf), "ec ... %s", p);
368761d06d6bSBaptiste Daroussin }
368861d06d6bSBaptiste Daroussin return ROFF_IGN;
368961d06d6bSBaptiste Daroussin }
369061d06d6bSBaptiste Daroussin
36917295610fSBaptiste Daroussin static int
roff_eo(ROFF_ARGS)369261d06d6bSBaptiste Daroussin roff_eo(ROFF_ARGS)
369361d06d6bSBaptiste Daroussin {
369461d06d6bSBaptiste Daroussin r->escape = '\0';
369561d06d6bSBaptiste Daroussin if (buf->buf[pos] != '\0')
36967295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ARG_SKIP,
369761d06d6bSBaptiste Daroussin ln, pos, "eo %s", buf->buf + pos);
369861d06d6bSBaptiste Daroussin return ROFF_IGN;
369961d06d6bSBaptiste Daroussin }
370061d06d6bSBaptiste Daroussin
37017295610fSBaptiste Daroussin static int
roff_mc(ROFF_ARGS)3702*c1c95addSBrooks Davis roff_mc(ROFF_ARGS)
3703*c1c95addSBrooks Davis {
3704*c1c95addSBrooks Davis struct roff_node *n;
3705*c1c95addSBrooks Davis char *cp;
3706*c1c95addSBrooks Davis
3707*c1c95addSBrooks Davis /* Parse the first argument. */
3708*c1c95addSBrooks Davis
3709*c1c95addSBrooks Davis cp = buf->buf + pos;
3710*c1c95addSBrooks Davis if (*cp != '\0')
3711*c1c95addSBrooks Davis cp++;
3712*c1c95addSBrooks Davis if (buf->buf[pos] == '\\') {
3713*c1c95addSBrooks Davis switch (mandoc_escape((const char **)&cp, NULL, NULL)) {
3714*c1c95addSBrooks Davis case ESCAPE_SPECIAL:
3715*c1c95addSBrooks Davis case ESCAPE_UNICODE:
3716*c1c95addSBrooks Davis case ESCAPE_NUMBERED:
3717*c1c95addSBrooks Davis break;
3718*c1c95addSBrooks Davis default:
3719*c1c95addSBrooks Davis *cp = '\0';
3720*c1c95addSBrooks Davis mandoc_msg(MANDOCERR_MC_ESC, ln, pos,
3721*c1c95addSBrooks Davis "mc %s", buf->buf + pos);
3722*c1c95addSBrooks Davis buf->buf[pos] = '\0';
3723*c1c95addSBrooks Davis break;
3724*c1c95addSBrooks Davis }
3725*c1c95addSBrooks Davis }
3726*c1c95addSBrooks Davis
3727*c1c95addSBrooks Davis /* Ignore additional arguments. */
3728*c1c95addSBrooks Davis
3729*c1c95addSBrooks Davis while (*cp == ' ')
3730*c1c95addSBrooks Davis *cp++ = '\0';
3731*c1c95addSBrooks Davis if (*cp != '\0') {
3732*c1c95addSBrooks Davis mandoc_msg(MANDOCERR_MC_DIST, ln, (int)(cp - buf->buf),
3733*c1c95addSBrooks Davis "mc ... %s", cp);
3734*c1c95addSBrooks Davis *cp = '\0';
3735*c1c95addSBrooks Davis }
3736*c1c95addSBrooks Davis
3737*c1c95addSBrooks Davis /* Create the .mc node. */
3738*c1c95addSBrooks Davis
3739*c1c95addSBrooks Davis roff_elem_alloc(r->man, ln, ppos, tok);
3740*c1c95addSBrooks Davis n = r->man->last;
3741*c1c95addSBrooks Davis if (buf->buf[pos] != '\0')
3742*c1c95addSBrooks Davis roff_word_alloc(r->man, ln, pos, buf->buf + pos);
3743*c1c95addSBrooks Davis n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED;
3744*c1c95addSBrooks Davis r->man->last = n;
3745*c1c95addSBrooks Davis r->man->next = ROFF_NEXT_SIBLING;
3746*c1c95addSBrooks Davis return ROFF_IGN;
3747*c1c95addSBrooks Davis }
3748*c1c95addSBrooks Davis
3749*c1c95addSBrooks Davis static int
roff_nop(ROFF_ARGS)37507295610fSBaptiste Daroussin roff_nop(ROFF_ARGS)
37517295610fSBaptiste Daroussin {
37527295610fSBaptiste Daroussin while (buf->buf[pos] == ' ')
37537295610fSBaptiste Daroussin pos++;
37547295610fSBaptiste Daroussin *offs = pos;
37557295610fSBaptiste Daroussin return ROFF_RERUN;
37567295610fSBaptiste Daroussin }
37577295610fSBaptiste Daroussin
37587295610fSBaptiste Daroussin static int
roff_tr(ROFF_ARGS)375961d06d6bSBaptiste Daroussin roff_tr(ROFF_ARGS)
376061d06d6bSBaptiste Daroussin {
376161d06d6bSBaptiste Daroussin const char *p, *first, *second;
376261d06d6bSBaptiste Daroussin size_t fsz, ssz;
376361d06d6bSBaptiste Daroussin
376461d06d6bSBaptiste Daroussin p = buf->buf + pos;
376561d06d6bSBaptiste Daroussin
376661d06d6bSBaptiste Daroussin if (*p == '\0') {
37677295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_EMPTY, ln, ppos, "tr");
376861d06d6bSBaptiste Daroussin return ROFF_IGN;
376961d06d6bSBaptiste Daroussin }
377061d06d6bSBaptiste Daroussin
377161d06d6bSBaptiste Daroussin while (*p != '\0') {
377261d06d6bSBaptiste Daroussin fsz = ssz = 1;
377361d06d6bSBaptiste Daroussin
377461d06d6bSBaptiste Daroussin first = p++;
377561d06d6bSBaptiste Daroussin if (*first == '\\') {
3776*c1c95addSBrooks Davis if (mandoc_escape(&p, NULL, NULL) == ESCAPE_ERROR)
377761d06d6bSBaptiste Daroussin return ROFF_IGN;
377861d06d6bSBaptiste Daroussin fsz = (size_t)(p - first);
377961d06d6bSBaptiste Daroussin }
378061d06d6bSBaptiste Daroussin
378161d06d6bSBaptiste Daroussin second = p++;
378261d06d6bSBaptiste Daroussin if (*second == '\\') {
3783*c1c95addSBrooks Davis if (mandoc_escape(&p, NULL, NULL) == ESCAPE_ERROR)
378461d06d6bSBaptiste Daroussin return ROFF_IGN;
378561d06d6bSBaptiste Daroussin ssz = (size_t)(p - second);
378661d06d6bSBaptiste Daroussin } else if (*second == '\0') {
37877295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TR_ODD, ln,
37887295610fSBaptiste Daroussin (int)(first - buf->buf), "tr %s", first);
378961d06d6bSBaptiste Daroussin second = " ";
379061d06d6bSBaptiste Daroussin p--;
379161d06d6bSBaptiste Daroussin }
379261d06d6bSBaptiste Daroussin
379361d06d6bSBaptiste Daroussin if (fsz > 1) {
379461d06d6bSBaptiste Daroussin roff_setstrn(&r->xmbtab, first, fsz,
379561d06d6bSBaptiste Daroussin second, ssz, 0);
379661d06d6bSBaptiste Daroussin continue;
379761d06d6bSBaptiste Daroussin }
379861d06d6bSBaptiste Daroussin
379961d06d6bSBaptiste Daroussin if (r->xtab == NULL)
380061d06d6bSBaptiste Daroussin r->xtab = mandoc_calloc(128,
380161d06d6bSBaptiste Daroussin sizeof(struct roffstr));
380261d06d6bSBaptiste Daroussin
380361d06d6bSBaptiste Daroussin free(r->xtab[(int)*first].p);
380461d06d6bSBaptiste Daroussin r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
380561d06d6bSBaptiste Daroussin r->xtab[(int)*first].sz = ssz;
380661d06d6bSBaptiste Daroussin }
380761d06d6bSBaptiste Daroussin
380861d06d6bSBaptiste Daroussin return ROFF_IGN;
380961d06d6bSBaptiste Daroussin }
381061d06d6bSBaptiste Daroussin
38117295610fSBaptiste Daroussin /*
38127295610fSBaptiste Daroussin * Implementation of the .return request.
38137295610fSBaptiste Daroussin * There is no need to call roff_userret() from here.
38147295610fSBaptiste Daroussin * The read module will call that after rewinding the reader stack
38157295610fSBaptiste Daroussin * to the place from where the current macro was called.
38167295610fSBaptiste Daroussin */
38177295610fSBaptiste Daroussin static int
roff_return(ROFF_ARGS)38187295610fSBaptiste Daroussin roff_return(ROFF_ARGS)
38197295610fSBaptiste Daroussin {
38207295610fSBaptiste Daroussin if (r->mstackpos >= 0)
38217295610fSBaptiste Daroussin return ROFF_IGN | ROFF_USERRET;
38227295610fSBaptiste Daroussin
38237295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "return");
38247295610fSBaptiste Daroussin return ROFF_IGN;
38257295610fSBaptiste Daroussin }
38267295610fSBaptiste Daroussin
38277295610fSBaptiste Daroussin static int
roff_rn(ROFF_ARGS)382861d06d6bSBaptiste Daroussin roff_rn(ROFF_ARGS)
382961d06d6bSBaptiste Daroussin {
383061d06d6bSBaptiste Daroussin const char *value;
383161d06d6bSBaptiste Daroussin char *oldn, *newn, *end;
383261d06d6bSBaptiste Daroussin size_t oldsz, newsz;
383361d06d6bSBaptiste Daroussin int deftype;
383461d06d6bSBaptiste Daroussin
383561d06d6bSBaptiste Daroussin oldn = newn = buf->buf + pos;
383661d06d6bSBaptiste Daroussin if (*oldn == '\0')
383761d06d6bSBaptiste Daroussin return ROFF_IGN;
383861d06d6bSBaptiste Daroussin
383961d06d6bSBaptiste Daroussin oldsz = roff_getname(r, &newn, ln, pos);
38407295610fSBaptiste Daroussin if (oldn[oldsz] == '\\' || oldn[oldsz] == '\t' || *newn == '\0')
384161d06d6bSBaptiste Daroussin return ROFF_IGN;
384261d06d6bSBaptiste Daroussin
384361d06d6bSBaptiste Daroussin end = newn;
384461d06d6bSBaptiste Daroussin newsz = roff_getname(r, &end, ln, newn - buf->buf);
384561d06d6bSBaptiste Daroussin if (newsz == 0)
384661d06d6bSBaptiste Daroussin return ROFF_IGN;
384761d06d6bSBaptiste Daroussin
384861d06d6bSBaptiste Daroussin deftype = ROFFDEF_ANY;
384961d06d6bSBaptiste Daroussin value = roff_getstrn(r, oldn, oldsz, &deftype);
385061d06d6bSBaptiste Daroussin switch (deftype) {
385161d06d6bSBaptiste Daroussin case ROFFDEF_USER:
385261d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
385361d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, oldn, oldsz, NULL, 0, 0);
385461d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
385561d06d6bSBaptiste Daroussin break;
385661d06d6bSBaptiste Daroussin case ROFFDEF_PRE:
385761d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0);
385861d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
385961d06d6bSBaptiste Daroussin break;
386061d06d6bSBaptiste Daroussin case ROFFDEF_REN:
386161d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, newn, newsz, value, strlen(value), 0);
386261d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, oldn, oldsz, NULL, 0, 0);
386361d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
386461d06d6bSBaptiste Daroussin break;
386561d06d6bSBaptiste Daroussin case ROFFDEF_STD:
386661d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, newn, newsz, oldn, oldsz, 0);
386761d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
386861d06d6bSBaptiste Daroussin break;
386961d06d6bSBaptiste Daroussin default:
387061d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0);
387161d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0);
387261d06d6bSBaptiste Daroussin break;
387361d06d6bSBaptiste Daroussin }
387461d06d6bSBaptiste Daroussin return ROFF_IGN;
387561d06d6bSBaptiste Daroussin }
387661d06d6bSBaptiste Daroussin
38777295610fSBaptiste Daroussin static int
roff_shift(ROFF_ARGS)38787295610fSBaptiste Daroussin roff_shift(ROFF_ARGS)
38797295610fSBaptiste Daroussin {
38807295610fSBaptiste Daroussin struct mctx *ctx;
3881*c1c95addSBrooks Davis int argpos, levels, i;
38827295610fSBaptiste Daroussin
3883*c1c95addSBrooks Davis argpos = pos;
38847295610fSBaptiste Daroussin levels = 1;
38857295610fSBaptiste Daroussin if (buf->buf[pos] != '\0' &&
38867295610fSBaptiste Daroussin roff_evalnum(r, ln, buf->buf, &pos, &levels, 0) == 0) {
38877295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_CE_NONUM,
38887295610fSBaptiste Daroussin ln, pos, "shift %s", buf->buf + pos);
38897295610fSBaptiste Daroussin levels = 1;
38907295610fSBaptiste Daroussin }
38917295610fSBaptiste Daroussin if (r->mstackpos < 0) {
38927295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "shift");
38937295610fSBaptiste Daroussin return ROFF_IGN;
38947295610fSBaptiste Daroussin }
38957295610fSBaptiste Daroussin ctx = r->mstack + r->mstackpos;
38967295610fSBaptiste Daroussin if (levels > ctx->argc) {
38977295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_SHIFT,
3898*c1c95addSBrooks Davis ln, argpos, "%d, but max is %d", levels, ctx->argc);
38997295610fSBaptiste Daroussin levels = ctx->argc;
39007295610fSBaptiste Daroussin }
3901*c1c95addSBrooks Davis if (levels < 0) {
3902*c1c95addSBrooks Davis mandoc_msg(MANDOCERR_ARG_NEG, ln, argpos, "shift %d", levels);
3903*c1c95addSBrooks Davis levels = 0;
3904*c1c95addSBrooks Davis }
39057295610fSBaptiste Daroussin if (levels == 0)
39067295610fSBaptiste Daroussin return ROFF_IGN;
39077295610fSBaptiste Daroussin for (i = 0; i < levels; i++)
39087295610fSBaptiste Daroussin free(ctx->argv[i]);
39097295610fSBaptiste Daroussin ctx->argc -= levels;
39107295610fSBaptiste Daroussin for (i = 0; i < ctx->argc; i++)
39117295610fSBaptiste Daroussin ctx->argv[i] = ctx->argv[i + levels];
39127295610fSBaptiste Daroussin return ROFF_IGN;
39137295610fSBaptiste Daroussin }
39147295610fSBaptiste Daroussin
39157295610fSBaptiste Daroussin static int
roff_so(ROFF_ARGS)391661d06d6bSBaptiste Daroussin roff_so(ROFF_ARGS)
391761d06d6bSBaptiste Daroussin {
391861d06d6bSBaptiste Daroussin char *name, *cp;
391961d06d6bSBaptiste Daroussin
392061d06d6bSBaptiste Daroussin name = buf->buf + pos;
39217295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_SO, ln, ppos, "so %s", name);
392261d06d6bSBaptiste Daroussin
392361d06d6bSBaptiste Daroussin /*
392461d06d6bSBaptiste Daroussin * Handle `so'. Be EXTREMELY careful, as we shouldn't be
392561d06d6bSBaptiste Daroussin * opening anything that's not in our cwd or anything beneath
392661d06d6bSBaptiste Daroussin * it. Thus, explicitly disallow traversing up the file-system
392761d06d6bSBaptiste Daroussin * or using absolute paths.
392861d06d6bSBaptiste Daroussin */
392961d06d6bSBaptiste Daroussin
393061d06d6bSBaptiste Daroussin if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) {
39317295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_SO_PATH, ln, ppos, ".so %s", name);
393261d06d6bSBaptiste Daroussin buf->sz = mandoc_asprintf(&cp,
393361d06d6bSBaptiste Daroussin ".sp\nSee the file %s.\n.sp", name) + 1;
393461d06d6bSBaptiste Daroussin free(buf->buf);
393561d06d6bSBaptiste Daroussin buf->buf = cp;
393661d06d6bSBaptiste Daroussin *offs = 0;
393761d06d6bSBaptiste Daroussin return ROFF_REPARSE;
393861d06d6bSBaptiste Daroussin }
393961d06d6bSBaptiste Daroussin
394061d06d6bSBaptiste Daroussin *offs = pos;
394161d06d6bSBaptiste Daroussin return ROFF_SO;
394261d06d6bSBaptiste Daroussin }
394361d06d6bSBaptiste Daroussin
394461d06d6bSBaptiste Daroussin /* --- user defined strings and macros ------------------------------------ */
394561d06d6bSBaptiste Daroussin
39467295610fSBaptiste Daroussin static int
roff_userdef(ROFF_ARGS)394761d06d6bSBaptiste Daroussin roff_userdef(ROFF_ARGS)
394861d06d6bSBaptiste Daroussin {
39497295610fSBaptiste Daroussin struct mctx *ctx;
39507295610fSBaptiste Daroussin char *arg, *ap, *dst, *src;
39517295610fSBaptiste Daroussin size_t sz;
395261d06d6bSBaptiste Daroussin
395345a5aec3SBaptiste Daroussin /* If the macro is empty, ignore it altogether. */
395445a5aec3SBaptiste Daroussin
395545a5aec3SBaptiste Daroussin if (*r->current_string == '\0')
395645a5aec3SBaptiste Daroussin return ROFF_IGN;
395745a5aec3SBaptiste Daroussin
39587295610fSBaptiste Daroussin /* Initialize a new macro stack context. */
395961d06d6bSBaptiste Daroussin
39607295610fSBaptiste Daroussin if (++r->mstackpos == r->mstacksz) {
39617295610fSBaptiste Daroussin r->mstack = mandoc_recallocarray(r->mstack,
39627295610fSBaptiste Daroussin r->mstacksz, r->mstacksz + 8, sizeof(*r->mstack));
39637295610fSBaptiste Daroussin r->mstacksz += 8;
396461d06d6bSBaptiste Daroussin }
39657295610fSBaptiste Daroussin ctx = r->mstack + r->mstackpos;
39667295610fSBaptiste Daroussin ctx->argc = 0;
39677295610fSBaptiste Daroussin
39687295610fSBaptiste Daroussin /*
39697295610fSBaptiste Daroussin * Collect pointers to macro argument strings,
39707295610fSBaptiste Daroussin * NUL-terminating them and escaping quotes.
39717295610fSBaptiste Daroussin */
39727295610fSBaptiste Daroussin
39737295610fSBaptiste Daroussin src = buf->buf + pos;
39747295610fSBaptiste Daroussin while (*src != '\0') {
39757295610fSBaptiste Daroussin if (ctx->argc == ctx->argsz) {
39767295610fSBaptiste Daroussin ctx->argsz += 8;
39777295610fSBaptiste Daroussin ctx->argv = mandoc_reallocarray(ctx->argv,
39787295610fSBaptiste Daroussin ctx->argsz, sizeof(*ctx->argv));
397961d06d6bSBaptiste Daroussin }
39807295610fSBaptiste Daroussin arg = roff_getarg(r, &src, ln, &pos);
39817295610fSBaptiste Daroussin sz = 1; /* For the terminating NUL. */
39827295610fSBaptiste Daroussin for (ap = arg; *ap != '\0'; ap++)
39837295610fSBaptiste Daroussin sz += *ap == '"' ? 4 : 1;
39847295610fSBaptiste Daroussin ctx->argv[ctx->argc++] = dst = mandoc_malloc(sz);
39857295610fSBaptiste Daroussin for (ap = arg; *ap != '\0'; ap++) {
398661d06d6bSBaptiste Daroussin if (*ap == '"') {
39877295610fSBaptiste Daroussin memcpy(dst, "\\(dq", 4);
39887295610fSBaptiste Daroussin dst += 4;
398961d06d6bSBaptiste Daroussin } else
39907295610fSBaptiste Daroussin *dst++ = *ap;
399161d06d6bSBaptiste Daroussin }
39927295610fSBaptiste Daroussin *dst = '\0';
39937295610fSBaptiste Daroussin free(arg);
399461d06d6bSBaptiste Daroussin }
399561d06d6bSBaptiste Daroussin
39967295610fSBaptiste Daroussin /* Replace the macro invocation by the macro definition. */
399761d06d6bSBaptiste Daroussin
399861d06d6bSBaptiste Daroussin free(buf->buf);
39997295610fSBaptiste Daroussin buf->buf = mandoc_strdup(r->current_string);
40007295610fSBaptiste Daroussin buf->sz = strlen(buf->buf) + 1;
400161d06d6bSBaptiste Daroussin *offs = 0;
400261d06d6bSBaptiste Daroussin
400345a5aec3SBaptiste Daroussin return buf->buf[buf->sz - 2] == '\n' ?
40047295610fSBaptiste Daroussin ROFF_REPARSE | ROFF_USERCALL : ROFF_IGN | ROFF_APPEND;
400561d06d6bSBaptiste Daroussin }
400661d06d6bSBaptiste Daroussin
400761d06d6bSBaptiste Daroussin /*
400861d06d6bSBaptiste Daroussin * Calling a high-level macro that was renamed with .rn.
400961d06d6bSBaptiste Daroussin * r->current_string has already been set up by roff_parse().
401061d06d6bSBaptiste Daroussin */
40117295610fSBaptiste Daroussin static int
roff_renamed(ROFF_ARGS)401261d06d6bSBaptiste Daroussin roff_renamed(ROFF_ARGS)
401361d06d6bSBaptiste Daroussin {
401461d06d6bSBaptiste Daroussin char *nbuf;
401561d06d6bSBaptiste Daroussin
401661d06d6bSBaptiste Daroussin buf->sz = mandoc_asprintf(&nbuf, ".%s%s%s", r->current_string,
401761d06d6bSBaptiste Daroussin buf->buf[pos] == '\0' ? "" : " ", buf->buf + pos) + 1;
401861d06d6bSBaptiste Daroussin free(buf->buf);
401961d06d6bSBaptiste Daroussin buf->buf = nbuf;
402061d06d6bSBaptiste Daroussin *offs = 0;
402161d06d6bSBaptiste Daroussin return ROFF_CONT;
402261d06d6bSBaptiste Daroussin }
402361d06d6bSBaptiste Daroussin
40247295610fSBaptiste Daroussin /*
40257295610fSBaptiste Daroussin * Measure the length in bytes of the roff identifier at *cpp
40267295610fSBaptiste Daroussin * and advance the pointer to the next word.
40277295610fSBaptiste Daroussin */
402861d06d6bSBaptiste Daroussin static size_t
roff_getname(struct roff * r,char ** cpp,int ln,int pos)402961d06d6bSBaptiste Daroussin roff_getname(struct roff *r, char **cpp, int ln, int pos)
403061d06d6bSBaptiste Daroussin {
403161d06d6bSBaptiste Daroussin char *name, *cp;
4032*c1c95addSBrooks Davis int namesz, inam, iend;
403361d06d6bSBaptiste Daroussin
403461d06d6bSBaptiste Daroussin name = *cpp;
40357295610fSBaptiste Daroussin if (*name == '\0')
403661d06d6bSBaptiste Daroussin return 0;
403761d06d6bSBaptiste Daroussin
40387295610fSBaptiste Daroussin /* Advance cp to the byte after the end of the name. */
40397295610fSBaptiste Daroussin
4040*c1c95addSBrooks Davis cp = name;
4041*c1c95addSBrooks Davis namesz = 0;
4042*c1c95addSBrooks Davis for (;;) {
40437295610fSBaptiste Daroussin if (*cp == '\0')
40447295610fSBaptiste Daroussin break;
40457295610fSBaptiste Daroussin if (*cp == ' ' || *cp == '\t') {
40467295610fSBaptiste Daroussin cp++;
404761d06d6bSBaptiste Daroussin break;
404861d06d6bSBaptiste Daroussin }
4049*c1c95addSBrooks Davis if (*cp != '\\') {
4050*c1c95addSBrooks Davis if (name + namesz < cp) {
4051*c1c95addSBrooks Davis name[namesz] = *cp;
4052*c1c95addSBrooks Davis *cp = ' ';
4053*c1c95addSBrooks Davis }
4054*c1c95addSBrooks Davis namesz++;
4055*c1c95addSBrooks Davis cp++;
405661d06d6bSBaptiste Daroussin continue;
4057*c1c95addSBrooks Davis }
40587295610fSBaptiste Daroussin if (cp[1] == '{' || cp[1] == '}')
405961d06d6bSBaptiste Daroussin break;
4060*c1c95addSBrooks Davis if (roff_escape(cp, 0, 0, NULL, &inam,
4061*c1c95addSBrooks Davis NULL, NULL, &iend) != ESCAPE_UNDEF) {
40627295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_NAMESC, ln, pos,
4063*c1c95addSBrooks Davis "%.*s%.*s", namesz, name, iend, cp);
4064*c1c95addSBrooks Davis cp += iend;
406561d06d6bSBaptiste Daroussin break;
406661d06d6bSBaptiste Daroussin }
406761d06d6bSBaptiste Daroussin
4068*c1c95addSBrooks Davis /*
4069*c1c95addSBrooks Davis * In an identifier, \\, \., \G and so on
4070*c1c95addSBrooks Davis * are reduced to \, ., G and so on,
4071*c1c95addSBrooks Davis * vaguely similar to copy mode.
4072*c1c95addSBrooks Davis */
4073*c1c95addSBrooks Davis
4074*c1c95addSBrooks Davis name[namesz++] = cp[inam];
4075*c1c95addSBrooks Davis while (iend--) {
4076*c1c95addSBrooks Davis if (cp >= name + namesz)
4077*c1c95addSBrooks Davis *cp = ' ';
4078*c1c95addSBrooks Davis cp++;
4079*c1c95addSBrooks Davis }
4080*c1c95addSBrooks Davis }
4081*c1c95addSBrooks Davis
408261d06d6bSBaptiste Daroussin /* Read past spaces. */
40837295610fSBaptiste Daroussin
40847295610fSBaptiste Daroussin while (*cp == ' ')
408561d06d6bSBaptiste Daroussin cp++;
408661d06d6bSBaptiste Daroussin
408761d06d6bSBaptiste Daroussin *cpp = cp;
408861d06d6bSBaptiste Daroussin return namesz;
408961d06d6bSBaptiste Daroussin }
409061d06d6bSBaptiste Daroussin
409161d06d6bSBaptiste Daroussin /*
409261d06d6bSBaptiste Daroussin * Store *string into the user-defined string called *name.
409361d06d6bSBaptiste Daroussin * To clear an existing entry, call with (*r, *name, NULL, 0).
409461d06d6bSBaptiste Daroussin * append == 0: replace mode
409561d06d6bSBaptiste Daroussin * append == 1: single-line append mode
409661d06d6bSBaptiste Daroussin * append == 2: multiline append mode, append '\n' after each call
409761d06d6bSBaptiste Daroussin */
409861d06d6bSBaptiste Daroussin static void
roff_setstr(struct roff * r,const char * name,const char * string,int append)409961d06d6bSBaptiste Daroussin roff_setstr(struct roff *r, const char *name, const char *string,
410061d06d6bSBaptiste Daroussin int append)
410161d06d6bSBaptiste Daroussin {
410261d06d6bSBaptiste Daroussin size_t namesz;
410361d06d6bSBaptiste Daroussin
410461d06d6bSBaptiste Daroussin namesz = strlen(name);
410561d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, name, namesz, string,
410661d06d6bSBaptiste Daroussin string ? strlen(string) : 0, append);
410761d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0);
410861d06d6bSBaptiste Daroussin }
410961d06d6bSBaptiste Daroussin
411061d06d6bSBaptiste Daroussin static void
roff_setstrn(struct roffkv ** r,const char * name,size_t namesz,const char * string,size_t stringsz,int append)411161d06d6bSBaptiste Daroussin roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
411261d06d6bSBaptiste Daroussin const char *string, size_t stringsz, int append)
411361d06d6bSBaptiste Daroussin {
411461d06d6bSBaptiste Daroussin struct roffkv *n;
411561d06d6bSBaptiste Daroussin char *c;
411661d06d6bSBaptiste Daroussin int i;
411761d06d6bSBaptiste Daroussin size_t oldch, newch;
411861d06d6bSBaptiste Daroussin
411961d06d6bSBaptiste Daroussin /* Search for an existing string with the same name. */
412061d06d6bSBaptiste Daroussin n = *r;
412161d06d6bSBaptiste Daroussin
412261d06d6bSBaptiste Daroussin while (n && (namesz != n->key.sz ||
412361d06d6bSBaptiste Daroussin strncmp(n->key.p, name, namesz)))
412461d06d6bSBaptiste Daroussin n = n->next;
412561d06d6bSBaptiste Daroussin
412661d06d6bSBaptiste Daroussin if (NULL == n) {
412761d06d6bSBaptiste Daroussin /* Create a new string table entry. */
412861d06d6bSBaptiste Daroussin n = mandoc_malloc(sizeof(struct roffkv));
412961d06d6bSBaptiste Daroussin n->key.p = mandoc_strndup(name, namesz);
413061d06d6bSBaptiste Daroussin n->key.sz = namesz;
413161d06d6bSBaptiste Daroussin n->val.p = NULL;
413261d06d6bSBaptiste Daroussin n->val.sz = 0;
413361d06d6bSBaptiste Daroussin n->next = *r;
413461d06d6bSBaptiste Daroussin *r = n;
413561d06d6bSBaptiste Daroussin } else if (0 == append) {
413661d06d6bSBaptiste Daroussin free(n->val.p);
413761d06d6bSBaptiste Daroussin n->val.p = NULL;
413861d06d6bSBaptiste Daroussin n->val.sz = 0;
413961d06d6bSBaptiste Daroussin }
414061d06d6bSBaptiste Daroussin
414161d06d6bSBaptiste Daroussin if (NULL == string)
414261d06d6bSBaptiste Daroussin return;
414361d06d6bSBaptiste Daroussin
414461d06d6bSBaptiste Daroussin /*
414561d06d6bSBaptiste Daroussin * One additional byte for the '\n' in multiline mode,
414661d06d6bSBaptiste Daroussin * and one for the terminating '\0'.
414761d06d6bSBaptiste Daroussin */
414861d06d6bSBaptiste Daroussin newch = stringsz + (1 < append ? 2u : 1u);
414961d06d6bSBaptiste Daroussin
415061d06d6bSBaptiste Daroussin if (NULL == n->val.p) {
415161d06d6bSBaptiste Daroussin n->val.p = mandoc_malloc(newch);
415261d06d6bSBaptiste Daroussin *n->val.p = '\0';
415361d06d6bSBaptiste Daroussin oldch = 0;
415461d06d6bSBaptiste Daroussin } else {
415561d06d6bSBaptiste Daroussin oldch = n->val.sz;
415661d06d6bSBaptiste Daroussin n->val.p = mandoc_realloc(n->val.p, oldch + newch);
415761d06d6bSBaptiste Daroussin }
415861d06d6bSBaptiste Daroussin
415961d06d6bSBaptiste Daroussin /* Skip existing content in the destination buffer. */
416061d06d6bSBaptiste Daroussin c = n->val.p + (int)oldch;
416161d06d6bSBaptiste Daroussin
416261d06d6bSBaptiste Daroussin /* Append new content to the destination buffer. */
416361d06d6bSBaptiste Daroussin i = 0;
416461d06d6bSBaptiste Daroussin while (i < (int)stringsz) {
416561d06d6bSBaptiste Daroussin /*
416661d06d6bSBaptiste Daroussin * Rudimentary roff copy mode:
416761d06d6bSBaptiste Daroussin * Handle escaped backslashes.
416861d06d6bSBaptiste Daroussin */
416961d06d6bSBaptiste Daroussin if ('\\' == string[i] && '\\' == string[i + 1])
417061d06d6bSBaptiste Daroussin i++;
417161d06d6bSBaptiste Daroussin *c++ = string[i++];
417261d06d6bSBaptiste Daroussin }
417361d06d6bSBaptiste Daroussin
417461d06d6bSBaptiste Daroussin /* Append terminating bytes. */
417561d06d6bSBaptiste Daroussin if (1 < append)
417661d06d6bSBaptiste Daroussin *c++ = '\n';
417761d06d6bSBaptiste Daroussin
417861d06d6bSBaptiste Daroussin *c = '\0';
417961d06d6bSBaptiste Daroussin n->val.sz = (int)(c - n->val.p);
418061d06d6bSBaptiste Daroussin }
418161d06d6bSBaptiste Daroussin
418261d06d6bSBaptiste Daroussin static const char *
roff_getstrn(struct roff * r,const char * name,size_t len,int * deftype)418361d06d6bSBaptiste Daroussin roff_getstrn(struct roff *r, const char *name, size_t len,
418461d06d6bSBaptiste Daroussin int *deftype)
418561d06d6bSBaptiste Daroussin {
418661d06d6bSBaptiste Daroussin const struct roffkv *n;
418761d06d6bSBaptiste Daroussin int found, i;
418861d06d6bSBaptiste Daroussin enum roff_tok tok;
418961d06d6bSBaptiste Daroussin
419061d06d6bSBaptiste Daroussin found = 0;
419161d06d6bSBaptiste Daroussin for (n = r->strtab; n != NULL; n = n->next) {
419261d06d6bSBaptiste Daroussin if (strncmp(name, n->key.p, len) != 0 ||
419361d06d6bSBaptiste Daroussin n->key.p[len] != '\0' || n->val.p == NULL)
419461d06d6bSBaptiste Daroussin continue;
419561d06d6bSBaptiste Daroussin if (*deftype & ROFFDEF_USER) {
419661d06d6bSBaptiste Daroussin *deftype = ROFFDEF_USER;
419761d06d6bSBaptiste Daroussin return n->val.p;
419861d06d6bSBaptiste Daroussin } else {
419961d06d6bSBaptiste Daroussin found = 1;
420061d06d6bSBaptiste Daroussin break;
420161d06d6bSBaptiste Daroussin }
420261d06d6bSBaptiste Daroussin }
420361d06d6bSBaptiste Daroussin for (n = r->rentab; n != NULL; n = n->next) {
420461d06d6bSBaptiste Daroussin if (strncmp(name, n->key.p, len) != 0 ||
420561d06d6bSBaptiste Daroussin n->key.p[len] != '\0' || n->val.p == NULL)
420661d06d6bSBaptiste Daroussin continue;
420761d06d6bSBaptiste Daroussin if (*deftype & ROFFDEF_REN) {
420861d06d6bSBaptiste Daroussin *deftype = ROFFDEF_REN;
420961d06d6bSBaptiste Daroussin return n->val.p;
421061d06d6bSBaptiste Daroussin } else {
421161d06d6bSBaptiste Daroussin found = 1;
421261d06d6bSBaptiste Daroussin break;
421361d06d6bSBaptiste Daroussin }
421461d06d6bSBaptiste Daroussin }
421561d06d6bSBaptiste Daroussin for (i = 0; i < PREDEFS_MAX; i++) {
421661d06d6bSBaptiste Daroussin if (strncmp(name, predefs[i].name, len) != 0 ||
421761d06d6bSBaptiste Daroussin predefs[i].name[len] != '\0')
421861d06d6bSBaptiste Daroussin continue;
421961d06d6bSBaptiste Daroussin if (*deftype & ROFFDEF_PRE) {
422061d06d6bSBaptiste Daroussin *deftype = ROFFDEF_PRE;
422161d06d6bSBaptiste Daroussin return predefs[i].str;
422261d06d6bSBaptiste Daroussin } else {
422361d06d6bSBaptiste Daroussin found = 1;
422461d06d6bSBaptiste Daroussin break;
422561d06d6bSBaptiste Daroussin }
422661d06d6bSBaptiste Daroussin }
42277295610fSBaptiste Daroussin if (r->man->meta.macroset != MACROSET_MAN) {
422861d06d6bSBaptiste Daroussin for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) {
422961d06d6bSBaptiste Daroussin if (strncmp(name, roff_name[tok], len) != 0 ||
423061d06d6bSBaptiste Daroussin roff_name[tok][len] != '\0')
423161d06d6bSBaptiste Daroussin continue;
423261d06d6bSBaptiste Daroussin if (*deftype & ROFFDEF_STD) {
423361d06d6bSBaptiste Daroussin *deftype = ROFFDEF_STD;
423461d06d6bSBaptiste Daroussin return NULL;
423561d06d6bSBaptiste Daroussin } else {
423661d06d6bSBaptiste Daroussin found = 1;
423761d06d6bSBaptiste Daroussin break;
423861d06d6bSBaptiste Daroussin }
423961d06d6bSBaptiste Daroussin }
424061d06d6bSBaptiste Daroussin }
42417295610fSBaptiste Daroussin if (r->man->meta.macroset != MACROSET_MDOC) {
424261d06d6bSBaptiste Daroussin for (tok = MAN_TH; tok < MAN_MAX; tok++) {
424361d06d6bSBaptiste Daroussin if (strncmp(name, roff_name[tok], len) != 0 ||
424461d06d6bSBaptiste Daroussin roff_name[tok][len] != '\0')
424561d06d6bSBaptiste Daroussin continue;
424661d06d6bSBaptiste Daroussin if (*deftype & ROFFDEF_STD) {
424761d06d6bSBaptiste Daroussin *deftype = ROFFDEF_STD;
424861d06d6bSBaptiste Daroussin return NULL;
424961d06d6bSBaptiste Daroussin } else {
425061d06d6bSBaptiste Daroussin found = 1;
425161d06d6bSBaptiste Daroussin break;
425261d06d6bSBaptiste Daroussin }
425361d06d6bSBaptiste Daroussin }
425461d06d6bSBaptiste Daroussin }
425561d06d6bSBaptiste Daroussin
425661d06d6bSBaptiste Daroussin if (found == 0 && *deftype != ROFFDEF_ANY) {
425761d06d6bSBaptiste Daroussin if (*deftype & ROFFDEF_REN) {
425861d06d6bSBaptiste Daroussin /*
425961d06d6bSBaptiste Daroussin * This might still be a request,
426061d06d6bSBaptiste Daroussin * so do not treat it as undefined yet.
426161d06d6bSBaptiste Daroussin */
426261d06d6bSBaptiste Daroussin *deftype = ROFFDEF_UNDEF;
426361d06d6bSBaptiste Daroussin return NULL;
426461d06d6bSBaptiste Daroussin }
426561d06d6bSBaptiste Daroussin
426661d06d6bSBaptiste Daroussin /* Using an undefined string defines it to be empty. */
426761d06d6bSBaptiste Daroussin
426861d06d6bSBaptiste Daroussin roff_setstrn(&r->strtab, name, len, "", 0, 0);
426961d06d6bSBaptiste Daroussin roff_setstrn(&r->rentab, name, len, NULL, 0, 0);
427061d06d6bSBaptiste Daroussin }
427161d06d6bSBaptiste Daroussin
427261d06d6bSBaptiste Daroussin *deftype = 0;
427361d06d6bSBaptiste Daroussin return NULL;
427461d06d6bSBaptiste Daroussin }
427561d06d6bSBaptiste Daroussin
427661d06d6bSBaptiste Daroussin static void
roff_freestr(struct roffkv * r)427761d06d6bSBaptiste Daroussin roff_freestr(struct roffkv *r)
427861d06d6bSBaptiste Daroussin {
427961d06d6bSBaptiste Daroussin struct roffkv *n, *nn;
428061d06d6bSBaptiste Daroussin
428161d06d6bSBaptiste Daroussin for (n = r; n; n = nn) {
428261d06d6bSBaptiste Daroussin free(n->key.p);
428361d06d6bSBaptiste Daroussin free(n->val.p);
428461d06d6bSBaptiste Daroussin nn = n->next;
428561d06d6bSBaptiste Daroussin free(n);
428661d06d6bSBaptiste Daroussin }
428761d06d6bSBaptiste Daroussin }
428861d06d6bSBaptiste Daroussin
428961d06d6bSBaptiste Daroussin /* --- accessors and utility functions ------------------------------------ */
429061d06d6bSBaptiste Daroussin
429161d06d6bSBaptiste Daroussin /*
429261d06d6bSBaptiste Daroussin * Duplicate an input string, making the appropriate character
429361d06d6bSBaptiste Daroussin * conversations (as stipulated by `tr') along the way.
429461d06d6bSBaptiste Daroussin * Returns a heap-allocated string with all the replacements made.
429561d06d6bSBaptiste Daroussin */
429661d06d6bSBaptiste Daroussin char *
roff_strdup(const struct roff * r,const char * p)429761d06d6bSBaptiste Daroussin roff_strdup(const struct roff *r, const char *p)
429861d06d6bSBaptiste Daroussin {
429961d06d6bSBaptiste Daroussin const struct roffkv *cp;
430061d06d6bSBaptiste Daroussin char *res;
430161d06d6bSBaptiste Daroussin const char *pp;
430261d06d6bSBaptiste Daroussin size_t ssz, sz;
430361d06d6bSBaptiste Daroussin enum mandoc_esc esc;
430461d06d6bSBaptiste Daroussin
430561d06d6bSBaptiste Daroussin if (NULL == r->xmbtab && NULL == r->xtab)
430661d06d6bSBaptiste Daroussin return mandoc_strdup(p);
430761d06d6bSBaptiste Daroussin else if ('\0' == *p)
430861d06d6bSBaptiste Daroussin return mandoc_strdup("");
430961d06d6bSBaptiste Daroussin
431061d06d6bSBaptiste Daroussin /*
431161d06d6bSBaptiste Daroussin * Step through each character looking for term matches
431261d06d6bSBaptiste Daroussin * (remember that a `tr' can be invoked with an escape, which is
431361d06d6bSBaptiste Daroussin * a glyph but the escape is multi-character).
431461d06d6bSBaptiste Daroussin * We only do this if the character hash has been initialised
431561d06d6bSBaptiste Daroussin * and the string is >0 length.
431661d06d6bSBaptiste Daroussin */
431761d06d6bSBaptiste Daroussin
431861d06d6bSBaptiste Daroussin res = NULL;
431961d06d6bSBaptiste Daroussin ssz = 0;
432061d06d6bSBaptiste Daroussin
432161d06d6bSBaptiste Daroussin while ('\0' != *p) {
432261d06d6bSBaptiste Daroussin assert((unsigned int)*p < 128);
432361d06d6bSBaptiste Daroussin if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) {
432461d06d6bSBaptiste Daroussin sz = r->xtab[(int)*p].sz;
432561d06d6bSBaptiste Daroussin res = mandoc_realloc(res, ssz + sz + 1);
432661d06d6bSBaptiste Daroussin memcpy(res + ssz, r->xtab[(int)*p].p, sz);
432761d06d6bSBaptiste Daroussin ssz += sz;
432861d06d6bSBaptiste Daroussin p++;
432961d06d6bSBaptiste Daroussin continue;
433061d06d6bSBaptiste Daroussin } else if ('\\' != *p) {
433161d06d6bSBaptiste Daroussin res = mandoc_realloc(res, ssz + 2);
433261d06d6bSBaptiste Daroussin res[ssz++] = *p++;
433361d06d6bSBaptiste Daroussin continue;
433461d06d6bSBaptiste Daroussin }
433561d06d6bSBaptiste Daroussin
433661d06d6bSBaptiste Daroussin /* Search for term matches. */
433761d06d6bSBaptiste Daroussin for (cp = r->xmbtab; cp; cp = cp->next)
433861d06d6bSBaptiste Daroussin if (0 == strncmp(p, cp->key.p, cp->key.sz))
433961d06d6bSBaptiste Daroussin break;
434061d06d6bSBaptiste Daroussin
434161d06d6bSBaptiste Daroussin if (NULL != cp) {
434261d06d6bSBaptiste Daroussin /*
434361d06d6bSBaptiste Daroussin * A match has been found.
434461d06d6bSBaptiste Daroussin * Append the match to the array and move
434561d06d6bSBaptiste Daroussin * forward by its keysize.
434661d06d6bSBaptiste Daroussin */
434761d06d6bSBaptiste Daroussin res = mandoc_realloc(res,
434861d06d6bSBaptiste Daroussin ssz + cp->val.sz + 1);
434961d06d6bSBaptiste Daroussin memcpy(res + ssz, cp->val.p, cp->val.sz);
435061d06d6bSBaptiste Daroussin ssz += cp->val.sz;
435161d06d6bSBaptiste Daroussin p += (int)cp->key.sz;
435261d06d6bSBaptiste Daroussin continue;
435361d06d6bSBaptiste Daroussin }
435461d06d6bSBaptiste Daroussin
435561d06d6bSBaptiste Daroussin /*
435661d06d6bSBaptiste Daroussin * Handle escapes carefully: we need to copy
435761d06d6bSBaptiste Daroussin * over just the escape itself, or else we might
435861d06d6bSBaptiste Daroussin * do replacements within the escape itself.
435961d06d6bSBaptiste Daroussin * Make sure to pass along the bogus string.
436061d06d6bSBaptiste Daroussin */
436161d06d6bSBaptiste Daroussin pp = p++;
436261d06d6bSBaptiste Daroussin esc = mandoc_escape(&p, NULL, NULL);
436361d06d6bSBaptiste Daroussin if (ESCAPE_ERROR == esc) {
436461d06d6bSBaptiste Daroussin sz = strlen(pp);
436561d06d6bSBaptiste Daroussin res = mandoc_realloc(res, ssz + sz + 1);
436661d06d6bSBaptiste Daroussin memcpy(res + ssz, pp, sz);
436761d06d6bSBaptiste Daroussin break;
436861d06d6bSBaptiste Daroussin }
436961d06d6bSBaptiste Daroussin /*
437061d06d6bSBaptiste Daroussin * We bail out on bad escapes.
437161d06d6bSBaptiste Daroussin * No need to warn: we already did so when
43727295610fSBaptiste Daroussin * roff_expand() was called.
437361d06d6bSBaptiste Daroussin */
437461d06d6bSBaptiste Daroussin sz = (int)(p - pp);
437561d06d6bSBaptiste Daroussin res = mandoc_realloc(res, ssz + sz + 1);
437661d06d6bSBaptiste Daroussin memcpy(res + ssz, pp, sz);
437761d06d6bSBaptiste Daroussin ssz += sz;
437861d06d6bSBaptiste Daroussin }
437961d06d6bSBaptiste Daroussin
438061d06d6bSBaptiste Daroussin res[(int)ssz] = '\0';
438161d06d6bSBaptiste Daroussin return res;
438261d06d6bSBaptiste Daroussin }
438361d06d6bSBaptiste Daroussin
438461d06d6bSBaptiste Daroussin int
roff_getformat(const struct roff * r)438561d06d6bSBaptiste Daroussin roff_getformat(const struct roff *r)
438661d06d6bSBaptiste Daroussin {
438761d06d6bSBaptiste Daroussin
438861d06d6bSBaptiste Daroussin return r->format;
438961d06d6bSBaptiste Daroussin }
439061d06d6bSBaptiste Daroussin
439161d06d6bSBaptiste Daroussin /*
439261d06d6bSBaptiste Daroussin * Find out whether a line is a macro line or not.
439361d06d6bSBaptiste Daroussin * If it is, adjust the current position and return one; if it isn't,
439461d06d6bSBaptiste Daroussin * return zero and don't change the current position.
439561d06d6bSBaptiste Daroussin * If the control character has been set with `.cc', then let that grain
439661d06d6bSBaptiste Daroussin * precedence.
4397*c1c95addSBrooks Davis * This is slightly contrary to groff, where using the non-breaking
439861d06d6bSBaptiste Daroussin * control character when `cc' has been invoked will cause the
439961d06d6bSBaptiste Daroussin * non-breaking macro contents to be printed verbatim.
440061d06d6bSBaptiste Daroussin */
440161d06d6bSBaptiste Daroussin int
roff_getcontrol(const struct roff * r,const char * cp,int * ppos)440261d06d6bSBaptiste Daroussin roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
440361d06d6bSBaptiste Daroussin {
440461d06d6bSBaptiste Daroussin int pos;
440561d06d6bSBaptiste Daroussin
440661d06d6bSBaptiste Daroussin pos = *ppos;
440761d06d6bSBaptiste Daroussin
440861d06d6bSBaptiste Daroussin if (r->control != '\0' && cp[pos] == r->control)
440961d06d6bSBaptiste Daroussin pos++;
441061d06d6bSBaptiste Daroussin else if (r->control != '\0')
441161d06d6bSBaptiste Daroussin return 0;
441261d06d6bSBaptiste Daroussin else if ('\\' == cp[pos] && '.' == cp[pos + 1])
441361d06d6bSBaptiste Daroussin pos += 2;
441461d06d6bSBaptiste Daroussin else if ('.' == cp[pos] || '\'' == cp[pos])
441561d06d6bSBaptiste Daroussin pos++;
441661d06d6bSBaptiste Daroussin else
441761d06d6bSBaptiste Daroussin return 0;
441861d06d6bSBaptiste Daroussin
441961d06d6bSBaptiste Daroussin while (' ' == cp[pos] || '\t' == cp[pos])
442061d06d6bSBaptiste Daroussin pos++;
442161d06d6bSBaptiste Daroussin
442261d06d6bSBaptiste Daroussin *ppos = pos;
442361d06d6bSBaptiste Daroussin return 1;
442461d06d6bSBaptiste Daroussin }
4425