diff options
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | libroff.h | 42 | ||||
-rw-r--r-- | main.c | 2 | ||||
-rw-r--r-- | mandoc.h | 2 | ||||
-rw-r--r-- | tbl.c | 18 | ||||
-rw-r--r-- | tbl_layout.c | 289 | ||||
-rw-r--r-- | tbl_opts.c | 3 |
7 files changed, 354 insertions, 8 deletions
@@ -31,11 +31,11 @@ CFLAGS += -g $(WFLAGS) $(VFLAGS) -DHAVE_CONFIG_H LINTFLAGS += $(VFLAGS) -ROFFLNS = roff.ln tbl.ln tbl_opts.ln +ROFFLNS = roff.ln tbl.ln tbl_opts.ln tbl_layout.ln -ROFFSRCS = roff.c tbl.c tbl_opts.c +ROFFSRCS = roff.c tbl.c tbl_opts.c tbl_layout.c -ROFFOBJS = roff.o tbl.o tbl_opts.o +ROFFOBJS = roff.o tbl.o tbl_opts.o tbl_layout.o MANDOCLNS = mandoc.ln @@ -25,9 +25,44 @@ enum tbl_part { TBL_PART_DATA /* creating data rows */ }; +enum tbl_cellt { + TBL_CELL_CENTRE, /* c, C */ + TBL_CELL_RIGHT, /* r, R */ + TBL_CELL_LEFT, /* l, L */ + TBL_CELL_NUMBER, /* n, N */ + TBL_CELL_SPAN, /* s, S */ + TBL_CELL_LONG, /* a, A */ + TBL_CELL_DOWN, /* ^ */ + TBL_CELL_HORIZ, /* _, - */ + TBL_CELL_DHORIZ, /* = */ + TBL_CELL_VERT, /* | */ + TBL_CELL_DVERT, /* || */ + TBL_CELL_MAX +}; + +struct tbl_cell { + struct tbl_cell *next; + enum tbl_cellt pos; + int spacing; + int flags; +#define TBL_CELL_TALIGN (1 << 0) /* t, T */ +#define TBL_CELL_BALIGN (1 << 1) /* d, D */ +#define TBL_CELL_BOLD (1 << 2) /* fB, B, b */ +#define TBL_CELL_ITALIC (1 << 3) /* fI, I, i */ +#define TBL_CELL_EQUAL (1 << 4) /* e, E */ +#define TBL_CELL_UP (1 << 5) /* u, U */ +#define TBL_CELL_WIGN (1 << 6) /* z, Z */ +}; + +struct tbl_row { + struct tbl_row *next; + struct tbl_cell *first; + struct tbl_cell *last; +}; + struct tbl { - mandocmsg msg; /* status messages */ - void *data; /* privdata for messages */ + mandocmsg msg; /* status messages */ + void *data; /* privdata for messages */ enum tbl_part part; char tab; /* cell-separator */ char decimal; /* decimal point */ @@ -41,6 +76,8 @@ struct tbl { #define TBL_OPT_ALLBOX (1 << 4) #define TBL_OPT_NOKEEP (1 << 5) #define TBL_OPT_NOSPACE (1 << 6) + struct tbl_row *first; + struct tbl_row *last; }; #define TBL_MSG(tblp, type, line, col) \ @@ -51,6 +88,7 @@ void tbl_free(struct tbl *); void tbl_reset(struct tbl *); enum rofferr tbl_read(struct tbl *, int, const char *, int); int tbl_option(struct tbl *, int, const char *); +int tbl_layout(struct tbl *, int, const char *); __END_DECLS @@ -181,6 +181,8 @@ static const char * const mandocerrs[MANDOCERR_MAX] = { "bad table syntax", "bad table option", + "bad table layout", + "no table layout cells specified", "input stack limit exceeded, infinite loop?", "skipping bad character", "skipping text before the first section header", @@ -103,6 +103,8 @@ enum mandocerr { MANDOCERR_TBL, /* bad table syntax */ MANDOCERR_TBLOPT, /* bad table option */ + MANDOCERR_TBLLAYOUT, /* bad table layout */ + MANDOCERR_TBLNOLAYOUT, /* no table layout cells specified */ MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */ MANDOCERR_BADCHAR, /* skipping bad character */ MANDOCERR_NOTEXT, /* skipping text before the first section header */ @@ -31,7 +31,21 @@ static void tbl_clear(struct tbl *); static void tbl_clear(struct tbl *tbl) { + struct tbl_row *rp; + struct tbl_cell *cp; + + while (tbl->first) { + rp = tbl->first; + tbl->first = rp->next; + while (rp->first) { + cp = rp->first; + rp->first = cp->next; + free(cp); + } + free(rp); + } + tbl->last = NULL; } static void @@ -69,6 +83,8 @@ tbl_read(struct tbl *tbl, int ln, const char *p, int offs) switch (tbl->part) { case (TBL_PART_OPTS): return(tbl_option(tbl, ln, p) ? ROFF_IGN : ROFF_ERR); + case (TBL_PART_LAYOUT): + return(tbl_layout(tbl, ln, p) ? ROFF_IGN : ROFF_ERR); default: break; } @@ -81,7 +97,7 @@ tbl_alloc(void *data, const mandocmsg msg) { struct tbl *p; - p = mandoc_malloc(sizeof(struct tbl)); + p = mandoc_calloc(1, sizeof(struct tbl)); p->data = data; p->msg = msg; tbl_init(p); diff --git a/tbl_layout.c b/tbl_layout.c new file mode 100644 index 00000000..004b3337 --- /dev/null +++ b/tbl_layout.c @@ -0,0 +1,289 @@ +/* $Id$ */ +/* + * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <assert.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "mandoc.h" +#include "libmandoc.h" +#include "libroff.h" + +struct tbl_phrase { + char name; + enum tbl_cellt key; +}; + +#define KEYS_MAX 17 + +static const struct tbl_phrase keys[KEYS_MAX] = { + { 'c', TBL_CELL_CENTRE }, + { 'C', TBL_CELL_CENTRE }, + { 'r', TBL_CELL_RIGHT }, + { 'R', TBL_CELL_RIGHT }, + { 'l', TBL_CELL_LEFT }, + { 'L', TBL_CELL_LEFT }, + { 'n', TBL_CELL_NUMBER }, + { 'N', TBL_CELL_NUMBER }, + { 's', TBL_CELL_SPAN }, + { 'S', TBL_CELL_SPAN }, + { 'a', TBL_CELL_LONG }, + { 'A', TBL_CELL_LONG }, + { '^', TBL_CELL_DOWN }, + { '-', TBL_CELL_HORIZ }, + { '_', TBL_CELL_HORIZ }, + { '=', TBL_CELL_DHORIZ }, + { '|', TBL_CELL_VERT } +}; + +static int mods(struct tbl *, struct tbl_cell *, + int, const char *, int *); +static int cell(struct tbl *, struct tbl_row *, + int, const char *, int *); +static void row(struct tbl *, int, const char *, int *); + +static int +mods(struct tbl *tbl, struct tbl_cell *cp, + int ln, const char *p, int *pos) +{ + char buf[5]; + int i; + +mod: + /* + * XXX: since, at least for now, modifiers are non-conflicting + * (are separable by value, regardless of position), we let + * modifiers come in any order. The existing tbl doesn't let + * this happen. + */ + switch (p[*pos]) { + case ('\0'): + /* FALLTHROUGH */ + case (' '): + /* FALLTHROUGH */ + case ('\t'): + /* FALLTHROUGH */ + case (','): + /* FALLTHROUGH */ + case ('.'): + return(1); + default: + break; + } + + /* Parse numerical spacing from modifier string. */ + + if (isdigit((unsigned char)p[*pos])) { + for (i = 0; i < 4; i++) { + if ( ! isdigit((unsigned char)p[*pos + i])) + break; + buf[i] = p[*pos + i]; + } + buf[i] = '\0'; + + /* No greater than 4 digits. */ + + if (4 == i) { + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos); + return(0); + } + + *pos += i; + cp->spacing = atoi(buf); + + goto mod; + /* NOTREACHED */ + } + + /* TODO: GNU has many more extensions. */ + + switch (p[(*pos)++]) { + case ('z'): + /* FALLTHROUGH */ + case ('Z'): + cp->flags |= TBL_CELL_WIGN; + goto mod; + case ('u'): + /* FALLTHROUGH */ + case ('U'): + cp->flags |= TBL_CELL_UP; + goto mod; + case ('e'): + /* FALLTHROUGH */ + case ('E'): + cp->flags |= TBL_CELL_EQUAL; + goto mod; + case ('t'): + /* FALLTHROUGH */ + case ('T'): + cp->flags |= TBL_CELL_TALIGN; + goto mod; + case ('d'): + /* FALLTHROUGH */ + case ('D'): + cp->flags |= TBL_CELL_BALIGN; + goto mod; + case ('f'): + /* FALLTHROUGH */ + case ('B'): + /* FALLTHROUGH */ + case ('I'): + /* FALLTHROUGH */ + case ('b'): + /* FALLTHROUGH */ + case ('i'): + break; + default: + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1); + return(0); + } + + switch (p[(*pos)++]) { + case ('b'): + /* FALLTHROUGH */ + case ('B'): + cp->flags |= TBL_CELL_BOLD; + goto mod; + case ('i'): + /* FALLTHROUGH */ + case ('I'): + cp->flags |= TBL_CELL_ITALIC; + goto mod; + default: + break; + } + + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1); + return(0); +} + +static int +cell(struct tbl *tbl, struct tbl_row *rp, + int ln, const char *p, int *pos) +{ + struct tbl_cell *cp; + int i; + enum tbl_cellt c; + + /* Parse the column position (`r', `R', `|', ...). */ + + for (i = 0; i < KEYS_MAX; i++) + if (p[*pos] == keys[i].name) + break; + + if (KEYS_MAX == i) { + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos); + return(0); + } + + (*pos)++; + c = keys[i].key; + + /* Extra check for the double-vertical. */ + + if (TBL_CELL_VERT == c && '|' == p[*pos]) { + (*pos)++; + c = TBL_CELL_DVERT; + } + + /* Disallow adjacent spacers. */ + + if (rp->last && (TBL_CELL_VERT == c || TBL_CELL_DVERT == c) && + (TBL_CELL_VERT == rp->last->pos || + TBL_CELL_DVERT == rp->last->pos)) { + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1); + return(0); + } + + /* Allocate cell then parse its modifiers. */ + + cp = mandoc_calloc(1, sizeof(struct tbl_cell)); + cp->pos = c; + + if (rp->last) { + rp->last->next = cp; + rp->last = cp; + } else + rp->last = rp->first = cp; + + return(mods(tbl, cp, ln, p, pos)); +} + + +static void +row(struct tbl *tbl, int ln, const char *p, int *pos) +{ + struct tbl_row *rp; + +row: /* + * EBNF describing this section: + * + * row ::= row_list [:space:]* [.]?[\n] + * row_list ::= [:space:]* row_elem row_tail + * row_tail ::= [:space:]*[,] row_list | + * epsilon + * row_elem ::= [\t\ ]*[:alpha:]+ + */ + + rp = mandoc_calloc(1, sizeof(struct tbl_row)); + if (tbl->last) { + tbl->last->next = rp; + tbl->last = rp; + } else + tbl->last = tbl->first = rp; + +cell: + while (isspace((unsigned char)p[*pos])) + (*pos)++; + + /* Safely exit layout context. */ + + if ('.' == p[*pos]) { + tbl->part = TBL_PART_DATA; + if (NULL == tbl->first) + TBL_MSG(tbl, MANDOCERR_TBLNOLAYOUT, ln, *pos); + (*pos)++; + return; + } + + /* End (and possibly restart) a row. */ + + if (',' == p[*pos]) { + (*pos)++; + goto row; + } else if ('\0' == p[*pos]) + return; + + if ( ! cell(tbl, rp, ln, p, pos)) + return; + + goto cell; + /* NOTREACHED */ +} + + +int +tbl_layout(struct tbl *tbl, int ln, const char *p) +{ + int pos; + + pos = 0; + row(tbl, ln, p, &pos); + + /* Always succeed. */ + return(1); +} @@ -235,9 +235,8 @@ again: /* if (KEY_MAXKEYS == i) TBL_MSG(tbl, MANDOCERR_TBLOPT, ln, sv); - /* Try again... */ - goto again; + /* NOTREACHED */ } int |