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