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