=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/mandoc/tbl.c,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- src/usr.bin/mandoc/tbl.c 2010/10/15 22:07:12 1.3 +++ src/usr.bin/mandoc/tbl.c 2011/01/04 22:28:17 1.4 @@ -1,6 +1,6 @@ -/* $Id: tbl.c,v 1.3 2010/10/15 22:07:12 schwarze Exp $ */ +/* $Id: tbl.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,533 +14,146 @@ * 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 -#include "out.h" -#include "term.h" -#include "tbl.h" -#include "tbl_extern.h" +#include "mandoc.h" +#include "roff.h" +#include "libmandoc.h" +#include "libroff.h" - -const char *const errnames[ERR_MAX] = { - "bad syntax", /* ERR_SYNTAX */ - "bad option" /* ERR_OPTION */ -}; - -static char buf[1024]; /* XXX */ - -static enum tbl_tok tbl_next_char(char); -static void tbl_init(struct tbl *); -static void tbl_clear(struct tbl *); -static struct tbl_head *tbl_head_alloc(struct tbl *); -static void tbl_span_free(struct tbl_span *); -static void tbl_data_free(struct tbl_data *); -static void tbl_row_free(struct tbl_row *); - -static void headadj(const struct tbl_cell *, - struct tbl_head *); - -static void -tbl_init(struct tbl *tbl) +enum rofferr +tbl_read(struct tbl_node *tbl, int ln, const char *p, int offs) { + int len; + const char *cp; - bzero(tbl, sizeof(struct tbl)); + cp = &p[offs]; + len = (int)strlen(cp); - tbl->part = TBL_PART_OPTS; - tbl->tab = '\t'; - tbl->linesize = 12; - tbl->decimal = '.'; + /* + * If we're in the options section and we don't have a + * terminating semicolon, assume we've moved directly into the + * layout section. No need to report a warning: this is, + * apparently, standard behaviour. + */ - TAILQ_INIT(&tbl->span); - TAILQ_INIT(&tbl->row); - TAILQ_INIT(&tbl->head); -} - - -int -tbl_read(struct tbl *tbl, const char *f, int ln, const char *p, int len) -{ - - if (len && TBL_PART_OPTS == tbl->part) - if (';' != p[len - 1]) + if (TBL_PART_OPTS == tbl->part && len) + if (';' != cp[len - 1]) tbl->part = TBL_PART_LAYOUT; + /* Now process each logical section of the table. */ + switch (tbl->part) { case (TBL_PART_OPTS): - return(tbl_option(tbl, f, ln, p)); - case (TBL_PART_CLAYOUT): - /* FALLTHROUGH */ + return(tbl_option(tbl, ln, p) ? ROFF_IGN : ROFF_ERR); case (TBL_PART_LAYOUT): - return(tbl_layout(tbl, f, ln, p)); - case (TBL_PART_DATA): - return(tbl_data(tbl, f, ln, p)); - case (TBL_PART_ERROR): - break; - } - - return(0); -} - - -int -tbl_close(struct termp *p, struct tbl *tbl, const char *f, int ln) -{ - - if (TBL_PART_DATA != tbl->part) - return(tbl_errx(tbl, ERR_SYNTAX, f, ln, 0)); - if ( ! tbl_data_close(tbl, f, ln)) - return(0); -#if 1 - return(tbl_calc_term(p, tbl)); -#else - return(tbl_calc_tree(tbl)); -#endif -} - - -int -tbl_write(struct termp *p, const struct tbl *tbl) -{ - -#if 1 - return(tbl_write_term(p, tbl)); -#else - return(tbl_write_tree(tbl)); -#endif -} - - -static enum tbl_tok -tbl_next_char(char c) -{ - - /* - * These are delimiting tokens. They separate out words in the - * token stream. - */ - - switch (c) { - case ('('): - return(TBL_TOK_OPENPAREN); - case (')'): - return(TBL_TOK_CLOSEPAREN); - case (' '): - return(TBL_TOK_SPACE); - case ('\t'): - return(TBL_TOK_TAB); - case (';'): - return(TBL_TOK_SEMICOLON); - case ('.'): - return(TBL_TOK_PERIOD); - case (','): - return(TBL_TOK_COMMA); - case (0): - return(TBL_TOK_NIL); + return(tbl_layout(tbl, ln, p) ? ROFF_IGN : ROFF_ERR); + case (TBL_PART_CDATA): + return(tbl_cdata(tbl, ln, p) ? ROFF_TBL : ROFF_IGN); default: break; } - return(TBL_TOK_WORD); -} - - -const char * -tbl_last(void) -{ - - return(buf); -} - - -int -tbl_last_uint(void) -{ - char *ep; - long lval; - - /* From OpenBSD's strtol(3). Gross. */ - - errno = 0; - lval = strtol(buf, &ep, 10); - if (buf[0] == 0 || *ep != 0) - return(-1); - if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) - return(-1); - if (lval < 0 || lval > INT_MAX) - return(-1); - - return((int)lval); -} - - -enum tbl_tok -tbl_next(const char *p, int *pos) -{ - int i; - enum tbl_tok c; - - buf[0] = 0; - - if (TBL_TOK_WORD != (c = tbl_next_char(p[*pos]))) { - if (TBL_TOK_NIL != c) { - buf[0] = p[*pos]; - buf[1] = 0; - (*pos)++; - } - return(c); - } - /* - * Copy words into a nil-terminated buffer. For now, we use a - * static buffer. Eventually this should be made into a dynamic - * one living in struct tbl. + * This only returns zero if the line is empty, so we ignore it + * and continue on. */ - - for (i = 0; i < 1023; i++, (*pos)++) - if (TBL_TOK_WORD == tbl_next_char(p[*pos])) - buf[i] = p[*pos]; - else - break; - - assert(i < 1023); - buf[i] = 0; - - return(TBL_TOK_WORD); + return(tbl_data(tbl, ln, p) ? ROFF_TBL : ROFF_IGN); } - -int -tbl_err(struct tbl *tbl) +struct tbl_node * +tbl_alloc(int pos, int line, void *data, const mandocmsg msg) { + struct tbl_node *p; - (void)fprintf(stderr, "%s\n", strerror(errno)); - tbl->part = TBL_PART_ERROR; - return(0); -} - - -/* ARGSUSED */ -int -tbl_warnx(struct tbl *tbl, enum tbl_err tok, - const char *f, int line, int pos) -{ - - (void)fprintf(stderr, "%s:%d:%d: %s\n", - f, line, pos + 1, errnames[tok]); - - /* TODO: -Werror */ - return(1); -} - - -int -tbl_errx(struct tbl *tbl, enum tbl_err tok, - const char *f, int line, int pos) -{ - - (void)fprintf(stderr, "%s:%d:%d: %s\n", - f, line, pos + 1, errnames[tok]); - - tbl->part = TBL_PART_ERROR; - return(0); -} - - -struct tbl * -tbl_alloc(void) -{ - struct tbl *p; - - if (NULL == (p = malloc(sizeof(struct tbl)))) - return(NULL); - - tbl_init(p); + p = mandoc_calloc(1, sizeof(struct tbl_node)); + p->line = line; + p->pos = pos; + p->data = data; + p->msg = msg; + p->part = TBL_PART_OPTS; + p->opts.tab = '\t'; + p->opts.linesize = 12; + p->opts.decimal = '.'; return(p); } - void -tbl_free(struct tbl *p) +tbl_free(struct tbl_node *p) { + struct tbl_row *rp; + struct tbl_cell *cp; + struct tbl_span *sp; + struct tbl_dat *dp; + struct tbl_head *hp; - tbl_clear(p); - free(p); -} - - -void -tbl_reset(struct tbl *tbl) -{ - - tbl_clear(tbl); - tbl_init(tbl); -} - - -struct tbl_span * -tbl_span_alloc(struct tbl *tbl) -{ - struct tbl_span *p, *pp; - struct tbl_row *row; - - if (NULL == (p = calloc(1, sizeof(struct tbl_span)))) { - (void)tbl_err(tbl); - return(NULL); + while (NULL != (rp = p->first_row)) { + p->first_row = rp->next; + while (rp->first) { + cp = rp->first; + rp->first = cp->next; + free(cp); + } + free(rp); } - TAILQ_INIT(&p->data); - TAILQ_INSERT_TAIL(&tbl->span, p, entries); - - /* LINTED */ - pp = TAILQ_PREV(p, tbl_spanh, entries); - - if (pp) { - row = TAILQ_NEXT(pp->row, entries); - if (NULL == row) - row = pp->row; - } else { - row = TAILQ_FIRST(&tbl->row); + while (NULL != (sp = p->first_span)) { + p->first_span = sp->next; + while (sp->first) { + dp = sp->first; + sp->first = dp->next; + if (dp->string) + free(dp->string); + free(dp); + } + free(sp); } - assert(row); - p->row = row; - p->tbl = tbl; - return(p); -} - - -struct tbl_row * -tbl_row_alloc(struct tbl *tbl) -{ - struct tbl_row *p; - - if (NULL == (p = calloc(1, sizeof(struct tbl_row)))) { - (void)tbl_err(tbl); - return(NULL); + while (NULL != (hp = p->first_head)) { + p->first_head = hp->next; + free(hp); } - TAILQ_INIT(&p->cell); - TAILQ_INSERT_TAIL(&tbl->row, p, entries); - p->tbl = tbl; - return(p); + free(p); } - -static void -headadj(const struct tbl_cell *cell, struct tbl_head *head) +void +tbl_restart(int line, int pos, struct tbl_node *tbl) { - 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; -} + if (TBL_PART_CDATA == tbl->part) + TBL_MSG(tbl, MANDOCERR_TBLBLOCK, tbl->line, tbl->pos); + tbl->part = TBL_PART_LAYOUT; + tbl->line = line; + tbl->pos = pos; -static struct tbl_head * -tbl_head_alloc(struct tbl *tbl) -{ - struct tbl_head *p; - - if (NULL == (p = calloc(1, sizeof(struct tbl_head)))) { - (void)tbl_err(tbl); - return(NULL); - } - p->tbl = tbl; - return(p); + if (NULL == tbl->first_span || NULL == tbl->first_span->first) + TBL_MSG(tbl, MANDOCERR_TBLNODATA, tbl->line, tbl->pos); } - -struct tbl_cell * -tbl_cell_alloc(struct tbl_row *rp, enum tbl_cellt pos) +const struct tbl_span * +tbl_span(const struct tbl_node *tbl) { - struct tbl_cell *p, *pp; - struct tbl_head *h, *hp; - if (NULL == (p = calloc(1, sizeof(struct tbl_cell)))) { - (void)tbl_err(rp->tbl); - return(NULL); - } - - TAILQ_INSERT_TAIL(&rp->cell, p, entries); - p->pos = pos; - p->row = rp; - - /* - * 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. - */ - - /* LINTED */ - pp = TAILQ_PREV(p, tbl_cellh, entries); - - h = pp ? TAILQ_NEXT(pp->head, entries) : - TAILQ_FIRST(&rp->tbl->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)) { - headadj(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)) { - if (NULL == (hp = tbl_head_alloc(rp->tbl))) - return(NULL); - TAILQ_INSERT_BEFORE(h, hp, entries); - headadj(p, hp); - p->head = hp; - return(p); - } - - h = TAILQ_NEXT(h, entries); - if (h) { - headadj(p, h); - p->head = h; - return(p); - } - - /* Fall through to default case... */ - } - - if (NULL == (hp = tbl_head_alloc(rp->tbl))) - return(NULL); - TAILQ_INSERT_TAIL(&rp->tbl->head, hp, entries); - headadj(p, hp); - p->head = hp; - return(p); + assert(tbl); + return(tbl->last_span); } - -struct tbl_data * -tbl_data_alloc(struct tbl_span *sp) +void +tbl_end(struct tbl_node *tbl) { - struct tbl_data *p; - struct tbl_cell *cp; - struct tbl_data *dp; - if (NULL == (p = calloc(1, sizeof(struct tbl_data)))) { - (void)tbl_err(sp->row->tbl); - return(NULL); - } + if (NULL == tbl->first_span || NULL == tbl->first_span->first) + TBL_MSG(tbl, MANDOCERR_TBLNODATA, tbl->line, tbl->pos); - cp = NULL; - /* LINTED */ - if (NULL == (dp = TAILQ_LAST(&sp->data, tbl_datah))) - cp = TAILQ_FIRST(&sp->row->cell); - else if (dp->cell) - cp = TAILQ_NEXT(dp->cell, entries); + if (tbl->last_span) + tbl->last_span->flags |= TBL_SPAN_LAST; - TAILQ_INSERT_TAIL(&sp->data, p, entries); - - if (cp && (TBL_CELL_VERT == cp->pos || - TBL_CELL_DVERT == cp->pos)) - cp = TAILQ_NEXT(cp, entries); - - p->span = sp; - p->cell = cp; - return(p); + if (TBL_PART_CDATA == tbl->part) + TBL_MSG(tbl, MANDOCERR_TBLBLOCK, tbl->line, tbl->pos); } - -static void -tbl_clear(struct tbl *p) -{ - struct tbl_span *span; - struct tbl_head *head; - struct tbl_row *row; - - /* LINTED */ - while ((span = TAILQ_FIRST(&p->span))) { - TAILQ_REMOVE(&p->span, span, entries); - tbl_span_free(span); - } - /* LINTED */ - while ((row = TAILQ_FIRST(&p->row))) { - TAILQ_REMOVE(&p->row, row, entries); - tbl_row_free(row); - } - /* LINTED */ - while ((head = TAILQ_FIRST(&p->head))) { - TAILQ_REMOVE(&p->head, head, entries); - free(head); - } -} - - -static void -tbl_span_free(struct tbl_span *p) -{ - struct tbl_data *data; - - /* LINTED */ - while ((data = TAILQ_FIRST(&p->data))) { - TAILQ_REMOVE(&p->data, data, entries); - tbl_data_free(data); - } - free(p); -} - - -static void -tbl_data_free(struct tbl_data *p) -{ - - if (p->string) - free(p->string); - free(p); -} - - -static void -tbl_row_free(struct tbl_row *p) -{ - struct tbl_cell *cell; - - /* LINTED */ - while ((cell = TAILQ_FIRST(&p->cell))) { - TAILQ_REMOVE(&p->cell, cell, entries); - free(cell); - } - free(p); -}