]>
git.cameronkatri.com Git - mandoc.git/blob - roff.c
1 /* $Id: roff.c,v 1.220 2014/07/07 11:35:06 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 int options
; /* parse options */
110 struct roffnode
*last
; /* leaf of stack */
111 int rstack
[RSTACK_MAX
]; /* stack of !`ie' rules */
112 char control
; /* control character */
113 int rstackpos
; /* position in rstack */
114 struct roffreg
*regtab
; /* number registers */
115 struct roffkv
*strtab
; /* user-defined strings & macros */
116 struct roffkv
*xmbtab
; /* multi-byte trans table (`tr') */
117 struct roffstr
*xtab
; /* single-byte trans table (`tr') */
118 const char *current_string
; /* value of last called user macro */
119 struct tbl_node
*first_tbl
; /* first table parsed */
120 struct tbl_node
*last_tbl
; /* last table parsed */
121 struct tbl_node
*tbl
; /* current table being parsed */
122 struct eqn_node
*last_eqn
; /* last equation parsed */
123 struct eqn_node
*first_eqn
; /* first equation parsed */
124 struct eqn_node
*eqn
; /* current equation being parsed */
128 enum rofft tok
; /* type of node */
129 struct roffnode
*parent
; /* up one in stack */
130 int line
; /* parse line */
131 int col
; /* parse col */
132 char *name
; /* node name, e.g. macro name */
133 char *end
; /* end-rules: custom token */
134 int endspan
; /* end-rules: next-line or infty */
135 int rule
; /* current evaluation rule */
138 #define ROFF_ARGS struct roff *r, /* parse ctx */ \
139 enum rofft tok, /* tok of macro */ \
140 char **bufp, /* input buffer */ \
141 size_t *szp, /* size of input buffer */ \
142 int ln, /* parse line */ \
143 int ppos, /* original pos in buffer */ \
144 int pos, /* current pos in buffer */ \
145 int *offs /* reset offset of buffer data */
147 typedef enum rofferr (*roffproc
)(ROFF_ARGS
);
150 const char *name
; /* macro name */
151 roffproc proc
; /* process new macro */
152 roffproc text
; /* process as child text of macro */
153 roffproc sub
; /* process as child of macro */
155 #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
156 struct roffmac
*next
;
160 const char *name
; /* predefined input name */
161 const char *str
; /* replacement symbol */
164 #define PREDEF(__name, __str) \
165 { (__name), (__str) },
167 static enum rofft
roffhash_find(const char *, size_t);
168 static void roffhash_init(void);
169 static void roffnode_cleanscope(struct roff
*);
170 static void roffnode_pop(struct roff
*);
171 static void roffnode_push(struct roff
*, enum rofft
,
172 const char *, int, int);
173 static enum rofferr
roff_block(ROFF_ARGS
);
174 static enum rofferr
roff_block_text(ROFF_ARGS
);
175 static enum rofferr
roff_block_sub(ROFF_ARGS
);
176 static enum rofferr
roff_cblock(ROFF_ARGS
);
177 static enum rofferr
roff_cc(ROFF_ARGS
);
178 static void roff_ccond(struct roff
*, int, int);
179 static enum rofferr
roff_cond(ROFF_ARGS
);
180 static enum rofferr
roff_cond_text(ROFF_ARGS
);
181 static enum rofferr
roff_cond_sub(ROFF_ARGS
);
182 static enum rofferr
roff_ds(ROFF_ARGS
);
183 static int roff_evalcond(const char *, int *);
184 static int roff_evalnum(const char *, int *, int *, int);
185 static int roff_evalpar(const char *, int *, int *);
186 static int roff_evalstrcond(const char *, int *);
187 static void roff_free1(struct roff
*);
188 static void roff_freereg(struct roffreg
*);
189 static void roff_freestr(struct roffkv
*);
190 static size_t roff_getname(struct roff
*, char **, int, int);
191 static int roff_getnum(const char *, int *, int *);
192 static int roff_getop(const char *, int *, char *);
193 static int roff_getregn(const struct roff
*,
194 const char *, size_t);
195 static int roff_getregro(const char *name
);
196 static const char *roff_getstrn(const struct roff
*,
197 const char *, size_t);
198 static enum rofferr
roff_it(ROFF_ARGS
);
199 static enum rofferr
roff_line_ignore(ROFF_ARGS
);
200 static enum rofferr
roff_nr(ROFF_ARGS
);
201 static void roff_openeqn(struct roff
*, const char *,
202 int, int, const char *);
203 static enum rofft
roff_parse(struct roff
*, char *, int *,
205 static enum rofferr
roff_parsetext(char **, size_t *, int, int *);
206 static enum rofferr
roff_res(struct roff
*,
207 char **, size_t *, int, int);
208 static enum rofferr
roff_rm(ROFF_ARGS
);
209 static enum rofferr
roff_rr(ROFF_ARGS
);
210 static void roff_setstr(struct roff
*,
211 const char *, const char *, int);
212 static void roff_setstrn(struct roffkv
**, const char *,
213 size_t, const char *, size_t, int);
214 static enum rofferr
roff_so(ROFF_ARGS
);
215 static enum rofferr
roff_tr(ROFF_ARGS
);
216 static enum rofferr
roff_Dd(ROFF_ARGS
);
217 static enum rofferr
roff_TH(ROFF_ARGS
);
218 static enum rofferr
roff_TE(ROFF_ARGS
);
219 static enum rofferr
roff_TS(ROFF_ARGS
);
220 static enum rofferr
roff_EQ(ROFF_ARGS
);
221 static enum rofferr
roff_EN(ROFF_ARGS
);
222 static enum rofferr
roff_T_(ROFF_ARGS
);
223 static enum rofferr
roff_userdef(ROFF_ARGS
);
225 /* See roffhash_find() */
229 #define HASHWIDTH (ASCII_HI - ASCII_LO + 1)
231 static struct roffmac
*hash
[HASHWIDTH
];
233 static struct roffmac roffs
[ROFF_MAX
] = {
234 { "ad", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
235 { "am", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
236 { "ami", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
237 { "am1", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
238 { "as", roff_ds
, NULL
, NULL
, 0, NULL
},
239 { "cc", roff_cc
, NULL
, NULL
, 0, NULL
},
240 { "ce", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
241 { "de", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
242 { "dei", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
243 { "de1", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
244 { "ds", roff_ds
, NULL
, NULL
, 0, NULL
},
245 { "el", roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
, NULL
},
246 { "fam", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
247 { "hw", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
248 { "hy", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
249 { "ie", roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
, NULL
},
250 { "if", roff_cond
, roff_cond_text
, roff_cond_sub
, ROFFMAC_STRUCT
, NULL
},
251 { "ig", roff_block
, roff_block_text
, roff_block_sub
, 0, NULL
},
252 { "it", roff_it
, NULL
, NULL
, 0, NULL
},
253 { "ne", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
254 { "nh", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
255 { "nr", roff_nr
, NULL
, NULL
, 0, NULL
},
256 { "ns", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
257 { "ps", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
258 { "rm", roff_rm
, NULL
, NULL
, 0, NULL
},
259 { "rr", roff_rr
, NULL
, NULL
, 0, NULL
},
260 { "so", roff_so
, NULL
, NULL
, 0, NULL
},
261 { "ta", roff_line_ignore
, NULL
, NULL
, 0, NULL
},
262 { "tr", roff_tr
, NULL
, NULL
, 0, NULL
},
263 { "Dd", roff_Dd
, NULL
, NULL
, 0, NULL
},
264 { "TH", roff_TH
, NULL
, NULL
, 0, NULL
},
265 { "TS", roff_TS
, NULL
, NULL
, 0, NULL
},
266 { "TE", roff_TE
, NULL
, NULL
, 0, NULL
},
267 { "T&", roff_T_
, NULL
, NULL
, 0, NULL
},
268 { "EQ", roff_EQ
, NULL
, NULL
, 0, NULL
},
269 { "EN", roff_EN
, NULL
, NULL
, 0, NULL
},
270 { ".", roff_cblock
, NULL
, NULL
, 0, NULL
},
271 { NULL
, roff_userdef
, NULL
, NULL
, 0, NULL
},
274 /* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */
275 const char *const __mdoc_reserved
[] = {
276 "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At",
277 "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq",
278 "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx",
279 "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq",
280 "Dt", "Dv", "Dx", "D1",
281 "Ec", "Ed", "Ef", "Ek", "El", "Em",
282 "En", "Eo", "Er", "Es", "Ev", "Ex",
283 "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx",
284 "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp",
285 "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx",
286 "Oc", "Oo", "Op", "Os", "Ot", "Ox",
287 "Pa", "Pc", "Pf", "Po", "Pp", "Pq",
288 "Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv",
289 "Sc", "Sh", "Sm", "So", "Sq",
290 "Ss", "St", "Sx", "Sy",
291 "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr",
292 "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O",
293 "%P", "%Q", "%R", "%T", "%U", "%V",
297 /* not currently implemented: BT DE DS ME MT PT SY TQ YS */
298 const char *const __man_reserved
[] = {
299 "AT", "B", "BI", "BR", "DT",
300 "EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR",
301 "LP", "OP", "P", "PD", "PP",
302 "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS",
303 "TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR",
307 /* Array of injected predefined strings. */
308 #define PREDEFS_MAX 38
309 static const struct predef predefs
[PREDEFS_MAX
] = {
310 #include "predefs.in"
313 /* See roffhash_find() */
314 #define ROFF_HASH(p) (p[0] - ASCII_LO)
316 static int roffit_lines
; /* number of lines to delay */
317 static char *roffit_macro
; /* nil-terminated macro line */
326 for (i
= 0; i
< (int)ROFF_USERDEF
; i
++) {
327 assert(roffs
[i
].name
[0] >= ASCII_LO
);
328 assert(roffs
[i
].name
[0] <= ASCII_HI
);
330 buc
= ROFF_HASH(roffs
[i
].name
);
332 if (NULL
!= (n
= hash
[buc
])) {
333 for ( ; n
->next
; n
= n
->next
)
337 hash
[buc
] = &roffs
[i
];
342 * Look up a roff token by its name. Returns ROFF_MAX if no macro by
343 * the nil-terminated string name could be found.
346 roffhash_find(const char *p
, size_t s
)
352 * libroff has an extremely simple hashtable, for the time
353 * being, which simply keys on the first character, which must
354 * be printable, then walks a chain. It works well enough until
358 if (p
[0] < ASCII_LO
|| p
[0] > ASCII_HI
)
363 if (NULL
== (n
= hash
[buc
]))
365 for ( ; n
; n
= n
->next
)
366 if (0 == strncmp(n
->name
, p
, s
) && '\0' == n
->name
[(int)s
])
367 return((enum rofft
)(n
- roffs
));
373 * Pop the current node off of the stack of roff instructions currently
377 roffnode_pop(struct roff
*r
)
384 r
->last
= r
->last
->parent
;
391 * Push a roff node onto the instruction stack. This must later be
392 * removed with roffnode_pop().
395 roffnode_push(struct roff
*r
, enum rofft tok
, const char *name
,
400 p
= mandoc_calloc(1, sizeof(struct roffnode
));
403 p
->name
= mandoc_strdup(name
);
407 p
->rule
= p
->parent
? p
->parent
->rule
: 0;
413 roff_free1(struct roff
*r
)
415 struct tbl_node
*tbl
;
419 while (NULL
!= (tbl
= r
->first_tbl
)) {
420 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
;
431 r
->first_eqn
= r
->last_eqn
= r
->eqn
= NULL
;
436 roff_freestr(r
->strtab
);
437 roff_freestr(r
->xmbtab
);
439 r
->strtab
= r
->xmbtab
= NULL
;
441 roff_freereg(r
->regtab
);
446 for (i
= 0; i
< 128; i
++)
454 roff_reset(struct roff
*r
)
462 roff_free(struct roff
*r
)
470 roff_alloc(struct mparse
*parse
, int options
)
474 r
= mandoc_calloc(1, sizeof(struct roff
));
476 r
->options
= options
;
485 * In the current line, expand escape sequences that tend to get
486 * used in numerical expressions and conditional requests.
487 * Also check the syntax of the remaining escape sequences.
490 roff_res(struct roff
*r
, char **bufp
, size_t *szp
, int ln
, int pos
)
492 char ubuf
[24]; /* buffer to print the number */
493 const char *start
; /* start of the string to process */
494 char *stesc
; /* start of an escape sequence ('\\') */
495 const char *stnam
; /* start of the name, after "[(*" */
496 const char *cp
; /* end of the name, e.g. before ']' */
497 const char *res
; /* the string to be substituted */
498 char *nbuf
; /* new buffer to copy bufp to */
499 size_t maxl
; /* expected length of the escape name */
500 size_t naml
; /* actual length of the escape name */
501 int expand_count
; /* to avoid infinite loops */
502 int npos
; /* position in numeric expression */
503 int arg_complete
; /* argument not interrupted by eol */
504 char term
; /* character terminating the escape */
508 stesc
= strchr(start
, '\0') - 1;
509 while (stesc
-- > start
) {
511 /* Search backwards for the next backslash. */
516 /* If it is escaped, skip it. */
518 for (cp
= stesc
- 1; cp
>= start
; cp
--)
522 if (0 == (stesc
- cp
) % 2) {
527 /* Decide whether to expand or to check only. */
544 if (ESCAPE_ERROR
== mandoc_escape(&cp
, NULL
, NULL
))
545 mandoc_vmsg(MANDOCERR_ESC_BAD
,
546 r
->parse
, ln
, (int)(stesc
- *bufp
),
547 "%.*s", (int)(cp
- stesc
), stesc
);
551 if (EXPAND_LIMIT
< ++expand_count
) {
552 mandoc_msg(MANDOCERR_ROFFLOOP
, r
->parse
,
553 ln
, (int)(stesc
- *bufp
), NULL
);
558 * The third character decides the length
559 * of the name of the string or register.
560 * Save a pointer to the name.
587 /* Advance to the end of the name. */
590 for (naml
= 0; 0 == maxl
|| naml
< maxl
; naml
++, cp
++) {
592 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
593 ln
, (int)(stesc
- *bufp
), stesc
);
597 if (0 == maxl
&& *cp
== term
) {
604 * Retrieve the replacement string; if it is
605 * undefined, resume searching for escapes.
611 res
= roff_getstrn(r
, stnam
, naml
);
615 ubuf
[0] = arg_complete
&&
616 roff_evalnum(stnam
, &npos
, NULL
, 0) &&
617 stnam
+ npos
+ 1 == cp
? '1' : '0';
622 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
623 roff_getregn(r
, stnam
, naml
));
628 /* use even incomplete args */
629 (void)snprintf(ubuf
, sizeof(ubuf
), "%d",
635 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
636 r
->parse
, ln
, (int)(stesc
- *bufp
),
637 "%.*s", (int)naml
, stnam
);
641 /* Replace the escape sequence by the string. */
644 *szp
= mandoc_asprintf(&nbuf
, "%s%s%s",
647 /* Prepare for the next replacement. */
650 stesc
= nbuf
+ (stesc
- *bufp
) + strlen(res
);
658 * Process text streams:
659 * Convert all breakable hyphens into ASCII_HYPH.
660 * Decrement and spring input line trap.
663 roff_parsetext(char **bufp
, size_t *szp
, int pos
, int *offs
)
671 start
= p
= *bufp
+ pos
;
674 sz
= strcspn(p
, "-\\");
681 /* Skip over escapes. */
683 esc
= mandoc_escape((const char **)&p
, NULL
, NULL
);
684 if (ESCAPE_ERROR
== esc
)
687 } else if (p
== start
) {
692 if (isalpha((unsigned char)p
[-1]) &&
693 isalpha((unsigned char)p
[1]))
698 /* Spring the input line trap. */
699 if (1 == roffit_lines
) {
700 isz
= mandoc_asprintf(&p
, "%s\n.%s", *bufp
, roffit_macro
);
707 return(ROFF_REPARSE
);
708 } else if (1 < roffit_lines
)
714 roff_parseln(struct roff
*r
, int ln
, char **bufp
,
715 size_t *szp
, int pos
, int *offs
)
722 * Run the reserved-word filter only if we have some reserved
726 e
= roff_res(r
, bufp
, szp
, ln
, pos
);
729 assert(ROFF_CONT
== e
);
732 ctl
= roff_getcontrol(r
, *bufp
, &pos
);
735 * First, if a scope is open and we're not a macro, pass the
736 * text through the macro's filter. If a scope isn't open and
737 * we're not a macro, just let it through.
738 * Finally, if there's an equation scope open, divert it into it
739 * no matter our state.
742 if (r
->last
&& ! ctl
) {
744 assert(roffs
[t
].text
);
745 e
= (*roffs
[t
].text
)(r
, t
, bufp
, szp
, ln
, pos
, pos
, offs
);
746 assert(ROFF_IGN
== e
|| ROFF_CONT
== e
);
751 return(eqn_read(&r
->eqn
, ln
, *bufp
, ppos
, offs
));
754 return(tbl_read(r
->tbl
, ln
, *bufp
, pos
));
755 return(roff_parsetext(bufp
, szp
, pos
, offs
));
759 * If a scope is open, go to the child handler for that macro,
760 * as it may want to preprocess before doing anything with it.
761 * Don't do so if an equation is open.
766 assert(roffs
[t
].sub
);
767 return((*roffs
[t
].sub
)(r
, t
, bufp
, szp
,
768 ln
, ppos
, pos
, offs
));
772 * Lastly, as we've no scope open, try to look up and execute
773 * the new macro. If no macro is found, simply return and let
774 * the compilers handle it.
777 if (ROFF_MAX
== (t
= roff_parse(r
, *bufp
, &pos
, ln
, ppos
)))
780 assert(roffs
[t
].proc
);
781 return((*roffs
[t
].proc
)(r
, t
, bufp
, szp
, ln
, ppos
, pos
, offs
));
785 roff_endparse(struct roff
*r
)
789 mandoc_msg(MANDOCERR_SCOPEEXIT
, r
->parse
,
790 r
->last
->line
, r
->last
->col
, NULL
);
793 mandoc_msg(MANDOCERR_SCOPEEXIT
, r
->parse
,
794 r
->eqn
->eqn
.ln
, r
->eqn
->eqn
.pos
, NULL
);
799 mandoc_msg(MANDOCERR_SCOPEEXIT
, r
->parse
,
800 r
->tbl
->line
, r
->tbl
->pos
, NULL
);
806 * Parse a roff node's type from the input buffer. This must be in the
807 * form of ".foo xxx" in the usual way.
810 roff_parse(struct roff
*r
, char *buf
, int *pos
, int ln
, int ppos
)
819 if ('\0' == *cp
|| '"' == *cp
|| '\t' == *cp
|| ' ' == *cp
)
823 maclen
= roff_getname(r
, &cp
, ln
, ppos
);
825 t
= (r
->current_string
= roff_getstrn(r
, mac
, maclen
))
826 ? ROFF_USERDEF
: roffhash_find(mac
, maclen
);
835 roff_cblock(ROFF_ARGS
)
839 * A block-close `..' should only be invoked as a child of an
840 * ignore macro, otherwise raise a warning and just ignore it.
843 if (NULL
== r
->last
) {
844 mandoc_msg(MANDOCERR_NOSCOPE
, r
->parse
, ln
, ppos
, NULL
);
848 switch (r
->last
->tok
) {
850 /* ROFF_am1 is remapped to ROFF_am in roff_block(). */
855 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
862 mandoc_msg(MANDOCERR_NOSCOPE
, r
->parse
, ln
, ppos
, NULL
);
867 mandoc_vmsg(MANDOCERR_ARG_SKIP
, r
->parse
, ln
, pos
,
868 ".. %s", *bufp
+ pos
);
871 roffnode_cleanscope(r
);
877 roffnode_cleanscope(struct roff
*r
)
881 if (--r
->last
->endspan
!= 0)
888 roff_ccond(struct roff
*r
, int ln
, int ppos
)
891 if (NULL
== r
->last
) {
892 mandoc_msg(MANDOCERR_NOSCOPE
, r
->parse
, ln
, ppos
, NULL
);
896 switch (r
->last
->tok
) {
904 mandoc_msg(MANDOCERR_NOSCOPE
, r
->parse
, ln
, ppos
, NULL
);
908 if (r
->last
->endspan
> -1) {
909 mandoc_msg(MANDOCERR_NOSCOPE
, r
->parse
, ln
, ppos
, NULL
);
914 roffnode_cleanscope(r
);
919 roff_block(ROFF_ARGS
)
925 /* Ignore groff compatibility mode for now. */
929 else if (ROFF_am1
== tok
)
932 /* Parse the macro name argument. */
935 if (ROFF_ig
== tok
) {
940 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
941 iname
[namesz
] = '\0';
944 /* Resolve the macro name argument if it is indirect. */
946 if (namesz
&& (ROFF_dei
== tok
|| ROFF_ami
== tok
)) {
947 if (NULL
== (name
= roff_getstrn(r
, iname
, namesz
))) {
948 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
949 r
->parse
, ln
, (int)(iname
- *bufp
),
950 "%.*s", (int)namesz
, iname
);
953 namesz
= strlen(name
);
957 if (0 == namesz
&& ROFF_ig
!= tok
) {
958 mandoc_msg(MANDOCERR_REQ_EMPTY
, r
->parse
,
959 ln
, ppos
, roffs
[tok
].name
);
963 roffnode_push(r
, tok
, name
, ln
, ppos
);
966 * At the beginning of a `de' macro, clear the existing string
967 * with the same name, if there is one. New content will be
968 * appended from roff_block_text() in multiline mode.
971 if (ROFF_de
== tok
|| ROFF_dei
== tok
)
972 roff_setstrn(&r
->strtab
, name
, namesz
, "", 0, 0);
977 /* Get the custom end marker. */
980 namesz
= roff_getname(r
, &cp
, ln
, ppos
);
982 /* Resolve the end marker if it is indirect. */
984 if (namesz
&& (ROFF_dei
== tok
|| ROFF_ami
== tok
)) {
985 if (NULL
== (name
= roff_getstrn(r
, iname
, namesz
))) {
986 mandoc_vmsg(MANDOCERR_STR_UNDEF
,
987 r
->parse
, ln
, (int)(iname
- *bufp
),
988 "%.*s", (int)namesz
, iname
);
991 namesz
= strlen(name
);
996 r
->last
->end
= mandoc_strndup(name
, namesz
);
999 mandoc_vmsg(MANDOCERR_ARG_EXCESS
, r
->parse
,
1000 ln
, pos
, ".%s ... %s", roffs
[tok
].name
, cp
);
1006 roff_block_sub(ROFF_ARGS
)
1012 * First check whether a custom macro exists at this level. If
1013 * it does, then check against it. This is some of groff's
1014 * stranger behaviours. If we encountered a custom end-scope
1015 * tag and that tag also happens to be a "real" macro, then we
1016 * need to try interpreting it again as a real macro. If it's
1017 * not, then return ignore. Else continue.
1021 for (i
= pos
, j
= 0; r
->last
->end
[j
]; j
++, i
++)
1022 if ((*bufp
)[i
] != r
->last
->end
[j
])
1025 if ('\0' == r
->last
->end
[j
] &&
1026 ('\0' == (*bufp
)[i
] ||
1027 ' ' == (*bufp
)[i
] ||
1028 '\t' == (*bufp
)[i
])) {
1030 roffnode_cleanscope(r
);
1032 while (' ' == (*bufp
)[i
] || '\t' == (*bufp
)[i
])
1036 if (ROFF_MAX
!= roff_parse(r
, *bufp
, &pos
, ln
, ppos
))
1043 * If we have no custom end-query or lookup failed, then try
1044 * pulling it out of the hashtable.
1047 t
= roff_parse(r
, *bufp
, &pos
, ln
, ppos
);
1049 if (ROFF_cblock
!= t
) {
1051 roff_setstr(r
, r
->last
->name
, *bufp
+ ppos
, 2);
1055 assert(roffs
[t
].proc
);
1056 return((*roffs
[t
].proc
)(r
, t
, bufp
, szp
, ln
, ppos
, pos
, offs
));
1060 roff_block_text(ROFF_ARGS
)
1064 roff_setstr(r
, r
->last
->name
, *bufp
+ pos
, 2);
1070 roff_cond_sub(ROFF_ARGS
)
1077 roffnode_cleanscope(r
);
1078 t
= roff_parse(r
, *bufp
, &pos
, ln
, ppos
);
1081 * Fully handle known macros when they are structurally
1082 * required or when the conditional evaluated to true.
1085 if ((ROFF_MAX
!= t
) &&
1086 (rr
|| ROFFMAC_STRUCT
& roffs
[t
].flags
)) {
1087 assert(roffs
[t
].proc
);
1088 return((*roffs
[t
].proc
)(r
, t
, bufp
, szp
,
1089 ln
, ppos
, pos
, offs
));
1093 * If `\}' occurs on a macro line without a preceding macro,
1094 * drop the line completely.
1098 if ('\\' == ep
[0] && '}' == ep
[1])
1101 /* Always check for the closing delimiter `\}'. */
1103 while (NULL
!= (ep
= strchr(ep
, '\\'))) {
1104 if ('}' == *(++ep
)) {
1106 roff_ccond(r
, ln
, ep
- *bufp
- 1);
1110 return(rr
? ROFF_CONT
: ROFF_IGN
);
1114 roff_cond_text(ROFF_ARGS
)
1120 roffnode_cleanscope(r
);
1123 while (NULL
!= (ep
= strchr(ep
, '\\'))) {
1124 if ('}' == *(++ep
)) {
1126 roff_ccond(r
, ln
, ep
- *bufp
- 1);
1130 return(rr
? ROFF_CONT
: ROFF_IGN
);
1134 * Parse a single signed integer number. Stop at the first non-digit.
1135 * If there is at least one digit, return success and advance the
1136 * parse point, else return failure and let the parse point unchanged.
1137 * Ignore overflows, treat them just like the C language.
1140 roff_getnum(const char *v
, int *pos
, int *res
)
1152 for (*res
= 0; isdigit((unsigned char)v
[p
]); p
++)
1153 *res
= 10 * *res
+ v
[p
] - '0';
1165 * Evaluate a string comparison condition.
1166 * The first character is the delimiter.
1167 * Succeed if the string up to its second occurrence
1168 * matches the string up to its third occurence.
1169 * Advance the cursor after the third occurrence
1170 * or lacking that, to the end of the line.
1173 roff_evalstrcond(const char *v
, int *pos
)
1175 const char *s1
, *s2
, *s3
;
1179 s1
= v
+ *pos
; /* initial delimiter */
1180 s2
= s1
+ 1; /* for scanning the first string */
1181 s3
= strchr(s2
, *s1
); /* for scanning the second string */
1183 if (NULL
== s3
) /* found no middle delimiter */
1186 while ('\0' != *++s3
) {
1187 if (*s2
!= *s3
) { /* mismatch */
1188 s3
= strchr(s3
, *s1
);
1191 if (*s3
== *s1
) { /* found the final delimiter */
1200 s3
= strchr(s2
, '\0');
1208 * Evaluate an optionally negated single character, numerical,
1209 * or string condition.
1212 roff_evalcond(const char *v
, int *pos
)
1214 int wanttrue
, number
;
1216 if ('!' == v
[*pos
]) {
1243 if (roff_evalnum(v
, pos
, &number
, 0))
1244 return((number
> 0) == wanttrue
);
1246 return(roff_evalstrcond(v
, pos
) == wanttrue
);
1250 roff_line_ignore(ROFF_ARGS
)
1257 roff_cond(ROFF_ARGS
)
1260 roffnode_push(r
, tok
, NULL
, ln
, ppos
);
1263 * An `.el' has no conditional body: it will consume the value
1264 * of the current rstack entry set in prior `ie' calls or
1267 * If we're not an `el', however, then evaluate the conditional.
1270 r
->last
->rule
= ROFF_el
== tok
?
1271 (r
->rstackpos
< 0 ? 0 : r
->rstack
[r
->rstackpos
--]) :
1272 roff_evalcond(*bufp
, &pos
);
1275 * An if-else will put the NEGATION of the current evaluated
1276 * conditional into the stack of rules.
1279 if (ROFF_ie
== tok
) {
1280 if (r
->rstackpos
== RSTACK_MAX
- 1) {
1281 mandoc_msg(MANDOCERR_MEM
,
1282 r
->parse
, ln
, ppos
, NULL
);
1285 r
->rstack
[++r
->rstackpos
] = !r
->last
->rule
;
1288 /* If the parent has false as its rule, then so do we. */
1290 if (r
->last
->parent
&& !r
->last
->parent
->rule
)
1295 * If there is nothing on the line after the conditional,
1296 * not even whitespace, use next-line scope.
1299 if ('\0' == (*bufp
)[pos
]) {
1300 r
->last
->endspan
= 2;
1304 while (' ' == (*bufp
)[pos
])
1307 /* An opening brace requests multiline scope. */
1309 if ('\\' == (*bufp
)[pos
] && '{' == (*bufp
)[pos
+ 1]) {
1310 r
->last
->endspan
= -1;
1316 * Anything else following the conditional causes
1317 * single-line scope. Warn if the scope contains
1318 * nothing but trailing whitespace.
1321 if ('\0' == (*bufp
)[pos
])
1322 mandoc_msg(MANDOCERR_COND_EMPTY
, r
->parse
,
1323 ln
, ppos
, roffs
[tok
].name
);
1325 r
->last
->endspan
= 1;
1340 * The first word is the name of the string.
1341 * If it is empty or terminated by an escape sequence,
1342 * abort the `ds' request without defining anything.
1345 name
= string
= *bufp
+ pos
;
1349 namesz
= roff_getname(r
, &string
, ln
, pos
);
1350 if ('\\' == name
[namesz
])
1353 /* Read past the initial double-quote, if any. */
1357 /* The rest is the value. */
1358 roff_setstrn(&r
->strtab
, name
, namesz
, string
, strlen(string
),
1364 * Parse a single operator, one or two characters long.
1365 * If the operator is recognized, return success and advance the
1366 * parse point, else return failure and let the parse point unchanged.
1369 roff_getop(const char *v
, int *pos
, char *res
)
1390 switch (v
[*pos
+ 1]) {
1408 switch (v
[*pos
+ 1]) {
1422 if ('=' == v
[*pos
+ 1])
1434 * Evaluate either a parenthesized numeric expression
1435 * or a single signed integer number.
1438 roff_evalpar(const char *v
, int *pos
, int *res
)
1442 return(roff_getnum(v
, pos
, res
));
1445 if ( ! roff_evalnum(v
, pos
, res
, 1))
1449 * Omission of the closing parenthesis
1450 * is an error in validation mode,
1451 * but ignored in evaluation mode.
1456 else if (NULL
== res
)
1463 * Evaluate a complete numeric expression.
1464 * Proceed left to right, there is no concept of precedence.
1467 roff_evalnum(const char *v
, int *pos
, int *res
, int skipwhite
)
1469 int mypos
, operand2
;
1478 while (isspace((unsigned char)v
[*pos
]))
1481 if ( ! roff_evalpar(v
, pos
, res
))
1486 while (isspace((unsigned char)v
[*pos
]))
1489 if ( ! roff_getop(v
, pos
, &operator))
1493 while (isspace((unsigned char)v
[*pos
]))
1496 if ( ! roff_evalpar(v
, pos
, &operand2
))
1500 while (isspace((unsigned char)v
[*pos
]))
1523 *res
= *res
< operand2
;
1526 *res
= *res
> operand2
;
1529 *res
= *res
<= operand2
;
1532 *res
= *res
>= operand2
;
1535 *res
= *res
== operand2
;
1538 *res
= *res
!= operand2
;
1541 *res
= *res
&& operand2
;
1544 *res
= *res
|| operand2
;
1547 if (operand2
< *res
)
1551 if (operand2
> *res
)
1562 roff_setreg(struct roff
*r
, const char *name
, int val
, char sign
)
1564 struct roffreg
*reg
;
1566 /* Search for an existing register with the same name. */
1569 while (reg
&& strcmp(name
, reg
->key
.p
))
1573 /* Create a new register. */
1574 reg
= mandoc_malloc(sizeof(struct roffreg
));
1575 reg
->key
.p
= mandoc_strdup(name
);
1576 reg
->key
.sz
= strlen(name
);
1578 reg
->next
= r
->regtab
;
1584 else if ('-' == sign
)
1591 * Handle some predefined read-only number registers.
1592 * For now, return -1 if the requested register is not predefined;
1593 * in case a predefined read-only register having the value -1
1594 * were to turn up, another special value would have to be chosen.
1597 roff_getregro(const char *name
)
1601 case 'A': /* ASCII approximation mode is always off. */
1603 case 'g': /* Groff compatibility mode is always on. */
1605 case 'H': /* Fixed horizontal resolution. */
1607 case 'j': /* Always adjust left margin only. */
1609 case 'T': /* Some output device is always defined. */
1611 case 'V': /* Fixed vertical resolution. */
1619 roff_getreg(const struct roff
*r
, const char *name
)
1621 struct roffreg
*reg
;
1624 if ('.' == name
[0] && '\0' != name
[1] && '\0' == name
[2]) {
1625 val
= roff_getregro(name
+ 1);
1630 for (reg
= r
->regtab
; reg
; reg
= reg
->next
)
1631 if (0 == strcmp(name
, reg
->key
.p
))
1638 roff_getregn(const struct roff
*r
, const char *name
, size_t len
)
1640 struct roffreg
*reg
;
1643 if ('.' == name
[0] && 2 == len
) {
1644 val
= roff_getregro(name
+ 1);
1649 for (reg
= r
->regtab
; reg
; reg
= reg
->next
)
1650 if (len
== reg
->key
.sz
&&
1651 0 == strncmp(name
, reg
->key
.p
, len
))
1658 roff_freereg(struct roffreg
*reg
)
1660 struct roffreg
*old_reg
;
1662 while (NULL
!= reg
) {
1678 key
= val
= *bufp
+ pos
;
1682 keysz
= roff_getname(r
, &val
, ln
, pos
);
1683 if ('\\' == key
[keysz
])
1688 if ('+' == sign
|| '-' == sign
)
1691 if (roff_evalnum(val
, NULL
, &iv
, 0))
1692 roff_setreg(r
, key
, iv
, sign
);
1700 struct roffreg
*reg
, **prev
;
1704 name
= cp
= *bufp
+ pos
;
1707 namesz
= roff_getname(r
, &cp
, ln
, pos
);
1708 name
[namesz
] = '\0';
1713 if (NULL
== reg
|| !strcmp(name
, reg
->key
.p
))
1733 while ('\0' != *cp
) {
1735 namesz
= roff_getname(r
, &cp
, ln
, (int)(cp
- *bufp
));
1736 roff_setstrn(&r
->strtab
, name
, namesz
, NULL
, 0, 0);
1737 if ('\\' == name
[namesz
])
1750 /* Parse the number of lines. */
1752 len
= strcspn(cp
, " \t");
1754 if ((iv
= mandoc_strntoi(cp
, len
, 10)) <= 0) {
1755 mandoc_msg(MANDOCERR_NUMERIC
, r
->parse
,
1756 ln
, ppos
, *bufp
+ 1);
1761 /* Arm the input line trap. */
1763 roffit_macro
= mandoc_strdup(cp
);
1770 const char *const *cp
;
1772 if (0 == ((MPARSE_MDOC
| MPARSE_QUICK
) & r
->options
))
1773 for (cp
= __mdoc_reserved
; *cp
; cp
++)
1774 roff_setstr(r
, *cp
, NULL
, 0);
1782 const char *const *cp
;
1784 if (0 == (MPARSE_QUICK
& r
->options
))
1785 for (cp
= __man_reserved
; *cp
; cp
++)
1786 roff_setstr(r
, *cp
, NULL
, 0);
1796 mandoc_msg(MANDOCERR_NOSCOPE
, r
->parse
, ln
, ppos
, NULL
);
1808 mandoc_msg(MANDOCERR_NOSCOPE
, r
->parse
, ln
, ppos
, NULL
);
1810 tbl_restart(ppos
, ln
, r
->tbl
);
1817 roff_closeeqn(struct roff
*r
)
1820 return(r
->eqn
&& ROFF_EQN
== eqn_end(&r
->eqn
) ? 1 : 0);
1825 roff_openeqn(struct roff
*r
, const char *name
, int line
,
1826 int offs
, const char *buf
)
1831 assert(NULL
== r
->eqn
);
1832 e
= eqn_alloc(name
, offs
, line
, r
->parse
);
1835 r
->last_eqn
->next
= e
;
1837 r
->first_eqn
= r
->last_eqn
= e
;
1839 r
->eqn
= r
->last_eqn
= e
;
1843 eqn_read(&r
->eqn
, line
, buf
, offs
, &poff
);
1851 roff_openeqn(r
, *bufp
+ pos
, ln
, ppos
, NULL
);
1859 mandoc_msg(MANDOCERR_NOSCOPE
, r
->parse
, ln
, ppos
, NULL
);
1866 struct tbl_node
*tbl
;
1869 mandoc_msg(MANDOCERR_SCOPEBROKEN
, r
->parse
, ln
, ppos
, NULL
);
1873 tbl
= tbl_alloc(ppos
, ln
, r
->parse
);
1876 r
->last_tbl
->next
= tbl
;
1878 r
->first_tbl
= r
->last_tbl
= tbl
;
1880 r
->tbl
= r
->last_tbl
= tbl
;
1891 if ('\0' == *p
|| '.' == (r
->control
= *p
++))
1895 mandoc_msg(MANDOCERR_ARGCOUNT
, r
->parse
, ln
, ppos
, NULL
);
1903 const char *p
, *first
, *second
;
1905 enum mandoc_esc esc
;
1910 mandoc_msg(MANDOCERR_ARGCOUNT
, r
->parse
, ln
, ppos
, NULL
);
1914 while ('\0' != *p
) {
1918 if ('\\' == *first
) {
1919 esc
= mandoc_escape(&p
, NULL
, NULL
);
1920 if (ESCAPE_ERROR
== esc
) {
1921 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
1922 ln
, (int)(p
- *bufp
), first
);
1925 fsz
= (size_t)(p
- first
);
1929 if ('\\' == *second
) {
1930 esc
= mandoc_escape(&p
, NULL
, NULL
);
1931 if (ESCAPE_ERROR
== esc
) {
1932 mandoc_msg(MANDOCERR_ESC_BAD
, r
->parse
,
1933 ln
, (int)(p
- *bufp
), second
);
1936 ssz
= (size_t)(p
- second
);
1937 } else if ('\0' == *second
) {
1938 mandoc_msg(MANDOCERR_ARGCOUNT
, r
->parse
,
1939 ln
, (int)(p
- *bufp
), NULL
);
1945 roff_setstrn(&r
->xmbtab
, first
, fsz
,
1950 if (NULL
== r
->xtab
)
1951 r
->xtab
= mandoc_calloc(128,
1952 sizeof(struct roffstr
));
1954 free(r
->xtab
[(int)*first
].p
);
1955 r
->xtab
[(int)*first
].p
= mandoc_strndup(second
, ssz
);
1956 r
->xtab
[(int)*first
].sz
= ssz
;
1968 mandoc_vmsg(MANDOCERR_SO
, r
->parse
, ln
, ppos
, ".so %s", name
);
1971 * Handle `so'. Be EXTREMELY careful, as we shouldn't be
1972 * opening anything that's not in our cwd or anything beneath
1973 * it. Thus, explicitly disallow traversing up the file-system
1974 * or using absolute paths.
1977 if ('/' == *name
|| strstr(name
, "../") || strstr(name
, "/..")) {
1978 mandoc_vmsg(MANDOCERR_SO_PATH
, r
->parse
, ln
, ppos
,
1988 roff_userdef(ROFF_ARGS
)
1995 * Collect pointers to macro argument strings
1996 * and NUL-terminate them.
1999 for (i
= 0; i
< 9; i
++)
2000 arg
[i
] = '\0' == *cp
? "" :
2001 mandoc_getarg(r
->parse
, &cp
, ln
, &pos
);
2004 * Expand macro arguments.
2007 n1
= cp
= mandoc_strdup(r
->current_string
);
2008 while (NULL
!= (cp
= strstr(cp
, "\\$"))) {
2010 if (0 > i
|| 8 < i
) {
2011 /* Not an argument invocation. */
2016 *szp
= mandoc_asprintf(&n2
, "%s%s%s",
2017 n1
, arg
[i
], cp
+ 3) + 1;
2018 cp
= n2
+ (cp
- n1
);
2024 * Replace the macro invocation
2025 * by the expanded macro.
2030 *szp
= strlen(*bufp
) + 1;
2032 return(*szp
> 1 && '\n' == (*bufp
)[(int)*szp
- 2] ?
2033 ROFF_REPARSE
: ROFF_APPEND
);
2037 roff_getname(struct roff
*r
, char **cpp
, int ln
, int pos
)
2046 /* Read until end of name and terminate it with NUL. */
2047 for (cp
= name
; 1; cp
++) {
2048 if ('\0' == *cp
|| ' ' == *cp
) {
2055 if ('{' == cp
[1] || '}' == cp
[1])
2060 mandoc_msg(MANDOCERR_NAMESC
, r
->parse
, ln
, pos
, NULL
);
2061 mandoc_escape((const char **)&cp
, NULL
, NULL
);
2065 /* Read past spaces. */
2074 * Store *string into the user-defined string called *name.
2075 * To clear an existing entry, call with (*r, *name, NULL, 0).
2076 * append == 0: replace mode
2077 * append == 1: single-line append mode
2078 * append == 2: multiline append mode, append '\n' after each call
2081 roff_setstr(struct roff
*r
, const char *name
, const char *string
,
2085 roff_setstrn(&r
->strtab
, name
, strlen(name
), string
,
2086 string
? strlen(string
) : 0, append
);
2090 roff_setstrn(struct roffkv
**r
, const char *name
, size_t namesz
,
2091 const char *string
, size_t stringsz
, int append
)
2096 size_t oldch
, newch
;
2098 /* Search for an existing string with the same name. */
2101 while (n
&& (namesz
!= n
->key
.sz
||
2102 strncmp(n
->key
.p
, name
, namesz
)))
2106 /* Create a new string table entry. */
2107 n
= mandoc_malloc(sizeof(struct roffkv
));
2108 n
->key
.p
= mandoc_strndup(name
, namesz
);
2114 } else if (0 == append
) {
2124 * One additional byte for the '\n' in multiline mode,
2125 * and one for the terminating '\0'.
2127 newch
= stringsz
+ (1 < append
? 2u : 1u);
2129 if (NULL
== n
->val
.p
) {
2130 n
->val
.p
= mandoc_malloc(newch
);
2135 n
->val
.p
= mandoc_realloc(n
->val
.p
, oldch
+ newch
);
2138 /* Skip existing content in the destination buffer. */
2139 c
= n
->val
.p
+ (int)oldch
;
2141 /* Append new content to the destination buffer. */
2143 while (i
< (int)stringsz
) {
2145 * Rudimentary roff copy mode:
2146 * Handle escaped backslashes.
2148 if ('\\' == string
[i
] && '\\' == string
[i
+ 1])
2153 /* Append terminating bytes. */
2158 n
->val
.sz
= (int)(c
- n
->val
.p
);
2162 roff_getstrn(const struct roff
*r
, const char *name
, size_t len
)
2164 const struct roffkv
*n
;
2167 for (n
= r
->strtab
; n
; n
= n
->next
)
2168 if (0 == strncmp(name
, n
->key
.p
, len
) &&
2169 '\0' == n
->key
.p
[(int)len
])
2172 for (i
= 0; i
< PREDEFS_MAX
; i
++)
2173 if (0 == strncmp(name
, predefs
[i
].name
, len
) &&
2174 '\0' == predefs
[i
].name
[(int)len
])
2175 return(predefs
[i
].str
);
2181 roff_freestr(struct roffkv
*r
)
2183 struct roffkv
*n
, *nn
;
2185 for (n
= r
; n
; n
= nn
) {
2193 const struct tbl_span
*
2194 roff_span(const struct roff
*r
)
2197 return(r
->tbl
? tbl_span(r
->tbl
) : NULL
);
2201 roff_eqn(const struct roff
*r
)
2204 return(r
->last_eqn
? &r
->last_eqn
->eqn
: NULL
);
2208 * Duplicate an input string, making the appropriate character
2209 * conversations (as stipulated by `tr') along the way.
2210 * Returns a heap-allocated string with all the replacements made.
2213 roff_strdup(const struct roff
*r
, const char *p
)
2215 const struct roffkv
*cp
;
2219 enum mandoc_esc esc
;
2221 if (NULL
== r
->xmbtab
&& NULL
== r
->xtab
)
2222 return(mandoc_strdup(p
));
2223 else if ('\0' == *p
)
2224 return(mandoc_strdup(""));
2227 * Step through each character looking for term matches
2228 * (remember that a `tr' can be invoked with an escape, which is
2229 * a glyph but the escape is multi-character).
2230 * We only do this if the character hash has been initialised
2231 * and the string is >0 length.
2237 while ('\0' != *p
) {
2238 if ('\\' != *p
&& r
->xtab
&& r
->xtab
[(int)*p
].p
) {
2239 sz
= r
->xtab
[(int)*p
].sz
;
2240 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
2241 memcpy(res
+ ssz
, r
->xtab
[(int)*p
].p
, sz
);
2245 } else if ('\\' != *p
) {
2246 res
= mandoc_realloc(res
, ssz
+ 2);
2251 /* Search for term matches. */
2252 for (cp
= r
->xmbtab
; cp
; cp
= cp
->next
)
2253 if (0 == strncmp(p
, cp
->key
.p
, cp
->key
.sz
))
2258 * A match has been found.
2259 * Append the match to the array and move
2260 * forward by its keysize.
2262 res
= mandoc_realloc(res
,
2263 ssz
+ cp
->val
.sz
+ 1);
2264 memcpy(res
+ ssz
, cp
->val
.p
, cp
->val
.sz
);
2266 p
+= (int)cp
->key
.sz
;
2271 * Handle escapes carefully: we need to copy
2272 * over just the escape itself, or else we might
2273 * do replacements within the escape itself.
2274 * Make sure to pass along the bogus string.
2277 esc
= mandoc_escape(&p
, NULL
, NULL
);
2278 if (ESCAPE_ERROR
== esc
) {
2280 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
2281 memcpy(res
+ ssz
, pp
, sz
);
2285 * We bail out on bad escapes.
2286 * No need to warn: we already did so when
2287 * roff_res() was called.
2290 res
= mandoc_realloc(res
, ssz
+ sz
+ 1);
2291 memcpy(res
+ ssz
, pp
, sz
);
2295 res
[(int)ssz
] = '\0';
2300 * Find out whether a line is a macro line or not.
2301 * If it is, adjust the current position and return one; if it isn't,
2302 * return zero and don't change the current position.
2303 * If the control character has been set with `.cc', then let that grain
2305 * This is slighly contrary to groff, where using the non-breaking
2306 * control character when `cc' has been invoked will cause the
2307 * non-breaking macro contents to be printed verbatim.
2310 roff_getcontrol(const struct roff
*r
, const char *cp
, int *ppos
)
2316 if (0 != r
->control
&& cp
[pos
] == r
->control
)
2318 else if (0 != r
->control
)
2320 else if ('\\' == cp
[pos
] && '.' == cp
[pos
+ 1])
2322 else if ('.' == cp
[pos
] || '\'' == cp
[pos
])
2327 while (' ' == cp
[pos
] || '\t' == cp
[pos
])