]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.224 2014/08/01 17:27:44 schwarze Exp $ */
3 * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2014 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.
29 #include "mandoc_aux.h"
31 #include "libmandoc.h"
33 /* Maximum number of nested if-else conditionals. */
34 #define RSTACK_MAX 128
36 /* Maximum number of string expansions per line, to break infinite loops. */
37 #define EXPAND_LIMIT 1000
82 * An incredibly-simple string buffer.
85 char *p
; /* nil-terminated buffer */
86 size_t sz
; /* saved strlen(p) */
90 * A key-value roffstr pair as part of a singly-linked list.
95 struct roffkv
*next
; /* next in list */
99 * A single number register as part of a singly-linked list.
104 struct roffreg
*next
;
108 struct mparse
*parse
; /* parse point */
109 struct roffnode
*last
; /* leaf of stack */
110 int *rstack
; /* stack of inverted `ie' values */
111 struct roffreg
*regtab
; /* number registers */
112 struct roffkv
*strtab
; /* user-defined strings & macros */
113 struct roffkv
*xmbtab
; /* multi-byte trans table (`tr') */
114 struct roffstr
*xtab
; /* single-byte trans table (`tr') */
115 const char *current_string
; /* value of last called user macro */
116 struct tbl_node
*first_tbl
; /* first table parsed */
117 struct tbl_node
*last_tbl
; /* last table parsed */
118 struct tbl_node
*tbl
; /* current table being parsed */
119 struct eqn_node
*last_eqn
; /* last equation parsed */
120 struct eqn_node
*first_eqn
; /* first equation parsed */
121 struct eqn_node
*eqn
; /* current equation being parsed */
122 int options
; /* parse options */
123 int rstacksz
; /* current size limit of rstack */
124 int rstackpos
; /* position in rstack */
125 char control
; /* control character */
129 enum rofft tok
; /* type of node */
130 struct roffnode
*parent
; /* up one in stack */
131 int line
; /* parse line */
132 int col
; /* parse col */
133 char *name
; /* node name, e.g. macro name */
134 char *end
; /* end-rules: custom token */
135 int endspan
; /* end-rules: next-line or infty */
136 int rule
; /* current evaluation rule */
139 #define ROFF_ARGS struct roff *r, /* parse ctx */ \
140 enum rofft tok, /* tok of macro */ \
141 char **bufp, /* input buffer */ \
142 size_t *szp, /* size of input buffer */ \
143 int ln, /* parse line */ \
144 int ppos, /* original pos in buffer */ \
145 int pos, /* current pos in buffer */ \
146 int *offs /* reset offset of buffer data */
148 typedef enum rofferr (*roffproc
)(ROFF_ARGS
);
151 const char *name
; /* macro name */
152 roffproc proc
; /* process new macro */
153 roffproc text
; /* process as child text of macro */
154 roffproc sub
; /* process as child of macro */
156 #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
157 struct roffmac
*next
;
161 const char *name
; /* predefined input name */
162 const char *str
; /* replacement symbol */
165 #define PREDEF(__name, __str) \
166 { (__name), (__str) },
168 static enum rofft
roffhash_find(const char *, size_t);
169 static void roffhash_init(void);
170 static void roffnode_cleanscope(struct roff
*);
171 static void roffnode_pop(struct roff
*);
172 static void roffnode_push(struct roff
*, enum rofft
,
173 const char *, int, int);
174 static enum rofferr
roff_block(ROFF_ARGS
);
175 static enum rofferr
roff_block_text(ROFF_ARGS
);
176 static enum rofferr
roff_block_sub(ROFF_ARGS
);
177 static enum rofferr
roff_cblock(ROFF_ARGS
);
178 static enum rofferr
roff_cc(ROFF_ARGS
);
179 static void roff_ccond(struct roff
*, int, int);
180 static enum rofferr
roff_cond(ROFF_ARGS
);
181 static enum rofferr
roff_cond_text(ROFF_ARGS
);
182 static enum rofferr
roff_cond_sub(ROFF_ARGS
);
183 static enum rofferr
roff_ds(ROFF_ARGS
);
184 static int roff_evalcond(const char *, int *);
185 static int roff_evalnum(const char *, int *, int *, int);
186 static int roff_evalpar(const char *, int *, int *);
187 static int roff_evalstrcond(const char *, int *);
188 static void roff_free1(struct roff
*);
189 static void roff_freereg(struct roffreg
*);
190 static void roff_freestr(struct roffkv
*);
191 static size_t roff_getname(struct roff
*, char **, int, int);
192 static int roff_getnum(const char *, int *, int *);
193 static int roff_getop(const char *, int *, char *);
194 static int roff_getregn(const struct roff
*,
195 const char *, size_t);
196 static int roff_getregro(const char *name
);
197 static const char *roff_getstrn(const struct roff
*,
198 const char *, size_t);
199 static enum rofferr
roff_it(ROFF_ARGS
);
200 static enum rofferr
roff_line_ignore(ROFF_ARGS
);
201 static enum rofferr
roff_nr(ROFF_ARGS
);
202 static void roff_openeqn(struct roff
*, const char *,
203 int, int, const char *);
204 static enum rofft
roff_parse(struct roff
*, char *, int *,
206 static enum rofferr
roff_parsetext(char **, size_t *, int, int *);
207 static enum rofferr
roff_res(struct roff
*,
208 char **, size_t *, int, int);
209 static enum rofferr
roff_rm(ROFF_ARGS
);
210 static enum rofferr
roff_rr(ROFF_ARGS
);
211 static void roff_setstr(struct roff
*,
212 const char *, const char *, int);
213 static void roff_setstrn(struct roffkv
**, const char *,
214 size_t, const char *, size_t, int);
215 static enum rofferr
roff_so(ROFF_ARGS
);
216 static enum rofferr
roff_tr(ROFF_ARGS
);
217 static enum rofferr
roff_Dd(ROFF_ARGS
);
218 static enum rofferr
roff_TH(ROFF_ARGS
);
219 static enum rofferr
roff_TE(ROFF_ARGS
);
220 static enum rofferr
roff_TS(ROFF_ARGS
);
221 static enum rofferr
roff_EQ(ROFF_ARGS
);
222 static enum rofferr
roff_EN(ROFF_ARGS
);
223 static enum rofferr
roff_T_(ROFF_ARGS
);
224 static enum rofferr
roff_userdef(ROFF_ARGS
);
226 /* See roffhash_find() */
230 #define HASHWIDTH (ASCII_HI - ASCII_LO + 1)
232 static struct roffmac
*hash
[HASHWIDTH
];
234 static struct roffmac roffs
[ROFF_MAX
] = {
235 { "ad", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
236 { "am", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
237 { "ami", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
238 { "am1", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
239 { "as", roff_ds
, NULL
, NULL
, 0, NULL
},
240 { "cc", roff_cc
, NULL
, NULL
, 0, NULL
},
241 { "ce", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
242 { "de", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
243 { "dei", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
244 { "de1", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
245 { "ds", roff_ds
, NULL
, NULL
, 0, NULL
},
246 { "el", roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
, NULL
},
247 { "fam", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
248 { "hw", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
249 { "hy", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
250 { "ie", roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
, NULL
},
251 { "if", roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
, NULL
},
252 { "ig", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
253 { "it", roff_it
, NULL
, NULL
, 0, NULL
},
254 { "ne", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
255 { "nh", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
256 { "nr", roff_nr
, NULL
, NULL
, 0, NULL
},
257 { "ns", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
258 { "ps", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
259 { "rm", roff_rm
, NULL
, NULL
, 0, NULL
},
260 { "rr", roff_rr
, NULL
, NULL
, 0, NULL
},
261 { "so", roff_so
, NULL
, NULL
, 0, NULL
},
262 { "ta", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
263 { "tr", roff_tr
, NULL
, NULL
, 0, NULL
},
264 { "Dd", roff_Dd
, NULL
, NULL
, 0, NULL
},
265 { "TH", roff_TH
, NULL
, NULL
, 0, NULL
},
266 { "TS", roff_TS
, NULL
, NULL
, 0, NULL
},
267 { "TE", roff_TE
, NULL
, NULL
, 0, NULL
},
268 { "T&", roff_T_
, NULL
, NULL
, 0, NULL
},
269 { "EQ", roff_EQ
, NULL
, NULL
, 0, NULL
},
270 { "EN", roff_EN
, NULL
, NULL
, 0, NULL
},
271 { ".", roff_cblock
, NULL
, NULL
, 0, NULL
},
272 { NULL
, roff_userdef
, NULL
, NULL
, 0, NULL
},
275 /* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */
276 const char *const __mdoc_reserved
[] = {
277 "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At",
278 "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq",
279 "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx",
280 "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq",
281 "Dt", "Dv", "Dx", "D1",
282 "Ec", "Ed", "Ef", "Ek", "El", "Em",
283 "En", "Eo", "Er", "Es", "Ev", "Ex",
284 "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx",
285 "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp",
286 "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx",
287 "Oc", "Oo", "Op", "Os", "Ot", "Ox",
288 "Pa", "Pc", "Pf", "Po", "Pp", "Pq",
289 "Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv",
290 "Sc", "Sh", "Sm", "So", "Sq",
291 "Ss", "St", "Sx", "Sy",
292 "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr",
293 "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O",
294 "%P", "%Q", "%R", "%T", "%U", "%V",
298 /* not currently implemented: BT DE DS ME MT PT SY TQ YS */
299 const char *const __man_reserved
[] = {
300 "AT", "B", "BI", "BR", "DT",
301 "EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR",
302 "LP", "OP", "P", "PD", "PP",
303 "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS",
304 "TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR",
308 /* Array of injected predefined strings. */
309 #define PREDEFS_MAX 38
310 static const struct predef predefs
[PREDEFS_MAX
] = {
311 #include "predefs.in"
314 /* See roffhash_find() */
315 #define ROFF_HASH(p) (p[0] - ASCII_LO)
317 static int roffit_lines
; /* number of lines to delay */
318 static char *roffit_macro
; /* nil-terminated macro line */
327 for (i
= 0; i
< (int)ROFF_USERDEF
; i
++) {
328 assert(roffs
[i
].name
[0] >= ASCII_LO
);
329 assert(roffs
[i
].name
[0] <= ASCII_HI
);
331 buc
= ROFF_HASH(roffs
[i
].name
);
333 if (NULL
!= (n
= hash
[buc
])) {
334 for ( ; n
->next
; n
= n
->next
)
338 hash
[buc
] = &roffs
[i
];
343 * Look up a roff token by its name. Returns ROFF_MAX if no macro by
344 * the nil-terminated string name could be found.
347 roffhash_find(const char *p
, size_t s
)
353 * libroff has an extremely simple hashtable, for the time
354 * being, which simply keys on the first character, which must
355 * be printable, then walks a chain. It works well enough until
359 if (p
[0] < ASCII_LO
|| p
[0] > ASCII_HI
)
364 if (NULL
== (n
= hash
[buc
]))
366 for ( ; n
; n
= n
->next
)
367 if (0 == strncmp(n
->name
, p
, s
) && '\0' == n
->name
[(int)s
])
368 return((enum rofft
)(n
- roffs
));
374 * Pop the current node off of the stack of roff instructions currently
378 roffnode_pop(struct roff
*r
)
385 r
->last
= r
->last
->parent
;
392 * Push a roff node onto the instruction stack. This must later be
393 * removed with roffnode_pop().
396 roffnode_push(struct roff
*r
, enum rofft tok
, const char *name
,
401 p
= mandoc_calloc(1, sizeof(struct roffnode
));
404 p
->name
= mandoc_strdup(name
);
408 p
->rule
= p
->parent
? p
->parent
->rule
: 0;
414 roff_free1(struct roff
*r
)
416 struct tbl_node
*tbl
;
420 while (NULL
!= (tbl
= r
->first_tbl
)) {
421 r
->first_tbl
= tbl
->next
;
424 r
->first_tbl
= r
->last_tbl
= r
->tbl
= NULL
;
426 while (NULL
!= (e
= r
->first_eqn
)) {
427 r
->first_eqn
= e
->next
;
430 r
->first_eqn
= r
->last_eqn
= r
->eqn
= NULL
;
440 roff_freereg(r
->regtab
);
443 roff_freestr(r
->strtab
);
444 roff_freestr(r
->xmbtab
);
445 r
->strtab
= r
->xmbtab
= NULL
;
448 for (i
= 0; i
< 128; i
++)
455 roff_reset(struct roff
*r
)
463 roff_free(struct roff
*r
)
471 roff_alloc(struct mparse
*parse
, int options
)
475 r
= mandoc_calloc(1, sizeof(struct roff
));
477 r
->options
= options
;
486 * In the current line, expand escape sequences that tend to get
487 * used in numerical expressions and conditional requests.
488 * Also check the syntax of the remaining escape sequences.
491 roff_res(struct roff
*r
, char **bufp
, size_t *szp
, int ln
, int pos
)
493 char ubuf
[24]; /* buffer to print the number */
494 const char *start
; /* start of the string to process */
495 char *stesc
; /* start of an escape sequence ('\\') */
496 const char *stnam
; /* start of the name, after "[(*" */
497 const char *cp
; /* end of the name, e.g. before ']' */
498 const char *res
; /* the string to be substituted */
499 char *nbuf
; /* new buffer to copy bufp to */
500 size_t maxl
; /* expected length of the escape name */
501 size_t naml
; /* actual length of the escape name */
502 int expand_count
; /* to avoid infinite loops */
503 int npos
; /* position in numeric expression */
504 int arg_complete
; /* argument not interrupted by eol */
505 char term
; /* character terminating the escape */
509 stesc
= strchr(start
, '\0') - 1;
510 while (stesc
-- > start
) {
512 /* Search backwards for the next backslash. */
517 /* If it is escaped, skip it. */
519 for (cp
= stesc
- 1; cp
>= start
; cp
--)
523 if (0 == (stesc
- cp
) % 2) {
528 /* Decide whether to expand or to check only. */
545 if (ESCAPE_ERROR
== mandoc_escape(&cp
, NULL
, NULL
))
546 mandoc_vmsg(MANDOCERR_ESC_BAD
,
547 r
->parse
, ln
, (int)(stesc
- *bufp
),
548 "%.*s", (int)(cp
- stesc
), stesc
);
552 if (EXPAND_LIMIT
< ++expand_count
) {
553 mandoc_msg(MANDOCERR_ROFFLOOP
, r
->parse
,
554 ln
, (int)(stesc
- *bufp
), NULL
);
559 * The third character decides the length
560 * of the name of the string or register.
561 * Save a pointer to the name.
588 /* Advance to the end of the name. */
591 for (naml
= 0; 0 == maxl
|| naml
< maxl
; naml
++, cp
++) {
593 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
594 ln
, (int)(stesc
- *bufp
), stesc
);
598 if (0 == maxl
&& *cp
== term
) {
605 * Retrieve the replacement string; if it is
606 * undefined, resume searching for escapes.
612 res
= roff_getstrn(r
, stnam
, naml
);
616 ubuf
[0] = arg_complete
&&
617 roff_evalnum(stnam
, &npos
, NULL
, 0) &&
618 stnam
+ npos
+ 1 == cp
? '1' : '0';
623 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
624 roff_getregn(r
, stnam
, naml
));
629 /* use even incomplete args */
630 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
636 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
637 r
->parse
, ln
, (int)(stesc
- *bufp
),
638 "%.*s", (int)naml
, stnam
);
642 /* Replace the escape sequence by the string. */
645 *szp
= mandoc_asprintf(&nbuf
, "%s%s%s",
648 /* Prepare for the next replacement. */
651 stesc
= nbuf
+ (stesc
- *bufp
) + strlen(res
);
659 * Process text streams:
660 * Convert all breakable hyphens into ASCII_HYPH.
661 * Decrement and spring input line trap.
664 roff_parsetext(char **bufp
, size_t *szp
, int pos
, int *offs
)
672 start
= p
= *bufp
+ pos
;
675 sz
= strcspn(p
, "-\\");
682 /* Skip over escapes. */
684 esc
= mandoc_escape((const char **)&p
, NULL
, NULL
);
685 if (ESCAPE_ERROR
== esc
)
688 } else if (p
== start
) {
693 if (isalpha((unsigned char)p
[-1]) &&
694 isalpha((unsigned char)p
[1]))
699 /* Spring the input line trap. */
700 if (1 == roffit_lines
) {
701 isz
= mandoc_asprintf(&p
, "%s\n.%s", *bufp
, roffit_macro
);
708 return(ROFF_REPARSE
);
709 } else if (1 < roffit_lines
)
715 roff_parseln(struct roff
*r
, int ln
, char **bufp
,
716 size_t *szp
, int pos
, int *offs
)
723 * Run the reserved-word filter only if we have some reserved
727 e
= roff_res(r
, bufp
, szp
, ln
, pos
);
730 assert(ROFF_CONT
== e
);
733 ctl
= roff_getcontrol(r
, *bufp
, &pos
);
736 * First, if a scope is open and we're not a macro, pass the
737 * text through the macro's filter. If a scope isn't open and
738 * we're not a macro, just let it through.
739 * Finally, if there's an equation scope open, divert it into it
740 * no matter our state.
743 if (r
->last
&& ! ctl
) {
745 assert(roffs
[t
].text
);
746 e
= (*roffs
[t
].text
)(r
, t
, bufp
, szp
, ln
, pos
, pos
, offs
);
747 assert(ROFF_IGN
== e
|| ROFF_CONT
== e
);
752 return(eqn_read(&r
->eqn
, ln
, *bufp
, ppos
, offs
));
755 return(tbl_read(r
->tbl
, ln
, *bufp
, pos
));
756 return(roff_parsetext(bufp
, szp
, pos
, offs
));
760 * If a scope is open, go to the child handler for that macro,
761 * as it may want to preprocess before doing anything with it.
762 * Don't do so if an equation is open.
767 assert(roffs
[t
].sub
);
768 return((*roffs
[t
].sub
)(r
, t
, bufp
, szp
,
769 ln
, ppos
, pos
, offs
));
773 * Lastly, as we've no scope open, try to look up and execute
774 * the new macro. If no macro is found, simply return and let
775 * the compilers handle it.
778 if (ROFF_MAX
== (t
= roff_parse(r
, *bufp
, &pos
, ln
, ppos
)))
781 assert(roffs
[t
].proc
);
782 return((*roffs
[t
].proc
)(r
, t
, bufp
, szp
, ln
, ppos
, pos
, offs
));
786 roff_endparse(struct roff
*r
)
790 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
791 r
->last
->line
, r
->last
->col
,
792 roffs
[r
->last
->tok
].name
);
795 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
796 r
->eqn
->eqn
.ln
, r
->eqn
->eqn
.pos
, "EQ");
801 mandoc_msg(MANDOCERR_BLK_NOEND
, r
->parse
,
802 r
->tbl
->line
, r
->tbl
->pos
, "TS");
808 * Parse a roff node's type from the input buffer. This must be in the
809 * form of ".foo xxx" in the usual way.
812 roff_parse(struct roff
*r
, char *buf
, int *pos
, int ln
, int ppos
)
821 if ('\0' == *cp
|| '"' == *cp
|| '\t' == *cp
|| ' ' == *cp
)
825 maclen
= roff_getname(r
, &cp
, ln
, ppos
);
827 t
= (r
->current_string
= roff_getstrn(r
, mac
, maclen
))
828 ? ROFF_USERDEF
: roffhash_find(mac
, maclen
);
837 roff_cblock(ROFF_ARGS
)
841 * A block-close `..' should only be invoked as a child of an
842 * ignore macro, otherwise raise a warning and just ignore it.
845 if (NULL
== r
->last
) {
846 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
851 switch (r
->last
->tok
) {
853 /* ROFF_am1 is remapped to ROFF_am in roff_block(). */
858 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
865 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
871 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
872 ".. %s", *bufp
+ pos
);
875 roffnode_cleanscope(r
);
881 roffnode_cleanscope(struct roff
*r
)
885 if (--r
->last
->endspan
!= 0)
892 roff_ccond(struct roff
*r
, int ln
, int ppos
)
895 if (NULL
== r
->last
) {
896 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
901 switch (r
->last
->tok
) {
909 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
914 if (r
->last
->endspan
> -1) {
915 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
921 roffnode_cleanscope(r
);
926 roff_block(ROFF_ARGS
)
932 /* Ignore groff compatibility mode for now. */
936 else if (ROFF_am1
== tok
)
939 /* Parse the macro name argument. */
942 if (ROFF_ig
== tok
) {
947 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
948 iname
[namesz
] = '\0';
951 /* Resolve the macro name argument if it is indirect. */
953 if (namesz
&& (ROFF_dei
== tok
|| ROFF_ami
== tok
)) {
954 if (NULL
== (name
= roff_getstrn(r
, iname
, namesz
))) {
955 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
956 r
->parse
, ln
, (int)(iname
- *bufp
),
957 "%.*s", (int)namesz
, iname
);
960 namesz
= strlen(name
);
964 if (0 == namesz
&& ROFF_ig
!= tok
) {
965 mandoc_msg(MANDOCERR_REQ_EMPTY
, r
->parse
,
966 ln
, ppos
, roffs
[tok
].name
);
970 roffnode_push(r
, tok
, name
, ln
, ppos
);
973 * At the beginning of a `de' macro, clear the existing string
974 * with the same name, if there is one. New content will be
975 * appended from roff_block_text() in multiline mode.
978 if (ROFF_de
== tok
|| ROFF_dei
== tok
)
979 roff_setstrn(&r
->strtab
, name
, namesz
, "", 0, 0);
984 /* Get the custom end marker. */
987 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
989 /* Resolve the end marker if it is indirect. */
991 if (namesz
&& (ROFF_dei
== tok
|| ROFF_ami
== tok
)) {
992 if (NULL
== (name
= roff_getstrn(r
, iname
, namesz
))) {
993 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
994 r
->parse
, ln
, (int)(iname
- *bufp
),
995 "%.*s", (int)namesz
, iname
);
998 namesz
= strlen(name
);
1003 r
->last
->end
= mandoc_strndup(name
, namesz
);
1006 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
1007 ln
, pos
, ".%s ... %s", roffs
[tok
].name
, cp
);
1013 roff_block_sub(ROFF_ARGS
)
1019 * First check whether a custom macro exists at this level. If
1020 * it does, then check against it. This is some of groff's
1021 * stranger behaviours. If we encountered a custom end-scope
1022 * tag and that tag also happens to be a "real" macro, then we
1023 * need to try interpreting it again as a real macro. If it's
1024 * not, then return ignore. Else continue.
1028 for (i
= pos
, j
= 0; r
->last
->end
[j
]; j
++, i
++)
1029 if ((*bufp
)[i
] != r
->last
->end
[j
])
1032 if ('\0' == r
->last
->end
[j
] &&
1033 ('\0' == (*bufp
)[i
] ||
1034 ' ' == (*bufp
)[i
] ||
1035 '\t' == (*bufp
)[i
])) {
1037 roffnode_cleanscope(r
);
1039 while (' ' == (*bufp
)[i
] || '\t' == (*bufp
)[i
])
1043 if (ROFF_MAX
!= roff_parse(r
, *bufp
, &pos
, ln
, ppos
))
1050 * If we have no custom end-query or lookup failed, then try
1051 * pulling it out of the hashtable.
1054 t
= roff_parse(r
, *bufp
, &pos
, ln
, ppos
);
1056 if (ROFF_cblock
!= t
) {
1058 roff_setstr(r
, r
->last
->name
, *bufp
+ ppos
, 2);
1062 assert(roffs
[t
].proc
);
1063 return((*roffs
[t
].proc
)(r
, t
, bufp
, szp
, ln
, ppos
, pos
, offs
));
1067 roff_block_text(ROFF_ARGS
)
1071 roff_setstr(r
, r
->last
->name
, *bufp
+ pos
, 2);
1077 roff_cond_sub(ROFF_ARGS
)
1084 roffnode_cleanscope(r
);
1085 t
= roff_parse(r
, *bufp
, &pos
, ln
, ppos
);
1088 * Fully handle known macros when they are structurally
1089 * required or when the conditional evaluated to true.
1092 if ((ROFF_MAX
!= t
) &&
1093 (rr
|| ROFFMAC_STRUCT
& roffs
[t
].flags
)) {
1094 assert(roffs
[t
].proc
);
1095 return((*roffs
[t
].proc
)(r
, t
, bufp
, szp
,
1096 ln
, ppos
, pos
, offs
));
1100 * If `\}' occurs on a macro line without a preceding macro,
1101 * drop the line completely.
1105 if ('\\' == ep
[0] && '}' == ep
[1])
1108 /* Always check for the closing delimiter `\}'. */
1110 while (NULL
!= (ep
= strchr(ep
, '\\'))) {
1111 if ('}' == *(++ep
)) {
1113 roff_ccond(r
, ln
, ep
- *bufp
- 1);
1117 return(rr
? ROFF_CONT
: ROFF_IGN
);
1121 roff_cond_text(ROFF_ARGS
)
1127 roffnode_cleanscope(r
);
1130 while (NULL
!= (ep
= strchr(ep
, '\\'))) {
1131 if ('}' == *(++ep
)) {
1133 roff_ccond(r
, ln
, ep
- *bufp
- 1);
1137 return(rr
? ROFF_CONT
: ROFF_IGN
);
1141 * Parse a single signed integer number. Stop at the first non-digit.
1142 * If there is at least one digit, return success and advance the
1143 * parse point, else return failure and let the parse point unchanged.
1144 * Ignore overflows, treat them just like the C language.
1147 roff_getnum(const char *v
, int *pos
, int *res
)
1159 for (*res
= 0; isdigit((unsigned char)v
[p
]); p
++)
1160 *res
= 10 * *res
+ v
[p
] - '0';
1172 * Evaluate a string comparison condition.
1173 * The first character is the delimiter.
1174 * Succeed if the string up to its second occurrence
1175 * matches the string up to its third occurence.
1176 * Advance the cursor after the third occurrence
1177 * or lacking that, to the end of the line.
1180 roff_evalstrcond(const char *v
, int *pos
)
1182 const char *s1
, *s2
, *s3
;
1186 s1
= v
+ *pos
; /* initial delimiter */
1187 s2
= s1
+ 1; /* for scanning the first string */
1188 s3
= strchr(s2
, *s1
); /* for scanning the second string */
1190 if (NULL
== s3
) /* found no middle delimiter */
1193 while ('\0' != *++s3
) {
1194 if (*s2
!= *s3
) { /* mismatch */
1195 s3
= strchr(s3
, *s1
);
1198 if (*s3
== *s1
) { /* found the final delimiter */
1207 s3
= strchr(s2
, '\0');
1215 * Evaluate an optionally negated single character, numerical,
1216 * or string condition.
1219 roff_evalcond(const char *v
, int *pos
)
1221 int wanttrue
, number
;
1223 if ('!' == v
[*pos
]) {
1250 if (roff_evalnum(v
, pos
, &number
, 0))
1251 return((number
> 0) == wanttrue
);
1253 return(roff_evalstrcond(v
, pos
) == wanttrue
);
1257 roff_line_ignore(ROFF_ARGS
)
1264 roff_cond(ROFF_ARGS
)
1267 roffnode_push(r
, tok
, NULL
, ln
, ppos
);
1270 * An `.el' has no conditional body: it will consume the value
1271 * of the current rstack entry set in prior `ie' calls or
1274 * If we're not an `el', however, then evaluate the conditional.
1277 r
->last
->rule
= ROFF_el
== tok
?
1278 (r
->rstackpos
< 0 ? 0 : r
->rstack
[r
->rstackpos
--]) :
1279 roff_evalcond(*bufp
, &pos
);
1282 * An if-else will put the NEGATION of the current evaluated
1283 * conditional into the stack of rules.
1286 if (ROFF_ie
== tok
) {
1287 if (r
->rstackpos
+ 1 == r
->rstacksz
) {
1289 r
->rstack
= mandoc_reallocarray(r
->rstack
,
1290 r
->rstacksz
, sizeof(int));
1292 r
->rstack
[++r
->rstackpos
] = !r
->last
->rule
;
1295 /* If the parent has false as its rule, then so do we. */
1297 if (r
->last
->parent
&& !r
->last
->parent
->rule
)
1302 * If there is nothing on the line after the conditional,
1303 * not even whitespace, use next-line scope.
1306 if ('\0' == (*bufp
)[pos
]) {
1307 r
->last
->endspan
= 2;
1311 while (' ' == (*bufp
)[pos
])
1314 /* An opening brace requests multiline scope. */
1316 if ('\\' == (*bufp
)[pos
] && '{' == (*bufp
)[pos
+ 1]) {
1317 r
->last
->endspan
= -1;
1323 * Anything else following the conditional causes
1324 * single-line scope. Warn if the scope contains
1325 * nothing but trailing whitespace.
1328 if ('\0' == (*bufp
)[pos
])
1329 mandoc_msg(MANDOCERR_COND_EMPTY
, r
->parse
,
1330 ln
, ppos
, roffs
[tok
].name
);
1332 r
->last
->endspan
= 1;
1347 * The first word is the name of the string.
1348 * If it is empty or terminated by an escape sequence,
1349 * abort the `ds' request without defining anything.
1352 name
= string
= *bufp
+ pos
;
1356 namesz
= roff_getname(r
, &string
, ln
, pos
);
1357 if ('\\' == name
[namesz
])
1360 /* Read past the initial double-quote, if any. */
1364 /* The rest is the value. */
1365 roff_setstrn(&r
->strtab
, name
, namesz
, string
, strlen(string
),
1371 * Parse a single operator, one or two characters long.
1372 * If the operator is recognized, return success and advance the
1373 * parse point, else return failure and let the parse point unchanged.
1376 roff_getop(const char *v
, int *pos
, char *res
)
1397 switch (v
[*pos
+ 1]) {
1415 switch (v
[*pos
+ 1]) {
1429 if ('=' == v
[*pos
+ 1])
1441 * Evaluate either a parenthesized numeric expression
1442 * or a single signed integer number.
1445 roff_evalpar(const char *v
, int *pos
, int *res
)
1449 return(roff_getnum(v
, pos
, res
));
1452 if ( ! roff_evalnum(v
, pos
, res
, 1))
1456 * Omission of the closing parenthesis
1457 * is an error in validation mode,
1458 * but ignored in evaluation mode.
1463 else if (NULL
== res
)
1470 * Evaluate a complete numeric expression.
1471 * Proceed left to right, there is no concept of precedence.
1474 roff_evalnum(const char *v
, int *pos
, int *res
, int skipwhite
)
1476 int mypos
, operand2
;
1485 while (isspace((unsigned char)v
[*pos
]))
1488 if ( ! roff_evalpar(v
, pos
, res
))
1493 while (isspace((unsigned char)v
[*pos
]))
1496 if ( ! roff_getop(v
, pos
, &operator))
1500 while (isspace((unsigned char)v
[*pos
]))
1503 if ( ! roff_evalpar(v
, pos
, &operand2
))
1507 while (isspace((unsigned char)v
[*pos
]))
1530 *res
= *res
< operand2
;
1533 *res
= *res
> operand2
;
1536 *res
= *res
<= operand2
;
1539 *res
= *res
>= operand2
;
1542 *res
= *res
== operand2
;
1545 *res
= *res
!= operand2
;
1548 *res
= *res
&& operand2
;
1551 *res
= *res
|| operand2
;
1554 if (operand2
< *res
)
1558 if (operand2
> *res
)
1569 roff_setreg(struct roff
*r
, const char *name
, int val
, char sign
)
1571 struct roffreg
*reg
;
1573 /* Search for an existing register with the same name. */
1576 while (reg
&& strcmp(name
, reg
->key
.p
))
1580 /* Create a new register. */
1581 reg
= mandoc_malloc(sizeof(struct roffreg
));
1582 reg
->key
.p
= mandoc_strdup(name
);
1583 reg
->key
.sz
= strlen(name
);
1585 reg
->next
= r
->regtab
;
1591 else if ('-' == sign
)
1598 * Handle some predefined read-only number registers.
1599 * For now, return -1 if the requested register is not predefined;
1600 * in case a predefined read-only register having the value -1
1601 * were to turn up, another special value would have to be chosen.
1604 roff_getregro(const char *name
)
1608 case 'A': /* ASCII approximation mode is always off. */
1610 case 'g': /* Groff compatibility mode is always on. */
1612 case 'H': /* Fixed horizontal resolution. */
1614 case 'j': /* Always adjust left margin only. */
1616 case 'T': /* Some output device is always defined. */
1618 case 'V': /* Fixed vertical resolution. */
1626 roff_getreg(const struct roff
*r
, const char *name
)
1628 struct roffreg
*reg
;
1631 if ('.' == name
[0] && '\0' != name
[1] && '\0' == name
[2]) {
1632 val
= roff_getregro(name
+ 1);
1637 for (reg
= r
->regtab
; reg
; reg
= reg
->next
)
1638 if (0 == strcmp(name
, reg
->key
.p
))
1645 roff_getregn(const struct roff
*r
, const char *name
, size_t len
)
1647 struct roffreg
*reg
;
1650 if ('.' == name
[0] && 2 == len
) {
1651 val
= roff_getregro(name
+ 1);
1656 for (reg
= r
->regtab
; reg
; reg
= reg
->next
)
1657 if (len
== reg
->key
.sz
&&
1658 0 == strncmp(name
, reg
->key
.p
, len
))
1665 roff_freereg(struct roffreg
*reg
)
1667 struct roffreg
*old_reg
;
1669 while (NULL
!= reg
) {
1685 key
= val
= *bufp
+ pos
;
1689 keysz
= roff_getname(r
, &val
, ln
, pos
);
1690 if ('\\' == key
[keysz
])
1695 if ('+' == sign
|| '-' == sign
)
1698 if (roff_evalnum(val
, NULL
, &iv
, 0))
1699 roff_setreg(r
, key
, iv
, sign
);
1707 struct roffreg
*reg
, **prev
;
1711 name
= cp
= *bufp
+ pos
;
1714 namesz
= roff_getname(r
, &cp
, ln
, pos
);
1715 name
[namesz
] = '\0';
1720 if (NULL
== reg
|| !strcmp(name
, reg
->key
.p
))
1740 while ('\0' != *cp
) {
1742 namesz
= roff_getname(r
, &cp
, ln
, (int)(cp
- *bufp
));
1743 roff_setstrn(&r
->strtab
, name
, namesz
, NULL
, 0, 0);
1744 if ('\\' == name
[namesz
])
1757 /* Parse the number of lines. */
1759 len
= strcspn(cp
, " \t");
1761 if ((iv
= mandoc_strntoi(cp
, len
, 10)) <= 0) {
1762 mandoc_msg(MANDOCERR_IT_NONUM
, r
->parse
,
1763 ln
, ppos
, *bufp
+ 1);
1768 /* Arm the input line trap. */
1770 roffit_macro
= mandoc_strdup(cp
);
1777 const char *const *cp
;
1779 if (0 == ((MPARSE_MDOC
| MPARSE_QUICK
) & r
->options
))
1780 for (cp
= __mdoc_reserved
; *cp
; cp
++)
1781 roff_setstr(r
, *cp
, NULL
, 0);
1789 const char *const *cp
;
1791 if (0 == (MPARSE_QUICK
& r
->options
))
1792 for (cp
= __man_reserved
; *cp
; cp
++)
1793 roff_setstr(r
, *cp
, NULL
, 0);
1803 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1816 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
,
1819 tbl_restart(ppos
, ln
, r
->tbl
);
1826 roff_closeeqn(struct roff
*r
)
1829 return(r
->eqn
&& ROFF_EQN
== eqn_end(&r
->eqn
) ? 1 : 0);
1834 roff_openeqn(struct roff
*r
, const char *name
, int line
,
1835 int offs
, const char *buf
)
1840 assert(NULL
== r
->eqn
);
1841 e
= eqn_alloc(name
, offs
, line
, r
->parse
);
1844 r
->last_eqn
->next
= e
;
1846 r
->first_eqn
= r
->last_eqn
= e
;
1848 r
->eqn
= r
->last_eqn
= e
;
1852 eqn_read(&r
->eqn
, line
, buf
, offs
, &poff
);
1860 roff_openeqn(r
, *bufp
+ pos
, ln
, ppos
, NULL
);
1868 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, r
->parse
, ln
, ppos
, "EN");
1875 struct tbl_node
*tbl
;
1878 mandoc_msg(MANDOCERR_BLK_BROKEN
, r
->parse
,
1879 ln
, ppos
, "TS breaks TS");
1883 tbl
= tbl_alloc(ppos
, ln
, r
->parse
);
1886 r
->last_tbl
->next
= tbl
;
1888 r
->first_tbl
= r
->last_tbl
= tbl
;
1890 r
->tbl
= r
->last_tbl
= tbl
;
1901 if ('\0' == *p
|| '.' == (r
->control
= *p
++))
1905 mandoc_msg(MANDOCERR_ARGCOUNT
, r
->parse
, ln
, ppos
, NULL
);
1913 const char *p
, *first
, *second
;
1915 enum mandoc_esc esc
;
1920 mandoc_msg(MANDOCERR_ARGCOUNT
, r
->parse
, ln
, ppos
, NULL
);
1924 while ('\0' != *p
) {
1928 if ('\\' == *first
) {
1929 esc
= mandoc_escape(&p
, NULL
, NULL
);
1930 if (ESCAPE_ERROR
== esc
) {
1931 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
1932 ln
, (int)(p
- *bufp
), first
);
1935 fsz
= (size_t)(p
- first
);
1939 if ('\\' == *second
) {
1940 esc
= mandoc_escape(&p
, NULL
, NULL
);
1941 if (ESCAPE_ERROR
== esc
) {
1942 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
1943 ln
, (int)(p
- *bufp
), second
);
1946 ssz
= (size_t)(p
- second
);
1947 } else if ('\0' == *second
) {
1948 mandoc_msg(MANDOCERR_ARGCOUNT
, r
->parse
,
1949 ln
, (int)(p
- *bufp
), NULL
);
1955 roff_setstrn(&r
->xmbtab
, first
, fsz
,
1960 if (NULL
== r
->xtab
)
1961 r
->xtab
= mandoc_calloc(128,
1962 sizeof(struct roffstr
));
1964 free(r
->xtab
[(int)*first
].p
);
1965 r
->xtab
[(int)*first
].p
= mandoc_strndup(second
, ssz
);
1966 r
->xtab
[(int)*first
].sz
= ssz
;
1978 mandoc_vmsg(MANDOCERR_SO
, r
->parse
, ln
, ppos
, "so %s", name
);
1981 * Handle `so'. Be EXTREMELY careful, as we shouldn't be
1982 * opening anything that's not in our cwd or anything beneath
1983 * it. Thus, explicitly disallow traversing up the file-system
1984 * or using absolute paths.
1987 if ('/' == *name
|| strstr(name
, "../") || strstr(name
, "/..")) {
1988 mandoc_vmsg(MANDOCERR_SO_PATH
, r
->parse
, ln
, ppos
,
1998 roff_userdef(ROFF_ARGS
)
2005 * Collect pointers to macro argument strings
2006 * and NUL-terminate them.
2009 for (i
= 0; i
< 9; i
++)
2010 arg
[i
] = '\0' == *cp
? "" :
2011 mandoc_getarg(r
->parse
, &cp
, ln
, &pos
);
2014 * Expand macro arguments.
2017 n1
= cp
= mandoc_strdup(r
->current_string
);
2018 while (NULL
!= (cp
= strstr(cp
, "\\$"))) {
2020 if (0 > i
|| 8 < i
) {
2021 /* Not an argument invocation. */
2026 *szp
= mandoc_asprintf(&n2
, "%s%s%s",
2027 n1
, arg
[i
], cp
+ 3) + 1;
2028 cp
= n2
+ (cp
- n1
);
2034 * Replace the macro invocation
2035 * by the expanded macro.
2040 *szp
= strlen(*bufp
) + 1;
2042 return(*szp
> 1 && '\n' == (*bufp
)[(int)*szp
- 2] ?
2043 ROFF_REPARSE
: ROFF_APPEND
);
2047 roff_getname(struct roff
*r
, char **cpp
, int ln
, int pos
)
2056 /* Read until end of name and terminate it with NUL. */
2057 for (cp
= name
; 1; cp
++) {
2058 if ('\0' == *cp
|| ' ' == *cp
) {
2065 if ('{' == cp
[1] || '}' == cp
[1])
2070 mandoc_vmsg(MANDOCERR_NAMESC
, r
->parse
, ln
, pos
,
2071 "%.*s", (int)(cp
- name
+ 1), name
);
2072 mandoc_escape((const char **)&cp
, NULL
, NULL
);
2076 /* Read past spaces. */
2085 * Store *string into the user-defined string called *name.
2086 * To clear an existing entry, call with (*r, *name, NULL, 0).
2087 * append == 0: replace mode
2088 * append == 1: single-line append mode
2089 * append == 2: multiline append mode, append '\n' after each call
2092 roff_setstr(struct roff
*r
, const char *name
, const char *string
,
2096 roff_setstrn(&r
->strtab
, name
, strlen(name
), string
,
2097 string
? strlen(string
) : 0, append
);
2101 roff_setstrn(struct roffkv
**r
, const char *name
, size_t namesz
,
2102 const char *string
, size_t stringsz
, int append
)
2107 size_t oldch
, newch
;
2109 /* Search for an existing string with the same name. */
2112 while (n
&& (namesz
!= n
->key
.sz
||
2113 strncmp(n
->key
.p
, name
, namesz
)))
2117 /* Create a new string table entry. */
2118 n
= mandoc_malloc(sizeof(struct roffkv
));
2119 n
->key
.p
= mandoc_strndup(name
, namesz
);
2125 } else if (0 == append
) {
2135 * One additional byte for the '\n' in multiline mode,
2136 * and one for the terminating '\0'.
2138 newch
= stringsz
+ (1 < append
? 2u : 1u);
2140 if (NULL
== n
->val
.p
) {
2141 n
->val
.p
= mandoc_malloc(newch
);
2146 n
->val
.p
= mandoc_realloc(n
->val
.p
, oldch
+ newch
);
2149 /* Skip existing content in the destination buffer. */
2150 c
= n
->val
.p
+ (int)oldch
;
2152 /* Append new content to the destination buffer. */
2154 while (i
< (int)stringsz
) {
2156 * Rudimentary roff copy mode:
2157 * Handle escaped backslashes.
2159 if ('\\' == string
[i
] && '\\' == string
[i
+ 1])
2164 /* Append terminating bytes. */
2169 n
->val
.sz
= (int)(c
- n
->val
.p
);
2173 roff_getstrn(const struct roff
*r
, const char *name
, size_t len
)
2175 const struct roffkv
*n
;
2178 for (n
= r
->strtab
; n
; n
= n
->next
)
2179 if (0 == strncmp(name
, n
->key
.p
, len
) &&
2180 '\0' == n
->key
.p
[(int)len
])
2183 for (i
= 0; i
< PREDEFS_MAX
; i
++)
2184 if (0 == strncmp(name
, predefs
[i
].name
, len
) &&
2185 '\0' == predefs
[i
].name
[(int)len
])
2186 return(predefs
[i
].str
);
2192 roff_freestr(struct roffkv
*r
)
2194 struct roffkv
*n
, *nn
;
2196 for (n
= r
; n
; n
= nn
) {
2204 const struct tbl_span
*
2205 roff_span(const struct roff
*r
)
2208 return(r
->tbl
? tbl_span(r
->tbl
) : NULL
);
2212 roff_eqn(const struct roff
*r
)
2215 return(r
->last_eqn
? &r
->last_eqn
->eqn
: NULL
);
2219 * Duplicate an input string, making the appropriate character
2220 * conversations (as stipulated by `tr') along the way.
2221 * Returns a heap-allocated string with all the replacements made.
2224 roff_strdup(const struct roff
*r
, const char *p
)
2226 const struct roffkv
*cp
;
2230 enum mandoc_esc esc
;
2232 if (NULL
== r
->xmbtab
&& NULL
== r
->xtab
)
2233 return(mandoc_strdup(p
));
2234 else if ('\0' == *p
)
2235 return(mandoc_strdup(""));
2238 * Step through each character looking for term matches
2239 * (remember that a `tr' can be invoked with an escape, which is
2240 * a glyph but the escape is multi-character).
2241 * We only do this if the character hash has been initialised
2242 * and the string is >0 length.
2248 while ('\0' != *p
) {
2249 if ('\\' != *p
&& r
->xtab
&& r
->xtab
[(int)*p
].p
) {
2250 sz
= r
->xtab
[(int)*p
].sz
;
2251 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
2252 memcpy(res
+ ssz
, r
->xtab
[(int)*p
].p
, sz
);
2256 } else if ('\\' != *p
) {
2257 res
= mandoc_realloc(res
, ssz
+ 2);
2262 /* Search for term matches. */
2263 for (cp
= r
->xmbtab
; cp
; cp
= cp
->next
)
2264 if (0 == strncmp(p
, cp
->key
.p
, cp
->key
.sz
))
2269 * A match has been found.
2270 * Append the match to the array and move
2271 * forward by its keysize.
2273 res
= mandoc_realloc(res
,
2274 ssz
+ cp
->val
.sz
+ 1);
2275 memcpy(res
+ ssz
, cp
->val
.p
, cp
->val
.sz
);
2277 p
+= (int)cp
->key
.sz
;
2282 * Handle escapes carefully: we need to copy
2283 * over just the escape itself, or else we might
2284 * do replacements within the escape itself.
2285 * Make sure to pass along the bogus string.
2288 esc
= mandoc_escape(&p
, NULL
, NULL
);
2289 if (ESCAPE_ERROR
== esc
) {
2291 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
2292 memcpy(res
+ ssz
, pp
, sz
);
2296 * We bail out on bad escapes.
2297 * No need to warn: we already did so when
2298 * roff_res() was called.
2301 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
2302 memcpy(res
+ ssz
, pp
, sz
);
2306 res
[(int)ssz
] = '\0';
2311 * Find out whether a line is a macro line or not.
2312 * If it is, adjust the current position and return one; if it isn't,
2313 * return zero and don't change the current position.
2314 * If the control character has been set with `.cc', then let that grain
2316 * This is slighly contrary to groff, where using the non-breaking
2317 * control character when `cc' has been invoked will cause the
2318 * non-breaking macro contents to be printed verbatim.
2321 roff_getcontrol(const struct roff
*r
, const char *cp
, int *ppos
)
2327 if (0 != r
->control
&& cp
[pos
] == r
->control
)
2329 else if (0 != r
->control
)
2331 else if ('\\' == cp
[pos
] && '.' == cp
[pos
+ 1])
2333 else if ('.' == cp
[pos
] || '\'' == cp
[pos
])
2338 while (' ' == cp
[pos
] || '\t' == cp
[pos
])