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