1 /* $Id: roff.c,v 1.224 2014/08/01 17:27:44 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #include <assert.h> 23 #include <ctype.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 #include "mandoc.h" 29 #include "mandoc_aux.h" 30 #include "libroff.h" 31 #include "libmandoc.h" 32 33 /* Maximum number of nested if-else conditionals. */ 34 #define RSTACK_MAX 128 35 36 /* Maximum number of string expansions per line, to break infinite loops. */ 37 #define EXPAND_LIMIT 1000 38 39 enum rofft { 40 ROFF_ad, 41 ROFF_am, 42 ROFF_ami, 43 ROFF_am1, 44 ROFF_as, 45 ROFF_cc, 46 ROFF_ce, 47 ROFF_de, 48 ROFF_dei, 49 ROFF_de1, 50 ROFF_ds, 51 ROFF_el, 52 ROFF_fam, 53 ROFF_hw, 54 ROFF_hy, 55 ROFF_ie, 56 ROFF_if, 57 ROFF_ig, 58 ROFF_it, 59 ROFF_ne, 60 ROFF_nh, 61 ROFF_nr, 62 ROFF_ns, 63 ROFF_ps, 64 ROFF_rm, 65 ROFF_rr, 66 ROFF_so, 67 ROFF_ta, 68 ROFF_tr, 69 ROFF_Dd, 70 ROFF_TH, 71 ROFF_TS, 72 ROFF_TE, 73 ROFF_T_, 74 ROFF_EQ, 75 ROFF_EN, 76 ROFF_cblock, 77 ROFF_USERDEF, 78 ROFF_MAX 79 }; 80 81 /* 82 * An incredibly-simple string buffer. 83 */ 84 struct roffstr { 85 char *p; /* nil-terminated buffer */ 86 size_t sz; /* saved strlen(p) */ 87 }; 88 89 /* 90 * A key-value roffstr pair as part of a singly-linked list. 91 */ 92 struct roffkv { 93 struct roffstr key; 94 struct roffstr val; 95 struct roffkv *next; /* next in list */ 96 }; 97 98 /* 99 * A single number register as part of a singly-linked list. 100 */ 101 struct roffreg { 102 struct roffstr key; 103 int val; 104 struct roffreg *next; 105 }; 106 107 struct roff { 108 struct mparse *parse; /* parse point */ 109 struct roffnode *last; /* leaf of stack */ 110 int *rstack; /* stack of inverted `ie' values */ 111 struct roffreg *regtab; /* number registers */ 112 struct roffkv *strtab; /* user-defined strings & macros */ 113 struct roffkv *xmbtab; /* multi-byte trans table (`tr') */ 114 struct roffstr *xtab; /* single-byte trans table (`tr') */ 115 const char *current_string; /* value of last called user macro */ 116 struct tbl_node *first_tbl; /* first table parsed */ 117 struct tbl_node *last_tbl; /* last table parsed */ 118 struct tbl_node *tbl; /* current table being parsed */ 119 struct eqn_node *last_eqn; /* last equation parsed */ 120 struct eqn_node *first_eqn; /* first equation parsed */ 121 struct eqn_node *eqn; /* current equation being parsed */ 122 int options; /* parse options */ 123 int rstacksz; /* current size limit of rstack */ 124 int rstackpos; /* position in rstack */ 125 char control; /* control character */ 126 }; 127 128 struct roffnode { 129 enum rofft tok; /* type of node */ 130 struct roffnode *parent; /* up one in stack */ 131 int line; /* parse line */ 132 int col; /* parse col */ 133 char *name; /* node name, e.g. macro name */ 134 char *end; /* end-rules: custom token */ 135 int endspan; /* end-rules: next-line or infty */ 136 int rule; /* current evaluation rule */ 137 }; 138 139 #define ROFF_ARGS struct roff *r, /* parse ctx */ \ 140 enum rofft tok, /* tok of macro */ \ 141 char **bufp, /* input buffer */ \ 142 size_t *szp, /* size of input buffer */ \ 143 int ln, /* parse line */ \ 144 int ppos, /* original pos in buffer */ \ 145 int pos, /* current pos in buffer */ \ 146 int *offs /* reset offset of buffer data */ 147 148 typedef enum rofferr (*roffproc)(ROFF_ARGS); 149 150 struct roffmac { 151 const char *name; /* macro name */ 152 roffproc proc; /* process new macro */ 153 roffproc text; /* process as child text of macro */ 154 roffproc sub; /* process as child of macro */ 155 int flags; 156 #define ROFFMAC_STRUCT (1 << 0) /* always interpret */ 157 struct roffmac *next; 158 }; 159 160 struct predef { 161 const char *name; /* predefined input name */ 162 const char *str; /* replacement symbol */ 163 }; 164 165 #define PREDEF(__name, __str) \ 166 { (__name), (__str) }, 167 168 static enum rofft roffhash_find(const char *, size_t); 169 static void roffhash_init(void); 170 static void roffnode_cleanscope(struct roff *); 171 static void roffnode_pop(struct roff *); 172 static void roffnode_push(struct roff *, enum rofft, 173 const char *, int, int); 174 static enum rofferr roff_block(ROFF_ARGS); 175 static enum rofferr roff_block_text(ROFF_ARGS); 176 static enum rofferr roff_block_sub(ROFF_ARGS); 177 static enum rofferr roff_cblock(ROFF_ARGS); 178 static enum rofferr roff_cc(ROFF_ARGS); 179 static void roff_ccond(struct roff *, int, int); 180 static enum rofferr roff_cond(ROFF_ARGS); 181 static enum rofferr roff_cond_text(ROFF_ARGS); 182 static enum rofferr roff_cond_sub(ROFF_ARGS); 183 static enum rofferr roff_ds(ROFF_ARGS); 184 static int roff_evalcond(const char *, int *); 185 static int roff_evalnum(const char *, int *, int *, int); 186 static int roff_evalpar(const char *, int *, int *); 187 static int roff_evalstrcond(const char *, int *); 188 static void roff_free1(struct roff *); 189 static void roff_freereg(struct roffreg *); 190 static void roff_freestr(struct roffkv *); 191 static size_t roff_getname(struct roff *, char **, int, int); 192 static int roff_getnum(const char *, int *, int *); 193 static int roff_getop(const char *, int *, char *); 194 static int roff_getregn(const struct roff *, 195 const char *, size_t); 196 static int roff_getregro(const char *name); 197 static const char *roff_getstrn(const struct roff *, 198 const char *, size_t); 199 static enum rofferr roff_it(ROFF_ARGS); 200 static enum rofferr roff_line_ignore(ROFF_ARGS); 201 static enum rofferr roff_nr(ROFF_ARGS); 202 static void roff_openeqn(struct roff *, const char *, 203 int, int, const char *); 204 static enum rofft roff_parse(struct roff *, char *, int *, 205 int, int); 206 static enum rofferr roff_parsetext(char **, size_t *, int, int *); 207 static enum rofferr roff_res(struct roff *, 208 char **, size_t *, int, int); 209 static enum rofferr roff_rm(ROFF_ARGS); 210 static enum rofferr roff_rr(ROFF_ARGS); 211 static void roff_setstr(struct roff *, 212 const char *, const char *, int); 213 static void roff_setstrn(struct roffkv **, const char *, 214 size_t, const char *, size_t, int); 215 static enum rofferr roff_so(ROFF_ARGS); 216 static enum rofferr roff_tr(ROFF_ARGS); 217 static enum rofferr roff_Dd(ROFF_ARGS); 218 static enum rofferr roff_TH(ROFF_ARGS); 219 static enum rofferr roff_TE(ROFF_ARGS); 220 static enum rofferr roff_TS(ROFF_ARGS); 221 static enum rofferr roff_EQ(ROFF_ARGS); 222 static enum rofferr roff_EN(ROFF_ARGS); 223 static enum rofferr roff_T_(ROFF_ARGS); 224 static enum rofferr roff_userdef(ROFF_ARGS); 225 226 /* See roffhash_find() */ 227 228 #define ASCII_HI 126 229 #define ASCII_LO 33 230 #define HASHWIDTH (ASCII_HI - ASCII_LO + 1) 231 232 static struct roffmac *hash[HASHWIDTH]; 233 234 static struct roffmac roffs[ROFF_MAX] = { 235 { "ad", roff_line_ignore, NULL, NULL, 0, NULL }, 236 { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 237 { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 238 { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 239 { "as", roff_ds, NULL, NULL, 0, NULL }, 240 { "cc", roff_cc, NULL, NULL, 0, NULL }, 241 { "ce", roff_line_ignore, NULL, NULL, 0, NULL }, 242 { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 243 { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 244 { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 245 { "ds", roff_ds, NULL, NULL, 0, NULL }, 246 { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, 247 { "fam", roff_line_ignore, NULL, NULL, 0, NULL }, 248 { "hw", roff_line_ignore, NULL, NULL, 0, NULL }, 249 { "hy", roff_line_ignore, NULL, NULL, 0, NULL }, 250 { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, 251 { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, 252 { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL }, 253 { "it", roff_it, NULL, NULL, 0, NULL }, 254 { "ne", roff_line_ignore, NULL, NULL, 0, NULL }, 255 { "nh", roff_line_ignore, NULL, NULL, 0, NULL }, 256 { "nr", roff_nr, NULL, NULL, 0, NULL }, 257 { "ns", roff_line_ignore, NULL, NULL, 0, NULL }, 258 { "ps", roff_line_ignore, NULL, NULL, 0, NULL }, 259 { "rm", roff_rm, NULL, NULL, 0, NULL }, 260 { "rr", roff_rr, NULL, NULL, 0, NULL }, 261 { "so", roff_so, NULL, NULL, 0, NULL }, 262 { "ta", roff_line_ignore, NULL, NULL, 0, NULL }, 263 { "tr", roff_tr, NULL, NULL, 0, NULL }, 264 { "Dd", roff_Dd, NULL, NULL, 0, NULL }, 265 { "TH", roff_TH, NULL, NULL, 0, NULL }, 266 { "TS", roff_TS, NULL, NULL, 0, NULL }, 267 { "TE", roff_TE, NULL, NULL, 0, NULL }, 268 { "T&", roff_T_, NULL, NULL, 0, NULL }, 269 { "EQ", roff_EQ, NULL, NULL, 0, NULL }, 270 { "EN", roff_EN, NULL, NULL, 0, NULL }, 271 { ".", roff_cblock, NULL, NULL, 0, NULL }, 272 { NULL, roff_userdef, NULL, NULL, 0, NULL }, 273 }; 274 275 /* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */ 276 const char *const __mdoc_reserved[] = { 277 "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", 278 "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq", 279 "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx", 280 "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq", 281 "Dt", "Dv", "Dx", "D1", 282 "Ec", "Ed", "Ef", "Ek", "El", "Em", 283 "En", "Eo", "Er", "Es", "Ev", "Ex", 284 "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx", 285 "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp", 286 "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx", 287 "Oc", "Oo", "Op", "Os", "Ot", "Ox", 288 "Pa", "Pc", "Pf", "Po", "Pp", "Pq", 289 "Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv", 290 "Sc", "Sh", "Sm", "So", "Sq", 291 "Ss", "St", "Sx", "Sy", 292 "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr", 293 "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O", 294 "%P", "%Q", "%R", "%T", "%U", "%V", 295 NULL 296 }; 297 298 /* not currently implemented: BT DE DS ME MT PT SY TQ YS */ 299 const char *const __man_reserved[] = { 300 "AT", "B", "BI", "BR", "DT", 301 "EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR", 302 "LP", "OP", "P", "PD", "PP", 303 "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS", 304 "TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR", 305 NULL 306 }; 307 308 /* Array of injected predefined strings. */ 309 #define PREDEFS_MAX 38 310 static const struct predef predefs[PREDEFS_MAX] = { 311 #include "predefs.in" 312 }; 313 314 /* See roffhash_find() */ 315 #define ROFF_HASH(p) (p[0] - ASCII_LO) 316 317 static int roffit_lines; /* number of lines to delay */ 318 static char *roffit_macro; /* nil-terminated macro line */ 319 320 321 static void 322 roffhash_init(void) 323 { 324 struct roffmac *n; 325 int buc, i; 326 327 for (i = 0; i < (int)ROFF_USERDEF; i++) { 328 assert(roffs[i].name[0] >= ASCII_LO); 329 assert(roffs[i].name[0] <= ASCII_HI); 330 331 buc = ROFF_HASH(roffs[i].name); 332 333 if (NULL != (n = hash[buc])) { 334 for ( ; n->next; n = n->next) 335 /* Do nothing. */ ; 336 n->next = &roffs[i]; 337 } else 338 hash[buc] = &roffs[i]; 339 } 340 } 341 342 /* 343 * Look up a roff token by its name. Returns ROFF_MAX if no macro by 344 * the nil-terminated string name could be found. 345 */ 346 static enum rofft 347 roffhash_find(const char *p, size_t s) 348 { 349 int buc; 350 struct roffmac *n; 351 352 /* 353 * libroff has an extremely simple hashtable, for the time 354 * being, which simply keys on the first character, which must 355 * be printable, then walks a chain. It works well enough until 356 * optimised. 357 */ 358 359 if (p[0] < ASCII_LO || p[0] > ASCII_HI) 360 return(ROFF_MAX); 361 362 buc = ROFF_HASH(p); 363 364 if (NULL == (n = hash[buc])) 365 return(ROFF_MAX); 366 for ( ; n; n = n->next) 367 if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s]) 368 return((enum rofft)(n - roffs)); 369 370 return(ROFF_MAX); 371 } 372 373 /* 374 * Pop the current node off of the stack of roff instructions currently 375 * pending. 376 */ 377 static void 378 roffnode_pop(struct roff *r) 379 { 380 struct roffnode *p; 381 382 assert(r->last); 383 p = r->last; 384 385 r->last = r->last->parent; 386 free(p->name); 387 free(p->end); 388 free(p); 389 } 390 391 /* 392 * Push a roff node onto the instruction stack. This must later be 393 * removed with roffnode_pop(). 394 */ 395 static void 396 roffnode_push(struct roff *r, enum rofft tok, const char *name, 397 int line, int col) 398 { 399 struct roffnode *p; 400 401 p = mandoc_calloc(1, sizeof(struct roffnode)); 402 p->tok = tok; 403 if (name) 404 p->name = mandoc_strdup(name); 405 p->parent = r->last; 406 p->line = line; 407 p->col = col; 408 p->rule = p->parent ? p->parent->rule : 0; 409 410 r->last = p; 411 } 412 413 static void 414 roff_free1(struct roff *r) 415 { 416 struct tbl_node *tbl; 417 struct eqn_node *e; 418 int i; 419 420 while (NULL != (tbl = r->first_tbl)) { 421 r->first_tbl = tbl->next; 422 tbl_free(tbl); 423 } 424 r->first_tbl = r->last_tbl = r->tbl = NULL; 425 426 while (NULL != (e = r->first_eqn)) { 427 r->first_eqn = e->next; 428 eqn_free(e); 429 } 430 r->first_eqn = r->last_eqn = r->eqn = NULL; 431 432 while (r->last) 433 roffnode_pop(r); 434 435 free (r->rstack); 436 r->rstack = NULL; 437 r->rstacksz = 0; 438 r->rstackpos = -1; 439 440 roff_freereg(r->regtab); 441 r->regtab = NULL; 442 443 roff_freestr(r->strtab); 444 roff_freestr(r->xmbtab); 445 r->strtab = r->xmbtab = NULL; 446 447 if (r->xtab) 448 for (i = 0; i < 128; i++) 449 free(r->xtab[i].p); 450 free(r->xtab); 451 r->xtab = NULL; 452 } 453 454 void 455 roff_reset(struct roff *r) 456 { 457 458 roff_free1(r); 459 r->control = 0; 460 } 461 462 void 463 roff_free(struct roff *r) 464 { 465 466 roff_free1(r); 467 free(r); 468 } 469 470 struct roff * 471 roff_alloc(struct mparse *parse, int options) 472 { 473 struct roff *r; 474 475 r = mandoc_calloc(1, sizeof(struct roff)); 476 r->parse = parse; 477 r->options = options; 478 r->rstackpos = -1; 479 480 roffhash_init(); 481 482 return(r); 483 } 484 485 /* 486 * In the current line, expand escape sequences that tend to get 487 * used in numerical expressions and conditional requests. 488 * Also check the syntax of the remaining escape sequences. 489 */ 490 static enum rofferr 491 roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) 492 { 493 char ubuf[24]; /* buffer to print the number */ 494 const char *start; /* start of the string to process */ 495 char *stesc; /* start of an escape sequence ('\\') */ 496 const char *stnam; /* start of the name, after "[(*" */ 497 const char *cp; /* end of the name, e.g. before ']' */ 498 const char *res; /* the string to be substituted */ 499 char *nbuf; /* new buffer to copy bufp to */ 500 size_t maxl; /* expected length of the escape name */ 501 size_t naml; /* actual length of the escape name */ 502 int expand_count; /* to avoid infinite loops */ 503 int npos; /* position in numeric expression */ 504 int arg_complete; /* argument not interrupted by eol */ 505 char term; /* character terminating the escape */ 506 507 expand_count = 0; 508 start = *bufp + pos; 509 stesc = strchr(start, '\0') - 1; 510 while (stesc-- > start) { 511 512 /* Search backwards for the next backslash. */ 513 514 if ('\\' != *stesc) 515 continue; 516 517 /* If it is escaped, skip it. */ 518 519 for (cp = stesc - 1; cp >= start; cp--) 520 if ('\\' != *cp) 521 break; 522 523 if (0 == (stesc - cp) % 2) { 524 stesc = (char *)cp; 525 continue; 526 } 527 528 /* Decide whether to expand or to check only. */ 529 530 term = '\0'; 531 cp = stesc + 1; 532 switch (*cp) { 533 case '*': 534 res = NULL; 535 break; 536 case 'B': 537 /* FALLTHROUGH */ 538 case 'w': 539 term = cp[1]; 540 /* FALLTHROUGH */ 541 case 'n': 542 res = ubuf; 543 break; 544 default: 545 if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL)) 546 mandoc_vmsg(MANDOCERR_ESC_BAD, 547 r->parse, ln, (int)(stesc - *bufp), 548 "%.*s", (int)(cp - stesc), stesc); 549 continue; 550 } 551 552 if (EXPAND_LIMIT < ++expand_count) { 553 mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, 554 ln, (int)(stesc - *bufp), NULL); 555 return(ROFF_IGN); 556 } 557 558 /* 559 * The third character decides the length 560 * of the name of the string or register. 561 * Save a pointer to the name. 562 */ 563 564 if ('\0' == term) { 565 switch (*++cp) { 566 case '\0': 567 maxl = 0; 568 break; 569 case '(': 570 cp++; 571 maxl = 2; 572 break; 573 case '[': 574 cp++; 575 term = ']'; 576 maxl = 0; 577 break; 578 default: 579 maxl = 1; 580 break; 581 } 582 } else { 583 cp += 2; 584 maxl = 0; 585 } 586 stnam = cp; 587 588 /* Advance to the end of the name. */ 589 590 arg_complete = 1; 591 for (naml = 0; 0 == maxl || naml < maxl; naml++, cp++) { 592 if ('\0' == *cp) { 593 mandoc_msg(MANDOCERR_ESC_BAD, r->parse, 594 ln, (int)(stesc - *bufp), stesc); 595 arg_complete = 0; 596 break; 597 } 598 if (0 == maxl && *cp == term) { 599 cp++; 600 break; 601 } 602 } 603 604 /* 605 * Retrieve the replacement string; if it is 606 * undefined, resume searching for escapes. 607 */ 608 609 switch (stesc[1]) { 610 case '*': 611 if (arg_complete) 612 res = roff_getstrn(r, stnam, naml); 613 break; 614 case 'B': 615 npos = 0; 616 ubuf[0] = arg_complete && 617 roff_evalnum(stnam, &npos, NULL, 0) && 618 stnam + npos + 1 == cp ? '1' : '0'; 619 ubuf[1] = '\0'; 620 break; 621 case 'n': 622 if (arg_complete) 623 (void)snprintf(ubuf, sizeof(ubuf), "%d", 624 roff_getregn(r, stnam, naml)); 625 else 626 ubuf[0] = '\0'; 627 break; 628 case 'w': 629 /* use even incomplete args */ 630 (void)snprintf(ubuf, sizeof(ubuf), "%d", 631 24 * (int)naml); 632 break; 633 } 634 635 if (NULL == res) { 636 mandoc_vmsg(MANDOCERR_STR_UNDEF, 637 r->parse, ln, (int)(stesc - *bufp), 638 "%.*s", (int)naml, stnam); 639 res = ""; 640 } 641 642 /* Replace the escape sequence by the string. */ 643 644 *stesc = '\0'; 645 *szp = mandoc_asprintf(&nbuf, "%s%s%s", 646 *bufp, res, cp) + 1; 647 648 /* Prepare for the next replacement. */ 649 650 start = nbuf + pos; 651 stesc = nbuf + (stesc - *bufp) + strlen(res); 652 free(*bufp); 653 *bufp = nbuf; 654 } 655 return(ROFF_CONT); 656 } 657 658 /* 659 * Process text streams: 660 * Convert all breakable hyphens into ASCII_HYPH. 661 * Decrement and spring input line trap. 662 */ 663 static enum rofferr 664 roff_parsetext(char **bufp, size_t *szp, int pos, int *offs) 665 { 666 size_t sz; 667 const char *start; 668 char *p; 669 int isz; 670 enum mandoc_esc esc; 671 672 start = p = *bufp + pos; 673 674 while ('\0' != *p) { 675 sz = strcspn(p, "-\\"); 676 p += sz; 677 678 if ('\0' == *p) 679 break; 680 681 if ('\\' == *p) { 682 /* Skip over escapes. */ 683 p++; 684 esc = mandoc_escape((const char **)&p, NULL, NULL); 685 if (ESCAPE_ERROR == esc) 686 break; 687 continue; 688 } else if (p == start) { 689 p++; 690 continue; 691 } 692 693 if (isalpha((unsigned char)p[-1]) && 694 isalpha((unsigned char)p[1])) 695 *p = ASCII_HYPH; 696 p++; 697 } 698 699 /* Spring the input line trap. */ 700 if (1 == roffit_lines) { 701 isz = mandoc_asprintf(&p, "%s\n.%s", *bufp, roffit_macro); 702 free(*bufp); 703 *bufp = p; 704 *szp = isz + 1; 705 *offs = 0; 706 free(roffit_macro); 707 roffit_lines = 0; 708 return(ROFF_REPARSE); 709 } else if (1 < roffit_lines) 710 --roffit_lines; 711 return(ROFF_CONT); 712 } 713 714 enum rofferr 715 roff_parseln(struct roff *r, int ln, char **bufp, 716 size_t *szp, int pos, int *offs) 717 { 718 enum rofft t; 719 enum rofferr e; 720 int ppos, ctl; 721 722 /* 723 * Run the reserved-word filter only if we have some reserved 724 * words to fill in. 725 */ 726 727 e = roff_res(r, bufp, szp, ln, pos); 728 if (ROFF_IGN == e) 729 return(e); 730 assert(ROFF_CONT == e); 731 732 ppos = pos; 733 ctl = roff_getcontrol(r, *bufp, &pos); 734 735 /* 736 * First, if a scope is open and we're not a macro, pass the 737 * text through the macro's filter. If a scope isn't open and 738 * we're not a macro, just let it through. 739 * Finally, if there's an equation scope open, divert it into it 740 * no matter our state. 741 */ 742 743 if (r->last && ! ctl) { 744 t = r->last->tok; 745 assert(roffs[t].text); 746 e = (*roffs[t].text)(r, t, bufp, szp, ln, pos, pos, offs); 747 assert(ROFF_IGN == e || ROFF_CONT == e); 748 if (ROFF_CONT != e) 749 return(e); 750 } 751 if (r->eqn) 752 return(eqn_read(&r->eqn, ln, *bufp, ppos, offs)); 753 if ( ! ctl) { 754 if (r->tbl) 755 return(tbl_read(r->tbl, ln, *bufp, pos)); 756 return(roff_parsetext(bufp, szp, pos, offs)); 757 } 758 759 /* 760 * If a scope is open, go to the child handler for that macro, 761 * as it may want to preprocess before doing anything with it. 762 * Don't do so if an equation is open. 763 */ 764 765 if (r->last) { 766 t = r->last->tok; 767 assert(roffs[t].sub); 768 return((*roffs[t].sub)(r, t, bufp, szp, 769 ln, ppos, pos, offs)); 770 } 771 772 /* 773 * Lastly, as we've no scope open, try to look up and execute 774 * the new macro. If no macro is found, simply return and let 775 * the compilers handle it. 776 */ 777 778 if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos, ln, ppos))) 779 return(ROFF_CONT); 780 781 assert(roffs[t].proc); 782 return((*roffs[t].proc)(r, t, bufp, szp, ln, ppos, pos, offs)); 783 } 784 785 void 786 roff_endparse(struct roff *r) 787 { 788 789 if (r->last) 790 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, 791 r->last->line, r->last->col, 792 roffs[r->last->tok].name); 793 794 if (r->eqn) { 795 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, 796 r->eqn->eqn.ln, r->eqn->eqn.pos, "EQ"); 797 eqn_end(&r->eqn); 798 } 799 800 if (r->tbl) { 801 mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, 802 r->tbl->line, r->tbl->pos, "TS"); 803 tbl_end(&r->tbl); 804 } 805 } 806 807 /* 808 * Parse a roff node's type from the input buffer. This must be in the 809 * form of ".foo xxx" in the usual way. 810 */ 811 static enum rofft 812 roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos) 813 { 814 char *cp; 815 const char *mac; 816 size_t maclen; 817 enum rofft t; 818 819 cp = buf + *pos; 820 821 if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp) 822 return(ROFF_MAX); 823 824 mac = cp; 825 maclen = roff_getname(r, &cp, ln, ppos); 826 827 t = (r->current_string = roff_getstrn(r, mac, maclen)) 828 ? ROFF_USERDEF : roffhash_find(mac, maclen); 829 830 if (ROFF_MAX != t) 831 *pos = cp - buf; 832 833 return(t); 834 } 835 836 static enum rofferr 837 roff_cblock(ROFF_ARGS) 838 { 839 840 /* 841 * A block-close `..' should only be invoked as a child of an 842 * ignore macro, otherwise raise a warning and just ignore it. 843 */ 844 845 if (NULL == r->last) { 846 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 847 ln, ppos, ".."); 848 return(ROFF_IGN); 849 } 850 851 switch (r->last->tok) { 852 case ROFF_am: 853 /* ROFF_am1 is remapped to ROFF_am in roff_block(). */ 854 /* FALLTHROUGH */ 855 case ROFF_ami: 856 /* FALLTHROUGH */ 857 case ROFF_de: 858 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */ 859 /* FALLTHROUGH */ 860 case ROFF_dei: 861 /* FALLTHROUGH */ 862 case ROFF_ig: 863 break; 864 default: 865 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 866 ln, ppos, ".."); 867 return(ROFF_IGN); 868 } 869 870 if ((*bufp)[pos]) 871 mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, 872 ".. %s", *bufp + pos); 873 874 roffnode_pop(r); 875 roffnode_cleanscope(r); 876 return(ROFF_IGN); 877 878 } 879 880 static void 881 roffnode_cleanscope(struct roff *r) 882 { 883 884 while (r->last) { 885 if (--r->last->endspan != 0) 886 break; 887 roffnode_pop(r); 888 } 889 } 890 891 static void 892 roff_ccond(struct roff *r, int ln, int ppos) 893 { 894 895 if (NULL == r->last) { 896 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 897 ln, ppos, "\\}"); 898 return; 899 } 900 901 switch (r->last->tok) { 902 case ROFF_el: 903 /* FALLTHROUGH */ 904 case ROFF_ie: 905 /* FALLTHROUGH */ 906 case ROFF_if: 907 break; 908 default: 909 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 910 ln, ppos, "\\}"); 911 return; 912 } 913 914 if (r->last->endspan > -1) { 915 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 916 ln, ppos, "\\}"); 917 return; 918 } 919 920 roffnode_pop(r); 921 roffnode_cleanscope(r); 922 return; 923 } 924 925 static enum rofferr 926 roff_block(ROFF_ARGS) 927 { 928 const char *name; 929 char *iname, *cp; 930 size_t namesz; 931 932 /* Ignore groff compatibility mode for now. */ 933 934 if (ROFF_de1 == tok) 935 tok = ROFF_de; 936 else if (ROFF_am1 == tok) 937 tok = ROFF_am; 938 939 /* Parse the macro name argument. */ 940 941 cp = *bufp + pos; 942 if (ROFF_ig == tok) { 943 iname = NULL; 944 namesz = 0; 945 } else { 946 iname = cp; 947 namesz = roff_getname(r, &cp, ln, ppos); 948 iname[namesz] = '\0'; 949 } 950 951 /* Resolve the macro name argument if it is indirect. */ 952 953 if (namesz && (ROFF_dei == tok || ROFF_ami == tok)) { 954 if (NULL == (name = roff_getstrn(r, iname, namesz))) { 955 mandoc_vmsg(MANDOCERR_STR_UNDEF, 956 r->parse, ln, (int)(iname - *bufp), 957 "%.*s", (int)namesz, iname); 958 namesz = 0; 959 } else 960 namesz = strlen(name); 961 } else 962 name = iname; 963 964 if (0 == namesz && ROFF_ig != tok) { 965 mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, 966 ln, ppos, roffs[tok].name); 967 return(ROFF_IGN); 968 } 969 970 roffnode_push(r, tok, name, ln, ppos); 971 972 /* 973 * At the beginning of a `de' macro, clear the existing string 974 * with the same name, if there is one. New content will be 975 * appended from roff_block_text() in multiline mode. 976 */ 977 978 if (ROFF_de == tok || ROFF_dei == tok) 979 roff_setstrn(&r->strtab, name, namesz, "", 0, 0); 980 981 if ('\0' == *cp) 982 return(ROFF_IGN); 983 984 /* Get the custom end marker. */ 985 986 iname = cp; 987 namesz = roff_getname(r, &cp, ln, ppos); 988 989 /* Resolve the end marker if it is indirect. */ 990 991 if (namesz && (ROFF_dei == tok || ROFF_ami == tok)) { 992 if (NULL == (name = roff_getstrn(r, iname, namesz))) { 993 mandoc_vmsg(MANDOCERR_STR_UNDEF, 994 r->parse, ln, (int)(iname - *bufp), 995 "%.*s", (int)namesz, iname); 996 namesz = 0; 997 } else 998 namesz = strlen(name); 999 } else 1000 name = iname; 1001 1002 if (namesz) 1003 r->last->end = mandoc_strndup(name, namesz); 1004 1005 if ('\0' != *cp) 1006 mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, 1007 ln, pos, ".%s ... %s", roffs[tok].name, cp); 1008 1009 return(ROFF_IGN); 1010 } 1011 1012 static enum rofferr 1013 roff_block_sub(ROFF_ARGS) 1014 { 1015 enum rofft t; 1016 int i, j; 1017 1018 /* 1019 * First check whether a custom macro exists at this level. If 1020 * it does, then check against it. This is some of groff's 1021 * stranger behaviours. If we encountered a custom end-scope 1022 * tag and that tag also happens to be a "real" macro, then we 1023 * need to try interpreting it again as a real macro. If it's 1024 * not, then return ignore. Else continue. 1025 */ 1026 1027 if (r->last->end) { 1028 for (i = pos, j = 0; r->last->end[j]; j++, i++) 1029 if ((*bufp)[i] != r->last->end[j]) 1030 break; 1031 1032 if ('\0' == r->last->end[j] && 1033 ('\0' == (*bufp)[i] || 1034 ' ' == (*bufp)[i] || 1035 '\t' == (*bufp)[i])) { 1036 roffnode_pop(r); 1037 roffnode_cleanscope(r); 1038 1039 while (' ' == (*bufp)[i] || '\t' == (*bufp)[i]) 1040 i++; 1041 1042 pos = i; 1043 if (ROFF_MAX != roff_parse(r, *bufp, &pos, ln, ppos)) 1044 return(ROFF_RERUN); 1045 return(ROFF_IGN); 1046 } 1047 } 1048 1049 /* 1050 * If we have no custom end-query or lookup failed, then try 1051 * pulling it out of the hashtable. 1052 */ 1053 1054 t = roff_parse(r, *bufp, &pos, ln, ppos); 1055 1056 if (ROFF_cblock != t) { 1057 if (ROFF_ig != tok) 1058 roff_setstr(r, r->last->name, *bufp + ppos, 2); 1059 return(ROFF_IGN); 1060 } 1061 1062 assert(roffs[t].proc); 1063 return((*roffs[t].proc)(r, t, bufp, szp, ln, ppos, pos, offs)); 1064 } 1065 1066 static enum rofferr 1067 roff_block_text(ROFF_ARGS) 1068 { 1069 1070 if (ROFF_ig != tok) 1071 roff_setstr(r, r->last->name, *bufp + pos, 2); 1072 1073 return(ROFF_IGN); 1074 } 1075 1076 static enum rofferr 1077 roff_cond_sub(ROFF_ARGS) 1078 { 1079 enum rofft t; 1080 char *ep; 1081 int rr; 1082 1083 rr = r->last->rule; 1084 roffnode_cleanscope(r); 1085 t = roff_parse(r, *bufp, &pos, ln, ppos); 1086 1087 /* 1088 * Fully handle known macros when they are structurally 1089 * required or when the conditional evaluated to true. 1090 */ 1091 1092 if ((ROFF_MAX != t) && 1093 (rr || ROFFMAC_STRUCT & roffs[t].flags)) { 1094 assert(roffs[t].proc); 1095 return((*roffs[t].proc)(r, t, bufp, szp, 1096 ln, ppos, pos, offs)); 1097 } 1098 1099 /* 1100 * If `\}' occurs on a macro line without a preceding macro, 1101 * drop the line completely. 1102 */ 1103 1104 ep = *bufp + pos; 1105 if ('\\' == ep[0] && '}' == ep[1]) 1106 rr = 0; 1107 1108 /* Always check for the closing delimiter `\}'. */ 1109 1110 while (NULL != (ep = strchr(ep, '\\'))) { 1111 if ('}' == *(++ep)) { 1112 *ep = '&'; 1113 roff_ccond(r, ln, ep - *bufp - 1); 1114 } 1115 ++ep; 1116 } 1117 return(rr ? ROFF_CONT : ROFF_IGN); 1118 } 1119 1120 static enum rofferr 1121 roff_cond_text(ROFF_ARGS) 1122 { 1123 char *ep; 1124 int rr; 1125 1126 rr = r->last->rule; 1127 roffnode_cleanscope(r); 1128 1129 ep = *bufp + pos; 1130 while (NULL != (ep = strchr(ep, '\\'))) { 1131 if ('}' == *(++ep)) { 1132 *ep = '&'; 1133 roff_ccond(r, ln, ep - *bufp - 1); 1134 } 1135 ++ep; 1136 } 1137 return(rr ? ROFF_CONT : ROFF_IGN); 1138 } 1139 1140 /* 1141 * Parse a single signed integer number. Stop at the first non-digit. 1142 * If there is at least one digit, return success and advance the 1143 * parse point, else return failure and let the parse point unchanged. 1144 * Ignore overflows, treat them just like the C language. 1145 */ 1146 static int 1147 roff_getnum(const char *v, int *pos, int *res) 1148 { 1149 int myres, n, p; 1150 1151 if (NULL == res) 1152 res = &myres; 1153 1154 p = *pos; 1155 n = v[p] == '-'; 1156 if (n) 1157 p++; 1158 1159 for (*res = 0; isdigit((unsigned char)v[p]); p++) 1160 *res = 10 * *res + v[p] - '0'; 1161 if (p == *pos + n) 1162 return 0; 1163 1164 if (n) 1165 *res = -*res; 1166 1167 *pos = p; 1168 return 1; 1169 } 1170 1171 /* 1172 * Evaluate a string comparison condition. 1173 * The first character is the delimiter. 1174 * Succeed if the string up to its second occurrence 1175 * matches the string up to its third occurence. 1176 * Advance the cursor after the third occurrence 1177 * or lacking that, to the end of the line. 1178 */ 1179 static int 1180 roff_evalstrcond(const char *v, int *pos) 1181 { 1182 const char *s1, *s2, *s3; 1183 int match; 1184 1185 match = 0; 1186 s1 = v + *pos; /* initial delimiter */ 1187 s2 = s1 + 1; /* for scanning the first string */ 1188 s3 = strchr(s2, *s1); /* for scanning the second string */ 1189 1190 if (NULL == s3) /* found no middle delimiter */ 1191 goto out; 1192 1193 while ('\0' != *++s3) { 1194 if (*s2 != *s3) { /* mismatch */ 1195 s3 = strchr(s3, *s1); 1196 break; 1197 } 1198 if (*s3 == *s1) { /* found the final delimiter */ 1199 match = 1; 1200 break; 1201 } 1202 s2++; 1203 } 1204 1205 out: 1206 if (NULL == s3) 1207 s3 = strchr(s2, '\0'); 1208 else 1209 s3++; 1210 *pos = s3 - v; 1211 return(match); 1212 } 1213 1214 /* 1215 * Evaluate an optionally negated single character, numerical, 1216 * or string condition. 1217 */ 1218 static int 1219 roff_evalcond(const char *v, int *pos) 1220 { 1221 int wanttrue, number; 1222 1223 if ('!' == v[*pos]) { 1224 wanttrue = 0; 1225 (*pos)++; 1226 } else 1227 wanttrue = 1; 1228 1229 switch (v[*pos]) { 1230 case 'n': 1231 /* FALLTHROUGH */ 1232 case 'o': 1233 (*pos)++; 1234 return(wanttrue); 1235 case 'c': 1236 /* FALLTHROUGH */ 1237 case 'd': 1238 /* FALLTHROUGH */ 1239 case 'e': 1240 /* FALLTHROUGH */ 1241 case 'r': 1242 /* FALLTHROUGH */ 1243 case 't': 1244 (*pos)++; 1245 return(!wanttrue); 1246 default: 1247 break; 1248 } 1249 1250 if (roff_evalnum(v, pos, &number, 0)) 1251 return((number > 0) == wanttrue); 1252 else 1253 return(roff_evalstrcond(v, pos) == wanttrue); 1254 } 1255 1256 static enum rofferr 1257 roff_line_ignore(ROFF_ARGS) 1258 { 1259 1260 return(ROFF_IGN); 1261 } 1262 1263 static enum rofferr 1264 roff_cond(ROFF_ARGS) 1265 { 1266 1267 roffnode_push(r, tok, NULL, ln, ppos); 1268 1269 /* 1270 * An `.el' has no conditional body: it will consume the value 1271 * of the current rstack entry set in prior `ie' calls or 1272 * defaults to DENY. 1273 * 1274 * If we're not an `el', however, then evaluate the conditional. 1275 */ 1276 1277 r->last->rule = ROFF_el == tok ? 1278 (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) : 1279 roff_evalcond(*bufp, &pos); 1280 1281 /* 1282 * An if-else will put the NEGATION of the current evaluated 1283 * conditional into the stack of rules. 1284 */ 1285 1286 if (ROFF_ie == tok) { 1287 if (r->rstackpos + 1 == r->rstacksz) { 1288 r->rstacksz += 16; 1289 r->rstack = mandoc_reallocarray(r->rstack, 1290 r->rstacksz, sizeof(int)); 1291 } 1292 r->rstack[++r->rstackpos] = !r->last->rule; 1293 } 1294 1295 /* If the parent has false as its rule, then so do we. */ 1296 1297 if (r->last->parent && !r->last->parent->rule) 1298 r->last->rule = 0; 1299 1300 /* 1301 * Determine scope. 1302 * If there is nothing on the line after the conditional, 1303 * not even whitespace, use next-line scope. 1304 */ 1305 1306 if ('\0' == (*bufp)[pos]) { 1307 r->last->endspan = 2; 1308 goto out; 1309 } 1310 1311 while (' ' == (*bufp)[pos]) 1312 pos++; 1313 1314 /* An opening brace requests multiline scope. */ 1315 1316 if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) { 1317 r->last->endspan = -1; 1318 pos += 2; 1319 goto out; 1320 } 1321 1322 /* 1323 * Anything else following the conditional causes 1324 * single-line scope. Warn if the scope contains 1325 * nothing but trailing whitespace. 1326 */ 1327 1328 if ('\0' == (*bufp)[pos]) 1329 mandoc_msg(MANDOCERR_COND_EMPTY, r->parse, 1330 ln, ppos, roffs[tok].name); 1331 1332 r->last->endspan = 1; 1333 1334 out: 1335 *offs = pos; 1336 return(ROFF_RERUN); 1337 } 1338 1339 static enum rofferr 1340 roff_ds(ROFF_ARGS) 1341 { 1342 char *string; 1343 const char *name; 1344 size_t namesz; 1345 1346 /* 1347 * The first word is the name of the string. 1348 * If it is empty or terminated by an escape sequence, 1349 * abort the `ds' request without defining anything. 1350 */ 1351 1352 name = string = *bufp + pos; 1353 if ('\0' == *name) 1354 return(ROFF_IGN); 1355 1356 namesz = roff_getname(r, &string, ln, pos); 1357 if ('\\' == name[namesz]) 1358 return(ROFF_IGN); 1359 1360 /* Read past the initial double-quote, if any. */ 1361 if ('"' == *string) 1362 string++; 1363 1364 /* The rest is the value. */ 1365 roff_setstrn(&r->strtab, name, namesz, string, strlen(string), 1366 ROFF_as == tok); 1367 return(ROFF_IGN); 1368 } 1369 1370 /* 1371 * Parse a single operator, one or two characters long. 1372 * If the operator is recognized, return success and advance the 1373 * parse point, else return failure and let the parse point unchanged. 1374 */ 1375 static int 1376 roff_getop(const char *v, int *pos, char *res) 1377 { 1378 1379 *res = v[*pos]; 1380 1381 switch (*res) { 1382 case '+': 1383 /* FALLTHROUGH */ 1384 case '-': 1385 /* FALLTHROUGH */ 1386 case '*': 1387 /* FALLTHROUGH */ 1388 case '/': 1389 /* FALLTHROUGH */ 1390 case '%': 1391 /* FALLTHROUGH */ 1392 case '&': 1393 /* FALLTHROUGH */ 1394 case ':': 1395 break; 1396 case '<': 1397 switch (v[*pos + 1]) { 1398 case '=': 1399 *res = 'l'; 1400 (*pos)++; 1401 break; 1402 case '>': 1403 *res = '!'; 1404 (*pos)++; 1405 break; 1406 case '?': 1407 *res = 'i'; 1408 (*pos)++; 1409 break; 1410 default: 1411 break; 1412 } 1413 break; 1414 case '>': 1415 switch (v[*pos + 1]) { 1416 case '=': 1417 *res = 'g'; 1418 (*pos)++; 1419 break; 1420 case '?': 1421 *res = 'a'; 1422 (*pos)++; 1423 break; 1424 default: 1425 break; 1426 } 1427 break; 1428 case '=': 1429 if ('=' == v[*pos + 1]) 1430 (*pos)++; 1431 break; 1432 default: 1433 return(0); 1434 } 1435 (*pos)++; 1436 1437 return(*res); 1438 } 1439 1440 /* 1441 * Evaluate either a parenthesized numeric expression 1442 * or a single signed integer number. 1443 */ 1444 static int 1445 roff_evalpar(const char *v, int *pos, int *res) 1446 { 1447 1448 if ('(' != v[*pos]) 1449 return(roff_getnum(v, pos, res)); 1450 1451 (*pos)++; 1452 if ( ! roff_evalnum(v, pos, res, 1)) 1453 return(0); 1454 1455 /* 1456 * Omission of the closing parenthesis 1457 * is an error in validation mode, 1458 * but ignored in evaluation mode. 1459 */ 1460 1461 if (')' == v[*pos]) 1462 (*pos)++; 1463 else if (NULL == res) 1464 return(0); 1465 1466 return(1); 1467 } 1468 1469 /* 1470 * Evaluate a complete numeric expression. 1471 * Proceed left to right, there is no concept of precedence. 1472 */ 1473 static int 1474 roff_evalnum(const char *v, int *pos, int *res, int skipwhite) 1475 { 1476 int mypos, operand2; 1477 char operator; 1478 1479 if (NULL == pos) { 1480 mypos = 0; 1481 pos = &mypos; 1482 } 1483 1484 if (skipwhite) 1485 while (isspace((unsigned char)v[*pos])) 1486 (*pos)++; 1487 1488 if ( ! roff_evalpar(v, pos, res)) 1489 return(0); 1490 1491 while (1) { 1492 if (skipwhite) 1493 while (isspace((unsigned char)v[*pos])) 1494 (*pos)++; 1495 1496 if ( ! roff_getop(v, pos, &operator)) 1497 break; 1498 1499 if (skipwhite) 1500 while (isspace((unsigned char)v[*pos])) 1501 (*pos)++; 1502 1503 if ( ! roff_evalpar(v, pos, &operand2)) 1504 return(0); 1505 1506 if (skipwhite) 1507 while (isspace((unsigned char)v[*pos])) 1508 (*pos)++; 1509 1510 if (NULL == res) 1511 continue; 1512 1513 switch (operator) { 1514 case '+': 1515 *res += operand2; 1516 break; 1517 case '-': 1518 *res -= operand2; 1519 break; 1520 case '*': 1521 *res *= operand2; 1522 break; 1523 case '/': 1524 *res /= operand2; 1525 break; 1526 case '%': 1527 *res %= operand2; 1528 break; 1529 case '<': 1530 *res = *res < operand2; 1531 break; 1532 case '>': 1533 *res = *res > operand2; 1534 break; 1535 case 'l': 1536 *res = *res <= operand2; 1537 break; 1538 case 'g': 1539 *res = *res >= operand2; 1540 break; 1541 case '=': 1542 *res = *res == operand2; 1543 break; 1544 case '!': 1545 *res = *res != operand2; 1546 break; 1547 case '&': 1548 *res = *res && operand2; 1549 break; 1550 case ':': 1551 *res = *res || operand2; 1552 break; 1553 case 'i': 1554 if (operand2 < *res) 1555 *res = operand2; 1556 break; 1557 case 'a': 1558 if (operand2 > *res) 1559 *res = operand2; 1560 break; 1561 default: 1562 abort(); 1563 } 1564 } 1565 return(1); 1566 } 1567 1568 void 1569 roff_setreg(struct roff *r, const char *name, int val, char sign) 1570 { 1571 struct roffreg *reg; 1572 1573 /* Search for an existing register with the same name. */ 1574 reg = r->regtab; 1575 1576 while (reg && strcmp(name, reg->key.p)) 1577 reg = reg->next; 1578 1579 if (NULL == reg) { 1580 /* Create a new register. */ 1581 reg = mandoc_malloc(sizeof(struct roffreg)); 1582 reg->key.p = mandoc_strdup(name); 1583 reg->key.sz = strlen(name); 1584 reg->val = 0; 1585 reg->next = r->regtab; 1586 r->regtab = reg; 1587 } 1588 1589 if ('+' == sign) 1590 reg->val += val; 1591 else if ('-' == sign) 1592 reg->val -= val; 1593 else 1594 reg->val = val; 1595 } 1596 1597 /* 1598 * Handle some predefined read-only number registers. 1599 * For now, return -1 if the requested register is not predefined; 1600 * in case a predefined read-only register having the value -1 1601 * were to turn up, another special value would have to be chosen. 1602 */ 1603 static int 1604 roff_getregro(const char *name) 1605 { 1606 1607 switch (*name) { 1608 case 'A': /* ASCII approximation mode is always off. */ 1609 return(0); 1610 case 'g': /* Groff compatibility mode is always on. */ 1611 return(1); 1612 case 'H': /* Fixed horizontal resolution. */ 1613 return (24); 1614 case 'j': /* Always adjust left margin only. */ 1615 return(0); 1616 case 'T': /* Some output device is always defined. */ 1617 return(1); 1618 case 'V': /* Fixed vertical resolution. */ 1619 return (40); 1620 default: 1621 return (-1); 1622 } 1623 } 1624 1625 int 1626 roff_getreg(const struct roff *r, const char *name) 1627 { 1628 struct roffreg *reg; 1629 int val; 1630 1631 if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) { 1632 val = roff_getregro(name + 1); 1633 if (-1 != val) 1634 return (val); 1635 } 1636 1637 for (reg = r->regtab; reg; reg = reg->next) 1638 if (0 == strcmp(name, reg->key.p)) 1639 return(reg->val); 1640 1641 return(0); 1642 } 1643 1644 static int 1645 roff_getregn(const struct roff *r, const char *name, size_t len) 1646 { 1647 struct roffreg *reg; 1648 int val; 1649 1650 if ('.' == name[0] && 2 == len) { 1651 val = roff_getregro(name + 1); 1652 if (-1 != val) 1653 return (val); 1654 } 1655 1656 for (reg = r->regtab; reg; reg = reg->next) 1657 if (len == reg->key.sz && 1658 0 == strncmp(name, reg->key.p, len)) 1659 return(reg->val); 1660 1661 return(0); 1662 } 1663 1664 static void 1665 roff_freereg(struct roffreg *reg) 1666 { 1667 struct roffreg *old_reg; 1668 1669 while (NULL != reg) { 1670 free(reg->key.p); 1671 old_reg = reg; 1672 reg = reg->next; 1673 free(old_reg); 1674 } 1675 } 1676 1677 static enum rofferr 1678 roff_nr(ROFF_ARGS) 1679 { 1680 char *key, *val; 1681 size_t keysz; 1682 int iv; 1683 char sign; 1684 1685 key = val = *bufp + pos; 1686 if ('\0' == *key) 1687 return(ROFF_IGN); 1688 1689 keysz = roff_getname(r, &val, ln, pos); 1690 if ('\\' == key[keysz]) 1691 return(ROFF_IGN); 1692 key[keysz] = '\0'; 1693 1694 sign = *val; 1695 if ('+' == sign || '-' == sign) 1696 val++; 1697 1698 if (roff_evalnum(val, NULL, &iv, 0)) 1699 roff_setreg(r, key, iv, sign); 1700 1701 return(ROFF_IGN); 1702 } 1703 1704 static enum rofferr 1705 roff_rr(ROFF_ARGS) 1706 { 1707 struct roffreg *reg, **prev; 1708 char *name, *cp; 1709 size_t namesz; 1710 1711 name = cp = *bufp + pos; 1712 if ('\0' == *name) 1713 return(ROFF_IGN); 1714 namesz = roff_getname(r, &cp, ln, pos); 1715 name[namesz] = '\0'; 1716 1717 prev = &r->regtab; 1718 while (1) { 1719 reg = *prev; 1720 if (NULL == reg || !strcmp(name, reg->key.p)) 1721 break; 1722 prev = ®->next; 1723 } 1724 if (NULL != reg) { 1725 *prev = reg->next; 1726 free(reg->key.p); 1727 free(reg); 1728 } 1729 return(ROFF_IGN); 1730 } 1731 1732 static enum rofferr 1733 roff_rm(ROFF_ARGS) 1734 { 1735 const char *name; 1736 char *cp; 1737 size_t namesz; 1738 1739 cp = *bufp + pos; 1740 while ('\0' != *cp) { 1741 name = cp; 1742 namesz = roff_getname(r, &cp, ln, (int)(cp - *bufp)); 1743 roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0); 1744 if ('\\' == name[namesz]) 1745 break; 1746 } 1747 return(ROFF_IGN); 1748 } 1749 1750 static enum rofferr 1751 roff_it(ROFF_ARGS) 1752 { 1753 char *cp; 1754 size_t len; 1755 int iv; 1756 1757 /* Parse the number of lines. */ 1758 cp = *bufp + pos; 1759 len = strcspn(cp, " \t"); 1760 cp[len] = '\0'; 1761 if ((iv = mandoc_strntoi(cp, len, 10)) <= 0) { 1762 mandoc_msg(MANDOCERR_IT_NONUM, r->parse, 1763 ln, ppos, *bufp + 1); 1764 return(ROFF_IGN); 1765 } 1766 cp += len + 1; 1767 1768 /* Arm the input line trap. */ 1769 roffit_lines = iv; 1770 roffit_macro = mandoc_strdup(cp); 1771 return(ROFF_IGN); 1772 } 1773 1774 static enum rofferr 1775 roff_Dd(ROFF_ARGS) 1776 { 1777 const char *const *cp; 1778 1779 if (0 == ((MPARSE_MDOC | MPARSE_QUICK) & r->options)) 1780 for (cp = __mdoc_reserved; *cp; cp++) 1781 roff_setstr(r, *cp, NULL, 0); 1782 1783 return(ROFF_CONT); 1784 } 1785 1786 static enum rofferr 1787 roff_TH(ROFF_ARGS) 1788 { 1789 const char *const *cp; 1790 1791 if (0 == (MPARSE_QUICK & r->options)) 1792 for (cp = __man_reserved; *cp; cp++) 1793 roff_setstr(r, *cp, NULL, 0); 1794 1795 return(ROFF_CONT); 1796 } 1797 1798 static enum rofferr 1799 roff_TE(ROFF_ARGS) 1800 { 1801 1802 if (NULL == r->tbl) 1803 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 1804 ln, ppos, "TE"); 1805 else 1806 tbl_end(&r->tbl); 1807 1808 return(ROFF_IGN); 1809 } 1810 1811 static enum rofferr 1812 roff_T_(ROFF_ARGS) 1813 { 1814 1815 if (NULL == r->tbl) 1816 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, 1817 ln, ppos, "T&"); 1818 else 1819 tbl_restart(ppos, ln, r->tbl); 1820 1821 return(ROFF_IGN); 1822 } 1823 1824 #if 0 1825 static int 1826 roff_closeeqn(struct roff *r) 1827 { 1828 1829 return(r->eqn && ROFF_EQN == eqn_end(&r->eqn) ? 1 : 0); 1830 } 1831 #endif 1832 1833 static void 1834 roff_openeqn(struct roff *r, const char *name, int line, 1835 int offs, const char *buf) 1836 { 1837 struct eqn_node *e; 1838 int poff; 1839 1840 assert(NULL == r->eqn); 1841 e = eqn_alloc(name, offs, line, r->parse); 1842 1843 if (r->last_eqn) 1844 r->last_eqn->next = e; 1845 else 1846 r->first_eqn = r->last_eqn = e; 1847 1848 r->eqn = r->last_eqn = e; 1849 1850 if (buf) { 1851 poff = 0; 1852 eqn_read(&r->eqn, line, buf, offs, &poff); 1853 } 1854 } 1855 1856 static enum rofferr 1857 roff_EQ(ROFF_ARGS) 1858 { 1859 1860 roff_openeqn(r, *bufp + pos, ln, ppos, NULL); 1861 return(ROFF_IGN); 1862 } 1863 1864 static enum rofferr 1865 roff_EN(ROFF_ARGS) 1866 { 1867 1868 mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN"); 1869 return(ROFF_IGN); 1870 } 1871 1872 static enum rofferr 1873 roff_TS(ROFF_ARGS) 1874 { 1875 struct tbl_node *tbl; 1876 1877 if (r->tbl) { 1878 mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse, 1879 ln, ppos, "TS breaks TS"); 1880 tbl_end(&r->tbl); 1881 } 1882 1883 tbl = tbl_alloc(ppos, ln, r->parse); 1884 1885 if (r->last_tbl) 1886 r->last_tbl->next = tbl; 1887 else 1888 r->first_tbl = r->last_tbl = tbl; 1889 1890 r->tbl = r->last_tbl = tbl; 1891 return(ROFF_IGN); 1892 } 1893 1894 static enum rofferr 1895 roff_cc(ROFF_ARGS) 1896 { 1897 const char *p; 1898 1899 p = *bufp + pos; 1900 1901 if ('\0' == *p || '.' == (r->control = *p++)) 1902 r->control = 0; 1903 1904 if ('\0' != *p) 1905 mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL); 1906 1907 return(ROFF_IGN); 1908 } 1909 1910 static enum rofferr 1911 roff_tr(ROFF_ARGS) 1912 { 1913 const char *p, *first, *second; 1914 size_t fsz, ssz; 1915 enum mandoc_esc esc; 1916 1917 p = *bufp + pos; 1918 1919 if ('\0' == *p) { 1920 mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL); 1921 return(ROFF_IGN); 1922 } 1923 1924 while ('\0' != *p) { 1925 fsz = ssz = 1; 1926 1927 first = p++; 1928 if ('\\' == *first) { 1929 esc = mandoc_escape(&p, NULL, NULL); 1930 if (ESCAPE_ERROR == esc) { 1931 mandoc_msg(MANDOCERR_ESC_BAD, r->parse, 1932 ln, (int)(p - *bufp), first); 1933 return(ROFF_IGN); 1934 } 1935 fsz = (size_t)(p - first); 1936 } 1937 1938 second = p++; 1939 if ('\\' == *second) { 1940 esc = mandoc_escape(&p, NULL, NULL); 1941 if (ESCAPE_ERROR == esc) { 1942 mandoc_msg(MANDOCERR_ESC_BAD, r->parse, 1943 ln, (int)(p - *bufp), second); 1944 return(ROFF_IGN); 1945 } 1946 ssz = (size_t)(p - second); 1947 } else if ('\0' == *second) { 1948 mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, 1949 ln, (int)(p - *bufp), NULL); 1950 second = " "; 1951 p--; 1952 } 1953 1954 if (fsz > 1) { 1955 roff_setstrn(&r->xmbtab, first, fsz, 1956 second, ssz, 0); 1957 continue; 1958 } 1959 1960 if (NULL == r->xtab) 1961 r->xtab = mandoc_calloc(128, 1962 sizeof(struct roffstr)); 1963 1964 free(r->xtab[(int)*first].p); 1965 r->xtab[(int)*first].p = mandoc_strndup(second, ssz); 1966 r->xtab[(int)*first].sz = ssz; 1967 } 1968 1969 return(ROFF_IGN); 1970 } 1971 1972 static enum rofferr 1973 roff_so(ROFF_ARGS) 1974 { 1975 char *name; 1976 1977 name = *bufp + pos; 1978 mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name); 1979 1980 /* 1981 * Handle `so'. Be EXTREMELY careful, as we shouldn't be 1982 * opening anything that's not in our cwd or anything beneath 1983 * it. Thus, explicitly disallow traversing up the file-system 1984 * or using absolute paths. 1985 */ 1986 1987 if ('/' == *name || strstr(name, "../") || strstr(name, "/..")) { 1988 mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos, 1989 ".so %s", name); 1990 return(ROFF_ERR); 1991 } 1992 1993 *offs = pos; 1994 return(ROFF_SO); 1995 } 1996 1997 static enum rofferr 1998 roff_userdef(ROFF_ARGS) 1999 { 2000 const char *arg[9]; 2001 char *cp, *n1, *n2; 2002 int i; 2003 2004 /* 2005 * Collect pointers to macro argument strings 2006 * and NUL-terminate them. 2007 */ 2008 cp = *bufp + pos; 2009 for (i = 0; i < 9; i++) 2010 arg[i] = '\0' == *cp ? "" : 2011 mandoc_getarg(r->parse, &cp, ln, &pos); 2012 2013 /* 2014 * Expand macro arguments. 2015 */ 2016 *szp = 0; 2017 n1 = cp = mandoc_strdup(r->current_string); 2018 while (NULL != (cp = strstr(cp, "\\$"))) { 2019 i = cp[2] - '1'; 2020 if (0 > i || 8 < i) { 2021 /* Not an argument invocation. */ 2022 cp += 2; 2023 continue; 2024 } 2025 *cp = '\0'; 2026 *szp = mandoc_asprintf(&n2, "%s%s%s", 2027 n1, arg[i], cp + 3) + 1; 2028 cp = n2 + (cp - n1); 2029 free(n1); 2030 n1 = n2; 2031 } 2032 2033 /* 2034 * Replace the macro invocation 2035 * by the expanded macro. 2036 */ 2037 free(*bufp); 2038 *bufp = n1; 2039 if (0 == *szp) 2040 *szp = strlen(*bufp) + 1; 2041 2042 return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ? 2043 ROFF_REPARSE : ROFF_APPEND); 2044 } 2045 2046 static size_t 2047 roff_getname(struct roff *r, char **cpp, int ln, int pos) 2048 { 2049 char *name, *cp; 2050 size_t namesz; 2051 2052 name = *cpp; 2053 if ('\0' == *name) 2054 return(0); 2055 2056 /* Read until end of name and terminate it with NUL. */ 2057 for (cp = name; 1; cp++) { 2058 if ('\0' == *cp || ' ' == *cp) { 2059 namesz = cp - name; 2060 break; 2061 } 2062 if ('\\' != *cp) 2063 continue; 2064 namesz = cp - name; 2065 if ('{' == cp[1] || '}' == cp[1]) 2066 break; 2067 cp++; 2068 if ('\\' == *cp) 2069 continue; 2070 mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos, 2071 "%.*s", (int)(cp - name + 1), name); 2072 mandoc_escape((const char **)&cp, NULL, NULL); 2073 break; 2074 } 2075 2076 /* Read past spaces. */ 2077 while (' ' == *cp) 2078 cp++; 2079 2080 *cpp = cp; 2081 return(namesz); 2082 } 2083 2084 /* 2085 * Store *string into the user-defined string called *name. 2086 * To clear an existing entry, call with (*r, *name, NULL, 0). 2087 * append == 0: replace mode 2088 * append == 1: single-line append mode 2089 * append == 2: multiline append mode, append '\n' after each call 2090 */ 2091 static void 2092 roff_setstr(struct roff *r, const char *name, const char *string, 2093 int append) 2094 { 2095 2096 roff_setstrn(&r->strtab, name, strlen(name), string, 2097 string ? strlen(string) : 0, append); 2098 } 2099 2100 static void 2101 roff_setstrn(struct roffkv **r, const char *name, size_t namesz, 2102 const char *string, size_t stringsz, int append) 2103 { 2104 struct roffkv *n; 2105 char *c; 2106 int i; 2107 size_t oldch, newch; 2108 2109 /* Search for an existing string with the same name. */ 2110 n = *r; 2111 2112 while (n && (namesz != n->key.sz || 2113 strncmp(n->key.p, name, namesz))) 2114 n = n->next; 2115 2116 if (NULL == n) { 2117 /* Create a new string table entry. */ 2118 n = mandoc_malloc(sizeof(struct roffkv)); 2119 n->key.p = mandoc_strndup(name, namesz); 2120 n->key.sz = namesz; 2121 n->val.p = NULL; 2122 n->val.sz = 0; 2123 n->next = *r; 2124 *r = n; 2125 } else if (0 == append) { 2126 free(n->val.p); 2127 n->val.p = NULL; 2128 n->val.sz = 0; 2129 } 2130 2131 if (NULL == string) 2132 return; 2133 2134 /* 2135 * One additional byte for the '\n' in multiline mode, 2136 * and one for the terminating '\0'. 2137 */ 2138 newch = stringsz + (1 < append ? 2u : 1u); 2139 2140 if (NULL == n->val.p) { 2141 n->val.p = mandoc_malloc(newch); 2142 *n->val.p = '\0'; 2143 oldch = 0; 2144 } else { 2145 oldch = n->val.sz; 2146 n->val.p = mandoc_realloc(n->val.p, oldch + newch); 2147 } 2148 2149 /* Skip existing content in the destination buffer. */ 2150 c = n->val.p + (int)oldch; 2151 2152 /* Append new content to the destination buffer. */ 2153 i = 0; 2154 while (i < (int)stringsz) { 2155 /* 2156 * Rudimentary roff copy mode: 2157 * Handle escaped backslashes. 2158 */ 2159 if ('\\' == string[i] && '\\' == string[i + 1]) 2160 i++; 2161 *c++ = string[i++]; 2162 } 2163 2164 /* Append terminating bytes. */ 2165 if (1 < append) 2166 *c++ = '\n'; 2167 2168 *c = '\0'; 2169 n->val.sz = (int)(c - n->val.p); 2170 } 2171 2172 static const char * 2173 roff_getstrn(const struct roff *r, const char *name, size_t len) 2174 { 2175 const struct roffkv *n; 2176 int i; 2177 2178 for (n = r->strtab; n; n = n->next) 2179 if (0 == strncmp(name, n->key.p, len) && 2180 '\0' == n->key.p[(int)len]) 2181 return(n->val.p); 2182 2183 for (i = 0; i < PREDEFS_MAX; i++) 2184 if (0 == strncmp(name, predefs[i].name, len) && 2185 '\0' == predefs[i].name[(int)len]) 2186 return(predefs[i].str); 2187 2188 return(NULL); 2189 } 2190 2191 static void 2192 roff_freestr(struct roffkv *r) 2193 { 2194 struct roffkv *n, *nn; 2195 2196 for (n = r; n; n = nn) { 2197 free(n->key.p); 2198 free(n->val.p); 2199 nn = n->next; 2200 free(n); 2201 } 2202 } 2203 2204 const struct tbl_span * 2205 roff_span(const struct roff *r) 2206 { 2207 2208 return(r->tbl ? tbl_span(r->tbl) : NULL); 2209 } 2210 2211 const struct eqn * 2212 roff_eqn(const struct roff *r) 2213 { 2214 2215 return(r->last_eqn ? &r->last_eqn->eqn : NULL); 2216 } 2217 2218 /* 2219 * Duplicate an input string, making the appropriate character 2220 * conversations (as stipulated by `tr') along the way. 2221 * Returns a heap-allocated string with all the replacements made. 2222 */ 2223 char * 2224 roff_strdup(const struct roff *r, const char *p) 2225 { 2226 const struct roffkv *cp; 2227 char *res; 2228 const char *pp; 2229 size_t ssz, sz; 2230 enum mandoc_esc esc; 2231 2232 if (NULL == r->xmbtab && NULL == r->xtab) 2233 return(mandoc_strdup(p)); 2234 else if ('\0' == *p) 2235 return(mandoc_strdup("")); 2236 2237 /* 2238 * Step through each character looking for term matches 2239 * (remember that a `tr' can be invoked with an escape, which is 2240 * a glyph but the escape is multi-character). 2241 * We only do this if the character hash has been initialised 2242 * and the string is >0 length. 2243 */ 2244 2245 res = NULL; 2246 ssz = 0; 2247 2248 while ('\0' != *p) { 2249 if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) { 2250 sz = r->xtab[(int)*p].sz; 2251 res = mandoc_realloc(res, ssz + sz + 1); 2252 memcpy(res + ssz, r->xtab[(int)*p].p, sz); 2253 ssz += sz; 2254 p++; 2255 continue; 2256 } else if ('\\' != *p) { 2257 res = mandoc_realloc(res, ssz + 2); 2258 res[ssz++] = *p++; 2259 continue; 2260 } 2261 2262 /* Search for term matches. */ 2263 for (cp = r->xmbtab; cp; cp = cp->next) 2264 if (0 == strncmp(p, cp->key.p, cp->key.sz)) 2265 break; 2266 2267 if (NULL != cp) { 2268 /* 2269 * A match has been found. 2270 * Append the match to the array and move 2271 * forward by its keysize. 2272 */ 2273 res = mandoc_realloc(res, 2274 ssz + cp->val.sz + 1); 2275 memcpy(res + ssz, cp->val.p, cp->val.sz); 2276 ssz += cp->val.sz; 2277 p += (int)cp->key.sz; 2278 continue; 2279 } 2280 2281 /* 2282 * Handle escapes carefully: we need to copy 2283 * over just the escape itself, or else we might 2284 * do replacements within the escape itself. 2285 * Make sure to pass along the bogus string. 2286 */ 2287 pp = p++; 2288 esc = mandoc_escape(&p, NULL, NULL); 2289 if (ESCAPE_ERROR == esc) { 2290 sz = strlen(pp); 2291 res = mandoc_realloc(res, ssz + sz + 1); 2292 memcpy(res + ssz, pp, sz); 2293 break; 2294 } 2295 /* 2296 * We bail out on bad escapes. 2297 * No need to warn: we already did so when 2298 * roff_res() was called. 2299 */ 2300 sz = (int)(p - pp); 2301 res = mandoc_realloc(res, ssz + sz + 1); 2302 memcpy(res + ssz, pp, sz); 2303 ssz += sz; 2304 } 2305 2306 res[(int)ssz] = '\0'; 2307 return(res); 2308 } 2309 2310 /* 2311 * Find out whether a line is a macro line or not. 2312 * If it is, adjust the current position and return one; if it isn't, 2313 * return zero and don't change the current position. 2314 * If the control character has been set with `.cc', then let that grain 2315 * precedence. 2316 * This is slighly contrary to groff, where using the non-breaking 2317 * control character when `cc' has been invoked will cause the 2318 * non-breaking macro contents to be printed verbatim. 2319 */ 2320 int 2321 roff_getcontrol(const struct roff *r, const char *cp, int *ppos) 2322 { 2323 int pos; 2324 2325 pos = *ppos; 2326 2327 if (0 != r->control && cp[pos] == r->control) 2328 pos++; 2329 else if (0 != r->control) 2330 return(0); 2331 else if ('\\' == cp[pos] && '.' == cp[pos + 1]) 2332 pos += 2; 2333 else if ('.' == cp[pos] || '\'' == cp[pos]) 2334 pos++; 2335 else 2336 return(0); 2337 2338 while (' ' == cp[pos] || '\t' == cp[pos]) 2339 pos++; 2340 2341 *ppos = pos; 2342 return(1); 2343 } 2344