Annotation of mandoc/roff.c, Revision 1.70
1.70 ! kristaps 1: /* $Id: roff.c,v 1.69 2010/05/15 18:48:32 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 {
29: ROFF_de,
30: ROFF_dei,
31: ROFF_am,
32: ROFF_ami,
33: ROFF_ig,
34: ROFF_close,
35: ROFF_MAX
36: };
37:
38: struct roff {
39: struct roffnode *last; /* leaf of stack */
40: mandocmsg msg; /* err/warn/fatal messages */
41: void *data; /* privdata for messages */
42: };
43:
44: struct roffnode {
45: enum rofft tok; /* type of node */
46: struct roffnode *parent; /* up one in stack */
47: int line; /* parse line */
48: int col; /* parse col */
49: };
50:
51: #define ROFF_ARGS struct roff *r, /* parse ctx */ \
52: char **bufp, /* input buffer */ \
53: size_t *szp, /* size of input buffer */ \
54: int ln, /* parse line */ \
55: int ppos /* current pos in buffer */
56:
57: typedef enum rofferr (*roffproc)(ROFF_ARGS);
58:
59: struct roffmac {
60: const char *name; /* macro name */
61: roffproc sub; /* child of control black */
62: roffproc new; /* root of stack (type = ROFF_MAX) */
63: };
64:
65: static enum rofferr roff_ignore(ROFF_ARGS);
66: static enum rofferr roff_new_close(ROFF_ARGS);
67: static enum rofferr roff_new_ig(ROFF_ARGS);
68: static enum rofferr roff_sub_ig(ROFF_ARGS);
69:
70: const struct roffmac roffs[ROFF_MAX] = {
71: { "de", NULL, roff_ignore },
72: { "dei", NULL, roff_ignore },
73: { "am", NULL, roff_ignore },
74: { "ami", NULL, roff_ignore },
75: { "ig", roff_sub_ig, roff_new_ig },
76: { ".", NULL, roff_new_close },
77: };
78:
79: static void roff_free1(struct roff *);
80: static enum rofft roff_hash_find(const char *);
81: static int roffnode_push(struct roff *,
82: enum rofft, int, int);
83: static void roffnode_pop(struct roff *);
84: static enum rofft roff_parse(const char *, int *);
85:
86:
87: /*
88: * Look up a roff token by its name. Returns ROFF_MAX if no macro by
89: * the nil-terminated string name could be found.
90: */
91: static enum rofft
92: roff_hash_find(const char *p)
93: {
94: int i;
95:
96: /* FIXME: make this be fast and efficient. */
97:
98: for (i = 0; i < (int)ROFF_MAX; i++)
99: if (0 == strcmp(roffs[i].name, p))
100: return((enum rofft)i);
101:
102: return(ROFF_MAX);
103: }
104:
105:
106: /*
107: * Pop the current node off of the stack of roff instructions currently
108: * pending.
109: */
110: static void
111: roffnode_pop(struct roff *r)
112: {
113: struct roffnode *p;
114:
115: if (NULL == (p = r->last))
116: return;
117: r->last = p->parent;
118: free(p);
119: }
120:
121:
122: /*
123: * Push a roff node onto the instruction stack. This must later be
124: * removed with roffnode_pop().
125: */
126: static int
127: roffnode_push(struct roff *r, enum rofft tok, int line, int col)
128: {
129: struct roffnode *p;
130:
131: if (NULL == (p = calloc(1, sizeof(struct roffnode)))) {
132: (*r->msg)(MANDOCERR_MEM, r->data, line, col, NULL);
133: return(0);
134: }
135:
136: p->tok = tok;
137: p->parent = r->last;
138: p->line = line;
139: p->col = col;
140:
141: r->last = p;
142: return(1);
143: }
144:
145:
146: static void
147: roff_free1(struct roff *r)
148: {
149:
150: while (r->last)
151: roffnode_pop(r);
152: }
153:
154:
155: void
156: roff_reset(struct roff *r)
157: {
158:
159: roff_free1(r);
160: }
161:
162:
163: void
164: roff_free(struct roff *r)
165: {
166:
167: roff_free1(r);
168: free(r);
169: }
170:
171:
172: struct roff *
173: roff_alloc(const mandocmsg msg, void *data)
174: {
175: struct roff *r;
176:
177: if (NULL == (r = calloc(1, sizeof(struct roff)))) {
178: (*msg)(MANDOCERR_MEM, data, 0, 0, NULL);
179: return(0);
180: }
181:
182: r->msg = msg;
183: r->data = data;
184: return(r);
185: }
186:
187:
188: enum rofferr
189: roff_parseln(struct roff *r, int ln, char **bufp, size_t *szp)
190: {
191: enum rofft t;
192: int ppos;
193:
194: if (NULL != r->last) {
195: /*
196: * If there's a node on the stack, then jump directly
197: * into its processing function.
198: */
199: t = r->last->tok;
200: assert(roffs[t].sub);
201: return((*roffs[t].sub)(r, bufp, szp, ln, 0));
202: } else if ('.' != (*bufp)[0] && NULL == r->last)
203: /* Return when in free text without a context. */
204: return(ROFF_CONT);
205:
206: /* There's nothing on the stack: make us anew. */
207:
208: if (ROFF_MAX == (t = roff_parse(*bufp, &ppos)))
209: return(ROFF_CONT);
210:
211: assert(roffs[t].new);
212: return((*roffs[t].new)(r, bufp, szp, ln, ppos));
213: }
214:
215:
216: /*
217: * Parse a roff node's type from the input buffer. This must be in the
218: * form of ".foo xxx" in the usual way.
219: */
220: static enum rofft
221: roff_parse(const char *buf, int *pos)
222: {
223: int j;
224: char mac[5];
225: enum rofft t;
226:
227: assert('.' == buf[0]);
228: *pos = 1;
229:
230: while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
231: (*pos)++;
232:
233: if ('\0' == buf[*pos])
234: return(ROFF_MAX);
235:
236: for (j = 0; j < 4; j++, (*pos)++)
237: if ('\0' == (mac[j] = buf[*pos]))
238: break;
239: else if (' ' == buf[*pos])
240: break;
241:
242: if (j == 4 || j < 1)
243: return(ROFF_MAX);
244:
245: mac[j] = '\0';
246:
247: if (ROFF_MAX == (t = roff_hash_find(mac)))
248: return(t);
249:
250: while (buf[*pos] && ' ' == buf[*pos])
251: (*pos)++;
252:
253: return(t);
254: }
255:
256:
257: /* ARGSUSED */
258: static enum rofferr
259: roff_ignore(ROFF_ARGS)
260: {
261:
262: return(ROFF_IGN);
263: }
264:
265:
266: /* ARGSUSED */
267: static enum rofferr
268: roff_sub_ig(ROFF_ARGS)
269: {
270: enum rofft t;
271: int pos;
272:
273: /* Ignore free-text lines. */
274:
275: if ('.' != (*bufp)[ppos])
276: return(ROFF_IGN);
277:
278: /* Ignore macros unless it's a closing macro. */
279:
280: t = roff_parse(*bufp, &pos);
281: if (ROFF_close != t)
282: return(ROFF_IGN);
283:
284: roffnode_pop(r);
285: return(ROFF_IGN);
286: }
287:
288:
289: /* ARGSUSED */
290: static enum rofferr
291: roff_new_close(ROFF_ARGS)
292: {
293:
1.68 kristaps 294: /*
1.67 kristaps 295: if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
296: return(ROFF_ERR);
1.68 kristaps 297: */
1.69 kristaps 298: return(ROFF_IGN);
1.67 kristaps 299: }
300:
301:
302: /* ARGSUSED */
303: static enum rofferr
304: roff_new_ig(ROFF_ARGS)
305: {
306:
307: return(roffnode_push(r, ROFF_ig, ln, ppos) ?
308: ROFF_IGN : ROFF_ERR);
309: }
310:
311:
312: int
313: roff_endparse(struct roff *r)
314: {
315:
316: if (NULL == r->last)
317: return(1);
318: return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data,
319: r->last->line, r->last->col, NULL));
320: }
CVSweb