summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@cvs.openbsd.org>2010-10-15 19:20:04 +0000
committerIngo Schwarze <schwarze@cvs.openbsd.org>2010-10-15 19:20:04 +0000
commit07117bb546ee4fe37197bfe24a605edc6f7e56ad (patch)
tree68875810f89effd737a1c912c9c800cfb08c9117
parent90b90d5f2aa014db30eee069d0d5ff207a1af4ef (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.c544
-rw-r--r--usr.bin/mandoc/tbl.h34
-rw-r--r--usr.bin/mandoc/tbl_data.c130
-rw-r--r--usr.bin/mandoc/tbl_extern.h183
-rw-r--r--usr.bin/mandoc/tbl_layout.c280
-rw-r--r--usr.bin/mandoc/tbl_option.c195
-rw-r--r--usr.bin/mandoc/tbl_term.c504
-rw-r--r--usr.bin/mandoc/tbl_tree.c86
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);
+}