1114402Sru// -*- C++ -*- 2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005 3114402Sru Free Software Foundation, Inc. 4114402Sru Written by James Clark (jjc@jclark.com) 5114402Sru 6114402SruThis file is part of groff. 7114402Sru 8114402Srugroff is free software; you can redistribute it and/or modify it under 9114402Sruthe terms of the GNU General Public License as published by the Free 10114402SruSoftware Foundation; either version 2, or (at your option) any later 11114402Sruversion. 12114402Sru 13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY 14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or 15114402SruFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16114402Srufor more details. 17114402Sru 18114402SruYou should have received a copy of the GNU General Public License along 19114402Sruwith groff; see the file COPYING. If not, write to the Free Software 20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21114402Sru 22114402Sru#include "table.h" 23114402Sru 24114402Sru#define MAX_POINT_SIZE 99 25114402Sru#define MAX_VERTICAL_SPACING 72 26114402Sru 27114402Sruextern "C" const char *Version_string; 28114402Sru 29151497Sruint compatible_flag = 0; 30114402Sru 31114402Sruclass table_input { 32114402Sru FILE *fp; 33151497Sru enum { START, MIDDLE, 34151497Sru REREAD_T, REREAD_TE, REREAD_E, 35151497Sru LEADER_1, LEADER_2, LEADER_3, LEADER_4, 36151497Sru END, ERROR } state; 37114402Sru string unget_stack; 38114402Srupublic: 39114402Sru table_input(FILE *); 40114402Sru int get(); 41114402Sru int ended() { return unget_stack.empty() && state == END; } 42114402Sru void unget(char); 43114402Sru}; 44114402Sru 45114402Srutable_input::table_input(FILE *p) 46114402Sru: fp(p), state(START) 47114402Sru{ 48114402Sru} 49114402Sru 50114402Sruvoid table_input::unget(char c) 51114402Sru{ 52114402Sru assert(c != '\0'); 53114402Sru unget_stack += c; 54114402Sru if (c == '\n') 55114402Sru current_lineno--; 56114402Sru} 57114402Sru 58114402Sruint table_input::get() 59114402Sru{ 60114402Sru int len = unget_stack.length(); 61114402Sru if (len != 0) { 62114402Sru unsigned char c = unget_stack[len - 1]; 63114402Sru unget_stack.set_length(len - 1); 64114402Sru if (c == '\n') 65114402Sru current_lineno++; 66114402Sru return c; 67114402Sru } 68114402Sru int c; 69114402Sru for (;;) { 70114402Sru switch (state) { 71114402Sru case START: 72114402Sru if ((c = getc(fp)) == '.') { 73114402Sru if ((c = getc(fp)) == 'T') { 74114402Sru if ((c = getc(fp)) == 'E') { 75114402Sru if (compatible_flag) { 76114402Sru state = END; 77114402Sru return EOF; 78114402Sru } 79114402Sru else { 80114402Sru c = getc(fp); 81114402Sru if (c != EOF) 82114402Sru ungetc(c, fp); 83114402Sru if (c == EOF || c == ' ' || c == '\n') { 84114402Sru state = END; 85114402Sru return EOF; 86114402Sru } 87114402Sru state = REREAD_TE; 88114402Sru return '.'; 89114402Sru } 90114402Sru } 91114402Sru else { 92114402Sru if (c != EOF) 93114402Sru ungetc(c, fp); 94114402Sru state = REREAD_T; 95114402Sru return '.'; 96114402Sru } 97114402Sru } 98114402Sru else { 99114402Sru if (c != EOF) 100114402Sru ungetc(c, fp); 101114402Sru state = MIDDLE; 102114402Sru return '.'; 103114402Sru } 104114402Sru } 105114402Sru else if (c == EOF) { 106114402Sru state = ERROR; 107114402Sru return EOF; 108114402Sru } 109114402Sru else { 110114402Sru if (c == '\n') 111114402Sru current_lineno++; 112114402Sru else { 113114402Sru state = MIDDLE; 114114402Sru if (c == '\0') { 115114402Sru error("invalid input character code 0"); 116114402Sru break; 117114402Sru } 118114402Sru } 119114402Sru return c; 120114402Sru } 121114402Sru break; 122114402Sru case MIDDLE: 123151497Sru // handle line continuation and uninterpreted leader character 124114402Sru if ((c = getc(fp)) == '\\') { 125114402Sru c = getc(fp); 126114402Sru if (c == '\n') 127114402Sru c = getc(fp); // perhaps state ought to be START now 128151497Sru else if (c == 'a' && compatible_flag) { 129151497Sru state = LEADER_1; 130151497Sru return '\\'; 131151497Sru } 132114402Sru else { 133114402Sru if (c != EOF) 134114402Sru ungetc(c, fp); 135114402Sru c = '\\'; 136114402Sru } 137114402Sru } 138114402Sru if (c == EOF) { 139114402Sru state = ERROR; 140114402Sru return EOF; 141114402Sru } 142114402Sru else { 143114402Sru if (c == '\n') { 144114402Sru state = START; 145114402Sru current_lineno++; 146114402Sru } 147114402Sru else if (c == '\0') { 148114402Sru error("invalid input character code 0"); 149114402Sru break; 150114402Sru } 151114402Sru return c; 152114402Sru } 153114402Sru case REREAD_T: 154114402Sru state = MIDDLE; 155114402Sru return 'T'; 156114402Sru case REREAD_TE: 157114402Sru state = REREAD_E; 158114402Sru return 'T'; 159114402Sru case REREAD_E: 160114402Sru state = MIDDLE; 161114402Sru return 'E'; 162151497Sru case LEADER_1: 163151497Sru state = LEADER_2; 164151497Sru return '*'; 165151497Sru case LEADER_2: 166151497Sru state = LEADER_3; 167151497Sru return '('; 168151497Sru case LEADER_3: 169151497Sru state = LEADER_4; 170151497Sru return PREFIX_CHAR; 171151497Sru case LEADER_4: 172151497Sru state = MIDDLE; 173151497Sru return LEADER_CHAR; 174114402Sru case END: 175114402Sru case ERROR: 176114402Sru return EOF; 177114402Sru } 178114402Sru } 179114402Sru} 180114402Sru 181114402Sruvoid process_input_file(FILE *); 182114402Sruvoid process_table(table_input &in); 183114402Sru 184114402Sruvoid process_input_file(FILE *fp) 185114402Sru{ 186114402Sru enum { START, MIDDLE, HAD_DOT, HAD_T, HAD_TS, HAD_l, HAD_lf } state; 187114402Sru state = START; 188114402Sru int c; 189114402Sru while ((c = getc(fp)) != EOF) 190114402Sru switch (state) { 191114402Sru case START: 192114402Sru if (c == '.') 193114402Sru state = HAD_DOT; 194114402Sru else { 195114402Sru if (c == '\n') 196114402Sru current_lineno++; 197114402Sru else 198114402Sru state = MIDDLE; 199114402Sru putchar(c); 200114402Sru } 201114402Sru break; 202114402Sru case MIDDLE: 203114402Sru if (c == '\n') { 204114402Sru current_lineno++; 205114402Sru state = START; 206114402Sru } 207114402Sru putchar(c); 208114402Sru break; 209114402Sru case HAD_DOT: 210114402Sru if (c == 'T') 211114402Sru state = HAD_T; 212114402Sru else if (c == 'l') 213114402Sru state = HAD_l; 214114402Sru else { 215114402Sru putchar('.'); 216114402Sru putchar(c); 217114402Sru if (c == '\n') { 218114402Sru current_lineno++; 219114402Sru state = START; 220114402Sru } 221114402Sru else 222114402Sru state = MIDDLE; 223114402Sru } 224114402Sru break; 225114402Sru case HAD_T: 226114402Sru if (c == 'S') 227114402Sru state = HAD_TS; 228114402Sru else { 229114402Sru putchar('.'); 230114402Sru putchar('T'); 231114402Sru putchar(c); 232114402Sru if (c == '\n') { 233114402Sru current_lineno++; 234114402Sru state = START; 235114402Sru } 236114402Sru else 237114402Sru state = MIDDLE; 238114402Sru } 239114402Sru break; 240114402Sru case HAD_TS: 241114402Sru if (c == ' ' || c == '\n' || compatible_flag) { 242114402Sru putchar('.'); 243114402Sru putchar('T'); 244114402Sru putchar('S'); 245114402Sru while (c != '\n') { 246114402Sru if (c == EOF) { 247114402Sru error("end of file at beginning of table"); 248114402Sru return; 249114402Sru } 250114402Sru putchar(c); 251114402Sru c = getc(fp); 252114402Sru } 253114402Sru putchar('\n'); 254114402Sru current_lineno++; 255114402Sru { 256114402Sru table_input input(fp); 257114402Sru process_table(input); 258114402Sru set_troff_location(current_filename, current_lineno); 259114402Sru if (input.ended()) { 260114402Sru fputs(".TE", stdout); 261114402Sru while ((c = getc(fp)) != '\n') { 262114402Sru if (c == EOF) { 263114402Sru putchar('\n'); 264114402Sru return; 265114402Sru } 266114402Sru putchar(c); 267114402Sru } 268114402Sru putchar('\n'); 269114402Sru current_lineno++; 270114402Sru } 271114402Sru } 272114402Sru state = START; 273114402Sru } 274114402Sru else { 275114402Sru fputs(".TS", stdout); 276114402Sru putchar(c); 277114402Sru state = MIDDLE; 278114402Sru } 279114402Sru break; 280114402Sru case HAD_l: 281114402Sru if (c == 'f') 282114402Sru state = HAD_lf; 283114402Sru else { 284114402Sru putchar('.'); 285114402Sru putchar('l'); 286114402Sru putchar(c); 287114402Sru if (c == '\n') { 288114402Sru current_lineno++; 289114402Sru state = START; 290114402Sru } 291114402Sru else 292114402Sru state = MIDDLE; 293114402Sru } 294114402Sru break; 295114402Sru case HAD_lf: 296114402Sru if (c == ' ' || c == '\n' || compatible_flag) { 297114402Sru string line; 298114402Sru while (c != EOF) { 299114402Sru line += c; 300114402Sru if (c == '\n') { 301114402Sru current_lineno++; 302114402Sru break; 303114402Sru } 304114402Sru c = getc(fp); 305114402Sru } 306114402Sru line += '\0'; 307114402Sru interpret_lf_args(line.contents()); 308114402Sru printf(".lf%s", line.contents()); 309114402Sru state = START; 310114402Sru } 311114402Sru else { 312114402Sru fputs(".lf", stdout); 313114402Sru putchar(c); 314114402Sru state = MIDDLE; 315114402Sru } 316114402Sru break; 317114402Sru default: 318114402Sru assert(0); 319114402Sru } 320114402Sru switch(state) { 321114402Sru case START: 322114402Sru break; 323114402Sru case MIDDLE: 324114402Sru putchar('\n'); 325114402Sru break; 326114402Sru case HAD_DOT: 327114402Sru fputs(".\n", stdout); 328114402Sru break; 329114402Sru case HAD_l: 330114402Sru fputs(".l\n", stdout); 331114402Sru break; 332114402Sru case HAD_T: 333114402Sru fputs(".T\n", stdout); 334114402Sru break; 335114402Sru case HAD_lf: 336114402Sru fputs(".lf\n", stdout); 337114402Sru break; 338114402Sru case HAD_TS: 339114402Sru fputs(".TS\n", stdout); 340114402Sru break; 341114402Sru } 342114402Sru if (fp != stdin) 343114402Sru fclose(fp); 344114402Sru} 345114402Sru 346114402Srustruct options { 347114402Sru unsigned flags; 348114402Sru int linesize; 349114402Sru char delim[2]; 350114402Sru char tab_char; 351114402Sru char decimal_point_char; 352114402Sru 353114402Sru options(); 354114402Sru}; 355114402Sru 356114402Sruoptions::options() 357114402Sru: flags(0), linesize(0), tab_char('\t'), decimal_point_char('.') 358114402Sru{ 359114402Sru delim[0] = delim[1] = '\0'; 360114402Sru} 361114402Sru 362114402Sru// Return non-zero if p and q are the same ignoring case. 363114402Sru 364114402Sruint strieq(const char *p, const char *q) 365114402Sru{ 366114402Sru for (; cmlower(*p) == cmlower(*q); p++, q++) 367114402Sru if (*p == '\0') 368114402Sru return 1; 369114402Sru return 0; 370114402Sru} 371114402Sru 372114402Sru// return 0 if we should give up in this table 373114402Sru 374114402Sruoptions *process_options(table_input &in) 375114402Sru{ 376114402Sru options *opt = new options; 377114402Sru string line; 378114402Sru int level = 0; 379114402Sru for (;;) { 380114402Sru int c = in.get(); 381114402Sru if (c == EOF) { 382114402Sru int i = line.length(); 383114402Sru while (--i >= 0) 384114402Sru in.unget(line[i]); 385114402Sru return opt; 386114402Sru } 387114402Sru if (c == '\n') { 388114402Sru in.unget(c); 389114402Sru int i = line.length(); 390114402Sru while (--i >= 0) 391114402Sru in.unget(line[i]); 392114402Sru return opt; 393114402Sru } 394114402Sru else if (c == '(') 395114402Sru level++; 396114402Sru else if (c == ')') 397114402Sru level--; 398114402Sru else if (c == ';' && level == 0) { 399114402Sru line += '\0'; 400114402Sru break; 401114402Sru } 402114402Sru line += c; 403114402Sru } 404114402Sru if (line.empty()) 405114402Sru return opt; 406114402Sru char *p = &line[0]; 407114402Sru for (;;) { 408114402Sru while (!csalpha(*p) && *p != '\0') 409114402Sru p++; 410114402Sru if (*p == '\0') 411114402Sru break; 412114402Sru char *q = p; 413114402Sru while (csalpha(*q)) 414114402Sru q++; 415114402Sru char *arg = 0; 416114402Sru if (*q != '(' && *q != '\0') 417114402Sru *q++ = '\0'; 418114402Sru while (csspace(*q)) 419114402Sru q++; 420114402Sru if (*q == '(') { 421114402Sru *q++ = '\0'; 422114402Sru arg = q; 423114402Sru while (*q != ')' && *q != '\0') 424114402Sru q++; 425114402Sru if (*q == '\0') 426114402Sru error("missing `)'"); 427114402Sru else 428114402Sru *q++ = '\0'; 429114402Sru } 430114402Sru if (*p == '\0') { 431114402Sru if (arg) 432114402Sru error("argument without option"); 433114402Sru } 434114402Sru else if (strieq(p, "tab")) { 435114402Sru if (!arg) 436114402Sru error("`tab' option requires argument in parentheses"); 437114402Sru else { 438114402Sru if (arg[0] == '\0' || arg[1] != '\0') 439114402Sru error("argument to `tab' option must be a single character"); 440114402Sru else 441114402Sru opt->tab_char = arg[0]; 442114402Sru } 443114402Sru } 444114402Sru else if (strieq(p, "linesize")) { 445114402Sru if (!arg) 446114402Sru error("`linesize' option requires argument in parentheses"); 447114402Sru else { 448114402Sru if (sscanf(arg, "%d", &opt->linesize) != 1) 449114402Sru error("bad linesize `%s'", arg); 450114402Sru else if (opt->linesize <= 0) { 451114402Sru error("linesize must be positive"); 452114402Sru opt->linesize = 0; 453114402Sru } 454114402Sru } 455114402Sru } 456114402Sru else if (strieq(p, "delim")) { 457114402Sru if (!arg) 458114402Sru error("`delim' option requires argument in parentheses"); 459114402Sru else if (arg[0] == '\0' || arg[1] == '\0' || arg[2] != '\0') 460114402Sru error("argument to `delim' option must be two characters"); 461114402Sru else { 462114402Sru opt->delim[0] = arg[0]; 463114402Sru opt->delim[1] = arg[1]; 464114402Sru } 465114402Sru } 466114402Sru else if (strieq(p, "center") || strieq(p, "centre")) { 467114402Sru if (arg) 468114402Sru error("`center' option does not take an argument"); 469114402Sru opt->flags |= table::CENTER; 470114402Sru } 471114402Sru else if (strieq(p, "expand")) { 472114402Sru if (arg) 473114402Sru error("`expand' option does not take an argument"); 474114402Sru opt->flags |= table::EXPAND; 475114402Sru } 476114402Sru else if (strieq(p, "box") || strieq(p, "frame")) { 477114402Sru if (arg) 478114402Sru error("`box' option does not take an argument"); 479114402Sru opt->flags |= table::BOX; 480114402Sru } 481114402Sru else if (strieq(p, "doublebox") || strieq(p, "doubleframe")) { 482114402Sru if (arg) 483114402Sru error("`doublebox' option does not take an argument"); 484114402Sru opt->flags |= table::DOUBLEBOX; 485114402Sru } 486114402Sru else if (strieq(p, "allbox")) { 487114402Sru if (arg) 488114402Sru error("`allbox' option does not take an argument"); 489114402Sru opt->flags |= table::ALLBOX; 490114402Sru } 491114402Sru else if (strieq(p, "nokeep")) { 492114402Sru if (arg) 493114402Sru error("`nokeep' option does not take an argument"); 494114402Sru opt->flags |= table::NOKEEP; 495114402Sru } 496114402Sru else if (strieq(p, "nospaces")) { 497114402Sru if (arg) 498114402Sru error("`nospaces' option does not take an argument"); 499114402Sru opt->flags |= table::NOSPACES; 500114402Sru } 501114402Sru else if (strieq(p, "decimalpoint")) { 502114402Sru if (!arg) 503114402Sru error("`decimalpoint' option requires argument in parentheses"); 504114402Sru else { 505114402Sru if (arg[0] == '\0' || arg[1] != '\0') 506114402Sru error("argument to `decimalpoint' option must be a single character"); 507114402Sru else 508114402Sru opt->decimal_point_char = arg[0]; 509114402Sru } 510114402Sru } 511114402Sru else { 512114402Sru error("unrecognised global option `%1'", p); 513114402Sru // delete opt; 514114402Sru // return 0; 515114402Sru } 516114402Sru p = q; 517114402Sru } 518114402Sru return opt; 519114402Sru} 520114402Sru 521114402Sruentry_modifier::entry_modifier() 522114402Sru: vertical_alignment(CENTER), zero_width(0), stagger(0) 523114402Sru{ 524114402Sru vertical_spacing.inc = vertical_spacing.val = 0; 525114402Sru point_size.inc = point_size.val = 0; 526114402Sru} 527114402Sru 528114402Sruentry_modifier::~entry_modifier() 529114402Sru{ 530114402Sru} 531114402Sru 532114402Sruentry_format::entry_format() : type(FORMAT_LEFT) 533114402Sru{ 534114402Sru} 535114402Sru 536114402Sruentry_format::entry_format(format_type t) : type(t) 537114402Sru{ 538114402Sru} 539114402Sru 540114402Sruvoid entry_format::debug_print() const 541114402Sru{ 542114402Sru switch (type) { 543114402Sru case FORMAT_LEFT: 544114402Sru putc('l', stderr); 545114402Sru break; 546114402Sru case FORMAT_CENTER: 547114402Sru putc('c', stderr); 548114402Sru break; 549114402Sru case FORMAT_RIGHT: 550114402Sru putc('r', stderr); 551114402Sru break; 552114402Sru case FORMAT_NUMERIC: 553114402Sru putc('n', stderr); 554114402Sru break; 555114402Sru case FORMAT_ALPHABETIC: 556114402Sru putc('a', stderr); 557114402Sru break; 558114402Sru case FORMAT_SPAN: 559114402Sru putc('s', stderr); 560114402Sru break; 561114402Sru case FORMAT_VSPAN: 562114402Sru putc('^', stderr); 563114402Sru break; 564114402Sru case FORMAT_HLINE: 565114402Sru putc('_', stderr); 566114402Sru break; 567114402Sru case FORMAT_DOUBLE_HLINE: 568114402Sru putc('=', stderr); 569114402Sru break; 570114402Sru default: 571114402Sru assert(0); 572114402Sru break; 573114402Sru } 574114402Sru if (point_size.val != 0) { 575114402Sru putc('p', stderr); 576114402Sru if (point_size.inc > 0) 577114402Sru putc('+', stderr); 578114402Sru else if (point_size.inc < 0) 579114402Sru putc('-', stderr); 580114402Sru fprintf(stderr, "%d ", point_size.val); 581114402Sru } 582114402Sru if (vertical_spacing.val != 0) { 583114402Sru putc('v', stderr); 584114402Sru if (vertical_spacing.inc > 0) 585114402Sru putc('+', stderr); 586114402Sru else if (vertical_spacing.inc < 0) 587114402Sru putc('-', stderr); 588114402Sru fprintf(stderr, "%d ", vertical_spacing.val); 589114402Sru } 590114402Sru if (!font.empty()) { 591114402Sru putc('f', stderr); 592114402Sru put_string(font, stderr); 593114402Sru putc(' ', stderr); 594114402Sru } 595151497Sru if (!macro.empty()) { 596151497Sru putc('m', stderr); 597151497Sru put_string(macro, stderr); 598151497Sru putc(' ', stderr); 599151497Sru } 600114402Sru switch (vertical_alignment) { 601114402Sru case entry_modifier::CENTER: 602114402Sru break; 603114402Sru case entry_modifier::TOP: 604114402Sru putc('t', stderr); 605114402Sru break; 606114402Sru case entry_modifier::BOTTOM: 607114402Sru putc('d', stderr); 608114402Sru break; 609114402Sru } 610114402Sru if (zero_width) 611114402Sru putc('z', stderr); 612114402Sru if (stagger) 613114402Sru putc('u', stderr); 614114402Sru} 615114402Sru 616114402Srustruct format { 617114402Sru int nrows; 618114402Sru int ncolumns; 619114402Sru int *separation; 620114402Sru string *width; 621114402Sru char *equal; 622114402Sru entry_format **entry; 623114402Sru char **vline; 624114402Sru 625114402Sru format(int nr, int nc); 626114402Sru ~format(); 627114402Sru void add_rows(int n); 628114402Sru}; 629114402Sru 630114402Sruformat::format(int nr, int nc) : nrows(nr), ncolumns(nc) 631114402Sru{ 632114402Sru int i; 633114402Sru separation = ncolumns > 1 ? new int[ncolumns - 1] : 0; 634114402Sru for (i = 0; i < ncolumns-1; i++) 635114402Sru separation[i] = -1; 636114402Sru width = new string[ncolumns]; 637114402Sru equal = new char[ncolumns]; 638114402Sru for (i = 0; i < ncolumns; i++) 639114402Sru equal[i] = 0; 640114402Sru entry = new entry_format *[nrows]; 641114402Sru for (i = 0; i < nrows; i++) 642114402Sru entry[i] = new entry_format[ncolumns]; 643114402Sru vline = new char*[nrows]; 644114402Sru for (i = 0; i < nrows; i++) { 645114402Sru vline[i] = new char[ncolumns+1]; 646114402Sru for (int j = 0; j < ncolumns+1; j++) 647114402Sru vline[i][j] = 0; 648114402Sru } 649114402Sru} 650114402Sru 651114402Sruvoid format::add_rows(int n) 652114402Sru{ 653114402Sru int i; 654114402Sru char **old_vline = vline; 655114402Sru vline = new char*[nrows + n]; 656114402Sru for (i = 0; i < nrows; i++) 657114402Sru vline[i] = old_vline[i]; 658114402Sru a_delete old_vline; 659114402Sru for (i = 0; i < n; i++) { 660114402Sru vline[nrows + i] = new char[ncolumns + 1]; 661114402Sru for (int j = 0; j < ncolumns + 1; j++) 662114402Sru vline[nrows + i][j] = 0; 663114402Sru } 664114402Sru entry_format **old_entry = entry; 665114402Sru entry = new entry_format *[nrows + n]; 666114402Sru for (i = 0; i < nrows; i++) 667114402Sru entry[i] = old_entry[i]; 668114402Sru a_delete old_entry; 669114402Sru for (i = 0; i < n; i++) 670114402Sru entry[nrows + i] = new entry_format[ncolumns]; 671114402Sru nrows += n; 672114402Sru} 673114402Sru 674114402Sruformat::~format() 675114402Sru{ 676114402Sru a_delete separation; 677114402Sru ad_delete(ncolumns) width; 678114402Sru a_delete equal; 679114402Sru for (int i = 0; i < nrows; i++) { 680114402Sru a_delete vline[i]; 681114402Sru ad_delete(ncolumns) entry[i]; 682114402Sru } 683114402Sru a_delete vline; 684114402Sru a_delete entry; 685114402Sru} 686114402Sru 687114402Srustruct input_entry_format : public entry_format { 688114402Sru input_entry_format *next; 689114402Sru string width; 690114402Sru int separation; 691114402Sru int vline; 692114402Sru int pre_vline; 693114402Sru int last_column; 694114402Sru int equal; 695114402Sru input_entry_format(format_type, input_entry_format * = 0); 696114402Sru ~input_entry_format(); 697114402Sru void debug_print(); 698114402Sru}; 699114402Sru 700114402Sruinput_entry_format::input_entry_format(format_type t, input_entry_format *p) 701114402Sru: entry_format(t), next(p) 702114402Sru{ 703114402Sru separation = -1; 704114402Sru last_column = 0; 705114402Sru vline = 0; 706114402Sru pre_vline = 0; 707114402Sru equal = 0; 708114402Sru} 709114402Sru 710114402Sruinput_entry_format::~input_entry_format() 711114402Sru{ 712114402Sru} 713114402Sru 714114402Sruvoid free_input_entry_format_list(input_entry_format *list) 715114402Sru{ 716114402Sru while (list) { 717114402Sru input_entry_format *tem = list; 718114402Sru list = list->next; 719114402Sru delete tem; 720114402Sru } 721114402Sru} 722114402Sru 723114402Sruvoid input_entry_format::debug_print() 724114402Sru{ 725114402Sru int i; 726114402Sru for (i = 0; i < pre_vline; i++) 727114402Sru putc('|', stderr); 728114402Sru entry_format::debug_print(); 729114402Sru if (!width.empty()) { 730114402Sru putc('w', stderr); 731114402Sru putc('(', stderr); 732114402Sru put_string(width, stderr); 733114402Sru putc(')', stderr); 734114402Sru } 735114402Sru if (equal) 736114402Sru putc('e', stderr); 737114402Sru if (separation >= 0) 738114402Sru fprintf(stderr, "%d", separation); 739114402Sru for (i = 0; i < vline; i++) 740114402Sru putc('|', stderr); 741114402Sru if (last_column) 742114402Sru putc(',', stderr); 743114402Sru} 744114402Sru 745114402Sru// Return zero if we should give up on this table. 746114402Sru// If this is a continuation format line, current_format will be the current 747114402Sru// format line. 748114402Sru 749114402Sruformat *process_format(table_input &in, options *opt, 750114402Sru format *current_format = 0) 751114402Sru{ 752114402Sru input_entry_format *list = 0; 753114402Sru int c = in.get(); 754114402Sru for (;;) { 755114402Sru int pre_vline = 0; 756114402Sru int got_format = 0; 757114402Sru int got_period = 0; 758114402Sru format_type t = FORMAT_LEFT; 759114402Sru for (;;) { 760114402Sru if (c == EOF) { 761114402Sru error("end of input while processing format"); 762114402Sru free_input_entry_format_list(list); 763114402Sru return 0; 764114402Sru } 765114402Sru switch (c) { 766114402Sru case 'n': 767114402Sru case 'N': 768114402Sru t = FORMAT_NUMERIC; 769114402Sru got_format = 1; 770114402Sru break; 771114402Sru case 'a': 772114402Sru case 'A': 773114402Sru got_format = 1; 774114402Sru t = FORMAT_ALPHABETIC; 775114402Sru break; 776114402Sru case 'c': 777114402Sru case 'C': 778114402Sru got_format = 1; 779114402Sru t = FORMAT_CENTER; 780114402Sru break; 781114402Sru case 'l': 782114402Sru case 'L': 783114402Sru got_format = 1; 784114402Sru t = FORMAT_LEFT; 785114402Sru break; 786114402Sru case 'r': 787114402Sru case 'R': 788114402Sru got_format = 1; 789114402Sru t = FORMAT_RIGHT; 790114402Sru break; 791114402Sru case 's': 792114402Sru case 'S': 793114402Sru got_format = 1; 794114402Sru t = FORMAT_SPAN; 795114402Sru break; 796114402Sru case '^': 797114402Sru got_format = 1; 798114402Sru t = FORMAT_VSPAN; 799114402Sru break; 800114402Sru case '_': 801114402Sru case '-': // tbl also accepts this 802114402Sru got_format = 1; 803114402Sru t = FORMAT_HLINE; 804114402Sru break; 805114402Sru case '=': 806114402Sru got_format = 1; 807114402Sru t = FORMAT_DOUBLE_HLINE; 808114402Sru break; 809114402Sru case '.': 810114402Sru got_period = 1; 811114402Sru break; 812114402Sru case '|': 813114402Sru pre_vline++; 814114402Sru break; 815114402Sru case ' ': 816114402Sru case '\t': 817114402Sru case '\n': 818114402Sru break; 819114402Sru default: 820114402Sru if (c == opt->tab_char) 821114402Sru break; 822114402Sru error("unrecognised format `%1'", char(c)); 823114402Sru free_input_entry_format_list(list); 824114402Sru return 0; 825114402Sru } 826114402Sru if (got_period) 827114402Sru break; 828114402Sru c = in.get(); 829114402Sru if (got_format) 830114402Sru break; 831114402Sru } 832114402Sru if (got_period) 833114402Sru break; 834114402Sru list = new input_entry_format(t, list); 835114402Sru if (pre_vline) 836114402Sru list->pre_vline = pre_vline; 837114402Sru int success = 1; 838114402Sru do { 839114402Sru switch (c) { 840114402Sru case 't': 841114402Sru case 'T': 842114402Sru c = in.get(); 843114402Sru list->vertical_alignment = entry_modifier::TOP; 844114402Sru break; 845114402Sru case 'd': 846114402Sru case 'D': 847114402Sru c = in.get(); 848114402Sru list->vertical_alignment = entry_modifier::BOTTOM; 849114402Sru break; 850114402Sru case 'u': 851114402Sru case 'U': 852114402Sru c = in.get(); 853114402Sru list->stagger = 1; 854114402Sru break; 855114402Sru case 'z': 856114402Sru case 'Z': 857114402Sru c = in.get(); 858114402Sru list->zero_width = 1; 859114402Sru break; 860114402Sru case '0': 861114402Sru case '1': 862114402Sru case '2': 863114402Sru case '3': 864114402Sru case '4': 865114402Sru case '5': 866114402Sru case '6': 867114402Sru case '7': 868114402Sru case '8': 869114402Sru case '9': 870114402Sru { 871114402Sru int w = 0; 872114402Sru do { 873114402Sru w = w*10 + (c - '0'); 874114402Sru c = in.get(); 875114402Sru } while (c != EOF && csdigit(c)); 876114402Sru list->separation = w; 877114402Sru } 878114402Sru break; 879114402Sru case 'f': 880114402Sru case 'F': 881114402Sru do { 882114402Sru c = in.get(); 883114402Sru } while (c == ' ' || c == '\t'); 884114402Sru if (c == EOF) { 885114402Sru error("missing font name"); 886114402Sru break; 887114402Sru } 888114402Sru if (c == '(') { 889114402Sru for (;;) { 890114402Sru c = in.get(); 891114402Sru if (c == EOF || c == ' ' || c == '\t') { 892114402Sru error("missing `)'"); 893114402Sru break; 894114402Sru } 895114402Sru if (c == ')') { 896114402Sru c = in.get(); 897114402Sru break; 898114402Sru } 899114402Sru list->font += char(c); 900114402Sru } 901114402Sru } 902114402Sru else { 903114402Sru list->font = c; 904114402Sru char cc = c; 905114402Sru c = in.get(); 906114402Sru if (!csdigit(cc) 907114402Sru && c != EOF && c != ' ' && c != '\t' && c != '.' && c != '\n') { 908114402Sru list->font += char(c); 909114402Sru c = in.get(); 910114402Sru } 911114402Sru } 912114402Sru break; 913151497Sru case 'x': 914151497Sru case 'X': 915151497Sru do { 916151497Sru c = in.get(); 917151497Sru } while (c == ' ' || c == '\t'); 918151497Sru if (c == EOF) { 919151497Sru error("missing macro name"); 920151497Sru break; 921151497Sru } 922151497Sru if (c == '(') { 923151497Sru for (;;) { 924151497Sru c = in.get(); 925151497Sru if (c == EOF || c == ' ' || c == '\t') { 926151497Sru error("missing `)'"); 927151497Sru break; 928151497Sru } 929151497Sru if (c == ')') { 930151497Sru c = in.get(); 931151497Sru break; 932151497Sru } 933151497Sru list->macro += char(c); 934151497Sru } 935151497Sru } 936151497Sru else { 937151497Sru list->macro = c; 938151497Sru char cc = c; 939151497Sru c = in.get(); 940151497Sru if (!csdigit(cc) 941151497Sru && c != EOF && c != ' ' && c != '\t' && c != '.' && c != '\n') { 942151497Sru list->macro += char(c); 943151497Sru c = in.get(); 944151497Sru } 945151497Sru } 946151497Sru break; 947114402Sru case 'v': 948114402Sru case 'V': 949114402Sru c = in.get(); 950114402Sru list->vertical_spacing.val = 0; 951114402Sru list->vertical_spacing.inc = 0; 952114402Sru if (c == '+' || c == '-') { 953114402Sru list->vertical_spacing.inc = (c == '+' ? 1 : -1); 954114402Sru c = in.get(); 955114402Sru } 956114402Sru if (c == EOF || !csdigit(c)) { 957114402Sru error("`v' modifier must be followed by number"); 958114402Sru list->vertical_spacing.inc = 0; 959114402Sru } 960114402Sru else { 961114402Sru do { 962114402Sru list->vertical_spacing.val *= 10; 963114402Sru list->vertical_spacing.val += c - '0'; 964114402Sru c = in.get(); 965114402Sru } while (c != EOF && csdigit(c)); 966114402Sru } 967114402Sru if (list->vertical_spacing.val > MAX_VERTICAL_SPACING 968114402Sru || list->vertical_spacing.val < -MAX_VERTICAL_SPACING) { 969114402Sru error("unreasonable vertical spacing"); 970114402Sru list->vertical_spacing.val = 0; 971114402Sru list->vertical_spacing.inc = 0; 972114402Sru } 973114402Sru break; 974114402Sru case 'p': 975114402Sru case 'P': 976114402Sru c = in.get(); 977114402Sru list->point_size.val = 0; 978114402Sru list->point_size.inc = 0; 979114402Sru if (c == '+' || c == '-') { 980114402Sru list->point_size.inc = (c == '+' ? 1 : -1); 981114402Sru c = in.get(); 982114402Sru } 983114402Sru if (c == EOF || !csdigit(c)) { 984114402Sru error("`p' modifier must be followed by number"); 985114402Sru list->point_size.inc = 0; 986114402Sru } 987114402Sru else { 988114402Sru do { 989114402Sru list->point_size.val *= 10; 990114402Sru list->point_size.val += c - '0'; 991114402Sru c = in.get(); 992114402Sru } while (c != EOF && csdigit(c)); 993114402Sru } 994114402Sru if (list->point_size.val > MAX_POINT_SIZE 995114402Sru || list->point_size.val < -MAX_POINT_SIZE) { 996114402Sru error("unreasonable point size"); 997114402Sru list->point_size.val = 0; 998114402Sru list->point_size.inc = 0; 999114402Sru } 1000114402Sru break; 1001114402Sru case 'w': 1002114402Sru case 'W': 1003114402Sru c = in.get(); 1004114402Sru while (c == ' ' || c == '\t') 1005114402Sru c = in.get(); 1006114402Sru if (c == '(') { 1007114402Sru list->width = ""; 1008114402Sru c = in.get(); 1009114402Sru while (c != ')') { 1010114402Sru if (c == EOF || c == '\n') { 1011114402Sru error("missing `)'"); 1012114402Sru free_input_entry_format_list(list); 1013114402Sru return 0; 1014114402Sru } 1015114402Sru list->width += c; 1016114402Sru c = in.get(); 1017114402Sru } 1018114402Sru c = in.get(); 1019114402Sru } 1020114402Sru else { 1021114402Sru if (c == '+' || c == '-') { 1022114402Sru list->width = char(c); 1023114402Sru c = in.get(); 1024114402Sru } 1025114402Sru else 1026114402Sru list->width = ""; 1027114402Sru if (c == EOF || !csdigit(c)) 1028114402Sru error("bad argument for `w' modifier"); 1029114402Sru else { 1030114402Sru do { 1031114402Sru list->width += char(c); 1032114402Sru c = in.get(); 1033114402Sru } while (c != EOF && csdigit(c)); 1034114402Sru } 1035114402Sru } 1036114402Sru break; 1037114402Sru case 'e': 1038114402Sru case 'E': 1039114402Sru c = in.get(); 1040114402Sru list->equal++; 1041114402Sru break; 1042114402Sru case '|': 1043114402Sru c = in.get(); 1044114402Sru list->vline++; 1045114402Sru break; 1046114402Sru case 'B': 1047114402Sru case 'b': 1048114402Sru c = in.get(); 1049114402Sru list->font = "B"; 1050114402Sru break; 1051114402Sru case 'I': 1052114402Sru case 'i': 1053114402Sru c = in.get(); 1054114402Sru list->font = "I"; 1055114402Sru break; 1056114402Sru case ' ': 1057114402Sru case '\t': 1058114402Sru c = in.get(); 1059114402Sru break; 1060114402Sru default: 1061114402Sru if (c == opt->tab_char) 1062114402Sru c = in.get(); 1063114402Sru else 1064114402Sru success = 0; 1065114402Sru break; 1066114402Sru } 1067114402Sru } while (success); 1068114402Sru if (list->vline > 2) { 1069114402Sru list->vline = 2; 1070114402Sru error("more than 2 vertical bars between key letters"); 1071114402Sru } 1072114402Sru if (c == '\n' || c == ',') { 1073114402Sru c = in.get(); 1074114402Sru list->last_column = 1; 1075114402Sru } 1076114402Sru } 1077114402Sru if (c == '.') { 1078114402Sru do { 1079114402Sru c = in.get(); 1080114402Sru } while (c == ' ' || c == '\t'); 1081114402Sru if (c != '\n') { 1082114402Sru error("`.' not last character on line"); 1083114402Sru free_input_entry_format_list(list); 1084114402Sru return 0; 1085114402Sru } 1086114402Sru } 1087114402Sru if (!list) { 1088114402Sru error("no format"); 1089114402Sru free_input_entry_format_list(list); 1090114402Sru return 0; 1091114402Sru } 1092114402Sru list->last_column = 1; 1093114402Sru // now reverse the list so that the first row is at the beginning 1094114402Sru input_entry_format *rev = 0; 1095114402Sru while (list != 0) { 1096114402Sru input_entry_format *tem = list->next; 1097114402Sru list->next = rev; 1098114402Sru rev = list; 1099114402Sru list = tem; 1100114402Sru } 1101114402Sru list = rev; 1102114402Sru input_entry_format *tem; 1103114402Sru 1104114402Sru#if 0 1105114402Sru for (tem = list; tem; tem = tem->next) 1106114402Sru tem->debug_print(); 1107114402Sru putc('\n', stderr); 1108114402Sru#endif 1109114402Sru // compute number of columns and rows 1110114402Sru int ncolumns = 0; 1111114402Sru int nrows = 0; 1112114402Sru int col = 0; 1113114402Sru for (tem = list; tem; tem = tem->next) { 1114114402Sru if (tem->last_column) { 1115114402Sru if (col >= ncolumns) 1116114402Sru ncolumns = col + 1; 1117114402Sru col = 0; 1118114402Sru nrows++; 1119114402Sru } 1120114402Sru else 1121114402Sru col++; 1122114402Sru } 1123114402Sru int row; 1124114402Sru format *f; 1125114402Sru if (current_format) { 1126114402Sru if (ncolumns > current_format->ncolumns) { 1127114402Sru error("cannot increase the number of columns in a continued format"); 1128114402Sru free_input_entry_format_list(list); 1129114402Sru return 0; 1130114402Sru } 1131114402Sru f = current_format; 1132114402Sru row = f->nrows; 1133114402Sru f->add_rows(nrows); 1134114402Sru } 1135114402Sru else { 1136114402Sru f = new format(nrows, ncolumns); 1137114402Sru row = 0; 1138114402Sru } 1139114402Sru col = 0; 1140114402Sru for (tem = list; tem; tem = tem->next) { 1141114402Sru f->entry[row][col] = *tem; 1142114402Sru if (col < ncolumns-1) { 1143114402Sru // use the greatest separation 1144114402Sru if (tem->separation > f->separation[col]) { 1145114402Sru if (current_format) 1146114402Sru error("cannot change column separation in continued format"); 1147114402Sru else 1148114402Sru f->separation[col] = tem->separation; 1149114402Sru } 1150114402Sru } 1151114402Sru else if (tem->separation >= 0) 1152114402Sru error("column separation specified for last column"); 1153114402Sru if (tem->equal && !f->equal[col]) { 1154114402Sru if (current_format) 1155114402Sru error("cannot change which columns are equal in continued format"); 1156114402Sru else 1157114402Sru f->equal[col] = 1; 1158114402Sru } 1159114402Sru if (!tem->width.empty()) { 1160114402Sru // use the last width 1161114402Sru if (!f->width[col].empty() && f->width[col] != tem->width) 1162114402Sru error("multiple widths for column %1", col+1); 1163114402Sru f->width[col] = tem->width; 1164114402Sru } 1165114402Sru if (tem->pre_vline) { 1166114402Sru assert(col == 0); 1167114402Sru f->vline[row][col] = tem->pre_vline; 1168114402Sru } 1169114402Sru f->vline[row][col+1] = tem->vline; 1170114402Sru if (tem->last_column) { 1171114402Sru row++; 1172114402Sru col = 0; 1173114402Sru } 1174114402Sru else 1175114402Sru col++; 1176114402Sru } 1177114402Sru free_input_entry_format_list(list); 1178114402Sru for (col = 0; col < ncolumns; col++) { 1179114402Sru entry_format *e = f->entry[f->nrows-1] + col; 1180114402Sru if (e->type != FORMAT_HLINE 1181114402Sru && e->type != FORMAT_DOUBLE_HLINE 1182114402Sru && e->type != FORMAT_SPAN) 1183114402Sru break; 1184114402Sru } 1185114402Sru if (col >= ncolumns) { 1186114402Sru error("last row of format is all lines"); 1187114402Sru delete f; 1188114402Sru return 0; 1189114402Sru } 1190114402Sru return f; 1191114402Sru} 1192114402Sru 1193114402Srutable *process_data(table_input &in, format *f, options *opt) 1194114402Sru{ 1195114402Sru char tab_char = opt->tab_char; 1196114402Sru int ncolumns = f->ncolumns; 1197114402Sru int current_row = 0; 1198114402Sru int format_index = 0; 1199114402Sru int give_up = 0; 1200114402Sru enum { DATA_INPUT_LINE, TROFF_INPUT_LINE, SINGLE_HLINE, DOUBLE_HLINE } type; 1201114402Sru table *tbl = new table(ncolumns, opt->flags, opt->linesize, 1202114402Sru opt->decimal_point_char); 1203114402Sru if (opt->delim[0] != '\0') 1204114402Sru tbl->set_delim(opt->delim[0], opt->delim[1]); 1205114402Sru for (;;) { 1206114402Sru // first determine what type of line this is 1207114402Sru int c = in.get(); 1208114402Sru if (c == EOF) 1209114402Sru break; 1210114402Sru if (c == '.') { 1211114402Sru int d = in.get(); 1212114402Sru if (d != EOF && csdigit(d)) { 1213114402Sru in.unget(d); 1214114402Sru type = DATA_INPUT_LINE; 1215114402Sru } 1216114402Sru else { 1217114402Sru in.unget(d); 1218114402Sru type = TROFF_INPUT_LINE; 1219114402Sru } 1220114402Sru } 1221114402Sru else if (c == '_' || c == '=') { 1222114402Sru int d = in.get(); 1223114402Sru if (d == '\n') { 1224114402Sru if (c == '_') 1225114402Sru type = SINGLE_HLINE; 1226114402Sru else 1227114402Sru type = DOUBLE_HLINE; 1228114402Sru } 1229114402Sru else { 1230114402Sru in.unget(d); 1231114402Sru type = DATA_INPUT_LINE; 1232114402Sru } 1233114402Sru } 1234114402Sru else { 1235114402Sru type = DATA_INPUT_LINE; 1236114402Sru } 1237114402Sru switch (type) { 1238114402Sru case DATA_INPUT_LINE: 1239114402Sru { 1240114402Sru string input_entry; 1241114402Sru if (format_index >= f->nrows) 1242114402Sru format_index = f->nrows - 1; 1243114402Sru // A format row that is all lines doesn't use up a data line. 1244114402Sru while (format_index < f->nrows - 1) { 1245151497Sru int cnt; 1246151497Sru for (cnt = 0; cnt < ncolumns; cnt++) { 1247151497Sru entry_format *e = f->entry[format_index] + cnt; 1248114402Sru if (e->type != FORMAT_HLINE 1249114402Sru && e->type != FORMAT_DOUBLE_HLINE 1250114402Sru // Unfortunately tbl treats a span as needing data. 1251114402Sru // && e->type != FORMAT_SPAN 1252114402Sru ) 1253114402Sru break; 1254114402Sru } 1255151497Sru if (cnt < ncolumns) 1256114402Sru break; 1257151497Sru for (cnt = 0; cnt < ncolumns; cnt++) 1258151497Sru tbl->add_entry(current_row, cnt, input_entry, 1259151497Sru f->entry[format_index] + cnt, current_filename, 1260114402Sru current_lineno); 1261114402Sru tbl->add_vlines(current_row, f->vline[format_index]); 1262114402Sru format_index++; 1263114402Sru current_row++; 1264114402Sru } 1265114402Sru entry_format *line_format = f->entry[format_index]; 1266114402Sru int col = 0; 1267114402Sru int row_comment = 0; 1268114402Sru for (;;) { 1269114402Sru if (c == tab_char || c == '\n') { 1270114402Sru int ln = current_lineno; 1271114402Sru if (c == '\n') 1272114402Sru --ln; 1273114402Sru if ((opt->flags & table::NOSPACES)) 1274114402Sru input_entry.remove_spaces(); 1275114402Sru while (col < ncolumns 1276114402Sru && line_format[col].type == FORMAT_SPAN) { 1277114402Sru tbl->add_entry(current_row, col, "", &line_format[col], 1278114402Sru current_filename, ln); 1279114402Sru col++; 1280114402Sru } 1281114402Sru if (c == '\n' && input_entry.length() == 2 1282114402Sru && input_entry[0] == 'T' && input_entry[1] == '{') { 1283114402Sru input_entry = ""; 1284114402Sru ln++; 1285114402Sru enum { 1286114402Sru START, MIDDLE, GOT_T, GOT_RIGHT_BRACE, GOT_DOT, 1287114402Sru GOT_l, GOT_lf, END 1288114402Sru } state = START; 1289114402Sru while (state != END) { 1290114402Sru c = in.get(); 1291114402Sru if (c == EOF) 1292114402Sru break; 1293114402Sru switch (state) { 1294114402Sru case START: 1295114402Sru if (c == 'T') 1296114402Sru state = GOT_T; 1297114402Sru else if (c == '.') 1298114402Sru state = GOT_DOT; 1299114402Sru else { 1300114402Sru input_entry += c; 1301114402Sru if (c != '\n') 1302114402Sru state = MIDDLE; 1303114402Sru } 1304114402Sru break; 1305114402Sru case GOT_T: 1306114402Sru if (c == '}') 1307114402Sru state = GOT_RIGHT_BRACE; 1308114402Sru else { 1309114402Sru input_entry += 'T'; 1310114402Sru input_entry += c; 1311114402Sru state = c == '\n' ? START : MIDDLE; 1312114402Sru } 1313114402Sru break; 1314114402Sru case GOT_DOT: 1315114402Sru if (c == 'l') 1316114402Sru state = GOT_l; 1317114402Sru else { 1318114402Sru input_entry += '.'; 1319114402Sru input_entry += c; 1320114402Sru state = c == '\n' ? START : MIDDLE; 1321114402Sru } 1322114402Sru break; 1323114402Sru case GOT_l: 1324114402Sru if (c == 'f') 1325114402Sru state = GOT_lf; 1326114402Sru else { 1327114402Sru input_entry += ".l"; 1328114402Sru input_entry += c; 1329114402Sru state = c == '\n' ? START : MIDDLE; 1330114402Sru } 1331114402Sru break; 1332114402Sru case GOT_lf: 1333114402Sru if (c == ' ' || c == '\n' || compatible_flag) { 1334114402Sru string args; 1335114402Sru input_entry += ".lf"; 1336114402Sru while (c != EOF) { 1337114402Sru args += c; 1338114402Sru if (c == '\n') 1339114402Sru break; 1340114402Sru c = in.get(); 1341114402Sru } 1342114402Sru args += '\0'; 1343114402Sru interpret_lf_args(args.contents()); 1344114402Sru // remove the '\0' 1345114402Sru args.set_length(args.length() - 1); 1346114402Sru input_entry += args; 1347114402Sru state = START; 1348114402Sru } 1349114402Sru else { 1350114402Sru input_entry += ".lf"; 1351114402Sru input_entry += c; 1352114402Sru state = MIDDLE; 1353114402Sru } 1354114402Sru break; 1355114402Sru case GOT_RIGHT_BRACE: 1356151497Sru if ((opt->flags & table::NOSPACES)) { 1357151497Sru while (c == ' ') 1358151497Sru c = in.get(); 1359151497Sru if (c == EOF) 1360151497Sru break; 1361151497Sru } 1362114402Sru if (c == '\n' || c == tab_char) 1363114402Sru state = END; 1364114402Sru else { 1365114402Sru input_entry += 'T'; 1366114402Sru input_entry += '}'; 1367114402Sru input_entry += c; 1368151497Sru state = MIDDLE; 1369114402Sru } 1370114402Sru break; 1371114402Sru case MIDDLE: 1372114402Sru if (c == '\n') 1373114402Sru state = START; 1374114402Sru input_entry += c; 1375114402Sru break; 1376114402Sru case END: 1377114402Sru default: 1378114402Sru assert(0); 1379114402Sru } 1380114402Sru } 1381114402Sru if (c == EOF) { 1382114402Sru error("end of data in middle of text block"); 1383114402Sru give_up = 1; 1384114402Sru break; 1385114402Sru } 1386114402Sru } 1387114402Sru if (col >= ncolumns) { 1388114402Sru if (!input_entry.empty()) { 1389114402Sru if (input_entry.length() >= 2 1390114402Sru && input_entry[0] == '\\' 1391114402Sru && input_entry[1] == '"') 1392114402Sru row_comment = 1; 1393114402Sru else if (!row_comment) { 1394114402Sru if (c == '\n') 1395114402Sru in.unget(c); 1396114402Sru input_entry += '\0'; 1397114402Sru error("excess data entry `%1' discarded", 1398114402Sru input_entry.contents()); 1399114402Sru if (c == '\n') 1400114402Sru (void)in.get(); 1401114402Sru } 1402114402Sru } 1403114402Sru } 1404114402Sru else 1405114402Sru tbl->add_entry(current_row, col, input_entry, 1406114402Sru &line_format[col], current_filename, ln); 1407114402Sru col++; 1408114402Sru if (c == '\n') 1409114402Sru break; 1410114402Sru input_entry = ""; 1411114402Sru } 1412114402Sru else 1413114402Sru input_entry += c; 1414114402Sru c = in.get(); 1415114402Sru if (c == EOF) 1416114402Sru break; 1417114402Sru } 1418114402Sru if (give_up) 1419114402Sru break; 1420114402Sru input_entry = ""; 1421114402Sru for (; col < ncolumns; col++) 1422114402Sru tbl->add_entry(current_row, col, input_entry, &line_format[col], 1423114402Sru current_filename, current_lineno - 1); 1424114402Sru tbl->add_vlines(current_row, f->vline[format_index]); 1425114402Sru current_row++; 1426114402Sru format_index++; 1427114402Sru } 1428114402Sru break; 1429114402Sru case TROFF_INPUT_LINE: 1430114402Sru { 1431114402Sru string line; 1432114402Sru int ln = current_lineno; 1433114402Sru for (;;) { 1434114402Sru line += c; 1435114402Sru if (c == '\n') 1436114402Sru break; 1437114402Sru c = in.get(); 1438114402Sru if (c == EOF) { 1439114402Sru break; 1440114402Sru } 1441114402Sru } 1442114402Sru tbl->add_text_line(current_row, line, current_filename, ln); 1443114402Sru if (line.length() >= 4 1444114402Sru && line[0] == '.' && line[1] == 'T' && line[2] == '&') { 1445114402Sru format *newf = process_format(in, opt, f); 1446114402Sru if (newf == 0) 1447114402Sru give_up = 1; 1448114402Sru else 1449114402Sru f = newf; 1450114402Sru } 1451114402Sru if (line.length() >= 3 1452114402Sru && line[0] == '.' && line[1] == 'l' && line[2] == 'f') { 1453114402Sru line += '\0'; 1454114402Sru interpret_lf_args(line.contents() + 3); 1455114402Sru } 1456114402Sru } 1457114402Sru break; 1458114402Sru case SINGLE_HLINE: 1459114402Sru tbl->add_single_hline(current_row); 1460114402Sru break; 1461114402Sru case DOUBLE_HLINE: 1462114402Sru tbl->add_double_hline(current_row); 1463114402Sru break; 1464114402Sru default: 1465114402Sru assert(0); 1466114402Sru } 1467114402Sru if (give_up) 1468114402Sru break; 1469114402Sru } 1470114402Sru if (!give_up && current_row == 0) { 1471114402Sru error("no real data"); 1472114402Sru give_up = 1; 1473114402Sru } 1474114402Sru if (give_up) { 1475114402Sru delete tbl; 1476114402Sru return 0; 1477114402Sru } 1478114402Sru // Do this here rather than at the beginning in case continued formats 1479114402Sru // change it. 1480114402Sru int i; 1481114402Sru for (i = 0; i < ncolumns - 1; i++) 1482114402Sru if (f->separation[i] >= 0) 1483114402Sru tbl->set_column_separation(i, f->separation[i]); 1484114402Sru for (i = 0; i < ncolumns; i++) 1485114402Sru if (!f->width[i].empty()) 1486114402Sru tbl->set_minimum_width(i, f->width[i]); 1487114402Sru for (i = 0; i < ncolumns; i++) 1488114402Sru if (f->equal[i]) 1489114402Sru tbl->set_equal_column(i); 1490114402Sru return tbl; 1491114402Sru} 1492114402Sru 1493114402Sruvoid process_table(table_input &in) 1494114402Sru{ 1495114402Sru options *opt = 0; 1496114402Sru format *form = 0; 1497114402Sru table *tbl = 0; 1498114402Sru if ((opt = process_options(in)) != 0 1499114402Sru && (form = process_format(in, opt)) != 0 1500114402Sru && (tbl = process_data(in, form, opt)) != 0) { 1501114402Sru tbl->print(); 1502114402Sru delete tbl; 1503114402Sru } 1504114402Sru else { 1505114402Sru error("giving up on this table"); 1506151497Sru while (in.get() != EOF) 1507114402Sru ; 1508114402Sru } 1509114402Sru delete opt; 1510114402Sru delete form; 1511114402Sru if (!in.ended()) 1512114402Sru error("premature end of file"); 1513114402Sru} 1514114402Sru 1515114402Srustatic void usage(FILE *stream) 1516114402Sru{ 1517114402Sru fprintf(stream, "usage: %s [ -vC ] [ files... ]\n", program_name); 1518114402Sru} 1519114402Sru 1520114402Sruint main(int argc, char **argv) 1521114402Sru{ 1522114402Sru program_name = argv[0]; 1523114402Sru static char stderr_buf[BUFSIZ]; 1524114402Sru setbuf(stderr, stderr_buf); 1525114402Sru int opt; 1526114402Sru static const struct option long_options[] = { 1527114402Sru { "help", no_argument, 0, CHAR_MAX + 1 }, 1528114402Sru { "version", no_argument, 0, 'v' }, 1529114402Sru { NULL, 0, 0, 0 } 1530114402Sru }; 1531114402Sru while ((opt = getopt_long(argc, argv, "vCT:", long_options, NULL)) != EOF) 1532114402Sru switch (opt) { 1533114402Sru case 'C': 1534114402Sru compatible_flag = 1; 1535114402Sru break; 1536114402Sru case 'v': 1537114402Sru { 1538114402Sru printf("GNU tbl (groff) version %s\n", Version_string); 1539114402Sru exit(0); 1540114402Sru break; 1541114402Sru } 1542114402Sru case 'T': 1543114402Sru // I'm sick of getting bug reports from IRIX users 1544114402Sru break; 1545114402Sru case CHAR_MAX + 1: // --help 1546114402Sru usage(stdout); 1547114402Sru exit(0); 1548114402Sru break; 1549114402Sru case '?': 1550114402Sru usage(stderr); 1551114402Sru exit(1); 1552114402Sru break; 1553114402Sru default: 1554114402Sru assert(0); 1555114402Sru } 1556114402Sru printf(".if !\\n(.g .ab GNU tbl requires GNU troff.\n" 1557114402Sru ".if !dTS .ds TS\n" 1558114402Sru ".if !dTE .ds TE\n"); 1559114402Sru if (argc > optind) { 1560114402Sru for (int i = optind; i < argc; i++) 1561114402Sru if (argv[i][0] == '-' && argv[i][1] == '\0') { 1562114402Sru current_filename = "-"; 1563114402Sru current_lineno = 1; 1564114402Sru printf(".lf 1 -\n"); 1565114402Sru process_input_file(stdin); 1566114402Sru } 1567114402Sru else { 1568114402Sru errno = 0; 1569114402Sru FILE *fp = fopen(argv[i], "r"); 1570151497Sru if (fp == 0) 1571151497Sru fatal("can't open `%1': %2", argv[i], strerror(errno)); 1572114402Sru else { 1573114402Sru current_lineno = 1; 1574114402Sru current_filename = argv[i]; 1575114402Sru printf(".lf 1 %s\n", current_filename); 1576114402Sru process_input_file(fp); 1577114402Sru } 1578114402Sru } 1579114402Sru } 1580114402Sru else { 1581114402Sru current_filename = "-"; 1582114402Sru current_lineno = 1; 1583114402Sru printf(".lf 1 -\n"); 1584114402Sru process_input_file(stdin); 1585114402Sru } 1586114402Sru if (ferror(stdout) || fflush(stdout) < 0) 1587114402Sru fatal("output error"); 1588114402Sru return 0; 1589114402Sru} 1590114402Sru 1591