1322249Sbapt/* $Id: roff.c,v 1.324 2017/07/14 17:16:16 schwarze Exp $ */ 2241675Suqs/* 3294113Sbapt * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4316420Sbapt * Copyright (c) 2010-2015, 2017 Ingo Schwarze <schwarze@openbsd.org> 5241675Suqs * 6241675Suqs * Permission to use, copy, modify, and distribute this software for any 7241675Suqs * purpose with or without fee is hereby granted, provided that the above 8241675Suqs * copyright notice and this permission notice appear in all copies. 9241675Suqs * 10241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17241675Suqs */ 18241675Suqs#include "config.h" 19241675Suqs 20275432Sbapt#include <sys/types.h> 21275432Sbapt 22241675Suqs#include <assert.h> 23241675Suqs#include <ctype.h> 24279527Sbapt#include <limits.h> 25322249Sbapt#include <stddef.h> 26322249Sbapt#include <stdint.h> 27261344Suqs#include <stdio.h> 28241675Suqs#include <stdlib.h> 29241675Suqs#include <string.h> 30241675Suqs 31241675Suqs#include "mandoc.h" 32274880Sbapt#include "mandoc_aux.h" 33322249Sbapt#include "mandoc_ohash.h" 34294113Sbapt#include "roff.h" 35275432Sbapt#include "libmandoc.h" 36294113Sbapt#include "roff_int.h" 37241675Suqs#include "libroff.h" 38241675Suqs 39241675Suqs/* Maximum number of string expansions per line, to break infinite loops. */ 40241675Suqs#define EXPAND_LIMIT 1000 41241675Suqs 42322249Sbapt/* Types of definitions of macros and strings. */ 43322249Sbapt#define ROFFDEF_USER (1 << 1) /* User-defined. */ 44322249Sbapt#define ROFFDEF_PRE (1 << 2) /* Predefined. */ 45322249Sbapt#define ROFFDEF_REN (1 << 3) /* Renamed standard macro. */ 46322249Sbapt#define ROFFDEF_STD (1 << 4) /* mdoc(7) or man(7) macro. */ 47322249Sbapt#define ROFFDEF_ANY (ROFFDEF_USER | ROFFDEF_PRE | \ 48322249Sbapt ROFFDEF_REN | ROFFDEF_STD) 49322249Sbapt 50294113Sbapt/* --- data types --------------------------------------------------------- */ 51294113Sbapt 52241675Suqs/* 53241675Suqs * An incredibly-simple string buffer. 54241675Suqs */ 55241675Suqsstruct roffstr { 56241675Suqs char *p; /* nil-terminated buffer */ 57241675Suqs size_t sz; /* saved strlen(p) */ 58241675Suqs}; 59241675Suqs 60241675Suqs/* 61241675Suqs * A key-value roffstr pair as part of a singly-linked list. 62241675Suqs */ 63241675Suqsstruct roffkv { 64241675Suqs struct roffstr key; 65241675Suqs struct roffstr val; 66241675Suqs struct roffkv *next; /* next in list */ 67241675Suqs}; 68241675Suqs 69261344Suqs/* 70261344Suqs * A single number register as part of a singly-linked list. 71261344Suqs */ 72261344Suqsstruct roffreg { 73261344Suqs struct roffstr key; 74261344Suqs int val; 75261344Suqs struct roffreg *next; 76261344Suqs}; 77261344Suqs 78322249Sbapt/* 79322249Sbapt * Association of request and macro names with token IDs. 80322249Sbapt */ 81322249Sbaptstruct roffreq { 82322249Sbapt enum roff_tok tok; 83322249Sbapt char name[]; 84322249Sbapt}; 85322249Sbapt 86241675Suqsstruct roff { 87241675Suqs struct mparse *parse; /* parse point */ 88322249Sbapt struct roff_man *man; /* mdoc or man parser */ 89241675Suqs struct roffnode *last; /* leaf of stack */ 90274880Sbapt int *rstack; /* stack of inverted `ie' values */ 91322249Sbapt struct ohash *reqtab; /* request lookup table */ 92261344Suqs struct roffreg *regtab; /* number registers */ 93241675Suqs struct roffkv *strtab; /* user-defined strings & macros */ 94322249Sbapt struct roffkv *rentab; /* renamed strings & macros */ 95241675Suqs struct roffkv *xmbtab; /* multi-byte trans table (`tr') */ 96241675Suqs struct roffstr *xtab; /* single-byte trans table (`tr') */ 97241675Suqs const char *current_string; /* value of last called user macro */ 98241675Suqs struct tbl_node *first_tbl; /* first table parsed */ 99241675Suqs struct tbl_node *last_tbl; /* last table parsed */ 100241675Suqs struct tbl_node *tbl; /* current table being parsed */ 101322249Sbapt struct eqn_node *last_eqn; /* equation parser */ 102322249Sbapt struct eqn_node *eqn; /* active equation parser */ 103275432Sbapt int eqn_inline; /* current equation is inline */ 104274880Sbapt int options; /* parse options */ 105274880Sbapt int rstacksz; /* current size limit of rstack */ 106274880Sbapt int rstackpos; /* position in rstack */ 107275432Sbapt int format; /* current file in mdoc or man format */ 108294113Sbapt int argc; /* number of args of the last macro */ 109274880Sbapt char control; /* control character */ 110322249Sbapt char escape; /* escape character */ 111241675Suqs}; 112241675Suqs 113241675Suqsstruct roffnode { 114322249Sbapt enum roff_tok tok; /* type of node */ 115241675Suqs struct roffnode *parent; /* up one in stack */ 116241675Suqs int line; /* parse line */ 117241675Suqs int col; /* parse col */ 118241675Suqs char *name; /* node name, e.g. macro name */ 119241675Suqs char *end; /* end-rules: custom token */ 120241675Suqs int endspan; /* end-rules: next-line or infty */ 121274880Sbapt int rule; /* current evaluation rule */ 122241675Suqs}; 123241675Suqs 124241675Suqs#define ROFF_ARGS struct roff *r, /* parse ctx */ \ 125322249Sbapt enum roff_tok tok, /* tok of macro */ \ 126275432Sbapt struct buf *buf, /* input buffer */ \ 127241675Suqs int ln, /* parse line */ \ 128241675Suqs int ppos, /* original pos in buffer */ \ 129241675Suqs int pos, /* current pos in buffer */ \ 130241675Suqs int *offs /* reset offset of buffer data */ 131241675Suqs 132241675Suqstypedef enum rofferr (*roffproc)(ROFF_ARGS); 133241675Suqs 134241675Suqsstruct roffmac { 135241675Suqs roffproc proc; /* process new macro */ 136241675Suqs roffproc text; /* process as child text of macro */ 137241675Suqs roffproc sub; /* process as child of macro */ 138241675Suqs int flags; 139241675Suqs#define ROFFMAC_STRUCT (1 << 0) /* always interpret */ 140241675Suqs}; 141241675Suqs 142241675Suqsstruct predef { 143241675Suqs const char *name; /* predefined input name */ 144241675Suqs const char *str; /* replacement symbol */ 145241675Suqs}; 146241675Suqs 147241675Suqs#define PREDEF(__name, __str) \ 148241675Suqs { (__name), (__str) }, 149241675Suqs 150294113Sbapt/* --- function prototypes ------------------------------------------------ */ 151294113Sbapt 152241675Suqsstatic void roffnode_cleanscope(struct roff *); 153241675Suqsstatic void roffnode_pop(struct roff *); 154322249Sbaptstatic void roffnode_push(struct roff *, enum roff_tok, 155241675Suqs const char *, int, int); 156322249Sbaptstatic void roff_addtbl(struct roff_man *, struct tbl_node *); 157322249Sbaptstatic enum rofferr roff_als(ROFF_ARGS); 158241675Suqsstatic enum rofferr roff_block(ROFF_ARGS); 159241675Suqsstatic enum rofferr roff_block_text(ROFF_ARGS); 160241675Suqsstatic enum rofferr roff_block_sub(ROFF_ARGS); 161322249Sbaptstatic enum rofferr roff_br(ROFF_ARGS); 162241675Suqsstatic enum rofferr roff_cblock(ROFF_ARGS); 163261344Suqsstatic enum rofferr roff_cc(ROFF_ARGS); 164274880Sbaptstatic void roff_ccond(struct roff *, int, int); 165241675Suqsstatic enum rofferr roff_cond(ROFF_ARGS); 166241675Suqsstatic enum rofferr roff_cond_text(ROFF_ARGS); 167241675Suqsstatic enum rofferr roff_cond_sub(ROFF_ARGS); 168241675Suqsstatic enum rofferr roff_ds(ROFF_ARGS); 169322249Sbaptstatic enum rofferr roff_ec(ROFF_ARGS); 170322249Sbaptstatic enum rofferr roff_eo(ROFF_ARGS); 171275432Sbaptstatic enum rofferr roff_eqndelim(struct roff *, struct buf *, int); 172294113Sbaptstatic int roff_evalcond(struct roff *r, int, char *, int *); 173275432Sbaptstatic int roff_evalnum(struct roff *, int, 174275432Sbapt const char *, int *, int *, int); 175275432Sbaptstatic int roff_evalpar(struct roff *, int, 176279527Sbapt const char *, int *, int *, int); 177274880Sbaptstatic int roff_evalstrcond(const char *, int *); 178241675Suqsstatic void roff_free1(struct roff *); 179261344Suqsstatic void roff_freereg(struct roffreg *); 180241675Suqsstatic void roff_freestr(struct roffkv *); 181274880Sbaptstatic size_t roff_getname(struct roff *, char **, int, int); 182279527Sbaptstatic int roff_getnum(const char *, int *, int *, int); 183261344Suqsstatic int roff_getop(const char *, int *, char *); 184261344Suqsstatic int roff_getregn(const struct roff *, 185261344Suqs const char *, size_t); 186294113Sbaptstatic int roff_getregro(const struct roff *, 187294113Sbapt const char *name); 188274880Sbaptstatic const char *roff_getstrn(const struct roff *, 189322249Sbapt const char *, size_t, int *); 190294113Sbaptstatic int roff_hasregn(const struct roff *, 191294113Sbapt const char *, size_t); 192279527Sbaptstatic enum rofferr roff_insec(ROFF_ARGS); 193261344Suqsstatic enum rofferr roff_it(ROFF_ARGS); 194241675Suqsstatic enum rofferr roff_line_ignore(ROFF_ARGS); 195294113Sbaptstatic void roff_man_alloc1(struct roff_man *); 196294113Sbaptstatic void roff_man_free1(struct roff_man *); 197322249Sbaptstatic enum rofferr roff_manyarg(ROFF_ARGS); 198241675Suqsstatic enum rofferr roff_nr(ROFF_ARGS); 199322249Sbaptstatic enum rofferr roff_onearg(ROFF_ARGS); 200322249Sbaptstatic enum roff_tok roff_parse(struct roff *, char *, int *, 201274880Sbapt int, int); 202322249Sbaptstatic enum rofferr roff_parsetext(struct roff *, struct buf *, 203322249Sbapt int, int *); 204322249Sbaptstatic enum rofferr roff_renamed(ROFF_ARGS); 205275432Sbaptstatic enum rofferr roff_res(struct roff *, struct buf *, int, int); 206241675Suqsstatic enum rofferr roff_rm(ROFF_ARGS); 207322249Sbaptstatic enum rofferr roff_rn(ROFF_ARGS); 208274880Sbaptstatic enum rofferr roff_rr(ROFF_ARGS); 209241675Suqsstatic void roff_setstr(struct roff *, 210241675Suqs const char *, const char *, int); 211274880Sbaptstatic void roff_setstrn(struct roffkv **, const char *, 212241675Suqs size_t, const char *, size_t, int); 213241675Suqsstatic enum rofferr roff_so(ROFF_ARGS); 214241675Suqsstatic enum rofferr roff_tr(ROFF_ARGS); 215261344Suqsstatic enum rofferr roff_Dd(ROFF_ARGS); 216241675Suqsstatic enum rofferr roff_TE(ROFF_ARGS); 217241675Suqsstatic enum rofferr roff_TS(ROFF_ARGS); 218241675Suqsstatic enum rofferr roff_EQ(ROFF_ARGS); 219241675Suqsstatic enum rofferr roff_EN(ROFF_ARGS); 220241675Suqsstatic enum rofferr roff_T_(ROFF_ARGS); 221279527Sbaptstatic enum rofferr roff_unsupp(ROFF_ARGS); 222241675Suqsstatic enum rofferr roff_userdef(ROFF_ARGS); 223241675Suqs 224294113Sbapt/* --- constant data ------------------------------------------------------ */ 225294113Sbapt 226279527Sbapt#define ROFFNUM_SCALE (1 << 0) /* Honour scaling in roff_getnum(). */ 227279527Sbapt#define ROFFNUM_WHITE (1 << 1) /* Skip whitespace in roff_evalnum(). */ 228279527Sbapt 229322249Sbaptconst char *__roff_name[MAN_MAX + 1] = { 230322249Sbapt "br", "ce", "ft", "ll", 231322249Sbapt "mc", "po", "rj", "sp", 232322249Sbapt "ta", "ti", NULL, 233322249Sbapt "ab", "ad", "af", "aln", 234322249Sbapt "als", "am", "am1", "ami", 235322249Sbapt "ami1", "as", "as1", "asciify", 236322249Sbapt "backtrace", "bd", "bleedat", "blm", 237322249Sbapt "box", "boxa", "bp", "BP", 238322249Sbapt "break", "breakchar", "brnl", "brp", 239322249Sbapt "brpnl", "c2", "cc", 240322249Sbapt "cf", "cflags", "ch", "char", 241322249Sbapt "chop", "class", "close", "CL", 242322249Sbapt "color", "composite", "continue", "cp", 243322249Sbapt "cropat", "cs", "cu", "da", 244322249Sbapt "dch", "Dd", "de", "de1", 245322249Sbapt "defcolor", "dei", "dei1", "device", 246322249Sbapt "devicem", "di", "do", "ds", 247322249Sbapt "ds1", "dwh", "dt", "ec", 248322249Sbapt "ecr", "ecs", "el", "em", 249322249Sbapt "EN", "eo", "EP", "EQ", 250322249Sbapt "errprint", "ev", "evc", "ex", 251322249Sbapt "fallback", "fam", "fc", "fchar", 252322249Sbapt "fcolor", "fdeferlig", "feature", "fkern", 253322249Sbapt "fl", "flig", "fp", "fps", 254322249Sbapt "fschar", "fspacewidth", "fspecial", "ftr", 255322249Sbapt "fzoom", "gcolor", "hc", "hcode", 256322249Sbapt "hidechar", "hla", "hlm", "hpf", 257322249Sbapt "hpfa", "hpfcode", "hw", "hy", 258322249Sbapt "hylang", "hylen", "hym", "hypp", 259322249Sbapt "hys", "ie", "if", "ig", 260322249Sbapt "index", "it", "itc", "IX", 261322249Sbapt "kern", "kernafter", "kernbefore", "kernpair", 262322249Sbapt "lc", "lc_ctype", "lds", "length", 263322249Sbapt "letadj", "lf", "lg", "lhang", 264322249Sbapt "linetabs", "lnr", "lnrf", "lpfx", 265322249Sbapt "ls", "lsm", "lt", 266322249Sbapt "mediasize", "minss", "mk", "mso", 267322249Sbapt "na", "ne", "nh", "nhychar", 268322249Sbapt "nm", "nn", "nop", "nr", 269322249Sbapt "nrf", "nroff", "ns", "nx", 270322249Sbapt "open", "opena", "os", "output", 271322249Sbapt "padj", "papersize", "pc", "pev", 272322249Sbapt "pi", "PI", "pl", "pm", 273322249Sbapt "pn", "pnr", "ps", 274322249Sbapt "psbb", "pshape", "pso", "ptr", 275322249Sbapt "pvs", "rchar", "rd", "recursionlimit", 276322249Sbapt "return", "rfschar", "rhang", 277322249Sbapt "rm", "rn", "rnn", "rr", 278322249Sbapt "rs", "rt", "schar", "sentchar", 279322249Sbapt "shc", "shift", "sizes", "so", 280322249Sbapt "spacewidth", "special", "spreadwarn", "ss", 281322249Sbapt "sty", "substring", "sv", "sy", 282322249Sbapt "T&", "tc", "TE", 283322249Sbapt "TH", "tkf", "tl", 284322249Sbapt "tm", "tm1", "tmc", "tr", 285322249Sbapt "track", "transchar", "trf", "trimat", 286322249Sbapt "trin", "trnt", "troff", "TS", 287322249Sbapt "uf", "ul", "unformat", "unwatch", 288322249Sbapt "unwatchn", "vpt", "vs", "warn", 289322249Sbapt "warnscale", "watch", "watchlength", "watchn", 290322249Sbapt "wh", "while", "write", "writec", 291322249Sbapt "writem", "xflag", ".", NULL, 292322249Sbapt NULL, "text", 293322249Sbapt "Dd", "Dt", "Os", "Sh", 294322249Sbapt "Ss", "Pp", "D1", "Dl", 295322249Sbapt "Bd", "Ed", "Bl", "El", 296322249Sbapt "It", "Ad", "An", "Ap", 297322249Sbapt "Ar", "Cd", "Cm", "Dv", 298322249Sbapt "Er", "Ev", "Ex", "Fa", 299322249Sbapt "Fd", "Fl", "Fn", "Ft", 300322249Sbapt "Ic", "In", "Li", "Nd", 301322249Sbapt "Nm", "Op", "Ot", "Pa", 302322249Sbapt "Rv", "St", "Va", "Vt", 303322249Sbapt "Xr", "%A", "%B", "%D", 304322249Sbapt "%I", "%J", "%N", "%O", 305322249Sbapt "%P", "%R", "%T", "%V", 306322249Sbapt "Ac", "Ao", "Aq", "At", 307322249Sbapt "Bc", "Bf", "Bo", "Bq", 308322249Sbapt "Bsx", "Bx", "Db", "Dc", 309322249Sbapt "Do", "Dq", "Ec", "Ef", 310322249Sbapt "Em", "Eo", "Fx", "Ms", 311322249Sbapt "No", "Ns", "Nx", "Ox", 312322249Sbapt "Pc", "Pf", "Po", "Pq", 313322249Sbapt "Qc", "Ql", "Qo", "Qq", 314322249Sbapt "Re", "Rs", "Sc", "So", 315322249Sbapt "Sq", "Sm", "Sx", "Sy", 316322249Sbapt "Tn", "Ux", "Xc", "Xo", 317322249Sbapt "Fo", "Fc", "Oo", "Oc", 318322249Sbapt "Bk", "Ek", "Bt", "Hf", 319322249Sbapt "Fr", "Ud", "Lb", "Lp", 320322249Sbapt "Lk", "Mt", "Brq", "Bro", 321322249Sbapt "Brc", "%C", "Es", "En", 322322249Sbapt "Dx", "%Q", "%U", "Ta", 323322249Sbapt NULL, 324322249Sbapt "TH", "SH", "SS", "TP", 325322249Sbapt "LP", "PP", "P", "IP", 326322249Sbapt "HP", "SM", "SB", "BI", 327322249Sbapt "IB", "BR", "RB", "R", 328322249Sbapt "B", "I", "IR", "RI", 329322249Sbapt "nf", "fi", 330322249Sbapt "RE", "RS", "DT", "UC", 331322249Sbapt "PD", "AT", "in", 332322249Sbapt "OP", "EX", "EE", "UR", 333322249Sbapt "UE", "MT", "ME", NULL 334241675Suqs}; 335322249Sbaptconst char *const *roff_name = __roff_name; 336241675Suqs 337322249Sbaptstatic struct roffmac roffs[TOKEN_NONE] = { 338322249Sbapt { roff_br, NULL, NULL, 0 }, /* br */ 339322249Sbapt { roff_onearg, NULL, NULL, 0 }, /* ce */ 340322249Sbapt { roff_onearg, NULL, NULL, 0 }, /* ft */ 341322249Sbapt { roff_onearg, NULL, NULL, 0 }, /* ll */ 342322249Sbapt { roff_onearg, NULL, NULL, 0 }, /* mc */ 343322249Sbapt { roff_onearg, NULL, NULL, 0 }, /* po */ 344322249Sbapt { roff_onearg, NULL, NULL, 0 }, /* rj */ 345322249Sbapt { roff_onearg, NULL, NULL, 0 }, /* sp */ 346322249Sbapt { roff_manyarg, NULL, NULL, 0 }, /* ta */ 347322249Sbapt { roff_onearg, NULL, NULL, 0 }, /* ti */ 348322249Sbapt { NULL, NULL, NULL, 0 }, /* ROFF_MAX */ 349322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* ab */ 350322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* ad */ 351322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* af */ 352322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* aln */ 353322249Sbapt { roff_als, NULL, NULL, 0 }, /* als */ 354322249Sbapt { roff_block, roff_block_text, roff_block_sub, 0 }, /* am */ 355322249Sbapt { roff_block, roff_block_text, roff_block_sub, 0 }, /* am1 */ 356322249Sbapt { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami */ 357322249Sbapt { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami1 */ 358322249Sbapt { roff_ds, NULL, NULL, 0 }, /* as */ 359322249Sbapt { roff_ds, NULL, NULL, 0 }, /* as1 */ 360322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* asciify */ 361322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* backtrace */ 362322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* bd */ 363322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* bleedat */ 364322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* blm */ 365322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* box */ 366322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* boxa */ 367322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* bp */ 368322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* BP */ 369322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* break */ 370322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* breakchar */ 371322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* brnl */ 372322249Sbapt { roff_br, NULL, NULL, 0 }, /* brp */ 373322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* brpnl */ 374322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* c2 */ 375322249Sbapt { roff_cc, NULL, NULL, 0 }, /* cc */ 376322249Sbapt { roff_insec, NULL, NULL, 0 }, /* cf */ 377322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* cflags */ 378322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* ch */ 379322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* char */ 380322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* chop */ 381322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* class */ 382322249Sbapt { roff_insec, NULL, NULL, 0 }, /* close */ 383322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* CL */ 384322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* color */ 385322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* composite */ 386322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* continue */ 387322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* cp */ 388322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* cropat */ 389322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* cs */ 390322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* cu */ 391322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* da */ 392322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* dch */ 393322249Sbapt { roff_Dd, NULL, NULL, 0 }, /* Dd */ 394322249Sbapt { roff_block, roff_block_text, roff_block_sub, 0 }, /* de */ 395322249Sbapt { roff_block, roff_block_text, roff_block_sub, 0 }, /* de1 */ 396322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* defcolor */ 397322249Sbapt { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei */ 398322249Sbapt { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei1 */ 399322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* device */ 400322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* devicem */ 401322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* di */ 402322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* do */ 403322249Sbapt { roff_ds, NULL, NULL, 0 }, /* ds */ 404322249Sbapt { roff_ds, NULL, NULL, 0 }, /* ds1 */ 405322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* dwh */ 406322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* dt */ 407322249Sbapt { roff_ec, NULL, NULL, 0 }, /* ec */ 408322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* ecr */ 409322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* ecs */ 410322249Sbapt { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* el */ 411322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* em */ 412322249Sbapt { roff_EN, NULL, NULL, 0 }, /* EN */ 413322249Sbapt { roff_eo, NULL, NULL, 0 }, /* eo */ 414322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* EP */ 415322249Sbapt { roff_EQ, NULL, NULL, 0 }, /* EQ */ 416322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* errprint */ 417322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* ev */ 418322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* evc */ 419322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* ex */ 420322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* fallback */ 421322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* fam */ 422322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* fc */ 423322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* fchar */ 424322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* fcolor */ 425322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* fdeferlig */ 426322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* feature */ 427322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* fkern */ 428322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* fl */ 429322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* flig */ 430322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* fp */ 431322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* fps */ 432322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* fschar */ 433322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* fspacewidth */ 434322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* fspecial */ 435322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* ftr */ 436322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* fzoom */ 437322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* gcolor */ 438322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hc */ 439322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hcode */ 440322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hidechar */ 441322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hla */ 442322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hlm */ 443322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hpf */ 444322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hpfa */ 445322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hpfcode */ 446322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hw */ 447322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hy */ 448322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hylang */ 449322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hylen */ 450322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hym */ 451322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hypp */ 452322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* hys */ 453322249Sbapt { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* ie */ 454322249Sbapt { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* if */ 455322249Sbapt { roff_block, roff_block_text, roff_block_sub, 0 }, /* ig */ 456322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* index */ 457322249Sbapt { roff_it, NULL, NULL, 0 }, /* it */ 458322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* itc */ 459322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* IX */ 460322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* kern */ 461322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* kernafter */ 462322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* kernbefore */ 463322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* kernpair */ 464322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* lc */ 465322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* lc_ctype */ 466322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* lds */ 467322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* length */ 468322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* letadj */ 469322249Sbapt { roff_insec, NULL, NULL, 0 }, /* lf */ 470322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* lg */ 471322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* lhang */ 472322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* linetabs */ 473322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* lnr */ 474322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* lnrf */ 475322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* lpfx */ 476322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* ls */ 477322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* lsm */ 478322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* lt */ 479322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* mediasize */ 480322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* minss */ 481322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* mk */ 482322249Sbapt { roff_insec, NULL, NULL, 0 }, /* mso */ 483322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* na */ 484322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* ne */ 485322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* nh */ 486322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* nhychar */ 487322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* nm */ 488322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* nn */ 489322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* nop */ 490322249Sbapt { roff_nr, NULL, NULL, 0 }, /* nr */ 491322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* nrf */ 492322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* nroff */ 493322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* ns */ 494322249Sbapt { roff_insec, NULL, NULL, 0 }, /* nx */ 495322249Sbapt { roff_insec, NULL, NULL, 0 }, /* open */ 496322249Sbapt { roff_insec, NULL, NULL, 0 }, /* opena */ 497322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* os */ 498322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* output */ 499322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* padj */ 500322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* papersize */ 501322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* pc */ 502322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* pev */ 503322249Sbapt { roff_insec, NULL, NULL, 0 }, /* pi */ 504322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* PI */ 505322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* pl */ 506322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* pm */ 507322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* pn */ 508322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* pnr */ 509322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* ps */ 510322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* psbb */ 511322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* pshape */ 512322249Sbapt { roff_insec, NULL, NULL, 0 }, /* pso */ 513322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* ptr */ 514322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* pvs */ 515322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* rchar */ 516322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* rd */ 517322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* recursionlimit */ 518322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* return */ 519322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* rfschar */ 520322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* rhang */ 521322249Sbapt { roff_rm, NULL, NULL, 0 }, /* rm */ 522322249Sbapt { roff_rn, NULL, NULL, 0 }, /* rn */ 523322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* rnn */ 524322249Sbapt { roff_rr, NULL, NULL, 0 }, /* rr */ 525322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* rs */ 526322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* rt */ 527322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* schar */ 528322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* sentchar */ 529322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* shc */ 530322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* shift */ 531322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* sizes */ 532322249Sbapt { roff_so, NULL, NULL, 0 }, /* so */ 533322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* spacewidth */ 534322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* special */ 535322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* spreadwarn */ 536322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* ss */ 537322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* sty */ 538322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* substring */ 539322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* sv */ 540322249Sbapt { roff_insec, NULL, NULL, 0 }, /* sy */ 541322249Sbapt { roff_T_, NULL, NULL, 0 }, /* T& */ 542322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* tc */ 543322249Sbapt { roff_TE, NULL, NULL, 0 }, /* TE */ 544322249Sbapt { roff_Dd, NULL, NULL, 0 }, /* TH */ 545322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* tkf */ 546322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* tl */ 547322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* tm */ 548322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* tm1 */ 549322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* tmc */ 550322249Sbapt { roff_tr, NULL, NULL, 0 }, /* tr */ 551322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* track */ 552322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* transchar */ 553322249Sbapt { roff_insec, NULL, NULL, 0 }, /* trf */ 554322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* trimat */ 555322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* trin */ 556322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* trnt */ 557322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* troff */ 558322249Sbapt { roff_TS, NULL, NULL, 0 }, /* TS */ 559322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* uf */ 560322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* ul */ 561322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* unformat */ 562322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* unwatch */ 563322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* unwatchn */ 564322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* vpt */ 565322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* vs */ 566322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* warn */ 567322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* warnscale */ 568322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* watch */ 569322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* watchlength */ 570322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* watchn */ 571322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* wh */ 572322249Sbapt { roff_unsupp, NULL, NULL, 0 }, /* while */ 573322249Sbapt { roff_insec, NULL, NULL, 0 }, /* write */ 574322249Sbapt { roff_insec, NULL, NULL, 0 }, /* writec */ 575322249Sbapt { roff_insec, NULL, NULL, 0 }, /* writem */ 576322249Sbapt { roff_line_ignore, NULL, NULL, 0 }, /* xflag */ 577322249Sbapt { roff_cblock, NULL, NULL, 0 }, /* . */ 578322249Sbapt { roff_renamed, NULL, NULL, 0 }, 579322249Sbapt { roff_userdef, NULL, NULL, 0 } 580261344Suqs}; 581261344Suqs 582241675Suqs/* Array of injected predefined strings. */ 583241675Suqs#define PREDEFS_MAX 38 584241675Suqsstatic const struct predef predefs[PREDEFS_MAX] = { 585241675Suqs#include "predefs.in" 586241675Suqs}; 587241675Suqs 588322249Sbaptstatic int roffce_lines; /* number of input lines to center */ 589322249Sbaptstatic struct roff_node *roffce_node; /* active request */ 590261344Suqsstatic int roffit_lines; /* number of lines to delay */ 591261344Suqsstatic char *roffit_macro; /* nil-terminated macro line */ 592261344Suqs 593274880Sbapt 594294113Sbapt/* --- request table ------------------------------------------------------ */ 595294113Sbapt 596322249Sbaptstruct ohash * 597322249Sbaptroffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok) 598241675Suqs{ 599322249Sbapt struct ohash *htab; 600322249Sbapt struct roffreq *req; 601322249Sbapt enum roff_tok tok; 602322249Sbapt size_t sz; 603322249Sbapt unsigned int slot; 604241675Suqs 605322249Sbapt htab = mandoc_malloc(sizeof(*htab)); 606322249Sbapt mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name)); 607241675Suqs 608322249Sbapt for (tok = mintok; tok < maxtok; tok++) { 609322249Sbapt if (roff_name[tok] == NULL) 610322249Sbapt continue; 611322249Sbapt sz = strlen(roff_name[tok]); 612322249Sbapt req = mandoc_malloc(sizeof(*req) + sz + 1); 613322249Sbapt req->tok = tok; 614322249Sbapt memcpy(req->name, roff_name[tok], sz + 1); 615322249Sbapt slot = ohash_qlookup(htab, req->name); 616322249Sbapt ohash_insert(htab, slot, req); 617241675Suqs } 618322249Sbapt return htab; 619241675Suqs} 620241675Suqs 621322249Sbaptvoid 622322249Sbaptroffhash_free(struct ohash *htab) 623241675Suqs{ 624322249Sbapt struct roffreq *req; 625322249Sbapt unsigned int slot; 626241675Suqs 627322249Sbapt if (htab == NULL) 628322249Sbapt return; 629322249Sbapt for (req = ohash_first(htab, &slot); req != NULL; 630322249Sbapt req = ohash_next(htab, &slot)) 631322249Sbapt free(req); 632322249Sbapt ohash_delete(htab); 633322249Sbapt free(htab); 634322249Sbapt} 635241675Suqs 636322249Sbaptenum roff_tok 637322249Sbaptroffhash_find(struct ohash *htab, const char *name, size_t sz) 638322249Sbapt{ 639322249Sbapt struct roffreq *req; 640322249Sbapt const char *end; 641241675Suqs 642322249Sbapt if (sz) { 643322249Sbapt end = name + sz; 644322249Sbapt req = ohash_find(htab, ohash_qlookupi(htab, name, &end)); 645322249Sbapt } else 646322249Sbapt req = ohash_find(htab, ohash_qlookup(htab, name)); 647322249Sbapt return req == NULL ? TOKEN_NONE : req->tok; 648241675Suqs} 649241675Suqs 650294113Sbapt/* --- stack of request blocks -------------------------------------------- */ 651294113Sbapt 652241675Suqs/* 653241675Suqs * Pop the current node off of the stack of roff instructions currently 654241675Suqs * pending. 655241675Suqs */ 656241675Suqsstatic void 657241675Suqsroffnode_pop(struct roff *r) 658241675Suqs{ 659241675Suqs struct roffnode *p; 660241675Suqs 661241675Suqs assert(r->last); 662274880Sbapt p = r->last; 663241675Suqs 664241675Suqs r->last = r->last->parent; 665241675Suqs free(p->name); 666241675Suqs free(p->end); 667241675Suqs free(p); 668241675Suqs} 669241675Suqs 670241675Suqs/* 671241675Suqs * Push a roff node onto the instruction stack. This must later be 672241675Suqs * removed with roffnode_pop(). 673241675Suqs */ 674241675Suqsstatic void 675322249Sbaptroffnode_push(struct roff *r, enum roff_tok tok, const char *name, 676241675Suqs int line, int col) 677241675Suqs{ 678241675Suqs struct roffnode *p; 679241675Suqs 680241675Suqs p = mandoc_calloc(1, sizeof(struct roffnode)); 681241675Suqs p->tok = tok; 682241675Suqs if (name) 683241675Suqs p->name = mandoc_strdup(name); 684241675Suqs p->parent = r->last; 685241675Suqs p->line = line; 686241675Suqs p->col = col; 687274880Sbapt p->rule = p->parent ? p->parent->rule : 0; 688241675Suqs 689241675Suqs r->last = p; 690241675Suqs} 691241675Suqs 692294113Sbapt/* --- roff parser state data management ---------------------------------- */ 693294113Sbapt 694241675Suqsstatic void 695241675Suqsroff_free1(struct roff *r) 696241675Suqs{ 697261344Suqs struct tbl_node *tbl; 698241675Suqs int i; 699241675Suqs 700261344Suqs while (NULL != (tbl = r->first_tbl)) { 701261344Suqs r->first_tbl = tbl->next; 702261344Suqs tbl_free(tbl); 703241675Suqs } 704241675Suqs r->first_tbl = r->last_tbl = r->tbl = NULL; 705241675Suqs 706322249Sbapt if (r->last_eqn != NULL) 707322249Sbapt eqn_free(r->last_eqn); 708322249Sbapt r->last_eqn = r->eqn = NULL; 709241675Suqs 710241675Suqs while (r->last) 711241675Suqs roffnode_pop(r); 712241675Suqs 713274880Sbapt free (r->rstack); 714274880Sbapt r->rstack = NULL; 715274880Sbapt r->rstacksz = 0; 716274880Sbapt r->rstackpos = -1; 717274880Sbapt 718274880Sbapt roff_freereg(r->regtab); 719274880Sbapt r->regtab = NULL; 720274880Sbapt 721241675Suqs roff_freestr(r->strtab); 722322249Sbapt roff_freestr(r->rentab); 723241675Suqs roff_freestr(r->xmbtab); 724322249Sbapt r->strtab = r->rentab = r->xmbtab = NULL; 725241675Suqs 726241675Suqs if (r->xtab) 727241675Suqs for (i = 0; i < 128; i++) 728241675Suqs free(r->xtab[i].p); 729241675Suqs free(r->xtab); 730241675Suqs r->xtab = NULL; 731241675Suqs} 732241675Suqs 733241675Suqsvoid 734241675Suqsroff_reset(struct roff *r) 735241675Suqs{ 736241675Suqs roff_free1(r); 737275432Sbapt r->format = r->options & (MPARSE_MDOC | MPARSE_MAN); 738322249Sbapt r->control = '\0'; 739322249Sbapt r->escape = '\\'; 740322249Sbapt roffce_lines = 0; 741322249Sbapt roffce_node = NULL; 742322249Sbapt roffit_lines = 0; 743322249Sbapt roffit_macro = NULL; 744241675Suqs} 745241675Suqs 746241675Suqsvoid 747241675Suqsroff_free(struct roff *r) 748241675Suqs{ 749241675Suqs roff_free1(r); 750322249Sbapt roffhash_free(r->reqtab); 751241675Suqs free(r); 752241675Suqs} 753241675Suqs 754241675Suqsstruct roff * 755294113Sbaptroff_alloc(struct mparse *parse, int options) 756241675Suqs{ 757241675Suqs struct roff *r; 758241675Suqs 759241675Suqs r = mandoc_calloc(1, sizeof(struct roff)); 760241675Suqs r->parse = parse; 761322249Sbapt r->reqtab = roffhash_alloc(0, ROFF_USERDEF); 762274880Sbapt r->options = options; 763275432Sbapt r->format = options & (MPARSE_MDOC | MPARSE_MAN); 764241675Suqs r->rstackpos = -1; 765322249Sbapt r->escape = '\\'; 766294113Sbapt return r; 767241675Suqs} 768241675Suqs 769294113Sbapt/* --- syntax tree state data management ---------------------------------- */ 770294113Sbapt 771294113Sbaptstatic void 772294113Sbaptroff_man_free1(struct roff_man *man) 773294113Sbapt{ 774294113Sbapt 775294113Sbapt if (man->first != NULL) 776294113Sbapt roff_node_delete(man, man->first); 777294113Sbapt free(man->meta.msec); 778294113Sbapt free(man->meta.vol); 779294113Sbapt free(man->meta.os); 780294113Sbapt free(man->meta.arch); 781294113Sbapt free(man->meta.title); 782294113Sbapt free(man->meta.name); 783294113Sbapt free(man->meta.date); 784294113Sbapt} 785294113Sbapt 786294113Sbaptstatic void 787294113Sbaptroff_man_alloc1(struct roff_man *man) 788294113Sbapt{ 789294113Sbapt 790294113Sbapt memset(&man->meta, 0, sizeof(man->meta)); 791294113Sbapt man->first = mandoc_calloc(1, sizeof(*man->first)); 792294113Sbapt man->first->type = ROFFT_ROOT; 793294113Sbapt man->last = man->first; 794294113Sbapt man->last_es = NULL; 795294113Sbapt man->flags = 0; 796294113Sbapt man->macroset = MACROSET_NONE; 797294113Sbapt man->lastsec = man->lastnamed = SEC_NONE; 798294113Sbapt man->next = ROFF_NEXT_CHILD; 799294113Sbapt} 800294113Sbapt 801294113Sbaptvoid 802294113Sbaptroff_man_reset(struct roff_man *man) 803294113Sbapt{ 804294113Sbapt 805294113Sbapt roff_man_free1(man); 806294113Sbapt roff_man_alloc1(man); 807294113Sbapt} 808294113Sbapt 809294113Sbaptvoid 810294113Sbaptroff_man_free(struct roff_man *man) 811294113Sbapt{ 812294113Sbapt 813294113Sbapt roff_man_free1(man); 814294113Sbapt free(man); 815294113Sbapt} 816294113Sbapt 817294113Sbaptstruct roff_man * 818294113Sbaptroff_man_alloc(struct roff *roff, struct mparse *parse, 819322249Sbapt const char *os_s, int quick) 820294113Sbapt{ 821294113Sbapt struct roff_man *man; 822294113Sbapt 823294113Sbapt man = mandoc_calloc(1, sizeof(*man)); 824294113Sbapt man->parse = parse; 825294113Sbapt man->roff = roff; 826322249Sbapt man->os_s = os_s; 827294113Sbapt man->quick = quick; 828294113Sbapt roff_man_alloc1(man); 829322249Sbapt roff->man = man; 830294113Sbapt return man; 831294113Sbapt} 832294113Sbapt 833294113Sbapt/* --- syntax tree handling ----------------------------------------------- */ 834294113Sbapt 835294113Sbaptstruct roff_node * 836294113Sbaptroff_node_alloc(struct roff_man *man, int line, int pos, 837294113Sbapt enum roff_type type, int tok) 838294113Sbapt{ 839294113Sbapt struct roff_node *n; 840294113Sbapt 841294113Sbapt n = mandoc_calloc(1, sizeof(*n)); 842294113Sbapt n->line = line; 843294113Sbapt n->pos = pos; 844294113Sbapt n->tok = tok; 845294113Sbapt n->type = type; 846294113Sbapt n->sec = man->lastsec; 847294113Sbapt 848294113Sbapt if (man->flags & MDOC_SYNOPSIS) 849316420Sbapt n->flags |= NODE_SYNPRETTY; 850294113Sbapt else 851316420Sbapt n->flags &= ~NODE_SYNPRETTY; 852294113Sbapt if (man->flags & MDOC_NEWLINE) 853316420Sbapt n->flags |= NODE_LINE; 854294113Sbapt man->flags &= ~MDOC_NEWLINE; 855294113Sbapt 856294113Sbapt return n; 857294113Sbapt} 858294113Sbapt 859294113Sbaptvoid 860294113Sbaptroff_node_append(struct roff_man *man, struct roff_node *n) 861294113Sbapt{ 862294113Sbapt 863294113Sbapt switch (man->next) { 864294113Sbapt case ROFF_NEXT_SIBLING: 865294113Sbapt if (man->last->next != NULL) { 866294113Sbapt n->next = man->last->next; 867294113Sbapt man->last->next->prev = n; 868294113Sbapt } else 869294113Sbapt man->last->parent->last = n; 870294113Sbapt man->last->next = n; 871294113Sbapt n->prev = man->last; 872294113Sbapt n->parent = man->last->parent; 873294113Sbapt break; 874294113Sbapt case ROFF_NEXT_CHILD: 875316420Sbapt if (man->last->child != NULL) { 876316420Sbapt n->next = man->last->child; 877316420Sbapt man->last->child->prev = n; 878316420Sbapt } else 879316420Sbapt man->last->last = n; 880294113Sbapt man->last->child = n; 881294113Sbapt n->parent = man->last; 882294113Sbapt break; 883294113Sbapt default: 884294113Sbapt abort(); 885294113Sbapt } 886294113Sbapt man->last = n; 887294113Sbapt 888294113Sbapt switch (n->type) { 889294113Sbapt case ROFFT_HEAD: 890294113Sbapt n->parent->head = n; 891294113Sbapt break; 892294113Sbapt case ROFFT_BODY: 893294113Sbapt if (n->end != ENDBODY_NOT) 894294113Sbapt return; 895294113Sbapt n->parent->body = n; 896294113Sbapt break; 897294113Sbapt case ROFFT_TAIL: 898294113Sbapt n->parent->tail = n; 899294113Sbapt break; 900294113Sbapt default: 901294113Sbapt return; 902294113Sbapt } 903294113Sbapt 904294113Sbapt /* 905294113Sbapt * Copy over the normalised-data pointer of our parent. Not 906294113Sbapt * everybody has one, but copying a null pointer is fine. 907294113Sbapt */ 908294113Sbapt 909294113Sbapt n->norm = n->parent->norm; 910294113Sbapt assert(n->parent->type == ROFFT_BLOCK); 911294113Sbapt} 912294113Sbapt 913294113Sbaptvoid 914294113Sbaptroff_word_alloc(struct roff_man *man, int line, int pos, const char *word) 915294113Sbapt{ 916294113Sbapt struct roff_node *n; 917294113Sbapt 918294113Sbapt n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE); 919294113Sbapt n->string = roff_strdup(man->roff, word); 920294113Sbapt roff_node_append(man, n); 921316420Sbapt n->flags |= NODE_VALID | NODE_ENDED; 922294113Sbapt man->next = ROFF_NEXT_SIBLING; 923294113Sbapt} 924294113Sbapt 925294113Sbaptvoid 926294113Sbaptroff_word_append(struct roff_man *man, const char *word) 927294113Sbapt{ 928294113Sbapt struct roff_node *n; 929294113Sbapt char *addstr, *newstr; 930294113Sbapt 931294113Sbapt n = man->last; 932294113Sbapt addstr = roff_strdup(man->roff, word); 933294113Sbapt mandoc_asprintf(&newstr, "%s %s", n->string, addstr); 934294113Sbapt free(addstr); 935294113Sbapt free(n->string); 936294113Sbapt n->string = newstr; 937294113Sbapt man->next = ROFF_NEXT_SIBLING; 938294113Sbapt} 939294113Sbapt 940294113Sbaptvoid 941294113Sbaptroff_elem_alloc(struct roff_man *man, int line, int pos, int tok) 942294113Sbapt{ 943294113Sbapt struct roff_node *n; 944294113Sbapt 945294113Sbapt n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok); 946294113Sbapt roff_node_append(man, n); 947294113Sbapt man->next = ROFF_NEXT_CHILD; 948294113Sbapt} 949294113Sbapt 950294113Sbaptstruct roff_node * 951294113Sbaptroff_block_alloc(struct roff_man *man, int line, int pos, int tok) 952294113Sbapt{ 953294113Sbapt struct roff_node *n; 954294113Sbapt 955294113Sbapt n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok); 956294113Sbapt roff_node_append(man, n); 957294113Sbapt man->next = ROFF_NEXT_CHILD; 958294113Sbapt return n; 959294113Sbapt} 960294113Sbapt 961294113Sbaptstruct roff_node * 962294113Sbaptroff_head_alloc(struct roff_man *man, int line, int pos, int tok) 963294113Sbapt{ 964294113Sbapt struct roff_node *n; 965294113Sbapt 966294113Sbapt n = roff_node_alloc(man, line, pos, ROFFT_HEAD, tok); 967294113Sbapt roff_node_append(man, n); 968294113Sbapt man->next = ROFF_NEXT_CHILD; 969294113Sbapt return n; 970294113Sbapt} 971294113Sbapt 972294113Sbaptstruct roff_node * 973294113Sbaptroff_body_alloc(struct roff_man *man, int line, int pos, int tok) 974294113Sbapt{ 975294113Sbapt struct roff_node *n; 976294113Sbapt 977294113Sbapt n = roff_node_alloc(man, line, pos, ROFFT_BODY, tok); 978294113Sbapt roff_node_append(man, n); 979294113Sbapt man->next = ROFF_NEXT_CHILD; 980294113Sbapt return n; 981294113Sbapt} 982294113Sbapt 983322249Sbaptstatic void 984322249Sbaptroff_addtbl(struct roff_man *man, struct tbl_node *tbl) 985294113Sbapt{ 986294113Sbapt struct roff_node *n; 987322249Sbapt const struct tbl_span *span; 988294113Sbapt 989294113Sbapt if (man->macroset == MACROSET_MAN) 990322249Sbapt man_breakscope(man, ROFF_TS); 991322249Sbapt while ((span = tbl_span(tbl)) != NULL) { 992322249Sbapt n = roff_node_alloc(man, tbl->line, 0, ROFFT_TBL, TOKEN_NONE); 993322249Sbapt n->span = span; 994322249Sbapt roff_node_append(man, n); 995322249Sbapt n->flags |= NODE_VALID | NODE_ENDED; 996322249Sbapt man->next = ROFF_NEXT_SIBLING; 997322249Sbapt } 998294113Sbapt} 999294113Sbapt 1000294113Sbaptvoid 1001294113Sbaptroff_node_unlink(struct roff_man *man, struct roff_node *n) 1002294113Sbapt{ 1003294113Sbapt 1004294113Sbapt /* Adjust siblings. */ 1005294113Sbapt 1006294113Sbapt if (n->prev) 1007294113Sbapt n->prev->next = n->next; 1008294113Sbapt if (n->next) 1009294113Sbapt n->next->prev = n->prev; 1010294113Sbapt 1011294113Sbapt /* Adjust parent. */ 1012294113Sbapt 1013294113Sbapt if (n->parent != NULL) { 1014294113Sbapt if (n->parent->child == n) 1015294113Sbapt n->parent->child = n->next; 1016294113Sbapt if (n->parent->last == n) 1017294113Sbapt n->parent->last = n->prev; 1018294113Sbapt } 1019294113Sbapt 1020294113Sbapt /* Adjust parse point. */ 1021294113Sbapt 1022294113Sbapt if (man == NULL) 1023294113Sbapt return; 1024294113Sbapt if (man->last == n) { 1025294113Sbapt if (n->prev == NULL) { 1026294113Sbapt man->last = n->parent; 1027294113Sbapt man->next = ROFF_NEXT_CHILD; 1028294113Sbapt } else { 1029294113Sbapt man->last = n->prev; 1030294113Sbapt man->next = ROFF_NEXT_SIBLING; 1031294113Sbapt } 1032294113Sbapt } 1033294113Sbapt if (man->first == n) 1034294113Sbapt man->first = NULL; 1035294113Sbapt} 1036294113Sbapt 1037294113Sbaptvoid 1038294113Sbaptroff_node_free(struct roff_node *n) 1039294113Sbapt{ 1040294113Sbapt 1041294113Sbapt if (n->args != NULL) 1042294113Sbapt mdoc_argv_free(n->args); 1043294113Sbapt if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM) 1044294113Sbapt free(n->norm); 1045322249Sbapt if (n->eqn != NULL) 1046322249Sbapt eqn_box_free(n->eqn); 1047294113Sbapt free(n->string); 1048294113Sbapt free(n); 1049294113Sbapt} 1050294113Sbapt 1051294113Sbaptvoid 1052294113Sbaptroff_node_delete(struct roff_man *man, struct roff_node *n) 1053294113Sbapt{ 1054294113Sbapt 1055294113Sbapt while (n->child != NULL) 1056294113Sbapt roff_node_delete(man, n->child); 1057294113Sbapt roff_node_unlink(man, n); 1058294113Sbapt roff_node_free(n); 1059294113Sbapt} 1060294113Sbapt 1061294113Sbaptvoid 1062294113Sbaptderoff(char **dest, const struct roff_node *n) 1063294113Sbapt{ 1064294113Sbapt char *cp; 1065294113Sbapt size_t sz; 1066294113Sbapt 1067294113Sbapt if (n->type != ROFFT_TEXT) { 1068294113Sbapt for (n = n->child; n != NULL; n = n->next) 1069294113Sbapt deroff(dest, n); 1070294113Sbapt return; 1071294113Sbapt } 1072294113Sbapt 1073316420Sbapt /* Skip leading whitespace. */ 1074294113Sbapt 1075316420Sbapt for (cp = n->string; *cp != '\0'; cp++) { 1076322249Sbapt if (cp[0] == '\\' && cp[1] != '\0' && 1077322249Sbapt strchr(" %&0^|~", cp[1]) != NULL) 1078294113Sbapt cp++; 1079316420Sbapt else if ( ! isspace((unsigned char)*cp)) 1080294113Sbapt break; 1081294113Sbapt } 1082294113Sbapt 1083322249Sbapt /* Skip trailing backslash. */ 1084322249Sbapt 1085322249Sbapt sz = strlen(cp); 1086322249Sbapt if (sz > 0 && cp[sz - 1] == '\\') 1087322249Sbapt sz--; 1088322249Sbapt 1089294113Sbapt /* Skip trailing whitespace. */ 1090294113Sbapt 1091322249Sbapt for (; sz; sz--) 1092294113Sbapt if ( ! isspace((unsigned char)cp[sz-1])) 1093294113Sbapt break; 1094294113Sbapt 1095294113Sbapt /* Skip empty strings. */ 1096294113Sbapt 1097294113Sbapt if (sz == 0) 1098294113Sbapt return; 1099294113Sbapt 1100294113Sbapt if (*dest == NULL) { 1101294113Sbapt *dest = mandoc_strndup(cp, sz); 1102294113Sbapt return; 1103294113Sbapt } 1104294113Sbapt 1105294113Sbapt mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp); 1106294113Sbapt free(*dest); 1107294113Sbapt *dest = cp; 1108294113Sbapt} 1109294113Sbapt 1110294113Sbapt/* --- main functions of the roff parser ---------------------------------- */ 1111294113Sbapt 1112241675Suqs/* 1113274880Sbapt * In the current line, expand escape sequences that tend to get 1114274880Sbapt * used in numerical expressions and conditional requests. 1115274880Sbapt * Also check the syntax of the remaining escape sequences. 1116241675Suqs */ 1117241675Suqsstatic enum rofferr 1118275432Sbaptroff_res(struct roff *r, struct buf *buf, int ln, int pos) 1119241675Suqs{ 1120274880Sbapt char ubuf[24]; /* buffer to print the number */ 1121274880Sbapt const char *start; /* start of the string to process */ 1122274880Sbapt char *stesc; /* start of an escape sequence ('\\') */ 1123241675Suqs const char *stnam; /* start of the name, after "[(*" */ 1124241675Suqs const char *cp; /* end of the name, e.g. before ']' */ 1125241675Suqs const char *res; /* the string to be substituted */ 1126275432Sbapt char *nbuf; /* new buffer to copy buf->buf to */ 1127261344Suqs size_t maxl; /* expected length of the escape name */ 1128261344Suqs size_t naml; /* actual length of the escape name */ 1129275432Sbapt enum mandoc_esc esc; /* type of the escape sequence */ 1130275432Sbapt int inaml; /* length returned from mandoc_escape() */ 1131261344Suqs int expand_count; /* to avoid infinite loops */ 1132274880Sbapt int npos; /* position in numeric expression */ 1133274880Sbapt int arg_complete; /* argument not interrupted by eol */ 1134322249Sbapt int done; /* no more input available */ 1135322249Sbapt int deftype; /* type of definition to paste */ 1136322249Sbapt int rcsid; /* kind of RCS id seen */ 1137274880Sbapt char term; /* character terminating the escape */ 1138241675Suqs 1139322249Sbapt /* Search forward for comments. */ 1140322249Sbapt 1141322249Sbapt done = 0; 1142275432Sbapt start = buf->buf + pos; 1143322249Sbapt for (stesc = buf->buf + pos; *stesc != '\0'; stesc++) { 1144322249Sbapt if (stesc[0] != r->escape || stesc[1] == '\0') 1145322249Sbapt continue; 1146322249Sbapt stesc++; 1147322249Sbapt if (*stesc != '"' && *stesc != '#') 1148322249Sbapt continue; 1149241675Suqs 1150322249Sbapt /* Comment found, look for RCS id. */ 1151322249Sbapt 1152322249Sbapt rcsid = 0; 1153322249Sbapt if ((cp = strstr(stesc, "$" "OpenBSD")) != NULL) { 1154322249Sbapt rcsid = 1 << MANDOC_OS_OPENBSD; 1155322249Sbapt cp += 8; 1156322249Sbapt } else if ((cp = strstr(stesc, "$" "NetBSD")) != NULL) { 1157322249Sbapt rcsid = 1 << MANDOC_OS_NETBSD; 1158322249Sbapt cp += 7; 1159322249Sbapt } 1160322249Sbapt if (cp != NULL && 1161322249Sbapt isalnum((unsigned char)*cp) == 0 && 1162322249Sbapt strchr(cp, '$') != NULL) { 1163322249Sbapt if (r->man->meta.rcsids & rcsid) 1164322249Sbapt mandoc_msg(MANDOCERR_RCS_REP, r->parse, 1165322249Sbapt ln, stesc + 1 - buf->buf, stesc + 1); 1166322249Sbapt r->man->meta.rcsids |= rcsid; 1167322249Sbapt } 1168322249Sbapt 1169322249Sbapt /* Handle trailing whitespace. */ 1170322249Sbapt 1171322249Sbapt cp = strchr(stesc--, '\0') - 1; 1172322249Sbapt if (*cp == '\n') { 1173322249Sbapt done = 1; 1174322249Sbapt cp--; 1175322249Sbapt } 1176322249Sbapt if (*cp == ' ' || *cp == '\t') 1177322249Sbapt mandoc_msg(MANDOCERR_SPACE_EOL, r->parse, 1178322249Sbapt ln, cp - buf->buf, NULL); 1179322249Sbapt while (stesc > start && stesc[-1] == ' ') 1180322249Sbapt stesc--; 1181322249Sbapt *stesc = '\0'; 1182322249Sbapt break; 1183322249Sbapt } 1184322249Sbapt if (stesc == start) 1185322249Sbapt return ROFF_CONT; 1186322249Sbapt stesc--; 1187322249Sbapt 1188322249Sbapt /* Notice the end of the input. */ 1189322249Sbapt 1190322249Sbapt if (*stesc == '\n') { 1191322249Sbapt *stesc-- = '\0'; 1192322249Sbapt done = 1; 1193322249Sbapt } 1194322249Sbapt 1195322249Sbapt expand_count = 0; 1196322249Sbapt while (stesc >= start) { 1197322249Sbapt 1198274880Sbapt /* Search backwards for the next backslash. */ 1199241675Suqs 1200322249Sbapt if (*stesc != r->escape) { 1201322249Sbapt if (*stesc == '\\') { 1202322249Sbapt *stesc = '\0'; 1203322249Sbapt buf->sz = mandoc_asprintf(&nbuf, "%s\\e%s", 1204322249Sbapt buf->buf, stesc + 1) + 1; 1205322249Sbapt start = nbuf + pos; 1206322249Sbapt stesc = nbuf + (stesc - buf->buf); 1207322249Sbapt free(buf->buf); 1208322249Sbapt buf->buf = nbuf; 1209322249Sbapt } 1210322249Sbapt stesc--; 1211274880Sbapt continue; 1212322249Sbapt } 1213241675Suqs 1214274880Sbapt /* If it is escaped, skip it. */ 1215241675Suqs 1216274880Sbapt for (cp = stesc - 1; cp >= start; cp--) 1217322249Sbapt if (*cp != r->escape) 1218274880Sbapt break; 1219274880Sbapt 1220275432Sbapt if ((stesc - cp) % 2 == 0) { 1221322249Sbapt while (stesc > cp) 1222322249Sbapt *stesc-- = '\\'; 1223274880Sbapt continue; 1224322249Sbapt } else if (stesc[1] != '\0') { 1225322249Sbapt *stesc = '\\'; 1226322249Sbapt } else { 1227322249Sbapt *stesc-- = '\0'; 1228322249Sbapt if (done) 1229322249Sbapt continue; 1230322249Sbapt else 1231322249Sbapt return ROFF_APPEND; 1232274880Sbapt } 1233274880Sbapt 1234274880Sbapt /* Decide whether to expand or to check only. */ 1235274880Sbapt 1236274880Sbapt term = '\0'; 1237274880Sbapt cp = stesc + 1; 1238261344Suqs switch (*cp) { 1239274880Sbapt case '*': 1240261344Suqs res = NULL; 1241261344Suqs break; 1242274880Sbapt case 'B': 1243274880Sbapt case 'w': 1244274880Sbapt term = cp[1]; 1245274880Sbapt /* FALLTHROUGH */ 1246274880Sbapt case 'n': 1247261344Suqs res = ubuf; 1248261344Suqs break; 1249261344Suqs default: 1250275432Sbapt esc = mandoc_escape(&cp, &stnam, &inaml); 1251275432Sbapt if (esc == ESCAPE_ERROR || 1252275432Sbapt (esc == ESCAPE_SPECIAL && 1253294113Sbapt mchars_spec2cp(stnam, inaml) < 0)) 1254274880Sbapt mandoc_vmsg(MANDOCERR_ESC_BAD, 1255275432Sbapt r->parse, ln, (int)(stesc - buf->buf), 1256274880Sbapt "%.*s", (int)(cp - stesc), stesc); 1257322249Sbapt stesc--; 1258274880Sbapt continue; 1259241675Suqs } 1260241675Suqs 1261274880Sbapt if (EXPAND_LIMIT < ++expand_count) { 1262274880Sbapt mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, 1263275432Sbapt ln, (int)(stesc - buf->buf), NULL); 1264294113Sbapt return ROFF_IGN; 1265274880Sbapt } 1266241675Suqs 1267241675Suqs /* 1268241675Suqs * The third character decides the length 1269261344Suqs * of the name of the string or register. 1270241675Suqs * Save a pointer to the name. 1271241675Suqs */ 1272241675Suqs 1273275432Sbapt if (term == '\0') { 1274274880Sbapt switch (*++cp) { 1275274880Sbapt case '\0': 1276274880Sbapt maxl = 0; 1277274880Sbapt break; 1278274880Sbapt case '(': 1279274880Sbapt cp++; 1280274880Sbapt maxl = 2; 1281274880Sbapt break; 1282274880Sbapt case '[': 1283274880Sbapt cp++; 1284274880Sbapt term = ']'; 1285274880Sbapt maxl = 0; 1286274880Sbapt break; 1287274880Sbapt default: 1288274880Sbapt maxl = 1; 1289274880Sbapt break; 1290274880Sbapt } 1291274880Sbapt } else { 1292274880Sbapt cp += 2; 1293241675Suqs maxl = 0; 1294241675Suqs } 1295241675Suqs stnam = cp; 1296241675Suqs 1297241675Suqs /* Advance to the end of the name. */ 1298241675Suqs 1299279527Sbapt naml = 0; 1300274880Sbapt arg_complete = 1; 1301279527Sbapt while (maxl == 0 || naml < maxl) { 1302275432Sbapt if (*cp == '\0') { 1303274880Sbapt mandoc_msg(MANDOCERR_ESC_BAD, r->parse, 1304275432Sbapt ln, (int)(stesc - buf->buf), stesc); 1305274880Sbapt arg_complete = 0; 1306274880Sbapt break; 1307241675Suqs } 1308275432Sbapt if (maxl == 0 && *cp == term) { 1309274880Sbapt cp++; 1310241675Suqs break; 1311274880Sbapt } 1312279527Sbapt if (*cp++ != '\\' || stesc[1] != 'w') { 1313279527Sbapt naml++; 1314279527Sbapt continue; 1315279527Sbapt } 1316279527Sbapt switch (mandoc_escape(&cp, NULL, NULL)) { 1317279527Sbapt case ESCAPE_SPECIAL: 1318279527Sbapt case ESCAPE_UNICODE: 1319279527Sbapt case ESCAPE_NUMBERED: 1320279527Sbapt case ESCAPE_OVERSTRIKE: 1321279527Sbapt naml++; 1322279527Sbapt break; 1323279527Sbapt default: 1324279527Sbapt break; 1325279527Sbapt } 1326241675Suqs } 1327241675Suqs 1328241675Suqs /* 1329241675Suqs * Retrieve the replacement string; if it is 1330241675Suqs * undefined, resume searching for escapes. 1331241675Suqs */ 1332241675Suqs 1333274880Sbapt switch (stesc[1]) { 1334274880Sbapt case '*': 1335322249Sbapt if (arg_complete) { 1336322249Sbapt deftype = ROFFDEF_USER | ROFFDEF_PRE; 1337322249Sbapt res = roff_getstrn(r, stnam, naml, &deftype); 1338322249Sbapt } 1339274880Sbapt break; 1340274880Sbapt case 'B': 1341274880Sbapt npos = 0; 1342274880Sbapt ubuf[0] = arg_complete && 1343279527Sbapt roff_evalnum(r, ln, stnam, &npos, 1344279527Sbapt NULL, ROFFNUM_SCALE) && 1345274880Sbapt stnam + npos + 1 == cp ? '1' : '0'; 1346274880Sbapt ubuf[1] = '\0'; 1347274880Sbapt break; 1348274880Sbapt case 'n': 1349274880Sbapt if (arg_complete) 1350274880Sbapt (void)snprintf(ubuf, sizeof(ubuf), "%d", 1351274880Sbapt roff_getregn(r, stnam, naml)); 1352274880Sbapt else 1353274880Sbapt ubuf[0] = '\0'; 1354274880Sbapt break; 1355274880Sbapt case 'w': 1356274880Sbapt /* use even incomplete args */ 1357274880Sbapt (void)snprintf(ubuf, sizeof(ubuf), "%d", 1358274880Sbapt 24 * (int)naml); 1359274880Sbapt break; 1360274880Sbapt } 1361241675Suqs 1362275432Sbapt if (res == NULL) { 1363274880Sbapt mandoc_vmsg(MANDOCERR_STR_UNDEF, 1364275432Sbapt r->parse, ln, (int)(stesc - buf->buf), 1365274880Sbapt "%.*s", (int)naml, stnam); 1366241675Suqs res = ""; 1367279527Sbapt } else if (buf->sz + strlen(res) > SHRT_MAX) { 1368279527Sbapt mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, 1369279527Sbapt ln, (int)(stesc - buf->buf), NULL); 1370294113Sbapt return ROFF_IGN; 1371241675Suqs } 1372241675Suqs 1373241675Suqs /* Replace the escape sequence by the string. */ 1374241675Suqs 1375274880Sbapt *stesc = '\0'; 1376275432Sbapt buf->sz = mandoc_asprintf(&nbuf, "%s%s%s", 1377275432Sbapt buf->buf, res, cp) + 1; 1378241675Suqs 1379274880Sbapt /* Prepare for the next replacement. */ 1380241675Suqs 1381274880Sbapt start = nbuf + pos; 1382275432Sbapt stesc = nbuf + (stesc - buf->buf) + strlen(res); 1383275432Sbapt free(buf->buf); 1384275432Sbapt buf->buf = nbuf; 1385241675Suqs } 1386294113Sbapt return ROFF_CONT; 1387241675Suqs} 1388241675Suqs 1389241675Suqs/* 1390294113Sbapt * Process text streams. 1391241675Suqs */ 1392241675Suqsstatic enum rofferr 1393322249Sbaptroff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs) 1394241675Suqs{ 1395241675Suqs size_t sz; 1396241675Suqs const char *start; 1397261344Suqs char *p; 1398261344Suqs int isz; 1399241675Suqs enum mandoc_esc esc; 1400241675Suqs 1401294113Sbapt /* Spring the input line trap. */ 1402294113Sbapt 1403294113Sbapt if (roffit_lines == 1) { 1404294113Sbapt isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro); 1405294113Sbapt free(buf->buf); 1406294113Sbapt buf->buf = p; 1407294113Sbapt buf->sz = isz + 1; 1408294113Sbapt *offs = 0; 1409294113Sbapt free(roffit_macro); 1410294113Sbapt roffit_lines = 0; 1411294113Sbapt return ROFF_REPARSE; 1412294113Sbapt } else if (roffit_lines > 1) 1413294113Sbapt --roffit_lines; 1414294113Sbapt 1415322249Sbapt if (roffce_node != NULL && buf->buf[pos] != '\0') { 1416322249Sbapt if (roffce_lines < 1) { 1417322249Sbapt r->man->last = roffce_node; 1418322249Sbapt r->man->next = ROFF_NEXT_SIBLING; 1419322249Sbapt roffce_lines = 0; 1420322249Sbapt roffce_node = NULL; 1421322249Sbapt } else 1422322249Sbapt roffce_lines--; 1423322249Sbapt } 1424322249Sbapt 1425294113Sbapt /* Convert all breakable hyphens into ASCII_HYPH. */ 1426294113Sbapt 1427275432Sbapt start = p = buf->buf + pos; 1428241675Suqs 1429275432Sbapt while (*p != '\0') { 1430241675Suqs sz = strcspn(p, "-\\"); 1431241675Suqs p += sz; 1432241675Suqs 1433275432Sbapt if (*p == '\0') 1434241675Suqs break; 1435241675Suqs 1436275432Sbapt if (*p == '\\') { 1437241675Suqs /* Skip over escapes. */ 1438241675Suqs p++; 1439261344Suqs esc = mandoc_escape((const char **)&p, NULL, NULL); 1440275432Sbapt if (esc == ESCAPE_ERROR) 1441241675Suqs break; 1442294113Sbapt while (*p == '-') 1443294113Sbapt p++; 1444241675Suqs continue; 1445241675Suqs } else if (p == start) { 1446241675Suqs p++; 1447241675Suqs continue; 1448241675Suqs } 1449241675Suqs 1450241675Suqs if (isalpha((unsigned char)p[-1]) && 1451241675Suqs isalpha((unsigned char)p[1])) 1452241675Suqs *p = ASCII_HYPH; 1453241675Suqs p++; 1454241675Suqs } 1455294113Sbapt return ROFF_CONT; 1456241675Suqs} 1457241675Suqs 1458241675Suqsenum rofferr 1459275432Sbaptroff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) 1460241675Suqs{ 1461322249Sbapt enum roff_tok t; 1462241675Suqs enum rofferr e; 1463275432Sbapt int pos; /* parse point */ 1464279527Sbapt int spos; /* saved parse point for messages */ 1465275432Sbapt int ppos; /* original offset in buf->buf */ 1466275432Sbapt int ctl; /* macro line (boolean) */ 1467241675Suqs 1468275432Sbapt ppos = pos = *offs; 1469241675Suqs 1470275432Sbapt /* Handle in-line equation delimiters. */ 1471275432Sbapt 1472275432Sbapt if (r->tbl == NULL && 1473275432Sbapt r->last_eqn != NULL && r->last_eqn->delim && 1474275432Sbapt (r->eqn == NULL || r->eqn_inline)) { 1475275432Sbapt e = roff_eqndelim(r, buf, pos); 1476275432Sbapt if (e == ROFF_REPARSE) 1477294113Sbapt return e; 1478275432Sbapt assert(e == ROFF_CONT); 1479275432Sbapt } 1480275432Sbapt 1481275432Sbapt /* Expand some escape sequences. */ 1482275432Sbapt 1483275432Sbapt e = roff_res(r, buf, ln, pos); 1484322249Sbapt if (e == ROFF_IGN || e == ROFF_APPEND) 1485294113Sbapt return e; 1486275432Sbapt assert(e == ROFF_CONT); 1487241675Suqs 1488275432Sbapt ctl = roff_getcontrol(r, buf->buf, &pos); 1489241675Suqs 1490241675Suqs /* 1491241675Suqs * First, if a scope is open and we're not a macro, pass the 1492279527Sbapt * text through the macro's filter. 1493279527Sbapt * Equations process all content themselves. 1494279527Sbapt * Tables process almost all content themselves, but we want 1495279527Sbapt * to warn about macros before passing it there. 1496241675Suqs */ 1497241675Suqs 1498279527Sbapt if (r->last != NULL && ! ctl) { 1499241675Suqs t = r->last->tok; 1500275432Sbapt e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs); 1501322249Sbapt if (e == ROFF_IGN) 1502294113Sbapt return e; 1503322249Sbapt assert(e == ROFF_CONT); 1504261344Suqs } 1505322249Sbapt if (r->eqn != NULL && strncmp(buf->buf + ppos, ".EN", 3)) { 1506322249Sbapt eqn_read(r->eqn, buf->buf + ppos); 1507322249Sbapt return ROFF_IGN; 1508322249Sbapt } 1509322249Sbapt if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) { 1510322249Sbapt tbl_read(r->tbl, ln, buf->buf, ppos); 1511322249Sbapt roff_addtbl(r->man, r->tbl); 1512322249Sbapt return ROFF_IGN; 1513322249Sbapt } 1514279527Sbapt if ( ! ctl) 1515322249Sbapt return roff_parsetext(r, buf, pos, offs); 1516241675Suqs 1517275432Sbapt /* Skip empty request lines. */ 1518275432Sbapt 1519275432Sbapt if (buf->buf[pos] == '"') { 1520275432Sbapt mandoc_msg(MANDOCERR_COMMENT_BAD, r->parse, 1521275432Sbapt ln, pos, NULL); 1522294113Sbapt return ROFF_IGN; 1523275432Sbapt } else if (buf->buf[pos] == '\0') 1524294113Sbapt return ROFF_IGN; 1525275432Sbapt 1526241675Suqs /* 1527241675Suqs * If a scope is open, go to the child handler for that macro, 1528241675Suqs * as it may want to preprocess before doing anything with it. 1529241675Suqs * Don't do so if an equation is open. 1530241675Suqs */ 1531241675Suqs 1532241675Suqs if (r->last) { 1533241675Suqs t = r->last->tok; 1534294113Sbapt return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs); 1535241675Suqs } 1536241675Suqs 1537279527Sbapt /* No scope is open. This is a new request or macro. */ 1538279527Sbapt 1539279527Sbapt spos = pos; 1540279527Sbapt t = roff_parse(r, buf->buf, &pos, ln, ppos); 1541279527Sbapt 1542279527Sbapt /* Tables ignore most macros. */ 1543279527Sbapt 1544322249Sbapt if (r->tbl != NULL && (t == TOKEN_NONE || t == ROFF_TS || 1545322249Sbapt t == ROFF_br || t == ROFF_ce || t == ROFF_rj || t == ROFF_sp)) { 1546279527Sbapt mandoc_msg(MANDOCERR_TBLMACRO, r->parse, 1547279527Sbapt ln, pos, buf->buf + spos); 1548322249Sbapt if (t != TOKEN_NONE) 1549294113Sbapt return ROFF_IGN; 1550279527Sbapt while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ') 1551279527Sbapt pos++; 1552322249Sbapt while (buf->buf[pos] == ' ') 1553279527Sbapt pos++; 1554322249Sbapt tbl_read(r->tbl, ln, buf->buf, pos); 1555322249Sbapt roff_addtbl(r->man, r->tbl); 1556322249Sbapt return ROFF_IGN; 1557279527Sbapt } 1558279527Sbapt 1559322249Sbapt /* For now, let high level macros abort .ce mode. */ 1560322249Sbapt 1561322249Sbapt if (ctl && roffce_node != NULL && 1562322249Sbapt (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ || 1563322249Sbapt t == ROFF_TH || t == ROFF_TS)) { 1564322249Sbapt r->man->last = roffce_node; 1565322249Sbapt r->man->next = ROFF_NEXT_SIBLING; 1566322249Sbapt roffce_lines = 0; 1567322249Sbapt roffce_node = NULL; 1568322249Sbapt } 1569322249Sbapt 1570241675Suqs /* 1571279527Sbapt * This is neither a roff request nor a user-defined macro. 1572279527Sbapt * Let the standard macro set parsers handle it. 1573241675Suqs */ 1574241675Suqs 1575322249Sbapt if (t == TOKEN_NONE) 1576294113Sbapt return ROFF_CONT; 1577241675Suqs 1578279527Sbapt /* Execute a roff request or a user defined macro. */ 1579279527Sbapt 1580322249Sbapt return (*roffs[t].proc)(r, t, buf, ln, spos, pos, offs); 1581241675Suqs} 1582241675Suqs 1583241675Suqsvoid 1584241675Suqsroff_endparse(struct roff *r) 1585241675Suqs{ 1586322249Sbapt if (r->last != NULL) 1587274880Sbapt mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, 1588274880Sbapt r->last->line, r->last->col, 1589322249Sbapt roff_name[r->last->tok]); 1590241675Suqs 1591322249Sbapt if (r->eqn != NULL) { 1592274880Sbapt mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, 1593322249Sbapt r->eqn->node->line, r->eqn->node->pos, "EQ"); 1594322249Sbapt eqn_parse(r->eqn); 1595322249Sbapt r->eqn = NULL; 1596241675Suqs } 1597241675Suqs 1598322249Sbapt if (r->tbl != NULL) { 1599274880Sbapt mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, 1600274880Sbapt r->tbl->line, r->tbl->pos, "TS"); 1601322249Sbapt tbl_end(r->tbl); 1602322249Sbapt r->tbl = NULL; 1603241675Suqs } 1604241675Suqs} 1605241675Suqs 1606241675Suqs/* 1607241675Suqs * Parse a roff node's type from the input buffer. This must be in the 1608241675Suqs * form of ".foo xxx" in the usual way. 1609241675Suqs */ 1610322249Sbaptstatic enum roff_tok 1611274880Sbaptroff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos) 1612241675Suqs{ 1613274880Sbapt char *cp; 1614241675Suqs const char *mac; 1615241675Suqs size_t maclen; 1616322249Sbapt int deftype; 1617322249Sbapt enum roff_tok t; 1618241675Suqs 1619274880Sbapt cp = buf + *pos; 1620274880Sbapt 1621274880Sbapt if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp) 1622322249Sbapt return TOKEN_NONE; 1623241675Suqs 1624274880Sbapt mac = cp; 1625274880Sbapt maclen = roff_getname(r, &cp, ln, ppos); 1626241675Suqs 1627322249Sbapt deftype = ROFFDEF_USER | ROFFDEF_REN; 1628322249Sbapt r->current_string = roff_getstrn(r, mac, maclen, &deftype); 1629322249Sbapt switch (deftype) { 1630322249Sbapt case ROFFDEF_USER: 1631322249Sbapt t = ROFF_USERDEF; 1632322249Sbapt break; 1633322249Sbapt case ROFFDEF_REN: 1634322249Sbapt t = ROFF_RENAMED; 1635322249Sbapt break; 1636322249Sbapt default: 1637322249Sbapt t = roffhash_find(r->reqtab, mac, maclen); 1638322249Sbapt break; 1639322249Sbapt } 1640322249Sbapt if (t != TOKEN_NONE) 1641274880Sbapt *pos = cp - buf; 1642294113Sbapt return t; 1643241675Suqs} 1644241675Suqs 1645294113Sbapt/* --- handling of request blocks ----------------------------------------- */ 1646294113Sbapt 1647241675Suqsstatic enum rofferr 1648241675Suqsroff_cblock(ROFF_ARGS) 1649241675Suqs{ 1650241675Suqs 1651241675Suqs /* 1652241675Suqs * A block-close `..' should only be invoked as a child of an 1653241675Suqs * ignore macro, otherwise raise a warning and just ignore it. 1654241675Suqs */ 1655241675Suqs 1656275432Sbapt if (r->last == NULL) { 1657274880Sbapt mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 1658274880Sbapt ln, ppos, ".."); 1659294113Sbapt return ROFF_IGN; 1660241675Suqs } 1661241675Suqs 1662241675Suqs switch (r->last->tok) { 1663274880Sbapt case ROFF_am: 1664274880Sbapt /* ROFF_am1 is remapped to ROFF_am in roff_block(). */ 1665274880Sbapt case ROFF_ami: 1666274880Sbapt case ROFF_de: 1667241675Suqs /* ROFF_de1 is remapped to ROFF_de in roff_block(). */ 1668274880Sbapt case ROFF_dei: 1669274880Sbapt case ROFF_ig: 1670241675Suqs break; 1671241675Suqs default: 1672274880Sbapt mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 1673274880Sbapt ln, ppos, ".."); 1674294113Sbapt return ROFF_IGN; 1675241675Suqs } 1676241675Suqs 1677275432Sbapt if (buf->buf[pos] != '\0') 1678274880Sbapt mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, 1679275432Sbapt ".. %s", buf->buf + pos); 1680241675Suqs 1681241675Suqs roffnode_pop(r); 1682241675Suqs roffnode_cleanscope(r); 1683294113Sbapt return ROFF_IGN; 1684241675Suqs 1685241675Suqs} 1686241675Suqs 1687241675Suqsstatic void 1688241675Suqsroffnode_cleanscope(struct roff *r) 1689241675Suqs{ 1690241675Suqs 1691241675Suqs while (r->last) { 1692261344Suqs if (--r->last->endspan != 0) 1693241675Suqs break; 1694241675Suqs roffnode_pop(r); 1695241675Suqs } 1696241675Suqs} 1697241675Suqs 1698274880Sbaptstatic void 1699274880Sbaptroff_ccond(struct roff *r, int ln, int ppos) 1700241675Suqs{ 1701241675Suqs 1702241675Suqs if (NULL == r->last) { 1703274880Sbapt mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 1704274880Sbapt ln, ppos, "\\}"); 1705274880Sbapt return; 1706241675Suqs } 1707241675Suqs 1708241675Suqs switch (r->last->tok) { 1709274880Sbapt case ROFF_el: 1710274880Sbapt case ROFF_ie: 1711274880Sbapt case ROFF_if: 1712241675Suqs break; 1713241675Suqs default: 1714274880Sbapt mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 1715274880Sbapt ln, ppos, "\\}"); 1716274880Sbapt return; 1717241675Suqs } 1718241675Suqs 1719241675Suqs if (r->last->endspan > -1) { 1720274880Sbapt mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 1721274880Sbapt ln, ppos, "\\}"); 1722274880Sbapt return; 1723241675Suqs } 1724241675Suqs 1725241675Suqs roffnode_pop(r); 1726241675Suqs roffnode_cleanscope(r); 1727274880Sbapt return; 1728241675Suqs} 1729241675Suqs 1730241675Suqsstatic enum rofferr 1731241675Suqsroff_block(ROFF_ARGS) 1732241675Suqs{ 1733322249Sbapt const char *name, *value; 1734322249Sbapt char *call, *cp, *iname, *rname; 1735322249Sbapt size_t csz, namesz, rsz; 1736322249Sbapt int deftype; 1737241675Suqs 1738274880Sbapt /* Ignore groff compatibility mode for now. */ 1739241675Suqs 1740275432Sbapt if (tok == ROFF_de1) 1741274880Sbapt tok = ROFF_de; 1742279527Sbapt else if (tok == ROFF_dei1) 1743279527Sbapt tok = ROFF_dei; 1744275432Sbapt else if (tok == ROFF_am1) 1745274880Sbapt tok = ROFF_am; 1746279527Sbapt else if (tok == ROFF_ami1) 1747279527Sbapt tok = ROFF_ami; 1748241675Suqs 1749274880Sbapt /* Parse the macro name argument. */ 1750241675Suqs 1751275432Sbapt cp = buf->buf + pos; 1752275432Sbapt if (tok == ROFF_ig) { 1753274880Sbapt iname = NULL; 1754274880Sbapt namesz = 0; 1755274880Sbapt } else { 1756274880Sbapt iname = cp; 1757274880Sbapt namesz = roff_getname(r, &cp, ln, ppos); 1758274880Sbapt iname[namesz] = '\0'; 1759274880Sbapt } 1760241675Suqs 1761274880Sbapt /* Resolve the macro name argument if it is indirect. */ 1762241675Suqs 1763275432Sbapt if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) { 1764322249Sbapt deftype = ROFFDEF_USER; 1765322249Sbapt name = roff_getstrn(r, iname, namesz, &deftype); 1766322249Sbapt if (name == NULL) { 1767274880Sbapt mandoc_vmsg(MANDOCERR_STR_UNDEF, 1768275432Sbapt r->parse, ln, (int)(iname - buf->buf), 1769274880Sbapt "%.*s", (int)namesz, iname); 1770274880Sbapt namesz = 0; 1771274880Sbapt } else 1772274880Sbapt namesz = strlen(name); 1773274880Sbapt } else 1774274880Sbapt name = iname; 1775274880Sbapt 1776275432Sbapt if (namesz == 0 && tok != ROFF_ig) { 1777274880Sbapt mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, 1778322249Sbapt ln, ppos, roff_name[tok]); 1779294113Sbapt return ROFF_IGN; 1780241675Suqs } 1781241675Suqs 1782241675Suqs roffnode_push(r, tok, name, ln, ppos); 1783241675Suqs 1784241675Suqs /* 1785241675Suqs * At the beginning of a `de' macro, clear the existing string 1786241675Suqs * with the same name, if there is one. New content will be 1787274880Sbapt * appended from roff_block_text() in multiline mode. 1788241675Suqs */ 1789241675Suqs 1790322249Sbapt if (tok == ROFF_de || tok == ROFF_dei) { 1791274880Sbapt roff_setstrn(&r->strtab, name, namesz, "", 0, 0); 1792322249Sbapt roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); 1793322249Sbapt } else if (tok == ROFF_am || tok == ROFF_ami) { 1794322249Sbapt deftype = ROFFDEF_ANY; 1795322249Sbapt value = roff_getstrn(r, iname, namesz, &deftype); 1796322249Sbapt switch (deftype) { /* Before appending, ... */ 1797322249Sbapt case ROFFDEF_PRE: /* copy predefined to user-defined. */ 1798322249Sbapt roff_setstrn(&r->strtab, name, namesz, 1799322249Sbapt value, strlen(value), 0); 1800322249Sbapt break; 1801322249Sbapt case ROFFDEF_REN: /* call original standard macro. */ 1802322249Sbapt csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n", 1803322249Sbapt (int)strlen(value), value); 1804322249Sbapt roff_setstrn(&r->strtab, name, namesz, call, csz, 0); 1805322249Sbapt roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); 1806322249Sbapt free(call); 1807322249Sbapt break; 1808322249Sbapt case ROFFDEF_STD: /* rename and call standard macro. */ 1809322249Sbapt rsz = mandoc_asprintf(&rname, "__%s_renamed", name); 1810322249Sbapt roff_setstrn(&r->rentab, rname, rsz, name, namesz, 0); 1811322249Sbapt csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n", 1812322249Sbapt (int)rsz, rname); 1813322249Sbapt roff_setstrn(&r->strtab, name, namesz, call, csz, 0); 1814322249Sbapt free(call); 1815322249Sbapt free(rname); 1816322249Sbapt break; 1817322249Sbapt default: 1818322249Sbapt break; 1819322249Sbapt } 1820322249Sbapt } 1821241675Suqs 1822275432Sbapt if (*cp == '\0') 1823294113Sbapt return ROFF_IGN; 1824241675Suqs 1825274880Sbapt /* Get the custom end marker. */ 1826241675Suqs 1827274880Sbapt iname = cp; 1828274880Sbapt namesz = roff_getname(r, &cp, ln, ppos); 1829241675Suqs 1830274880Sbapt /* Resolve the end marker if it is indirect. */ 1831241675Suqs 1832275432Sbapt if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) { 1833322249Sbapt deftype = ROFFDEF_USER; 1834322249Sbapt name = roff_getstrn(r, iname, namesz, &deftype); 1835322249Sbapt if (name == NULL) { 1836274880Sbapt mandoc_vmsg(MANDOCERR_STR_UNDEF, 1837275432Sbapt r->parse, ln, (int)(iname - buf->buf), 1838274880Sbapt "%.*s", (int)namesz, iname); 1839274880Sbapt namesz = 0; 1840274880Sbapt } else 1841274880Sbapt namesz = strlen(name); 1842274880Sbapt } else 1843274880Sbapt name = iname; 1844241675Suqs 1845274880Sbapt if (namesz) 1846274880Sbapt r->last->end = mandoc_strndup(name, namesz); 1847241675Suqs 1848275432Sbapt if (*cp != '\0') 1849274880Sbapt mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, 1850322249Sbapt ln, pos, ".%s ... %s", roff_name[tok], cp); 1851241675Suqs 1852294113Sbapt return ROFF_IGN; 1853241675Suqs} 1854241675Suqs 1855241675Suqsstatic enum rofferr 1856241675Suqsroff_block_sub(ROFF_ARGS) 1857241675Suqs{ 1858322249Sbapt enum roff_tok t; 1859241675Suqs int i, j; 1860241675Suqs 1861241675Suqs /* 1862241675Suqs * First check whether a custom macro exists at this level. If 1863241675Suqs * it does, then check against it. This is some of groff's 1864241675Suqs * stranger behaviours. If we encountered a custom end-scope 1865241675Suqs * tag and that tag also happens to be a "real" macro, then we 1866241675Suqs * need to try interpreting it again as a real macro. If it's 1867241675Suqs * not, then return ignore. Else continue. 1868241675Suqs */ 1869241675Suqs 1870241675Suqs if (r->last->end) { 1871241675Suqs for (i = pos, j = 0; r->last->end[j]; j++, i++) 1872275432Sbapt if (buf->buf[i] != r->last->end[j]) 1873241675Suqs break; 1874241675Suqs 1875275432Sbapt if (r->last->end[j] == '\0' && 1876275432Sbapt (buf->buf[i] == '\0' || 1877275432Sbapt buf->buf[i] == ' ' || 1878275432Sbapt buf->buf[i] == '\t')) { 1879241675Suqs roffnode_pop(r); 1880241675Suqs roffnode_cleanscope(r); 1881241675Suqs 1882275432Sbapt while (buf->buf[i] == ' ' || buf->buf[i] == '\t') 1883241675Suqs i++; 1884241675Suqs 1885241675Suqs pos = i; 1886275432Sbapt if (roff_parse(r, buf->buf, &pos, ln, ppos) != 1887322249Sbapt TOKEN_NONE) 1888294113Sbapt return ROFF_RERUN; 1889294113Sbapt return ROFF_IGN; 1890241675Suqs } 1891241675Suqs } 1892241675Suqs 1893241675Suqs /* 1894241675Suqs * If we have no custom end-query or lookup failed, then try 1895241675Suqs * pulling it out of the hashtable. 1896241675Suqs */ 1897241675Suqs 1898275432Sbapt t = roff_parse(r, buf->buf, &pos, ln, ppos); 1899241675Suqs 1900275432Sbapt if (t != ROFF_cblock) { 1901275432Sbapt if (tok != ROFF_ig) 1902275432Sbapt roff_setstr(r, r->last->name, buf->buf + ppos, 2); 1903294113Sbapt return ROFF_IGN; 1904241675Suqs } 1905241675Suqs 1906294113Sbapt return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs); 1907241675Suqs} 1908241675Suqs 1909241675Suqsstatic enum rofferr 1910241675Suqsroff_block_text(ROFF_ARGS) 1911241675Suqs{ 1912241675Suqs 1913275432Sbapt if (tok != ROFF_ig) 1914275432Sbapt roff_setstr(r, r->last->name, buf->buf + pos, 2); 1915241675Suqs 1916294113Sbapt return ROFF_IGN; 1917241675Suqs} 1918241675Suqs 1919241675Suqsstatic enum rofferr 1920241675Suqsroff_cond_sub(ROFF_ARGS) 1921241675Suqs{ 1922322249Sbapt enum roff_tok t; 1923241675Suqs char *ep; 1924274880Sbapt int rr; 1925241675Suqs 1926241675Suqs rr = r->last->rule; 1927241675Suqs roffnode_cleanscope(r); 1928241675Suqs 1929241675Suqs /* 1930274880Sbapt * If `\}' occurs on a macro line without a preceding macro, 1931274880Sbapt * drop the line completely. 1932274880Sbapt */ 1933274880Sbapt 1934275432Sbapt ep = buf->buf + pos; 1935275432Sbapt if (ep[0] == '\\' && ep[1] == '}') 1936274880Sbapt rr = 0; 1937274880Sbapt 1938261344Suqs /* Always check for the closing delimiter `\}'. */ 1939241675Suqs 1940275432Sbapt while ((ep = strchr(ep, '\\')) != NULL) { 1941322249Sbapt switch (ep[1]) { 1942322249Sbapt case '}': 1943322249Sbapt memmove(ep, ep + 2, strlen(ep + 2) + 1); 1944322249Sbapt roff_ccond(r, ln, ep - buf->buf); 1945322249Sbapt break; 1946322249Sbapt case '\0': 1947322249Sbapt ++ep; 1948322249Sbapt break; 1949322249Sbapt default: 1950322249Sbapt ep += 2; 1951322249Sbapt break; 1952274880Sbapt } 1953261344Suqs } 1954322249Sbapt 1955322249Sbapt /* 1956322249Sbapt * Fully handle known macros when they are structurally 1957322249Sbapt * required or when the conditional evaluated to true. 1958322249Sbapt */ 1959322249Sbapt 1960322249Sbapt t = roff_parse(r, buf->buf, &pos, ln, ppos); 1961322249Sbapt return t != TOKEN_NONE && (rr || roffs[t].flags & ROFFMAC_STRUCT) 1962322249Sbapt ? (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs) : rr 1963322249Sbapt ? ROFF_CONT : ROFF_IGN; 1964241675Suqs} 1965241675Suqs 1966241675Suqsstatic enum rofferr 1967241675Suqsroff_cond_text(ROFF_ARGS) 1968241675Suqs{ 1969241675Suqs char *ep; 1970274880Sbapt int rr; 1971241675Suqs 1972241675Suqs rr = r->last->rule; 1973241675Suqs roffnode_cleanscope(r); 1974241675Suqs 1975275432Sbapt ep = buf->buf + pos; 1976275432Sbapt while ((ep = strchr(ep, '\\')) != NULL) { 1977275432Sbapt if (*(++ep) == '}') { 1978274880Sbapt *ep = '&'; 1979275432Sbapt roff_ccond(r, ln, ep - buf->buf - 1); 1980274880Sbapt } 1981279527Sbapt if (*ep != '\0') 1982279527Sbapt ++ep; 1983241675Suqs } 1984294113Sbapt return rr ? ROFF_CONT : ROFF_IGN; 1985241675Suqs} 1986241675Suqs 1987294113Sbapt/* --- handling of numeric and conditional expressions -------------------- */ 1988294113Sbapt 1989274880Sbapt/* 1990274880Sbapt * Parse a single signed integer number. Stop at the first non-digit. 1991274880Sbapt * If there is at least one digit, return success and advance the 1992274880Sbapt * parse point, else return failure and let the parse point unchanged. 1993274880Sbapt * Ignore overflows, treat them just like the C language. 1994274880Sbapt */ 1995261344Suqsstatic int 1996279527Sbaptroff_getnum(const char *v, int *pos, int *res, int flags) 1997261344Suqs{ 1998279527Sbapt int myres, scaled, n, p; 1999261344Suqs 2000274880Sbapt if (NULL == res) 2001274880Sbapt res = &myres; 2002274880Sbapt 2003261344Suqs p = *pos; 2004261344Suqs n = v[p] == '-'; 2005279527Sbapt if (n || v[p] == '+') 2006261344Suqs p++; 2007261344Suqs 2008279527Sbapt if (flags & ROFFNUM_WHITE) 2009279527Sbapt while (isspace((unsigned char)v[p])) 2010279527Sbapt p++; 2011279527Sbapt 2012261344Suqs for (*res = 0; isdigit((unsigned char)v[p]); p++) 2013274880Sbapt *res = 10 * *res + v[p] - '0'; 2014261344Suqs if (p == *pos + n) 2015261344Suqs return 0; 2016261344Suqs 2017261344Suqs if (n) 2018261344Suqs *res = -*res; 2019261344Suqs 2020279527Sbapt /* Each number may be followed by one optional scaling unit. */ 2021279527Sbapt 2022279527Sbapt switch (v[p]) { 2023279527Sbapt case 'f': 2024279527Sbapt scaled = *res * 65536; 2025279527Sbapt break; 2026279527Sbapt case 'i': 2027279527Sbapt scaled = *res * 240; 2028279527Sbapt break; 2029279527Sbapt case 'c': 2030279527Sbapt scaled = *res * 240 / 2.54; 2031279527Sbapt break; 2032279527Sbapt case 'v': 2033279527Sbapt case 'P': 2034279527Sbapt scaled = *res * 40; 2035279527Sbapt break; 2036279527Sbapt case 'm': 2037279527Sbapt case 'n': 2038279527Sbapt scaled = *res * 24; 2039279527Sbapt break; 2040279527Sbapt case 'p': 2041279527Sbapt scaled = *res * 10 / 3; 2042279527Sbapt break; 2043279527Sbapt case 'u': 2044279527Sbapt scaled = *res; 2045279527Sbapt break; 2046279527Sbapt case 'M': 2047279527Sbapt scaled = *res * 6 / 25; 2048279527Sbapt break; 2049279527Sbapt default: 2050279527Sbapt scaled = *res; 2051279527Sbapt p--; 2052279527Sbapt break; 2053279527Sbapt } 2054279527Sbapt if (flags & ROFFNUM_SCALE) 2055279527Sbapt *res = scaled; 2056279527Sbapt 2057279527Sbapt *pos = p + 1; 2058294113Sbapt return 1; 2059261344Suqs} 2060261344Suqs 2061274880Sbapt/* 2062274880Sbapt * Evaluate a string comparison condition. 2063274880Sbapt * The first character is the delimiter. 2064274880Sbapt * Succeed if the string up to its second occurrence 2065274880Sbapt * matches the string up to its third occurence. 2066274880Sbapt * Advance the cursor after the third occurrence 2067274880Sbapt * or lacking that, to the end of the line. 2068274880Sbapt */ 2069261344Suqsstatic int 2070274880Sbaptroff_evalstrcond(const char *v, int *pos) 2071261344Suqs{ 2072274880Sbapt const char *s1, *s2, *s3; 2073274880Sbapt int match; 2074261344Suqs 2075274880Sbapt match = 0; 2076274880Sbapt s1 = v + *pos; /* initial delimiter */ 2077274880Sbapt s2 = s1 + 1; /* for scanning the first string */ 2078274880Sbapt s3 = strchr(s2, *s1); /* for scanning the second string */ 2079261344Suqs 2080274880Sbapt if (NULL == s3) /* found no middle delimiter */ 2081274880Sbapt goto out; 2082274880Sbapt 2083274880Sbapt while ('\0' != *++s3) { 2084274880Sbapt if (*s2 != *s3) { /* mismatch */ 2085274880Sbapt s3 = strchr(s3, *s1); 2086274880Sbapt break; 2087274880Sbapt } 2088274880Sbapt if (*s3 == *s1) { /* found the final delimiter */ 2089274880Sbapt match = 1; 2090274880Sbapt break; 2091274880Sbapt } 2092274880Sbapt s2++; 2093261344Suqs } 2094261344Suqs 2095274880Sbaptout: 2096274880Sbapt if (NULL == s3) 2097274880Sbapt s3 = strchr(s2, '\0'); 2098279527Sbapt else if (*s3 != '\0') 2099274880Sbapt s3++; 2100274880Sbapt *pos = s3 - v; 2101294113Sbapt return match; 2102261344Suqs} 2103261344Suqs 2104274880Sbapt/* 2105274880Sbapt * Evaluate an optionally negated single character, numerical, 2106274880Sbapt * or string condition. 2107274880Sbapt */ 2108274880Sbaptstatic int 2109294113Sbaptroff_evalcond(struct roff *r, int ln, char *v, int *pos) 2110241675Suqs{ 2111294113Sbapt char *cp, *name; 2112294113Sbapt size_t sz; 2113322249Sbapt int deftype, number, savepos, istrue, wanttrue; 2114241675Suqs 2115274880Sbapt if ('!' == v[*pos]) { 2116274880Sbapt wanttrue = 0; 2117274880Sbapt (*pos)++; 2118274880Sbapt } else 2119274880Sbapt wanttrue = 1; 2120274880Sbapt 2121241675Suqs switch (v[*pos]) { 2122279527Sbapt case '\0': 2123294113Sbapt return 0; 2124274880Sbapt case 'n': 2125274880Sbapt case 'o': 2126241675Suqs (*pos)++; 2127294113Sbapt return wanttrue; 2128274880Sbapt case 'c': 2129274880Sbapt case 'e': 2130274880Sbapt case 't': 2131275432Sbapt case 'v': 2132241675Suqs (*pos)++; 2133294113Sbapt return !wanttrue; 2134322249Sbapt case 'd': 2135294113Sbapt case 'r': 2136322249Sbapt cp = v + *pos + 1; 2137322249Sbapt while (*cp == ' ') 2138322249Sbapt cp++; 2139322249Sbapt name = cp; 2140322249Sbapt sz = roff_getname(r, &cp, ln, cp - v); 2141322249Sbapt if (sz == 0) 2142322249Sbapt istrue = 0; 2143322249Sbapt else if (v[*pos] == 'r') 2144322249Sbapt istrue = roff_hasregn(r, name, sz); 2145322249Sbapt else { 2146322249Sbapt deftype = ROFFDEF_ANY; 2147322249Sbapt roff_getstrn(r, name, sz, &deftype); 2148322249Sbapt istrue = !!deftype; 2149322249Sbapt } 2150294113Sbapt *pos = cp - v; 2151322249Sbapt return istrue == wanttrue; 2152241675Suqs default: 2153241675Suqs break; 2154241675Suqs } 2155241675Suqs 2156279527Sbapt savepos = *pos; 2157279527Sbapt if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE)) 2158294113Sbapt return (number > 0) == wanttrue; 2159279527Sbapt else if (*pos == savepos) 2160294113Sbapt return roff_evalstrcond(v, pos) == wanttrue; 2161274880Sbapt else 2162294113Sbapt return 0; 2163241675Suqs} 2164241675Suqs 2165241675Suqsstatic enum rofferr 2166241675Suqsroff_line_ignore(ROFF_ARGS) 2167241675Suqs{ 2168241675Suqs 2169294113Sbapt return ROFF_IGN; 2170241675Suqs} 2171241675Suqs 2172241675Suqsstatic enum rofferr 2173279527Sbaptroff_insec(ROFF_ARGS) 2174279527Sbapt{ 2175279527Sbapt 2176279527Sbapt mandoc_msg(MANDOCERR_REQ_INSEC, r->parse, 2177322249Sbapt ln, ppos, roff_name[tok]); 2178294113Sbapt return ROFF_IGN; 2179279527Sbapt} 2180279527Sbapt 2181279527Sbaptstatic enum rofferr 2182279527Sbaptroff_unsupp(ROFF_ARGS) 2183279527Sbapt{ 2184279527Sbapt 2185279527Sbapt mandoc_msg(MANDOCERR_REQ_UNSUPP, r->parse, 2186322249Sbapt ln, ppos, roff_name[tok]); 2187294113Sbapt return ROFF_IGN; 2188279527Sbapt} 2189279527Sbapt 2190279527Sbaptstatic enum rofferr 2191241675Suqsroff_cond(ROFF_ARGS) 2192241675Suqs{ 2193241675Suqs 2194261344Suqs roffnode_push(r, tok, NULL, ln, ppos); 2195261344Suqs 2196274880Sbapt /* 2197241675Suqs * An `.el' has no conditional body: it will consume the value 2198241675Suqs * of the current rstack entry set in prior `ie' calls or 2199274880Sbapt * defaults to DENY. 2200241675Suqs * 2201241675Suqs * If we're not an `el', however, then evaluate the conditional. 2202241675Suqs */ 2203241675Suqs 2204275432Sbapt r->last->rule = tok == ROFF_el ? 2205274880Sbapt (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) : 2206275432Sbapt roff_evalcond(r, ln, buf->buf, &pos); 2207241675Suqs 2208241675Suqs /* 2209241675Suqs * An if-else will put the NEGATION of the current evaluated 2210241675Suqs * conditional into the stack of rules. 2211241675Suqs */ 2212241675Suqs 2213275432Sbapt if (tok == ROFF_ie) { 2214274880Sbapt if (r->rstackpos + 1 == r->rstacksz) { 2215274880Sbapt r->rstacksz += 16; 2216274880Sbapt r->rstack = mandoc_reallocarray(r->rstack, 2217274880Sbapt r->rstacksz, sizeof(int)); 2218241675Suqs } 2219274880Sbapt r->rstack[++r->rstackpos] = !r->last->rule; 2220241675Suqs } 2221241675Suqs 2222241675Suqs /* If the parent has false as its rule, then so do we. */ 2223241675Suqs 2224274880Sbapt if (r->last->parent && !r->last->parent->rule) 2225274880Sbapt r->last->rule = 0; 2226241675Suqs 2227241675Suqs /* 2228261344Suqs * Determine scope. 2229261344Suqs * If there is nothing on the line after the conditional, 2230261344Suqs * not even whitespace, use next-line scope. 2231241675Suqs */ 2232241675Suqs 2233275432Sbapt if (buf->buf[pos] == '\0') { 2234261344Suqs r->last->endspan = 2; 2235261344Suqs goto out; 2236261344Suqs } 2237241675Suqs 2238275432Sbapt while (buf->buf[pos] == ' ') 2239261344Suqs pos++; 2240261344Suqs 2241261344Suqs /* An opening brace requests multiline scope. */ 2242261344Suqs 2243275432Sbapt if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') { 2244241675Suqs r->last->endspan = -1; 2245241675Suqs pos += 2; 2246294113Sbapt while (buf->buf[pos] == ' ') 2247294113Sbapt pos++; 2248261344Suqs goto out; 2249274880Sbapt } 2250241675Suqs 2251241675Suqs /* 2252261344Suqs * Anything else following the conditional causes 2253261344Suqs * single-line scope. Warn if the scope contains 2254261344Suqs * nothing but trailing whitespace. 2255241675Suqs */ 2256241675Suqs 2257275432Sbapt if (buf->buf[pos] == '\0') 2258274880Sbapt mandoc_msg(MANDOCERR_COND_EMPTY, r->parse, 2259322249Sbapt ln, ppos, roff_name[tok]); 2260241675Suqs 2261261344Suqs r->last->endspan = 1; 2262241675Suqs 2263261344Suqsout: 2264241675Suqs *offs = pos; 2265294113Sbapt return ROFF_RERUN; 2266241675Suqs} 2267241675Suqs 2268241675Suqsstatic enum rofferr 2269241675Suqsroff_ds(ROFF_ARGS) 2270241675Suqs{ 2271274880Sbapt char *string; 2272274880Sbapt const char *name; 2273274880Sbapt size_t namesz; 2274241675Suqs 2275279527Sbapt /* Ignore groff compatibility mode for now. */ 2276279527Sbapt 2277279527Sbapt if (tok == ROFF_ds1) 2278279527Sbapt tok = ROFF_ds; 2279279527Sbapt else if (tok == ROFF_as1) 2280279527Sbapt tok = ROFF_as; 2281279527Sbapt 2282241675Suqs /* 2283274880Sbapt * The first word is the name of the string. 2284274880Sbapt * If it is empty or terminated by an escape sequence, 2285274880Sbapt * abort the `ds' request without defining anything. 2286241675Suqs */ 2287241675Suqs 2288275432Sbapt name = string = buf->buf + pos; 2289275432Sbapt if (*name == '\0') 2290294113Sbapt return ROFF_IGN; 2291241675Suqs 2292274880Sbapt namesz = roff_getname(r, &string, ln, pos); 2293275432Sbapt if (name[namesz] == '\\') 2294294113Sbapt return ROFF_IGN; 2295274880Sbapt 2296274880Sbapt /* Read past the initial double-quote, if any. */ 2297275432Sbapt if (*string == '"') 2298241675Suqs string++; 2299241675Suqs 2300241675Suqs /* The rest is the value. */ 2301274880Sbapt roff_setstrn(&r->strtab, name, namesz, string, strlen(string), 2302274880Sbapt ROFF_as == tok); 2303322249Sbapt roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); 2304294113Sbapt return ROFF_IGN; 2305241675Suqs} 2306241675Suqs 2307274880Sbapt/* 2308274880Sbapt * Parse a single operator, one or two characters long. 2309274880Sbapt * If the operator is recognized, return success and advance the 2310274880Sbapt * parse point, else return failure and let the parse point unchanged. 2311274880Sbapt */ 2312274880Sbaptstatic int 2313274880Sbaptroff_getop(const char *v, int *pos, char *res) 2314274880Sbapt{ 2315274880Sbapt 2316274880Sbapt *res = v[*pos]; 2317274880Sbapt 2318274880Sbapt switch (*res) { 2319274880Sbapt case '+': 2320274880Sbapt case '-': 2321274880Sbapt case '*': 2322274880Sbapt case '/': 2323274880Sbapt case '%': 2324274880Sbapt case '&': 2325274880Sbapt case ':': 2326274880Sbapt break; 2327274880Sbapt case '<': 2328274880Sbapt switch (v[*pos + 1]) { 2329274880Sbapt case '=': 2330274880Sbapt *res = 'l'; 2331274880Sbapt (*pos)++; 2332274880Sbapt break; 2333274880Sbapt case '>': 2334274880Sbapt *res = '!'; 2335274880Sbapt (*pos)++; 2336274880Sbapt break; 2337274880Sbapt case '?': 2338274880Sbapt *res = 'i'; 2339274880Sbapt (*pos)++; 2340274880Sbapt break; 2341274880Sbapt default: 2342274880Sbapt break; 2343274880Sbapt } 2344274880Sbapt break; 2345274880Sbapt case '>': 2346274880Sbapt switch (v[*pos + 1]) { 2347274880Sbapt case '=': 2348274880Sbapt *res = 'g'; 2349274880Sbapt (*pos)++; 2350274880Sbapt break; 2351274880Sbapt case '?': 2352274880Sbapt *res = 'a'; 2353274880Sbapt (*pos)++; 2354274880Sbapt break; 2355274880Sbapt default: 2356274880Sbapt break; 2357274880Sbapt } 2358274880Sbapt break; 2359274880Sbapt case '=': 2360274880Sbapt if ('=' == v[*pos + 1]) 2361274880Sbapt (*pos)++; 2362274880Sbapt break; 2363274880Sbapt default: 2364294113Sbapt return 0; 2365274880Sbapt } 2366274880Sbapt (*pos)++; 2367274880Sbapt 2368294113Sbapt return *res; 2369274880Sbapt} 2370274880Sbapt 2371274880Sbapt/* 2372274880Sbapt * Evaluate either a parenthesized numeric expression 2373274880Sbapt * or a single signed integer number. 2374274880Sbapt */ 2375274880Sbaptstatic int 2376275432Sbaptroff_evalpar(struct roff *r, int ln, 2377279527Sbapt const char *v, int *pos, int *res, int flags) 2378274880Sbapt{ 2379274880Sbapt 2380274880Sbapt if ('(' != v[*pos]) 2381294113Sbapt return roff_getnum(v, pos, res, flags); 2382274880Sbapt 2383274880Sbapt (*pos)++; 2384279527Sbapt if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE)) 2385294113Sbapt return 0; 2386274880Sbapt 2387274880Sbapt /* 2388274880Sbapt * Omission of the closing parenthesis 2389274880Sbapt * is an error in validation mode, 2390274880Sbapt * but ignored in evaluation mode. 2391274880Sbapt */ 2392274880Sbapt 2393274880Sbapt if (')' == v[*pos]) 2394274880Sbapt (*pos)++; 2395274880Sbapt else if (NULL == res) 2396294113Sbapt return 0; 2397274880Sbapt 2398294113Sbapt return 1; 2399274880Sbapt} 2400274880Sbapt 2401274880Sbapt/* 2402274880Sbapt * Evaluate a complete numeric expression. 2403274880Sbapt * Proceed left to right, there is no concept of precedence. 2404274880Sbapt */ 2405274880Sbaptstatic int 2406275432Sbaptroff_evalnum(struct roff *r, int ln, const char *v, 2407279527Sbapt int *pos, int *res, int flags) 2408274880Sbapt{ 2409274880Sbapt int mypos, operand2; 2410274880Sbapt char operator; 2411274880Sbapt 2412274880Sbapt if (NULL == pos) { 2413274880Sbapt mypos = 0; 2414274880Sbapt pos = &mypos; 2415274880Sbapt } 2416274880Sbapt 2417279527Sbapt if (flags & ROFFNUM_WHITE) 2418274880Sbapt while (isspace((unsigned char)v[*pos])) 2419274880Sbapt (*pos)++; 2420274880Sbapt 2421279527Sbapt if ( ! roff_evalpar(r, ln, v, pos, res, flags)) 2422294113Sbapt return 0; 2423274880Sbapt 2424274880Sbapt while (1) { 2425279527Sbapt if (flags & ROFFNUM_WHITE) 2426274880Sbapt while (isspace((unsigned char)v[*pos])) 2427274880Sbapt (*pos)++; 2428274880Sbapt 2429274880Sbapt if ( ! roff_getop(v, pos, &operator)) 2430274880Sbapt break; 2431274880Sbapt 2432279527Sbapt if (flags & ROFFNUM_WHITE) 2433274880Sbapt while (isspace((unsigned char)v[*pos])) 2434274880Sbapt (*pos)++; 2435274880Sbapt 2436279527Sbapt if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags)) 2437294113Sbapt return 0; 2438274880Sbapt 2439279527Sbapt if (flags & ROFFNUM_WHITE) 2440274880Sbapt while (isspace((unsigned char)v[*pos])) 2441274880Sbapt (*pos)++; 2442274880Sbapt 2443274880Sbapt if (NULL == res) 2444274880Sbapt continue; 2445274880Sbapt 2446274880Sbapt switch (operator) { 2447274880Sbapt case '+': 2448274880Sbapt *res += operand2; 2449274880Sbapt break; 2450274880Sbapt case '-': 2451274880Sbapt *res -= operand2; 2452274880Sbapt break; 2453274880Sbapt case '*': 2454274880Sbapt *res *= operand2; 2455274880Sbapt break; 2456274880Sbapt case '/': 2457279527Sbapt if (operand2 == 0) { 2458275432Sbapt mandoc_msg(MANDOCERR_DIVZERO, 2459275432Sbapt r->parse, ln, *pos, v); 2460275432Sbapt *res = 0; 2461275432Sbapt break; 2462275432Sbapt } 2463274880Sbapt *res /= operand2; 2464274880Sbapt break; 2465274880Sbapt case '%': 2466279527Sbapt if (operand2 == 0) { 2467279527Sbapt mandoc_msg(MANDOCERR_DIVZERO, 2468279527Sbapt r->parse, ln, *pos, v); 2469279527Sbapt *res = 0; 2470279527Sbapt break; 2471279527Sbapt } 2472274880Sbapt *res %= operand2; 2473274880Sbapt break; 2474274880Sbapt case '<': 2475274880Sbapt *res = *res < operand2; 2476274880Sbapt break; 2477274880Sbapt case '>': 2478274880Sbapt *res = *res > operand2; 2479274880Sbapt break; 2480274880Sbapt case 'l': 2481274880Sbapt *res = *res <= operand2; 2482274880Sbapt break; 2483274880Sbapt case 'g': 2484274880Sbapt *res = *res >= operand2; 2485274880Sbapt break; 2486274880Sbapt case '=': 2487274880Sbapt *res = *res == operand2; 2488274880Sbapt break; 2489274880Sbapt case '!': 2490274880Sbapt *res = *res != operand2; 2491274880Sbapt break; 2492274880Sbapt case '&': 2493274880Sbapt *res = *res && operand2; 2494274880Sbapt break; 2495274880Sbapt case ':': 2496274880Sbapt *res = *res || operand2; 2497274880Sbapt break; 2498274880Sbapt case 'i': 2499274880Sbapt if (operand2 < *res) 2500274880Sbapt *res = operand2; 2501274880Sbapt break; 2502274880Sbapt case 'a': 2503274880Sbapt if (operand2 > *res) 2504274880Sbapt *res = operand2; 2505274880Sbapt break; 2506274880Sbapt default: 2507274880Sbapt abort(); 2508274880Sbapt } 2509274880Sbapt } 2510294113Sbapt return 1; 2511274880Sbapt} 2512274880Sbapt 2513294113Sbapt/* --- register management ------------------------------------------------ */ 2514294113Sbapt 2515261344Suqsvoid 2516261344Suqsroff_setreg(struct roff *r, const char *name, int val, char sign) 2517261344Suqs{ 2518261344Suqs struct roffreg *reg; 2519261344Suqs 2520261344Suqs /* Search for an existing register with the same name. */ 2521261344Suqs reg = r->regtab; 2522261344Suqs 2523261344Suqs while (reg && strcmp(name, reg->key.p)) 2524261344Suqs reg = reg->next; 2525261344Suqs 2526261344Suqs if (NULL == reg) { 2527261344Suqs /* Create a new register. */ 2528261344Suqs reg = mandoc_malloc(sizeof(struct roffreg)); 2529261344Suqs reg->key.p = mandoc_strdup(name); 2530261344Suqs reg->key.sz = strlen(name); 2531261344Suqs reg->val = 0; 2532261344Suqs reg->next = r->regtab; 2533261344Suqs r->regtab = reg; 2534261344Suqs } 2535261344Suqs 2536261344Suqs if ('+' == sign) 2537261344Suqs reg->val += val; 2538261344Suqs else if ('-' == sign) 2539261344Suqs reg->val -= val; 2540261344Suqs else 2541261344Suqs reg->val = val; 2542261344Suqs} 2543261344Suqs 2544274880Sbapt/* 2545274880Sbapt * Handle some predefined read-only number registers. 2546274880Sbapt * For now, return -1 if the requested register is not predefined; 2547274880Sbapt * in case a predefined read-only register having the value -1 2548274880Sbapt * were to turn up, another special value would have to be chosen. 2549274880Sbapt */ 2550274880Sbaptstatic int 2551294113Sbaptroff_getregro(const struct roff *r, const char *name) 2552274880Sbapt{ 2553274880Sbapt 2554274880Sbapt switch (*name) { 2555294113Sbapt case '$': /* Number of arguments of the last macro evaluated. */ 2556294113Sbapt return r->argc; 2557274880Sbapt case 'A': /* ASCII approximation mode is always off. */ 2558294113Sbapt return 0; 2559274880Sbapt case 'g': /* Groff compatibility mode is always on. */ 2560294113Sbapt return 1; 2561274880Sbapt case 'H': /* Fixed horizontal resolution. */ 2562294113Sbapt return 24; 2563274880Sbapt case 'j': /* Always adjust left margin only. */ 2564294113Sbapt return 0; 2565274880Sbapt case 'T': /* Some output device is always defined. */ 2566294113Sbapt return 1; 2567274880Sbapt case 'V': /* Fixed vertical resolution. */ 2568294113Sbapt return 40; 2569274880Sbapt default: 2570294113Sbapt return -1; 2571274880Sbapt } 2572274880Sbapt} 2573274880Sbapt 2574241675Suqsint 2575261344Suqsroff_getreg(const struct roff *r, const char *name) 2576241675Suqs{ 2577261344Suqs struct roffreg *reg; 2578274880Sbapt int val; 2579241675Suqs 2580274880Sbapt if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) { 2581294113Sbapt val = roff_getregro(r, name + 1); 2582274880Sbapt if (-1 != val) 2583294113Sbapt return val; 2584274880Sbapt } 2585274880Sbapt 2586261344Suqs for (reg = r->regtab; reg; reg = reg->next) 2587261344Suqs if (0 == strcmp(name, reg->key.p)) 2588294113Sbapt return reg->val; 2589261344Suqs 2590294113Sbapt return 0; 2591241675Suqs} 2592241675Suqs 2593261344Suqsstatic int 2594261344Suqsroff_getregn(const struct roff *r, const char *name, size_t len) 2595241675Suqs{ 2596261344Suqs struct roffreg *reg; 2597274880Sbapt int val; 2598241675Suqs 2599274880Sbapt if ('.' == name[0] && 2 == len) { 2600294113Sbapt val = roff_getregro(r, name + 1); 2601274880Sbapt if (-1 != val) 2602294113Sbapt return val; 2603274880Sbapt } 2604274880Sbapt 2605261344Suqs for (reg = r->regtab; reg; reg = reg->next) 2606261344Suqs if (len == reg->key.sz && 2607261344Suqs 0 == strncmp(name, reg->key.p, len)) 2608294113Sbapt return reg->val; 2609261344Suqs 2610294113Sbapt return 0; 2611241675Suqs} 2612241675Suqs 2613294113Sbaptstatic int 2614294113Sbaptroff_hasregn(const struct roff *r, const char *name, size_t len) 2615294113Sbapt{ 2616294113Sbapt struct roffreg *reg; 2617294113Sbapt int val; 2618294113Sbapt 2619294113Sbapt if ('.' == name[0] && 2 == len) { 2620294113Sbapt val = roff_getregro(r, name + 1); 2621294113Sbapt if (-1 != val) 2622294113Sbapt return 1; 2623294113Sbapt } 2624294113Sbapt 2625294113Sbapt for (reg = r->regtab; reg; reg = reg->next) 2626294113Sbapt if (len == reg->key.sz && 2627294113Sbapt 0 == strncmp(name, reg->key.p, len)) 2628294113Sbapt return 1; 2629294113Sbapt 2630294113Sbapt return 0; 2631294113Sbapt} 2632294113Sbapt 2633261344Suqsstatic void 2634261344Suqsroff_freereg(struct roffreg *reg) 2635241675Suqs{ 2636261344Suqs struct roffreg *old_reg; 2637241675Suqs 2638261344Suqs while (NULL != reg) { 2639261344Suqs free(reg->key.p); 2640261344Suqs old_reg = reg; 2641261344Suqs reg = reg->next; 2642261344Suqs free(old_reg); 2643261344Suqs } 2644241675Suqs} 2645241675Suqs 2646241675Suqsstatic enum rofferr 2647241675Suqsroff_nr(ROFF_ARGS) 2648241675Suqs{ 2649274880Sbapt char *key, *val; 2650274880Sbapt size_t keysz; 2651241675Suqs int iv; 2652261344Suqs char sign; 2653241675Suqs 2654275432Sbapt key = val = buf->buf + pos; 2655275432Sbapt if (*key == '\0') 2656294113Sbapt return ROFF_IGN; 2657241675Suqs 2658274880Sbapt keysz = roff_getname(r, &val, ln, pos); 2659275432Sbapt if (key[keysz] == '\\') 2660294113Sbapt return ROFF_IGN; 2661274880Sbapt key[keysz] = '\0'; 2662274880Sbapt 2663261344Suqs sign = *val; 2664275432Sbapt if (sign == '+' || sign == '-') 2665261344Suqs val++; 2666241675Suqs 2667279527Sbapt if (roff_evalnum(r, ln, val, NULL, &iv, ROFFNUM_SCALE)) 2668274880Sbapt roff_setreg(r, key, iv, sign); 2669261344Suqs 2670294113Sbapt return ROFF_IGN; 2671274880Sbapt} 2672261344Suqs 2673274880Sbaptstatic enum rofferr 2674274880Sbaptroff_rr(ROFF_ARGS) 2675274880Sbapt{ 2676274880Sbapt struct roffreg *reg, **prev; 2677274880Sbapt char *name, *cp; 2678274880Sbapt size_t namesz; 2679274880Sbapt 2680275432Sbapt name = cp = buf->buf + pos; 2681275432Sbapt if (*name == '\0') 2682294113Sbapt return ROFF_IGN; 2683274880Sbapt namesz = roff_getname(r, &cp, ln, pos); 2684274880Sbapt name[namesz] = '\0'; 2685274880Sbapt 2686274880Sbapt prev = &r->regtab; 2687274880Sbapt while (1) { 2688274880Sbapt reg = *prev; 2689275432Sbapt if (reg == NULL || !strcmp(name, reg->key.p)) 2690274880Sbapt break; 2691274880Sbapt prev = ®->next; 2692274880Sbapt } 2693275432Sbapt if (reg != NULL) { 2694274880Sbapt *prev = reg->next; 2695274880Sbapt free(reg->key.p); 2696274880Sbapt free(reg); 2697274880Sbapt } 2698294113Sbapt return ROFF_IGN; 2699241675Suqs} 2700241675Suqs 2701294113Sbapt/* --- handler functions for roff requests -------------------------------- */ 2702294113Sbapt 2703241675Suqsstatic enum rofferr 2704241675Suqsroff_rm(ROFF_ARGS) 2705241675Suqs{ 2706241675Suqs const char *name; 2707241675Suqs char *cp; 2708274880Sbapt size_t namesz; 2709241675Suqs 2710275432Sbapt cp = buf->buf + pos; 2711275432Sbapt while (*cp != '\0') { 2712274880Sbapt name = cp; 2713275432Sbapt namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf)); 2714274880Sbapt roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0); 2715322249Sbapt roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); 2716275432Sbapt if (name[namesz] == '\\') 2717274880Sbapt break; 2718241675Suqs } 2719294113Sbapt return ROFF_IGN; 2720241675Suqs} 2721241675Suqs 2722241675Suqsstatic enum rofferr 2723261344Suqsroff_it(ROFF_ARGS) 2724261344Suqs{ 2725261344Suqs int iv; 2726261344Suqs 2727261344Suqs /* Parse the number of lines. */ 2728279527Sbapt 2729279527Sbapt if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) { 2730274880Sbapt mandoc_msg(MANDOCERR_IT_NONUM, r->parse, 2731275432Sbapt ln, ppos, buf->buf + 1); 2732294113Sbapt return ROFF_IGN; 2733261344Suqs } 2734261344Suqs 2735279527Sbapt while (isspace((unsigned char)buf->buf[pos])) 2736279527Sbapt pos++; 2737279527Sbapt 2738279527Sbapt /* 2739279527Sbapt * Arm the input line trap. 2740279527Sbapt * Special-casing "an-trap" is an ugly workaround to cope 2741279527Sbapt * with DocBook stupidly fiddling with man(7) internals. 2742279527Sbapt */ 2743279527Sbapt 2744261344Suqs roffit_lines = iv; 2745279527Sbapt roffit_macro = mandoc_strdup(iv != 1 || 2746279527Sbapt strcmp(buf->buf + pos, "an-trap") ? 2747279527Sbapt buf->buf + pos : "br"); 2748294113Sbapt return ROFF_IGN; 2749261344Suqs} 2750261344Suqs 2751261344Suqsstatic enum rofferr 2752261344Suqsroff_Dd(ROFF_ARGS) 2753261344Suqs{ 2754322249Sbapt int mask; 2755322249Sbapt enum roff_tok t, te; 2756261344Suqs 2757322249Sbapt switch (tok) { 2758322249Sbapt case ROFF_Dd: 2759322249Sbapt tok = MDOC_Dd; 2760322249Sbapt te = MDOC_MAX; 2761322249Sbapt if (r->format == 0) 2762322249Sbapt r->format = MPARSE_MDOC; 2763322249Sbapt mask = MPARSE_MDOC | MPARSE_QUICK; 2764322249Sbapt break; 2765322249Sbapt case ROFF_TH: 2766322249Sbapt tok = MAN_TH; 2767322249Sbapt te = MAN_MAX; 2768322249Sbapt if (r->format == 0) 2769322249Sbapt r->format = MPARSE_MAN; 2770322249Sbapt mask = MPARSE_QUICK; 2771322249Sbapt break; 2772322249Sbapt default: 2773322249Sbapt abort(); 2774322249Sbapt } 2775322249Sbapt if ((r->options & mask) == 0) 2776322249Sbapt for (t = tok; t < te; t++) 2777322249Sbapt roff_setstr(r, roff_name[t], NULL, 0); 2778294113Sbapt return ROFF_CONT; 2779261344Suqs} 2780261344Suqs 2781261344Suqsstatic enum rofferr 2782241675Suqsroff_TE(ROFF_ARGS) 2783241675Suqs{ 2784322249Sbapt if (r->tbl == NULL) { 2785274880Sbapt mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 2786274880Sbapt ln, ppos, "TE"); 2787322249Sbapt return ROFF_IGN; 2788322249Sbapt } 2789322249Sbapt if (tbl_end(r->tbl) == 0) { 2790322249Sbapt r->tbl = NULL; 2791279527Sbapt free(buf->buf); 2792279527Sbapt buf->buf = mandoc_strdup(".sp"); 2793279527Sbapt buf->sz = 4; 2794294113Sbapt return ROFF_REPARSE; 2795279527Sbapt } 2796322249Sbapt r->tbl = NULL; 2797294113Sbapt return ROFF_IGN; 2798241675Suqs} 2799241675Suqs 2800241675Suqsstatic enum rofferr 2801241675Suqsroff_T_(ROFF_ARGS) 2802241675Suqs{ 2803241675Suqs 2804241675Suqs if (NULL == r->tbl) 2805274880Sbapt mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 2806274880Sbapt ln, ppos, "T&"); 2807241675Suqs else 2808322249Sbapt tbl_restart(ln, ppos, r->tbl); 2809241675Suqs 2810294113Sbapt return ROFF_IGN; 2811241675Suqs} 2812241675Suqs 2813275432Sbapt/* 2814275432Sbapt * Handle in-line equation delimiters. 2815275432Sbapt */ 2816275432Sbaptstatic enum rofferr 2817275432Sbaptroff_eqndelim(struct roff *r, struct buf *buf, int pos) 2818241675Suqs{ 2819275432Sbapt char *cp1, *cp2; 2820275432Sbapt const char *bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr; 2821241675Suqs 2822275432Sbapt /* 2823275432Sbapt * Outside equations, look for an opening delimiter. 2824275432Sbapt * If we are inside an equation, we already know it is 2825275432Sbapt * in-line, or this function wouldn't have been called; 2826275432Sbapt * so look for a closing delimiter. 2827275432Sbapt */ 2828275432Sbapt 2829275432Sbapt cp1 = buf->buf + pos; 2830275432Sbapt cp2 = strchr(cp1, r->eqn == NULL ? 2831275432Sbapt r->last_eqn->odelim : r->last_eqn->cdelim); 2832275432Sbapt if (cp2 == NULL) 2833294113Sbapt return ROFF_CONT; 2834275432Sbapt 2835275432Sbapt *cp2++ = '\0'; 2836275432Sbapt bef_pr = bef_nl = aft_nl = aft_pr = ""; 2837275432Sbapt 2838275432Sbapt /* Handle preceding text, protecting whitespace. */ 2839275432Sbapt 2840275432Sbapt if (*buf->buf != '\0') { 2841275432Sbapt if (r->eqn == NULL) 2842275432Sbapt bef_pr = "\\&"; 2843275432Sbapt bef_nl = "\n"; 2844275432Sbapt } 2845275432Sbapt 2846275432Sbapt /* 2847275432Sbapt * Prepare replacing the delimiter with an equation macro 2848275432Sbapt * and drop leading white space from the equation. 2849275432Sbapt */ 2850275432Sbapt 2851275432Sbapt if (r->eqn == NULL) { 2852275432Sbapt while (*cp2 == ' ') 2853275432Sbapt cp2++; 2854275432Sbapt mac = ".EQ"; 2855275432Sbapt } else 2856275432Sbapt mac = ".EN"; 2857275432Sbapt 2858275432Sbapt /* Handle following text, protecting whitespace. */ 2859275432Sbapt 2860275432Sbapt if (*cp2 != '\0') { 2861275432Sbapt aft_nl = "\n"; 2862275432Sbapt if (r->eqn != NULL) 2863275432Sbapt aft_pr = "\\&"; 2864275432Sbapt } 2865275432Sbapt 2866275432Sbapt /* Do the actual replacement. */ 2867275432Sbapt 2868275432Sbapt buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf, 2869275432Sbapt bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1; 2870275432Sbapt free(buf->buf); 2871275432Sbapt buf->buf = cp1; 2872275432Sbapt 2873275432Sbapt /* Toggle the in-line state of the eqn subsystem. */ 2874275432Sbapt 2875275432Sbapt r->eqn_inline = r->eqn == NULL; 2876294113Sbapt return ROFF_REPARSE; 2877241675Suqs} 2878241675Suqs 2879275432Sbaptstatic enum rofferr 2880275432Sbaptroff_EQ(ROFF_ARGS) 2881241675Suqs{ 2882322249Sbapt struct roff_node *n; 2883241675Suqs 2884322249Sbapt if (r->man->macroset == MACROSET_MAN) 2885322249Sbapt man_breakscope(r->man, ROFF_EQ); 2886322249Sbapt n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE); 2887322249Sbapt if (ln > r->man->last->line) 2888322249Sbapt n->flags |= NODE_LINE; 2889322249Sbapt n->eqn = mandoc_calloc(1, sizeof(*n->eqn)); 2890322249Sbapt n->eqn->expectargs = UINT_MAX; 2891322249Sbapt roff_node_append(r->man, n); 2892322249Sbapt r->man->next = ROFF_NEXT_SIBLING; 2893322249Sbapt 2894275432Sbapt assert(r->eqn == NULL); 2895322249Sbapt if (r->last_eqn == NULL) 2896322249Sbapt r->last_eqn = eqn_alloc(r->parse); 2897322249Sbapt else 2898322249Sbapt eqn_reset(r->last_eqn); 2899322249Sbapt r->eqn = r->last_eqn; 2900322249Sbapt r->eqn->node = n; 2901241675Suqs 2902275432Sbapt if (buf->buf[pos] != '\0') 2903275432Sbapt mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, 2904275432Sbapt ".EQ %s", buf->buf + pos); 2905241675Suqs 2906294113Sbapt return ROFF_IGN; 2907241675Suqs} 2908241675Suqs 2909241675Suqsstatic enum rofferr 2910241675Suqsroff_EN(ROFF_ARGS) 2911241675Suqs{ 2912322249Sbapt if (r->eqn != NULL) { 2913322249Sbapt eqn_parse(r->eqn); 2914322249Sbapt r->eqn = NULL; 2915322249Sbapt } else 2916322249Sbapt mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN"); 2917322249Sbapt if (buf->buf[pos] != '\0') 2918322249Sbapt mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, 2919322249Sbapt "EN %s", buf->buf + pos); 2920294113Sbapt return ROFF_IGN; 2921241675Suqs} 2922241675Suqs 2923241675Suqsstatic enum rofferr 2924241675Suqsroff_TS(ROFF_ARGS) 2925241675Suqs{ 2926322249Sbapt if (r->tbl != NULL) { 2927274880Sbapt mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse, 2928274880Sbapt ln, ppos, "TS breaks TS"); 2929322249Sbapt tbl_end(r->tbl); 2930241675Suqs } 2931322249Sbapt r->tbl = tbl_alloc(ppos, ln, r->parse); 2932241675Suqs if (r->last_tbl) 2933322249Sbapt r->last_tbl->next = r->tbl; 2934241675Suqs else 2935322249Sbapt r->first_tbl = r->tbl; 2936322249Sbapt r->last_tbl = r->tbl; 2937322249Sbapt return ROFF_IGN; 2938322249Sbapt} 2939241675Suqs 2940322249Sbaptstatic enum rofferr 2941322249Sbaptroff_onearg(ROFF_ARGS) 2942322249Sbapt{ 2943322249Sbapt struct roff_node *n; 2944322249Sbapt char *cp; 2945322249Sbapt int npos; 2946322249Sbapt 2947322249Sbapt if (r->man->flags & (MAN_BLINE | MAN_ELINE) && 2948322249Sbapt (tok == ROFF_ce || tok == ROFF_rj || tok == ROFF_sp || 2949322249Sbapt tok == ROFF_ti)) 2950322249Sbapt man_breakscope(r->man, tok); 2951322249Sbapt 2952322249Sbapt if (roffce_node != NULL && (tok == ROFF_ce || tok == ROFF_rj)) { 2953322249Sbapt r->man->last = roffce_node; 2954322249Sbapt r->man->next = ROFF_NEXT_SIBLING; 2955322249Sbapt } 2956322249Sbapt 2957322249Sbapt roff_elem_alloc(r->man, ln, ppos, tok); 2958322249Sbapt n = r->man->last; 2959322249Sbapt 2960322249Sbapt cp = buf->buf + pos; 2961322249Sbapt if (*cp != '\0') { 2962322249Sbapt while (*cp != '\0' && *cp != ' ') 2963322249Sbapt cp++; 2964322249Sbapt while (*cp == ' ') 2965322249Sbapt *cp++ = '\0'; 2966322249Sbapt if (*cp != '\0') 2967322249Sbapt mandoc_vmsg(MANDOCERR_ARG_EXCESS, 2968322249Sbapt r->parse, ln, cp - buf->buf, 2969322249Sbapt "%s ... %s", roff_name[tok], cp); 2970322249Sbapt roff_word_alloc(r->man, ln, pos, buf->buf + pos); 2971322249Sbapt } 2972322249Sbapt 2973322249Sbapt if (tok == ROFF_ce || tok == ROFF_rj) { 2974322249Sbapt if (r->man->last->type == ROFFT_ELEM) { 2975322249Sbapt roff_word_alloc(r->man, ln, pos, "1"); 2976322249Sbapt r->man->last->flags |= NODE_NOSRC; 2977322249Sbapt } 2978322249Sbapt npos = 0; 2979322249Sbapt if (roff_evalnum(r, ln, r->man->last->string, &npos, 2980322249Sbapt &roffce_lines, 0) == 0) { 2981322249Sbapt mandoc_vmsg(MANDOCERR_CE_NONUM, 2982322249Sbapt r->parse, ln, pos, "ce %s", buf->buf + pos); 2983322249Sbapt roffce_lines = 1; 2984322249Sbapt } 2985322249Sbapt if (roffce_lines < 1) { 2986322249Sbapt r->man->last = r->man->last->parent; 2987322249Sbapt roffce_node = NULL; 2988322249Sbapt roffce_lines = 0; 2989322249Sbapt } else 2990322249Sbapt roffce_node = r->man->last->parent; 2991322249Sbapt } else { 2992322249Sbapt n->flags |= NODE_VALID | NODE_ENDED; 2993322249Sbapt r->man->last = n; 2994322249Sbapt } 2995322249Sbapt n->flags |= NODE_LINE; 2996322249Sbapt r->man->next = ROFF_NEXT_SIBLING; 2997294113Sbapt return ROFF_IGN; 2998241675Suqs} 2999241675Suqs 3000241675Suqsstatic enum rofferr 3001322249Sbaptroff_manyarg(ROFF_ARGS) 3002279527Sbapt{ 3003322249Sbapt struct roff_node *n; 3004322249Sbapt char *sp, *ep; 3005279527Sbapt 3006322249Sbapt roff_elem_alloc(r->man, ln, ppos, tok); 3007322249Sbapt n = r->man->last; 3008322249Sbapt 3009322249Sbapt for (sp = ep = buf->buf + pos; *sp != '\0'; sp = ep) { 3010322249Sbapt while (*ep != '\0' && *ep != ' ') 3011322249Sbapt ep++; 3012322249Sbapt while (*ep == ' ') 3013322249Sbapt *ep++ = '\0'; 3014322249Sbapt roff_word_alloc(r->man, ln, sp - buf->buf, sp); 3015322249Sbapt } 3016322249Sbapt 3017322249Sbapt n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED; 3018322249Sbapt r->man->last = n; 3019322249Sbapt r->man->next = ROFF_NEXT_SIBLING; 3020322249Sbapt return ROFF_IGN; 3021279527Sbapt} 3022279527Sbapt 3023279527Sbaptstatic enum rofferr 3024322249Sbaptroff_als(ROFF_ARGS) 3025322249Sbapt{ 3026322249Sbapt char *oldn, *newn, *end, *value; 3027322249Sbapt size_t oldsz, newsz, valsz; 3028322249Sbapt 3029322249Sbapt newn = oldn = buf->buf + pos; 3030322249Sbapt if (*newn == '\0') 3031322249Sbapt return ROFF_IGN; 3032322249Sbapt 3033322249Sbapt newsz = roff_getname(r, &oldn, ln, pos); 3034322249Sbapt if (newn[newsz] == '\\' || *oldn == '\0') 3035322249Sbapt return ROFF_IGN; 3036322249Sbapt 3037322249Sbapt end = oldn; 3038322249Sbapt oldsz = roff_getname(r, &end, ln, oldn - buf->buf); 3039322249Sbapt if (oldsz == 0) 3040322249Sbapt return ROFF_IGN; 3041322249Sbapt 3042322249Sbapt valsz = mandoc_asprintf(&value, ".%.*s \\$*\\\"\n", 3043322249Sbapt (int)oldsz, oldn); 3044322249Sbapt roff_setstrn(&r->strtab, newn, newsz, value, valsz, 0); 3045322249Sbapt roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); 3046322249Sbapt free(value); 3047322249Sbapt return ROFF_IGN; 3048322249Sbapt} 3049322249Sbapt 3050322249Sbaptstatic enum rofferr 3051322249Sbaptroff_br(ROFF_ARGS) 3052322249Sbapt{ 3053322249Sbapt if (r->man->flags & (MAN_BLINE | MAN_ELINE)) 3054322249Sbapt man_breakscope(r->man, ROFF_br); 3055322249Sbapt roff_elem_alloc(r->man, ln, ppos, ROFF_br); 3056322249Sbapt if (buf->buf[pos] != '\0') 3057322249Sbapt mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, 3058322249Sbapt "%s %s", roff_name[tok], buf->buf + pos); 3059322249Sbapt r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED; 3060322249Sbapt r->man->next = ROFF_NEXT_SIBLING; 3061322249Sbapt return ROFF_IGN; 3062322249Sbapt} 3063322249Sbapt 3064322249Sbaptstatic enum rofferr 3065261344Suqsroff_cc(ROFF_ARGS) 3066261344Suqs{ 3067261344Suqs const char *p; 3068261344Suqs 3069275432Sbapt p = buf->buf + pos; 3070261344Suqs 3071275432Sbapt if (*p == '\0' || (r->control = *p++) == '.') 3072322249Sbapt r->control = '\0'; 3073261344Suqs 3074275432Sbapt if (*p != '\0') 3075279527Sbapt mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, 3076279527Sbapt ln, p - buf->buf, "cc ... %s", p); 3077261344Suqs 3078294113Sbapt return ROFF_IGN; 3079261344Suqs} 3080261344Suqs 3081261344Suqsstatic enum rofferr 3082322249Sbaptroff_ec(ROFF_ARGS) 3083322249Sbapt{ 3084322249Sbapt const char *p; 3085322249Sbapt 3086322249Sbapt p = buf->buf + pos; 3087322249Sbapt if (*p == '\0') 3088322249Sbapt r->escape = '\\'; 3089322249Sbapt else { 3090322249Sbapt r->escape = *p; 3091322249Sbapt if (*++p != '\0') 3092322249Sbapt mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, 3093322249Sbapt ln, p - buf->buf, "ec ... %s", p); 3094322249Sbapt } 3095322249Sbapt return ROFF_IGN; 3096322249Sbapt} 3097322249Sbapt 3098322249Sbaptstatic enum rofferr 3099322249Sbaptroff_eo(ROFF_ARGS) 3100322249Sbapt{ 3101322249Sbapt r->escape = '\0'; 3102322249Sbapt if (buf->buf[pos] != '\0') 3103322249Sbapt mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, 3104322249Sbapt ln, pos, "eo %s", buf->buf + pos); 3105322249Sbapt return ROFF_IGN; 3106322249Sbapt} 3107322249Sbapt 3108322249Sbaptstatic enum rofferr 3109241675Suqsroff_tr(ROFF_ARGS) 3110241675Suqs{ 3111241675Suqs const char *p, *first, *second; 3112241675Suqs size_t fsz, ssz; 3113241675Suqs enum mandoc_esc esc; 3114241675Suqs 3115275432Sbapt p = buf->buf + pos; 3116241675Suqs 3117275432Sbapt if (*p == '\0') { 3118279527Sbapt mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, ln, ppos, "tr"); 3119294113Sbapt return ROFF_IGN; 3120241675Suqs } 3121241675Suqs 3122275432Sbapt while (*p != '\0') { 3123241675Suqs fsz = ssz = 1; 3124241675Suqs 3125241675Suqs first = p++; 3126275432Sbapt if (*first == '\\') { 3127241675Suqs esc = mandoc_escape(&p, NULL, NULL); 3128275432Sbapt if (esc == ESCAPE_ERROR) { 3129274880Sbapt mandoc_msg(MANDOCERR_ESC_BAD, r->parse, 3130275432Sbapt ln, (int)(p - buf->buf), first); 3131294113Sbapt return ROFF_IGN; 3132241675Suqs } 3133241675Suqs fsz = (size_t)(p - first); 3134241675Suqs } 3135241675Suqs 3136241675Suqs second = p++; 3137275432Sbapt if (*second == '\\') { 3138241675Suqs esc = mandoc_escape(&p, NULL, NULL); 3139275432Sbapt if (esc == ESCAPE_ERROR) { 3140274880Sbapt mandoc_msg(MANDOCERR_ESC_BAD, r->parse, 3141275432Sbapt ln, (int)(p - buf->buf), second); 3142294113Sbapt return ROFF_IGN; 3143241675Suqs } 3144241675Suqs ssz = (size_t)(p - second); 3145275432Sbapt } else if (*second == '\0') { 3146279527Sbapt mandoc_vmsg(MANDOCERR_TR_ODD, r->parse, 3147279527Sbapt ln, first - buf->buf, "tr %s", first); 3148241675Suqs second = " "; 3149241675Suqs p--; 3150241675Suqs } 3151241675Suqs 3152241675Suqs if (fsz > 1) { 3153274880Sbapt roff_setstrn(&r->xmbtab, first, fsz, 3154274880Sbapt second, ssz, 0); 3155241675Suqs continue; 3156241675Suqs } 3157241675Suqs 3158275432Sbapt if (r->xtab == NULL) 3159274880Sbapt r->xtab = mandoc_calloc(128, 3160274880Sbapt sizeof(struct roffstr)); 3161241675Suqs 3162241675Suqs free(r->xtab[(int)*first].p); 3163241675Suqs r->xtab[(int)*first].p = mandoc_strndup(second, ssz); 3164241675Suqs r->xtab[(int)*first].sz = ssz; 3165241675Suqs } 3166241675Suqs 3167294113Sbapt return ROFF_IGN; 3168241675Suqs} 3169241675Suqs 3170241675Suqsstatic enum rofferr 3171322249Sbaptroff_rn(ROFF_ARGS) 3172322249Sbapt{ 3173322249Sbapt const char *value; 3174322249Sbapt char *oldn, *newn, *end; 3175322249Sbapt size_t oldsz, newsz; 3176322249Sbapt int deftype; 3177322249Sbapt 3178322249Sbapt oldn = newn = buf->buf + pos; 3179322249Sbapt if (*oldn == '\0') 3180322249Sbapt return ROFF_IGN; 3181322249Sbapt 3182322249Sbapt oldsz = roff_getname(r, &newn, ln, pos); 3183322249Sbapt if (oldn[oldsz] == '\\' || *newn == '\0') 3184322249Sbapt return ROFF_IGN; 3185322249Sbapt 3186322249Sbapt end = newn; 3187322249Sbapt newsz = roff_getname(r, &end, ln, newn - buf->buf); 3188322249Sbapt if (newsz == 0) 3189322249Sbapt return ROFF_IGN; 3190322249Sbapt 3191322249Sbapt deftype = ROFFDEF_ANY; 3192322249Sbapt value = roff_getstrn(r, oldn, oldsz, &deftype); 3193322249Sbapt switch (deftype) { 3194322249Sbapt case ROFFDEF_USER: 3195322249Sbapt roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0); 3196322249Sbapt roff_setstrn(&r->strtab, oldn, oldsz, NULL, 0, 0); 3197322249Sbapt roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); 3198322249Sbapt break; 3199322249Sbapt case ROFFDEF_PRE: 3200322249Sbapt roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0); 3201322249Sbapt roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); 3202322249Sbapt break; 3203322249Sbapt case ROFFDEF_REN: 3204322249Sbapt roff_setstrn(&r->rentab, newn, newsz, value, strlen(value), 0); 3205322249Sbapt roff_setstrn(&r->rentab, oldn, oldsz, NULL, 0, 0); 3206322249Sbapt roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0); 3207322249Sbapt break; 3208322249Sbapt case ROFFDEF_STD: 3209322249Sbapt roff_setstrn(&r->rentab, newn, newsz, oldn, oldsz, 0); 3210322249Sbapt roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0); 3211322249Sbapt break; 3212322249Sbapt default: 3213322249Sbapt roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0); 3214322249Sbapt roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); 3215322249Sbapt break; 3216322249Sbapt } 3217322249Sbapt return ROFF_IGN; 3218322249Sbapt} 3219322249Sbapt 3220322249Sbaptstatic enum rofferr 3221241675Suqsroff_so(ROFF_ARGS) 3222241675Suqs{ 3223279527Sbapt char *name, *cp; 3224241675Suqs 3225275432Sbapt name = buf->buf + pos; 3226274880Sbapt mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name); 3227241675Suqs 3228241675Suqs /* 3229241675Suqs * Handle `so'. Be EXTREMELY careful, as we shouldn't be 3230241675Suqs * opening anything that's not in our cwd or anything beneath 3231241675Suqs * it. Thus, explicitly disallow traversing up the file-system 3232241675Suqs * or using absolute paths. 3233241675Suqs */ 3234241675Suqs 3235275432Sbapt if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) { 3236274880Sbapt mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos, 3237274880Sbapt ".so %s", name); 3238279527Sbapt buf->sz = mandoc_asprintf(&cp, 3239279527Sbapt ".sp\nSee the file %s.\n.sp", name) + 1; 3240279527Sbapt free(buf->buf); 3241279527Sbapt buf->buf = cp; 3242279527Sbapt *offs = 0; 3243294113Sbapt return ROFF_REPARSE; 3244241675Suqs } 3245241675Suqs 3246241675Suqs *offs = pos; 3247294113Sbapt return ROFF_SO; 3248241675Suqs} 3249241675Suqs 3250294113Sbapt/* --- user defined strings and macros ------------------------------------ */ 3251294113Sbapt 3252241675Suqsstatic enum rofferr 3253241675Suqsroff_userdef(ROFF_ARGS) 3254241675Suqs{ 3255322249Sbapt const char *arg[16], *ap; 3256241675Suqs char *cp, *n1, *n2; 3257322249Sbapt int expand_count, i, ib, ie; 3258279527Sbapt size_t asz, rsz; 3259241675Suqs 3260241675Suqs /* 3261241675Suqs * Collect pointers to macro argument strings 3262261344Suqs * and NUL-terminate them. 3263241675Suqs */ 3264279527Sbapt 3265294113Sbapt r->argc = 0; 3266275432Sbapt cp = buf->buf + pos; 3267322249Sbapt for (i = 0; i < 16; i++) { 3268294113Sbapt if (*cp == '\0') 3269294113Sbapt arg[i] = ""; 3270294113Sbapt else { 3271294113Sbapt arg[i] = mandoc_getarg(r->parse, &cp, ln, &pos); 3272294113Sbapt r->argc = i + 1; 3273294113Sbapt } 3274294113Sbapt } 3275241675Suqs 3276241675Suqs /* 3277241675Suqs * Expand macro arguments. 3278241675Suqs */ 3279279527Sbapt 3280279527Sbapt buf->sz = strlen(r->current_string) + 1; 3281322249Sbapt n1 = n2 = cp = mandoc_malloc(buf->sz); 3282279527Sbapt memcpy(n1, r->current_string, buf->sz); 3283322249Sbapt expand_count = 0; 3284279527Sbapt while (*cp != '\0') { 3285279527Sbapt 3286279527Sbapt /* Scan ahead for the next argument invocation. */ 3287279527Sbapt 3288279527Sbapt if (*cp++ != '\\') 3289241675Suqs continue; 3290279527Sbapt if (*cp++ != '$') 3291279527Sbapt continue; 3292294113Sbapt if (*cp == '*') { /* \\$* inserts all arguments */ 3293294113Sbapt ib = 0; 3294294113Sbapt ie = r->argc - 1; 3295294113Sbapt } else { /* \\$1 .. \\$9 insert one argument */ 3296294113Sbapt ib = ie = *cp - '1'; 3297294113Sbapt if (ib < 0 || ib > 8) 3298294113Sbapt continue; 3299294113Sbapt } 3300279527Sbapt cp -= 2; 3301279527Sbapt 3302279527Sbapt /* 3303322249Sbapt * Prevent infinite recursion. 3304322249Sbapt */ 3305322249Sbapt 3306322249Sbapt if (cp >= n2) 3307322249Sbapt expand_count = 1; 3308322249Sbapt else if (++expand_count > EXPAND_LIMIT) { 3309322249Sbapt mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, 3310322249Sbapt ln, (int)(cp - n1), NULL); 3311322249Sbapt free(buf->buf); 3312322249Sbapt buf->buf = n1; 3313322249Sbapt return ROFF_IGN; 3314322249Sbapt } 3315322249Sbapt 3316322249Sbapt /* 3317279527Sbapt * Determine the size of the expanded argument, 3318279527Sbapt * taking escaping of quotes into account. 3319279527Sbapt */ 3320279527Sbapt 3321294113Sbapt asz = ie > ib ? ie - ib : 0; /* for blanks */ 3322294113Sbapt for (i = ib; i <= ie; i++) { 3323294113Sbapt for (ap = arg[i]; *ap != '\0'; ap++) { 3324294113Sbapt asz++; 3325294113Sbapt if (*ap == '"') 3326294113Sbapt asz += 3; 3327294113Sbapt } 3328241675Suqs } 3329279527Sbapt if (asz != 3) { 3330279527Sbapt 3331279527Sbapt /* 3332279527Sbapt * Determine the size of the rest of the 3333279527Sbapt * unexpanded macro, including the NUL. 3334279527Sbapt */ 3335279527Sbapt 3336279527Sbapt rsz = buf->sz - (cp - n1) - 3; 3337279527Sbapt 3338279527Sbapt /* 3339279527Sbapt * When shrinking, move before 3340279527Sbapt * releasing the storage. 3341279527Sbapt */ 3342279527Sbapt 3343279527Sbapt if (asz < 3) 3344279527Sbapt memmove(cp + asz, cp + 3, rsz); 3345279527Sbapt 3346279527Sbapt /* 3347279527Sbapt * Resize the storage for the macro 3348279527Sbapt * and readjust the parse pointer. 3349279527Sbapt */ 3350279527Sbapt 3351279527Sbapt buf->sz += asz - 3; 3352279527Sbapt n2 = mandoc_realloc(n1, buf->sz); 3353279527Sbapt cp = n2 + (cp - n1); 3354279527Sbapt n1 = n2; 3355279527Sbapt 3356279527Sbapt /* 3357279527Sbapt * When growing, make room 3358279527Sbapt * for the expanded argument. 3359279527Sbapt */ 3360279527Sbapt 3361279527Sbapt if (asz > 3) 3362279527Sbapt memmove(cp + asz, cp + 3, rsz); 3363279527Sbapt } 3364279527Sbapt 3365279527Sbapt /* Copy the expanded argument, escaping quotes. */ 3366279527Sbapt 3367279527Sbapt n2 = cp; 3368294113Sbapt for (i = ib; i <= ie; i++) { 3369294113Sbapt for (ap = arg[i]; *ap != '\0'; ap++) { 3370294113Sbapt if (*ap == '"') { 3371294113Sbapt memcpy(n2, "\\(dq", 4); 3372294113Sbapt n2 += 4; 3373294113Sbapt } else 3374294113Sbapt *n2++ = *ap; 3375294113Sbapt } 3376294113Sbapt if (i < ie) 3377294113Sbapt *n2++ = ' '; 3378279527Sbapt } 3379241675Suqs } 3380241675Suqs 3381241675Suqs /* 3382241675Suqs * Replace the macro invocation 3383241675Suqs * by the expanded macro. 3384241675Suqs */ 3385279527Sbapt 3386275432Sbapt free(buf->buf); 3387275432Sbapt buf->buf = n1; 3388279527Sbapt *offs = 0; 3389241675Suqs 3390294113Sbapt return buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ? 3391294113Sbapt ROFF_REPARSE : ROFF_APPEND; 3392241675Suqs} 3393241675Suqs 3394322249Sbapt/* 3395322249Sbapt * Calling a high-level macro that was renamed with .rn. 3396322249Sbapt * r->current_string has already been set up by roff_parse(). 3397322249Sbapt */ 3398322249Sbaptstatic enum rofferr 3399322249Sbaptroff_renamed(ROFF_ARGS) 3400322249Sbapt{ 3401322249Sbapt char *nbuf; 3402322249Sbapt 3403322249Sbapt buf->sz = mandoc_asprintf(&nbuf, ".%s%s%s", r->current_string, 3404322249Sbapt buf->buf[pos] == '\0' ? "" : " ", buf->buf + pos) + 1; 3405322249Sbapt free(buf->buf); 3406322249Sbapt buf->buf = nbuf; 3407322249Sbapt return ROFF_CONT; 3408322249Sbapt} 3409322249Sbapt 3410274880Sbaptstatic size_t 3411241675Suqsroff_getname(struct roff *r, char **cpp, int ln, int pos) 3412241675Suqs{ 3413241675Suqs char *name, *cp; 3414274880Sbapt size_t namesz; 3415241675Suqs 3416241675Suqs name = *cpp; 3417241675Suqs if ('\0' == *name) 3418294113Sbapt return 0; 3419241675Suqs 3420274880Sbapt /* Read until end of name and terminate it with NUL. */ 3421274880Sbapt for (cp = name; 1; cp++) { 3422274880Sbapt if ('\0' == *cp || ' ' == *cp) { 3423274880Sbapt namesz = cp - name; 3424274880Sbapt break; 3425274880Sbapt } 3426241675Suqs if ('\\' != *cp) 3427241675Suqs continue; 3428274880Sbapt namesz = cp - name; 3429274880Sbapt if ('{' == cp[1] || '}' == cp[1]) 3430274880Sbapt break; 3431241675Suqs cp++; 3432241675Suqs if ('\\' == *cp) 3433241675Suqs continue; 3434274880Sbapt mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos, 3435274880Sbapt "%.*s", (int)(cp - name + 1), name); 3436274880Sbapt mandoc_escape((const char **)&cp, NULL, NULL); 3437274880Sbapt break; 3438241675Suqs } 3439241675Suqs 3440241675Suqs /* Read past spaces. */ 3441241675Suqs while (' ' == *cp) 3442241675Suqs cp++; 3443241675Suqs 3444241675Suqs *cpp = cp; 3445294113Sbapt return namesz; 3446241675Suqs} 3447241675Suqs 3448241675Suqs/* 3449241675Suqs * Store *string into the user-defined string called *name. 3450241675Suqs * To clear an existing entry, call with (*r, *name, NULL, 0). 3451274880Sbapt * append == 0: replace mode 3452274880Sbapt * append == 1: single-line append mode 3453274880Sbapt * append == 2: multiline append mode, append '\n' after each call 3454241675Suqs */ 3455241675Suqsstatic void 3456241675Suqsroff_setstr(struct roff *r, const char *name, const char *string, 3457274880Sbapt int append) 3458241675Suqs{ 3459322249Sbapt size_t namesz; 3460241675Suqs 3461322249Sbapt namesz = strlen(name); 3462322249Sbapt roff_setstrn(&r->strtab, name, namesz, string, 3463274880Sbapt string ? strlen(string) : 0, append); 3464322249Sbapt roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); 3465241675Suqs} 3466241675Suqs 3467241675Suqsstatic void 3468241675Suqsroff_setstrn(struct roffkv **r, const char *name, size_t namesz, 3469274880Sbapt const char *string, size_t stringsz, int append) 3470241675Suqs{ 3471241675Suqs struct roffkv *n; 3472241675Suqs char *c; 3473241675Suqs int i; 3474241675Suqs size_t oldch, newch; 3475241675Suqs 3476241675Suqs /* Search for an existing string with the same name. */ 3477241675Suqs n = *r; 3478241675Suqs 3479274880Sbapt while (n && (namesz != n->key.sz || 3480274880Sbapt strncmp(n->key.p, name, namesz))) 3481241675Suqs n = n->next; 3482241675Suqs 3483241675Suqs if (NULL == n) { 3484241675Suqs /* Create a new string table entry. */ 3485241675Suqs n = mandoc_malloc(sizeof(struct roffkv)); 3486241675Suqs n->key.p = mandoc_strndup(name, namesz); 3487241675Suqs n->key.sz = namesz; 3488241675Suqs n->val.p = NULL; 3489241675Suqs n->val.sz = 0; 3490241675Suqs n->next = *r; 3491241675Suqs *r = n; 3492274880Sbapt } else if (0 == append) { 3493241675Suqs free(n->val.p); 3494241675Suqs n->val.p = NULL; 3495241675Suqs n->val.sz = 0; 3496241675Suqs } 3497241675Suqs 3498241675Suqs if (NULL == string) 3499241675Suqs return; 3500241675Suqs 3501241675Suqs /* 3502241675Suqs * One additional byte for the '\n' in multiline mode, 3503241675Suqs * and one for the terminating '\0'. 3504241675Suqs */ 3505274880Sbapt newch = stringsz + (1 < append ? 2u : 1u); 3506241675Suqs 3507241675Suqs if (NULL == n->val.p) { 3508241675Suqs n->val.p = mandoc_malloc(newch); 3509241675Suqs *n->val.p = '\0'; 3510241675Suqs oldch = 0; 3511241675Suqs } else { 3512241675Suqs oldch = n->val.sz; 3513241675Suqs n->val.p = mandoc_realloc(n->val.p, oldch + newch); 3514241675Suqs } 3515241675Suqs 3516241675Suqs /* Skip existing content in the destination buffer. */ 3517241675Suqs c = n->val.p + (int)oldch; 3518241675Suqs 3519241675Suqs /* Append new content to the destination buffer. */ 3520241675Suqs i = 0; 3521241675Suqs while (i < (int)stringsz) { 3522241675Suqs /* 3523241675Suqs * Rudimentary roff copy mode: 3524241675Suqs * Handle escaped backslashes. 3525241675Suqs */ 3526241675Suqs if ('\\' == string[i] && '\\' == string[i + 1]) 3527241675Suqs i++; 3528241675Suqs *c++ = string[i++]; 3529241675Suqs } 3530241675Suqs 3531241675Suqs /* Append terminating bytes. */ 3532274880Sbapt if (1 < append) 3533241675Suqs *c++ = '\n'; 3534241675Suqs 3535241675Suqs *c = '\0'; 3536241675Suqs n->val.sz = (int)(c - n->val.p); 3537241675Suqs} 3538241675Suqs 3539241675Suqsstatic const char * 3540322249Sbaptroff_getstrn(const struct roff *r, const char *name, size_t len, 3541322249Sbapt int *deftype) 3542241675Suqs{ 3543322249Sbapt const struct roffkv *n; 3544322249Sbapt int i; 3545322249Sbapt enum roff_tok tok; 3546241675Suqs 3547322249Sbapt if (*deftype & ROFFDEF_USER) { 3548322249Sbapt for (n = r->strtab; n != NULL; n = n->next) { 3549322249Sbapt if (strncmp(name, n->key.p, len) == 0 && 3550322249Sbapt n->key.p[len] == '\0' && 3551322249Sbapt n->val.p != NULL) { 3552322249Sbapt *deftype = ROFFDEF_USER; 3553322249Sbapt return n->val.p; 3554322249Sbapt } 3555322249Sbapt } 3556322249Sbapt } 3557322249Sbapt if (*deftype & ROFFDEF_PRE) { 3558322249Sbapt for (i = 0; i < PREDEFS_MAX; i++) { 3559322249Sbapt if (strncmp(name, predefs[i].name, len) == 0 && 3560322249Sbapt predefs[i].name[len] == '\0') { 3561322249Sbapt *deftype = ROFFDEF_PRE; 3562322249Sbapt return predefs[i].str; 3563322249Sbapt } 3564322249Sbapt } 3565322249Sbapt } 3566322249Sbapt if (*deftype & ROFFDEF_REN) { 3567322249Sbapt for (n = r->rentab; n != NULL; n = n->next) { 3568322249Sbapt if (strncmp(name, n->key.p, len) == 0 && 3569322249Sbapt n->key.p[len] == '\0' && 3570322249Sbapt n->val.p != NULL) { 3571322249Sbapt *deftype = ROFFDEF_REN; 3572322249Sbapt return n->val.p; 3573322249Sbapt } 3574322249Sbapt } 3575322249Sbapt } 3576322249Sbapt if (*deftype & ROFFDEF_STD) { 3577322249Sbapt if (r->man->macroset != MACROSET_MAN) { 3578322249Sbapt for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) { 3579322249Sbapt if (strncmp(name, roff_name[tok], len) == 0 && 3580322249Sbapt roff_name[tok][len] == '\0') { 3581322249Sbapt *deftype = ROFFDEF_STD; 3582322249Sbapt return NULL; 3583322249Sbapt } 3584322249Sbapt } 3585322249Sbapt } 3586322249Sbapt if (r->man->macroset != MACROSET_MDOC) { 3587322249Sbapt for (tok = MAN_TH; tok < MAN_MAX; tok++) { 3588322249Sbapt if (strncmp(name, roff_name[tok], len) == 0 && 3589322249Sbapt roff_name[tok][len] == '\0') { 3590322249Sbapt *deftype = ROFFDEF_STD; 3591322249Sbapt return NULL; 3592322249Sbapt } 3593322249Sbapt } 3594322249Sbapt } 3595322249Sbapt } 3596322249Sbapt *deftype = 0; 3597294113Sbapt return NULL; 3598241675Suqs} 3599241675Suqs 3600241675Suqsstatic void 3601241675Suqsroff_freestr(struct roffkv *r) 3602241675Suqs{ 3603241675Suqs struct roffkv *n, *nn; 3604241675Suqs 3605241675Suqs for (n = r; n; n = nn) { 3606241675Suqs free(n->key.p); 3607241675Suqs free(n->val.p); 3608241675Suqs nn = n->next; 3609241675Suqs free(n); 3610241675Suqs } 3611241675Suqs} 3612241675Suqs 3613294113Sbapt/* --- accessors and utility functions ------------------------------------ */ 3614294113Sbapt 3615241675Suqs/* 3616241675Suqs * Duplicate an input string, making the appropriate character 3617241675Suqs * conversations (as stipulated by `tr') along the way. 3618241675Suqs * Returns a heap-allocated string with all the replacements made. 3619241675Suqs */ 3620241675Suqschar * 3621241675Suqsroff_strdup(const struct roff *r, const char *p) 3622241675Suqs{ 3623241675Suqs const struct roffkv *cp; 3624241675Suqs char *res; 3625241675Suqs const char *pp; 3626241675Suqs size_t ssz, sz; 3627241675Suqs enum mandoc_esc esc; 3628241675Suqs 3629241675Suqs if (NULL == r->xmbtab && NULL == r->xtab) 3630294113Sbapt return mandoc_strdup(p); 3631241675Suqs else if ('\0' == *p) 3632294113Sbapt return mandoc_strdup(""); 3633241675Suqs 3634241675Suqs /* 3635241675Suqs * Step through each character looking for term matches 3636241675Suqs * (remember that a `tr' can be invoked with an escape, which is 3637241675Suqs * a glyph but the escape is multi-character). 3638241675Suqs * We only do this if the character hash has been initialised 3639241675Suqs * and the string is >0 length. 3640241675Suqs */ 3641241675Suqs 3642241675Suqs res = NULL; 3643241675Suqs ssz = 0; 3644241675Suqs 3645241675Suqs while ('\0' != *p) { 3646322249Sbapt assert((unsigned int)*p < 128); 3647322249Sbapt if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) { 3648241675Suqs sz = r->xtab[(int)*p].sz; 3649241675Suqs res = mandoc_realloc(res, ssz + sz + 1); 3650241675Suqs memcpy(res + ssz, r->xtab[(int)*p].p, sz); 3651241675Suqs ssz += sz; 3652241675Suqs p++; 3653241675Suqs continue; 3654241675Suqs } else if ('\\' != *p) { 3655241675Suqs res = mandoc_realloc(res, ssz + 2); 3656241675Suqs res[ssz++] = *p++; 3657241675Suqs continue; 3658241675Suqs } 3659241675Suqs 3660241675Suqs /* Search for term matches. */ 3661241675Suqs for (cp = r->xmbtab; cp; cp = cp->next) 3662241675Suqs if (0 == strncmp(p, cp->key.p, cp->key.sz)) 3663241675Suqs break; 3664241675Suqs 3665241675Suqs if (NULL != cp) { 3666241675Suqs /* 3667241675Suqs * A match has been found. 3668241675Suqs * Append the match to the array and move 3669241675Suqs * forward by its keysize. 3670241675Suqs */ 3671274880Sbapt res = mandoc_realloc(res, 3672274880Sbapt ssz + cp->val.sz + 1); 3673241675Suqs memcpy(res + ssz, cp->val.p, cp->val.sz); 3674241675Suqs ssz += cp->val.sz; 3675241675Suqs p += (int)cp->key.sz; 3676241675Suqs continue; 3677241675Suqs } 3678241675Suqs 3679241675Suqs /* 3680241675Suqs * Handle escapes carefully: we need to copy 3681241675Suqs * over just the escape itself, or else we might 3682241675Suqs * do replacements within the escape itself. 3683241675Suqs * Make sure to pass along the bogus string. 3684241675Suqs */ 3685241675Suqs pp = p++; 3686241675Suqs esc = mandoc_escape(&p, NULL, NULL); 3687241675Suqs if (ESCAPE_ERROR == esc) { 3688241675Suqs sz = strlen(pp); 3689241675Suqs res = mandoc_realloc(res, ssz + sz + 1); 3690241675Suqs memcpy(res + ssz, pp, sz); 3691241675Suqs break; 3692241675Suqs } 3693274880Sbapt /* 3694274880Sbapt * We bail out on bad escapes. 3695241675Suqs * No need to warn: we already did so when 3696241675Suqs * roff_res() was called. 3697241675Suqs */ 3698241675Suqs sz = (int)(p - pp); 3699241675Suqs res = mandoc_realloc(res, ssz + sz + 1); 3700241675Suqs memcpy(res + ssz, pp, sz); 3701241675Suqs ssz += sz; 3702241675Suqs } 3703241675Suqs 3704241675Suqs res[(int)ssz] = '\0'; 3705294113Sbapt return res; 3706241675Suqs} 3707261344Suqs 3708275432Sbaptint 3709275432Sbaptroff_getformat(const struct roff *r) 3710275432Sbapt{ 3711275432Sbapt 3712294113Sbapt return r->format; 3713275432Sbapt} 3714275432Sbapt 3715261344Suqs/* 3716274880Sbapt * Find out whether a line is a macro line or not. 3717261344Suqs * If it is, adjust the current position and return one; if it isn't, 3718261344Suqs * return zero and don't change the current position. 3719261344Suqs * If the control character has been set with `.cc', then let that grain 3720261344Suqs * precedence. 3721261344Suqs * This is slighly contrary to groff, where using the non-breaking 3722261344Suqs * control character when `cc' has been invoked will cause the 3723261344Suqs * non-breaking macro contents to be printed verbatim. 3724261344Suqs */ 3725261344Suqsint 3726261344Suqsroff_getcontrol(const struct roff *r, const char *cp, int *ppos) 3727261344Suqs{ 3728261344Suqs int pos; 3729261344Suqs 3730261344Suqs pos = *ppos; 3731261344Suqs 3732322249Sbapt if (r->control != '\0' && cp[pos] == r->control) 3733261344Suqs pos++; 3734322249Sbapt else if (r->control != '\0') 3735294113Sbapt return 0; 3736261344Suqs else if ('\\' == cp[pos] && '.' == cp[pos + 1]) 3737261344Suqs pos += 2; 3738261344Suqs else if ('.' == cp[pos] || '\'' == cp[pos]) 3739261344Suqs pos++; 3740261344Suqs else 3741294113Sbapt return 0; 3742261344Suqs 3743261344Suqs while (' ' == cp[pos] || '\t' == cp[pos]) 3744261344Suqs pos++; 3745261344Suqs 3746261344Suqs *ppos = pos; 3747294113Sbapt return 1; 3748261344Suqs} 3749