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