1241675Suqs/* $Id: roff.c,v 1.172 2011/10/24 21:41:45 schwarze Exp $ */ 2241675Suqs/* 3241675Suqs * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4241675Suqs * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> 5241675Suqs * 6241675Suqs * Permission to use, copy, modify, and distribute this software for any 7241675Suqs * purpose with or without fee is hereby granted, provided that the above 8241675Suqs * copyright notice and this permission notice appear in all copies. 9241675Suqs * 10241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17241675Suqs */ 18241675Suqs#ifdef HAVE_CONFIG_H 19241675Suqs#include "config.h" 20241675Suqs#endif 21241675Suqs 22241675Suqs#include <assert.h> 23241675Suqs#include <ctype.h> 24241675Suqs#include <stdlib.h> 25241675Suqs#include <string.h> 26241675Suqs 27241675Suqs#include "mandoc.h" 28241675Suqs#include "libroff.h" 29241675Suqs#include "libmandoc.h" 30241675Suqs 31241675Suqs/* Maximum number of nested if-else conditionals. */ 32241675Suqs#define RSTACK_MAX 128 33241675Suqs 34241675Suqs/* Maximum number of string expansions per line, to break infinite loops. */ 35241675Suqs#define EXPAND_LIMIT 1000 36241675Suqs 37241675Suqsenum rofft { 38241675Suqs ROFF_ad, 39241675Suqs ROFF_am, 40241675Suqs ROFF_ami, 41241675Suqs ROFF_am1, 42241675Suqs ROFF_de, 43241675Suqs ROFF_dei, 44241675Suqs ROFF_de1, 45241675Suqs ROFF_ds, 46241675Suqs ROFF_el, 47241675Suqs ROFF_hy, 48241675Suqs ROFF_ie, 49241675Suqs ROFF_if, 50241675Suqs ROFF_ig, 51241675Suqs ROFF_it, 52241675Suqs ROFF_ne, 53241675Suqs ROFF_nh, 54241675Suqs ROFF_nr, 55241675Suqs ROFF_ns, 56241675Suqs ROFF_ps, 57241675Suqs ROFF_rm, 58241675Suqs ROFF_so, 59241675Suqs ROFF_ta, 60241675Suqs ROFF_tr, 61241675Suqs ROFF_TS, 62241675Suqs ROFF_TE, 63241675Suqs ROFF_T_, 64241675Suqs ROFF_EQ, 65241675Suqs ROFF_EN, 66241675Suqs ROFF_cblock, 67241675Suqs ROFF_ccond, 68241675Suqs ROFF_USERDEF, 69241675Suqs ROFF_MAX 70241675Suqs}; 71241675Suqs 72241675Suqsenum roffrule { 73241675Suqs ROFFRULE_ALLOW, 74241675Suqs ROFFRULE_DENY 75241675Suqs}; 76241675Suqs 77241675Suqs/* 78241675Suqs * A single register entity. If "set" is zero, the value of the 79241675Suqs * register should be the default one, which is per-register. 80241675Suqs * Registers are assumed to be unsigned ints for now. 81241675Suqs */ 82241675Suqsstruct reg { 83241675Suqs int set; /* whether set or not */ 84241675Suqs unsigned int u; /* unsigned integer */ 85241675Suqs}; 86241675Suqs 87241675Suqs/* 88241675Suqs * An incredibly-simple string buffer. 89241675Suqs */ 90241675Suqsstruct roffstr { 91241675Suqs char *p; /* nil-terminated buffer */ 92241675Suqs size_t sz; /* saved strlen(p) */ 93241675Suqs}; 94241675Suqs 95241675Suqs/* 96241675Suqs * A key-value roffstr pair as part of a singly-linked list. 97241675Suqs */ 98241675Suqsstruct roffkv { 99241675Suqs struct roffstr key; 100241675Suqs struct roffstr val; 101241675Suqs struct roffkv *next; /* next in list */ 102241675Suqs}; 103241675Suqs 104241675Suqsstruct roff { 105241675Suqs struct mparse *parse; /* parse point */ 106241675Suqs struct roffnode *last; /* leaf of stack */ 107241675Suqs enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */ 108241675Suqs int rstackpos; /* position in rstack */ 109241675Suqs struct reg regs[REG__MAX]; 110241675Suqs struct roffkv *strtab; /* user-defined strings & macros */ 111241675Suqs struct roffkv *xmbtab; /* multi-byte trans table (`tr') */ 112241675Suqs struct roffstr *xtab; /* single-byte trans table (`tr') */ 113241675Suqs const char *current_string; /* value of last called user macro */ 114241675Suqs struct tbl_node *first_tbl; /* first table parsed */ 115241675Suqs struct tbl_node *last_tbl; /* last table parsed */ 116241675Suqs struct tbl_node *tbl; /* current table being parsed */ 117241675Suqs struct eqn_node *last_eqn; /* last equation parsed */ 118241675Suqs struct eqn_node *first_eqn; /* first equation parsed */ 119241675Suqs struct eqn_node *eqn; /* current equation being parsed */ 120241675Suqs}; 121241675Suqs 122241675Suqsstruct roffnode { 123241675Suqs enum rofft tok; /* type of node */ 124241675Suqs struct roffnode *parent; /* up one in stack */ 125241675Suqs int line; /* parse line */ 126241675Suqs int col; /* parse col */ 127241675Suqs char *name; /* node name, e.g. macro name */ 128241675Suqs char *end; /* end-rules: custom token */ 129241675Suqs int endspan; /* end-rules: next-line or infty */ 130241675Suqs enum roffrule rule; /* current evaluation rule */ 131241675Suqs}; 132241675Suqs 133241675Suqs#define ROFF_ARGS struct roff *r, /* parse ctx */ \ 134241675Suqs enum rofft tok, /* tok of macro */ \ 135241675Suqs char **bufp, /* input buffer */ \ 136241675Suqs size_t *szp, /* size of input buffer */ \ 137241675Suqs int ln, /* parse line */ \ 138241675Suqs int ppos, /* original pos in buffer */ \ 139241675Suqs int pos, /* current pos in buffer */ \ 140241675Suqs int *offs /* reset offset of buffer data */ 141241675Suqs 142241675Suqstypedef enum rofferr (*roffproc)(ROFF_ARGS); 143241675Suqs 144241675Suqsstruct roffmac { 145241675Suqs const char *name; /* macro name */ 146241675Suqs roffproc proc; /* process new macro */ 147241675Suqs roffproc text; /* process as child text of macro */ 148241675Suqs roffproc sub; /* process as child of macro */ 149241675Suqs int flags; 150241675Suqs#define ROFFMAC_STRUCT (1 << 0) /* always interpret */ 151241675Suqs struct roffmac *next; 152241675Suqs}; 153241675Suqs 154241675Suqsstruct predef { 155241675Suqs const char *name; /* predefined input name */ 156241675Suqs const char *str; /* replacement symbol */ 157241675Suqs}; 158241675Suqs 159241675Suqs#define PREDEF(__name, __str) \ 160241675Suqs { (__name), (__str) }, 161241675Suqs 162241675Suqsstatic enum rofft roffhash_find(const char *, size_t); 163241675Suqsstatic void roffhash_init(void); 164241675Suqsstatic void roffnode_cleanscope(struct roff *); 165241675Suqsstatic void roffnode_pop(struct roff *); 166241675Suqsstatic void roffnode_push(struct roff *, enum rofft, 167241675Suqs const char *, int, int); 168241675Suqsstatic enum rofferr roff_block(ROFF_ARGS); 169241675Suqsstatic enum rofferr roff_block_text(ROFF_ARGS); 170241675Suqsstatic enum rofferr roff_block_sub(ROFF_ARGS); 171241675Suqsstatic enum rofferr roff_cblock(ROFF_ARGS); 172241675Suqsstatic enum rofferr roff_ccond(ROFF_ARGS); 173241675Suqsstatic enum rofferr roff_cond(ROFF_ARGS); 174241675Suqsstatic enum rofferr roff_cond_text(ROFF_ARGS); 175241675Suqsstatic enum rofferr roff_cond_sub(ROFF_ARGS); 176241675Suqsstatic enum rofferr roff_ds(ROFF_ARGS); 177241675Suqsstatic enum roffrule roff_evalcond(const char *, int *); 178241675Suqsstatic void roff_free1(struct roff *); 179241675Suqsstatic void roff_freestr(struct roffkv *); 180241675Suqsstatic char *roff_getname(struct roff *, char **, int, int); 181241675Suqsstatic const char *roff_getstrn(const struct roff *, 182241675Suqs const char *, size_t); 183241675Suqsstatic enum rofferr roff_line_ignore(ROFF_ARGS); 184241675Suqsstatic enum rofferr roff_nr(ROFF_ARGS); 185241675Suqsstatic void roff_openeqn(struct roff *, const char *, 186241675Suqs int, int, const char *); 187241675Suqsstatic enum rofft roff_parse(struct roff *, const char *, int *); 188241675Suqsstatic enum rofferr roff_parsetext(char *); 189241675Suqsstatic enum rofferr roff_res(struct roff *, 190241675Suqs char **, size_t *, int, int); 191241675Suqsstatic enum rofferr roff_rm(ROFF_ARGS); 192241675Suqsstatic void roff_setstr(struct roff *, 193241675Suqs const char *, const char *, int); 194241675Suqsstatic void roff_setstrn(struct roffkv **, const char *, 195241675Suqs size_t, const char *, size_t, int); 196241675Suqsstatic enum rofferr roff_so(ROFF_ARGS); 197241675Suqsstatic enum rofferr roff_tr(ROFF_ARGS); 198241675Suqsstatic enum rofferr roff_TE(ROFF_ARGS); 199241675Suqsstatic enum rofferr roff_TS(ROFF_ARGS); 200241675Suqsstatic enum rofferr roff_EQ(ROFF_ARGS); 201241675Suqsstatic enum rofferr roff_EN(ROFF_ARGS); 202241675Suqsstatic enum rofferr roff_T_(ROFF_ARGS); 203241675Suqsstatic enum rofferr roff_userdef(ROFF_ARGS); 204241675Suqs 205241675Suqs/* See roffhash_find() */ 206241675Suqs 207241675Suqs#define ASCII_HI 126 208241675Suqs#define ASCII_LO 33 209241675Suqs#define HASHWIDTH (ASCII_HI - ASCII_LO + 1) 210241675Suqs 211241675Suqsstatic struct roffmac *hash[HASHWIDTH]; 212241675Suqs 213241675Suqsstatic struct roffmac roffs[ROFF_MAX] = { 214241675Suqs { "ad", roff_line_ignore, NULL, NULL, 0, NULL }, 215241675Suqs { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 216241675Suqs { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 217241675Suqs { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 218241675Suqs { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 219241675Suqs { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 220241675Suqs { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 221241675Suqs { "ds", roff_ds, NULL, NULL, 0, NULL }, 222241675Suqs { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, 223241675Suqs { "hy", roff_line_ignore, NULL, NULL, 0, NULL }, 224241675Suqs { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, 225241675Suqs { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, 226241675Suqs { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 227241675Suqs { "it", roff_line_ignore, NULL, NULL, 0, NULL }, 228241675Suqs { "ne", roff_line_ignore, NULL, NULL, 0, NULL }, 229241675Suqs { "nh", roff_line_ignore, NULL, NULL, 0, NULL }, 230241675Suqs { "nr", roff_nr, NULL, NULL, 0, NULL }, 231241675Suqs { "ns", roff_line_ignore, NULL, NULL, 0, NULL }, 232241675Suqs { "ps", roff_line_ignore, NULL, NULL, 0, NULL }, 233241675Suqs { "rm", roff_rm, NULL, NULL, 0, NULL }, 234241675Suqs { "so", roff_so, NULL, NULL, 0, NULL }, 235241675Suqs { "ta", roff_line_ignore, NULL, NULL, 0, NULL }, 236241675Suqs { "tr", roff_tr, NULL, NULL, 0, NULL }, 237241675Suqs { "TS", roff_TS, NULL, NULL, 0, NULL }, 238241675Suqs { "TE", roff_TE, NULL, NULL, 0, NULL }, 239241675Suqs { "T&", roff_T_, NULL, NULL, 0, NULL }, 240241675Suqs { "EQ", roff_EQ, NULL, NULL, 0, NULL }, 241241675Suqs { "EN", roff_EN, NULL, NULL, 0, NULL }, 242241675Suqs { ".", roff_cblock, NULL, NULL, 0, NULL }, 243241675Suqs { "\\}", roff_ccond, NULL, NULL, 0, NULL }, 244241675Suqs { NULL, roff_userdef, NULL, NULL, 0, NULL }, 245241675Suqs}; 246241675Suqs 247241675Suqs/* Array of injected predefined strings. */ 248241675Suqs#define PREDEFS_MAX 38 249241675Suqsstatic const struct predef predefs[PREDEFS_MAX] = { 250241675Suqs#include "predefs.in" 251241675Suqs}; 252241675Suqs 253241675Suqs/* See roffhash_find() */ 254241675Suqs#define ROFF_HASH(p) (p[0] - ASCII_LO) 255241675Suqs 256241675Suqsstatic void 257241675Suqsroffhash_init(void) 258241675Suqs{ 259241675Suqs struct roffmac *n; 260241675Suqs int buc, i; 261241675Suqs 262241675Suqs for (i = 0; i < (int)ROFF_USERDEF; i++) { 263241675Suqs assert(roffs[i].name[0] >= ASCII_LO); 264241675Suqs assert(roffs[i].name[0] <= ASCII_HI); 265241675Suqs 266241675Suqs buc = ROFF_HASH(roffs[i].name); 267241675Suqs 268241675Suqs if (NULL != (n = hash[buc])) { 269241675Suqs for ( ; n->next; n = n->next) 270241675Suqs /* Do nothing. */ ; 271241675Suqs n->next = &roffs[i]; 272241675Suqs } else 273241675Suqs hash[buc] = &roffs[i]; 274241675Suqs } 275241675Suqs} 276241675Suqs 277241675Suqs/* 278241675Suqs * Look up a roff token by its name. Returns ROFF_MAX if no macro by 279241675Suqs * the nil-terminated string name could be found. 280241675Suqs */ 281241675Suqsstatic enum rofft 282241675Suqsroffhash_find(const char *p, size_t s) 283241675Suqs{ 284241675Suqs int buc; 285241675Suqs struct roffmac *n; 286241675Suqs 287241675Suqs /* 288241675Suqs * libroff has an extremely simple hashtable, for the time 289241675Suqs * being, which simply keys on the first character, which must 290241675Suqs * be printable, then walks a chain. It works well enough until 291241675Suqs * optimised. 292241675Suqs */ 293241675Suqs 294241675Suqs if (p[0] < ASCII_LO || p[0] > ASCII_HI) 295241675Suqs return(ROFF_MAX); 296241675Suqs 297241675Suqs buc = ROFF_HASH(p); 298241675Suqs 299241675Suqs if (NULL == (n = hash[buc])) 300241675Suqs return(ROFF_MAX); 301241675Suqs for ( ; n; n = n->next) 302241675Suqs if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s]) 303241675Suqs return((enum rofft)(n - roffs)); 304241675Suqs 305241675Suqs return(ROFF_MAX); 306241675Suqs} 307241675Suqs 308241675Suqs 309241675Suqs/* 310241675Suqs * Pop the current node off of the stack of roff instructions currently 311241675Suqs * pending. 312241675Suqs */ 313241675Suqsstatic void 314241675Suqsroffnode_pop(struct roff *r) 315241675Suqs{ 316241675Suqs struct roffnode *p; 317241675Suqs 318241675Suqs assert(r->last); 319241675Suqs p = r->last; 320241675Suqs 321241675Suqs r->last = r->last->parent; 322241675Suqs free(p->name); 323241675Suqs free(p->end); 324241675Suqs free(p); 325241675Suqs} 326241675Suqs 327241675Suqs 328241675Suqs/* 329241675Suqs * Push a roff node onto the instruction stack. This must later be 330241675Suqs * removed with roffnode_pop(). 331241675Suqs */ 332241675Suqsstatic void 333241675Suqsroffnode_push(struct roff *r, enum rofft tok, const char *name, 334241675Suqs int line, int col) 335241675Suqs{ 336241675Suqs struct roffnode *p; 337241675Suqs 338241675Suqs p = mandoc_calloc(1, sizeof(struct roffnode)); 339241675Suqs p->tok = tok; 340241675Suqs if (name) 341241675Suqs p->name = mandoc_strdup(name); 342241675Suqs p->parent = r->last; 343241675Suqs p->line = line; 344241675Suqs p->col = col; 345241675Suqs p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY; 346241675Suqs 347241675Suqs r->last = p; 348241675Suqs} 349241675Suqs 350241675Suqs 351241675Suqsstatic void 352241675Suqsroff_free1(struct roff *r) 353241675Suqs{ 354241675Suqs struct tbl_node *t; 355241675Suqs struct eqn_node *e; 356241675Suqs int i; 357241675Suqs 358241675Suqs while (NULL != (t = r->first_tbl)) { 359241675Suqs r->first_tbl = t->next; 360241675Suqs tbl_free(t); 361241675Suqs } 362241675Suqs 363241675Suqs r->first_tbl = r->last_tbl = r->tbl = NULL; 364241675Suqs 365241675Suqs while (NULL != (e = r->first_eqn)) { 366241675Suqs r->first_eqn = e->next; 367241675Suqs eqn_free(e); 368241675Suqs } 369241675Suqs 370241675Suqs r->first_eqn = r->last_eqn = r->eqn = NULL; 371241675Suqs 372241675Suqs while (r->last) 373241675Suqs roffnode_pop(r); 374241675Suqs 375241675Suqs roff_freestr(r->strtab); 376241675Suqs roff_freestr(r->xmbtab); 377241675Suqs 378241675Suqs r->strtab = r->xmbtab = NULL; 379241675Suqs 380241675Suqs if (r->xtab) 381241675Suqs for (i = 0; i < 128; i++) 382241675Suqs free(r->xtab[i].p); 383241675Suqs 384241675Suqs free(r->xtab); 385241675Suqs r->xtab = NULL; 386241675Suqs} 387241675Suqs 388241675Suqsvoid 389241675Suqsroff_reset(struct roff *r) 390241675Suqs{ 391241675Suqs int i; 392241675Suqs 393241675Suqs roff_free1(r); 394241675Suqs 395241675Suqs memset(&r->regs, 0, sizeof(struct reg) * REG__MAX); 396241675Suqs 397241675Suqs for (i = 0; i < PREDEFS_MAX; i++) 398241675Suqs roff_setstr(r, predefs[i].name, predefs[i].str, 0); 399241675Suqs} 400241675Suqs 401241675Suqs 402241675Suqsvoid 403241675Suqsroff_free(struct roff *r) 404241675Suqs{ 405241675Suqs 406241675Suqs roff_free1(r); 407241675Suqs free(r); 408241675Suqs} 409241675Suqs 410241675Suqs 411241675Suqsstruct roff * 412241675Suqsroff_alloc(struct mparse *parse) 413241675Suqs{ 414241675Suqs struct roff *r; 415241675Suqs int i; 416241675Suqs 417241675Suqs r = mandoc_calloc(1, sizeof(struct roff)); 418241675Suqs r->parse = parse; 419241675Suqs r->rstackpos = -1; 420241675Suqs 421241675Suqs roffhash_init(); 422241675Suqs 423241675Suqs for (i = 0; i < PREDEFS_MAX; i++) 424241675Suqs roff_setstr(r, predefs[i].name, predefs[i].str, 0); 425241675Suqs 426241675Suqs return(r); 427241675Suqs} 428241675Suqs 429241675Suqs/* 430241675Suqs * Pre-filter each and every line for reserved words (one beginning with 431241675Suqs * `\*', e.g., `\*(ab'). These must be handled before the actual line 432241675Suqs * is processed. 433241675Suqs * This also checks the syntax of regular escapes. 434241675Suqs */ 435241675Suqsstatic enum rofferr 436241675Suqsroff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) 437241675Suqs{ 438241675Suqs enum mandoc_esc esc; 439241675Suqs const char *stesc; /* start of an escape sequence ('\\') */ 440241675Suqs const char *stnam; /* start of the name, after "[(*" */ 441241675Suqs const char *cp; /* end of the name, e.g. before ']' */ 442241675Suqs const char *res; /* the string to be substituted */ 443241675Suqs int i, maxl, expand_count; 444241675Suqs size_t nsz; 445241675Suqs char *n; 446241675Suqs 447241675Suqs expand_count = 0; 448241675Suqs 449241675Suqsagain: 450241675Suqs cp = *bufp + pos; 451241675Suqs while (NULL != (cp = strchr(cp, '\\'))) { 452241675Suqs stesc = cp++; 453241675Suqs 454241675Suqs /* 455241675Suqs * The second character must be an asterisk. 456241675Suqs * If it isn't, skip it anyway: It is escaped, 457241675Suqs * so it can't start another escape sequence. 458241675Suqs */ 459241675Suqs 460241675Suqs if ('\0' == *cp) 461241675Suqs return(ROFF_CONT); 462241675Suqs 463241675Suqs if ('*' != *cp) { 464241675Suqs res = cp; 465241675Suqs esc = mandoc_escape(&cp, NULL, NULL); 466241675Suqs if (ESCAPE_ERROR != esc) 467241675Suqs continue; 468241675Suqs cp = res; 469241675Suqs mandoc_msg 470241675Suqs (MANDOCERR_BADESCAPE, r->parse, 471241675Suqs ln, (int)(stesc - *bufp), NULL); 472241675Suqs return(ROFF_CONT); 473241675Suqs } 474241675Suqs 475241675Suqs cp++; 476241675Suqs 477241675Suqs /* 478241675Suqs * The third character decides the length 479241675Suqs * of the name of the string. 480241675Suqs * Save a pointer to the name. 481241675Suqs */ 482241675Suqs 483241675Suqs switch (*cp) { 484241675Suqs case ('\0'): 485241675Suqs return(ROFF_CONT); 486241675Suqs case ('('): 487241675Suqs cp++; 488241675Suqs maxl = 2; 489241675Suqs break; 490241675Suqs case ('['): 491241675Suqs cp++; 492241675Suqs maxl = 0; 493241675Suqs break; 494241675Suqs default: 495241675Suqs maxl = 1; 496241675Suqs break; 497241675Suqs } 498241675Suqs stnam = cp; 499241675Suqs 500241675Suqs /* Advance to the end of the name. */ 501241675Suqs 502241675Suqs for (i = 0; 0 == maxl || i < maxl; i++, cp++) { 503241675Suqs if ('\0' == *cp) { 504241675Suqs mandoc_msg 505241675Suqs (MANDOCERR_BADESCAPE, 506241675Suqs r->parse, ln, 507241675Suqs (int)(stesc - *bufp), NULL); 508241675Suqs return(ROFF_CONT); 509241675Suqs } 510241675Suqs if (0 == maxl && ']' == *cp) 511241675Suqs break; 512241675Suqs } 513241675Suqs 514241675Suqs /* 515241675Suqs * Retrieve the replacement string; if it is 516241675Suqs * undefined, resume searching for escapes. 517241675Suqs */ 518241675Suqs 519241675Suqs res = roff_getstrn(r, stnam, (size_t)i); 520241675Suqs 521241675Suqs if (NULL == res) { 522241675Suqs mandoc_msg 523241675Suqs (MANDOCERR_BADESCAPE, r->parse, 524241675Suqs ln, (int)(stesc - *bufp), NULL); 525241675Suqs res = ""; 526241675Suqs } 527241675Suqs 528241675Suqs /* Replace the escape sequence by the string. */ 529241675Suqs 530241675Suqs pos = stesc - *bufp; 531241675Suqs 532241675Suqs nsz = *szp + strlen(res) + 1; 533241675Suqs n = mandoc_malloc(nsz); 534241675Suqs 535241675Suqs strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1)); 536241675Suqs strlcat(n, res, nsz); 537241675Suqs strlcat(n, cp + (maxl ? 0 : 1), nsz); 538241675Suqs 539241675Suqs free(*bufp); 540241675Suqs 541241675Suqs *bufp = n; 542241675Suqs *szp = nsz; 543241675Suqs 544241675Suqs if (EXPAND_LIMIT >= ++expand_count) 545241675Suqs goto again; 546241675Suqs 547241675Suqs /* Just leave the string unexpanded. */ 548241675Suqs mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL); 549241675Suqs return(ROFF_IGN); 550241675Suqs } 551241675Suqs return(ROFF_CONT); 552241675Suqs} 553241675Suqs 554241675Suqs/* 555241675Suqs * Process text streams: convert all breakable hyphens into ASCII_HYPH. 556241675Suqs */ 557241675Suqsstatic enum rofferr 558241675Suqsroff_parsetext(char *p) 559241675Suqs{ 560241675Suqs size_t sz; 561241675Suqs const char *start; 562241675Suqs enum mandoc_esc esc; 563241675Suqs 564241675Suqs start = p; 565241675Suqs 566241675Suqs while ('\0' != *p) { 567241675Suqs sz = strcspn(p, "-\\"); 568241675Suqs p += sz; 569241675Suqs 570241675Suqs if ('\0' == *p) 571241675Suqs break; 572241675Suqs 573241675Suqs if ('\\' == *p) { 574241675Suqs /* Skip over escapes. */ 575241675Suqs p++; 576241675Suqs esc = mandoc_escape 577241675Suqs ((const char **)&p, NULL, NULL); 578241675Suqs if (ESCAPE_ERROR == esc) 579241675Suqs break; 580241675Suqs continue; 581241675Suqs } else if (p == start) { 582241675Suqs p++; 583241675Suqs continue; 584241675Suqs } 585241675Suqs 586241675Suqs if (isalpha((unsigned char)p[-1]) && 587241675Suqs isalpha((unsigned char)p[1])) 588241675Suqs *p = ASCII_HYPH; 589241675Suqs p++; 590241675Suqs } 591241675Suqs 592241675Suqs return(ROFF_CONT); 593241675Suqs} 594241675Suqs 595241675Suqsenum rofferr 596241675Suqsroff_parseln(struct roff *r, int ln, char **bufp, 597241675Suqs size_t *szp, int pos, int *offs) 598241675Suqs{ 599241675Suqs enum rofft t; 600241675Suqs enum rofferr e; 601241675Suqs int ppos, ctl; 602241675Suqs 603241675Suqs /* 604241675Suqs * Run the reserved-word filter only if we have some reserved 605241675Suqs * words to fill in. 606241675Suqs */ 607241675Suqs 608241675Suqs e = roff_res(r, bufp, szp, ln, pos); 609241675Suqs if (ROFF_IGN == e) 610241675Suqs return(e); 611241675Suqs assert(ROFF_CONT == e); 612241675Suqs 613241675Suqs ppos = pos; 614241675Suqs ctl = mandoc_getcontrol(*bufp, &pos); 615241675Suqs 616241675Suqs /* 617241675Suqs * First, if a scope is open and we're not a macro, pass the 618241675Suqs * text through the macro's filter. If a scope isn't open and 619241675Suqs * we're not a macro, just let it through. 620241675Suqs * Finally, if there's an equation scope open, divert it into it 621241675Suqs * no matter our state. 622241675Suqs */ 623241675Suqs 624241675Suqs if (r->last && ! ctl) { 625241675Suqs t = r->last->tok; 626241675Suqs assert(roffs[t].text); 627241675Suqs e = (*roffs[t].text) 628241675Suqs (r, t, bufp, szp, ln, pos, pos, offs); 629241675Suqs assert(ROFF_IGN == e || ROFF_CONT == e); 630241675Suqs if (ROFF_CONT != e) 631241675Suqs return(e); 632241675Suqs if (r->eqn) 633241675Suqs return(eqn_read(&r->eqn, ln, *bufp, pos, offs)); 634241675Suqs if (r->tbl) 635241675Suqs return(tbl_read(r->tbl, ln, *bufp, pos)); 636241675Suqs return(roff_parsetext(*bufp + pos)); 637241675Suqs } else if ( ! ctl) { 638241675Suqs if (r->eqn) 639241675Suqs return(eqn_read(&r->eqn, ln, *bufp, pos, offs)); 640241675Suqs if (r->tbl) 641241675Suqs return(tbl_read(r->tbl, ln, *bufp, pos)); 642241675Suqs return(roff_parsetext(*bufp + pos)); 643241675Suqs } else if (r->eqn) 644241675Suqs return(eqn_read(&r->eqn, ln, *bufp, ppos, offs)); 645241675Suqs 646241675Suqs /* 647241675Suqs * If a scope is open, go to the child handler for that macro, 648241675Suqs * as it may want to preprocess before doing anything with it. 649241675Suqs * Don't do so if an equation is open. 650241675Suqs */ 651241675Suqs 652241675Suqs if (r->last) { 653241675Suqs t = r->last->tok; 654241675Suqs assert(roffs[t].sub); 655241675Suqs return((*roffs[t].sub) 656241675Suqs (r, t, bufp, szp, 657241675Suqs ln, ppos, pos, offs)); 658241675Suqs } 659241675Suqs 660241675Suqs /* 661241675Suqs * Lastly, as we've no scope open, try to look up and execute 662241675Suqs * the new macro. If no macro is found, simply return and let 663241675Suqs * the compilers handle it. 664241675Suqs */ 665241675Suqs 666241675Suqs if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) 667241675Suqs return(ROFF_CONT); 668241675Suqs 669241675Suqs assert(roffs[t].proc); 670241675Suqs return((*roffs[t].proc) 671241675Suqs (r, t, bufp, szp, 672241675Suqs ln, ppos, pos, offs)); 673241675Suqs} 674241675Suqs 675241675Suqs 676241675Suqsvoid 677241675Suqsroff_endparse(struct roff *r) 678241675Suqs{ 679241675Suqs 680241675Suqs if (r->last) 681241675Suqs mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse, 682241675Suqs r->last->line, r->last->col, NULL); 683241675Suqs 684241675Suqs if (r->eqn) { 685241675Suqs mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse, 686241675Suqs r->eqn->eqn.ln, r->eqn->eqn.pos, NULL); 687241675Suqs eqn_end(&r->eqn); 688241675Suqs } 689241675Suqs 690241675Suqs if (r->tbl) { 691241675Suqs mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse, 692241675Suqs r->tbl->line, r->tbl->pos, NULL); 693241675Suqs tbl_end(&r->tbl); 694241675Suqs } 695241675Suqs} 696241675Suqs 697241675Suqs/* 698241675Suqs * Parse a roff node's type from the input buffer. This must be in the 699241675Suqs * form of ".foo xxx" in the usual way. 700241675Suqs */ 701241675Suqsstatic enum rofft 702241675Suqsroff_parse(struct roff *r, const char *buf, int *pos) 703241675Suqs{ 704241675Suqs const char *mac; 705241675Suqs size_t maclen; 706241675Suqs enum rofft t; 707241675Suqs 708241675Suqs if ('\0' == buf[*pos] || '"' == buf[*pos] || 709241675Suqs '\t' == buf[*pos] || ' ' == buf[*pos]) 710241675Suqs return(ROFF_MAX); 711241675Suqs 712241675Suqs /* 713241675Suqs * We stop the macro parse at an escape, tab, space, or nil. 714241675Suqs * However, `\}' is also a valid macro, so make sure we don't 715241675Suqs * clobber it by seeing the `\' as the end of token. 716241675Suqs */ 717241675Suqs 718241675Suqs mac = buf + *pos; 719241675Suqs maclen = strcspn(mac + 1, " \\\t\0") + 1; 720241675Suqs 721241675Suqs t = (r->current_string = roff_getstrn(r, mac, maclen)) 722241675Suqs ? ROFF_USERDEF : roffhash_find(mac, maclen); 723241675Suqs 724241675Suqs *pos += (int)maclen; 725241675Suqs 726241675Suqs while (buf[*pos] && ' ' == buf[*pos]) 727241675Suqs (*pos)++; 728241675Suqs 729241675Suqs return(t); 730241675Suqs} 731241675Suqs 732241675Suqs/* ARGSUSED */ 733241675Suqsstatic enum rofferr 734241675Suqsroff_cblock(ROFF_ARGS) 735241675Suqs{ 736241675Suqs 737241675Suqs /* 738241675Suqs * A block-close `..' should only be invoked as a child of an 739241675Suqs * ignore macro, otherwise raise a warning and just ignore it. 740241675Suqs */ 741241675Suqs 742241675Suqs if (NULL == r->last) { 743241675Suqs mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL); 744241675Suqs return(ROFF_IGN); 745241675Suqs } 746241675Suqs 747241675Suqs switch (r->last->tok) { 748241675Suqs case (ROFF_am): 749241675Suqs /* FALLTHROUGH */ 750241675Suqs case (ROFF_ami): 751241675Suqs /* FALLTHROUGH */ 752241675Suqs case (ROFF_am1): 753241675Suqs /* FALLTHROUGH */ 754241675Suqs case (ROFF_de): 755241675Suqs /* ROFF_de1 is remapped to ROFF_de in roff_block(). */ 756241675Suqs /* FALLTHROUGH */ 757241675Suqs case (ROFF_dei): 758241675Suqs /* FALLTHROUGH */ 759241675Suqs case (ROFF_ig): 760241675Suqs break; 761241675Suqs default: 762241675Suqs mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL); 763241675Suqs return(ROFF_IGN); 764241675Suqs } 765241675Suqs 766241675Suqs if ((*bufp)[pos]) 767241675Suqs mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL); 768241675Suqs 769241675Suqs roffnode_pop(r); 770241675Suqs roffnode_cleanscope(r); 771241675Suqs return(ROFF_IGN); 772241675Suqs 773241675Suqs} 774241675Suqs 775241675Suqs 776241675Suqsstatic void 777241675Suqsroffnode_cleanscope(struct roff *r) 778241675Suqs{ 779241675Suqs 780241675Suqs while (r->last) { 781241675Suqs if (--r->last->endspan < 0) 782241675Suqs break; 783241675Suqs roffnode_pop(r); 784241675Suqs } 785241675Suqs} 786241675Suqs 787241675Suqs 788241675Suqs/* ARGSUSED */ 789241675Suqsstatic enum rofferr 790241675Suqsroff_ccond(ROFF_ARGS) 791241675Suqs{ 792241675Suqs 793241675Suqs if (NULL == r->last) { 794241675Suqs mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL); 795241675Suqs return(ROFF_IGN); 796241675Suqs } 797241675Suqs 798241675Suqs switch (r->last->tok) { 799241675Suqs case (ROFF_el): 800241675Suqs /* FALLTHROUGH */ 801241675Suqs case (ROFF_ie): 802241675Suqs /* FALLTHROUGH */ 803241675Suqs case (ROFF_if): 804241675Suqs break; 805241675Suqs default: 806241675Suqs mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL); 807241675Suqs return(ROFF_IGN); 808241675Suqs } 809241675Suqs 810241675Suqs if (r->last->endspan > -1) { 811241675Suqs mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL); 812241675Suqs return(ROFF_IGN); 813241675Suqs } 814241675Suqs 815241675Suqs if ((*bufp)[pos]) 816241675Suqs mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL); 817241675Suqs 818241675Suqs roffnode_pop(r); 819241675Suqs roffnode_cleanscope(r); 820241675Suqs return(ROFF_IGN); 821241675Suqs} 822241675Suqs 823241675Suqs 824241675Suqs/* ARGSUSED */ 825241675Suqsstatic enum rofferr 826241675Suqsroff_block(ROFF_ARGS) 827241675Suqs{ 828241675Suqs int sv; 829241675Suqs size_t sz; 830241675Suqs char *name; 831241675Suqs 832241675Suqs name = NULL; 833241675Suqs 834241675Suqs if (ROFF_ig != tok) { 835241675Suqs if ('\0' == (*bufp)[pos]) { 836241675Suqs mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL); 837241675Suqs return(ROFF_IGN); 838241675Suqs } 839241675Suqs 840241675Suqs /* 841241675Suqs * Re-write `de1', since we don't really care about 842241675Suqs * groff's strange compatibility mode, into `de'. 843241675Suqs */ 844241675Suqs 845241675Suqs if (ROFF_de1 == tok) 846241675Suqs tok = ROFF_de; 847241675Suqs if (ROFF_de == tok) 848241675Suqs name = *bufp + pos; 849241675Suqs else 850241675Suqs mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos, 851241675Suqs roffs[tok].name); 852241675Suqs 853241675Suqs while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos])) 854241675Suqs pos++; 855241675Suqs 856241675Suqs while (isspace((unsigned char)(*bufp)[pos])) 857241675Suqs (*bufp)[pos++] = '\0'; 858241675Suqs } 859241675Suqs 860241675Suqs roffnode_push(r, tok, name, ln, ppos); 861241675Suqs 862241675Suqs /* 863241675Suqs * At the beginning of a `de' macro, clear the existing string 864241675Suqs * with the same name, if there is one. New content will be 865241675Suqs * added from roff_block_text() in multiline mode. 866241675Suqs */ 867241675Suqs 868241675Suqs if (ROFF_de == tok) 869241675Suqs roff_setstr(r, name, "", 0); 870241675Suqs 871241675Suqs if ('\0' == (*bufp)[pos]) 872241675Suqs return(ROFF_IGN); 873241675Suqs 874241675Suqs /* If present, process the custom end-of-line marker. */ 875241675Suqs 876241675Suqs sv = pos; 877241675Suqs while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos])) 878241675Suqs pos++; 879241675Suqs 880241675Suqs /* 881241675Suqs * Note: groff does NOT like escape characters in the input. 882241675Suqs * Instead of detecting this, we're just going to let it fly and 883241675Suqs * to hell with it. 884241675Suqs */ 885241675Suqs 886241675Suqs assert(pos > sv); 887241675Suqs sz = (size_t)(pos - sv); 888241675Suqs 889241675Suqs if (1 == sz && '.' == (*bufp)[sv]) 890241675Suqs return(ROFF_IGN); 891241675Suqs 892241675Suqs r->last->end = mandoc_malloc(sz + 1); 893241675Suqs 894241675Suqs memcpy(r->last->end, *bufp + sv, sz); 895241675Suqs r->last->end[(int)sz] = '\0'; 896241675Suqs 897241675Suqs if ((*bufp)[pos]) 898241675Suqs mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL); 899241675Suqs 900241675Suqs return(ROFF_IGN); 901241675Suqs} 902241675Suqs 903241675Suqs 904241675Suqs/* ARGSUSED */ 905241675Suqsstatic enum rofferr 906241675Suqsroff_block_sub(ROFF_ARGS) 907241675Suqs{ 908241675Suqs enum rofft t; 909241675Suqs int i, j; 910241675Suqs 911241675Suqs /* 912241675Suqs * First check whether a custom macro exists at this level. If 913241675Suqs * it does, then check against it. This is some of groff's 914241675Suqs * stranger behaviours. If we encountered a custom end-scope 915241675Suqs * tag and that tag also happens to be a "real" macro, then we 916241675Suqs * need to try interpreting it again as a real macro. If it's 917241675Suqs * not, then return ignore. Else continue. 918241675Suqs */ 919241675Suqs 920241675Suqs if (r->last->end) { 921241675Suqs for (i = pos, j = 0; r->last->end[j]; j++, i++) 922241675Suqs if ((*bufp)[i] != r->last->end[j]) 923241675Suqs break; 924241675Suqs 925241675Suqs if ('\0' == r->last->end[j] && 926241675Suqs ('\0' == (*bufp)[i] || 927241675Suqs ' ' == (*bufp)[i] || 928241675Suqs '\t' == (*bufp)[i])) { 929241675Suqs roffnode_pop(r); 930241675Suqs roffnode_cleanscope(r); 931241675Suqs 932241675Suqs while (' ' == (*bufp)[i] || '\t' == (*bufp)[i]) 933241675Suqs i++; 934241675Suqs 935241675Suqs pos = i; 936241675Suqs if (ROFF_MAX != roff_parse(r, *bufp, &pos)) 937241675Suqs return(ROFF_RERUN); 938241675Suqs return(ROFF_IGN); 939241675Suqs } 940241675Suqs } 941241675Suqs 942241675Suqs /* 943241675Suqs * If we have no custom end-query or lookup failed, then try 944241675Suqs * pulling it out of the hashtable. 945241675Suqs */ 946241675Suqs 947241675Suqs t = roff_parse(r, *bufp, &pos); 948241675Suqs 949241675Suqs /* 950241675Suqs * Macros other than block-end are only significant 951241675Suqs * in `de' blocks; elsewhere, simply throw them away. 952241675Suqs */ 953241675Suqs if (ROFF_cblock != t) { 954241675Suqs if (ROFF_de == tok) 955241675Suqs roff_setstr(r, r->last->name, *bufp + ppos, 1); 956241675Suqs return(ROFF_IGN); 957241675Suqs } 958241675Suqs 959241675Suqs assert(roffs[t].proc); 960241675Suqs return((*roffs[t].proc)(r, t, bufp, szp, 961241675Suqs ln, ppos, pos, offs)); 962241675Suqs} 963241675Suqs 964241675Suqs 965241675Suqs/* ARGSUSED */ 966241675Suqsstatic enum rofferr 967241675Suqsroff_block_text(ROFF_ARGS) 968241675Suqs{ 969241675Suqs 970241675Suqs if (ROFF_de == tok) 971241675Suqs roff_setstr(r, r->last->name, *bufp + pos, 1); 972241675Suqs 973241675Suqs return(ROFF_IGN); 974241675Suqs} 975241675Suqs 976241675Suqs 977241675Suqs/* ARGSUSED */ 978241675Suqsstatic enum rofferr 979241675Suqsroff_cond_sub(ROFF_ARGS) 980241675Suqs{ 981241675Suqs enum rofft t; 982241675Suqs enum roffrule rr; 983241675Suqs char *ep; 984241675Suqs 985241675Suqs rr = r->last->rule; 986241675Suqs roffnode_cleanscope(r); 987241675Suqs 988241675Suqs /* 989241675Suqs * If the macro is unknown, first check if it contains a closing 990241675Suqs * delimiter `\}'. If it does, close out our scope and return 991241675Suqs * the currently-scoped rule (ignore or continue). Else, drop 992241675Suqs * into the currently-scoped rule. 993241675Suqs */ 994241675Suqs 995241675Suqs if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) { 996241675Suqs ep = &(*bufp)[pos]; 997241675Suqs for ( ; NULL != (ep = strchr(ep, '\\')); ep++) { 998241675Suqs ep++; 999241675Suqs if ('}' != *ep) 1000241675Suqs continue; 1001241675Suqs 1002241675Suqs /* 1003241675Suqs * Make the \} go away. 1004241675Suqs * This is a little haphazard, as it's not quite 1005241675Suqs * clear how nroff does this. 1006241675Suqs * If we're at the end of line, then just chop 1007241675Suqs * off the \} and resize the buffer. 1008241675Suqs * If we aren't, then conver it to spaces. 1009241675Suqs */ 1010241675Suqs 1011241675Suqs if ('\0' == *(ep + 1)) { 1012241675Suqs *--ep = '\0'; 1013241675Suqs *szp -= 2; 1014241675Suqs } else 1015241675Suqs *(ep - 1) = *ep = ' '; 1016241675Suqs 1017241675Suqs roff_ccond(r, ROFF_ccond, bufp, szp, 1018241675Suqs ln, pos, pos + 2, offs); 1019241675Suqs break; 1020241675Suqs } 1021241675Suqs return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); 1022241675Suqs } 1023241675Suqs 1024241675Suqs /* 1025241675Suqs * A denied conditional must evaluate its children if and only 1026241675Suqs * if they're either structurally required (such as loops and 1027241675Suqs * conditionals) or a closing macro. 1028241675Suqs */ 1029241675Suqs 1030241675Suqs if (ROFFRULE_DENY == rr) 1031241675Suqs if ( ! (ROFFMAC_STRUCT & roffs[t].flags)) 1032241675Suqs if (ROFF_ccond != t) 1033241675Suqs return(ROFF_IGN); 1034241675Suqs 1035241675Suqs assert(roffs[t].proc); 1036241675Suqs return((*roffs[t].proc)(r, t, bufp, szp, 1037241675Suqs ln, ppos, pos, offs)); 1038241675Suqs} 1039241675Suqs 1040241675Suqs/* ARGSUSED */ 1041241675Suqsstatic enum rofferr 1042241675Suqsroff_cond_text(ROFF_ARGS) 1043241675Suqs{ 1044241675Suqs char *ep; 1045241675Suqs enum roffrule rr; 1046241675Suqs 1047241675Suqs rr = r->last->rule; 1048241675Suqs roffnode_cleanscope(r); 1049241675Suqs 1050241675Suqs ep = &(*bufp)[pos]; 1051241675Suqs for ( ; NULL != (ep = strchr(ep, '\\')); ep++) { 1052241675Suqs ep++; 1053241675Suqs if ('}' != *ep) 1054241675Suqs continue; 1055241675Suqs *ep = '&'; 1056241675Suqs roff_ccond(r, ROFF_ccond, bufp, szp, 1057241675Suqs ln, pos, pos + 2, offs); 1058241675Suqs } 1059241675Suqs return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); 1060241675Suqs} 1061241675Suqs 1062241675Suqsstatic enum roffrule 1063241675Suqsroff_evalcond(const char *v, int *pos) 1064241675Suqs{ 1065241675Suqs 1066241675Suqs switch (v[*pos]) { 1067241675Suqs case ('n'): 1068241675Suqs (*pos)++; 1069241675Suqs return(ROFFRULE_ALLOW); 1070241675Suqs case ('e'): 1071241675Suqs /* FALLTHROUGH */ 1072241675Suqs case ('o'): 1073241675Suqs /* FALLTHROUGH */ 1074241675Suqs case ('t'): 1075241675Suqs (*pos)++; 1076241675Suqs return(ROFFRULE_DENY); 1077241675Suqs default: 1078241675Suqs break; 1079241675Suqs } 1080241675Suqs 1081241675Suqs while (v[*pos] && ' ' != v[*pos]) 1082241675Suqs (*pos)++; 1083241675Suqs return(ROFFRULE_DENY); 1084241675Suqs} 1085241675Suqs 1086241675Suqs/* ARGSUSED */ 1087241675Suqsstatic enum rofferr 1088241675Suqsroff_line_ignore(ROFF_ARGS) 1089241675Suqs{ 1090241675Suqs 1091241675Suqs if (ROFF_it == tok) 1092241675Suqs mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos, "it"); 1093241675Suqs 1094241675Suqs return(ROFF_IGN); 1095241675Suqs} 1096241675Suqs 1097241675Suqs/* ARGSUSED */ 1098241675Suqsstatic enum rofferr 1099241675Suqsroff_cond(ROFF_ARGS) 1100241675Suqs{ 1101241675Suqs int sv; 1102241675Suqs enum roffrule rule; 1103241675Suqs 1104241675Suqs /* 1105241675Suqs * An `.el' has no conditional body: it will consume the value 1106241675Suqs * of the current rstack entry set in prior `ie' calls or 1107241675Suqs * defaults to DENY. 1108241675Suqs * 1109241675Suqs * If we're not an `el', however, then evaluate the conditional. 1110241675Suqs */ 1111241675Suqs 1112241675Suqs rule = ROFF_el == tok ? 1113241675Suqs (r->rstackpos < 0 ? 1114241675Suqs ROFFRULE_DENY : r->rstack[r->rstackpos--]) : 1115241675Suqs roff_evalcond(*bufp, &pos); 1116241675Suqs 1117241675Suqs sv = pos; 1118241675Suqs while (' ' == (*bufp)[pos]) 1119241675Suqs pos++; 1120241675Suqs 1121241675Suqs /* 1122241675Suqs * Roff is weird. If we have just white-space after the 1123241675Suqs * conditional, it's considered the BODY and we exit without 1124241675Suqs * really doing anything. Warn about this. It's probably 1125241675Suqs * wrong. 1126241675Suqs */ 1127241675Suqs 1128241675Suqs if ('\0' == (*bufp)[pos] && sv != pos) { 1129241675Suqs mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL); 1130241675Suqs return(ROFF_IGN); 1131241675Suqs } 1132241675Suqs 1133241675Suqs roffnode_push(r, tok, NULL, ln, ppos); 1134241675Suqs 1135241675Suqs r->last->rule = rule; 1136241675Suqs 1137241675Suqs /* 1138241675Suqs * An if-else will put the NEGATION of the current evaluated 1139241675Suqs * conditional into the stack of rules. 1140241675Suqs */ 1141241675Suqs 1142241675Suqs if (ROFF_ie == tok) { 1143241675Suqs if (r->rstackpos == RSTACK_MAX - 1) { 1144241675Suqs mandoc_msg(MANDOCERR_MEM, 1145241675Suqs r->parse, ln, ppos, NULL); 1146241675Suqs return(ROFF_ERR); 1147241675Suqs } 1148241675Suqs r->rstack[++r->rstackpos] = 1149241675Suqs ROFFRULE_DENY == r->last->rule ? 1150241675Suqs ROFFRULE_ALLOW : ROFFRULE_DENY; 1151241675Suqs } 1152241675Suqs 1153241675Suqs /* If the parent has false as its rule, then so do we. */ 1154241675Suqs 1155241675Suqs if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule) 1156241675Suqs r->last->rule = ROFFRULE_DENY; 1157241675Suqs 1158241675Suqs /* 1159241675Suqs * Determine scope. If we're invoked with "\{" trailing the 1160241675Suqs * conditional, then we're in a multiline scope. Else our scope 1161241675Suqs * expires on the next line. 1162241675Suqs */ 1163241675Suqs 1164241675Suqs r->last->endspan = 1; 1165241675Suqs 1166241675Suqs if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) { 1167241675Suqs r->last->endspan = -1; 1168241675Suqs pos += 2; 1169241675Suqs } 1170241675Suqs 1171241675Suqs /* 1172241675Suqs * If there are no arguments on the line, the next-line scope is 1173241675Suqs * assumed. 1174241675Suqs */ 1175241675Suqs 1176241675Suqs if ('\0' == (*bufp)[pos]) 1177241675Suqs return(ROFF_IGN); 1178241675Suqs 1179241675Suqs /* Otherwise re-run the roff parser after recalculating. */ 1180241675Suqs 1181241675Suqs *offs = pos; 1182241675Suqs return(ROFF_RERUN); 1183241675Suqs} 1184241675Suqs 1185241675Suqs 1186241675Suqs/* ARGSUSED */ 1187241675Suqsstatic enum rofferr 1188241675Suqsroff_ds(ROFF_ARGS) 1189241675Suqs{ 1190241675Suqs char *name, *string; 1191241675Suqs 1192241675Suqs /* 1193241675Suqs * A symbol is named by the first word following the macro 1194241675Suqs * invocation up to a space. Its value is anything after the 1195241675Suqs * name's trailing whitespace and optional double-quote. Thus, 1196241675Suqs * 1197241675Suqs * [.ds foo "bar " ] 1198241675Suqs * 1199241675Suqs * will have `bar " ' as its value. 1200241675Suqs */ 1201241675Suqs 1202241675Suqs string = *bufp + pos; 1203241675Suqs name = roff_getname(r, &string, ln, pos); 1204241675Suqs if ('\0' == *name) 1205241675Suqs return(ROFF_IGN); 1206241675Suqs 1207241675Suqs /* Read past initial double-quote. */ 1208241675Suqs if ('"' == *string) 1209241675Suqs string++; 1210241675Suqs 1211241675Suqs /* The rest is the value. */ 1212241675Suqs roff_setstr(r, name, string, 0); 1213241675Suqs return(ROFF_IGN); 1214241675Suqs} 1215241675Suqs 1216241675Suqsint 1217241675Suqsroff_regisset(const struct roff *r, enum regs reg) 1218241675Suqs{ 1219241675Suqs 1220241675Suqs return(r->regs[(int)reg].set); 1221241675Suqs} 1222241675Suqs 1223241675Suqsunsigned int 1224241675Suqsroff_regget(const struct roff *r, enum regs reg) 1225241675Suqs{ 1226241675Suqs 1227241675Suqs return(r->regs[(int)reg].u); 1228241675Suqs} 1229241675Suqs 1230241675Suqsvoid 1231241675Suqsroff_regunset(struct roff *r, enum regs reg) 1232241675Suqs{ 1233241675Suqs 1234241675Suqs r->regs[(int)reg].set = 0; 1235241675Suqs} 1236241675Suqs 1237241675Suqs/* ARGSUSED */ 1238241675Suqsstatic enum rofferr 1239241675Suqsroff_nr(ROFF_ARGS) 1240241675Suqs{ 1241241675Suqs const char *key; 1242241675Suqs char *val; 1243241675Suqs int iv; 1244241675Suqs 1245241675Suqs val = *bufp + pos; 1246241675Suqs key = roff_getname(r, &val, ln, pos); 1247241675Suqs 1248241675Suqs if (0 == strcmp(key, "nS")) { 1249241675Suqs r->regs[(int)REG_nS].set = 1; 1250241675Suqs if ((iv = mandoc_strntoi(val, strlen(val), 10)) >= 0) 1251241675Suqs r->regs[(int)REG_nS].u = (unsigned)iv; 1252241675Suqs else 1253241675Suqs r->regs[(int)REG_nS].u = 0u; 1254241675Suqs } 1255241675Suqs 1256241675Suqs return(ROFF_IGN); 1257241675Suqs} 1258241675Suqs 1259241675Suqs/* ARGSUSED */ 1260241675Suqsstatic enum rofferr 1261241675Suqsroff_rm(ROFF_ARGS) 1262241675Suqs{ 1263241675Suqs const char *name; 1264241675Suqs char *cp; 1265241675Suqs 1266241675Suqs cp = *bufp + pos; 1267241675Suqs while ('\0' != *cp) { 1268241675Suqs name = roff_getname(r, &cp, ln, (int)(cp - *bufp)); 1269241675Suqs if ('\0' != *name) 1270241675Suqs roff_setstr(r, name, NULL, 0); 1271241675Suqs } 1272241675Suqs return(ROFF_IGN); 1273241675Suqs} 1274241675Suqs 1275241675Suqs/* ARGSUSED */ 1276241675Suqsstatic enum rofferr 1277241675Suqsroff_TE(ROFF_ARGS) 1278241675Suqs{ 1279241675Suqs 1280241675Suqs if (NULL == r->tbl) 1281241675Suqs mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL); 1282241675Suqs else 1283241675Suqs tbl_end(&r->tbl); 1284241675Suqs 1285241675Suqs return(ROFF_IGN); 1286241675Suqs} 1287241675Suqs 1288241675Suqs/* ARGSUSED */ 1289241675Suqsstatic enum rofferr 1290241675Suqsroff_T_(ROFF_ARGS) 1291241675Suqs{ 1292241675Suqs 1293241675Suqs if (NULL == r->tbl) 1294241675Suqs mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL); 1295241675Suqs else 1296241675Suqs tbl_restart(ppos, ln, r->tbl); 1297241675Suqs 1298241675Suqs return(ROFF_IGN); 1299241675Suqs} 1300241675Suqs 1301241675Suqs#if 0 1302241675Suqsstatic int 1303241675Suqsroff_closeeqn(struct roff *r) 1304241675Suqs{ 1305241675Suqs 1306241675Suqs return(r->eqn && ROFF_EQN == eqn_end(&r->eqn) ? 1 : 0); 1307241675Suqs} 1308241675Suqs#endif 1309241675Suqs 1310241675Suqsstatic void 1311241675Suqsroff_openeqn(struct roff *r, const char *name, int line, 1312241675Suqs int offs, const char *buf) 1313241675Suqs{ 1314241675Suqs struct eqn_node *e; 1315241675Suqs int poff; 1316241675Suqs 1317241675Suqs assert(NULL == r->eqn); 1318241675Suqs e = eqn_alloc(name, offs, line, r->parse); 1319241675Suqs 1320241675Suqs if (r->last_eqn) 1321241675Suqs r->last_eqn->next = e; 1322241675Suqs else 1323241675Suqs r->first_eqn = r->last_eqn = e; 1324241675Suqs 1325241675Suqs r->eqn = r->last_eqn = e; 1326241675Suqs 1327241675Suqs if (buf) { 1328241675Suqs poff = 0; 1329241675Suqs eqn_read(&r->eqn, line, buf, offs, &poff); 1330241675Suqs } 1331241675Suqs} 1332241675Suqs 1333241675Suqs/* ARGSUSED */ 1334241675Suqsstatic enum rofferr 1335241675Suqsroff_EQ(ROFF_ARGS) 1336241675Suqs{ 1337241675Suqs 1338241675Suqs roff_openeqn(r, *bufp + pos, ln, ppos, NULL); 1339241675Suqs return(ROFF_IGN); 1340241675Suqs} 1341241675Suqs 1342241675Suqs/* ARGSUSED */ 1343241675Suqsstatic enum rofferr 1344241675Suqsroff_EN(ROFF_ARGS) 1345241675Suqs{ 1346241675Suqs 1347241675Suqs mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL); 1348241675Suqs return(ROFF_IGN); 1349241675Suqs} 1350241675Suqs 1351241675Suqs/* ARGSUSED */ 1352241675Suqsstatic enum rofferr 1353241675Suqsroff_TS(ROFF_ARGS) 1354241675Suqs{ 1355241675Suqs struct tbl_node *t; 1356241675Suqs 1357241675Suqs if (r->tbl) { 1358241675Suqs mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL); 1359241675Suqs tbl_end(&r->tbl); 1360241675Suqs } 1361241675Suqs 1362241675Suqs t = tbl_alloc(ppos, ln, r->parse); 1363241675Suqs 1364241675Suqs if (r->last_tbl) 1365241675Suqs r->last_tbl->next = t; 1366241675Suqs else 1367241675Suqs r->first_tbl = r->last_tbl = t; 1368241675Suqs 1369241675Suqs r->tbl = r->last_tbl = t; 1370241675Suqs return(ROFF_IGN); 1371241675Suqs} 1372241675Suqs 1373241675Suqs/* ARGSUSED */ 1374241675Suqsstatic enum rofferr 1375241675Suqsroff_tr(ROFF_ARGS) 1376241675Suqs{ 1377241675Suqs const char *p, *first, *second; 1378241675Suqs size_t fsz, ssz; 1379241675Suqs enum mandoc_esc esc; 1380241675Suqs 1381241675Suqs p = *bufp + pos; 1382241675Suqs 1383241675Suqs if ('\0' == *p) { 1384241675Suqs mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL); 1385241675Suqs return(ROFF_IGN); 1386241675Suqs } 1387241675Suqs 1388241675Suqs while ('\0' != *p) { 1389241675Suqs fsz = ssz = 1; 1390241675Suqs 1391241675Suqs first = p++; 1392241675Suqs if ('\\' == *first) { 1393241675Suqs esc = mandoc_escape(&p, NULL, NULL); 1394241675Suqs if (ESCAPE_ERROR == esc) { 1395241675Suqs mandoc_msg 1396241675Suqs (MANDOCERR_BADESCAPE, r->parse, 1397241675Suqs ln, (int)(p - *bufp), NULL); 1398241675Suqs return(ROFF_IGN); 1399241675Suqs } 1400241675Suqs fsz = (size_t)(p - first); 1401241675Suqs } 1402241675Suqs 1403241675Suqs second = p++; 1404241675Suqs if ('\\' == *second) { 1405241675Suqs esc = mandoc_escape(&p, NULL, NULL); 1406241675Suqs if (ESCAPE_ERROR == esc) { 1407241675Suqs mandoc_msg 1408241675Suqs (MANDOCERR_BADESCAPE, r->parse, 1409241675Suqs ln, (int)(p - *bufp), NULL); 1410241675Suqs return(ROFF_IGN); 1411241675Suqs } 1412241675Suqs ssz = (size_t)(p - second); 1413241675Suqs } else if ('\0' == *second) { 1414241675Suqs mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, 1415241675Suqs ln, (int)(p - *bufp), NULL); 1416241675Suqs second = " "; 1417241675Suqs p--; 1418241675Suqs } 1419241675Suqs 1420241675Suqs if (fsz > 1) { 1421241675Suqs roff_setstrn(&r->xmbtab, first, 1422241675Suqs fsz, second, ssz, 0); 1423241675Suqs continue; 1424241675Suqs } 1425241675Suqs 1426241675Suqs if (NULL == r->xtab) 1427241675Suqs r->xtab = mandoc_calloc 1428241675Suqs (128, sizeof(struct roffstr)); 1429241675Suqs 1430241675Suqs free(r->xtab[(int)*first].p); 1431241675Suqs r->xtab[(int)*first].p = mandoc_strndup(second, ssz); 1432241675Suqs r->xtab[(int)*first].sz = ssz; 1433241675Suqs } 1434241675Suqs 1435241675Suqs return(ROFF_IGN); 1436241675Suqs} 1437241675Suqs 1438241675Suqs/* ARGSUSED */ 1439241675Suqsstatic enum rofferr 1440241675Suqsroff_so(ROFF_ARGS) 1441241675Suqs{ 1442241675Suqs char *name; 1443241675Suqs 1444241675Suqs mandoc_msg(MANDOCERR_SO, r->parse, ln, ppos, NULL); 1445241675Suqs 1446241675Suqs /* 1447241675Suqs * Handle `so'. Be EXTREMELY careful, as we shouldn't be 1448241675Suqs * opening anything that's not in our cwd or anything beneath 1449241675Suqs * it. Thus, explicitly disallow traversing up the file-system 1450241675Suqs * or using absolute paths. 1451241675Suqs */ 1452241675Suqs 1453241675Suqs name = *bufp + pos; 1454241675Suqs if ('/' == *name || strstr(name, "../") || strstr(name, "/..")) { 1455241675Suqs mandoc_msg(MANDOCERR_SOPATH, r->parse, ln, pos, NULL); 1456241675Suqs return(ROFF_ERR); 1457241675Suqs } 1458241675Suqs 1459241675Suqs *offs = pos; 1460241675Suqs return(ROFF_SO); 1461241675Suqs} 1462241675Suqs 1463241675Suqs/* ARGSUSED */ 1464241675Suqsstatic enum rofferr 1465241675Suqsroff_userdef(ROFF_ARGS) 1466241675Suqs{ 1467241675Suqs const char *arg[9]; 1468241675Suqs char *cp, *n1, *n2; 1469241675Suqs int i; 1470241675Suqs 1471241675Suqs /* 1472241675Suqs * Collect pointers to macro argument strings 1473241675Suqs * and null-terminate them. 1474241675Suqs */ 1475241675Suqs cp = *bufp + pos; 1476241675Suqs for (i = 0; i < 9; i++) 1477241675Suqs arg[i] = '\0' == *cp ? "" : 1478241675Suqs mandoc_getarg(r->parse, &cp, ln, &pos); 1479241675Suqs 1480241675Suqs /* 1481241675Suqs * Expand macro arguments. 1482241675Suqs */ 1483241675Suqs *szp = 0; 1484241675Suqs n1 = cp = mandoc_strdup(r->current_string); 1485241675Suqs while (NULL != (cp = strstr(cp, "\\$"))) { 1486241675Suqs i = cp[2] - '1'; 1487241675Suqs if (0 > i || 8 < i) { 1488241675Suqs /* Not an argument invocation. */ 1489241675Suqs cp += 2; 1490241675Suqs continue; 1491241675Suqs } 1492241675Suqs 1493241675Suqs *szp = strlen(n1) - 3 + strlen(arg[i]) + 1; 1494241675Suqs n2 = mandoc_malloc(*szp); 1495241675Suqs 1496241675Suqs strlcpy(n2, n1, (size_t)(cp - n1 + 1)); 1497241675Suqs strlcat(n2, arg[i], *szp); 1498241675Suqs strlcat(n2, cp + 3, *szp); 1499241675Suqs 1500241675Suqs cp = n2 + (cp - n1); 1501241675Suqs free(n1); 1502241675Suqs n1 = n2; 1503241675Suqs } 1504241675Suqs 1505241675Suqs /* 1506241675Suqs * Replace the macro invocation 1507241675Suqs * by the expanded macro. 1508241675Suqs */ 1509241675Suqs free(*bufp); 1510241675Suqs *bufp = n1; 1511241675Suqs if (0 == *szp) 1512241675Suqs *szp = strlen(*bufp) + 1; 1513241675Suqs 1514241675Suqs return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ? 1515241675Suqs ROFF_REPARSE : ROFF_APPEND); 1516241675Suqs} 1517241675Suqs 1518241675Suqsstatic char * 1519241675Suqsroff_getname(struct roff *r, char **cpp, int ln, int pos) 1520241675Suqs{ 1521241675Suqs char *name, *cp; 1522241675Suqs 1523241675Suqs name = *cpp; 1524241675Suqs if ('\0' == *name) 1525241675Suqs return(name); 1526241675Suqs 1527241675Suqs /* Read until end of name. */ 1528241675Suqs for (cp = name; '\0' != *cp && ' ' != *cp; cp++) { 1529241675Suqs if ('\\' != *cp) 1530241675Suqs continue; 1531241675Suqs cp++; 1532241675Suqs if ('\\' == *cp) 1533241675Suqs continue; 1534241675Suqs mandoc_msg(MANDOCERR_NAMESC, r->parse, ln, pos, NULL); 1535241675Suqs *cp = '\0'; 1536241675Suqs name = cp; 1537241675Suqs } 1538241675Suqs 1539241675Suqs /* Nil-terminate name. */ 1540241675Suqs if ('\0' != *cp) 1541241675Suqs *(cp++) = '\0'; 1542241675Suqs 1543241675Suqs /* Read past spaces. */ 1544241675Suqs while (' ' == *cp) 1545241675Suqs cp++; 1546241675Suqs 1547241675Suqs *cpp = cp; 1548241675Suqs return(name); 1549241675Suqs} 1550241675Suqs 1551241675Suqs/* 1552241675Suqs * Store *string into the user-defined string called *name. 1553241675Suqs * In multiline mode, append to an existing entry and append '\n'; 1554241675Suqs * else replace the existing entry, if there is one. 1555241675Suqs * To clear an existing entry, call with (*r, *name, NULL, 0). 1556241675Suqs */ 1557241675Suqsstatic void 1558241675Suqsroff_setstr(struct roff *r, const char *name, const char *string, 1559241675Suqs int multiline) 1560241675Suqs{ 1561241675Suqs 1562241675Suqs roff_setstrn(&r->strtab, name, strlen(name), string, 1563241675Suqs string ? strlen(string) : 0, multiline); 1564241675Suqs} 1565241675Suqs 1566241675Suqsstatic void 1567241675Suqsroff_setstrn(struct roffkv **r, const char *name, size_t namesz, 1568241675Suqs const char *string, size_t stringsz, int multiline) 1569241675Suqs{ 1570241675Suqs struct roffkv *n; 1571241675Suqs char *c; 1572241675Suqs int i; 1573241675Suqs size_t oldch, newch; 1574241675Suqs 1575241675Suqs /* Search for an existing string with the same name. */ 1576241675Suqs n = *r; 1577241675Suqs 1578241675Suqs while (n && strcmp(name, n->key.p)) 1579241675Suqs n = n->next; 1580241675Suqs 1581241675Suqs if (NULL == n) { 1582241675Suqs /* Create a new string table entry. */ 1583241675Suqs n = mandoc_malloc(sizeof(struct roffkv)); 1584241675Suqs n->key.p = mandoc_strndup(name, namesz); 1585241675Suqs n->key.sz = namesz; 1586241675Suqs n->val.p = NULL; 1587241675Suqs n->val.sz = 0; 1588241675Suqs n->next = *r; 1589241675Suqs *r = n; 1590241675Suqs } else if (0 == multiline) { 1591241675Suqs /* In multiline mode, append; else replace. */ 1592241675Suqs free(n->val.p); 1593241675Suqs n->val.p = NULL; 1594241675Suqs n->val.sz = 0; 1595241675Suqs } 1596241675Suqs 1597241675Suqs if (NULL == string) 1598241675Suqs return; 1599241675Suqs 1600241675Suqs /* 1601241675Suqs * One additional byte for the '\n' in multiline mode, 1602241675Suqs * and one for the terminating '\0'. 1603241675Suqs */ 1604241675Suqs newch = stringsz + (multiline ? 2u : 1u); 1605241675Suqs 1606241675Suqs if (NULL == n->val.p) { 1607241675Suqs n->val.p = mandoc_malloc(newch); 1608241675Suqs *n->val.p = '\0'; 1609241675Suqs oldch = 0; 1610241675Suqs } else { 1611241675Suqs oldch = n->val.sz; 1612241675Suqs n->val.p = mandoc_realloc(n->val.p, oldch + newch); 1613241675Suqs } 1614241675Suqs 1615241675Suqs /* Skip existing content in the destination buffer. */ 1616241675Suqs c = n->val.p + (int)oldch; 1617241675Suqs 1618241675Suqs /* Append new content to the destination buffer. */ 1619241675Suqs i = 0; 1620241675Suqs while (i < (int)stringsz) { 1621241675Suqs /* 1622241675Suqs * Rudimentary roff copy mode: 1623241675Suqs * Handle escaped backslashes. 1624241675Suqs */ 1625241675Suqs if ('\\' == string[i] && '\\' == string[i + 1]) 1626241675Suqs i++; 1627241675Suqs *c++ = string[i++]; 1628241675Suqs } 1629241675Suqs 1630241675Suqs /* Append terminating bytes. */ 1631241675Suqs if (multiline) 1632241675Suqs *c++ = '\n'; 1633241675Suqs 1634241675Suqs *c = '\0'; 1635241675Suqs n->val.sz = (int)(c - n->val.p); 1636241675Suqs} 1637241675Suqs 1638241675Suqsstatic const char * 1639241675Suqsroff_getstrn(const struct roff *r, const char *name, size_t len) 1640241675Suqs{ 1641241675Suqs const struct roffkv *n; 1642241675Suqs 1643241675Suqs for (n = r->strtab; n; n = n->next) 1644241675Suqs if (0 == strncmp(name, n->key.p, len) && 1645241675Suqs '\0' == n->key.p[(int)len]) 1646241675Suqs return(n->val.p); 1647241675Suqs 1648241675Suqs return(NULL); 1649241675Suqs} 1650241675Suqs 1651241675Suqsstatic void 1652241675Suqsroff_freestr(struct roffkv *r) 1653241675Suqs{ 1654241675Suqs struct roffkv *n, *nn; 1655241675Suqs 1656241675Suqs for (n = r; n; n = nn) { 1657241675Suqs free(n->key.p); 1658241675Suqs free(n->val.p); 1659241675Suqs nn = n->next; 1660241675Suqs free(n); 1661241675Suqs } 1662241675Suqs} 1663241675Suqs 1664241675Suqsconst struct tbl_span * 1665241675Suqsroff_span(const struct roff *r) 1666241675Suqs{ 1667241675Suqs 1668241675Suqs return(r->tbl ? tbl_span(r->tbl) : NULL); 1669241675Suqs} 1670241675Suqs 1671241675Suqsconst struct eqn * 1672241675Suqsroff_eqn(const struct roff *r) 1673241675Suqs{ 1674241675Suqs 1675241675Suqs return(r->last_eqn ? &r->last_eqn->eqn : NULL); 1676241675Suqs} 1677241675Suqs 1678241675Suqs/* 1679241675Suqs * Duplicate an input string, making the appropriate character 1680241675Suqs * conversations (as stipulated by `tr') along the way. 1681241675Suqs * Returns a heap-allocated string with all the replacements made. 1682241675Suqs */ 1683241675Suqschar * 1684241675Suqsroff_strdup(const struct roff *r, const char *p) 1685241675Suqs{ 1686241675Suqs const struct roffkv *cp; 1687241675Suqs char *res; 1688241675Suqs const char *pp; 1689241675Suqs size_t ssz, sz; 1690241675Suqs enum mandoc_esc esc; 1691241675Suqs 1692241675Suqs if (NULL == r->xmbtab && NULL == r->xtab) 1693241675Suqs return(mandoc_strdup(p)); 1694241675Suqs else if ('\0' == *p) 1695241675Suqs return(mandoc_strdup("")); 1696241675Suqs 1697241675Suqs /* 1698241675Suqs * Step through each character looking for term matches 1699241675Suqs * (remember that a `tr' can be invoked with an escape, which is 1700241675Suqs * a glyph but the escape is multi-character). 1701241675Suqs * We only do this if the character hash has been initialised 1702241675Suqs * and the string is >0 length. 1703241675Suqs */ 1704241675Suqs 1705241675Suqs res = NULL; 1706241675Suqs ssz = 0; 1707241675Suqs 1708241675Suqs while ('\0' != *p) { 1709241675Suqs if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) { 1710241675Suqs sz = r->xtab[(int)*p].sz; 1711241675Suqs res = mandoc_realloc(res, ssz + sz + 1); 1712241675Suqs memcpy(res + ssz, r->xtab[(int)*p].p, sz); 1713241675Suqs ssz += sz; 1714241675Suqs p++; 1715241675Suqs continue; 1716241675Suqs } else if ('\\' != *p) { 1717241675Suqs res = mandoc_realloc(res, ssz + 2); 1718241675Suqs res[ssz++] = *p++; 1719241675Suqs continue; 1720241675Suqs } 1721241675Suqs 1722241675Suqs /* Search for term matches. */ 1723241675Suqs for (cp = r->xmbtab; cp; cp = cp->next) 1724241675Suqs if (0 == strncmp(p, cp->key.p, cp->key.sz)) 1725241675Suqs break; 1726241675Suqs 1727241675Suqs if (NULL != cp) { 1728241675Suqs /* 1729241675Suqs * A match has been found. 1730241675Suqs * Append the match to the array and move 1731241675Suqs * forward by its keysize. 1732241675Suqs */ 1733241675Suqs res = mandoc_realloc 1734241675Suqs (res, ssz + cp->val.sz + 1); 1735241675Suqs memcpy(res + ssz, cp->val.p, cp->val.sz); 1736241675Suqs ssz += cp->val.sz; 1737241675Suqs p += (int)cp->key.sz; 1738241675Suqs continue; 1739241675Suqs } 1740241675Suqs 1741241675Suqs /* 1742241675Suqs * Handle escapes carefully: we need to copy 1743241675Suqs * over just the escape itself, or else we might 1744241675Suqs * do replacements within the escape itself. 1745241675Suqs * Make sure to pass along the bogus string. 1746241675Suqs */ 1747241675Suqs pp = p++; 1748241675Suqs esc = mandoc_escape(&p, NULL, NULL); 1749241675Suqs if (ESCAPE_ERROR == esc) { 1750241675Suqs sz = strlen(pp); 1751241675Suqs res = mandoc_realloc(res, ssz + sz + 1); 1752241675Suqs memcpy(res + ssz, pp, sz); 1753241675Suqs break; 1754241675Suqs } 1755241675Suqs /* 1756241675Suqs * We bail out on bad escapes. 1757241675Suqs * No need to warn: we already did so when 1758241675Suqs * roff_res() was called. 1759241675Suqs */ 1760241675Suqs sz = (int)(p - pp); 1761241675Suqs res = mandoc_realloc(res, ssz + sz + 1); 1762241675Suqs memcpy(res + ssz, pp, sz); 1763241675Suqs ssz += sz; 1764241675Suqs } 1765241675Suqs 1766241675Suqs res[(int)ssz] = '\0'; 1767241675Suqs return(res); 1768241675Suqs} 1769