home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / groff / tbl / main.cc next >
Encoding:
C/C++ Source or Header  |  1991-04-30  |  29.6 KB  |  1,445 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.uucp)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 1, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file LICENSE.  If not, write to the Free Software
  19. Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. #include "table.h"
  22.  
  23. #define MAX_POINT_SIZE 99
  24. #define MAX_VERTICAL_SPACING 72
  25.  
  26. static int compatible_flag = 0;
  27.  
  28. class table_input {
  29.   FILE *fp;
  30.   enum { START, MIDDLE, REREAD_T, REREAD_TE, REREAD_E, END, ERROR } state;
  31.   string unget_stack;
  32. public:
  33.   table_input(FILE *);
  34.   int get();
  35.   int ended() { return unget_stack.empty() && state == END; }
  36.   void unget(char);
  37. };
  38.  
  39. table_input::table_input(FILE *p)
  40. : fp(p), state(START)
  41. {
  42. }
  43.  
  44. void table_input::unget(char c)
  45. {
  46.   assert(c != '\0');
  47.   unget_stack += c;
  48.   if (c == '\n')
  49.     current_lineno--;
  50. }
  51.  
  52. int table_input::get()
  53. {
  54.   int len = unget_stack.length();
  55.   if (len != 0) {
  56.     unsigned char c = unget_stack[len - 1];
  57.     unget_stack.set_length(len - 1);
  58.     if (c == '\n')
  59.       current_lineno++;
  60.     return c;
  61.   }
  62.   int c;
  63.   for (;;) {
  64.     switch (state) {
  65.     case START:
  66.       if ((c = getc(fp)) == '.') {
  67.     if ((c = getc(fp)) == 'T') {
  68.       if ((c = getc(fp)) == 'E') {
  69.         if (compatible_flag) {
  70.           state = END;
  71.           return EOF;
  72.         }
  73.         else {
  74.           c = getc(fp);
  75.           if (c != EOF)
  76.         ungetc(c, fp);
  77.           if (c == EOF || c == ' ' || c == '\n') {
  78.         state = END;
  79.         return EOF;
  80.           }
  81.           state = REREAD_TE;
  82.           return '.';
  83.         }
  84.       }
  85.       else {
  86.         if (c != EOF)
  87.           ungetc(c, fp);
  88.         state = REREAD_T;
  89.         return '.';
  90.       }
  91.     }
  92.     else {
  93.       if (c != EOF)
  94.         ungetc(c, fp);
  95.       state = MIDDLE;
  96.       return '.';
  97.     }
  98.       }
  99.       else if (c == EOF) {
  100.     state = ERROR;
  101.     return EOF;
  102.       }
  103.       else {
  104.     if (c == '\n')
  105.       current_lineno++;
  106.     else {
  107.       state = MIDDLE;
  108.       if (c == '\0') {
  109.         error("illegal input character code 0");
  110.         break;
  111.       }
  112.     }
  113.     return c;
  114.       }
  115.       break;
  116.     case MIDDLE:
  117.       // handle line continuation
  118.       if ((c = getc(fp)) == '\\') {
  119.     c = getc(fp);
  120.     if (c == '\n')
  121.       c = getc(fp);        // perhaps state ought to be START now
  122.     else {
  123.       if (c != EOF)
  124.         ungetc(c, fp);
  125.       c = '\\';
  126.     }
  127.       }
  128.       if (c == EOF) {
  129.     state = ERROR;
  130.     return EOF;
  131.       }
  132.       else {
  133.     if (c == '\n') {
  134.       state = START;
  135.       current_lineno++;
  136.     }
  137.     else if (c == '\0') {
  138.       error("illegal input character code 0");
  139.       break;
  140.     }
  141.     return c;
  142.       }
  143.     case REREAD_T:
  144.       state = MIDDLE;
  145.       return 'T';
  146.     case REREAD_TE:
  147.       state = REREAD_E;
  148.       return 'T';
  149.     case REREAD_E:
  150.       state = MIDDLE;
  151.       return 'E';
  152.     case END:
  153.     case ERROR:
  154.       return EOF;
  155.     }
  156.   }
  157. }
  158.  
  159. void process_input_file(FILE *);
  160. void process_table(table_input &in);
  161.  
  162. void process_input_file(FILE *fp)
  163. {
  164.   enum { START, MIDDLE, HAD_DOT, HAD_T, HAD_TS, HAD_l, HAD_lf } state;
  165.   state = START;
  166.   int c;
  167.   while ((c = getc(fp)) != EOF)
  168.     switch (state) {
  169.     case START:
  170.       if (c == '.')
  171.     state = HAD_DOT;
  172.       else {
  173.     if (c == '\n')
  174.       current_lineno++;
  175.     else
  176.       state = MIDDLE;
  177.     putchar(c);
  178.       }
  179.       break;
  180.     case MIDDLE:
  181.       if (c == '\n') {
  182.     current_lineno++;
  183.     state = START;
  184.       }
  185.       putchar(c);
  186.       break;
  187.     case HAD_DOT:
  188.       if (c == 'T')
  189.     state = HAD_T;
  190.       else if (c == 'l')
  191.     state = HAD_l;
  192.       else {
  193.     putchar('.');
  194.     putchar(c);
  195.     if (c == '\n') {
  196.       current_lineno++;
  197.       state = START;
  198.     }
  199.     else
  200.       state = MIDDLE;
  201.       }
  202.       break;
  203.     case HAD_T:
  204.       if (c == 'S')
  205.     state = HAD_TS;
  206.       else {
  207.     putchar('.');
  208.     putchar('T');
  209.     putchar(c);
  210.     if (c == '\n') {
  211.        current_lineno++;
  212.       state = START;
  213.     }
  214.     else
  215.       state = MIDDLE;
  216.       }
  217.       break;
  218.     case HAD_TS:
  219.       if (c == ' ' || c == '\n' || compatible_flag) {
  220.     putchar('.');
  221.     putchar('T');
  222.     putchar('S');
  223.     while (c != '\n') {
  224.       if (c == EOF) {
  225.         error("end of file at beginning of table");
  226.         return;
  227.       }
  228.       putchar(c);
  229.       c = getc(fp);
  230.     }
  231.     putchar('\n');
  232.     current_lineno++;
  233.     {
  234.       table_input input(fp);
  235.       process_table(input);
  236.       set_troff_location(current_filename, current_lineno);
  237.       if (input.ended()) {
  238.         fputs(".TE", stdout);
  239.         while ((c = getc(fp)) != '\n') {
  240.           if (c == EOF) {
  241.         putchar('\n');
  242.         return;
  243.           }
  244.           putchar(c);
  245.         }
  246.         putchar('\n');
  247.         current_lineno++;
  248.       }
  249.     }
  250.     state = START;
  251.       }
  252.       else {
  253.     fputs(".TS", stdout);
  254.     putchar(c);
  255.     state = MIDDLE;
  256.       }
  257.       break;
  258.     case HAD_l:
  259.       if (c == 'f')
  260.     state = HAD_lf;
  261.       else {
  262.     putchar('.');
  263.     putchar('l');
  264.     putchar(c);
  265.     if (c == '\n') {
  266.        current_lineno++;
  267.       state = START;
  268.     }
  269.     else
  270.       state = MIDDLE;
  271.       }
  272.       break;
  273.     case HAD_lf:
  274.       if (c == ' ' || c == '\n' || compatible_flag) {
  275.     string line;
  276.     while (c != EOF) {
  277.       line += c;
  278.       if (c == '\n') {
  279.         current_lineno++;
  280.         break;
  281.       }
  282.       c = getc(fp);
  283.     }
  284.     line += '\0';
  285.     interpret_lf_args(line.contents());
  286.     printf(".lf%s", line.contents());
  287.     state = START;
  288.       }
  289.       else {
  290.     fputs(".lf", stdout);
  291.     putchar(c);
  292.     state = MIDDLE;
  293.       }
  294.       break;
  295.     default:
  296.       assert(0);
  297.     }
  298.   switch(state) {
  299.   case START:
  300.     break;
  301.   case MIDDLE:
  302.     putchar('\n');
  303.     break;
  304.   case HAD_DOT:
  305.     fputs(".\n", stdout);
  306.     break;
  307.   case HAD_l:
  308.     fputs(".l\n", stdout);
  309.     break;
  310.   case HAD_T:
  311.     fputs(".T\n", stdout);
  312.     break;
  313.   case HAD_lf:
  314.     fputs(".lf\n", stdout);
  315.     break;
  316.   case HAD_TS:
  317.     fputs(".TS\n", stdout);
  318.     break;
  319.   }
  320.   if (fp != stdin)
  321.     fclose(fp);
  322. }
  323.  
  324. struct options {
  325.   unsigned flags;
  326.   int linesize;
  327.   char delim[2];
  328.   char tab_char;
  329.  
  330.   options();
  331. };
  332.  
  333. options::options()
  334. : flags(0), tab_char('\t'), linesize(0)
  335. {
  336.   delim[0] = delim[1] = '\0';
  337. }
  338.  
  339. // Return non-zero if p and q are the same ignoring case.
  340.  
  341. int strieq(const char *p, const char *q)
  342. {
  343.   for (; cmlower(*p) == cmlower(*q); p++, q++)
  344.     if (*p == '\0')
  345.       return 1;
  346.   return 0;
  347. }
  348.  
  349. // return 0 if we should give up in this table
  350.  
  351. options *process_options(table_input &in)
  352. {
  353.   options *opt = new options;
  354.   string line;
  355.   int level = 0;
  356.   for (;;) {
  357.     int c = in.get();
  358.     if (c == EOF) {
  359.       int i = line.length();
  360.       while (--i >= 0)
  361.     in.unget(line[i]);
  362.       return opt;
  363.     }
  364.     if (c == '\n') {
  365.       in.unget(c);
  366.       int i = line.length();
  367.       while (--i >= 0)
  368.     in.unget(line[i]);
  369.       return opt;
  370.     }
  371.     else if (c == '(')
  372.       level++;
  373.     else if (c == ')')
  374.       level--;
  375.     else if (c == ';' && level == 0) {
  376.       line += '\0';
  377.       break;
  378.     }
  379.     line += c;
  380.   }
  381.   if (line.empty())
  382.     return opt;
  383.   char *p = &line[0];
  384.   for (;;) {
  385.     while (csspace(*p) || *p == ',')
  386.       p++;
  387.     if (*p == '\0')
  388.       break;
  389.     char *q = p;
  390.     while (*q != ' ' && *q != '\0' && *q != '\t' && *q != ',' && *q != '(')
  391.       q++;
  392.     char *arg = 0;
  393.     if (*q != '(' && *q != '\0')
  394.       *q++ = '\0';
  395.     while (csspace(*q))
  396.       q++;
  397.     if (*q == '(') {
  398.       *q++ = '\0';
  399.       arg = q;
  400.       while (*q != ')' && *q != '\0')
  401.     q++;
  402.       if (*q == '\0')
  403.     error("missing `)'");
  404.       else
  405.     *q++ = '\0';
  406.     }
  407.     if (*p == '\0') {
  408.       if (arg)
  409.     error("argument without option");
  410.     }
  411.     else if (strieq(p, "tab")) {
  412.       if (!arg)
  413.     error("`tab' option requires argument in parentheses");
  414.       else {
  415.     if (arg[0] == '\0' || arg[1] != '\0')
  416.       error("argument to `tab' option must be a single character");
  417.     else
  418.       opt->tab_char = arg[0];
  419.       }
  420.     }
  421.     else if (strieq(p, "linesize")) {
  422.       if (!arg)
  423.     error("`linesize' option requires argument in parentheses");
  424.       else {
  425.     if (sscanf(arg, "%d", &opt->linesize) != 1)
  426.       error("bad linesize `%s'", arg);
  427.     else if (opt->linesize <= 0) {
  428.       error("linesize must be positive");
  429.       opt->linesize = 0;
  430.     }
  431.       }
  432.     }
  433.     else if (strieq(p, "delim")) {
  434.       if (!arg)
  435.     error("`delim' option requires argument in parentheses");
  436.       else if (arg[0] == '\0' || arg[1] == '\0' || arg[2] != '\0')
  437.     error("argument to `delim' option must be two characters");
  438.       else {
  439.     opt->delim[0] = arg[0];
  440.     opt->delim[1] = arg[1];
  441.       }
  442.     }
  443.     else if (strieq(p, "center") || strieq(p, "centre")) {
  444.       if (arg)
  445.     error("`center' option does not take a argument");
  446.       opt->flags |= table::CENTER;
  447.     }
  448.     else if (strieq(p, "expand")) {
  449.       if (arg)
  450.     error("`expand' option does not take a argument");
  451.       opt->flags |= table::EXPAND;
  452.     }
  453.     else if (strieq(p, "box") || strieq(p, "frame")) {
  454.       if (arg)
  455.     error("`box' option does not take a argument");
  456.       opt->flags |= table::BOX;
  457.     }
  458.     else if (strieq(p, "doublebox") || strieq(p, "doubleframe")) {
  459.       if (arg)
  460.     error("`doublebox' option does not take a argument");
  461.       opt->flags |= table::DOUBLEBOX;
  462.     }
  463.     else if (strieq(p, "allbox")) {
  464.       if (arg)
  465.     error("`allbox' option does not take a argument");
  466.       opt->flags |= table::ALLBOX;
  467.     }
  468.     else {
  469.       error("unrecognised global option `%1'", p);
  470.       // delete opt;
  471.       // return 0;
  472.     }
  473.     p = q;
  474.   }
  475.   return opt;
  476. }
  477.  
  478. entry_modifier::entry_modifier()
  479. : vertical_alignment(CENTER), zero_width(0), stagger(0)
  480. {
  481.   vertical_spacing.inc = vertical_spacing.val = 0;
  482.   point_size.inc = point_size.val = 0;
  483. }
  484.  
  485. entry_modifier::~entry_modifier()
  486. {
  487. }
  488.  
  489. entry_format::entry_format() : type(LEFT)
  490. {
  491. }
  492.  
  493. entry_format::entry_format(format_type t) : type(t)
  494. {
  495. }
  496.  
  497. void entry_format::debug_print() const
  498. {
  499.   switch (type) {
  500.   case LEFT:
  501.     putc('l', stderr);
  502.     break;
  503.   case CENTER:
  504.     putc('c', stderr);
  505.     break;
  506.   case RIGHT:
  507.     putc('r', stderr);
  508.     break;
  509.   case NUMERIC:
  510.     putc('n', stderr);
  511.     break;
  512.   case ALPHABETIC:
  513.     putc('a', stderr);
  514.     break;
  515.   case SPAN:
  516.     putc('s', stderr);
  517.     break;
  518.   case VSPAN:
  519.     putc('^', stderr);
  520.     break;
  521.   case HLINE:
  522.     putc('_', stderr);
  523.     break;
  524.   case DOUBLE_HLINE:
  525.     putc('=', stderr);
  526.     break;
  527.   default:
  528.     assert(0);
  529.     break;
  530.   }
  531.   if (point_size.val != 0) {
  532.     putc('p', stderr);
  533.     if (point_size.inc > 0)
  534.       putc('+', stderr);
  535.     else if (point_size.inc < 0)
  536.       putc('-', stderr);
  537.     fprintf(stderr, "%d ", point_size.val);
  538.   }
  539.   if (vertical_spacing.val != 0) {
  540.     putc('v', stderr);
  541.     if (vertical_spacing.inc > 0)
  542.       putc('+', stderr);
  543.     else if (vertical_spacing.inc < 0)
  544.       putc('-', stderr);
  545.     fprintf(stderr, "%d ", vertical_spacing.val);
  546.   }
  547.   if (!font.empty()) {
  548.     putc('f', stderr);
  549.     put_string(font, stderr);
  550.     putc(' ', stderr);
  551.   }
  552.   switch (vertical_alignment) {
  553.   case entry_modifier::CENTER:
  554.     break;
  555.   case entry_modifier::TOP:
  556.     putc('t', stderr);
  557.     break;
  558.   case entry_modifier::BOTTOM:
  559.     putc('d', stderr);
  560.     break;
  561.   }
  562.   if (zero_width)
  563.     putc('z', stderr);
  564.   if (stagger)
  565.     putc('u', stderr);
  566. }
  567.  
  568. struct format {
  569.   int nrows;
  570.   int ncolumns;
  571.   int *separation;
  572.   string *width;
  573.   char *equal;
  574.   entry_format **entry;
  575.   char **vline;
  576.  
  577.   format(int nr, int nc);
  578.   ~format();
  579.   void add_rows(int n);
  580. };
  581.  
  582. format::format(int nr, int nc) : nrows(nr), ncolumns(nc)
  583. {
  584.   int i;
  585.   separation = new int[ncolumns - 1];
  586.   for (i = 0; i < ncolumns-1; i++)
  587.     separation[i] = -1;
  588.   width = new string[ncolumns];
  589.   equal = new char[ncolumns];
  590.   for (i = 0; i < ncolumns; i++)
  591.     equal[i] = 0;
  592.   entry = new entry_format *[nrows];
  593.   for (i = 0; i < nrows; i++)
  594.     entry[i] = new entry_format[ncolumns];
  595.   vline = new char*[nrows];
  596.   for (i = 0; i < nrows; i++) {
  597.     vline[i] = new char[ncolumns+1];
  598.     for (int j = 0; j < ncolumns+1; j++)
  599.       vline[i][j] = 0;
  600.   }
  601. }
  602.  
  603. void format::add_rows(int n)
  604. {
  605.   int i;
  606.   char **old_vline = vline;
  607.   vline = new char*[nrows + n];
  608.   for (i = 0; i < nrows; i++)
  609.     vline[i] = old_vline[i];
  610.   delete old_vline;
  611.   for (i = 0; i < n; i++) {
  612.     vline[nrows + i] = new char[ncolumns + 1];
  613.     for (int j = 0; j < ncolumns + 1; j++)
  614.       vline[nrows + i][j] = 0;
  615.   }
  616.   entry_format **old_entry = entry;
  617.   entry = new entry_format *[nrows + n];
  618.   for (i = 0; i < nrows; i++)
  619.     entry[i] = old_entry[i];
  620.   delete old_entry;
  621.   for (i = 0; i < n; i++)
  622.     entry[nrows + i] = new entry_format[ncolumns];
  623.   nrows += n;
  624. }
  625.  
  626. format::~format()
  627. {
  628.   delete separation;
  629.   delete [ncolumns] width;
  630.   delete equal;
  631.   for (int i = 0; i < nrows; i++) {
  632.     delete vline[i];
  633.     delete [ncolumns] entry[i];
  634.   }
  635.   delete vline;
  636.   delete entry;
  637. }
  638.  
  639. struct input_entry_format : entry_format {
  640.   input_entry_format *next;
  641.   string width;
  642.   int separation;
  643.   int vline;
  644.   int pre_vline;
  645.   int last_column;
  646.   int equal;
  647.   input_entry_format(format_type, input_entry_format * = 0);
  648.   void debug_print();
  649. };
  650.  
  651. input_entry_format::input_entry_format(format_type t, input_entry_format *p)
  652. : entry_format(t), next(p)
  653. {
  654.   separation = -1;
  655.   last_column = 0;
  656.   vline = 0;
  657.   pre_vline = 0;
  658.   equal = 0;
  659. }
  660.  
  661. void free_input_entry_format_list(input_entry_format *list)
  662. {
  663.   while (list) {
  664.     input_entry_format *tem = list;
  665.     list = list->next;
  666.     delete tem;
  667.   }
  668. }
  669.  
  670. void input_entry_format::debug_print()
  671. {
  672.   for (int i = 0; i < pre_vline; i++)
  673.     putc('|', stderr);
  674.   entry_format::debug_print();
  675.   if (!width.empty()) {
  676.     putc('w', stderr);
  677.     putc('(', stderr);
  678.     put_string(width, stderr);
  679.     putc(')', stderr);
  680.   }
  681.   if (equal)
  682.     putc('e', stderr);
  683.   if (separation >= 0)
  684.     fprintf(stderr, "%d", separation); 
  685.   for (i = 0; i < vline; i++)
  686.     putc('|', stderr);
  687.   if (last_column)
  688.     putc(',', stderr);
  689. }
  690.  
  691.  
  692. // return zero if we should give up on this table
  693. // if this is a continuation format line, current_format will be the current
  694. // format line
  695.  
  696. format *process_format(table_input &in, options *opt,
  697.                format *current_format = 0)
  698. {
  699.   input_entry_format *list = 0;
  700.   int c = in.get();
  701.   for (;;) {
  702.     while (c == ' ' || c == '\t' || c == opt->tab_char || c == '\n')
  703.       c = in.get();
  704.     int pre_vline = 0;
  705.     if (c == '|') {
  706.       pre_vline = 1;
  707.       c = in.get();
  708.       while (c == ' ' || c == '\t' || c == opt->tab_char)
  709.     c = in.get();
  710.       if (c == '|') {
  711.     pre_vline = 2;
  712.     c = in.get();
  713.     while (c == ' ' || c == '\t' || c == opt->tab_char)
  714.       c = in.get();
  715.       }
  716.     }
  717.     if (c == '.')
  718.       break;
  719.     if (c == EOF) {
  720.       error("end of input while processing format");
  721.       free_input_entry_format_list(list);
  722.       return 0;
  723.     }
  724.     format_type t;
  725.     switch (c) {
  726.     case 'n':
  727.     case 'N':
  728.       t = entry_format::NUMERIC;
  729.       break;
  730.     case 'a':
  731.     case 'A':
  732.       t = entry_format::ALPHABETIC;
  733.       break;
  734.     case 'c':
  735.     case 'C':
  736.       t = entry_format::CENTER;
  737.       break;
  738.     case 'l':
  739.     case 'L':
  740.       t = entry_format::LEFT;
  741.       break;
  742.     case 'r':
  743.     case 'R':
  744.       t = entry_format::RIGHT;
  745.       break;
  746.     case 's':
  747.     case 'S':
  748.       t = entry_format::SPAN;
  749.       break;
  750.     case '^':
  751.       t = entry_format::VSPAN;
  752.       break;
  753.     case '_':
  754.       t = entry_format::HLINE;
  755.       break;
  756.     case '=':
  757.       t = entry_format::DOUBLE_HLINE;
  758.       break;
  759.     default:
  760.       error("unrecognised format `%1'", char(c));
  761.       free_input_entry_format_list(list);
  762.       return 0;
  763.     }
  764.     c = in.get();
  765.     list = new input_entry_format(t, list);
  766.     if (pre_vline)
  767.       list->pre_vline = pre_vline;
  768.     int success = 1;
  769.     do {
  770.       while (c == ' ' || c == '\t' || c == opt->tab_char)
  771.     c = in.get();
  772.       switch (c) {
  773.       case 't':
  774.       case 'T':
  775.     c = in.get();
  776.     list->vertical_alignment = entry_modifier::TOP;
  777.     break;
  778.       case 'd':
  779.       case 'D':
  780.     c = in.get();
  781.     list->vertical_alignment = entry_modifier::BOTTOM;
  782.     break;
  783.       case 'u':
  784.       case 'U':
  785.     c = in.get();
  786.     list->stagger = 1;
  787.     break;
  788.       case 'z':
  789.       case 'Z':
  790.     c = in.get();
  791.     list->zero_width = 1;
  792.     break;
  793.       case '0':
  794.       case '1':
  795.       case '2':
  796.       case '3':
  797.       case '4':
  798.       case '5':
  799.       case '6':
  800.       case '7':
  801.       case '8':
  802.       case '9':
  803.     int w = 0;
  804.     do {
  805.       w = w*10 + (c - '0');
  806.       c = in.get();
  807.     } while (c != EOF && csdigit(c));
  808.     list->separation = w;
  809.     break;
  810.       case 'f':
  811.       case 'F':
  812.     do {
  813.       c = in.get();
  814.     } while (c == ' ' || c == '\t');
  815.     if (c == EOF) {
  816.       error("missing font name");
  817.       break;
  818.     }
  819.     if (c == '(') {
  820.       for (;;) {
  821.         c = in.get();
  822.         if (c == EOF || c == ' ' || c == '\t') {
  823.           error("missing `)'");
  824.           break;
  825.         }
  826.         if (c == ')') {
  827.           c = in.get();
  828.           break;
  829.         }
  830.         list->font += char(c);
  831.       }
  832.     }
  833.     else {
  834.       list->font = c;
  835.       c = in.get();
  836.       if (c != ' ' && c != '\t' && c != '.' && c != '\n') {
  837.         list->font += char(c);
  838.         c = in.get();
  839.       }
  840.     }
  841.     break;
  842.       case 'v':
  843.       case 'V':
  844.     c = in.get();
  845.     list->vertical_spacing.val = 0;
  846.     list->vertical_spacing.inc = 0;
  847.     if (c == '+' || c == '-') {
  848.       list->vertical_spacing.inc = (c == '+' ? 1 : -1);
  849.       c = in.get();
  850.     }
  851.     if (c == EOF || !csdigit(c)) {
  852.       error("`v' modifier must be followed by number");
  853.       list->vertical_spacing.inc = 0;
  854.     }
  855.     else {
  856.       do {
  857.         list->vertical_spacing.val *= 10;
  858.         list->vertical_spacing.val += c - '0';
  859.         c = in.get();
  860.       } while (c != EOF && csdigit(c));
  861.     }
  862.     if (list->vertical_spacing.val > MAX_VERTICAL_SPACING
  863.         || list->vertical_spacing.val < -MAX_VERTICAL_SPACING) {
  864.       error("unreasonable point size");
  865.       list->vertical_spacing.val = 0;
  866.       list->vertical_spacing.inc = 0;
  867.     }
  868.     break;
  869.       case 'p':
  870.       case 'P':
  871.     c = in.get();
  872.     list->point_size.val = 0;
  873.     list->point_size.inc = 0;
  874.     if (c == '+' || c == '-') {
  875.       list->point_size.inc = (c == '+' ? 1 : -1);
  876.       c = in.get();
  877.     }
  878.     if (c == EOF || !csdigit(c)) {
  879.       error("`p' modifier must be followed by number");
  880.       list->point_size.inc = 0;
  881.     }
  882.     else {
  883.       do {
  884.         list->point_size.val *= 10;
  885.         list->point_size.val += c - '0';
  886.         c = in.get();
  887.       } while (c != EOF && csdigit(c));
  888.     }
  889.     if (list->point_size.val > MAX_POINT_SIZE
  890.         || list->point_size.val < -MAX_POINT_SIZE) {
  891.       error("unreasonable point size");
  892.       list->point_size.val = 0;
  893.       list->point_size.inc = 0;
  894.     }
  895.     break;
  896.       case 'w':
  897.       case 'W':
  898.     c = in.get();
  899.     while (c == ' ' || c == '\t')
  900.       c = in.get();
  901.     if (c == '(') {
  902.       list->width = "";
  903.       c = in.get();
  904.       while (c != ')') {
  905.         if (c == EOF || c == '\n') {
  906.           error("missing `)'");
  907.           free_input_entry_format_list(list);
  908.           return 0;
  909.         }
  910.         list->width += c;
  911.         c = in.get();
  912.       }
  913.       c = in.get();
  914.     }
  915.     else {
  916.       if (c == '+' || c == '-') {
  917.         list->width = char(c);
  918.         c = in.get();
  919.       }
  920.       else
  921.         list->width = "";
  922.       if (c == EOF || !csdigit(c))
  923.         error("bad argument for `w' modifier");
  924.       else {
  925.         do {
  926.           list->width += char(c);
  927.           c = in.get();
  928.         } while (c != EOF && csdigit(c));
  929.       }
  930.     }
  931.     break;
  932.       case 'e':
  933.       case 'E':
  934.     c = in.get();
  935.     list->equal++;
  936.     break;
  937.       case '|':
  938.     c = in.get();
  939.     list->vline++;
  940.     break;
  941.       case 'B':
  942.       case 'b':
  943.     c = in.get();
  944.     list->font = "B";
  945.     break;
  946.       case 'I':
  947.       case 'i':
  948.     c = in.get();
  949.     list->font = "I";
  950.     break;
  951.       default:
  952.     success = 0;
  953.     break;
  954.       }
  955.     } while (success);
  956.     if (list->vline > 2) {
  957.       list->vline = 2;
  958.       error("more than 2 vertical bars between key letters");
  959.     }
  960.     if (c == '\n' || c == ',') {
  961.       c = in.get();
  962.       list->last_column = 1;
  963.     }
  964.   }
  965.   if (c == '.') {
  966.     do {
  967.       c = in.get();
  968.     } while (c == ' ' || c == '\t');
  969.     if (c != '\n') {
  970.       error("`.' not last character on line");
  971.       free_input_entry_format_list(list);
  972.       return 0;
  973.     }
  974.   }
  975.   if (!list) {
  976.     error("no format");
  977.     free_input_entry_format_list(list);
  978.     return 0;
  979.   }
  980.   list->last_column = 1;
  981.   // now reverse the list so that the first row is at the beginning
  982.   input_entry_format *rev = 0;
  983.   while (list != 0) {
  984.     input_entry_format *tem = list->next;
  985.     list->next = rev;
  986.     rev = list;
  987.     list = tem;
  988.   }
  989.   list = rev;
  990.   input_entry_format *tem;
  991.  
  992. #if 0
  993.   for (tem = list; tem; tem = tem->next)
  994.     tem->debug_print();
  995.   putc('\n', stderr);
  996. #endif
  997.   // compute number of columns and rows
  998.   int ncolumns = 0;
  999.   int nrows = 0;
  1000.   int col = 0;
  1001.   for (tem = list; tem; tem = tem->next) {
  1002.     if (tem->last_column) {
  1003.       if (col >= ncolumns)
  1004.     ncolumns = col + 1;
  1005.       col = 0;
  1006.       nrows++;
  1007.     }
  1008.     else
  1009.       col++;
  1010.   }
  1011.   int row;
  1012.   format *f;
  1013.   if (current_format) {
  1014.     if (ncolumns > current_format->ncolumns) {
  1015.       error("cannot increase the number of columns in a continued format");
  1016.       free_input_entry_format_list(list);
  1017.       return 0;
  1018.     }
  1019.     f = current_format;
  1020.     row = f->nrows;
  1021.     f->add_rows(nrows);
  1022.   }
  1023.   else {
  1024.     f = new format(nrows, ncolumns);
  1025.     row = 0;
  1026.   }
  1027.   col = 0;
  1028.   for (tem = list; tem; tem = tem->next) {
  1029.     f->entry[row][col] = *tem;
  1030.     if (!current_format) {
  1031.       if (col < ncolumns-1) {
  1032.     // use the greatest separation
  1033.     if (tem->separation > f->separation[col])
  1034.       f->separation[col] = tem->separation;
  1035.       }
  1036.       else if (tem->separation >= 0)
  1037.     error("column separation specified for last column");
  1038.       if (!tem->width.empty()) {
  1039.     // use the last width
  1040.     if (!f->width[col].empty() && f->width[col] != tem->width)
  1041.       error("multiple widths for column %1", col+1);
  1042.     f->width[col] = tem->width;
  1043.       }
  1044.       if (tem->equal)
  1045.     f->equal[col] = 1;
  1046.     }
  1047.     if (tem->pre_vline) {
  1048.       assert(col == 0);
  1049.       f->vline[row][col] = tem->pre_vline;
  1050.     }
  1051.     f->vline[row][col+1] = tem->vline;
  1052.     if (tem->last_column) {
  1053.       row++;
  1054.       col = 0;
  1055.     }
  1056.     else
  1057.       col++;
  1058.   }
  1059.   free_input_entry_format_list(list);
  1060.   for (col = 0; col < ncolumns; col++) {
  1061.     entry_format *e = f->entry[f->nrows-1] + col;
  1062.     if (e->type != entry_format::HLINE
  1063.     && e->type != entry_format::DOUBLE_HLINE
  1064.     && e->type != entry_format::SPAN)
  1065.       break;
  1066.   }
  1067.   if (col >= ncolumns) {
  1068.     error("last row of format is all lines");
  1069.     delete f;
  1070.     return 0;
  1071.   }
  1072.   return f;
  1073. }
  1074.  
  1075.  
  1076. table *process_data(table_input &in, format *f, options *opt)
  1077. {
  1078.   char tab_char = opt->tab_char;
  1079.   int ncolumns = f->ncolumns;
  1080.   int current_row = 0;
  1081.   int format_index = 0;
  1082.   int give_up = 0;
  1083.   enum { DATA_INPUT_LINE, TROFF_INPUT_LINE, SINGLE_HLINE, DOUBLE_HLINE } type;
  1084.   table *tbl = new table(ncolumns, opt->flags, opt->linesize);
  1085.   for (int i = 0; i < ncolumns - 1; i++)
  1086.     if (f->separation[i] >= 0)
  1087.       tbl->set_column_separation(i, f->separation[i]);
  1088.   for (i = 0; i < ncolumns; i++)
  1089.     if (!f->width[i].empty())
  1090.       tbl->set_minimum_width(i, f->width[i]);
  1091.   for (i = 0; i < ncolumns; i++)
  1092.     if (f->equal[i])
  1093.       tbl->set_equal_column(i);
  1094.   if (opt->delim[0] != '\0')
  1095.     tbl->set_delim(opt->delim[0], opt->delim[1]);
  1096.   for (;;) {
  1097.     // first determine what type of line this is
  1098.     int c = in.get();
  1099.     if (c == EOF)
  1100.       break;
  1101.     if (c == '.') {
  1102.       int d = in.get();
  1103.       if (d != EOF && csdigit(d)) {
  1104.     in.unget(d);
  1105.     type = DATA_INPUT_LINE;
  1106.       }
  1107.       else {
  1108.     in.unget(d);
  1109.     type = TROFF_INPUT_LINE;
  1110.       }
  1111.     }
  1112.     else if (c == '_' || c == '=') {
  1113.       int d = in.get();
  1114.       if (d == '\n') {
  1115.     if (c == '_')
  1116.       type = SINGLE_HLINE;
  1117.     else
  1118.       type = DOUBLE_HLINE;
  1119.       }
  1120.       else {
  1121.     in.unget(d);
  1122.     type = DATA_INPUT_LINE;
  1123.       }
  1124.     }
  1125.     else {
  1126.       type = DATA_INPUT_LINE;
  1127.     }
  1128.     switch (type) {
  1129.     case DATA_INPUT_LINE:
  1130.       {
  1131.     string input_entry;
  1132.     if (format_index >= f->nrows)
  1133.       format_index = f->nrows - 1;
  1134.     // A format row that is all lines doesn't use up a data line.
  1135.     while (format_index < f->nrows - 1) {
  1136.       for (int c = 0; c < ncolumns; c++) {
  1137.         entry_format *e = f->entry[format_index] + c;
  1138.         if (e->type != entry_format::HLINE
  1139.         && e->type != entry_format::DOUBLE_HLINE
  1140.         // Unfortunately tbl treats a span as needing data.
  1141.         // && e->type != entry_format::SPAN
  1142.         )
  1143.           break;
  1144.       }
  1145.       if (c < ncolumns)
  1146.         break;
  1147.       for (c = 0; c < ncolumns; c++)
  1148.         tbl->add_entry(current_row, c, input_entry,
  1149.                f->entry[format_index] + c, current_filename,
  1150.                current_lineno);
  1151.       tbl->add_vlines(current_row, f->vline[format_index]);
  1152.       format_index++;
  1153.       current_row++;
  1154.     }
  1155.     entry_format *line_format = f->entry[format_index];
  1156.     int col = 0;
  1157.     for (;;) {
  1158.       if (c == tab_char || c == '\n') {
  1159.         int ln = current_lineno;
  1160.         if (c == '\n')
  1161.           --ln;
  1162.         while (col < ncolumns
  1163.            && line_format[col].type == entry_format::SPAN) {
  1164.           tbl->add_entry(current_row, col, "", &line_format[col],
  1165.                  current_filename, ln);
  1166.           col++;
  1167.         }
  1168.         if (c == '\n' && input_entry.length() == 2
  1169.         && input_entry[0] == 'T' && input_entry[1] == '{') {
  1170.           input_entry = "";
  1171.           ln++;
  1172.           enum {
  1173.         START, MIDDLE, GOT_T, GOT_RIGHT_BRACE, GOT_DOT,
  1174.         GOT_l, GOT_lf, END,
  1175.           } state = START;
  1176.           while (state != END) {
  1177.         c = in.get();
  1178.         if (c == EOF)
  1179.           break;
  1180.         switch (state) {
  1181.         case START:
  1182.           if (c == 'T')
  1183.             state = GOT_T;
  1184.           else if (c == '.')
  1185.             state = GOT_DOT;
  1186.           else {
  1187.             input_entry += c;
  1188.             if (c != '\n')
  1189.               state = MIDDLE;
  1190.           }
  1191.           break;
  1192.         case GOT_T:
  1193.           if (c == '}')
  1194.             state = GOT_RIGHT_BRACE;
  1195.           else {
  1196.             input_entry += 'T';
  1197.             input_entry += c;
  1198.             state = c == '\n' ? START : MIDDLE;
  1199.           }
  1200.           break;
  1201.         case GOT_DOT:
  1202.           if (c == 'l')
  1203.             state = GOT_l;
  1204.           else {
  1205.             input_entry += '.';
  1206.             input_entry += c;
  1207.             state = c == '\n' ? START : MIDDLE;
  1208.           }
  1209.           break;
  1210.         case GOT_l:
  1211.           if (c == 'f')
  1212.             state = GOT_lf;
  1213.           else {
  1214.             input_entry += ".l";
  1215.             input_entry += c;
  1216.             state = c == '\n' ? START : MIDDLE;
  1217.           }
  1218.           break;
  1219.         case GOT_lf:
  1220.           if (c == ' ' || c == '\n' || compatible_flag) {
  1221.             string args;
  1222.             input_entry += ".lf";
  1223.             while (c != EOF) {
  1224.               args += c;
  1225.               if (c == '\n')
  1226.             break;
  1227.               c = in.get();
  1228.             }
  1229.             args += '\0';
  1230.             interpret_lf_args(args.contents());
  1231.             // remove the '\0'
  1232.             args.set_length(args.length() - 1);
  1233.             input_entry += args;
  1234.             state = START;
  1235.           }
  1236.           else {
  1237.             input_entry += ".lf";
  1238.             input_entry += c;
  1239.             state = MIDDLE;
  1240.           }
  1241.           break;
  1242.         case GOT_RIGHT_BRACE:
  1243.           if (c == '\n' || c == tab_char)
  1244.             state = END;
  1245.           else {
  1246.             input_entry += 'T';
  1247.             input_entry += '}';
  1248.             input_entry += c;
  1249.             state = c == '\n' ? START : MIDDLE;
  1250.           }
  1251.           break;
  1252.         case MIDDLE:
  1253.           if (c == '\n')
  1254.             state = START;
  1255.           input_entry += c;
  1256.           break;
  1257.         case END:
  1258.         default:
  1259.           assert(0);
  1260.         }
  1261.           }
  1262.           if (c == EOF) {
  1263.         error("end of data in middle of text block");
  1264.         give_up = 1;
  1265.         break;
  1266.           }
  1267.         }
  1268.         if (col >= ncolumns) {
  1269.           if (!input_entry.empty()) {
  1270.         if (c == '\n')
  1271.           in.unget(c);
  1272.         input_entry += '\0';
  1273.         error("excess data entry `%1' discarded",
  1274.               input_entry.contents());
  1275.         if (c == '\n')
  1276.           (void)in.get();
  1277.           }
  1278.         }
  1279.         else
  1280.           tbl->add_entry(current_row, col, input_entry,
  1281.                  &line_format[col], current_filename, ln);
  1282.         col++;
  1283.         if (c == '\n')
  1284.           break;
  1285.         input_entry = "";
  1286.       }
  1287.       else
  1288.         input_entry += c;
  1289.       c = in.get();
  1290.       if (c == EOF)
  1291.         break;
  1292.     }
  1293.     if (give_up)
  1294.       break;
  1295.     input_entry = "";
  1296.     for (; col < ncolumns; col++)
  1297.       tbl->add_entry(current_row, col, input_entry, &line_format[col],
  1298.              current_filename, current_lineno - 1);
  1299.     tbl->add_vlines(current_row, f->vline[format_index]);
  1300.     current_row++;
  1301.     format_index++;
  1302.       }
  1303.       break;
  1304.     case TROFF_INPUT_LINE:
  1305.       {
  1306.     string line;
  1307.     int ln = current_lineno;
  1308.     for (;;) {
  1309.       line += c;
  1310.       if (c == '\n')
  1311.         break;
  1312.       c = in.get();
  1313.       if (c == EOF) {
  1314.         break;
  1315.       }
  1316.     }
  1317.     tbl->add_text_line(current_row, line, current_filename, ln);
  1318.     if (line.length() >= 4 
  1319.         && line[0] == '.' && line[1] == 'T' && line[2] == '&') {
  1320.       format *newf = process_format(in, opt, f);
  1321.       if (newf == 0)
  1322.         give_up = 1;
  1323.       else
  1324.         f = newf;
  1325.     }
  1326.     if (line.length() >= 3
  1327.         && line[0] == '.' && line[1] == 'f' && line[2] == 'f') {
  1328.       line += '\0';
  1329.       interpret_lf_args(line.contents() + 3);
  1330.     }
  1331.       }
  1332.       break;
  1333.     case SINGLE_HLINE:
  1334.       tbl->add_single_hline(current_row);
  1335.       break;
  1336.     case DOUBLE_HLINE:
  1337.       tbl->add_double_hline(current_row);
  1338.       break;
  1339.     default:
  1340.       assert(0);
  1341.     }
  1342.     if (give_up)
  1343.       break;
  1344.   }
  1345.   if (!give_up && current_row == 0) {
  1346.     error("no real data");
  1347.     give_up = 1;
  1348.   }
  1349.   if (give_up) {
  1350.     delete tbl;
  1351.     return 0;
  1352.   }
  1353.   return tbl;
  1354. }
  1355.  
  1356. void process_table(table_input &in)
  1357. {
  1358.   int c;
  1359.   options *opt = 0;
  1360.   format *form = 0;
  1361.   table *tbl = 0;
  1362.   if ((opt = process_options(in)) != 0 
  1363.       && (form = process_format(in, opt)) != 0
  1364.       && (tbl = process_data(in, form, opt)) != 0) {
  1365.     tbl->print();
  1366.     delete tbl;
  1367.   }
  1368.   else {
  1369.     error("giving up on this table");
  1370.     while ((c = in.get()) != EOF)
  1371.       ;
  1372.   }
  1373.   delete opt;
  1374.   delete form;
  1375.   if (!in.ended())
  1376.     error("premature end of file");
  1377. }
  1378.  
  1379. static void usage()
  1380. {
  1381.   fprintf(stderr, "usage: %s [ -vC ] [ files... ]\n", program_name);
  1382.   exit(1);
  1383. }
  1384.  
  1385. int main(int argc, char **argv)
  1386. {
  1387.   program_name = argv[0];
  1388.   static char stderr_buf[BUFSIZ];
  1389.   setbuf(stderr, stderr_buf);
  1390.   int opt;
  1391.   while ((opt = getopt(argc, argv, "vC")) != EOF)
  1392.     switch (opt) {
  1393.     case 'C':
  1394.       compatible_flag = 1;
  1395.       break;
  1396.     case 'v':
  1397.       {
  1398.     extern const char *version_string;
  1399.     fprintf(stderr, "GNU tbl version %s\n", version_string);
  1400.     fflush(stderr);
  1401.     break;
  1402.       }
  1403.     case '?':
  1404.       usage();
  1405.       break;
  1406.     default:
  1407.       assert(0);
  1408.     }
  1409.   printf(".if !\\n(.g .ab GNU tbl requires GNU troff.\n"
  1410.      ".if !dTS .ds TS\n"
  1411.      ".if !dTE .ds TE\n");
  1412.   if (argc > optind) {
  1413.     for (int i = optind; i < argc; i++) 
  1414.       if (argv[i][0] == '-' && argv[i][1] == '\0') {
  1415.     current_filename = "-";
  1416.     current_lineno = 1;
  1417.     if (i != 1)
  1418.       printf(".lf 1 -\n");
  1419.     process_input_file(stdin);
  1420.       }
  1421.       else {
  1422.     FILE *fp = fopen(argv[i], "r");
  1423.     if (fp == 0) {
  1424.       current_lineno = -1;
  1425.       error("can't open `%1': %2", argv[i], strerror(errno));
  1426.     }
  1427.     else {
  1428.       current_lineno = 1;
  1429.       current_filename = argv[i];
  1430.       printf(".lf 1 %s\n", current_filename);
  1431.       process_input_file(fp);
  1432.     }
  1433.       }
  1434.   }
  1435.   else {
  1436.     current_filename = "-";
  1437.     current_lineno = 1;
  1438.     process_input_file(stdin);
  1439.   }
  1440.   if (ferror(stdout) || fflush(stdout) < 0)
  1441.     fatal("output error");
  1442.   exit(0);
  1443. }
  1444.  
  1445.