summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@openbsd.org>2015-01-27 05:21:44 +0000
committerIngo Schwarze <schwarze@openbsd.org>2015-01-27 05:21:44 +0000
commit3d1973ffce60279bff4ec548c2c90f4d1d31f1a2 (patch)
treeff7ba8b19b0c296892ac283cae5c138a94b2e04f
parentfb1f845c9ebafe8648f3df58f8a4581041ff067e (diff)
downloadmandoc-3d1973ffce60279bff4ec548c2c90f4d1d31f1a2.tar.gz
Multiple parser and formatter fixes for line drawing in tbl(7).
* Allow mixing vertical line bars with the layout options of the preceding layout cell. * Correctly combine box options with layout lines. * Correctly print vertical lines in data rows, with the right spacing. * Correctly print cross markers and left and right ends of horizontal lines even if vertical lines differ above and below. * Avoid the bogus error message "no table data cells" when a table data section starts with a horizontal line. No increase in code size.
-rw-r--r--mandoc.h10
-rw-r--r--tbl.c6
-rw-r--r--tbl_data.c14
-rw-r--r--tbl_layout.c69
-rw-r--r--tbl_term.c219
5 files changed, 160 insertions, 158 deletions
diff --git a/mandoc.h b/mandoc.h
index 3148221b..47cd7292 100644
--- a/mandoc.h
+++ b/mandoc.h
@@ -206,6 +206,8 @@ struct tbl_opts {
#define TBL_OPT_NOSPACE (1 << 6)
#define TBL_OPT_NOWARN (1 << 7)
int cols; /* number of columns */
+ int lvert; /* width of left vertical line */
+ int rvert; /* width of right vertical line */
};
/*
@@ -215,7 +217,6 @@ struct tbl_opts {
*/
struct tbl_head {
int ident; /* 0 <= unique id < cols */
- int vert; /* width of preceding vertical line */
struct tbl_head *next;
struct tbl_head *prev;
};
@@ -238,7 +239,7 @@ enum tbl_cellt {
*/
struct tbl_cell {
struct tbl_cell *next;
- int vert; /* width of preceding vertical line */
+ int vert; /* width of subsequent vertical line */
enum tbl_cellt pos;
size_t spacing;
int flags;
@@ -260,7 +261,7 @@ struct tbl_row {
struct tbl_row *next;
struct tbl_cell *first;
struct tbl_cell *last;
- int vert; /* trailing vertical line */
+ int vert; /* width of left vertical line */
};
enum tbl_datt {
@@ -299,12 +300,13 @@ struct tbl_span {
struct tbl_row *layout; /* layout row */
struct tbl_dat *first;
struct tbl_dat *last;
+ struct tbl_span *prev;
+ struct tbl_span *next;
int line; /* parse line */
int flags;
#define TBL_SPAN_FIRST (1 << 0)
#define TBL_SPAN_LAST (1 << 1)
enum tbl_spant pos;
- struct tbl_span *next;
};
enum eqn_boxt {
diff --git a/tbl.c b/tbl.c
index 20ccb389..7019f4d2 100644
--- a/tbl.c
+++ b/tbl.c
@@ -173,11 +173,15 @@ void
tbl_end(struct tbl_node **tblp)
{
struct tbl_node *tbl;
+ struct tbl_span *sp;
tbl = *tblp;
*tblp = NULL;
- if (NULL == tbl->first_span || NULL == tbl->first_span->first)
+ sp = tbl->first_span;
+ while (sp != NULL && sp->first == NULL)
+ sp = sp->next;
+ if (sp == NULL)
mandoc_msg(MANDOCERR_TBLNODATA, tbl->parse,
tbl->line, tbl->pos, NULL);
diff --git a/tbl_data.c b/tbl_data.c
index 33a91e86..2b889542 100644
--- a/tbl_data.c
+++ b/tbl_data.c
@@ -1,7 +1,7 @@
/* $Id$ */
/*
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011, 2015 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -190,15 +190,15 @@ newspan(struct tbl_node *tbl, int line, struct tbl_row *rp)
dp->opts = &tbl->opts;
dp->layout = rp;
dp->head = tbl->first_head;
+ dp->prev = tbl->last_span;
- if (tbl->last_span) {
- tbl->last_span->next = dp;
- tbl->last_span = dp;
- } else {
- tbl->last_span = tbl->first_span = dp;
+ if (dp->prev == NULL) {
+ tbl->first_span = dp;
tbl->current_span = NULL;
dp->flags |= TBL_SPAN_FIRST;
- }
+ } else
+ dp->prev->next = dp;
+ tbl->last_span = dp;
return(dp);
}
diff --git a/tbl_layout.c b/tbl_layout.c
index 7ca81659..adf728aa 100644
--- a/tbl_layout.c
+++ b/tbl_layout.c
@@ -54,7 +54,7 @@ static void mods(struct tbl_node *, struct tbl_cell *,
static void cell(struct tbl_node *, struct tbl_row *,
int, const char *, int *);
static struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
- enum tbl_cellt, int vert);
+ enum tbl_cellt);
static void
@@ -69,7 +69,7 @@ mod:
/* Row delimiters and cell specifiers end modifier lists. */
- if (strchr(".,-=^_ACLNRSaclnrs|", p[*pos]) != NULL)
+ if (strchr(".,-=^_ACLNRSaclnrs", p[*pos]) != NULL)
return;
/* Throw away parenthesised expression. */
@@ -137,6 +137,13 @@ mod:
case 'z':
cp->flags |= TBL_CELL_WIGN;
goto mod;
+ case '|':
+ if (cp->vert < 2)
+ cp->vert++;
+ else
+ mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
+ tbl->parse, ln, *pos - 1, NULL);
+ goto mod;
default:
mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
ln, *pos - 1, "%c", p[*pos - 1]);
@@ -169,17 +176,15 @@ static void
cell(struct tbl_node *tbl, struct tbl_row *rp,
int ln, const char *p, int *pos)
{
- int vert, i;
+ int i;
enum tbl_cellt c;
- /* Handle vertical lines. */
+ /* Handle leading vertical lines */
- vert = 0;
-again:
while (p[*pos] == ' ' || p[*pos] == '\t' || p[*pos] == '|') {
if (p[*pos] == '|') {
- if (vert < 2)
- vert++;
+ if (rp->vert < 2)
+ rp->vert++;
else
mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
tbl->parse, ln, *pos, NULL);
@@ -187,12 +192,12 @@ again:
(*pos)++;
}
- /* Handle trailing vertical lines */
+again:
+ while (p[*pos] == ' ' || p[*pos] == '\t')
+ (*pos)++;
- if ('.' == p[*pos] || '\0' == p[*pos]) {
- rp->vert = vert;
+ if (p[*pos] == '.' || p[*pos] == '\0')
return;
- }
/* Parse the column position (`c', `l', `r', ...). */
@@ -225,7 +230,7 @@ again:
/* Allocate cell then parse its modifiers. */
- mods(tbl, cell_alloc(tbl, rp, c, vert), ln, p, pos);
+ mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos);
}
void
@@ -253,13 +258,34 @@ tbl_layout(struct tbl_node *tbl, int ln, const char *p)
case '.': /* End of layout. */
pos++;
tbl->part = TBL_PART_DATA;
- if (tbl->first_row != NULL)
+
+ /*
+ * When the layout is completely empty,
+ * default to one left-justified column.
+ */
+
+ if (tbl->first_row == NULL) {
+ mandoc_msg(MANDOCERR_TBLLAYOUT_NONE,
+ tbl->parse, ln, pos, NULL);
+ rp = mandoc_calloc(1, sizeof(*rp));
+ cell_alloc(tbl, rp, TBL_CELL_LEFT);
+ tbl->first_row = tbl->last_row = rp;
return;
- mandoc_msg(MANDOCERR_TBLLAYOUT_NONE,
- tbl->parse, ln, pos, NULL);
- rp = mandoc_calloc(1, sizeof(*rp));
- cell_alloc(tbl, rp, TBL_CELL_LEFT, 0);
- tbl->first_row = tbl->last_row = rp;
+ }
+
+ /*
+ * Search for the widest line
+ * along the left and right margins.
+ */
+
+ for (rp = tbl->first_row; rp; rp = rp->next) {
+ if (tbl->opts.lvert < rp->vert)
+ tbl->opts.lvert = rp->vert;
+ if (rp->last != NULL &&
+ rp->last->head == tbl->last_head &&
+ tbl->opts.rvert < rp->last->vert)
+ tbl->opts.rvert = rp->last->vert;
+ }
return;
default: /* Cell. */
break;
@@ -278,8 +304,7 @@ tbl_layout(struct tbl_node *tbl, int ln, const char *p)
}
static struct tbl_cell *
-cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos,
- int vert)
+cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos)
{
struct tbl_cell *p, *pp;
struct tbl_head *h, *hp;
@@ -296,7 +321,6 @@ cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos,
rp->last = p;
p->pos = pos;
- p->vert = vert;
/* Re-use header. */
@@ -307,7 +331,6 @@ cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos,
hp = mandoc_calloc(1, sizeof(struct tbl_head));
hp->ident = tbl->opts.cols++;
- hp->vert = vert;
if (tbl->last_head) {
hp->prev = tbl->last_head;
diff --git a/tbl_term.c b/tbl_term.c
index 25c32cdb..d53f1797 100644
--- a/tbl_term.c
+++ b/tbl_term.c
@@ -1,7 +1,7 @@
/* $Id$ */
/*
* Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2011, 2012, 2014 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011, 2012, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -34,15 +34,12 @@ static void tbl_char(struct termp *, char, size_t);
static void tbl_data(struct termp *, const struct tbl_opts *,
const struct tbl_dat *,
const struct roffcol *);
-static size_t tbl_rulewidth(struct termp *, const struct tbl_head *);
-static void tbl_hframe(struct termp *, const struct tbl_span *, int);
static void tbl_literal(struct termp *, const struct tbl_dat *,
const struct roffcol *);
static void tbl_number(struct termp *, const struct tbl_opts *,
const struct tbl_dat *,
const struct roffcol *);
-static void tbl_hrule(struct termp *, const struct tbl_span *);
-static void tbl_vrule(struct termp *, const struct tbl_head *);
+static void tbl_hrule(struct termp *, const struct tbl_span *, int);
static void tbl_word(struct termp *, const struct tbl_dat *);
@@ -64,9 +61,9 @@ void
term_tbl(struct termp *tp, const struct tbl_span *sp)
{
const struct tbl_head *hp;
+ const struct tbl_cell *cp;
const struct tbl_dat *dp;
- struct roffcol *col;
- int spans;
+ int horiz, spans, vert;
size_t rmargin, maxrmargin;
rmargin = tp->rmargin;
@@ -84,7 +81,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
* calculate the table widths and decimal positions.
*/
- if (TBL_SPAN_FIRST & sp->flags) {
+ if (sp->flags & TBL_SPAN_FIRST) {
term_flushln(tp);
tp->tbl.len = term_tbl_len;
@@ -92,24 +89,25 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tbl.arg = tp;
tblcalc(&tp->tbl, sp, rmargin - tp->offset);
- }
- /* Horizontal frame at the start of boxed tables. */
+ /* Horizontal frame at the start of boxed tables. */
- if (TBL_SPAN_FIRST & sp->flags) {
- if (TBL_OPT_DBOX & sp->opts->opts)
- tbl_hframe(tp, sp, 1);
- if (TBL_OPT_DBOX & sp->opts->opts ||
- TBL_OPT_BOX & sp->opts->opts)
- tbl_hframe(tp, sp, 0);
+ if (sp->opts->opts & TBL_OPT_DBOX)
+ tbl_hrule(tp, sp, 2);
+ if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))
+ tbl_hrule(tp, sp, 1);
}
/* Vertical frame at the start of each row. */
- if ((TBL_OPT_BOX | TBL_OPT_DBOX) & sp->opts->opts ||
- (sp->head != NULL && sp->head->vert))
- term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
- TBL_SPAN_DHORIZ == sp->pos ? "+" : "|");
+ horiz = sp->pos == TBL_SPAN_HORIZ || sp->pos == TBL_SPAN_DHORIZ;
+
+ if (sp->layout->vert ||
+ (sp->prev != NULL && sp->prev->layout->vert) ||
+ sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))
+ term_word(tp, horiz ? "+" : "|");
+ else if (sp->opts->lvert)
+ tbl_char(tp, horiz ? '-' : ASCII_NBRSP, 1);
/*
* Now print the actual data itself depending on the span type.
@@ -117,54 +115,61 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
* data printed by matching data to header.
*/
- switch (sp->pos) {
- case TBL_SPAN_HORIZ:
- /* FALLTHROUGH */
- case TBL_SPAN_DHORIZ:
- tbl_hrule(tp, sp);
- break;
- case TBL_SPAN_DATA:
+ if (sp->pos == TBL_SPAN_DATA) {
/* Iterate over template headers. */
+ cp = sp->layout->first;
dp = sp->first;
spans = 0;
- for (hp = sp->head; hp; hp = hp->next) {
+ for (hp = sp->head; hp != NULL; hp = hp->next) {
/*
- * If the current data header is invoked during
- * a spanner ("spans" > 0), don't emit anything
- * at all.
+ * Remeber whether we need a vertical bar
+ * after this cell.
*/
- if (--spans >= 0)
- continue;
-
- /* Separate columns. */
+ vert = cp == NULL ? 0 : cp->vert;
- if (NULL != hp->prev)
- tbl_vrule(tp, hp);
+ /*
+ * Print the data and advance to the next cell.
+ */
- col = &tp->tbl.cols[hp->ident];
- tbl_data(tp, sp->opts, dp, col);
+ if (spans == 0) {
+ tbl_data(tp, sp->opts, dp,
+ tp->tbl.cols + hp->ident);
+ if (dp != NULL) {
+ spans = dp->spans;
+ dp = dp->next;
+ }
+ } else
+ spans--;
+ if (cp != NULL)
+ cp = cp->next;
/*
- * Go to the next data cell and assign the
- * number of subsequent spans, if applicable.
+ * Separate columns, except in the middle
+ * of spans and after the last cell.
*/
- if (dp) {
- spans = dp->spans;
- dp = dp->next;
- }
+ if (hp->next == NULL || spans)
+ continue;
+
+ tbl_char(tp, ASCII_NBRSP, 1);
+ if (vert > 0)
+ tbl_char(tp, '|', vert);
+ if (vert < 2 && hp->next != NULL)
+ tbl_char(tp, ASCII_NBRSP, 2 - vert);
}
- break;
- }
+ } else if (horiz)
+ tbl_hrule(tp, sp, 0);
/* Vertical frame at the end of each row. */
- if ((TBL_OPT_BOX | TBL_OPT_DBOX) & sp->opts->opts ||
- sp->layout->vert)
- term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
- TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
+ if (sp->layout->last->vert ||
+ (sp->prev != NULL && sp->prev->layout->last->vert) ||
+ (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)))
+ term_word(tp, horiz ? "+" : " |");
+ else if (sp->opts->rvert)
+ tbl_char(tp, horiz ? '-' : ASCII_NBRSP, 1);
term_flushln(tp);
/*
@@ -172,14 +177,13 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
* existing table configuration and set it to NULL.
*/
- if (TBL_SPAN_LAST & sp->flags) {
- if (TBL_OPT_DBOX & sp->opts->opts ||
- TBL_OPT_BOX & sp->opts->opts) {
- tbl_hframe(tp, sp, 0);
+ if (sp->flags & TBL_SPAN_LAST) {
+ if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) {
+ tbl_hrule(tp, sp, 1);
tp->skipvsp = 1;
}
- if (TBL_OPT_DBOX & sp->opts->opts) {
- tbl_hframe(tp, sp, 1);
+ if (sp->opts->opts & TBL_OPT_DBOX) {
+ tbl_hrule(tp, sp, 2);
tp->skipvsp = 2;
}
assert(tp->tbl.cols);
@@ -194,66 +198,46 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
}
/*
- * Horizontal rules extend across the entire table.
- * Calculate the width by iterating over columns.
- */
-static size_t
-tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
-{
- size_t width;
-
- width = tp->tbl.cols[hp->ident].width;
-
- /* Account for leading blanks. */
- if (hp->prev)
- width += 2 - hp->vert;
-
- /* Account for trailing blank. */
- width++;
-
- return(width);
-}
-
-/*
- * Rules inside the table can be single or double
- * and have crossings with vertical rules marked with pluses.
+ * Kinds of horizontal rulers:
+ * 0: inside the table (single or double line with crossings)
+ * 1: inner frame (single line with crossings and ends)
+ * 2: outer frame (single line without crossings with ends)
*/
static void
-tbl_hrule(struct termp *tp, const struct tbl_span *sp)
+tbl_hrule(struct termp *tp, const struct tbl_span *sp, int kind)
{
- const struct tbl_head *hp;
- char c;
-
- c = '-';
- if (TBL_SPAN_DHORIZ == sp->pos)
- c = '=';
-
- for (hp = sp->head; hp; hp = hp->next) {
- if (hp->prev && hp->vert)
- tbl_char(tp, '+', hp->vert);
- tbl_char(tp, c, tbl_rulewidth(tp, hp));
+ const struct tbl_cell *c1, *c2;
+ int vert;
+ char line, cross;
+
+ line = (kind == 0 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-';
+ cross = (kind < 2) ? '+' : '-';
+
+ if (kind)
+ term_word(tp, "+");
+ c1 = sp->layout->first;
+ c2 = sp->prev == NULL ? NULL : sp->prev->layout->first;
+ if (c2 == c1)
+ c2 = NULL;
+ for (;;) {
+ tbl_char(tp, line, tp->tbl.cols[c1->head->ident].width + 1);
+ vert = c1->vert;
+ if ((c1 = c1->next) == NULL)
+ break;
+ if (c2 != NULL) {
+ if (vert < c2->vert)
+ vert = c2->vert;
+ c2 = c2->next;
+ }
+ if (vert)
+ tbl_char(tp, cross, vert);
+ if (vert < 2)
+ tbl_char(tp, line, 2 - vert);
}
-}
-
-/*
- * Rules above and below the table are always single
- * and have an additional plus at the beginning and end.
- * For double frames, this function is called twice,
- * and the outer one does not have crossings.
- */
-static void
-tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
-{
- const struct tbl_head *hp;
-
- term_word(tp, "+");
- for (hp = sp->head; hp; hp = hp->next) {
- if (hp->prev && hp->vert)
- tbl_char(tp, (outer ? '-' : '+'), hp->vert);
- tbl_char(tp, '-', tbl_rulewidth(tp, hp));
+ if (kind) {
+ term_word(tp, "+");
+ term_flushln(tp);
}
- term_word(tp, "+");
- term_flushln(tp);
}
static void
@@ -315,17 +299,6 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
}
static void
-tbl_vrule(struct termp *tp, const struct tbl_head *hp)
-{
-
- tbl_char(tp, ASCII_NBRSP, 1);
- if (0 < hp->vert)
- tbl_char(tp, '|', hp->vert);
- if (2 > hp->vert)
- tbl_char(tp, ASCII_NBRSP, 2 - hp->vert);
-}
-
-static void
tbl_char(struct termp *tp, char c, size_t len)
{
size_t i, sz;