Annotation of src/usr.bin/mandoc/tbl.c, Revision 1.2
1.1 schwarze 1: /* $Id: tbl.c,v 1.14 2009/09/12 16:05:34 kristaps Exp $ */
2: /*
3: * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
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.
16: */
17: #include <sys/queue.h>
18:
19: #include <assert.h>
20: #include <errno.h>
21: #include <limits.h>
22: #include <stdio.h>
23: #include <stdlib.h>
24: #include <string.h>
25:
1.2 ! schwarze 26: #include "out.h"
! 27: #include "term.h"
1.1 schwarze 28: #include "tbl.h"
29: #include "tbl_extern.h"
30:
31:
32: const char *const errnames[ERR_MAX] = {
33: "bad syntax", /* ERR_SYNTAX */
34: "bad option" /* ERR_OPTION */
35: };
36:
37: static char buf[1024]; /* XXX */
38:
39: static enum tbl_tok tbl_next_char(char);
40: static void tbl_init(struct tbl *);
41: static void tbl_clear(struct tbl *);
42: static struct tbl_head *tbl_head_alloc(struct tbl *);
43: static void tbl_span_free(struct tbl_span *);
44: static void tbl_data_free(struct tbl_data *);
45: static void tbl_row_free(struct tbl_row *);
46:
47: static void headadj(const struct tbl_cell *,
48: struct tbl_head *);
49:
50: static void
51: tbl_init(struct tbl *tbl)
52: {
53:
54: bzero(tbl, sizeof(struct tbl));
55:
56: tbl->part = TBL_PART_OPTS;
57: tbl->tab = '\t';
58: tbl->linesize = 12;
59: tbl->decimal = '.';
60:
61: TAILQ_INIT(&tbl->span);
62: TAILQ_INIT(&tbl->row);
63: TAILQ_INIT(&tbl->head);
64: }
65:
66:
67: int
68: tbl_read(struct tbl *tbl, const char *f, int ln, const char *p, int len)
69: {
70:
71: if (len && TBL_PART_OPTS == tbl->part)
72: if (';' != p[len - 1])
73: tbl->part = TBL_PART_LAYOUT;
74:
75: switch (tbl->part) {
76: case (TBL_PART_OPTS):
77: return(tbl_option(tbl, f, ln, p));
78: case (TBL_PART_CLAYOUT):
79: /* FALLTHROUGH */
80: case (TBL_PART_LAYOUT):
81: return(tbl_layout(tbl, f, ln, p));
82: case (TBL_PART_DATA):
83: return(tbl_data(tbl, f, ln, p));
84: case (TBL_PART_ERROR):
85: break;
86: }
87:
88: return(0);
89: }
90:
91:
92: int
93: tbl_close(struct tbl *tbl, const char *f, int ln)
94: {
95:
96: if (TBL_PART_DATA != tbl->part)
97: return(tbl_errx(tbl, ERR_SYNTAX, f, ln, 0));
98: if ( ! tbl_data_close(tbl, f, ln))
99: return(0);
100: #if 1
101: return(tbl_calc_term(tbl));
102: #else
103: return(tbl_calc_tree(tbl));
104: #endif
105: }
106:
107:
108: int
1.2 ! schwarze 109: tbl_write(struct termp *p, const struct tbl *tbl)
1.1 schwarze 110: {
111:
112: #if 1
1.2 ! schwarze 113: return(tbl_write_term(p, tbl));
1.1 schwarze 114: #else
115: return(tbl_write_tree(tbl));
116: #endif
117: }
118:
119:
120: static enum tbl_tok
121: tbl_next_char(char c)
122: {
123:
124: /*
125: * These are delimiting tokens. They separate out words in the
126: * token stream.
127: */
128:
129: switch (c) {
130: case ('('):
131: return(TBL_TOK_OPENPAREN);
132: case (')'):
133: return(TBL_TOK_CLOSEPAREN);
134: case (' '):
135: return(TBL_TOK_SPACE);
136: case ('\t'):
137: return(TBL_TOK_TAB);
138: case (';'):
139: return(TBL_TOK_SEMICOLON);
140: case ('.'):
141: return(TBL_TOK_PERIOD);
142: case (','):
143: return(TBL_TOK_COMMA);
144: case (0):
145: return(TBL_TOK_NIL);
146: default:
147: break;
148: }
149:
150: return(TBL_TOK_WORD);
151: }
152:
153:
154: const char *
155: tbl_last(void)
156: {
157:
158: return(buf);
159: }
160:
161:
162: int
163: tbl_last_uint(void)
164: {
165: char *ep;
166: long lval;
167:
168: /* From OpenBSD's strtol(3). Gross. */
169:
170: errno = 0;
171: lval = strtol(buf, &ep, 10);
172: if (buf[0] == 0 || *ep != 0)
173: return(-1);
174: if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN))
175: return(-1);
176: if (lval < 0 || lval > INT_MAX)
177: return(-1);
178:
179: return((int)lval);
180: }
181:
182:
183: enum tbl_tok
184: tbl_next(const char *p, int *pos)
185: {
186: int i;
187: enum tbl_tok c;
188:
189: buf[0] = 0;
190:
191: if (TBL_TOK_WORD != (c = tbl_next_char(p[*pos]))) {
192: if (TBL_TOK_NIL != c) {
193: buf[0] = p[*pos];
194: buf[1] = 0;
195: (*pos)++;
196: }
197: return(c);
198: }
199:
200: /*
201: * Copy words into a nil-terminated buffer. For now, we use a
202: * static buffer. Eventually this should be made into a dynamic
203: * one living in struct tbl.
204: */
205:
206: for (i = 0; i < 1023; i++, (*pos)++)
207: if (TBL_TOK_WORD == tbl_next_char(p[*pos]))
208: buf[i] = p[*pos];
209: else
210: break;
211:
212: assert(i < 1023);
213: buf[i] = 0;
214:
215: return(TBL_TOK_WORD);
216: }
217:
218:
219: int
220: tbl_err(struct tbl *tbl)
221: {
222:
223: (void)fprintf(stderr, "%s\n", strerror(errno));
224: tbl->part = TBL_PART_ERROR;
225: return(0);
226: }
227:
228:
229: /* ARGSUSED */
230: int
231: tbl_warnx(struct tbl *tbl, enum tbl_err tok,
232: const char *f, int line, int pos)
233: {
234:
235: (void)fprintf(stderr, "%s:%d:%d: %s\n",
236: f, line, pos + 1, errnames[tok]);
237:
238: /* TODO: -Werror */
239: return(1);
240: }
241:
242:
243: int
244: tbl_errx(struct tbl *tbl, enum tbl_err tok,
245: const char *f, int line, int pos)
246: {
247:
248: (void)fprintf(stderr, "%s:%d:%d: %s\n",
249: f, line, pos + 1, errnames[tok]);
250:
251: tbl->part = TBL_PART_ERROR;
252: return(0);
253: }
254:
255:
256: struct tbl *
257: tbl_alloc(void)
258: {
259: struct tbl *p;
260:
261: if (NULL == (p = malloc(sizeof(struct tbl))))
262: return(NULL);
263:
264: tbl_init(p);
265: return(p);
266: }
267:
268:
269: void
270: tbl_free(struct tbl *p)
271: {
272:
273: tbl_clear(p);
274: free(p);
275: }
276:
277:
278: void
279: tbl_reset(struct tbl *tbl)
280: {
281:
282: tbl_clear(tbl);
283: tbl_init(tbl);
284: }
285:
286:
287: struct tbl_span *
288: tbl_span_alloc(struct tbl *tbl)
289: {
290: struct tbl_span *p, *pp;
291: struct tbl_row *row;
292:
293: if (NULL == (p = calloc(1, sizeof(struct tbl_span)))) {
294: (void)tbl_err(tbl);
295: return(NULL);
296: }
297:
298: TAILQ_INIT(&p->data);
299: TAILQ_INSERT_TAIL(&tbl->span, p, entries);
300:
301: /* LINTED */
302: pp = TAILQ_PREV(p, tbl_spanh, entries);
303:
304: if (pp) {
305: row = TAILQ_NEXT(pp->row, entries);
306: if (NULL == row)
307: row = pp->row;
308: } else {
309: row = TAILQ_FIRST(&tbl->row);
310: }
311:
312: assert(row);
313: p->row = row;
314: p->tbl = tbl;
315: return(p);
316: }
317:
318:
319: struct tbl_row *
320: tbl_row_alloc(struct tbl *tbl)
321: {
322: struct tbl_row *p;
323:
324: if (NULL == (p = calloc(1, sizeof(struct tbl_row)))) {
325: (void)tbl_err(tbl);
326: return(NULL);
327: }
328:
329: TAILQ_INIT(&p->cell);
330: TAILQ_INSERT_TAIL(&tbl->row, p, entries);
331: p->tbl = tbl;
332: return(p);
333: }
334:
335:
336: static void
337: headadj(const struct tbl_cell *cell, struct tbl_head *head)
338: {
339: if (TBL_CELL_VERT != cell->pos &&
340: TBL_CELL_DVERT != cell->pos) {
341: head->pos = TBL_HEAD_DATA;
342: return;
343: }
344: if (TBL_CELL_VERT == cell->pos)
345: if (TBL_HEAD_DVERT != head->pos)
346: head->pos = TBL_HEAD_VERT;
347: if (TBL_CELL_DVERT == cell->pos)
348: head->pos = TBL_HEAD_DVERT;
349: }
350:
351:
352: static struct tbl_head *
353: tbl_head_alloc(struct tbl *tbl)
354: {
355: struct tbl_head *p;
356:
357: if (NULL == (p = calloc(1, sizeof(struct tbl_head)))) {
358: (void)tbl_err(tbl);
359: return(NULL);
360: }
361: p->tbl = tbl;
362: return(p);
363: }
364:
365:
366: struct tbl_cell *
367: tbl_cell_alloc(struct tbl_row *rp, enum tbl_cellt pos)
368: {
369: struct tbl_cell *p, *pp;
370: struct tbl_head *h, *hp;
371:
372: if (NULL == (p = calloc(1, sizeof(struct tbl_cell)))) {
373: (void)tbl_err(rp->tbl);
374: return(NULL);
375: }
376:
377: TAILQ_INSERT_TAIL(&rp->cell, p, entries);
378: p->pos = pos;
379: p->row = rp;
380:
381: /*
382: * This is a little bit complicated. Here we determine the
383: * header the corresponds to a cell. We add headers dynamically
384: * when need be or re-use them, otherwise. As an example, given
385: * the following:
386: *
387: * 1 c || l
388: * 2 | c | l
389: * 3 l l
390: * 3 || c | l |.
391: *
392: * We first add the new headers (as there are none) in (1); then
393: * in (2) we insert the first spanner (as it doesn't match up
394: * with the header); then we re-use the prior data headers,
395: * skipping over the spanners; then we re-use everything and add
396: * a last spanner. Note that VERT headers are made into DVERT
397: * ones.
398: */
399:
400: /* LINTED */
401: pp = TAILQ_PREV(p, tbl_cellh, entries);
402:
403: h = pp ? TAILQ_NEXT(pp->head, entries) :
404: TAILQ_FIRST(&rp->tbl->head);
405:
406: if (h) {
407: /* Re-use data header. */
408: if (TBL_HEAD_DATA == h->pos &&
409: (TBL_CELL_VERT != p->pos &&
410: TBL_CELL_DVERT != p->pos)) {
411: p->head = h;
412: return(p);
413: }
414:
415: /* Re-use spanner header. */
416: if (TBL_HEAD_DATA != h->pos &&
417: (TBL_CELL_VERT == p->pos ||
418: TBL_CELL_DVERT == p->pos)) {
419: headadj(p, h);
420: p->head = h;
421: return(p);
422: }
423:
424: /* Right-shift headers with a new spanner. */
425: if (TBL_HEAD_DATA == h->pos &&
426: (TBL_CELL_VERT == p->pos ||
427: TBL_CELL_DVERT == p->pos)) {
428: if (NULL == (hp = tbl_head_alloc(rp->tbl)))
429: return(NULL);
430: TAILQ_INSERT_BEFORE(h, hp, entries);
431: headadj(p, hp);
432: p->head = hp;
433: return(p);
434: }
435:
436: h = TAILQ_NEXT(h, entries);
437: if (h) {
438: headadj(p, h);
439: p->head = h;
440: return(p);
441: }
442:
443: /* Fall through to default case... */
444: }
445:
446: if (NULL == (hp = tbl_head_alloc(rp->tbl)))
447: return(NULL);
448: TAILQ_INSERT_TAIL(&rp->tbl->head, hp, entries);
449: headadj(p, hp);
450: p->head = hp;
451: return(p);
452: }
453:
454:
455: struct tbl_data *
456: tbl_data_alloc(struct tbl_span *sp)
457: {
458: struct tbl_data *p;
459: struct tbl_cell *cp;
460: struct tbl_data *dp;
461:
462: if (NULL == (p = calloc(1, sizeof(struct tbl_data)))) {
463: (void)tbl_err(sp->row->tbl);
464: return(NULL);
465: }
466:
467: cp = NULL;
468: /* LINTED */
469: if (NULL == (dp = TAILQ_LAST(&sp->data, tbl_datah)))
470: cp = TAILQ_FIRST(&sp->row->cell);
471: else if (dp->cell)
472: cp = TAILQ_NEXT(dp->cell, entries);
473:
474: TAILQ_INSERT_TAIL(&sp->data, p, entries);
475:
476: if (cp && (TBL_CELL_VERT == cp->pos ||
477: TBL_CELL_DVERT == cp->pos))
478: cp = TAILQ_NEXT(cp, entries);
479:
480: p->span = sp;
481: p->cell = cp;
482: return(p);
483: }
484:
485:
486: static void
487: tbl_clear(struct tbl *p)
488: {
489: struct tbl_span *span;
490: struct tbl_head *head;
491: struct tbl_row *row;
492:
493: /* LINTED */
494: while ((span = TAILQ_FIRST(&p->span))) {
495: TAILQ_REMOVE(&p->span, span, entries);
496: tbl_span_free(span);
497: }
498: /* LINTED */
499: while ((row = TAILQ_FIRST(&p->row))) {
500: TAILQ_REMOVE(&p->row, row, entries);
501: tbl_row_free(row);
502: }
503: /* LINTED */
504: while ((head = TAILQ_FIRST(&p->head))) {
505: TAILQ_REMOVE(&p->head, head, entries);
506: free(head);
507: }
508: }
509:
510:
511: static void
512: tbl_span_free(struct tbl_span *p)
513: {
514: struct tbl_data *data;
515:
516: /* LINTED */
517: while ((data = TAILQ_FIRST(&p->data))) {
518: TAILQ_REMOVE(&p->data, data, entries);
519: tbl_data_free(data);
520: }
521: free(p);
522: }
523:
524:
525: static void
526: tbl_data_free(struct tbl_data *p)
527: {
528:
529: if (p->string)
530: free(p->string);
531: free(p);
532: }
533:
534:
535: static void
536: tbl_row_free(struct tbl_row *p)
537: {
538: struct tbl_cell *cell;
539:
540: /* LINTED */
541: while ((cell = TAILQ_FIRST(&p->cell))) {
542: TAILQ_REMOVE(&p->cell, cell, entries);
543: free(cell);
544: }
545: free(p);
546: }