Annotation of mandoc/roff.c, Revision 1.75
1.75 ! kristaps 1: /* $Id: roff.c,v 1.74 2010/05/16 10:59:36 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))
! 31: #ifdef ROFF_DEBUG
! 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,
! 42: ROFF_ccond,
1.74 kristaps 43: #if 0
44: ROFF_am,
45: ROFF_ami,
1.67 kristaps 46: ROFF_de,
47: ROFF_dei,
48: ROFF_ig,
49: ROFF_close,
1.74 kristaps 50: #endif
1.67 kristaps 51: ROFF_MAX
52: };
53:
54: struct roff {
55: struct roffnode *last; /* leaf of stack */
56: mandocmsg msg; /* err/warn/fatal messages */
57: void *data; /* privdata for messages */
58: };
59:
60: struct roffnode {
61: enum rofft tok; /* type of node */
62: struct roffnode *parent; /* up one in stack */
1.74 kristaps 63: char *end; /* end-token: custom */
1.67 kristaps 64: int line; /* parse line */
65: int col; /* parse col */
1.75 ! kristaps 66: int endspan;
1.67 kristaps 67: };
68:
69: #define ROFF_ARGS struct roff *r, /* parse ctx */ \
1.72 kristaps 70: enum rofft tok, /* tok of macro */ \
1.67 kristaps 71: char **bufp, /* input buffer */ \
72: size_t *szp, /* size of input buffer */ \
73: int ln, /* parse line */ \
1.75 ! kristaps 74: int ppos, /* original pos in buffer */ \
! 75: int pos, /* current pos in buffer */ \
1.74 kristaps 76: int *offs /* reset offset of buffer data */
1.67 kristaps 77:
78: typedef enum rofferr (*roffproc)(ROFF_ARGS);
79:
80: struct roffmac {
81: const char *name; /* macro name */
1.75 ! kristaps 82: roffproc proc;
1.67 kristaps 83: };
84:
1.75 ! kristaps 85: static enum rofferr roff_if(ROFF_ARGS);
! 86: static enum rofferr roff_ccond(ROFF_ARGS);
1.74 kristaps 87: #if 0
1.67 kristaps 88: static enum rofferr roff_new_close(ROFF_ARGS);
89: static enum rofferr roff_new_ig(ROFF_ARGS);
90: static enum rofferr roff_sub_ig(ROFF_ARGS);
1.74 kristaps 91: #endif
1.67 kristaps 92:
93: const struct roffmac roffs[ROFF_MAX] = {
1.75 ! kristaps 94: { "if", roff_if },
! 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.67 kristaps 101: { "ig", roff_sub_ig, roff_new_ig },
102: { ".", NULL, roff_new_close },
1.74 kristaps 103: #endif
1.67 kristaps 104: };
105:
106: static void roff_free1(struct roff *);
107: static enum rofft roff_hash_find(const char *);
108: static int roffnode_push(struct roff *,
109: enum rofft, int, int);
110: static void roffnode_pop(struct roff *);
111: static enum rofft roff_parse(const char *, int *);
112:
113:
114: /*
115: * Look up a roff token by its name. Returns ROFF_MAX if no macro by
116: * the nil-terminated string name could be found.
117: */
118: static enum rofft
119: roff_hash_find(const char *p)
120: {
121: int i;
122:
123: /* FIXME: make this be fast and efficient. */
124:
125: for (i = 0; i < (int)ROFF_MAX; i++)
126: if (0 == strcmp(roffs[i].name, p))
127: return((enum rofft)i);
128:
129: return(ROFF_MAX);
130: }
131:
132:
133: /*
134: * Pop the current node off of the stack of roff instructions currently
135: * pending.
136: */
137: static void
138: roffnode_pop(struct roff *r)
139: {
140: struct roffnode *p;
141:
1.75 ! kristaps 142: assert(r->last);
! 143: p = r->last;
! 144: r->last = r->last->parent;
1.74 kristaps 145: if (p->end)
146: free(p->end);
1.67 kristaps 147: free(p);
148: }
149:
150:
151: /*
152: * Push a roff node onto the instruction stack. This must later be
153: * removed with roffnode_pop().
154: */
155: static int
156: roffnode_push(struct roff *r, enum rofft tok, int line, int col)
157: {
158: struct roffnode *p;
159:
160: if (NULL == (p = calloc(1, sizeof(struct roffnode)))) {
161: (*r->msg)(MANDOCERR_MEM, r->data, line, col, NULL);
162: return(0);
163: }
164:
165: p->tok = tok;
166: p->parent = r->last;
167: p->line = line;
168: p->col = col;
169:
170: r->last = p;
171: return(1);
172: }
173:
174:
175: static void
176: roff_free1(struct roff *r)
177: {
178:
179: while (r->last)
180: roffnode_pop(r);
181: }
182:
183:
184: void
185: roff_reset(struct roff *r)
186: {
187:
188: roff_free1(r);
189: }
190:
191:
192: void
193: roff_free(struct roff *r)
194: {
195:
196: roff_free1(r);
197: free(r);
198: }
199:
200:
201: struct roff *
202: roff_alloc(const mandocmsg msg, void *data)
203: {
204: struct roff *r;
205:
206: if (NULL == (r = calloc(1, sizeof(struct roff)))) {
207: (*msg)(MANDOCERR_MEM, data, 0, 0, NULL);
208: return(0);
209: }
210:
211: r->msg = msg;
212: r->data = data;
213: return(r);
214: }
215:
216:
217: enum rofferr
1.74 kristaps 218: roff_parseln(struct roff *r, int ln,
219: char **bufp, size_t *szp, int pos, int *offs)
1.67 kristaps 220: {
221: enum rofft t;
1.75 ! kristaps 222: int ppos;
1.74 kristaps 223:
224: /* Return when in free text without a context. */
225:
1.75 ! kristaps 226: if (r->last && ! ROFF_CTL((*bufp)[pos])) {
! 227: /* XXX: this assumes we're just discarding. */
! 228: while (r->last) {
! 229: if (r->last->endspan-- < 0)
! 230: break;
! 231: ROFF_MDEBUG(r, "closing implicit scope");
! 232: roffnode_pop(r);
! 233: }
! 234: return(ROFF_IGN);
! 235: } else if ( ! ROFF_CTL((*bufp)[pos]))
1.67 kristaps 236: return(ROFF_CONT);
237:
238: /* There's nothing on the stack: make us anew. */
239:
1.75 ! kristaps 240: ppos = pos;
1.74 kristaps 241: if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
1.67 kristaps 242: return(ROFF_CONT);
243:
1.75 ! kristaps 244: assert(roffs[t].proc);
! 245: return((*roffs[t].proc)(r, t, bufp, szp, ln, ppos, pos, offs));
1.74 kristaps 246: }
247:
248:
249: int
250: roff_endparse(struct roff *r)
251: {
252:
253: if (NULL == r->last)
254: return(1);
255: return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data, r->last->line,
256: r->last->col, NULL));
1.67 kristaps 257: }
258:
259:
260: /*
261: * Parse a roff node's type from the input buffer. This must be in the
262: * form of ".foo xxx" in the usual way.
263: */
264: static enum rofft
265: roff_parse(const char *buf, int *pos)
266: {
267: int j;
268: char mac[5];
269: enum rofft t;
270:
1.75 ! kristaps 271: assert(ROFF_CTL(buf[*pos]));
! 272: (*pos)++;
1.67 kristaps 273:
274: while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
275: (*pos)++;
276:
277: if ('\0' == buf[*pos])
278: return(ROFF_MAX);
279:
280: for (j = 0; j < 4; j++, (*pos)++)
281: if ('\0' == (mac[j] = buf[*pos]))
282: break;
283: else if (' ' == buf[*pos])
284: break;
285:
286: if (j == 4 || j < 1)
287: return(ROFF_MAX);
288:
289: mac[j] = '\0';
290:
291: if (ROFF_MAX == (t = roff_hash_find(mac)))
292: return(t);
293:
294: while (buf[*pos] && ' ' == buf[*pos])
295: (*pos)++;
296:
297: return(t);
298: }
299:
300:
1.74 kristaps 301: #if 0
1.67 kristaps 302: /* ARGSUSED */
303: static enum rofferr
304: roff_sub_ig(ROFF_ARGS)
305: {
1.71 kristaps 306: int i, j;
1.67 kristaps 307:
308: /* Ignore free-text lines. */
309:
1.74 kristaps 310: if ('.' != (*bufp)[ppos] && '\'' != (*bufp)[ppos])
1.67 kristaps 311: return(ROFF_IGN);
312:
1.71 kristaps 313: if (r->last->end) {
314: i = ppos + 1;
1.72 kristaps 315:
1.71 kristaps 316: while ((*bufp)[i] && ' ' == (*bufp)[i])
317: i++;
318:
319: for (j = 0; r->last->end[j]; i++, j++)
320: if ((*bufp)[i] != r->last->end[j])
321: return(ROFF_IGN);
322:
323: if (r->last->end[j])
324: return(ROFF_IGN);
325: if ((*bufp)[i] && ' ' != (*bufp)[i])
326: return(ROFF_IGN);
327:
328: while (' ' == (*bufp)[i])
329: i++;
1.72 kristaps 330:
1.71 kristaps 331: } else if (ROFF_close != roff_parse(*bufp, &i))
332: return(ROFF_IGN);
333:
334: roffnode_pop(r);
335:
336: if ('\0' == (*bufp)[i])
1.67 kristaps 337: return(ROFF_IGN);
1.71 kristaps 338: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, i, NULL))
339: return(ROFF_ERR);
1.67 kristaps 340:
341: return(ROFF_IGN);
342: }
343:
344:
345: /* ARGSUSED */
346: static enum rofferr
347: roff_new_close(ROFF_ARGS)
348: {
349:
350: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
351: return(ROFF_ERR);
1.72 kristaps 352:
1.69 kristaps 353: return(ROFF_IGN);
1.67 kristaps 354: }
1.75 ! kristaps 355: #endif
1.67 kristaps 356:
357:
1.75 ! kristaps 358: /* ARGSUSED */
1.74 kristaps 359: static enum rofferr
1.75 ! kristaps 360: roff_ccond(ROFF_ARGS)
1.74 kristaps 361: {
362:
1.75 ! kristaps 363: if (NULL == r->last || ROFF_if != r->last->tok || r->last->endspan > -1) {
! 364: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
! 365: return(ROFF_ERR);
! 366: return(ROFF_IGN);
! 367: }
! 368:
! 369: ROFF_MDEBUG(r, "closing explicit scope");
! 370: roffnode_pop(r);
! 371:
! 372: while (r->last) {
! 373: if (--r->last->endspan < 0)
! 374: break;
1.74 kristaps 375:
1.75 ! kristaps 376: ROFF_MDEBUG(r, "closing implicit scope");
1.74 kristaps 377: roffnode_pop(r);
1.75 ! kristaps 378: }
1.74 kristaps 379:
380: return(ROFF_IGN);
381: }
382:
383:
1.75 ! kristaps 384: /* ARGSUSED */
1.74 kristaps 385: static enum rofferr
1.75 ! kristaps 386: roff_if(ROFF_ARGS)
1.74 kristaps 387: {
388:
389: /*
390: * Read ahead past the conditional.
391: * FIXME: this does not work, as conditionals don't end on
392: * whitespace, but are parsed according to a formal grammar.
393: * It's good enough for now, however.
394: */
395:
396: if ( ! roffnode_push(r, tok, ln, ppos))
397: return(ROFF_ERR);
398:
1.75 ! kristaps 399: while ((*bufp)[pos] && ' ' != (*bufp)[pos])
! 400: pos++;
! 401: while (' ' == (*bufp)[pos])
! 402: pos++;
1.74 kristaps 403:
404: /* Don't evaluate: just assume NO. */
405:
1.75 ! kristaps 406: r->last->endspan = 1;
! 407:
! 408: if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
! 409: ROFF_MDEBUG(r, "opening explicit scope");
! 410: r->last->endspan = -1;
! 411: pos += 2;
! 412: } else
! 413: ROFF_MDEBUG(r, "opening implicit scope");
1.74 kristaps 414:
1.75 ! kristaps 415: if ('\0' == (*bufp)[pos])
! 416: return(ROFF_IGN);
1.74 kristaps 417:
1.75 ! kristaps 418: *offs = pos;
! 419: return(ROFF_RERUN);
1.74 kristaps 420: }
421:
422:
1.75 ! kristaps 423: #if 0
1.67 kristaps 424: static enum rofferr
425: roff_new_ig(ROFF_ARGS)
426: {
1.71 kristaps 427: int i;
1.67 kristaps 428:
1.72 kristaps 429: if ( ! roffnode_push(r, tok, ln, ppos))
1.71 kristaps 430: return(ROFF_ERR);
1.74 kristaps 431:
432: /*
433: * Other macros (not `ig') using this routine have additional
434: * crap here that we discard.
435: */
1.71 kristaps 436:
1.72 kristaps 437: if (ROFF_ig != tok) {
438: while ((*bufp)[ppos] && ' ' != (*bufp)[ppos])
439: ppos++;
440: while (' ' == (*bufp)[ppos])
441: ppos++;
442: }
443:
1.71 kristaps 444: i = (int)ppos;
1.72 kristaps 445:
1.71 kristaps 446: while ((*bufp)[i] && ' ' != (*bufp)[i])
447: i++;
448:
449: if (i == (int)ppos)
450: return(ROFF_IGN);
1.72 kristaps 451:
1.71 kristaps 452: if ((*bufp)[i])
453: if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, i, NULL))
454: return(ROFF_ERR);
455:
456: /*
1.72 kristaps 457: * If the macro has arguments, the first argument (up to the
458: * next whitespace) is interpreted as an argument marking the
459: * macro close. Thus, `.ig foo' will close at `.foo'.
1.71 kristaps 460: *
461: * NOTE: the closing macro `.foo' in the above case is not
462: * allowed to have leading spaces with old groff! Thus `.foo'
463: * != `. foo'. Oh yeah, everything after the `.foo' is lost.
464: * Merry fucking Christmas.
465: */
466:
1.73 kristaps 467: r->last->end = malloc((size_t)(i - ppos) + 1);
1.71 kristaps 468: if (NULL == r->last->end) {
469: (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
470: return(ROFF_ERR);
471: }
472:
1.73 kristaps 473: memcpy(r->last->end, &(*bufp)[ppos], (size_t)(i - ppos));
474: r->last->end[i - ppos] = '\0';
1.71 kristaps 475:
476: return(ROFF_IGN);
1.67 kristaps 477: }
1.74 kristaps 478: #endif
CVSweb