=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/mandoc/tbl.c,v retrieving revision 1.3 retrieving revision 1.4 diff -c -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 **** ! /* $Id: tbl.c,v 1.3 2010/10/15 22:07:12 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.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,546 **** * 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.h" ! #include "tbl_extern.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) { ! bzero(tbl, sizeof(struct tbl)); ! tbl->part = TBL_PART_OPTS; ! tbl->tab = '\t'; ! tbl->linesize = 12; ! tbl->decimal = '.'; ! 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]) tbl->part = TBL_PART_LAYOUT; switch (tbl->part) { case (TBL_PART_OPTS): ! return(tbl_option(tbl, f, ln, p)); ! case (TBL_PART_CLAYOUT): ! /* FALLTHROUGH */ 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); 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. */ ! ! 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); } ! ! int ! tbl_err(struct tbl *tbl) { ! (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); return(p); } - void ! tbl_free(struct tbl *p) { ! 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); } ! 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); } ! 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); } ! TAILQ_INIT(&p->cell); ! TAILQ_INSERT_TAIL(&tbl->row, p, entries); ! p->tbl = tbl; ! return(p); } ! ! static void ! headadj(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; ! } ! 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); } ! ! struct tbl_cell * ! tbl_cell_alloc(struct tbl_row *rp, enum tbl_cellt pos) { - 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); } ! ! struct tbl_data * ! tbl_data_alloc(struct tbl_span *sp) { - 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); ! } ! 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); ! 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); } - - 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); - } --- 14,159 ---- * 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 "roff.h" ! #include "libmandoc.h" ! #include "libroff.h" ! enum rofferr ! tbl_read(struct tbl_node *tbl, int ln, const char *p, int offs) { + int len; + const char *cp; ! cp = &p[offs]; ! len = (int)strlen(cp); ! /* ! * 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. ! */ ! 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, ln, p) ? ROFF_IGN : ROFF_ERR); case (TBL_PART_LAYOUT): ! 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; } /* ! * This only returns zero if the line is empty, so we ignore it ! * and continue on. */ ! return(tbl_data(tbl, ln, p) ? ROFF_TBL : ROFF_IGN); } ! struct tbl_node * ! tbl_alloc(int pos, int line, void *data, const mandocmsg msg) { + struct tbl_node *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_node *p) { + struct tbl_row *rp; + struct tbl_cell *cp; + struct tbl_span *sp; + struct tbl_dat *dp; + struct tbl_head *hp; ! 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); } ! 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); } ! while (NULL != (hp = p->first_head)) { ! p->first_head = hp->next; ! free(hp); } ! free(p); } ! void ! tbl_restart(int line, int pos, struct tbl_node *tbl) { ! 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; ! if (NULL == tbl->first_span || NULL == tbl->first_span->first) ! TBL_MSG(tbl, MANDOCERR_TBLNODATA, tbl->line, tbl->pos); } ! const struct tbl_span * ! tbl_span(const struct tbl_node *tbl) { ! assert(tbl); ! return(tbl->last_span); } ! void ! tbl_end(struct tbl_node *tbl) { ! if (NULL == tbl->first_span || NULL == tbl->first_span->first) ! TBL_MSG(tbl, MANDOCERR_TBLNODATA, tbl->line, tbl->pos); ! if (tbl->last_span) ! tbl->last_span->flags |= TBL_SPAN_LAST; ! if (TBL_PART_CDATA == tbl->part) ! TBL_MSG(tbl, MANDOCERR_TBLBLOCK, tbl->line, tbl->pos); }