Annotation of mandoc/roff.c, Revision 1.77
1.77 ! kristaps 1: /* $Id: roff.c,v 1.76 2010/05/16 14:47:19 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.77 ! kristaps 384: if ((*bufp)[pos])
! 385: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
! 386: return(ROFF_ERR);
1.74 kristaps 387:
388: return(ROFF_IGN);
389: }
390:
391:
1.75 kristaps 392: /* ARGSUSED */
1.74 kristaps 393: static enum rofferr
1.75 kristaps 394: roff_if(ROFF_ARGS)
1.74 kristaps 395: {
1.77 ! kristaps 396: int sv;
1.74 kristaps 397:
398: /*
399: * Read ahead past the conditional.
400: * FIXME: this does not work, as conditionals don't end on
401: * whitespace, but are parsed according to a formal grammar.
402: * It's good enough for now, however.
403: */
404:
1.75 kristaps 405: while ((*bufp)[pos] && ' ' != (*bufp)[pos])
406: pos++;
1.77 ! kristaps 407:
! 408: sv = pos;
1.75 kristaps 409: while (' ' == (*bufp)[pos])
410: pos++;
1.74 kristaps 411:
1.77 ! kristaps 412: /*
! 413: * Roff is weird. If we have just white-space after the
! 414: * conditional, it's considered the BODY and we exit without
! 415: * really doing anything. Warn about this. It's probably
! 416: * wrong.
! 417: */
! 418:
! 419: if ('\0' == (*bufp)[pos] && sv != pos) {
! 420: if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
! 421: return(ROFF_ERR);
! 422: return(ROFF_IGN);
! 423: }
! 424:
! 425: if ( ! roffnode_push(r, tok, ln, ppos))
! 426: return(ROFF_ERR);
! 427:
1.74 kristaps 428: /* Don't evaluate: just assume NO. */
429:
1.75 kristaps 430: r->last->endspan = 1;
431:
432: if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
433: ROFF_MDEBUG(r, "opening explicit scope");
434: r->last->endspan = -1;
435: pos += 2;
436: } else
437: ROFF_MDEBUG(r, "opening implicit scope");
1.74 kristaps 438:
1.77 ! kristaps 439: /*
! 440: * If there are no arguments on the line, the next-line scope is
! 441: * assumed.
! 442: */
! 443:
1.75 kristaps 444: if ('\0' == (*bufp)[pos])
445: return(ROFF_IGN);
1.77 ! kristaps 446:
! 447: /* Otherwise re-run the roff parser after recalculating. */
1.74 kristaps 448:
1.75 kristaps 449: *offs = pos;
450: return(ROFF_RERUN);
1.74 kristaps 451: }
CVSweb