=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/mandoc/tbl_layout.c,v retrieving revision 1.3 retrieving revision 1.4 diff -c -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 **** ! /* $Id: tbl_layout.c,v 1.3 2010/10/15 22:50:28 schwarze Exp $ */ /* ! * Copyright (c) 2009 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 --- 1,6 ---- ! /* $Id: tbl_layout.c,v 1.4 2011/01/04 22:28:17 schwarze Exp $ */ /* ! * 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,51 **** * 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 "out.h" ! #include "term.h" ! #include "tbl_extern.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 }, --- 14,43 ---- * 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 "mandoc.h" ! #include "libmandoc.h" ! #include "libroff.h" struct tbl_phrase { char name; enum tbl_cellt key; }; ! #define KEYS_MAX 11 static const struct tbl_phrase keys[KEYS_MAX] = { { 'c', TBL_CELL_CENTRE }, { 'r', TBL_CELL_RIGHT }, { 'l', TBL_CELL_LEFT }, { 'n', TBL_CELL_NUMBER }, { 's', TBL_CELL_SPAN }, { 'a', TBL_CELL_LONG }, { '^', TBL_CELL_DOWN }, { '-', TBL_CELL_HORIZ }, { '_', TBL_CELL_HORIZ }, *************** *** 53,243 **** { '|', 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 *, int, const char *, int *); - static int ! mods(struct tbl *tbl, struct tbl_cell *cp, const char *p, ! int pp, const char *f, int ln, int pos) { char buf[5]; int i; /* * 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]) return(1); /* Parse numerical spacing from modifier string. */ ! if (isdigit((u_char)p[pp])) { for (i = 0; i < 4; i++) { ! if ( ! isdigit((u_char)p[pp + i])) break; ! buf[i] = p[pp + i]; } ! 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)) return(0); ! ! /* Continue parsing modifiers. */ ! return(mods(tbl, cp, p, pp + i, f, ln, pos)); } /* TODO: GNU has many more extensions. */ ! switch (p[pp]) { 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)); case ('u'): - /* FALLTHROUGH */ - case ('U'): cp->flags |= TBL_CELL_UP; ! return(mods(tbl, cp, p, pp + 1, f, ln, pos)); case ('e'): - /* FALLTHROUGH */ - case ('E'): cp->flags |= TBL_CELL_EQUAL; ! return(mods(tbl, cp, p, pp + 1, f, ln, pos)); case ('t'): - /* FALLTHROUGH */ - case ('T'): cp->flags |= TBL_CELL_TALIGN; ! return(mods(tbl, cp, p, pp + 1, f, ln, pos)); case ('d'): - /* FALLTHROUGH */ - case ('D'): cp->flags |= TBL_CELL_BALIGN; ! return(mods(tbl, cp, p, pp + 1, f, ln, pos)); case ('f'): ! pp++; ! /* FALLTHROUGH */ ! case ('B'): ! /* FALLTHROUGH */ ! case ('I'): ! /* FALLTHROUGH */ case ('b'): /* FALLTHROUGH */ case ('i'): break; default: ! return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos + pp)); } ! switch (p[pp]) { case ('b'): - /* FALLTHROUGH */ - case ('B'): cp->flags |= TBL_CELL_BOLD; ! return(mods(tbl, cp, p, pp + 1, f, ln, pos)); case ('i'): - /* FALLTHROUGH */ - case ('I'): cp->flags |= TBL_CELL_ITALIC; ! return(mods(tbl, cp, p, pp + 1, f, ln, pos)); default: break; } ! return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos + pp)); } - static int ! cell(struct tbl *tbl, struct tbl_row *rp, ! const char *f, int ln, int pos) { ! struct tbl_cell *cp; ! const char *p; ! int j, 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; } ! if (i == KEYS_MAX) ! return(tbl_errx(tbl, ERR_SYNTAX, f, ln, pos)); /* Extra check for the double-vertical. */ ! if (TBL_CELL_VERT == c && '|' == p[1]) { ! j = 2; c = TBL_CELL_DVERT; ! } else ! j = 1; ! /* Disallow subsequent spacers. */ ! /* LINTED */ ! cp = TAILQ_LAST(&rp->cell, tbl_cellh); - 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)); } ! static int ! row(struct tbl *tbl, const char *f, int ln, ! const char *p, int *pos) { struct tbl_row *rp; - int sv; ! rp = tbl_row_alloc(tbl); ! again: ! sv = *pos; ! ! /* * EBNF describing this section: * * row ::= row_list [:space:]* [.]?[\n] --- 45,207 ---- { '|', TBL_CELL_VERT } }; ! 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_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. */ ! 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 (tolower(p[(*pos)++])) { case ('z'): cp->flags |= TBL_CELL_WIGN; ! goto mod; case ('u'): cp->flags |= TBL_CELL_UP; ! goto mod; case ('e'): cp->flags |= TBL_CELL_EQUAL; ! goto mod; case ('t'): cp->flags |= TBL_CELL_TALIGN; ! goto mod; case ('d'): cp->flags |= TBL_CELL_BALIGN; ! goto mod; case ('f'): ! break; case ('b'): /* FALLTHROUGH */ case ('i'): + (*pos)--; break; default: ! TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1); ! return(0); } ! switch (tolower(p[(*pos)++])) { case ('b'): cp->flags |= TBL_CELL_BOLD; ! goto mod; 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_node *tbl, struct tbl_row *rp, ! int ln, const char *p, int *pos) { ! int i; enum tbl_cellt c; /* Parse the column position (`r', `R', `|', ...). */ ! 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); } ! (*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. */ ! return(mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos)); } ! static void ! row(struct tbl_node *tbl, int ln, const char *p, int *pos) { struct tbl_row *rp; ! row: /* * EBNF describing this section: * * row ::= row_list [:space:]* [.]?[\n] *************** *** 247,287 **** * 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)); 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)); } ! return(1); ! } int ! tbl_layout(struct tbl *tbl, const char *f, int ln, const char *p) { int pos; pos = 0; ! return(row(tbl, f, ln, p, &pos)); } --- 211,375 ---- * row_elem ::= [\t\ ]*[:alpha:]+ */ ! 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; ! if (NULL == tbl->first_row) ! 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_node *tbl, int ln, const char *p) { int pos; pos = 0; ! 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; + } +