1*260e9a87SYuri Pankov /* $Id: roff.c,v 1.263 2015/02/21 14:46:58 schwarze Exp $ */ 295c635efSGarrett D'Amore /* 3*260e9a87SYuri Pankov * Copyright (c) 2010, 2011, 2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4*260e9a87SYuri Pankov * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org> 595c635efSGarrett D'Amore * 695c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any 795c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above 895c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies. 995c635efSGarrett D'Amore * 1095c635efSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1195c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1295c635efSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 1395c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1495c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1595c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1695c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1795c635efSGarrett D'Amore */ 1895c635efSGarrett D'Amore #include "config.h" 19*260e9a87SYuri Pankov 20*260e9a87SYuri Pankov #include <sys/types.h> 2195c635efSGarrett D'Amore 2295c635efSGarrett D'Amore #include <assert.h> 2395c635efSGarrett D'Amore #include <ctype.h> 24*260e9a87SYuri Pankov #include <limits.h> 25698f87a4SGarrett D'Amore #include <stdio.h> 2695c635efSGarrett D'Amore #include <stdlib.h> 2795c635efSGarrett D'Amore #include <string.h> 2895c635efSGarrett D'Amore 2995c635efSGarrett D'Amore #include "mandoc.h" 30*260e9a87SYuri Pankov #include "mandoc_aux.h" 3195c635efSGarrett D'Amore #include "libmandoc.h" 32*260e9a87SYuri Pankov #include "libroff.h" 3395c635efSGarrett D'Amore 3495c635efSGarrett D'Amore /* Maximum number of nested if-else conditionals. */ 3595c635efSGarrett D'Amore #define RSTACK_MAX 128 3695c635efSGarrett D'Amore 3795c635efSGarrett D'Amore /* Maximum number of string expansions per line, to break infinite loops. */ 3895c635efSGarrett D'Amore #define EXPAND_LIMIT 1000 3995c635efSGarrett D'Amore 4095c635efSGarrett D'Amore enum rofft { 41*260e9a87SYuri Pankov ROFF_ab, 4295c635efSGarrett D'Amore ROFF_ad, 43*260e9a87SYuri Pankov ROFF_af, 44*260e9a87SYuri Pankov ROFF_aln, 45*260e9a87SYuri Pankov ROFF_als, 4695c635efSGarrett D'Amore ROFF_am, 4795c635efSGarrett D'Amore ROFF_am1, 48*260e9a87SYuri Pankov ROFF_ami, 49*260e9a87SYuri Pankov ROFF_ami1, 50*260e9a87SYuri Pankov ROFF_as, 51*260e9a87SYuri Pankov ROFF_as1, 52*260e9a87SYuri Pankov ROFF_asciify, 53*260e9a87SYuri Pankov ROFF_backtrace, 54*260e9a87SYuri Pankov ROFF_bd, 55*260e9a87SYuri Pankov ROFF_bleedat, 56*260e9a87SYuri Pankov ROFF_blm, 57*260e9a87SYuri Pankov ROFF_box, 58*260e9a87SYuri Pankov ROFF_boxa, 59*260e9a87SYuri Pankov ROFF_bp, 60*260e9a87SYuri Pankov ROFF_BP, 61*260e9a87SYuri Pankov /* MAN_br, MDOC_br */ 62*260e9a87SYuri Pankov ROFF_break, 63*260e9a87SYuri Pankov ROFF_breakchar, 64*260e9a87SYuri Pankov ROFF_brnl, 65*260e9a87SYuri Pankov ROFF_brp, 66*260e9a87SYuri Pankov ROFF_brpnl, 67*260e9a87SYuri Pankov ROFF_c2, 68698f87a4SGarrett D'Amore ROFF_cc, 69*260e9a87SYuri Pankov ROFF_ce, 70*260e9a87SYuri Pankov ROFF_cf, 71*260e9a87SYuri Pankov ROFF_cflags, 72*260e9a87SYuri Pankov ROFF_ch, 73*260e9a87SYuri Pankov ROFF_char, 74*260e9a87SYuri Pankov ROFF_chop, 75*260e9a87SYuri Pankov ROFF_class, 76*260e9a87SYuri Pankov ROFF_close, 77*260e9a87SYuri Pankov ROFF_CL, 78*260e9a87SYuri Pankov ROFF_color, 79*260e9a87SYuri Pankov ROFF_composite, 80*260e9a87SYuri Pankov ROFF_continue, 81*260e9a87SYuri Pankov ROFF_cp, 82*260e9a87SYuri Pankov ROFF_cropat, 83*260e9a87SYuri Pankov ROFF_cs, 84*260e9a87SYuri Pankov ROFF_cu, 85*260e9a87SYuri Pankov ROFF_da, 86*260e9a87SYuri Pankov ROFF_dch, 87*260e9a87SYuri Pankov ROFF_Dd, 8895c635efSGarrett D'Amore ROFF_de, 8995c635efSGarrett D'Amore ROFF_de1, 90*260e9a87SYuri Pankov ROFF_defcolor, 91*260e9a87SYuri Pankov ROFF_dei, 92*260e9a87SYuri Pankov ROFF_dei1, 93*260e9a87SYuri Pankov ROFF_device, 94*260e9a87SYuri Pankov ROFF_devicem, 95*260e9a87SYuri Pankov ROFF_di, 96*260e9a87SYuri Pankov ROFF_do, 9795c635efSGarrett D'Amore ROFF_ds, 98*260e9a87SYuri Pankov ROFF_ds1, 99*260e9a87SYuri Pankov ROFF_dwh, 100*260e9a87SYuri Pankov ROFF_dt, 101*260e9a87SYuri Pankov ROFF_ec, 102*260e9a87SYuri Pankov ROFF_ecr, 103*260e9a87SYuri Pankov ROFF_ecs, 10495c635efSGarrett D'Amore ROFF_el, 105*260e9a87SYuri Pankov ROFF_em, 106*260e9a87SYuri Pankov ROFF_EN, 107*260e9a87SYuri Pankov ROFF_eo, 108*260e9a87SYuri Pankov ROFF_EP, 109*260e9a87SYuri Pankov ROFF_EQ, 110*260e9a87SYuri Pankov ROFF_errprint, 111*260e9a87SYuri Pankov ROFF_ev, 112*260e9a87SYuri Pankov ROFF_evc, 113*260e9a87SYuri Pankov ROFF_ex, 114*260e9a87SYuri Pankov ROFF_fallback, 115698f87a4SGarrett D'Amore ROFF_fam, 116*260e9a87SYuri Pankov ROFF_fc, 117*260e9a87SYuri Pankov ROFF_fchar, 118*260e9a87SYuri Pankov ROFF_fcolor, 119*260e9a87SYuri Pankov ROFF_fdeferlig, 120*260e9a87SYuri Pankov ROFF_feature, 121*260e9a87SYuri Pankov /* MAN_fi; ignored in mdoc(7) */ 122*260e9a87SYuri Pankov ROFF_fkern, 123*260e9a87SYuri Pankov ROFF_fl, 124*260e9a87SYuri Pankov ROFF_flig, 125*260e9a87SYuri Pankov ROFF_fp, 126*260e9a87SYuri Pankov ROFF_fps, 127*260e9a87SYuri Pankov ROFF_fschar, 128*260e9a87SYuri Pankov ROFF_fspacewidth, 129*260e9a87SYuri Pankov ROFF_fspecial, 130*260e9a87SYuri Pankov /* MAN_ft; ignored in mdoc(7) */ 131*260e9a87SYuri Pankov ROFF_ftr, 132*260e9a87SYuri Pankov ROFF_fzoom, 133*260e9a87SYuri Pankov ROFF_gcolor, 134*260e9a87SYuri Pankov ROFF_hc, 135*260e9a87SYuri Pankov ROFF_hcode, 136*260e9a87SYuri Pankov ROFF_hidechar, 137*260e9a87SYuri Pankov ROFF_hla, 138*260e9a87SYuri Pankov ROFF_hlm, 139*260e9a87SYuri Pankov ROFF_hpf, 140*260e9a87SYuri Pankov ROFF_hpfa, 141*260e9a87SYuri Pankov ROFF_hpfcode, 142698f87a4SGarrett D'Amore ROFF_hw, 14395c635efSGarrett D'Amore ROFF_hy, 144*260e9a87SYuri Pankov ROFF_hylang, 145*260e9a87SYuri Pankov ROFF_hylen, 146*260e9a87SYuri Pankov ROFF_hym, 147*260e9a87SYuri Pankov ROFF_hypp, 148*260e9a87SYuri Pankov ROFF_hys, 14995c635efSGarrett D'Amore ROFF_ie, 15095c635efSGarrett D'Amore ROFF_if, 15195c635efSGarrett D'Amore ROFF_ig, 152*260e9a87SYuri Pankov /* MAN_in; ignored in mdoc(7) */ 153*260e9a87SYuri Pankov ROFF_index, 15495c635efSGarrett D'Amore ROFF_it, 155*260e9a87SYuri Pankov ROFF_itc, 156*260e9a87SYuri Pankov ROFF_IX, 157*260e9a87SYuri Pankov ROFF_kern, 158*260e9a87SYuri Pankov ROFF_kernafter, 159*260e9a87SYuri Pankov ROFF_kernbefore, 160*260e9a87SYuri Pankov ROFF_kernpair, 161*260e9a87SYuri Pankov ROFF_lc, 162*260e9a87SYuri Pankov ROFF_lc_ctype, 163*260e9a87SYuri Pankov ROFF_lds, 164*260e9a87SYuri Pankov ROFF_length, 165*260e9a87SYuri Pankov ROFF_letadj, 166*260e9a87SYuri Pankov ROFF_lf, 167*260e9a87SYuri Pankov ROFF_lg, 168*260e9a87SYuri Pankov ROFF_lhang, 169*260e9a87SYuri Pankov ROFF_linetabs, 170*260e9a87SYuri Pankov /* MAN_ll, MDOC_ll */ 171*260e9a87SYuri Pankov ROFF_lnr, 172*260e9a87SYuri Pankov ROFF_lnrf, 173*260e9a87SYuri Pankov ROFF_lpfx, 174*260e9a87SYuri Pankov ROFF_ls, 175*260e9a87SYuri Pankov ROFF_lsm, 176*260e9a87SYuri Pankov ROFF_lt, 177*260e9a87SYuri Pankov ROFF_mc, 178*260e9a87SYuri Pankov ROFF_mediasize, 179*260e9a87SYuri Pankov ROFF_minss, 180*260e9a87SYuri Pankov ROFF_mk, 181*260e9a87SYuri Pankov ROFF_mso, 182*260e9a87SYuri Pankov ROFF_na, 18395c635efSGarrett D'Amore ROFF_ne, 184*260e9a87SYuri Pankov /* MAN_nf; ignored in mdoc(7) */ 18595c635efSGarrett D'Amore ROFF_nh, 186*260e9a87SYuri Pankov ROFF_nhychar, 187*260e9a87SYuri Pankov ROFF_nm, 188*260e9a87SYuri Pankov ROFF_nn, 189*260e9a87SYuri Pankov ROFF_nop, 19095c635efSGarrett D'Amore ROFF_nr, 191*260e9a87SYuri Pankov ROFF_nrf, 192*260e9a87SYuri Pankov ROFF_nroff, 19395c635efSGarrett D'Amore ROFF_ns, 194*260e9a87SYuri Pankov ROFF_nx, 195*260e9a87SYuri Pankov ROFF_open, 196*260e9a87SYuri Pankov ROFF_opena, 197*260e9a87SYuri Pankov ROFF_os, 198*260e9a87SYuri Pankov ROFF_output, 199*260e9a87SYuri Pankov ROFF_padj, 200*260e9a87SYuri Pankov ROFF_papersize, 201*260e9a87SYuri Pankov ROFF_pc, 202*260e9a87SYuri Pankov ROFF_pev, 203*260e9a87SYuri Pankov ROFF_pi, 204*260e9a87SYuri Pankov ROFF_PI, 205*260e9a87SYuri Pankov ROFF_pl, 206*260e9a87SYuri Pankov ROFF_pm, 207*260e9a87SYuri Pankov ROFF_pn, 208*260e9a87SYuri Pankov ROFF_pnr, 209*260e9a87SYuri Pankov ROFF_po, 21095c635efSGarrett D'Amore ROFF_ps, 211*260e9a87SYuri Pankov ROFF_psbb, 212*260e9a87SYuri Pankov ROFF_pshape, 213*260e9a87SYuri Pankov ROFF_pso, 214*260e9a87SYuri Pankov ROFF_ptr, 215*260e9a87SYuri Pankov ROFF_pvs, 216*260e9a87SYuri Pankov ROFF_rchar, 217*260e9a87SYuri Pankov ROFF_rd, 218*260e9a87SYuri Pankov ROFF_recursionlimit, 219*260e9a87SYuri Pankov ROFF_return, 220*260e9a87SYuri Pankov ROFF_rfschar, 221*260e9a87SYuri Pankov ROFF_rhang, 222*260e9a87SYuri Pankov ROFF_rj, 22395c635efSGarrett D'Amore ROFF_rm, 224*260e9a87SYuri Pankov ROFF_rn, 225*260e9a87SYuri Pankov ROFF_rnn, 226*260e9a87SYuri Pankov ROFF_rr, 227*260e9a87SYuri Pankov ROFF_rs, 228*260e9a87SYuri Pankov ROFF_rt, 229*260e9a87SYuri Pankov ROFF_schar, 230*260e9a87SYuri Pankov ROFF_sentchar, 231*260e9a87SYuri Pankov ROFF_shc, 232*260e9a87SYuri Pankov ROFF_shift, 233*260e9a87SYuri Pankov ROFF_sizes, 23495c635efSGarrett D'Amore ROFF_so, 235*260e9a87SYuri Pankov /* MAN_sp, MDOC_sp */ 236*260e9a87SYuri Pankov ROFF_spacewidth, 237*260e9a87SYuri Pankov ROFF_special, 238*260e9a87SYuri Pankov ROFF_spreadwarn, 239*260e9a87SYuri Pankov ROFF_ss, 240*260e9a87SYuri Pankov ROFF_sty, 241*260e9a87SYuri Pankov ROFF_substring, 242*260e9a87SYuri Pankov ROFF_sv, 243*260e9a87SYuri Pankov ROFF_sy, 24495c635efSGarrett D'Amore ROFF_T_, 245*260e9a87SYuri Pankov ROFF_ta, 246*260e9a87SYuri Pankov ROFF_tc, 247*260e9a87SYuri Pankov ROFF_TE, 248*260e9a87SYuri Pankov ROFF_TH, 249*260e9a87SYuri Pankov ROFF_ti, 250*260e9a87SYuri Pankov ROFF_tkf, 251*260e9a87SYuri Pankov ROFF_tl, 252*260e9a87SYuri Pankov ROFF_tm, 253*260e9a87SYuri Pankov ROFF_tm1, 254*260e9a87SYuri Pankov ROFF_tmc, 255*260e9a87SYuri Pankov ROFF_tr, 256*260e9a87SYuri Pankov ROFF_track, 257*260e9a87SYuri Pankov ROFF_transchar, 258*260e9a87SYuri Pankov ROFF_trf, 259*260e9a87SYuri Pankov ROFF_trimat, 260*260e9a87SYuri Pankov ROFF_trin, 261*260e9a87SYuri Pankov ROFF_trnt, 262*260e9a87SYuri Pankov ROFF_troff, 263*260e9a87SYuri Pankov ROFF_TS, 264*260e9a87SYuri Pankov ROFF_uf, 265*260e9a87SYuri Pankov ROFF_ul, 266*260e9a87SYuri Pankov ROFF_unformat, 267*260e9a87SYuri Pankov ROFF_unwatch, 268*260e9a87SYuri Pankov ROFF_unwatchn, 269*260e9a87SYuri Pankov ROFF_vpt, 270*260e9a87SYuri Pankov ROFF_vs, 271*260e9a87SYuri Pankov ROFF_warn, 272*260e9a87SYuri Pankov ROFF_warnscale, 273*260e9a87SYuri Pankov ROFF_watch, 274*260e9a87SYuri Pankov ROFF_watchlength, 275*260e9a87SYuri Pankov ROFF_watchn, 276*260e9a87SYuri Pankov ROFF_wh, 277*260e9a87SYuri Pankov ROFF_while, 278*260e9a87SYuri Pankov ROFF_write, 279*260e9a87SYuri Pankov ROFF_writec, 280*260e9a87SYuri Pankov ROFF_writem, 281*260e9a87SYuri Pankov ROFF_xflag, 28295c635efSGarrett D'Amore ROFF_cblock, 28395c635efSGarrett D'Amore ROFF_USERDEF, 28495c635efSGarrett D'Amore ROFF_MAX 28595c635efSGarrett D'Amore }; 28695c635efSGarrett D'Amore 28795c635efSGarrett D'Amore /* 28895c635efSGarrett D'Amore * An incredibly-simple string buffer. 28995c635efSGarrett D'Amore */ 29095c635efSGarrett D'Amore struct roffstr { 29195c635efSGarrett D'Amore char *p; /* nil-terminated buffer */ 29295c635efSGarrett D'Amore size_t sz; /* saved strlen(p) */ 29395c635efSGarrett D'Amore }; 29495c635efSGarrett D'Amore 29595c635efSGarrett D'Amore /* 29695c635efSGarrett D'Amore * A key-value roffstr pair as part of a singly-linked list. 29795c635efSGarrett D'Amore */ 29895c635efSGarrett D'Amore struct roffkv { 29995c635efSGarrett D'Amore struct roffstr key; 30095c635efSGarrett D'Amore struct roffstr val; 30195c635efSGarrett D'Amore struct roffkv *next; /* next in list */ 30295c635efSGarrett D'Amore }; 30395c635efSGarrett D'Amore 304698f87a4SGarrett D'Amore /* 305698f87a4SGarrett D'Amore * A single number register as part of a singly-linked list. 306698f87a4SGarrett D'Amore */ 307698f87a4SGarrett D'Amore struct roffreg { 308698f87a4SGarrett D'Amore struct roffstr key; 309698f87a4SGarrett D'Amore int val; 310698f87a4SGarrett D'Amore struct roffreg *next; 311698f87a4SGarrett D'Amore }; 312698f87a4SGarrett D'Amore 31395c635efSGarrett D'Amore struct roff { 31495c635efSGarrett D'Amore struct mparse *parse; /* parse point */ 315*260e9a87SYuri Pankov const struct mchars *mchars; /* character table */ 31695c635efSGarrett D'Amore struct roffnode *last; /* leaf of stack */ 317*260e9a87SYuri Pankov int *rstack; /* stack of inverted `ie' values */ 318698f87a4SGarrett D'Amore struct roffreg *regtab; /* number registers */ 31995c635efSGarrett D'Amore struct roffkv *strtab; /* user-defined strings & macros */ 32095c635efSGarrett D'Amore struct roffkv *xmbtab; /* multi-byte trans table (`tr') */ 32195c635efSGarrett D'Amore struct roffstr *xtab; /* single-byte trans table (`tr') */ 32295c635efSGarrett D'Amore const char *current_string; /* value of last called user macro */ 32395c635efSGarrett D'Amore struct tbl_node *first_tbl; /* first table parsed */ 32495c635efSGarrett D'Amore struct tbl_node *last_tbl; /* last table parsed */ 32595c635efSGarrett D'Amore struct tbl_node *tbl; /* current table being parsed */ 32695c635efSGarrett D'Amore struct eqn_node *last_eqn; /* last equation parsed */ 32795c635efSGarrett D'Amore struct eqn_node *first_eqn; /* first equation parsed */ 32895c635efSGarrett D'Amore struct eqn_node *eqn; /* current equation being parsed */ 329*260e9a87SYuri Pankov int eqn_inline; /* current equation is inline */ 330*260e9a87SYuri Pankov int options; /* parse options */ 331*260e9a87SYuri Pankov int rstacksz; /* current size limit of rstack */ 332*260e9a87SYuri Pankov int rstackpos; /* position in rstack */ 333*260e9a87SYuri Pankov int format; /* current file in mdoc or man format */ 334*260e9a87SYuri Pankov char control; /* control character */ 33595c635efSGarrett D'Amore }; 33695c635efSGarrett D'Amore 33795c635efSGarrett D'Amore struct roffnode { 33895c635efSGarrett D'Amore enum rofft tok; /* type of node */ 33995c635efSGarrett D'Amore struct roffnode *parent; /* up one in stack */ 34095c635efSGarrett D'Amore int line; /* parse line */ 34195c635efSGarrett D'Amore int col; /* parse col */ 34295c635efSGarrett D'Amore char *name; /* node name, e.g. macro name */ 34395c635efSGarrett D'Amore char *end; /* end-rules: custom token */ 34495c635efSGarrett D'Amore int endspan; /* end-rules: next-line or infty */ 345*260e9a87SYuri Pankov int rule; /* current evaluation rule */ 34695c635efSGarrett D'Amore }; 34795c635efSGarrett D'Amore 34895c635efSGarrett D'Amore #define ROFF_ARGS struct roff *r, /* parse ctx */ \ 34995c635efSGarrett D'Amore enum rofft tok, /* tok of macro */ \ 350*260e9a87SYuri Pankov struct buf *buf, /* input buffer */ \ 35195c635efSGarrett D'Amore int ln, /* parse line */ \ 35295c635efSGarrett D'Amore int ppos, /* original pos in buffer */ \ 35395c635efSGarrett D'Amore int pos, /* current pos in buffer */ \ 35495c635efSGarrett D'Amore int *offs /* reset offset of buffer data */ 35595c635efSGarrett D'Amore 35695c635efSGarrett D'Amore typedef enum rofferr (*roffproc)(ROFF_ARGS); 35795c635efSGarrett D'Amore 35895c635efSGarrett D'Amore struct roffmac { 35995c635efSGarrett D'Amore const char *name; /* macro name */ 36095c635efSGarrett D'Amore roffproc proc; /* process new macro */ 36195c635efSGarrett D'Amore roffproc text; /* process as child text of macro */ 36295c635efSGarrett D'Amore roffproc sub; /* process as child of macro */ 36395c635efSGarrett D'Amore int flags; 36495c635efSGarrett D'Amore #define ROFFMAC_STRUCT (1 << 0) /* always interpret */ 36595c635efSGarrett D'Amore struct roffmac *next; 36695c635efSGarrett D'Amore }; 36795c635efSGarrett D'Amore 36895c635efSGarrett D'Amore struct predef { 36995c635efSGarrett D'Amore const char *name; /* predefined input name */ 37095c635efSGarrett D'Amore const char *str; /* replacement symbol */ 37195c635efSGarrett D'Amore }; 37295c635efSGarrett D'Amore 37395c635efSGarrett D'Amore #define PREDEF(__name, __str) \ 37495c635efSGarrett D'Amore { (__name), (__str) }, 37595c635efSGarrett D'Amore 37695c635efSGarrett D'Amore static enum rofft roffhash_find(const char *, size_t); 37795c635efSGarrett D'Amore static void roffhash_init(void); 37895c635efSGarrett D'Amore static void roffnode_cleanscope(struct roff *); 37995c635efSGarrett D'Amore static void roffnode_pop(struct roff *); 38095c635efSGarrett D'Amore static void roffnode_push(struct roff *, enum rofft, 38195c635efSGarrett D'Amore const char *, int, int); 38295c635efSGarrett D'Amore static enum rofferr roff_block(ROFF_ARGS); 38395c635efSGarrett D'Amore static enum rofferr roff_block_text(ROFF_ARGS); 38495c635efSGarrett D'Amore static enum rofferr roff_block_sub(ROFF_ARGS); 385*260e9a87SYuri Pankov static enum rofferr roff_brp(ROFF_ARGS); 38695c635efSGarrett D'Amore static enum rofferr roff_cblock(ROFF_ARGS); 387698f87a4SGarrett D'Amore static enum rofferr roff_cc(ROFF_ARGS); 388*260e9a87SYuri Pankov static void roff_ccond(struct roff *, int, int); 38995c635efSGarrett D'Amore static enum rofferr roff_cond(ROFF_ARGS); 39095c635efSGarrett D'Amore static enum rofferr roff_cond_text(ROFF_ARGS); 39195c635efSGarrett D'Amore static enum rofferr roff_cond_sub(ROFF_ARGS); 39295c635efSGarrett D'Amore static enum rofferr roff_ds(ROFF_ARGS); 393*260e9a87SYuri Pankov static enum rofferr roff_eqndelim(struct roff *, struct buf *, int); 394*260e9a87SYuri Pankov static int roff_evalcond(struct roff *r, int, 395*260e9a87SYuri Pankov const char *, int *); 396*260e9a87SYuri Pankov static int roff_evalnum(struct roff *, int, 397*260e9a87SYuri Pankov const char *, int *, int *, int); 398*260e9a87SYuri Pankov static int roff_evalpar(struct roff *, int, 399*260e9a87SYuri Pankov const char *, int *, int *, int); 400*260e9a87SYuri Pankov static int roff_evalstrcond(const char *, int *); 40195c635efSGarrett D'Amore static void roff_free1(struct roff *); 402698f87a4SGarrett D'Amore static void roff_freereg(struct roffreg *); 40395c635efSGarrett D'Amore static void roff_freestr(struct roffkv *); 404*260e9a87SYuri Pankov static size_t roff_getname(struct roff *, char **, int, int); 405*260e9a87SYuri Pankov static int roff_getnum(const char *, int *, int *, int); 406698f87a4SGarrett D'Amore static int roff_getop(const char *, int *, char *); 407698f87a4SGarrett D'Amore static int roff_getregn(const struct roff *, 408698f87a4SGarrett D'Amore const char *, size_t); 409*260e9a87SYuri Pankov static int roff_getregro(const char *name); 41095c635efSGarrett D'Amore static const char *roff_getstrn(const struct roff *, 41195c635efSGarrett D'Amore const char *, size_t); 412*260e9a87SYuri Pankov static enum rofferr roff_insec(ROFF_ARGS); 413698f87a4SGarrett D'Amore static enum rofferr roff_it(ROFF_ARGS); 41495c635efSGarrett D'Amore static enum rofferr roff_line_ignore(ROFF_ARGS); 41595c635efSGarrett D'Amore static enum rofferr roff_nr(ROFF_ARGS); 416*260e9a87SYuri Pankov static enum rofft roff_parse(struct roff *, char *, int *, 417*260e9a87SYuri Pankov int, int); 418*260e9a87SYuri Pankov static enum rofferr roff_parsetext(struct buf *, int, int *); 419*260e9a87SYuri Pankov static enum rofferr roff_res(struct roff *, struct buf *, int, int); 42095c635efSGarrett D'Amore static enum rofferr roff_rm(ROFF_ARGS); 421*260e9a87SYuri Pankov static enum rofferr roff_rr(ROFF_ARGS); 42295c635efSGarrett D'Amore static void roff_setstr(struct roff *, 42395c635efSGarrett D'Amore const char *, const char *, int); 42495c635efSGarrett D'Amore static void roff_setstrn(struct roffkv **, const char *, 42595c635efSGarrett D'Amore size_t, const char *, size_t, int); 42695c635efSGarrett D'Amore static enum rofferr roff_so(ROFF_ARGS); 42795c635efSGarrett D'Amore static enum rofferr roff_tr(ROFF_ARGS); 428698f87a4SGarrett D'Amore static enum rofferr roff_Dd(ROFF_ARGS); 429698f87a4SGarrett D'Amore static enum rofferr roff_TH(ROFF_ARGS); 43095c635efSGarrett D'Amore static enum rofferr roff_TE(ROFF_ARGS); 43195c635efSGarrett D'Amore static enum rofferr roff_TS(ROFF_ARGS); 43295c635efSGarrett D'Amore static enum rofferr roff_EQ(ROFF_ARGS); 43395c635efSGarrett D'Amore static enum rofferr roff_EN(ROFF_ARGS); 43495c635efSGarrett D'Amore static enum rofferr roff_T_(ROFF_ARGS); 435*260e9a87SYuri Pankov static enum rofferr roff_unsupp(ROFF_ARGS); 43695c635efSGarrett D'Amore static enum rofferr roff_userdef(ROFF_ARGS); 43795c635efSGarrett D'Amore 43895c635efSGarrett D'Amore /* See roffhash_find() */ 43995c635efSGarrett D'Amore 44095c635efSGarrett D'Amore #define ASCII_HI 126 44195c635efSGarrett D'Amore #define ASCII_LO 33 44295c635efSGarrett D'Amore #define HASHWIDTH (ASCII_HI - ASCII_LO + 1) 44395c635efSGarrett D'Amore 444*260e9a87SYuri Pankov #define ROFFNUM_SCALE (1 << 0) /* Honour scaling in roff_getnum(). */ 445*260e9a87SYuri Pankov #define ROFFNUM_WHITE (1 << 1) /* Skip whitespace in roff_evalnum(). */ 446*260e9a87SYuri Pankov 44795c635efSGarrett D'Amore static struct roffmac *hash[HASHWIDTH]; 44895c635efSGarrett D'Amore 44995c635efSGarrett D'Amore static struct roffmac roffs[ROFF_MAX] = { 450*260e9a87SYuri Pankov { "ab", roff_unsupp, NULL, NULL, 0, NULL }, 45195c635efSGarrett D'Amore { "ad", roff_line_ignore, NULL, NULL, 0, NULL }, 452*260e9a87SYuri Pankov { "af", roff_line_ignore, NULL, NULL, 0, NULL }, 453*260e9a87SYuri Pankov { "aln", roff_unsupp, NULL, NULL, 0, NULL }, 454*260e9a87SYuri Pankov { "als", roff_unsupp, NULL, NULL, 0, NULL }, 45595c635efSGarrett D'Amore { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 45695c635efSGarrett D'Amore { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 457*260e9a87SYuri Pankov { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 458*260e9a87SYuri Pankov { "ami1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 459*260e9a87SYuri Pankov { "as", roff_ds, NULL, NULL, 0, NULL }, 460*260e9a87SYuri Pankov { "as1", roff_ds, NULL, NULL, 0, NULL }, 461*260e9a87SYuri Pankov { "asciify", roff_unsupp, NULL, NULL, 0, NULL }, 462*260e9a87SYuri Pankov { "backtrace", roff_line_ignore, NULL, NULL, 0, NULL }, 463*260e9a87SYuri Pankov { "bd", roff_line_ignore, NULL, NULL, 0, NULL }, 464*260e9a87SYuri Pankov { "bleedat", roff_line_ignore, NULL, NULL, 0, NULL }, 465*260e9a87SYuri Pankov { "blm", roff_unsupp, NULL, NULL, 0, NULL }, 466*260e9a87SYuri Pankov { "box", roff_unsupp, NULL, NULL, 0, NULL }, 467*260e9a87SYuri Pankov { "boxa", roff_unsupp, NULL, NULL, 0, NULL }, 468*260e9a87SYuri Pankov { "bp", roff_line_ignore, NULL, NULL, 0, NULL }, 469*260e9a87SYuri Pankov { "BP", roff_unsupp, NULL, NULL, 0, NULL }, 470*260e9a87SYuri Pankov { "break", roff_unsupp, NULL, NULL, 0, NULL }, 471*260e9a87SYuri Pankov { "breakchar", roff_line_ignore, NULL, NULL, 0, NULL }, 472*260e9a87SYuri Pankov { "brnl", roff_line_ignore, NULL, NULL, 0, NULL }, 473*260e9a87SYuri Pankov { "brp", roff_brp, NULL, NULL, 0, NULL }, 474*260e9a87SYuri Pankov { "brpnl", roff_line_ignore, NULL, NULL, 0, NULL }, 475*260e9a87SYuri Pankov { "c2", roff_unsupp, NULL, NULL, 0, NULL }, 476698f87a4SGarrett D'Amore { "cc", roff_cc, NULL, NULL, 0, NULL }, 477*260e9a87SYuri Pankov { "ce", roff_line_ignore, NULL, NULL, 0, NULL }, 478*260e9a87SYuri Pankov { "cf", roff_insec, NULL, NULL, 0, NULL }, 479*260e9a87SYuri Pankov { "cflags", roff_line_ignore, NULL, NULL, 0, NULL }, 480*260e9a87SYuri Pankov { "ch", roff_line_ignore, NULL, NULL, 0, NULL }, 481*260e9a87SYuri Pankov { "char", roff_unsupp, NULL, NULL, 0, NULL }, 482*260e9a87SYuri Pankov { "chop", roff_unsupp, NULL, NULL, 0, NULL }, 483*260e9a87SYuri Pankov { "class", roff_line_ignore, NULL, NULL, 0, NULL }, 484*260e9a87SYuri Pankov { "close", roff_insec, NULL, NULL, 0, NULL }, 485*260e9a87SYuri Pankov { "CL", roff_unsupp, NULL, NULL, 0, NULL }, 486*260e9a87SYuri Pankov { "color", roff_line_ignore, NULL, NULL, 0, NULL }, 487*260e9a87SYuri Pankov { "composite", roff_unsupp, NULL, NULL, 0, NULL }, 488*260e9a87SYuri Pankov { "continue", roff_unsupp, NULL, NULL, 0, NULL }, 489*260e9a87SYuri Pankov { "cp", roff_line_ignore, NULL, NULL, 0, NULL }, 490*260e9a87SYuri Pankov { "cropat", roff_line_ignore, NULL, NULL, 0, NULL }, 491*260e9a87SYuri Pankov { "cs", roff_line_ignore, NULL, NULL, 0, NULL }, 492*260e9a87SYuri Pankov { "cu", roff_line_ignore, NULL, NULL, 0, NULL }, 493*260e9a87SYuri Pankov { "da", roff_unsupp, NULL, NULL, 0, NULL }, 494*260e9a87SYuri Pankov { "dch", roff_unsupp, NULL, NULL, 0, NULL }, 495*260e9a87SYuri Pankov { "Dd", roff_Dd, NULL, NULL, 0, NULL }, 49695c635efSGarrett D'Amore { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 49795c635efSGarrett D'Amore { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 498*260e9a87SYuri Pankov { "defcolor", roff_line_ignore, NULL, NULL, 0, NULL }, 499*260e9a87SYuri Pankov { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 500*260e9a87SYuri Pankov { "dei1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 501*260e9a87SYuri Pankov { "device", roff_unsupp, NULL, NULL, 0, NULL }, 502*260e9a87SYuri Pankov { "devicem", roff_unsupp, NULL, NULL, 0, NULL }, 503*260e9a87SYuri Pankov { "di", roff_unsupp, NULL, NULL, 0, NULL }, 504*260e9a87SYuri Pankov { "do", roff_unsupp, NULL, NULL, 0, NULL }, 50595c635efSGarrett D'Amore { "ds", roff_ds, NULL, NULL, 0, NULL }, 506*260e9a87SYuri Pankov { "ds1", roff_ds, NULL, NULL, 0, NULL }, 507*260e9a87SYuri Pankov { "dwh", roff_unsupp, NULL, NULL, 0, NULL }, 508*260e9a87SYuri Pankov { "dt", roff_unsupp, NULL, NULL, 0, NULL }, 509*260e9a87SYuri Pankov { "ec", roff_unsupp, NULL, NULL, 0, NULL }, 510*260e9a87SYuri Pankov { "ecr", roff_unsupp, NULL, NULL, 0, NULL }, 511*260e9a87SYuri Pankov { "ecs", roff_unsupp, NULL, NULL, 0, NULL }, 51295c635efSGarrett D'Amore { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, 513*260e9a87SYuri Pankov { "em", roff_unsupp, NULL, NULL, 0, NULL }, 514*260e9a87SYuri Pankov { "EN", roff_EN, NULL, NULL, 0, NULL }, 515*260e9a87SYuri Pankov { "eo", roff_unsupp, NULL, NULL, 0, NULL }, 516*260e9a87SYuri Pankov { "EP", roff_unsupp, NULL, NULL, 0, NULL }, 517*260e9a87SYuri Pankov { "EQ", roff_EQ, NULL, NULL, 0, NULL }, 518*260e9a87SYuri Pankov { "errprint", roff_line_ignore, NULL, NULL, 0, NULL }, 519*260e9a87SYuri Pankov { "ev", roff_unsupp, NULL, NULL, 0, NULL }, 520*260e9a87SYuri Pankov { "evc", roff_unsupp, NULL, NULL, 0, NULL }, 521*260e9a87SYuri Pankov { "ex", roff_unsupp, NULL, NULL, 0, NULL }, 522*260e9a87SYuri Pankov { "fallback", roff_line_ignore, NULL, NULL, 0, NULL }, 523698f87a4SGarrett D'Amore { "fam", roff_line_ignore, NULL, NULL, 0, NULL }, 524*260e9a87SYuri Pankov { "fc", roff_unsupp, NULL, NULL, 0, NULL }, 525*260e9a87SYuri Pankov { "fchar", roff_unsupp, NULL, NULL, 0, NULL }, 526*260e9a87SYuri Pankov { "fcolor", roff_line_ignore, NULL, NULL, 0, NULL }, 527*260e9a87SYuri Pankov { "fdeferlig", roff_line_ignore, NULL, NULL, 0, NULL }, 528*260e9a87SYuri Pankov { "feature", roff_line_ignore, NULL, NULL, 0, NULL }, 529*260e9a87SYuri Pankov { "fkern", roff_line_ignore, NULL, NULL, 0, NULL }, 530*260e9a87SYuri Pankov { "fl", roff_line_ignore, NULL, NULL, 0, NULL }, 531*260e9a87SYuri Pankov { "flig", roff_line_ignore, NULL, NULL, 0, NULL }, 532*260e9a87SYuri Pankov { "fp", roff_line_ignore, NULL, NULL, 0, NULL }, 533*260e9a87SYuri Pankov { "fps", roff_line_ignore, NULL, NULL, 0, NULL }, 534*260e9a87SYuri Pankov { "fschar", roff_unsupp, NULL, NULL, 0, NULL }, 535*260e9a87SYuri Pankov { "fspacewidth", roff_line_ignore, NULL, NULL, 0, NULL }, 536*260e9a87SYuri Pankov { "fspecial", roff_line_ignore, NULL, NULL, 0, NULL }, 537*260e9a87SYuri Pankov { "ftr", roff_line_ignore, NULL, NULL, 0, NULL }, 538*260e9a87SYuri Pankov { "fzoom", roff_line_ignore, NULL, NULL, 0, NULL }, 539*260e9a87SYuri Pankov { "gcolor", roff_line_ignore, NULL, NULL, 0, NULL }, 540*260e9a87SYuri Pankov { "hc", roff_line_ignore, NULL, NULL, 0, NULL }, 541*260e9a87SYuri Pankov { "hcode", roff_line_ignore, NULL, NULL, 0, NULL }, 542*260e9a87SYuri Pankov { "hidechar", roff_line_ignore, NULL, NULL, 0, NULL }, 543*260e9a87SYuri Pankov { "hla", roff_line_ignore, NULL, NULL, 0, NULL }, 544*260e9a87SYuri Pankov { "hlm", roff_line_ignore, NULL, NULL, 0, NULL }, 545*260e9a87SYuri Pankov { "hpf", roff_line_ignore, NULL, NULL, 0, NULL }, 546*260e9a87SYuri Pankov { "hpfa", roff_line_ignore, NULL, NULL, 0, NULL }, 547*260e9a87SYuri Pankov { "hpfcode", roff_line_ignore, NULL, NULL, 0, NULL }, 548698f87a4SGarrett D'Amore { "hw", roff_line_ignore, NULL, NULL, 0, NULL }, 54995c635efSGarrett D'Amore { "hy", roff_line_ignore, NULL, NULL, 0, NULL }, 550*260e9a87SYuri Pankov { "hylang", roff_line_ignore, NULL, NULL, 0, NULL }, 551*260e9a87SYuri Pankov { "hylen", roff_line_ignore, NULL, NULL, 0, NULL }, 552*260e9a87SYuri Pankov { "hym", roff_line_ignore, NULL, NULL, 0, NULL }, 553*260e9a87SYuri Pankov { "hypp", roff_line_ignore, NULL, NULL, 0, NULL }, 554*260e9a87SYuri Pankov { "hys", roff_line_ignore, NULL, NULL, 0, NULL }, 55595c635efSGarrett D'Amore { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, 55695c635efSGarrett D'Amore { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, 55795c635efSGarrett D'Amore { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 558*260e9a87SYuri Pankov { "index", roff_unsupp, NULL, NULL, 0, NULL }, 559698f87a4SGarrett D'Amore { "it", roff_it, NULL, NULL, 0, NULL }, 560*260e9a87SYuri Pankov { "itc", roff_unsupp, NULL, NULL, 0, NULL }, 561*260e9a87SYuri Pankov { "IX", roff_line_ignore, NULL, NULL, 0, NULL }, 562*260e9a87SYuri Pankov { "kern", roff_line_ignore, NULL, NULL, 0, NULL }, 563*260e9a87SYuri Pankov { "kernafter", roff_line_ignore, NULL, NULL, 0, NULL }, 564*260e9a87SYuri Pankov { "kernbefore", roff_line_ignore, NULL, NULL, 0, NULL }, 565*260e9a87SYuri Pankov { "kernpair", roff_line_ignore, NULL, NULL, 0, NULL }, 566*260e9a87SYuri Pankov { "lc", roff_unsupp, NULL, NULL, 0, NULL }, 567*260e9a87SYuri Pankov { "lc_ctype", roff_unsupp, NULL, NULL, 0, NULL }, 568*260e9a87SYuri Pankov { "lds", roff_unsupp, NULL, NULL, 0, NULL }, 569*260e9a87SYuri Pankov { "length", roff_unsupp, NULL, NULL, 0, NULL }, 570*260e9a87SYuri Pankov { "letadj", roff_line_ignore, NULL, NULL, 0, NULL }, 571*260e9a87SYuri Pankov { "lf", roff_insec, NULL, NULL, 0, NULL }, 572*260e9a87SYuri Pankov { "lg", roff_line_ignore, NULL, NULL, 0, NULL }, 573*260e9a87SYuri Pankov { "lhang", roff_line_ignore, NULL, NULL, 0, NULL }, 574*260e9a87SYuri Pankov { "linetabs", roff_unsupp, NULL, NULL, 0, NULL }, 575*260e9a87SYuri Pankov { "lnr", roff_unsupp, NULL, NULL, 0, NULL }, 576*260e9a87SYuri Pankov { "lnrf", roff_unsupp, NULL, NULL, 0, NULL }, 577*260e9a87SYuri Pankov { "lpfx", roff_unsupp, NULL, NULL, 0, NULL }, 578*260e9a87SYuri Pankov { "ls", roff_line_ignore, NULL, NULL, 0, NULL }, 579*260e9a87SYuri Pankov { "lsm", roff_unsupp, NULL, NULL, 0, NULL }, 580*260e9a87SYuri Pankov { "lt", roff_line_ignore, NULL, NULL, 0, NULL }, 581*260e9a87SYuri Pankov { "mc", roff_line_ignore, NULL, NULL, 0, NULL }, 582*260e9a87SYuri Pankov { "mediasize", roff_line_ignore, NULL, NULL, 0, NULL }, 583*260e9a87SYuri Pankov { "minss", roff_line_ignore, NULL, NULL, 0, NULL }, 584*260e9a87SYuri Pankov { "mk", roff_line_ignore, NULL, NULL, 0, NULL }, 585*260e9a87SYuri Pankov { "mso", roff_insec, NULL, NULL, 0, NULL }, 586*260e9a87SYuri Pankov { "na", roff_line_ignore, NULL, NULL, 0, NULL }, 58795c635efSGarrett D'Amore { "ne", roff_line_ignore, NULL, NULL, 0, NULL }, 58895c635efSGarrett D'Amore { "nh", roff_line_ignore, NULL, NULL, 0, NULL }, 589*260e9a87SYuri Pankov { "nhychar", roff_line_ignore, NULL, NULL, 0, NULL }, 590*260e9a87SYuri Pankov { "nm", roff_unsupp, NULL, NULL, 0, NULL }, 591*260e9a87SYuri Pankov { "nn", roff_unsupp, NULL, NULL, 0, NULL }, 592*260e9a87SYuri Pankov { "nop", roff_unsupp, NULL, NULL, 0, NULL }, 59395c635efSGarrett D'Amore { "nr", roff_nr, NULL, NULL, 0, NULL }, 594*260e9a87SYuri Pankov { "nrf", roff_unsupp, NULL, NULL, 0, NULL }, 595*260e9a87SYuri Pankov { "nroff", roff_line_ignore, NULL, NULL, 0, NULL }, 59695c635efSGarrett D'Amore { "ns", roff_line_ignore, NULL, NULL, 0, NULL }, 597*260e9a87SYuri Pankov { "nx", roff_insec, NULL, NULL, 0, NULL }, 598*260e9a87SYuri Pankov { "open", roff_insec, NULL, NULL, 0, NULL }, 599*260e9a87SYuri Pankov { "opena", roff_insec, NULL, NULL, 0, NULL }, 600*260e9a87SYuri Pankov { "os", roff_line_ignore, NULL, NULL, 0, NULL }, 601*260e9a87SYuri Pankov { "output", roff_unsupp, NULL, NULL, 0, NULL }, 602*260e9a87SYuri Pankov { "padj", roff_line_ignore, NULL, NULL, 0, NULL }, 603*260e9a87SYuri Pankov { "papersize", roff_line_ignore, NULL, NULL, 0, NULL }, 604*260e9a87SYuri Pankov { "pc", roff_line_ignore, NULL, NULL, 0, NULL }, 605*260e9a87SYuri Pankov { "pev", roff_line_ignore, NULL, NULL, 0, NULL }, 606*260e9a87SYuri Pankov { "pi", roff_insec, NULL, NULL, 0, NULL }, 607*260e9a87SYuri Pankov { "PI", roff_unsupp, NULL, NULL, 0, NULL }, 608*260e9a87SYuri Pankov { "pl", roff_line_ignore, NULL, NULL, 0, NULL }, 609*260e9a87SYuri Pankov { "pm", roff_line_ignore, NULL, NULL, 0, NULL }, 610*260e9a87SYuri Pankov { "pn", roff_line_ignore, NULL, NULL, 0, NULL }, 611*260e9a87SYuri Pankov { "pnr", roff_line_ignore, NULL, NULL, 0, NULL }, 612*260e9a87SYuri Pankov { "po", roff_line_ignore, NULL, NULL, 0, NULL }, 61395c635efSGarrett D'Amore { "ps", roff_line_ignore, NULL, NULL, 0, NULL }, 614*260e9a87SYuri Pankov { "psbb", roff_unsupp, NULL, NULL, 0, NULL }, 615*260e9a87SYuri Pankov { "pshape", roff_unsupp, NULL, NULL, 0, NULL }, 616*260e9a87SYuri Pankov { "pso", roff_insec, NULL, NULL, 0, NULL }, 617*260e9a87SYuri Pankov { "ptr", roff_line_ignore, NULL, NULL, 0, NULL }, 618*260e9a87SYuri Pankov { "pvs", roff_line_ignore, NULL, NULL, 0, NULL }, 619*260e9a87SYuri Pankov { "rchar", roff_unsupp, NULL, NULL, 0, NULL }, 620*260e9a87SYuri Pankov { "rd", roff_line_ignore, NULL, NULL, 0, NULL }, 621*260e9a87SYuri Pankov { "recursionlimit", roff_line_ignore, NULL, NULL, 0, NULL }, 622*260e9a87SYuri Pankov { "return", roff_unsupp, NULL, NULL, 0, NULL }, 623*260e9a87SYuri Pankov { "rfschar", roff_unsupp, NULL, NULL, 0, NULL }, 624*260e9a87SYuri Pankov { "rhang", roff_line_ignore, NULL, NULL, 0, NULL }, 625*260e9a87SYuri Pankov { "rj", roff_line_ignore, NULL, NULL, 0, NULL }, 62695c635efSGarrett D'Amore { "rm", roff_rm, NULL, NULL, 0, NULL }, 627*260e9a87SYuri Pankov { "rn", roff_unsupp, NULL, NULL, 0, NULL }, 628*260e9a87SYuri Pankov { "rnn", roff_unsupp, NULL, NULL, 0, NULL }, 629*260e9a87SYuri Pankov { "rr", roff_rr, NULL, NULL, 0, NULL }, 630*260e9a87SYuri Pankov { "rs", roff_line_ignore, NULL, NULL, 0, NULL }, 631*260e9a87SYuri Pankov { "rt", roff_line_ignore, NULL, NULL, 0, NULL }, 632*260e9a87SYuri Pankov { "schar", roff_unsupp, NULL, NULL, 0, NULL }, 633*260e9a87SYuri Pankov { "sentchar", roff_line_ignore, NULL, NULL, 0, NULL }, 634*260e9a87SYuri Pankov { "shc", roff_line_ignore, NULL, NULL, 0, NULL }, 635*260e9a87SYuri Pankov { "shift", roff_unsupp, NULL, NULL, 0, NULL }, 636*260e9a87SYuri Pankov { "sizes", roff_line_ignore, NULL, NULL, 0, NULL }, 63795c635efSGarrett D'Amore { "so", roff_so, NULL, NULL, 0, NULL }, 638*260e9a87SYuri Pankov { "spacewidth", roff_line_ignore, NULL, NULL, 0, NULL }, 639*260e9a87SYuri Pankov { "special", roff_line_ignore, NULL, NULL, 0, NULL }, 640*260e9a87SYuri Pankov { "spreadwarn", roff_line_ignore, NULL, NULL, 0, NULL }, 641*260e9a87SYuri Pankov { "ss", roff_line_ignore, NULL, NULL, 0, NULL }, 642*260e9a87SYuri Pankov { "sty", roff_line_ignore, NULL, NULL, 0, NULL }, 643*260e9a87SYuri Pankov { "substring", roff_unsupp, NULL, NULL, 0, NULL }, 644*260e9a87SYuri Pankov { "sv", roff_line_ignore, NULL, NULL, 0, NULL }, 645*260e9a87SYuri Pankov { "sy", roff_insec, NULL, NULL, 0, NULL }, 64695c635efSGarrett D'Amore { "T&", roff_T_, NULL, NULL, 0, NULL }, 647*260e9a87SYuri Pankov { "ta", roff_unsupp, NULL, NULL, 0, NULL }, 648*260e9a87SYuri Pankov { "tc", roff_unsupp, NULL, NULL, 0, NULL }, 649*260e9a87SYuri Pankov { "TE", roff_TE, NULL, NULL, 0, NULL }, 650*260e9a87SYuri Pankov { "TH", roff_TH, NULL, NULL, 0, NULL }, 651*260e9a87SYuri Pankov { "ti", roff_unsupp, NULL, NULL, 0, NULL }, 652*260e9a87SYuri Pankov { "tkf", roff_line_ignore, NULL, NULL, 0, NULL }, 653*260e9a87SYuri Pankov { "tl", roff_unsupp, NULL, NULL, 0, NULL }, 654*260e9a87SYuri Pankov { "tm", roff_line_ignore, NULL, NULL, 0, NULL }, 655*260e9a87SYuri Pankov { "tm1", roff_line_ignore, NULL, NULL, 0, NULL }, 656*260e9a87SYuri Pankov { "tmc", roff_line_ignore, NULL, NULL, 0, NULL }, 657*260e9a87SYuri Pankov { "tr", roff_tr, NULL, NULL, 0, NULL }, 658*260e9a87SYuri Pankov { "track", roff_line_ignore, NULL, NULL, 0, NULL }, 659*260e9a87SYuri Pankov { "transchar", roff_line_ignore, NULL, NULL, 0, NULL }, 660*260e9a87SYuri Pankov { "trf", roff_insec, NULL, NULL, 0, NULL }, 661*260e9a87SYuri Pankov { "trimat", roff_line_ignore, NULL, NULL, 0, NULL }, 662*260e9a87SYuri Pankov { "trin", roff_unsupp, NULL, NULL, 0, NULL }, 663*260e9a87SYuri Pankov { "trnt", roff_unsupp, NULL, NULL, 0, NULL }, 664*260e9a87SYuri Pankov { "troff", roff_line_ignore, NULL, NULL, 0, NULL }, 665*260e9a87SYuri Pankov { "TS", roff_TS, NULL, NULL, 0, NULL }, 666*260e9a87SYuri Pankov { "uf", roff_line_ignore, NULL, NULL, 0, NULL }, 667*260e9a87SYuri Pankov { "ul", roff_line_ignore, NULL, NULL, 0, NULL }, 668*260e9a87SYuri Pankov { "unformat", roff_unsupp, NULL, NULL, 0, NULL }, 669*260e9a87SYuri Pankov { "unwatch", roff_line_ignore, NULL, NULL, 0, NULL }, 670*260e9a87SYuri Pankov { "unwatchn", roff_line_ignore, NULL, NULL, 0, NULL }, 671*260e9a87SYuri Pankov { "vpt", roff_line_ignore, NULL, NULL, 0, NULL }, 672*260e9a87SYuri Pankov { "vs", roff_line_ignore, NULL, NULL, 0, NULL }, 673*260e9a87SYuri Pankov { "warn", roff_line_ignore, NULL, NULL, 0, NULL }, 674*260e9a87SYuri Pankov { "warnscale", roff_line_ignore, NULL, NULL, 0, NULL }, 675*260e9a87SYuri Pankov { "watch", roff_line_ignore, NULL, NULL, 0, NULL }, 676*260e9a87SYuri Pankov { "watchlength", roff_line_ignore, NULL, NULL, 0, NULL }, 677*260e9a87SYuri Pankov { "watchn", roff_line_ignore, NULL, NULL, 0, NULL }, 678*260e9a87SYuri Pankov { "wh", roff_unsupp, NULL, NULL, 0, NULL }, 679*260e9a87SYuri Pankov { "while", roff_unsupp, NULL, NULL, 0, NULL }, 680*260e9a87SYuri Pankov { "write", roff_insec, NULL, NULL, 0, NULL }, 681*260e9a87SYuri Pankov { "writec", roff_insec, NULL, NULL, 0, NULL }, 682*260e9a87SYuri Pankov { "writem", roff_insec, NULL, NULL, 0, NULL }, 683*260e9a87SYuri Pankov { "xflag", roff_line_ignore, NULL, NULL, 0, NULL }, 68495c635efSGarrett D'Amore { ".", roff_cblock, NULL, NULL, 0, NULL }, 68595c635efSGarrett D'Amore { NULL, roff_userdef, NULL, NULL, 0, NULL }, 68695c635efSGarrett D'Amore }; 68795c635efSGarrett D'Amore 688*260e9a87SYuri Pankov /* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */ 689698f87a4SGarrett D'Amore const char *const __mdoc_reserved[] = { 690698f87a4SGarrett D'Amore "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", 691698f87a4SGarrett D'Amore "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq", 692698f87a4SGarrett D'Amore "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx", 693698f87a4SGarrett D'Amore "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq", 694*260e9a87SYuri Pankov "Dt", "Dv", "Dx", "D1", 695*260e9a87SYuri Pankov "Ec", "Ed", "Ef", "Ek", "El", "Em", 696*260e9a87SYuri Pankov "En", "Eo", "Er", "Es", "Ev", "Ex", 697698f87a4SGarrett D'Amore "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx", 698*260e9a87SYuri Pankov "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp", 699*260e9a87SYuri Pankov "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx", 700698f87a4SGarrett D'Amore "Oc", "Oo", "Op", "Os", "Ot", "Ox", 701*260e9a87SYuri Pankov "Pa", "Pc", "Pf", "Po", "Pp", "Pq", 702*260e9a87SYuri Pankov "Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv", 703*260e9a87SYuri Pankov "Sc", "Sh", "Sm", "So", "Sq", 704698f87a4SGarrett D'Amore "Ss", "St", "Sx", "Sy", 705698f87a4SGarrett D'Amore "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr", 706*260e9a87SYuri Pankov "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O", 707698f87a4SGarrett D'Amore "%P", "%Q", "%R", "%T", "%U", "%V", 708698f87a4SGarrett D'Amore NULL 709698f87a4SGarrett D'Amore }; 710698f87a4SGarrett D'Amore 711*260e9a87SYuri Pankov /* not currently implemented: BT DE DS ME MT PT SY TQ YS */ 712698f87a4SGarrett D'Amore const char *const __man_reserved[] = { 713*260e9a87SYuri Pankov "AT", "B", "BI", "BR", "DT", 714*260e9a87SYuri Pankov "EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR", 715*260e9a87SYuri Pankov "LP", "OP", "P", "PD", "PP", 716*260e9a87SYuri Pankov "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS", 717*260e9a87SYuri Pankov "TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR", 718698f87a4SGarrett D'Amore NULL 719698f87a4SGarrett D'Amore }; 720698f87a4SGarrett D'Amore 72195c635efSGarrett D'Amore /* Array of injected predefined strings. */ 72295c635efSGarrett D'Amore #define PREDEFS_MAX 38 72395c635efSGarrett D'Amore static const struct predef predefs[PREDEFS_MAX] = { 72495c635efSGarrett D'Amore #include "predefs.in" 72595c635efSGarrett D'Amore }; 72695c635efSGarrett D'Amore 72795c635efSGarrett D'Amore /* See roffhash_find() */ 72895c635efSGarrett D'Amore #define ROFF_HASH(p) (p[0] - ASCII_LO) 72995c635efSGarrett D'Amore 730698f87a4SGarrett D'Amore static int roffit_lines; /* number of lines to delay */ 731698f87a4SGarrett D'Amore static char *roffit_macro; /* nil-terminated macro line */ 732698f87a4SGarrett D'Amore 733*260e9a87SYuri Pankov 73495c635efSGarrett D'Amore static void 73595c635efSGarrett D'Amore roffhash_init(void) 73695c635efSGarrett D'Amore { 73795c635efSGarrett D'Amore struct roffmac *n; 73895c635efSGarrett D'Amore int buc, i; 73995c635efSGarrett D'Amore 74095c635efSGarrett D'Amore for (i = 0; i < (int)ROFF_USERDEF; i++) { 74195c635efSGarrett D'Amore assert(roffs[i].name[0] >= ASCII_LO); 74295c635efSGarrett D'Amore assert(roffs[i].name[0] <= ASCII_HI); 74395c635efSGarrett D'Amore 74495c635efSGarrett D'Amore buc = ROFF_HASH(roffs[i].name); 74595c635efSGarrett D'Amore 74695c635efSGarrett D'Amore if (NULL != (n = hash[buc])) { 74795c635efSGarrett D'Amore for ( ; n->next; n = n->next) 74895c635efSGarrett D'Amore /* Do nothing. */ ; 74995c635efSGarrett D'Amore n->next = &roffs[i]; 75095c635efSGarrett D'Amore } else 75195c635efSGarrett D'Amore hash[buc] = &roffs[i]; 75295c635efSGarrett D'Amore } 75395c635efSGarrett D'Amore } 75495c635efSGarrett D'Amore 75595c635efSGarrett D'Amore /* 75695c635efSGarrett D'Amore * Look up a roff token by its name. Returns ROFF_MAX if no macro by 75795c635efSGarrett D'Amore * the nil-terminated string name could be found. 75895c635efSGarrett D'Amore */ 75995c635efSGarrett D'Amore static enum rofft 76095c635efSGarrett D'Amore roffhash_find(const char *p, size_t s) 76195c635efSGarrett D'Amore { 76295c635efSGarrett D'Amore int buc; 76395c635efSGarrett D'Amore struct roffmac *n; 76495c635efSGarrett D'Amore 76595c635efSGarrett D'Amore /* 76695c635efSGarrett D'Amore * libroff has an extremely simple hashtable, for the time 76795c635efSGarrett D'Amore * being, which simply keys on the first character, which must 76895c635efSGarrett D'Amore * be printable, then walks a chain. It works well enough until 76995c635efSGarrett D'Amore * optimised. 77095c635efSGarrett D'Amore */ 77195c635efSGarrett D'Amore 77295c635efSGarrett D'Amore if (p[0] < ASCII_LO || p[0] > ASCII_HI) 77395c635efSGarrett D'Amore return(ROFF_MAX); 77495c635efSGarrett D'Amore 77595c635efSGarrett D'Amore buc = ROFF_HASH(p); 77695c635efSGarrett D'Amore 77795c635efSGarrett D'Amore if (NULL == (n = hash[buc])) 77895c635efSGarrett D'Amore return(ROFF_MAX); 77995c635efSGarrett D'Amore for ( ; n; n = n->next) 78095c635efSGarrett D'Amore if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s]) 78195c635efSGarrett D'Amore return((enum rofft)(n - roffs)); 78295c635efSGarrett D'Amore 78395c635efSGarrett D'Amore return(ROFF_MAX); 78495c635efSGarrett D'Amore } 78595c635efSGarrett D'Amore 78695c635efSGarrett D'Amore /* 78795c635efSGarrett D'Amore * Pop the current node off of the stack of roff instructions currently 78895c635efSGarrett D'Amore * pending. 78995c635efSGarrett D'Amore */ 79095c635efSGarrett D'Amore static void 79195c635efSGarrett D'Amore roffnode_pop(struct roff *r) 79295c635efSGarrett D'Amore { 79395c635efSGarrett D'Amore struct roffnode *p; 79495c635efSGarrett D'Amore 79595c635efSGarrett D'Amore assert(r->last); 79695c635efSGarrett D'Amore p = r->last; 79795c635efSGarrett D'Amore 79895c635efSGarrett D'Amore r->last = r->last->parent; 79995c635efSGarrett D'Amore free(p->name); 80095c635efSGarrett D'Amore free(p->end); 80195c635efSGarrett D'Amore free(p); 80295c635efSGarrett D'Amore } 80395c635efSGarrett D'Amore 80495c635efSGarrett D'Amore /* 80595c635efSGarrett D'Amore * Push a roff node onto the instruction stack. This must later be 80695c635efSGarrett D'Amore * removed with roffnode_pop(). 80795c635efSGarrett D'Amore */ 80895c635efSGarrett D'Amore static void 80995c635efSGarrett D'Amore roffnode_push(struct roff *r, enum rofft tok, const char *name, 81095c635efSGarrett D'Amore int line, int col) 81195c635efSGarrett D'Amore { 81295c635efSGarrett D'Amore struct roffnode *p; 81395c635efSGarrett D'Amore 81495c635efSGarrett D'Amore p = mandoc_calloc(1, sizeof(struct roffnode)); 81595c635efSGarrett D'Amore p->tok = tok; 81695c635efSGarrett D'Amore if (name) 81795c635efSGarrett D'Amore p->name = mandoc_strdup(name); 81895c635efSGarrett D'Amore p->parent = r->last; 81995c635efSGarrett D'Amore p->line = line; 82095c635efSGarrett D'Amore p->col = col; 821*260e9a87SYuri Pankov p->rule = p->parent ? p->parent->rule : 0; 82295c635efSGarrett D'Amore 82395c635efSGarrett D'Amore r->last = p; 82495c635efSGarrett D'Amore } 82595c635efSGarrett D'Amore 82695c635efSGarrett D'Amore static void 82795c635efSGarrett D'Amore roff_free1(struct roff *r) 82895c635efSGarrett D'Amore { 829698f87a4SGarrett D'Amore struct tbl_node *tbl; 83095c635efSGarrett D'Amore struct eqn_node *e; 83195c635efSGarrett D'Amore int i; 83295c635efSGarrett D'Amore 833698f87a4SGarrett D'Amore while (NULL != (tbl = r->first_tbl)) { 834698f87a4SGarrett D'Amore r->first_tbl = tbl->next; 835698f87a4SGarrett D'Amore tbl_free(tbl); 83695c635efSGarrett D'Amore } 83795c635efSGarrett D'Amore r->first_tbl = r->last_tbl = r->tbl = NULL; 83895c635efSGarrett D'Amore 83995c635efSGarrett D'Amore while (NULL != (e = r->first_eqn)) { 84095c635efSGarrett D'Amore r->first_eqn = e->next; 84195c635efSGarrett D'Amore eqn_free(e); 84295c635efSGarrett D'Amore } 84395c635efSGarrett D'Amore r->first_eqn = r->last_eqn = r->eqn = NULL; 84495c635efSGarrett D'Amore 84595c635efSGarrett D'Amore while (r->last) 84695c635efSGarrett D'Amore roffnode_pop(r); 84795c635efSGarrett D'Amore 848*260e9a87SYuri Pankov free (r->rstack); 849*260e9a87SYuri Pankov r->rstack = NULL; 850*260e9a87SYuri Pankov r->rstacksz = 0; 851*260e9a87SYuri Pankov r->rstackpos = -1; 85295c635efSGarrett D'Amore 853698f87a4SGarrett D'Amore roff_freereg(r->regtab); 854698f87a4SGarrett D'Amore r->regtab = NULL; 855698f87a4SGarrett D'Amore 856*260e9a87SYuri Pankov roff_freestr(r->strtab); 857*260e9a87SYuri Pankov roff_freestr(r->xmbtab); 858*260e9a87SYuri Pankov r->strtab = r->xmbtab = NULL; 859*260e9a87SYuri Pankov 86095c635efSGarrett D'Amore if (r->xtab) 86195c635efSGarrett D'Amore for (i = 0; i < 128; i++) 86295c635efSGarrett D'Amore free(r->xtab[i].p); 86395c635efSGarrett D'Amore free(r->xtab); 86495c635efSGarrett D'Amore r->xtab = NULL; 86595c635efSGarrett D'Amore } 86695c635efSGarrett D'Amore 86795c635efSGarrett D'Amore void 86895c635efSGarrett D'Amore roff_reset(struct roff *r) 86995c635efSGarrett D'Amore { 87095c635efSGarrett D'Amore 87195c635efSGarrett D'Amore roff_free1(r); 872*260e9a87SYuri Pankov r->format = r->options & (MPARSE_MDOC | MPARSE_MAN); 873698f87a4SGarrett D'Amore r->control = 0; 87495c635efSGarrett D'Amore } 87595c635efSGarrett D'Amore 87695c635efSGarrett D'Amore void 87795c635efSGarrett D'Amore roff_free(struct roff *r) 87895c635efSGarrett D'Amore { 87995c635efSGarrett D'Amore 88095c635efSGarrett D'Amore roff_free1(r); 88195c635efSGarrett D'Amore free(r); 88295c635efSGarrett D'Amore } 88395c635efSGarrett D'Amore 88495c635efSGarrett D'Amore struct roff * 885*260e9a87SYuri Pankov roff_alloc(struct mparse *parse, const struct mchars *mchars, int options) 88695c635efSGarrett D'Amore { 88795c635efSGarrett D'Amore struct roff *r; 88895c635efSGarrett D'Amore 88995c635efSGarrett D'Amore r = mandoc_calloc(1, sizeof(struct roff)); 89095c635efSGarrett D'Amore r->parse = parse; 891*260e9a87SYuri Pankov r->mchars = mchars; 892*260e9a87SYuri Pankov r->options = options; 893*260e9a87SYuri Pankov r->format = options & (MPARSE_MDOC | MPARSE_MAN); 89495c635efSGarrett D'Amore r->rstackpos = -1; 89595c635efSGarrett D'Amore 89695c635efSGarrett D'Amore roffhash_init(); 89795c635efSGarrett D'Amore 89895c635efSGarrett D'Amore return(r); 89995c635efSGarrett D'Amore } 90095c635efSGarrett D'Amore 90195c635efSGarrett D'Amore /* 902*260e9a87SYuri Pankov * In the current line, expand escape sequences that tend to get 903*260e9a87SYuri Pankov * used in numerical expressions and conditional requests. 904*260e9a87SYuri Pankov * Also check the syntax of the remaining escape sequences. 90595c635efSGarrett D'Amore */ 90695c635efSGarrett D'Amore static enum rofferr 907*260e9a87SYuri Pankov roff_res(struct roff *r, struct buf *buf, int ln, int pos) 90895c635efSGarrett D'Amore { 909*260e9a87SYuri Pankov char ubuf[24]; /* buffer to print the number */ 910*260e9a87SYuri Pankov const char *start; /* start of the string to process */ 911*260e9a87SYuri Pankov char *stesc; /* start of an escape sequence ('\\') */ 91295c635efSGarrett D'Amore const char *stnam; /* start of the name, after "[(*" */ 91395c635efSGarrett D'Amore const char *cp; /* end of the name, e.g. before ']' */ 91495c635efSGarrett D'Amore const char *res; /* the string to be substituted */ 915*260e9a87SYuri Pankov char *nbuf; /* new buffer to copy buf->buf to */ 916698f87a4SGarrett D'Amore size_t maxl; /* expected length of the escape name */ 917698f87a4SGarrett D'Amore size_t naml; /* actual length of the escape name */ 918*260e9a87SYuri Pankov enum mandoc_esc esc; /* type of the escape sequence */ 919*260e9a87SYuri Pankov int inaml; /* length returned from mandoc_escape() */ 920698f87a4SGarrett D'Amore int expand_count; /* to avoid infinite loops */ 921*260e9a87SYuri Pankov int npos; /* position in numeric expression */ 922*260e9a87SYuri Pankov int arg_complete; /* argument not interrupted by eol */ 923*260e9a87SYuri Pankov char term; /* character terminating the escape */ 92495c635efSGarrett D'Amore 92595c635efSGarrett D'Amore expand_count = 0; 926*260e9a87SYuri Pankov start = buf->buf + pos; 927*260e9a87SYuri Pankov stesc = strchr(start, '\0') - 1; 928*260e9a87SYuri Pankov while (stesc-- > start) { 92995c635efSGarrett D'Amore 930*260e9a87SYuri Pankov /* Search backwards for the next backslash. */ 93195c635efSGarrett D'Amore 932*260e9a87SYuri Pankov if (*stesc != '\\') 933*260e9a87SYuri Pankov continue; 93495c635efSGarrett D'Amore 935*260e9a87SYuri Pankov /* If it is escaped, skip it. */ 93695c635efSGarrett D'Amore 937*260e9a87SYuri Pankov for (cp = stesc - 1; cp >= start; cp--) 938*260e9a87SYuri Pankov if (*cp != '\\') 939*260e9a87SYuri Pankov break; 940*260e9a87SYuri Pankov 941*260e9a87SYuri Pankov if ((stesc - cp) % 2 == 0) { 942*260e9a87SYuri Pankov stesc = (char *)cp; 943*260e9a87SYuri Pankov continue; 944*260e9a87SYuri Pankov } 945*260e9a87SYuri Pankov 946*260e9a87SYuri Pankov /* Decide whether to expand or to check only. */ 947*260e9a87SYuri Pankov 948*260e9a87SYuri Pankov term = '\0'; 949*260e9a87SYuri Pankov cp = stesc + 1; 950698f87a4SGarrett D'Amore switch (*cp) { 951*260e9a87SYuri Pankov case '*': 952698f87a4SGarrett D'Amore res = NULL; 953698f87a4SGarrett D'Amore break; 954*260e9a87SYuri Pankov case 'B': 955*260e9a87SYuri Pankov /* FALLTHROUGH */ 956*260e9a87SYuri Pankov case 'w': 957*260e9a87SYuri Pankov term = cp[1]; 958*260e9a87SYuri Pankov /* FALLTHROUGH */ 959*260e9a87SYuri Pankov case 'n': 960698f87a4SGarrett D'Amore res = ubuf; 961698f87a4SGarrett D'Amore break; 962698f87a4SGarrett D'Amore default: 963*260e9a87SYuri Pankov esc = mandoc_escape(&cp, &stnam, &inaml); 964*260e9a87SYuri Pankov if (esc == ESCAPE_ERROR || 965*260e9a87SYuri Pankov (esc == ESCAPE_SPECIAL && 966*260e9a87SYuri Pankov mchars_spec2cp(r->mchars, stnam, inaml) < 0)) 967*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_ESC_BAD, 968*260e9a87SYuri Pankov r->parse, ln, (int)(stesc - buf->buf), 969*260e9a87SYuri Pankov "%.*s", (int)(cp - stesc), stesc); 97095c635efSGarrett D'Amore continue; 97195c635efSGarrett D'Amore } 97295c635efSGarrett D'Amore 973*260e9a87SYuri Pankov if (EXPAND_LIMIT < ++expand_count) { 974*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, 975*260e9a87SYuri Pankov ln, (int)(stesc - buf->buf), NULL); 976*260e9a87SYuri Pankov return(ROFF_IGN); 977*260e9a87SYuri Pankov } 97895c635efSGarrett D'Amore 97995c635efSGarrett D'Amore /* 98095c635efSGarrett D'Amore * The third character decides the length 981698f87a4SGarrett D'Amore * of the name of the string or register. 98295c635efSGarrett D'Amore * Save a pointer to the name. 98395c635efSGarrett D'Amore */ 98495c635efSGarrett D'Amore 985*260e9a87SYuri Pankov if (term == '\0') { 986*260e9a87SYuri Pankov switch (*++cp) { 987*260e9a87SYuri Pankov case '\0': 988*260e9a87SYuri Pankov maxl = 0; 989*260e9a87SYuri Pankov break; 990*260e9a87SYuri Pankov case '(': 99195c635efSGarrett D'Amore cp++; 99295c635efSGarrett D'Amore maxl = 2; 99395c635efSGarrett D'Amore break; 994*260e9a87SYuri Pankov case '[': 99595c635efSGarrett D'Amore cp++; 996*260e9a87SYuri Pankov term = ']'; 99795c635efSGarrett D'Amore maxl = 0; 99895c635efSGarrett D'Amore break; 99995c635efSGarrett D'Amore default: 100095c635efSGarrett D'Amore maxl = 1; 100195c635efSGarrett D'Amore break; 100295c635efSGarrett D'Amore } 1003*260e9a87SYuri Pankov } else { 1004*260e9a87SYuri Pankov cp += 2; 1005*260e9a87SYuri Pankov maxl = 0; 1006*260e9a87SYuri Pankov } 100795c635efSGarrett D'Amore stnam = cp; 100895c635efSGarrett D'Amore 100995c635efSGarrett D'Amore /* Advance to the end of the name. */ 101095c635efSGarrett D'Amore 1011*260e9a87SYuri Pankov naml = 0; 1012*260e9a87SYuri Pankov arg_complete = 1; 1013*260e9a87SYuri Pankov while (maxl == 0 || naml < maxl) { 1014*260e9a87SYuri Pankov if (*cp == '\0') { 1015*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ESC_BAD, r->parse, 1016*260e9a87SYuri Pankov ln, (int)(stesc - buf->buf), stesc); 1017*260e9a87SYuri Pankov arg_complete = 0; 101895c635efSGarrett D'Amore break; 101995c635efSGarrett D'Amore } 1020*260e9a87SYuri Pankov if (maxl == 0 && *cp == term) { 1021*260e9a87SYuri Pankov cp++; 1022*260e9a87SYuri Pankov break; 1023*260e9a87SYuri Pankov } 1024*260e9a87SYuri Pankov if (*cp++ != '\\' || stesc[1] != 'w') { 1025*260e9a87SYuri Pankov naml++; 1026*260e9a87SYuri Pankov continue; 1027*260e9a87SYuri Pankov } 1028*260e9a87SYuri Pankov switch (mandoc_escape(&cp, NULL, NULL)) { 1029*260e9a87SYuri Pankov case ESCAPE_SPECIAL: 1030*260e9a87SYuri Pankov /* FALLTHROUGH */ 1031*260e9a87SYuri Pankov case ESCAPE_UNICODE: 1032*260e9a87SYuri Pankov /* FALLTHROUGH */ 1033*260e9a87SYuri Pankov case ESCAPE_NUMBERED: 1034*260e9a87SYuri Pankov /* FALLTHROUGH */ 1035*260e9a87SYuri Pankov case ESCAPE_OVERSTRIKE: 1036*260e9a87SYuri Pankov naml++; 1037*260e9a87SYuri Pankov break; 1038*260e9a87SYuri Pankov default: 1039*260e9a87SYuri Pankov break; 1040*260e9a87SYuri Pankov } 1041*260e9a87SYuri Pankov } 104295c635efSGarrett D'Amore 104395c635efSGarrett D'Amore /* 104495c635efSGarrett D'Amore * Retrieve the replacement string; if it is 104595c635efSGarrett D'Amore * undefined, resume searching for escapes. 104695c635efSGarrett D'Amore */ 104795c635efSGarrett D'Amore 1048*260e9a87SYuri Pankov switch (stesc[1]) { 1049*260e9a87SYuri Pankov case '*': 1050*260e9a87SYuri Pankov if (arg_complete) 1051698f87a4SGarrett D'Amore res = roff_getstrn(r, stnam, naml); 1052*260e9a87SYuri Pankov break; 1053*260e9a87SYuri Pankov case 'B': 1054*260e9a87SYuri Pankov npos = 0; 1055*260e9a87SYuri Pankov ubuf[0] = arg_complete && 1056*260e9a87SYuri Pankov roff_evalnum(r, ln, stnam, &npos, 1057*260e9a87SYuri Pankov NULL, ROFFNUM_SCALE) && 1058*260e9a87SYuri Pankov stnam + npos + 1 == cp ? '1' : '0'; 1059*260e9a87SYuri Pankov ubuf[1] = '\0'; 1060*260e9a87SYuri Pankov break; 1061*260e9a87SYuri Pankov case 'n': 1062*260e9a87SYuri Pankov if (arg_complete) 1063*260e9a87SYuri Pankov (void)snprintf(ubuf, sizeof(ubuf), "%d", 1064698f87a4SGarrett D'Amore roff_getregn(r, stnam, naml)); 1065*260e9a87SYuri Pankov else 1066*260e9a87SYuri Pankov ubuf[0] = '\0'; 1067*260e9a87SYuri Pankov break; 1068*260e9a87SYuri Pankov case 'w': 1069*260e9a87SYuri Pankov /* use even incomplete args */ 1070*260e9a87SYuri Pankov (void)snprintf(ubuf, sizeof(ubuf), "%d", 1071*260e9a87SYuri Pankov 24 * (int)naml); 1072*260e9a87SYuri Pankov break; 1073*260e9a87SYuri Pankov } 107495c635efSGarrett D'Amore 1075*260e9a87SYuri Pankov if (res == NULL) { 1076*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_STR_UNDEF, 1077*260e9a87SYuri Pankov r->parse, ln, (int)(stesc - buf->buf), 1078*260e9a87SYuri Pankov "%.*s", (int)naml, stnam); 107995c635efSGarrett D'Amore res = ""; 1080*260e9a87SYuri Pankov } else if (buf->sz + strlen(res) > SHRT_MAX) { 1081*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, 1082*260e9a87SYuri Pankov ln, (int)(stesc - buf->buf), NULL); 1083*260e9a87SYuri Pankov return(ROFF_IGN); 108495c635efSGarrett D'Amore } 108595c635efSGarrett D'Amore 108695c635efSGarrett D'Amore /* Replace the escape sequence by the string. */ 108795c635efSGarrett D'Amore 1088*260e9a87SYuri Pankov *stesc = '\0'; 1089*260e9a87SYuri Pankov buf->sz = mandoc_asprintf(&nbuf, "%s%s%s", 1090*260e9a87SYuri Pankov buf->buf, res, cp) + 1; 109195c635efSGarrett D'Amore 1092*260e9a87SYuri Pankov /* Prepare for the next replacement. */ 109395c635efSGarrett D'Amore 1094*260e9a87SYuri Pankov start = nbuf + pos; 1095*260e9a87SYuri Pankov stesc = nbuf + (stesc - buf->buf) + strlen(res); 1096*260e9a87SYuri Pankov free(buf->buf); 1097*260e9a87SYuri Pankov buf->buf = nbuf; 109895c635efSGarrett D'Amore } 109995c635efSGarrett D'Amore return(ROFF_CONT); 110095c635efSGarrett D'Amore } 110195c635efSGarrett D'Amore 110295c635efSGarrett D'Amore /* 1103698f87a4SGarrett D'Amore * Process text streams: 1104698f87a4SGarrett D'Amore * Convert all breakable hyphens into ASCII_HYPH. 1105698f87a4SGarrett D'Amore * Decrement and spring input line trap. 110695c635efSGarrett D'Amore */ 110795c635efSGarrett D'Amore static enum rofferr 1108*260e9a87SYuri Pankov roff_parsetext(struct buf *buf, int pos, int *offs) 110995c635efSGarrett D'Amore { 111095c635efSGarrett D'Amore size_t sz; 111195c635efSGarrett D'Amore const char *start; 1112698f87a4SGarrett D'Amore char *p; 1113698f87a4SGarrett D'Amore int isz; 111495c635efSGarrett D'Amore enum mandoc_esc esc; 111595c635efSGarrett D'Amore 1116*260e9a87SYuri Pankov start = p = buf->buf + pos; 111795c635efSGarrett D'Amore 1118*260e9a87SYuri Pankov while (*p != '\0') { 111995c635efSGarrett D'Amore sz = strcspn(p, "-\\"); 112095c635efSGarrett D'Amore p += sz; 112195c635efSGarrett D'Amore 1122*260e9a87SYuri Pankov if (*p == '\0') 112395c635efSGarrett D'Amore break; 112495c635efSGarrett D'Amore 1125*260e9a87SYuri Pankov if (*p == '\\') { 112695c635efSGarrett D'Amore /* Skip over escapes. */ 112795c635efSGarrett D'Amore p++; 1128698f87a4SGarrett D'Amore esc = mandoc_escape((const char **)&p, NULL, NULL); 1129*260e9a87SYuri Pankov if (esc == ESCAPE_ERROR) 113095c635efSGarrett D'Amore break; 113195c635efSGarrett D'Amore continue; 113295c635efSGarrett D'Amore } else if (p == start) { 113395c635efSGarrett D'Amore p++; 113495c635efSGarrett D'Amore continue; 113595c635efSGarrett D'Amore } 113695c635efSGarrett D'Amore 113795c635efSGarrett D'Amore if (isalpha((unsigned char)p[-1]) && 113895c635efSGarrett D'Amore isalpha((unsigned char)p[1])) 113995c635efSGarrett D'Amore *p = ASCII_HYPH; 114095c635efSGarrett D'Amore p++; 114195c635efSGarrett D'Amore } 114295c635efSGarrett D'Amore 1143698f87a4SGarrett D'Amore /* Spring the input line trap. */ 1144*260e9a87SYuri Pankov if (roffit_lines == 1) { 1145*260e9a87SYuri Pankov isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro); 1146*260e9a87SYuri Pankov free(buf->buf); 1147*260e9a87SYuri Pankov buf->buf = p; 1148*260e9a87SYuri Pankov buf->sz = isz + 1; 1149698f87a4SGarrett D'Amore *offs = 0; 1150698f87a4SGarrett D'Amore free(roffit_macro); 1151698f87a4SGarrett D'Amore roffit_lines = 0; 1152698f87a4SGarrett D'Amore return(ROFF_REPARSE); 1153*260e9a87SYuri Pankov } else if (roffit_lines > 1) 1154698f87a4SGarrett D'Amore --roffit_lines; 115595c635efSGarrett D'Amore return(ROFF_CONT); 115695c635efSGarrett D'Amore } 115795c635efSGarrett D'Amore 115895c635efSGarrett D'Amore enum rofferr 1159*260e9a87SYuri Pankov roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) 116095c635efSGarrett D'Amore { 116195c635efSGarrett D'Amore enum rofft t; 116295c635efSGarrett D'Amore enum rofferr e; 1163*260e9a87SYuri Pankov int pos; /* parse point */ 1164*260e9a87SYuri Pankov int spos; /* saved parse point for messages */ 1165*260e9a87SYuri Pankov int ppos; /* original offset in buf->buf */ 1166*260e9a87SYuri Pankov int ctl; /* macro line (boolean) */ 116795c635efSGarrett D'Amore 1168*260e9a87SYuri Pankov ppos = pos = *offs; 116995c635efSGarrett D'Amore 1170*260e9a87SYuri Pankov /* Handle in-line equation delimiters. */ 1171*260e9a87SYuri Pankov 1172*260e9a87SYuri Pankov if (r->tbl == NULL && 1173*260e9a87SYuri Pankov r->last_eqn != NULL && r->last_eqn->delim && 1174*260e9a87SYuri Pankov (r->eqn == NULL || r->eqn_inline)) { 1175*260e9a87SYuri Pankov e = roff_eqndelim(r, buf, pos); 1176*260e9a87SYuri Pankov if (e == ROFF_REPARSE) 117795c635efSGarrett D'Amore return(e); 1178*260e9a87SYuri Pankov assert(e == ROFF_CONT); 1179*260e9a87SYuri Pankov } 118095c635efSGarrett D'Amore 1181*260e9a87SYuri Pankov /* Expand some escape sequences. */ 1182*260e9a87SYuri Pankov 1183*260e9a87SYuri Pankov e = roff_res(r, buf, ln, pos); 1184*260e9a87SYuri Pankov if (e == ROFF_IGN) 1185*260e9a87SYuri Pankov return(e); 1186*260e9a87SYuri Pankov assert(e == ROFF_CONT); 1187*260e9a87SYuri Pankov 1188*260e9a87SYuri Pankov ctl = roff_getcontrol(r, buf->buf, &pos); 118995c635efSGarrett D'Amore 119095c635efSGarrett D'Amore /* 119195c635efSGarrett D'Amore * First, if a scope is open and we're not a macro, pass the 1192*260e9a87SYuri Pankov * text through the macro's filter. 1193*260e9a87SYuri Pankov * Equations process all content themselves. 1194*260e9a87SYuri Pankov * Tables process almost all content themselves, but we want 1195*260e9a87SYuri Pankov * to warn about macros before passing it there. 119695c635efSGarrett D'Amore */ 119795c635efSGarrett D'Amore 1198*260e9a87SYuri Pankov if (r->last != NULL && ! ctl) { 119995c635efSGarrett D'Amore t = r->last->tok; 120095c635efSGarrett D'Amore assert(roffs[t].text); 1201*260e9a87SYuri Pankov e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs); 1202*260e9a87SYuri Pankov assert(e == ROFF_IGN || e == ROFF_CONT); 1203*260e9a87SYuri Pankov if (e != ROFF_CONT) 120495c635efSGarrett D'Amore return(e); 1205698f87a4SGarrett D'Amore } 1206*260e9a87SYuri Pankov if (r->eqn != NULL) 1207*260e9a87SYuri Pankov return(eqn_read(&r->eqn, ln, buf->buf, ppos, offs)); 1208*260e9a87SYuri Pankov if (r->tbl != NULL && ( ! ctl || buf->buf[pos] == '\0')) 1209*260e9a87SYuri Pankov return(tbl_read(r->tbl, ln, buf->buf, ppos)); 1210*260e9a87SYuri Pankov if ( ! ctl) 1211*260e9a87SYuri Pankov return(roff_parsetext(buf, pos, offs)); 1212*260e9a87SYuri Pankov 1213*260e9a87SYuri Pankov /* Skip empty request lines. */ 1214*260e9a87SYuri Pankov 1215*260e9a87SYuri Pankov if (buf->buf[pos] == '"') { 1216*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_COMMENT_BAD, r->parse, 1217*260e9a87SYuri Pankov ln, pos, NULL); 1218*260e9a87SYuri Pankov return(ROFF_IGN); 1219*260e9a87SYuri Pankov } else if (buf->buf[pos] == '\0') 1220*260e9a87SYuri Pankov return(ROFF_IGN); 122195c635efSGarrett D'Amore 122295c635efSGarrett D'Amore /* 122395c635efSGarrett D'Amore * If a scope is open, go to the child handler for that macro, 122495c635efSGarrett D'Amore * as it may want to preprocess before doing anything with it. 122595c635efSGarrett D'Amore * Don't do so if an equation is open. 122695c635efSGarrett D'Amore */ 122795c635efSGarrett D'Amore 122895c635efSGarrett D'Amore if (r->last) { 122995c635efSGarrett D'Amore t = r->last->tok; 123095c635efSGarrett D'Amore assert(roffs[t].sub); 1231*260e9a87SYuri Pankov return((*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs)); 1232*260e9a87SYuri Pankov } 1233*260e9a87SYuri Pankov 1234*260e9a87SYuri Pankov /* No scope is open. This is a new request or macro. */ 1235*260e9a87SYuri Pankov 1236*260e9a87SYuri Pankov spos = pos; 1237*260e9a87SYuri Pankov t = roff_parse(r, buf->buf, &pos, ln, ppos); 1238*260e9a87SYuri Pankov 1239*260e9a87SYuri Pankov /* Tables ignore most macros. */ 1240*260e9a87SYuri Pankov 1241*260e9a87SYuri Pankov if (r->tbl != NULL && (t == ROFF_MAX || t == ROFF_TS)) { 1242*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_TBLMACRO, r->parse, 1243*260e9a87SYuri Pankov ln, pos, buf->buf + spos); 1244*260e9a87SYuri Pankov if (t == ROFF_TS) 1245*260e9a87SYuri Pankov return(ROFF_IGN); 1246*260e9a87SYuri Pankov while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ') 1247*260e9a87SYuri Pankov pos++; 1248*260e9a87SYuri Pankov while (buf->buf[pos] != '\0' && buf->buf[pos] == ' ') 1249*260e9a87SYuri Pankov pos++; 1250*260e9a87SYuri Pankov return(tbl_read(r->tbl, ln, buf->buf, pos)); 125195c635efSGarrett D'Amore } 125295c635efSGarrett D'Amore 125395c635efSGarrett D'Amore /* 1254*260e9a87SYuri Pankov * This is neither a roff request nor a user-defined macro. 1255*260e9a87SYuri Pankov * Let the standard macro set parsers handle it. 125695c635efSGarrett D'Amore */ 125795c635efSGarrett D'Amore 1258*260e9a87SYuri Pankov if (t == ROFF_MAX) 125995c635efSGarrett D'Amore return(ROFF_CONT); 126095c635efSGarrett D'Amore 1261*260e9a87SYuri Pankov /* Execute a roff request or a user defined macro. */ 126295c635efSGarrett D'Amore 1263*260e9a87SYuri Pankov assert(roffs[t].proc); 1264*260e9a87SYuri Pankov return((*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs)); 1265*260e9a87SYuri Pankov } 126695c635efSGarrett D'Amore 126795c635efSGarrett D'Amore void 126895c635efSGarrett D'Amore roff_endparse(struct roff *r) 126995c635efSGarrett D'Amore { 127095c635efSGarrett D'Amore 127195c635efSGarrett D'Amore if (r->last) 1272*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, 1273*260e9a87SYuri Pankov r->last->line, r->last->col, 1274*260e9a87SYuri Pankov roffs[r->last->tok].name); 127595c635efSGarrett D'Amore 127695c635efSGarrett D'Amore if (r->eqn) { 1277*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, 1278*260e9a87SYuri Pankov r->eqn->eqn.ln, r->eqn->eqn.pos, "EQ"); 127995c635efSGarrett D'Amore eqn_end(&r->eqn); 128095c635efSGarrett D'Amore } 128195c635efSGarrett D'Amore 128295c635efSGarrett D'Amore if (r->tbl) { 1283*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, 1284*260e9a87SYuri Pankov r->tbl->line, r->tbl->pos, "TS"); 128595c635efSGarrett D'Amore tbl_end(&r->tbl); 128695c635efSGarrett D'Amore } 128795c635efSGarrett D'Amore } 128895c635efSGarrett D'Amore 128995c635efSGarrett D'Amore /* 129095c635efSGarrett D'Amore * Parse a roff node's type from the input buffer. This must be in the 129195c635efSGarrett D'Amore * form of ".foo xxx" in the usual way. 129295c635efSGarrett D'Amore */ 129395c635efSGarrett D'Amore static enum rofft 1294*260e9a87SYuri Pankov roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos) 129595c635efSGarrett D'Amore { 1296*260e9a87SYuri Pankov char *cp; 129795c635efSGarrett D'Amore const char *mac; 129895c635efSGarrett D'Amore size_t maclen; 129995c635efSGarrett D'Amore enum rofft t; 130095c635efSGarrett D'Amore 1301*260e9a87SYuri Pankov cp = buf + *pos; 1302*260e9a87SYuri Pankov 1303*260e9a87SYuri Pankov if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp) 130495c635efSGarrett D'Amore return(ROFF_MAX); 130595c635efSGarrett D'Amore 1306*260e9a87SYuri Pankov mac = cp; 1307*260e9a87SYuri Pankov maclen = roff_getname(r, &cp, ln, ppos); 130895c635efSGarrett D'Amore 130995c635efSGarrett D'Amore t = (r->current_string = roff_getstrn(r, mac, maclen)) 131095c635efSGarrett D'Amore ? ROFF_USERDEF : roffhash_find(mac, maclen); 131195c635efSGarrett D'Amore 1312*260e9a87SYuri Pankov if (ROFF_MAX != t) 1313*260e9a87SYuri Pankov *pos = cp - buf; 131495c635efSGarrett D'Amore 131595c635efSGarrett D'Amore return(t); 131695c635efSGarrett D'Amore } 131795c635efSGarrett D'Amore 131895c635efSGarrett D'Amore static enum rofferr 131995c635efSGarrett D'Amore roff_cblock(ROFF_ARGS) 132095c635efSGarrett D'Amore { 132195c635efSGarrett D'Amore 132295c635efSGarrett D'Amore /* 132395c635efSGarrett D'Amore * A block-close `..' should only be invoked as a child of an 132495c635efSGarrett D'Amore * ignore macro, otherwise raise a warning and just ignore it. 132595c635efSGarrett D'Amore */ 132695c635efSGarrett D'Amore 1327*260e9a87SYuri Pankov if (r->last == NULL) { 1328*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 1329*260e9a87SYuri Pankov ln, ppos, ".."); 133095c635efSGarrett D'Amore return(ROFF_IGN); 133195c635efSGarrett D'Amore } 133295c635efSGarrett D'Amore 133395c635efSGarrett D'Amore switch (r->last->tok) { 1334*260e9a87SYuri Pankov case ROFF_am: 1335*260e9a87SYuri Pankov /* ROFF_am1 is remapped to ROFF_am in roff_block(). */ 133695c635efSGarrett D'Amore /* FALLTHROUGH */ 1337*260e9a87SYuri Pankov case ROFF_ami: 133895c635efSGarrett D'Amore /* FALLTHROUGH */ 1339*260e9a87SYuri Pankov case ROFF_de: 134095c635efSGarrett D'Amore /* ROFF_de1 is remapped to ROFF_de in roff_block(). */ 134195c635efSGarrett D'Amore /* FALLTHROUGH */ 1342*260e9a87SYuri Pankov case ROFF_dei: 134395c635efSGarrett D'Amore /* FALLTHROUGH */ 1344*260e9a87SYuri Pankov case ROFF_ig: 134595c635efSGarrett D'Amore break; 134695c635efSGarrett D'Amore default: 1347*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 1348*260e9a87SYuri Pankov ln, ppos, ".."); 134995c635efSGarrett D'Amore return(ROFF_IGN); 135095c635efSGarrett D'Amore } 135195c635efSGarrett D'Amore 1352*260e9a87SYuri Pankov if (buf->buf[pos] != '\0') 1353*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, 1354*260e9a87SYuri Pankov ".. %s", buf->buf + pos); 135595c635efSGarrett D'Amore 135695c635efSGarrett D'Amore roffnode_pop(r); 135795c635efSGarrett D'Amore roffnode_cleanscope(r); 135895c635efSGarrett D'Amore return(ROFF_IGN); 135995c635efSGarrett D'Amore 136095c635efSGarrett D'Amore } 136195c635efSGarrett D'Amore 136295c635efSGarrett D'Amore static void 136395c635efSGarrett D'Amore roffnode_cleanscope(struct roff *r) 136495c635efSGarrett D'Amore { 136595c635efSGarrett D'Amore 136695c635efSGarrett D'Amore while (r->last) { 1367698f87a4SGarrett D'Amore if (--r->last->endspan != 0) 136895c635efSGarrett D'Amore break; 136995c635efSGarrett D'Amore roffnode_pop(r); 137095c635efSGarrett D'Amore } 137195c635efSGarrett D'Amore } 137295c635efSGarrett D'Amore 1373*260e9a87SYuri Pankov static void 1374*260e9a87SYuri Pankov roff_ccond(struct roff *r, int ln, int ppos) 137595c635efSGarrett D'Amore { 137695c635efSGarrett D'Amore 137795c635efSGarrett D'Amore if (NULL == r->last) { 1378*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 1379*260e9a87SYuri Pankov ln, ppos, "\\}"); 1380*260e9a87SYuri Pankov return; 138195c635efSGarrett D'Amore } 138295c635efSGarrett D'Amore 138395c635efSGarrett D'Amore switch (r->last->tok) { 1384*260e9a87SYuri Pankov case ROFF_el: 138595c635efSGarrett D'Amore /* FALLTHROUGH */ 1386*260e9a87SYuri Pankov case ROFF_ie: 138795c635efSGarrett D'Amore /* FALLTHROUGH */ 1388*260e9a87SYuri Pankov case ROFF_if: 138995c635efSGarrett D'Amore break; 139095c635efSGarrett D'Amore default: 1391*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 1392*260e9a87SYuri Pankov ln, ppos, "\\}"); 1393*260e9a87SYuri Pankov return; 139495c635efSGarrett D'Amore } 139595c635efSGarrett D'Amore 139695c635efSGarrett D'Amore if (r->last->endspan > -1) { 1397*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 1398*260e9a87SYuri Pankov ln, ppos, "\\}"); 1399*260e9a87SYuri Pankov return; 140095c635efSGarrett D'Amore } 140195c635efSGarrett D'Amore 140295c635efSGarrett D'Amore roffnode_pop(r); 140395c635efSGarrett D'Amore roffnode_cleanscope(r); 1404*260e9a87SYuri Pankov return; 140595c635efSGarrett D'Amore } 140695c635efSGarrett D'Amore 140795c635efSGarrett D'Amore static enum rofferr 140895c635efSGarrett D'Amore roff_block(ROFF_ARGS) 140995c635efSGarrett D'Amore { 1410*260e9a87SYuri Pankov const char *name; 1411*260e9a87SYuri Pankov char *iname, *cp; 1412*260e9a87SYuri Pankov size_t namesz; 141395c635efSGarrett D'Amore 1414*260e9a87SYuri Pankov /* Ignore groff compatibility mode for now. */ 141595c635efSGarrett D'Amore 1416*260e9a87SYuri Pankov if (tok == ROFF_de1) 1417*260e9a87SYuri Pankov tok = ROFF_de; 1418*260e9a87SYuri Pankov else if (tok == ROFF_dei1) 1419*260e9a87SYuri Pankov tok = ROFF_dei; 1420*260e9a87SYuri Pankov else if (tok == ROFF_am1) 1421*260e9a87SYuri Pankov tok = ROFF_am; 1422*260e9a87SYuri Pankov else if (tok == ROFF_ami1) 1423*260e9a87SYuri Pankov tok = ROFF_ami; 1424*260e9a87SYuri Pankov 1425*260e9a87SYuri Pankov /* Parse the macro name argument. */ 1426*260e9a87SYuri Pankov 1427*260e9a87SYuri Pankov cp = buf->buf + pos; 1428*260e9a87SYuri Pankov if (tok == ROFF_ig) { 1429*260e9a87SYuri Pankov iname = NULL; 1430*260e9a87SYuri Pankov namesz = 0; 1431*260e9a87SYuri Pankov } else { 1432*260e9a87SYuri Pankov iname = cp; 1433*260e9a87SYuri Pankov namesz = roff_getname(r, &cp, ln, ppos); 1434*260e9a87SYuri Pankov iname[namesz] = '\0'; 143595c635efSGarrett D'Amore } 143695c635efSGarrett D'Amore 1437*260e9a87SYuri Pankov /* Resolve the macro name argument if it is indirect. */ 143895c635efSGarrett D'Amore 1439*260e9a87SYuri Pankov if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) { 1440*260e9a87SYuri Pankov if ((name = roff_getstrn(r, iname, namesz)) == NULL) { 1441*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_STR_UNDEF, 1442*260e9a87SYuri Pankov r->parse, ln, (int)(iname - buf->buf), 1443*260e9a87SYuri Pankov "%.*s", (int)namesz, iname); 1444*260e9a87SYuri Pankov namesz = 0; 1445*260e9a87SYuri Pankov } else 1446*260e9a87SYuri Pankov namesz = strlen(name); 1447*260e9a87SYuri Pankov } else 1448*260e9a87SYuri Pankov name = iname; 144995c635efSGarrett D'Amore 1450*260e9a87SYuri Pankov if (namesz == 0 && tok != ROFF_ig) { 1451*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, 1452*260e9a87SYuri Pankov ln, ppos, roffs[tok].name); 1453*260e9a87SYuri Pankov return(ROFF_IGN); 145495c635efSGarrett D'Amore } 145595c635efSGarrett D'Amore 145695c635efSGarrett D'Amore roffnode_push(r, tok, name, ln, ppos); 145795c635efSGarrett D'Amore 145895c635efSGarrett D'Amore /* 145995c635efSGarrett D'Amore * At the beginning of a `de' macro, clear the existing string 146095c635efSGarrett D'Amore * with the same name, if there is one. New content will be 1461*260e9a87SYuri Pankov * appended from roff_block_text() in multiline mode. 146295c635efSGarrett D'Amore */ 146395c635efSGarrett D'Amore 1464*260e9a87SYuri Pankov if (tok == ROFF_de || tok == ROFF_dei) 1465*260e9a87SYuri Pankov roff_setstrn(&r->strtab, name, namesz, "", 0, 0); 146695c635efSGarrett D'Amore 1467*260e9a87SYuri Pankov if (*cp == '\0') 146895c635efSGarrett D'Amore return(ROFF_IGN); 146995c635efSGarrett D'Amore 1470*260e9a87SYuri Pankov /* Get the custom end marker. */ 147195c635efSGarrett D'Amore 1472*260e9a87SYuri Pankov iname = cp; 1473*260e9a87SYuri Pankov namesz = roff_getname(r, &cp, ln, ppos); 147495c635efSGarrett D'Amore 1475*260e9a87SYuri Pankov /* Resolve the end marker if it is indirect. */ 147695c635efSGarrett D'Amore 1477*260e9a87SYuri Pankov if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) { 1478*260e9a87SYuri Pankov if ((name = roff_getstrn(r, iname, namesz)) == NULL) { 1479*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_STR_UNDEF, 1480*260e9a87SYuri Pankov r->parse, ln, (int)(iname - buf->buf), 1481*260e9a87SYuri Pankov "%.*s", (int)namesz, iname); 1482*260e9a87SYuri Pankov namesz = 0; 1483*260e9a87SYuri Pankov } else 1484*260e9a87SYuri Pankov namesz = strlen(name); 1485*260e9a87SYuri Pankov } else 1486*260e9a87SYuri Pankov name = iname; 148795c635efSGarrett D'Amore 1488*260e9a87SYuri Pankov if (namesz) 1489*260e9a87SYuri Pankov r->last->end = mandoc_strndup(name, namesz); 149095c635efSGarrett D'Amore 1491*260e9a87SYuri Pankov if (*cp != '\0') 1492*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, 1493*260e9a87SYuri Pankov ln, pos, ".%s ... %s", roffs[tok].name, cp); 149495c635efSGarrett D'Amore 149595c635efSGarrett D'Amore return(ROFF_IGN); 149695c635efSGarrett D'Amore } 149795c635efSGarrett D'Amore 149895c635efSGarrett D'Amore static enum rofferr 149995c635efSGarrett D'Amore roff_block_sub(ROFF_ARGS) 150095c635efSGarrett D'Amore { 150195c635efSGarrett D'Amore enum rofft t; 150295c635efSGarrett D'Amore int i, j; 150395c635efSGarrett D'Amore 150495c635efSGarrett D'Amore /* 150595c635efSGarrett D'Amore * First check whether a custom macro exists at this level. If 150695c635efSGarrett D'Amore * it does, then check against it. This is some of groff's 150795c635efSGarrett D'Amore * stranger behaviours. If we encountered a custom end-scope 150895c635efSGarrett D'Amore * tag and that tag also happens to be a "real" macro, then we 150995c635efSGarrett D'Amore * need to try interpreting it again as a real macro. If it's 151095c635efSGarrett D'Amore * not, then return ignore. Else continue. 151195c635efSGarrett D'Amore */ 151295c635efSGarrett D'Amore 151395c635efSGarrett D'Amore if (r->last->end) { 151495c635efSGarrett D'Amore for (i = pos, j = 0; r->last->end[j]; j++, i++) 1515*260e9a87SYuri Pankov if (buf->buf[i] != r->last->end[j]) 151695c635efSGarrett D'Amore break; 151795c635efSGarrett D'Amore 1518*260e9a87SYuri Pankov if (r->last->end[j] == '\0' && 1519*260e9a87SYuri Pankov (buf->buf[i] == '\0' || 1520*260e9a87SYuri Pankov buf->buf[i] == ' ' || 1521*260e9a87SYuri Pankov buf->buf[i] == '\t')) { 152295c635efSGarrett D'Amore roffnode_pop(r); 152395c635efSGarrett D'Amore roffnode_cleanscope(r); 152495c635efSGarrett D'Amore 1525*260e9a87SYuri Pankov while (buf->buf[i] == ' ' || buf->buf[i] == '\t') 152695c635efSGarrett D'Amore i++; 152795c635efSGarrett D'Amore 152895c635efSGarrett D'Amore pos = i; 1529*260e9a87SYuri Pankov if (roff_parse(r, buf->buf, &pos, ln, ppos) != 1530*260e9a87SYuri Pankov ROFF_MAX) 153195c635efSGarrett D'Amore return(ROFF_RERUN); 153295c635efSGarrett D'Amore return(ROFF_IGN); 153395c635efSGarrett D'Amore } 153495c635efSGarrett D'Amore } 153595c635efSGarrett D'Amore 153695c635efSGarrett D'Amore /* 153795c635efSGarrett D'Amore * If we have no custom end-query or lookup failed, then try 153895c635efSGarrett D'Amore * pulling it out of the hashtable. 153995c635efSGarrett D'Amore */ 154095c635efSGarrett D'Amore 1541*260e9a87SYuri Pankov t = roff_parse(r, buf->buf, &pos, ln, ppos); 154295c635efSGarrett D'Amore 1543*260e9a87SYuri Pankov if (t != ROFF_cblock) { 1544*260e9a87SYuri Pankov if (tok != ROFF_ig) 1545*260e9a87SYuri Pankov roff_setstr(r, r->last->name, buf->buf + ppos, 2); 154695c635efSGarrett D'Amore return(ROFF_IGN); 154795c635efSGarrett D'Amore } 154895c635efSGarrett D'Amore 154995c635efSGarrett D'Amore assert(roffs[t].proc); 1550*260e9a87SYuri Pankov return((*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs)); 155195c635efSGarrett D'Amore } 155295c635efSGarrett D'Amore 155395c635efSGarrett D'Amore static enum rofferr 155495c635efSGarrett D'Amore roff_block_text(ROFF_ARGS) 155595c635efSGarrett D'Amore { 155695c635efSGarrett D'Amore 1557*260e9a87SYuri Pankov if (tok != ROFF_ig) 1558*260e9a87SYuri Pankov roff_setstr(r, r->last->name, buf->buf + pos, 2); 155995c635efSGarrett D'Amore 156095c635efSGarrett D'Amore return(ROFF_IGN); 156195c635efSGarrett D'Amore } 156295c635efSGarrett D'Amore 156395c635efSGarrett D'Amore static enum rofferr 156495c635efSGarrett D'Amore roff_cond_sub(ROFF_ARGS) 156595c635efSGarrett D'Amore { 156695c635efSGarrett D'Amore enum rofft t; 156795c635efSGarrett D'Amore char *ep; 1568*260e9a87SYuri Pankov int rr; 156995c635efSGarrett D'Amore 157095c635efSGarrett D'Amore rr = r->last->rule; 157195c635efSGarrett D'Amore roffnode_cleanscope(r); 1572*260e9a87SYuri Pankov t = roff_parse(r, buf->buf, &pos, ln, ppos); 157395c635efSGarrett D'Amore 157495c635efSGarrett D'Amore /* 1575698f87a4SGarrett D'Amore * Fully handle known macros when they are structurally 1576698f87a4SGarrett D'Amore * required or when the conditional evaluated to true. 157795c635efSGarrett D'Amore */ 157895c635efSGarrett D'Amore 1579*260e9a87SYuri Pankov if ((t != ROFF_MAX) && 1580*260e9a87SYuri Pankov (rr || roffs[t].flags & ROFFMAC_STRUCT)) { 1581698f87a4SGarrett D'Amore assert(roffs[t].proc); 1582*260e9a87SYuri Pankov return((*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs)); 1583698f87a4SGarrett D'Amore } 1584698f87a4SGarrett D'Amore 1585*260e9a87SYuri Pankov /* 1586*260e9a87SYuri Pankov * If `\}' occurs on a macro line without a preceding macro, 1587*260e9a87SYuri Pankov * drop the line completely. 1588*260e9a87SYuri Pankov */ 1589*260e9a87SYuri Pankov 1590*260e9a87SYuri Pankov ep = buf->buf + pos; 1591*260e9a87SYuri Pankov if (ep[0] == '\\' && ep[1] == '}') 1592*260e9a87SYuri Pankov rr = 0; 1593*260e9a87SYuri Pankov 1594698f87a4SGarrett D'Amore /* Always check for the closing delimiter `\}'. */ 1595698f87a4SGarrett D'Amore 1596*260e9a87SYuri Pankov while ((ep = strchr(ep, '\\')) != NULL) { 1597*260e9a87SYuri Pankov if (*(++ep) == '}') { 1598*260e9a87SYuri Pankov *ep = '&'; 1599*260e9a87SYuri Pankov roff_ccond(r, ln, ep - buf->buf - 1); 160095c635efSGarrett D'Amore } 1601*260e9a87SYuri Pankov if (*ep != '\0') 1602*260e9a87SYuri Pankov ++ep; 1603*260e9a87SYuri Pankov } 1604*260e9a87SYuri Pankov return(rr ? ROFF_CONT : ROFF_IGN); 160595c635efSGarrett D'Amore } 160695c635efSGarrett D'Amore 160795c635efSGarrett D'Amore static enum rofferr 160895c635efSGarrett D'Amore roff_cond_text(ROFF_ARGS) 160995c635efSGarrett D'Amore { 161095c635efSGarrett D'Amore char *ep; 1611*260e9a87SYuri Pankov int rr; 161295c635efSGarrett D'Amore 161395c635efSGarrett D'Amore rr = r->last->rule; 161495c635efSGarrett D'Amore roffnode_cleanscope(r); 161595c635efSGarrett D'Amore 1616*260e9a87SYuri Pankov ep = buf->buf + pos; 1617*260e9a87SYuri Pankov while ((ep = strchr(ep, '\\')) != NULL) { 1618*260e9a87SYuri Pankov if (*(++ep) == '}') { 161995c635efSGarrett D'Amore *ep = '&'; 1620*260e9a87SYuri Pankov roff_ccond(r, ln, ep - buf->buf - 1); 162195c635efSGarrett D'Amore } 1622*260e9a87SYuri Pankov if (*ep != '\0') 1623*260e9a87SYuri Pankov ++ep; 1624*260e9a87SYuri Pankov } 1625*260e9a87SYuri Pankov return(rr ? ROFF_CONT : ROFF_IGN); 162695c635efSGarrett D'Amore } 162795c635efSGarrett D'Amore 1628*260e9a87SYuri Pankov /* 1629*260e9a87SYuri Pankov * Parse a single signed integer number. Stop at the first non-digit. 1630*260e9a87SYuri Pankov * If there is at least one digit, return success and advance the 1631*260e9a87SYuri Pankov * parse point, else return failure and let the parse point unchanged. 1632*260e9a87SYuri Pankov * Ignore overflows, treat them just like the C language. 1633*260e9a87SYuri Pankov */ 1634698f87a4SGarrett D'Amore static int 1635*260e9a87SYuri Pankov roff_getnum(const char *v, int *pos, int *res, int flags) 1636698f87a4SGarrett D'Amore { 1637*260e9a87SYuri Pankov int myres, scaled, n, p; 1638*260e9a87SYuri Pankov 1639*260e9a87SYuri Pankov if (NULL == res) 1640*260e9a87SYuri Pankov res = &myres; 1641698f87a4SGarrett D'Amore 1642698f87a4SGarrett D'Amore p = *pos; 1643698f87a4SGarrett D'Amore n = v[p] == '-'; 1644*260e9a87SYuri Pankov if (n || v[p] == '+') 1645*260e9a87SYuri Pankov p++; 1646*260e9a87SYuri Pankov 1647*260e9a87SYuri Pankov if (flags & ROFFNUM_WHITE) 1648*260e9a87SYuri Pankov while (isspace((unsigned char)v[p])) 1649698f87a4SGarrett D'Amore p++; 1650698f87a4SGarrett D'Amore 1651698f87a4SGarrett D'Amore for (*res = 0; isdigit((unsigned char)v[p]); p++) 1652*260e9a87SYuri Pankov *res = 10 * *res + v[p] - '0'; 1653698f87a4SGarrett D'Amore if (p == *pos + n) 1654698f87a4SGarrett D'Amore return 0; 1655698f87a4SGarrett D'Amore 1656698f87a4SGarrett D'Amore if (n) 1657698f87a4SGarrett D'Amore *res = -*res; 1658698f87a4SGarrett D'Amore 1659*260e9a87SYuri Pankov /* Each number may be followed by one optional scaling unit. */ 1660698f87a4SGarrett D'Amore 1661*260e9a87SYuri Pankov switch (v[p]) { 1662*260e9a87SYuri Pankov case 'f': 1663*260e9a87SYuri Pankov scaled = *res * 65536; 1664698f87a4SGarrett D'Amore break; 1665*260e9a87SYuri Pankov case 'i': 1666*260e9a87SYuri Pankov scaled = *res * 240; 1667698f87a4SGarrett D'Amore break; 1668*260e9a87SYuri Pankov case 'c': 1669*260e9a87SYuri Pankov scaled = *res * 240 / 2.54; 1670*260e9a87SYuri Pankov break; 1671*260e9a87SYuri Pankov case 'v': 1672*260e9a87SYuri Pankov /* FALLTROUGH */ 1673*260e9a87SYuri Pankov case 'P': 1674*260e9a87SYuri Pankov scaled = *res * 40; 1675*260e9a87SYuri Pankov break; 1676*260e9a87SYuri Pankov case 'm': 1677*260e9a87SYuri Pankov /* FALLTROUGH */ 1678*260e9a87SYuri Pankov case 'n': 1679*260e9a87SYuri Pankov scaled = *res * 24; 1680*260e9a87SYuri Pankov break; 1681*260e9a87SYuri Pankov case 'p': 1682*260e9a87SYuri Pankov scaled = *res * 10 / 3; 1683*260e9a87SYuri Pankov break; 1684*260e9a87SYuri Pankov case 'u': 1685*260e9a87SYuri Pankov scaled = *res; 1686*260e9a87SYuri Pankov break; 1687*260e9a87SYuri Pankov case 'M': 1688*260e9a87SYuri Pankov scaled = *res * 6 / 25; 1689698f87a4SGarrett D'Amore break; 1690698f87a4SGarrett D'Amore default: 1691*260e9a87SYuri Pankov scaled = *res; 1692*260e9a87SYuri Pankov p--; 1693*260e9a87SYuri Pankov break; 1694*260e9a87SYuri Pankov } 1695*260e9a87SYuri Pankov if (flags & ROFFNUM_SCALE) 1696*260e9a87SYuri Pankov *res = scaled; 1697*260e9a87SYuri Pankov 1698*260e9a87SYuri Pankov *pos = p + 1; 1699*260e9a87SYuri Pankov return(1); 1700*260e9a87SYuri Pankov } 1701*260e9a87SYuri Pankov 1702*260e9a87SYuri Pankov /* 1703*260e9a87SYuri Pankov * Evaluate a string comparison condition. 1704*260e9a87SYuri Pankov * The first character is the delimiter. 1705*260e9a87SYuri Pankov * Succeed if the string up to its second occurrence 1706*260e9a87SYuri Pankov * matches the string up to its third occurence. 1707*260e9a87SYuri Pankov * Advance the cursor after the third occurrence 1708*260e9a87SYuri Pankov * or lacking that, to the end of the line. 1709*260e9a87SYuri Pankov */ 1710*260e9a87SYuri Pankov static int 1711*260e9a87SYuri Pankov roff_evalstrcond(const char *v, int *pos) 1712*260e9a87SYuri Pankov { 1713*260e9a87SYuri Pankov const char *s1, *s2, *s3; 1714*260e9a87SYuri Pankov int match; 1715*260e9a87SYuri Pankov 1716*260e9a87SYuri Pankov match = 0; 1717*260e9a87SYuri Pankov s1 = v + *pos; /* initial delimiter */ 1718*260e9a87SYuri Pankov s2 = s1 + 1; /* for scanning the first string */ 1719*260e9a87SYuri Pankov s3 = strchr(s2, *s1); /* for scanning the second string */ 1720*260e9a87SYuri Pankov 1721*260e9a87SYuri Pankov if (NULL == s3) /* found no middle delimiter */ 1722*260e9a87SYuri Pankov goto out; 1723*260e9a87SYuri Pankov 1724*260e9a87SYuri Pankov while ('\0' != *++s3) { 1725*260e9a87SYuri Pankov if (*s2 != *s3) { /* mismatch */ 1726*260e9a87SYuri Pankov s3 = strchr(s3, *s1); 1727*260e9a87SYuri Pankov break; 1728*260e9a87SYuri Pankov } 1729*260e9a87SYuri Pankov if (*s3 == *s1) { /* found the final delimiter */ 1730*260e9a87SYuri Pankov match = 1; 1731*260e9a87SYuri Pankov break; 1732*260e9a87SYuri Pankov } 1733*260e9a87SYuri Pankov s2++; 1734*260e9a87SYuri Pankov } 1735*260e9a87SYuri Pankov 1736*260e9a87SYuri Pankov out: 1737*260e9a87SYuri Pankov if (NULL == s3) 1738*260e9a87SYuri Pankov s3 = strchr(s2, '\0'); 1739*260e9a87SYuri Pankov else if (*s3 != '\0') 1740*260e9a87SYuri Pankov s3++; 1741*260e9a87SYuri Pankov *pos = s3 - v; 1742*260e9a87SYuri Pankov return(match); 1743*260e9a87SYuri Pankov } 1744*260e9a87SYuri Pankov 1745*260e9a87SYuri Pankov /* 1746*260e9a87SYuri Pankov * Evaluate an optionally negated single character, numerical, 1747*260e9a87SYuri Pankov * or string condition. 1748*260e9a87SYuri Pankov */ 1749*260e9a87SYuri Pankov static int 1750*260e9a87SYuri Pankov roff_evalcond(struct roff *r, int ln, const char *v, int *pos) 1751*260e9a87SYuri Pankov { 1752*260e9a87SYuri Pankov int number, savepos, wanttrue; 1753*260e9a87SYuri Pankov 1754*260e9a87SYuri Pankov if ('!' == v[*pos]) { 1755*260e9a87SYuri Pankov wanttrue = 0; 1756*260e9a87SYuri Pankov (*pos)++; 1757*260e9a87SYuri Pankov } else 1758*260e9a87SYuri Pankov wanttrue = 1; 1759*260e9a87SYuri Pankov 1760*260e9a87SYuri Pankov switch (v[*pos]) { 1761*260e9a87SYuri Pankov case '\0': 1762*260e9a87SYuri Pankov return(0); 1763*260e9a87SYuri Pankov case 'n': 1764*260e9a87SYuri Pankov /* FALLTHROUGH */ 1765*260e9a87SYuri Pankov case 'o': 1766*260e9a87SYuri Pankov (*pos)++; 1767*260e9a87SYuri Pankov return(wanttrue); 1768*260e9a87SYuri Pankov case 'c': 1769*260e9a87SYuri Pankov /* FALLTHROUGH */ 1770*260e9a87SYuri Pankov case 'd': 1771*260e9a87SYuri Pankov /* FALLTHROUGH */ 1772*260e9a87SYuri Pankov case 'e': 1773*260e9a87SYuri Pankov /* FALLTHROUGH */ 1774*260e9a87SYuri Pankov case 'r': 1775*260e9a87SYuri Pankov /* FALLTHROUGH */ 1776*260e9a87SYuri Pankov case 't': 1777*260e9a87SYuri Pankov /* FALLTHROUGH */ 1778*260e9a87SYuri Pankov case 'v': 1779*260e9a87SYuri Pankov (*pos)++; 1780*260e9a87SYuri Pankov return(!wanttrue); 1781*260e9a87SYuri Pankov default: 1782*260e9a87SYuri Pankov break; 1783*260e9a87SYuri Pankov } 1784*260e9a87SYuri Pankov 1785*260e9a87SYuri Pankov savepos = *pos; 1786*260e9a87SYuri Pankov if (roff_evalnum(r, ln, v, pos, &number, ROFFNUM_SCALE)) 1787*260e9a87SYuri Pankov return((number > 0) == wanttrue); 1788*260e9a87SYuri Pankov else if (*pos == savepos) 1789*260e9a87SYuri Pankov return(roff_evalstrcond(v, pos) == wanttrue); 1790*260e9a87SYuri Pankov else 1791698f87a4SGarrett D'Amore return (0); 1792698f87a4SGarrett D'Amore } 1793698f87a4SGarrett D'Amore 179495c635efSGarrett D'Amore static enum rofferr 179595c635efSGarrett D'Amore roff_line_ignore(ROFF_ARGS) 179695c635efSGarrett D'Amore { 179795c635efSGarrett D'Amore 179895c635efSGarrett D'Amore return(ROFF_IGN); 179995c635efSGarrett D'Amore } 180095c635efSGarrett D'Amore 1801*260e9a87SYuri Pankov static enum rofferr 1802*260e9a87SYuri Pankov roff_insec(ROFF_ARGS) 1803*260e9a87SYuri Pankov { 1804*260e9a87SYuri Pankov 1805*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_INSEC, r->parse, 1806*260e9a87SYuri Pankov ln, ppos, roffs[tok].name); 1807*260e9a87SYuri Pankov return(ROFF_IGN); 1808*260e9a87SYuri Pankov } 1809*260e9a87SYuri Pankov 1810*260e9a87SYuri Pankov static enum rofferr 1811*260e9a87SYuri Pankov roff_unsupp(ROFF_ARGS) 1812*260e9a87SYuri Pankov { 1813*260e9a87SYuri Pankov 1814*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_UNSUPP, r->parse, 1815*260e9a87SYuri Pankov ln, ppos, roffs[tok].name); 1816*260e9a87SYuri Pankov return(ROFF_IGN); 1817*260e9a87SYuri Pankov } 1818*260e9a87SYuri Pankov 181995c635efSGarrett D'Amore static enum rofferr 182095c635efSGarrett D'Amore roff_cond(ROFF_ARGS) 182195c635efSGarrett D'Amore { 1822698f87a4SGarrett D'Amore 1823698f87a4SGarrett D'Amore roffnode_push(r, tok, NULL, ln, ppos); 182495c635efSGarrett D'Amore 182595c635efSGarrett D'Amore /* 182695c635efSGarrett D'Amore * An `.el' has no conditional body: it will consume the value 182795c635efSGarrett D'Amore * of the current rstack entry set in prior `ie' calls or 182895c635efSGarrett D'Amore * defaults to DENY. 182995c635efSGarrett D'Amore * 183095c635efSGarrett D'Amore * If we're not an `el', however, then evaluate the conditional. 183195c635efSGarrett D'Amore */ 183295c635efSGarrett D'Amore 1833*260e9a87SYuri Pankov r->last->rule = tok == ROFF_el ? 1834*260e9a87SYuri Pankov (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) : 1835*260e9a87SYuri Pankov roff_evalcond(r, ln, buf->buf, &pos); 183695c635efSGarrett D'Amore 183795c635efSGarrett D'Amore /* 183895c635efSGarrett D'Amore * An if-else will put the NEGATION of the current evaluated 183995c635efSGarrett D'Amore * conditional into the stack of rules. 184095c635efSGarrett D'Amore */ 184195c635efSGarrett D'Amore 1842*260e9a87SYuri Pankov if (tok == ROFF_ie) { 1843*260e9a87SYuri Pankov if (r->rstackpos + 1 == r->rstacksz) { 1844*260e9a87SYuri Pankov r->rstacksz += 16; 1845*260e9a87SYuri Pankov r->rstack = mandoc_reallocarray(r->rstack, 1846*260e9a87SYuri Pankov r->rstacksz, sizeof(int)); 184795c635efSGarrett D'Amore } 1848*260e9a87SYuri Pankov r->rstack[++r->rstackpos] = !r->last->rule; 184995c635efSGarrett D'Amore } 185095c635efSGarrett D'Amore 185195c635efSGarrett D'Amore /* If the parent has false as its rule, then so do we. */ 185295c635efSGarrett D'Amore 1853*260e9a87SYuri Pankov if (r->last->parent && !r->last->parent->rule) 1854*260e9a87SYuri Pankov r->last->rule = 0; 185595c635efSGarrett D'Amore 185695c635efSGarrett D'Amore /* 1857698f87a4SGarrett D'Amore * Determine scope. 1858698f87a4SGarrett D'Amore * If there is nothing on the line after the conditional, 1859698f87a4SGarrett D'Amore * not even whitespace, use next-line scope. 186095c635efSGarrett D'Amore */ 186195c635efSGarrett D'Amore 1862*260e9a87SYuri Pankov if (buf->buf[pos] == '\0') { 1863698f87a4SGarrett D'Amore r->last->endspan = 2; 1864698f87a4SGarrett D'Amore goto out; 1865698f87a4SGarrett D'Amore } 1866698f87a4SGarrett D'Amore 1867*260e9a87SYuri Pankov while (buf->buf[pos] == ' ') 1868698f87a4SGarrett D'Amore pos++; 1869698f87a4SGarrett D'Amore 1870698f87a4SGarrett D'Amore /* An opening brace requests multiline scope. */ 187195c635efSGarrett D'Amore 1872*260e9a87SYuri Pankov if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') { 187395c635efSGarrett D'Amore r->last->endspan = -1; 187495c635efSGarrett D'Amore pos += 2; 1875698f87a4SGarrett D'Amore goto out; 187695c635efSGarrett D'Amore } 187795c635efSGarrett D'Amore 187895c635efSGarrett D'Amore /* 1879698f87a4SGarrett D'Amore * Anything else following the conditional causes 1880698f87a4SGarrett D'Amore * single-line scope. Warn if the scope contains 1881698f87a4SGarrett D'Amore * nothing but trailing whitespace. 188295c635efSGarrett D'Amore */ 188395c635efSGarrett D'Amore 1884*260e9a87SYuri Pankov if (buf->buf[pos] == '\0') 1885*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_COND_EMPTY, r->parse, 1886*260e9a87SYuri Pankov ln, ppos, roffs[tok].name); 188795c635efSGarrett D'Amore 1888698f87a4SGarrett D'Amore r->last->endspan = 1; 188995c635efSGarrett D'Amore 1890698f87a4SGarrett D'Amore out: 189195c635efSGarrett D'Amore *offs = pos; 189295c635efSGarrett D'Amore return(ROFF_RERUN); 189395c635efSGarrett D'Amore } 189495c635efSGarrett D'Amore 189595c635efSGarrett D'Amore static enum rofferr 189695c635efSGarrett D'Amore roff_ds(ROFF_ARGS) 189795c635efSGarrett D'Amore { 1898*260e9a87SYuri Pankov char *string; 1899*260e9a87SYuri Pankov const char *name; 1900*260e9a87SYuri Pankov size_t namesz; 1901*260e9a87SYuri Pankov 1902*260e9a87SYuri Pankov /* Ignore groff compatibility mode for now. */ 1903*260e9a87SYuri Pankov 1904*260e9a87SYuri Pankov if (tok == ROFF_ds1) 1905*260e9a87SYuri Pankov tok = ROFF_ds; 1906*260e9a87SYuri Pankov else if (tok == ROFF_as1) 1907*260e9a87SYuri Pankov tok = ROFF_as; 190895c635efSGarrett D'Amore 190995c635efSGarrett D'Amore /* 1910*260e9a87SYuri Pankov * The first word is the name of the string. 1911*260e9a87SYuri Pankov * If it is empty or terminated by an escape sequence, 1912*260e9a87SYuri Pankov * abort the `ds' request without defining anything. 191395c635efSGarrett D'Amore */ 191495c635efSGarrett D'Amore 1915*260e9a87SYuri Pankov name = string = buf->buf + pos; 1916*260e9a87SYuri Pankov if (*name == '\0') 191795c635efSGarrett D'Amore return(ROFF_IGN); 191895c635efSGarrett D'Amore 1919*260e9a87SYuri Pankov namesz = roff_getname(r, &string, ln, pos); 1920*260e9a87SYuri Pankov if (name[namesz] == '\\') 1921*260e9a87SYuri Pankov return(ROFF_IGN); 1922*260e9a87SYuri Pankov 1923*260e9a87SYuri Pankov /* Read past the initial double-quote, if any. */ 1924*260e9a87SYuri Pankov if (*string == '"') 192595c635efSGarrett D'Amore string++; 192695c635efSGarrett D'Amore 192795c635efSGarrett D'Amore /* The rest is the value. */ 1928*260e9a87SYuri Pankov roff_setstrn(&r->strtab, name, namesz, string, strlen(string), 1929*260e9a87SYuri Pankov ROFF_as == tok); 193095c635efSGarrett D'Amore return(ROFF_IGN); 193195c635efSGarrett D'Amore } 193295c635efSGarrett D'Amore 1933*260e9a87SYuri Pankov /* 1934*260e9a87SYuri Pankov * Parse a single operator, one or two characters long. 1935*260e9a87SYuri Pankov * If the operator is recognized, return success and advance the 1936*260e9a87SYuri Pankov * parse point, else return failure and let the parse point unchanged. 1937*260e9a87SYuri Pankov */ 1938*260e9a87SYuri Pankov static int 1939*260e9a87SYuri Pankov roff_getop(const char *v, int *pos, char *res) 1940*260e9a87SYuri Pankov { 1941*260e9a87SYuri Pankov 1942*260e9a87SYuri Pankov *res = v[*pos]; 1943*260e9a87SYuri Pankov 1944*260e9a87SYuri Pankov switch (*res) { 1945*260e9a87SYuri Pankov case '+': 1946*260e9a87SYuri Pankov /* FALLTHROUGH */ 1947*260e9a87SYuri Pankov case '-': 1948*260e9a87SYuri Pankov /* FALLTHROUGH */ 1949*260e9a87SYuri Pankov case '*': 1950*260e9a87SYuri Pankov /* FALLTHROUGH */ 1951*260e9a87SYuri Pankov case '/': 1952*260e9a87SYuri Pankov /* FALLTHROUGH */ 1953*260e9a87SYuri Pankov case '%': 1954*260e9a87SYuri Pankov /* FALLTHROUGH */ 1955*260e9a87SYuri Pankov case '&': 1956*260e9a87SYuri Pankov /* FALLTHROUGH */ 1957*260e9a87SYuri Pankov case ':': 1958*260e9a87SYuri Pankov break; 1959*260e9a87SYuri Pankov case '<': 1960*260e9a87SYuri Pankov switch (v[*pos + 1]) { 1961*260e9a87SYuri Pankov case '=': 1962*260e9a87SYuri Pankov *res = 'l'; 1963*260e9a87SYuri Pankov (*pos)++; 1964*260e9a87SYuri Pankov break; 1965*260e9a87SYuri Pankov case '>': 1966*260e9a87SYuri Pankov *res = '!'; 1967*260e9a87SYuri Pankov (*pos)++; 1968*260e9a87SYuri Pankov break; 1969*260e9a87SYuri Pankov case '?': 1970*260e9a87SYuri Pankov *res = 'i'; 1971*260e9a87SYuri Pankov (*pos)++; 1972*260e9a87SYuri Pankov break; 1973*260e9a87SYuri Pankov default: 1974*260e9a87SYuri Pankov break; 1975*260e9a87SYuri Pankov } 1976*260e9a87SYuri Pankov break; 1977*260e9a87SYuri Pankov case '>': 1978*260e9a87SYuri Pankov switch (v[*pos + 1]) { 1979*260e9a87SYuri Pankov case '=': 1980*260e9a87SYuri Pankov *res = 'g'; 1981*260e9a87SYuri Pankov (*pos)++; 1982*260e9a87SYuri Pankov break; 1983*260e9a87SYuri Pankov case '?': 1984*260e9a87SYuri Pankov *res = 'a'; 1985*260e9a87SYuri Pankov (*pos)++; 1986*260e9a87SYuri Pankov break; 1987*260e9a87SYuri Pankov default: 1988*260e9a87SYuri Pankov break; 1989*260e9a87SYuri Pankov } 1990*260e9a87SYuri Pankov break; 1991*260e9a87SYuri Pankov case '=': 1992*260e9a87SYuri Pankov if ('=' == v[*pos + 1]) 1993*260e9a87SYuri Pankov (*pos)++; 1994*260e9a87SYuri Pankov break; 1995*260e9a87SYuri Pankov default: 1996*260e9a87SYuri Pankov return(0); 1997*260e9a87SYuri Pankov } 1998*260e9a87SYuri Pankov (*pos)++; 1999*260e9a87SYuri Pankov 2000*260e9a87SYuri Pankov return(*res); 2001*260e9a87SYuri Pankov } 2002*260e9a87SYuri Pankov 2003*260e9a87SYuri Pankov /* 2004*260e9a87SYuri Pankov * Evaluate either a parenthesized numeric expression 2005*260e9a87SYuri Pankov * or a single signed integer number. 2006*260e9a87SYuri Pankov */ 2007*260e9a87SYuri Pankov static int 2008*260e9a87SYuri Pankov roff_evalpar(struct roff *r, int ln, 2009*260e9a87SYuri Pankov const char *v, int *pos, int *res, int flags) 2010*260e9a87SYuri Pankov { 2011*260e9a87SYuri Pankov 2012*260e9a87SYuri Pankov if ('(' != v[*pos]) 2013*260e9a87SYuri Pankov return(roff_getnum(v, pos, res, flags)); 2014*260e9a87SYuri Pankov 2015*260e9a87SYuri Pankov (*pos)++; 2016*260e9a87SYuri Pankov if ( ! roff_evalnum(r, ln, v, pos, res, flags | ROFFNUM_WHITE)) 2017*260e9a87SYuri Pankov return(0); 2018*260e9a87SYuri Pankov 2019*260e9a87SYuri Pankov /* 2020*260e9a87SYuri Pankov * Omission of the closing parenthesis 2021*260e9a87SYuri Pankov * is an error in validation mode, 2022*260e9a87SYuri Pankov * but ignored in evaluation mode. 2023*260e9a87SYuri Pankov */ 2024*260e9a87SYuri Pankov 2025*260e9a87SYuri Pankov if (')' == v[*pos]) 2026*260e9a87SYuri Pankov (*pos)++; 2027*260e9a87SYuri Pankov else if (NULL == res) 2028*260e9a87SYuri Pankov return(0); 2029*260e9a87SYuri Pankov 2030*260e9a87SYuri Pankov return(1); 2031*260e9a87SYuri Pankov } 2032*260e9a87SYuri Pankov 2033*260e9a87SYuri Pankov /* 2034*260e9a87SYuri Pankov * Evaluate a complete numeric expression. 2035*260e9a87SYuri Pankov * Proceed left to right, there is no concept of precedence. 2036*260e9a87SYuri Pankov */ 2037*260e9a87SYuri Pankov static int 2038*260e9a87SYuri Pankov roff_evalnum(struct roff *r, int ln, const char *v, 2039*260e9a87SYuri Pankov int *pos, int *res, int flags) 2040*260e9a87SYuri Pankov { 2041*260e9a87SYuri Pankov int mypos, operand2; 2042*260e9a87SYuri Pankov char operator; 2043*260e9a87SYuri Pankov 2044*260e9a87SYuri Pankov if (NULL == pos) { 2045*260e9a87SYuri Pankov mypos = 0; 2046*260e9a87SYuri Pankov pos = &mypos; 2047*260e9a87SYuri Pankov } 2048*260e9a87SYuri Pankov 2049*260e9a87SYuri Pankov if (flags & ROFFNUM_WHITE) 2050*260e9a87SYuri Pankov while (isspace((unsigned char)v[*pos])) 2051*260e9a87SYuri Pankov (*pos)++; 2052*260e9a87SYuri Pankov 2053*260e9a87SYuri Pankov if ( ! roff_evalpar(r, ln, v, pos, res, flags)) 2054*260e9a87SYuri Pankov return(0); 2055*260e9a87SYuri Pankov 2056*260e9a87SYuri Pankov while (1) { 2057*260e9a87SYuri Pankov if (flags & ROFFNUM_WHITE) 2058*260e9a87SYuri Pankov while (isspace((unsigned char)v[*pos])) 2059*260e9a87SYuri Pankov (*pos)++; 2060*260e9a87SYuri Pankov 2061*260e9a87SYuri Pankov if ( ! roff_getop(v, pos, &operator)) 2062*260e9a87SYuri Pankov break; 2063*260e9a87SYuri Pankov 2064*260e9a87SYuri Pankov if (flags & ROFFNUM_WHITE) 2065*260e9a87SYuri Pankov while (isspace((unsigned char)v[*pos])) 2066*260e9a87SYuri Pankov (*pos)++; 2067*260e9a87SYuri Pankov 2068*260e9a87SYuri Pankov if ( ! roff_evalpar(r, ln, v, pos, &operand2, flags)) 2069*260e9a87SYuri Pankov return(0); 2070*260e9a87SYuri Pankov 2071*260e9a87SYuri Pankov if (flags & ROFFNUM_WHITE) 2072*260e9a87SYuri Pankov while (isspace((unsigned char)v[*pos])) 2073*260e9a87SYuri Pankov (*pos)++; 2074*260e9a87SYuri Pankov 2075*260e9a87SYuri Pankov if (NULL == res) 2076*260e9a87SYuri Pankov continue; 2077*260e9a87SYuri Pankov 2078*260e9a87SYuri Pankov switch (operator) { 2079*260e9a87SYuri Pankov case '+': 2080*260e9a87SYuri Pankov *res += operand2; 2081*260e9a87SYuri Pankov break; 2082*260e9a87SYuri Pankov case '-': 2083*260e9a87SYuri Pankov *res -= operand2; 2084*260e9a87SYuri Pankov break; 2085*260e9a87SYuri Pankov case '*': 2086*260e9a87SYuri Pankov *res *= operand2; 2087*260e9a87SYuri Pankov break; 2088*260e9a87SYuri Pankov case '/': 2089*260e9a87SYuri Pankov if (operand2 == 0) { 2090*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_DIVZERO, 2091*260e9a87SYuri Pankov r->parse, ln, *pos, v); 2092*260e9a87SYuri Pankov *res = 0; 2093*260e9a87SYuri Pankov break; 2094*260e9a87SYuri Pankov } 2095*260e9a87SYuri Pankov *res /= operand2; 2096*260e9a87SYuri Pankov break; 2097*260e9a87SYuri Pankov case '%': 2098*260e9a87SYuri Pankov if (operand2 == 0) { 2099*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_DIVZERO, 2100*260e9a87SYuri Pankov r->parse, ln, *pos, v); 2101*260e9a87SYuri Pankov *res = 0; 2102*260e9a87SYuri Pankov break; 2103*260e9a87SYuri Pankov } 2104*260e9a87SYuri Pankov *res %= operand2; 2105*260e9a87SYuri Pankov break; 2106*260e9a87SYuri Pankov case '<': 2107*260e9a87SYuri Pankov *res = *res < operand2; 2108*260e9a87SYuri Pankov break; 2109*260e9a87SYuri Pankov case '>': 2110*260e9a87SYuri Pankov *res = *res > operand2; 2111*260e9a87SYuri Pankov break; 2112*260e9a87SYuri Pankov case 'l': 2113*260e9a87SYuri Pankov *res = *res <= operand2; 2114*260e9a87SYuri Pankov break; 2115*260e9a87SYuri Pankov case 'g': 2116*260e9a87SYuri Pankov *res = *res >= operand2; 2117*260e9a87SYuri Pankov break; 2118*260e9a87SYuri Pankov case '=': 2119*260e9a87SYuri Pankov *res = *res == operand2; 2120*260e9a87SYuri Pankov break; 2121*260e9a87SYuri Pankov case '!': 2122*260e9a87SYuri Pankov *res = *res != operand2; 2123*260e9a87SYuri Pankov break; 2124*260e9a87SYuri Pankov case '&': 2125*260e9a87SYuri Pankov *res = *res && operand2; 2126*260e9a87SYuri Pankov break; 2127*260e9a87SYuri Pankov case ':': 2128*260e9a87SYuri Pankov *res = *res || operand2; 2129*260e9a87SYuri Pankov break; 2130*260e9a87SYuri Pankov case 'i': 2131*260e9a87SYuri Pankov if (operand2 < *res) 2132*260e9a87SYuri Pankov *res = operand2; 2133*260e9a87SYuri Pankov break; 2134*260e9a87SYuri Pankov case 'a': 2135*260e9a87SYuri Pankov if (operand2 > *res) 2136*260e9a87SYuri Pankov *res = operand2; 2137*260e9a87SYuri Pankov break; 2138*260e9a87SYuri Pankov default: 2139*260e9a87SYuri Pankov abort(); 2140*260e9a87SYuri Pankov } 2141*260e9a87SYuri Pankov } 2142*260e9a87SYuri Pankov return(1); 2143*260e9a87SYuri Pankov } 2144*260e9a87SYuri Pankov 214595c635efSGarrett D'Amore void 2146698f87a4SGarrett D'Amore roff_setreg(struct roff *r, const char *name, int val, char sign) 214795c635efSGarrett D'Amore { 2148698f87a4SGarrett D'Amore struct roffreg *reg; 214995c635efSGarrett D'Amore 2150698f87a4SGarrett D'Amore /* Search for an existing register with the same name. */ 2151698f87a4SGarrett D'Amore reg = r->regtab; 2152698f87a4SGarrett D'Amore 2153698f87a4SGarrett D'Amore while (reg && strcmp(name, reg->key.p)) 2154698f87a4SGarrett D'Amore reg = reg->next; 2155698f87a4SGarrett D'Amore 2156698f87a4SGarrett D'Amore if (NULL == reg) { 2157698f87a4SGarrett D'Amore /* Create a new register. */ 2158698f87a4SGarrett D'Amore reg = mandoc_malloc(sizeof(struct roffreg)); 2159698f87a4SGarrett D'Amore reg->key.p = mandoc_strdup(name); 2160698f87a4SGarrett D'Amore reg->key.sz = strlen(name); 2161698f87a4SGarrett D'Amore reg->val = 0; 2162698f87a4SGarrett D'Amore reg->next = r->regtab; 2163698f87a4SGarrett D'Amore r->regtab = reg; 2164698f87a4SGarrett D'Amore } 2165698f87a4SGarrett D'Amore 2166698f87a4SGarrett D'Amore if ('+' == sign) 2167698f87a4SGarrett D'Amore reg->val += val; 2168698f87a4SGarrett D'Amore else if ('-' == sign) 2169698f87a4SGarrett D'Amore reg->val -= val; 2170698f87a4SGarrett D'Amore else 2171698f87a4SGarrett D'Amore reg->val = val; 2172698f87a4SGarrett D'Amore } 2173698f87a4SGarrett D'Amore 2174*260e9a87SYuri Pankov /* 2175*260e9a87SYuri Pankov * Handle some predefined read-only number registers. 2176*260e9a87SYuri Pankov * For now, return -1 if the requested register is not predefined; 2177*260e9a87SYuri Pankov * in case a predefined read-only register having the value -1 2178*260e9a87SYuri Pankov * were to turn up, another special value would have to be chosen. 2179*260e9a87SYuri Pankov */ 2180*260e9a87SYuri Pankov static int 2181*260e9a87SYuri Pankov roff_getregro(const char *name) 2182*260e9a87SYuri Pankov { 2183*260e9a87SYuri Pankov 2184*260e9a87SYuri Pankov switch (*name) { 2185*260e9a87SYuri Pankov case 'A': /* ASCII approximation mode is always off. */ 2186*260e9a87SYuri Pankov return(0); 2187*260e9a87SYuri Pankov case 'g': /* Groff compatibility mode is always on. */ 2188*260e9a87SYuri Pankov return(1); 2189*260e9a87SYuri Pankov case 'H': /* Fixed horizontal resolution. */ 2190*260e9a87SYuri Pankov return (24); 2191*260e9a87SYuri Pankov case 'j': /* Always adjust left margin only. */ 2192*260e9a87SYuri Pankov return(0); 2193*260e9a87SYuri Pankov case 'T': /* Some output device is always defined. */ 2194*260e9a87SYuri Pankov return(1); 2195*260e9a87SYuri Pankov case 'V': /* Fixed vertical resolution. */ 2196*260e9a87SYuri Pankov return (40); 2197*260e9a87SYuri Pankov default: 2198*260e9a87SYuri Pankov return (-1); 2199*260e9a87SYuri Pankov } 2200*260e9a87SYuri Pankov } 2201*260e9a87SYuri Pankov 2202698f87a4SGarrett D'Amore int 2203698f87a4SGarrett D'Amore roff_getreg(const struct roff *r, const char *name) 2204698f87a4SGarrett D'Amore { 2205698f87a4SGarrett D'Amore struct roffreg *reg; 2206*260e9a87SYuri Pankov int val; 2207*260e9a87SYuri Pankov 2208*260e9a87SYuri Pankov if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) { 2209*260e9a87SYuri Pankov val = roff_getregro(name + 1); 2210*260e9a87SYuri Pankov if (-1 != val) 2211*260e9a87SYuri Pankov return (val); 2212*260e9a87SYuri Pankov } 2213698f87a4SGarrett D'Amore 2214698f87a4SGarrett D'Amore for (reg = r->regtab; reg; reg = reg->next) 2215698f87a4SGarrett D'Amore if (0 == strcmp(name, reg->key.p)) 2216698f87a4SGarrett D'Amore return(reg->val); 2217698f87a4SGarrett D'Amore 2218698f87a4SGarrett D'Amore return(0); 2219698f87a4SGarrett D'Amore } 2220698f87a4SGarrett D'Amore 2221698f87a4SGarrett D'Amore static int 2222698f87a4SGarrett D'Amore roff_getregn(const struct roff *r, const char *name, size_t len) 2223698f87a4SGarrett D'Amore { 2224698f87a4SGarrett D'Amore struct roffreg *reg; 2225*260e9a87SYuri Pankov int val; 2226*260e9a87SYuri Pankov 2227*260e9a87SYuri Pankov if ('.' == name[0] && 2 == len) { 2228*260e9a87SYuri Pankov val = roff_getregro(name + 1); 2229*260e9a87SYuri Pankov if (-1 != val) 2230*260e9a87SYuri Pankov return (val); 2231*260e9a87SYuri Pankov } 2232698f87a4SGarrett D'Amore 2233698f87a4SGarrett D'Amore for (reg = r->regtab; reg; reg = reg->next) 2234698f87a4SGarrett D'Amore if (len == reg->key.sz && 2235698f87a4SGarrett D'Amore 0 == strncmp(name, reg->key.p, len)) 2236698f87a4SGarrett D'Amore return(reg->val); 2237698f87a4SGarrett D'Amore 2238698f87a4SGarrett D'Amore return(0); 2239698f87a4SGarrett D'Amore } 2240698f87a4SGarrett D'Amore 2241698f87a4SGarrett D'Amore static void 2242698f87a4SGarrett D'Amore roff_freereg(struct roffreg *reg) 2243698f87a4SGarrett D'Amore { 2244698f87a4SGarrett D'Amore struct roffreg *old_reg; 2245698f87a4SGarrett D'Amore 2246698f87a4SGarrett D'Amore while (NULL != reg) { 2247698f87a4SGarrett D'Amore free(reg->key.p); 2248698f87a4SGarrett D'Amore old_reg = reg; 2249698f87a4SGarrett D'Amore reg = reg->next; 2250698f87a4SGarrett D'Amore free(old_reg); 2251698f87a4SGarrett D'Amore } 225295c635efSGarrett D'Amore } 225395c635efSGarrett D'Amore 225495c635efSGarrett D'Amore static enum rofferr 225595c635efSGarrett D'Amore roff_nr(ROFF_ARGS) 225695c635efSGarrett D'Amore { 2257*260e9a87SYuri Pankov char *key, *val; 2258*260e9a87SYuri Pankov size_t keysz; 225995c635efSGarrett D'Amore int iv; 2260698f87a4SGarrett D'Amore char sign; 226195c635efSGarrett D'Amore 2262*260e9a87SYuri Pankov key = val = buf->buf + pos; 2263*260e9a87SYuri Pankov if (*key == '\0') 2264*260e9a87SYuri Pankov return(ROFF_IGN); 2265*260e9a87SYuri Pankov 2266*260e9a87SYuri Pankov keysz = roff_getname(r, &val, ln, pos); 2267*260e9a87SYuri Pankov if (key[keysz] == '\\') 2268*260e9a87SYuri Pankov return(ROFF_IGN); 2269*260e9a87SYuri Pankov key[keysz] = '\0'; 227095c635efSGarrett D'Amore 2271698f87a4SGarrett D'Amore sign = *val; 2272*260e9a87SYuri Pankov if (sign == '+' || sign == '-') 2273698f87a4SGarrett D'Amore val++; 2274698f87a4SGarrett D'Amore 2275*260e9a87SYuri Pankov if (roff_evalnum(r, ln, val, NULL, &iv, ROFFNUM_SCALE)) 2276698f87a4SGarrett D'Amore roff_setreg(r, key, iv, sign); 227795c635efSGarrett D'Amore 227895c635efSGarrett D'Amore return(ROFF_IGN); 227995c635efSGarrett D'Amore } 228095c635efSGarrett D'Amore 2281*260e9a87SYuri Pankov static enum rofferr 2282*260e9a87SYuri Pankov roff_rr(ROFF_ARGS) 2283*260e9a87SYuri Pankov { 2284*260e9a87SYuri Pankov struct roffreg *reg, **prev; 2285*260e9a87SYuri Pankov char *name, *cp; 2286*260e9a87SYuri Pankov size_t namesz; 2287*260e9a87SYuri Pankov 2288*260e9a87SYuri Pankov name = cp = buf->buf + pos; 2289*260e9a87SYuri Pankov if (*name == '\0') 2290*260e9a87SYuri Pankov return(ROFF_IGN); 2291*260e9a87SYuri Pankov namesz = roff_getname(r, &cp, ln, pos); 2292*260e9a87SYuri Pankov name[namesz] = '\0'; 2293*260e9a87SYuri Pankov 2294*260e9a87SYuri Pankov prev = &r->regtab; 2295*260e9a87SYuri Pankov while (1) { 2296*260e9a87SYuri Pankov reg = *prev; 2297*260e9a87SYuri Pankov if (reg == NULL || !strcmp(name, reg->key.p)) 2298*260e9a87SYuri Pankov break; 2299*260e9a87SYuri Pankov prev = ®->next; 2300*260e9a87SYuri Pankov } 2301*260e9a87SYuri Pankov if (reg != NULL) { 2302*260e9a87SYuri Pankov *prev = reg->next; 2303*260e9a87SYuri Pankov free(reg->key.p); 2304*260e9a87SYuri Pankov free(reg); 2305*260e9a87SYuri Pankov } 2306*260e9a87SYuri Pankov return(ROFF_IGN); 2307*260e9a87SYuri Pankov } 2308*260e9a87SYuri Pankov 230995c635efSGarrett D'Amore static enum rofferr 231095c635efSGarrett D'Amore roff_rm(ROFF_ARGS) 231195c635efSGarrett D'Amore { 231295c635efSGarrett D'Amore const char *name; 231395c635efSGarrett D'Amore char *cp; 2314*260e9a87SYuri Pankov size_t namesz; 231595c635efSGarrett D'Amore 2316*260e9a87SYuri Pankov cp = buf->buf + pos; 2317*260e9a87SYuri Pankov while (*cp != '\0') { 2318*260e9a87SYuri Pankov name = cp; 2319*260e9a87SYuri Pankov namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf)); 2320*260e9a87SYuri Pankov roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0); 2321*260e9a87SYuri Pankov if (name[namesz] == '\\') 2322*260e9a87SYuri Pankov break; 232395c635efSGarrett D'Amore } 232495c635efSGarrett D'Amore return(ROFF_IGN); 232595c635efSGarrett D'Amore } 232695c635efSGarrett D'Amore 232795c635efSGarrett D'Amore static enum rofferr 2328698f87a4SGarrett D'Amore roff_it(ROFF_ARGS) 2329698f87a4SGarrett D'Amore { 2330698f87a4SGarrett D'Amore int iv; 2331698f87a4SGarrett D'Amore 2332698f87a4SGarrett D'Amore /* Parse the number of lines. */ 2333*260e9a87SYuri Pankov 2334*260e9a87SYuri Pankov if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) { 2335*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_IT_NONUM, r->parse, 2336*260e9a87SYuri Pankov ln, ppos, buf->buf + 1); 2337698f87a4SGarrett D'Amore return(ROFF_IGN); 2338698f87a4SGarrett D'Amore } 2339698f87a4SGarrett D'Amore 2340*260e9a87SYuri Pankov while (isspace((unsigned char)buf->buf[pos])) 2341*260e9a87SYuri Pankov pos++; 2342*260e9a87SYuri Pankov 2343*260e9a87SYuri Pankov /* 2344*260e9a87SYuri Pankov * Arm the input line trap. 2345*260e9a87SYuri Pankov * Special-casing "an-trap" is an ugly workaround to cope 2346*260e9a87SYuri Pankov * with DocBook stupidly fiddling with man(7) internals. 2347*260e9a87SYuri Pankov */ 2348*260e9a87SYuri Pankov 2349698f87a4SGarrett D'Amore roffit_lines = iv; 2350*260e9a87SYuri Pankov roffit_macro = mandoc_strdup(iv != 1 || 2351*260e9a87SYuri Pankov strcmp(buf->buf + pos, "an-trap") ? 2352*260e9a87SYuri Pankov buf->buf + pos : "br"); 2353698f87a4SGarrett D'Amore return(ROFF_IGN); 2354698f87a4SGarrett D'Amore } 2355698f87a4SGarrett D'Amore 2356698f87a4SGarrett D'Amore static enum rofferr 2357698f87a4SGarrett D'Amore roff_Dd(ROFF_ARGS) 2358698f87a4SGarrett D'Amore { 2359698f87a4SGarrett D'Amore const char *const *cp; 2360698f87a4SGarrett D'Amore 2361*260e9a87SYuri Pankov if ((r->options & (MPARSE_MDOC | MPARSE_QUICK)) == 0) 2362698f87a4SGarrett D'Amore for (cp = __mdoc_reserved; *cp; cp++) 2363698f87a4SGarrett D'Amore roff_setstr(r, *cp, NULL, 0); 2364698f87a4SGarrett D'Amore 2365*260e9a87SYuri Pankov if (r->format == 0) 2366*260e9a87SYuri Pankov r->format = MPARSE_MDOC; 2367*260e9a87SYuri Pankov 2368698f87a4SGarrett D'Amore return(ROFF_CONT); 2369698f87a4SGarrett D'Amore } 2370698f87a4SGarrett D'Amore 2371698f87a4SGarrett D'Amore static enum rofferr 2372698f87a4SGarrett D'Amore roff_TH(ROFF_ARGS) 2373698f87a4SGarrett D'Amore { 2374698f87a4SGarrett D'Amore const char *const *cp; 2375698f87a4SGarrett D'Amore 2376*260e9a87SYuri Pankov if ((r->options & MPARSE_QUICK) == 0) 2377698f87a4SGarrett D'Amore for (cp = __man_reserved; *cp; cp++) 2378698f87a4SGarrett D'Amore roff_setstr(r, *cp, NULL, 0); 2379698f87a4SGarrett D'Amore 2380*260e9a87SYuri Pankov if (r->format == 0) 2381*260e9a87SYuri Pankov r->format = MPARSE_MAN; 2382*260e9a87SYuri Pankov 2383698f87a4SGarrett D'Amore return(ROFF_CONT); 2384698f87a4SGarrett D'Amore } 2385698f87a4SGarrett D'Amore 2386698f87a4SGarrett D'Amore static enum rofferr 238795c635efSGarrett D'Amore roff_TE(ROFF_ARGS) 238895c635efSGarrett D'Amore { 238995c635efSGarrett D'Amore 239095c635efSGarrett D'Amore if (NULL == r->tbl) 2391*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 2392*260e9a87SYuri Pankov ln, ppos, "TE"); 2393*260e9a87SYuri Pankov else if ( ! tbl_end(&r->tbl)) { 2394*260e9a87SYuri Pankov free(buf->buf); 2395*260e9a87SYuri Pankov buf->buf = mandoc_strdup(".sp"); 2396*260e9a87SYuri Pankov buf->sz = 4; 2397*260e9a87SYuri Pankov return(ROFF_REPARSE); 2398*260e9a87SYuri Pankov } 239995c635efSGarrett D'Amore return(ROFF_IGN); 240095c635efSGarrett D'Amore } 240195c635efSGarrett D'Amore 240295c635efSGarrett D'Amore static enum rofferr 240395c635efSGarrett D'Amore roff_T_(ROFF_ARGS) 240495c635efSGarrett D'Amore { 240595c635efSGarrett D'Amore 240695c635efSGarrett D'Amore if (NULL == r->tbl) 2407*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 2408*260e9a87SYuri Pankov ln, ppos, "T&"); 240995c635efSGarrett D'Amore else 241095c635efSGarrett D'Amore tbl_restart(ppos, ln, r->tbl); 241195c635efSGarrett D'Amore 241295c635efSGarrett D'Amore return(ROFF_IGN); 241395c635efSGarrett D'Amore } 241495c635efSGarrett D'Amore 2415*260e9a87SYuri Pankov /* 2416*260e9a87SYuri Pankov * Handle in-line equation delimiters. 2417*260e9a87SYuri Pankov */ 2418*260e9a87SYuri Pankov static enum rofferr 2419*260e9a87SYuri Pankov roff_eqndelim(struct roff *r, struct buf *buf, int pos) 242095c635efSGarrett D'Amore { 2421*260e9a87SYuri Pankov char *cp1, *cp2; 2422*260e9a87SYuri Pankov const char *bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr; 242395c635efSGarrett D'Amore 2424*260e9a87SYuri Pankov /* 2425*260e9a87SYuri Pankov * Outside equations, look for an opening delimiter. 2426*260e9a87SYuri Pankov * If we are inside an equation, we already know it is 2427*260e9a87SYuri Pankov * in-line, or this function wouldn't have been called; 2428*260e9a87SYuri Pankov * so look for a closing delimiter. 2429*260e9a87SYuri Pankov */ 2430*260e9a87SYuri Pankov 2431*260e9a87SYuri Pankov cp1 = buf->buf + pos; 2432*260e9a87SYuri Pankov cp2 = strchr(cp1, r->eqn == NULL ? 2433*260e9a87SYuri Pankov r->last_eqn->odelim : r->last_eqn->cdelim); 2434*260e9a87SYuri Pankov if (cp2 == NULL) 2435*260e9a87SYuri Pankov return(ROFF_CONT); 2436*260e9a87SYuri Pankov 2437*260e9a87SYuri Pankov *cp2++ = '\0'; 2438*260e9a87SYuri Pankov bef_pr = bef_nl = aft_nl = aft_pr = ""; 2439*260e9a87SYuri Pankov 2440*260e9a87SYuri Pankov /* Handle preceding text, protecting whitespace. */ 2441*260e9a87SYuri Pankov 2442*260e9a87SYuri Pankov if (*buf->buf != '\0') { 2443*260e9a87SYuri Pankov if (r->eqn == NULL) 2444*260e9a87SYuri Pankov bef_pr = "\\&"; 2445*260e9a87SYuri Pankov bef_nl = "\n"; 244695c635efSGarrett D'Amore } 244795c635efSGarrett D'Amore 2448*260e9a87SYuri Pankov /* 2449*260e9a87SYuri Pankov * Prepare replacing the delimiter with an equation macro 2450*260e9a87SYuri Pankov * and drop leading white space from the equation. 2451*260e9a87SYuri Pankov */ 2452*260e9a87SYuri Pankov 2453*260e9a87SYuri Pankov if (r->eqn == NULL) { 2454*260e9a87SYuri Pankov while (*cp2 == ' ') 2455*260e9a87SYuri Pankov cp2++; 2456*260e9a87SYuri Pankov mac = ".EQ"; 2457*260e9a87SYuri Pankov } else 2458*260e9a87SYuri Pankov mac = ".EN"; 2459*260e9a87SYuri Pankov 2460*260e9a87SYuri Pankov /* Handle following text, protecting whitespace. */ 2461*260e9a87SYuri Pankov 2462*260e9a87SYuri Pankov if (*cp2 != '\0') { 2463*260e9a87SYuri Pankov aft_nl = "\n"; 2464*260e9a87SYuri Pankov if (r->eqn != NULL) 2465*260e9a87SYuri Pankov aft_pr = "\\&"; 2466*260e9a87SYuri Pankov } 2467*260e9a87SYuri Pankov 2468*260e9a87SYuri Pankov /* Do the actual replacement. */ 2469*260e9a87SYuri Pankov 2470*260e9a87SYuri Pankov buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf, 2471*260e9a87SYuri Pankov bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1; 2472*260e9a87SYuri Pankov free(buf->buf); 2473*260e9a87SYuri Pankov buf->buf = cp1; 2474*260e9a87SYuri Pankov 2475*260e9a87SYuri Pankov /* Toggle the in-line state of the eqn subsystem. */ 2476*260e9a87SYuri Pankov 2477*260e9a87SYuri Pankov r->eqn_inline = r->eqn == NULL; 2478*260e9a87SYuri Pankov return(ROFF_REPARSE); 2479*260e9a87SYuri Pankov } 2480*260e9a87SYuri Pankov 2481*260e9a87SYuri Pankov static enum rofferr 2482*260e9a87SYuri Pankov roff_EQ(ROFF_ARGS) 248395c635efSGarrett D'Amore { 248495c635efSGarrett D'Amore struct eqn_node *e; 248595c635efSGarrett D'Amore 2486*260e9a87SYuri Pankov assert(r->eqn == NULL); 2487*260e9a87SYuri Pankov e = eqn_alloc(ppos, ln, r->parse); 248895c635efSGarrett D'Amore 2489*260e9a87SYuri Pankov if (r->last_eqn) { 249095c635efSGarrett D'Amore r->last_eqn->next = e; 2491*260e9a87SYuri Pankov e->delim = r->last_eqn->delim; 2492*260e9a87SYuri Pankov e->odelim = r->last_eqn->odelim; 2493*260e9a87SYuri Pankov e->cdelim = r->last_eqn->cdelim; 2494*260e9a87SYuri Pankov } else 249595c635efSGarrett D'Amore r->first_eqn = r->last_eqn = e; 249695c635efSGarrett D'Amore 249795c635efSGarrett D'Amore r->eqn = r->last_eqn = e; 249895c635efSGarrett D'Amore 2499*260e9a87SYuri Pankov if (buf->buf[pos] != '\0') 2500*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, 2501*260e9a87SYuri Pankov ".EQ %s", buf->buf + pos); 250295c635efSGarrett D'Amore 250395c635efSGarrett D'Amore return(ROFF_IGN); 250495c635efSGarrett D'Amore } 250595c635efSGarrett D'Amore 250695c635efSGarrett D'Amore static enum rofferr 250795c635efSGarrett D'Amore roff_EN(ROFF_ARGS) 250895c635efSGarrett D'Amore { 250995c635efSGarrett D'Amore 2510*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN"); 251195c635efSGarrett D'Amore return(ROFF_IGN); 251295c635efSGarrett D'Amore } 251395c635efSGarrett D'Amore 251495c635efSGarrett D'Amore static enum rofferr 251595c635efSGarrett D'Amore roff_TS(ROFF_ARGS) 251695c635efSGarrett D'Amore { 2517698f87a4SGarrett D'Amore struct tbl_node *tbl; 251895c635efSGarrett D'Amore 251995c635efSGarrett D'Amore if (r->tbl) { 2520*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse, 2521*260e9a87SYuri Pankov ln, ppos, "TS breaks TS"); 252295c635efSGarrett D'Amore tbl_end(&r->tbl); 252395c635efSGarrett D'Amore } 252495c635efSGarrett D'Amore 2525698f87a4SGarrett D'Amore tbl = tbl_alloc(ppos, ln, r->parse); 252695c635efSGarrett D'Amore 252795c635efSGarrett D'Amore if (r->last_tbl) 2528698f87a4SGarrett D'Amore r->last_tbl->next = tbl; 252995c635efSGarrett D'Amore else 2530698f87a4SGarrett D'Amore r->first_tbl = r->last_tbl = tbl; 253195c635efSGarrett D'Amore 2532698f87a4SGarrett D'Amore r->tbl = r->last_tbl = tbl; 2533698f87a4SGarrett D'Amore return(ROFF_IGN); 2534698f87a4SGarrett D'Amore } 2535698f87a4SGarrett D'Amore 2536*260e9a87SYuri Pankov static enum rofferr 2537*260e9a87SYuri Pankov roff_brp(ROFF_ARGS) 2538*260e9a87SYuri Pankov { 2539*260e9a87SYuri Pankov 2540*260e9a87SYuri Pankov buf->buf[pos - 1] = '\0'; 2541*260e9a87SYuri Pankov return(ROFF_CONT); 2542*260e9a87SYuri Pankov } 2543*260e9a87SYuri Pankov 2544698f87a4SGarrett D'Amore static enum rofferr 2545698f87a4SGarrett D'Amore roff_cc(ROFF_ARGS) 2546698f87a4SGarrett D'Amore { 2547698f87a4SGarrett D'Amore const char *p; 2548698f87a4SGarrett D'Amore 2549*260e9a87SYuri Pankov p = buf->buf + pos; 2550698f87a4SGarrett D'Amore 2551*260e9a87SYuri Pankov if (*p == '\0' || (r->control = *p++) == '.') 2552698f87a4SGarrett D'Amore r->control = 0; 2553698f87a4SGarrett D'Amore 2554*260e9a87SYuri Pankov if (*p != '\0') 2555*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, 2556*260e9a87SYuri Pankov ln, p - buf->buf, "cc ... %s", p); 2557698f87a4SGarrett D'Amore 255895c635efSGarrett D'Amore return(ROFF_IGN); 255995c635efSGarrett D'Amore } 256095c635efSGarrett D'Amore 256195c635efSGarrett D'Amore static enum rofferr 256295c635efSGarrett D'Amore roff_tr(ROFF_ARGS) 256395c635efSGarrett D'Amore { 256495c635efSGarrett D'Amore const char *p, *first, *second; 256595c635efSGarrett D'Amore size_t fsz, ssz; 256695c635efSGarrett D'Amore enum mandoc_esc esc; 256795c635efSGarrett D'Amore 2568*260e9a87SYuri Pankov p = buf->buf + pos; 256995c635efSGarrett D'Amore 2570*260e9a87SYuri Pankov if (*p == '\0') { 2571*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, ln, ppos, "tr"); 257295c635efSGarrett D'Amore return(ROFF_IGN); 257395c635efSGarrett D'Amore } 257495c635efSGarrett D'Amore 2575*260e9a87SYuri Pankov while (*p != '\0') { 257695c635efSGarrett D'Amore fsz = ssz = 1; 257795c635efSGarrett D'Amore 257895c635efSGarrett D'Amore first = p++; 2579*260e9a87SYuri Pankov if (*first == '\\') { 258095c635efSGarrett D'Amore esc = mandoc_escape(&p, NULL, NULL); 2581*260e9a87SYuri Pankov if (esc == ESCAPE_ERROR) { 2582*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ESC_BAD, r->parse, 2583*260e9a87SYuri Pankov ln, (int)(p - buf->buf), first); 258495c635efSGarrett D'Amore return(ROFF_IGN); 258595c635efSGarrett D'Amore } 258695c635efSGarrett D'Amore fsz = (size_t)(p - first); 258795c635efSGarrett D'Amore } 258895c635efSGarrett D'Amore 258995c635efSGarrett D'Amore second = p++; 2590*260e9a87SYuri Pankov if (*second == '\\') { 259195c635efSGarrett D'Amore esc = mandoc_escape(&p, NULL, NULL); 2592*260e9a87SYuri Pankov if (esc == ESCAPE_ERROR) { 2593*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ESC_BAD, r->parse, 2594*260e9a87SYuri Pankov ln, (int)(p - buf->buf), second); 259595c635efSGarrett D'Amore return(ROFF_IGN); 259695c635efSGarrett D'Amore } 259795c635efSGarrett D'Amore ssz = (size_t)(p - second); 2598*260e9a87SYuri Pankov } else if (*second == '\0') { 2599*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_TR_ODD, r->parse, 2600*260e9a87SYuri Pankov ln, first - buf->buf, "tr %s", first); 260195c635efSGarrett D'Amore second = " "; 260295c635efSGarrett D'Amore p--; 260395c635efSGarrett D'Amore } 260495c635efSGarrett D'Amore 260595c635efSGarrett D'Amore if (fsz > 1) { 2606*260e9a87SYuri Pankov roff_setstrn(&r->xmbtab, first, fsz, 2607*260e9a87SYuri Pankov second, ssz, 0); 260895c635efSGarrett D'Amore continue; 260995c635efSGarrett D'Amore } 261095c635efSGarrett D'Amore 2611*260e9a87SYuri Pankov if (r->xtab == NULL) 2612*260e9a87SYuri Pankov r->xtab = mandoc_calloc(128, 2613*260e9a87SYuri Pankov sizeof(struct roffstr)); 261495c635efSGarrett D'Amore 261595c635efSGarrett D'Amore free(r->xtab[(int)*first].p); 261695c635efSGarrett D'Amore r->xtab[(int)*first].p = mandoc_strndup(second, ssz); 261795c635efSGarrett D'Amore r->xtab[(int)*first].sz = ssz; 261895c635efSGarrett D'Amore } 261995c635efSGarrett D'Amore 262095c635efSGarrett D'Amore return(ROFF_IGN); 262195c635efSGarrett D'Amore } 262295c635efSGarrett D'Amore 262395c635efSGarrett D'Amore static enum rofferr 262495c635efSGarrett D'Amore roff_so(ROFF_ARGS) 262595c635efSGarrett D'Amore { 2626*260e9a87SYuri Pankov char *name, *cp; 262795c635efSGarrett D'Amore 2628*260e9a87SYuri Pankov name = buf->buf + pos; 2629*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name); 263095c635efSGarrett D'Amore 263195c635efSGarrett D'Amore /* 263295c635efSGarrett D'Amore * Handle `so'. Be EXTREMELY careful, as we shouldn't be 263395c635efSGarrett D'Amore * opening anything that's not in our cwd or anything beneath 263495c635efSGarrett D'Amore * it. Thus, explicitly disallow traversing up the file-system 263595c635efSGarrett D'Amore * or using absolute paths. 263695c635efSGarrett D'Amore */ 263795c635efSGarrett D'Amore 2638*260e9a87SYuri Pankov if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) { 2639*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos, 2640*260e9a87SYuri Pankov ".so %s", name); 2641*260e9a87SYuri Pankov buf->sz = mandoc_asprintf(&cp, 2642*260e9a87SYuri Pankov ".sp\nSee the file %s.\n.sp", name) + 1; 2643*260e9a87SYuri Pankov free(buf->buf); 2644*260e9a87SYuri Pankov buf->buf = cp; 2645*260e9a87SYuri Pankov *offs = 0; 2646*260e9a87SYuri Pankov return(ROFF_REPARSE); 264795c635efSGarrett D'Amore } 264895c635efSGarrett D'Amore 264995c635efSGarrett D'Amore *offs = pos; 265095c635efSGarrett D'Amore return(ROFF_SO); 265195c635efSGarrett D'Amore } 265295c635efSGarrett D'Amore 265395c635efSGarrett D'Amore static enum rofferr 265495c635efSGarrett D'Amore roff_userdef(ROFF_ARGS) 265595c635efSGarrett D'Amore { 2656*260e9a87SYuri Pankov const char *arg[9], *ap; 265795c635efSGarrett D'Amore char *cp, *n1, *n2; 265895c635efSGarrett D'Amore int i; 2659*260e9a87SYuri Pankov size_t asz, rsz; 266095c635efSGarrett D'Amore 266195c635efSGarrett D'Amore /* 266295c635efSGarrett D'Amore * Collect pointers to macro argument strings 2663698f87a4SGarrett D'Amore * and NUL-terminate them. 266495c635efSGarrett D'Amore */ 2665*260e9a87SYuri Pankov 2666*260e9a87SYuri Pankov cp = buf->buf + pos; 266795c635efSGarrett D'Amore for (i = 0; i < 9; i++) 2668*260e9a87SYuri Pankov arg[i] = *cp == '\0' ? "" : 266995c635efSGarrett D'Amore mandoc_getarg(r->parse, &cp, ln, &pos); 267095c635efSGarrett D'Amore 267195c635efSGarrett D'Amore /* 267295c635efSGarrett D'Amore * Expand macro arguments. 267395c635efSGarrett D'Amore */ 2674*260e9a87SYuri Pankov 2675*260e9a87SYuri Pankov buf->sz = strlen(r->current_string) + 1; 2676*260e9a87SYuri Pankov n1 = cp = mandoc_malloc(buf->sz); 2677*260e9a87SYuri Pankov memcpy(n1, r->current_string, buf->sz); 2678*260e9a87SYuri Pankov while (*cp != '\0') { 2679*260e9a87SYuri Pankov 2680*260e9a87SYuri Pankov /* Scan ahead for the next argument invocation. */ 2681*260e9a87SYuri Pankov 2682*260e9a87SYuri Pankov if (*cp++ != '\\') 268395c635efSGarrett D'Amore continue; 2684*260e9a87SYuri Pankov if (*cp++ != '$') 2685*260e9a87SYuri Pankov continue; 2686*260e9a87SYuri Pankov i = *cp - '1'; 2687*260e9a87SYuri Pankov if (0 > i || 8 < i) 2688*260e9a87SYuri Pankov continue; 2689*260e9a87SYuri Pankov cp -= 2; 2690*260e9a87SYuri Pankov 2691*260e9a87SYuri Pankov /* 2692*260e9a87SYuri Pankov * Determine the size of the expanded argument, 2693*260e9a87SYuri Pankov * taking escaping of quotes into account. 2694*260e9a87SYuri Pankov */ 2695*260e9a87SYuri Pankov 2696*260e9a87SYuri Pankov asz = 0; 2697*260e9a87SYuri Pankov for (ap = arg[i]; *ap != '\0'; ap++) { 2698*260e9a87SYuri Pankov asz++; 2699*260e9a87SYuri Pankov if (*ap == '"') 2700*260e9a87SYuri Pankov asz += 3; 2701*260e9a87SYuri Pankov } 2702*260e9a87SYuri Pankov if (asz != 3) { 2703*260e9a87SYuri Pankov 2704*260e9a87SYuri Pankov /* 2705*260e9a87SYuri Pankov * Determine the size of the rest of the 2706*260e9a87SYuri Pankov * unexpanded macro, including the NUL. 2707*260e9a87SYuri Pankov */ 2708*260e9a87SYuri Pankov 2709*260e9a87SYuri Pankov rsz = buf->sz - (cp - n1) - 3; 2710*260e9a87SYuri Pankov 2711*260e9a87SYuri Pankov /* 2712*260e9a87SYuri Pankov * When shrinking, move before 2713*260e9a87SYuri Pankov * releasing the storage. 2714*260e9a87SYuri Pankov */ 2715*260e9a87SYuri Pankov 2716*260e9a87SYuri Pankov if (asz < 3) 2717*260e9a87SYuri Pankov memmove(cp + asz, cp + 3, rsz); 2718*260e9a87SYuri Pankov 2719*260e9a87SYuri Pankov /* 2720*260e9a87SYuri Pankov * Resize the storage for the macro 2721*260e9a87SYuri Pankov * and readjust the parse pointer. 2722*260e9a87SYuri Pankov */ 2723*260e9a87SYuri Pankov 2724*260e9a87SYuri Pankov buf->sz += asz - 3; 2725*260e9a87SYuri Pankov n2 = mandoc_realloc(n1, buf->sz); 2726*260e9a87SYuri Pankov cp = n2 + (cp - n1); 2727*260e9a87SYuri Pankov n1 = n2; 2728*260e9a87SYuri Pankov 2729*260e9a87SYuri Pankov /* 2730*260e9a87SYuri Pankov * When growing, make room 2731*260e9a87SYuri Pankov * for the expanded argument. 2732*260e9a87SYuri Pankov */ 2733*260e9a87SYuri Pankov 2734*260e9a87SYuri Pankov if (asz > 3) 2735*260e9a87SYuri Pankov memmove(cp + asz, cp + 3, rsz); 273695c635efSGarrett D'Amore } 273795c635efSGarrett D'Amore 2738*260e9a87SYuri Pankov /* Copy the expanded argument, escaping quotes. */ 273995c635efSGarrett D'Amore 2740*260e9a87SYuri Pankov n2 = cp; 2741*260e9a87SYuri Pankov for (ap = arg[i]; *ap != '\0'; ap++) { 2742*260e9a87SYuri Pankov if (*ap == '"') { 2743*260e9a87SYuri Pankov memcpy(n2, "\\(dq", 4); 2744*260e9a87SYuri Pankov n2 += 4; 2745*260e9a87SYuri Pankov } else 2746*260e9a87SYuri Pankov *n2++ = *ap; 2747*260e9a87SYuri Pankov } 274895c635efSGarrett D'Amore } 274995c635efSGarrett D'Amore 275095c635efSGarrett D'Amore /* 275195c635efSGarrett D'Amore * Replace the macro invocation 275295c635efSGarrett D'Amore * by the expanded macro. 275395c635efSGarrett D'Amore */ 275495c635efSGarrett D'Amore 2755*260e9a87SYuri Pankov free(buf->buf); 2756*260e9a87SYuri Pankov buf->buf = n1; 2757*260e9a87SYuri Pankov *offs = 0; 2758*260e9a87SYuri Pankov 2759*260e9a87SYuri Pankov return(buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ? 276095c635efSGarrett D'Amore ROFF_REPARSE : ROFF_APPEND); 276195c635efSGarrett D'Amore } 276295c635efSGarrett D'Amore 2763*260e9a87SYuri Pankov static size_t 276495c635efSGarrett D'Amore roff_getname(struct roff *r, char **cpp, int ln, int pos) 276595c635efSGarrett D'Amore { 276695c635efSGarrett D'Amore char *name, *cp; 2767*260e9a87SYuri Pankov size_t namesz; 276895c635efSGarrett D'Amore 276995c635efSGarrett D'Amore name = *cpp; 277095c635efSGarrett D'Amore if ('\0' == *name) 2771*260e9a87SYuri Pankov return(0); 277295c635efSGarrett D'Amore 2773*260e9a87SYuri Pankov /* Read until end of name and terminate it with NUL. */ 2774*260e9a87SYuri Pankov for (cp = name; 1; cp++) { 2775*260e9a87SYuri Pankov if ('\0' == *cp || ' ' == *cp) { 2776*260e9a87SYuri Pankov namesz = cp - name; 2777*260e9a87SYuri Pankov break; 2778*260e9a87SYuri Pankov } 277995c635efSGarrett D'Amore if ('\\' != *cp) 278095c635efSGarrett D'Amore continue; 2781*260e9a87SYuri Pankov namesz = cp - name; 2782*260e9a87SYuri Pankov if ('{' == cp[1] || '}' == cp[1]) 2783*260e9a87SYuri Pankov break; 278495c635efSGarrett D'Amore cp++; 278595c635efSGarrett D'Amore if ('\\' == *cp) 278695c635efSGarrett D'Amore continue; 2787*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos, 2788*260e9a87SYuri Pankov "%.*s", (int)(cp - name + 1), name); 2789*260e9a87SYuri Pankov mandoc_escape((const char **)&cp, NULL, NULL); 2790*260e9a87SYuri Pankov break; 279195c635efSGarrett D'Amore } 279295c635efSGarrett D'Amore 279395c635efSGarrett D'Amore /* Read past spaces. */ 279495c635efSGarrett D'Amore while (' ' == *cp) 279595c635efSGarrett D'Amore cp++; 279695c635efSGarrett D'Amore 279795c635efSGarrett D'Amore *cpp = cp; 2798*260e9a87SYuri Pankov return(namesz); 279995c635efSGarrett D'Amore } 280095c635efSGarrett D'Amore 280195c635efSGarrett D'Amore /* 280295c635efSGarrett D'Amore * Store *string into the user-defined string called *name. 280395c635efSGarrett D'Amore * To clear an existing entry, call with (*r, *name, NULL, 0). 2804*260e9a87SYuri Pankov * append == 0: replace mode 2805*260e9a87SYuri Pankov * append == 1: single-line append mode 2806*260e9a87SYuri Pankov * append == 2: multiline append mode, append '\n' after each call 280795c635efSGarrett D'Amore */ 280895c635efSGarrett D'Amore static void 280995c635efSGarrett D'Amore roff_setstr(struct roff *r, const char *name, const char *string, 2810*260e9a87SYuri Pankov int append) 281195c635efSGarrett D'Amore { 281295c635efSGarrett D'Amore 281395c635efSGarrett D'Amore roff_setstrn(&r->strtab, name, strlen(name), string, 2814*260e9a87SYuri Pankov string ? strlen(string) : 0, append); 281595c635efSGarrett D'Amore } 281695c635efSGarrett D'Amore 281795c635efSGarrett D'Amore static void 281895c635efSGarrett D'Amore roff_setstrn(struct roffkv **r, const char *name, size_t namesz, 2819*260e9a87SYuri Pankov const char *string, size_t stringsz, int append) 282095c635efSGarrett D'Amore { 282195c635efSGarrett D'Amore struct roffkv *n; 282295c635efSGarrett D'Amore char *c; 282395c635efSGarrett D'Amore int i; 282495c635efSGarrett D'Amore size_t oldch, newch; 282595c635efSGarrett D'Amore 282695c635efSGarrett D'Amore /* Search for an existing string with the same name. */ 282795c635efSGarrett D'Amore n = *r; 282895c635efSGarrett D'Amore 2829*260e9a87SYuri Pankov while (n && (namesz != n->key.sz || 2830*260e9a87SYuri Pankov strncmp(n->key.p, name, namesz))) 283195c635efSGarrett D'Amore n = n->next; 283295c635efSGarrett D'Amore 283395c635efSGarrett D'Amore if (NULL == n) { 283495c635efSGarrett D'Amore /* Create a new string table entry. */ 283595c635efSGarrett D'Amore n = mandoc_malloc(sizeof(struct roffkv)); 283695c635efSGarrett D'Amore n->key.p = mandoc_strndup(name, namesz); 283795c635efSGarrett D'Amore n->key.sz = namesz; 283895c635efSGarrett D'Amore n->val.p = NULL; 283995c635efSGarrett D'Amore n->val.sz = 0; 284095c635efSGarrett D'Amore n->next = *r; 284195c635efSGarrett D'Amore *r = n; 2842*260e9a87SYuri Pankov } else if (0 == append) { 284395c635efSGarrett D'Amore free(n->val.p); 284495c635efSGarrett D'Amore n->val.p = NULL; 284595c635efSGarrett D'Amore n->val.sz = 0; 284695c635efSGarrett D'Amore } 284795c635efSGarrett D'Amore 284895c635efSGarrett D'Amore if (NULL == string) 284995c635efSGarrett D'Amore return; 285095c635efSGarrett D'Amore 285195c635efSGarrett D'Amore /* 285295c635efSGarrett D'Amore * One additional byte for the '\n' in multiline mode, 285395c635efSGarrett D'Amore * and one for the terminating '\0'. 285495c635efSGarrett D'Amore */ 2855*260e9a87SYuri Pankov newch = stringsz + (1 < append ? 2u : 1u); 285695c635efSGarrett D'Amore 285795c635efSGarrett D'Amore if (NULL == n->val.p) { 285895c635efSGarrett D'Amore n->val.p = mandoc_malloc(newch); 285995c635efSGarrett D'Amore *n->val.p = '\0'; 286095c635efSGarrett D'Amore oldch = 0; 286195c635efSGarrett D'Amore } else { 286295c635efSGarrett D'Amore oldch = n->val.sz; 286395c635efSGarrett D'Amore n->val.p = mandoc_realloc(n->val.p, oldch + newch); 286495c635efSGarrett D'Amore } 286595c635efSGarrett D'Amore 286695c635efSGarrett D'Amore /* Skip existing content in the destination buffer. */ 286795c635efSGarrett D'Amore c = n->val.p + (int)oldch; 286895c635efSGarrett D'Amore 286995c635efSGarrett D'Amore /* Append new content to the destination buffer. */ 287095c635efSGarrett D'Amore i = 0; 287195c635efSGarrett D'Amore while (i < (int)stringsz) { 287295c635efSGarrett D'Amore /* 287395c635efSGarrett D'Amore * Rudimentary roff copy mode: 287495c635efSGarrett D'Amore * Handle escaped backslashes. 287595c635efSGarrett D'Amore */ 287695c635efSGarrett D'Amore if ('\\' == string[i] && '\\' == string[i + 1]) 287795c635efSGarrett D'Amore i++; 287895c635efSGarrett D'Amore *c++ = string[i++]; 287995c635efSGarrett D'Amore } 288095c635efSGarrett D'Amore 288195c635efSGarrett D'Amore /* Append terminating bytes. */ 2882*260e9a87SYuri Pankov if (1 < append) 288395c635efSGarrett D'Amore *c++ = '\n'; 288495c635efSGarrett D'Amore 288595c635efSGarrett D'Amore *c = '\0'; 288695c635efSGarrett D'Amore n->val.sz = (int)(c - n->val.p); 288795c635efSGarrett D'Amore } 288895c635efSGarrett D'Amore 288995c635efSGarrett D'Amore static const char * 289095c635efSGarrett D'Amore roff_getstrn(const struct roff *r, const char *name, size_t len) 289195c635efSGarrett D'Amore { 289295c635efSGarrett D'Amore const struct roffkv *n; 2893*260e9a87SYuri Pankov int i; 289495c635efSGarrett D'Amore 289595c635efSGarrett D'Amore for (n = r->strtab; n; n = n->next) 289695c635efSGarrett D'Amore if (0 == strncmp(name, n->key.p, len) && 289795c635efSGarrett D'Amore '\0' == n->key.p[(int)len]) 289895c635efSGarrett D'Amore return(n->val.p); 289995c635efSGarrett D'Amore 2900*260e9a87SYuri Pankov for (i = 0; i < PREDEFS_MAX; i++) 2901*260e9a87SYuri Pankov if (0 == strncmp(name, predefs[i].name, len) && 2902*260e9a87SYuri Pankov '\0' == predefs[i].name[(int)len]) 2903*260e9a87SYuri Pankov return(predefs[i].str); 2904*260e9a87SYuri Pankov 290595c635efSGarrett D'Amore return(NULL); 290695c635efSGarrett D'Amore } 290795c635efSGarrett D'Amore 290895c635efSGarrett D'Amore static void 290995c635efSGarrett D'Amore roff_freestr(struct roffkv *r) 291095c635efSGarrett D'Amore { 291195c635efSGarrett D'Amore struct roffkv *n, *nn; 291295c635efSGarrett D'Amore 291395c635efSGarrett D'Amore for (n = r; n; n = nn) { 291495c635efSGarrett D'Amore free(n->key.p); 291595c635efSGarrett D'Amore free(n->val.p); 291695c635efSGarrett D'Amore nn = n->next; 291795c635efSGarrett D'Amore free(n); 291895c635efSGarrett D'Amore } 291995c635efSGarrett D'Amore } 292095c635efSGarrett D'Amore 292195c635efSGarrett D'Amore const struct tbl_span * 292295c635efSGarrett D'Amore roff_span(const struct roff *r) 292395c635efSGarrett D'Amore { 292495c635efSGarrett D'Amore 292595c635efSGarrett D'Amore return(r->tbl ? tbl_span(r->tbl) : NULL); 292695c635efSGarrett D'Amore } 292795c635efSGarrett D'Amore 292895c635efSGarrett D'Amore const struct eqn * 292995c635efSGarrett D'Amore roff_eqn(const struct roff *r) 293095c635efSGarrett D'Amore { 293195c635efSGarrett D'Amore 293295c635efSGarrett D'Amore return(r->last_eqn ? &r->last_eqn->eqn : NULL); 293395c635efSGarrett D'Amore } 293495c635efSGarrett D'Amore 293595c635efSGarrett D'Amore /* 293695c635efSGarrett D'Amore * Duplicate an input string, making the appropriate character 293795c635efSGarrett D'Amore * conversations (as stipulated by `tr') along the way. 293895c635efSGarrett D'Amore * Returns a heap-allocated string with all the replacements made. 293995c635efSGarrett D'Amore */ 294095c635efSGarrett D'Amore char * 294195c635efSGarrett D'Amore roff_strdup(const struct roff *r, const char *p) 294295c635efSGarrett D'Amore { 294395c635efSGarrett D'Amore const struct roffkv *cp; 294495c635efSGarrett D'Amore char *res; 294595c635efSGarrett D'Amore const char *pp; 294695c635efSGarrett D'Amore size_t ssz, sz; 294795c635efSGarrett D'Amore enum mandoc_esc esc; 294895c635efSGarrett D'Amore 294995c635efSGarrett D'Amore if (NULL == r->xmbtab && NULL == r->xtab) 295095c635efSGarrett D'Amore return(mandoc_strdup(p)); 295195c635efSGarrett D'Amore else if ('\0' == *p) 295295c635efSGarrett D'Amore return(mandoc_strdup("")); 295395c635efSGarrett D'Amore 295495c635efSGarrett D'Amore /* 295595c635efSGarrett D'Amore * Step through each character looking for term matches 295695c635efSGarrett D'Amore * (remember that a `tr' can be invoked with an escape, which is 295795c635efSGarrett D'Amore * a glyph but the escape is multi-character). 295895c635efSGarrett D'Amore * We only do this if the character hash has been initialised 295995c635efSGarrett D'Amore * and the string is >0 length. 296095c635efSGarrett D'Amore */ 296195c635efSGarrett D'Amore 296295c635efSGarrett D'Amore res = NULL; 296395c635efSGarrett D'Amore ssz = 0; 296495c635efSGarrett D'Amore 296595c635efSGarrett D'Amore while ('\0' != *p) { 296695c635efSGarrett D'Amore if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) { 296795c635efSGarrett D'Amore sz = r->xtab[(int)*p].sz; 296895c635efSGarrett D'Amore res = mandoc_realloc(res, ssz + sz + 1); 296995c635efSGarrett D'Amore memcpy(res + ssz, r->xtab[(int)*p].p, sz); 297095c635efSGarrett D'Amore ssz += sz; 297195c635efSGarrett D'Amore p++; 297295c635efSGarrett D'Amore continue; 297395c635efSGarrett D'Amore } else if ('\\' != *p) { 297495c635efSGarrett D'Amore res = mandoc_realloc(res, ssz + 2); 297595c635efSGarrett D'Amore res[ssz++] = *p++; 297695c635efSGarrett D'Amore continue; 297795c635efSGarrett D'Amore } 297895c635efSGarrett D'Amore 297995c635efSGarrett D'Amore /* Search for term matches. */ 298095c635efSGarrett D'Amore for (cp = r->xmbtab; cp; cp = cp->next) 298195c635efSGarrett D'Amore if (0 == strncmp(p, cp->key.p, cp->key.sz)) 298295c635efSGarrett D'Amore break; 298395c635efSGarrett D'Amore 298495c635efSGarrett D'Amore if (NULL != cp) { 298595c635efSGarrett D'Amore /* 298695c635efSGarrett D'Amore * A match has been found. 298795c635efSGarrett D'Amore * Append the match to the array and move 298895c635efSGarrett D'Amore * forward by its keysize. 298995c635efSGarrett D'Amore */ 2990*260e9a87SYuri Pankov res = mandoc_realloc(res, 2991*260e9a87SYuri Pankov ssz + cp->val.sz + 1); 299295c635efSGarrett D'Amore memcpy(res + ssz, cp->val.p, cp->val.sz); 299395c635efSGarrett D'Amore ssz += cp->val.sz; 299495c635efSGarrett D'Amore p += (int)cp->key.sz; 299595c635efSGarrett D'Amore continue; 299695c635efSGarrett D'Amore } 299795c635efSGarrett D'Amore 299895c635efSGarrett D'Amore /* 299995c635efSGarrett D'Amore * Handle escapes carefully: we need to copy 300095c635efSGarrett D'Amore * over just the escape itself, or else we might 300195c635efSGarrett D'Amore * do replacements within the escape itself. 300295c635efSGarrett D'Amore * Make sure to pass along the bogus string. 300395c635efSGarrett D'Amore */ 300495c635efSGarrett D'Amore pp = p++; 300595c635efSGarrett D'Amore esc = mandoc_escape(&p, NULL, NULL); 300695c635efSGarrett D'Amore if (ESCAPE_ERROR == esc) { 300795c635efSGarrett D'Amore sz = strlen(pp); 300895c635efSGarrett D'Amore res = mandoc_realloc(res, ssz + sz + 1); 300995c635efSGarrett D'Amore memcpy(res + ssz, pp, sz); 301095c635efSGarrett D'Amore break; 301195c635efSGarrett D'Amore } 301295c635efSGarrett D'Amore /* 301395c635efSGarrett D'Amore * We bail out on bad escapes. 301495c635efSGarrett D'Amore * No need to warn: we already did so when 301595c635efSGarrett D'Amore * roff_res() was called. 301695c635efSGarrett D'Amore */ 301795c635efSGarrett D'Amore sz = (int)(p - pp); 301895c635efSGarrett D'Amore res = mandoc_realloc(res, ssz + sz + 1); 301995c635efSGarrett D'Amore memcpy(res + ssz, pp, sz); 302095c635efSGarrett D'Amore ssz += sz; 302195c635efSGarrett D'Amore } 302295c635efSGarrett D'Amore 302395c635efSGarrett D'Amore res[(int)ssz] = '\0'; 302495c635efSGarrett D'Amore return(res); 302595c635efSGarrett D'Amore } 3026698f87a4SGarrett D'Amore 3027*260e9a87SYuri Pankov int 3028*260e9a87SYuri Pankov roff_getformat(const struct roff *r) 3029*260e9a87SYuri Pankov { 3030*260e9a87SYuri Pankov 3031*260e9a87SYuri Pankov return(r->format); 3032*260e9a87SYuri Pankov } 3033*260e9a87SYuri Pankov 3034698f87a4SGarrett D'Amore /* 3035698f87a4SGarrett D'Amore * Find out whether a line is a macro line or not. 3036698f87a4SGarrett D'Amore * If it is, adjust the current position and return one; if it isn't, 3037698f87a4SGarrett D'Amore * return zero and don't change the current position. 3038698f87a4SGarrett D'Amore * If the control character has been set with `.cc', then let that grain 3039698f87a4SGarrett D'Amore * precedence. 3040698f87a4SGarrett D'Amore * This is slighly contrary to groff, where using the non-breaking 3041698f87a4SGarrett D'Amore * control character when `cc' has been invoked will cause the 3042698f87a4SGarrett D'Amore * non-breaking macro contents to be printed verbatim. 3043698f87a4SGarrett D'Amore */ 3044698f87a4SGarrett D'Amore int 3045698f87a4SGarrett D'Amore roff_getcontrol(const struct roff *r, const char *cp, int *ppos) 3046698f87a4SGarrett D'Amore { 3047698f87a4SGarrett D'Amore int pos; 3048698f87a4SGarrett D'Amore 3049698f87a4SGarrett D'Amore pos = *ppos; 3050698f87a4SGarrett D'Amore 3051698f87a4SGarrett D'Amore if (0 != r->control && cp[pos] == r->control) 3052698f87a4SGarrett D'Amore pos++; 3053698f87a4SGarrett D'Amore else if (0 != r->control) 3054698f87a4SGarrett D'Amore return(0); 3055698f87a4SGarrett D'Amore else if ('\\' == cp[pos] && '.' == cp[pos + 1]) 3056698f87a4SGarrett D'Amore pos += 2; 3057698f87a4SGarrett D'Amore else if ('.' == cp[pos] || '\'' == cp[pos]) 3058698f87a4SGarrett D'Amore pos++; 3059698f87a4SGarrett D'Amore else 3060698f87a4SGarrett D'Amore return(0); 3061698f87a4SGarrett D'Amore 3062698f87a4SGarrett D'Amore while (' ' == cp[pos] || '\t' == cp[pos]) 3063698f87a4SGarrett D'Amore pos++; 3064698f87a4SGarrett D'Amore 3065698f87a4SGarrett D'Amore *ppos = pos; 3066698f87a4SGarrett D'Amore return(1); 3067698f87a4SGarrett D'Amore } 3068