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