]> git.cameronkatri.com Git - mandoc.git/blob - roff.c
Add `cc' support.
[mandoc.git] / roff.c
1 /* $Id: roff.c,v 1.174 2012/06/12 20:21:04 kristaps Exp $ */
2 /*
3 * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "mandoc.h"
28 #include "libroff.h"
29 #include "libmandoc.h"
30
31 /* Maximum number of nested if-else conditionals. */
32 #define RSTACK_MAX 128
33
34 /* Maximum number of string expansions per line, to break infinite loops. */
35 #define EXPAND_LIMIT 1000
36
37 enum rofft {
38 ROFF_ad,
39 ROFF_am,
40 ROFF_ami,
41 ROFF_am1,
42 ROFF_cc,
43 ROFF_de,
44 ROFF_dei,
45 ROFF_de1,
46 ROFF_ds,
47 ROFF_el,
48 ROFF_hy,
49 ROFF_ie,
50 ROFF_if,
51 ROFF_ig,
52 ROFF_it,
53 ROFF_ne,
54 ROFF_nh,
55 ROFF_nr,
56 ROFF_ns,
57 ROFF_ps,
58 ROFF_rm,
59 ROFF_so,
60 ROFF_ta,
61 ROFF_tr,
62 ROFF_TS,
63 ROFF_TE,
64 ROFF_T_,
65 ROFF_EQ,
66 ROFF_EN,
67 ROFF_cblock,
68 ROFF_ccond,
69 ROFF_USERDEF,
70 ROFF_MAX
71 };
72
73 enum roffrule {
74 ROFFRULE_ALLOW,
75 ROFFRULE_DENY
76 };
77
78 /*
79 * A single register entity. If "set" is zero, the value of the
80 * register should be the default one, which is per-register.
81 * Registers are assumed to be unsigned ints for now.
82 */
83 struct reg {
84 int set; /* whether set or not */
85 unsigned int u; /* unsigned integer */
86 };
87
88 /*
89 * An incredibly-simple string buffer.
90 */
91 struct roffstr {
92 char *p; /* nil-terminated buffer */
93 size_t sz; /* saved strlen(p) */
94 };
95
96 /*
97 * A key-value roffstr pair as part of a singly-linked list.
98 */
99 struct roffkv {
100 struct roffstr key;
101 struct roffstr val;
102 struct roffkv *next; /* next in list */
103 };
104
105 struct roff {
106 struct mparse *parse; /* parse point */
107 struct roffnode *last; /* leaf of stack */
108 enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */
109 char control; /* control character */
110 int rstackpos; /* position in rstack */
111 struct reg regs[REG__MAX];
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 };
123
124 struct roffnode {
125 enum rofft tok; /* type of node */
126 struct roffnode *parent; /* up one in stack */
127 int line; /* parse line */
128 int col; /* parse col */
129 char *name; /* node name, e.g. macro name */
130 char *end; /* end-rules: custom token */
131 int endspan; /* end-rules: next-line or infty */
132 enum roffrule rule; /* current evaluation rule */
133 };
134
135 #define ROFF_ARGS struct roff *r, /* parse ctx */ \
136 enum rofft tok, /* tok of macro */ \
137 char **bufp, /* input buffer */ \
138 size_t *szp, /* size of input buffer */ \
139 int ln, /* parse line */ \
140 int ppos, /* original pos in buffer */ \
141 int pos, /* current pos in buffer */ \
142 int *offs /* reset offset of buffer data */
143
144 typedef enum rofferr (*roffproc)(ROFF_ARGS);
145
146 struct roffmac {
147 const char *name; /* macro name */
148 roffproc proc; /* process new macro */
149 roffproc text; /* process as child text of macro */
150 roffproc sub; /* process as child of macro */
151 int flags;
152 #define ROFFMAC_STRUCT (1 << 0) /* always interpret */
153 struct roffmac *next;
154 };
155
156 struct predef {
157 const char *name; /* predefined input name */
158 const char *str; /* replacement symbol */
159 };
160
161 #define PREDEF(__name, __str) \
162 { (__name), (__str) },
163
164 static enum rofft roffhash_find(const char *, size_t);
165 static void roffhash_init(void);
166 static void roffnode_cleanscope(struct roff *);
167 static void roffnode_pop(struct roff *);
168 static void roffnode_push(struct roff *, enum rofft,
169 const char *, int, int);
170 static enum rofferr roff_block(ROFF_ARGS);
171 static enum rofferr roff_block_text(ROFF_ARGS);
172 static enum rofferr roff_block_sub(ROFF_ARGS);
173 static enum rofferr roff_cblock(ROFF_ARGS);
174 static enum rofferr roff_cc(ROFF_ARGS);
175 static enum rofferr roff_ccond(ROFF_ARGS);
176 static enum rofferr roff_cond(ROFF_ARGS);
177 static enum rofferr roff_cond_text(ROFF_ARGS);
178 static enum rofferr roff_cond_sub(ROFF_ARGS);
179 static enum rofferr roff_ds(ROFF_ARGS);
180 static enum roffrule roff_evalcond(const char *, int *);
181 static void roff_free1(struct roff *);
182 static void roff_freestr(struct roffkv *);
183 static char *roff_getname(struct roff *, char **, int, int);
184 static const char *roff_getstrn(const struct roff *,
185 const char *, size_t);
186 static enum rofferr roff_line_ignore(ROFF_ARGS);
187 static enum rofferr roff_nr(ROFF_ARGS);
188 static void roff_openeqn(struct roff *, const char *,
189 int, int, const char *);
190 static enum rofft roff_parse(struct roff *, const char *, int *);
191 static enum rofferr roff_parsetext(char *);
192 static enum rofferr roff_res(struct roff *,
193 char **, size_t *, int, int);
194 static enum rofferr roff_rm(ROFF_ARGS);
195 static void roff_setstr(struct roff *,
196 const char *, const char *, int);
197 static void roff_setstrn(struct roffkv **, const char *,
198 size_t, const char *, size_t, int);
199 static enum rofferr roff_so(ROFF_ARGS);
200 static enum rofferr roff_tr(ROFF_ARGS);
201 static enum rofferr roff_TE(ROFF_ARGS);
202 static enum rofferr roff_TS(ROFF_ARGS);
203 static enum rofferr roff_EQ(ROFF_ARGS);
204 static enum rofferr roff_EN(ROFF_ARGS);
205 static enum rofferr roff_T_(ROFF_ARGS);
206 static enum rofferr roff_userdef(ROFF_ARGS);
207
208 /* See roffhash_find() */
209
210 #define ASCII_HI 126
211 #define ASCII_LO 33
212 #define HASHWIDTH (ASCII_HI - ASCII_LO + 1)
213
214 static struct roffmac *hash[HASHWIDTH];
215
216 static struct roffmac roffs[ROFF_MAX] = {
217 { "ad", roff_line_ignore, NULL, NULL, 0, NULL },
218 { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
219 { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
220 { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
221 { "cc", roff_cc, NULL, NULL, 0, NULL },
222 { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
223 { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
224 { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
225 { "ds", roff_ds, NULL, NULL, 0, NULL },
226 { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
227 { "hy", roff_line_ignore, NULL, NULL, 0, NULL },
228 { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
229 { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
230 { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
231 { "it", roff_line_ignore, NULL, NULL, 0, NULL },
232 { "ne", roff_line_ignore, NULL, NULL, 0, NULL },
233 { "nh", roff_line_ignore, NULL, NULL, 0, NULL },
234 { "nr", roff_nr, NULL, NULL, 0, NULL },
235 { "ns", roff_line_ignore, NULL, NULL, 0, NULL },
236 { "ps", roff_line_ignore, NULL, NULL, 0, NULL },
237 { "rm", roff_rm, NULL, NULL, 0, NULL },
238 { "so", roff_so, NULL, NULL, 0, NULL },
239 { "ta", roff_line_ignore, NULL, NULL, 0, NULL },
240 { "tr", roff_tr, NULL, NULL, 0, NULL },
241 { "TS", roff_TS, NULL, NULL, 0, NULL },
242 { "TE", roff_TE, NULL, NULL, 0, NULL },
243 { "T&", roff_T_, NULL, NULL, 0, NULL },
244 { "EQ", roff_EQ, NULL, NULL, 0, NULL },
245 { "EN", roff_EN, NULL, NULL, 0, NULL },
246 { ".", roff_cblock, NULL, NULL, 0, NULL },
247 { "\\}", roff_ccond, NULL, NULL, 0, NULL },
248 { NULL, roff_userdef, NULL, NULL, 0, NULL },
249 };
250
251 /* Array of injected predefined strings. */
252 #define PREDEFS_MAX 38
253 static const struct predef predefs[PREDEFS_MAX] = {
254 #include "predefs.in"
255 };
256
257 /* See roffhash_find() */
258 #define ROFF_HASH(p) (p[0] - ASCII_LO)
259
260 static void
261 roffhash_init(void)
262 {
263 struct roffmac *n;
264 int buc, i;
265
266 for (i = 0; i < (int)ROFF_USERDEF; i++) {
267 assert(roffs[i].name[0] >= ASCII_LO);
268 assert(roffs[i].name[0] <= ASCII_HI);
269
270 buc = ROFF_HASH(roffs[i].name);
271
272 if (NULL != (n = hash[buc])) {
273 for ( ; n->next; n = n->next)
274 /* Do nothing. */ ;
275 n->next = &roffs[i];
276 } else
277 hash[buc] = &roffs[i];
278 }
279 }
280
281 /*
282 * Look up a roff token by its name. Returns ROFF_MAX if no macro by
283 * the nil-terminated string name could be found.
284 */
285 static enum rofft
286 roffhash_find(const char *p, size_t s)
287 {
288 int buc;
289 struct roffmac *n;
290
291 /*
292 * libroff has an extremely simple hashtable, for the time
293 * being, which simply keys on the first character, which must
294 * be printable, then walks a chain. It works well enough until
295 * optimised.
296 */
297
298 if (p[0] < ASCII_LO || p[0] > ASCII_HI)
299 return(ROFF_MAX);
300
301 buc = ROFF_HASH(p);
302
303 if (NULL == (n = hash[buc]))
304 return(ROFF_MAX);
305 for ( ; n; n = n->next)
306 if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s])
307 return((enum rofft)(n - roffs));
308
309 return(ROFF_MAX);
310 }
311
312
313 /*
314 * Pop the current node off of the stack of roff instructions currently
315 * pending.
316 */
317 static void
318 roffnode_pop(struct roff *r)
319 {
320 struct roffnode *p;
321
322 assert(r->last);
323 p = r->last;
324
325 r->last = r->last->parent;
326 free(p->name);
327 free(p->end);
328 free(p);
329 }
330
331
332 /*
333 * Push a roff node onto the instruction stack. This must later be
334 * removed with roffnode_pop().
335 */
336 static void
337 roffnode_push(struct roff *r, enum rofft tok, const char *name,
338 int line, int col)
339 {
340 struct roffnode *p;
341
342 p = mandoc_calloc(1, sizeof(struct roffnode));
343 p->tok = tok;
344 if (name)
345 p->name = mandoc_strdup(name);
346 p->parent = r->last;
347 p->line = line;
348 p->col = col;
349 p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
350
351 r->last = p;
352 }
353
354
355 static void
356 roff_free1(struct roff *r)
357 {
358 struct tbl_node *t;
359 struct eqn_node *e;
360 int i;
361
362 while (NULL != (t = r->first_tbl)) {
363 r->first_tbl = t->next;
364 tbl_free(t);
365 }
366
367 r->first_tbl = r->last_tbl = r->tbl = NULL;
368
369 while (NULL != (e = r->first_eqn)) {
370 r->first_eqn = e->next;
371 eqn_free(e);
372 }
373
374 r->first_eqn = r->last_eqn = r->eqn = NULL;
375
376 while (r->last)
377 roffnode_pop(r);
378
379 roff_freestr(r->strtab);
380 roff_freestr(r->xmbtab);
381
382 r->strtab = r->xmbtab = NULL;
383
384 if (r->xtab)
385 for (i = 0; i < 128; i++)
386 free(r->xtab[i].p);
387
388 free(r->xtab);
389 r->xtab = NULL;
390 }
391
392 void
393 roff_reset(struct roff *r)
394 {
395 int i;
396
397 roff_free1(r);
398
399 r->control = 0;
400 memset(&r->regs, 0, sizeof(struct reg) * REG__MAX);
401
402 for (i = 0; i < PREDEFS_MAX; i++)
403 roff_setstr(r, predefs[i].name, predefs[i].str, 0);
404 }
405
406
407 void
408 roff_free(struct roff *r)
409 {
410
411 roff_free1(r);
412 free(r);
413 }
414
415
416 struct roff *
417 roff_alloc(struct mparse *parse)
418 {
419 struct roff *r;
420 int i;
421
422 r = mandoc_calloc(1, sizeof(struct roff));
423 r->parse = parse;
424 r->rstackpos = -1;
425
426 roffhash_init();
427
428 for (i = 0; i < PREDEFS_MAX; i++)
429 roff_setstr(r, predefs[i].name, predefs[i].str, 0);
430
431 return(r);
432 }
433
434 /*
435 * Pre-filter each and every line for reserved words (one beginning with
436 * `\*', e.g., `\*(ab'). These must be handled before the actual line
437 * is processed.
438 * This also checks the syntax of regular escapes.
439 */
440 static enum rofferr
441 roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
442 {
443 enum mandoc_esc esc;
444 const char *stesc; /* start of an escape sequence ('\\') */
445 const char *stnam; /* start of the name, after "[(*" */
446 const char *cp; /* end of the name, e.g. before ']' */
447 const char *res; /* the string to be substituted */
448 int i, maxl, expand_count;
449 size_t nsz;
450 char *n;
451
452 expand_count = 0;
453
454 again:
455 cp = *bufp + pos;
456 while (NULL != (cp = strchr(cp, '\\'))) {
457 stesc = cp++;
458
459 /*
460 * The second character must be an asterisk.
461 * If it isn't, skip it anyway: It is escaped,
462 * so it can't start another escape sequence.
463 */
464
465 if ('\0' == *cp)
466 return(ROFF_CONT);
467
468 if ('*' != *cp) {
469 res = cp;
470 esc = mandoc_escape(&cp, NULL, NULL);
471 if (ESCAPE_ERROR != esc)
472 continue;
473 cp = res;
474 mandoc_msg
475 (MANDOCERR_BADESCAPE, r->parse,
476 ln, (int)(stesc - *bufp), NULL);
477 return(ROFF_CONT);
478 }
479
480 cp++;
481
482 /*
483 * The third character decides the length
484 * of the name of the string.
485 * Save a pointer to the name.
486 */
487
488 switch (*cp) {
489 case ('\0'):
490 return(ROFF_CONT);
491 case ('('):
492 cp++;
493 maxl = 2;
494 break;
495 case ('['):
496 cp++;
497 maxl = 0;
498 break;
499 default:
500 maxl = 1;
501 break;
502 }
503 stnam = cp;
504
505 /* Advance to the end of the name. */
506
507 for (i = 0; 0 == maxl || i < maxl; i++, cp++) {
508 if ('\0' == *cp) {
509 mandoc_msg
510 (MANDOCERR_BADESCAPE,
511 r->parse, ln,
512 (int)(stesc - *bufp), NULL);
513 return(ROFF_CONT);
514 }
515 if (0 == maxl && ']' == *cp)
516 break;
517 }
518
519 /*
520 * Retrieve the replacement string; if it is
521 * undefined, resume searching for escapes.
522 */
523
524 res = roff_getstrn(r, stnam, (size_t)i);
525
526 if (NULL == res) {
527 mandoc_msg
528 (MANDOCERR_BADESCAPE, r->parse,
529 ln, (int)(stesc - *bufp), NULL);
530 res = "";
531 }
532
533 /* Replace the escape sequence by the string. */
534
535 pos = stesc - *bufp;
536
537 nsz = *szp + strlen(res) + 1;
538 n = mandoc_malloc(nsz);
539
540 strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1));
541 strlcat(n, res, nsz);
542 strlcat(n, cp + (maxl ? 0 : 1), nsz);
543
544 free(*bufp);
545
546 *bufp = n;
547 *szp = nsz;
548
549 if (EXPAND_LIMIT >= ++expand_count)
550 goto again;
551
552 /* Just leave the string unexpanded. */
553 mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL);
554 return(ROFF_IGN);
555 }
556 return(ROFF_CONT);
557 }
558
559 /*
560 * Process text streams: convert all breakable hyphens into ASCII_HYPH.
561 */
562 static enum rofferr
563 roff_parsetext(char *p)
564 {
565 size_t sz;
566 const char *start;
567 enum mandoc_esc esc;
568
569 start = p;
570
571 while ('\0' != *p) {
572 sz = strcspn(p, "-\\");
573 p += sz;
574
575 if ('\0' == *p)
576 break;
577
578 if ('\\' == *p) {
579 /* Skip over escapes. */
580 p++;
581 esc = mandoc_escape
582 ((const char **)&p, NULL, NULL);
583 if (ESCAPE_ERROR == esc)
584 break;
585 continue;
586 } else if (p == start) {
587 p++;
588 continue;
589 }
590
591 if (isalpha((unsigned char)p[-1]) &&
592 isalpha((unsigned char)p[1]))
593 *p = ASCII_HYPH;
594 p++;
595 }
596
597 return(ROFF_CONT);
598 }
599
600 enum rofferr
601 roff_parseln(struct roff *r, int ln, char **bufp,
602 size_t *szp, int pos, int *offs)
603 {
604 enum rofft t;
605 enum rofferr e;
606 int ppos, ctl;
607
608 /*
609 * Run the reserved-word filter only if we have some reserved
610 * words to fill in.
611 */
612
613 e = roff_res(r, bufp, szp, ln, pos);
614 if (ROFF_IGN == e)
615 return(e);
616 assert(ROFF_CONT == e);
617
618 ppos = pos;
619 ctl = roff_getcontrol(r, *bufp, &pos);
620
621 /*
622 * First, if a scope is open and we're not a macro, pass the
623 * text through the macro's filter. If a scope isn't open and
624 * we're not a macro, just let it through.
625 * Finally, if there's an equation scope open, divert it into it
626 * no matter our state.
627 */
628
629 if (r->last && ! ctl) {
630 t = r->last->tok;
631 assert(roffs[t].text);
632 e = (*roffs[t].text)
633 (r, t, bufp, szp, ln, pos, pos, offs);
634 assert(ROFF_IGN == e || ROFF_CONT == e);
635 if (ROFF_CONT != e)
636 return(e);
637 if (r->eqn)
638 return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
639 if (r->tbl)
640 return(tbl_read(r->tbl, ln, *bufp, pos));
641 return(roff_parsetext(*bufp + pos));
642 } else if ( ! ctl) {
643 if (r->eqn)
644 return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
645 if (r->tbl)
646 return(tbl_read(r->tbl, ln, *bufp, pos));
647 return(roff_parsetext(*bufp + pos));
648 } else if (r->eqn)
649 return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
650
651 /*
652 * If a scope is open, go to the child handler for that macro,
653 * as it may want to preprocess before doing anything with it.
654 * Don't do so if an equation is open.
655 */
656
657 if (r->last) {
658 t = r->last->tok;
659 assert(roffs[t].sub);
660 return((*roffs[t].sub)
661 (r, t, bufp, szp,
662 ln, ppos, pos, offs));
663 }
664
665 /*
666 * Lastly, as we've no scope open, try to look up and execute
667 * the new macro. If no macro is found, simply return and let
668 * the compilers handle it.
669 */
670
671 if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos)))
672 return(ROFF_CONT);
673
674 assert(roffs[t].proc);
675 return((*roffs[t].proc)
676 (r, t, bufp, szp,
677 ln, ppos, pos, offs));
678 }
679
680
681 void
682 roff_endparse(struct roff *r)
683 {
684
685 if (r->last)
686 mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
687 r->last->line, r->last->col, NULL);
688
689 if (r->eqn) {
690 mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
691 r->eqn->eqn.ln, r->eqn->eqn.pos, NULL);
692 eqn_end(&r->eqn);
693 }
694
695 if (r->tbl) {
696 mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse,
697 r->tbl->line, r->tbl->pos, NULL);
698 tbl_end(&r->tbl);
699 }
700 }
701
702 /*
703 * Parse a roff node's type from the input buffer. This must be in the
704 * form of ".foo xxx" in the usual way.
705 */
706 static enum rofft
707 roff_parse(struct roff *r, const char *buf, int *pos)
708 {
709 const char *mac;
710 size_t maclen;
711 enum rofft t;
712
713 if ('\0' == buf[*pos] || '"' == buf[*pos] ||
714 '\t' == buf[*pos] || ' ' == buf[*pos])
715 return(ROFF_MAX);
716
717 /*
718 * We stop the macro parse at an escape, tab, space, or nil.
719 * However, `\}' is also a valid macro, so make sure we don't
720 * clobber it by seeing the `\' as the end of token.
721 */
722
723 mac = buf + *pos;
724 maclen = strcspn(mac + 1, " \\\t\0") + 1;
725
726 t = (r->current_string = roff_getstrn(r, mac, maclen))
727 ? ROFF_USERDEF : roffhash_find(mac, maclen);
728
729 *pos += (int)maclen;
730
731 while (buf[*pos] && ' ' == buf[*pos])
732 (*pos)++;
733
734 return(t);
735 }
736
737 /* ARGSUSED */
738 static enum rofferr
739 roff_cblock(ROFF_ARGS)
740 {
741
742 /*
743 * A block-close `..' should only be invoked as a child of an
744 * ignore macro, otherwise raise a warning and just ignore it.
745 */
746
747 if (NULL == r->last) {
748 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
749 return(ROFF_IGN);
750 }
751
752 switch (r->last->tok) {
753 case (ROFF_am):
754 /* FALLTHROUGH */
755 case (ROFF_ami):
756 /* FALLTHROUGH */
757 case (ROFF_am1):
758 /* FALLTHROUGH */
759 case (ROFF_de):
760 /* ROFF_de1 is remapped to ROFF_de in roff_block(). */
761 /* FALLTHROUGH */
762 case (ROFF_dei):
763 /* FALLTHROUGH */
764 case (ROFF_ig):
765 break;
766 default:
767 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
768 return(ROFF_IGN);
769 }
770
771 if ((*bufp)[pos])
772 mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
773
774 roffnode_pop(r);
775 roffnode_cleanscope(r);
776 return(ROFF_IGN);
777
778 }
779
780
781 static void
782 roffnode_cleanscope(struct roff *r)
783 {
784
785 while (r->last) {
786 if (--r->last->endspan != 0)
787 break;
788 roffnode_pop(r);
789 }
790 }
791
792
793 /* ARGSUSED */
794 static enum rofferr
795 roff_ccond(ROFF_ARGS)
796 {
797
798 if (NULL == r->last) {
799 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
800 return(ROFF_IGN);
801 }
802
803 switch (r->last->tok) {
804 case (ROFF_el):
805 /* FALLTHROUGH */
806 case (ROFF_ie):
807 /* FALLTHROUGH */
808 case (ROFF_if):
809 break;
810 default:
811 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
812 return(ROFF_IGN);
813 }
814
815 if (r->last->endspan > -1) {
816 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
817 return(ROFF_IGN);
818 }
819
820 if ((*bufp)[pos])
821 mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
822
823 roffnode_pop(r);
824 roffnode_cleanscope(r);
825 return(ROFF_IGN);
826 }
827
828
829 /* ARGSUSED */
830 static enum rofferr
831 roff_block(ROFF_ARGS)
832 {
833 int sv;
834 size_t sz;
835 char *name;
836
837 name = NULL;
838
839 if (ROFF_ig != tok) {
840 if ('\0' == (*bufp)[pos]) {
841 mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
842 return(ROFF_IGN);
843 }
844
845 /*
846 * Re-write `de1', since we don't really care about
847 * groff's strange compatibility mode, into `de'.
848 */
849
850 if (ROFF_de1 == tok)
851 tok = ROFF_de;
852 if (ROFF_de == tok)
853 name = *bufp + pos;
854 else
855 mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos,
856 roffs[tok].name);
857
858 while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos]))
859 pos++;
860
861 while (isspace((unsigned char)(*bufp)[pos]))
862 (*bufp)[pos++] = '\0';
863 }
864
865 roffnode_push(r, tok, name, ln, ppos);
866
867 /*
868 * At the beginning of a `de' macro, clear the existing string
869 * with the same name, if there is one. New content will be
870 * added from roff_block_text() in multiline mode.
871 */
872
873 if (ROFF_de == tok)
874 roff_setstr(r, name, "", 0);
875
876 if ('\0' == (*bufp)[pos])
877 return(ROFF_IGN);
878
879 /* If present, process the custom end-of-line marker. */
880
881 sv = pos;
882 while ((*bufp)[pos] && ! isspace((unsigned char)(*bufp)[pos]))
883 pos++;
884
885 /*
886 * Note: groff does NOT like escape characters in the input.
887 * Instead of detecting this, we're just going to let it fly and
888 * to hell with it.
889 */
890
891 assert(pos > sv);
892 sz = (size_t)(pos - sv);
893
894 if (1 == sz && '.' == (*bufp)[sv])
895 return(ROFF_IGN);
896
897 r->last->end = mandoc_malloc(sz + 1);
898
899 memcpy(r->last->end, *bufp + sv, sz);
900 r->last->end[(int)sz] = '\0';
901
902 if ((*bufp)[pos])
903 mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL);
904
905 return(ROFF_IGN);
906 }
907
908
909 /* ARGSUSED */
910 static enum rofferr
911 roff_block_sub(ROFF_ARGS)
912 {
913 enum rofft t;
914 int i, j;
915
916 /*
917 * First check whether a custom macro exists at this level. If
918 * it does, then check against it. This is some of groff's
919 * stranger behaviours. If we encountered a custom end-scope
920 * tag and that tag also happens to be a "real" macro, then we
921 * need to try interpreting it again as a real macro. If it's
922 * not, then return ignore. Else continue.
923 */
924
925 if (r->last->end) {
926 for (i = pos, j = 0; r->last->end[j]; j++, i++)
927 if ((*bufp)[i] != r->last->end[j])
928 break;
929
930 if ('\0' == r->last->end[j] &&
931 ('\0' == (*bufp)[i] ||
932 ' ' == (*bufp)[i] ||
933 '\t' == (*bufp)[i])) {
934 roffnode_pop(r);
935 roffnode_cleanscope(r);
936
937 while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
938 i++;
939
940 pos = i;
941 if (ROFF_MAX != roff_parse(r, *bufp, &pos))
942 return(ROFF_RERUN);
943 return(ROFF_IGN);
944 }
945 }
946
947 /*
948 * If we have no custom end-query or lookup failed, then try
949 * pulling it out of the hashtable.
950 */
951
952 t = roff_parse(r, *bufp, &pos);
953
954 /*
955 * Macros other than block-end are only significant
956 * in `de' blocks; elsewhere, simply throw them away.
957 */
958 if (ROFF_cblock != t) {
959 if (ROFF_de == tok)
960 roff_setstr(r, r->last->name, *bufp + ppos, 1);
961 return(ROFF_IGN);
962 }
963
964 assert(roffs[t].proc);
965 return((*roffs[t].proc)(r, t, bufp, szp,
966 ln, ppos, pos, offs));
967 }
968
969
970 /* ARGSUSED */
971 static enum rofferr
972 roff_block_text(ROFF_ARGS)
973 {
974
975 if (ROFF_de == tok)
976 roff_setstr(r, r->last->name, *bufp + pos, 1);
977
978 return(ROFF_IGN);
979 }
980
981
982 /* ARGSUSED */
983 static enum rofferr
984 roff_cond_sub(ROFF_ARGS)
985 {
986 enum rofft t;
987 enum roffrule rr;
988 char *ep;
989
990 rr = r->last->rule;
991 roffnode_cleanscope(r);
992
993 /*
994 * If the macro is unknown, first check if it contains a closing
995 * delimiter `\}'. If it does, close out our scope and return
996 * the currently-scoped rule (ignore or continue). Else, drop
997 * into the currently-scoped rule.
998 */
999
1000 if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) {
1001 ep = &(*bufp)[pos];
1002 for ( ; NULL != (ep = strchr(ep, '\\')); ep++) {
1003 ep++;
1004 if ('}' != *ep)
1005 continue;
1006
1007 /*
1008 * Make the \} go away.
1009 * This is a little haphazard, as it's not quite
1010 * clear how nroff does this.
1011 * If we're at the end of line, then just chop
1012 * off the \} and resize the buffer.
1013 * If we aren't, then conver it to spaces.
1014 */
1015
1016 if ('\0' == *(ep + 1)) {
1017 *--ep = '\0';
1018 *szp -= 2;
1019 } else
1020 *(ep - 1) = *ep = ' ';
1021
1022 roff_ccond(r, ROFF_ccond, bufp, szp,
1023 ln, pos, pos + 2, offs);
1024 break;
1025 }
1026 return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1027 }
1028
1029 /*
1030 * A denied conditional must evaluate its children if and only
1031 * if they're either structurally required (such as loops and
1032 * conditionals) or a closing macro.
1033 */
1034
1035 if (ROFFRULE_DENY == rr)
1036 if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
1037 if (ROFF_ccond != t)
1038 return(ROFF_IGN);
1039
1040 assert(roffs[t].proc);
1041 return((*roffs[t].proc)(r, t, bufp, szp,
1042 ln, ppos, pos, offs));
1043 }
1044
1045 /* ARGSUSED */
1046 static enum rofferr
1047 roff_cond_text(ROFF_ARGS)
1048 {
1049 char *ep;
1050 enum roffrule rr;
1051
1052 rr = r->last->rule;
1053 roffnode_cleanscope(r);
1054
1055 ep = &(*bufp)[pos];
1056 for ( ; NULL != (ep = strchr(ep, '\\')); ep++) {
1057 ep++;
1058 if ('}' != *ep)
1059 continue;
1060 *ep = '&';
1061 roff_ccond(r, ROFF_ccond, bufp, szp,
1062 ln, pos, pos + 2, offs);
1063 }
1064 return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1065 }
1066
1067 static enum roffrule
1068 roff_evalcond(const char *v, int *pos)
1069 {
1070
1071 switch (v[*pos]) {
1072 case ('n'):
1073 (*pos)++;
1074 return(ROFFRULE_ALLOW);
1075 case ('e'):
1076 /* FALLTHROUGH */
1077 case ('o'):
1078 /* FALLTHROUGH */
1079 case ('t'):
1080 (*pos)++;
1081 return(ROFFRULE_DENY);
1082 default:
1083 break;
1084 }
1085
1086 while (v[*pos] && ' ' != v[*pos])
1087 (*pos)++;
1088 return(ROFFRULE_DENY);
1089 }
1090
1091 /* ARGSUSED */
1092 static enum rofferr
1093 roff_line_ignore(ROFF_ARGS)
1094 {
1095
1096 if (ROFF_it == tok)
1097 mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos, "it");
1098
1099 return(ROFF_IGN);
1100 }
1101
1102 /* ARGSUSED */
1103 static enum rofferr
1104 roff_cond(ROFF_ARGS)
1105 {
1106
1107 roffnode_push(r, tok, NULL, ln, ppos);
1108
1109 /*
1110 * An `.el' has no conditional body: it will consume the value
1111 * of the current rstack entry set in prior `ie' calls or
1112 * defaults to DENY.
1113 *
1114 * If we're not an `el', however, then evaluate the conditional.
1115 */
1116
1117 r->last->rule = ROFF_el == tok ?
1118 (r->rstackpos < 0 ?
1119 ROFFRULE_DENY : r->rstack[r->rstackpos--]) :
1120 roff_evalcond(*bufp, &pos);
1121
1122 /*
1123 * An if-else will put the NEGATION of the current evaluated
1124 * conditional into the stack of rules.
1125 */
1126
1127 if (ROFF_ie == tok) {
1128 if (r->rstackpos == RSTACK_MAX - 1) {
1129 mandoc_msg(MANDOCERR_MEM,
1130 r->parse, ln, ppos, NULL);
1131 return(ROFF_ERR);
1132 }
1133 r->rstack[++r->rstackpos] =
1134 ROFFRULE_DENY == r->last->rule ?
1135 ROFFRULE_ALLOW : ROFFRULE_DENY;
1136 }
1137
1138 /* If the parent has false as its rule, then so do we. */
1139
1140 if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule)
1141 r->last->rule = ROFFRULE_DENY;
1142
1143 /*
1144 * Determine scope.
1145 * If there is nothing on the line after the conditional,
1146 * not even whitespace, use next-line scope.
1147 */
1148
1149 if ('\0' == (*bufp)[pos]) {
1150 r->last->endspan = 2;
1151 goto out;
1152 }
1153
1154 while (' ' == (*bufp)[pos])
1155 pos++;
1156
1157 /* An opening brace requests multiline scope. */
1158
1159 if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
1160 r->last->endspan = -1;
1161 pos += 2;
1162 goto out;
1163 }
1164
1165 /*
1166 * Anything else following the conditional causes
1167 * single-line scope. Warn if the scope contains
1168 * nothing but trailing whitespace.
1169 */
1170
1171 if ('\0' == (*bufp)[pos])
1172 mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
1173
1174 r->last->endspan = 1;
1175
1176 out:
1177 *offs = pos;
1178 return(ROFF_RERUN);
1179 }
1180
1181
1182 /* ARGSUSED */
1183 static enum rofferr
1184 roff_ds(ROFF_ARGS)
1185 {
1186 char *name, *string;
1187
1188 /*
1189 * A symbol is named by the first word following the macro
1190 * invocation up to a space. Its value is anything after the
1191 * name's trailing whitespace and optional double-quote. Thus,
1192 *
1193 * [.ds foo "bar " ]
1194 *
1195 * will have `bar " ' as its value.
1196 */
1197
1198 string = *bufp + pos;
1199 name = roff_getname(r, &string, ln, pos);
1200 if ('\0' == *name)
1201 return(ROFF_IGN);
1202
1203 /* Read past initial double-quote. */
1204 if ('"' == *string)
1205 string++;
1206
1207 /* The rest is the value. */
1208 roff_setstr(r, name, string, 0);
1209 return(ROFF_IGN);
1210 }
1211
1212 int
1213 roff_regisset(const struct roff *r, enum regs reg)
1214 {
1215
1216 return(r->regs[(int)reg].set);
1217 }
1218
1219 unsigned int
1220 roff_regget(const struct roff *r, enum regs reg)
1221 {
1222
1223 return(r->regs[(int)reg].u);
1224 }
1225
1226 void
1227 roff_regunset(struct roff *r, enum regs reg)
1228 {
1229
1230 r->regs[(int)reg].set = 0;
1231 }
1232
1233 /* ARGSUSED */
1234 static enum rofferr
1235 roff_nr(ROFF_ARGS)
1236 {
1237 const char *key;
1238 char *val;
1239 int iv;
1240
1241 val = *bufp + pos;
1242 key = roff_getname(r, &val, ln, pos);
1243
1244 if (0 == strcmp(key, "nS")) {
1245 r->regs[(int)REG_nS].set = 1;
1246 if ((iv = mandoc_strntoi(val, strlen(val), 10)) >= 0)
1247 r->regs[(int)REG_nS].u = (unsigned)iv;
1248 else
1249 r->regs[(int)REG_nS].u = 0u;
1250 }
1251
1252 return(ROFF_IGN);
1253 }
1254
1255 /* ARGSUSED */
1256 static enum rofferr
1257 roff_rm(ROFF_ARGS)
1258 {
1259 const char *name;
1260 char *cp;
1261
1262 cp = *bufp + pos;
1263 while ('\0' != *cp) {
1264 name = roff_getname(r, &cp, ln, (int)(cp - *bufp));
1265 if ('\0' != *name)
1266 roff_setstr(r, name, NULL, 0);
1267 }
1268 return(ROFF_IGN);
1269 }
1270
1271 /* ARGSUSED */
1272 static enum rofferr
1273 roff_TE(ROFF_ARGS)
1274 {
1275
1276 if (NULL == r->tbl)
1277 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1278 else
1279 tbl_end(&r->tbl);
1280
1281 return(ROFF_IGN);
1282 }
1283
1284 /* ARGSUSED */
1285 static enum rofferr
1286 roff_T_(ROFF_ARGS)
1287 {
1288
1289 if (NULL == r->tbl)
1290 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1291 else
1292 tbl_restart(ppos, ln, r->tbl);
1293
1294 return(ROFF_IGN);
1295 }
1296
1297 #if 0
1298 static int
1299 roff_closeeqn(struct roff *r)
1300 {
1301
1302 return(r->eqn && ROFF_EQN == eqn_end(&r->eqn) ? 1 : 0);
1303 }
1304 #endif
1305
1306 static void
1307 roff_openeqn(struct roff *r, const char *name, int line,
1308 int offs, const char *buf)
1309 {
1310 struct eqn_node *e;
1311 int poff;
1312
1313 assert(NULL == r->eqn);
1314 e = eqn_alloc(name, offs, line, r->parse);
1315
1316 if (r->last_eqn)
1317 r->last_eqn->next = e;
1318 else
1319 r->first_eqn = r->last_eqn = e;
1320
1321 r->eqn = r->last_eqn = e;
1322
1323 if (buf) {
1324 poff = 0;
1325 eqn_read(&r->eqn, line, buf, offs, &poff);
1326 }
1327 }
1328
1329 /* ARGSUSED */
1330 static enum rofferr
1331 roff_EQ(ROFF_ARGS)
1332 {
1333
1334 roff_openeqn(r, *bufp + pos, ln, ppos, NULL);
1335 return(ROFF_IGN);
1336 }
1337
1338 /* ARGSUSED */
1339 static enum rofferr
1340 roff_EN(ROFF_ARGS)
1341 {
1342
1343 mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1344 return(ROFF_IGN);
1345 }
1346
1347 /* ARGSUSED */
1348 static enum rofferr
1349 roff_TS(ROFF_ARGS)
1350 {
1351 struct tbl_node *t;
1352
1353 if (r->tbl) {
1354 mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL);
1355 tbl_end(&r->tbl);
1356 }
1357
1358 t = tbl_alloc(ppos, ln, r->parse);
1359
1360 if (r->last_tbl)
1361 r->last_tbl->next = t;
1362 else
1363 r->first_tbl = r->last_tbl = t;
1364
1365 r->tbl = r->last_tbl = t;
1366 return(ROFF_IGN);
1367 }
1368
1369 /* ARGSUSED */
1370 static enum rofferr
1371 roff_cc(ROFF_ARGS)
1372 {
1373 const char *p;
1374
1375 p = *bufp + pos;
1376
1377 if ('\0' == *p || '.' == (r->control = *p++))
1378 r->control = 0;
1379
1380 if ('\0' != *p)
1381 mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
1382
1383 return(ROFF_IGN);
1384 }
1385
1386 /* ARGSUSED */
1387 static enum rofferr
1388 roff_tr(ROFF_ARGS)
1389 {
1390 const char *p, *first, *second;
1391 size_t fsz, ssz;
1392 enum mandoc_esc esc;
1393
1394 p = *bufp + pos;
1395
1396 if ('\0' == *p) {
1397 mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
1398 return(ROFF_IGN);
1399 }
1400
1401 while ('\0' != *p) {
1402 fsz = ssz = 1;
1403
1404 first = p++;
1405 if ('\\' == *first) {
1406 esc = mandoc_escape(&p, NULL, NULL);
1407 if (ESCAPE_ERROR == esc) {
1408 mandoc_msg
1409 (MANDOCERR_BADESCAPE, r->parse,
1410 ln, (int)(p - *bufp), NULL);
1411 return(ROFF_IGN);
1412 }
1413 fsz = (size_t)(p - first);
1414 }
1415
1416 second = p++;
1417 if ('\\' == *second) {
1418 esc = mandoc_escape(&p, NULL, NULL);
1419 if (ESCAPE_ERROR == esc) {
1420 mandoc_msg
1421 (MANDOCERR_BADESCAPE, r->parse,
1422 ln, (int)(p - *bufp), NULL);
1423 return(ROFF_IGN);
1424 }
1425 ssz = (size_t)(p - second);
1426 } else if ('\0' == *second) {
1427 mandoc_msg(MANDOCERR_ARGCOUNT, r->parse,
1428 ln, (int)(p - *bufp), NULL);
1429 second = " ";
1430 p--;
1431 }
1432
1433 if (fsz > 1) {
1434 roff_setstrn(&r->xmbtab, first,
1435 fsz, second, ssz, 0);
1436 continue;
1437 }
1438
1439 if (NULL == r->xtab)
1440 r->xtab = mandoc_calloc
1441 (128, sizeof(struct roffstr));
1442
1443 free(r->xtab[(int)*first].p);
1444 r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
1445 r->xtab[(int)*first].sz = ssz;
1446 }
1447
1448 return(ROFF_IGN);
1449 }
1450
1451 /* ARGSUSED */
1452 static enum rofferr
1453 roff_so(ROFF_ARGS)
1454 {
1455 char *name;
1456
1457 mandoc_msg(MANDOCERR_SO, r->parse, ln, ppos, NULL);
1458
1459 /*
1460 * Handle `so'. Be EXTREMELY careful, as we shouldn't be
1461 * opening anything that's not in our cwd or anything beneath
1462 * it. Thus, explicitly disallow traversing up the file-system
1463 * or using absolute paths.
1464 */
1465
1466 name = *bufp + pos;
1467 if ('/' == *name || strstr(name, "../") || strstr(name, "/..")) {
1468 mandoc_msg(MANDOCERR_SOPATH, r->parse, ln, pos, NULL);
1469 return(ROFF_ERR);
1470 }
1471
1472 *offs = pos;
1473 return(ROFF_SO);
1474 }
1475
1476 /* ARGSUSED */
1477 static enum rofferr
1478 roff_userdef(ROFF_ARGS)
1479 {
1480 const char *arg[9];
1481 char *cp, *n1, *n2;
1482 int i;
1483
1484 /*
1485 * Collect pointers to macro argument strings
1486 * and null-terminate them.
1487 */
1488 cp = *bufp + pos;
1489 for (i = 0; i < 9; i++)
1490 arg[i] = '\0' == *cp ? "" :
1491 mandoc_getarg(r->parse, &cp, ln, &pos);
1492
1493 /*
1494 * Expand macro arguments.
1495 */
1496 *szp = 0;
1497 n1 = cp = mandoc_strdup(r->current_string);
1498 while (NULL != (cp = strstr(cp, "\\$"))) {
1499 i = cp[2] - '1';
1500 if (0 > i || 8 < i) {
1501 /* Not an argument invocation. */
1502 cp += 2;
1503 continue;
1504 }
1505
1506 *szp = strlen(n1) - 3 + strlen(arg[i]) + 1;
1507 n2 = mandoc_malloc(*szp);
1508
1509 strlcpy(n2, n1, (size_t)(cp - n1 + 1));
1510 strlcat(n2, arg[i], *szp);
1511 strlcat(n2, cp + 3, *szp);
1512
1513 cp = n2 + (cp - n1);
1514 free(n1);
1515 n1 = n2;
1516 }
1517
1518 /*
1519 * Replace the macro invocation
1520 * by the expanded macro.
1521 */
1522 free(*bufp);
1523 *bufp = n1;
1524 if (0 == *szp)
1525 *szp = strlen(*bufp) + 1;
1526
1527 return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ?
1528 ROFF_REPARSE : ROFF_APPEND);
1529 }
1530
1531 static char *
1532 roff_getname(struct roff *r, char **cpp, int ln, int pos)
1533 {
1534 char *name, *cp;
1535
1536 name = *cpp;
1537 if ('\0' == *name)
1538 return(name);
1539
1540 /* Read until end of name. */
1541 for (cp = name; '\0' != *cp && ' ' != *cp; cp++) {
1542 if ('\\' != *cp)
1543 continue;
1544 cp++;
1545 if ('\\' == *cp)
1546 continue;
1547 mandoc_msg(MANDOCERR_NAMESC, r->parse, ln, pos, NULL);
1548 *cp = '\0';
1549 name = cp;
1550 }
1551
1552 /* Nil-terminate name. */
1553 if ('\0' != *cp)
1554 *(cp++) = '\0';
1555
1556 /* Read past spaces. */
1557 while (' ' == *cp)
1558 cp++;
1559
1560 *cpp = cp;
1561 return(name);
1562 }
1563
1564 /*
1565 * Store *string into the user-defined string called *name.
1566 * In multiline mode, append to an existing entry and append '\n';
1567 * else replace the existing entry, if there is one.
1568 * To clear an existing entry, call with (*r, *name, NULL, 0).
1569 */
1570 static void
1571 roff_setstr(struct roff *r, const char *name, const char *string,
1572 int multiline)
1573 {
1574
1575 roff_setstrn(&r->strtab, name, strlen(name), string,
1576 string ? strlen(string) : 0, multiline);
1577 }
1578
1579 static void
1580 roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
1581 const char *string, size_t stringsz, int multiline)
1582 {
1583 struct roffkv *n;
1584 char *c;
1585 int i;
1586 size_t oldch, newch;
1587
1588 /* Search for an existing string with the same name. */
1589 n = *r;
1590
1591 while (n && strcmp(name, n->key.p))
1592 n = n->next;
1593
1594 if (NULL == n) {
1595 /* Create a new string table entry. */
1596 n = mandoc_malloc(sizeof(struct roffkv));
1597 n->key.p = mandoc_strndup(name, namesz);
1598 n->key.sz = namesz;
1599 n->val.p = NULL;
1600 n->val.sz = 0;
1601 n->next = *r;
1602 *r = n;
1603 } else if (0 == multiline) {
1604 /* In multiline mode, append; else replace. */
1605 free(n->val.p);
1606 n->val.p = NULL;
1607 n->val.sz = 0;
1608 }
1609
1610 if (NULL == string)
1611 return;
1612
1613 /*
1614 * One additional byte for the '\n' in multiline mode,
1615 * and one for the terminating '\0'.
1616 */
1617 newch = stringsz + (multiline ? 2u : 1u);
1618
1619 if (NULL == n->val.p) {
1620 n->val.p = mandoc_malloc(newch);
1621 *n->val.p = '\0';
1622 oldch = 0;
1623 } else {
1624 oldch = n->val.sz;
1625 n->val.p = mandoc_realloc(n->val.p, oldch + newch);
1626 }
1627
1628 /* Skip existing content in the destination buffer. */
1629 c = n->val.p + (int)oldch;
1630
1631 /* Append new content to the destination buffer. */
1632 i = 0;
1633 while (i < (int)stringsz) {
1634 /*
1635 * Rudimentary roff copy mode:
1636 * Handle escaped backslashes.
1637 */
1638 if ('\\' == string[i] && '\\' == string[i + 1])
1639 i++;
1640 *c++ = string[i++];
1641 }
1642
1643 /* Append terminating bytes. */
1644 if (multiline)
1645 *c++ = '\n';
1646
1647 *c = '\0';
1648 n->val.sz = (int)(c - n->val.p);
1649 }
1650
1651 static const char *
1652 roff_getstrn(const struct roff *r, const char *name, size_t len)
1653 {
1654 const struct roffkv *n;
1655
1656 for (n = r->strtab; n; n = n->next)
1657 if (0 == strncmp(name, n->key.p, len) &&
1658 '\0' == n->key.p[(int)len])
1659 return(n->val.p);
1660
1661 return(NULL);
1662 }
1663
1664 static void
1665 roff_freestr(struct roffkv *r)
1666 {
1667 struct roffkv *n, *nn;
1668
1669 for (n = r; n; n = nn) {
1670 free(n->key.p);
1671 free(n->val.p);
1672 nn = n->next;
1673 free(n);
1674 }
1675 }
1676
1677 const struct tbl_span *
1678 roff_span(const struct roff *r)
1679 {
1680
1681 return(r->tbl ? tbl_span(r->tbl) : NULL);
1682 }
1683
1684 const struct eqn *
1685 roff_eqn(const struct roff *r)
1686 {
1687
1688 return(r->last_eqn ? &r->last_eqn->eqn : NULL);
1689 }
1690
1691 /*
1692 * Duplicate an input string, making the appropriate character
1693 * conversations (as stipulated by `tr') along the way.
1694 * Returns a heap-allocated string with all the replacements made.
1695 */
1696 char *
1697 roff_strdup(const struct roff *r, const char *p)
1698 {
1699 const struct roffkv *cp;
1700 char *res;
1701 const char *pp;
1702 size_t ssz, sz;
1703 enum mandoc_esc esc;
1704
1705 if (NULL == r->xmbtab && NULL == r->xtab)
1706 return(mandoc_strdup(p));
1707 else if ('\0' == *p)
1708 return(mandoc_strdup(""));
1709
1710 /*
1711 * Step through each character looking for term matches
1712 * (remember that a `tr' can be invoked with an escape, which is
1713 * a glyph but the escape is multi-character).
1714 * We only do this if the character hash has been initialised
1715 * and the string is >0 length.
1716 */
1717
1718 res = NULL;
1719 ssz = 0;
1720
1721 while ('\0' != *p) {
1722 if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) {
1723 sz = r->xtab[(int)*p].sz;
1724 res = mandoc_realloc(res, ssz + sz + 1);
1725 memcpy(res + ssz, r->xtab[(int)*p].p, sz);
1726 ssz += sz;
1727 p++;
1728 continue;
1729 } else if ('\\' != *p) {
1730 res = mandoc_realloc(res, ssz + 2);
1731 res[ssz++] = *p++;
1732 continue;
1733 }
1734
1735 /* Search for term matches. */
1736 for (cp = r->xmbtab; cp; cp = cp->next)
1737 if (0 == strncmp(p, cp->key.p, cp->key.sz))
1738 break;
1739
1740 if (NULL != cp) {
1741 /*
1742 * A match has been found.
1743 * Append the match to the array and move
1744 * forward by its keysize.
1745 */
1746 res = mandoc_realloc
1747 (res, ssz + cp->val.sz + 1);
1748 memcpy(res + ssz, cp->val.p, cp->val.sz);
1749 ssz += cp->val.sz;
1750 p += (int)cp->key.sz;
1751 continue;
1752 }
1753
1754 /*
1755 * Handle escapes carefully: we need to copy
1756 * over just the escape itself, or else we might
1757 * do replacements within the escape itself.
1758 * Make sure to pass along the bogus string.
1759 */
1760 pp = p++;
1761 esc = mandoc_escape(&p, NULL, NULL);
1762 if (ESCAPE_ERROR == esc) {
1763 sz = strlen(pp);
1764 res = mandoc_realloc(res, ssz + sz + 1);
1765 memcpy(res + ssz, pp, sz);
1766 break;
1767 }
1768 /*
1769 * We bail out on bad escapes.
1770 * No need to warn: we already did so when
1771 * roff_res() was called.
1772 */
1773 sz = (int)(p - pp);
1774 res = mandoc_realloc(res, ssz + sz + 1);
1775 memcpy(res + ssz, pp, sz);
1776 ssz += sz;
1777 }
1778
1779 res[(int)ssz] = '\0';
1780 return(res);
1781 }
1782
1783 /*
1784 * Find out whether a line is a macro line or not.
1785 * If it is, adjust the current position and return one; if it isn't,
1786 * return zero and don't change the current position.
1787 * If the control character has been set with `.cc', then let that grain
1788 * precedence.
1789 * This is slighly contrary to groff, where using the non-breaking
1790 * control character when `cc' has been invoked will cause the
1791 * non-breaking macro contents to be printed verbatim.
1792 */
1793 int
1794 roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
1795 {
1796 int pos;
1797
1798 pos = *ppos;
1799
1800 if (0 != r->control && cp[pos] == r->control)
1801 pos++;
1802 else if (0 != r->control)
1803 return(0);
1804 else if ('\\' == cp[pos] && '.' == cp[pos + 1])
1805 pos += 2;
1806 else if ('.' == cp[pos] || '\'' == cp[pos])
1807 pos++;
1808 else
1809 return(0);
1810
1811 while (' ' == cp[pos] || '\t' == cp[pos])
1812 pos++;
1813
1814 *ppos = pos;
1815 return(1);
1816 }