1*7c5f3f4eSschwarze /* $OpenBSD: roff.c,v 1.276 2025/01/06 18:48:13 schwarze Exp $ */ 2f673a3c7Sschwarze /* 3f4432377Sschwarze * Copyright (c) 2010-2015, 2017-2025 Ingo Schwarze <schwarze@openbsd.org> 46a6803e4Sschwarze * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 5f673a3c7Sschwarze * 6f673a3c7Sschwarze * Permission to use, copy, modify, and distribute this software for any 7f673a3c7Sschwarze * purpose with or without fee is hereby granted, provided that the above 8f673a3c7Sschwarze * copyright notice and this permission notice appear in all copies. 9f673a3c7Sschwarze * 10e53bb406Sschwarze * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 11f673a3c7Sschwarze * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12e53bb406Sschwarze * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 13f673a3c7Sschwarze * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14f673a3c7Sschwarze * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15f673a3c7Sschwarze * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16f673a3c7Sschwarze * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 176a6803e4Sschwarze * 186a6803e4Sschwarze * Implementation of the roff(7) parser for mandoc(1). 19f673a3c7Sschwarze */ 20f0d7487dSschwarze #include <sys/types.h> 21f0d7487dSschwarze 22f673a3c7Sschwarze #include <assert.h> 23b31af00dSschwarze #include <ctype.h> 2495bbf73aSschwarze #include <limits.h> 256050a3daSschwarze #include <stddef.h> 266050a3daSschwarze #include <stdint.h> 27a91e242cSschwarze #include <stdio.h> 28f673a3c7Sschwarze #include <stdlib.h> 29f673a3c7Sschwarze #include <string.h> 30f673a3c7Sschwarze 314f4f7972Sschwarze #include "mandoc_aux.h" 326050a3daSschwarze #include "mandoc_ohash.h" 33fae2491eSschwarze #include "mandoc.h" 34405987fcSschwarze #include "roff.h" 3599acaf1eSschwarze #include "mandoc_parse.h" 36769ee804Sschwarze #include "libmandoc.h" 37fa2127f9Sschwarze #include "roff_int.h" 38fb382a01Sschwarze #include "tbl_parse.h" 39abe31b56Sschwarze #include "eqn_parse.h" 40f673a3c7Sschwarze 411631389bSschwarze /* Maximum number of string expansions per line, to break infinite loops. */ 421631389bSschwarze #define EXPAND_LIMIT 1000 431631389bSschwarze 4407530f0aSschwarze /* Types of definitions of macros and strings. */ 4507530f0aSschwarze #define ROFFDEF_USER (1 << 1) /* User-defined. */ 4607530f0aSschwarze #define ROFFDEF_PRE (1 << 2) /* Predefined. */ 4707530f0aSschwarze #define ROFFDEF_REN (1 << 3) /* Renamed standard macro. */ 4807530f0aSschwarze #define ROFFDEF_STD (1 << 4) /* mdoc(7) or man(7) macro. */ 4907530f0aSschwarze #define ROFFDEF_ANY (ROFFDEF_USER | ROFFDEF_PRE | \ 5007530f0aSschwarze ROFFDEF_REN | ROFFDEF_STD) 510cdab9e9Sschwarze #define ROFFDEF_UNDEF (1 << 5) /* Completely undefined. */ 5207530f0aSschwarze 53fa2127f9Sschwarze /* --- data types --------------------------------------------------------- */ 54fa2127f9Sschwarze 55f8618d99Sschwarze /* 5604e980cbSschwarze * An incredibly-simple string buffer. 5704e980cbSschwarze */ 58769ee804Sschwarze struct roffstr { 5904e980cbSschwarze char *p; /* nil-terminated buffer */ 6004e980cbSschwarze size_t sz; /* saved strlen(p) */ 6104e980cbSschwarze }; 6204e980cbSschwarze 6304e980cbSschwarze /* 6404e980cbSschwarze * A key-value roffstr pair as part of a singly-linked list. 6504e980cbSschwarze */ 6604e980cbSschwarze struct roffkv { 6704e980cbSschwarze struct roffstr key; 6804e980cbSschwarze struct roffstr val; 6904e980cbSschwarze struct roffkv *next; /* next in list */ 70769ee804Sschwarze }; 71769ee804Sschwarze 7222881299Sschwarze /* 7322881299Sschwarze * A single number register as part of a singly-linked list. 7422881299Sschwarze */ 7522881299Sschwarze struct roffreg { 7622881299Sschwarze struct roffstr key; 77c542e817Sschwarze int val; 78dcd9eb04Sschwarze int step; 7922881299Sschwarze struct roffreg *next; 8022881299Sschwarze }; 8122881299Sschwarze 826050a3daSschwarze /* 836050a3daSschwarze * Association of request and macro names with token IDs. 846050a3daSschwarze */ 856050a3daSschwarze struct roffreq { 866050a3daSschwarze enum roff_tok tok; 876050a3daSschwarze char name[]; 886050a3daSschwarze }; 896050a3daSschwarze 903dc5225dSschwarze /* 913dc5225dSschwarze * A macro processing context. 923dc5225dSschwarze * More than one is needed when macro calls are nested. 933dc5225dSschwarze */ 943dc5225dSschwarze struct mctx { 953dc5225dSschwarze char **argv; 963dc5225dSschwarze int argc; 973dc5225dSschwarze int argsz; 983dc5225dSschwarze }; 993dc5225dSschwarze 100f673a3c7Sschwarze struct roff { 10129478532Sschwarze struct roff_man *man; /* mdoc or man parser */ 102f673a3c7Sschwarze struct roffnode *last; /* leaf of stack */ 1033dc5225dSschwarze struct mctx *mstack; /* stack of macro contexts */ 1044bc98c72Sschwarze int *rstack; /* stack of inverted `ie' values */ 1056050a3daSschwarze struct ohash *reqtab; /* request lookup table */ 10622881299Sschwarze struct roffreg *regtab; /* number registers */ 10704e980cbSschwarze struct roffkv *strtab; /* user-defined strings & macros */ 1087e291f10Sschwarze struct roffkv *rentab; /* renamed strings & macros */ 10904e980cbSschwarze struct roffkv *xmbtab; /* multi-byte trans table (`tr') */ 11004e980cbSschwarze struct roffstr *xtab; /* single-byte trans table (`tr') */ 111e53bb406Sschwarze const char *current_string; /* value of last called user macro */ 1122791bd1cSschwarze struct tbl_node *first_tbl; /* first table parsed */ 1132791bd1cSschwarze struct tbl_node *last_tbl; /* last table parsed */ 1142791bd1cSschwarze struct tbl_node *tbl; /* current table being parsed */ 115bf9acac6Sschwarze struct eqn_node *last_eqn; /* equation parser */ 116bf9acac6Sschwarze struct eqn_node *eqn; /* active equation parser */ 11757ca9bd5Sschwarze int eqn_inline; /* current equation is inline */ 1184bc98c72Sschwarze int options; /* parse options */ 1193dc5225dSschwarze int mstacksz; /* current size of mstack */ 1203dc5225dSschwarze int mstackpos; /* position in mstack */ 1214bc98c72Sschwarze int rstacksz; /* current size limit of rstack */ 1224bc98c72Sschwarze int rstackpos; /* position in rstack */ 123f0d7487dSschwarze int format; /* current file in mdoc or man format */ 1244bc98c72Sschwarze char control; /* control character */ 125be477484Sschwarze char escape; /* escape character */ 126f673a3c7Sschwarze }; 127f673a3c7Sschwarze 1287dd0ff50Sschwarze /* 1297dd0ff50Sschwarze * A macro definition, condition, or ignored block. 1307dd0ff50Sschwarze */ 131f673a3c7Sschwarze struct roffnode { 13214a309e3Sschwarze enum roff_tok tok; /* type of node */ 133f673a3c7Sschwarze struct roffnode *parent; /* up one in stack */ 134f673a3c7Sschwarze int line; /* parse line */ 135f673a3c7Sschwarze int col; /* parse col */ 136e53bb406Sschwarze char *name; /* node name, e.g. macro name */ 1377dd0ff50Sschwarze char *end; /* custom end macro of the block */ 1387dd0ff50Sschwarze int endspan; /* scope to: 1=eol 2=next line -1=\} */ 1397dd0ff50Sschwarze int rule; /* content is: 1=evaluated 0=skipped */ 140f673a3c7Sschwarze }; 141f673a3c7Sschwarze 142f673a3c7Sschwarze #define ROFF_ARGS struct roff *r, /* parse ctx */ \ 14314a309e3Sschwarze enum roff_tok tok, /* tok of macro */ \ 14428687875Sschwarze struct buf *buf, /* input buffer */ \ 145f673a3c7Sschwarze int ln, /* parse line */ \ 14662d9ccdbSschwarze int ppos, /* original pos in buffer */ \ 14762d9ccdbSschwarze int pos, /* current pos in buffer */ \ 14862d9ccdbSschwarze int *offs /* reset offset of buffer data */ 149f673a3c7Sschwarze 150b7f92c5fSschwarze typedef int (*roffproc)(ROFF_ARGS); 151f673a3c7Sschwarze 152f673a3c7Sschwarze struct roffmac { 15362d9ccdbSschwarze roffproc proc; /* process new macro */ 15462d9ccdbSschwarze roffproc text; /* process as child text of macro */ 15562d9ccdbSschwarze roffproc sub; /* process as child of macro */ 15662d9ccdbSschwarze int flags; 15762d9ccdbSschwarze #define ROFFMAC_STRUCT (1 << 0) /* always interpret */ 158f673a3c7Sschwarze }; 159f673a3c7Sschwarze 160a5e11edeSschwarze struct predef { 161a5e11edeSschwarze const char *name; /* predefined input name */ 162a5e11edeSschwarze const char *str; /* replacement symbol */ 163a5e11edeSschwarze }; 164a5e11edeSschwarze 165a5e11edeSschwarze #define PREDEF(__name, __str) \ 166a5e11edeSschwarze { (__name), (__str) }, 167a5e11edeSschwarze 168fa2127f9Sschwarze /* --- function prototypes ------------------------------------------------ */ 169fa2127f9Sschwarze 170b7f92c5fSschwarze static int roffnode_cleanscope(struct roff *); 171b7f92c5fSschwarze static int roffnode_pop(struct roff *); 17214a309e3Sschwarze static void roffnode_push(struct roff *, enum roff_tok, 17304e980cbSschwarze const char *, int, int); 174fb382a01Sschwarze static void roff_addtbl(struct roff_man *, int, struct tbl_node *); 175b7f92c5fSschwarze static int roff_als(ROFF_ARGS); 176b7f92c5fSschwarze static int roff_block(ROFF_ARGS); 177b7f92c5fSschwarze static int roff_block_text(ROFF_ARGS); 178b7f92c5fSschwarze static int roff_block_sub(ROFF_ARGS); 1797dd0ff50Sschwarze static int roff_break(ROFF_ARGS); 180b7f92c5fSschwarze static int roff_cblock(ROFF_ARGS); 181b7f92c5fSschwarze static int roff_cc(ROFF_ARGS); 182b7f92c5fSschwarze static int roff_ccond(struct roff *, int, int); 18329fcab3eSschwarze static int roff_char(ROFF_ARGS); 184b7f92c5fSschwarze static int roff_cond(ROFF_ARGS); 185fd9d9245Sschwarze static int roff_cond_checkend(ROFF_ARGS); 186b7f92c5fSschwarze static int roff_cond_text(ROFF_ARGS); 187b7f92c5fSschwarze static int roff_cond_sub(ROFF_ARGS); 188b7f92c5fSschwarze static int roff_ds(ROFF_ARGS); 189b7f92c5fSschwarze static int roff_ec(ROFF_ARGS); 190b7f92c5fSschwarze static int roff_eo(ROFF_ARGS); 191b7f92c5fSschwarze static int roff_eqndelim(struct roff *, struct buf *, int); 1926a6803e4Sschwarze static int roff_evalcond(struct roff *, int, char *, int *); 193f4432377Sschwarze static int roff_evalpar(int, const char *, int *, int *, 194f4432377Sschwarze char, int); 1951271a177Sschwarze static int roff_evalstrcond(const char *, int *); 196e94357f9Sschwarze static int roff_expand(struct roff *, struct buf *, 197e94357f9Sschwarze int, int, char); 198cd14d642Sschwarze static void roff_expand_patch(struct buf *, int, 199cd14d642Sschwarze const char *, int); 20004e980cbSschwarze static void roff_free1(struct roff *); 20122881299Sschwarze static void roff_freereg(struct roffreg *); 20204e980cbSschwarze static void roff_freestr(struct roffkv *); 2033b5bd91eSschwarze static size_t roff_getname(char **, int, int); 204f4432377Sschwarze static int roff_getnum(const char *, int *, int *, char, int); 205541e3395Sschwarze static int roff_getop(const char *, int *, char *); 206dcd9eb04Sschwarze static int roff_getregn(struct roff *, 207dcd9eb04Sschwarze const char *, size_t, char); 20845ff8f42Sschwarze static int roff_getregro(const struct roff *, 20945ff8f42Sschwarze const char *name); 2100cdab9e9Sschwarze static const char *roff_getstrn(struct roff *, 21107530f0aSschwarze const char *, size_t, int *); 21224ab75f2Sschwarze static int roff_hasregn(const struct roff *, 21324ab75f2Sschwarze const char *, size_t); 214b7f92c5fSschwarze static int roff_insec(ROFF_ARGS); 215b7f92c5fSschwarze static int roff_it(ROFF_ARGS); 216b7f92c5fSschwarze static int roff_line_ignore(ROFF_ARGS); 217405987fcSschwarze static void roff_man_alloc1(struct roff_man *); 218405987fcSschwarze static void roff_man_free1(struct roff_man *); 219b7f92c5fSschwarze static int roff_manyarg(ROFF_ARGS); 220edb0312fSschwarze static int roff_mc(ROFF_ARGS); 2210438bfdfSschwarze static int roff_noarg(ROFF_ARGS); 222b7f92c5fSschwarze static int roff_nop(ROFF_ARGS); 223b7f92c5fSschwarze static int roff_nr(ROFF_ARGS); 224b7f92c5fSschwarze static int roff_onearg(ROFF_ARGS); 22514a309e3Sschwarze static enum roff_tok roff_parse(struct roff *, char *, int *, 2267963e2c1Sschwarze int, int); 227928431b4Sschwarze static int roff_parse_comment(struct roff *, struct buf *, 228928431b4Sschwarze int, int, char); 229b7f92c5fSschwarze static int roff_parsetext(struct roff *, struct buf *, 230e13b4195Sschwarze int, int *); 231b7f92c5fSschwarze static int roff_renamed(ROFF_ARGS); 232f79258f3Sschwarze static int roff_req_or_macro(ROFF_ARGS); 233b7f92c5fSschwarze static int roff_return(ROFF_ARGS); 234b7f92c5fSschwarze static int roff_rm(ROFF_ARGS); 235b7f92c5fSschwarze static int roff_rn(ROFF_ARGS); 236b7f92c5fSschwarze static int roff_rr(ROFF_ARGS); 2379f6d70bcSschwarze static void roff_setregn(struct roff *, const char *, 238dcd9eb04Sschwarze size_t, int, char, int); 239769ee804Sschwarze static void roff_setstr(struct roff *, 240e53bb406Sschwarze const char *, const char *, int); 24104e980cbSschwarze static void roff_setstrn(struct roffkv **, const char *, 24204e980cbSschwarze size_t, const char *, size_t, int); 243b7f92c5fSschwarze static int roff_shift(ROFF_ARGS); 244b7f92c5fSschwarze static int roff_so(ROFF_ARGS); 245b7f92c5fSschwarze static int roff_tr(ROFF_ARGS); 246b7f92c5fSschwarze static int roff_Dd(ROFF_ARGS); 247b7f92c5fSschwarze static int roff_TE(ROFF_ARGS); 248b7f92c5fSschwarze static int roff_TS(ROFF_ARGS); 249b7f92c5fSschwarze static int roff_EQ(ROFF_ARGS); 250b7f92c5fSschwarze static int roff_EN(ROFF_ARGS); 251b7f92c5fSschwarze static int roff_T_(ROFF_ARGS); 252b7f92c5fSschwarze static int roff_unsupp(ROFF_ARGS); 253b7f92c5fSschwarze static int roff_userdef(ROFF_ARGS); 254f673a3c7Sschwarze 255fa2127f9Sschwarze /* --- constant data ------------------------------------------------------ */ 256fa2127f9Sschwarze 25714a309e3Sschwarze const char *__roff_name[MAN_MAX + 1] = { 2580438bfdfSschwarze "br", "ce", "fi", "ft", 2590438bfdfSschwarze "ll", "mc", "nf", 2600438bfdfSschwarze "po", "rj", "sp", 261af1e8f15Sschwarze "ta", "ti", NULL, 26214a309e3Sschwarze "ab", "ad", "af", "aln", 26314a309e3Sschwarze "als", "am", "am1", "ami", 26414a309e3Sschwarze "ami1", "as", "as1", "asciify", 26514a309e3Sschwarze "backtrace", "bd", "bleedat", "blm", 26614a309e3Sschwarze "box", "boxa", "bp", "BP", 26714a309e3Sschwarze "break", "breakchar", "brnl", "brp", 268e13b4195Sschwarze "brpnl", "c2", "cc", 26914a309e3Sschwarze "cf", "cflags", "ch", "char", 27014a309e3Sschwarze "chop", "class", "close", "CL", 27114a309e3Sschwarze "color", "composite", "continue", "cp", 27214a309e3Sschwarze "cropat", "cs", "cu", "da", 27314a309e3Sschwarze "dch", "Dd", "de", "de1", 27414a309e3Sschwarze "defcolor", "dei", "dei1", "device", 27514a309e3Sschwarze "devicem", "di", "do", "ds", 27614a309e3Sschwarze "ds1", "dwh", "dt", "ec", 27714a309e3Sschwarze "ecr", "ecs", "el", "em", 27814a309e3Sschwarze "EN", "eo", "EP", "EQ", 27914a309e3Sschwarze "errprint", "ev", "evc", "ex", 28014a309e3Sschwarze "fallback", "fam", "fc", "fchar", 28114a309e3Sschwarze "fcolor", "fdeferlig", "feature", "fkern", 28214a309e3Sschwarze "fl", "flig", "fp", "fps", 28314a309e3Sschwarze "fschar", "fspacewidth", "fspecial", "ftr", 28414a309e3Sschwarze "fzoom", "gcolor", "hc", "hcode", 28514a309e3Sschwarze "hidechar", "hla", "hlm", "hpf", 28614a309e3Sschwarze "hpfa", "hpfcode", "hw", "hy", 28714a309e3Sschwarze "hylang", "hylen", "hym", "hypp", 28814a309e3Sschwarze "hys", "ie", "if", "ig", 28914a309e3Sschwarze "index", "it", "itc", "IX", 29014a309e3Sschwarze "kern", "kernafter", "kernbefore", "kernpair", 29114a309e3Sschwarze "lc", "lc_ctype", "lds", "length", 29214a309e3Sschwarze "letadj", "lf", "lg", "lhang", 29314a309e3Sschwarze "linetabs", "lnr", "lnrf", "lpfx", 29424f1eaadSschwarze "ls", "lsm", "lt", 29514a309e3Sschwarze "mediasize", "minss", "mk", "mso", 29614a309e3Sschwarze "na", "ne", "nh", "nhychar", 29714a309e3Sschwarze "nm", "nn", "nop", "nr", 29814a309e3Sschwarze "nrf", "nroff", "ns", "nx", 29914a309e3Sschwarze "open", "opena", "os", "output", 30014a309e3Sschwarze "padj", "papersize", "pc", "pev", 30114a309e3Sschwarze "pi", "PI", "pl", "pm", 302af1e8f15Sschwarze "pn", "pnr", "ps", 30314a309e3Sschwarze "psbb", "pshape", "pso", "ptr", 30414a309e3Sschwarze "pvs", "rchar", "rd", "recursionlimit", 3056de096f4Sschwarze "return", "rfschar", "rhang", 30614a309e3Sschwarze "rm", "rn", "rnn", "rr", 30714a309e3Sschwarze "rs", "rt", "schar", "sentchar", 30814a309e3Sschwarze "shc", "shift", "sizes", "so", 30914a309e3Sschwarze "spacewidth", "special", "spreadwarn", "ss", 31014a309e3Sschwarze "sty", "substring", "sv", "sy", 311f7242c43Sschwarze "T&", "tc", "TE", 31211d70615Sschwarze "TH", "tkf", "tl", 31314a309e3Sschwarze "tm", "tm1", "tmc", "tr", 31414a309e3Sschwarze "track", "transchar", "trf", "trimat", 31514a309e3Sschwarze "trin", "trnt", "troff", "TS", 31614a309e3Sschwarze "uf", "ul", "unformat", "unwatch", 31714a309e3Sschwarze "unwatchn", "vpt", "vs", "warn", 31814a309e3Sschwarze "warnscale", "watch", "watchlength", "watchn", 31914a309e3Sschwarze "wh", "while", "write", "writec", 32014a309e3Sschwarze "writem", "xflag", ".", NULL, 3217e291f10Sschwarze NULL, "text", 32214a309e3Sschwarze "Dd", "Dt", "Os", "Sh", 32314a309e3Sschwarze "Ss", "Pp", "D1", "Dl", 32414a309e3Sschwarze "Bd", "Ed", "Bl", "El", 32514a309e3Sschwarze "It", "Ad", "An", "Ap", 32614a309e3Sschwarze "Ar", "Cd", "Cm", "Dv", 32714a309e3Sschwarze "Er", "Ev", "Ex", "Fa", 32814a309e3Sschwarze "Fd", "Fl", "Fn", "Ft", 32914a309e3Sschwarze "Ic", "In", "Li", "Nd", 33014a309e3Sschwarze "Nm", "Op", "Ot", "Pa", 33114a309e3Sschwarze "Rv", "St", "Va", "Vt", 33214a309e3Sschwarze "Xr", "%A", "%B", "%D", 33314a309e3Sschwarze "%I", "%J", "%N", "%O", 33414a309e3Sschwarze "%P", "%R", "%T", "%V", 33514a309e3Sschwarze "Ac", "Ao", "Aq", "At", 33614a309e3Sschwarze "Bc", "Bf", "Bo", "Bq", 33714a309e3Sschwarze "Bsx", "Bx", "Db", "Dc", 33814a309e3Sschwarze "Do", "Dq", "Ec", "Ef", 33914a309e3Sschwarze "Em", "Eo", "Fx", "Ms", 34014a309e3Sschwarze "No", "Ns", "Nx", "Ox", 34114a309e3Sschwarze "Pc", "Pf", "Po", "Pq", 34214a309e3Sschwarze "Qc", "Ql", "Qo", "Qq", 34314a309e3Sschwarze "Re", "Rs", "Sc", "So", 34414a309e3Sschwarze "Sq", "Sm", "Sx", "Sy", 34514a309e3Sschwarze "Tn", "Ux", "Xc", "Xo", 34614a309e3Sschwarze "Fo", "Fc", "Oo", "Oc", 34714a309e3Sschwarze "Bk", "Ek", "Bt", "Hf", 34814a309e3Sschwarze "Fr", "Ud", "Lb", "Lp", 34914a309e3Sschwarze "Lk", "Mt", "Brq", "Bro", 35014a309e3Sschwarze "Brc", "%C", "Es", "En", 3516561cb23Sschwarze "Dx", "%Q", "%U", "Ta", 35292929bf6Sschwarze "Tg", NULL, 35314a309e3Sschwarze "TH", "SH", "SS", "TP", 354d991fc2cSschwarze "TQ", 35514a309e3Sschwarze "LP", "PP", "P", "IP", 35614a309e3Sschwarze "HP", "SM", "SB", "BI", 35714a309e3Sschwarze "IB", "BR", "RB", "R", 35814a309e3Sschwarze "B", "I", "IR", "RI", 35914a309e3Sschwarze "RE", "RS", "DT", "UC", 360c4d3fa85Sschwarze "PD", "AT", "in", 3615e5a9c61Sschwarze "SY", "YS", "OP", 3625e5a9c61Sschwarze "EX", "EE", "UR", 363f6697133Sschwarze "UE", "MT", "ME", "MR", 364f6697133Sschwarze NULL 36514a309e3Sschwarze }; 36614a309e3Sschwarze const char *const *roff_name = __roff_name; 36714a309e3Sschwarze 36814a309e3Sschwarze static struct roffmac roffs[TOKEN_NONE] = { 3690438bfdfSschwarze { roff_noarg, NULL, NULL, 0 }, /* br */ 370e13b4195Sschwarze { roff_onearg, NULL, NULL, 0 }, /* ce */ 3710438bfdfSschwarze { roff_noarg, NULL, NULL, 0 }, /* fi */ 372c4d3fa85Sschwarze { roff_onearg, NULL, NULL, 0 }, /* ft */ 373644b390bSschwarze { roff_onearg, NULL, NULL, 0 }, /* ll */ 374edb0312fSschwarze { roff_mc, NULL, NULL, 0 }, /* mc */ 3750438bfdfSschwarze { roff_noarg, NULL, NULL, 0 }, /* nf */ 376af1e8f15Sschwarze { roff_onearg, NULL, NULL, 0 }, /* po */ 3776de096f4Sschwarze { roff_onearg, NULL, NULL, 0 }, /* rj */ 3786561cb23Sschwarze { roff_onearg, NULL, NULL, 0 }, /* sp */ 379f7242c43Sschwarze { roff_manyarg, NULL, NULL, 0 }, /* ta */ 38011d70615Sschwarze { roff_onearg, NULL, NULL, 0 }, /* ti */ 38129478532Sschwarze { NULL, NULL, NULL, 0 }, /* ROFF_MAX */ 3826050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* ab */ 3836050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* ad */ 3846050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* af */ 3856050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* aln */ 3860856de67Sschwarze { roff_als, NULL, NULL, 0 }, /* als */ 3876050a3daSschwarze { roff_block, roff_block_text, roff_block_sub, 0 }, /* am */ 3886050a3daSschwarze { roff_block, roff_block_text, roff_block_sub, 0 }, /* am1 */ 3896050a3daSschwarze { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami */ 3906050a3daSschwarze { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami1 */ 3916050a3daSschwarze { roff_ds, NULL, NULL, 0 }, /* as */ 3926050a3daSschwarze { roff_ds, NULL, NULL, 0 }, /* as1 */ 3936050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* asciify */ 3946050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* backtrace */ 3956050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* bd */ 3966050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* bleedat */ 3976050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* blm */ 3986050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* box */ 3996050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* boxa */ 4006050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* bp */ 4016050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* BP */ 4027dd0ff50Sschwarze { roff_break, NULL, NULL, 0 }, /* break */ 4036050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* breakchar */ 4046050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* brnl */ 4050438bfdfSschwarze { roff_noarg, NULL, NULL, 0 }, /* brp */ 4066050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* brpnl */ 4076050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* c2 */ 4086050a3daSschwarze { roff_cc, NULL, NULL, 0 }, /* cc */ 4096050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* cf */ 4106050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* cflags */ 4116050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* ch */ 41229fcab3eSschwarze { roff_char, NULL, NULL, 0 }, /* char */ 4136050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* chop */ 4146050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* class */ 4156050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* close */ 4166050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* CL */ 4176050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* color */ 4186050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* composite */ 4196050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* continue */ 4206050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* cp */ 4216050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* cropat */ 4226050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* cs */ 4236050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* cu */ 4246050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* da */ 4256050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* dch */ 4266050a3daSschwarze { roff_Dd, NULL, NULL, 0 }, /* Dd */ 4276050a3daSschwarze { roff_block, roff_block_text, roff_block_sub, 0 }, /* de */ 4286050a3daSschwarze { roff_block, roff_block_text, roff_block_sub, 0 }, /* de1 */ 4296050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* defcolor */ 4306050a3daSschwarze { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei */ 4316050a3daSschwarze { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei1 */ 4326050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* device */ 4336050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* devicem */ 4346050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* di */ 4356050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* do */ 4366050a3daSschwarze { roff_ds, NULL, NULL, 0 }, /* ds */ 4376050a3daSschwarze { roff_ds, NULL, NULL, 0 }, /* ds1 */ 4386050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* dwh */ 4396050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* dt */ 440be477484Sschwarze { roff_ec, NULL, NULL, 0 }, /* ec */ 4416050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* ecr */ 4426050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* ecs */ 4436050a3daSschwarze { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* el */ 4446050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* em */ 4456050a3daSschwarze { roff_EN, NULL, NULL, 0 }, /* EN */ 446be477484Sschwarze { roff_eo, NULL, NULL, 0 }, /* eo */ 4476050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* EP */ 4486050a3daSschwarze { roff_EQ, NULL, NULL, 0 }, /* EQ */ 4496050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* errprint */ 4506050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* ev */ 4516050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* evc */ 4526050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* ex */ 4536050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* fallback */ 4546050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* fam */ 4556050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* fc */ 4566050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* fchar */ 4576050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* fcolor */ 4586050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* fdeferlig */ 4596050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* feature */ 4606050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* fkern */ 4616050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* fl */ 4626050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* flig */ 4636050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* fp */ 4646050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* fps */ 4656050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* fschar */ 4666050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* fspacewidth */ 4676050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* fspecial */ 4686050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* ftr */ 4696050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* fzoom */ 4706050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* gcolor */ 4716050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hc */ 4726050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hcode */ 4736050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hidechar */ 4746050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hla */ 4756050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hlm */ 4766050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hpf */ 4776050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hpfa */ 4786050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hpfcode */ 4796050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hw */ 4806050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hy */ 4816050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hylang */ 4826050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hylen */ 4836050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hym */ 4846050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hypp */ 4856050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* hys */ 4866050a3daSschwarze { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* ie */ 4876050a3daSschwarze { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* if */ 4886050a3daSschwarze { roff_block, roff_block_text, roff_block_sub, 0 }, /* ig */ 4896050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* index */ 4906050a3daSschwarze { roff_it, NULL, NULL, 0 }, /* it */ 4916050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* itc */ 4926050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* IX */ 4936050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* kern */ 4946050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* kernafter */ 4956050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* kernbefore */ 4966050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* kernpair */ 4976050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* lc */ 4986050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* lc_ctype */ 4996050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* lds */ 5006050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* length */ 5016050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* letadj */ 5026050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* lf */ 5036050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* lg */ 5046050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* lhang */ 5056050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* linetabs */ 5066050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* lnr */ 5076050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* lnrf */ 5086050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* lpfx */ 5096050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* ls */ 5106050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* lsm */ 5116050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* lt */ 5126050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* mediasize */ 5136050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* minss */ 5146050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* mk */ 5156050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* mso */ 5166050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* na */ 5176050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* ne */ 5186050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* nh */ 5196050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* nhychar */ 5206050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* nm */ 5216050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* nn */ 522b20e20eaSschwarze { roff_nop, NULL, NULL, 0 }, /* nop */ 5236050a3daSschwarze { roff_nr, NULL, NULL, 0 }, /* nr */ 5246050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* nrf */ 5256050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* nroff */ 5266050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* ns */ 5276050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* nx */ 5286050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* open */ 5296050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* opena */ 5306050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* os */ 5316050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* output */ 5326050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* padj */ 5336050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* papersize */ 5346050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* pc */ 5356050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* pev */ 5366050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* pi */ 5376050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* PI */ 5386050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* pl */ 5396050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* pm */ 5406050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* pn */ 5416050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* pnr */ 5426050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* ps */ 5436050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* psbb */ 5446050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* pshape */ 5456050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* pso */ 5466050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* ptr */ 5476050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* pvs */ 5486050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* rchar */ 5496050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* rd */ 5506050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* recursionlimit */ 5513dc5225dSschwarze { roff_return, NULL, NULL, 0 }, /* return */ 5526050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* rfschar */ 5536050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* rhang */ 5546050a3daSschwarze { roff_rm, NULL, NULL, 0 }, /* rm */ 5557e291f10Sschwarze { roff_rn, NULL, NULL, 0 }, /* rn */ 5566050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* rnn */ 5576050a3daSschwarze { roff_rr, NULL, NULL, 0 }, /* rr */ 5586050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* rs */ 5596050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* rt */ 5606050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* schar */ 5616050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* sentchar */ 5626050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* shc */ 5633dc5225dSschwarze { roff_shift, NULL, NULL, 0 }, /* shift */ 5646050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* sizes */ 5656050a3daSschwarze { roff_so, NULL, NULL, 0 }, /* so */ 5666050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* spacewidth */ 5676050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* special */ 5686050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* spreadwarn */ 5696050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* ss */ 5706050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* sty */ 5716050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* substring */ 5726050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* sv */ 5736050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* sy */ 5746050a3daSschwarze { roff_T_, NULL, NULL, 0 }, /* T& */ 5756050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* tc */ 5766050a3daSschwarze { roff_TE, NULL, NULL, 0 }, /* TE */ 57707530f0aSschwarze { roff_Dd, NULL, NULL, 0 }, /* TH */ 5786050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* tkf */ 5796050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* tl */ 5806050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* tm */ 5816050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* tm1 */ 5826050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* tmc */ 5836050a3daSschwarze { roff_tr, NULL, NULL, 0 }, /* tr */ 5846050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* track */ 5856050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* transchar */ 5866050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* trf */ 5876050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* trimat */ 5886050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* trin */ 5896050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* trnt */ 5906050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* troff */ 5916050a3daSschwarze { roff_TS, NULL, NULL, 0 }, /* TS */ 5926050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* uf */ 5936050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* ul */ 5946050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* unformat */ 5956050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* unwatch */ 5966050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* unwatchn */ 5976050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* vpt */ 5986050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* vs */ 5996050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* warn */ 6006050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* warnscale */ 6016050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* watch */ 6026050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* watchlength */ 6036050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* watchn */ 6046050a3daSschwarze { roff_unsupp, NULL, NULL, 0 }, /* wh */ 605b7f92c5fSschwarze { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /*while*/ 6066050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* write */ 6076050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* writec */ 6086050a3daSschwarze { roff_insec, NULL, NULL, 0 }, /* writem */ 6096050a3daSschwarze { roff_line_ignore, NULL, NULL, 0 }, /* xflag */ 6106050a3daSschwarze { roff_cblock, NULL, NULL, 0 }, /* . */ 6117e291f10Sschwarze { roff_renamed, NULL, NULL, 0 }, 6126050a3daSschwarze { roff_userdef, NULL, NULL, 0 } 613f673a3c7Sschwarze }; 614f673a3c7Sschwarze 615a5e11edeSschwarze /* Array of injected predefined strings. */ 616a5e11edeSschwarze #define PREDEFS_MAX 38 617a5e11edeSschwarze static const struct predef predefs[PREDEFS_MAX] = { 618a5e11edeSschwarze #include "predefs.in" 619a5e11edeSschwarze }; 620a5e11edeSschwarze 621e13b4195Sschwarze static int roffce_lines; /* number of input lines to center */ 622e13b4195Sschwarze static struct roff_node *roffce_node; /* active request */ 623a91e242cSschwarze static int roffit_lines; /* number of lines to delay */ 624a91e242cSschwarze static char *roffit_macro; /* nil-terminated macro line */ 625a91e242cSschwarze 62649aff9f8Sschwarze 627fa2127f9Sschwarze /* --- request table ------------------------------------------------------ */ 628fa2127f9Sschwarze 6296050a3daSschwarze struct ohash * 6306050a3daSschwarze roffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok) 631b31af00dSschwarze { 6326050a3daSschwarze struct ohash *htab; 6336050a3daSschwarze struct roffreq *req; 6346050a3daSschwarze enum roff_tok tok; 6356050a3daSschwarze size_t sz; 6366050a3daSschwarze unsigned int slot; 637b31af00dSschwarze 6386050a3daSschwarze htab = mandoc_malloc(sizeof(*htab)); 6396050a3daSschwarze mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name)); 640b31af00dSschwarze 6416050a3daSschwarze for (tok = mintok; tok < maxtok; tok++) { 64229478532Sschwarze if (roff_name[tok] == NULL) 64329478532Sschwarze continue; 6446050a3daSschwarze sz = strlen(roff_name[tok]); 6456050a3daSschwarze req = mandoc_malloc(sizeof(*req) + sz + 1); 6466050a3daSschwarze req->tok = tok; 6476050a3daSschwarze memcpy(req->name, roff_name[tok], sz + 1); 6486050a3daSschwarze slot = ohash_qlookup(htab, req->name); 6496050a3daSschwarze ohash_insert(htab, slot, req); 6506050a3daSschwarze } 6516050a3daSschwarze return htab; 6526050a3daSschwarze } 653b31af00dSschwarze 6546050a3daSschwarze void 6556050a3daSschwarze roffhash_free(struct ohash *htab) 6566050a3daSschwarze { 6576050a3daSschwarze struct roffreq *req; 6586050a3daSschwarze unsigned int slot; 6596050a3daSschwarze 6606050a3daSschwarze if (htab == NULL) 6616050a3daSschwarze return; 6626050a3daSschwarze for (req = ohash_first(htab, &slot); req != NULL; 6636050a3daSschwarze req = ohash_next(htab, &slot)) 6646050a3daSschwarze free(req); 6656050a3daSschwarze ohash_delete(htab); 6666050a3daSschwarze free(htab); 6676050a3daSschwarze } 6686050a3daSschwarze 6696050a3daSschwarze enum roff_tok 6706050a3daSschwarze roffhash_find(struct ohash *htab, const char *name, size_t sz) 6716050a3daSschwarze { 6726050a3daSschwarze struct roffreq *req; 6736050a3daSschwarze const char *end; 6746050a3daSschwarze 6756050a3daSschwarze if (sz) { 6766050a3daSschwarze end = name + sz; 6776050a3daSschwarze req = ohash_find(htab, ohash_qlookupi(htab, name, &end)); 678b31af00dSschwarze } else 6796050a3daSschwarze req = ohash_find(htab, ohash_qlookup(htab, name)); 6806050a3daSschwarze return req == NULL ? TOKEN_NONE : req->tok; 681f673a3c7Sschwarze } 682f673a3c7Sschwarze 683fa2127f9Sschwarze /* --- stack of request blocks -------------------------------------------- */ 684fa2127f9Sschwarze 685f673a3c7Sschwarze /* 686f673a3c7Sschwarze * Pop the current node off of the stack of roff instructions currently 6877dd0ff50Sschwarze * pending. Return 1 if it is a loop or 0 otherwise. 688f673a3c7Sschwarze */ 689b7f92c5fSschwarze static int 690f673a3c7Sschwarze roffnode_pop(struct roff *r) 691f673a3c7Sschwarze { 692f673a3c7Sschwarze struct roffnode *p; 693b7f92c5fSschwarze int inloop; 694f673a3c7Sschwarze 69562d9ccdbSschwarze p = r->last; 696b7f92c5fSschwarze inloop = p->tok == ROFF_while; 697b7f92c5fSschwarze r->last = p->parent; 698e53bb406Sschwarze free(p->name); 69962d9ccdbSschwarze free(p->end); 700f673a3c7Sschwarze free(p); 701b7f92c5fSschwarze return inloop; 702f673a3c7Sschwarze } 703f673a3c7Sschwarze 704f673a3c7Sschwarze /* 705f673a3c7Sschwarze * Push a roff node onto the instruction stack. This must later be 706f673a3c7Sschwarze * removed with roffnode_pop(). 707f673a3c7Sschwarze */ 708cfd2bfaaSschwarze static void 70914a309e3Sschwarze roffnode_push(struct roff *r, enum roff_tok tok, const char *name, 710e53bb406Sschwarze int line, int col) 711f673a3c7Sschwarze { 712f673a3c7Sschwarze struct roffnode *p; 713f673a3c7Sschwarze 714cfd2bfaaSschwarze p = mandoc_calloc(1, sizeof(struct roffnode)); 715f673a3c7Sschwarze p->tok = tok; 716e53bb406Sschwarze if (name) 717e53bb406Sschwarze p->name = mandoc_strdup(name); 718f673a3c7Sschwarze p->parent = r->last; 719f673a3c7Sschwarze p->line = line; 720f673a3c7Sschwarze p->col = col; 7211271a177Sschwarze p->rule = p->parent ? p->parent->rule : 0; 722f673a3c7Sschwarze 723f673a3c7Sschwarze r->last = p; 724f673a3c7Sschwarze } 725f673a3c7Sschwarze 726fa2127f9Sschwarze /* --- roff parser state data management ---------------------------------- */ 727fa2127f9Sschwarze 728f673a3c7Sschwarze static void 729f673a3c7Sschwarze roff_free1(struct roff *r) 730f673a3c7Sschwarze { 73104e980cbSschwarze int i; 7322791bd1cSschwarze 733fb382a01Sschwarze tbl_free(r->first_tbl); 7342791bd1cSschwarze r->first_tbl = r->last_tbl = r->tbl = NULL; 735f673a3c7Sschwarze 736bf9acac6Sschwarze eqn_free(r->last_eqn); 737bf9acac6Sschwarze r->last_eqn = r->eqn = NULL; 7388d973ab1Sschwarze 7393dc5225dSschwarze while (r->mstackpos >= 0) 7403dc5225dSschwarze roff_userret(r); 7413dc5225dSschwarze 742f673a3c7Sschwarze while (r->last) 743f673a3c7Sschwarze roffnode_pop(r); 7442791bd1cSschwarze 7454bc98c72Sschwarze free (r->rstack); 7464bc98c72Sschwarze r->rstack = NULL; 7474bc98c72Sschwarze r->rstacksz = 0; 7484bc98c72Sschwarze r->rstackpos = -1; 74904e980cbSschwarze 75022881299Sschwarze roff_freereg(r->regtab); 75122881299Sschwarze r->regtab = NULL; 75222881299Sschwarze 7534bc98c72Sschwarze roff_freestr(r->strtab); 7547e291f10Sschwarze roff_freestr(r->rentab); 7554bc98c72Sschwarze roff_freestr(r->xmbtab); 7567e291f10Sschwarze r->strtab = r->rentab = r->xmbtab = NULL; 7574bc98c72Sschwarze 75804e980cbSschwarze if (r->xtab) 75904e980cbSschwarze for (i = 0; i < 128; i++) 76004e980cbSschwarze free(r->xtab[i].p); 76104e980cbSschwarze free(r->xtab); 76204e980cbSschwarze r->xtab = NULL; 76304e980cbSschwarze } 764f673a3c7Sschwarze 765f673a3c7Sschwarze void 766f673a3c7Sschwarze roff_reset(struct roff *r) 767f673a3c7Sschwarze { 768f673a3c7Sschwarze roff_free1(r); 76967217f5cSschwarze r->options |= MPARSE_COMMENT; 770f0d7487dSschwarze r->format = r->options & (MPARSE_MDOC | MPARSE_MAN); 771be477484Sschwarze r->control = '\0'; 772be477484Sschwarze r->escape = '\\'; 7735aaaa1efSschwarze roffce_lines = 0; 7745aaaa1efSschwarze roffce_node = NULL; 7755aaaa1efSschwarze roffit_lines = 0; 7765aaaa1efSschwarze roffit_macro = NULL; 777f673a3c7Sschwarze } 778f673a3c7Sschwarze 779f673a3c7Sschwarze void 780f673a3c7Sschwarze roff_free(struct roff *r) 781f673a3c7Sschwarze { 7823dc5225dSschwarze int i; 7833dc5225dSschwarze 784f673a3c7Sschwarze roff_free1(r); 7853dc5225dSschwarze for (i = 0; i < r->mstacksz; i++) 7863dc5225dSschwarze free(r->mstack[i].argv); 7873dc5225dSschwarze free(r->mstack); 7886050a3daSschwarze roffhash_free(r->reqtab); 789f673a3c7Sschwarze free(r); 790f673a3c7Sschwarze } 791f673a3c7Sschwarze 792f673a3c7Sschwarze struct roff * 79391305757Sschwarze roff_alloc(int options) 794f673a3c7Sschwarze { 795f673a3c7Sschwarze struct roff *r; 796f673a3c7Sschwarze 797cfd2bfaaSschwarze r = mandoc_calloc(1, sizeof(struct roff)); 7984c293873Sschwarze r->reqtab = roffhash_alloc(0, ROFF_RENAMED); 79967217f5cSschwarze r->options = options | MPARSE_COMMENT; 800f0d7487dSschwarze r->format = options & (MPARSE_MDOC | MPARSE_MAN); 8013dc5225dSschwarze r->mstackpos = -1; 80262d9ccdbSschwarze r->rstackpos = -1; 803be477484Sschwarze r->escape = '\\'; 804526e306bSschwarze return r; 805f673a3c7Sschwarze } 806f673a3c7Sschwarze 807fa2127f9Sschwarze /* --- syntax tree state data management ---------------------------------- */ 808fa2127f9Sschwarze 809405987fcSschwarze static void 810405987fcSschwarze roff_man_free1(struct roff_man *man) 811405987fcSschwarze { 8126b86842eSschwarze if (man->meta.first != NULL) 8136b86842eSschwarze roff_node_delete(man, man->meta.first); 814405987fcSschwarze free(man->meta.msec); 815405987fcSschwarze free(man->meta.vol); 816405987fcSschwarze free(man->meta.os); 817405987fcSschwarze free(man->meta.arch); 818405987fcSschwarze free(man->meta.title); 819405987fcSschwarze free(man->meta.name); 820405987fcSschwarze free(man->meta.date); 8216b86842eSschwarze free(man->meta.sodest); 822405987fcSschwarze } 823405987fcSschwarze 82483d65a5aSschwarze void 82583d65a5aSschwarze roff_state_reset(struct roff_man *man) 82683d65a5aSschwarze { 82783d65a5aSschwarze man->last = man->meta.first; 82883d65a5aSschwarze man->last_es = NULL; 82983d65a5aSschwarze man->flags = 0; 83083d65a5aSschwarze man->lastsec = man->lastnamed = SEC_NONE; 83183d65a5aSschwarze man->next = ROFF_NEXT_CHILD; 83283d65a5aSschwarze roff_setreg(man->roff, "nS", 0, '='); 83383d65a5aSschwarze } 83483d65a5aSschwarze 835405987fcSschwarze static void 836405987fcSschwarze roff_man_alloc1(struct roff_man *man) 837405987fcSschwarze { 838405987fcSschwarze memset(&man->meta, 0, sizeof(man->meta)); 8396b86842eSschwarze man->meta.first = mandoc_calloc(1, sizeof(*man->meta.first)); 8406b86842eSschwarze man->meta.first->type = ROFFT_ROOT; 8416b86842eSschwarze man->meta.macroset = MACROSET_NONE; 84283d65a5aSschwarze roff_state_reset(man); 843405987fcSschwarze } 844405987fcSschwarze 845405987fcSschwarze void 846405987fcSschwarze roff_man_reset(struct roff_man *man) 847405987fcSschwarze { 848405987fcSschwarze roff_man_free1(man); 849405987fcSschwarze roff_man_alloc1(man); 850405987fcSschwarze } 851405987fcSschwarze 852405987fcSschwarze void 853405987fcSschwarze roff_man_free(struct roff_man *man) 854405987fcSschwarze { 855405987fcSschwarze roff_man_free1(man); 8568055da74Sschwarze free(man->os_r); 857405987fcSschwarze free(man); 858405987fcSschwarze } 859405987fcSschwarze 860405987fcSschwarze struct roff_man * 86191305757Sschwarze roff_man_alloc(struct roff *roff, const char *os_s, int quick) 862405987fcSschwarze { 863405987fcSschwarze struct roff_man *man; 864405987fcSschwarze 865405987fcSschwarze man = mandoc_calloc(1, sizeof(*man)); 866405987fcSschwarze man->roff = roff; 867f3476b07Sschwarze man->os_s = os_s; 868405987fcSschwarze man->quick = quick; 869405987fcSschwarze roff_man_alloc1(man); 87029478532Sschwarze roff->man = man; 871526e306bSschwarze return man; 872405987fcSschwarze } 873405987fcSschwarze 874fa2127f9Sschwarze /* --- syntax tree handling ----------------------------------------------- */ 875fa2127f9Sschwarze 876fa2127f9Sschwarze struct roff_node * 877fa2127f9Sschwarze roff_node_alloc(struct roff_man *man, int line, int pos, 878fa2127f9Sschwarze enum roff_type type, int tok) 879fa2127f9Sschwarze { 880fa2127f9Sschwarze struct roff_node *n; 881fa2127f9Sschwarze 882fa2127f9Sschwarze n = mandoc_calloc(1, sizeof(*n)); 883fa2127f9Sschwarze n->line = line; 884fa2127f9Sschwarze n->pos = pos; 885fa2127f9Sschwarze n->tok = tok; 886fa2127f9Sschwarze n->type = type; 887fa2127f9Sschwarze n->sec = man->lastsec; 888fa2127f9Sschwarze 889fa2127f9Sschwarze if (man->flags & MDOC_SYNOPSIS) 890c4b0939cSschwarze n->flags |= NODE_SYNPRETTY; 891fa2127f9Sschwarze else 892c4b0939cSschwarze n->flags &= ~NODE_SYNPRETTY; 89394a3c318Sschwarze if ((man->flags & (ROFF_NOFILL | ROFF_NONOFILL)) == ROFF_NOFILL) 89421dcc2b4Sschwarze n->flags |= NODE_NOFILL; 89521dcc2b4Sschwarze else 89621dcc2b4Sschwarze n->flags &= ~NODE_NOFILL; 897fa2127f9Sschwarze if (man->flags & MDOC_NEWLINE) 898c4b0939cSschwarze n->flags |= NODE_LINE; 899fa2127f9Sschwarze man->flags &= ~MDOC_NEWLINE; 900fa2127f9Sschwarze 901526e306bSschwarze return n; 902fa2127f9Sschwarze } 903fa2127f9Sschwarze 904fa2127f9Sschwarze void 905fa2127f9Sschwarze roff_node_append(struct roff_man *man, struct roff_node *n) 906fa2127f9Sschwarze { 907fa2127f9Sschwarze 908fa2127f9Sschwarze switch (man->next) { 909fa2127f9Sschwarze case ROFF_NEXT_SIBLING: 910396853b5Sschwarze if (man->last->next != NULL) { 911396853b5Sschwarze n->next = man->last->next; 912396853b5Sschwarze man->last->next->prev = n; 913396853b5Sschwarze } else 914396853b5Sschwarze man->last->parent->last = n; 915fa2127f9Sschwarze man->last->next = n; 916fa2127f9Sschwarze n->prev = man->last; 917fa2127f9Sschwarze n->parent = man->last->parent; 918fa2127f9Sschwarze break; 919fa2127f9Sschwarze case ROFF_NEXT_CHILD: 920816c3c54Sschwarze if (man->last->child != NULL) { 921816c3c54Sschwarze n->next = man->last->child; 922816c3c54Sschwarze man->last->child->prev = n; 923816c3c54Sschwarze } else 924816c3c54Sschwarze man->last->last = n; 925fa2127f9Sschwarze man->last->child = n; 926fa2127f9Sschwarze n->parent = man->last; 927fa2127f9Sschwarze break; 928fa2127f9Sschwarze default: 929fa2127f9Sschwarze abort(); 930fa2127f9Sschwarze } 9313e642ba0Sschwarze man->last = n; 9323e642ba0Sschwarze 9333e642ba0Sschwarze switch (n->type) { 9343e642ba0Sschwarze case ROFFT_HEAD: 9353e642ba0Sschwarze n->parent->head = n; 9363e642ba0Sschwarze break; 9373e642ba0Sschwarze case ROFFT_BODY: 9383e642ba0Sschwarze if (n->end != ENDBODY_NOT) 9393e642ba0Sschwarze return; 9403e642ba0Sschwarze n->parent->body = n; 9413e642ba0Sschwarze break; 9423e642ba0Sschwarze case ROFFT_TAIL: 9433e642ba0Sschwarze n->parent->tail = n; 9443e642ba0Sschwarze break; 9453e642ba0Sschwarze default: 9463e642ba0Sschwarze return; 9473e642ba0Sschwarze } 948fa2127f9Sschwarze 949fa2127f9Sschwarze /* 950fa2127f9Sschwarze * Copy over the normalised-data pointer of our parent. Not 951fa2127f9Sschwarze * everybody has one, but copying a null pointer is fine. 952fa2127f9Sschwarze */ 953fa2127f9Sschwarze 954fa2127f9Sschwarze n->norm = n->parent->norm; 955fa2127f9Sschwarze assert(n->parent->type == ROFFT_BLOCK); 956fa2127f9Sschwarze } 957fa2127f9Sschwarze 95869c34eaaSschwarze void 95969c34eaaSschwarze roff_word_alloc(struct roff_man *man, int line, int pos, const char *word) 96069c34eaaSschwarze { 96169c34eaaSschwarze struct roff_node *n; 96269c34eaaSschwarze 96369c34eaaSschwarze n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE); 96469c34eaaSschwarze n->string = roff_strdup(man->roff, word); 96569c34eaaSschwarze roff_node_append(man, n); 966c4b0939cSschwarze n->flags |= NODE_VALID | NODE_ENDED; 96769c34eaaSschwarze man->next = ROFF_NEXT_SIBLING; 96869c34eaaSschwarze } 96969c34eaaSschwarze 97069c34eaaSschwarze void 97169c34eaaSschwarze roff_word_append(struct roff_man *man, const char *word) 97269c34eaaSschwarze { 97369c34eaaSschwarze struct roff_node *n; 97469c34eaaSschwarze char *addstr, *newstr; 97569c34eaaSschwarze 97669c34eaaSschwarze n = man->last; 97769c34eaaSschwarze addstr = roff_strdup(man->roff, word); 97869c34eaaSschwarze mandoc_asprintf(&newstr, "%s %s", n->string, addstr); 97969c34eaaSschwarze free(addstr); 98069c34eaaSschwarze free(n->string); 98169c34eaaSschwarze n->string = newstr; 98269c34eaaSschwarze man->next = ROFF_NEXT_SIBLING; 98369c34eaaSschwarze } 98469c34eaaSschwarze 985e32c44d4Sschwarze void 986e32c44d4Sschwarze roff_elem_alloc(struct roff_man *man, int line, int pos, int tok) 987e32c44d4Sschwarze { 988e32c44d4Sschwarze struct roff_node *n; 989e32c44d4Sschwarze 990e32c44d4Sschwarze n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok); 991e32c44d4Sschwarze roff_node_append(man, n); 992e32c44d4Sschwarze man->next = ROFF_NEXT_CHILD; 993e32c44d4Sschwarze } 994e32c44d4Sschwarze 995e32c44d4Sschwarze struct roff_node * 996e32c44d4Sschwarze roff_block_alloc(struct roff_man *man, int line, int pos, int tok) 997e32c44d4Sschwarze { 998e32c44d4Sschwarze struct roff_node *n; 999e32c44d4Sschwarze 1000e32c44d4Sschwarze n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok); 1001e32c44d4Sschwarze roff_node_append(man, n); 1002e32c44d4Sschwarze man->next = ROFF_NEXT_CHILD; 1003526e306bSschwarze return n; 1004e32c44d4Sschwarze } 1005e32c44d4Sschwarze 1006fa2127f9Sschwarze struct roff_node * 1007fa2127f9Sschwarze roff_head_alloc(struct roff_man *man, int line, int pos, int tok) 1008fa2127f9Sschwarze { 1009fa2127f9Sschwarze struct roff_node *n; 1010fa2127f9Sschwarze 1011fa2127f9Sschwarze n = roff_node_alloc(man, line, pos, ROFFT_HEAD, tok); 1012fa2127f9Sschwarze roff_node_append(man, n); 1013fa2127f9Sschwarze man->next = ROFF_NEXT_CHILD; 1014526e306bSschwarze return n; 1015fa2127f9Sschwarze } 1016fa2127f9Sschwarze 1017fa2127f9Sschwarze struct roff_node * 1018fa2127f9Sschwarze roff_body_alloc(struct roff_man *man, int line, int pos, int tok) 1019fa2127f9Sschwarze { 1020fa2127f9Sschwarze struct roff_node *n; 1021fa2127f9Sschwarze 1022fa2127f9Sschwarze n = roff_node_alloc(man, line, pos, ROFFT_BODY, tok); 1023fa2127f9Sschwarze roff_node_append(man, n); 1024fa2127f9Sschwarze man->next = ROFF_NEXT_CHILD; 1025526e306bSschwarze return n; 1026fa2127f9Sschwarze } 1027fa2127f9Sschwarze 1028d93f8561Sschwarze static void 1029fb382a01Sschwarze roff_addtbl(struct roff_man *man, int line, struct tbl_node *tbl) 103069c34eaaSschwarze { 103169c34eaaSschwarze struct roff_node *n; 1032fb382a01Sschwarze struct tbl_span *span; 103369c34eaaSschwarze 10346b86842eSschwarze if (man->meta.macroset == MACROSET_MAN) 1035885ec428Sschwarze man_breakscope(man, ROFF_TS); 1036d93f8561Sschwarze while ((span = tbl_span(tbl)) != NULL) { 1037fb382a01Sschwarze n = roff_node_alloc(man, line, 0, ROFFT_TBL, TOKEN_NONE); 1038d93f8561Sschwarze n->span = span; 103969c34eaaSschwarze roff_node_append(man, n); 1040c4b0939cSschwarze n->flags |= NODE_VALID | NODE_ENDED; 104169c34eaaSschwarze man->next = ROFF_NEXT_SIBLING; 104269c34eaaSschwarze } 1043d93f8561Sschwarze } 104469c34eaaSschwarze 104569c34eaaSschwarze void 1046fa2127f9Sschwarze roff_node_unlink(struct roff_man *man, struct roff_node *n) 1047fa2127f9Sschwarze { 1048fa2127f9Sschwarze 1049fa2127f9Sschwarze /* Adjust siblings. */ 1050fa2127f9Sschwarze 1051fa2127f9Sschwarze if (n->prev) 1052fa2127f9Sschwarze n->prev->next = n->next; 1053fa2127f9Sschwarze if (n->next) 1054fa2127f9Sschwarze n->next->prev = n->prev; 1055fa2127f9Sschwarze 1056fa2127f9Sschwarze /* Adjust parent. */ 1057fa2127f9Sschwarze 1058fa2127f9Sschwarze if (n->parent != NULL) { 1059fa2127f9Sschwarze if (n->parent->child == n) 1060fa2127f9Sschwarze n->parent->child = n->next; 1061fa2127f9Sschwarze if (n->parent->last == n) 1062fa2127f9Sschwarze n->parent->last = n->prev; 1063fa2127f9Sschwarze } 1064fa2127f9Sschwarze 1065fa2127f9Sschwarze /* Adjust parse point. */ 1066fa2127f9Sschwarze 1067fa2127f9Sschwarze if (man == NULL) 1068fa2127f9Sschwarze return; 1069fa2127f9Sschwarze if (man->last == n) { 1070fa2127f9Sschwarze if (n->prev == NULL) { 1071fa2127f9Sschwarze man->last = n->parent; 1072fa2127f9Sschwarze man->next = ROFF_NEXT_CHILD; 1073fa2127f9Sschwarze } else { 1074fa2127f9Sschwarze man->last = n->prev; 1075fa2127f9Sschwarze man->next = ROFF_NEXT_SIBLING; 1076fa2127f9Sschwarze } 1077fa2127f9Sschwarze } 10786b86842eSschwarze if (man->meta.first == n) 10796b86842eSschwarze man->meta.first = NULL; 1080fa2127f9Sschwarze } 1081fa2127f9Sschwarze 1082fa2127f9Sschwarze void 10838251afdeSschwarze roff_node_relink(struct roff_man *man, struct roff_node *n) 10848251afdeSschwarze { 10858251afdeSschwarze roff_node_unlink(man, n); 10868251afdeSschwarze n->prev = n->next = NULL; 10878251afdeSschwarze roff_node_append(man, n); 10888251afdeSschwarze } 10898251afdeSschwarze 10908251afdeSschwarze void 1091fa2127f9Sschwarze roff_node_free(struct roff_node *n) 1092fa2127f9Sschwarze { 1093fa2127f9Sschwarze 1094fa2127f9Sschwarze if (n->args != NULL) 1095fa2127f9Sschwarze mdoc_argv_free(n->args); 1096fa2127f9Sschwarze if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM) 1097fa2127f9Sschwarze free(n->norm); 1098bf9acac6Sschwarze eqn_box_free(n->eqn); 1099fa2127f9Sschwarze free(n->string); 1100c220f9cfSschwarze free(n->tag); 1101fa2127f9Sschwarze free(n); 1102fa2127f9Sschwarze } 1103fa2127f9Sschwarze 1104fa2127f9Sschwarze void 1105fa2127f9Sschwarze roff_node_delete(struct roff_man *man, struct roff_node *n) 1106fa2127f9Sschwarze { 1107fa2127f9Sschwarze 1108fa2127f9Sschwarze while (n->child != NULL) 1109fa2127f9Sschwarze roff_node_delete(man, n->child); 1110fa2127f9Sschwarze roff_node_unlink(man, n); 1111fa2127f9Sschwarze roff_node_free(n); 1112fa2127f9Sschwarze } 1113fa2127f9Sschwarze 11147ebbefbeSschwarze int 11157ebbefbeSschwarze roff_node_transparent(struct roff_node *n) 11167ebbefbeSschwarze { 11177ebbefbeSschwarze if (n == NULL) 11187ebbefbeSschwarze return 0; 11197ebbefbeSschwarze if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) 11207ebbefbeSschwarze return 1; 1121e053e0fdSschwarze return roff_tok_transparent(n->tok); 1122e053e0fdSschwarze } 1123e053e0fdSschwarze 1124e053e0fdSschwarze int 1125e053e0fdSschwarze roff_tok_transparent(enum roff_tok tok) 1126e053e0fdSschwarze { 1127e053e0fdSschwarze switch (tok) { 11287ebbefbeSschwarze case ROFF_ft: 11297ebbefbeSschwarze case ROFF_ll: 11307ebbefbeSschwarze case ROFF_mc: 11317ebbefbeSschwarze case ROFF_po: 11327ebbefbeSschwarze case ROFF_ta: 11337ebbefbeSschwarze case MDOC_Db: 11347ebbefbeSschwarze case MDOC_Es: 11357ebbefbeSschwarze case MDOC_Sm: 11367ebbefbeSschwarze case MDOC_Tg: 11377ebbefbeSschwarze case MAN_DT: 11387ebbefbeSschwarze case MAN_UC: 11397ebbefbeSschwarze case MAN_PD: 11407ebbefbeSschwarze case MAN_AT: 11417ebbefbeSschwarze return 1; 11427ebbefbeSschwarze default: 11437ebbefbeSschwarze return 0; 11447ebbefbeSschwarze } 11457ebbefbeSschwarze } 11467ebbefbeSschwarze 11477ebbefbeSschwarze struct roff_node * 11487ebbefbeSschwarze roff_node_child(struct roff_node *n) 11497ebbefbeSschwarze { 11507ebbefbeSschwarze for (n = n->child; roff_node_transparent(n); n = n->next) 11517ebbefbeSschwarze continue; 11527ebbefbeSschwarze return n; 11537ebbefbeSschwarze } 11547ebbefbeSschwarze 11557ebbefbeSschwarze struct roff_node * 11567ebbefbeSschwarze roff_node_prev(struct roff_node *n) 11577ebbefbeSschwarze { 11587ebbefbeSschwarze do { 11597ebbefbeSschwarze n = n->prev; 11607ebbefbeSschwarze } while (roff_node_transparent(n)); 11617ebbefbeSschwarze return n; 11627ebbefbeSschwarze } 11637ebbefbeSschwarze 11647ebbefbeSschwarze struct roff_node * 11657ebbefbeSschwarze roff_node_next(struct roff_node *n) 11667ebbefbeSschwarze { 11677ebbefbeSschwarze do { 11687ebbefbeSschwarze n = n->next; 11697ebbefbeSschwarze } while (roff_node_transparent(n)); 11707ebbefbeSschwarze return n; 11717ebbefbeSschwarze } 11727ebbefbeSschwarze 1173423631c9Sschwarze void 1174423631c9Sschwarze deroff(char **dest, const struct roff_node *n) 1175423631c9Sschwarze { 1176423631c9Sschwarze char *cp; 1177423631c9Sschwarze size_t sz; 1178423631c9Sschwarze 11799a542ed3Sschwarze if (n->string == NULL) { 1180423631c9Sschwarze for (n = n->child; n != NULL; n = n->next) 1181423631c9Sschwarze deroff(dest, n); 1182423631c9Sschwarze return; 1183423631c9Sschwarze } 1184423631c9Sschwarze 11859fc28d5cSschwarze /* Skip leading whitespace. */ 1186423631c9Sschwarze 11879fc28d5cSschwarze for (cp = n->string; *cp != '\0'; cp++) { 1188972cfc25Sschwarze if (cp[0] == '\\' && cp[1] != '\0' && 1189972cfc25Sschwarze strchr(" %&0^|~", cp[1]) != NULL) 1190423631c9Sschwarze cp++; 11919fc28d5cSschwarze else if ( ! isspace((unsigned char)*cp)) 1192423631c9Sschwarze break; 1193423631c9Sschwarze } 1194423631c9Sschwarze 1195972cfc25Sschwarze /* Skip trailing backslash. */ 1196972cfc25Sschwarze 1197972cfc25Sschwarze sz = strlen(cp); 119830ffce23Sschwarze if (sz > 0 && cp[sz - 1] == '\\') 1199972cfc25Sschwarze sz--; 1200972cfc25Sschwarze 1201423631c9Sschwarze /* Skip trailing whitespace. */ 1202423631c9Sschwarze 1203972cfc25Sschwarze for (; sz; sz--) 1204423631c9Sschwarze if ( ! isspace((unsigned char)cp[sz-1])) 1205423631c9Sschwarze break; 1206423631c9Sschwarze 1207423631c9Sschwarze /* Skip empty strings. */ 1208423631c9Sschwarze 1209423631c9Sschwarze if (sz == 0) 1210423631c9Sschwarze return; 1211423631c9Sschwarze 1212423631c9Sschwarze if (*dest == NULL) { 1213423631c9Sschwarze *dest = mandoc_strndup(cp, sz); 1214423631c9Sschwarze return; 1215423631c9Sschwarze } 1216423631c9Sschwarze 1217423631c9Sschwarze mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp); 1218423631c9Sschwarze free(*dest); 1219423631c9Sschwarze *dest = cp; 1220423631c9Sschwarze } 1221423631c9Sschwarze 1222fa2127f9Sschwarze /* --- main functions of the roff parser ---------------------------------- */ 1223fa2127f9Sschwarze 1224cd14d642Sschwarze /* 1225cd14d642Sschwarze * Save comments preceding the title macro, for example in order to 1226cd14d642Sschwarze * preserve Copyright and license headers in HTML output, 1227cd14d642Sschwarze * provide diagnostics about RCS ids and trailing whitespace in comments, 1228cd14d642Sschwarze * then discard comments including preceding whitespace. 1229cd14d642Sschwarze * This function also handles input line continuation. 1230cd14d642Sschwarze */ 1231928431b4Sschwarze static int 1232cd14d642Sschwarze roff_parse_comment(struct roff *r, struct buf *buf, int ln, int pos, char ec) 1233928431b4Sschwarze { 1234928431b4Sschwarze struct roff_node *n; /* used for header comments */ 1235928431b4Sschwarze const char *start; /* start of the string to process */ 1236928431b4Sschwarze const char *cp; /* for RCS id parsing */ 1237928431b4Sschwarze char *stesc; /* start of an escape sequence ('\\') */ 1238928431b4Sschwarze char *ep; /* end of comment string */ 1239928431b4Sschwarze int rcsid; /* kind of RCS id seen */ 1240928431b4Sschwarze 1241928431b4Sschwarze for (start = stesc = buf->buf + pos;; stesc++) { 1242cd14d642Sschwarze /* 1243cd14d642Sschwarze * XXX Ugly hack: Remove the newline character that 1244cd14d642Sschwarze * mparse_buf_r() appended to mark the end of input 1245cd14d642Sschwarze * if it is not preceded by an escape character. 1246cd14d642Sschwarze */ 1247cd14d642Sschwarze if (stesc[0] == '\n') { 1248cd14d642Sschwarze assert(stesc[1] == '\0'); 1249cd14d642Sschwarze stesc[0] = '\0'; 1250cd14d642Sschwarze } 1251cd14d642Sschwarze 1252928431b4Sschwarze /* The line ends without continuation or comment. */ 1253928431b4Sschwarze if (stesc[0] == '\0') 1254928431b4Sschwarze return ROFF_CONT; 1255928431b4Sschwarze 1256928431b4Sschwarze /* Unescaped byte: skip it. */ 1257cd14d642Sschwarze if (stesc[0] != ec) 1258928431b4Sschwarze continue; 1259928431b4Sschwarze 1260cd14d642Sschwarze /* 1261cd14d642Sschwarze * XXX Ugly hack: Do not attempt to append another line 1262cd14d642Sschwarze * if the function mparse_buf_r() appended a newline 1263cd14d642Sschwarze * character to indicate the end of input. 1264cd14d642Sschwarze */ 1265cd14d642Sschwarze if (stesc[1] == '\n') { 1266cd14d642Sschwarze assert(stesc[2] == '\0'); 1267cd14d642Sschwarze stesc[0] = '\0'; 1268cd14d642Sschwarze return ROFF_CONT; 1269cd14d642Sschwarze } 1270cd14d642Sschwarze 1271cd14d642Sschwarze /* 1272cd14d642Sschwarze * An escape character at the end of an input line 1273cd14d642Sschwarze * requests line continuation. 1274cd14d642Sschwarze */ 1275928431b4Sschwarze if (stesc[1] == '\0') { 1276928431b4Sschwarze stesc[0] = '\0'; 1277928431b4Sschwarze return ROFF_IGN | ROFF_APPEND; 1278928431b4Sschwarze } 1279928431b4Sschwarze 1280928431b4Sschwarze /* Found a comment: process it. */ 1281928431b4Sschwarze if (stesc[1] == '"' || stesc[1] == '#') 1282928431b4Sschwarze break; 1283928431b4Sschwarze 1284928431b4Sschwarze /* Escaped escape character: skip them both. */ 1285cd14d642Sschwarze if (stesc[1] == ec) 1286928431b4Sschwarze stesc++; 1287928431b4Sschwarze } 1288928431b4Sschwarze 1289928431b4Sschwarze /* Look for an RCS id in the comment. */ 1290928431b4Sschwarze 1291928431b4Sschwarze rcsid = 0; 1292928431b4Sschwarze if ((cp = strstr(stesc + 2, "$" "OpenBSD")) != NULL) { 1293928431b4Sschwarze rcsid = 1 << MANDOC_OS_OPENBSD; 1294928431b4Sschwarze cp += 8; 1295928431b4Sschwarze } else if ((cp = strstr(stesc + 2, "$" "NetBSD")) != NULL) { 1296928431b4Sschwarze rcsid = 1 << MANDOC_OS_NETBSD; 1297928431b4Sschwarze cp += 7; 1298928431b4Sschwarze } 1299928431b4Sschwarze if (cp != NULL && isalnum((unsigned char)*cp) == 0 && 1300928431b4Sschwarze strchr(cp, '$') != NULL) { 1301928431b4Sschwarze if (r->man->meta.rcsids & rcsid) 1302928431b4Sschwarze mandoc_msg(MANDOCERR_RCS_REP, ln, 1303928431b4Sschwarze (int)(stesc - buf->buf) + 2, "%s", stesc + 1); 1304928431b4Sschwarze r->man->meta.rcsids |= rcsid; 1305928431b4Sschwarze } 1306928431b4Sschwarze 1307928431b4Sschwarze /* Warn about trailing whitespace at the end of the comment. */ 1308928431b4Sschwarze 1309928431b4Sschwarze ep = strchr(stesc + 2, '\0') - 1; 1310928431b4Sschwarze if (*ep == '\n') 1311928431b4Sschwarze *ep-- = '\0'; 1312928431b4Sschwarze if (*ep == ' ' || *ep == '\t') 1313928431b4Sschwarze mandoc_msg(MANDOCERR_SPACE_EOL, 1314928431b4Sschwarze ln, (int)(ep - buf->buf), NULL); 1315928431b4Sschwarze 1316928431b4Sschwarze /* Save comments preceding the title macro in the syntax tree. */ 1317928431b4Sschwarze 1318928431b4Sschwarze if (r->options & MPARSE_COMMENT) { 1319928431b4Sschwarze while (*ep == ' ' || *ep == '\t') 1320928431b4Sschwarze ep--; 1321928431b4Sschwarze ep[1] = '\0'; 1322928431b4Sschwarze n = roff_node_alloc(r->man, ln, stesc + 1 - buf->buf, 1323928431b4Sschwarze ROFFT_COMMENT, TOKEN_NONE); 1324928431b4Sschwarze n->string = mandoc_strdup(stesc + 2); 1325928431b4Sschwarze roff_node_append(r->man, n); 1326928431b4Sschwarze n->flags |= NODE_VALID | NODE_ENDED; 1327928431b4Sschwarze r->man->next = ROFF_NEXT_SIBLING; 1328928431b4Sschwarze } 1329928431b4Sschwarze 1330928431b4Sschwarze /* The comment requests line continuation. */ 1331928431b4Sschwarze 1332928431b4Sschwarze if (stesc[1] == '#') { 1333928431b4Sschwarze *stesc = '\0'; 1334928431b4Sschwarze return ROFF_IGN | ROFF_APPEND; 1335928431b4Sschwarze } 1336928431b4Sschwarze 1337928431b4Sschwarze /* Discard the comment including preceding whitespace. */ 1338928431b4Sschwarze 1339928431b4Sschwarze while (stesc > start && stesc[-1] == ' ' && 1340928431b4Sschwarze (stesc == start + 1 || stesc[-2] != '\\')) 1341928431b4Sschwarze stesc--; 1342928431b4Sschwarze *stesc = '\0'; 1343928431b4Sschwarze return ROFF_CONT; 1344928431b4Sschwarze } 1345928431b4Sschwarze 1346769ee804Sschwarze /* 1347e94357f9Sschwarze * In the current line, expand escape sequences that produce parsable 1348e94357f9Sschwarze * input text. Also check the syntax of the remaining escape sequences, 1349e94357f9Sschwarze * which typically produce output glyphs or change formatter state. 1350769ee804Sschwarze */ 1351b7f92c5fSschwarze static int 1352cd14d642Sschwarze roff_expand(struct roff *r, struct buf *buf, int ln, int pos, char ec) 1353769ee804Sschwarze { 1354cd14d642Sschwarze char ubuf[24]; /* buffer to print a number */ 13553dc5225dSschwarze struct mctx *ctx; /* current macro call context */ 1356cd14d642Sschwarze const char *res; /* the string to be pasted */ 1357cd14d642Sschwarze const char *src; /* source for copying */ 1358cd14d642Sschwarze char *dst; /* destination for copying */ 13596d9b308dSschwarze enum mandoc_esc subtype; /* return value from roff_escape */ 1360cd14d642Sschwarze int iesc; /* index of leading escape char */ 1361cd14d642Sschwarze int inam; /* index of the escape name */ 1362cd14d642Sschwarze int iarg; /* index beginning the argument */ 1363cd14d642Sschwarze int iendarg; /* index right after the argument */ 1364cd14d642Sschwarze int iend; /* index right after the sequence */ 1365a72149c5Sschwarze int isrc, idst; /* to reduce \\ and \. in names */ 136607530f0aSschwarze int deftype; /* type of definition to paste */ 1367cd14d642Sschwarze int argi; /* macro argument index */ 1368cd14d642Sschwarze int quote_args; /* true for \\$@, false for \\$* */ 1369cd14d642Sschwarze int asz; /* length of the replacement */ 1370cd14d642Sschwarze int rsz; /* length of the rest of the string */ 1371cd14d642Sschwarze int npos; /* position in numeric expression */ 1372cd14d642Sschwarze int expand_count; /* to avoid infinite loops */ 1373be477484Sschwarze 1374be477484Sschwarze expand_count = 0; 1375cd14d642Sschwarze while (buf->buf[pos] != '\0') { 13761631389bSschwarze 1377e94357f9Sschwarze /* 1378cd14d642Sschwarze * Skip plain ASCII characters. 1379e94357f9Sschwarze * If we have a non-standard escape character, 1380cd14d642Sschwarze * escape literal backslashes because all processing in 1381cd14d642Sschwarze * subsequent functions uses the standard escaping rules. 1382e94357f9Sschwarze */ 13838376723aSschwarze 1384cd14d642Sschwarze if (buf->buf[pos] != ec) { 13857bda13b1Sschwarze if (buf->buf[pos] == '\\') { 1386cd14d642Sschwarze roff_expand_patch(buf, pos, "\\e", pos + 1); 1387cd14d642Sschwarze pos++; 1388be477484Sschwarze } 1389cd14d642Sschwarze pos++; 13908376723aSschwarze continue; 1391be477484Sschwarze } 13928376723aSschwarze 1393cd14d642Sschwarze /* 1394cd14d642Sschwarze * Parse escape sequences, 1395cd14d642Sschwarze * issue diagnostic messages when appropriate, 1396cd14d642Sschwarze * and skip sequences that do not need expansion. 1397cd14d642Sschwarze * If we have a non-standard escape character, translate 1398cd14d642Sschwarze * it to backslashes and translate backslashes to \e. 1399cd14d642Sschwarze */ 14008376723aSschwarze 14019784ce3eSschwarze if (roff_escape(buf->buf, ln, pos, &iesc, &inam, 14029784ce3eSschwarze &iarg, &iendarg, &iend) != ESCAPE_EXPAND) { 1403cd14d642Sschwarze while (pos < iend) { 1404cd14d642Sschwarze if (buf->buf[pos] == ec) { 1405cd14d642Sschwarze buf->buf[pos] = '\\'; 1406cd14d642Sschwarze if (pos + 1 < iend) 1407cd14d642Sschwarze pos++; 1408cd14d642Sschwarze } else if (buf->buf[pos] == '\\') { 1409cd14d642Sschwarze roff_expand_patch(buf, 1410cd14d642Sschwarze pos, "\\e", pos + 1); 1411cd14d642Sschwarze pos++; 1412cd14d642Sschwarze iend++; 1413cd14d642Sschwarze } 1414cd14d642Sschwarze pos++; 1415cd14d642Sschwarze } 14168376723aSschwarze continue; 1417cd14d642Sschwarze } 14181eb5132dSschwarze 1419a72149c5Sschwarze /* Reduce \\ and \. in names. */ 1420a72149c5Sschwarze 1421a72149c5Sschwarze if (buf->buf[inam] == '*' || buf->buf[inam] == 'n') { 1422a72149c5Sschwarze isrc = idst = iarg; 1423a72149c5Sschwarze while (isrc < iendarg) { 1424a72149c5Sschwarze if (isrc + 1 < iendarg && 1425a72149c5Sschwarze buf->buf[isrc] == '\\' && 1426a72149c5Sschwarze (buf->buf[isrc + 1] == '\\' || 1427a72149c5Sschwarze buf->buf[isrc + 1] == '.')) 1428a72149c5Sschwarze isrc++; 1429a72149c5Sschwarze buf->buf[idst++] = buf->buf[isrc++]; 1430a72149c5Sschwarze } 1431a72149c5Sschwarze iendarg -= isrc - idst; 1432a72149c5Sschwarze } 1433a72149c5Sschwarze 1434cd14d642Sschwarze /* Handle expansion. */ 1435cd14d642Sschwarze 1436c542e817Sschwarze res = NULL; 1437cd14d642Sschwarze switch (buf->buf[inam]) { 143849aff9f8Sschwarze case '*': 1439cd14d642Sschwarze if (iendarg == iarg) 1440cd14d642Sschwarze break; 144107530f0aSschwarze deftype = ROFFDEF_USER | ROFFDEF_PRE; 1442cd14d642Sschwarze if ((res = roff_getstrn(r, buf->buf + iarg, 1443cd14d642Sschwarze iendarg - iarg, &deftype)) != NULL) 1444cd14d642Sschwarze break; 14458138dde8Sschwarze 14468138dde8Sschwarze /* 1447d9a51c35Sjmc * If not overridden, 1448cd14d642Sschwarze * let \*(.T through to the formatters. 14498138dde8Sschwarze */ 14508138dde8Sschwarze 1451cd14d642Sschwarze if (iendarg - iarg == 2 && 1452cd14d642Sschwarze buf->buf[iarg] == '.' && 1453cd14d642Sschwarze buf->buf[iarg + 1] == 'T') { 1454cd14d642Sschwarze roff_setstrn(&r->strtab, ".T", 2, NULL, 0, 0); 1455cd14d642Sschwarze pos = iend; 14568138dde8Sschwarze continue; 14578138dde8Sschwarze } 1458cd14d642Sschwarze 1459cd14d642Sschwarze mandoc_msg(MANDOCERR_STR_UNDEF, ln, iesc, 1460cd14d642Sschwarze "%.*s", iendarg - iarg, buf->buf + iarg); 1461778fe90eSschwarze break; 1462cd14d642Sschwarze 14633dc5225dSschwarze case '$': 14643dc5225dSschwarze if (r->mstackpos < 0) { 1465cd14d642Sschwarze mandoc_msg(MANDOCERR_ARG_UNDEF, ln, iesc, 1466cd14d642Sschwarze "%.*s", iend - iesc, buf->buf + iesc); 14673dc5225dSschwarze break; 14683dc5225dSschwarze } 14693dc5225dSschwarze ctx = r->mstack + r->mstackpos; 1470cd14d642Sschwarze argi = buf->buf[iarg] - '1'; 1471cd14d642Sschwarze if (argi >= 0 && argi <= 8) { 1472cd14d642Sschwarze if (argi < ctx->argc) 1473cd14d642Sschwarze res = ctx->argv[argi]; 14743dc5225dSschwarze break; 14753dc5225dSschwarze } 1476cd14d642Sschwarze if (buf->buf[iarg] == '*') 14773dc5225dSschwarze quote_args = 0; 1478cd14d642Sschwarze else if (buf->buf[iarg] == '@') 14793dc5225dSschwarze quote_args = 1; 14803dc5225dSschwarze else { 1481cd14d642Sschwarze mandoc_msg(MANDOCERR_ARG_NONUM, ln, iesc, 1482cd14d642Sschwarze "%.*s", iend - iesc, buf->buf + iesc); 14833dc5225dSschwarze break; 14843dc5225dSschwarze } 14853dc5225dSschwarze asz = 0; 1486cd14d642Sschwarze for (argi = 0; argi < ctx->argc; argi++) { 1487cd14d642Sschwarze if (argi) 14883dc5225dSschwarze asz++; /* blank */ 14893dc5225dSschwarze if (quote_args) 14903dc5225dSschwarze asz += 2; /* quotes */ 1491cd14d642Sschwarze asz += strlen(ctx->argv[argi]); 14923dc5225dSschwarze } 1493cd14d642Sschwarze if (asz != iend - iesc) { 1494cd14d642Sschwarze rsz = buf->sz - iend; 1495cd14d642Sschwarze if (asz < iend - iesc) 1496cd14d642Sschwarze memmove(buf->buf + iesc + asz, 1497cd14d642Sschwarze buf->buf + iend, rsz); 1498cd14d642Sschwarze buf->sz = iesc + asz + rsz; 1499cd14d642Sschwarze buf->buf = mandoc_realloc(buf->buf, buf->sz); 1500cd14d642Sschwarze if (asz > iend - iesc) 1501cd14d642Sschwarze memmove(buf->buf + iesc + asz, 1502cd14d642Sschwarze buf->buf + iend, rsz); 15033dc5225dSschwarze } 1504cd14d642Sschwarze dst = buf->buf + iesc; 1505cd14d642Sschwarze for (argi = 0; argi < ctx->argc; argi++) { 1506cd14d642Sschwarze if (argi) 1507cd14d642Sschwarze *dst++ = ' '; 15083dc5225dSschwarze if (quote_args) 1509cd14d642Sschwarze *dst++ = '"'; 1510cd14d642Sschwarze src = ctx->argv[argi]; 1511cd14d642Sschwarze while (*src != '\0') 1512cd14d642Sschwarze *dst++ = *src++; 15133dc5225dSschwarze if (quote_args) 1514cd14d642Sschwarze *dst++ = '"'; 15153dc5225dSschwarze } 15163dc5225dSschwarze continue; 151775a6bad9Sschwarze case 'A': 151875a6bad9Sschwarze ubuf[0] = iendarg > iarg ? '1' : '0'; 151975a6bad9Sschwarze ubuf[1] = '\0'; 152075a6bad9Sschwarze res = ubuf; 152175a6bad9Sschwarze break; 152249aff9f8Sschwarze case 'B': 1523778fe90eSschwarze npos = 0; 1524cd14d642Sschwarze ubuf[0] = iendarg > iarg && iend > iendarg && 15253b5bd91eSschwarze roff_evalnum(ln, buf->buf + iarg, &npos, 1526f4432377Sschwarze NULL, 'u', 0) && 1527cd14d642Sschwarze npos == iendarg - iarg ? '1' : '0'; 1528778fe90eSschwarze ubuf[1] = '\0'; 1529cd14d642Sschwarze res = ubuf; 1530778fe90eSschwarze break; 153183a9dfe1Sschwarze case 'V': 153283a9dfe1Sschwarze mandoc_msg(MANDOCERR_UNSUPP, ln, iesc, 153383a9dfe1Sschwarze "%.*s", iend - iesc, buf->buf + iesc); 153483a9dfe1Sschwarze roff_expand_patch(buf, iendarg, "}", iend); 153583a9dfe1Sschwarze roff_expand_patch(buf, iesc, "${", iarg); 153683a9dfe1Sschwarze continue; 15376f49ebc4Sschwarze case 'g': 15386f49ebc4Sschwarze break; 153949aff9f8Sschwarze case 'n': 1540cd14d642Sschwarze if (iendarg > iarg) 154147813146Sschwarze (void)snprintf(ubuf, sizeof(ubuf), "%d", 1542cd14d642Sschwarze roff_getregn(r, buf->buf + iarg, 1543cd14d642Sschwarze iendarg - iarg, buf->buf[inam + 1])); 1544ce205d5fSschwarze else 1545ce205d5fSschwarze ubuf[0] = '\0'; 1546cd14d642Sschwarze res = ubuf; 1547778fe90eSschwarze break; 154849aff9f8Sschwarze case 'w': 15496d9b308dSschwarze rsz = 0; 15506d9b308dSschwarze subtype = ESCAPE_UNDEF; 15516d9b308dSschwarze while (iarg < iendarg) { 15526d9b308dSschwarze asz = subtype == ESCAPE_SKIPCHAR ? 0 : 1; 15536d9b308dSschwarze if (buf->buf[iarg] != '\\') { 15546d9b308dSschwarze rsz += asz; 15556d9b308dSschwarze iarg++; 15566d9b308dSschwarze continue; 15576d9b308dSschwarze } 15586d9b308dSschwarze switch ((subtype = roff_escape(buf->buf, 0, 15596d9b308dSschwarze iarg, NULL, NULL, NULL, NULL, &iarg))) { 15606d9b308dSschwarze case ESCAPE_SPECIAL: 15616d9b308dSschwarze case ESCAPE_NUMBERED: 15626d9b308dSschwarze case ESCAPE_UNICODE: 15636d9b308dSschwarze case ESCAPE_OVERSTRIKE: 15646d9b308dSschwarze case ESCAPE_UNDEF: 15656d9b308dSschwarze break; 15666d9b308dSschwarze case ESCAPE_DEVICE: 15676d9b308dSschwarze asz *= 8; 15686d9b308dSschwarze break; 15696d9b308dSschwarze case ESCAPE_EXPAND: 15706d9b308dSschwarze abort(); 15716d9b308dSschwarze default: 15726d9b308dSschwarze continue; 15736d9b308dSschwarze } 15746d9b308dSschwarze rsz += asz; 15756d9b308dSschwarze } 15766d9b308dSschwarze (void)snprintf(ubuf, sizeof(ubuf), "%d", rsz * 24); 1577cd14d642Sschwarze res = ubuf; 1578cd14d642Sschwarze break; 1579cd14d642Sschwarze default: 1580778fe90eSschwarze break; 1581778fe90eSschwarze } 1582cd14d642Sschwarze if (res == NULL) 1583a5e11edeSschwarze res = ""; 1584cd14d642Sschwarze if (++expand_count > EXPAND_LIMIT || 1585cd14d642Sschwarze buf->sz + strlen(res) > SHRT_MAX) { 1586cd14d642Sschwarze mandoc_msg(MANDOCERR_ROFFLOOP, ln, iesc, NULL); 1587526e306bSschwarze return ROFF_IGN; 1588769ee804Sschwarze } 1589cd14d642Sschwarze roff_expand_patch(buf, iesc, res, iend); 159004e980cbSschwarze } 1591526e306bSschwarze return ROFF_CONT; 1592769ee804Sschwarze } 1593769ee804Sschwarze 159404e980cbSschwarze /* 1595cd14d642Sschwarze * Replace the substring from the start position (inclusive) 1596cd14d642Sschwarze * to end position (exclusive) with the repl(acement) string. 1597cd14d642Sschwarze */ 1598cd14d642Sschwarze static void 1599cd14d642Sschwarze roff_expand_patch(struct buf *buf, int start, const char *repl, int end) 1600cd14d642Sschwarze { 1601cd14d642Sschwarze char *nbuf; 1602cd14d642Sschwarze 160383a9dfe1Sschwarze buf->sz = mandoc_asprintf(&nbuf, "%.*s%s%s", start, buf->buf, 160483a9dfe1Sschwarze repl, buf->buf + end) + 1; 1605cd14d642Sschwarze free(buf->buf); 1606cd14d642Sschwarze buf->buf = nbuf; 1607cd14d642Sschwarze } 1608cd14d642Sschwarze 1609cd14d642Sschwarze /* 16109485fb47Sschwarze * Parse a quoted or unquoted roff-style request or macro argument. 16119485fb47Sschwarze * Return a pointer to the parsed argument, which is either the original 16129485fb47Sschwarze * pointer or advanced by one byte in case the argument is quoted. 16139485fb47Sschwarze * NUL-terminate the argument in place. 16149485fb47Sschwarze * Collapse pairs of quotes inside quoted arguments. 16159485fb47Sschwarze * Advance the argument pointer to the next argument, 16169485fb47Sschwarze * or to the NUL byte terminating the argument line. 16179485fb47Sschwarze */ 16189485fb47Sschwarze char * 1619e94357f9Sschwarze roff_getarg(struct roff *r, char **cpp, int ln, int *pos) 16209485fb47Sschwarze { 1621e94357f9Sschwarze struct buf buf; 1622e94357f9Sschwarze char *cp, *start; 1623e94357f9Sschwarze int newesc, pairs, quoted, white; 16249485fb47Sschwarze 16259485fb47Sschwarze /* Quoting can only start with a new word. */ 16269485fb47Sschwarze start = *cpp; 16279485fb47Sschwarze quoted = 0; 16289485fb47Sschwarze if ('"' == *start) { 16299485fb47Sschwarze quoted = 1; 16309485fb47Sschwarze start++; 16319485fb47Sschwarze } 16329485fb47Sschwarze 1633e94357f9Sschwarze newesc = pairs = white = 0; 16349485fb47Sschwarze for (cp = start; '\0' != *cp; cp++) { 16359485fb47Sschwarze 16369485fb47Sschwarze /* 16379485fb47Sschwarze * Move the following text left 16389485fb47Sschwarze * after quoted quotes and after "\\" and "\t". 16399485fb47Sschwarze */ 16409485fb47Sschwarze if (pairs) 16419485fb47Sschwarze cp[-pairs] = cp[0]; 16429485fb47Sschwarze 16439485fb47Sschwarze if ('\\' == cp[0]) { 16449485fb47Sschwarze /* 16459485fb47Sschwarze * In copy mode, translate double to single 16469485fb47Sschwarze * backslashes and backslash-t to literal tabs. 16479485fb47Sschwarze */ 16489485fb47Sschwarze switch (cp[1]) { 16499485fb47Sschwarze case 'a': 16509485fb47Sschwarze case 't': 1651395bfb10Sschwarze cp[-pairs] = '\t'; 1652e94357f9Sschwarze pairs++; 1653e94357f9Sschwarze cp++; 1654e94357f9Sschwarze break; 16559485fb47Sschwarze case '\\': 16567bda13b1Sschwarze cp[-pairs] = '\\'; 16571c23c756Sschwarze newesc = 1; 16589485fb47Sschwarze pairs++; 16599485fb47Sschwarze cp++; 16609485fb47Sschwarze break; 16619485fb47Sschwarze case ' ': 16629485fb47Sschwarze /* Skip escaped blanks. */ 16639485fb47Sschwarze if (0 == quoted) 16649485fb47Sschwarze cp++; 16659485fb47Sschwarze break; 16669485fb47Sschwarze default: 16679485fb47Sschwarze break; 16689485fb47Sschwarze } 16699485fb47Sschwarze } else if (0 == quoted) { 16709485fb47Sschwarze if (' ' == cp[0]) { 16719485fb47Sschwarze /* Unescaped blanks end unquoted args. */ 16729485fb47Sschwarze white = 1; 16739485fb47Sschwarze break; 16749485fb47Sschwarze } 16759485fb47Sschwarze } else if ('"' == cp[0]) { 16769485fb47Sschwarze if ('"' == cp[1]) { 16779485fb47Sschwarze /* Quoted quotes collapse. */ 16789485fb47Sschwarze pairs++; 16799485fb47Sschwarze cp++; 16809485fb47Sschwarze } else { 16819485fb47Sschwarze /* Unquoted quotes end quoted args. */ 16829485fb47Sschwarze quoted = 2; 16839485fb47Sschwarze break; 16849485fb47Sschwarze } 16859485fb47Sschwarze } 16869485fb47Sschwarze } 16879485fb47Sschwarze 16889485fb47Sschwarze /* Quoted argument without a closing quote. */ 16899485fb47Sschwarze if (1 == quoted) 16909485fb47Sschwarze mandoc_msg(MANDOCERR_ARG_QUOTE, ln, *pos, NULL); 16919485fb47Sschwarze 16929485fb47Sschwarze /* NUL-terminate this argument and move to the next one. */ 16939485fb47Sschwarze if (pairs) 16949485fb47Sschwarze cp[-pairs] = '\0'; 16959485fb47Sschwarze if ('\0' != *cp) { 16969485fb47Sschwarze *cp++ = '\0'; 16979485fb47Sschwarze while (' ' == *cp) 16989485fb47Sschwarze cp++; 16999485fb47Sschwarze } 17009485fb47Sschwarze *pos += (int)(cp - start) + (quoted ? 1 : 0); 17019485fb47Sschwarze *cpp = cp; 17029485fb47Sschwarze 17039485fb47Sschwarze if ('\0' == *cp && (white || ' ' == cp[-1])) 17049485fb47Sschwarze mandoc_msg(MANDOCERR_SPACE_EOL, ln, *pos, NULL); 17059485fb47Sschwarze 1706e94357f9Sschwarze start = mandoc_strdup(start); 1707e94357f9Sschwarze if (newesc == 0) 17089485fb47Sschwarze return start; 1709e94357f9Sschwarze 1710e94357f9Sschwarze buf.buf = start; 1711e94357f9Sschwarze buf.sz = strlen(start) + 1; 1712e94357f9Sschwarze buf.next = NULL; 171329079a11Sschwarze if (roff_expand(r, &buf, ln, 0, '\\') == ROFF_IGN) { 1714e94357f9Sschwarze free(buf.buf); 1715e94357f9Sschwarze buf.buf = mandoc_strdup(""); 1716e94357f9Sschwarze } 1717e94357f9Sschwarze return buf.buf; 17189485fb47Sschwarze } 17199485fb47Sschwarze 17209485fb47Sschwarze 17219485fb47Sschwarze /* 1722f16a1164Sschwarze * Process text streams. 172304e980cbSschwarze */ 1724b7f92c5fSschwarze static int 1725e13b4195Sschwarze roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs) 172604e980cbSschwarze { 172704e980cbSschwarze size_t sz; 172804e980cbSschwarze const char *start; 1729a91e242cSschwarze char *p; 1730a91e242cSschwarze int isz; 173104e980cbSschwarze enum mandoc_esc esc; 173204e980cbSschwarze 1733f16a1164Sschwarze /* Spring the input line trap. */ 1734f16a1164Sschwarze 1735f16a1164Sschwarze if (roffit_lines == 1) { 1736f16a1164Sschwarze isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro); 1737f16a1164Sschwarze free(buf->buf); 1738f16a1164Sschwarze buf->buf = p; 1739f16a1164Sschwarze buf->sz = isz + 1; 1740f16a1164Sschwarze *offs = 0; 1741f16a1164Sschwarze free(roffit_macro); 1742f16a1164Sschwarze roffit_lines = 0; 1743526e306bSschwarze return ROFF_REPARSE; 1744f16a1164Sschwarze } else if (roffit_lines > 1) 1745f16a1164Sschwarze --roffit_lines; 1746f16a1164Sschwarze 1747e13b4195Sschwarze if (roffce_node != NULL && buf->buf[pos] != '\0') { 1748e13b4195Sschwarze if (roffce_lines < 1) { 1749e13b4195Sschwarze r->man->last = roffce_node; 1750e13b4195Sschwarze r->man->next = ROFF_NEXT_SIBLING; 1751e13b4195Sschwarze roffce_lines = 0; 1752e13b4195Sschwarze roffce_node = NULL; 1753e13b4195Sschwarze } else 1754e13b4195Sschwarze roffce_lines--; 1755e13b4195Sschwarze } 1756e13b4195Sschwarze 1757f16a1164Sschwarze /* Convert all breakable hyphens into ASCII_HYPH. */ 1758f16a1164Sschwarze 175928687875Sschwarze start = p = buf->buf + pos; 176004e980cbSschwarze 176128687875Sschwarze while (*p != '\0') { 176204e980cbSschwarze sz = strcspn(p, "-\\"); 176304e980cbSschwarze p += sz; 176404e980cbSschwarze 176528687875Sschwarze if (*p == '\0') 176604e980cbSschwarze break; 176704e980cbSschwarze 176828687875Sschwarze if (*p == '\\') { 176904e980cbSschwarze /* Skip over escapes. */ 177004e980cbSschwarze p++; 1771c702e996Sschwarze esc = mandoc_escape((const char **)&p, NULL, NULL); 177228687875Sschwarze if (esc == ESCAPE_ERROR) 177304e980cbSschwarze break; 177452b834b3Sschwarze while (*p == '-') 177552b834b3Sschwarze p++; 177604e980cbSschwarze continue; 177704e980cbSschwarze } else if (p == start) { 177804e980cbSschwarze p++; 177904e980cbSschwarze continue; 1780769ee804Sschwarze } 1781769ee804Sschwarze 1782d724826eSschwarze if (isalpha((unsigned char)p[-1]) && 1783d724826eSschwarze isalpha((unsigned char)p[1])) 178404e980cbSschwarze *p = ASCII_HYPH; 178504e980cbSschwarze p++; 178604e980cbSschwarze } 1787526e306bSschwarze return ROFF_CONT; 178804e980cbSschwarze } 1789769ee804Sschwarze 1790b7f92c5fSschwarze int 1791dd9cc97dSschwarze roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs, size_t len) 1792f673a3c7Sschwarze { 179314a309e3Sschwarze enum roff_tok t; 1794b7f92c5fSschwarze int e; 179528687875Sschwarze int pos; /* parse point */ 1796a2c9ff1aSschwarze int spos; /* saved parse point for messages */ 179728687875Sschwarze int ppos; /* original offset in buf->buf */ 179828687875Sschwarze int ctl; /* macro line (boolean) */ 179928687875Sschwarze 180028687875Sschwarze ppos = pos = *offs; 1801f673a3c7Sschwarze 1802dd9cc97dSschwarze if (len > 80 && r->tbl == NULL && r->eqn == NULL && 1803dd9cc97dSschwarze (r->man->flags & ROFF_NOFILL) == 0 && 1804dd9cc97dSschwarze strchr(" .\\", buf->buf[pos]) == NULL && 1805dd9cc97dSschwarze buf->buf[pos] != r->control && 1806dd9cc97dSschwarze strcspn(buf->buf, " ") < 80) 1807dd9cc97dSschwarze mandoc_msg(MANDOCERR_TEXT_LONG, ln, (int)len - 1, 1808dd9cc97dSschwarze "%.20s...", buf->buf + pos); 1809dd9cc97dSschwarze 181057ca9bd5Sschwarze /* Handle in-line equation delimiters. */ 181157ca9bd5Sschwarze 18128e45305fSschwarze if (r->tbl == NULL && 18138e45305fSschwarze r->last_eqn != NULL && r->last_eqn->delim && 181457ca9bd5Sschwarze (r->eqn == NULL || r->eqn_inline)) { 181528687875Sschwarze e = roff_eqndelim(r, buf, pos); 181657ca9bd5Sschwarze if (e == ROFF_REPARSE) 1817526e306bSschwarze return e; 181857ca9bd5Sschwarze assert(e == ROFF_CONT); 181957ca9bd5Sschwarze } 182057ca9bd5Sschwarze 1821928431b4Sschwarze /* Handle comments and escape sequences. */ 1822928431b4Sschwarze 1823928431b4Sschwarze e = roff_parse_comment(r, buf, ln, pos, r->escape); 1824928431b4Sschwarze if ((e & ROFF_MASK) == ROFF_IGN) 1825928431b4Sschwarze return e; 1826928431b4Sschwarze assert(e == ROFF_CONT); 1827769ee804Sschwarze 1828e94357f9Sschwarze e = roff_expand(r, buf, ln, pos, r->escape); 1829b7f92c5fSschwarze if ((e & ROFF_MASK) == ROFF_IGN) 1830526e306bSschwarze return e; 183128687875Sschwarze assert(e == ROFF_CONT); 1832769ee804Sschwarze 183328687875Sschwarze ctl = roff_getcontrol(r, buf->buf, &pos); 1834a35fc07aSschwarze 1835769ee804Sschwarze /* 183662d9ccdbSschwarze * First, if a scope is open and we're not a macro, pass the 1837328cf81fSschwarze * text through the macro's filter. 1838328cf81fSschwarze * Equations process all content themselves. 1839328cf81fSschwarze * Tables process almost all content themselves, but we want 1840328cf81fSschwarze * to warn about macros before passing it there. 1841f673a3c7Sschwarze */ 184262d9ccdbSschwarze 1843328cf81fSschwarze if (r->last != NULL && ! ctl) { 184462d9ccdbSschwarze t = r->last->tok; 184528687875Sschwarze e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs); 1846b7f92c5fSschwarze if ((e & ROFF_MASK) == ROFF_IGN) 1847526e306bSschwarze return e; 1848b7f92c5fSschwarze e &= ~ROFF_MASK; 1849b7f92c5fSschwarze } else 1850b7f92c5fSschwarze e = ROFF_IGN; 1851bf9acac6Sschwarze if (r->eqn != NULL && strncmp(buf->buf + ppos, ".EN", 3)) { 1852bf9acac6Sschwarze eqn_read(r->eqn, buf->buf + ppos); 1853b7f92c5fSschwarze return e; 1854bf9acac6Sschwarze } 1855d93f8561Sschwarze if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) { 1856d93f8561Sschwarze tbl_read(r->tbl, ln, buf->buf, ppos); 1857fb382a01Sschwarze roff_addtbl(r->man, ln, r->tbl); 1858b7f92c5fSschwarze return e; 1859d93f8561Sschwarze } 186067217f5cSschwarze if ( ! ctl) { 186167217f5cSschwarze r->options &= ~MPARSE_COMMENT; 1862b7f92c5fSschwarze return roff_parsetext(r, buf, pos, offs) | e; 186367217f5cSschwarze } 186462d9ccdbSschwarze 18659d7b4fe8Sschwarze /* Skip empty request lines. */ 18669d7b4fe8Sschwarze 186728687875Sschwarze if (buf->buf[pos] == '"') { 1868a5a5f808Sschwarze mandoc_msg(MANDOCERR_COMMENT_BAD, ln, pos, NULL); 1869526e306bSschwarze return ROFF_IGN; 187028687875Sschwarze } else if (buf->buf[pos] == '\0') 1871526e306bSschwarze return ROFF_IGN; 18729d7b4fe8Sschwarze 187362d9ccdbSschwarze /* 187462d9ccdbSschwarze * If a scope is open, go to the child handler for that macro, 187562d9ccdbSschwarze * as it may want to preprocess before doing anything with it. 187662d9ccdbSschwarze */ 187762d9ccdbSschwarze 187862d9ccdbSschwarze if (r->last) { 1879f673a3c7Sschwarze t = r->last->tok; 1880526e306bSschwarze return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs); 188162d9ccdbSschwarze } 188262d9ccdbSschwarze 188367217f5cSschwarze r->options &= ~MPARSE_COMMENT; 1884a2c9ff1aSschwarze spos = pos; 1885a2c9ff1aSschwarze t = roff_parse(r, buf->buf, &pos, ln, ppos); 1886f79258f3Sschwarze return roff_req_or_macro(r, t, buf, ln, spos, pos, offs); 1887f79258f3Sschwarze } 1888a2c9ff1aSschwarze 1889f79258f3Sschwarze /* 1890f79258f3Sschwarze * Handle a new request or macro. 1891f79258f3Sschwarze * May be called outside any scope or from inside a conditional scope. 1892f79258f3Sschwarze */ 1893f79258f3Sschwarze static int 1894f79258f3Sschwarze roff_req_or_macro(ROFF_ARGS) { 1895a2c9ff1aSschwarze 1896f79258f3Sschwarze /* For now, tables ignore most macros and some request. */ 1897f79258f3Sschwarze 1898f79258f3Sschwarze if (r->tbl != NULL && (tok == TOKEN_NONE || tok == ROFF_TS || 1899f79258f3Sschwarze tok == ROFF_br || tok == ROFF_ce || tok == ROFF_rj || 1900f79258f3Sschwarze tok == ROFF_sp)) { 1901a5a5f808Sschwarze mandoc_msg(MANDOCERR_TBLMACRO, 1902f79258f3Sschwarze ln, ppos, "%s", buf->buf + ppos); 1903f79258f3Sschwarze if (tok != TOKEN_NONE) 1904526e306bSschwarze return ROFF_IGN; 190539aede77Sschwarze while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ') 190639aede77Sschwarze pos++; 19079b423113Sschwarze while (buf->buf[pos] == ' ') 190839aede77Sschwarze pos++; 1909d93f8561Sschwarze tbl_read(r->tbl, ln, buf->buf, pos); 1910fb382a01Sschwarze roff_addtbl(r->man, ln, r->tbl); 1911d93f8561Sschwarze return ROFF_IGN; 1912a2c9ff1aSschwarze } 1913a2c9ff1aSschwarze 1914e13b4195Sschwarze /* For now, let high level macros abort .ce mode. */ 1915e13b4195Sschwarze 1916f79258f3Sschwarze if (roffce_node != NULL && 1917f79258f3Sschwarze (tok == TOKEN_NONE || tok == ROFF_Dd || tok == ROFF_EQ || 1918f79258f3Sschwarze tok == ROFF_TH || tok == ROFF_TS)) { 1919e13b4195Sschwarze r->man->last = roffce_node; 1920e13b4195Sschwarze r->man->next = ROFF_NEXT_SIBLING; 1921e13b4195Sschwarze roffce_lines = 0; 1922e13b4195Sschwarze roffce_node = NULL; 1923e13b4195Sschwarze } 1924e13b4195Sschwarze 192562d9ccdbSschwarze /* 1926a2c9ff1aSschwarze * This is neither a roff request nor a user-defined macro. 1927a2c9ff1aSschwarze * Let the standard macro set parsers handle it. 192862d9ccdbSschwarze */ 192962d9ccdbSschwarze 1930f79258f3Sschwarze if (tok == TOKEN_NONE) 1931526e306bSschwarze return ROFF_CONT; 1932f673a3c7Sschwarze 1933f79258f3Sschwarze /* Execute a roff request or a user-defined macro. */ 1934a2c9ff1aSschwarze 1935f79258f3Sschwarze return (*roffs[tok].proc)(r, tok, buf, ln, ppos, pos, offs); 193662d9ccdbSschwarze } 1937f673a3c7Sschwarze 19383dc5225dSschwarze /* 19393dc5225dSschwarze * Internal interface function to tell the roff parser that execution 19403dc5225dSschwarze * of the current macro ended. This is required because macro 19413dc5225dSschwarze * definitions usually do not end with a .return request. 19423dc5225dSschwarze */ 19433dc5225dSschwarze void 19443dc5225dSschwarze roff_userret(struct roff *r) 19453dc5225dSschwarze { 19463dc5225dSschwarze struct mctx *ctx; 19473dc5225dSschwarze int i; 19483dc5225dSschwarze 19493dc5225dSschwarze assert(r->mstackpos >= 0); 19503dc5225dSschwarze ctx = r->mstack + r->mstackpos; 19513dc5225dSschwarze for (i = 0; i < ctx->argc; i++) 19523dc5225dSschwarze free(ctx->argv[i]); 19533dc5225dSschwarze ctx->argc = 0; 19543dc5225dSschwarze r->mstackpos--; 19553dc5225dSschwarze } 19563dc5225dSschwarze 19572791bd1cSschwarze void 195862d9ccdbSschwarze roff_endparse(struct roff *r) 195962d9ccdbSschwarze { 1960d93f8561Sschwarze if (r->last != NULL) 1961a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_NOEND, r->last->line, 1962a5a5f808Sschwarze r->last->col, "%s", roff_name[r->last->tok]); 19632791bd1cSschwarze 1964d93f8561Sschwarze if (r->eqn != NULL) { 1965a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_NOEND, 1966bf9acac6Sschwarze r->eqn->node->line, r->eqn->node->pos, "EQ"); 1967bf9acac6Sschwarze eqn_parse(r->eqn); 1968bf9acac6Sschwarze r->eqn = NULL; 19698d973ab1Sschwarze } 19708d973ab1Sschwarze 1971d93f8561Sschwarze if (r->tbl != NULL) { 1972fb382a01Sschwarze tbl_end(r->tbl, 1); 1973d93f8561Sschwarze r->tbl = NULL; 19742791bd1cSschwarze } 1975f673a3c7Sschwarze } 1976f673a3c7Sschwarze 1977f673a3c7Sschwarze /* 1978f79258f3Sschwarze * Parse the request or macro name at buf[*pos]. 1979f79258f3Sschwarze * Return ROFF_RENAMED, ROFF_USERDEF, or a ROFF_* token value. 1980f79258f3Sschwarze * For empty, undefined, mdoc(7), and man(7) macros, return TOKEN_NONE. 1981f79258f3Sschwarze * As a side effect, set r->current_string to the definition or to NULL. 1982f673a3c7Sschwarze */ 198314a309e3Sschwarze static enum roff_tok 19847963e2c1Sschwarze roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos) 1985f673a3c7Sschwarze { 19867963e2c1Sschwarze char *cp; 1987e53bb406Sschwarze const char *mac; 1988e53bb406Sschwarze size_t maclen; 198907530f0aSschwarze int deftype; 199014a309e3Sschwarze enum roff_tok t; 1991f673a3c7Sschwarze 19927963e2c1Sschwarze cp = buf + *pos; 19937963e2c1Sschwarze 19947963e2c1Sschwarze if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp) 199514a309e3Sschwarze return TOKEN_NONE; 1996f673a3c7Sschwarze 19977963e2c1Sschwarze mac = cp; 19983b5bd91eSschwarze maclen = roff_getname(&cp, ln, ppos); 1999f673a3c7Sschwarze 200007530f0aSschwarze deftype = ROFFDEF_USER | ROFFDEF_REN; 200107530f0aSschwarze r->current_string = roff_getstrn(r, mac, maclen, &deftype); 200207530f0aSschwarze switch (deftype) { 200307530f0aSschwarze case ROFFDEF_USER: 200407530f0aSschwarze t = ROFF_USERDEF; 200507530f0aSschwarze break; 200607530f0aSschwarze case ROFFDEF_REN: 200707530f0aSschwarze t = ROFF_RENAMED; 200807530f0aSschwarze break; 200907530f0aSschwarze default: 201007530f0aSschwarze t = roffhash_find(r->reqtab, mac, maclen); 201107530f0aSschwarze break; 201207530f0aSschwarze } 201314a309e3Sschwarze if (t != TOKEN_NONE) 20147963e2c1Sschwarze *pos = cp - buf; 20150cdab9e9Sschwarze else if (deftype == ROFFDEF_UNDEF) { 20160cdab9e9Sschwarze /* Using an undefined macro defines it to be empty. */ 20170cdab9e9Sschwarze roff_setstrn(&r->strtab, mac, maclen, "", 0, 0); 20180cdab9e9Sschwarze roff_setstrn(&r->rentab, mac, maclen, NULL, 0, 0); 20190cdab9e9Sschwarze } 2020526e306bSschwarze return t; 2021f673a3c7Sschwarze } 2022f673a3c7Sschwarze 2023fa2127f9Sschwarze /* --- handling of request blocks ----------------------------------------- */ 2024fa2127f9Sschwarze 2025fd9d9245Sschwarze /* 2026fd9d9245Sschwarze * Close a macro definition block or an "ignore" block. 2027fd9d9245Sschwarze */ 2028b7f92c5fSschwarze static int 202962d9ccdbSschwarze roff_cblock(ROFF_ARGS) 2030f673a3c7Sschwarze { 2031fd9d9245Sschwarze int rr; 203262d9ccdbSschwarze 203328687875Sschwarze if (r->last == NULL) { 2034a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, ".."); 2035526e306bSschwarze return ROFF_IGN; 203662d9ccdbSschwarze } 2037f673a3c7Sschwarze 203862d9ccdbSschwarze switch (r->last->tok) { 203949aff9f8Sschwarze case ROFF_am: 204049aff9f8Sschwarze case ROFF_ami: 204149aff9f8Sschwarze case ROFF_de: 204249aff9f8Sschwarze case ROFF_dei: 204349aff9f8Sschwarze case ROFF_ig: 204462d9ccdbSschwarze break; 2045fd9d9245Sschwarze case ROFF_am1: 2046fd9d9245Sschwarze case ROFF_de1: 2047fd9d9245Sschwarze /* Remapped in roff_block(). */ 2048fd9d9245Sschwarze abort(); 204962d9ccdbSschwarze default: 2050a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, ".."); 2051526e306bSschwarze return ROFF_IGN; 205262d9ccdbSschwarze } 205362d9ccdbSschwarze 2054fd9d9245Sschwarze roffnode_pop(r); 2055fd9d9245Sschwarze roffnode_cleanscope(r); 2056fd9d9245Sschwarze 2057fd9d9245Sschwarze /* 2058fd9d9245Sschwarze * If a conditional block with braces is still open, 2059fd9d9245Sschwarze * check for "\}" block end markers. 2060fd9d9245Sschwarze */ 2061fd9d9245Sschwarze 2062fd9d9245Sschwarze if (r->last != NULL && r->last->endspan < 0) { 2063fd9d9245Sschwarze rr = 1; /* If arguments follow "\}", warn about them. */ 2064fd9d9245Sschwarze roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr); 2065fd9d9245Sschwarze } 2066fd9d9245Sschwarze 206728687875Sschwarze if (buf->buf[pos] != '\0') 2068a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, 206928687875Sschwarze ".. %s", buf->buf + pos); 207062d9ccdbSschwarze 2071526e306bSschwarze return ROFF_IGN; 207262d9ccdbSschwarze } 207362d9ccdbSschwarze 20747dd0ff50Sschwarze /* 20757dd0ff50Sschwarze * Pop all nodes ending at the end of the current input line. 20767dd0ff50Sschwarze * Return the number of loops ended. 20777dd0ff50Sschwarze */ 2078b7f92c5fSschwarze static int 207962d9ccdbSschwarze roffnode_cleanscope(struct roff *r) 208062d9ccdbSschwarze { 2081b7f92c5fSschwarze int inloop; 208262d9ccdbSschwarze 2083b7f92c5fSschwarze inloop = 0; 2084fd9d9245Sschwarze while (r->last != NULL && r->last->endspan > 0) { 208544f8358dSschwarze if (--r->last->endspan != 0) 208662d9ccdbSschwarze break; 2087b7f92c5fSschwarze inloop += roffnode_pop(r); 208862d9ccdbSschwarze } 2089b7f92c5fSschwarze return inloop; 209062d9ccdbSschwarze } 209162d9ccdbSschwarze 20927dd0ff50Sschwarze /* 2093fd9d9245Sschwarze * Handle the closing "\}" of a conditional block. 20947dd0ff50Sschwarze * Apart from generating warnings, this only pops nodes. 20957dd0ff50Sschwarze * Return the number of loops ended. 20967dd0ff50Sschwarze */ 2097b7f92c5fSschwarze static int 20984d112f14Sschwarze roff_ccond(struct roff *r, int ln, int ppos) 209962d9ccdbSschwarze { 210062d9ccdbSschwarze if (NULL == r->last) { 2101a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}"); 2102b7f92c5fSschwarze return 0; 210362d9ccdbSschwarze } 210462d9ccdbSschwarze 210562d9ccdbSschwarze switch (r->last->tok) { 210649aff9f8Sschwarze case ROFF_el: 210749aff9f8Sschwarze case ROFF_ie: 210849aff9f8Sschwarze case ROFF_if: 2109b7f92c5fSschwarze case ROFF_while: 211062d9ccdbSschwarze break; 211162d9ccdbSschwarze default: 2112a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}"); 2113b7f92c5fSschwarze return 0; 211462d9ccdbSschwarze } 211562d9ccdbSschwarze 211662d9ccdbSschwarze if (r->last->endspan > -1) { 2117a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}"); 2118b7f92c5fSschwarze return 0; 211962d9ccdbSschwarze } 212062d9ccdbSschwarze 2121b7f92c5fSschwarze return roffnode_pop(r) + roffnode_cleanscope(r); 2122f673a3c7Sschwarze } 2123f673a3c7Sschwarze 2124b7f92c5fSschwarze static int 212562d9ccdbSschwarze roff_block(ROFF_ARGS) 2126f673a3c7Sschwarze { 212707530f0aSschwarze const char *name, *value; 212807530f0aSschwarze char *call, *cp, *iname, *rname; 212907530f0aSschwarze size_t csz, namesz, rsz; 213007530f0aSschwarze int deftype; 213162d9ccdbSschwarze 2132803e5876Sschwarze /* Ignore groff compatibility mode for now. */ 2133e53bb406Sschwarze 213428687875Sschwarze if (tok == ROFF_de1) 2135803e5876Sschwarze tok = ROFF_de; 2136d04ca39fSschwarze else if (tok == ROFF_dei1) 2137d04ca39fSschwarze tok = ROFF_dei; 213828687875Sschwarze else if (tok == ROFF_am1) 2139803e5876Sschwarze tok = ROFF_am; 2140d04ca39fSschwarze else if (tok == ROFF_ami1) 2141d04ca39fSschwarze tok = ROFF_ami; 2142803e5876Sschwarze 2143803e5876Sschwarze /* Parse the macro name argument. */ 2144803e5876Sschwarze 214528687875Sschwarze cp = buf->buf + pos; 214628687875Sschwarze if (tok == ROFF_ig) { 2147803e5876Sschwarze iname = NULL; 2148803e5876Sschwarze namesz = 0; 2149803e5876Sschwarze } else { 2150803e5876Sschwarze iname = cp; 21513b5bd91eSschwarze namesz = roff_getname(&cp, ln, ppos); 2152803e5876Sschwarze iname[namesz] = '\0'; 2153803e5876Sschwarze } 2154803e5876Sschwarze 2155803e5876Sschwarze /* Resolve the macro name argument if it is indirect. */ 2156803e5876Sschwarze 215728687875Sschwarze if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) { 215807530f0aSschwarze deftype = ROFFDEF_USER; 215907530f0aSschwarze name = roff_getstrn(r, iname, namesz, &deftype); 216007530f0aSschwarze if (name == NULL) { 2161a5a5f808Sschwarze mandoc_msg(MANDOCERR_STR_UNDEF, 2162a5a5f808Sschwarze ln, (int)(iname - buf->buf), 2163803e5876Sschwarze "%.*s", (int)namesz, iname); 2164803e5876Sschwarze namesz = 0; 2165803e5876Sschwarze } else 2166803e5876Sschwarze namesz = strlen(name); 2167803e5876Sschwarze } else 2168803e5876Sschwarze name = iname; 2169803e5876Sschwarze 217028687875Sschwarze if (namesz == 0 && tok != ROFF_ig) { 2171a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_EMPTY, 2172a5a5f808Sschwarze ln, ppos, "%s", roff_name[tok]); 2173526e306bSschwarze return ROFF_IGN; 2174e53bb406Sschwarze } 2175b58a7f4fSschwarze 2176e53bb406Sschwarze roffnode_push(r, tok, name, ln, ppos); 2177e53bb406Sschwarze 2178e53bb406Sschwarze /* 2179e53bb406Sschwarze * At the beginning of a `de' macro, clear the existing string 2180e53bb406Sschwarze * with the same name, if there is one. New content will be 21810c05b9caSschwarze * appended from roff_block_text() in multiline mode. 2182e53bb406Sschwarze */ 2183b58a7f4fSschwarze 21840856de67Sschwarze if (tok == ROFF_de || tok == ROFF_dei) { 2185d08dc9e5Sschwarze roff_setstrn(&r->strtab, name, namesz, "", 0, 0); 21860856de67Sschwarze roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); 218707530f0aSschwarze } else if (tok == ROFF_am || tok == ROFF_ami) { 218807530f0aSschwarze deftype = ROFFDEF_ANY; 218907530f0aSschwarze value = roff_getstrn(r, iname, namesz, &deftype); 219007530f0aSschwarze switch (deftype) { /* Before appending, ... */ 219107530f0aSschwarze case ROFFDEF_PRE: /* copy predefined to user-defined. */ 219207530f0aSschwarze roff_setstrn(&r->strtab, name, namesz, 219307530f0aSschwarze value, strlen(value), 0); 219407530f0aSschwarze break; 219507530f0aSschwarze case ROFFDEF_REN: /* call original standard macro. */ 219607530f0aSschwarze csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n", 219707530f0aSschwarze (int)strlen(value), value); 219807530f0aSschwarze roff_setstrn(&r->strtab, name, namesz, call, csz, 0); 219907530f0aSschwarze roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); 220007530f0aSschwarze free(call); 220107530f0aSschwarze break; 220207530f0aSschwarze case ROFFDEF_STD: /* rename and call standard macro. */ 220307530f0aSschwarze rsz = mandoc_asprintf(&rname, "__%s_renamed", name); 220407530f0aSschwarze roff_setstrn(&r->rentab, rname, rsz, name, namesz, 0); 220507530f0aSschwarze csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n", 220607530f0aSschwarze (int)rsz, rname); 220707530f0aSschwarze roff_setstrn(&r->strtab, name, namesz, call, csz, 0); 220807530f0aSschwarze free(call); 220907530f0aSschwarze free(rname); 221007530f0aSschwarze break; 221107530f0aSschwarze default: 221207530f0aSschwarze break; 221307530f0aSschwarze } 22140856de67Sschwarze } 2215f673a3c7Sschwarze 221628687875Sschwarze if (*cp == '\0') 2217526e306bSschwarze return ROFF_IGN; 2218f673a3c7Sschwarze 2219803e5876Sschwarze /* Get the custom end marker. */ 2220b58a7f4fSschwarze 2221803e5876Sschwarze iname = cp; 22223b5bd91eSschwarze namesz = roff_getname(&cp, ln, ppos); 2223803e5876Sschwarze 2224803e5876Sschwarze /* Resolve the end marker if it is indirect. */ 2225803e5876Sschwarze 222628687875Sschwarze if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) { 222707530f0aSschwarze deftype = ROFFDEF_USER; 222807530f0aSschwarze name = roff_getstrn(r, iname, namesz, &deftype); 222907530f0aSschwarze if (name == NULL) { 2230a5a5f808Sschwarze mandoc_msg(MANDOCERR_STR_UNDEF, 2231a5a5f808Sschwarze ln, (int)(iname - buf->buf), 2232803e5876Sschwarze "%.*s", (int)namesz, iname); 2233803e5876Sschwarze namesz = 0; 2234803e5876Sschwarze } else 2235803e5876Sschwarze namesz = strlen(name); 2236803e5876Sschwarze } else 2237803e5876Sschwarze name = iname; 2238803e5876Sschwarze 2239d08dc9e5Sschwarze if (namesz) 2240d08dc9e5Sschwarze r->last->end = mandoc_strndup(name, namesz); 2241f673a3c7Sschwarze 224228687875Sschwarze if (*cp != '\0') 2243a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_EXCESS, 22446050a3daSschwarze ln, pos, ".%s ... %s", roff_name[tok], cp); 224562d9ccdbSschwarze 2246526e306bSschwarze return ROFF_IGN; 224762d9ccdbSschwarze } 224862d9ccdbSschwarze 2249b7f92c5fSschwarze static int 225062d9ccdbSschwarze roff_block_sub(ROFF_ARGS) 225162d9ccdbSschwarze { 225214a309e3Sschwarze enum roff_tok t; 225362d9ccdbSschwarze int i, j; 225462d9ccdbSschwarze 225562d9ccdbSschwarze /* 2256ed59e75bSschwarze * If a custom end marker is a user-defined or predefined macro 2257ed59e75bSschwarze * or a request, interpret it. 225862d9ccdbSschwarze */ 225962d9ccdbSschwarze 226062d9ccdbSschwarze if (r->last->end) { 2261a35fc07aSschwarze for (i = pos, j = 0; r->last->end[j]; j++, i++) 226228687875Sschwarze if (buf->buf[i] != r->last->end[j]) 226362d9ccdbSschwarze break; 226462d9ccdbSschwarze 226528687875Sschwarze if (r->last->end[j] == '\0' && 226628687875Sschwarze (buf->buf[i] == '\0' || 226728687875Sschwarze buf->buf[i] == ' ' || 226828687875Sschwarze buf->buf[i] == '\t')) { 226962d9ccdbSschwarze roffnode_pop(r); 227062d9ccdbSschwarze roffnode_cleanscope(r); 227162d9ccdbSschwarze 227228687875Sschwarze while (buf->buf[i] == ' ' || buf->buf[i] == '\t') 2273a35fc07aSschwarze i++; 2274a35fc07aSschwarze 2275a35fc07aSschwarze pos = i; 227628687875Sschwarze if (roff_parse(r, buf->buf, &pos, ln, ppos) != 227714a309e3Sschwarze TOKEN_NONE) 2278526e306bSschwarze return ROFF_RERUN; 2279526e306bSschwarze return ROFF_IGN; 228062d9ccdbSschwarze } 228162d9ccdbSschwarze } 228262d9ccdbSschwarze 2283ed59e75bSschwarze /* Handle the standard end marker. */ 228462d9ccdbSschwarze 228528687875Sschwarze t = roff_parse(r, buf->buf, &pos, ln, ppos); 2286ed59e75bSschwarze if (t == ROFF_cblock) 2287ed59e75bSschwarze return roff_cblock(r, t, buf, ln, ppos, pos, offs); 228862d9ccdbSschwarze 2289ed59e75bSschwarze /* Not an end marker, so append the line to the block. */ 2290ed59e75bSschwarze 229128687875Sschwarze if (tok != ROFF_ig) 229228687875Sschwarze roff_setstr(r, r->last->name, buf->buf + ppos, 2); 2293526e306bSschwarze return ROFF_IGN; 2294e53bb406Sschwarze } 229562d9ccdbSschwarze 2296b7f92c5fSschwarze static int 229762d9ccdbSschwarze roff_block_text(ROFF_ARGS) 229862d9ccdbSschwarze { 229962d9ccdbSschwarze 230028687875Sschwarze if (tok != ROFF_ig) 230128687875Sschwarze roff_setstr(r, r->last->name, buf->buf + pos, 2); 2302e53bb406Sschwarze 2303526e306bSschwarze return ROFF_IGN; 230462d9ccdbSschwarze } 230562d9ccdbSschwarze 2306fd9d9245Sschwarze /* 2307fd9d9245Sschwarze * Check for a closing "\}" and handle it. 2308fd9d9245Sschwarze * In this function, the final "int *offs" argument is used for 2309fd9d9245Sschwarze * different purposes than elsewhere: 2310fd9d9245Sschwarze * Input: *offs == 0: caller wants to discard arguments following \} 2311fd9d9245Sschwarze * *offs == 1: caller wants to preserve text following \} 2312fd9d9245Sschwarze * Output: *offs = 0: tell caller to discard input line 2313fd9d9245Sschwarze * *offs = 1: tell caller to use input line 2314fd9d9245Sschwarze */ 2315b7f92c5fSschwarze static int 2316fd9d9245Sschwarze roff_cond_checkend(ROFF_ARGS) 231762d9ccdbSschwarze { 2318a5e11edeSschwarze char *ep; 2319b7f92c5fSschwarze int endloop, irc, rr; 232062d9ccdbSschwarze 2321b7f92c5fSschwarze irc = ROFF_IGN; 232262d9ccdbSschwarze rr = r->last->rule; 2323b7f92c5fSschwarze endloop = tok != ROFF_while ? ROFF_IGN : 2324b7f92c5fSschwarze rr ? ROFF_LOOPCONT : ROFF_LOOPEXIT; 2325b7f92c5fSschwarze if (roffnode_cleanscope(r)) 2326b7f92c5fSschwarze irc |= endloop; 2327e4baf46eSschwarze 23281f60b4bfSschwarze /* 2329fd9d9245Sschwarze * If "\}" occurs on a macro line without a preceding macro or 2330fd9d9245Sschwarze * a text line contains nothing else, drop the line completely. 23311f60b4bfSschwarze */ 23321f60b4bfSschwarze 233328687875Sschwarze ep = buf->buf + pos; 2334fd9d9245Sschwarze if (ep[0] == '\\' && ep[1] == '}' && (ep[2] == '\0' || *offs == 0)) 23351271a177Sschwarze rr = 0; 23361f60b4bfSschwarze 23376ac4ca49Sschwarze /* 2338fd9d9245Sschwarze * The closing delimiter "\}" rewinds the conditional scope 23396ac4ca49Sschwarze * but is otherwise ignored when interpreting the line. 23406ac4ca49Sschwarze */ 2341e4baf46eSschwarze 234228687875Sschwarze while ((ep = strchr(ep, '\\')) != NULL) { 2343a6f1c49aSschwarze switch (ep[1]) { 2344a6f1c49aSschwarze case '}': 2345fbe6102bSschwarze if (ep[2] == '\0') 2346fbe6102bSschwarze ep[0] = '\0'; 2347fbe6102bSschwarze else if (rr) 2348fd9d9245Sschwarze ep[1] = '&'; 2349fd9d9245Sschwarze else 2350a6f1c49aSschwarze memmove(ep, ep + 2, strlen(ep + 2) + 1); 2351b7f92c5fSschwarze if (roff_ccond(r, ln, ep - buf->buf)) 2352b7f92c5fSschwarze irc |= endloop; 2353a6f1c49aSschwarze break; 2354a6f1c49aSschwarze case '\0': 23557be1115fSschwarze ++ep; 2356a6f1c49aSschwarze break; 2357a6f1c49aSschwarze default: 2358a6f1c49aSschwarze ep += 2; 2359a6f1c49aSschwarze break; 2360a5e11edeSschwarze } 2361a6f1c49aSschwarze } 2362fd9d9245Sschwarze *offs = rr; 2363fd9d9245Sschwarze return irc; 2364fd9d9245Sschwarze } 2365a6f1c49aSschwarze 2366fd9d9245Sschwarze /* 2367fd9d9245Sschwarze * Parse and process a request or macro line in conditional scope. 2368fd9d9245Sschwarze */ 2369fd9d9245Sschwarze static int 2370fd9d9245Sschwarze roff_cond_sub(ROFF_ARGS) 2371fd9d9245Sschwarze { 2372fd9d9245Sschwarze struct roffnode *bl; 2373f79258f3Sschwarze int irc, rr, spos; 2374fd9d9245Sschwarze enum roff_tok t; 2375fd9d9245Sschwarze 2376fd9d9245Sschwarze rr = 0; /* If arguments follow "\}", skip them. */ 2377fd9d9245Sschwarze irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr); 2378f79258f3Sschwarze spos = pos; 2379b19021b2Sschwarze t = roff_parse(r, buf->buf, &pos, ln, ppos); 2380b19021b2Sschwarze 2381a6f1c49aSschwarze /* 2382f79258f3Sschwarze * Handle requests and macros if the conditional evaluated 2383f79258f3Sschwarze * to true or if they are structurally required. 2384f79258f3Sschwarze * The .break request is always handled specially. 2385a6f1c49aSschwarze */ 2386a6f1c49aSschwarze 23877dd0ff50Sschwarze if (t == ROFF_break) { 23887dd0ff50Sschwarze if (irc & ROFF_LOOPMASK) 23897dd0ff50Sschwarze irc = ROFF_IGN | ROFF_LOOPEXIT; 23907dd0ff50Sschwarze else if (rr) { 23917dd0ff50Sschwarze for (bl = r->last; bl != NULL; bl = bl->parent) { 23927dd0ff50Sschwarze bl->rule = 0; 23937dd0ff50Sschwarze if (bl->tok == ROFF_while) 23947dd0ff50Sschwarze break; 23957dd0ff50Sschwarze } 23967dd0ff50Sschwarze } 2397f79258f3Sschwarze } else if (rr || (t < TOKEN_NONE && roffs[t].flags & ROFFMAC_STRUCT)) { 2398f79258f3Sschwarze irc |= roff_req_or_macro(r, t, buf, ln, spos, pos, offs); 2399a68c8a85Sschwarze if (irc & ROFF_WHILE) 2400a68c8a85Sschwarze irc &= ~(ROFF_LOOPCONT | ROFF_LOOPEXIT); 2401f79258f3Sschwarze } 2402b7f92c5fSschwarze return irc; 240392853774Sschwarze } 240462d9ccdbSschwarze 2405fd9d9245Sschwarze /* 2406fd9d9245Sschwarze * Parse and process a text line in conditional scope. 2407fd9d9245Sschwarze */ 2408b7f92c5fSschwarze static int 240962d9ccdbSschwarze roff_cond_text(ROFF_ARGS) 241062d9ccdbSschwarze { 2411fd9d9245Sschwarze int irc, rr; 241262d9ccdbSschwarze 2413fd9d9245Sschwarze rr = 1; /* If arguments follow "\}", preserve them. */ 2414fd9d9245Sschwarze irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr); 2415b7f92c5fSschwarze if (rr) 2416b7f92c5fSschwarze irc |= ROFF_CONT; 2417b7f92c5fSschwarze return irc; 241862d9ccdbSschwarze } 241962d9ccdbSschwarze 2420fa2127f9Sschwarze /* --- handling of numeric and conditional expressions -------------------- */ 2421fa2127f9Sschwarze 2422b6a36a9bSschwarze /* 2423*7c5f3f4eSschwarze * Parse a single signed decimal number. Stop at the first non-digit. 2424b6a36a9bSschwarze * If there is at least one digit, return success and advance the 2425b6a36a9bSschwarze * parse point, else return failure and let the parse point unchanged. 2426b6a36a9bSschwarze * Ignore overflows, treat them just like the C language. 2427b6a36a9bSschwarze */ 2428541e3395Sschwarze static int 2429f4432377Sschwarze roff_getnum(const char *v, int *pos, int *res, char unit, int skipspace) 2430541e3395Sschwarze { 2431*7c5f3f4eSschwarze double frac, myres; 2432*7c5f3f4eSschwarze int n, p; 2433541e3395Sschwarze 2434541e3395Sschwarze p = *pos; 2435541e3395Sschwarze n = v[p] == '-'; 24363a1681a6Sschwarze if (n || v[p] == '+') 24373a1681a6Sschwarze p++; 24383a1681a6Sschwarze 2439f4432377Sschwarze if (skipspace) 24403a1681a6Sschwarze while (isspace((unsigned char)v[p])) 2441541e3395Sschwarze p++; 2442541e3395Sschwarze 2443*7c5f3f4eSschwarze for (myres = 0.0; isdigit((unsigned char)v[p]); p++) 2444*7c5f3f4eSschwarze myres = myres * 10.0 + (v[p] - '0'); 2445*7c5f3f4eSschwarze if (v[p] == '.') 2446*7c5f3f4eSschwarze for (frac = 0.1; isdigit((unsigned char)v[++p]); frac *= 0.1) 2447*7c5f3f4eSschwarze myres += frac * (v[p] - '0'); 2448*7c5f3f4eSschwarze 2449541e3395Sschwarze if (p == *pos + n) 2450541e3395Sschwarze return 0; 2451541e3395Sschwarze 2452541e3395Sschwarze if (n) 2453*7c5f3f4eSschwarze myres *= -1.0; 2454541e3395Sschwarze 24554a635483Sschwarze /* Each number may be followed by one optional scaling unit. */ 24564a635483Sschwarze 2457f4432377Sschwarze if (v[p] != '\0' && strchr("ficvPmnpuM", v[p]) != NULL) { 2458f4432377Sschwarze if (unit != '\0') 2459f4432377Sschwarze unit = v[p]; 2460f4432377Sschwarze p++; 2461f4432377Sschwarze } 2462f4432377Sschwarze 2463f4432377Sschwarze switch (unit) { 24644a635483Sschwarze case 'f': 2465*7c5f3f4eSschwarze myres *= 65536.0; 24664a635483Sschwarze break; 24674a635483Sschwarze case 'i': 2468*7c5f3f4eSschwarze myres *= 240.0; 24694a635483Sschwarze break; 24704a635483Sschwarze case 'c': 2471*7c5f3f4eSschwarze myres *= 240.0 / 2.54; 24724a635483Sschwarze break; 24734a635483Sschwarze case 'v': 24744a635483Sschwarze case 'P': 2475*7c5f3f4eSschwarze myres *= 40.0; 24764a635483Sschwarze break; 24774a635483Sschwarze case 'm': 24784a635483Sschwarze case 'n': 2479*7c5f3f4eSschwarze myres *= 24.0; 24804a635483Sschwarze break; 24814a635483Sschwarze case 'p': 2482*7c5f3f4eSschwarze myres *= 40.0 / 12.0; 24834a635483Sschwarze break; 24844a635483Sschwarze case 'u': 24854a635483Sschwarze break; 24864a635483Sschwarze case 'M': 2487*7c5f3f4eSschwarze myres *= 24.0 / 100.0; 24884a635483Sschwarze break; 24894a635483Sschwarze default: 24904a635483Sschwarze break; 24914a635483Sschwarze } 2492*7c5f3f4eSschwarze if (res != NULL) 2493*7c5f3f4eSschwarze *res = myres; 2494f4432377Sschwarze *pos = p; 2495526e306bSschwarze return 1; 2496541e3395Sschwarze } 2497541e3395Sschwarze 24981271a177Sschwarze /* 24991271a177Sschwarze * Evaluate a string comparison condition. 25001271a177Sschwarze * The first character is the delimiter. 25011271a177Sschwarze * Succeed if the string up to its second occurrence 2502d9a51c35Sjmc * matches the string up to its third occurrence. 25031271a177Sschwarze * Advance the cursor after the third occurrence 25041271a177Sschwarze * or lacking that, to the end of the line. 25051271a177Sschwarze */ 25061271a177Sschwarze static int 25071271a177Sschwarze roff_evalstrcond(const char *v, int *pos) 25081271a177Sschwarze { 25091271a177Sschwarze const char *s1, *s2, *s3; 25101271a177Sschwarze int match; 25111271a177Sschwarze 25121271a177Sschwarze match = 0; 25131271a177Sschwarze s1 = v + *pos; /* initial delimiter */ 25141271a177Sschwarze s2 = s1 + 1; /* for scanning the first string */ 25151271a177Sschwarze s3 = strchr(s2, *s1); /* for scanning the second string */ 25161271a177Sschwarze 25171271a177Sschwarze if (NULL == s3) /* found no middle delimiter */ 25181271a177Sschwarze goto out; 25191271a177Sschwarze 25201271a177Sschwarze while ('\0' != *++s3) { 25211271a177Sschwarze if (*s2 != *s3) { /* mismatch */ 25221271a177Sschwarze s3 = strchr(s3, *s1); 25231271a177Sschwarze break; 25241271a177Sschwarze } 25251271a177Sschwarze if (*s3 == *s1) { /* found the final delimiter */ 25261271a177Sschwarze match = 1; 25271271a177Sschwarze break; 25281271a177Sschwarze } 25291271a177Sschwarze s2++; 25301271a177Sschwarze } 25311271a177Sschwarze 25321271a177Sschwarze out: 25331271a177Sschwarze if (NULL == s3) 25341271a177Sschwarze s3 = strchr(s2, '\0'); 253597111628Sschwarze else if (*s3 != '\0') 25361271a177Sschwarze s3++; 25371271a177Sschwarze *pos = s3 - v; 2538526e306bSschwarze return match; 25391271a177Sschwarze } 25401271a177Sschwarze 2541b6a36a9bSschwarze /* 2542b6a36a9bSschwarze * Evaluate an optionally negated single character, numerical, 2543b6a36a9bSschwarze * or string condition. 2544b6a36a9bSschwarze */ 25451271a177Sschwarze static int 254624ab75f2Sschwarze roff_evalcond(struct roff *r, int ln, char *v, int *pos) 254731e23753Sschwarze { 2548a24ea326Sschwarze const char *start, *end; 254924ab75f2Sschwarze char *cp, *name; 255024ab75f2Sschwarze size_t sz; 2551a24ea326Sschwarze int deftype, len, number, savepos, istrue, wanttrue; 255231e23753Sschwarze 25531271a177Sschwarze if ('!' == v[*pos]) { 25541271a177Sschwarze wanttrue = 0; 25551271a177Sschwarze (*pos)++; 25561271a177Sschwarze } else 25571271a177Sschwarze wanttrue = 1; 25581271a177Sschwarze 255931e23753Sschwarze switch (v[*pos]) { 2560f7d129ddSschwarze case '\0': 2561526e306bSschwarze return 0; 256249aff9f8Sschwarze case 'n': 256349aff9f8Sschwarze case 'o': 25641271a177Sschwarze (*pos)++; 2565526e306bSschwarze return wanttrue; 256649aff9f8Sschwarze case 'e': 256749aff9f8Sschwarze case 't': 2568f660dfc5Sschwarze case 'v': 256931e23753Sschwarze (*pos)++; 2570526e306bSschwarze return !wanttrue; 2571a24ea326Sschwarze case 'c': 2572a24ea326Sschwarze do { 2573a24ea326Sschwarze (*pos)++; 2574a24ea326Sschwarze } while (v[*pos] == ' '); 2575a24ea326Sschwarze 2576a24ea326Sschwarze /* 2577a24ea326Sschwarze * Quirk for groff compatibility: 2578a24ea326Sschwarze * The horizontal tab is neither available nor unavailable. 2579a24ea326Sschwarze */ 2580a24ea326Sschwarze 2581a24ea326Sschwarze if (v[*pos] == '\t') { 2582a24ea326Sschwarze (*pos)++; 2583a24ea326Sschwarze return 0; 2584a24ea326Sschwarze } 2585a24ea326Sschwarze 2586a24ea326Sschwarze /* Printable ASCII characters are available. */ 2587a24ea326Sschwarze 2588a24ea326Sschwarze if (v[*pos] != '\\') { 2589a24ea326Sschwarze (*pos)++; 2590a24ea326Sschwarze return wanttrue; 2591a24ea326Sschwarze } 2592a24ea326Sschwarze 2593a24ea326Sschwarze end = v + ++*pos; 2594a24ea326Sschwarze switch (mandoc_escape(&end, &start, &len)) { 2595a24ea326Sschwarze case ESCAPE_SPECIAL: 2596a24ea326Sschwarze istrue = mchars_spec2cp(start, len) != -1; 2597a24ea326Sschwarze break; 2598a24ea326Sschwarze case ESCAPE_UNICODE: 2599a24ea326Sschwarze istrue = 1; 2600a24ea326Sschwarze break; 2601a24ea326Sschwarze case ESCAPE_NUMBERED: 2602a24ea326Sschwarze istrue = mchars_num2char(start, len) != -1; 2603a24ea326Sschwarze break; 2604a24ea326Sschwarze default: 2605a24ea326Sschwarze istrue = !wanttrue; 2606a24ea326Sschwarze break; 2607a24ea326Sschwarze } 2608a24ea326Sschwarze *pos = end - v; 2609a24ea326Sschwarze return istrue == wanttrue; 2610c2f35d21Sschwarze case 'd': 261124ab75f2Sschwarze case 'r': 2612c2f35d21Sschwarze cp = v + *pos + 1; 2613c2f35d21Sschwarze while (*cp == ' ') 2614c2f35d21Sschwarze cp++; 2615c2f35d21Sschwarze name = cp; 26163b5bd91eSschwarze sz = roff_getname(&cp, ln, cp - v); 261707530f0aSschwarze if (sz == 0) 261807530f0aSschwarze istrue = 0; 261907530f0aSschwarze else if (v[*pos] == 'r') 262007530f0aSschwarze istrue = roff_hasregn(r, name, sz); 262107530f0aSschwarze else { 262207530f0aSschwarze deftype = ROFFDEF_ANY; 262307530f0aSschwarze roff_getstrn(r, name, sz, &deftype); 262407530f0aSschwarze istrue = !!deftype; 262507530f0aSschwarze } 26267ad9cfd6Sschwarze *pos = (name + sz) - v; 2627c2f35d21Sschwarze return istrue == wanttrue; 262831e23753Sschwarze default: 262931e23753Sschwarze break; 263031e23753Sschwarze } 263131e23753Sschwarze 2632583f2f70Sschwarze savepos = *pos; 2633f4432377Sschwarze if (roff_evalnum(ln, v, pos, &number, 'u', 0)) 2634526e306bSschwarze return (number > 0) == wanttrue; 2635583f2f70Sschwarze else if (*pos == savepos) 2636526e306bSschwarze return roff_evalstrcond(v, pos) == wanttrue; 2637583f2f70Sschwarze else 2638526e306bSschwarze return 0; 263931e23753Sschwarze } 264031e23753Sschwarze 2641b7f92c5fSschwarze static int 264224483150Sschwarze roff_line_ignore(ROFF_ARGS) 26439830bf9fSschwarze { 26449830bf9fSschwarze 2645526e306bSschwarze return ROFF_IGN; 26469830bf9fSschwarze } 26479830bf9fSschwarze 2648b7f92c5fSschwarze static int 2649d04ca39fSschwarze roff_insec(ROFF_ARGS) 2650d04ca39fSschwarze { 2651d04ca39fSschwarze 2652a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_INSEC, ln, ppos, "%s", roff_name[tok]); 2653526e306bSschwarze return ROFF_IGN; 2654d04ca39fSschwarze } 2655d04ca39fSschwarze 2656b7f92c5fSschwarze static int 2657d04ca39fSschwarze roff_unsupp(ROFF_ARGS) 2658d04ca39fSschwarze { 2659d04ca39fSschwarze 2660a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_UNSUPP, ln, ppos, "%s", roff_name[tok]); 2661526e306bSschwarze return ROFF_IGN; 2662d04ca39fSschwarze } 2663d04ca39fSschwarze 2664b7f92c5fSschwarze static int 266562d9ccdbSschwarze roff_cond(ROFF_ARGS) 266662d9ccdbSschwarze { 2667b7f92c5fSschwarze int irc; 266844f8358dSschwarze 266944f8358dSschwarze roffnode_push(r, tok, NULL, ln, ppos); 267062d9ccdbSschwarze 267162d9ccdbSschwarze /* 2672a35fc07aSschwarze * An `.el' has no conditional body: it will consume the value 2673a35fc07aSschwarze * of the current rstack entry set in prior `ie' calls or 2674a35fc07aSschwarze * defaults to DENY. 2675a35fc07aSschwarze * 2676a35fc07aSschwarze * If we're not an `el', however, then evaluate the conditional. 267762d9ccdbSschwarze */ 2678a35fc07aSschwarze 267928687875Sschwarze r->last->rule = tok == ROFF_el ? 26801271a177Sschwarze (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) : 268128687875Sschwarze roff_evalcond(r, ln, buf->buf, &pos); 268262d9ccdbSschwarze 268362d9ccdbSschwarze /* 2684a35fc07aSschwarze * An if-else will put the NEGATION of the current evaluated 2685a35fc07aSschwarze * conditional into the stack of rules. 268662d9ccdbSschwarze */ 2687a35fc07aSschwarze 268828687875Sschwarze if (tok == ROFF_ie) { 26894bc98c72Sschwarze if (r->rstackpos + 1 == r->rstacksz) { 26904bc98c72Sschwarze r->rstacksz += 16; 26914bc98c72Sschwarze r->rstack = mandoc_reallocarray(r->rstack, 26924bc98c72Sschwarze r->rstacksz, sizeof(int)); 2693a35fc07aSschwarze } 26941271a177Sschwarze r->rstack[++r->rstackpos] = !r->last->rule; 269562d9ccdbSschwarze } 269631e23753Sschwarze 269731e23753Sschwarze /* If the parent has false as its rule, then so do we. */ 269831e23753Sschwarze 26991271a177Sschwarze if (r->last->parent && !r->last->parent->rule) 27001271a177Sschwarze r->last->rule = 0; 270162d9ccdbSschwarze 270231e23753Sschwarze /* 270344f8358dSschwarze * Determine scope. 270444f8358dSschwarze * If there is nothing on the line after the conditional, 270544f8358dSschwarze * not even whitespace, use next-line scope. 2706b7f92c5fSschwarze * Except that .while does not support next-line scope. 270731e23753Sschwarze */ 270831e23753Sschwarze 2709b7f92c5fSschwarze if (buf->buf[pos] == '\0' && tok != ROFF_while) { 271044f8358dSschwarze r->last->endspan = 2; 271144f8358dSschwarze goto out; 271244f8358dSschwarze } 271344f8358dSschwarze 271428687875Sschwarze while (buf->buf[pos] == ' ') 271544f8358dSschwarze pos++; 271644f8358dSschwarze 271744f8358dSschwarze /* An opening brace requests multiline scope. */ 271862d9ccdbSschwarze 271928687875Sschwarze if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') { 272062d9ccdbSschwarze r->last->endspan = -1; 272162d9ccdbSschwarze pos += 2; 2722099e02ebSschwarze while (buf->buf[pos] == ' ') 2723099e02ebSschwarze pos++; 272444f8358dSschwarze goto out; 272562d9ccdbSschwarze } 272662d9ccdbSschwarze 272762d9ccdbSschwarze /* 272844f8358dSschwarze * Anything else following the conditional causes 272944f8358dSschwarze * single-line scope. Warn if the scope contains 273044f8358dSschwarze * nothing but trailing whitespace. 273162d9ccdbSschwarze */ 273262d9ccdbSschwarze 273328687875Sschwarze if (buf->buf[pos] == '\0') 2734a5a5f808Sschwarze mandoc_msg(MANDOCERR_COND_EMPTY, 2735a5a5f808Sschwarze ln, ppos, "%s", roff_name[tok]); 273662d9ccdbSschwarze 273744f8358dSschwarze r->last->endspan = 1; 273862d9ccdbSschwarze 273944f8358dSschwarze out: 274062d9ccdbSschwarze *offs = pos; 2741b7f92c5fSschwarze irc = ROFF_RERUN; 2742b7f92c5fSschwarze if (tok == ROFF_while) 2743b7f92c5fSschwarze irc |= ROFF_WHILE; 2744b7f92c5fSschwarze return irc; 274562d9ccdbSschwarze } 274662d9ccdbSschwarze 2747b7f92c5fSschwarze static int 2748b49ce91bSschwarze roff_ds(ROFF_ARGS) 2749b49ce91bSschwarze { 275059b3009eSschwarze char *string; 275159b3009eSschwarze const char *name; 275259b3009eSschwarze size_t namesz; 27538cd724fbSschwarze 2754d04ca39fSschwarze /* Ignore groff compatibility mode for now. */ 2755d04ca39fSschwarze 2756d04ca39fSschwarze if (tok == ROFF_ds1) 2757d04ca39fSschwarze tok = ROFF_ds; 2758d04ca39fSschwarze else if (tok == ROFF_as1) 2759d04ca39fSschwarze tok = ROFF_as; 2760d04ca39fSschwarze 27618cd724fbSschwarze /* 276259b3009eSschwarze * The first word is the name of the string. 276359b3009eSschwarze * If it is empty or terminated by an escape sequence, 276459b3009eSschwarze * abort the `ds' request without defining anything. 27658cd724fbSschwarze */ 2766b49ce91bSschwarze 276728687875Sschwarze name = string = buf->buf + pos; 276828687875Sschwarze if (*name == '\0') 2769526e306bSschwarze return ROFF_IGN; 2770b49ce91bSschwarze 27713b5bd91eSschwarze namesz = roff_getname(&string, ln, pos); 27727ad9cfd6Sschwarze switch (name[namesz]) { 27737ad9cfd6Sschwarze case '\\': 2774526e306bSschwarze return ROFF_IGN; 27757ad9cfd6Sschwarze case '\t': 27767ad9cfd6Sschwarze string = buf->buf + pos + namesz; 27777ad9cfd6Sschwarze break; 27787ad9cfd6Sschwarze default: 27797ad9cfd6Sschwarze break; 27807ad9cfd6Sschwarze } 278159b3009eSschwarze 278259b3009eSschwarze /* Read past the initial double-quote, if any. */ 278328687875Sschwarze if (*string == '"') 27848cd724fbSschwarze string++; 27858cd724fbSschwarze 27868cd724fbSschwarze /* The rest is the value. */ 278759b3009eSschwarze roff_setstrn(&r->strtab, name, namesz, string, strlen(string), 278859b3009eSschwarze ROFF_as == tok); 27890856de67Sschwarze roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); 2790526e306bSschwarze return ROFF_IGN; 2791b49ce91bSschwarze } 2792b49ce91bSschwarze 2793b6a36a9bSschwarze /* 2794b6a36a9bSschwarze * Parse a single operator, one or two characters long. 2795b6a36a9bSschwarze * If the operator is recognized, return success and advance the 2796b6a36a9bSschwarze * parse point, else return failure and let the parse point unchanged. 2797b6a36a9bSschwarze */ 2798b6a36a9bSschwarze static int 2799b6a36a9bSschwarze roff_getop(const char *v, int *pos, char *res) 2800b6a36a9bSschwarze { 2801b6a36a9bSschwarze 2802b6a36a9bSschwarze *res = v[*pos]; 2803b6a36a9bSschwarze 2804b6a36a9bSschwarze switch (*res) { 280549aff9f8Sschwarze case '+': 280649aff9f8Sschwarze case '-': 280749aff9f8Sschwarze case '*': 280849aff9f8Sschwarze case '/': 280949aff9f8Sschwarze case '%': 281049aff9f8Sschwarze case '&': 281149aff9f8Sschwarze case ':': 2812b6a36a9bSschwarze break; 2813b6a36a9bSschwarze case '<': 2814b6a36a9bSschwarze switch (v[*pos + 1]) { 281549aff9f8Sschwarze case '=': 2816b6a36a9bSschwarze *res = 'l'; 2817b6a36a9bSschwarze (*pos)++; 2818b6a36a9bSschwarze break; 281949aff9f8Sschwarze case '>': 2820b6a36a9bSschwarze *res = '!'; 2821b6a36a9bSschwarze (*pos)++; 2822b6a36a9bSschwarze break; 282349aff9f8Sschwarze case '?': 2824b6a36a9bSschwarze *res = 'i'; 2825b6a36a9bSschwarze (*pos)++; 2826b6a36a9bSschwarze break; 2827b6a36a9bSschwarze default: 2828b6a36a9bSschwarze break; 2829b6a36a9bSschwarze } 2830b6a36a9bSschwarze break; 2831b6a36a9bSschwarze case '>': 2832b6a36a9bSschwarze switch (v[*pos + 1]) { 283349aff9f8Sschwarze case '=': 2834b6a36a9bSschwarze *res = 'g'; 2835b6a36a9bSschwarze (*pos)++; 2836b6a36a9bSschwarze break; 283749aff9f8Sschwarze case '?': 2838b6a36a9bSschwarze *res = 'a'; 2839b6a36a9bSschwarze (*pos)++; 2840b6a36a9bSschwarze break; 2841b6a36a9bSschwarze default: 2842b6a36a9bSschwarze break; 2843b6a36a9bSschwarze } 2844b6a36a9bSschwarze break; 2845b6a36a9bSschwarze case '=': 2846b6a36a9bSschwarze if ('=' == v[*pos + 1]) 2847b6a36a9bSschwarze (*pos)++; 2848b6a36a9bSschwarze break; 2849b6a36a9bSschwarze default: 2850526e306bSschwarze return 0; 2851b6a36a9bSschwarze } 2852b6a36a9bSschwarze (*pos)++; 2853b6a36a9bSschwarze 2854526e306bSschwarze return *res; 2855b6a36a9bSschwarze } 2856b6a36a9bSschwarze 2857b6a36a9bSschwarze /* 2858b6a36a9bSschwarze * Evaluate either a parenthesized numeric expression 2859b6a36a9bSschwarze * or a single signed integer number. 2860b6a36a9bSschwarze */ 2861b6a36a9bSschwarze static int 2862f4432377Sschwarze roff_evalpar(int ln, const char *v, int *pos, int *res, char unit, 2863f4432377Sschwarze int skipspace) 2864b6a36a9bSschwarze { 2865b6a36a9bSschwarze 2866b6a36a9bSschwarze if ('(' != v[*pos]) 2867f4432377Sschwarze return roff_getnum(v, pos, res, unit, skipspace); 2868b6a36a9bSschwarze 2869b6a36a9bSschwarze (*pos)++; 2870f4432377Sschwarze if ( ! roff_evalnum(ln, v, pos, res, unit, 1)) 2871526e306bSschwarze return 0; 2872b6a36a9bSschwarze 2873778fe90eSschwarze /* 2874778fe90eSschwarze * Omission of the closing parenthesis 2875778fe90eSschwarze * is an error in validation mode, 2876778fe90eSschwarze * but ignored in evaluation mode. 2877778fe90eSschwarze */ 2878778fe90eSschwarze 2879b6a36a9bSschwarze if (')' == v[*pos]) 2880b6a36a9bSschwarze (*pos)++; 2881778fe90eSschwarze else if (NULL == res) 2882526e306bSschwarze return 0; 2883b6a36a9bSschwarze 2884526e306bSschwarze return 1; 2885b6a36a9bSschwarze } 2886b6a36a9bSschwarze 2887b6a36a9bSschwarze /* 2888b6a36a9bSschwarze * Evaluate a complete numeric expression. 2889b6a36a9bSschwarze * Proceed left to right, there is no concept of precedence. 2890b6a36a9bSschwarze */ 2891f4432377Sschwarze int 2892f4432377Sschwarze roff_evalnum(int ln, const char *v, int *pos, int *res, char unit, 2893f4432377Sschwarze int skipspace) 2894b6a36a9bSschwarze { 2895b6a36a9bSschwarze int mypos, operand2; 2896b6a36a9bSschwarze char operator; 2897b6a36a9bSschwarze 2898b6a36a9bSschwarze if (NULL == pos) { 2899b6a36a9bSschwarze mypos = 0; 2900b6a36a9bSschwarze pos = &mypos; 2901b6a36a9bSschwarze } 2902b6a36a9bSschwarze 2903f4432377Sschwarze if (skipspace) 2904b6a36a9bSschwarze while (isspace((unsigned char)v[*pos])) 2905b6a36a9bSschwarze (*pos)++; 2906b6a36a9bSschwarze 2907f4432377Sschwarze if ( ! roff_evalpar(ln, v, pos, res, unit, skipspace)) 2908526e306bSschwarze return 0; 2909b6a36a9bSschwarze 2910b6a36a9bSschwarze while (1) { 2911f4432377Sschwarze if (skipspace) 2912b6a36a9bSschwarze while (isspace((unsigned char)v[*pos])) 2913b6a36a9bSschwarze (*pos)++; 2914b6a36a9bSschwarze 2915b6a36a9bSschwarze if ( ! roff_getop(v, pos, &operator)) 2916b6a36a9bSschwarze break; 2917b6a36a9bSschwarze 2918f4432377Sschwarze if (skipspace) 2919b6a36a9bSschwarze while (isspace((unsigned char)v[*pos])) 2920b6a36a9bSschwarze (*pos)++; 2921b6a36a9bSschwarze 2922f4432377Sschwarze if ( ! roff_evalpar(ln, v, pos, &operand2, unit, skipspace)) 2923526e306bSschwarze return 0; 2924b6a36a9bSschwarze 2925f4432377Sschwarze if (skipspace) 2926b6a36a9bSschwarze while (isspace((unsigned char)v[*pos])) 2927b6a36a9bSschwarze (*pos)++; 2928b6a36a9bSschwarze 2929778fe90eSschwarze if (NULL == res) 2930778fe90eSschwarze continue; 2931778fe90eSschwarze 2932b6a36a9bSschwarze switch (operator) { 293349aff9f8Sschwarze case '+': 2934b6a36a9bSschwarze *res += operand2; 2935b6a36a9bSschwarze break; 293649aff9f8Sschwarze case '-': 2937b6a36a9bSschwarze *res -= operand2; 2938b6a36a9bSschwarze break; 293949aff9f8Sschwarze case '*': 2940b6a36a9bSschwarze *res *= operand2; 2941b6a36a9bSschwarze break; 294249aff9f8Sschwarze case '/': 29434fcca957Sschwarze if (operand2 == 0) { 294496f45df3Sschwarze mandoc_msg(MANDOCERR_DIVZERO, 2945a5a5f808Sschwarze ln, *pos, "%s", v); 294696f45df3Sschwarze *res = 0; 294796f45df3Sschwarze break; 294896f45df3Sschwarze } 2949b6a36a9bSschwarze *res /= operand2; 2950b6a36a9bSschwarze break; 295149aff9f8Sschwarze case '%': 29524fcca957Sschwarze if (operand2 == 0) { 29534fcca957Sschwarze mandoc_msg(MANDOCERR_DIVZERO, 2954a5a5f808Sschwarze ln, *pos, "%s", v); 29554fcca957Sschwarze *res = 0; 29564fcca957Sschwarze break; 29574fcca957Sschwarze } 2958b6a36a9bSschwarze *res %= operand2; 2959b6a36a9bSschwarze break; 296049aff9f8Sschwarze case '<': 2961b6a36a9bSschwarze *res = *res < operand2; 2962b6a36a9bSschwarze break; 296349aff9f8Sschwarze case '>': 2964b6a36a9bSschwarze *res = *res > operand2; 2965b6a36a9bSschwarze break; 296649aff9f8Sschwarze case 'l': 2967b6a36a9bSschwarze *res = *res <= operand2; 2968b6a36a9bSschwarze break; 296949aff9f8Sschwarze case 'g': 2970b6a36a9bSschwarze *res = *res >= operand2; 2971b6a36a9bSschwarze break; 297249aff9f8Sschwarze case '=': 2973b6a36a9bSschwarze *res = *res == operand2; 2974b6a36a9bSschwarze break; 297549aff9f8Sschwarze case '!': 2976b6a36a9bSschwarze *res = *res != operand2; 2977b6a36a9bSschwarze break; 297849aff9f8Sschwarze case '&': 2979b6a36a9bSschwarze *res = *res && operand2; 2980b6a36a9bSschwarze break; 298149aff9f8Sschwarze case ':': 2982b6a36a9bSschwarze *res = *res || operand2; 2983b6a36a9bSschwarze break; 298449aff9f8Sschwarze case 'i': 2985b6a36a9bSschwarze if (operand2 < *res) 2986b6a36a9bSschwarze *res = operand2; 2987b6a36a9bSschwarze break; 298849aff9f8Sschwarze case 'a': 2989b6a36a9bSschwarze if (operand2 > *res) 2990b6a36a9bSschwarze *res = operand2; 2991b6a36a9bSschwarze break; 2992b6a36a9bSschwarze default: 2993b6a36a9bSschwarze abort(); 2994b6a36a9bSschwarze } 2995b6a36a9bSschwarze } 2996526e306bSschwarze return 1; 2997b6a36a9bSschwarze } 2998b6a36a9bSschwarze 2999fa2127f9Sschwarze /* --- register management ------------------------------------------------ */ 3000fa2127f9Sschwarze 300122881299Sschwarze void 300275088a49Sschwarze roff_setreg(struct roff *r, const char *name, int val, char sign) 3003f8618d99Sschwarze { 3004dcd9eb04Sschwarze roff_setregn(r, name, strlen(name), val, sign, INT_MIN); 30059f6d70bcSschwarze } 30069f6d70bcSschwarze 30079f6d70bcSschwarze static void 30089f6d70bcSschwarze roff_setregn(struct roff *r, const char *name, size_t len, 3009dcd9eb04Sschwarze int val, char sign, int step) 30109f6d70bcSschwarze { 301122881299Sschwarze struct roffreg *reg; 3012f8618d99Sschwarze 301322881299Sschwarze /* Search for an existing register with the same name. */ 301422881299Sschwarze reg = r->regtab; 301522881299Sschwarze 30169f6d70bcSschwarze while (reg != NULL && (reg->key.sz != len || 30179f6d70bcSschwarze strncmp(reg->key.p, name, len) != 0)) 301822881299Sschwarze reg = reg->next; 301922881299Sschwarze 302022881299Sschwarze if (NULL == reg) { 302122881299Sschwarze /* Create a new register. */ 302222881299Sschwarze reg = mandoc_malloc(sizeof(struct roffreg)); 30239f6d70bcSschwarze reg->key.p = mandoc_strndup(name, len); 30249f6d70bcSschwarze reg->key.sz = len; 302575088a49Sschwarze reg->val = 0; 3026dcd9eb04Sschwarze reg->step = 0; 302722881299Sschwarze reg->next = r->regtab; 302822881299Sschwarze r->regtab = reg; 302922881299Sschwarze } 303022881299Sschwarze 303175088a49Sschwarze if ('+' == sign) 303275088a49Sschwarze reg->val += val; 303375088a49Sschwarze else if ('-' == sign) 303475088a49Sschwarze reg->val -= val; 303575088a49Sschwarze else 3036c542e817Sschwarze reg->val = val; 3037dcd9eb04Sschwarze if (step != INT_MIN) 3038dcd9eb04Sschwarze reg->step = step; 3039f8618d99Sschwarze } 3040f8618d99Sschwarze 3041b382ec6eSschwarze /* 3042b382ec6eSschwarze * Handle some predefined read-only number registers. 3043b382ec6eSschwarze * For now, return -1 if the requested register is not predefined; 3044b382ec6eSschwarze * in case a predefined read-only register having the value -1 3045b382ec6eSschwarze * were to turn up, another special value would have to be chosen. 3046b382ec6eSschwarze */ 3047b382ec6eSschwarze static int 304845ff8f42Sschwarze roff_getregro(const struct roff *r, const char *name) 3049b382ec6eSschwarze { 3050b382ec6eSschwarze 3051b382ec6eSschwarze switch (*name) { 305245ff8f42Sschwarze case '$': /* Number of arguments of the last macro evaluated. */ 30533dc5225dSschwarze return r->mstackpos < 0 ? 0 : r->mstack[r->mstackpos].argc; 305449aff9f8Sschwarze case 'A': /* ASCII approximation mode is always off. */ 3055526e306bSschwarze return 0; 305649aff9f8Sschwarze case 'g': /* Groff compatibility mode is always on. */ 3057526e306bSschwarze return 1; 305849aff9f8Sschwarze case 'H': /* Fixed horizontal resolution. */ 3059526e306bSschwarze return 24; 306049aff9f8Sschwarze case 'j': /* Always adjust left margin only. */ 3061526e306bSschwarze return 0; 30622e4bcbabSschwarze case 'l': /* Fixed line width for DocBook. */ 30632e4bcbabSschwarze return 78 * 24; 306449aff9f8Sschwarze case 'T': /* Some output device is always defined. */ 3065526e306bSschwarze return 1; 306649aff9f8Sschwarze case 'V': /* Fixed vertical resolution. */ 3067526e306bSschwarze return 40; 3068b382ec6eSschwarze default: 3069526e306bSschwarze return -1; 3070b382ec6eSschwarze } 3071b382ec6eSschwarze } 3072b382ec6eSschwarze 3073c542e817Sschwarze int 30749f6d70bcSschwarze roff_getreg(struct roff *r, const char *name) 3075f8618d99Sschwarze { 3076dcd9eb04Sschwarze return roff_getregn(r, name, strlen(name), '\0'); 3077c542e817Sschwarze } 3078c542e817Sschwarze 3079c542e817Sschwarze static int 3080dcd9eb04Sschwarze roff_getregn(struct roff *r, const char *name, size_t len, char sign) 3081c542e817Sschwarze { 3082c542e817Sschwarze struct roffreg *reg; 3083b382ec6eSschwarze int val; 3084b382ec6eSschwarze 3085b382ec6eSschwarze if ('.' == name[0] && 2 == len) { 308645ff8f42Sschwarze val = roff_getregro(r, name + 1); 3087b382ec6eSschwarze if (-1 != val) 3088526e306bSschwarze return val; 3089b382ec6eSschwarze } 3090c542e817Sschwarze 3091dcd9eb04Sschwarze for (reg = r->regtab; reg; reg = reg->next) { 3092c542e817Sschwarze if (len == reg->key.sz && 3093dcd9eb04Sschwarze 0 == strncmp(name, reg->key.p, len)) { 3094dcd9eb04Sschwarze switch (sign) { 3095dcd9eb04Sschwarze case '+': 3096dcd9eb04Sschwarze reg->val += reg->step; 3097dcd9eb04Sschwarze break; 3098dcd9eb04Sschwarze case '-': 3099dcd9eb04Sschwarze reg->val -= reg->step; 3100dcd9eb04Sschwarze break; 3101dcd9eb04Sschwarze default: 3102dcd9eb04Sschwarze break; 3103dcd9eb04Sschwarze } 3104526e306bSschwarze return reg->val; 3105dcd9eb04Sschwarze } 3106dcd9eb04Sschwarze } 310722881299Sschwarze 3108dcd9eb04Sschwarze roff_setregn(r, name, len, 0, '\0', INT_MIN); 3109526e306bSschwarze return 0; 3110f8618d99Sschwarze } 3111f8618d99Sschwarze 311224ab75f2Sschwarze static int 311324ab75f2Sschwarze roff_hasregn(const struct roff *r, const char *name, size_t len) 311424ab75f2Sschwarze { 311524ab75f2Sschwarze struct roffreg *reg; 311624ab75f2Sschwarze int val; 311724ab75f2Sschwarze 311824ab75f2Sschwarze if ('.' == name[0] && 2 == len) { 311945ff8f42Sschwarze val = roff_getregro(r, name + 1); 312024ab75f2Sschwarze if (-1 != val) 3121526e306bSschwarze return 1; 312224ab75f2Sschwarze } 312324ab75f2Sschwarze 312424ab75f2Sschwarze for (reg = r->regtab; reg; reg = reg->next) 312524ab75f2Sschwarze if (len == reg->key.sz && 312624ab75f2Sschwarze 0 == strncmp(name, reg->key.p, len)) 3127526e306bSschwarze return 1; 312824ab75f2Sschwarze 3129526e306bSschwarze return 0; 313024ab75f2Sschwarze } 313124ab75f2Sschwarze 313222881299Sschwarze static void 313322881299Sschwarze roff_freereg(struct roffreg *reg) 3134f8618d99Sschwarze { 313522881299Sschwarze struct roffreg *old_reg; 3136f8618d99Sschwarze 313722881299Sschwarze while (NULL != reg) { 313822881299Sschwarze free(reg->key.p); 313922881299Sschwarze old_reg = reg; 314022881299Sschwarze reg = reg->next; 314122881299Sschwarze free(old_reg); 314222881299Sschwarze } 3143f8618d99Sschwarze } 3144b49ce91bSschwarze 3145b7f92c5fSschwarze static int 31469830bf9fSschwarze roff_nr(ROFF_ARGS) 3147f673a3c7Sschwarze { 3148dcd9eb04Sschwarze char *key, *val, *step; 314959b3009eSschwarze size_t keysz; 3150dcd9eb04Sschwarze int iv, is, len; 315175088a49Sschwarze char sign; 31529830bf9fSschwarze 315328687875Sschwarze key = val = buf->buf + pos; 315428687875Sschwarze if (*key == '\0') 3155526e306bSschwarze return ROFF_IGN; 315659b3009eSschwarze 31573b5bd91eSschwarze keysz = roff_getname(&val, ln, pos); 31587ad9cfd6Sschwarze if (key[keysz] == '\\' || key[keysz] == '\t') 3159526e306bSschwarze return ROFF_IGN; 31609830bf9fSschwarze 316175088a49Sschwarze sign = *val; 316228687875Sschwarze if (sign == '+' || sign == '-') 316375088a49Sschwarze val++; 316422881299Sschwarze 3165dcd9eb04Sschwarze len = 0; 3166f4432377Sschwarze if (roff_evalnum(ln, val, &len, &iv, 'u', 0) == 0) 3167dcd9eb04Sschwarze return ROFF_IGN; 3168f673a3c7Sschwarze 3169dcd9eb04Sschwarze step = val + len; 3170dcd9eb04Sschwarze while (isspace((unsigned char)*step)) 3171dcd9eb04Sschwarze step++; 3172f4432377Sschwarze if (roff_evalnum(ln, step, NULL, &is, '\0', 0) == 0) 3173dcd9eb04Sschwarze is = INT_MIN; 3174dcd9eb04Sschwarze 3175dcd9eb04Sschwarze roff_setregn(r, key, keysz, iv, sign, is); 3176526e306bSschwarze return ROFF_IGN; 3177f673a3c7Sschwarze } 3178b49ce91bSschwarze 3179b7f92c5fSschwarze static int 3180e7693622Sschwarze roff_rr(ROFF_ARGS) 3181e7693622Sschwarze { 3182e7693622Sschwarze struct roffreg *reg, **prev; 318359b3009eSschwarze char *name, *cp; 318459b3009eSschwarze size_t namesz; 3185e7693622Sschwarze 318628687875Sschwarze name = cp = buf->buf + pos; 318728687875Sschwarze if (*name == '\0') 3188526e306bSschwarze return ROFF_IGN; 31893b5bd91eSschwarze namesz = roff_getname(&cp, ln, pos); 319059b3009eSschwarze name[namesz] = '\0'; 3191e7693622Sschwarze 3192e7693622Sschwarze prev = &r->regtab; 3193e7693622Sschwarze while (1) { 3194e7693622Sschwarze reg = *prev; 319528687875Sschwarze if (reg == NULL || !strcmp(name, reg->key.p)) 3196e7693622Sschwarze break; 3197e7693622Sschwarze prev = ®->next; 3198e7693622Sschwarze } 319928687875Sschwarze if (reg != NULL) { 3200e7693622Sschwarze *prev = reg->next; 3201e7693622Sschwarze free(reg->key.p); 3202e7693622Sschwarze free(reg); 3203e7693622Sschwarze } 3204526e306bSschwarze return ROFF_IGN; 3205e7693622Sschwarze } 3206e7693622Sschwarze 3207fa2127f9Sschwarze /* --- handler functions for roff requests -------------------------------- */ 3208fa2127f9Sschwarze 3209b7f92c5fSschwarze static int 3210dce4c3c7Sschwarze roff_rm(ROFF_ARGS) 3211dce4c3c7Sschwarze { 3212dce4c3c7Sschwarze const char *name; 3213dce4c3c7Sschwarze char *cp; 321459b3009eSschwarze size_t namesz; 3215dce4c3c7Sschwarze 321628687875Sschwarze cp = buf->buf + pos; 321728687875Sschwarze while (*cp != '\0') { 321859b3009eSschwarze name = cp; 32193b5bd91eSschwarze namesz = roff_getname(&cp, ln, (int)(cp - buf->buf)); 322059b3009eSschwarze roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0); 32210856de67Sschwarze roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); 32227ad9cfd6Sschwarze if (name[namesz] == '\\' || name[namesz] == '\t') 322359b3009eSschwarze break; 3224dce4c3c7Sschwarze } 3225526e306bSschwarze return ROFF_IGN; 3226dce4c3c7Sschwarze } 3227dce4c3c7Sschwarze 3228b7f92c5fSschwarze static int 3229a91e242cSschwarze roff_it(ROFF_ARGS) 3230a91e242cSschwarze { 3231a91e242cSschwarze int iv; 3232a91e242cSschwarze 3233a91e242cSschwarze /* Parse the number of lines. */ 32343a1681a6Sschwarze 3235f4432377Sschwarze if ( ! roff_evalnum(ln, buf->buf, &pos, &iv, '\0', 0)) { 3236a5a5f808Sschwarze mandoc_msg(MANDOCERR_IT_NONUM, 3237a5a5f808Sschwarze ln, ppos, "%s", buf->buf + 1); 3238526e306bSschwarze return ROFF_IGN; 3239a91e242cSschwarze } 3240a91e242cSschwarze 3241c527ae42Sschwarze while (isspace((unsigned char)buf->buf[pos])) 3242c527ae42Sschwarze pos++; 3243c527ae42Sschwarze 3244c527ae42Sschwarze /* 3245c527ae42Sschwarze * Arm the input line trap. 3246c527ae42Sschwarze * Special-casing "an-trap" is an ugly workaround to cope 3247c527ae42Sschwarze * with DocBook stupidly fiddling with man(7) internals. 3248c527ae42Sschwarze */ 32493a1681a6Sschwarze 3250a91e242cSschwarze roffit_lines = iv; 3251c527ae42Sschwarze roffit_macro = mandoc_strdup(iv != 1 || 3252c527ae42Sschwarze strcmp(buf->buf + pos, "an-trap") ? 3253c527ae42Sschwarze buf->buf + pos : "br"); 3254526e306bSschwarze return ROFF_IGN; 3255a91e242cSschwarze } 3256a91e242cSschwarze 3257b7f92c5fSschwarze static int 325893aaf9baSschwarze roff_Dd(ROFF_ARGS) 325993aaf9baSschwarze { 326007530f0aSschwarze int mask; 326107530f0aSschwarze enum roff_tok t, te; 326293aaf9baSschwarze 326307530f0aSschwarze switch (tok) { 326407530f0aSschwarze case ROFF_Dd: 326507530f0aSschwarze tok = MDOC_Dd; 326607530f0aSschwarze te = MDOC_MAX; 3267f0d7487dSschwarze if (r->format == 0) 3268f0d7487dSschwarze r->format = MPARSE_MDOC; 326907530f0aSschwarze mask = MPARSE_MDOC | MPARSE_QUICK; 327007530f0aSschwarze break; 327107530f0aSschwarze case ROFF_TH: 327207530f0aSschwarze tok = MAN_TH; 327307530f0aSschwarze te = MAN_MAX; 3274f0d7487dSschwarze if (r->format == 0) 3275f0d7487dSschwarze r->format = MPARSE_MAN; 327607530f0aSschwarze mask = MPARSE_QUICK; 327707530f0aSschwarze break; 327807530f0aSschwarze default: 327907530f0aSschwarze abort(); 328007530f0aSschwarze } 328107530f0aSschwarze if ((r->options & mask) == 0) 328207530f0aSschwarze for (t = tok; t < te; t++) 328307530f0aSschwarze roff_setstr(r, roff_name[t], NULL, 0); 3284526e306bSschwarze return ROFF_CONT; 328593aaf9baSschwarze } 328693aaf9baSschwarze 3287b7f92c5fSschwarze static int 32882791bd1cSschwarze roff_TE(ROFF_ARGS) 32892791bd1cSschwarze { 32906bce724aSschwarze r->man->flags &= ~ROFF_NONOFILL; 3291d93f8561Sschwarze if (r->tbl == NULL) { 3292a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "TE"); 3293d93f8561Sschwarze return ROFF_IGN; 3294d93f8561Sschwarze } 3295fb382a01Sschwarze if (tbl_end(r->tbl, 0) == 0) { 3296d93f8561Sschwarze r->tbl = NULL; 329792ff8da6Sschwarze free(buf->buf); 329892ff8da6Sschwarze buf->buf = mandoc_strdup(".sp"); 329992ff8da6Sschwarze buf->sz = 4; 3300d0365455Sschwarze *offs = 0; 3301526e306bSschwarze return ROFF_REPARSE; 330292ff8da6Sschwarze } 3303d93f8561Sschwarze r->tbl = NULL; 3304526e306bSschwarze return ROFF_IGN; 33052791bd1cSschwarze } 33062791bd1cSschwarze 3307b7f92c5fSschwarze static int 33082791bd1cSschwarze roff_T_(ROFF_ARGS) 33092791bd1cSschwarze { 33102791bd1cSschwarze 33112791bd1cSschwarze if (NULL == r->tbl) 3312a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "T&"); 33132791bd1cSschwarze else 331429478532Sschwarze tbl_restart(ln, ppos, r->tbl); 33152791bd1cSschwarze 3316526e306bSschwarze return ROFF_IGN; 33172791bd1cSschwarze } 33182791bd1cSschwarze 331957ca9bd5Sschwarze /* 332057ca9bd5Sschwarze * Handle in-line equation delimiters. 332157ca9bd5Sschwarze */ 3322b7f92c5fSschwarze static int 332328687875Sschwarze roff_eqndelim(struct roff *r, struct buf *buf, int pos) 3324f8618d99Sschwarze { 332557ca9bd5Sschwarze char *cp1, *cp2; 332655e7d5daSschwarze const char *bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr; 3327f8618d99Sschwarze 332857ca9bd5Sschwarze /* 332957ca9bd5Sschwarze * Outside equations, look for an opening delimiter. 333057ca9bd5Sschwarze * If we are inside an equation, we already know it is 333157ca9bd5Sschwarze * in-line, or this function wouldn't have been called; 333257ca9bd5Sschwarze * so look for a closing delimiter. 333357ca9bd5Sschwarze */ 333457ca9bd5Sschwarze 333528687875Sschwarze cp1 = buf->buf + pos; 333657ca9bd5Sschwarze cp2 = strchr(cp1, r->eqn == NULL ? 333757ca9bd5Sschwarze r->last_eqn->odelim : r->last_eqn->cdelim); 333857ca9bd5Sschwarze if (cp2 == NULL) 3339526e306bSschwarze return ROFF_CONT; 334057ca9bd5Sschwarze 334196e1823cSschwarze *cp2++ = '\0'; 334255e7d5daSschwarze bef_pr = bef_nl = aft_nl = aft_pr = ""; 334355e7d5daSschwarze 334455e7d5daSschwarze /* Handle preceding text, protecting whitespace. */ 334555e7d5daSschwarze 334628687875Sschwarze if (*buf->buf != '\0') { 334755e7d5daSschwarze if (r->eqn == NULL) 334855e7d5daSschwarze bef_pr = "\\&"; 334955e7d5daSschwarze bef_nl = "\n"; 335055e7d5daSschwarze } 335155e7d5daSschwarze 335255e7d5daSschwarze /* 335355e7d5daSschwarze * Prepare replacing the delimiter with an equation macro 335455e7d5daSschwarze * and drop leading white space from the equation. 335555e7d5daSschwarze */ 335655e7d5daSschwarze 335755e7d5daSschwarze if (r->eqn == NULL) { 335855e7d5daSschwarze while (*cp2 == ' ') 335955e7d5daSschwarze cp2++; 336055e7d5daSschwarze mac = ".EQ"; 336155e7d5daSschwarze } else 336255e7d5daSschwarze mac = ".EN"; 336355e7d5daSschwarze 336455e7d5daSschwarze /* Handle following text, protecting whitespace. */ 336555e7d5daSschwarze 336655e7d5daSschwarze if (*cp2 != '\0') { 336755e7d5daSschwarze aft_nl = "\n"; 336855e7d5daSschwarze if (r->eqn != NULL) 336955e7d5daSschwarze aft_pr = "\\&"; 337055e7d5daSschwarze } 337155e7d5daSschwarze 337255e7d5daSschwarze /* Do the actual replacement. */ 337355e7d5daSschwarze 337428687875Sschwarze buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf, 337555e7d5daSschwarze bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1; 337628687875Sschwarze free(buf->buf); 337728687875Sschwarze buf->buf = cp1; 337857ca9bd5Sschwarze 337957ca9bd5Sschwarze /* Toggle the in-line state of the eqn subsystem. */ 338057ca9bd5Sschwarze 338157ca9bd5Sschwarze r->eqn_inline = r->eqn == NULL; 3382526e306bSschwarze return ROFF_REPARSE; 3383f8618d99Sschwarze } 3384f8618d99Sschwarze 3385b7f92c5fSschwarze static int 3386a64ea134Sschwarze roff_EQ(ROFF_ARGS) 33878d973ab1Sschwarze { 3388bf9acac6Sschwarze struct roff_node *n; 3389bf9acac6Sschwarze 33906b86842eSschwarze if (r->man->meta.macroset == MACROSET_MAN) 339101647224Sschwarze man_breakscope(r->man, ROFF_EQ); 3392bf9acac6Sschwarze n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE); 3393bf9acac6Sschwarze if (ln > r->man->last->line) 3394bf9acac6Sschwarze n->flags |= NODE_LINE; 3395d4c8d4a3Sschwarze n->eqn = eqn_box_new(); 3396bf9acac6Sschwarze roff_node_append(r->man, n); 3397bf9acac6Sschwarze r->man->next = ROFF_NEXT_SIBLING; 33988d973ab1Sschwarze 339928687875Sschwarze assert(r->eqn == NULL); 3400bf9acac6Sschwarze if (r->last_eqn == NULL) 340191305757Sschwarze r->last_eqn = eqn_alloc(); 3402bf9acac6Sschwarze else 3403bf9acac6Sschwarze eqn_reset(r->last_eqn); 3404bf9acac6Sschwarze r->eqn = r->last_eqn; 3405bf9acac6Sschwarze r->eqn->node = n; 3406f8618d99Sschwarze 340728687875Sschwarze if (buf->buf[pos] != '\0') 3408a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, 340928687875Sschwarze ".EQ %s", buf->buf + pos); 3410f8618d99Sschwarze 3411526e306bSschwarze return ROFF_IGN; 34128d973ab1Sschwarze } 34138d973ab1Sschwarze 3414b7f92c5fSschwarze static int 34158d973ab1Sschwarze roff_EN(ROFF_ARGS) 34168d973ab1Sschwarze { 3417bf9acac6Sschwarze if (r->eqn != NULL) { 3418bf9acac6Sschwarze eqn_parse(r->eqn); 3419bf9acac6Sschwarze r->eqn = NULL; 3420bf9acac6Sschwarze } else 3421a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "EN"); 3422bf9acac6Sschwarze if (buf->buf[pos] != '\0') 3423a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, 3424bf9acac6Sschwarze "EN %s", buf->buf + pos); 3425526e306bSschwarze return ROFF_IGN; 34268d973ab1Sschwarze } 34278d973ab1Sschwarze 3428b7f92c5fSschwarze static int 34292791bd1cSschwarze roff_TS(ROFF_ARGS) 34302791bd1cSschwarze { 3431d93f8561Sschwarze if (r->tbl != NULL) { 3432a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_BROKEN, ln, ppos, "TS breaks TS"); 3433fb382a01Sschwarze tbl_end(r->tbl, 0); 34342791bd1cSschwarze } 34356bce724aSschwarze r->man->flags |= ROFF_NONOFILL; 343691305757Sschwarze r->tbl = tbl_alloc(ppos, ln, r->last_tbl); 3437fb382a01Sschwarze if (r->last_tbl == NULL) 3438d93f8561Sschwarze r->first_tbl = r->tbl; 3439d93f8561Sschwarze r->last_tbl = r->tbl; 3440526e306bSschwarze return ROFF_IGN; 34412791bd1cSschwarze } 34422791bd1cSschwarze 3443b7f92c5fSschwarze static int 34440438bfdfSschwarze roff_noarg(ROFF_ARGS) 34450438bfdfSschwarze { 34460438bfdfSschwarze if (r->man->flags & (MAN_BLINE | MAN_ELINE)) 34470438bfdfSschwarze man_breakscope(r->man, tok); 34480438bfdfSschwarze if (tok == ROFF_brp) 34490438bfdfSschwarze tok = ROFF_br; 34500438bfdfSschwarze roff_elem_alloc(r->man, ln, ppos, tok); 34510438bfdfSschwarze if (buf->buf[pos] != '\0') 34520438bfdfSschwarze mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, 34530438bfdfSschwarze "%s %s", roff_name[tok], buf->buf + pos); 34540438bfdfSschwarze if (tok == ROFF_nf) 34550438bfdfSschwarze r->man->flags |= ROFF_NOFILL; 34560438bfdfSschwarze else if (tok == ROFF_fi) 34570438bfdfSschwarze r->man->flags &= ~ROFF_NOFILL; 34580438bfdfSschwarze r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED; 34590438bfdfSschwarze r->man->next = ROFF_NEXT_SIBLING; 34600438bfdfSschwarze return ROFF_IGN; 34610438bfdfSschwarze } 34620438bfdfSschwarze 34630438bfdfSschwarze static int 3464c4d3fa85Sschwarze roff_onearg(ROFF_ARGS) 3465c4d3fa85Sschwarze { 3466c4d3fa85Sschwarze struct roff_node *n; 3467c4d3fa85Sschwarze char *cp; 3468e13b4195Sschwarze int npos; 3469c4d3fa85Sschwarze 3470885ec428Sschwarze if (r->man->flags & (MAN_BLINE | MAN_ELINE) && 347139dd362eSschwarze (tok == ROFF_ce || tok == ROFF_rj || tok == ROFF_sp || 347239dd362eSschwarze tok == ROFF_ti)) 3473885ec428Sschwarze man_breakscope(r->man, tok); 3474885ec428Sschwarze 34756de096f4Sschwarze if (roffce_node != NULL && (tok == ROFF_ce || tok == ROFF_rj)) { 3476e13b4195Sschwarze r->man->last = roffce_node; 3477e13b4195Sschwarze r->man->next = ROFF_NEXT_SIBLING; 3478e13b4195Sschwarze } 3479e13b4195Sschwarze 3480c4d3fa85Sschwarze roff_elem_alloc(r->man, ln, ppos, tok); 3481c4d3fa85Sschwarze n = r->man->last; 3482c4d3fa85Sschwarze 3483c4d3fa85Sschwarze cp = buf->buf + pos; 3484c4d3fa85Sschwarze if (*cp != '\0') { 3485c4d3fa85Sschwarze while (*cp != '\0' && *cp != ' ') 3486c4d3fa85Sschwarze cp++; 3487c4d3fa85Sschwarze while (*cp == ' ') 3488c4d3fa85Sschwarze *cp++ = '\0'; 3489c4d3fa85Sschwarze if (*cp != '\0') 3490a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_EXCESS, 3491a5a5f808Sschwarze ln, (int)(cp - buf->buf), 3492c4d3fa85Sschwarze "%s ... %s", roff_name[tok], cp); 3493c4d3fa85Sschwarze roff_word_alloc(r->man, ln, pos, buf->buf + pos); 3494c4d3fa85Sschwarze } 3495c4d3fa85Sschwarze 34966de096f4Sschwarze if (tok == ROFF_ce || tok == ROFF_rj) { 34976de096f4Sschwarze if (r->man->last->type == ROFFT_ELEM) { 3498e13b4195Sschwarze roff_word_alloc(r->man, ln, pos, "1"); 3499e13b4195Sschwarze r->man->last->flags |= NODE_NOSRC; 3500e13b4195Sschwarze } 3501e13b4195Sschwarze npos = 0; 35023b5bd91eSschwarze if (roff_evalnum(ln, r->man->last->string, &npos, 3503f4432377Sschwarze &roffce_lines, '\0', 0) == 0) { 3504a5a5f808Sschwarze mandoc_msg(MANDOCERR_CE_NONUM, 3505a5a5f808Sschwarze ln, pos, "ce %s", buf->buf + pos); 3506e13b4195Sschwarze roffce_lines = 1; 3507e13b4195Sschwarze } 3508e13b4195Sschwarze if (roffce_lines < 1) { 3509e13b4195Sschwarze r->man->last = r->man->last->parent; 3510e13b4195Sschwarze roffce_node = NULL; 3511e13b4195Sschwarze roffce_lines = 0; 3512e13b4195Sschwarze } else 3513e13b4195Sschwarze roffce_node = r->man->last->parent; 3514e13b4195Sschwarze } else { 3515e13b4195Sschwarze n->flags |= NODE_VALID | NODE_ENDED; 3516c4d3fa85Sschwarze r->man->last = n; 3517e13b4195Sschwarze } 3518e13b4195Sschwarze n->flags |= NODE_LINE; 3519c4d3fa85Sschwarze r->man->next = ROFF_NEXT_SIBLING; 3520c4d3fa85Sschwarze return ROFF_IGN; 3521c4d3fa85Sschwarze } 3522c4d3fa85Sschwarze 3523b7f92c5fSschwarze static int 3524f7242c43Sschwarze roff_manyarg(ROFF_ARGS) 3525f7242c43Sschwarze { 3526f7242c43Sschwarze struct roff_node *n; 3527f7242c43Sschwarze char *sp, *ep; 3528f7242c43Sschwarze 3529f7242c43Sschwarze roff_elem_alloc(r->man, ln, ppos, tok); 3530f7242c43Sschwarze n = r->man->last; 3531f7242c43Sschwarze 3532f7242c43Sschwarze for (sp = ep = buf->buf + pos; *sp != '\0'; sp = ep) { 3533f7242c43Sschwarze while (*ep != '\0' && *ep != ' ') 3534f7242c43Sschwarze ep++; 3535f7242c43Sschwarze while (*ep == ' ') 3536f7242c43Sschwarze *ep++ = '\0'; 3537f7242c43Sschwarze roff_word_alloc(r->man, ln, sp - buf->buf, sp); 3538f7242c43Sschwarze } 3539f7242c43Sschwarze 3540f7242c43Sschwarze n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED; 3541f7242c43Sschwarze r->man->last = n; 3542f7242c43Sschwarze r->man->next = ROFF_NEXT_SIBLING; 3543f7242c43Sschwarze return ROFF_IGN; 3544f7242c43Sschwarze } 3545f7242c43Sschwarze 3546b7f92c5fSschwarze static int 35470856de67Sschwarze roff_als(ROFF_ARGS) 35480856de67Sschwarze { 35490856de67Sschwarze char *oldn, *newn, *end, *value; 35500856de67Sschwarze size_t oldsz, newsz, valsz; 35510856de67Sschwarze 35520856de67Sschwarze newn = oldn = buf->buf + pos; 35530856de67Sschwarze if (*newn == '\0') 35540856de67Sschwarze return ROFF_IGN; 35550856de67Sschwarze 35563b5bd91eSschwarze newsz = roff_getname(&oldn, ln, pos); 35577ad9cfd6Sschwarze if (newn[newsz] == '\\' || newn[newsz] == '\t' || *oldn == '\0') 35580856de67Sschwarze return ROFF_IGN; 35590856de67Sschwarze 35600856de67Sschwarze end = oldn; 35613b5bd91eSschwarze oldsz = roff_getname(&end, ln, oldn - buf->buf); 35620856de67Sschwarze if (oldsz == 0) 35630856de67Sschwarze return ROFF_IGN; 35640856de67Sschwarze 3565e1866606Sschwarze valsz = mandoc_asprintf(&value, ".%.*s \\$@\\\"\n", 356607530f0aSschwarze (int)oldsz, oldn); 35670856de67Sschwarze roff_setstrn(&r->strtab, newn, newsz, value, valsz, 0); 35680856de67Sschwarze roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); 35690856de67Sschwarze free(value); 35700856de67Sschwarze return ROFF_IGN; 35710856de67Sschwarze } 35720856de67Sschwarze 35737dd0ff50Sschwarze /* 35747dd0ff50Sschwarze * The .break request only makes sense inside conditionals, 35757dd0ff50Sschwarze * and that case is already handled in roff_cond_sub(). 35767dd0ff50Sschwarze */ 35777dd0ff50Sschwarze static int 35787dd0ff50Sschwarze roff_break(ROFF_ARGS) 35797dd0ff50Sschwarze { 35807dd0ff50Sschwarze mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, pos, "break"); 35817dd0ff50Sschwarze return ROFF_IGN; 35827dd0ff50Sschwarze } 35837dd0ff50Sschwarze 3584b7f92c5fSschwarze static int 35851e6d02e2Sschwarze roff_cc(ROFF_ARGS) 35861e6d02e2Sschwarze { 35871e6d02e2Sschwarze const char *p; 35881e6d02e2Sschwarze 358928687875Sschwarze p = buf->buf + pos; 35901e6d02e2Sschwarze 359128687875Sschwarze if (*p == '\0' || (r->control = *p++) == '.') 3592be477484Sschwarze r->control = '\0'; 35931e6d02e2Sschwarze 359428687875Sschwarze if (*p != '\0') 3595a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_EXCESS, 3596bce599dfSschwarze ln, p - buf->buf, "cc ... %s", p); 35971e6d02e2Sschwarze 3598526e306bSschwarze return ROFF_IGN; 35991e6d02e2Sschwarze } 36001e6d02e2Sschwarze 3601b7f92c5fSschwarze static int 360229fcab3eSschwarze roff_char(ROFF_ARGS) 360329fcab3eSschwarze { 360429fcab3eSschwarze const char *p, *kp, *vp; 360529fcab3eSschwarze size_t ksz, vsz; 360629fcab3eSschwarze int font; 360729fcab3eSschwarze 360829fcab3eSschwarze /* Parse the character to be replaced. */ 360929fcab3eSschwarze 361029fcab3eSschwarze kp = buf->buf + pos; 361129fcab3eSschwarze p = kp + 1; 361229fcab3eSschwarze if (*kp == '\0' || (*kp == '\\' && 361329fcab3eSschwarze mandoc_escape(&p, NULL, NULL) != ESCAPE_SPECIAL) || 361429fcab3eSschwarze (*p != ' ' && *p != '\0')) { 3615a5a5f808Sschwarze mandoc_msg(MANDOCERR_CHAR_ARG, ln, pos, "char %s", kp); 361629fcab3eSschwarze return ROFF_IGN; 361729fcab3eSschwarze } 361829fcab3eSschwarze ksz = p - kp; 361929fcab3eSschwarze while (*p == ' ') 362029fcab3eSschwarze p++; 362129fcab3eSschwarze 362229fcab3eSschwarze /* 362329fcab3eSschwarze * If the replacement string contains a font escape sequence, 362429fcab3eSschwarze * we have to restore the font at the end. 362529fcab3eSschwarze */ 362629fcab3eSschwarze 362729fcab3eSschwarze vp = p; 362829fcab3eSschwarze vsz = strlen(p); 362929fcab3eSschwarze font = 0; 363029fcab3eSschwarze while (*p != '\0') { 363129fcab3eSschwarze if (*p++ != '\\') 363229fcab3eSschwarze continue; 363329fcab3eSschwarze switch (mandoc_escape(&p, NULL, NULL)) { 363429fcab3eSschwarze case ESCAPE_FONT: 363529fcab3eSschwarze case ESCAPE_FONTROMAN: 363629fcab3eSschwarze case ESCAPE_FONTITALIC: 363729fcab3eSschwarze case ESCAPE_FONTBOLD: 363829fcab3eSschwarze case ESCAPE_FONTBI: 36397d063611Sschwarze case ESCAPE_FONTCR: 36407d063611Sschwarze case ESCAPE_FONTCB: 36417d063611Sschwarze case ESCAPE_FONTCI: 364229fcab3eSschwarze case ESCAPE_FONTPREV: 364329fcab3eSschwarze font++; 364429fcab3eSschwarze break; 364529fcab3eSschwarze default: 364629fcab3eSschwarze break; 364729fcab3eSschwarze } 364829fcab3eSschwarze } 364929fcab3eSschwarze if (font > 1) 3650a5a5f808Sschwarze mandoc_msg(MANDOCERR_CHAR_FONT, 3651a5a5f808Sschwarze ln, (int)(vp - buf->buf), "%s", vp); 365229fcab3eSschwarze 365329fcab3eSschwarze /* 365429fcab3eSschwarze * Approximate the effect of .char using the .tr tables. 365529fcab3eSschwarze * XXX In groff, .char and .tr interact differently. 365629fcab3eSschwarze */ 365729fcab3eSschwarze 365829fcab3eSschwarze if (ksz == 1) { 365929fcab3eSschwarze if (r->xtab == NULL) 366029fcab3eSschwarze r->xtab = mandoc_calloc(128, sizeof(*r->xtab)); 366129fcab3eSschwarze assert((unsigned int)*kp < 128); 366229fcab3eSschwarze free(r->xtab[(int)*kp].p); 366329fcab3eSschwarze r->xtab[(int)*kp].sz = mandoc_asprintf(&r->xtab[(int)*kp].p, 366429fcab3eSschwarze "%s%s", vp, font ? "\fP" : ""); 366529fcab3eSschwarze } else { 366629fcab3eSschwarze roff_setstrn(&r->xmbtab, kp, ksz, vp, vsz, 0); 366729fcab3eSschwarze if (font) 366829fcab3eSschwarze roff_setstrn(&r->xmbtab, kp, ksz, "\\fP", 3, 1); 366929fcab3eSschwarze } 367029fcab3eSschwarze return ROFF_IGN; 367129fcab3eSschwarze } 367229fcab3eSschwarze 367329fcab3eSschwarze static int 3674be477484Sschwarze roff_ec(ROFF_ARGS) 3675be477484Sschwarze { 3676be477484Sschwarze const char *p; 3677be477484Sschwarze 3678be477484Sschwarze p = buf->buf + pos; 3679be477484Sschwarze if (*p == '\0') 3680be477484Sschwarze r->escape = '\\'; 3681be477484Sschwarze else { 3682be477484Sschwarze r->escape = *p; 3683be477484Sschwarze if (*++p != '\0') 3684a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_EXCESS, ln, 3685a5a5f808Sschwarze (int)(p - buf->buf), "ec ... %s", p); 3686be477484Sschwarze } 3687be477484Sschwarze return ROFF_IGN; 3688be477484Sschwarze } 3689be477484Sschwarze 3690b7f92c5fSschwarze static int 3691be477484Sschwarze roff_eo(ROFF_ARGS) 3692be477484Sschwarze { 3693be477484Sschwarze r->escape = '\0'; 3694be477484Sschwarze if (buf->buf[pos] != '\0') 3695a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_SKIP, 3696be477484Sschwarze ln, pos, "eo %s", buf->buf + pos); 3697be477484Sschwarze return ROFF_IGN; 3698be477484Sschwarze } 3699be477484Sschwarze 3700b7f92c5fSschwarze static int 3701edb0312fSschwarze roff_mc(ROFF_ARGS) 3702edb0312fSschwarze { 3703edb0312fSschwarze struct roff_node *n; 3704edb0312fSschwarze char *cp; 3705edb0312fSschwarze 3706edb0312fSschwarze /* Parse the first argument. */ 3707edb0312fSschwarze 3708edb0312fSschwarze cp = buf->buf + pos; 3709edb0312fSschwarze if (*cp != '\0') 3710edb0312fSschwarze cp++; 3711edb0312fSschwarze if (buf->buf[pos] == '\\') { 3712edb0312fSschwarze switch (mandoc_escape((const char **)&cp, NULL, NULL)) { 3713edb0312fSschwarze case ESCAPE_SPECIAL: 3714edb0312fSschwarze case ESCAPE_UNICODE: 3715edb0312fSschwarze case ESCAPE_NUMBERED: 3716edb0312fSschwarze break; 3717edb0312fSschwarze default: 3718edb0312fSschwarze *cp = '\0'; 3719edb0312fSschwarze mandoc_msg(MANDOCERR_MC_ESC, ln, pos, 3720edb0312fSschwarze "mc %s", buf->buf + pos); 3721edb0312fSschwarze buf->buf[pos] = '\0'; 3722edb0312fSschwarze break; 3723edb0312fSschwarze } 3724edb0312fSschwarze } 3725edb0312fSschwarze 3726edb0312fSschwarze /* Ignore additional arguments. */ 3727edb0312fSschwarze 3728edb0312fSschwarze while (*cp == ' ') 3729edb0312fSschwarze *cp++ = '\0'; 3730edb0312fSschwarze if (*cp != '\0') { 3731edb0312fSschwarze mandoc_msg(MANDOCERR_MC_DIST, ln, (int)(cp - buf->buf), 3732edb0312fSschwarze "mc ... %s", cp); 3733edb0312fSschwarze *cp = '\0'; 3734edb0312fSschwarze } 3735edb0312fSschwarze 3736edb0312fSschwarze /* Create the .mc node. */ 3737edb0312fSschwarze 3738edb0312fSschwarze roff_elem_alloc(r->man, ln, ppos, tok); 3739edb0312fSschwarze n = r->man->last; 3740edb0312fSschwarze if (buf->buf[pos] != '\0') 3741edb0312fSschwarze roff_word_alloc(r->man, ln, pos, buf->buf + pos); 3742edb0312fSschwarze n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED; 3743edb0312fSschwarze r->man->last = n; 3744edb0312fSschwarze r->man->next = ROFF_NEXT_SIBLING; 3745edb0312fSschwarze return ROFF_IGN; 3746edb0312fSschwarze } 3747edb0312fSschwarze 3748edb0312fSschwarze static int 3749b20e20eaSschwarze roff_nop(ROFF_ARGS) 3750b20e20eaSschwarze { 3751b20e20eaSschwarze while (buf->buf[pos] == ' ') 3752b20e20eaSschwarze pos++; 3753b20e20eaSschwarze *offs = pos; 3754b20e20eaSschwarze return ROFF_RERUN; 3755b20e20eaSschwarze } 3756b20e20eaSschwarze 3757b7f92c5fSschwarze static int 375804e980cbSschwarze roff_tr(ROFF_ARGS) 375904e980cbSschwarze { 376004e980cbSschwarze const char *p, *first, *second; 376104e980cbSschwarze size_t fsz, ssz; 376204e980cbSschwarze 376328687875Sschwarze p = buf->buf + pos; 376404e980cbSschwarze 376528687875Sschwarze if (*p == '\0') { 3766a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_EMPTY, ln, ppos, "tr"); 3767526e306bSschwarze return ROFF_IGN; 376804e980cbSschwarze } 376904e980cbSschwarze 377028687875Sschwarze while (*p != '\0') { 377104e980cbSschwarze fsz = ssz = 1; 377204e980cbSschwarze 377304e980cbSschwarze first = p++; 377428687875Sschwarze if (*first == '\\') { 3775140f9883Sschwarze if (mandoc_escape(&p, NULL, NULL) == ESCAPE_ERROR) 3776526e306bSschwarze return ROFF_IGN; 377704e980cbSschwarze fsz = (size_t)(p - first); 377804e980cbSschwarze } 377904e980cbSschwarze 378004e980cbSschwarze second = p++; 378128687875Sschwarze if (*second == '\\') { 3782140f9883Sschwarze if (mandoc_escape(&p, NULL, NULL) == ESCAPE_ERROR) 3783526e306bSschwarze return ROFF_IGN; 378404e980cbSschwarze ssz = (size_t)(p - second); 378528687875Sschwarze } else if (*second == '\0') { 3786a5a5f808Sschwarze mandoc_msg(MANDOCERR_TR_ODD, ln, 3787a5a5f808Sschwarze (int)(first - buf->buf), "tr %s", first); 378804e980cbSschwarze second = " "; 378904e980cbSschwarze p--; 379004e980cbSschwarze } 379104e980cbSschwarze 379204e980cbSschwarze if (fsz > 1) { 379349aff9f8Sschwarze roff_setstrn(&r->xmbtab, first, fsz, 379449aff9f8Sschwarze second, ssz, 0); 379504e980cbSschwarze continue; 379604e980cbSschwarze } 379704e980cbSschwarze 379828687875Sschwarze if (r->xtab == NULL) 379949aff9f8Sschwarze r->xtab = mandoc_calloc(128, 380049aff9f8Sschwarze sizeof(struct roffstr)); 380104e980cbSschwarze 380204e980cbSschwarze free(r->xtab[(int)*first].p); 380304e980cbSschwarze r->xtab[(int)*first].p = mandoc_strndup(second, ssz); 380404e980cbSschwarze r->xtab[(int)*first].sz = ssz; 380504e980cbSschwarze } 380604e980cbSschwarze 3807526e306bSschwarze return ROFF_IGN; 380804e980cbSschwarze } 380904e980cbSschwarze 38103dc5225dSschwarze /* 38113dc5225dSschwarze * Implementation of the .return request. 38123dc5225dSschwarze * There is no need to call roff_userret() from here. 38133dc5225dSschwarze * The read module will call that after rewinding the reader stack 38143dc5225dSschwarze * to the place from where the current macro was called. 38153dc5225dSschwarze */ 3816b7f92c5fSschwarze static int 38173dc5225dSschwarze roff_return(ROFF_ARGS) 38183dc5225dSschwarze { 38193dc5225dSschwarze if (r->mstackpos >= 0) 3820b7f92c5fSschwarze return ROFF_IGN | ROFF_USERRET; 38213dc5225dSschwarze 3822a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "return"); 38233dc5225dSschwarze return ROFF_IGN; 38243dc5225dSschwarze } 38253dc5225dSschwarze 3826b7f92c5fSschwarze static int 38277e291f10Sschwarze roff_rn(ROFF_ARGS) 38287e291f10Sschwarze { 38297e291f10Sschwarze const char *value; 38307e291f10Sschwarze char *oldn, *newn, *end; 38317e291f10Sschwarze size_t oldsz, newsz; 383207530f0aSschwarze int deftype; 38337e291f10Sschwarze 38347e291f10Sschwarze oldn = newn = buf->buf + pos; 38357e291f10Sschwarze if (*oldn == '\0') 38367e291f10Sschwarze return ROFF_IGN; 38377e291f10Sschwarze 38383b5bd91eSschwarze oldsz = roff_getname(&newn, ln, pos); 38397ad9cfd6Sschwarze if (oldn[oldsz] == '\\' || oldn[oldsz] == '\t' || *newn == '\0') 38407e291f10Sschwarze return ROFF_IGN; 38417e291f10Sschwarze 38427e291f10Sschwarze end = newn; 38433b5bd91eSschwarze newsz = roff_getname(&end, ln, newn - buf->buf); 38447e291f10Sschwarze if (newsz == 0) 38457e291f10Sschwarze return ROFF_IGN; 38467e291f10Sschwarze 384707530f0aSschwarze deftype = ROFFDEF_ANY; 384807530f0aSschwarze value = roff_getstrn(r, oldn, oldsz, &deftype); 384907530f0aSschwarze switch (deftype) { 385007530f0aSschwarze case ROFFDEF_USER: 38517e291f10Sschwarze roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0); 38527e291f10Sschwarze roff_setstrn(&r->strtab, oldn, oldsz, NULL, 0, 0); 38537e291f10Sschwarze roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); 385407530f0aSschwarze break; 385507530f0aSschwarze case ROFFDEF_PRE: 385607530f0aSschwarze roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0); 385707530f0aSschwarze roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); 385807530f0aSschwarze break; 385907530f0aSschwarze case ROFFDEF_REN: 38607e291f10Sschwarze roff_setstrn(&r->rentab, newn, newsz, value, strlen(value), 0); 38617e291f10Sschwarze roff_setstrn(&r->rentab, oldn, oldsz, NULL, 0, 0); 386207530f0aSschwarze roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0); 386307530f0aSschwarze break; 386407530f0aSschwarze case ROFFDEF_STD: 38657e291f10Sschwarze roff_setstrn(&r->rentab, newn, newsz, oldn, oldsz, 0); 38667e291f10Sschwarze roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0); 386707530f0aSschwarze break; 386807530f0aSschwarze default: 386907530f0aSschwarze roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0); 387007530f0aSschwarze roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); 387107530f0aSschwarze break; 387207530f0aSschwarze } 38737e291f10Sschwarze return ROFF_IGN; 38747e291f10Sschwarze } 38757e291f10Sschwarze 3876b7f92c5fSschwarze static int 38773dc5225dSschwarze roff_shift(ROFF_ARGS) 38783dc5225dSschwarze { 38793dc5225dSschwarze struct mctx *ctx; 3880c1a68d52Sschwarze int argpos, levels, i; 38813dc5225dSschwarze 3882c1a68d52Sschwarze argpos = pos; 38833dc5225dSschwarze levels = 1; 38843dc5225dSschwarze if (buf->buf[pos] != '\0' && 3885f4432377Sschwarze roff_evalnum(ln, buf->buf, &pos, &levels, '\0', 0) == 0) { 3886a5a5f808Sschwarze mandoc_msg(MANDOCERR_CE_NONUM, 38873dc5225dSschwarze ln, pos, "shift %s", buf->buf + pos); 38883dc5225dSschwarze levels = 1; 38893dc5225dSschwarze } 38903dc5225dSschwarze if (r->mstackpos < 0) { 3891a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "shift"); 38923dc5225dSschwarze return ROFF_IGN; 38933dc5225dSschwarze } 38943dc5225dSschwarze ctx = r->mstack + r->mstackpos; 38953dc5225dSschwarze if (levels > ctx->argc) { 3896a5a5f808Sschwarze mandoc_msg(MANDOCERR_SHIFT, 3897c1a68d52Sschwarze ln, argpos, "%d, but max is %d", levels, ctx->argc); 38983dc5225dSschwarze levels = ctx->argc; 38993dc5225dSschwarze } 3900c1a68d52Sschwarze if (levels < 0) { 3901c1a68d52Sschwarze mandoc_msg(MANDOCERR_ARG_NEG, ln, argpos, "shift %d", levels); 3902c1a68d52Sschwarze levels = 0; 3903c1a68d52Sschwarze } 39043dc5225dSschwarze if (levels == 0) 39053dc5225dSschwarze return ROFF_IGN; 39063dc5225dSschwarze for (i = 0; i < levels; i++) 39073dc5225dSschwarze free(ctx->argv[i]); 39083dc5225dSschwarze ctx->argc -= levels; 39093dc5225dSschwarze for (i = 0; i < ctx->argc; i++) 39103dc5225dSschwarze ctx->argv[i] = ctx->argv[i + levels]; 39113dc5225dSschwarze return ROFF_IGN; 39123dc5225dSschwarze } 39133dc5225dSschwarze 3914b7f92c5fSschwarze static int 3915aeef6c13Sschwarze roff_so(ROFF_ARGS) 3916aeef6c13Sschwarze { 3917f8fd269eSschwarze char *name, *cp; 3918aeef6c13Sschwarze 391928687875Sschwarze name = buf->buf + pos; 3920a5a5f808Sschwarze mandoc_msg(MANDOCERR_SO, ln, ppos, "so %s", name); 3921d494779eSschwarze 3922b58a7f4fSschwarze /* 3923b58a7f4fSschwarze * Handle `so'. Be EXTREMELY careful, as we shouldn't be 3924b58a7f4fSschwarze * opening anything that's not in our cwd or anything beneath 3925b58a7f4fSschwarze * it. Thus, explicitly disallow traversing up the file-system 3926b58a7f4fSschwarze * or using absolute paths. 3927b58a7f4fSschwarze */ 3928b58a7f4fSschwarze 392928687875Sschwarze if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) { 3930a5a5f808Sschwarze mandoc_msg(MANDOCERR_SO_PATH, ln, ppos, ".so %s", name); 3931f8fd269eSschwarze buf->sz = mandoc_asprintf(&cp, 3932f8fd269eSschwarze ".sp\nSee the file %s.\n.sp", name) + 1; 3933f8fd269eSschwarze free(buf->buf); 3934f8fd269eSschwarze buf->buf = cp; 3935f8fd269eSschwarze *offs = 0; 3936526e306bSschwarze return ROFF_REPARSE; 3937aeef6c13Sschwarze } 3938aeef6c13Sschwarze 3939aeef6c13Sschwarze *offs = pos; 3940526e306bSschwarze return ROFF_SO; 3941aeef6c13Sschwarze } 3942aeef6c13Sschwarze 3943fa2127f9Sschwarze /* --- user defined strings and macros ------------------------------------ */ 3944fa2127f9Sschwarze 3945b7f92c5fSschwarze static int 3946e53bb406Sschwarze roff_userdef(ROFF_ARGS) 394792853774Sschwarze { 39483dc5225dSschwarze struct mctx *ctx; 39493dc5225dSschwarze char *arg, *ap, *dst, *src; 39503dc5225dSschwarze size_t sz; 395192853774Sschwarze 395261dc5079Sschwarze /* If the macro is empty, ignore it altogether. */ 395361dc5079Sschwarze 395461dc5079Sschwarze if (*r->current_string == '\0') 395561dc5079Sschwarze return ROFF_IGN; 395661dc5079Sschwarze 39573dc5225dSschwarze /* Initialize a new macro stack context. */ 3958633c28f2Sschwarze 39593dc5225dSschwarze if (++r->mstackpos == r->mstacksz) { 39603dc5225dSschwarze r->mstack = mandoc_recallocarray(r->mstack, 39613dc5225dSschwarze r->mstacksz, r->mstacksz + 8, sizeof(*r->mstack)); 39623dc5225dSschwarze r->mstacksz += 8; 396345ff8f42Sschwarze } 39643dc5225dSschwarze ctx = r->mstack + r->mstackpos; 39653dc5225dSschwarze ctx->argc = 0; 39663dc5225dSschwarze 39673dc5225dSschwarze /* 39683dc5225dSschwarze * Collect pointers to macro argument strings, 39693dc5225dSschwarze * NUL-terminating them and escaping quotes. 39703dc5225dSschwarze */ 39713dc5225dSschwarze 39723dc5225dSschwarze src = buf->buf + pos; 39733dc5225dSschwarze while (*src != '\0') { 39743dc5225dSschwarze if (ctx->argc == ctx->argsz) { 39753dc5225dSschwarze ctx->argsz += 8; 39763dc5225dSschwarze ctx->argv = mandoc_reallocarray(ctx->argv, 39773dc5225dSschwarze ctx->argsz, sizeof(*ctx->argv)); 397845ff8f42Sschwarze } 3979e94357f9Sschwarze arg = roff_getarg(r, &src, ln, &pos); 39803dc5225dSschwarze sz = 1; /* For the terminating NUL. */ 39813dc5225dSschwarze for (ap = arg; *ap != '\0'; ap++) 39823dc5225dSschwarze sz += *ap == '"' ? 4 : 1; 39833dc5225dSschwarze ctx->argv[ctx->argc++] = dst = mandoc_malloc(sz); 39843dc5225dSschwarze for (ap = arg; *ap != '\0'; ap++) { 3985633c28f2Sschwarze if (*ap == '"') { 39863dc5225dSschwarze memcpy(dst, "\\(dq", 4); 39873dc5225dSschwarze dst += 4; 3988633c28f2Sschwarze } else 39893dc5225dSschwarze *dst++ = *ap; 3990633c28f2Sschwarze } 39913dc5225dSschwarze *dst = '\0'; 3992e94357f9Sschwarze free(arg); 3993e53bb406Sschwarze } 3994e53bb406Sschwarze 39953dc5225dSschwarze /* Replace the macro invocation by the macro definition. */ 3996633c28f2Sschwarze 399728687875Sschwarze free(buf->buf); 39983dc5225dSschwarze buf->buf = mandoc_strdup(r->current_string); 39993dc5225dSschwarze buf->sz = strlen(buf->buf) + 1; 4000c65a03d4Sschwarze *offs = 0; 4001e53bb406Sschwarze 400261dc5079Sschwarze return buf->buf[buf->sz - 2] == '\n' ? 4003b7f92c5fSschwarze ROFF_REPARSE | ROFF_USERCALL : ROFF_IGN | ROFF_APPEND; 4004e53bb406Sschwarze } 4005e53bb406Sschwarze 40067e291f10Sschwarze /* 40077e291f10Sschwarze * Calling a high-level macro that was renamed with .rn. 40087e291f10Sschwarze * r->current_string has already been set up by roff_parse(). 40097e291f10Sschwarze */ 4010b7f92c5fSschwarze static int 40117e291f10Sschwarze roff_renamed(ROFF_ARGS) 40127e291f10Sschwarze { 40137e291f10Sschwarze char *nbuf; 40147e291f10Sschwarze 401507530f0aSschwarze buf->sz = mandoc_asprintf(&nbuf, ".%s%s%s", r->current_string, 401607530f0aSschwarze buf->buf[pos] == '\0' ? "" : " ", buf->buf + pos) + 1; 40177e291f10Sschwarze free(buf->buf); 40187e291f10Sschwarze buf->buf = nbuf; 4019d0365455Sschwarze *offs = 0; 40207e291f10Sschwarze return ROFF_CONT; 40217e291f10Sschwarze } 40227e291f10Sschwarze 4023aac18afaSschwarze /* 4024aac18afaSschwarze * Measure the length in bytes of the roff identifier at *cpp 4025aac18afaSschwarze * and advance the pointer to the next word. 4026aac18afaSschwarze */ 402759b3009eSschwarze static size_t 40283b5bd91eSschwarze roff_getname(char **cpp, int ln, int pos) 402980364048Sschwarze { 403080364048Sschwarze char *name, *cp; 4031a72149c5Sschwarze int namesz, inam, iend; 403280364048Sschwarze 403380364048Sschwarze name = *cpp; 4034aac18afaSschwarze if (*name == '\0') 4035526e306bSschwarze return 0; 403680364048Sschwarze 4037aac18afaSschwarze /* Advance cp to the byte after the end of the name. */ 4038aac18afaSschwarze 4039a72149c5Sschwarze cp = name; 4040a72149c5Sschwarze namesz = 0; 4041a72149c5Sschwarze for (;;) { 40427ad9cfd6Sschwarze if (*cp == '\0') 404359b3009eSschwarze break; 40447ad9cfd6Sschwarze if (*cp == ' ' || *cp == '\t') { 40457ad9cfd6Sschwarze cp++; 40467ad9cfd6Sschwarze break; 40477ad9cfd6Sschwarze } 4048a72149c5Sschwarze if (*cp != '\\') { 4049a72149c5Sschwarze if (name + namesz < cp) { 4050a72149c5Sschwarze name[namesz] = *cp; 4051a72149c5Sschwarze *cp = ' '; 4052a72149c5Sschwarze } 4053a72149c5Sschwarze namesz++; 4054a72149c5Sschwarze cp++; 405580364048Sschwarze continue; 4056a72149c5Sschwarze } 4057aac18afaSschwarze if (cp[1] == '{' || cp[1] == '}') 4058b34deaa1Sschwarze break; 4059a72149c5Sschwarze if (roff_escape(cp, 0, 0, NULL, &inam, 4060a72149c5Sschwarze NULL, NULL, &iend) != ESCAPE_UNDEF) { 4061a5a5f808Sschwarze mandoc_msg(MANDOCERR_NAMESC, ln, pos, 4062a72149c5Sschwarze "%.*s%.*s", namesz, name, iend, cp); 4063a72149c5Sschwarze cp += iend; 406459b3009eSschwarze break; 406580364048Sschwarze } 406680364048Sschwarze 4067a72149c5Sschwarze /* 4068a72149c5Sschwarze * In an identifier, \\, \., \G and so on 4069a72149c5Sschwarze * are reduced to \, ., G and so on, 4070a72149c5Sschwarze * vaguely similar to copy mode. 4071a72149c5Sschwarze */ 4072a72149c5Sschwarze 4073a72149c5Sschwarze name[namesz++] = cp[inam]; 4074a72149c5Sschwarze while (iend--) { 4075a72149c5Sschwarze if (cp >= name + namesz) 4076a72149c5Sschwarze *cp = ' '; 4077a72149c5Sschwarze cp++; 4078a72149c5Sschwarze } 4079a72149c5Sschwarze } 4080a72149c5Sschwarze 408180364048Sschwarze /* Read past spaces. */ 4082aac18afaSschwarze 4083aac18afaSschwarze while (*cp == ' ') 408480364048Sschwarze cp++; 408580364048Sschwarze 408680364048Sschwarze *cpp = cp; 4087526e306bSschwarze return namesz; 408880364048Sschwarze } 408980364048Sschwarze 4090e53bb406Sschwarze /* 4091e53bb406Sschwarze * Store *string into the user-defined string called *name. 4092e53bb406Sschwarze * To clear an existing entry, call with (*r, *name, NULL, 0). 40930c05b9caSschwarze * append == 0: replace mode 40940c05b9caSschwarze * append == 1: single-line append mode 40950c05b9caSschwarze * append == 2: multiline append mode, append '\n' after each call 4096e53bb406Sschwarze */ 4097769ee804Sschwarze static void 4098e53bb406Sschwarze roff_setstr(struct roff *r, const char *name, const char *string, 40990c05b9caSschwarze int append) 4100b49ce91bSschwarze { 410107530f0aSschwarze size_t namesz; 4102b49ce91bSschwarze 410307530f0aSschwarze namesz = strlen(name); 410407530f0aSschwarze roff_setstrn(&r->strtab, name, namesz, string, 41050c05b9caSschwarze string ? strlen(string) : 0, append); 410607530f0aSschwarze roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); 410782475fc3Sschwarze } 410882475fc3Sschwarze 410904e980cbSschwarze static void 411004e980cbSschwarze roff_setstrn(struct roffkv **r, const char *name, size_t namesz, 41110c05b9caSschwarze const char *string, size_t stringsz, int append) 411204e980cbSschwarze { 411304e980cbSschwarze struct roffkv *n; 411404e980cbSschwarze char *c; 411504e980cbSschwarze int i; 411604e980cbSschwarze size_t oldch, newch; 411704e980cbSschwarze 4118e53bb406Sschwarze /* Search for an existing string with the same name. */ 411904e980cbSschwarze n = *r; 412004e980cbSschwarze 4121a8590a9aSschwarze while (n && (namesz != n->key.sz || 4122a8590a9aSschwarze strncmp(n->key.p, name, namesz))) 4123b49ce91bSschwarze n = n->next; 4124769ee804Sschwarze 4125769ee804Sschwarze if (NULL == n) { 4126e53bb406Sschwarze /* Create a new string table entry. */ 412704e980cbSschwarze n = mandoc_malloc(sizeof(struct roffkv)); 412804e980cbSschwarze n->key.p = mandoc_strndup(name, namesz); 412904e980cbSschwarze n->key.sz = namesz; 413004e980cbSschwarze n->val.p = NULL; 413104e980cbSschwarze n->val.sz = 0; 413204e980cbSschwarze n->next = *r; 413304e980cbSschwarze *r = n; 41340c05b9caSschwarze } else if (0 == append) { 413504e980cbSschwarze free(n->val.p); 413604e980cbSschwarze n->val.p = NULL; 413704e980cbSschwarze n->val.sz = 0; 4138e53bb406Sschwarze } 4139769ee804Sschwarze 4140e53bb406Sschwarze if (NULL == string) 4141e53bb406Sschwarze return; 4142e53bb406Sschwarze 4143e53bb406Sschwarze /* 4144e53bb406Sschwarze * One additional byte for the '\n' in multiline mode, 4145e53bb406Sschwarze * and one for the terminating '\0'. 4146e53bb406Sschwarze */ 41470c05b9caSschwarze newch = stringsz + (1 < append ? 2u : 1u); 414804e980cbSschwarze 414904e980cbSschwarze if (NULL == n->val.p) { 415004e980cbSschwarze n->val.p = mandoc_malloc(newch); 415104e980cbSschwarze *n->val.p = '\0'; 4152e53bb406Sschwarze oldch = 0; 4153e53bb406Sschwarze } else { 415404e980cbSschwarze oldch = n->val.sz; 415504e980cbSschwarze n->val.p = mandoc_realloc(n->val.p, oldch + newch); 4156e53bb406Sschwarze } 4157e53bb406Sschwarze 4158e53bb406Sschwarze /* Skip existing content in the destination buffer. */ 415904e980cbSschwarze c = n->val.p + (int)oldch; 4160e53bb406Sschwarze 4161e53bb406Sschwarze /* Append new content to the destination buffer. */ 416204e980cbSschwarze i = 0; 416304e980cbSschwarze while (i < (int)stringsz) { 4164e53bb406Sschwarze /* 4165e53bb406Sschwarze * Rudimentary roff copy mode: 4166e53bb406Sschwarze * Handle escaped backslashes. 4167e53bb406Sschwarze */ 416804e980cbSschwarze if ('\\' == string[i] && '\\' == string[i + 1]) 416904e980cbSschwarze i++; 417004e980cbSschwarze *c++ = string[i++]; 4171e53bb406Sschwarze } 4172e53bb406Sschwarze 4173e53bb406Sschwarze /* Append terminating bytes. */ 41740c05b9caSschwarze if (1 < append) 4175e53bb406Sschwarze *c++ = '\n'; 417604e980cbSschwarze 4177e53bb406Sschwarze *c = '\0'; 417804e980cbSschwarze n->val.sz = (int)(c - n->val.p); 4179b49ce91bSschwarze } 4180b49ce91bSschwarze 4181769ee804Sschwarze static const char * 41820cdab9e9Sschwarze roff_getstrn(struct roff *r, const char *name, size_t len, 418307530f0aSschwarze int *deftype) 4184b49ce91bSschwarze { 418504e980cbSschwarze const struct roffkv *n; 41860cdab9e9Sschwarze int found, i; 418707530f0aSschwarze enum roff_tok tok; 4188b49ce91bSschwarze 41890cdab9e9Sschwarze found = 0; 419007530f0aSschwarze for (n = r->strtab; n != NULL; n = n->next) { 41910cdab9e9Sschwarze if (strncmp(name, n->key.p, len) != 0 || 41920cdab9e9Sschwarze n->key.p[len] != '\0' || n->val.p == NULL) 41930cdab9e9Sschwarze continue; 41940cdab9e9Sschwarze if (*deftype & ROFFDEF_USER) { 419507530f0aSschwarze *deftype = ROFFDEF_USER; 4196526e306bSschwarze return n->val.p; 41970cdab9e9Sschwarze } else { 41980cdab9e9Sschwarze found = 1; 41990cdab9e9Sschwarze break; 420007530f0aSschwarze } 420107530f0aSschwarze } 420207530f0aSschwarze for (n = r->rentab; n != NULL; n = n->next) { 42030cdab9e9Sschwarze if (strncmp(name, n->key.p, len) != 0 || 42040cdab9e9Sschwarze n->key.p[len] != '\0' || n->val.p == NULL) 42050cdab9e9Sschwarze continue; 42060cdab9e9Sschwarze if (*deftype & ROFFDEF_REN) { 420707530f0aSschwarze *deftype = ROFFDEF_REN; 420807530f0aSschwarze return n->val.p; 42090cdab9e9Sschwarze } else { 42100cdab9e9Sschwarze found = 1; 42110cdab9e9Sschwarze break; 421207530f0aSschwarze } 421307530f0aSschwarze } 42140cdab9e9Sschwarze for (i = 0; i < PREDEFS_MAX; i++) { 42150cdab9e9Sschwarze if (strncmp(name, predefs[i].name, len) != 0 || 42160cdab9e9Sschwarze predefs[i].name[len] != '\0') 42170cdab9e9Sschwarze continue; 42180cdab9e9Sschwarze if (*deftype & ROFFDEF_PRE) { 42190cdab9e9Sschwarze *deftype = ROFFDEF_PRE; 42200cdab9e9Sschwarze return predefs[i].str; 42210cdab9e9Sschwarze } else { 42220cdab9e9Sschwarze found = 1; 42230cdab9e9Sschwarze break; 422407530f0aSschwarze } 42250cdab9e9Sschwarze } 42266b86842eSschwarze if (r->man->meta.macroset != MACROSET_MAN) { 422707530f0aSschwarze for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) { 42280cdab9e9Sschwarze if (strncmp(name, roff_name[tok], len) != 0 || 42290cdab9e9Sschwarze roff_name[tok][len] != '\0') 42300cdab9e9Sschwarze continue; 42310cdab9e9Sschwarze if (*deftype & ROFFDEF_STD) { 423207530f0aSschwarze *deftype = ROFFDEF_STD; 4233526e306bSschwarze return NULL; 42340cdab9e9Sschwarze } else { 42350cdab9e9Sschwarze found = 1; 42360cdab9e9Sschwarze break; 4237b49ce91bSschwarze } 423807530f0aSschwarze } 423907530f0aSschwarze } 42406b86842eSschwarze if (r->man->meta.macroset != MACROSET_MDOC) { 424107530f0aSschwarze for (tok = MAN_TH; tok < MAN_MAX; tok++) { 42420cdab9e9Sschwarze if (strncmp(name, roff_name[tok], len) != 0 || 42430cdab9e9Sschwarze roff_name[tok][len] != '\0') 42440cdab9e9Sschwarze continue; 42450cdab9e9Sschwarze if (*deftype & ROFFDEF_STD) { 424607530f0aSschwarze *deftype = ROFFDEF_STD; 424707530f0aSschwarze return NULL; 42480cdab9e9Sschwarze } else { 42490cdab9e9Sschwarze found = 1; 42500cdab9e9Sschwarze break; 425107530f0aSschwarze } 425207530f0aSschwarze } 425307530f0aSschwarze } 42540cdab9e9Sschwarze 42550cdab9e9Sschwarze if (found == 0 && *deftype != ROFFDEF_ANY) { 42560cdab9e9Sschwarze if (*deftype & ROFFDEF_REN) { 42570cdab9e9Sschwarze /* 42580cdab9e9Sschwarze * This might still be a request, 42590cdab9e9Sschwarze * so do not treat it as undefined yet. 42600cdab9e9Sschwarze */ 42610cdab9e9Sschwarze *deftype = ROFFDEF_UNDEF; 42620cdab9e9Sschwarze return NULL; 426307530f0aSschwarze } 42640cdab9e9Sschwarze 42650cdab9e9Sschwarze /* Using an undefined string defines it to be empty. */ 42660cdab9e9Sschwarze 42670cdab9e9Sschwarze roff_setstrn(&r->strtab, name, len, "", 0, 0); 42680cdab9e9Sschwarze roff_setstrn(&r->rentab, name, len, NULL, 0, 0); 42690cdab9e9Sschwarze } 42700cdab9e9Sschwarze 427107530f0aSschwarze *deftype = 0; 42727e291f10Sschwarze return NULL; 42737e291f10Sschwarze } 42747e291f10Sschwarze 4275769ee804Sschwarze static void 427604e980cbSschwarze roff_freestr(struct roffkv *r) 4277b49ce91bSschwarze { 427804e980cbSschwarze struct roffkv *n, *nn; 4279b49ce91bSschwarze 428004e980cbSschwarze for (n = r; n; n = nn) { 428104e980cbSschwarze free(n->key.p); 428204e980cbSschwarze free(n->val.p); 4283b49ce91bSschwarze nn = n->next; 4284b49ce91bSschwarze free(n); 4285b49ce91bSschwarze } 4286b49ce91bSschwarze } 42872791bd1cSschwarze 4288fa2127f9Sschwarze /* --- accessors and utility functions ------------------------------------ */ 4289fa2127f9Sschwarze 429004e980cbSschwarze /* 429104e980cbSschwarze * Duplicate an input string, making the appropriate character 429204e980cbSschwarze * conversations (as stipulated by `tr') along the way. 429304e980cbSschwarze * Returns a heap-allocated string with all the replacements made. 429404e980cbSschwarze */ 429504e980cbSschwarze char * 429604e980cbSschwarze roff_strdup(const struct roff *r, const char *p) 429704e980cbSschwarze { 429804e980cbSschwarze const struct roffkv *cp; 429904e980cbSschwarze char *res; 430004e980cbSschwarze const char *pp; 430104e980cbSschwarze size_t ssz, sz; 430204e980cbSschwarze enum mandoc_esc esc; 430304e980cbSschwarze 430404e980cbSschwarze if (NULL == r->xmbtab && NULL == r->xtab) 4305526e306bSschwarze return mandoc_strdup(p); 430604e980cbSschwarze else if ('\0' == *p) 4307526e306bSschwarze return mandoc_strdup(""); 430804e980cbSschwarze 430904e980cbSschwarze /* 431004e980cbSschwarze * Step through each character looking for term matches 431104e980cbSschwarze * (remember that a `tr' can be invoked with an escape, which is 431204e980cbSschwarze * a glyph but the escape is multi-character). 431304e980cbSschwarze * We only do this if the character hash has been initialised 431404e980cbSschwarze * and the string is >0 length. 431504e980cbSschwarze */ 431604e980cbSschwarze 431704e980cbSschwarze res = NULL; 431804e980cbSschwarze ssz = 0; 431904e980cbSschwarze 432004e980cbSschwarze while ('\0' != *p) { 4321972cfc25Sschwarze assert((unsigned int)*p < 128); 4322972cfc25Sschwarze if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) { 432304e980cbSschwarze sz = r->xtab[(int)*p].sz; 432404e980cbSschwarze res = mandoc_realloc(res, ssz + sz + 1); 432504e980cbSschwarze memcpy(res + ssz, r->xtab[(int)*p].p, sz); 432604e980cbSschwarze ssz += sz; 432704e980cbSschwarze p++; 432804e980cbSschwarze continue; 432904e980cbSschwarze } else if ('\\' != *p) { 433004e980cbSschwarze res = mandoc_realloc(res, ssz + 2); 433104e980cbSschwarze res[ssz++] = *p++; 433204e980cbSschwarze continue; 433304e980cbSschwarze } 433404e980cbSschwarze 433504e980cbSschwarze /* Search for term matches. */ 433604e980cbSschwarze for (cp = r->xmbtab; cp; cp = cp->next) 433704e980cbSschwarze if (0 == strncmp(p, cp->key.p, cp->key.sz)) 433804e980cbSschwarze break; 433904e980cbSschwarze 434004e980cbSschwarze if (NULL != cp) { 434104e980cbSschwarze /* 434204e980cbSschwarze * A match has been found. 434304e980cbSschwarze * Append the match to the array and move 434404e980cbSschwarze * forward by its keysize. 434504e980cbSschwarze */ 434649aff9f8Sschwarze res = mandoc_realloc(res, 434749aff9f8Sschwarze ssz + cp->val.sz + 1); 434804e980cbSschwarze memcpy(res + ssz, cp->val.p, cp->val.sz); 434904e980cbSschwarze ssz += cp->val.sz; 435004e980cbSschwarze p += (int)cp->key.sz; 435104e980cbSschwarze continue; 435204e980cbSschwarze } 435304e980cbSschwarze 435404e980cbSschwarze /* 435504e980cbSschwarze * Handle escapes carefully: we need to copy 435604e980cbSschwarze * over just the escape itself, or else we might 435704e980cbSschwarze * do replacements within the escape itself. 435804e980cbSschwarze * Make sure to pass along the bogus string. 435904e980cbSschwarze */ 436004e980cbSschwarze pp = p++; 436104e980cbSschwarze esc = mandoc_escape(&p, NULL, NULL); 436204e980cbSschwarze if (ESCAPE_ERROR == esc) { 436304e980cbSschwarze sz = strlen(pp); 436404e980cbSschwarze res = mandoc_realloc(res, ssz + sz + 1); 436504e980cbSschwarze memcpy(res + ssz, pp, sz); 436604e980cbSschwarze break; 436704e980cbSschwarze } 436804e980cbSschwarze /* 436904e980cbSschwarze * We bail out on bad escapes. 437004e980cbSschwarze * No need to warn: we already did so when 4371e94357f9Sschwarze * roff_expand() was called. 437204e980cbSschwarze */ 437304e980cbSschwarze sz = (int)(p - pp); 437404e980cbSschwarze res = mandoc_realloc(res, ssz + sz + 1); 437504e980cbSschwarze memcpy(res + ssz, pp, sz); 437604e980cbSschwarze ssz += sz; 437704e980cbSschwarze } 437804e980cbSschwarze 437904e980cbSschwarze res[(int)ssz] = '\0'; 4380526e306bSschwarze return res; 438104e980cbSschwarze } 43821e6d02e2Sschwarze 4383f0d7487dSschwarze int 4384f0d7487dSschwarze roff_getformat(const struct roff *r) 4385f0d7487dSschwarze { 4386f0d7487dSschwarze 4387526e306bSschwarze return r->format; 4388f0d7487dSschwarze } 4389f0d7487dSschwarze 43901e6d02e2Sschwarze /* 43911e6d02e2Sschwarze * Find out whether a line is a macro line or not. 43921e6d02e2Sschwarze * If it is, adjust the current position and return one; if it isn't, 43931e6d02e2Sschwarze * return zero and don't change the current position. 43941e6d02e2Sschwarze * If the control character has been set with `.cc', then let that grain 43951e6d02e2Sschwarze * precedence. 4396d9a51c35Sjmc * This is slightly contrary to groff, where using the non-breaking 43971e6d02e2Sschwarze * control character when `cc' has been invoked will cause the 43981e6d02e2Sschwarze * non-breaking macro contents to be printed verbatim. 43991e6d02e2Sschwarze */ 44001e6d02e2Sschwarze int 44011e6d02e2Sschwarze roff_getcontrol(const struct roff *r, const char *cp, int *ppos) 44021e6d02e2Sschwarze { 44031e6d02e2Sschwarze int pos; 44041e6d02e2Sschwarze 44051e6d02e2Sschwarze pos = *ppos; 44061e6d02e2Sschwarze 4407be477484Sschwarze if (r->control != '\0' && cp[pos] == r->control) 44081e6d02e2Sschwarze pos++; 4409be477484Sschwarze else if (r->control != '\0') 4410526e306bSschwarze return 0; 44111e6d02e2Sschwarze else if ('\\' == cp[pos] && '.' == cp[pos + 1]) 44121e6d02e2Sschwarze pos += 2; 44131e6d02e2Sschwarze else if ('.' == cp[pos] || '\'' == cp[pos]) 44141e6d02e2Sschwarze pos++; 44151e6d02e2Sschwarze else 4416526e306bSschwarze return 0; 44171e6d02e2Sschwarze 44181e6d02e2Sschwarze while (' ' == cp[pos] || '\t' == cp[pos]) 44191e6d02e2Sschwarze pos++; 44201e6d02e2Sschwarze 44211e6d02e2Sschwarze *ppos = pos; 4422526e306bSschwarze return 1; 44231e6d02e2Sschwarze } 4424