Annotation of mandoc/roff.c, Revision 1.76
1.76 ! kristaps 1: /* $Id: roff.c,v 1.75 2010/05/16 13:49:23 kristaps Exp $ */
1.1 kristaps 2: /*
1.67 kristaps 3: * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
1.66 kristaps 6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 8: *
1.66 kristaps 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 16: */
1.66 kristaps 17: #ifdef HAVE_CONFIG_H
18: #include "config.h"
19: #endif
1.30 kristaps 20:
1.67 kristaps 21: #include <assert.h>
1.1 kristaps 22: #include <stdlib.h>
1.67 kristaps 23: #include <string.h>
1.75 kristaps 24: #include <stdio.h>
1.1 kristaps 25:
1.67 kristaps 26: #include "mandoc.h"
1.43 kristaps 27: #include "roff.h"
1.33 kristaps 28:
1.75 kristaps 29: #define ROFF_CTL(c) \
30: ('.' == (c) || '\'' == (c))
1.76 ! kristaps 31: #if 0
1.75 kristaps 32: #define ROFF_MDEBUG(p, str) \
33: fprintf(stderr, "%s: %s (%d:%d)\n", (str), \
34: roffs[(p)->last->tok].name, \
35: (p)->last->line, (p)->last->col)
36: #else
37: #define ROFF_MDEBUG(p, str) while (/* CONSTCOND */ 0)
38: #endif
39:
1.67 kristaps 40: enum rofft {
1.75 kristaps 41: ROFF_if,
1.76 ! kristaps 42: ROFF_ig,
! 43: ROFF_cblock,
1.75 kristaps 44: ROFF_ccond,
1.74 kristaps 45: #if 0
46: ROFF_am,
47: ROFF_ami,
1.67 kristaps 48: ROFF_de,
49: ROFF_dei,
50: ROFF_close,
1.74 kristaps 51: #endif
1.67 kristaps 52: ROFF_MAX
53: };
54:
55: struct roff {
56: struct roffnode *last; /* leaf of stack */
57: mandocmsg msg; /* err/warn/fatal messages */
58: void *data; /* privdata for messages */
59: };
60:
61: struct roffnode {
62: enum rofft tok; /* type of node */
63: struct roffnode *parent; /* up one in stack */
1.74 kristaps 64: char *end; /* end-token: custom */
1.67 kristaps 65: int line; /* parse line */
66: int col; /* parse col */
1.75 kristaps 67: int endspan;
1.67 kristaps 68: };
69:
70: #define ROFF_ARGS struct roff *r, /* parse ctx */ \
1.72 kristaps 71: enum rofft tok, /* tok of macro */ \
1.67 kristaps 72: char **bufp, /* input buffer */ \
73: size_t *szp, /* size of input buffer */ \
74: int ln, /* parse line */ \
1.75 kristaps 75: int ppos, /* original pos in buffer */ \
76: int pos, /* current pos in buffer */ \
1.74 kristaps 77: int *offs /* reset offset of buffer data */
1.67 kristaps 78:
79: typedef enum rofferr (*roffproc)(ROFF_ARGS);
80:
81: struct roffmac {
82: const char *name; /* macro name */
1.75 kristaps 83: roffproc proc;
1.67 kristaps 84: };
85:
1.75 kristaps 86: static enum rofferr roff_if(ROFF_ARGS);
1.76 ! kristaps 87: static enum rofferr roff_ig(ROFF_ARGS);
! 88: static enum rofferr roff_cblock(ROFF_ARGS);
1.75 kristaps 89: static enum rofferr roff_ccond(ROFF_ARGS);
1.67 kristaps 90:
91: const struct roffmac roffs[ROFF_MAX] = {
1.75 kristaps 92: { "if", roff_if },
1.76 ! kristaps 93: { "ig", roff_ig },
! 94: { ".", roff_cblock },
1.75 kristaps 95: { "\\}", roff_ccond },
1.74 kristaps 96: #if 0
97: { "am", roff_sub_ig, roff_new_ig },
98: { "ami", roff_sub_ig, roff_new_ig },
1.72 kristaps 99: { "de", roff_sub_ig, roff_new_ig },
100: { "dei", roff_sub_ig, roff_new_ig },
1.74 kristaps 101: #endif
1.67 kristaps 102: };
103:
104: static void roff_free1(struct roff *);
105: static enum rofft roff_hash_find(const char *);
1.76 ! kristaps 106: static void roffnode_cleanscope(struct roff *);
1.67 kristaps 107: static int roffnode_push(struct roff *,
108: enum rofft, int, int);
109: static void roffnode_pop(struct roff *);
110: static enum rofft roff_parse(const char *, int *);
111:
112:
113: /*
114: * Look up a roff token by its name. Returns ROFF_MAX if no macro by
115: * the nil-terminated string name could be found.
116: */
117: static enum rofft
118: roff_hash_find(const char *p)
119: {
120: int i;
121:
122: /* FIXME: make this be fast and efficient. */
123:
124: for (i = 0; i < (int)ROFF_MAX; i++)
125: if (0 == strcmp(roffs[i].name, p))
126: return((enum rofft)i);
127:
128: return(ROFF_MAX);
129: }
130:
131:
132: /*
133: * Pop the current node off of the stack of roff instructions currently
134: * pending.
135: */
136: static void
137: roffnode_pop(struct roff *r)
138: {
139: struct roffnode *p;
140:
1.75 kristaps 141: assert(r->last);
142: p = r->last;
143: r->last = r->last->parent;
1.74 kristaps 144: if (p->end)
145: free(p->end);
1.67 kristaps 146: free(p);
147: }
148:
149:
150: /*
151: * Push a roff node onto the instruction stack. This must later be
152: * removed with roffnode_pop().
153: */
154: static int
155: roffnode_push(struct roff *r, enum rofft tok, int line, int col)
156: {
157: struct roffnode *p;
158:
159: if (NULL == (p = calloc(1, sizeof(struct roffnode)))) {
160: (*r->msg)(MANDOCERR_MEM, r->data, line, col, NULL);
161: return(0);
162: }
163:
164: p->tok = tok;
165: p->parent = r->last;
166: p->line = line;
167: p->col = col;
168:
169: r->last = p;
170: return(1);
171: }
172:
173:
174: static void
175: roff_free1(struct roff *r)
176: {
177:
178: while (r->last)
179: roffnode_pop(r);
180: }
181:
182:
183: void
184: roff_reset(struct roff *r)
185: {
186:
187: roff_free1(r);
188: }
189:
190:
191: void
192: roff_free(struct roff *r)
193: {
194:
195: roff_free1(r);
196: free(r);
197: }
198:
199:
200: struct roff *
201: roff_alloc(const mandocmsg msg, void *data)
202: {
203: struct roff *r;
204:
205: if (NULL == (r = calloc(1, sizeof(struct roff)))) {
206: (*msg)(MANDOCERR_MEM, data, 0, 0, NULL);
207: return(0);
208: }
209:
210: r->msg = msg;
211: r->data = data;
212: return(r);
213: }
214:
215:
216: enum rofferr
1.74 kristaps 217: roff_parseln(struct roff *r, int ln,
218: char **bufp, size_t *szp, int pos, int *offs)
1.67 kristaps 219: {
220: enum rofft t;
1.75 kristaps 221: int ppos;
1.74 kristaps 222:
1.75 kristaps 223: if (r->last && ! ROFF_CTL((*bufp)[pos])) {
1.76 ! kristaps 224: if (ROFF_ig == r->last->tok)
! 225: return(ROFF_IGN);
! 226: roffnode_cleanscope(r);
! 227: /* FIXME: this assumes we're discarding! */
1.75 kristaps 228: return(ROFF_IGN);
229: } else if ( ! ROFF_CTL((*bufp)[pos]))
1.67 kristaps 230: return(ROFF_CONT);
231:
232: /* There's nothing on the stack: make us anew. */
233:
1.75 kristaps 234: ppos = pos;
1.76 ! kristaps 235: if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) {
! 236: if (r->last && ROFF_ig == r->last->tok)
! 237: return(ROFF_IGN);
1.67 kristaps 238: return(ROFF_CONT);
1.76 ! kristaps 239: }
1.67 kristaps 240:
1.75 kristaps 241: assert(roffs[t].proc);
242: return((*roffs[t].proc)(r, t, bufp, szp, ln, ppos, pos, offs));
1.74 kristaps 243: }
244:
245:
246: int
247: roff_endparse(struct roff *r)
248: {
249:
250: if (NULL == r->last)
251: return(1);
252: return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data, r->last->line,
253: r->last->col, NULL));
1.67 kristaps 254: }
255:
256:
257: /*
258: * Parse a roff node's type from the input buffer. This must be in the
259: * form of ".foo xxx" in the usual way.
260: */
261: static enum rofft
262: roff_parse(const char *buf, int *pos)
263: {
264: int j;
265: char mac[5];
266: enum rofft t;
267:
1.75 kristaps 268: assert(ROFF_CTL(buf[*pos]));
269: (*pos)++;
1.67 kristaps 270:
271: while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
272: (*pos)++;
273:
274: if ('\0' == buf[*pos])
275: return(ROFF_MAX);
276:
277: for (j = 0; j < 4; j++, (*pos)++)
278: if ('\0' == (mac[j] = buf[*pos]))
279: break;
280: else if (' ' == buf[*pos])
281: break;
282:
283: if (j == 4 || j < 1)
284: return(ROFF_MAX);
285:
286: mac[j] = '\0';
287:
288: if (ROFF_MAX == (t = roff_hash_find(mac)))
289: return(t);
290:
291: while (buf[*pos] && ' ' == buf[*pos])
292: (*pos)++;
293:
294: return(t);
295: }
296:
297:
298: /* ARGSUSED */
299: static enum rofferr
1.76 ! kristaps 300: roff_cblock(ROFF_ARGS)
1.67 kristaps 301: {
302:
1.76 ! kristaps 303: if (NULL == r->last) {
! 304: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
! 305: return(ROFF_ERR);
! 306: return(ROFF_IGN);
! 307: }
1.67 kristaps 308:
1.76 ! kristaps 309: if (ROFF_ig != r->last->tok) {
! 310: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
! 311: return(ROFF_ERR);
1.67 kristaps 312: return(ROFF_IGN);
1.76 ! kristaps 313: }
1.67 kristaps 314:
1.76 ! kristaps 315: if ((*bufp)[pos])
! 316: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
! 317: return(ROFF_ERR);
1.71 kristaps 318:
1.76 ! kristaps 319: ROFF_MDEBUG(r, "closing ignore block");
1.71 kristaps 320: roffnode_pop(r);
1.76 ! kristaps 321: roffnode_cleanscope(r);
! 322: return(ROFF_IGN);
1.71 kristaps 323:
1.67 kristaps 324: }
325:
326:
1.76 ! kristaps 327: static void
! 328: roffnode_cleanscope(struct roff *r)
1.67 kristaps 329: {
330:
1.76 ! kristaps 331: while (r->last) {
! 332: if (--r->last->endspan < 0)
! 333: break;
! 334: ROFF_MDEBUG(r, "closing implicit scope");
! 335: roffnode_pop(r);
! 336: }
1.67 kristaps 337: }
338:
339:
1.75 kristaps 340: /* ARGSUSED */
1.74 kristaps 341: static enum rofferr
1.75 kristaps 342: roff_ccond(ROFF_ARGS)
1.74 kristaps 343: {
344:
1.76 ! kristaps 345: if (NULL == r->last) {
! 346: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
! 347: return(ROFF_ERR);
! 348: return(ROFF_IGN);
! 349: }
! 350:
! 351: if (ROFF_if != r->last->tok) {
1.75 kristaps 352: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
353: return(ROFF_ERR);
354: return(ROFF_IGN);
355: }
356:
1.76 ! kristaps 357: if (r->last->endspan > -1) {
! 358: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
! 359: return(ROFF_ERR);
! 360: return(ROFF_IGN);
! 361: }
! 362:
! 363: if ((*bufp)[pos])
! 364: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
! 365: return(ROFF_ERR);
! 366:
1.75 kristaps 367: ROFF_MDEBUG(r, "closing explicit scope");
368: roffnode_pop(r);
1.76 ! kristaps 369: roffnode_cleanscope(r);
! 370: return(ROFF_IGN);
! 371: }
! 372:
1.75 kristaps 373:
1.76 ! kristaps 374: /* ARGSUSED */
! 375: static enum rofferr
! 376: roff_ig(ROFF_ARGS)
! 377: {
! 378:
! 379: if ( ! roffnode_push(r, tok, ln, ppos))
! 380: return(ROFF_ERR);
! 381:
! 382: ROFF_MDEBUG(r, "opening ignore block");
1.74 kristaps 383:
1.76 ! kristaps 384: /* FIXME: warn about end of line. */
1.74 kristaps 385:
386: return(ROFF_IGN);
387: }
388:
389:
1.75 kristaps 390: /* ARGSUSED */
1.74 kristaps 391: static enum rofferr
1.75 kristaps 392: roff_if(ROFF_ARGS)
1.74 kristaps 393: {
394:
395: /*
396: * Read ahead past the conditional.
397: * FIXME: this does not work, as conditionals don't end on
398: * whitespace, but are parsed according to a formal grammar.
399: * It's good enough for now, however.
400: */
401:
402: if ( ! roffnode_push(r, tok, ln, ppos))
403: return(ROFF_ERR);
404:
1.75 kristaps 405: while ((*bufp)[pos] && ' ' != (*bufp)[pos])
406: pos++;
407: while (' ' == (*bufp)[pos])
408: pos++;
1.74 kristaps 409:
410: /* Don't evaluate: just assume NO. */
411:
1.75 kristaps 412: r->last->endspan = 1;
413:
414: if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
415: ROFF_MDEBUG(r, "opening explicit scope");
416: r->last->endspan = -1;
417: pos += 2;
418: } else
419: ROFF_MDEBUG(r, "opening implicit scope");
1.74 kristaps 420:
1.75 kristaps 421: if ('\0' == (*bufp)[pos])
422: return(ROFF_IGN);
1.74 kristaps 423:
1.75 kristaps 424: *offs = pos;
425: return(ROFF_RERUN);
1.74 kristaps 426: }
CVSweb