home *** CD-ROM | disk | FTP | other *** search
/ Aminet 10 / aminetcdnumber101996.iso / Aminet / util / gnu / groff_src.lha / groff-1.10src / tbl / main.cc next >
C/C++ Source or Header  |  1995-08-23  |  32KB  |  1,512 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.com)
  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 2, 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 COPYING.  If not, write to the Free Software
  19. Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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.   char decimal_point_char;
  330.  
  331.   options();
  332. };
  333.  
  334. options::options()
  335. : flags(0), tab_char('\t'), decimal_point_char('.'), linesize(0)
  336. {
  337.   delim[0] = delim[1] = '\0';
  338. }
  339.  
  340. // Return non-zero if p and q are the same ignoring case.
  341.  
  342. int strieq(const char *p, const char *q)
  343. {
  344.   for (; cmlower(*p) == cmlower(*q); p++, q++)
  345.     if (*p == '\0')
  346.       return 1;
  347.   return 0;
  348. }
  349.  
  350. // return 0 if we should give up in this table
  351.  
  352. options *process_options(table_input &in)
  353. {
  354.   options *opt = new options;
  355.   string line;
  356.   int level = 0;
  357.   for (;;) {
  358.     int c = in.get();
  359.     if (c == EOF) {
  360.       int i = line.length();
  361.       while (--i >= 0)
  362.     in.unget(line[i]);
  363.       return opt;
  364.     }
  365.     if (c == '\n') {
  366.       in.unget(c);
  367.       int i = line.length();
  368.       while (--i >= 0)
  369.     in.unget(line[i]);
  370.       return opt;
  371.     }
  372.     else if (c == '(')
  373.       level++;
  374.     else if (c == ')')
  375.       level--;
  376.     else if (c == ';' && level == 0) {
  377.       line += '\0';
  378.       break;
  379.     }
  380.     line += c;
  381.   }
  382.   if (line.empty())
  383.     return opt;
  384.   char *p = &line[0];
  385.   for (;;) {
  386.     while (csspace(*p) || *p == ',')
  387.       p++;
  388.     if (*p == '\0')
  389.       break;
  390.     char *q = p;
  391.     while (*q != ' ' && *q != '\0' && *q != '\t' && *q != ',' && *q != '(')
  392.       q++;
  393.     char *arg = 0;
  394.     if (*q != '(' && *q != '\0')
  395.       *q++ = '\0';
  396.     while (csspace(*q))
  397.       q++;
  398.     if (*q == '(') {
  399.       *q++ = '\0';
  400.       arg = q;
  401.       while (*q != ')' && *q != '\0')
  402.     q++;
  403.       if (*q == '\0')
  404.     error("missing `)'");
  405.       else
  406.     *q++ = '\0';
  407.     }
  408.     if (*p == '\0') {
  409.       if (arg)
  410.     error("argument without option");
  411.     }
  412.     else if (strieq(p, "tab")) {
  413.       if (!arg)
  414.     error("`tab' option requires argument in parentheses");
  415.       else {
  416.     if (arg[0] == '\0' || arg[1] != '\0')
  417.       error("argument to `tab' option must be a single character");
  418.     else
  419.       opt->tab_char = arg[0];
  420.       }
  421.     }
  422.     else if (strieq(p, "linesize")) {
  423.       if (!arg)
  424.     error("`linesize' option requires argument in parentheses");
  425.       else {
  426.     if (sscanf(arg, "%d", &opt->linesize) != 1)
  427.       error("bad linesize `%s'", arg);
  428.     else if (opt->linesize <= 0) {
  429.       error("linesize must be positive");
  430.       opt->linesize = 0;
  431.     }
  432.       }
  433.     }
  434.     else if (strieq(p, "delim")) {
  435.       if (!arg)
  436.     error("`delim' option requires argument in parentheses");
  437.       else if (arg[0] == '\0' || arg[1] == '\0' || arg[2] != '\0')
  438.     error("argument to `delim' option must be two characters");
  439.       else {
  440.     opt->delim[0] = arg[0];
  441.     opt->delim[1] = arg[1];
  442.       }
  443.     }
  444.     else if (strieq(p, "center") || strieq(p, "centre")) {
  445.       if (arg)
  446.     error("`center' option does not take a argument");
  447.       opt->flags |= table::CENTER;
  448.     }
  449.     else if (strieq(p, "expand")) {
  450.       if (arg)
  451.     error("`expand' option does not take a argument");
  452.       opt->flags |= table::EXPAND;
  453.     }
  454.     else if (strieq(p, "box") || strieq(p, "frame")) {
  455.       if (arg)
  456.     error("`box' option does not take a argument");
  457.       opt->flags |= table::BOX;
  458.     }
  459.     else if (strieq(p, "doublebox") || strieq(p, "doubleframe")) {
  460.       if (arg)
  461.     error("`doublebox' option does not take a argument");
  462.       opt->flags |= table::DOUBLEBOX;
  463.     }
  464.     else if (strieq(p, "allbox")) {
  465.       if (arg)
  466.     error("`allbox' option does not take a argument");
  467.       opt->flags |= table::ALLBOX;
  468.     }
  469.     else if (strieq(p, "nokeep")) {
  470.       if (arg)
  471.     error("`nokeep' option does not take a argument");
  472.       opt->flags |= table::NOKEEP;
  473.     }
  474.     else if (strieq(p, "decimalpoint")) {
  475.       if (!arg)
  476.     error("`decimalpoint' option requires argument in parentheses");
  477.       else {
  478.     if (arg[0] == '\0' || arg[1] != '\0')
  479.       error("argument to `decimalpoint' option must be a single character");
  480.     else
  481.       opt->decimal_point_char = arg[0];
  482.       }
  483.     }
  484.     else {
  485.       error("unrecognised global option `%1'", p);
  486.       // delete opt;
  487.       // return 0;
  488.     }
  489.     p = q;
  490.   }
  491.   return opt;
  492. }
  493.  
  494. entry_modifier::entry_modifier()
  495. : vertical_alignment(CENTER), zero_width(0), stagger(0)
  496. {
  497.   vertical_spacing.inc = vertical_spacing.val = 0;
  498.   point_size.inc = point_size.val = 0;
  499. }
  500.  
  501. entry_modifier::~entry_modifier()
  502. {
  503. }
  504.  
  505. entry_format::entry_format() : type(FORMAT_LEFT)
  506. {
  507. }
  508.  
  509. entry_format::entry_format(format_type t) : type(t)
  510. {
  511. }
  512.  
  513. void entry_format::debug_print() const
  514. {
  515.   switch (type) {
  516.   case FORMAT_LEFT:
  517.     putc('l', stderr);
  518.     break;
  519.   case FORMAT_CENTER:
  520.     putc('c', stderr);
  521.     break;
  522.   case FORMAT_RIGHT:
  523.     putc('r', stderr);
  524.     break;
  525.   case FORMAT_NUMERIC:
  526.     putc('n', stderr);
  527.     break;
  528.   case FORMAT_ALPHABETIC:
  529.     putc('a', stderr);
  530.     break;
  531.   case FORMAT_SPAN:
  532.     putc('s', stderr);
  533.     break;
  534.   case FORMAT_VSPAN:
  535.     putc('^', stderr);
  536.     break;
  537.   case FORMAT_HLINE:
  538.     putc('_', stderr);
  539.     break;
  540.   case FORMAT_DOUBLE_HLINE:
  541.     putc('=', stderr);
  542.     break;
  543.   default:
  544.     assert(0);
  545.     break;
  546.   }
  547.   if (point_size.val != 0) {
  548.     putc('p', stderr);
  549.     if (point_size.inc > 0)
  550.       putc('+', stderr);
  551.     else if (point_size.inc < 0)
  552.       putc('-', stderr);
  553.     fprintf(stderr, "%d ", point_size.val);
  554.   }
  555.   if (vertical_spacing.val != 0) {
  556.     putc('v', stderr);
  557.     if (vertical_spacing.inc > 0)
  558.       putc('+', stderr);
  559.     else if (vertical_spacing.inc < 0)
  560.       putc('-', stderr);
  561.     fprintf(stderr, "%d ", vertical_spacing.val);
  562.   }
  563.   if (!font.empty()) {
  564.     putc('f', stderr);
  565.     put_string(font, stderr);
  566.     putc(' ', stderr);
  567.   }
  568.   switch (vertical_alignment) {
  569.   case entry_modifier::CENTER:
  570.     break;
  571.   case entry_modifier::TOP:
  572.     putc('t', stderr);
  573.     break;
  574.   case entry_modifier::BOTTOM:
  575.     putc('d', stderr);
  576.     break;
  577.   }
  578.   if (zero_width)
  579.     putc('z', stderr);
  580.   if (stagger)
  581.     putc('u', stderr);
  582. }
  583.  
  584. struct format {
  585.   int nrows;
  586.   int ncolumns;
  587.   int *separation;
  588.   string *width;
  589.   char *equal;
  590.   entry_format **entry;
  591.   char **vline;
  592.  
  593.   format(int nr, int nc);
  594.   ~format();
  595.   void add_rows(int n);
  596. };
  597.  
  598. format::format(int nr, int nc) : nrows(nr), ncolumns(nc)
  599. {
  600.   int i;
  601.   separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
  602.   for (i = 0; i < ncolumns-1; i++)
  603.     separation[i] = -1;
  604.   width = new string[ncolumns];
  605.   equal = new char[ncolumns];
  606.   for (i = 0; i < ncolumns; i++)
  607.     equal[i] = 0;
  608.   entry = new entry_format *[nrows];
  609.   for (i = 0; i < nrows; i++)
  610.     entry[i] = new entry_format[ncolumns];
  611.   vline = new char*[nrows];
  612.   for (i = 0; i < nrows; i++) {
  613.     vline[i] = new char[ncolumns+1];
  614.     for (int j = 0; j < ncolumns+1; j++)
  615.       vline[i][j] = 0;
  616.   }
  617. }
  618.  
  619. void format::add_rows(int n)
  620. {
  621.   int i;
  622.   char **old_vline = vline;
  623.   vline = new char*[nrows + n];
  624.   for (i = 0; i < nrows; i++)
  625.     vline[i] = old_vline[i];
  626.   a_delete old_vline;
  627.   for (i = 0; i < n; i++) {
  628.     vline[nrows + i] = new char[ncolumns + 1];
  629.     for (int j = 0; j < ncolumns + 1; j++)
  630.       vline[nrows + i][j] = 0;
  631.   }
  632.   entry_format **old_entry = entry;
  633.   entry = new entry_format *[nrows + n];
  634.   for (i = 0; i < nrows; i++)
  635.     entry[i] = old_entry[i];
  636.   a_delete old_entry;
  637.   for (i = 0; i < n; i++)
  638.     entry[nrows + i] = new entry_format[ncolumns];
  639.   nrows += n;
  640. }
  641.  
  642. format::~format()
  643. {
  644.   a_delete separation;
  645.   ad_delete(ncolumns) width;
  646.   a_delete equal;
  647.   for (int i = 0; i < nrows; i++) {
  648.     a_delete vline[i];
  649.     ad_delete(ncolumns) entry[i];
  650.   }
  651.   a_delete vline;
  652.   a_delete entry;
  653. }
  654.  
  655. struct input_entry_format : public entry_format {
  656.   input_entry_format *next;
  657.   string width;
  658.   int separation;
  659.   int vline;
  660.   int pre_vline;
  661.   int last_column;
  662.   int equal;
  663.   input_entry_format(format_type, input_entry_format * = 0);
  664.   ~input_entry_format();
  665.   void debug_print();
  666. };
  667.  
  668. input_entry_format::input_entry_format(format_type t, input_entry_format *p)
  669. : entry_format(t), next(p)
  670. {
  671.   separation = -1;
  672.   last_column = 0;
  673.   vline = 0;
  674.   pre_vline = 0;
  675.   equal = 0;
  676. }
  677.  
  678. input_entry_format::~input_entry_format()
  679. {
  680. }
  681.  
  682. void free_input_entry_format_list(input_entry_format *list)
  683. {
  684.   while (list) {
  685.     input_entry_format *tem = list;
  686.     list = list->next;
  687.     delete tem;
  688.   }
  689. }
  690.  
  691. void input_entry_format::debug_print()
  692. {
  693.   int i;
  694.   for (i = 0; i < pre_vline; i++)
  695.     putc('|', stderr);
  696.   entry_format::debug_print();
  697.   if (!width.empty()) {
  698.     putc('w', stderr);
  699.     putc('(', stderr);
  700.     put_string(width, stderr);
  701.     putc(')', stderr);
  702.   }
  703.   if (equal)
  704.     putc('e', stderr);
  705.   if (separation >= 0)
  706.     fprintf(stderr, "%d", separation); 
  707.   for (i = 0; i < vline; i++)
  708.     putc('|', stderr);
  709.   if (last_column)
  710.     putc(',', stderr);
  711. }
  712.  
  713. // Return zero if we should give up on this table.
  714. // If this is a continuation format line, current_format will be the current
  715. // format line.
  716.  
  717. format *process_format(table_input &in, options *opt,
  718.                format *current_format = 0)
  719. {
  720.   input_entry_format *list = 0;
  721.   int c = in.get();
  722.   for (;;) {
  723.     int pre_vline = 0;
  724.     int got_format = 0;
  725.     int got_period = 0;
  726.     format_type t;
  727.     for (;;) {
  728.       if (c == EOF) {
  729.     error("end of input while processing format");
  730.     free_input_entry_format_list(list);
  731.     return 0;
  732.       }
  733.       switch (c) {
  734.       case 'n':
  735.       case 'N':
  736.     t = FORMAT_NUMERIC;
  737.     got_format = 1;
  738.     break;
  739.       case 'a':
  740.       case 'A':
  741.     got_format = 1;
  742.     t = FORMAT_ALPHABETIC;
  743.     break;
  744.       case 'c':
  745.       case 'C':
  746.     got_format = 1;
  747.     t = FORMAT_CENTER;
  748.     break;
  749.       case 'l':
  750.       case 'L':
  751.     got_format = 1;
  752.     t = FORMAT_LEFT;
  753.     break;
  754.       case 'r':
  755.       case 'R':
  756.     got_format = 1;
  757.     t = FORMAT_RIGHT;
  758.     break;
  759.       case 's':
  760.       case 'S':
  761.     got_format = 1;
  762.     t = FORMAT_SPAN;
  763.     break;
  764.       case '^':
  765.     got_format = 1;
  766.     t = FORMAT_VSPAN;
  767.     break;
  768.       case '_':
  769.       case '-':            // tbl also accepts this
  770.     got_format = 1;
  771.     t = FORMAT_HLINE;
  772.     break;
  773.       case '=':
  774.     got_format = 1;
  775.     t = FORMAT_DOUBLE_HLINE;
  776.     break;
  777.       case '.':
  778.     got_period = 1;
  779.     break;
  780.       case '|':
  781.     pre_vline++;
  782.     break;
  783.       case ' ':
  784.       case '\t':
  785.       case '\n':
  786.     break;
  787.       default:
  788.     if (c == opt->tab_char)
  789.       break;
  790.     error("unrecognised format `%1'", char(c));
  791.     free_input_entry_format_list(list);
  792.     return 0;
  793.       }
  794.       if (got_period)
  795.     break;
  796.       c = in.get();
  797.       if (got_format)
  798.     break;
  799.     }
  800.     if (got_period)
  801.       break;
  802.     list = new input_entry_format(t, list);
  803.     if (pre_vline)
  804.       list->pre_vline = pre_vline;
  805.     int success = 1;
  806.     do {
  807.       switch (c) {
  808.       case 't':
  809.       case 'T':
  810.     c = in.get();
  811.     list->vertical_alignment = entry_modifier::TOP;
  812.     break;
  813.       case 'd':
  814.       case 'D':
  815.     c = in.get();
  816.     list->vertical_alignment = entry_modifier::BOTTOM;
  817.     break;
  818.       case 'u':
  819.       case 'U':
  820.     c = in.get();
  821.     list->stagger = 1;
  822.     break;
  823.       case 'z':
  824.       case 'Z':
  825.     c = in.get();
  826.     list->zero_width = 1;
  827.     break;
  828.       case '0':
  829.       case '1':
  830.       case '2':
  831.       case '3':
  832.       case '4':
  833.       case '5':
  834.       case '6':
  835.       case '7':
  836.       case '8':
  837.       case '9':
  838.     {
  839.       int w = 0;
  840.       do {
  841.         w = w*10 + (c - '0');
  842.         c = in.get();
  843.       } while (c != EOF && csdigit(c));
  844.       list->separation = w;
  845.     }
  846.     break;
  847.       case 'f':
  848.       case 'F':
  849.     do {
  850.       c = in.get();
  851.     } while (c == ' ' || c == '\t');
  852.     if (c == EOF) {
  853.       error("missing font name");
  854.       break;
  855.     }
  856.     if (c == '(') {
  857.       for (;;) {
  858.         c = in.get();
  859.         if (c == EOF || c == ' ' || c == '\t') {
  860.           error("missing `)'");
  861.           break;
  862.         }
  863.         if (c == ')') {
  864.           c = in.get();
  865.           break;
  866.         }
  867.         list->font += char(c);
  868.       }
  869.     }
  870.     else {
  871.       list->font = c;
  872.       char cc = c;
  873.       c = in.get();
  874.       if (!csdigit(cc)
  875.           && c != EOF && c != ' ' && c != '\t' && c != '.' && c != '\n') {
  876.         list->font += char(c);
  877.         c = in.get();
  878.       }
  879.     }
  880.     break;
  881.       case 'v':
  882.       case 'V':
  883.     c = in.get();
  884.     list->vertical_spacing.val = 0;
  885.     list->vertical_spacing.inc = 0;
  886.     if (c == '+' || c == '-') {
  887.       list->vertical_spacing.inc = (c == '+' ? 1 : -1);
  888.       c = in.get();
  889.     }
  890.     if (c == EOF || !csdigit(c)) {
  891.       error("`v' modifier must be followed by number");
  892.       list->vertical_spacing.inc = 0;
  893.     }
  894.     else {
  895.       do {
  896.         list->vertical_spacing.val *= 10;
  897.         list->vertical_spacing.val += c - '0';
  898.         c = in.get();
  899.       } while (c != EOF && csdigit(c));
  900.     }
  901.     if (list->vertical_spacing.val > MAX_VERTICAL_SPACING
  902.         || list->vertical_spacing.val < -MAX_VERTICAL_SPACING) {
  903.       error("unreasonable point size");
  904.       list->vertical_spacing.val = 0;
  905.       list->vertical_spacing.inc = 0;
  906.     }
  907.     break;
  908.       case 'p':
  909.       case 'P':
  910.     c = in.get();
  911.     list->point_size.val = 0;
  912.     list->point_size.inc = 0;
  913.     if (c == '+' || c == '-') {
  914.       list->point_size.inc = (c == '+' ? 1 : -1);
  915.       c = in.get();
  916.     }
  917.     if (c == EOF || !csdigit(c)) {
  918.       error("`p' modifier must be followed by number");
  919.       list->point_size.inc = 0;
  920.     }
  921.     else {
  922.       do {
  923.         list->point_size.val *= 10;
  924.         list->point_size.val += c - '0';
  925.         c = in.get();
  926.       } while (c != EOF && csdigit(c));
  927.     }
  928.     if (list->point_size.val > MAX_POINT_SIZE
  929.         || list->point_size.val < -MAX_POINT_SIZE) {
  930.       error("unreasonable point size");
  931.       list->point_size.val = 0;
  932.       list->point_size.inc = 0;
  933.     }
  934.     break;
  935.       case 'w':
  936.       case 'W':
  937.     c = in.get();
  938.     while (c == ' ' || c == '\t')
  939.       c = in.get();
  940.     if (c == '(') {
  941.       list->width = "";
  942.       c = in.get();
  943.       while (c != ')') {
  944.         if (c == EOF || c == '\n') {
  945.           error("missing `)'");
  946.           free_input_entry_format_list(list);
  947.           return 0;
  948.         }
  949.         list->width += c;
  950.         c = in.get();
  951.       }
  952.       c = in.get();
  953.     }
  954.     else {
  955.       if (c == '+' || c == '-') {
  956.         list->width = char(c);
  957.         c = in.get();
  958.       }
  959.       else
  960.         list->width = "";
  961.       if (c == EOF || !csdigit(c))
  962.         error("bad argument for `w' modifier");
  963.       else {
  964.         do {
  965.           list->width += char(c);
  966.           c = in.get();
  967.         } while (c != EOF && csdigit(c));
  968.       }
  969.     }
  970.     break;
  971.       case 'e':
  972.       case 'E':
  973.     c = in.get();
  974.     list->equal++;
  975.     break;
  976.       case '|':
  977.     c = in.get();
  978.     list->vline++;
  979.     break;
  980.       case 'B':
  981.       case 'b':
  982.     c = in.get();
  983.     list->font = "B";
  984.     break;
  985.       case 'I':
  986.       case 'i':
  987.     c = in.get();
  988.     list->font = "I";
  989.     break;
  990.       case ' ':
  991.       case '\t':
  992.     c = in.get();
  993.     break;
  994.       default:
  995.     if (c == opt->tab_char)
  996.       c = in.get();
  997.     else
  998.       success = 0;
  999.     break;
  1000.       }
  1001.     } while (success);
  1002.     if (list->vline > 2) {
  1003.       list->vline = 2;
  1004.       error("more than 2 vertical bars between key letters");
  1005.     }
  1006.     if (c == '\n' || c == ',') {
  1007.       c = in.get();
  1008.       list->last_column = 1;
  1009.     }
  1010.   }
  1011.   if (c == '.') {
  1012.     do {
  1013.       c = in.get();
  1014.     } while (c == ' ' || c == '\t');
  1015.     if (c != '\n') {
  1016.       error("`.' not last character on line");
  1017.       free_input_entry_format_list(list);
  1018.       return 0;
  1019.     }
  1020.   }
  1021.   if (!list) {
  1022.     error("no format");
  1023.     free_input_entry_format_list(list);
  1024.     return 0;
  1025.   }
  1026.   list->last_column = 1;
  1027.   // now reverse the list so that the first row is at the beginning
  1028.   input_entry_format *rev = 0;
  1029.   while (list != 0) {
  1030.     input_entry_format *tem = list->next;
  1031.     list->next = rev;
  1032.     rev = list;
  1033.     list = tem;
  1034.   }
  1035.   list = rev;
  1036.   input_entry_format *tem;
  1037.  
  1038. #if 0
  1039.   for (tem = list; tem; tem = tem->next)
  1040.     tem->debug_print();
  1041.   putc('\n', stderr);
  1042. #endif
  1043.   // compute number of columns and rows
  1044.   int ncolumns = 0;
  1045.   int nrows = 0;
  1046.   int col = 0;
  1047.   for (tem = list; tem; tem = tem->next) {
  1048.     if (tem->last_column) {
  1049.       if (col >= ncolumns)
  1050.     ncolumns = col + 1;
  1051.       col = 0;
  1052.       nrows++;
  1053.     }
  1054.     else
  1055.       col++;
  1056.   }
  1057.   int row;
  1058.   format *f;
  1059.   if (current_format) {
  1060.     if (ncolumns > current_format->ncolumns) {
  1061.       error("cannot increase the number of columns in a continued format");
  1062.       free_input_entry_format_list(list);
  1063.       return 0;
  1064.     }
  1065.     f = current_format;
  1066.     row = f->nrows;
  1067.     f->add_rows(nrows);
  1068.   }
  1069.   else {
  1070.     f = new format(nrows, ncolumns);
  1071.     row = 0;
  1072.   }
  1073.   col = 0;
  1074.   for (tem = list; tem; tem = tem->next) {
  1075.     f->entry[row][col] = *tem;
  1076.     if (col < ncolumns-1) {
  1077.       // use the greatest separation
  1078.       if (tem->separation > f->separation[col]) {
  1079.     if (current_format)
  1080.       error("cannot change column separation in continued format");
  1081.     else
  1082.       f->separation[col] = tem->separation;
  1083.       }
  1084.     }
  1085.     else if (tem->separation >= 0)
  1086.       error("column separation specified for last column");
  1087.     if (tem->equal && !f->equal[col]) {
  1088.       if (current_format)
  1089.     error("cannot change which columns are equal in continued format");
  1090.       else
  1091.     f->equal[col] = 1;
  1092.     }
  1093.     if (!tem->width.empty()) {
  1094.       // use the last width
  1095.       if (!f->width[col].empty() && f->width[col] != tem->width)
  1096.     error("multiple widths for column %1", col+1);
  1097.       f->width[col] = tem->width;
  1098.     }
  1099.     if (tem->pre_vline) {
  1100.       assert(col == 0);
  1101.       f->vline[row][col] = tem->pre_vline;
  1102.     }
  1103.     f->vline[row][col+1] = tem->vline;
  1104.     if (tem->last_column) {
  1105.       row++;
  1106.       col = 0;
  1107.     }
  1108.     else
  1109.       col++;
  1110.   }
  1111.   free_input_entry_format_list(list);
  1112.   for (col = 0; col < ncolumns; col++) {
  1113.     entry_format *e = f->entry[f->nrows-1] + col;
  1114.     if (e->type != FORMAT_HLINE
  1115.     && e->type != FORMAT_DOUBLE_HLINE
  1116.     && e->type != FORMAT_SPAN)
  1117.       break;
  1118.   }
  1119.   if (col >= ncolumns) {
  1120.     error("last row of format is all lines");
  1121.     delete f;
  1122.     return 0;
  1123.   }
  1124.   return f;
  1125. }
  1126.  
  1127. table *process_data(table_input &in, format *f, options *opt)
  1128. {
  1129.   char tab_char = opt->tab_char;
  1130.   int ncolumns = f->ncolumns;
  1131.   int current_row = 0;
  1132.   int format_index = 0;
  1133.   int give_up = 0;
  1134.   enum { DATA_INPUT_LINE, TROFF_INPUT_LINE, SINGLE_HLINE, DOUBLE_HLINE } type;
  1135.   table *tbl = new table(ncolumns, opt->flags, opt->linesize,
  1136.              opt->decimal_point_char);
  1137.   if (opt->delim[0] != '\0')
  1138.     tbl->set_delim(opt->delim[0], opt->delim[1]);
  1139.   for (;;) {
  1140.     // first determine what type of line this is
  1141.     int c = in.get();
  1142.     if (c == EOF)
  1143.       break;
  1144.     if (c == '.') {
  1145.       int d = in.get();
  1146.       if (d != EOF && csdigit(d)) {
  1147.     in.unget(d);
  1148.     type = DATA_INPUT_LINE;
  1149.       }
  1150.       else {
  1151.     in.unget(d);
  1152.     type = TROFF_INPUT_LINE;
  1153.       }
  1154.     }
  1155.     else if (c == '_' || c == '=') {
  1156.       int d = in.get();
  1157.       if (d == '\n') {
  1158.     if (c == '_')
  1159.       type = SINGLE_HLINE;
  1160.     else
  1161.       type = DOUBLE_HLINE;
  1162.       }
  1163.       else {
  1164.     in.unget(d);
  1165.     type = DATA_INPUT_LINE;
  1166.       }
  1167.     }
  1168.     else {
  1169.       type = DATA_INPUT_LINE;
  1170.     }
  1171.     switch (type) {
  1172.     case DATA_INPUT_LINE:
  1173.       {
  1174.     string input_entry;
  1175.     if (format_index >= f->nrows)
  1176.       format_index = f->nrows - 1;
  1177.     // A format row that is all lines doesn't use up a data line.
  1178.     while (format_index < f->nrows - 1) {
  1179.       int c;
  1180.       for (c = 0; c < ncolumns; c++) {
  1181.         entry_format *e = f->entry[format_index] + c;
  1182.         if (e->type != FORMAT_HLINE
  1183.         && e->type != FORMAT_DOUBLE_HLINE
  1184.         // Unfortunately tbl treats a span as needing data.
  1185.         // && e->type != FORMAT_SPAN
  1186.         )
  1187.           break;
  1188.       }
  1189.       if (c < ncolumns)
  1190.         break;
  1191.       for (c = 0; c < ncolumns; c++)
  1192.         tbl->add_entry(current_row, c, input_entry,
  1193.                f->entry[format_index] + c, current_filename,
  1194.                current_lineno);
  1195.       tbl->add_vlines(current_row, f->vline[format_index]);
  1196.       format_index++;
  1197.       current_row++;
  1198.     }
  1199.     entry_format *line_format = f->entry[format_index];
  1200.     int col = 0;
  1201.     int row_comment = 0;
  1202.     for (;;) {
  1203.       if (c == tab_char || c == '\n') {
  1204.         int ln = current_lineno;
  1205.         if (c == '\n')
  1206.           --ln;
  1207.         while (col < ncolumns
  1208.            && line_format[col].type == FORMAT_SPAN) {
  1209.           tbl->add_entry(current_row, col, "", &line_format[col],
  1210.                  current_filename, ln);
  1211.           col++;
  1212.         }
  1213.         if (c == '\n' && input_entry.length() == 2
  1214.         && input_entry[0] == 'T' && input_entry[1] == '{') {
  1215.           input_entry = "";
  1216.           ln++;
  1217.           enum {
  1218.         START, MIDDLE, GOT_T, GOT_RIGHT_BRACE, GOT_DOT,
  1219.         GOT_l, GOT_lf, END
  1220.           } state = START;
  1221.           while (state != END) {
  1222.         c = in.get();
  1223.         if (c == EOF)
  1224.           break;
  1225.         switch (state) {
  1226.         case START:
  1227.           if (c == 'T')
  1228.             state = GOT_T;
  1229.           else if (c == '.')
  1230.             state = GOT_DOT;
  1231.           else {
  1232.             input_entry += c;
  1233.             if (c != '\n')
  1234.               state = MIDDLE;
  1235.           }
  1236.           break;
  1237.         case GOT_T:
  1238.           if (c == '}')
  1239.             state = GOT_RIGHT_BRACE;
  1240.           else {
  1241.             input_entry += 'T';
  1242.             input_entry += c;
  1243.             state = c == '\n' ? START : MIDDLE;
  1244.           }
  1245.           break;
  1246.         case GOT_DOT:
  1247.           if (c == 'l')
  1248.             state = GOT_l;
  1249.           else {
  1250.             input_entry += '.';
  1251.             input_entry += c;
  1252.             state = c == '\n' ? START : MIDDLE;
  1253.           }
  1254.           break;
  1255.         case GOT_l:
  1256.           if (c == 'f')
  1257.             state = GOT_lf;
  1258.           else {
  1259.             input_entry += ".l";
  1260.             input_entry += c;
  1261.             state = c == '\n' ? START : MIDDLE;
  1262.           }
  1263.           break;
  1264.         case GOT_lf:
  1265.           if (c == ' ' || c == '\n' || compatible_flag) {
  1266.             string args;
  1267.             input_entry += ".lf";
  1268.             while (c != EOF) {
  1269.               args += c;
  1270.               if (c == '\n')
  1271.             break;
  1272.               c = in.get();
  1273.             }
  1274.             args += '\0';
  1275.             interpret_lf_args(args.contents());
  1276.             // remove the '\0'
  1277.             args.set_length(args.length() - 1);
  1278.             input_entry += args;
  1279.             state = START;
  1280.           }
  1281.           else {
  1282.             input_entry += ".lf";
  1283.             input_entry += c;
  1284.             state = MIDDLE;
  1285.           }
  1286.           break;
  1287.         case GOT_RIGHT_BRACE:
  1288.           if (c == '\n' || c == tab_char)
  1289.             state = END;
  1290.           else {
  1291.             input_entry += 'T';
  1292.             input_entry += '}';
  1293.             input_entry += c;
  1294.             state = c == '\n' ? START : MIDDLE;
  1295.           }
  1296.           break;
  1297.         case MIDDLE:
  1298.           if (c == '\n')
  1299.             state = START;
  1300.           input_entry += c;
  1301.           break;
  1302.         case END:
  1303.         default:
  1304.           assert(0);
  1305.         }
  1306.           }
  1307.           if (c == EOF) {
  1308.         error("end of data in middle of text block");
  1309.         give_up = 1;
  1310.         break;
  1311.           }
  1312.         }
  1313.         if (col >= ncolumns) {
  1314.           if (!input_entry.empty()) {
  1315.         if (input_entry.length() >= 2
  1316.             && input_entry[0] == '\\'
  1317.             && input_entry[1] == '"')
  1318.           row_comment = 1;
  1319.         else if (!row_comment) {
  1320.           if (c == '\n')
  1321.             in.unget(c);
  1322.           input_entry += '\0';
  1323.           error("excess data entry `%1' discarded",
  1324.             input_entry.contents());
  1325.           if (c == '\n')
  1326.             (void)in.get();
  1327.         }
  1328.           }
  1329.         }
  1330.         else
  1331.           tbl->add_entry(current_row, col, input_entry,
  1332.                  &line_format[col], current_filename, ln);
  1333.         col++;
  1334.         if (c == '\n')
  1335.           break;
  1336.         input_entry = "";
  1337.       }
  1338.       else
  1339.         input_entry += c;
  1340.       c = in.get();
  1341.       if (c == EOF)
  1342.         break;
  1343.     }
  1344.     if (give_up)
  1345.       break;
  1346.     input_entry = "";
  1347.     for (; col < ncolumns; col++)
  1348.       tbl->add_entry(current_row, col, input_entry, &line_format[col],
  1349.              current_filename, current_lineno - 1);
  1350.     tbl->add_vlines(current_row, f->vline[format_index]);
  1351.     current_row++;
  1352.     format_index++;
  1353.       }
  1354.       break;
  1355.     case TROFF_INPUT_LINE:
  1356.       {
  1357.     string line;
  1358.     int ln = current_lineno;
  1359.     for (;;) {
  1360.       line += c;
  1361.       if (c == '\n')
  1362.         break;
  1363.       c = in.get();
  1364.       if (c == EOF) {
  1365.         break;
  1366.       }
  1367.     }
  1368.     tbl->add_text_line(current_row, line, current_filename, ln);
  1369.     if (line.length() >= 4 
  1370.         && line[0] == '.' && line[1] == 'T' && line[2] == '&') {
  1371.       format *newf = process_format(in, opt, f);
  1372.       if (newf == 0)
  1373.         give_up = 1;
  1374.       else
  1375.         f = newf;
  1376.     }
  1377.     if (line.length() >= 3
  1378.         && line[0] == '.' && line[1] == 'f' && line[2] == 'f') {
  1379.       line += '\0';
  1380.       interpret_lf_args(line.contents() + 3);
  1381.     }
  1382.       }
  1383.       break;
  1384.     case SINGLE_HLINE:
  1385.       tbl->add_single_hline(current_row);
  1386.       break;
  1387.     case DOUBLE_HLINE:
  1388.       tbl->add_double_hline(current_row);
  1389.       break;
  1390.     default:
  1391.       assert(0);
  1392.     }
  1393.     if (give_up)
  1394.       break;
  1395.   }
  1396.   if (!give_up && current_row == 0) {
  1397.     error("no real data");
  1398.     give_up = 1;
  1399.   }
  1400.   if (give_up) {
  1401.     delete tbl;
  1402.     return 0;
  1403.   }
  1404.   // Do this here rather than at the beginning in case continued formats
  1405.   // change it.
  1406.   int i;
  1407.   for (i = 0; i < ncolumns - 1; i++)
  1408.     if (f->separation[i] >= 0)
  1409.       tbl->set_column_separation(i, f->separation[i]);
  1410.   for (i = 0; i < ncolumns; i++)
  1411.     if (!f->width[i].empty())
  1412.       tbl->set_minimum_width(i, f->width[i]);
  1413.   for (i = 0; i < ncolumns; i++)
  1414.     if (f->equal[i])
  1415.       tbl->set_equal_column(i);
  1416.   return tbl;
  1417. }
  1418.  
  1419. void process_table(table_input &in)
  1420. {
  1421.   int c;
  1422.   options *opt = 0;
  1423.   format *form = 0;
  1424.   table *tbl = 0;
  1425.   if ((opt = process_options(in)) != 0 
  1426.       && (form = process_format(in, opt)) != 0
  1427.       && (tbl = process_data(in, form, opt)) != 0) {
  1428.     tbl->print();
  1429.     delete tbl;
  1430.   }
  1431.   else {
  1432.     error("giving up on this table");
  1433.     while ((c = in.get()) != EOF)
  1434.       ;
  1435.   }
  1436.   delete opt;
  1437.   delete form;
  1438.   if (!in.ended())
  1439.     error("premature end of file");
  1440. }
  1441.  
  1442. static void usage()
  1443. {
  1444.   fprintf(stderr, "usage: %s [ -vC ] [ files... ]\n", program_name);
  1445.   exit(1);
  1446. }
  1447.  
  1448. int main(int argc, char **argv)
  1449. {
  1450.   program_name = argv[0];
  1451.   static char stderr_buf[BUFSIZ];
  1452.   setbuf(stderr, stderr_buf);
  1453.   int opt;
  1454.   while ((opt = getopt(argc, argv, "vCT:")) != EOF)
  1455.     switch (opt) {
  1456.     case 'C':
  1457.       compatible_flag = 1;
  1458.       break;
  1459.     case 'v':
  1460.       {
  1461.     extern const char *version_string;
  1462.     fprintf(stderr, "GNU tbl version %s\n", version_string);
  1463.     fflush(stderr);
  1464.     break;
  1465.       }
  1466.     case 'T':
  1467.       // I'm sick of getting bug reports from IRIX users
  1468.       break;
  1469.     case '?':
  1470.       usage();
  1471.       break;
  1472.     default:
  1473.       assert(0);
  1474.     }
  1475.   printf(".if !\\n(.g .ab GNU tbl requires GNU troff.\n"
  1476.      ".if !dTS .ds TS\n"
  1477.      ".if !dTE .ds TE\n");
  1478.   if (argc > optind) {
  1479.     for (int i = optind; i < argc; i++) 
  1480.       if (argv[i][0] == '-' && argv[i][1] == '\0') {
  1481.     current_filename = "-";
  1482.     current_lineno = 1;
  1483.     printf(".lf 1 -\n");
  1484.     process_input_file(stdin);
  1485.       }
  1486.       else {
  1487.     errno = 0;
  1488.     FILE *fp = fopen(argv[i], "r");
  1489.     if (fp == 0) {
  1490.       current_lineno = -1;
  1491.       error("can't open `%1': %2", argv[i], strerror(errno));
  1492.     }
  1493.     else {
  1494.       current_lineno = 1;
  1495.       current_filename = argv[i];
  1496.       printf(".lf 1 %s\n", current_filename);
  1497.       process_input_file(fp);
  1498.     }
  1499.       }
  1500.   }
  1501.   else {
  1502.     current_filename = "-";
  1503.     current_lineno = 1;
  1504.     printf(".lf 1 -\n");
  1505.     process_input_file(stdin);
  1506.   }
  1507.   if (ferror(stdout) || fflush(stdout) < 0)
  1508.     fatal("output error");
  1509.   return 0;
  1510. }
  1511.  
  1512.