diff options
author | Ingo Schwarze <schwarze@cvs.openbsd.org> | 2010-10-15 19:20:04 +0000 |
---|---|---|
committer | Ingo Schwarze <schwarze@cvs.openbsd.org> | 2010-10-15 19:20:04 +0000 |
commit | 07117bb546ee4fe37197bfe24a605edc6f7e56ad (patch) | |
tree | 68875810f89effd737a1c912c9c800cfb08c9117 /usr.bin/mandoc/tbl.c | |
parent | 90b90d5f2aa014db30eee069d0d5ff207a1af4ef (diff) |
Import tbl parser and renderer written by kristaps@.
Unchanged code from bsd.lv release 0.1.5, but without the main program.
Not yet linked to the build; next commit will integrate it into mandoc.
Diffstat (limited to 'usr.bin/mandoc/tbl.c')
-rw-r--r-- | usr.bin/mandoc/tbl.c | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/usr.bin/mandoc/tbl.c b/usr.bin/mandoc/tbl.c new file mode 100644 index 00000000000..b0e5d6b951e --- /dev/null +++ b/usr.bin/mandoc/tbl.c @@ -0,0 +1,544 @@ +/* $Id: tbl.c,v 1.1 2010/10/15 19:20:03 schwarze Exp $ */ +/* + * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se> + * + * 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 <sys/queue.h> + +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.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 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(tbl)); +#else + return(tbl_calc_tree(tbl)); +#endif +} + + +int +tbl_write(const struct tbl *tbl) +{ + +#if 1 + return(tbl_write_term(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); +} |