From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp1.rz.uni-karlsruhe.de (Debian-exim@smtp1.rz.uni-karlsruhe.de [129.13.185.217]) by krisdoz.my.domain (8.14.3/8.14.3) with ESMTP id o9D0kODG011363 for ; Tue, 12 Oct 2010 20:46:27 -0400 (EDT) Received: from hekate.usta.de (asta-nat.asta.uni-karlsruhe.de [172.22.63.82]) by smtp1.rz.uni-karlsruhe.de with esmtp (Exim 4.63 #1) id 1P5pU6-0003mQ-W2; Wed, 13 Oct 2010 02:46:23 +0200 Received: from donnerwolke.usta.de ([172.24.96.3]) by hekate.usta.de with esmtp (Exim 4.71) (envelope-from ) id 1P5pU6-0005Ut-Tb for tech@mdocml.bsd.lv; Wed, 13 Oct 2010 02:46:22 +0200 Received: from iris.usta.de ([172.24.96.5] helo=usta.de) by donnerwolke.usta.de with esmtp (Exim 4.69) (envelope-from ) id 1P5pU6-0001JU-SO for tech@mdocml.bsd.lv; Wed, 13 Oct 2010 02:46:22 +0200 Received: from schwarze by usta.de with local (Exim 4.71) (envelope-from ) id 1P5pU6-0007Ja-RG for tech@mdocml.bsd.lv; Wed, 13 Oct 2010 02:46:22 +0200 Date: Wed, 13 Oct 2010 02:46:22 +0200 From: Ingo Schwarze To: tech@mdocml.bsd.lv Subject: integrate tbl into mandoc Message-ID: <20101013004622.GC18513@iris.usta.de> X-Mailinglist: mdocml-tech Reply-To: tech@mdocml.bsd.lv MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.20 (2009-06-14) Hi, i should like to remove groff out of the OpenBSD tree as soon as possible. I was slacking: we should not wait until the 4.9 release comes close. The last holdup is that we still need groff to format about one or two dozens of manuals using tbl(1) syntax. Thus, i am trying to get mandoc to deal with tbl(1) as quickly as possible. During EuroBSDCon last weekend, i discussed various approaches to get this done with Joerg Sonnenberger and Marc Espie. With Joerg, i came to the conclusion that it would be most rigorous to do tbl parsing before roff parsing, because when using groff, tbl output is piped into nroff, so that's the proper order. On the other hand, i now looked at Kristaps' actual tbl code and it turns out that a clean solution portable to various frontends will rather be to put the tbl parser alongside the outher parsers, adding its AST output to the other ASTs, and to put the tbl output frontend alongside the other frontends. Unfortunately, that is hard to do when parsing tbl before roff: It would mean to have the tbl ASTs before having the man AST ready to hook it into. Thus, for now, here is a proposal to implement the .TS/.TE macros in libman and call the tbl parser and frontend from the man code. The same can of course be done for mdoc. The files are completely unchanged with respect to Kristaps' latest published version, tbl-0.1.5. Here, i'm just showing the glue. Note that this patch applies to OpenBSD, not to bsd.lv. As i said, i hope to get it in quickly, even if we do not merge the same to mdocml.bsd.lv, and even if we have to do more cleanup later. One thing to do before commit is probably to replace the printfs in the output frontend by calls into term.c, such that indentation is respected and escape sequences get resolved. There may be more i need to do. The following already look semi-useful with this: /usr/src/lib/libcurses/curs_attr.3tbl /usr/src/lib/libcurses/curs_getch.3tbl /usr/src/lib/libcurses/curses.3tbl /usr/src/usr.bin/infocmp/infocmp.1tbl The following still crash: /usr/src/lib/libcurses/curs_addch.3tbl /usr/src/lib/libcurses/curs_inch.3tbl /usr/src/lib/libcurses/curs_mouse.3tbl /usr/src/lib/libform/form.3tbl /usr/src/lib/libmenu/menu.3tbl /usr/src/usr.bin/tic/captoinfo.1tbl /usr/src/gnu/usr.sbin/mkhybrid/src/mkhybrid.8tbl The following have a tbl extension, but no .TS: /usr/src/lib/libcurses/term.5tbl The following are mdoc(7) with tbl: /usr/src/share/man/man4/wi.4tbl /usr/src/share/man/man4/man4.hppa/cpu.4tbl /usr/src/games/phantasia/phantasia.6tbl Thoughts? Ingo Index: Makefile =================================================================== RCS file: /cvs/src/usr.bin/mandoc/Makefile,v retrieving revision 1.45 diff -u -p -r1.45 Makefile --- Makefile 27 Sep 2010 21:25:28 -0000 1.45 +++ Makefile 12 Oct 2010 23:37:36 -0000 @@ -20,6 +20,8 @@ SRCS+= main.c mdoc_term.c chars.c term.c SRCS+= html.c mdoc_html.c man_html.c out.c SRCS+= term_ps.c term_ascii.c +SRCS+= tbl_data.c tbl_layout.c tbl_option.c tbl.c tbl_term.c tbl_tree.c + PROG= mandoc .include Index: man.c =================================================================== RCS file: /cvs/src/usr.bin/mandoc/man.c,v retrieving revision 1.40 diff -u -p -r1.40 man.c --- man.c 20 Aug 2010 00:53:35 -0000 1.40 +++ man.c 12 Oct 2010 23:37:36 -0000 @@ -25,6 +25,7 @@ #include "mandoc.h" #include "libman.h" #include "libmandoc.h" +#include "tbl.h" const char *const __man_macronames[MAN_MAX] = { "br", "TH", "SH", "SS", @@ -36,7 +37,7 @@ const char *const __man_macronames[MAN_M "nf", "fi", "r", "RE", "RS", "DT", "UC", "PD", "Sp", "Vb", "Ve", "AT", - "in" + "in", "TS", "TE" }; const char * const *man_macronames = __man_macronames; @@ -121,10 +122,19 @@ man_endparse(struct man *m) int man_parseln(struct man *m, int ln, char *buf, int offs) { + struct man_node *n; if (MAN_HALT & m->flags) return(0); + n = m->last; + + if (n && MAN_TS == n->tok && MAN_BODY == n->type && + strncmp(buf+offs, ".TE", 3)) { + n = n->parent; + return(tbl_read(n->data.TS, "", ln, buf, offs) ? 1 : 0); + } + return(('.' == buf[offs] || '\'' == buf[offs]) ? man_pmacro(m, ln, buf, offs) : man_ptext(m, ln, buf, offs)); @@ -322,6 +332,8 @@ man_node_free(struct man_node *p) if (p->string) free(p->string); + if (p->data.TS) + tbl_free(p->data.TS); free(p); } Index: man.h =================================================================== RCS file: /cvs/src/usr.bin/mandoc/man.h,v retrieving revision 1.26 diff -u -p -r1.26 man.h --- man.h 20 Aug 2010 00:53:35 -0000 1.26 +++ man.h 12 Oct 2010 23:37:36 -0000 @@ -57,6 +57,8 @@ enum mant { MAN_Ve, MAN_AT, MAN_in, + MAN_TS, + MAN_TE, MAN_MAX }; @@ -95,6 +97,9 @@ struct man_node { char *string; struct man_node *head; struct man_node *body; + union { + struct tbl *TS; + } data; }; extern const char *const *man_macronames; Index: man_action.c =================================================================== RCS file: /cvs/src/usr.bin/mandoc/man_action.c,v retrieving revision 1.24 diff -u -p -r1.24 man_action.c --- man_action.c 25 Jul 2010 18:05:54 -0000 1.24 +++ man_action.c 12 Oct 2010 23:37:36 -0000 @@ -22,6 +22,7 @@ #include "mandoc.h" #include "libman.h" #include "libmandoc.h" +#include "tbl.h" struct actions { int (*post)(struct man *); @@ -32,6 +33,7 @@ static int post_fi(struct man *); static int post_nf(struct man *); static int post_AT(struct man *); static int post_UC(struct man *); +static int post_TS(struct man *); const struct actions man_actions[MAN_MAX] = { { NULL }, /* br */ @@ -71,6 +73,8 @@ const struct actions man_actions[MAN_MAX { post_fi }, /* Ve */ { post_AT }, /* AT */ { NULL }, /* in */ + { post_TS }, /* TS */ + { NULL }, /* TE */ }; @@ -273,6 +277,29 @@ post_UC(struct man *m) free(m->meta.source); m->meta.source = mandoc_strdup(p); + + return(1); +} + + +static int +post_TS(struct man *m) +{ + struct man_node *n; + + n = m->last; + + switch (n->type) { + case (MAN_HEAD): + n->parent->data.TS = tbl_alloc(); + break; + case (MAN_BODY): + if ( ! tbl_close(n->parent->data.TS, "", n->line)) + return(0); + break; + default: + break; + } return(1); } Index: man_html.c =================================================================== RCS file: /cvs/src/usr.bin/mandoc/man_html.c,v retrieving revision 1.18 diff -u -p -r1.18 man_html.c --- man_html.c 25 Jul 2010 18:05:54 -0000 1.18 +++ man_html.c 12 Oct 2010 23:37:37 -0000 @@ -113,6 +113,8 @@ static const struct htmlman mans[MAN_MAX { man_literal_pre, NULL }, /* Ve */ { man_ign_pre, NULL }, /* AT */ { man_in_pre, NULL }, /* in */ + { NULL, NULL }, /* TS */ + { NULL, NULL }, /* TE */ }; Index: man_macro.c =================================================================== RCS file: /cvs/src/usr.bin/mandoc/man_macro.c,v retrieving revision 1.20 diff -u -p -r1.20 man_macro.c --- man_macro.c 25 Jul 2010 18:05:54 -0000 1.20 +++ man_macro.c 12 Oct 2010 23:37:37 -0000 @@ -80,6 +80,8 @@ const struct man_macro __man_macros[MAN_ { in_line_eoln, 0 }, /* Ve */ { in_line_eoln, 0 }, /* AT */ { in_line_eoln, 0 }, /* in */ + { blk_exp, MAN_EXPLICIT }, /* TS */ + { blk_close, 0 }, /* TE */ }; const struct man_macro * const man_macros = __man_macros; @@ -264,6 +266,9 @@ blk_close(MACRO_PROT_ARGS) switch (tok) { case (MAN_RE): ntok = MAN_RS; + break; + case (MAN_TE): + ntok = MAN_TS; break; default: abort(); Index: man_term.c =================================================================== RCS file: /cvs/src/usr.bin/mandoc/man_term.c,v retrieving revision 1.46 diff -u -p -r1.46 man_term.c --- man_term.c 21 Sep 2010 22:33:41 -0000 1.46 +++ man_term.c 12 Oct 2010 23:37:37 -0000 @@ -28,6 +28,7 @@ #include "term.h" #include "chars.h" #include "main.h" +#include "tbl.h" #define INDENT 7 #define HALFINDENT 3 @@ -92,6 +93,7 @@ static int pre_ign(DECL_ARGS); static int pre_in(DECL_ARGS); static int pre_literal(DECL_ARGS); static int pre_sp(DECL_ARGS); +static int pre_TS(DECL_ARGS); static void post_IP(DECL_ARGS); static void post_HP(DECL_ARGS); @@ -138,6 +140,8 @@ static const struct termact termacts[MAN { pre_literal, NULL, 0 }, /* Ve */ { pre_ign, NULL, 0 }, /* AT */ { pre_in, NULL, MAN_NOTEXT }, /* in */ + { pre_TS, NULL, 0 }, /* TS */ + { NULL, NULL, 0 }, /* TE */ }; @@ -823,6 +827,18 @@ post_RS(DECL_ARGS) p->offset = term_len(p, INDENT); break; } +} + + +/* ARGSUSED */ +static int +pre_TS(DECL_ARGS) +{ + + if (MAN_BLOCK == n->type) + tbl_write(n->data.TS); + + return(0); } Index: man_validate.c =================================================================== RCS file: /cvs/src/usr.bin/mandoc/man_validate.c,v retrieving revision 1.29 diff -u -p -r1.29 man_validate.c --- man_validate.c 20 Aug 2010 00:53:35 -0000 1.29 +++ man_validate.c 12 Oct 2010 23:37:37 -0000 @@ -95,6 +95,8 @@ static const struct man_valid man_valids { pres_bline, posts_eq0 }, /* Ve */ { NULL, NULL }, /* AT */ { NULL, NULL }, /* in */ + { NULL, NULL }, /* TS */ + { NULL, NULL }, /* RE */ }; Index: tbl.c =================================================================== RCS file: tbl.c diff -N tbl.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tbl.c 12 Oct 2010 23:37:37 -0000 @@ -0,0 +1,544 @@ +/* $Id: tbl.c,v 1.14 2009/09/12 16:05:34 kristaps Exp $ */ +/* + * Copyright (c) 2009 Kristaps Dzonsons + * + * 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 + +#include +#include +#include +#include +#include +#include + +#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); +} Index: tbl.h =================================================================== RCS file: tbl.h diff -N tbl.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tbl.h 12 Oct 2010 23:37:37 -0000 @@ -0,0 +1,34 @@ +/* $Id: tbl.h,v 1.3 2009/09/11 15:01:24 kristaps Exp $ */ +/* + * Copyright (c) 2009 Kristaps Dzonsons + * + * 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*/ Index: tbl_data.c =================================================================== RCS file: tbl_data.c diff -N tbl_data.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tbl_data.c 12 Oct 2010 23:37:37 -0000 @@ -0,0 +1,130 @@ +/* $Id: data.c,v 1.11 2009/09/12 16:05:34 kristaps Exp $ */ +/* + * Copyright (c) 2009 Kristaps Dzonsons + * + * 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 +#include + +#include +#include +#include +#include + +#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); +} Index: tbl_extern.h =================================================================== RCS file: tbl_extern.h diff -N tbl_extern.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tbl_extern.h 12 Oct 2010 23:37:37 -0000 @@ -0,0 +1,183 @@ +/* $Id: extern.h,v 1.10 2009/09/13 12:37:28 kristaps Exp $ */ +/* + * Copyright (c) 2009 Kristaps Dzonsons + * + * 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*/ Index: tbl_layout.c =================================================================== RCS file: tbl_layout.c diff -N tbl_layout.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tbl_layout.c 12 Oct 2010 23:37:37 -0000 @@ -0,0 +1,280 @@ +/* $Id: layout.c,v 1.7 2009/09/11 13:24:04 kristaps Exp $ */ +/* + * Copyright (c) 2009 Kristaps Dzonsons + * + * 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 +#include + +#include +#include +#include +#include + +#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)); +} Index: tbl_option.c =================================================================== RCS file: tbl_option.c diff -N tbl_option.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tbl_option.c 12 Oct 2010 23:37:37 -0000 @@ -0,0 +1,195 @@ +/* $Id: option.c,v 1.5 2009/09/09 12:51:34 kristaps Exp $ */ +/* + * Copyright (c) 2009 Kristaps Dzonsons + * + * 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 + +#include +#include + +#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)); +} Index: tbl_term.c =================================================================== RCS file: tbl_term.c diff -N tbl_term.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tbl_term.c 12 Oct 2010 23:37:37 -0000 @@ -0,0 +1,504 @@ +/* $Id: term.c,v 1.13 2009/09/14 09:06:40 kristaps Exp $ */ +/* + * Copyright (c) 2009 Kristaps Dzonsons + * + * 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 + +#include +#include +#include +#include + +#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); +} Index: tbl_tree.c =================================================================== RCS file: tbl_tree.c diff -N tbl_tree.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tbl_tree.c 12 Oct 2010 23:37:37 -0000 @@ -0,0 +1,86 @@ +/* $Id: tree.c,v 1.2 2009/09/11 13:24:04 kristaps Exp $ */ +/* + * Copyright (c) 2009 Kristaps Dzonsons + * + * 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 + +#include +#include +#include +#include + +#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); +} -- To unsubscribe send an email to tech+unsubscribe@mdocml.bsd.lv