=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/mandoc/tbl_layout.c,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- src/usr.bin/mandoc/tbl_layout.c 2010/10/15 22:50:28 1.3 +++ src/usr.bin/mandoc/tbl_layout.c 2011/01/04 22:28:17 1.4 @@ -1,6 +1,6 @@ -/* $Id: tbl_layout.c,v 1.3 2010/10/15 22:50:28 schwarze Exp $ */ +/* $Id: tbl_layout.c,v 1.4 2011/01/04 22:28:17 schwarze Exp $ */ /* - * Copyright (c) 2009 Kristaps Dzonsons + * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,38 +14,30 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include - #include #include #include #include +#include -#include "out.h" -#include "term.h" -#include "tbl_extern.h" +#include "mandoc.h" +#include "libmandoc.h" +#include "libroff.h" struct tbl_phrase { char name; enum tbl_cellt key; }; -#define KEYS_MAX 17 +#define KEYS_MAX 11 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 }, @@ -53,191 +45,163 @@ { '|', TBL_CELL_VERT } }; -static int mods(struct tbl *, struct tbl_cell *, - const char *, int, - const char *, int, int); -static int cell(struct tbl *, struct tbl_row *, - const char *, int, int); -static int row(struct tbl *, const char *, +static int mods(struct tbl_node *, struct tbl_cell *, int, const char *, int *); +static int cell(struct tbl_node *, struct tbl_row *, + int, const char *, int *); +static void row(struct tbl_node *, int, const char *, int *); +static struct tbl_cell *cell_alloc(struct tbl_node *, + struct tbl_row *, enum tbl_cellt); +static void head_adjust(const struct tbl_cell *, + struct tbl_head *); - static int -mods(struct tbl *tbl, struct tbl_cell *cp, const char *p, - int pp, const char *f, int ln, int pos) +mods(struct tbl_node *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. */ - - if (0 == p[pp]) + 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((u_char)p[pp])) { + if (isdigit((unsigned char)p[*pos])) { for (i = 0; i < 4; i++) { - if ( ! isdigit((u_char)p[pp + i])) + if ( ! isdigit((unsigned char)p[*pos + i])) break; - buf[i] = p[pp + i]; + buf[i] = p[*pos + i]; } - buf[i] = 0; + buf[i] = '\0'; /* No greater than 4 digits. */ - if (4 == i) - return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos + pp)); - - /* - * We can't change the spacing in any subsequent layout - * definitions. FIXME: I don't think we can change the - * spacing for a column at all, after it's already been - * initialised. - */ - - if (TBL_PART_CLAYOUT != tbl->part) - cp->spacing = atoi(buf); - else if ( ! tbl_warnx(tbl, ERR_SYNTAX, f, ln, pos + pp)) + if (4 == i) { + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos); return(0); - - /* Continue parsing modifiers. */ + } - return(mods(tbl, cp, p, pp + i, f, ln, pos)); + *pos += i; + cp->spacing = atoi(buf); + + goto mod; + /* NOTREACHED */ } /* TODO: GNU has many more extensions. */ - switch (p[pp]) { + switch (tolower(p[(*pos)++])) { case ('z'): - /* FALLTHROUGH */ - case ('Z'): cp->flags |= TBL_CELL_WIGN; - return(mods(tbl, cp, p, pp + 1, f, ln, pos)); - case ('w'): - /* FALLTHROUGH */ - case ('W'): /* XXX for now, ignore minimal column width */ - while (isdigit((u_char)p[++pp])); - return(mods(tbl, cp, p, pp, f, ln, pos)); + goto mod; case ('u'): - /* FALLTHROUGH */ - case ('U'): cp->flags |= TBL_CELL_UP; - return(mods(tbl, cp, p, pp + 1, f, ln, pos)); + goto mod; case ('e'): - /* FALLTHROUGH */ - case ('E'): cp->flags |= TBL_CELL_EQUAL; - return(mods(tbl, cp, p, pp + 1, f, ln, pos)); + goto mod; case ('t'): - /* FALLTHROUGH */ - case ('T'): cp->flags |= TBL_CELL_TALIGN; - return(mods(tbl, cp, p, pp + 1, f, ln, pos)); + goto mod; case ('d'): - /* FALLTHROUGH */ - case ('D'): cp->flags |= TBL_CELL_BALIGN; - return(mods(tbl, cp, p, pp + 1, f, ln, pos)); + goto mod; case ('f'): - pp++; - /* FALLTHROUGH */ - case ('B'): - /* FALLTHROUGH */ - case ('I'): - /* FALLTHROUGH */ + break; case ('b'): /* FALLTHROUGH */ case ('i'): + (*pos)--; break; default: - return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos + pp)); + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1); + return(0); } - switch (p[pp]) { + switch (tolower(p[(*pos)++])) { case ('b'): - /* FALLTHROUGH */ - case ('B'): cp->flags |= TBL_CELL_BOLD; - return(mods(tbl, cp, p, pp + 1, f, ln, pos)); + goto mod; case ('i'): - /* FALLTHROUGH */ - case ('I'): cp->flags |= TBL_CELL_ITALIC; - return(mods(tbl, cp, p, pp + 1, f, ln, pos)); + goto mod; default: break; } - return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos + pp)); + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1); + return(0); } - static int -cell(struct tbl *tbl, struct tbl_row *rp, - const char *f, int ln, int pos) +cell(struct tbl_node *tbl, struct tbl_row *rp, + int ln, const char *p, int *pos) { - struct tbl_cell *cp; - const char *p; - int j, i; + int i; enum tbl_cellt c; /* Parse the column position (`r', `R', `|', ...). */ - c = TBL_CELL_MAX; - for (p = tbl_last(), i = 0; i < KEYS_MAX; i++) { - if (keys[i].name != p[0]) - continue; - c = keys[i].key; - break; + for (i = 0; i < KEYS_MAX; i++) + if (tolower(p[*pos]) == keys[i].name) + break; + + if (KEYS_MAX == i) { + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos); + return(0); } - if (i == KEYS_MAX) - return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos)); + (*pos)++; + c = keys[i].key; /* Extra check for the double-vertical. */ - if (TBL_CELL_VERT == c && '|' == p[1]) { - j = 2; + if (TBL_CELL_VERT == c && '|' == p[*pos]) { + (*pos)++; c = TBL_CELL_DVERT; - } else - j = 1; + } - /* Disallow subsequent spacers. */ + /* Disallow adjacent spacers. */ - /* LINTED */ - cp = TAILQ_LAST(&rp->cell, tbl_cellh); + 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); + } - if (cp && (TBL_CELL_VERT == c || TBL_CELL_DVERT == c) && - (TBL_CELL_VERT == cp->pos || - TBL_CELL_DVERT == cp->pos)) - return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos)); - /* Allocate cell then parse its modifiers. */ - if (NULL == (cp = tbl_cell_alloc(rp, c))) - return(0); - return(mods(tbl, cp, p, j, f, ln, pos)); + return(mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos)); } -static int -row(struct tbl *tbl, const char *f, int ln, - const char *p, int *pos) +static void +row(struct tbl_node *tbl, int ln, const char *p, int *pos) { struct tbl_row *rp; - int sv; - rp = tbl_row_alloc(tbl); -again: - sv = *pos; - - /* +row: /* * EBNF describing this section: * * row ::= row_list [:space:]* [.]?[\n] @@ -247,41 +211,165 @@ * row_elem ::= [\t\ ]*[:alpha:]+ */ - switch (tbl_next(p, pos)) { - case (TBL_TOK_TAB): - /* FALLTHROUGH */ - case (TBL_TOK_SPACE): - goto again; - case (TBL_TOK_WORD): - if ( ! cell(tbl, rp, f, ln, sv)) - return(0); - goto again; - case (TBL_TOK_COMMA): - if (NULL == TAILQ_FIRST(&rp->cell)) - return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv)); - return(row(tbl, f, ln, p, pos)); - case (TBL_TOK_PERIOD): - if (NULL == TAILQ_FIRST(&rp->cell)) - return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv)); + rp = mandoc_calloc(1, sizeof(struct tbl_row)); + if (tbl->last_row) { + tbl->last_row->next = rp; + tbl->last_row = rp; + } else + tbl->last_row = tbl->first_row = rp; + +cell: + while (isspace((unsigned char)p[*pos])) + (*pos)++; + + /* Safely exit layout context. */ + + if ('.' == p[*pos]) { tbl->part = TBL_PART_DATA; - break; - case (TBL_TOK_NIL): - if (NULL == TAILQ_FIRST(&rp->cell)) - return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv)); - break; - default: - return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv)); + if (NULL == tbl->first_row) + TBL_MSG(tbl, MANDOCERR_TBLNOLAYOUT, ln, *pos); + (*pos)++; + return; } - return(1); -} + /* 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, const char *f, int ln, const char *p) +tbl_layout(struct tbl_node *tbl, int ln, const char *p) { int pos; pos = 0; - return(row(tbl, f, ln, p, &pos)); + row(tbl, ln, p, &pos); + + /* Always succeed. */ + return(1); } + +static struct tbl_cell * +cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos) +{ + struct tbl_cell *p, *pp; + struct tbl_head *h, *hp; + + p = mandoc_calloc(1, sizeof(struct tbl_cell)); + + if (NULL != (pp = rp->last)) { + rp->last->next = p; + rp->last = p; + } else + rp->last = rp->first = p; + + p->pos = pos; + + /* + * This is a little bit complicated. Here we determine the + * header the corresponds to a cell. We add headers dynamically + * when need be or re-use them, otherwise. As an example, given + * the following: + * + * 1 c || l + * 2 | c | l + * 3 l l + * 3 || c | l |. + * + * We first add the new headers (as there are none) in (1); then + * in (2) we insert the first spanner (as it doesn't match up + * with the header); then we re-use the prior data headers, + * skipping over the spanners; then we re-use everything and add + * a last spanner. Note that VERT headers are made into DVERT + * ones. + */ + + h = pp ? pp->head->next : tbl->first_head; + + if (h) { + /* Re-use data header. */ + if (TBL_HEAD_DATA == h->pos && + (TBL_CELL_VERT != p->pos && + TBL_CELL_DVERT != p->pos)) { + p->head = h; + return(p); + } + + /* Re-use spanner header. */ + if (TBL_HEAD_DATA != h->pos && + (TBL_CELL_VERT == p->pos || + TBL_CELL_DVERT == p->pos)) { + head_adjust(p, h); + p->head = h; + return(p); + } + + /* Right-shift headers with a new spanner. */ + if (TBL_HEAD_DATA == h->pos && + (TBL_CELL_VERT == p->pos || + TBL_CELL_DVERT == p->pos)) { + hp = mandoc_calloc(1, sizeof(struct tbl_head)); + hp->ident = tbl->opts.cols++; + hp->prev = h->prev; + if (h->prev) + h->prev->next = hp; + if (h == tbl->first_head) + tbl->first_head = hp; + h->prev = hp; + hp->next = h; + head_adjust(p, hp); + p->head = hp; + return(p); + } + + if (NULL != (h = h->next)) { + head_adjust(p, h); + p->head = h; + return(p); + } + + /* Fall through to default case... */ + } + + hp = mandoc_calloc(1, sizeof(struct tbl_head)); + hp->ident = tbl->opts.cols++; + + if (tbl->last_head) { + hp->prev = tbl->last_head; + tbl->last_head->next = hp; + tbl->last_head = hp; + } else + tbl->last_head = tbl->first_head = hp; + + head_adjust(p, hp); + p->head = hp; + return(p); +} + +static void +head_adjust(const struct tbl_cell *cell, struct tbl_head *head) +{ + if (TBL_CELL_VERT != cell->pos && + TBL_CELL_DVERT != cell->pos) { + head->pos = TBL_HEAD_DATA; + return; + } + + if (TBL_CELL_VERT == cell->pos) + if (TBL_HEAD_DVERT != head->pos) + head->pos = TBL_HEAD_VERT; + + if (TBL_CELL_DVERT == cell->pos) + head->pos = TBL_HEAD_DVERT; +} +