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