diff options
author | Paul Traina <pst@FreeBSD.org> | 1996-09-07 16:18:32 +0000 |
---|---|---|
committer | Paul Traina <pst@FreeBSD.org> | 1996-09-07 16:18:32 +0000 |
commit | 4e5867aa82604bc07408d50845eff8fa467f671b (patch) | |
tree | 0abcbad9804fcf7a7157983937cadcf61449b840 /contrib/groff/tbl |
Virgin import of FSF groff v1.10
Notes
Notes:
svn path=/vendor/groff/dist/; revision=18099
Diffstat (limited to 'contrib/groff/tbl')
-rw-r--r-- | contrib/groff/tbl/Makefile.dep | 6 | ||||
-rw-r--r-- | contrib/groff/tbl/Makefile.sub | 7 | ||||
-rw-r--r-- | contrib/groff/tbl/main.cc | 1511 | ||||
-rw-r--r-- | contrib/groff/tbl/table.cc | 2778 | ||||
-rw-r--r-- | contrib/groff/tbl/table.h | 152 | ||||
-rw-r--r-- | contrib/groff/tbl/tbl.man | 178 |
6 files changed, 4632 insertions, 0 deletions
diff --git a/contrib/groff/tbl/Makefile.dep b/contrib/groff/tbl/Makefile.dep new file mode 100644 index 000000000000..fd68809e2112 --- /dev/null +++ b/contrib/groff/tbl/Makefile.dep @@ -0,0 +1,6 @@ +main.o: main.cc table.h ../include/cset.h ../include/cmap.h \ + ../include/stringclass.h ../include/errarg.h ../include/error.h \ + ../include/lib.h +table.o: table.cc table.h ../include/cset.h ../include/cmap.h \ + ../include/stringclass.h ../include/errarg.h ../include/error.h \ + ../include/lib.h diff --git a/contrib/groff/tbl/Makefile.sub b/contrib/groff/tbl/Makefile.sub new file mode 100644 index 000000000000..8ab4a74785e0 --- /dev/null +++ b/contrib/groff/tbl/Makefile.sub @@ -0,0 +1,7 @@ +PROG=tbl +MAN1=tbl.n +XLIBS=$(LIBGROFF) +OBJS=main.o table.o +CCSRCS=main.cc table.cc +HDRS=table.h +NAMEPREFIX=$(g) diff --git a/contrib/groff/tbl/main.cc b/contrib/groff/tbl/main.cc new file mode 100644 index 000000000000..0b79bc81e97c --- /dev/null +++ b/contrib/groff/tbl/main.cc @@ -0,0 +1,1511 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "table.h" + +#define MAX_POINT_SIZE 99 +#define MAX_VERTICAL_SPACING 72 + +static int compatible_flag = 0; + +class table_input { + FILE *fp; + enum { START, MIDDLE, REREAD_T, REREAD_TE, REREAD_E, END, ERROR } state; + string unget_stack; +public: + table_input(FILE *); + int get(); + int ended() { return unget_stack.empty() && state == END; } + void unget(char); +}; + +table_input::table_input(FILE *p) +: fp(p), state(START) +{ +} + +void table_input::unget(char c) +{ + assert(c != '\0'); + unget_stack += c; + if (c == '\n') + current_lineno--; +} + +int table_input::get() +{ + int len = unget_stack.length(); + if (len != 0) { + unsigned char c = unget_stack[len - 1]; + unget_stack.set_length(len - 1); + if (c == '\n') + current_lineno++; + return c; + } + int c; + for (;;) { + switch (state) { + case START: + if ((c = getc(fp)) == '.') { + if ((c = getc(fp)) == 'T') { + if ((c = getc(fp)) == 'E') { + if (compatible_flag) { + state = END; + return EOF; + } + else { + c = getc(fp); + if (c != EOF) + ungetc(c, fp); + if (c == EOF || c == ' ' || c == '\n') { + state = END; + return EOF; + } + state = REREAD_TE; + return '.'; + } + } + else { + if (c != EOF) + ungetc(c, fp); + state = REREAD_T; + return '.'; + } + } + else { + if (c != EOF) + ungetc(c, fp); + state = MIDDLE; + return '.'; + } + } + else if (c == EOF) { + state = ERROR; + return EOF; + } + else { + if (c == '\n') + current_lineno++; + else { + state = MIDDLE; + if (c == '\0') { + error("illegal input character code 0"); + break; + } + } + return c; + } + break; + case MIDDLE: + // handle line continuation + if ((c = getc(fp)) == '\\') { + c = getc(fp); + if (c == '\n') + c = getc(fp); // perhaps state ought to be START now + else { + if (c != EOF) + ungetc(c, fp); + c = '\\'; + } + } + if (c == EOF) { + state = ERROR; + return EOF; + } + else { + if (c == '\n') { + state = START; + current_lineno++; + } + else if (c == '\0') { + error("illegal input character code 0"); + break; + } + return c; + } + case REREAD_T: + state = MIDDLE; + return 'T'; + case REREAD_TE: + state = REREAD_E; + return 'T'; + case REREAD_E: + state = MIDDLE; + return 'E'; + case END: + case ERROR: + return EOF; + } + } +} + +void process_input_file(FILE *); +void process_table(table_input &in); + +void process_input_file(FILE *fp) +{ + enum { START, MIDDLE, HAD_DOT, HAD_T, HAD_TS, HAD_l, HAD_lf } state; + state = START; + int c; + while ((c = getc(fp)) != EOF) + switch (state) { + case START: + if (c == '.') + state = HAD_DOT; + else { + if (c == '\n') + current_lineno++; + else + state = MIDDLE; + putchar(c); + } + break; + case MIDDLE: + if (c == '\n') { + current_lineno++; + state = START; + } + putchar(c); + break; + case HAD_DOT: + if (c == 'T') + state = HAD_T; + else if (c == 'l') + state = HAD_l; + else { + putchar('.'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_T: + if (c == 'S') + state = HAD_TS; + else { + putchar('.'); + putchar('T'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_TS: + if (c == ' ' || c == '\n' || compatible_flag) { + putchar('.'); + putchar('T'); + putchar('S'); + while (c != '\n') { + if (c == EOF) { + error("end of file at beginning of table"); + return; + } + putchar(c); + c = getc(fp); + } + putchar('\n'); + current_lineno++; + { + table_input input(fp); + process_table(input); + set_troff_location(current_filename, current_lineno); + if (input.ended()) { + fputs(".TE", stdout); + while ((c = getc(fp)) != '\n') { + if (c == EOF) { + putchar('\n'); + return; + } + putchar(c); + } + putchar('\n'); + current_lineno++; + } + } + state = START; + } + else { + fputs(".TS", stdout); + putchar(c); + state = MIDDLE; + } + break; + case HAD_l: + if (c == 'f') + state = HAD_lf; + else { + putchar('.'); + putchar('l'); + putchar(c); + if (c == '\n') { + current_lineno++; + state = START; + } + else + state = MIDDLE; + } + break; + case HAD_lf: + if (c == ' ' || c == '\n' || compatible_flag) { + string line; + while (c != EOF) { + line += c; + if (c == '\n') { + current_lineno++; + break; + } + c = getc(fp); + } + line += '\0'; + interpret_lf_args(line.contents()); + printf(".lf%s", line.contents()); + state = START; + } + else { + fputs(".lf", stdout); + putchar(c); + state = MIDDLE; + } + break; + default: + assert(0); + } + switch(state) { + case START: + break; + case MIDDLE: + putchar('\n'); + break; + case HAD_DOT: + fputs(".\n", stdout); + break; + case HAD_l: + fputs(".l\n", stdout); + break; + case HAD_T: + fputs(".T\n", stdout); + break; + case HAD_lf: + fputs(".lf\n", stdout); + break; + case HAD_TS: + fputs(".TS\n", stdout); + break; + } + if (fp != stdin) + fclose(fp); +} + +struct options { + unsigned flags; + int linesize; + char delim[2]; + char tab_char; + char decimal_point_char; + + options(); +}; + +options::options() +: flags(0), tab_char('\t'), decimal_point_char('.'), linesize(0) +{ + delim[0] = delim[1] = '\0'; +} + +// Return non-zero if p and q are the same ignoring case. + +int strieq(const char *p, const char *q) +{ + for (; cmlower(*p) == cmlower(*q); p++, q++) + if (*p == '\0') + return 1; + return 0; +} + +// return 0 if we should give up in this table + +options *process_options(table_input &in) +{ + options *opt = new options; + string line; + int level = 0; + for (;;) { + int c = in.get(); + if (c == EOF) { + int i = line.length(); + while (--i >= 0) + in.unget(line[i]); + return opt; + } + if (c == '\n') { + in.unget(c); + int i = line.length(); + while (--i >= 0) + in.unget(line[i]); + return opt; + } + else if (c == '(') + level++; + else if (c == ')') + level--; + else if (c == ';' && level == 0) { + line += '\0'; + break; + } + line += c; + } + if (line.empty()) + return opt; + char *p = &line[0]; + for (;;) { + while (csspace(*p) || *p == ',') + p++; + if (*p == '\0') + break; + char *q = p; + while (*q != ' ' && *q != '\0' && *q != '\t' && *q != ',' && *q != '(') + q++; + char *arg = 0; + if (*q != '(' && *q != '\0') + *q++ = '\0'; + while (csspace(*q)) + q++; + if (*q == '(') { + *q++ = '\0'; + arg = q; + while (*q != ')' && *q != '\0') + q++; + if (*q == '\0') + error("missing `)'"); + else + *q++ = '\0'; + } + if (*p == '\0') { + if (arg) + error("argument without option"); + } + else if (strieq(p, "tab")) { + if (!arg) + error("`tab' option requires argument in parentheses"); + else { + if (arg[0] == '\0' || arg[1] != '\0') + error("argument to `tab' option must be a single character"); + else + opt->tab_char = arg[0]; + } + } + else if (strieq(p, "linesize")) { + if (!arg) + error("`linesize' option requires argument in parentheses"); + else { + if (sscanf(arg, "%d", &opt->linesize) != 1) + error("bad linesize `%s'", arg); + else if (opt->linesize <= 0) { + error("linesize must be positive"); + opt->linesize = 0; + } + } + } + else if (strieq(p, "delim")) { + if (!arg) + error("`delim' option requires argument in parentheses"); + else if (arg[0] == '\0' || arg[1] == '\0' || arg[2] != '\0') + error("argument to `delim' option must be two characters"); + else { + opt->delim[0] = arg[0]; + opt->delim[1] = arg[1]; + } + } + else if (strieq(p, "center") || strieq(p, "centre")) { + if (arg) + error("`center' option does not take a argument"); + opt->flags |= table::CENTER; + } + else if (strieq(p, "expand")) { + if (arg) + error("`expand' option does not take a argument"); + opt->flags |= table::EXPAND; + } + else if (strieq(p, "box") || strieq(p, "frame")) { + if (arg) + error("`box' option does not take a argument"); + opt->flags |= table::BOX; + } + else if (strieq(p, "doublebox") || strieq(p, "doubleframe")) { + if (arg) + error("`doublebox' option does not take a argument"); + opt->flags |= table::DOUBLEBOX; + } + else if (strieq(p, "allbox")) { + if (arg) + error("`allbox' option does not take a argument"); + opt->flags |= table::ALLBOX; + } + else if (strieq(p, "nokeep")) { + if (arg) + error("`nokeep' option does not take a argument"); + opt->flags |= table::NOKEEP; + } + else if (strieq(p, "decimalpoint")) { + if (!arg) + error("`decimalpoint' option requires argument in parentheses"); + else { + if (arg[0] == '\0' || arg[1] != '\0') + error("argument to `decimalpoint' option must be a single character"); + else + opt->decimal_point_char = arg[0]; + } + } + else { + error("unrecognised global option `%1'", p); + // delete opt; + // return 0; + } + p = q; + } + return opt; +} + +entry_modifier::entry_modifier() +: vertical_alignment(CENTER), zero_width(0), stagger(0) +{ + vertical_spacing.inc = vertical_spacing.val = 0; + point_size.inc = point_size.val = 0; +} + +entry_modifier::~entry_modifier() +{ +} + +entry_format::entry_format() : type(FORMAT_LEFT) +{ +} + +entry_format::entry_format(format_type t) : type(t) +{ +} + +void entry_format::debug_print() const +{ + switch (type) { + case FORMAT_LEFT: + putc('l', stderr); + break; + case FORMAT_CENTER: + putc('c', stderr); + break; + case FORMAT_RIGHT: + putc('r', stderr); + break; + case FORMAT_NUMERIC: + putc('n', stderr); + break; + case FORMAT_ALPHABETIC: + putc('a', stderr); + break; + case FORMAT_SPAN: + putc('s', stderr); + break; + case FORMAT_VSPAN: + putc('^', stderr); + break; + case FORMAT_HLINE: + putc('_', stderr); + break; + case FORMAT_DOUBLE_HLINE: + putc('=', stderr); + break; + default: + assert(0); + break; + } + if (point_size.val != 0) { + putc('p', stderr); + if (point_size.inc > 0) + putc('+', stderr); + else if (point_size.inc < 0) + putc('-', stderr); + fprintf(stderr, "%d ", point_size.val); + } + if (vertical_spacing.val != 0) { + putc('v', stderr); + if (vertical_spacing.inc > 0) + putc('+', stderr); + else if (vertical_spacing.inc < 0) + putc('-', stderr); + fprintf(stderr, "%d ", vertical_spacing.val); + } + if (!font.empty()) { + putc('f', stderr); + put_string(font, stderr); + putc(' ', stderr); + } + switch (vertical_alignment) { + case entry_modifier::CENTER: + break; + case entry_modifier::TOP: + putc('t', stderr); + break; + case entry_modifier::BOTTOM: + putc('d', stderr); + break; + } + if (zero_width) + putc('z', stderr); + if (stagger) + putc('u', stderr); +} + +struct format { + int nrows; + int ncolumns; + int *separation; + string *width; + char *equal; + entry_format **entry; + char **vline; + + format(int nr, int nc); + ~format(); + void add_rows(int n); +}; + +format::format(int nr, int nc) : nrows(nr), ncolumns(nc) +{ + int i; + separation = ncolumns > 1 ? new int[ncolumns - 1] : 0; + for (i = 0; i < ncolumns-1; i++) + separation[i] = -1; + width = new string[ncolumns]; + equal = new char[ncolumns]; + for (i = 0; i < ncolumns; i++) + equal[i] = 0; + entry = new entry_format *[nrows]; + for (i = 0; i < nrows; i++) + entry[i] = new entry_format[ncolumns]; + vline = new char*[nrows]; + for (i = 0; i < nrows; i++) { + vline[i] = new char[ncolumns+1]; + for (int j = 0; j < ncolumns+1; j++) + vline[i][j] = 0; + } +} + +void format::add_rows(int n) +{ + int i; + char **old_vline = vline; + vline = new char*[nrows + n]; + for (i = 0; i < nrows; i++) + vline[i] = old_vline[i]; + a_delete old_vline; + for (i = 0; i < n; i++) { + vline[nrows + i] = new char[ncolumns + 1]; + for (int j = 0; j < ncolumns + 1; j++) + vline[nrows + i][j] = 0; + } + entry_format **old_entry = entry; + entry = new entry_format *[nrows + n]; + for (i = 0; i < nrows; i++) + entry[i] = old_entry[i]; + a_delete old_entry; + for (i = 0; i < n; i++) + entry[nrows + i] = new entry_format[ncolumns]; + nrows += n; +} + +format::~format() +{ + a_delete separation; + ad_delete(ncolumns) width; + a_delete equal; + for (int i = 0; i < nrows; i++) { + a_delete vline[i]; + ad_delete(ncolumns) entry[i]; + } + a_delete vline; + a_delete entry; +} + +struct input_entry_format : public entry_format { + input_entry_format *next; + string width; + int separation; + int vline; + int pre_vline; + int last_column; + int equal; + input_entry_format(format_type, input_entry_format * = 0); + ~input_entry_format(); + void debug_print(); +}; + +input_entry_format::input_entry_format(format_type t, input_entry_format *p) +: entry_format(t), next(p) +{ + separation = -1; + last_column = 0; + vline = 0; + pre_vline = 0; + equal = 0; +} + +input_entry_format::~input_entry_format() +{ +} + +void free_input_entry_format_list(input_entry_format *list) +{ + while (list) { + input_entry_format *tem = list; + list = list->next; + delete tem; + } +} + +void input_entry_format::debug_print() +{ + int i; + for (i = 0; i < pre_vline; i++) + putc('|', stderr); + entry_format::debug_print(); + if (!width.empty()) { + putc('w', stderr); + putc('(', stderr); + put_string(width, stderr); + putc(')', stderr); + } + if (equal) + putc('e', stderr); + if (separation >= 0) + fprintf(stderr, "%d", separation); + for (i = 0; i < vline; i++) + putc('|', stderr); + if (last_column) + putc(',', stderr); +} + +// Return zero if we should give up on this table. +// If this is a continuation format line, current_format will be the current +// format line. + +format *process_format(table_input &in, options *opt, + format *current_format = 0) +{ + input_entry_format *list = 0; + int c = in.get(); + for (;;) { + int pre_vline = 0; + int got_format = 0; + int got_period = 0; + format_type t; + for (;;) { + if (c == EOF) { + error("end of input while processing format"); + free_input_entry_format_list(list); + return 0; + } + switch (c) { + case 'n': + case 'N': + t = FORMAT_NUMERIC; + got_format = 1; + break; + case 'a': + case 'A': + got_format = 1; + t = FORMAT_ALPHABETIC; + break; + case 'c': + case 'C': + got_format = 1; + t = FORMAT_CENTER; + break; + case 'l': + case 'L': + got_format = 1; + t = FORMAT_LEFT; + break; + case 'r': + case 'R': + got_format = 1; + t = FORMAT_RIGHT; + break; + case 's': + case 'S': + got_format = 1; + t = FORMAT_SPAN; + break; + case '^': + got_format = 1; + t = FORMAT_VSPAN; + break; + case '_': + case '-': // tbl also accepts this + got_format = 1; + t = FORMAT_HLINE; + break; + case '=': + got_format = 1; + t = FORMAT_DOUBLE_HLINE; + break; + case '.': + got_period = 1; + break; + case '|': + pre_vline++; + break; + case ' ': + case '\t': + case '\n': + break; + default: + if (c == opt->tab_char) + break; + error("unrecognised format `%1'", char(c)); + free_input_entry_format_list(list); + return 0; + } + if (got_period) + break; + c = in.get(); + if (got_format) + break; + } + if (got_period) + break; + list = new input_entry_format(t, list); + if (pre_vline) + list->pre_vline = pre_vline; + int success = 1; + do { + switch (c) { + case 't': + case 'T': + c = in.get(); + list->vertical_alignment = entry_modifier::TOP; + break; + case 'd': + case 'D': + c = in.get(); + list->vertical_alignment = entry_modifier::BOTTOM; + break; + case 'u': + case 'U': + c = in.get(); + list->stagger = 1; + break; + case 'z': + case 'Z': + c = in.get(); + list->zero_width = 1; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + int w = 0; + do { + w = w*10 + (c - '0'); + c = in.get(); + } while (c != EOF && csdigit(c)); + list->separation = w; + } + break; + case 'f': + case 'F': + do { + c = in.get(); + } while (c == ' ' || c == '\t'); + if (c == EOF) { + error("missing font name"); + break; + } + if (c == '(') { + for (;;) { + c = in.get(); + if (c == EOF || c == ' ' || c == '\t') { + error("missing `)'"); + break; + } + if (c == ')') { + c = in.get(); + break; + } + list->font += char(c); + } + } + else { + list->font = c; + char cc = c; + c = in.get(); + if (!csdigit(cc) + && c != EOF && c != ' ' && c != '\t' && c != '.' && c != '\n') { + list->font += char(c); + c = in.get(); + } + } + break; + case 'v': + case 'V': + c = in.get(); + list->vertical_spacing.val = 0; + list->vertical_spacing.inc = 0; + if (c == '+' || c == '-') { + list->vertical_spacing.inc = (c == '+' ? 1 : -1); + c = in.get(); + } + if (c == EOF || !csdigit(c)) { + error("`v' modifier must be followed by number"); + list->vertical_spacing.inc = 0; + } + else { + do { + list->vertical_spacing.val *= 10; + list->vertical_spacing.val += c - '0'; + c = in.get(); + } while (c != EOF && csdigit(c)); + } + if (list->vertical_spacing.val > MAX_VERTICAL_SPACING + || list->vertical_spacing.val < -MAX_VERTICAL_SPACING) { + error("unreasonable point size"); + list->vertical_spacing.val = 0; + list->vertical_spacing.inc = 0; + } + break; + case 'p': + case 'P': + c = in.get(); + list->point_size.val = 0; + list->point_size.inc = 0; + if (c == '+' || c == '-') { + list->point_size.inc = (c == '+' ? 1 : -1); + c = in.get(); + } + if (c == EOF || !csdigit(c)) { + error("`p' modifier must be followed by number"); + list->point_size.inc = 0; + } + else { + do { + list->point_size.val *= 10; + list->point_size.val += c - '0'; + c = in.get(); + } while (c != EOF && csdigit(c)); + } + if (list->point_size.val > MAX_POINT_SIZE + || list->point_size.val < -MAX_POINT_SIZE) { + error("unreasonable point size"); + list->point_size.val = 0; + list->point_size.inc = 0; + } + break; + case 'w': + case 'W': + c = in.get(); + while (c == ' ' || c == '\t') + c = in.get(); + if (c == '(') { + list->width = ""; + c = in.get(); + while (c != ')') { + if (c == EOF || c == '\n') { + error("missing `)'"); + free_input_entry_format_list(list); + return 0; + } + list->width += c; + c = in.get(); + } + c = in.get(); + } + else { + if (c == '+' || c == '-') { + list->width = char(c); + c = in.get(); + } + else + list->width = ""; + if (c == EOF || !csdigit(c)) + error("bad argument for `w' modifier"); + else { + do { + list->width += char(c); + c = in.get(); + } while (c != EOF && csdigit(c)); + } + } + break; + case 'e': + case 'E': + c = in.get(); + list->equal++; + break; + case '|': + c = in.get(); + list->vline++; + break; + case 'B': + case 'b': + c = in.get(); + list->font = "B"; + break; + case 'I': + case 'i': + c = in.get(); + list->font = "I"; + break; + case ' ': + case '\t': + c = in.get(); + break; + default: + if (c == opt->tab_char) + c = in.get(); + else + success = 0; + break; + } + } while (success); + if (list->vline > 2) { + list->vline = 2; + error("more than 2 vertical bars between key letters"); + } + if (c == '\n' || c == ',') { + c = in.get(); + list->last_column = 1; + } + } + if (c == '.') { + do { + c = in.get(); + } while (c == ' ' || c == '\t'); + if (c != '\n') { + error("`.' not last character on line"); + free_input_entry_format_list(list); + return 0; + } + } + if (!list) { + error("no format"); + free_input_entry_format_list(list); + return 0; + } + list->last_column = 1; + // now reverse the list so that the first row is at the beginning + input_entry_format *rev = 0; + while (list != 0) { + input_entry_format *tem = list->next; + list->next = rev; + rev = list; + list = tem; + } + list = rev; + input_entry_format *tem; + +#if 0 + for (tem = list; tem; tem = tem->next) + tem->debug_print(); + putc('\n', stderr); +#endif + // compute number of columns and rows + int ncolumns = 0; + int nrows = 0; + int col = 0; + for (tem = list; tem; tem = tem->next) { + if (tem->last_column) { + if (col >= ncolumns) + ncolumns = col + 1; + col = 0; + nrows++; + } + else + col++; + } + int row; + format *f; + if (current_format) { + if (ncolumns > current_format->ncolumns) { + error("cannot increase the number of columns in a continued format"); + free_input_entry_format_list(list); + return 0; + } + f = current_format; + row = f->nrows; + f->add_rows(nrows); + } + else { + f = new format(nrows, ncolumns); + row = 0; + } + col = 0; + for (tem = list; tem; tem = tem->next) { + f->entry[row][col] = *tem; + if (col < ncolumns-1) { + // use the greatest separation + if (tem->separation > f->separation[col]) { + if (current_format) + error("cannot change column separation in continued format"); + else + f->separation[col] = tem->separation; + } + } + else if (tem->separation >= 0) + error("column separation specified for last column"); + if (tem->equal && !f->equal[col]) { + if (current_format) + error("cannot change which columns are equal in continued format"); + else + f->equal[col] = 1; + } + if (!tem->width.empty()) { + // use the last width + if (!f->width[col].empty() && f->width[col] != tem->width) + error("multiple widths for column %1", col+1); + f->width[col] = tem->width; + } + if (tem->pre_vline) { + assert(col == 0); + f->vline[row][col] = tem->pre_vline; + } + f->vline[row][col+1] = tem->vline; + if (tem->last_column) { + row++; + col = 0; + } + else + col++; + } + free_input_entry_format_list(list); + for (col = 0; col < ncolumns; col++) { + entry_format *e = f->entry[f->nrows-1] + col; + if (e->type != FORMAT_HLINE + && e->type != FORMAT_DOUBLE_HLINE + && e->type != FORMAT_SPAN) + break; + } + if (col >= ncolumns) { + error("last row of format is all lines"); + delete f; + return 0; + } + return f; +} + +table *process_data(table_input &in, format *f, options *opt) +{ + char tab_char = opt->tab_char; + int ncolumns = f->ncolumns; + int current_row = 0; + int format_index = 0; + int give_up = 0; + enum { DATA_INPUT_LINE, TROFF_INPUT_LINE, SINGLE_HLINE, DOUBLE_HLINE } type; + table *tbl = new table(ncolumns, opt->flags, opt->linesize, + opt->decimal_point_char); + if (opt->delim[0] != '\0') + tbl->set_delim(opt->delim[0], opt->delim[1]); + for (;;) { + // first determine what type of line this is + int c = in.get(); + if (c == EOF) + break; + if (c == '.') { + int d = in.get(); + if (d != EOF && csdigit(d)) { + in.unget(d); + type = DATA_INPUT_LINE; + } + else { + in.unget(d); + type = TROFF_INPUT_LINE; + } + } + else if (c == '_' || c == '=') { + int d = in.get(); + if (d == '\n') { + if (c == '_') + type = SINGLE_HLINE; + else + type = DOUBLE_HLINE; + } + else { + in.unget(d); + type = DATA_INPUT_LINE; + } + } + else { + type = DATA_INPUT_LINE; + } + switch (type) { + case DATA_INPUT_LINE: + { + string input_entry; + if (format_index >= f->nrows) + format_index = f->nrows - 1; + // A format row that is all lines doesn't use up a data line. + while (format_index < f->nrows - 1) { + int c; + for (c = 0; c < ncolumns; c++) { + entry_format *e = f->entry[format_index] + c; + if (e->type != FORMAT_HLINE + && e->type != FORMAT_DOUBLE_HLINE + // Unfortunately tbl treats a span as needing data. + // && e->type != FORMAT_SPAN + ) + break; + } + if (c < ncolumns) + break; + for (c = 0; c < ncolumns; c++) + tbl->add_entry(current_row, c, input_entry, + f->entry[format_index] + c, current_filename, + current_lineno); + tbl->add_vlines(current_row, f->vline[format_index]); + format_index++; + current_row++; + } + entry_format *line_format = f->entry[format_index]; + int col = 0; + int row_comment = 0; + for (;;) { + if (c == tab_char || c == '\n') { + int ln = current_lineno; + if (c == '\n') + --ln; + while (col < ncolumns + && line_format[col].type == FORMAT_SPAN) { + tbl->add_entry(current_row, col, "", &line_format[col], + current_filename, ln); + col++; + } + if (c == '\n' && input_entry.length() == 2 + && input_entry[0] == 'T' && input_entry[1] == '{') { + input_entry = ""; + ln++; + enum { + START, MIDDLE, GOT_T, GOT_RIGHT_BRACE, GOT_DOT, + GOT_l, GOT_lf, END + } state = START; + while (state != END) { + c = in.get(); + if (c == EOF) + break; + switch (state) { + case START: + if (c == 'T') + state = GOT_T; + else if (c == '.') + state = GOT_DOT; + else { + input_entry += c; + if (c != '\n') + state = MIDDLE; + } + break; + case GOT_T: + if (c == '}') + state = GOT_RIGHT_BRACE; + else { + input_entry += 'T'; + input_entry += c; + state = c == '\n' ? START : MIDDLE; + } + break; + case GOT_DOT: + if (c == 'l') + state = GOT_l; + else { + input_entry += '.'; + input_entry += c; + state = c == '\n' ? START : MIDDLE; + } + break; + case GOT_l: + if (c == 'f') + state = GOT_lf; + else { + input_entry += ".l"; + input_entry += c; + state = c == '\n' ? START : MIDDLE; + } + break; + case GOT_lf: + if (c == ' ' || c == '\n' || compatible_flag) { + string args; + input_entry += ".lf"; + while (c != EOF) { + args += c; + if (c == '\n') + break; + c = in.get(); + } + args += '\0'; + interpret_lf_args(args.contents()); + // remove the '\0' + args.set_length(args.length() - 1); + input_entry += args; + state = START; + } + else { + input_entry += ".lf"; + input_entry += c; + state = MIDDLE; + } + break; + case GOT_RIGHT_BRACE: + if (c == '\n' || c == tab_char) + state = END; + else { + input_entry += 'T'; + input_entry += '}'; + input_entry += c; + state = c == '\n' ? START : MIDDLE; + } + break; + case MIDDLE: + if (c == '\n') + state = START; + input_entry += c; + break; + case END: + default: + assert(0); + } + } + if (c == EOF) { + error("end of data in middle of text block"); + give_up = 1; + break; + } + } + if (col >= ncolumns) { + if (!input_entry.empty()) { + if (input_entry.length() >= 2 + && input_entry[0] == '\\' + && input_entry[1] == '"') + row_comment = 1; + else if (!row_comment) { + if (c == '\n') + in.unget(c); + input_entry += '\0'; + error("excess data entry `%1' discarded", + input_entry.contents()); + if (c == '\n') + (void)in.get(); + } + } + } + else + tbl->add_entry(current_row, col, input_entry, + &line_format[col], current_filename, ln); + col++; + if (c == '\n') + break; + input_entry = ""; + } + else + input_entry += c; + c = in.get(); + if (c == EOF) + break; + } + if (give_up) + break; + input_entry = ""; + for (; col < ncolumns; col++) + tbl->add_entry(current_row, col, input_entry, &line_format[col], + current_filename, current_lineno - 1); + tbl->add_vlines(current_row, f->vline[format_index]); + current_row++; + format_index++; + } + break; + case TROFF_INPUT_LINE: + { + string line; + int ln = current_lineno; + for (;;) { + line += c; + if (c == '\n') + break; + c = in.get(); + if (c == EOF) { + break; + } + } + tbl->add_text_line(current_row, line, current_filename, ln); + if (line.length() >= 4 + && line[0] == '.' && line[1] == 'T' && line[2] == '&') { + format *newf = process_format(in, opt, f); + if (newf == 0) + give_up = 1; + else + f = newf; + } + if (line.length() >= 3 + && line[0] == '.' && line[1] == 'f' && line[2] == 'f') { + line += '\0'; + interpret_lf_args(line.contents() + 3); + } + } + break; + case SINGLE_HLINE: + tbl->add_single_hline(current_row); + break; + case DOUBLE_HLINE: + tbl->add_double_hline(current_row); + break; + default: + assert(0); + } + if (give_up) + break; + } + if (!give_up && current_row == 0) { + error("no real data"); + give_up = 1; + } + if (give_up) { + delete tbl; + return 0; + } + // Do this here rather than at the beginning in case continued formats + // change it. + int i; + for (i = 0; i < ncolumns - 1; i++) + if (f->separation[i] >= 0) + tbl->set_column_separation(i, f->separation[i]); + for (i = 0; i < ncolumns; i++) + if (!f->width[i].empty()) + tbl->set_minimum_width(i, f->width[i]); + for (i = 0; i < ncolumns; i++) + if (f->equal[i]) + tbl->set_equal_column(i); + return tbl; +} + +void process_table(table_input &in) +{ + int c; + options *opt = 0; + format *form = 0; + table *tbl = 0; + if ((opt = process_options(in)) != 0 + && (form = process_format(in, opt)) != 0 + && (tbl = process_data(in, form, opt)) != 0) { + tbl->print(); + delete tbl; + } + else { + error("giving up on this table"); + while ((c = in.get()) != EOF) + ; + } + delete opt; + delete form; + if (!in.ended()) + error("premature end of file"); +} + +static void usage() +{ + fprintf(stderr, "usage: %s [ -vC ] [ files... ]\n", program_name); + exit(1); +} + +int main(int argc, char **argv) +{ + program_name = argv[0]; + static char stderr_buf[BUFSIZ]; + setbuf(stderr, stderr_buf); + int opt; + while ((opt = getopt(argc, argv, "vCT:")) != EOF) + switch (opt) { + case 'C': + compatible_flag = 1; + break; + case 'v': + { + extern const char *version_string; + fprintf(stderr, "GNU tbl version %s\n", version_string); + fflush(stderr); + break; + } + case 'T': + // I'm sick of getting bug reports from IRIX users + break; + case '?': + usage(); + break; + default: + assert(0); + } + printf(".if !\\n(.g .ab GNU tbl requires GNU troff.\n" + ".if !dTS .ds TS\n" + ".if !dTE .ds TE\n"); + if (argc > optind) { + for (int i = optind; i < argc; i++) + if (argv[i][0] == '-' && argv[i][1] == '\0') { + current_filename = "-"; + current_lineno = 1; + printf(".lf 1 -\n"); + process_input_file(stdin); + } + else { + errno = 0; + FILE *fp = fopen(argv[i], "r"); + if (fp == 0) { + current_lineno = -1; + error("can't open `%1': %2", argv[i], strerror(errno)); + } + else { + current_lineno = 1; + current_filename = argv[i]; + printf(".lf 1 %s\n", current_filename); + process_input_file(fp); + } + } + } + else { + current_filename = "-"; + current_lineno = 1; + printf(".lf 1 -\n"); + process_input_file(stdin); + } + if (ferror(stdout) || fflush(stdout) < 0) + fatal("output error"); + return 0; +} + diff --git a/contrib/groff/tbl/table.cc b/contrib/groff/tbl/table.cc new file mode 100644 index 000000000000..160ff9012558 --- /dev/null +++ b/contrib/groff/tbl/table.cc @@ -0,0 +1,2778 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "table.h" + +#define BAR_HEIGHT ".25m" +#define DOUBLE_LINE_SEP "2p" +#define HALF_DOUBLE_LINE_SEP "1p" +#define LINE_SEP "2p" +#define BODY_DEPTH ".25m" + +const int DEFAULT_COLUMN_SEPARATION = 3; + +#define DELIMITER_CHAR "\\[tbl]" +#define PREFIX "3" +#define SEPARATION_FACTOR_REG PREFIX "sep" +#define BOTTOM_REG PREFIX "bot" +#define RESET_MACRO_NAME PREFIX "init" +#define LINESIZE_REG PREFIX "lps" +#define TOP_REG PREFIX "top" +#define CURRENT_ROW_REG PREFIX "crow" +#define LAST_PASSED_ROW_REG PREFIX "passed" +#define TRANSPARENT_STRING_NAME PREFIX "trans" +#define QUOTE_STRING_NAME PREFIX "quote" +#define SECTION_DIVERSION_NAME PREFIX "section" +#define SECTION_DIVERSION_FLAG_REG PREFIX "sflag" +#define SAVED_VERTICAL_POS_REG PREFIX "vert" +#define NEED_BOTTOM_RULE_REG PREFIX "brule" +#define KEEP_MACRO_NAME PREFIX "keep" +#define RELEASE_MACRO_NAME PREFIX "release" +#define SAVED_FONT_REG PREFIX "fnt" +#define SAVED_SIZE_REG PREFIX "sz" +#define SAVED_FILL_REG PREFIX "fll" +#define SAVED_INDENT_REG PREFIX "ind" +#define SAVED_CENTER_REG PREFIX "cent" +#define TABLE_DIVERSION_NAME PREFIX "table" +#define TABLE_DIVERSION_FLAG_REG PREFIX "tflag" +#define TABLE_KEEP_MACRO_NAME PREFIX "tkeep" +#define TABLE_RELEASE_MACRO_NAME PREFIX "trelease" +#define NEEDED_REG PREFIX "needed" +#define REPEATED_MARK_MACRO PREFIX "rmk" +#define REPEATED_VPT_MACRO PREFIX "rvpt" +#define SUPPRESS_BOTTOM_REG PREFIX "supbot" +#define SAVED_DN_REG PREFIX "dn" + +// this must be one character +#define COMPATIBLE_REG PREFIX "c" + +#define BLOCK_WIDTH_PREFIX PREFIX "tbw" +#define BLOCK_DIVERSION_PREFIX PREFIX "tbd" +#define BLOCK_HEIGHT_PREFIX PREFIX "tbh" +#define SPAN_WIDTH_PREFIX PREFIX "w" +#define SPAN_LEFT_NUMERIC_WIDTH_PREFIX PREFIX "lnw" +#define SPAN_RIGHT_NUMERIC_WIDTH_PREFIX PREFIX "rnw" +#define SPAN_ALPHABETIC_WIDTH_PREFIX PREFIX "aw" +#define COLUMN_SEPARATION_PREFIX PREFIX "cs" +#define ROW_START_PREFIX PREFIX "rs" +#define COLUMN_START_PREFIX PREFIX "cl" +#define COLUMN_END_PREFIX PREFIX "ce" +#define COLUMN_DIVIDE_PREFIX PREFIX "cd" +#define ROW_TOP_PREFIX PREFIX "rt" + +string block_width_reg(int r, int c); +string block_diversion_name(int r, int c); +string block_height_reg(int r, int c); +string span_width_reg(int start_col, int end_col); +string span_left_numeric_width_reg(int start_col, int end_col); +string span_right_numeric_width_reg(int start_col, int end_col); +string span_alphabetic_width_reg(int start_col, int end_col); +string column_separation_reg(int col); +string row_start_reg(int r); +string column_start_reg(int c); +string column_end_reg(int c); +string column_divide_reg(int c); +string row_top_reg(int r); + +void set_inline_modifier(const entry_modifier *); +void restore_inline_modifier(const entry_modifier *m); +void set_modifier(const entry_modifier *); +int find_decimal_point(const char *s, char decimal_point_char, + const char *delim); + +string an_empty_string; +int location_force_filename = 0; + +void printfs(const char *, + const string &arg1 = an_empty_string, + const string &arg2 = an_empty_string, + const string &arg3 = an_empty_string, + const string &arg4 = an_empty_string, + const string &arg5 = an_empty_string); + +void prints(const string &); + +inline void prints(char c) +{ + putchar(c); +} + +inline void prints(const char *s) +{ + fputs(s, stdout); +} + +void prints(const string &s) +{ + if (!s.empty()) + fwrite(s.contents(), 1, s.length(), stdout); +} + +struct horizontal_span { + horizontal_span *next; + short start_col; + short end_col; + horizontal_span(int, int, horizontal_span *); +}; + +struct single_line_entry; +struct double_line_entry; +struct simple_entry; + +class table_entry { +friend class table; + table_entry *next; + int input_lineno; + const char *input_filename; +protected: + int start_row; + int end_row; + short start_col; + short end_col; + const entry_modifier *mod; +public: + void set_location(); + table_entry(const entry_modifier *); + virtual ~table_entry(); + virtual int divert(int ncols, const string *mw, int *sep); + virtual void do_width(); + virtual void do_depth(); + virtual void print() = 0; + virtual void position_vertically() = 0; + virtual single_line_entry *to_single_line_entry(); + virtual double_line_entry *to_double_line_entry(); + virtual simple_entry *to_simple_entry(); + virtual int line_type(); + virtual void note_double_vrule_on_right(int); + virtual void note_double_vrule_on_left(int); +}; + +class simple_entry : public table_entry { +public: + simple_entry(const entry_modifier *); + void print(); + void position_vertically(); + simple_entry *to_simple_entry(); + virtual void add_tab(); + virtual void simple_print(int); +}; + +class empty_entry : public simple_entry { +public: + empty_entry(const entry_modifier *); + int line_type(); +}; + +class text_entry : public simple_entry { +protected: + char *contents; + void print_contents(); +public: + text_entry(char *, const entry_modifier *); + ~text_entry(); +}; + +void text_entry::print_contents() +{ + set_inline_modifier(mod); + prints(contents); + restore_inline_modifier(mod); +} + +class repeated_char_entry : public text_entry { +public: + repeated_char_entry(char *s, const entry_modifier *m); + void simple_print(int); +}; + +class simple_text_entry : public text_entry { +public: + simple_text_entry(char *s, const entry_modifier *m); + void do_width(); +}; + +class left_text_entry : public simple_text_entry { +public: + left_text_entry(char *s, const entry_modifier *m); + void simple_print(int); + void add_tab(); +}; + +class right_text_entry : public simple_text_entry { +public: + right_text_entry(char *s, const entry_modifier *m); + void simple_print(int); + void add_tab(); +}; + +class center_text_entry : public simple_text_entry { +public: + center_text_entry(char *s, const entry_modifier *m); + void simple_print(int); + void add_tab(); +}; + +class numeric_text_entry : public text_entry { + int dot_pos; +public: + numeric_text_entry(char *s, const entry_modifier *m, int pos); + void do_width(); + void simple_print(int); +}; + +class alphabetic_text_entry : public text_entry { +public: + alphabetic_text_entry(char *s, const entry_modifier *m); + void do_width(); + void simple_print(int); + void add_tab(); +}; + +class line_entry : public simple_entry { +protected: + char double_vrule_on_right; + char double_vrule_on_left; +public: + line_entry(const entry_modifier *); + void note_double_vrule_on_right(int); + void note_double_vrule_on_left(int); + void simple_print(int) = 0; +}; + +class single_line_entry : public line_entry { +public: + single_line_entry(const entry_modifier *m); + void simple_print(int); + single_line_entry *to_single_line_entry(); + int line_type(); +}; + +class double_line_entry : public line_entry { +public: + double_line_entry(const entry_modifier *m); + void simple_print(int); + double_line_entry *to_double_line_entry(); + int line_type(); +}; + +class short_line_entry : public simple_entry { +public: + short_line_entry(const entry_modifier *m); + void simple_print(int); + int line_type(); +}; + +class short_double_line_entry : public simple_entry { +public: + short_double_line_entry(const entry_modifier *m); + void simple_print(int); + int line_type(); +}; + +class block_entry : public table_entry { + char *contents; +protected: + void do_divert(int alphabetic, int ncols, const string *mw, int *sep); +public: + block_entry(char *s, const entry_modifier *m); + ~block_entry(); + int divert(int ncols, const string *mw, int *sep); + void do_width(); + void do_depth(); + void position_vertically(); + void print() = 0; +}; + +class left_block_entry : public block_entry { +public: + left_block_entry(char *s, const entry_modifier *m); + void print(); +}; + +class right_block_entry : public block_entry { +public: + right_block_entry(char *s, const entry_modifier *m); + void print(); +}; + +class center_block_entry : public block_entry { +public: + center_block_entry(char *s, const entry_modifier *m); + void print(); +}; + +class alphabetic_block_entry : public block_entry { +public: + alphabetic_block_entry(char *s, const entry_modifier *m); + void print(); + int divert(int ncols, const string *mw, int *sep); +}; + +table_entry::table_entry(const entry_modifier *m) +: next(0), start_row(-1), end_row(-1), start_col(-1), end_col(-1), mod(m), + input_lineno(-1), input_filename(0) +{ +} + +table_entry::~table_entry() +{ +} + +int table_entry::divert(int, const string *, int *) +{ + return 0; +} + +void table_entry::do_width() +{ +} + +single_line_entry *table_entry::to_single_line_entry() +{ + return 0; +} + +double_line_entry *table_entry::to_double_line_entry() +{ + return 0; +} + +simple_entry *table_entry::to_simple_entry() +{ + return 0; +} + +void table_entry::do_depth() +{ +} + +void table_entry::set_location() +{ + set_troff_location(input_filename, input_lineno); +} + +int table_entry::line_type() +{ + return -1; +} + +void table_entry::note_double_vrule_on_right(int) +{ +} + +void table_entry::note_double_vrule_on_left(int) +{ +} + +simple_entry::simple_entry(const entry_modifier *m) : table_entry(m) +{ +} + +void simple_entry::add_tab() +{ + // do nothing +} + +void simple_entry::simple_print(int) +{ + // do nothing +} + +void simple_entry::position_vertically() +{ + if (start_row != end_row) + switch (mod->vertical_alignment) { + case entry_modifier::TOP: + printfs(".sp |\\n[%1]u\n", row_start_reg(start_row)); + break; + case entry_modifier::CENTER: + // Peform the motion in two stages so that the center is rounded + // vertically upwards even if net vertical motion is upwards. + printfs(".sp |\\n[%1]u\n", row_start_reg(start_row)); + printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-1v/2u\n", + row_start_reg(start_row)); + break; + case entry_modifier::BOTTOM: + printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-1v\n", + row_start_reg(start_row)); + break; + default: + assert(0); + } +} + +void simple_entry::print() +{ + prints(".ta"); + add_tab(); + prints('\n'); + set_location(); + prints("\\&"); + simple_print(0); + prints('\n'); +} + +simple_entry *simple_entry::to_simple_entry() +{ + return this; +} + +empty_entry::empty_entry(const entry_modifier *m) +: simple_entry(m) +{ +} + +int empty_entry::line_type() +{ + return 0; +} + +text_entry::text_entry(char *s, const entry_modifier *m) +: contents(s), simple_entry(m) +{ +} + +text_entry::~text_entry() +{ + a_delete contents; +} + + +repeated_char_entry::repeated_char_entry(char *s, const entry_modifier *m) +: text_entry(s, m) +{ +} + +void repeated_char_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + set_inline_modifier(mod); + printfs("\\l" DELIMITER_CHAR "\\n[%1]u\\&", + span_width_reg(start_col, end_col)); + prints(contents); + prints(DELIMITER_CHAR); + restore_inline_modifier(mod); +} + +simple_text_entry::simple_text_entry(char *s, const entry_modifier *m) +: text_entry(s, m) +{ +} + +void simple_text_entry::do_width() +{ + set_location(); + printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR, + span_width_reg(start_col, end_col)); + print_contents(); + prints(DELIMITER_CHAR "\n"); +} + +left_text_entry::left_text_entry(char *s, const entry_modifier *m) +: simple_text_entry(s, m) +{ +} + +void left_text_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + print_contents(); +} + +// The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr. + +void left_text_entry::add_tab() +{ + printfs(" \\n[%1]u", column_end_reg(end_col)); +} + +right_text_entry::right_text_entry(char *s, const entry_modifier *m) +: simple_text_entry(s, m) +{ +} + +void right_text_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + prints("\002\003"); + print_contents(); + prints("\002"); +} + +void right_text_entry::add_tab() +{ + printfs(" \\n[%1]u", column_end_reg(end_col)); +} + +center_text_entry::center_text_entry(char *s, const entry_modifier *m) +: simple_text_entry(s, m) +{ +} + +void center_text_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + prints("\002\003"); + print_contents(); + prints("\003\002"); +} + +void center_text_entry::add_tab() +{ + printfs(" \\n[%1]u", column_end_reg(end_col)); +} + +numeric_text_entry::numeric_text_entry(char *s, const entry_modifier *m, int pos) +: text_entry(s, m), dot_pos(pos) +{ +} + +void numeric_text_entry::do_width() +{ + if (dot_pos != 0) { + set_location(); + printfs(".nr %1 0\\w" DELIMITER_CHAR, + block_width_reg(start_row, start_col)); + set_inline_modifier(mod); + for (int i = 0; i < dot_pos; i++) + prints(contents[i]); + restore_inline_modifier(mod); + prints(DELIMITER_CHAR "\n"); + printfs(".nr %1 \\n[%1]>?\\n[%2]\n", + span_left_numeric_width_reg(start_col, end_col), + block_width_reg(start_row, start_col)); + } + else + printfs(".nr %1 0\n", block_width_reg(start_row, start_col)); + if (contents[dot_pos] != '\0') { + set_location(); + printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR, + span_right_numeric_width_reg(start_col, end_col)); + set_inline_modifier(mod); + prints(contents + dot_pos); + restore_inline_modifier(mod); + prints(DELIMITER_CHAR "\n"); + } +} + +void numeric_text_entry::simple_print(int) +{ + printfs("\\h'|(\\n[%1]u-\\n[%2]u-\\n[%3]u/2u+\\n[%2]u+\\n[%4]u-\\n[%5]u)'", + span_width_reg(start_col, end_col), + span_left_numeric_width_reg(start_col, end_col), + span_right_numeric_width_reg(start_col, end_col), + column_start_reg(start_col), + block_width_reg(start_row, start_col)); + print_contents(); +} + +alphabetic_text_entry::alphabetic_text_entry(char *s, const entry_modifier *m) +: text_entry(s, m) +{ +} + +void alphabetic_text_entry::do_width() +{ + set_location(); + printfs(".nr %1 \\n[%1]>?\\w" DELIMITER_CHAR, + span_alphabetic_width_reg(start_col, end_col)); + print_contents(); + prints(DELIMITER_CHAR "\n"); +} + +void alphabetic_text_entry::simple_print(int) +{ + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + printfs("\\h'\\n[%1]u-\\n[%2]u/2u'", + span_width_reg(start_col, end_col), + span_alphabetic_width_reg(start_col, end_col)); + print_contents(); +} + +// The only point of this is to make `\a' ``work'' as in Unix tbl. Grrr. + +void alphabetic_text_entry::add_tab() +{ + printfs(" \\n[%1]u", column_end_reg(end_col)); +} + +block_entry::block_entry(char *s, const entry_modifier *m) +: table_entry(m), contents(s) +{ +} + +block_entry::~block_entry() +{ + a_delete contents; +} + +void block_entry::position_vertically() +{ + if (start_row != end_row) + switch(mod->vertical_alignment) { + case entry_modifier::TOP: + printfs(".sp |\\n[%1]u\n", row_start_reg(start_row)); + break; + case entry_modifier::CENTER: + // Peform the motion in two stages so that the center is rounded + // vertically upwards even if net vertical motion is upwards. + printfs(".sp |\\n[%1]u\n", row_start_reg(start_row)); + printfs(".sp \\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u/2u\n", + row_start_reg(start_row), + block_height_reg(start_row, start_col)); + break; + case entry_modifier::BOTTOM: + printfs(".sp |\\n[%1]u+\\n[" BOTTOM_REG "]u-\\n[%1]u-\\n[%2]u\n", + row_start_reg(start_row), + block_height_reg(start_row, start_col)); + break; + default: + assert(0); + } + if (mod->stagger) + prints(".sp -.5v\n"); +} + +int block_entry::divert(int ncols, const string *mw, int *sep) +{ + do_divert(0, ncols, mw, sep); + return 1; +} + +void block_entry::do_divert(int alphabetic, int ncols, const string *mw, + int *sep) +{ + printfs(".di %1\n", block_diversion_name(start_row, start_col)); + prints(".if \\n[" SAVED_FILL_REG "] .fi\n" + ".in 0\n"); + prints(".ll "); + int i; + for (i = start_col; i <= end_col; i++) + if (mw[i].empty()) + break; + if (i > end_col) { + // Every column spanned by this entry has a minimum width. + for (int i = start_col; i <= end_col; i++) { + if (i > start_col) { + if (sep) + printfs("+%1n", as_string(sep[i - 1])); + prints('+'); + } + printfs("(n;%1)", mw[i]); + } + printfs(">?\\n[%1]u", span_width_reg(start_col, end_col)); + } + else + printfs("(u;\\n[%1]>?(\\n[.l]*%2/%3))", + span_width_reg(start_col, end_col), + as_string(end_col - start_col + 1), + as_string(ncols + 1)); + if (alphabetic) + prints("-2n"); + prints("\n"); + set_modifier(mod); + prints(".cp \\n(" COMPATIBLE_REG "\n"); + set_location(); + prints(contents); + prints(".br\n.di\n.cp 0\n"); + if (!mod->zero_width) { + if (alphabetic) { + printfs(".nr %1 \\n[%1]>?(\\n[dl]+2n)\n", + span_width_reg(start_col, end_col)); + printfs(".nr %1 \\n[%1]>?\\n[dl]\n", + span_alphabetic_width_reg(start_col, end_col)); + } + else + printfs(".nr %1 \\n[%1]>?\\n[dl]\n", span_width_reg(start_col, end_col)); + } + printfs(".nr %1 \\n[dn]\n", block_height_reg(start_row, start_col)); + printfs(".nr %1 \\n[dl]\n", block_width_reg(start_row, start_col)); + prints("." RESET_MACRO_NAME "\n" + ".in \\n[" SAVED_INDENT_REG "]u\n" + ".nf\n"); + // the block might have contained .lf commands + location_force_filename = 1; +} + +void block_entry::do_width() +{ + // do nothing; the action happens in divert +} + +void block_entry::do_depth() +{ + printfs(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?(\\n[%1]+\\n[%2])\n", + row_start_reg(start_row), + block_height_reg(start_row, start_col)); +} + +left_block_entry::left_block_entry(char *s, const entry_modifier *m) +: block_entry(s, m) +{ +} + +void left_block_entry::print() +{ + printfs(".in +\\n[%1]u\n", column_start_reg(start_col)); + printfs(".%1\n", block_diversion_name(start_row, start_col)); + prints(".in\n"); +} + + + +right_block_entry::right_block_entry(char *s, const entry_modifier *m) +: block_entry(s, m) +{ +} + +void right_block_entry::print() +{ + printfs(".in +\\n[%1]u+\\n[%2]u-\\n[%3]u\n", + column_start_reg(start_col), + span_width_reg(start_col, end_col), + block_width_reg(start_row, start_col)); + printfs(".%1\n", block_diversion_name(start_row, start_col)); + prints(".in\n"); +} + +center_block_entry::center_block_entry(char *s, const entry_modifier *m) +: block_entry(s, m) +{ +} + +void center_block_entry::print() +{ + printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n", + column_start_reg(start_col), + span_width_reg(start_col, end_col), + block_width_reg(start_row, start_col)); + printfs(".%1\n", block_diversion_name(start_row, start_col)); + prints(".in\n"); +} + +alphabetic_block_entry::alphabetic_block_entry(char *s, + const entry_modifier *m) +: block_entry(s, m) +{ +} + +int alphabetic_block_entry::divert(int ncols, const string *mw, int *sep) +{ + do_divert(1, ncols, mw, sep); + return 1; +} + +void alphabetic_block_entry::print() +{ + printfs(".in +\\n[%1]u+(\\n[%2]u-\\n[%3]u/2u)\n", + column_start_reg(start_col), + span_width_reg(start_col, end_col), + span_alphabetic_width_reg(start_col, end_col)); + printfs(".%1\n", block_diversion_name(start_row, start_col)); + prints(".in\n"); +} + +line_entry::line_entry(const entry_modifier *m) +: simple_entry(m), double_vrule_on_right(0), double_vrule_on_left(0) +{ +} + +void line_entry::note_double_vrule_on_right(int is_corner) +{ + double_vrule_on_right = is_corner ? 1 : 2; +} + +void line_entry::note_double_vrule_on_left(int is_corner) +{ + double_vrule_on_left = is_corner ? 1 : 2; +} + + +single_line_entry::single_line_entry(const entry_modifier *m) +: line_entry(m) +{ +} + +int single_line_entry::line_type() +{ + return 1; +} + +void single_line_entry::simple_print(int dont_move) +{ + printfs("\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if (double_vrule_on_left) { + prints(double_vrule_on_left == 1 ? "-" : "+"); + prints(HALF_DOUBLE_LINE_SEP); + } + prints("'"); + if (!dont_move) + prints("\\v'-" BAR_HEIGHT "'"); + printfs("\\s[\\n[" LINESIZE_REG "]]" "\\D'l |\\n[%1]u", + column_divide_reg(end_col+1)); + if (double_vrule_on_right) { + prints(double_vrule_on_left == 1 ? "+" : "-"); + prints(HALF_DOUBLE_LINE_SEP); + } + prints("0'\\s0"); + if (!dont_move) + prints("\\v'" BAR_HEIGHT "'"); +} + +single_line_entry *single_line_entry::to_single_line_entry() +{ + return this; +} + +double_line_entry::double_line_entry(const entry_modifier *m) +: line_entry(m) +{ +} + +int double_line_entry::line_type() +{ + return 2; +} + +void double_line_entry::simple_print(int dont_move) +{ + if (!dont_move) + prints("\\v'-" BAR_HEIGHT "'"); + printfs("\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if (double_vrule_on_left) { + prints(double_vrule_on_left == 1 ? "-" : "+"); + prints(HALF_DOUBLE_LINE_SEP); + } + prints("'"); + printfs("\\v'-" HALF_DOUBLE_LINE_SEP "'" + "\\s[\\n[" LINESIZE_REG "]]" + "\\D'l |\\n[%1]u", + column_divide_reg(end_col+1)); + if (double_vrule_on_right) + prints("-" HALF_DOUBLE_LINE_SEP); + prints(" 0'"); + printfs("\\v'" DOUBLE_LINE_SEP "'" + "\\D'l |\\n[%1]u", + column_divide_reg(start_col)); + if (double_vrule_on_right) { + prints(double_vrule_on_left == 1 ? "+" : "-"); + prints(HALF_DOUBLE_LINE_SEP); + } + prints(" 0'"); + prints("\\s0" + "\\v'-" HALF_DOUBLE_LINE_SEP "'"); + if (!dont_move) + prints("\\v'" BAR_HEIGHT "'"); +} + +double_line_entry *double_line_entry::to_double_line_entry() +{ + return this; +} + +short_line_entry::short_line_entry(const entry_modifier *m) +: simple_entry(m) +{ +} + +int short_line_entry::line_type() +{ + return 1; +} + +void short_line_entry::simple_print(int dont_move) +{ + if (mod->stagger) + prints("\\v'-.5v'"); + if (!dont_move) + prints("\\v'-" BAR_HEIGHT "'"); + printfs("\\h'|\\n[%1]u'", column_start_reg(start_col)); + printfs("\\s[\\n[" LINESIZE_REG "]]" + "\\D'l \\n[%1]u 0'" + "\\s0", + span_width_reg(start_col, end_col)); + if (!dont_move) + prints("\\v'" BAR_HEIGHT "'"); + if (mod->stagger) + prints("\\v'.5v'"); +} + +short_double_line_entry::short_double_line_entry(const entry_modifier *m) +: simple_entry(m) +{ +} + +int short_double_line_entry::line_type() +{ + return 2; +} + +void short_double_line_entry::simple_print(int dont_move) +{ + if (mod->stagger) + prints("\\v'-.5v'"); + if (!dont_move) + prints("\\v'-" BAR_HEIGHT "'"); + printfs("\\h'|\\n[%2]u'" + "\\v'-" HALF_DOUBLE_LINE_SEP "'" + "\\s[\\n[" LINESIZE_REG "]]" + "\\D'l \\n[%1]u 0'" + "\\v'" DOUBLE_LINE_SEP "'" + "\\D'l |\\n[%2]u 0'" + "\\s0" + "\\v'-" HALF_DOUBLE_LINE_SEP "'", + span_width_reg(start_col, end_col), + column_start_reg(start_col)); + if (!dont_move) + prints("\\v'" BAR_HEIGHT "'"); + if (mod->stagger) + prints("\\v'.5v'"); +} + +void set_modifier(const entry_modifier *m) +{ + if (!m->font.empty()) + printfs(".ft %1\n", m->font); + if (m->point_size.val != 0) { + prints(".ps "); + if (m->point_size.inc > 0) + prints('+'); + else if (m->point_size.inc < 0) + prints('-'); + printfs("%1\n", as_string(m->point_size.val)); + } + if (m->vertical_spacing.val != 0) { + prints(".vs "); + if (m->vertical_spacing.inc > 0) + prints('+'); + else if (m->vertical_spacing.inc < 0) + prints('-'); + printfs("%1\n", as_string(m->vertical_spacing.val)); + } +} + +void set_inline_modifier(const entry_modifier *m) +{ + if (!m->font.empty()) + printfs("\\f[%1]", m->font); + if (m->point_size.val != 0) { + prints("\\s["); + if (m->point_size.inc > 0) + prints('+'); + else if (m->point_size.inc < 0) + prints('-'); + printfs("%1]", as_string(m->point_size.val)); + } + if (m->stagger) + prints("\\v'-.5v'"); +} + +void restore_inline_modifier(const entry_modifier *m) +{ + if (!m->font.empty()) + prints("\\f[\\n[" SAVED_FONT_REG "]]"); + if (m->point_size.val != 0) + prints("\\s[\\n[" SAVED_SIZE_REG "]]"); + if (m->stagger) + prints("\\v'.5v'"); +} + + +struct stuff { + stuff *next; + int row; // occurs before row `row' + char printed; // has it been printed? + + stuff(int); + virtual void print(table *) = 0; + virtual ~stuff(); + virtual int is_single_line() { return 0; }; + virtual int is_double_line() { return 0; }; +}; + +stuff::stuff(int r) : row(r), next(0), printed(0) +{ +} + +stuff::~stuff() +{ +} + +struct text_stuff : public stuff { + string contents; + const char *filename; + int lineno; + + text_stuff(const string &, int r, const char *fn, int ln); + ~text_stuff(); + void print(table *); +}; + + +text_stuff::text_stuff(const string &s, int r, const char *fn, int ln) +: contents(s), stuff(r), filename(fn), lineno(ln) +{ +} + +text_stuff::~text_stuff() +{ +} + +void text_stuff::print(table *) +{ + printed = 1; + prints(".cp \\n(" COMPATIBLE_REG "\n"); + set_troff_location(filename, lineno); + prints(contents); + prints(".cp 0\n"); + location_force_filename = 1; // it might have been a .lf command +} + +struct single_hline_stuff : public stuff { + single_hline_stuff(int r); + void print(table *); + int is_single_line(); +}; + +single_hline_stuff::single_hline_stuff(int r) : stuff(r) +{ +} + +void single_hline_stuff::print(table *tbl) +{ + printed = 1; + tbl->print_single_hline(row); +} + +int single_hline_stuff::is_single_line() +{ + return 1; +} + +struct double_hline_stuff : stuff { + double_hline_stuff(int r); + void print(table *); + int is_double_line(); +}; + +double_hline_stuff::double_hline_stuff(int r) : stuff(r) +{ +} + +void double_hline_stuff::print(table *tbl) +{ + printed = 1; + tbl->print_double_hline(row); +} + +int double_hline_stuff::is_double_line() +{ + return 1; +} + +struct vertical_rule { + vertical_rule *next; + int start_row; + int end_row; + short col; + char is_double; + string top_adjust; + string bot_adjust; + + vertical_rule(int sr, int er, int c, int dbl, vertical_rule *); + ~vertical_rule(); + void contribute_to_bottom_macro(table *); + void print(); +}; + +vertical_rule::vertical_rule(int sr, int er, int c, int dbl, vertical_rule *p) +: start_row(sr), end_row(er), col(c), is_double(dbl), next(p) +{ +} + +vertical_rule::~vertical_rule() +{ +} + +void vertical_rule::contribute_to_bottom_macro(table *tbl) +{ + printfs(".if \\n[" CURRENT_ROW_REG "]>=%1", + as_string(start_row)); + if (end_row != tbl->get_nrows() - 1) + printfs("&(\\n[" CURRENT_ROW_REG "]<%1)", + as_string(end_row)); + prints(" \\{"); + printfs(".if %1<=\\n[" LAST_PASSED_ROW_REG "] .nr %2 \\n[#T]\n", + as_string(start_row), + row_top_reg(start_row)); + const char *offset_table[3]; + if (is_double) { + offset_table[0] = "-" HALF_DOUBLE_LINE_SEP; + offset_table[1] = "+" HALF_DOUBLE_LINE_SEP; + offset_table[2] = 0; + } + else { + offset_table[0] = ""; + offset_table[1] = 0; + } + for (const char **offsetp = offset_table; *offsetp; offsetp++) { + prints(".sp -1\n" + "\\v'" BODY_DEPTH); + if (!bot_adjust.empty()) + printfs("+%1", bot_adjust); + prints("'"); + printfs("\\h'\\n[%1]u%3'\\s[\\n[" LINESIZE_REG "]]\\D'l 0 |\\n[%2]u-1v", + column_divide_reg(col), + row_top_reg(start_row), + *offsetp); + if (!bot_adjust.empty()) + printfs("-(%1)", bot_adjust); + // don't perform the top adjustment if the top is actually #T + if (!top_adjust.empty()) + printfs("+((%1)*(%2>\\n[" LAST_PASSED_ROW_REG "]))", + top_adjust, + as_string(start_row)); + prints("'\\s0\n"); + } + prints(".\\}\n"); +} + +void vertical_rule::print() +{ + printfs("\\*[" TRANSPARENT_STRING_NAME "]" + ".if %1<=\\*[" QUOTE_STRING_NAME "]\\n[" LAST_PASSED_ROW_REG "] " + ".nr %2 \\*[" QUOTE_STRING_NAME "]\\n[#T]\n", + as_string(start_row), + row_top_reg(start_row)); + const char *offset_table[3]; + if (is_double) { + offset_table[0] = "-" HALF_DOUBLE_LINE_SEP; + offset_table[1] = "+" HALF_DOUBLE_LINE_SEP; + offset_table[2] = 0; + } + else { + offset_table[0] = ""; + offset_table[1] = 0; + } + for (const char **offsetp = offset_table; *offsetp; offsetp++) { + prints("\\*[" TRANSPARENT_STRING_NAME "].sp -1\n" + "\\*[" TRANSPARENT_STRING_NAME "]\\v'" BODY_DEPTH); + if (!bot_adjust.empty()) + printfs("+%1", bot_adjust); + prints("'"); + printfs("\\h'\\n[%1]u%3'" + "\\s[\\n[" LINESIZE_REG "]]" + "\\D'l 0 |\\*[" QUOTE_STRING_NAME "]\\n[%2]u-1v", + column_divide_reg(col), + row_top_reg(start_row), + *offsetp); + if (!bot_adjust.empty()) + printfs("-(%1)", bot_adjust); + // don't perform the top adjustment if the top is actually #T + if (!top_adjust.empty()) + printfs("+((%1)*(%2>\\*[" QUOTE_STRING_NAME "]\\n[" + LAST_PASSED_ROW_REG "]))", + top_adjust, + as_string(start_row)); + prints("'" + "\\s0\n"); + } +} + +table::table(int nc, unsigned f, int ls, char dpc) +: ncolumns(nc), flags(f), linesize(ls), decimal_point_char(dpc), + nrows(0), allocated_rows(0), entry(0), entry_list(0), + entry_list_tailp(&entry_list), + left_separation(0), right_separation(0), stuff_list(0), vline(0), + vrule_list(0), row_is_all_lines(0), span_list(0) +{ + minimum_width = new string[ncolumns]; + column_separation = ncolumns > 1 ? new int[ncolumns - 1] : 0; + equal = new char[ncolumns]; + int i; + for (i = 0; i < ncolumns; i++) + equal[i] = 0; + for (i = 0; i < ncolumns-1; i++) + column_separation[i] = DEFAULT_COLUMN_SEPARATION; + delim[0] = delim[1] = '\0'; +} + +table::~table() +{ + for (int i = 0; i < nrows; i++) { + a_delete entry[i]; + a_delete vline[i]; + } + a_delete entry; + a_delete vline; + while (entry_list) { + table_entry *tem = entry_list; + entry_list = entry_list->next; + delete tem; + } + ad_delete(ncolumns) minimum_width; + a_delete column_separation; + a_delete equal; + while (stuff_list) { + stuff *tem = stuff_list; + stuff_list = stuff_list->next; + delete tem; + } + while (vrule_list) { + vertical_rule *tem = vrule_list; + vrule_list = vrule_list->next; + delete tem; + } + a_delete row_is_all_lines; + while (span_list) { + horizontal_span *tem = span_list; + span_list = span_list->next; + delete tem; + } +} + +void table::set_delim(char c1, char c2) +{ + delim[0] = c1; + delim[1] = c2; +} + +void table::set_minimum_width(int c, const string &w) +{ + assert(c >= 0 && c < ncolumns); + minimum_width[c] = w; +} + +void table::set_column_separation(int c, int n) +{ + assert(c >= 0 && c < ncolumns - 1); + column_separation[c] = n; +} + +void table::set_equal_column(int c) +{ + assert(c >= 0 && c < ncolumns); + equal[c] = 1; +} + +void table::add_stuff(stuff *p) +{ + stuff **pp; + for (pp = &stuff_list; *pp; pp = &(*pp)->next) + ; + *pp = p; +} + +void table::add_text_line(int r, const string &s, const char *filename, int lineno) +{ + add_stuff(new text_stuff(s, r, filename, lineno)); +} + +void table::add_single_hline(int r) +{ + add_stuff(new single_hline_stuff(r)); +} + +void table::add_double_hline(int r) +{ + add_stuff(new double_hline_stuff(r)); +} + +void table::allocate(int r) +{ + if (r >= nrows) { + typedef table_entry **PPtable_entry; // work around g++ 1.36.1 bug + if (r >= allocated_rows) { + if (allocated_rows == 0) { + allocated_rows = 16; + if (allocated_rows <= r) + allocated_rows = r + 1; + entry = new PPtable_entry[allocated_rows]; + vline = new char*[allocated_rows]; + } + else { + table_entry ***old_entry = entry; + int old_allocated_rows = allocated_rows; + allocated_rows *= 2; + if (allocated_rows <= r) + allocated_rows = r + 1; + entry = new PPtable_entry[allocated_rows]; + memcpy(entry, old_entry, sizeof(table_entry**)*old_allocated_rows); + a_delete old_entry; + char **old_vline = vline; + vline = new char*[allocated_rows]; + memcpy(vline, old_vline, sizeof(char*)*old_allocated_rows); + a_delete old_vline; + } + } + assert(allocated_rows > r); + while (nrows <= r) { + entry[nrows] = new table_entry*[ncolumns]; + int i; + for (i = 0; i < ncolumns; i++) + entry[nrows][i] = 0; + vline[nrows] = new char[ncolumns+1]; + for (i = 0; i < ncolumns+1; i++) + vline[nrows][i] = 0; + nrows++; + } + } +} + +void table::do_hspan(int r, int c) +{ + assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns); + if (c == 0) { + error("first column cannot be horizontally spanned"); + return; + } + table_entry *e = entry[r][c]; + if (e) { + assert(e->start_row <= r && r <= e->end_row + && e->start_col <= c && c <= e->end_col + && e->end_row - e->start_row > 0 + && e->end_col - e->start_col > 0); + return; + } + e = entry[r][c-1]; + // e can be 0 if we had an empty entry or an error + if (e == 0) + return; + if (e->start_row != r) { + /* + l l + ^ s */ + error("impossible horizontal span at row %1, column %2", r + 1, c + 1); + } + else { + e->end_col = c; + entry[r][c] = e; + } +} + +void table::do_vspan(int r, int c) +{ + assert(r >= 0 && c >= 0 && r < nrows && c < ncolumns); + if (r == 0) { + error("first row cannot be vertically spanned"); + return; + } + table_entry *e = entry[r][c]; + if (e) { + assert(e->start_row <= r && r <= e->end_row + && e->start_col <= c && c <= e->end_col + && e->end_row - e->start_row > 0 + && e->end_col - e->start_col > 0); + return; + } + e = entry[r-1][c]; + // e can be 0 if we had an empty entry or an error + if (e == 0) + return; + if (e->start_col != c) { + /* l s + l ^ */ + error("impossible vertical span at row %1, column %2", r + 1, c + 1); + } + else { + for (int i = c; i <= e->end_col; i++) { + assert(entry[r][i] == 0); + entry[r][i] = e; + } + e->end_row = r; + } +} + +int find_decimal_point(const char *s, char decimal_point_char, + const char *delim) +{ + if (s == 0 || *s == '\0') + return -1; + const char *p; + int in_delim = 0; // is p within eqn delimiters? + // tbl recognises \& even within eqn delimiters; I don't + for (p = s; *p; p++) + if (in_delim) { + if (*p == delim[1]) + in_delim = 0; + } + else if (*p == delim[0]) + in_delim = 1; + else if (p[0] == '\\' && p[1] == '&') + return p - s; + int possible_pos = -1; + in_delim = 0; + for (p = s; *p; p++) + if (in_delim) { + if (*p == delim[1]) + in_delim = 0; + } + else if (*p == delim[0]) + in_delim = 1; + else if (p[0] == decimal_point_char && csdigit(p[1])) + possible_pos = p - s; + if (possible_pos >= 0) + return possible_pos; + in_delim = 0; + for (p = s; *p; p++) + if (in_delim) { + if (*p == delim[1]) + in_delim = 0; + } + else if (*p == delim[0]) + in_delim = 1; + else if (csdigit(*p)) + possible_pos = p + 1 - s; + return possible_pos; +} + +void table::add_entry(int r, int c, const string &str, const entry_format *f, + const char *fn, int ln) +{ + allocate(r); + table_entry *e = 0; + if (str == "\\_") { + e = new short_line_entry(f); + } + else if (str == "\\=") { + e = new short_double_line_entry(f); + } + else if (str == "_") { + single_line_entry *lefte; + if (c > 0 && entry[r][c-1] != 0 && + (lefte = entry[r][c-1]->to_single_line_entry()) != 0 + && lefte->start_row == r + && lefte->mod->stagger == f->stagger) { + lefte->end_col = c; + entry[r][c] = lefte; + } + else + e = new single_line_entry(f); + } + else if (str == "=") { + double_line_entry *lefte; + if (c > 0 && entry[r][c-1] != 0 && + (lefte = entry[r][c-1]->to_double_line_entry()) != 0 + && lefte->start_row == r + && lefte->mod->stagger == f->stagger) { + lefte->end_col = c; + entry[r][c] = lefte; + } + else + e = new double_line_entry(f); + } + else if (str == "\\^") { + do_vspan(r, c); + } + else if (str.length() > 2 && str[0] == '\\' && str[1] == 'R') { + if (str.search('\n') >= 0) + error_with_file_and_line(fn, ln, "bad repeated character"); + else { + char *s = str.substring(2, str.length() - 2).extract(); + e = new repeated_char_entry(s, f); + } + } + else { + int is_block = str.search('\n') >= 0; + char *s; + switch (f->type) { + case FORMAT_SPAN: + assert(str.empty()); + do_hspan(r, c); + break; + case FORMAT_LEFT: + if (!str.empty()) { + s = str.extract(); + if (is_block) + e = new left_block_entry(s, f); + else + e = new left_text_entry(s, f); + } + else + e = new empty_entry(f); + break; + case FORMAT_CENTER: + if (!str.empty()) { + s = str.extract(); + if (is_block) + e = new center_block_entry(s, f); + else + e = new center_text_entry(s, f); + } + else + e = new empty_entry(f); + break; + case FORMAT_RIGHT: + if (!str.empty()) { + s = str.extract(); + if (is_block) + e = new right_block_entry(s, f); + else + e = new right_text_entry(s, f); + } + else + e = new empty_entry(f); + break; + case FORMAT_NUMERIC: + if (!str.empty()) { + s = str.extract(); + if (is_block) { + error_with_file_and_line(fn, ln, "can't have numeric text block"); + e = new left_block_entry(s, f); + } + else { + int pos = find_decimal_point(s, decimal_point_char, delim); + if (pos < 0) + e = new center_text_entry(s, f); + else + e = new numeric_text_entry(s, f, pos); + } + } + else + e = new empty_entry(f); + break; + case FORMAT_ALPHABETIC: + if (!str.empty()) { + s = str.extract(); + if (is_block) + e = new alphabetic_block_entry(s, f); + else + e = new alphabetic_text_entry(s, f); + } + else + e = new empty_entry(f); + break; + case FORMAT_VSPAN: + do_vspan(r, c); + break; + case FORMAT_HLINE: + if (str.length() != 0) + error_with_file_and_line(fn, ln, + "non-empty data entry for `_' format ignored"); + e = new single_line_entry(f); + break; + case FORMAT_DOUBLE_HLINE: + if (str.length() != 0) + error_with_file_and_line(fn, ln, + "non-empty data entry for `=' format ignored"); + e = new double_line_entry(f); + break; + default: + assert(0); + } + } + if (e) { + table_entry *preve = entry[r][c]; + if (preve) { + /* c s + ^ l */ + error_with_file_and_line(fn, ln, "row %1, column %2 already spanned", + r + 1, c + 1); + delete e; + } + else { + e->input_lineno = ln; + e->input_filename = fn; + e->start_row = e->end_row = r; + e->start_col = e->end_col = c; + *entry_list_tailp = e; + entry_list_tailp = &e->next; + entry[r][c] = e; + } + } +} + +// add vertical lines for row r + +void table::add_vlines(int r, const char *v) +{ + allocate(r); + for (int i = 0; i < ncolumns+1; i++) + vline[r][i] = v[i]; +} + +void table::check() +{ + table_entry *p = entry_list; + int i, j; + while (p) { + for (i = p->start_row; i <= p->end_row; i++) + for (j = p->start_col; j <= p->end_col; j++) + assert(entry[i][j] == p); + p = p->next; + } +} + +void table::print() +{ + location_force_filename = 1; + check(); + init_output(); + determine_row_type(); + compute_widths(); + if (!(flags & CENTER)) + prints(".if \\n[" SAVED_CENTER_REG "] \\{"); + prints(".in +(u;\\n[.l]-\\n[.i]-\\n[TW]/2)\n" + ".nr " SAVED_INDENT_REG " \\n[.i]\n"); + if (!(flags & CENTER)) + prints(".\\}\n"); + build_vrule_list(); + define_bottom_macro(); + do_top(); + for (int i = 0; i < nrows; i++) + do_row(i); + do_bottom(); +} + +void table::determine_row_type() +{ + row_is_all_lines = new char[nrows]; + for (int i = 0; i < nrows; i++) { + int had_single = 0; + int had_double = 0; + int had_non_line = 0; + for (int c = 0; c < ncolumns; c++) { + table_entry *e = entry[i][c]; + if (e != 0) { + if (e->start_row == e->end_row) { + int t = e->line_type(); + switch (t) { + case -1: + had_non_line = 1; + break; + case 0: + // empty + break; + case 1: + had_single = 1; + break; + case 2: + had_double = 1; + break; + default: + assert(0); + } + if (had_non_line) + break; + } + c = e->end_col; + } + } + if (had_non_line) + row_is_all_lines[i] = 0; + else if (had_double) + row_is_all_lines[i] = 2; + else if (had_single) + row_is_all_lines[i] = 1; + else + row_is_all_lines[i] = 0; + } +} + + +void table::init_output() +{ + prints(".nr " COMPATIBLE_REG " \\n(.C\n" + ".cp 0\n"); + if (linesize > 0) + printfs(".nr " LINESIZE_REG " %1\n", as_string(linesize)); + else + prints(".nr " LINESIZE_REG " \\n[.s]\n"); + if (!(flags & CENTER)) + prints(".nr " SAVED_CENTER_REG " \\n[.ce]\n"); + prints(".de " RESET_MACRO_NAME "\n" + ".ft \\n[.f]\n" + ".ps \\n[.s]\n" + ".vs \\n[.v]u\n" + ".in \\n[.i]u\n" + ".ll \\n[.l]u\n" + ".ls \\n[.L]\n" + ".ad \\n[.j]\n" + ".ie \\n[.u] .fi\n" + ".el .nf\n" + ".ce \\n[.ce]\n" + "..\n" + ".nr " SAVED_INDENT_REG " \\n[.i]\n" + ".nr " SAVED_FONT_REG " \\n[.f]\n" + ".nr " SAVED_SIZE_REG " \\n[.s]\n" + ".nr " SAVED_FILL_REG " \\n[.u]\n" + ".nr T. 0\n" + ".nr " CURRENT_ROW_REG " 0-1\n" + ".nr " LAST_PASSED_ROW_REG " 0-1\n" + ".nr " SECTION_DIVERSION_FLAG_REG " 0\n" + ".ds " TRANSPARENT_STRING_NAME "\n" + ".ds " QUOTE_STRING_NAME "\n" + ".nr " NEED_BOTTOM_RULE_REG " 1\n" + ".nr " SUPPRESS_BOTTOM_REG " 0\n" + ".eo\n" + ".de " REPEATED_MARK_MACRO "\n" + ".mk \\$1\n" + ".if !'\\n(.z'' \\!." REPEATED_MARK_MACRO " \"\\$1\"\n" + "..\n" + ".de " REPEATED_VPT_MACRO "\n" + ".vpt \\$1\n" + ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n" + "..\n"); + if (!(flags & NOKEEP)) + prints(".de " KEEP_MACRO_NAME "\n" + ".if '\\n[.z]'' \\{.ds " QUOTE_STRING_NAME " \\\\\n" + ".ds " TRANSPARENT_STRING_NAME " \\!\n" + ".di " SECTION_DIVERSION_NAME "\n" + ".nr " SECTION_DIVERSION_FLAG_REG " 1\n" + ".in 0\n" + ".\\}\n" + "..\n" + ".de " RELEASE_MACRO_NAME "\n" + ".if \\n[" SECTION_DIVERSION_FLAG_REG "] \\{" + ".di\n" + ".in \\n[" SAVED_INDENT_REG "]u\n" + ".nr " SAVED_DN_REG " \\n[dn]\n" + ".ds " QUOTE_STRING_NAME "\n" + ".ds " TRANSPARENT_STRING_NAME "\n" + ".nr " SECTION_DIVERSION_FLAG_REG " 0\n" + ".if \\n[.t]<=\\n[dn] \\{" + ".nr T. 1\n" + ".T#\n" + ".nr " SUPPRESS_BOTTOM_REG " 1\n" + ".sp \\n[.t]u\n" + ".nr " SUPPRESS_BOTTOM_REG " 0\n" + ".mk #T\n" + ".\\}\n" + ".if \\n[.t]<=\\n[" SAVED_DN_REG "] " + /* Since we turn off traps, it won't get into an infinite loop + when we try and print it; it will just go off the bottom of the + page. */ + ".tm warning: page \\n%: table text block will not fit on one page\n" + ".nf\n" + ".ls 1\n" + "." SECTION_DIVERSION_NAME "\n" + ".ls\n" + ".rm " SECTION_DIVERSION_NAME "\n" + ".\\}\n" + "..\n" + ".nr " TABLE_DIVERSION_FLAG_REG " 0\n" + ".de " TABLE_KEEP_MACRO_NAME "\n" + ".if '\\n[.z]'' \\{" + ".di " TABLE_DIVERSION_NAME "\n" + ".nr " TABLE_DIVERSION_FLAG_REG " 1\n" + ".\\}\n" + "..\n" + ".de " TABLE_RELEASE_MACRO_NAME "\n" + ".if \\n[" TABLE_DIVERSION_FLAG_REG "] \\{.br\n" + ".di\n" + ".nr " SAVED_DN_REG " \\n[dn]\n" + ".ne \\n[dn]u+\\n[.V]u\n" + ".ie \\n[.t]<=\\n[" SAVED_DN_REG "] " + ".tm error: page \\n%: table will not fit on one page; use .TS H/.TH with a supporting macro package\n" + ".el \\{" + ".in 0\n" + ".ls 1\n" + ".nf\n" + "." TABLE_DIVERSION_NAME "\n" + ".\\}\n" + ".rm " TABLE_DIVERSION_NAME "\n" + ".\\}\n" + "..\n"); + prints(".ec\n" + ".ce 0\n" + ".nf\n"); +} + +string block_width_reg(int r, int c) +{ + static char name[sizeof(BLOCK_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, BLOCK_WIDTH_PREFIX "%d,%d", r, c); + return string(name); +} + +string block_diversion_name(int r, int c) +{ + static char name[sizeof(BLOCK_DIVERSION_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, BLOCK_DIVERSION_PREFIX "%d,%d", r, c); + return string(name); +} + +string block_height_reg(int r, int c) +{ + static char name[sizeof(BLOCK_HEIGHT_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, BLOCK_HEIGHT_PREFIX "%d,%d", r, c); + return string(name); +} + +string span_width_reg(int start_col, int end_col) +{ + static char name[sizeof(SPAN_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, SPAN_WIDTH_PREFIX "%d", start_col); + if (end_col != start_col) + sprintf(strchr(name, '\0'), ",%d", end_col); + return string(name); +} + +string span_left_numeric_width_reg(int start_col, int end_col) +{ + static char name[sizeof(SPAN_LEFT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, SPAN_LEFT_NUMERIC_WIDTH_PREFIX "%d", start_col); + if (end_col != start_col) + sprintf(strchr(name, '\0'), ",%d", end_col); + return string(name); +} + +string span_right_numeric_width_reg(int start_col, int end_col) +{ + static char name[sizeof(SPAN_RIGHT_NUMERIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, SPAN_RIGHT_NUMERIC_WIDTH_PREFIX "%d", start_col); + if (end_col != start_col) + sprintf(strchr(name, '\0'), ",%d", end_col); + return string(name); +} + +string span_alphabetic_width_reg(int start_col, int end_col) +{ + static char name[sizeof(SPAN_ALPHABETIC_WIDTH_PREFIX)+INT_DIGITS+1+INT_DIGITS]; + sprintf(name, SPAN_ALPHABETIC_WIDTH_PREFIX "%d", start_col); + if (end_col != start_col) + sprintf(strchr(name, '\0'), ",%d", end_col); + return string(name); +} + + +string column_separation_reg(int col) +{ + static char name[sizeof(COLUMN_SEPARATION_PREFIX)+INT_DIGITS]; + sprintf(name, COLUMN_SEPARATION_PREFIX "%d", col); + return string(name); +} + +string row_start_reg(int row) +{ + static char name[sizeof(ROW_START_PREFIX)+INT_DIGITS]; + sprintf(name, ROW_START_PREFIX "%d", row); + return string(name); +} + +string column_start_reg(int col) +{ + static char name[sizeof(COLUMN_START_PREFIX)+INT_DIGITS]; + sprintf(name, COLUMN_START_PREFIX "%d", col); + return string(name); +} + +string column_end_reg(int col) +{ + static char name[sizeof(COLUMN_END_PREFIX)+INT_DIGITS]; + sprintf(name, COLUMN_END_PREFIX "%d", col); + return string(name); +} + +string column_divide_reg(int col) +{ + static char name[sizeof(COLUMN_DIVIDE_PREFIX)+INT_DIGITS]; + sprintf(name, COLUMN_DIVIDE_PREFIX "%d", col); + return string(name); +} + +string row_top_reg(int row) +{ + static char name[sizeof(ROW_TOP_PREFIX)+INT_DIGITS]; + sprintf(name, ROW_TOP_PREFIX "%d", row); + return string(name); +} + +void init_span_reg(int start_col, int end_col) +{ + printfs(".nr %1 \\n(.H\n.nr %2 0\n.nr %3 0\n.nr %4 0\n", + span_width_reg(start_col, end_col), + span_alphabetic_width_reg(start_col, end_col), + span_left_numeric_width_reg(start_col, end_col), + span_right_numeric_width_reg(start_col, end_col)); +} + +void compute_span_width(int start_col, int end_col) +{ + printfs(".nr %1 \\n[%1]>?(\\n[%2]+\\n[%3])\n" + ".if \\n[%4] .nr %1 \\n[%1]>?(\\n[%4]+2n)\n", + span_width_reg(start_col, end_col), + span_left_numeric_width_reg(start_col, end_col), + span_right_numeric_width_reg(start_col, end_col), + span_alphabetic_width_reg(start_col, end_col)); + +} + +// Increase the widths of columns so that the width of any spanning entry +// is no greater than the sum of the widths of the columns that it spans. +// Ensure that the widths of columns remain equal. + +void table::divide_span(int start_col, int end_col) +{ + assert(end_col > start_col); + printfs(".nr " NEEDED_REG " \\n[%1]-(\\n[%2]", + span_width_reg(start_col, end_col), + span_width_reg(start_col, start_col)); + int i; + for (i = start_col + 1; i <= end_col; i++) { + // The column separation may shrink with the expand option. + if (!(flags & EXPAND)) + printfs("+%1n", as_string(column_separation[i - 1])); + printfs("+\\n[%1]", span_width_reg(i, i)); + } + prints(")\n"); + printfs(".nr " NEEDED_REG " \\n[" NEEDED_REG "]/%1\n", + as_string(end_col - start_col + 1)); + prints(".if \\n[" NEEDED_REG "] \\{"); + for (i = start_col; i <= end_col; i++) + printfs(".nr %1 +\\n[" NEEDED_REG "]\n", + span_width_reg(i, i)); + int equal_flag = 0; + for (i = start_col; i <= end_col && !equal_flag; i++) + if (equal[i]) + equal_flag = 1; + if (equal_flag) { + for (i = 0; i < ncolumns; i++) + if (i < start_col || i > end_col) + printfs(".nr %1 +\\n[" NEEDED_REG "]\n", + span_width_reg(i, i)); + } + prints(".\\}\n"); +} + + +void table::sum_columns(int start_col, int end_col) +{ + assert(end_col > start_col); + printfs(".nr %1 \\n[%2]", + span_width_reg(start_col, end_col), + span_width_reg(start_col, start_col)); + for (int i = start_col + 1; i <= end_col; i++) + printfs("+(%1*\\n[" SEPARATION_FACTOR_REG "])+\\n[%2]", + as_string(column_separation[i - 1]), + span_width_reg(i, i)); + prints('\n'); +} + +horizontal_span::horizontal_span(int sc, int ec, horizontal_span *p) +: start_col(sc), end_col(ec), next(p) +{ +} + +void table::build_span_list() +{ + span_list = 0; + table_entry *p = entry_list; + while (p) { + if (p->end_col != p->start_col) { + horizontal_span *q; + for (q = span_list; q; q = q->next) + if (q->start_col == p->start_col + && q->end_col == p->end_col) + break; + if (!q) + span_list = new horizontal_span(p->start_col, p->end_col, span_list); + } + p = p->next; + } + // Now sort span_list primarily by order of end_row, and secondarily + // by reverse order of start_row. This ensures that if we divide + // spans using the order in span_list, we will get reasonable results. + horizontal_span *unsorted = span_list; + span_list = 0; + while (unsorted) { + horizontal_span **pp; + for (pp = &span_list; *pp; pp = &(*pp)->next) + if (unsorted->end_col < (*pp)->end_col + || (unsorted->end_col == (*pp)->end_col + && (unsorted->start_col > (*pp)->start_col))) + break; + horizontal_span *tem = unsorted->next; + unsorted->next = *pp; + *pp = unsorted; + unsorted = tem; + } +} + + +void table::compute_separation_factor() +{ + if (flags & (ALLBOX|BOX|DOUBLEBOX)) + left_separation = right_separation = 1; + else { + for (int i = 0; i < nrows; i++) { + if (vline[i][0] > 0) + left_separation = 1; + if (vline[i][ncolumns] > 0) + right_separation = 1; + } + } + if (flags & EXPAND) { + int total_sep = left_separation + right_separation; + int i; + for (i = 0; i < ncolumns - 1; i++) + total_sep += column_separation[i]; + if (total_sep != 0) { + // Don't let the separation factor be negative. + prints(".nr " SEPARATION_FACTOR_REG " \\n[.l]-\\n[.i]"); + for (i = 0; i < ncolumns; i++) + printfs("-\\n[%1]", span_width_reg(i, i)); + printfs("/%1>?0\n", as_string(total_sep)); + } + } +} + +void table::compute_column_positions() +{ + printfs(".nr %1 0\n", column_divide_reg(0)); + printfs(".nr %1 %2*\\n[" SEPARATION_FACTOR_REG "]\n", + column_start_reg(0), + as_string(left_separation)); + int i; + for (i = 1;; i++) { + printfs(".nr %1 \\n[%2]+\\n[%3]\n", + column_end_reg(i-1), + column_start_reg(i-1), + span_width_reg(i-1, i-1)); + if (i >= ncolumns) + break; + printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n", + column_start_reg(i), + column_end_reg(i-1), + as_string(column_separation[i-1])); + printfs(".nr %1 \\n[%2]+\\n[%3]/2\n", + column_divide_reg(i), + column_end_reg(i-1), + column_start_reg(i)); + } + printfs(".nr %1 \\n[%2]+(%3*\\n[" SEPARATION_FACTOR_REG "])\n", + column_divide_reg(ncolumns), + column_end_reg(i-1), + as_string(right_separation)); + printfs(".nr TW \\n[%1]\n", + column_divide_reg(ncolumns)); + if (flags & DOUBLEBOX) { + printfs(".nr %1 +" DOUBLE_LINE_SEP "\n", column_divide_reg(0)); + printfs(".nr %1 -" DOUBLE_LINE_SEP "\n", column_divide_reg(ncolumns)); + } +} + +void table::make_columns_equal() +{ + int first = -1; // index of first equal column + int i; + for (i = 0; i < ncolumns; i++) + if (equal[i]) { + if (first < 0) { + printfs(".nr %1 \\n[%1]", span_width_reg(i, i)); + first = i; + } + else + printfs(">?\\n[%1]", span_width_reg(i, i)); + } + if (first >= 0) { + prints('\n'); + for (i = first + 1; i < ncolumns; i++) + if (equal[i]) + printfs(".nr %1 \\n[%2]\n", + span_width_reg(i, i), + span_width_reg(first, first)); + } +} + +void table::compute_widths() +{ + build_span_list(); + int i; + horizontal_span *p; + prints(".nr " SEPARATION_FACTOR_REG " 1n\n"); + for (i = 0; i < ncolumns; i++) { + init_span_reg(i, i); + if (!minimum_width[i].empty()) + printfs(".nr %1 %2\n", span_width_reg(i, i), minimum_width[i]); + } + for (p = span_list; p; p = p->next) + init_span_reg(p->start_col, p->end_col); + table_entry *q; + for (q = entry_list; q; q = q->next) + if (!q->mod->zero_width) + q->do_width(); + for (i = 0; i < ncolumns; i++) + compute_span_width(i, i); + for (p = span_list; p; p = p->next) + compute_span_width(p->start_col, p->end_col); + make_columns_equal(); + // Note that divide_span keeps equal width columns equal. + for (p = span_list; p; p = p->next) + divide_span(p->start_col, p->end_col); + for (p = span_list; p; p = p->next) + sum_columns(p->start_col, p->end_col); + int had_spanning_block = 0; + int had_equal_block = 0; + for (q = entry_list; q; q = q->next) + if (q->divert(ncolumns, minimum_width, + (flags & EXPAND) ? column_separation : 0)) { + if (q->end_col > q->start_col) + had_spanning_block = 1; + for (i = q->start_col; i <= q->end_col && !had_equal_block; i++) + if (equal[i]) + had_equal_block = 1; + } + if (had_equal_block) + make_columns_equal(); + if (had_spanning_block) + for (p = span_list; p; p = p->next) + divide_span(p->start_col, p->end_col); + compute_separation_factor(); + for (p = span_list; p; p = p->next) + sum_columns(p->start_col, p->end_col); + compute_column_positions(); +} + +void table::print_single_hline(int r) +{ + prints(".vs " LINE_SEP ">?\\n[.V]u\n" + ".ls 1\n" + "\\v'" BODY_DEPTH "'" + "\\s[\\n[" LINESIZE_REG "]]"); + if (r > nrows - 1) + prints("\\D'l |\\n[TW]u 0'"); + else { + int start_col = 0; + for (;;) { + while (start_col < ncolumns + && entry[r][start_col] != 0 + && entry[r][start_col]->start_row != r) + start_col++; + int end_col; + for (end_col = start_col; + end_col < ncolumns + && (entry[r][end_col] == 0 + || entry[r][end_col]->start_row == r); + end_col++) + ; + if (end_col <= start_col) + break; + printfs("\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if ((r > 0 && vline[r-1][start_col] == 2) + || (r < nrows && vline[r][start_col] == 2)) + prints("-" HALF_DOUBLE_LINE_SEP); + prints("'"); + printfs("\\D'l |\\n[%1]u", + column_divide_reg(end_col)); + if ((r > 0 && vline[r-1][end_col] == 2) + || (r < nrows && vline[r][end_col] == 2)) + prints("+" HALF_DOUBLE_LINE_SEP); + prints(" 0'"); + start_col = end_col; + } + } + prints("\\s0\n"); + prints(".ls\n" + ".vs\n"); +} + +void table::print_double_hline(int r) +{ + prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP + ">?\\n[.V]u\n" + ".ls 1\n" + "\\v'" BODY_DEPTH "'" + "\\s[\\n[" LINESIZE_REG "]]"); + if (r > nrows - 1) + prints("\\v'-" DOUBLE_LINE_SEP "'" + "\\D'l |\\n[TW]u 0'" + "\\v'" DOUBLE_LINE_SEP "'" + "\\h'|0'" + "\\D'l |\\n[TW]u 0'"); + else { + int start_col = 0; + for (;;) { + while (start_col < ncolumns + && entry[r][start_col] != 0 + && entry[r][start_col]->start_row != r) + start_col++; + int end_col; + for (end_col = start_col; + end_col < ncolumns + && (entry[r][end_col] == 0 + || entry[r][end_col]->start_row == r); + end_col++) + ; + if (end_col <= start_col) + break; + const char *left_adjust = 0; + if ((r > 0 && vline[r-1][start_col] == 2) + || (r < nrows && vline[r][start_col] == 2)) + left_adjust = "-" HALF_DOUBLE_LINE_SEP; + const char *right_adjust = 0; + if ((r > 0 && vline[r-1][end_col] == 2) + || (r < nrows && vline[r][end_col] == 2)) + right_adjust = "+" HALF_DOUBLE_LINE_SEP; + printfs("\\v'-" DOUBLE_LINE_SEP "'" + "\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if (left_adjust) + prints(left_adjust); + prints("'"); + printfs("\\D'l |\\n[%1]u", + column_divide_reg(end_col)); + if (right_adjust) + prints(right_adjust); + prints(" 0'"); + printfs("\\v'" DOUBLE_LINE_SEP "'" + "\\h'|\\n[%1]u", + column_divide_reg(start_col)); + if (left_adjust) + prints(left_adjust); + prints("'"); + printfs("\\D'l |\\n[%1]u", + column_divide_reg(end_col)); + if (right_adjust) + prints(right_adjust); + prints(" 0'"); + start_col = end_col; + } + } + prints("\\s0\n" + ".ls\n" + ".vs\n"); +} + +void table::compute_vrule_top_adjust(int start_row, int col, string &result) +{ + if (row_is_all_lines[start_row] && start_row < nrows - 1) { + if (row_is_all_lines[start_row] == 2) + result = LINE_SEP ">?\\n[.V]u" "+" DOUBLE_LINE_SEP; + else + result = LINE_SEP ">?\\n[.V]u"; + start_row++; + } + else { + result = ""; + if (start_row == 0) + return; + for (stuff *p = stuff_list; p && p->row <= start_row; p = p->next) + if (p->row == start_row + && (p->is_single_line() || p->is_double_line())) + return; + } + int left = 0; + if (col > 0) { + table_entry *e = entry[start_row-1][col-1]; + if (e && e->start_row == e->end_row) { + if (e->to_double_line_entry() != 0) + left = 2; + else if (e->to_single_line_entry() != 0) + left = 1; + } + } + int right = 0; + if (col < ncolumns) { + table_entry *e = entry[start_row-1][col]; + if (e && e->start_row == e->end_row) { + if (e->to_double_line_entry() != 0) + right = 2; + else if (e->to_single_line_entry() != 0) + right = 1; + } + } + if (row_is_all_lines[start_row-1] == 0) { + if (left > 0 || right > 0) { + result += "-" BODY_DEPTH "-" BAR_HEIGHT; + if ((left == 2 && right != 2) || (right == 2 && left != 2)) + result += "-" HALF_DOUBLE_LINE_SEP; + else if (left == 2 && right == 2) + result += "+" HALF_DOUBLE_LINE_SEP; + } + } + else if (row_is_all_lines[start_row-1] == 2) { + if ((left == 2 && right != 2) || (right == 2 && left != 2)) + result += "-" DOUBLE_LINE_SEP; + else if (left == 1 || right == 1) + result += "-" HALF_DOUBLE_LINE_SEP; + } +} + +void table::compute_vrule_bot_adjust(int end_row, int col, string &result) +{ + if (row_is_all_lines[end_row] && end_row > 0) { + end_row--; + result = ""; + } + else { + stuff *p; + for (p = stuff_list; p && p->row < end_row + 1; p = p->next) + ; + if (p && p->row == end_row + 1 && p->is_double_line()) { + result = "-" DOUBLE_LINE_SEP; + return; + } + if ((p != 0 && p->row == end_row + 1) + || end_row == nrows - 1) { + result = ""; + return; + } + if (row_is_all_lines[end_row+1] == 1) + result = LINE_SEP; + else if (row_is_all_lines[end_row+1] == 2) + result = LINE_SEP "+" DOUBLE_LINE_SEP; + else + result = ""; + } + int left = 0; + if (col > 0) { + table_entry *e = entry[end_row+1][col-1]; + if (e && e->start_row == e->end_row) { + if (e->to_double_line_entry() != 0) + left = 2; + else if (e->to_single_line_entry() != 0) + left = 1; + } + } + int right = 0; + if (col < ncolumns) { + table_entry *e = entry[end_row+1][col]; + if (e && e->start_row == e->end_row) { + if (e->to_double_line_entry() != 0) + right = 2; + else if (e->to_single_line_entry() != 0) + right = 1; + } + } + if (row_is_all_lines[end_row+1] == 0) { + if (left > 0 || right > 0) { + result = "1v-" BODY_DEPTH "-" BAR_HEIGHT; + if ((left == 2 && right != 2) || (right == 2 && left != 2)) + result += "+" HALF_DOUBLE_LINE_SEP; + else if (left == 2 && right == 2) + result += "-" HALF_DOUBLE_LINE_SEP; + } + } + else if (row_is_all_lines[end_row+1] == 2) { + if (left == 2 && right == 2) + result += "-" DOUBLE_LINE_SEP; + else if (left != 2 && right != 2 && (left == 1 || right == 1)) + result += "-" HALF_DOUBLE_LINE_SEP; + } +} + +void table::add_vertical_rule(int start_row, int end_row, int col, int is_double) +{ + vrule_list = new vertical_rule(start_row, end_row, col, is_double, + vrule_list); + compute_vrule_top_adjust(start_row, col, vrule_list->top_adjust); + compute_vrule_bot_adjust(end_row, col, vrule_list->bot_adjust); +} + +void table::build_vrule_list() +{ + int col; + if (flags & ALLBOX) { + for (col = 1; col < ncolumns; col++) { + int start_row = 0; + for (;;) { + while (start_row < nrows && vline_spanned(start_row, col)) + start_row++; + if (start_row >= nrows) + break; + int end_row = start_row; + while (end_row < nrows && !vline_spanned(end_row, col)) + end_row++; + end_row--; + add_vertical_rule(start_row, end_row, col, 0); + start_row = end_row + 1; + } + } + } + if (flags & (BOX|ALLBOX|DOUBLEBOX)) { + add_vertical_rule(0, nrows - 1, 0, 0); + add_vertical_rule(0, nrows - 1, ncolumns, 0); + } + for (int end_row = 0; end_row < nrows; end_row++) + for (col = 0; col < ncolumns+1; col++) + if (vline[end_row][col] > 0 + && !vline_spanned(end_row, col) + && (end_row == nrows - 1 + || vline[end_row+1][col] != vline[end_row][col] + || vline_spanned(end_row+1, col))) { + int start_row; + for (start_row = end_row - 1; + start_row >= 0 + && vline[start_row][col] == vline[end_row][col] + && !vline_spanned(start_row, col); + start_row--) + ; + start_row++; + add_vertical_rule(start_row, end_row, col, vline[end_row][col] > 1); + } + for (vertical_rule *p = vrule_list; p; p = p->next) + if (p->is_double) + for (int r = p->start_row; r <= p->end_row; r++) { + if (p->col > 0 && entry[r][p->col-1] != 0 + && entry[r][p->col-1]->end_col == p->col-1) { + int is_corner = r == p->start_row || r == p->end_row; + entry[r][p->col-1]->note_double_vrule_on_right(is_corner); + } + if (p->col < ncolumns && entry[r][p->col] != 0 + && entry[r][p->col]->start_col == p->col) { + int is_corner = r == p->start_row || r == p->end_row; + entry[r][p->col]->note_double_vrule_on_left(is_corner); + } + } +} + +void table::define_bottom_macro() +{ + prints(".eo\n" + ".de T#\n" + ".if !\\n[" SUPPRESS_BOTTOM_REG "] \\{" + "." REPEATED_VPT_MACRO " 0\n" + ".mk " SAVED_VERTICAL_POS_REG "\n"); + if (flags & (BOX|ALLBOX|DOUBLEBOX)) { + prints(".if \\n[T.]&\\n[" NEED_BOTTOM_RULE_REG "] \\{"); + print_single_hline(0); + prints(".\\}\n"); + } + prints(".ls 1\n"); + for (vertical_rule *p = vrule_list; p; p = p->next) + p->contribute_to_bottom_macro(this); + if (flags & DOUBLEBOX) + prints(".if \\n[T.] \\{.vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n" + "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]" + "\\D'l \\n[TW]u 0'\\s0\n" + ".vs\n" + ".\\}\n" + ".if \\n[" LAST_PASSED_ROW_REG "]>=0 " + ".nr " TOP_REG " \\n[#T]-" DOUBLE_LINE_SEP "\n" + ".sp -1\n" + "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]" + "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n" + ".sp -1\n" + "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]" + "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n"); + prints(".ls\n"); + prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n" + ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n" + "." REPEATED_VPT_MACRO " 1\n" + ".\\}\n" + "..\n" + ".ec\n"); +} + + +// is the vertical line before column c in row r horizontally spanned? + +int table::vline_spanned(int r, int c) +{ + assert(r >= 0 && r < nrows && c >= 0 && c < ncolumns + 1); + return (c != 0 && c != ncolumns && entry[r][c] != 0 + && entry[r][c]->start_col != c + // horizontally spanning lines don't count + && entry[r][c]->to_double_line_entry() == 0 + && entry[r][c]->to_single_line_entry() == 0); +} + +int table::row_begins_section(int r) +{ + assert(r >= 0 && r < nrows); + for (int i = 0; i < ncolumns; i++) + if (entry[r][i] && entry[r][i]->start_row != r) + return 0; + return 1; +} + +int table::row_ends_section(int r) +{ + assert(r >= 0 && r < nrows); + for (int i = 0; i < ncolumns; i++) + if (entry[r][i] && entry[r][i]->end_row != r) + return 0; + return 1; +} + +void table::do_row(int r) +{ + if (!(flags & NOKEEP) && row_begins_section(r)) + prints("." KEEP_MACRO_NAME "\n"); + int had_line = 0; + stuff *p; + for (p = stuff_list; p && p->row < r; p = p->next) + ; + for (stuff *p1 = p; p1 && p1->row == r; p1 = p1->next) + if (!p1->printed && (p1->is_single_line() || p1->is_double_line())) { + had_line = 1; + break; + } + if (!had_line && !row_is_all_lines[r]) + printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r)); + had_line = 0; + for (; p && p->row == r; p = p->next) + if (!p->printed) { + p->print(this); + if (!had_line && (p->is_single_line() || p->is_double_line())) { + printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r)); + had_line = 1; + } + } + // Change the row *after* printing the stuff list (which might contain .TH). + printfs("\\*[" TRANSPARENT_STRING_NAME "].nr " CURRENT_ROW_REG " %1\n", + as_string(r)); + if (!had_line && row_is_all_lines[r]) + printfs("." REPEATED_MARK_MACRO " %1\n", row_top_reg(r)); + // we might have had a .TH, for example, since we last tried + if (!(flags & NOKEEP) && row_begins_section(r)) + prints("." KEEP_MACRO_NAME "\n"); + printfs(".mk %1\n", row_start_reg(r)); + prints(".mk " BOTTOM_REG "\n" + "." REPEATED_VPT_MACRO " 0\n"); + int c; + int row_is_blank = 1; + int first_start_row = r; + for (c = 0; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == r) { + e->do_depth(); + if (e->start_row < first_start_row) + first_start_row = e->start_row; + row_is_blank = 0; + } + c = e->end_col; + } + } + if (row_is_blank) + prints(".nr " BOTTOM_REG " +1v\n"); + if (row_is_all_lines[r]) { + prints(".vs " LINE_SEP); + if (row_is_all_lines[r] == 2) + prints("+" DOUBLE_LINE_SEP); + prints(">?\\n[.V]u\n.ls 1\n"); + prints("\\&"); + prints("\\v'" BODY_DEPTH); + if (row_is_all_lines[r] == 2) + prints("-" HALF_DOUBLE_LINE_SEP); + prints("'"); + for (c = 0; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == e->start_row) + e->to_simple_entry()->simple_print(1); + c = e->end_col; + } + } + prints("\n"); + prints(".ls\n" + ".vs\n"); + prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n"); + printfs(".sp |\\n[%1]u\n", row_start_reg(r)); + } + for (int i = row_is_all_lines[r] ? r - 1 : r; + i >= first_start_row; + i--) { + simple_entry *first = 0; + for (c = 0; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == r && e->start_row == i) { + simple_entry *simple = e->to_simple_entry(); + if (simple) { + if (!first) { + prints(".ta"); + first = simple; + } + simple->add_tab(); + } + } + c = e->end_col; + } + } + if (first) { + prints('\n'); + first->position_vertically(); + first->set_location(); + prints("\\&"); + first->simple_print(0); + for (c = first->end_col + 1; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == r && e->start_row == i) { + simple_entry *simple = e->to_simple_entry(); + if (simple) + simple->simple_print(0); + } + c = e->end_col; + } + } + prints('\n'); + prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n"); + printfs(".sp |\\n[%1]u\n", row_start_reg(r)); + } + } + for (c = 0; c < ncolumns; c++) { + table_entry *e = entry[r][c]; + if (e) { + if (e->end_row == r && e->to_simple_entry() == 0) { + e->position_vertically(); + e->print(); + prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n"); + printfs(".sp |\\n[%1]u\n", row_start_reg(r)); + } + c = e->end_col; + } + } + prints("." REPEATED_VPT_MACRO " 1\n" + ".sp |\\n[" BOTTOM_REG "]u\n" + "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n"); + if (r != nrows - 1 && (flags & ALLBOX)) { + print_single_hline(r + 1); + prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 0\n"); + } + if (r != nrows - 1) { + if (p && p->row == r + 1 + && (p->is_single_line() || p->is_double_line())) { + p->print(this); + prints("\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG + " 0\n"); + } + int printed_one = 0; + for (vertical_rule *p = vrule_list; p; p = p->next) + if (p->end_row == r) { + if (!printed_one) { + prints("." REPEATED_VPT_MACRO " 0\n"); + printed_one = 1; + } + p->print(); + } + if (printed_one) + prints("." REPEATED_VPT_MACRO " 1\n"); + if (!(flags & NOKEEP) && row_ends_section(r)) + prints("." RELEASE_MACRO_NAME "\n"); + } +} + +void table::do_top() +{ + prints(".fc \002\003\n"); + if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX))) + prints("." TABLE_KEEP_MACRO_NAME "\n"); + if (flags & DOUBLEBOX) { + prints(".ls 1\n" + ".vs " LINE_SEP ">?\\n[.V]u\n" + "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n" + ".vs\n" + "." REPEATED_MARK_MACRO " " TOP_REG "\n" + ".vs " DOUBLE_LINE_SEP ">?\\n[.V]u\n"); + printfs("\\v'" BODY_DEPTH "'" + "\\s[\\n[" LINESIZE_REG "]]" + "\\h'\\n[%1]u'" + "\\D'l |\\n[%2]u 0'" + "\\s0" + "\n", + column_divide_reg(0), + column_divide_reg(ncolumns)); + prints(".ls\n" + ".vs\n"); + } + else if (flags & (ALLBOX|BOX)) { + print_single_hline(0); + } + //printfs(".mk %1\n", row_top_reg(0)); +} + +void table::do_bottom() +{ + // print stuff after last row + for (stuff *p = stuff_list; p; p = p->next) + if (p->row > nrows - 1) + p->print(this); + if (!(flags & NOKEEP)) + prints("." RELEASE_MACRO_NAME "\n"); + printfs(".mk %1\n", row_top_reg(nrows)); + prints(".nr " NEED_BOTTOM_RULE_REG " 1\n" + ".nr T. 1\n" + ".T#\n"); + if (!(flags & NOKEEP) && (flags & (BOX|DOUBLEBOX|ALLBOX))) + prints("." TABLE_RELEASE_MACRO_NAME "\n"); + if (flags & DOUBLEBOX) + prints(".sp " DOUBLE_LINE_SEP "\n"); + prints("." RESET_MACRO_NAME "\n" + ".fc\n" + ".cp \\n(" COMPATIBLE_REG "\n"); +} + +int table::get_nrows() +{ + return nrows; +} + +const char *last_filename = 0; + +void set_troff_location(const char *fn, int ln) +{ + if (!location_force_filename && last_filename != 0 + && strcmp(fn, last_filename) == 0) + printfs(".lf %1\n", as_string(ln)); + else { + printfs(".lf %1 %2\n", as_string(ln), fn); + last_filename = fn; + location_force_filename = 0; + } +} + +void printfs(const char *s, const string &arg1, const string &arg2, + const string &arg3, const string &arg4, const string &arg5) +{ + if (s) { + char c; + while ((c = *s++) != '\0') { + if (c == '%') { + switch (*s++) { + case '1': + prints(arg1); + break; + case '2': + prints(arg2); + break; + case '3': + prints(arg3); + break; + case '4': + prints(arg4); + break; + case '5': + prints(arg5); + break; + case '6': + case '7': + case '8': + case '9': + break; + case '%': + prints('%'); + break; + default: + assert(0); + } + } + else + prints(c); + } + } +} + diff --git a/contrib/groff/tbl/table.h b/contrib/groff/tbl/table.h new file mode 100644 index 000000000000..ca55b8026c22 --- /dev/null +++ b/contrib/groff/tbl/table.h @@ -0,0 +1,152 @@ +// -*- C++ -*- +/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.com) + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2, or (at your option) any later +version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with groff; see the file COPYING. If not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> + +#include "cset.h" +#include "cmap.h" +#include "stringclass.h" +#include "errarg.h" +#include "error.h" +#include "lib.h" + +struct inc_number { + short inc; + short val; +}; + +struct entry_modifier { + inc_number point_size; + inc_number vertical_spacing; + string font; + enum { CENTER, TOP, BOTTOM } vertical_alignment; + char zero_width; + char stagger; + + entry_modifier(); + ~entry_modifier(); +}; + +enum format_type { + FORMAT_LEFT, + FORMAT_CENTER, + FORMAT_RIGHT, + FORMAT_NUMERIC, + FORMAT_ALPHABETIC, + FORMAT_SPAN, + FORMAT_VSPAN, + FORMAT_HLINE, + FORMAT_DOUBLE_HLINE +}; + +struct entry_format : public entry_modifier { + format_type type; + + entry_format(format_type); + entry_format(); + void debug_print() const; +}; + +struct table_entry; +struct horizontal_span; +struct stuff; +struct vertical_rule; + +class table { + unsigned flags; + int nrows; + int ncolumns; + int linesize; + char delim[2]; + char decimal_point_char; + vertical_rule *vrule_list; + stuff *stuff_list; + horizontal_span *span_list; + table_entry *entry_list; + table_entry **entry_list_tailp; + table_entry ***entry; + char **vline; + char *row_is_all_lines; + string *minimum_width; + int *column_separation; + char *equal; + int left_separation; + int right_separation; + int allocated_rows; + void build_span_list(); + void do_hspan(int r, int c); + void do_vspan(int r, int c); + void allocate(int r); + void compute_widths(); + void divide_span(int, int); + void sum_columns(int, int); + void compute_separation_factor(); + void compute_column_positions(); + void do_row(int); + void init_output(); + void add_stuff(stuff *); + void do_top(); + void do_bottom(); + void do_vertical_rules(); + void build_vrule_list(); + void add_vertical_rule(int, int, int, int); + void define_bottom_macro(); + int vline_spanned(int r, int c); + int row_begins_section(int); + int row_ends_section(int); + void make_columns_equal(); + void compute_vrule_top_adjust(int, int, string &); + void compute_vrule_bot_adjust(int, int, string &); + void determine_row_type(); +public: + /* used by flags */ + enum { + CENTER = 01, + EXPAND = 02, + BOX = 04, + ALLBOX = 010, + DOUBLEBOX = 020, + NOKEEP = 040 + }; + table(int nc, unsigned flags, int linesize, char decimal_point_char); + ~table(); + + void add_text_line(int r, const string &, const char *, int); + void add_single_hline(int r); + void add_double_hline(int r); + void add_entry(int r, int c, const string &, const entry_format *, + const char *, int lineno); + void add_vlines(int r, const char *); + void check(); + void print(); + void set_minimum_width(int c, const string &w); + void set_column_separation(int c, int n); + void set_equal_column(int c); + void set_delim(char c1, char c2); + void print_single_hline(int r); + void print_double_hline(int r); + int get_nrows(); +}; + +void set_troff_location(const char *, int); diff --git a/contrib/groff/tbl/tbl.man b/contrib/groff/tbl/tbl.man new file mode 100644 index 000000000000..6016ddf35f8f --- /dev/null +++ b/contrib/groff/tbl/tbl.man @@ -0,0 +1,178 @@ +.ig \"-*- nroff -*- +Copyright (C) 1989-1995 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be included in +translations approved by the Free Software Foundation instead of in +the original English. +.. +.TH @G@TBL @MAN1EXT@ "@MDATE@" "Groff Version @VERSION@" +.SH NAME +@g@tbl \- format tables for troff +.SH SYNOPSIS +.B @g@tbl +[ +.B \-Cv +] +[ +.IR files \|.\|.\|. +] +.SH DESCRIPTION +This manual page describes the GNU version of +.BR tbl , +which is part of the groff document formatting system. +.B tbl +compiles descriptions of tables embedded within +.B troff +input files into commands that are understood by +.BR troff . +Normally, it should be invoked using the +.B \-t +option of +.B groff. +It is highly compatible with Unix +.BR tbl . +The output generated by GNU +.B tbl +cannot be processed with Unix +.BR troff ; +it must be processed with GNU +.BR troff . +If no files are given on the command line, the standard input +will be read. +A filename of +.B \- +will cause the standard input to be read. +.SH OPTIONS +.TP +.B \-C +Recognize +.B .TS +and +.B .TE +even when followed by a character other than space or newline. +.TP +.B \-v +Print the version number. +.SH USAGE +Only the differences between GNU +.B tbl +and Unix +.B tbl +are described here. +.LP +Normally +.B tbl +attempts to prevent undesirable breaks in the table by using diversions. +This can sometimes interact badly with macro packages' own use of diversions, +when footnotes, for example, are used. +The +.B nokeep +option tells +.B tbl +not to try and prevent breaks in this way. +.LP +The +.B decimalpoint +option specifies the character to be recognized as the decimal +point character in place of the default period. +It takes an argument in parentheses, which must be a single +character, as for the +.B tab +option. +.LP +The +.B f +format modifier can be followed by an arbitrary length +font name in parentheses. +.LP +There is a +.B d +format modifier which means that a vertically spanning entry +should be aligned at the bottom of its range. +.LP +There is no limit on the number of columns in a table, nor any limit +on the number of text blocks. +All the lines of a table are considered in deciding column +widths, not just the first 200. +Table continuation +.RB ( .T& ) +lines are not restricted to the first 200 lines. +.LP +Numeric and alphabetic items may appear in the same column. +.LP +Numeric and alphabetic items may span horizontally. +.LP +.B tbl +uses register, string, macro and diversion names beginning with +.BR 3 . +When using +.B tbl +you should avoid using any names beginning with a +.BR 3 . +.SH BUGS +You should use +.BR .TS\ H / .TH +in conjunction with a supporting macro package for +.I all +multi-page boxed tables. +If there is no header that you wish to appear at the top of each page +of the table, place the +.B .TH +line immediately after the format section. +Do not enclose a multi-page table within keep/release macros, +or divert it in any other way. +.LP +A text block within a table must be able to fit on one page. +.LP +The +.B bp +request cannot be used to force a page-break in a multi-page table. +Instead, define +.B BP +as follows +.IP +.B .de BP +.br +.B .ie '\e\en(.z'' .bp \e\e$1 +.br +.B .el \e!.BP \e\e$1 +.br +.B .. +.br +.LP +and use +.B BP +instead of +.BR bp . +.LP +Using \ea directly in a table to get leaders will not work. +This is correct behaviour: \ea is a +.B uninterpreted +leader. +To get leaders use a real leader, either by using a control A or like +this: +.IP +.nf +.ft B +\&.ds a \ea +\&.TS +tab(;); +lw(1i) l. +A\e*a;B +\&.TE +.ft +.fi +.SH "SEE ALSO" +.BR groff (@MAN1EXT@), +.BR @g@troff (@MAN1EXT@) |