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 | |
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.
-rw-r--r-- | usr.bin/mandoc/tbl.c | 544 | ||||
-rw-r--r-- | usr.bin/mandoc/tbl.h | 34 | ||||
-rw-r--r-- | usr.bin/mandoc/tbl_data.c | 130 | ||||
-rw-r--r-- | usr.bin/mandoc/tbl_extern.h | 183 | ||||
-rw-r--r-- | usr.bin/mandoc/tbl_layout.c | 280 | ||||
-rw-r--r-- | usr.bin/mandoc/tbl_option.c | 195 | ||||
-rw-r--r-- | usr.bin/mandoc/tbl_term.c | 504 | ||||
-rw-r--r-- | usr.bin/mandoc/tbl_tree.c | 86 |
8 files changed, 1956 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); +} diff --git a/usr.bin/mandoc/tbl.h b/usr.bin/mandoc/tbl.h new file mode 100644 index 00000000000..d1830453511 --- /dev/null +++ b/usr.bin/mandoc/tbl.h @@ -0,0 +1,34 @@ +/* $Id: tbl.h,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. + */ +#ifndef TBL_H +#define TBL_H + +__BEGIN_DECLS + +struct tbl; + +struct tbl *tbl_alloc(void); +void tbl_free(struct tbl *); +void tbl_reset(struct tbl *); + +int tbl_read(struct tbl *, const char *, int, const char *, int); +int tbl_close(struct tbl *, const char *, int); +int tbl_write(const struct tbl *); + +__END_DECLS + +#endif /*TBL_H*/ diff --git a/usr.bin/mandoc/tbl_data.c b/usr.bin/mandoc/tbl_data.c new file mode 100644 index 00000000000..de900cb936b --- /dev/null +++ b/usr.bin/mandoc/tbl_data.c @@ -0,0 +1,130 @@ +/* $Id: tbl_data.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 <sys/types.h> + +#include <assert.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "tbl_extern.h" + +/* FIXME: warn about losing data contents if cell is HORIZ. */ + +static int data(struct tbl *, struct tbl_span *, + const char *, int, int, + const char *, int, int); + + +int +data(struct tbl *tbl, struct tbl_span *dp, + const char *f, int ln, int pos, + const char *p, int start, int end) +{ + struct tbl_data *dat; + + if (NULL == (dat = tbl_data_alloc(dp))) + return(0); + + if (NULL == dat->cell) + if ( ! tbl_warnx(tbl, ERR_SYNTAX, f, ln, pos)) + return(0); + + assert(end >= start); + if (NULL == (dat->string = malloc((size_t)(end - start + 1)))) + return(tbl_err(tbl)); + + (void)memcpy(dat->string, &p[start], (size_t)(end - start)); + dat->string[end - start] = 0; + + /* XXX: do the strcmps, then malloc(). */ + + if ( ! strcmp(dat->string, "_")) + dat->flags |= TBL_DATA_HORIZ; + else if ( ! strcmp(dat->string, "=")) + dat->flags |= TBL_DATA_DHORIZ; + else if ( ! strcmp(dat->string, "\\_")) + dat->flags |= TBL_DATA_NHORIZ; + else if ( ! strcmp(dat->string, "\\=")) + dat->flags |= TBL_DATA_NDHORIZ; + else + return(1); + + free(dat->string); + dat->string = NULL; + return(1); +} + + +int +tbl_data(struct tbl *tbl, const char *f, int ln, const char *p) +{ + struct tbl_span *dp; + int i, j; + + if (0 == p[0]) + return(tbl_errx(tbl, ERR_SYNTAX, f, ln, 0)); + + if ('.' == p[0] && ! isdigit((u_char)p[1])) { + /* + * XXX: departs from tbl convention in that we disallow + * macros in the data body. + */ + if (strncasecmp(p, ".T&", 3)) + return(tbl_errx(tbl, ERR_SYNTAX, f, ln, 0)); + return(tbl_data_close(tbl, f, ln)); + } + + if (NULL == (dp = tbl_span_alloc(tbl))) + return(0); + + if ( ! strcmp(p, "_")) { + dp->flags |= TBL_SPAN_HORIZ; + return(1); + } else if ( ! strcmp(p, "=")) { + dp->flags |= TBL_SPAN_DHORIZ; + return(1); + } + + for (j = i = 0; p[i]; i++) { + if (p[i] != tbl->tab) + continue; + if ( ! data(tbl, dp, f, ln, i, p, j, i)) + return(0); + j = i + 1; + } + + return(data(tbl, dp, f, ln, i, p, j, i)); +} + + +int +tbl_data_close(struct tbl *tbl, const char *f, int ln) +{ + struct tbl_span *span; + + /* LINTED */ + span = TAILQ_LAST(&tbl->span, tbl_spanh); + if (NULL == span) + return(tbl_errx(tbl, ERR_SYNTAX, f, ln, 0)); + if (TAILQ_NEXT(span->row, entries)) + return(tbl_errx(tbl, ERR_SYNTAX, f, ln, 0)); + + tbl->part = TBL_PART_LAYOUT; + return(1); +} diff --git a/usr.bin/mandoc/tbl_extern.h b/usr.bin/mandoc/tbl_extern.h new file mode 100644 index 00000000000..123253e9b10 --- /dev/null +++ b/usr.bin/mandoc/tbl_extern.h @@ -0,0 +1,183 @@ +/* $Id: tbl_extern.h,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. + */ +#ifndef TBL_EXTERN_H +#define TBL_EXTERN_H + +enum tbl_err { + ERR_SYNTAX, + ERR_OPTION, + ERR_MAX +}; + +enum tbl_tok { + TBL_TOK_WORD, + TBL_TOK_OPENPAREN, + TBL_TOK_CLOSEPAREN, + TBL_TOK_COMMA, + TBL_TOK_SEMICOLON, + TBL_TOK_PERIOD, + TBL_TOK_SPACE, + TBL_TOK_TAB, + TBL_TOK_NIL +}; + +enum tbl_part { + TBL_PART_OPTS, + TBL_PART_LAYOUT, + TBL_PART_CLAYOUT, + TBL_PART_DATA, + TBL_PART_ERROR +}; + +struct tbl; +struct tbl_head; +struct tbl_row; +struct tbl_cell; +struct tbl_span; +struct tbl_data; + +TAILQ_HEAD(tbl_rowh, tbl_row); +TAILQ_HEAD(tbl_cellh, tbl_cell); +TAILQ_HEAD(tbl_headh, tbl_head); +TAILQ_HEAD(tbl_spanh, tbl_span); +TAILQ_HEAD(tbl_datah, tbl_data); + +struct tbl { + enum tbl_part part; + int opts; +#define TBL_OPT_CENTRE (1 << 0) +#define TBL_OPT_EXPAND (1 << 1) +#define TBL_OPT_BOX (1 << 2) +#define TBL_OPT_DBOX (1 << 3) +#define TBL_OPT_ALLBOX (1 << 4) +#define TBL_OPT_NOKEEP (1 << 5) +#define TBL_OPT_NOSPACE (1 << 6) + char tab; + char decimal; + int linesize; + char delims[2]; + struct tbl_spanh span; + struct tbl_headh head; + struct tbl_rowh row; +}; + +enum tbl_headt { + TBL_HEAD_DATA, + TBL_HEAD_VERT, + TBL_HEAD_DVERT, + TBL_HEAD_MAX +}; + +struct tbl_head { + struct tbl *tbl; + enum tbl_headt pos; + int width; + int decimal; + TAILQ_ENTRY(tbl_head) entries; +}; + +struct tbl_row { + struct tbl *tbl; + struct tbl_cellh cell; + TAILQ_ENTRY(tbl_row) entries; +}; + +enum tbl_cellt { + TBL_CELL_CENTRE, /* c, C */ + TBL_CELL_RIGHT, /* r, R */ + TBL_CELL_LEFT, /* l, L */ + TBL_CELL_NUMBER, /* n, N */ + TBL_CELL_SPAN, /* s, S */ + TBL_CELL_LONG, /* a, A */ + TBL_CELL_DOWN, /* ^ */ + TBL_CELL_HORIZ, /* _, - */ + TBL_CELL_DHORIZ, /* = */ + TBL_CELL_VERT, /* | */ + TBL_CELL_DVERT, /* || */ + TBL_CELL_MAX +}; + +struct tbl_cell { + struct tbl_row *row; + struct tbl_head *head; + enum tbl_cellt pos; + int spacing; + int flags; +#define TBL_CELL_TALIGN (1 << 0) /* t, T */ +#define TBL_CELL_BALIGN (1 << 1) /* d, D */ +#define TBL_CELL_BOLD (1 << 2) /* fB, B, b */ +#define TBL_CELL_ITALIC (1 << 3) /* fI, I, i */ +#define TBL_CELL_EQUAL (1 << 4) /* e, E */ +#define TBL_CELL_UP (1 << 5) /* u, U */ +#define TBL_CELL_WIGN (1 << 6) /* z, Z */ + TAILQ_ENTRY(tbl_cell) entries; +}; + +struct tbl_data { + struct tbl_span *span; + struct tbl_cell *cell; + int flags; +#define TBL_DATA_HORIZ (1 << 0) +#define TBL_DATA_DHORIZ (1 << 1) +#define TBL_DATA_NHORIZ (1 << 2) +#define TBL_DATA_NDHORIZ (1 << 3) + char *string; + TAILQ_ENTRY(tbl_data) entries; +}; + +struct tbl_span { + struct tbl_row *row; + struct tbl *tbl; + int flags; +#define TBL_SPAN_HORIZ (1 << 0) +#define TBL_SPAN_DHORIZ (1 << 1) + struct tbl_datah data; + TAILQ_ENTRY(tbl_span) entries; +}; + +__BEGIN_DECLS + +int tbl_option(struct tbl *, + const char *, int, const char *); +int tbl_layout(struct tbl *, + const char *, int, const char *); +int tbl_data(struct tbl *, + const char *, int, const char *); +int tbl_data_close(struct tbl *, const char *, int); + +enum tbl_tok tbl_next(const char *, int *); +const char *tbl_last(void); +int tbl_last_uint(void); +int tbl_errx(struct tbl *, enum tbl_err, + const char *, int, int); +int tbl_warnx(struct tbl *, enum tbl_err, + const char *, int, int); +int tbl_err(struct tbl *); + +struct tbl_row *tbl_row_alloc(struct tbl *); +struct tbl_cell *tbl_cell_alloc(struct tbl_row *, enum tbl_cellt); +struct tbl_span *tbl_span_alloc(struct tbl *); +struct tbl_data *tbl_data_alloc(struct tbl_span *); + +int tbl_write_term(const struct tbl *); +int tbl_calc_term(struct tbl *); +int tbl_write_tree(const struct tbl *); +int tbl_calc_tree(struct tbl *); + +__END_DECLS + +#endif /*TBL_EXTERN_H*/ diff --git a/usr.bin/mandoc/tbl_layout.c b/usr.bin/mandoc/tbl_layout.c new file mode 100644 index 00000000000..eb47c2b0026 --- /dev/null +++ b/usr.bin/mandoc/tbl_layout.c @@ -0,0 +1,280 @@ +/* $Id: tbl_layout.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 <sys/types.h> + +#include <assert.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.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 }, + { '=', TBL_CELL_DHORIZ }, + { '|', 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 ('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] + * row_list ::= [:space:]* row_elem row_tail + * row_tail ::= [:space:]*[,] row_list | + * epsilon + * 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)); +} diff --git a/usr.bin/mandoc/tbl_option.c b/usr.bin/mandoc/tbl_option.c new file mode 100644 index 00000000000..9362dd0c1bf --- /dev/null +++ b/usr.bin/mandoc/tbl_option.c @@ -0,0 +1,195 @@ +/* $Id: tbl_option.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 <stdlib.h> +#include <string.h> + +#include "tbl_extern.h" + +struct tbl_phrase { + char *name; + int key; + int ident; +#define KEY_CENTRE 0 +#define KEY_DELIM 1 +#define KEY_EXPAND 2 +#define KEY_BOX 3 +#define KEY_DBOX 4 +#define KEY_ALLBOX 5 +#define KEY_TAB 6 +#define KEY_LINESIZE 7 +#define KEY_NOKEEP 8 +#define KEY_DPOINT 9 +#define KEY_NOSPACE 10 +#define KEY_FRAME 11 +#define KEY_DFRAME 12 +}; + +#define KEY_MAXKEYS 14 + +static const struct tbl_phrase keys[KEY_MAXKEYS] = { + { "center", TBL_OPT_CENTRE, KEY_CENTRE}, + { "centre", TBL_OPT_CENTRE, KEY_CENTRE}, + { "delim", 0, KEY_DELIM}, + { "expand", TBL_OPT_EXPAND, KEY_EXPAND}, + { "box", TBL_OPT_BOX, KEY_BOX}, + { "doublebox", TBL_OPT_DBOX, KEY_DBOX}, + { "allbox", TBL_OPT_ALLBOX, KEY_ALLBOX}, + { "frame", TBL_OPT_BOX, KEY_FRAME}, + { "doubleframe", TBL_OPT_DBOX, KEY_DFRAME}, + { "tab", 0, KEY_TAB}, + { "linesize", 0, KEY_LINESIZE}, + { "nokeep", TBL_OPT_NOKEEP, KEY_NOKEEP}, + { "decimalpoint", 0, KEY_DPOINT}, + { "nospaces", TBL_OPT_NOSPACE, KEY_NOSPACE}, +}; + +static int arg(struct tbl *, const char *, + int, const char *, int *, int); +static int opt(struct tbl *, const char *, + int, const char *, int *); + +static int +arg(struct tbl *tbl, const char *f, int ln, + const char *p, int *pos, int key) +{ + const char *buf; + int sv; + +again: + sv = *pos; + + switch (tbl_next(p, pos)) { + case (TBL_TOK_OPENPAREN): + break; + case (TBL_TOK_SPACE): + /* FALLTHROUGH */ + case (TBL_TOK_TAB): + goto again; + default: + return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv)); + } + + sv = *pos; + + switch (tbl_next(p, pos)) { + case (TBL_TOK_WORD): + break; + default: + return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv)); + } + + buf = tbl_last(); + + switch (key) { + case (KEY_DELIM): + if (2 != strlen(buf)) + return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv)); + tbl->delims[0] = buf[0]; + tbl->delims[1] = buf[1]; + break; + case (KEY_TAB): + if (1 != strlen(buf)) + return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv)); + tbl->tab = buf[0]; + break; + case (KEY_LINESIZE): + if (-1 == (tbl->linesize = tbl_last_uint())) + return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv)); + break; + case (KEY_DPOINT): + if (1 != strlen(buf)) + return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv)); + tbl->decimal = buf[0]; + break; + default: + abort(); + } + + sv = *pos; + + switch (tbl_next(p, pos)) { + case (TBL_TOK_CLOSEPAREN): + break; + default: + return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv)); + } + + return(1); +} + + +static int +opt(struct tbl *tbl, const char *f, int ln, const char *p, int *pos) +{ + int i, sv; + +again: + sv = *pos; + + /* + * EBNF describing this section: + * + * options ::= option_list [:space:]* [;][\n] + * option_list ::= option option_tail + * option_tail ::= [:space:]+ option_list | + * ::= epsilon + * option ::= [:alpha:]+ args + * args ::= [:space:]* [(] [:alpha:]+ [)] + */ + + switch (tbl_next(p, pos)) { + case (TBL_TOK_WORD): + break; + case (TBL_TOK_SPACE): + /* FALLTHROUGH */ + case (TBL_TOK_TAB): + goto again; + case (TBL_TOK_SEMICOLON): + tbl->part = TBL_PART_LAYOUT; + return(1); + default: + return(tbl_errx(tbl, ERR_SYNTAX, f, ln, sv)); + } + + for (i = 0; i < KEY_MAXKEYS; i++) { + if (strcasecmp(tbl_last(), keys[i].name)) + continue; + if (keys[i].key) + tbl->opts |= keys[i].key; + else if ( ! arg(tbl, f, ln, p, pos, keys[i].ident)) + return(0); + + break; + } + + if (KEY_MAXKEYS == i) + return(tbl_errx(tbl, ERR_OPTION, f, ln, sv)); + + return(opt(tbl, f, ln, p, pos)); +} + + +int +tbl_option(struct tbl *tbl, const char *f, int ln, const char *p) +{ + int pos; + + pos = 0; + return(opt(tbl, f, ln, p, &pos)); +} diff --git a/usr.bin/mandoc/tbl_term.c b/usr.bin/mandoc/tbl_term.c new file mode 100644 index 00000000000..9548e11098e --- /dev/null +++ b/usr.bin/mandoc/tbl_term.c @@ -0,0 +1,504 @@ +/* $Id: tbl_term.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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "tbl_extern.h" + +/* FIXME: `n' modifier doesn't always do the right thing. */ +/* FIXME: `n' modifier doesn't use the cell-spacing buffer. */ + +static void calc_data(struct tbl_data *); +static void calc_data_literal(struct tbl_data *); +static void calc_data_number(struct tbl_data *); +static void calc_data_spanner(struct tbl_data *); +static inline void write_char(char, int); +static void write_data(const struct tbl_data *, int); +static void write_data_literal(const struct tbl_data *, int); +static void write_data_number(const struct tbl_data *, int); +static void write_data_spanner(const struct tbl_data *, int); +static void write_hframe(const struct tbl *); +static void write_hrule(const struct tbl_span *); +static void write_spanner(const struct tbl_head *); +static void write_vframe(const struct tbl *); + + +int +tbl_write_term(const struct tbl *tbl) +{ + const struct tbl_span *span; + const struct tbl_data *data; + const struct tbl_head *head; + + /* + * Note that the absolute widths and decimal places for headers + * were set when tbl_calc_term was called. + */ + + /* First, write out our head horizontal frame. */ + + write_hframe(tbl); + + /* + * Iterate through each span, and inside, through the global + * headers. If the global header's a spanner, print it + * directly; if it's data, use the corresponding data in the + * span as the object to print. + */ + + TAILQ_FOREACH(span, &tbl->span, entries) { + write_vframe(tbl); + + /* Accomodate for the horizontal rule. */ + if (TBL_DATA_DHORIZ & span->flags || + TBL_DATA_HORIZ & span->flags) { + write_hrule(span); + write_vframe(tbl); + printf("\n"); + continue; + } + + data = TAILQ_FIRST(&span->data); + TAILQ_FOREACH(head, &tbl->head, entries) { + switch (head->pos) { + case (TBL_HEAD_VERT): + /* FALLTHROUGH */ + case (TBL_HEAD_DVERT): + write_spanner(head); + break; + case (TBL_HEAD_DATA): + write_data(data, head->width); + if (data) + data = TAILQ_NEXT(data, entries); + break; + default: + abort(); + /* NOTREACHED */ + } + } + write_vframe(tbl); + printf("\n"); + } + + /* Last, write out our tail horizontal frame. */ + + write_hframe(tbl); + + return(1); +} + + +int +tbl_calc_term(struct tbl *tbl) +{ + struct tbl_span *span; + struct tbl_data *data; + struct tbl_head *head; + + /* Calculate width as the max of column cells' widths. */ + + TAILQ_FOREACH(span, &tbl->span, entries) { + if (TBL_DATA_HORIZ & span->flags) + continue; + if (TBL_DATA_DHORIZ & span->flags) + continue; + if (TBL_DATA_NHORIZ & span->flags) + continue; + if (TBL_DATA_NDHORIZ & span->flags) + continue; + TAILQ_FOREACH(data, &span->data, entries) + calc_data(data); + } + + /* Calculate width as the simple spanner value. */ + + TAILQ_FOREACH(head, &tbl->head, entries) + switch (head->pos) { + case (TBL_HEAD_VERT): + head->width = 1; + break; + case (TBL_HEAD_DVERT): + head->width = 2; + break; + default: + break; + } + + return(1); +} + + +static void +write_hrule(const struct tbl_span *span) +{ + const struct tbl_head *head; + char c; + + /* + * An hrule extends across the entire table and is demarked by a + * standalone `_' or whatnot in lieu of a table row. Spanning + * headers are marked by a `+', as are table boundaries. + */ + + c = '-'; + if (TBL_SPAN_DHORIZ & span->flags) + c = '='; + + /* FIXME: don't use `+' between data and a spanner! */ + + TAILQ_FOREACH(head, &span->tbl->head, entries) { + switch (head->pos) { + case (TBL_HEAD_DATA): + write_char(c, head->width); + break; + case (TBL_HEAD_DVERT): + write_char('+', head->width); + /* FALLTHROUGH */ + case (TBL_HEAD_VERT): + write_char('+', head->width); + break; + default: + abort(); + /* NOTREACHED */ + } + } +} + + +static void +write_hframe(const struct tbl *tbl) +{ + const struct tbl_head *head; + + if ( ! (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts)) + return; + + /* + * Print out the horizontal part of a frame or double frame. A + * double frame has an unbroken `-' outer line the width of the + * table, bordered by `+'. The frame (or inner frame, in the + * case of the double frame) is a `-' bordered by `+' and broken + * by `+' whenever a span is encountered. + */ + + if (TBL_OPT_DBOX & tbl->opts) { + printf("+"); + TAILQ_FOREACH(head, &tbl->head, entries) + write_char('-', head->width); + printf("+\n"); + } + + printf("+"); + TAILQ_FOREACH(head, &tbl->head, entries) { + switch (head->pos) { + case (TBL_HEAD_DATA): + write_char('-', head->width); + break; + default: + write_char('+', head->width); + break; + } + } + printf("+\n"); +} + + +static void +write_vframe(const struct tbl *tbl) +{ + /* Always just a single vertical line. */ + + if ( ! (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts)) + return; + printf("|"); +} + + +static void +calc_data_spanner(struct tbl_data *data) +{ + + /* N.B., these are horiz spanners (not vert) so always 1. */ + data->cell->head->width = 1; +} + + +static void +calc_data_number(struct tbl_data *data) +{ + int sz, d; + char *dp, pnt; + + /* + * First calculate number width and decimal place (last + 1 for + * no-decimal numbers). If the stored decimal is subsequent + * ours, make our size longer by that difference + * (right-"shifting"); similarly, if ours is subsequent the + * stored, then extend the stored size by the difference. + * Finally, re-assign the stored values. + */ + + /* TODO: use spacing modifier. */ + + assert(data->string); + sz = (int)strlen(data->string); + pnt = data->span->tbl->decimal; + + if (NULL == (dp = strchr(data->string, pnt))) + d = sz + 1; + else + d = (int)(dp - data->string) + 1; + + sz += 2; + + if (data->cell->head->decimal > d) { + sz += data->cell->head->decimal - d; + d = data->cell->head->decimal; + } else + data->cell->head->width += + d - data->cell->head->decimal; + + if (sz > data->cell->head->width) + data->cell->head->width = sz; + if (d > data->cell->head->decimal) + data->cell->head->decimal = d; +} + + +static void +calc_data_literal(struct tbl_data *data) +{ + int sz, bufsz; + + /* + * Calculate our width and use the spacing, with a minimum + * spacing dictated by position (centre, e.g,. gets a space on + * either side, while right/left get a single adjacent space). + */ + + assert(data->string); + sz = (int)strlen(data->string); + + switch (data->cell->pos) { + case (TBL_CELL_LONG): + /* FALLTHROUGH */ + case (TBL_CELL_CENTRE): + bufsz = 2; + break; + default: + bufsz = 1; + break; + } + + if (data->cell->spacing) + bufsz = bufsz > data->cell->spacing ? + bufsz : data->cell->spacing; + + sz += bufsz; + if (data->cell->head->width < sz) + data->cell->head->width = sz; +} + + +static void +calc_data(struct tbl_data *data) +{ + + switch (data->cell->pos) { + case (TBL_CELL_HORIZ): + /* FALLTHROUGH */ + case (TBL_CELL_DHORIZ): + calc_data_spanner(data); + break; + case (TBL_CELL_LONG): + /* FALLTHROUGH */ + case (TBL_CELL_CENTRE): + /* FALLTHROUGH */ + case (TBL_CELL_LEFT): + /* FALLTHROUGH */ + case (TBL_CELL_RIGHT): + calc_data_literal(data); + break; + case (TBL_CELL_NUMBER): + calc_data_number(data); + break; + default: + abort(); + /* NOTREACHED */ + } +} + + +static void +write_data_spanner(const struct tbl_data *data, int width) +{ + + /* + * Write spanners dictated by both our cell designation (in the + * layout) or as data. + */ + if (TBL_DATA_HORIZ & data->flags) + write_char('-', width); + else if (TBL_DATA_DHORIZ & data->flags) + write_char('=', width); + else if (TBL_CELL_HORIZ == data->cell->pos) + write_char('-', width); + else if (TBL_CELL_DHORIZ == data->cell->pos) + write_char('=', width); +} + + +static void +write_data_number(const struct tbl_data *data, int width) +{ + char *dp, pnt; + int d, padl, sz; + + /* + * See calc_data_number(). Left-pad by taking the offset of our + * and the maximum decimal; right-pad by the remaining amount. + */ + + sz = (int)strlen(data->string); + pnt = data->span->tbl->decimal; + + if (NULL == (dp = strchr(data->string, pnt))) { + d = sz + 1; + } else { + d = (int)(dp - data->string) + 1; + } + + assert(d <= data->cell->head->decimal); + assert(sz - d <= data->cell->head->width - + data->cell->head->decimal); + + padl = data->cell->head->decimal - d + 1; + assert(width - sz - padl); + + write_char(' ', padl); + (void)printf("%s", data->string); + write_char(' ', width - sz - padl); +} + + +static void +write_data_literal(const struct tbl_data *data, int width) +{ + int padl, padr; + + padl = padr = 0; + + switch (data->cell->pos) { + case (TBL_CELL_LONG): + padl = 1; + padr = width - (int)strlen(data->string) - 1; + break; + case (TBL_CELL_CENTRE): + padl = width - (int)strlen(data->string); + if (padl % 2) + padr++; + padl /= 2; + padr += padl; + break; + case (TBL_CELL_RIGHT): + padl = width - (int)strlen(data->string); + break; + default: + padr = width - (int)strlen(data->string); + break; + } + + write_char(' ', padl); + (void)printf("%s", data->string); + write_char(' ', padr); +} + + +static void +write_data(const struct tbl_data *data, int width) +{ + + if (NULL == data) { + write_char(' ', width); + return; + } + + if (TBL_DATA_HORIZ & data->flags || + TBL_DATA_DHORIZ & data->flags) { + write_data_spanner(data, width); + return; + } + + switch (data->cell->pos) { + case (TBL_CELL_HORIZ): + /* FALLTHROUGH */ + case (TBL_CELL_DHORIZ): + write_data_spanner(data, width); + break; + case (TBL_CELL_LONG): + /* FALLTHROUGH */ + case (TBL_CELL_CENTRE): + /* FALLTHROUGH */ + case (TBL_CELL_LEFT): + /* FALLTHROUGH */ + case (TBL_CELL_RIGHT): + write_data_literal(data, width); + break; + case (TBL_CELL_NUMBER): + write_data_number(data, width); + break; + default: + abort(); + /* NOTREACHED */ + } +} + + +static void +write_spanner(const struct tbl_head *head) +{ + char *p; + + p = NULL; + switch (head->pos) { + case (TBL_HEAD_VERT): + p = "|"; + break; + case (TBL_HEAD_DVERT): + p = "||"; + break; + default: + break; + } + + assert(p); + printf("%s", p); +} + + +static inline void +write_char(char c, int len) +{ + int i; + + for (i = 0; i < len; i++) + printf("%c", c); +} diff --git a/usr.bin/mandoc/tbl_tree.c b/usr.bin/mandoc/tbl_tree.c new file mode 100644 index 00000000000..a4cee3ab87d --- /dev/null +++ b/usr.bin/mandoc/tbl_tree.c @@ -0,0 +1,86 @@ +/* $Id: tbl_tree.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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "tbl_extern.h" + +static const char * const htypes[TBL_HEAD_MAX] = { + "data", + "vert", + "dvert", +}; + +static const char * const ctypes[TBL_CELL_MAX] = { + "centre", + "right", + "left", + "number", + "span", + "long", + "down", + "horiz", + "dhoriz", + "vert", + "dvert", +}; + + +/* ARGSUSED */ +int +tbl_calc_tree(struct tbl *tbl) +{ + + return(1); +} + + +int +tbl_write_tree(const struct tbl *tbl) +{ + struct tbl_row *row; + struct tbl_cell *cell; + struct tbl_span *span; + struct tbl_data *data; + struct tbl_head *head; + + (void)printf("header\n"); + TAILQ_FOREACH(head, &tbl->head, entries) + (void)printf("\t%s (=%p)\n", htypes[head->pos], head); + + (void)printf("layout\n"); + TAILQ_FOREACH(row, &tbl->row, entries) { + (void)printf("\trow (=%p)\n", row); + TAILQ_FOREACH(cell, &row->cell, entries) + (void)printf("\t\t%s (=%p) >%p\n", + ctypes[cell->pos], + cell, cell->head); + } + + (void)printf("data\n"); + TAILQ_FOREACH(span, &tbl->span, entries) { + (void)printf("\tspan >%p\n", span->row); + TAILQ_FOREACH(data, &span->data, entries) + (void)printf("\t\tdata >%p\n", data->cell); + } + + return(1); +} |