]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.260 2015/02/06 16:06:25 schwarze Exp $ */
3 * Copyright (c) 2010, 2011, 2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
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.
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.
20 #include <sys/types.h>
30 #include "mandoc_aux.h"
31 #include "libmandoc.h"
34 /* Maximum number of nested if-else conditionals. */
35 #define RSTACK_MAX 128
37 /* Maximum number of string expansions per line, to break infinite loops. */
38 #define EXPAND_LIMIT 1000
121 /* MAN_fi; ignored in mdoc(7) */
130 /* MAN_ft; ignored in mdoc(7) */
152 /* MAN_in; ignored in mdoc(7) */
170 /* MAN_ll, MDOC_ll */
184 /* MAN_nf; ignored in mdoc(7) */
235 /* MAN_sp, MDOC_sp */
288 * An incredibly-simple string buffer.
291 char *p
; /* nil-terminated buffer */
292 size_t sz
; /* saved strlen(p) */
296 * A key-value roffstr pair as part of a singly-linked list.
301 struct roffkv
*next
; /* next in list */
305 * A single number register as part of a singly-linked list.
310 struct roffreg
*next
;
314 struct mparse
*parse
; /* parse point */
315 const struct mchars
*mchars
; /* character table */
316 struct roffnode
*last
; /* leaf of stack */
317 int *rstack
; /* stack of inverted `ie' values */
318 struct roffreg
*regtab
; /* number registers */
319 struct roffkv
*strtab
; /* user-defined strings & macros */
320 struct roffkv
*xmbtab
; /* multi-byte trans table (`tr') */
321 struct roffstr
*xtab
; /* single-byte trans table (`tr') */
322 const char *current_string
; /* value of last called user macro */
323 struct tbl_node
*first_tbl
; /* first table parsed */
324 struct tbl_node
*last_tbl
; /* last table parsed */
325 struct tbl_node
*tbl
; /* current table being parsed */
326 struct eqn_node
*last_eqn
; /* last equation parsed */
327 struct eqn_node
*first_eqn
; /* first equation parsed */
328 struct eqn_node
*eqn
; /* current equation being parsed */
329 int eqn_inline
; /* current equation is inline */
330 int options
; /* parse options */
331 int rstacksz
; /* current size limit of rstack */
332 int rstackpos
; /* position in rstack */
333 int format
; /* current file in mdoc or man format */
334 char control
; /* control character */
338 enum rofft tok
; /* type of node */
339 struct roffnode
*parent
; /* up one in stack */
340 int line
; /* parse line */
341 int col
; /* parse col */
342 char *name
; /* node name, e.g. macro name */
343 char *end
; /* end-rules: custom token */
344 int endspan
; /* end-rules: next-line or infty */
345 int rule
; /* current evaluation rule */
348 #define ROFF_ARGS struct roff *r, /* parse ctx */ \
349 enum rofft tok, /* tok of macro */ \
350 struct buf *buf, /* input buffer */ \
351 int ln, /* parse line */ \
352 int ppos, /* original pos in buffer */ \
353 int pos, /* current pos in buffer */ \
354 int *offs /* reset offset of buffer data */
356 typedef enum rofferr (*roffproc
)(ROFF_ARGS
);
359 const char *name
; /* macro name */
360 roffproc proc
; /* process new macro */
361 roffproc text
; /* process as child text of macro */
362 roffproc sub
; /* process as child of macro */
364 #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
365 struct roffmac
*next
;
369 const char *name
; /* predefined input name */
370 const char *str
; /* replacement symbol */
373 #define PREDEF(__name, __str) \
374 { (__name), (__str) },
376 static enum rofft
roffhash_find(const char *, size_t);
377 static void roffhash_init(void);
378 static void roffnode_cleanscope(struct roff
*);
379 static void roffnode_pop(struct roff
*);
380 static void roffnode_push(struct roff
*, enum rofft
,
381 const char *, int, int);
382 static enum rofferr
roff_block(ROFF_ARGS
);
383 static enum rofferr
roff_block_text(ROFF_ARGS
);
384 static enum rofferr
roff_block_sub(ROFF_ARGS
);
385 static enum rofferr
roff_brp(ROFF_ARGS
);
386 static enum rofferr
roff_cblock(ROFF_ARGS
);
387 static enum rofferr
roff_cc(ROFF_ARGS
);
388 static void roff_ccond(struct roff
*, int, int);
389 static enum rofferr
roff_cond(ROFF_ARGS
);
390 static enum rofferr
roff_cond_text(ROFF_ARGS
);
391 static enum rofferr
roff_cond_sub(ROFF_ARGS
);
392 static enum rofferr
roff_ds(ROFF_ARGS
);
393 static enum rofferr
roff_eqndelim(struct roff
*, struct buf
*, int);
394 static int roff_evalcond(struct roff
*r
, int,
395 const char *, int *);
396 static int roff_evalnum(struct roff
*, int,
397 const char *, int *, int *, int);
398 static int roff_evalpar(struct roff
*, int,
399 const char *, int *, int *);
400 static int roff_evalstrcond(const char *, int *);
401 static void roff_free1(struct roff
*);
402 static void roff_freereg(struct roffreg
*);
403 static void roff_freestr(struct roffkv
*);
404 static size_t roff_getname(struct roff
*, char **, int, int);
405 static int roff_getnum(const char *, int *, int *);
406 static int roff_getop(const char *, int *, char *);
407 static int roff_getregn(const struct roff
*,
408 const char *, size_t);
409 static int roff_getregro(const char *name
);
410 static const char *roff_getstrn(const struct roff
*,
411 const char *, size_t);
412 static enum rofferr
roff_insec(ROFF_ARGS
);
413 static enum rofferr
roff_it(ROFF_ARGS
);
414 static enum rofferr
roff_line_ignore(ROFF_ARGS
);
415 static enum rofferr
roff_nr(ROFF_ARGS
);
416 static enum rofft
roff_parse(struct roff
*, char *, int *,
418 static enum rofferr
roff_parsetext(struct buf
*, int, int *);
419 static enum rofferr
roff_res(struct roff
*, struct buf
*, int, int);
420 static enum rofferr
roff_rm(ROFF_ARGS
);
421 static enum rofferr
roff_rr(ROFF_ARGS
);
422 static void roff_setstr(struct roff
*,
423 const char *, const char *, int);
424 static void roff_setstrn(struct roffkv
**, const char *,
425 size_t, const char *, size_t, int);
426 static enum rofferr
roff_so(ROFF_ARGS
);
427 static enum rofferr
roff_tr(ROFF_ARGS
);
428 static enum rofferr
roff_Dd(ROFF_ARGS
);
429 static enum rofferr
roff_TH(ROFF_ARGS
);
430 static enum rofferr
roff_TE(ROFF_ARGS
);
431 static enum rofferr
roff_TS(ROFF_ARGS
);
432 static enum rofferr
roff_EQ(ROFF_ARGS
);
433 static enum rofferr
roff_EN(ROFF_ARGS
);
434 static enum rofferr
roff_T_(ROFF_ARGS
);
435 static enum rofferr
roff_unsupp(ROFF_ARGS
);
436 static enum rofferr
roff_userdef(ROFF_ARGS
);
438 /* See roffhash_find() */
442 #define HASHWIDTH (ASCII_HI - ASCII_LO + 1)
444 static struct roffmac
*hash
[HASHWIDTH
];
446 static struct roffmac roffs
[ROFF_MAX
] = {
447 { "ab", roff_unsupp
, NULL
, NULL
, 0, NULL
},
448 { "ad", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
449 { "af", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
450 { "aln", roff_unsupp
, NULL
, NULL
, 0, NULL
},
451 { "als", roff_unsupp
, NULL
, NULL
, 0, NULL
},
452 { "am", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
453 { "am1", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
454 { "ami", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
455 { "ami1", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
456 { "as", roff_ds
, NULL
, NULL
, 0, NULL
},
457 { "as1", roff_ds
, NULL
, NULL
, 0, NULL
},
458 { "asciify", roff_unsupp
, NULL
, NULL
, 0, NULL
},
459 { "backtrace", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
460 { "bd", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
461 { "bleedat", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
462 { "blm", roff_unsupp
, NULL
, NULL
, 0, NULL
},
463 { "box", roff_unsupp
, NULL
, NULL
, 0, NULL
},
464 { "boxa", roff_unsupp
, NULL
, NULL
, 0, NULL
},
465 { "bp", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
466 { "BP", roff_unsupp
, NULL
, NULL
, 0, NULL
},
467 { "break", roff_unsupp
, NULL
, NULL
, 0, NULL
},
468 { "breakchar", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
469 { "brnl", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
470 { "brp", roff_brp
, NULL
, NULL
, 0, NULL
},
471 { "brpnl", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
472 { "c2", roff_unsupp
, NULL
, NULL
, 0, NULL
},
473 { "cc", roff_cc
, NULL
, NULL
, 0, NULL
},
474 { "ce", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
475 { "cf", roff_insec
, NULL
, NULL
, 0, NULL
},
476 { "cflags", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
477 { "ch", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
478 { "char", roff_unsupp
, NULL
, NULL
, 0, NULL
},
479 { "chop", roff_unsupp
, NULL
, NULL
, 0, NULL
},
480 { "class", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
481 { "close", roff_insec
, NULL
, NULL
, 0, NULL
},
482 { "CL", roff_unsupp
, NULL
, NULL
, 0, NULL
},
483 { "color", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
484 { "composite", roff_unsupp
, NULL
, NULL
, 0, NULL
},
485 { "continue", roff_unsupp
, NULL
, NULL
, 0, NULL
},
486 { "cp", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
487 { "cropat", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
488 { "cs", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
489 { "cu", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
490 { "da", roff_unsupp
, NULL
, NULL
, 0, NULL
},
491 { "dch", roff_unsupp
, NULL
, NULL
, 0, NULL
},
492 { "Dd", roff_Dd
, NULL
, NULL
, 0, NULL
},
493 { "de", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
494 { "de1", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
495 { "defcolor", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
496 { "dei", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
497 { "dei1", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
498 { "device", roff_unsupp
, NULL
, NULL
, 0, NULL
},
499 { "devicem", roff_unsupp
, NULL
, NULL
, 0, NULL
},
500 { "di", roff_unsupp
, NULL
, NULL
, 0, NULL
},
501 { "do", roff_unsupp
, NULL
, NULL
, 0, NULL
},
502 { "ds", roff_ds
, NULL
, NULL
, 0, NULL
},
503 { "ds1", roff_ds
, NULL
, NULL
, 0, NULL
},
504 { "dwh", roff_unsupp
, NULL
, NULL
, 0, NULL
},
505 { "dt", roff_unsupp
, NULL
, NULL
, 0, NULL
},
506 { "ec", roff_unsupp
, NULL
, NULL
, 0, NULL
},
507 { "ecr", roff_unsupp
, NULL
, NULL
, 0, NULL
},
508 { "ecs", roff_unsupp
, NULL
, NULL
, 0, NULL
},
509 { "el", roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
, NULL
},
510 { "em", roff_unsupp
, NULL
, NULL
, 0, NULL
},
511 { "EN", roff_EN
, NULL
, NULL
, 0, NULL
},
512 { "eo", roff_unsupp
, NULL
, NULL
, 0, NULL
},
513 { "EP", roff_unsupp
, NULL
, NULL
, 0, NULL
},
514 { "EQ", roff_EQ
, NULL
, NULL
, 0, NULL
},
515 { "errprint", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
516 { "ev", roff_unsupp
, NULL
, NULL
, 0, NULL
},
517 { "evc", roff_unsupp
, NULL
, NULL
, 0, NULL
},
518 { "ex", roff_unsupp
, NULL
, NULL
, 0, NULL
},
519 { "fallback", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
520 { "fam", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
521 { "fc", roff_unsupp
, NULL
, NULL
, 0, NULL
},
522 { "fchar", roff_unsupp
, NULL
, NULL
, 0, NULL
},
523 { "fcolor", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
524 { "fdeferlig", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
525 { "feature", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
526 { "fkern", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
527 { "fl", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
528 { "flig", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
529 { "fp", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
530 { "fps", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
531 { "fschar", roff_unsupp
, NULL
, NULL
, 0, NULL
},
532 { "fspacewidth", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
533 { "fspecial", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
534 { "ftr", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
535 { "fzoom", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
536 { "gcolor", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
537 { "hc", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
538 { "hcode", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
539 { "hidechar", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
540 { "hla", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
541 { "hlm", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
542 { "hpf", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
543 { "hpfa", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
544 { "hpfcode", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
545 { "hw", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
546 { "hy", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
547 { "hylang", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
548 { "hylen", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
549 { "hym", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
550 { "hypp", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
551 { "hys", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
552 { "ie", roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
, NULL
},
553 { "if", roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
, NULL
},
554 { "ig", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
555 { "index", roff_unsupp
, NULL
, NULL
, 0, NULL
},
556 { "it", roff_it
, NULL
, NULL
, 0, NULL
},
557 { "itc", roff_unsupp
, NULL
, NULL
, 0, NULL
},
558 { "IX", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
559 { "kern", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
560 { "kernafter", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
561 { "kernbefore", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
562 { "kernpair", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
563 { "lc", roff_unsupp
, NULL
, NULL
, 0, NULL
},
564 { "lc_ctype", roff_unsupp
, NULL
, NULL
, 0, NULL
},
565 { "lds", roff_unsupp
, NULL
, NULL
, 0, NULL
},
566 { "length", roff_unsupp
, NULL
, NULL
, 0, NULL
},
567 { "letadj", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
568 { "lf", roff_insec
, NULL
, NULL
, 0, NULL
},
569 { "lg", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
570 { "lhang", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
571 { "linetabs", roff_unsupp
, NULL
, NULL
, 0, NULL
},
572 { "lnr", roff_unsupp
, NULL
, NULL
, 0, NULL
},
573 { "lnrf", roff_unsupp
, NULL
, NULL
, 0, NULL
},
574 { "lpfx", roff_unsupp
, NULL
, NULL
, 0, NULL
},
575 { "ls", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
576 { "lsm", roff_unsupp
, NULL
, NULL
, 0, NULL
},
577 { "lt", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
578 { "mc", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
579 { "mediasize", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
580 { "minss", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
581 { "mk", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
582 { "mso", roff_insec
, NULL
, NULL
, 0, NULL
},
583 { "na", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
584 { "ne", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
585 { "nh", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
586 { "nhychar", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
587 { "nm", roff_unsupp
, NULL
, NULL
, 0, NULL
},
588 { "nn", roff_unsupp
, NULL
, NULL
, 0, NULL
},
589 { "nop", roff_unsupp
, NULL
, NULL
, 0, NULL
},
590 { "nr", roff_nr
, NULL
, NULL
, 0, NULL
},
591 { "nrf", roff_unsupp
, NULL
, NULL
, 0, NULL
},
592 { "nroff", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
593 { "ns", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
594 { "nx", roff_insec
, NULL
, NULL
, 0, NULL
},
595 { "open", roff_insec
, NULL
, NULL
, 0, NULL
},
596 { "opena", roff_insec
, NULL
, NULL
, 0, NULL
},
597 { "os", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
598 { "output", roff_unsupp
, NULL
, NULL
, 0, NULL
},
599 { "padj", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
600 { "papersize", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
601 { "pc", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
602 { "pev", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
603 { "pi", roff_insec
, NULL
, NULL
, 0, NULL
},
604 { "PI", roff_unsupp
, NULL
, NULL
, 0, NULL
},
605 { "pl", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
606 { "pm", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
607 { "pn", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
608 { "pnr", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
609 { "po", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
610 { "ps", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
611 { "psbb", roff_unsupp
, NULL
, NULL
, 0, NULL
},
612 { "pshape", roff_unsupp
, NULL
, NULL
, 0, NULL
},
613 { "pso", roff_insec
, NULL
, NULL
, 0, NULL
},
614 { "ptr", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
615 { "pvs", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
616 { "rchar", roff_unsupp
, NULL
, NULL
, 0, NULL
},
617 { "rd", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
618 { "recursionlimit", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
619 { "return", roff_unsupp
, NULL
, NULL
, 0, NULL
},
620 { "rfschar", roff_unsupp
, NULL
, NULL
, 0, NULL
},
621 { "rhang", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
622 { "rj", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
623 { "rm", roff_rm
, NULL
, NULL
, 0, NULL
},
624 { "rn", roff_unsupp
, NULL
, NULL
, 0, NULL
},
625 { "rnn", roff_unsupp
, NULL
, NULL
, 0, NULL
},
626 { "rr", roff_rr
, NULL
, NULL
, 0, NULL
},
627 { "rs", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
628 { "rt", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
629 { "schar", roff_unsupp
, NULL
, NULL
, 0, NULL
},
630 { "sentchar", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
631 { "shc", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
632 { "shift", roff_unsupp
, NULL
, NULL
, 0, NULL
},
633 { "sizes", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
634 { "so", roff_so
, NULL
, NULL
, 0, NULL
},
635 { "spacewidth", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
636 { "special", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
637 { "spreadwarn", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
638 { "ss", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
639 { "sty", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
640 { "substring", roff_unsupp
, NULL
, NULL
, 0, NULL
},
641 { "sv", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
642 { "sy", roff_insec
, NULL
, NULL
, 0, NULL
},
643 { "T&", roff_T_
, NULL
, NULL
, 0, NULL
},
644 { "ta", roff_unsupp
, NULL
, NULL
, 0, NULL
},
645 { "tc", roff_unsupp
, NULL
, NULL
, 0, NULL
},
646 { "TE", roff_TE
, NULL
, NULL
, 0, NULL
},
647 { "TH", roff_TH
, NULL
, NULL
, 0, NULL
},
648 { "ti", roff_unsupp
, NULL
, NULL
, 0, NULL
},
649 { "tkf", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
650 { "tl", roff_unsupp
, NULL
, NULL
, 0, NULL
},
651 { "tm", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
652 { "tm1", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
653 { "tmc", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
654 { "tr", roff_tr
, NULL
, NULL
, 0, NULL
},
655 { "track", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
656 { "transchar", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
657 { "trf", roff_insec
, NULL
, NULL
, 0, NULL
},
658 { "trimat", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
659 { "trin", roff_unsupp
, NULL
, NULL
, 0, NULL
},
660 { "trnt", roff_unsupp
, NULL
, NULL
, 0, NULL
},
661 { "troff", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
662 { "TS", roff_TS
, NULL
, NULL
, 0, NULL
},
663 { "uf", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
664 { "ul", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
665 { "unformat", roff_unsupp
, NULL
, NULL
, 0, NULL
},
666 { "unwatch", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
667 { "unwatchn", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
668 { "vpt", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
669 { "vs", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
670 { "warn", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
671 { "warnscale", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
672 { "watch", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
673 { "watchlength", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
674 { "watchn", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
675 { "wh", roff_unsupp
, NULL
, NULL
, 0, NULL
},
676 { "while", roff_unsupp
, NULL
, NULL
, 0, NULL
},
677 { "write", roff_insec
, NULL
, NULL
, 0, NULL
},
678 { "writec", roff_insec
, NULL
, NULL
, 0, NULL
},
679 { "writem", roff_insec
, NULL
, NULL
, 0, NULL
},
680 { "xflag", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
681 { ".", roff_cblock
, NULL
, NULL
, 0, NULL
},
682 { NULL
, roff_userdef
, NULL
, NULL
, 0, NULL
},
685 /* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */
686 const char *const __mdoc_reserved
[] = {
687 "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At",
688 "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq",
689 "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx",
690 "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq",
691 "Dt", "Dv", "Dx", "D1",
692 "Ec", "Ed", "Ef", "Ek", "El", "Em",
693 "En", "Eo", "Er", "Es", "Ev", "Ex",
694 "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx",
695 "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp",
696 "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx",
697 "Oc", "Oo", "Op", "Os", "Ot", "Ox",
698 "Pa", "Pc", "Pf", "Po", "Pp", "Pq",
699 "Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv",
700 "Sc", "Sh", "Sm", "So", "Sq",
701 "Ss", "St", "Sx", "Sy",
702 "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr",
703 "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O",
704 "%P", "%Q", "%R", "%T", "%U", "%V",
708 /* not currently implemented: BT DE DS ME MT PT SY TQ YS */
709 const char *const __man_reserved
[] = {
710 "AT", "B", "BI", "BR", "DT",
711 "EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR",
712 "LP", "OP", "P", "PD", "PP",
713 "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS",
714 "TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR",
718 /* Array of injected predefined strings. */
719 #define PREDEFS_MAX 38
720 static const struct predef predefs
[PREDEFS_MAX
] = {
721 #include "predefs.in"
724 /* See roffhash_find() */
725 #define ROFF_HASH(p) (p[0] - ASCII_LO)
727 static int roffit_lines
; /* number of lines to delay */
728 static char *roffit_macro
; /* nil-terminated macro line */
737 for (i
= 0; i
< (int)ROFF_USERDEF
; i
++) {
738 assert(roffs
[i
].name
[0] >= ASCII_LO
);
739 assert(roffs
[i
].name
[0] <= ASCII_HI
);
741 buc
= ROFF_HASH(roffs
[i
].name
);
743 if (NULL
!= (n
= hash
[buc
])) {
744 for ( ; n
->next
; n
= n
->next
)
748 hash
[buc
] = &roffs
[i
];
753 * Look up a roff token by its name. Returns ROFF_MAX if no macro by
754 * the nil-terminated string name could be found.
757 roffhash_find(const char *p
, size_t s
)
763 * libroff has an extremely simple hashtable, for the time
764 * being, which simply keys on the first character, which must
765 * be printable, then walks a chain. It works well enough until
769 if (p
[0] < ASCII_LO
|| p
[0] > ASCII_HI
)
774 if (NULL
== (n
= hash
[buc
]))
776 for ( ; n
; n
= n
->next
)
777 if (0 == strncmp(n
->name
, p
, s
) && '\0' == n
->name
[(int)s
])
778 return((enum rofft
)(n
- roffs
));
784 * Pop the current node off of the stack of roff instructions currently
788 roffnode_pop(struct roff
*r
)
795 r
->last
= r
->last
->parent
;
802 * Push a roff node onto the instruction stack. This must later be
803 * removed with roffnode_pop().
806 roffnode_push(struct roff
*r
, enum rofft tok
, const char *name
,
811 p
= mandoc_calloc(1, sizeof(struct roffnode
));
814 p
->name
= mandoc_strdup(name
);
818 p
->rule
= p
->parent
? p
->parent
->rule
: 0;
824 roff_free1(struct roff
*r
)
826 struct tbl_node
*tbl
;
830 while (NULL
!= (tbl
= r
->first_tbl
)) {
831 r
->first_tbl
= tbl
->next
;
834 r
->first_tbl
= r
->last_tbl
= r
->tbl
= NULL
;
836 while (NULL
!= (e
= r
->first_eqn
)) {
837 r
->first_eqn
= e
->next
;
840 r
->first_eqn
= r
->last_eqn
= r
->eqn
= NULL
;
850 roff_freereg(r
->regtab
);
853 roff_freestr(r
->strtab
);
854 roff_freestr(r
->xmbtab
);
855 r
->strtab
= r
->xmbtab
= NULL
;
858 for (i
= 0; i
< 128; i
++)
865 roff_reset(struct roff
*r
)
869 r
->format
= r
->options
& (MPARSE_MDOC
| MPARSE_MAN
);
874 roff_free(struct roff
*r
)
882 roff_alloc(struct mparse
*parse
, const struct mchars
*mchars
, int options
)
886 r
= mandoc_calloc(1, sizeof(struct roff
));
889 r
->options
= options
;
890 r
->format
= options
& (MPARSE_MDOC
| MPARSE_MAN
);
899 * In the current line, expand escape sequences that tend to get
900 * used in numerical expressions and conditional requests.
901 * Also check the syntax of the remaining escape sequences.
904 roff_res(struct roff
*r
, struct buf
*buf
, int ln
, int pos
)
906 char ubuf
[24]; /* buffer to print the number */
907 const char *start
; /* start of the string to process */
908 char *stesc
; /* start of an escape sequence ('\\') */
909 const char *stnam
; /* start of the name, after "[(*" */
910 const char *cp
; /* end of the name, e.g. before ']' */
911 const char *res
; /* the string to be substituted */
912 char *nbuf
; /* new buffer to copy buf->buf to */
913 size_t maxl
; /* expected length of the escape name */
914 size_t naml
; /* actual length of the escape name */
915 enum mandoc_esc esc
; /* type of the escape sequence */
916 int inaml
; /* length returned from mandoc_escape() */
917 int expand_count
; /* to avoid infinite loops */
918 int npos
; /* position in numeric expression */
919 int arg_complete
; /* argument not interrupted by eol */
920 char term
; /* character terminating the escape */
923 start
= buf
->buf
+ pos
;
924 stesc
= strchr(start
, '\0') - 1;
925 while (stesc
-- > start
) {
927 /* Search backwards for the next backslash. */
932 /* If it is escaped, skip it. */
934 for (cp
= stesc
- 1; cp
>= start
; cp
--)
938 if ((stesc
- cp
) % 2 == 0) {
943 /* Decide whether to expand or to check only. */
960 esc
= mandoc_escape(&cp
, &stnam
, &inaml
);
961 if (esc
== ESCAPE_ERROR
||
962 (esc
== ESCAPE_SPECIAL
&&
963 mchars_spec2cp(r
->mchars
, stnam
, inaml
) < 0))
964 mandoc_vmsg(MANDOCERR_ESC_BAD
,
965 r
->parse
, ln
, (int)(stesc
- buf
->buf
),
966 "%.*s", (int)(cp
- stesc
), stesc
);
970 if (EXPAND_LIMIT
< ++expand_count
) {
971 mandoc_msg(MANDOCERR_ROFFLOOP
, r
->parse
,
972 ln
, (int)(stesc
- buf
->buf
), NULL
);
977 * The third character decides the length
978 * of the name of the string or register.
979 * Save a pointer to the name.
1006 /* Advance to the end of the name. */
1010 while (maxl
== 0 || naml
< maxl
) {
1012 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
1013 ln
, (int)(stesc
- buf
->buf
), stesc
);
1017 if (maxl
== 0 && *cp
== term
) {
1021 if (*cp
++ != '\\' || stesc
[1] != 'w') {
1025 switch (mandoc_escape(&cp
, NULL
, NULL
)) {
1026 case ESCAPE_SPECIAL
:
1028 case ESCAPE_UNICODE
:
1030 case ESCAPE_NUMBERED
:
1032 case ESCAPE_OVERSTRIKE
:
1041 * Retrieve the replacement string; if it is
1042 * undefined, resume searching for escapes.
1048 res
= roff_getstrn(r
, stnam
, naml
);
1052 ubuf
[0] = arg_complete
&&
1053 roff_evalnum(r
, ln
, stnam
, &npos
, NULL
, 0) &&
1054 stnam
+ npos
+ 1 == cp
? '1' : '0';
1059 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
1060 roff_getregn(r
, stnam
, naml
));
1065 /* use even incomplete args */
1066 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
1072 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
1073 r
->parse
, ln
, (int)(stesc
- buf
->buf
),
1074 "%.*s", (int)naml
, stnam
);
1076 } else if (buf
->sz
+ strlen(res
) > SHRT_MAX
) {
1077 mandoc_msg(MANDOCERR_ROFFLOOP
, r
->parse
,
1078 ln
, (int)(stesc
- buf
->buf
), NULL
);
1082 /* Replace the escape sequence by the string. */
1085 buf
->sz
= mandoc_asprintf(&nbuf
, "%s%s%s",
1086 buf
->buf
, res
, cp
) + 1;
1088 /* Prepare for the next replacement. */
1091 stesc
= nbuf
+ (stesc
- buf
->buf
) + strlen(res
);
1099 * Process text streams:
1100 * Convert all breakable hyphens into ASCII_HYPH.
1101 * Decrement and spring input line trap.
1104 roff_parsetext(struct buf
*buf
, int pos
, int *offs
)
1110 enum mandoc_esc esc
;
1112 start
= p
= buf
->buf
+ pos
;
1114 while (*p
!= '\0') {
1115 sz
= strcspn(p
, "-\\");
1122 /* Skip over escapes. */
1124 esc
= mandoc_escape((const char **)&p
, NULL
, NULL
);
1125 if (esc
== ESCAPE_ERROR
)
1128 } else if (p
== start
) {
1133 if (isalpha((unsigned char)p
[-1]) &&
1134 isalpha((unsigned char)p
[1]))
1139 /* Spring the input line trap. */
1140 if (roffit_lines
== 1) {
1141 isz
= mandoc_asprintf(&p
, "%s\n.%s", buf
->buf
, roffit_macro
);
1148 return(ROFF_REPARSE
);
1149 } else if (roffit_lines
> 1)
1155 roff_parseln(struct roff
*r
, int ln
, struct buf
*buf
, int *offs
)
1159 int pos
; /* parse point */
1160 int spos
; /* saved parse point for messages */
1161 int ppos
; /* original offset in buf->buf */
1162 int ctl
; /* macro line (boolean) */
1166 /* Handle in-line equation delimiters. */
1168 if (r
->tbl
== NULL
&&
1169 r
->last_eqn
!= NULL
&& r
->last_eqn
->delim
&&
1170 (r
->eqn
== NULL
|| r
->eqn_inline
)) {
1171 e
= roff_eqndelim(r
, buf
, pos
);
1172 if (e
== ROFF_REPARSE
)
1174 assert(e
== ROFF_CONT
);
1177 /* Expand some escape sequences. */
1179 e
= roff_res(r
, buf
, ln
, pos
);
1182 assert(e
== ROFF_CONT
);
1184 ctl
= roff_getcontrol(r
, buf
->buf
, &pos
);
1187 * First, if a scope is open and we're not a macro, pass the
1188 * text through the macro's filter.
1189 * Equations process all content themselves.
1190 * Tables process almost all content themselves, but we want
1191 * to warn about macros before passing it there.
1194 if (r
->last
!= NULL
&& ! ctl
) {
1196 assert(roffs
[t
].text
);
1197 e
= (*roffs
[t
].text
)(r
, t
, buf
, ln
, pos
, pos
, offs
);
1198 assert(e
== ROFF_IGN
|| e
== ROFF_CONT
);
1203 return(eqn_read(&r
->eqn
, ln
, buf
->buf
, ppos
, offs
));
1204 if (r
->tbl
!= NULL
&& ( ! ctl
|| buf
->buf
[pos
] == '\0'))
1205 return(tbl_read(r
->tbl
, ln
, buf
->buf
, ppos
));
1207 return(roff_parsetext(buf
, pos
, offs
));
1209 /* Skip empty request lines. */
1211 if (buf
->buf
[pos
] == '"') {
1212 mandoc_msg(MANDOCERR_COMMENT_BAD
, r
->parse
,
1215 } else if (buf
->buf
[pos
] == '\0')
1219 * If a scope is open, go to the child handler for that macro,
1220 * as it may want to preprocess before doing anything with it.
1221 * Don't do so if an equation is open.
1226 assert(roffs
[t
].sub
);
1227 return((*roffs
[t
].sub
)(r
, t
, buf
, ln
, ppos
, pos
, offs
));
1230 /* No scope is open. This is a new request or macro. */
1233 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
1235 /* Tables ignore most macros. */
1237 if (r
->tbl
!= NULL
&& (t
== ROFF_MAX
|| t
== ROFF_TS
)) {
1238 mandoc_msg(MANDOCERR_TBLMACRO
, r
->parse
,
1239 ln
, pos
, buf
->buf
+ spos
);
1242 while (buf
->buf
[pos
] != '\0' && buf
->buf
[pos
] != ' ')
1244 while (buf
->buf
[pos
] != '\0' && buf
->buf
[pos
] == ' ')
1246 return(tbl_read(r
->tbl
, ln
, buf
->buf
, pos
));
1250 * This is neither a roff request nor a user-defined macro.
1251 * Let the standard macro set parsers handle it.
1257 /* Execute a roff request or a user defined macro. */
1259 assert(roffs
[t
].proc
);
1260 return((*roffs
[t
].proc
)(r
, t
, buf
, ln
, ppos
, pos
, offs
));
1264 roff_endparse(struct roff
*r
)
1268 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
1269 r
->last
->line
, r
->last
->col
,
1270 roffs
[r
->last
->tok
].name
);
1273 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
1274 r
->eqn
->eqn
.ln
, r
->eqn
->eqn
.pos
, "EQ");
1279 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
1280 r
->tbl
->line
, r
->tbl
->pos
, "TS");
1286 * Parse a roff node's type from the input buffer. This must be in the
1287 * form of ".foo xxx" in the usual way.
1290 roff_parse(struct roff
*r
, char *buf
, int *pos
, int ln
, int ppos
)
1299 if ('\0' == *cp
|| '"' == *cp
|| '\t' == *cp
|| ' ' == *cp
)
1303 maclen
= roff_getname(r
, &cp
, ln
, ppos
);
1305 t
= (r
->current_string
= roff_getstrn(r
, mac
, maclen
))
1306 ? ROFF_USERDEF
: roffhash_find(mac
, maclen
);
1315 roff_cblock(ROFF_ARGS
)
1319 * A block-close `..' should only be invoked as a child of an
1320 * ignore macro, otherwise raise a warning and just ignore it.
1323 if (r
->last
== NULL
) {
1324 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1329 switch (r
->last
->tok
) {
1331 /* ROFF_am1 is remapped to ROFF_am in roff_block(). */
1336 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
1343 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1348 if (buf
->buf
[pos
] != '\0')
1349 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
1350 ".. %s", buf
->buf
+ pos
);
1353 roffnode_cleanscope(r
);
1359 roffnode_cleanscope(struct roff
*r
)
1363 if (--r
->last
->endspan
!= 0)
1370 roff_ccond(struct roff
*r
, int ln
, int ppos
)
1373 if (NULL
== r
->last
) {
1374 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1379 switch (r
->last
->tok
) {
1387 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1392 if (r
->last
->endspan
> -1) {
1393 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1399 roffnode_cleanscope(r
);
1404 roff_block(ROFF_ARGS
)
1410 /* Ignore groff compatibility mode for now. */
1412 if (tok
== ROFF_de1
)
1414 else if (tok
== ROFF_dei1
)
1416 else if (tok
== ROFF_am1
)
1418 else if (tok
== ROFF_ami1
)
1421 /* Parse the macro name argument. */
1423 cp
= buf
->buf
+ pos
;
1424 if (tok
== ROFF_ig
) {
1429 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
1430 iname
[namesz
] = '\0';
1433 /* Resolve the macro name argument if it is indirect. */
1435 if (namesz
&& (tok
== ROFF_dei
|| tok
== ROFF_ami
)) {
1436 if ((name
= roff_getstrn(r
, iname
, namesz
)) == NULL
) {
1437 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
1438 r
->parse
, ln
, (int)(iname
- buf
->buf
),
1439 "%.*s", (int)namesz
, iname
);
1442 namesz
= strlen(name
);
1446 if (namesz
== 0 && tok
!= ROFF_ig
) {
1447 mandoc_msg(MANDOCERR_REQ_EMPTY
, r
->parse
,
1448 ln
, ppos
, roffs
[tok
].name
);
1452 roffnode_push(r
, tok
, name
, ln
, ppos
);
1455 * At the beginning of a `de' macro, clear the existing string
1456 * with the same name, if there is one. New content will be
1457 * appended from roff_block_text() in multiline mode.
1460 if (tok
== ROFF_de
|| tok
== ROFF_dei
)
1461 roff_setstrn(&r
->strtab
, name
, namesz
, "", 0, 0);
1466 /* Get the custom end marker. */
1469 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
1471 /* Resolve the end marker if it is indirect. */
1473 if (namesz
&& (tok
== ROFF_dei
|| tok
== ROFF_ami
)) {
1474 if ((name
= roff_getstrn(r
, iname
, namesz
)) == NULL
) {
1475 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
1476 r
->parse
, ln
, (int)(iname
- buf
->buf
),
1477 "%.*s", (int)namesz
, iname
);
1480 namesz
= strlen(name
);
1485 r
->last
->end
= mandoc_strndup(name
, namesz
);
1488 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
1489 ln
, pos
, ".%s ... %s", roffs
[tok
].name
, cp
);
1495 roff_block_sub(ROFF_ARGS
)
1501 * First check whether a custom macro exists at this level. If
1502 * it does, then check against it. This is some of groff's
1503 * stranger behaviours. If we encountered a custom end-scope
1504 * tag and that tag also happens to be a "real" macro, then we
1505 * need to try interpreting it again as a real macro. If it's
1506 * not, then return ignore. Else continue.
1510 for (i
= pos
, j
= 0; r
->last
->end
[j
]; j
++, i
++)
1511 if (buf
->buf
[i
] != r
->last
->end
[j
])
1514 if (r
->last
->end
[j
] == '\0' &&
1515 (buf
->buf
[i
] == '\0' ||
1516 buf
->buf
[i
] == ' ' ||
1517 buf
->buf
[i
] == '\t')) {
1519 roffnode_cleanscope(r
);
1521 while (buf
->buf
[i
] == ' ' || buf
->buf
[i
] == '\t')
1525 if (roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
) !=
1533 * If we have no custom end-query or lookup failed, then try
1534 * pulling it out of the hashtable.
1537 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
1539 if (t
!= ROFF_cblock
) {
1541 roff_setstr(r
, r
->last
->name
, buf
->buf
+ ppos
, 2);
1545 assert(roffs
[t
].proc
);
1546 return((*roffs
[t
].proc
)(r
, t
, buf
, ln
, ppos
, pos
, offs
));
1550 roff_block_text(ROFF_ARGS
)
1554 roff_setstr(r
, r
->last
->name
, buf
->buf
+ pos
, 2);
1560 roff_cond_sub(ROFF_ARGS
)
1567 roffnode_cleanscope(r
);
1568 t
= roff_parse(r
, buf
->buf
, &pos
, ln
, ppos
);
1571 * Fully handle known macros when they are structurally
1572 * required or when the conditional evaluated to true.
1575 if ((t
!= ROFF_MAX
) &&
1576 (rr
|| roffs
[t
].flags
& ROFFMAC_STRUCT
)) {
1577 assert(roffs
[t
].proc
);
1578 return((*roffs
[t
].proc
)(r
, t
, buf
, ln
, ppos
, pos
, offs
));
1582 * If `\}' occurs on a macro line without a preceding macro,
1583 * drop the line completely.
1586 ep
= buf
->buf
+ pos
;
1587 if (ep
[0] == '\\' && ep
[1] == '}')
1590 /* Always check for the closing delimiter `\}'. */
1592 while ((ep
= strchr(ep
, '\\')) != NULL
) {
1593 if (*(++ep
) == '}') {
1595 roff_ccond(r
, ln
, ep
- buf
->buf
- 1);
1600 return(rr
? ROFF_CONT
: ROFF_IGN
);
1604 roff_cond_text(ROFF_ARGS
)
1610 roffnode_cleanscope(r
);
1612 ep
= buf
->buf
+ pos
;
1613 while ((ep
= strchr(ep
, '\\')) != NULL
) {
1614 if (*(++ep
) == '}') {
1616 roff_ccond(r
, ln
, ep
- buf
->buf
- 1);
1621 return(rr
? ROFF_CONT
: ROFF_IGN
);
1625 * Parse a single signed integer number. Stop at the first non-digit.
1626 * If there is at least one digit, return success and advance the
1627 * parse point, else return failure and let the parse point unchanged.
1628 * Ignore overflows, treat them just like the C language.
1631 roff_getnum(const char *v
, int *pos
, int *res
)
1643 for (*res
= 0; isdigit((unsigned char)v
[p
]); p
++)
1644 *res
= 10 * *res
+ v
[p
] - '0';
1651 /* Each number may be followed by one optional scaling unit. */
1694 * Evaluate a string comparison condition.
1695 * The first character is the delimiter.
1696 * Succeed if the string up to its second occurrence
1697 * matches the string up to its third occurence.
1698 * Advance the cursor after the third occurrence
1699 * or lacking that, to the end of the line.
1702 roff_evalstrcond(const char *v
, int *pos
)
1704 const char *s1
, *s2
, *s3
;
1708 s1
= v
+ *pos
; /* initial delimiter */
1709 s2
= s1
+ 1; /* for scanning the first string */
1710 s3
= strchr(s2
, *s1
); /* for scanning the second string */
1712 if (NULL
== s3
) /* found no middle delimiter */
1715 while ('\0' != *++s3
) {
1716 if (*s2
!= *s3
) { /* mismatch */
1717 s3
= strchr(s3
, *s1
);
1720 if (*s3
== *s1
) { /* found the final delimiter */
1729 s3
= strchr(s2
, '\0');
1730 else if (*s3
!= '\0')
1737 * Evaluate an optionally negated single character, numerical,
1738 * or string condition.
1741 roff_evalcond(struct roff
*r
, int ln
, const char *v
, int *pos
)
1743 int number
, savepos
, wanttrue
;
1745 if ('!' == v
[*pos
]) {
1777 if (roff_evalnum(r
, ln
, v
, pos
, &number
, 0))
1778 return((number
> 0) == wanttrue
);
1779 else if (*pos
== savepos
)
1780 return(roff_evalstrcond(v
, pos
) == wanttrue
);
1786 roff_line_ignore(ROFF_ARGS
)
1793 roff_insec(ROFF_ARGS
)
1796 mandoc_msg(MANDOCERR_REQ_INSEC
, r
->parse
,
1797 ln
, ppos
, roffs
[tok
].name
);
1802 roff_unsupp(ROFF_ARGS
)
1805 mandoc_msg(MANDOCERR_REQ_UNSUPP
, r
->parse
,
1806 ln
, ppos
, roffs
[tok
].name
);
1811 roff_cond(ROFF_ARGS
)
1814 roffnode_push(r
, tok
, NULL
, ln
, ppos
);
1817 * An `.el' has no conditional body: it will consume the value
1818 * of the current rstack entry set in prior `ie' calls or
1821 * If we're not an `el', however, then evaluate the conditional.
1824 r
->last
->rule
= tok
== ROFF_el
?
1825 (r
->rstackpos
< 0 ? 0 : r
->rstack
[r
->rstackpos
--]) :
1826 roff_evalcond(r
, ln
, buf
->buf
, &pos
);
1829 * An if-else will put the NEGATION of the current evaluated
1830 * conditional into the stack of rules.
1833 if (tok
== ROFF_ie
) {
1834 if (r
->rstackpos
+ 1 == r
->rstacksz
) {
1836 r
->rstack
= mandoc_reallocarray(r
->rstack
,
1837 r
->rstacksz
, sizeof(int));
1839 r
->rstack
[++r
->rstackpos
] = !r
->last
->rule
;
1842 /* If the parent has false as its rule, then so do we. */
1844 if (r
->last
->parent
&& !r
->last
->parent
->rule
)
1849 * If there is nothing on the line after the conditional,
1850 * not even whitespace, use next-line scope.
1853 if (buf
->buf
[pos
] == '\0') {
1854 r
->last
->endspan
= 2;
1858 while (buf
->buf
[pos
] == ' ')
1861 /* An opening brace requests multiline scope. */
1863 if (buf
->buf
[pos
] == '\\' && buf
->buf
[pos
+ 1] == '{') {
1864 r
->last
->endspan
= -1;
1870 * Anything else following the conditional causes
1871 * single-line scope. Warn if the scope contains
1872 * nothing but trailing whitespace.
1875 if (buf
->buf
[pos
] == '\0')
1876 mandoc_msg(MANDOCERR_COND_EMPTY
, r
->parse
,
1877 ln
, ppos
, roffs
[tok
].name
);
1879 r
->last
->endspan
= 1;
1893 /* Ignore groff compatibility mode for now. */
1895 if (tok
== ROFF_ds1
)
1897 else if (tok
== ROFF_as1
)
1901 * The first word is the name of the string.
1902 * If it is empty or terminated by an escape sequence,
1903 * abort the `ds' request without defining anything.
1906 name
= string
= buf
->buf
+ pos
;
1910 namesz
= roff_getname(r
, &string
, ln
, pos
);
1911 if (name
[namesz
] == '\\')
1914 /* Read past the initial double-quote, if any. */
1918 /* The rest is the value. */
1919 roff_setstrn(&r
->strtab
, name
, namesz
, string
, strlen(string
),
1925 * Parse a single operator, one or two characters long.
1926 * If the operator is recognized, return success and advance the
1927 * parse point, else return failure and let the parse point unchanged.
1930 roff_getop(const char *v
, int *pos
, char *res
)
1951 switch (v
[*pos
+ 1]) {
1969 switch (v
[*pos
+ 1]) {
1983 if ('=' == v
[*pos
+ 1])
1995 * Evaluate either a parenthesized numeric expression
1996 * or a single signed integer number.
1999 roff_evalpar(struct roff
*r
, int ln
,
2000 const char *v
, int *pos
, int *res
)
2004 return(roff_getnum(v
, pos
, res
));
2007 if ( ! roff_evalnum(r
, ln
, v
, pos
, res
, 1))
2011 * Omission of the closing parenthesis
2012 * is an error in validation mode,
2013 * but ignored in evaluation mode.
2018 else if (NULL
== res
)
2025 * Evaluate a complete numeric expression.
2026 * Proceed left to right, there is no concept of precedence.
2029 roff_evalnum(struct roff
*r
, int ln
, const char *v
,
2030 int *pos
, int *res
, int skipwhite
)
2032 int mypos
, operand2
;
2041 while (isspace((unsigned char)v
[*pos
]))
2044 if ( ! roff_evalpar(r
, ln
, v
, pos
, res
))
2049 while (isspace((unsigned char)v
[*pos
]))
2052 if ( ! roff_getop(v
, pos
, &operator))
2056 while (isspace((unsigned char)v
[*pos
]))
2059 if ( ! roff_evalpar(r
, ln
, v
, pos
, &operand2
))
2063 while (isspace((unsigned char)v
[*pos
]))
2080 if (operand2
== 0) {
2081 mandoc_msg(MANDOCERR_DIVZERO
,
2082 r
->parse
, ln
, *pos
, v
);
2089 if (operand2
== 0) {
2090 mandoc_msg(MANDOCERR_DIVZERO
,
2091 r
->parse
, ln
, *pos
, v
);
2098 *res
= *res
< operand2
;
2101 *res
= *res
> operand2
;
2104 *res
= *res
<= operand2
;
2107 *res
= *res
>= operand2
;
2110 *res
= *res
== operand2
;
2113 *res
= *res
!= operand2
;
2116 *res
= *res
&& operand2
;
2119 *res
= *res
|| operand2
;
2122 if (operand2
< *res
)
2126 if (operand2
> *res
)
2137 roff_setreg(struct roff
*r
, const char *name
, int val
, char sign
)
2139 struct roffreg
*reg
;
2141 /* Search for an existing register with the same name. */
2144 while (reg
&& strcmp(name
, reg
->key
.p
))
2148 /* Create a new register. */
2149 reg
= mandoc_malloc(sizeof(struct roffreg
));
2150 reg
->key
.p
= mandoc_strdup(name
);
2151 reg
->key
.sz
= strlen(name
);
2153 reg
->next
= r
->regtab
;
2159 else if ('-' == sign
)
2166 * Handle some predefined read-only number registers.
2167 * For now, return -1 if the requested register is not predefined;
2168 * in case a predefined read-only register having the value -1
2169 * were to turn up, another special value would have to be chosen.
2172 roff_getregro(const char *name
)
2176 case 'A': /* ASCII approximation mode is always off. */
2178 case 'g': /* Groff compatibility mode is always on. */
2180 case 'H': /* Fixed horizontal resolution. */
2182 case 'j': /* Always adjust left margin only. */
2184 case 'T': /* Some output device is always defined. */
2186 case 'V': /* Fixed vertical resolution. */
2194 roff_getreg(const struct roff
*r
, const char *name
)
2196 struct roffreg
*reg
;
2199 if ('.' == name
[0] && '\0' != name
[1] && '\0' == name
[2]) {
2200 val
= roff_getregro(name
+ 1);
2205 for (reg
= r
->regtab
; reg
; reg
= reg
->next
)
2206 if (0 == strcmp(name
, reg
->key
.p
))
2213 roff_getregn(const struct roff
*r
, const char *name
, size_t len
)
2215 struct roffreg
*reg
;
2218 if ('.' == name
[0] && 2 == len
) {
2219 val
= roff_getregro(name
+ 1);
2224 for (reg
= r
->regtab
; reg
; reg
= reg
->next
)
2225 if (len
== reg
->key
.sz
&&
2226 0 == strncmp(name
, reg
->key
.p
, len
))
2233 roff_freereg(struct roffreg
*reg
)
2235 struct roffreg
*old_reg
;
2237 while (NULL
!= reg
) {
2253 key
= val
= buf
->buf
+ pos
;
2257 keysz
= roff_getname(r
, &val
, ln
, pos
);
2258 if (key
[keysz
] == '\\')
2263 if (sign
== '+' || sign
== '-')
2266 if (roff_evalnum(r
, ln
, val
, NULL
, &iv
, 0))
2267 roff_setreg(r
, key
, iv
, sign
);
2275 struct roffreg
*reg
, **prev
;
2279 name
= cp
= buf
->buf
+ pos
;
2282 namesz
= roff_getname(r
, &cp
, ln
, pos
);
2283 name
[namesz
] = '\0';
2288 if (reg
== NULL
|| !strcmp(name
, reg
->key
.p
))
2307 cp
= buf
->buf
+ pos
;
2308 while (*cp
!= '\0') {
2310 namesz
= roff_getname(r
, &cp
, ln
, (int)(cp
- buf
->buf
));
2311 roff_setstrn(&r
->strtab
, name
, namesz
, NULL
, 0, 0);
2312 if (name
[namesz
] == '\\')
2325 /* Parse the number of lines. */
2326 cp
= buf
->buf
+ pos
;
2327 len
= strcspn(cp
, " \t");
2329 if ((iv
= mandoc_strntoi(cp
, len
, 10)) <= 0) {
2330 mandoc_msg(MANDOCERR_IT_NONUM
, r
->parse
,
2331 ln
, ppos
, buf
->buf
+ 1);
2336 /* Arm the input line trap. */
2338 roffit_macro
= mandoc_strdup(cp
);
2345 const char *const *cp
;
2347 if ((r
->options
& (MPARSE_MDOC
| MPARSE_QUICK
)) == 0)
2348 for (cp
= __mdoc_reserved
; *cp
; cp
++)
2349 roff_setstr(r
, *cp
, NULL
, 0);
2352 r
->format
= MPARSE_MDOC
;
2360 const char *const *cp
;
2362 if ((r
->options
& MPARSE_QUICK
) == 0)
2363 for (cp
= __man_reserved
; *cp
; cp
++)
2364 roff_setstr(r
, *cp
, NULL
, 0);
2367 r
->format
= MPARSE_MAN
;
2377 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
2379 else if ( ! tbl_end(&r
->tbl
)) {
2381 buf
->buf
= mandoc_strdup(".sp");
2383 return(ROFF_REPARSE
);
2393 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
2396 tbl_restart(ppos
, ln
, r
->tbl
);
2402 * Handle in-line equation delimiters.
2405 roff_eqndelim(struct roff
*r
, struct buf
*buf
, int pos
)
2408 const char *bef_pr
, *bef_nl
, *mac
, *aft_nl
, *aft_pr
;
2411 * Outside equations, look for an opening delimiter.
2412 * If we are inside an equation, we already know it is
2413 * in-line, or this function wouldn't have been called;
2414 * so look for a closing delimiter.
2417 cp1
= buf
->buf
+ pos
;
2418 cp2
= strchr(cp1
, r
->eqn
== NULL
?
2419 r
->last_eqn
->odelim
: r
->last_eqn
->cdelim
);
2424 bef_pr
= bef_nl
= aft_nl
= aft_pr
= "";
2426 /* Handle preceding text, protecting whitespace. */
2428 if (*buf
->buf
!= '\0') {
2435 * Prepare replacing the delimiter with an equation macro
2436 * and drop leading white space from the equation.
2439 if (r
->eqn
== NULL
) {
2446 /* Handle following text, protecting whitespace. */
2454 /* Do the actual replacement. */
2456 buf
->sz
= mandoc_asprintf(&cp1
, "%s%s%s%s%s%s%s", buf
->buf
,
2457 bef_pr
, bef_nl
, mac
, aft_nl
, aft_pr
, cp2
) + 1;
2461 /* Toggle the in-line state of the eqn subsystem. */
2463 r
->eqn_inline
= r
->eqn
== NULL
;
2464 return(ROFF_REPARSE
);
2472 assert(r
->eqn
== NULL
);
2473 e
= eqn_alloc(ppos
, ln
, r
->parse
);
2476 r
->last_eqn
->next
= e
;
2477 e
->delim
= r
->last_eqn
->delim
;
2478 e
->odelim
= r
->last_eqn
->odelim
;
2479 e
->cdelim
= r
->last_eqn
->cdelim
;
2481 r
->first_eqn
= r
->last_eqn
= e
;
2483 r
->eqn
= r
->last_eqn
= e
;
2485 if (buf
->buf
[pos
] != '\0')
2486 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
2487 ".EQ %s", buf
->buf
+ pos
);
2496 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
, ln
, ppos
, "EN");
2503 struct tbl_node
*tbl
;
2506 mandoc_msg(MANDOCERR_BLK_BROKEN
, r
->parse
,
2507 ln
, ppos
, "TS breaks TS");
2511 tbl
= tbl_alloc(ppos
, ln
, r
->parse
);
2514 r
->last_tbl
->next
= tbl
;
2516 r
->first_tbl
= r
->last_tbl
= tbl
;
2518 r
->tbl
= r
->last_tbl
= tbl
;
2526 buf
->buf
[pos
- 1] = '\0';
2537 if (*p
== '\0' || (r
->control
= *p
++) == '.')
2541 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
2542 ln
, p
- buf
->buf
, "cc ... %s", p
);
2550 const char *p
, *first
, *second
;
2552 enum mandoc_esc esc
;
2557 mandoc_msg(MANDOCERR_REQ_EMPTY
, r
->parse
, ln
, ppos
, "tr");
2561 while (*p
!= '\0') {
2565 if (*first
== '\\') {
2566 esc
= mandoc_escape(&p
, NULL
, NULL
);
2567 if (esc
== ESCAPE_ERROR
) {
2568 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
2569 ln
, (int)(p
- buf
->buf
), first
);
2572 fsz
= (size_t)(p
- first
);
2576 if (*second
== '\\') {
2577 esc
= mandoc_escape(&p
, NULL
, NULL
);
2578 if (esc
== ESCAPE_ERROR
) {
2579 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
2580 ln
, (int)(p
- buf
->buf
), second
);
2583 ssz
= (size_t)(p
- second
);
2584 } else if (*second
== '\0') {
2585 mandoc_vmsg(MANDOCERR_TR_ODD
, r
->parse
,
2586 ln
, first
- buf
->buf
, "tr %s", first
);
2592 roff_setstrn(&r
->xmbtab
, first
, fsz
,
2597 if (r
->xtab
== NULL
)
2598 r
->xtab
= mandoc_calloc(128,
2599 sizeof(struct roffstr
));
2601 free(r
->xtab
[(int)*first
].p
);
2602 r
->xtab
[(int)*first
].p
= mandoc_strndup(second
, ssz
);
2603 r
->xtab
[(int)*first
].sz
= ssz
;
2614 name
= buf
->buf
+ pos
;
2615 mandoc_vmsg(MANDOCERR_SO
, r
->parse
, ln
, ppos
, "so %s", name
);
2618 * Handle `so'. Be EXTREMELY careful, as we shouldn't be
2619 * opening anything that's not in our cwd or anything beneath
2620 * it. Thus, explicitly disallow traversing up the file-system
2621 * or using absolute paths.
2624 if (*name
== '/' || strstr(name
, "../") || strstr(name
, "/..")) {
2625 mandoc_vmsg(MANDOCERR_SO_PATH
, r
->parse
, ln
, ppos
,
2627 buf
->sz
= mandoc_asprintf(&cp
,
2628 ".sp\nSee the file %s.\n.sp", name
) + 1;
2632 return(ROFF_REPARSE
);
2640 roff_userdef(ROFF_ARGS
)
2647 * Collect pointers to macro argument strings
2648 * and NUL-terminate them.
2650 cp
= buf
->buf
+ pos
;
2651 for (i
= 0; i
< 9; i
++)
2652 arg
[i
] = *cp
== '\0' ? "" :
2653 mandoc_getarg(r
->parse
, &cp
, ln
, &pos
);
2656 * Expand macro arguments.
2659 n1
= cp
= mandoc_strdup(r
->current_string
);
2660 while ((cp
= strstr(cp
, "\\$")) != NULL
) {
2662 if (0 > i
|| 8 < i
) {
2663 /* Not an argument invocation. */
2668 buf
->sz
= mandoc_asprintf(&n2
, "%s%s%s",
2669 n1
, arg
[i
], cp
+ 3) + 1;
2670 cp
= n2
+ (cp
- n1
);
2676 * Replace the macro invocation
2677 * by the expanded macro.
2682 buf
->sz
= strlen(buf
->buf
) + 1;
2685 return(buf
->sz
> 1 && buf
->buf
[buf
->sz
- 2] == '\n' ?
2686 ROFF_REPARSE
: ROFF_APPEND
);
2690 roff_getname(struct roff
*r
, char **cpp
, int ln
, int pos
)
2699 /* Read until end of name and terminate it with NUL. */
2700 for (cp
= name
; 1; cp
++) {
2701 if ('\0' == *cp
|| ' ' == *cp
) {
2708 if ('{' == cp
[1] || '}' == cp
[1])
2713 mandoc_vmsg(MANDOCERR_NAMESC
, r
->parse
, ln
, pos
,
2714 "%.*s", (int)(cp
- name
+ 1), name
);
2715 mandoc_escape((const char **)&cp
, NULL
, NULL
);
2719 /* Read past spaces. */
2728 * Store *string into the user-defined string called *name.
2729 * To clear an existing entry, call with (*r, *name, NULL, 0).
2730 * append == 0: replace mode
2731 * append == 1: single-line append mode
2732 * append == 2: multiline append mode, append '\n' after each call
2735 roff_setstr(struct roff
*r
, const char *name
, const char *string
,
2739 roff_setstrn(&r
->strtab
, name
, strlen(name
), string
,
2740 string
? strlen(string
) : 0, append
);
2744 roff_setstrn(struct roffkv
**r
, const char *name
, size_t namesz
,
2745 const char *string
, size_t stringsz
, int append
)
2750 size_t oldch
, newch
;
2752 /* Search for an existing string with the same name. */
2755 while (n
&& (namesz
!= n
->key
.sz
||
2756 strncmp(n
->key
.p
, name
, namesz
)))
2760 /* Create a new string table entry. */
2761 n
= mandoc_malloc(sizeof(struct roffkv
));
2762 n
->key
.p
= mandoc_strndup(name
, namesz
);
2768 } else if (0 == append
) {
2778 * One additional byte for the '\n' in multiline mode,
2779 * and one for the terminating '\0'.
2781 newch
= stringsz
+ (1 < append
? 2u : 1u);
2783 if (NULL
== n
->val
.p
) {
2784 n
->val
.p
= mandoc_malloc(newch
);
2789 n
->val
.p
= mandoc_realloc(n
->val
.p
, oldch
+ newch
);
2792 /* Skip existing content in the destination buffer. */
2793 c
= n
->val
.p
+ (int)oldch
;
2795 /* Append new content to the destination buffer. */
2797 while (i
< (int)stringsz
) {
2799 * Rudimentary roff copy mode:
2800 * Handle escaped backslashes.
2802 if ('\\' == string
[i
] && '\\' == string
[i
+ 1])
2807 /* Append terminating bytes. */
2812 n
->val
.sz
= (int)(c
- n
->val
.p
);
2816 roff_getstrn(const struct roff
*r
, const char *name
, size_t len
)
2818 const struct roffkv
*n
;
2821 for (n
= r
->strtab
; n
; n
= n
->next
)
2822 if (0 == strncmp(name
, n
->key
.p
, len
) &&
2823 '\0' == n
->key
.p
[(int)len
])
2826 for (i
= 0; i
< PREDEFS_MAX
; i
++)
2827 if (0 == strncmp(name
, predefs
[i
].name
, len
) &&
2828 '\0' == predefs
[i
].name
[(int)len
])
2829 return(predefs
[i
].str
);
2835 roff_freestr(struct roffkv
*r
)
2837 struct roffkv
*n
, *nn
;
2839 for (n
= r
; n
; n
= nn
) {
2847 const struct tbl_span
*
2848 roff_span(const struct roff
*r
)
2851 return(r
->tbl
? tbl_span(r
->tbl
) : NULL
);
2855 roff_eqn(const struct roff
*r
)
2858 return(r
->last_eqn
? &r
->last_eqn
->eqn
: NULL
);
2862 * Duplicate an input string, making the appropriate character
2863 * conversations (as stipulated by `tr') along the way.
2864 * Returns a heap-allocated string with all the replacements made.
2867 roff_strdup(const struct roff
*r
, const char *p
)
2869 const struct roffkv
*cp
;
2873 enum mandoc_esc esc
;
2875 if (NULL
== r
->xmbtab
&& NULL
== r
->xtab
)
2876 return(mandoc_strdup(p
));
2877 else if ('\0' == *p
)
2878 return(mandoc_strdup(""));
2881 * Step through each character looking for term matches
2882 * (remember that a `tr' can be invoked with an escape, which is
2883 * a glyph but the escape is multi-character).
2884 * We only do this if the character hash has been initialised
2885 * and the string is >0 length.
2891 while ('\0' != *p
) {
2892 if ('\\' != *p
&& r
->xtab
&& r
->xtab
[(int)*p
].p
) {
2893 sz
= r
->xtab
[(int)*p
].sz
;
2894 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
2895 memcpy(res
+ ssz
, r
->xtab
[(int)*p
].p
, sz
);
2899 } else if ('\\' != *p
) {
2900 res
= mandoc_realloc(res
, ssz
+ 2);
2905 /* Search for term matches. */
2906 for (cp
= r
->xmbtab
; cp
; cp
= cp
->next
)
2907 if (0 == strncmp(p
, cp
->key
.p
, cp
->key
.sz
))
2912 * A match has been found.
2913 * Append the match to the array and move
2914 * forward by its keysize.
2916 res
= mandoc_realloc(res
,
2917 ssz
+ cp
->val
.sz
+ 1);
2918 memcpy(res
+ ssz
, cp
->val
.p
, cp
->val
.sz
);
2920 p
+= (int)cp
->key
.sz
;
2925 * Handle escapes carefully: we need to copy
2926 * over just the escape itself, or else we might
2927 * do replacements within the escape itself.
2928 * Make sure to pass along the bogus string.
2931 esc
= mandoc_escape(&p
, NULL
, NULL
);
2932 if (ESCAPE_ERROR
== esc
) {
2934 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
2935 memcpy(res
+ ssz
, pp
, sz
);
2939 * We bail out on bad escapes.
2940 * No need to warn: we already did so when
2941 * roff_res() was called.
2944 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
2945 memcpy(res
+ ssz
, pp
, sz
);
2949 res
[(int)ssz
] = '\0';
2954 roff_getformat(const struct roff
*r
)
2961 * Find out whether a line is a macro line or not.
2962 * If it is, adjust the current position and return one; if it isn't,
2963 * return zero and don't change the current position.
2964 * If the control character has been set with `.cc', then let that grain
2966 * This is slighly contrary to groff, where using the non-breaking
2967 * control character when `cc' has been invoked will cause the
2968 * non-breaking macro contents to be printed verbatim.
2971 roff_getcontrol(const struct roff
*r
, const char *cp
, int *ppos
)
2977 if (0 != r
->control
&& cp
[pos
] == r
->control
)
2979 else if (0 != r
->control
)
2981 else if ('\\' == cp
[pos
] && '.' == cp
[pos
+ 1])
2983 else if ('.' == cp
[pos
] || '\'' == cp
[pos
])
2988 while (' ' == cp
[pos
] || '\t' == cp
[pos
])