![]() ![]() | ![]() |
version 1.1, 2010/12/29 14:38:14 | version 1.10, 2011/01/04 23:48:39 | ||
---|---|---|---|
|
|
||
#include <ctype.h> | #include <ctype.h> | ||
#include <stdlib.h> | #include <stdlib.h> | ||
#include <string.h> | #include <string.h> | ||
#include <time.h> | |||
#include "mandoc.h" | #include "mandoc.h" | ||
#include "libmandoc.h" | #include "libmandoc.h" | ||
|
|
||
enum tbl_cellt key; | enum tbl_cellt key; | ||
}; | }; | ||
#define KEYS_MAX 17 | #define KEYS_MAX 11 | ||
static const struct tbl_phrase keys[KEYS_MAX] = { | static const struct tbl_phrase keys[KEYS_MAX] = { | ||
{ 'c', TBL_CELL_CENTRE }, | { 'c', TBL_CELL_CENTRE }, | ||
{ 'C', TBL_CELL_CENTRE }, | |||
{ 'r', TBL_CELL_RIGHT }, | { 'r', TBL_CELL_RIGHT }, | ||
{ 'R', TBL_CELL_RIGHT }, | |||
{ 'l', TBL_CELL_LEFT }, | { 'l', TBL_CELL_LEFT }, | ||
{ 'L', TBL_CELL_LEFT }, | |||
{ 'n', TBL_CELL_NUMBER }, | { 'n', TBL_CELL_NUMBER }, | ||
{ 'N', TBL_CELL_NUMBER }, | |||
{ 's', TBL_CELL_SPAN }, | { 's', TBL_CELL_SPAN }, | ||
{ 'S', TBL_CELL_SPAN }, | |||
{ 'a', TBL_CELL_LONG }, | { 'a', TBL_CELL_LONG }, | ||
{ 'A', TBL_CELL_LONG }, | |||
{ '^', TBL_CELL_DOWN }, | { '^', TBL_CELL_DOWN }, | ||
{ '-', TBL_CELL_HORIZ }, | { '-', TBL_CELL_HORIZ }, | ||
{ '_', TBL_CELL_HORIZ }, | { '_', TBL_CELL_HORIZ }, | ||
|
|
||
{ '|', TBL_CELL_VERT } | { '|', TBL_CELL_VERT } | ||
}; | }; | ||
static int mods(struct tbl *, struct tbl_cell *, | static int mods(struct tbl_node *, struct tbl_cell *, | ||
int, const char *, int *); | int, const char *, int *); | ||
static int cell(struct tbl *, struct tbl_row *, | static int cell(struct tbl_node *, struct tbl_row *, | ||
int, const char *, int *); | int, const char *, int *); | ||
static void row(struct tbl *, int, const char *, int *); | static void row(struct tbl_node *, int, const char *, int *); | ||
static struct tbl_cell *cell_alloc(struct tbl_node *, | |||
struct tbl_row *, enum tbl_cellt); | |||
static void head_adjust(const struct tbl_cell *, | |||
struct tbl_head *); | |||
static int | static int | ||
mods(struct tbl *tbl, struct tbl_cell *cp, | mods(struct tbl_node *tbl, struct tbl_cell *cp, | ||
int ln, const char *p, int *pos) | int ln, const char *p, int *pos) | ||
{ | { | ||
char buf[5]; | char buf[5]; | ||
|
|
||
/* TODO: GNU has many more extensions. */ | /* TODO: GNU has many more extensions. */ | ||
switch (p[(*pos)++]) { | switch (tolower(p[(*pos)++])) { | ||
case ('z'): | case ('z'): | ||
/* FALLTHROUGH */ | |||
case ('Z'): | |||
cp->flags |= TBL_CELL_WIGN; | cp->flags |= TBL_CELL_WIGN; | ||
goto mod; | goto mod; | ||
case ('u'): | case ('u'): | ||
/* FALLTHROUGH */ | |||
case ('U'): | |||
cp->flags |= TBL_CELL_UP; | cp->flags |= TBL_CELL_UP; | ||
goto mod; | goto mod; | ||
case ('e'): | case ('e'): | ||
/* FALLTHROUGH */ | |||
case ('E'): | |||
cp->flags |= TBL_CELL_EQUAL; | cp->flags |= TBL_CELL_EQUAL; | ||
goto mod; | goto mod; | ||
case ('t'): | case ('t'): | ||
/* FALLTHROUGH */ | |||
case ('T'): | |||
cp->flags |= TBL_CELL_TALIGN; | cp->flags |= TBL_CELL_TALIGN; | ||
goto mod; | goto mod; | ||
case ('d'): | case ('d'): | ||
/* FALLTHROUGH */ | |||
case ('D'): | |||
cp->flags |= TBL_CELL_BALIGN; | cp->flags |= TBL_CELL_BALIGN; | ||
goto mod; | goto mod; | ||
case ('w'): /* XXX for now, ignore minimal column width */ | |||
goto mod; | |||
case ('f'): | case ('f'): | ||
/* FALLTHROUGH */ | break; | ||
case ('B'): | |||
/* FALLTHROUGH */ | |||
case ('I'): | |||
/* FALLTHROUGH */ | |||
case ('b'): | case ('b'): | ||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||
case ('i'): | case ('i'): | ||
(*pos)--; | |||
break; | break; | ||
default: | default: | ||
TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1); | TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1); | ||
return(0); | return(0); | ||
} | } | ||
switch (p[(*pos)++]) { | switch (tolower(p[(*pos)++])) { | ||
case ('b'): | case ('b'): | ||
/* FALLTHROUGH */ | |||
case ('B'): | |||
cp->flags |= TBL_CELL_BOLD; | cp->flags |= TBL_CELL_BOLD; | ||
goto mod; | goto mod; | ||
case ('i'): | case ('i'): | ||
/* FALLTHROUGH */ | |||
case ('I'): | |||
cp->flags |= TBL_CELL_ITALIC; | cp->flags |= TBL_CELL_ITALIC; | ||
goto mod; | goto mod; | ||
default: | default: | ||
|
|
||
} | } | ||
static int | static int | ||
cell(struct tbl *tbl, struct tbl_row *rp, | cell(struct tbl_node *tbl, struct tbl_row *rp, | ||
int ln, const char *p, int *pos) | int ln, const char *p, int *pos) | ||
{ | { | ||
struct tbl_cell *cp; | |||
int i; | int i; | ||
enum tbl_cellt c; | enum tbl_cellt c; | ||
/* Parse the column position (`r', `R', `|', ...). */ | /* Parse the column position (`r', `R', `|', ...). */ | ||
for (i = 0; i < KEYS_MAX; i++) | for (i = 0; i < KEYS_MAX; i++) | ||
if (p[*pos] == keys[i].name) | if (tolower(p[*pos]) == keys[i].name) | ||
break; | break; | ||
if (KEYS_MAX == i) { | if (KEYS_MAX == i) { | ||
|
|
||
/* Allocate cell then parse its modifiers. */ | /* Allocate cell then parse its modifiers. */ | ||
cp = mandoc_calloc(1, sizeof(struct tbl_cell)); | return(mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos)); | ||
cp->pos = c; | |||
if (rp->last) { | |||
rp->last->next = cp; | |||
rp->last = cp; | |||
} else | |||
rp->last = rp->first = cp; | |||
return(mods(tbl, cp, ln, p, pos)); | |||
} | } | ||
static void | static void | ||
row(struct tbl *tbl, int ln, const char *p, int *pos) | row(struct tbl_node *tbl, int ln, const char *p, int *pos) | ||
{ | { | ||
struct tbl_row *rp; | struct tbl_row *rp; | ||
|
|
||
*/ | */ | ||
rp = mandoc_calloc(1, sizeof(struct tbl_row)); | rp = mandoc_calloc(1, sizeof(struct tbl_row)); | ||
if (tbl->last) { | if (tbl->last_row) { | ||
tbl->last->next = rp; | tbl->last_row->next = rp; | ||
tbl->last = rp; | tbl->last_row = rp; | ||
} else | } else | ||
tbl->last = tbl->first = rp; | tbl->last_row = tbl->first_row = rp; | ||
cell: | cell: | ||
while (isspace((unsigned char)p[*pos])) | while (isspace((unsigned char)p[*pos])) | ||
|
|
||
if ('.' == p[*pos]) { | if ('.' == p[*pos]) { | ||
tbl->part = TBL_PART_DATA; | tbl->part = TBL_PART_DATA; | ||
if (NULL == tbl->first) | if (NULL == tbl->first_row) | ||
TBL_MSG(tbl, MANDOCERR_TBLNOLAYOUT, ln, *pos); | TBL_MSG(tbl, MANDOCERR_TBLNOLAYOUT, ln, *pos); | ||
(*pos)++; | (*pos)++; | ||
return; | return; | ||
|
|
||
/* NOTREACHED */ | /* NOTREACHED */ | ||
} | } | ||
int | int | ||
tbl_layout(struct tbl *tbl, int ln, const char *p) | tbl_layout(struct tbl_node *tbl, int ln, const char *p) | ||
{ | { | ||
int pos; | int pos; | ||
|
|
||
/* Always succeed. */ | /* Always succeed. */ | ||
return(1); | return(1); | ||
} | } | ||
static struct tbl_cell * | |||
cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos) | |||
{ | |||
struct tbl_cell *p, *pp; | |||
struct tbl_head *h, *hp; | |||
p = mandoc_calloc(1, sizeof(struct tbl_cell)); | |||
if (NULL != (pp = rp->last)) { | |||
rp->last->next = p; | |||
rp->last = p; | |||
} else | |||
rp->last = rp->first = p; | |||
p->pos = pos; | |||
/* | |||
* 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. | |||
*/ | |||
h = pp ? pp->head->next : tbl->first_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)) { | |||
head_adjust(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)) { | |||
hp = mandoc_calloc(1, sizeof(struct tbl_head)); | |||
hp->ident = tbl->opts.cols++; | |||
hp->prev = h->prev; | |||
if (h->prev) | |||
h->prev->next = hp; | |||
if (h == tbl->first_head) | |||
tbl->first_head = hp; | |||
h->prev = hp; | |||
hp->next = h; | |||
head_adjust(p, hp); | |||
p->head = hp; | |||
return(p); | |||
} | |||
if (NULL != (h = h->next)) { | |||
head_adjust(p, h); | |||
p->head = h; | |||
return(p); | |||
} | |||
/* Fall through to default case... */ | |||
} | |||
hp = mandoc_calloc(1, sizeof(struct tbl_head)); | |||
hp->ident = tbl->opts.cols++; | |||
if (tbl->last_head) { | |||
hp->prev = tbl->last_head; | |||
tbl->last_head->next = hp; | |||
tbl->last_head = hp; | |||
} else | |||
tbl->last_head = tbl->first_head = hp; | |||
head_adjust(p, hp); | |||
p->head = hp; | |||
return(p); | |||
} | |||
static void | |||
head_adjust(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; | |||
} | |||