LCOV - code coverage report
Current view: top level - preproc/tbl - main.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 756 1211 62.4 %
Date: 2026-01-16 17:51:41 Functions: 22 25 88.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright 1989-2025 Free Software Foundation, Inc.
       2             :      Written by James Clark (jjc@jclark.com)
       3             : 
       4             : This file is part of groff, the GNU roff typesetting system.
       5             : 
       6             : groff is free software; you can redistribute it and/or modify it under
       7             : the terms of the GNU General Public License as published by the Free
       8             : Software Foundation, either version 3 of the License, or
       9             : (at your option) any later version.
      10             : 
      11             : groff is distributed in the hope that it will be useful, but WITHOUT ANY
      12             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14             : for more details.
      15             : 
      16             : You should have received a copy of the GNU General Public License
      17             : along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      18             : 
      19             : #ifdef HAVE_CONFIG_H
      20             : #include <config.h>
      21             : #endif
      22             : 
      23             : #include <assert.h>
      24             : #include <errno.h>
      25             : #include <stdlib.h> // EXIT_SUCCESS, exit()
      26             : #include <stdio.h> // EOF, FILE, fclose(), ferror(), fflush(), fopen(),
      27             :                    // fprintf(), fputs(), getc(), printf(), putchar(),
      28             :                    // setbuf(), stderr, stdin, stdout, ungetc()
      29             : #include <string.h> // strerror()
      30             : 
      31             : #include <getopt.h> // getopt_long()
      32             : 
      33             : #include "table.h"
      34             : 
      35             : #define MAX_POINT_SIZE 99
      36             : #define MAX_VERTICAL_SPACING 72
      37             : 
      38             : extern "C" const char *Version_string;
      39             : 
      40             : int compatible_flag = 0;
      41             : 
      42             : class table_input {
      43             :   FILE *fp;
      44             :   enum { START, MIDDLE,
      45             :          REREAD_T, REREAD_TE, REREAD_E,
      46             :          LEADER_1, LEADER_2, LEADER_3, LEADER_4,
      47             :          END, ERROR } state;
      48             :   string unget_stack;
      49             : public:
      50             :   table_input(FILE *);
      51             :   int get();
      52         942 :   int ended() { return unget_stack.empty() && state == END; }
      53             :   void unget(char);
      54             : };
      55             : 
      56         471 : table_input::table_input(FILE *p)
      57         471 : : fp(p), state(START)
      58             : {
      59         471 : }
      60             : 
      61        2099 : void table_input::unget(char c)
      62             : {
      63        2099 :   assert(c != '\0');
      64        2099 :   unget_stack += c;
      65        2099 :   if (c == '\n')
      66         306 :     current_lineno--;
      67        2099 : }
      68             : 
      69      220466 : int table_input::get()
      70             : {
      71      220466 :   int len = unget_stack.length();
      72      220466 :   if (len != 0) {
      73        2099 :     unsigned char c = unget_stack[len - 1];
      74        2099 :     unget_stack.set_length(len - 1);
      75        2099 :     if (c == '\n')
      76         306 :       current_lineno++;
      77        2099 :     return c;
      78             :   }
      79             :   int c;
      80             :   for (;;) {
      81      218367 :     switch (state) {
      82        9594 :     case START:
      83        9594 :       if ((c = getc(fp)) == '.') {
      84        1568 :         if ((c = getc(fp)) == 'T') {
      85         568 :           if ((c = getc(fp)) == 'E') {
      86         471 :             if (compatible_flag) {
      87           0 :               state = END;
      88           0 :               return EOF;
      89             :             }
      90             :             else {
      91         471 :               c = getc(fp);
      92         471 :               if (c != EOF)
      93         456 :                 ungetc(c, fp);
      94         471 :               if (c == EOF || c == ' ' || c == '\n') {
      95         471 :                 state = END;
      96         471 :                 return EOF;
      97             :               }
      98           0 :               state = REREAD_TE;
      99           0 :               return '.';
     100             :             }
     101             :           }
     102             :           else {
     103          97 :             if (c != EOF)
     104          97 :               ungetc(c, fp);
     105          97 :             state = REREAD_T;
     106          97 :             return '.';
     107             :           }
     108             :         }
     109             :         else {
     110        1000 :           if (c != EOF)
     111        1000 :             ungetc(c, fp);
     112        1000 :           state = MIDDLE;
     113        1000 :           return '.';
     114             :         }
     115             :       }
     116        8026 :       else if (c == EOF) {
     117           0 :         state = ERROR;
     118           0 :         return EOF;
     119             :       }
     120             :       else {
     121        8026 :         if (c == '\n')
     122          60 :           current_lineno++;
     123             :         else {
     124        7966 :           state = MIDDLE;
     125        7966 :           if (c == '\0') {
     126           0 :             error("invalid input character code 0");
     127           0 :             break;
     128             :           }
     129             :         }
     130        8026 :         return c;
     131             :       }
     132             :       break;
     133      208676 :     case MIDDLE:
     134             :       // handle line continuation and uninterpreted leader character
     135      208676 :       if ((c = getc(fp)) == '\\') {
     136        5259 :         c = getc(fp);
     137        5259 :         if (c == '\n') {
     138          88 :           current_lineno++;
     139          88 :           c = getc(fp);
     140             :         }
     141        5171 :         else if (c == 'a' && compatible_flag) {
     142           0 :           state = LEADER_1;
     143           0 :           return '\\';
     144             :         }
     145             :         else {
     146        5171 :           if (c != EOF)
     147        5171 :             ungetc(c, fp);
     148        5171 :           c = '\\';
     149             :         }
     150             :       }
     151      208676 :       if (c == EOF) {
     152           0 :         state = ERROR;
     153           0 :         return EOF;
     154             :       }
     155             :       else {
     156      208676 :         if (c == '\n') {
     157        9063 :           state = START;
     158        9063 :           current_lineno++;
     159             :         }
     160      199613 :         else if (c == '\0') {
     161           0 :           error("invalid input character code 0");
     162           0 :           break;
     163             :         }
     164      208676 :         return c;
     165             :       }
     166          97 :     case REREAD_T:
     167          97 :       state = MIDDLE;
     168          97 :       return 'T';
     169           0 :     case REREAD_TE:
     170           0 :       state = REREAD_E;
     171           0 :       return 'T';
     172           0 :     case REREAD_E:
     173           0 :       state = MIDDLE;
     174           0 :       return 'E';
     175           0 :     case LEADER_1:
     176           0 :       state = LEADER_2;
     177           0 :       return '*';
     178           0 :     case LEADER_2:
     179           0 :       state = LEADER_3;
     180           0 :       return '(';
     181           0 :     case LEADER_3:
     182           0 :       state = LEADER_4;
     183           0 :       return PREFIX_CHAR;
     184           0 :     case LEADER_4:
     185           0 :       state = MIDDLE;
     186           0 :       return LEADER_CHAR;
     187           0 :     case END:
     188             :     case ERROR:
     189           0 :       return EOF;
     190             :     }
     191             :   }
     192             : }
     193             : 
     194             : void process_input_file(FILE *);
     195             : void process_table(table_input &in);
     196             : 
     197         134 : void process_input_file(FILE *fp)
     198             : {
     199             :   enum { START, MIDDLE, HAD_DOT, HAD_T, HAD_TS, HAD_l, HAD_lf } state;
     200         134 :   state = START;
     201             :   int c;
     202     5944745 :   while ((c = getc(fp)) != EOF)
     203     5944626 :     switch (state) {
     204      296960 :     case START:
     205      296960 :       if (c == '.')
     206      178985 :         state = HAD_DOT;
     207             :       else {
     208      117975 :         if (c == '\n')
     209         823 :           current_lineno++;
     210             :         else
     211      117152 :           state = MIDDLE;
     212      117975 :         putchar(c);
     213             :       }
     214      296960 :       break;
     215     5455713 :     case MIDDLE:
     216     5455713 :       if (c == '\n') {
     217      242116 :         current_lineno++;
     218      242116 :         state = START;
     219             :       }
     220     5455713 :       putchar(c);
     221     5455713 :       break;
     222      178985 :     case HAD_DOT:
     223      178985 :       if (c == 'T')
     224        9494 :         state = HAD_T;
     225      169491 :       else if (c == 'l')
     226        1523 :         state = HAD_l;
     227             :       else {
     228      167968 :         putchar('.');
     229      167968 :         putchar(c);
     230      167968 :         if (c == '\n') {
     231       52069 :           current_lineno++;
     232       52069 :           state = START;
     233             :         }
     234             :         else
     235      115899 :           state = MIDDLE;
     236             :       }
     237      178985 :       break;
     238        9494 :     case HAD_T:
     239        9494 :       if (c == 'S')
     240         471 :         state = HAD_TS;
     241             :       else {
     242        9023 :         putchar('.');
     243        9023 :         putchar('T');
     244        9023 :         putchar(c);
     245        9023 :         if (c == '\n') {
     246           0 :           current_lineno++;
     247           0 :           state = START;
     248             :         }
     249             :         else
     250        9023 :           state = MIDDLE;
     251             :       }
     252        9494 :       break;
     253         471 :     case HAD_TS:
     254         471 :       if (c == ' ' || c == '\n' || compatible_flag) {
     255         471 :         putchar('.');
     256         471 :         putchar('T');
     257         471 :         putchar('S');
     258         521 :         while (c != '\n') {
     259          50 :           if (c == EOF) {
     260           0 :             error("end of input encountered at beginning of table");
     261           0 :             return;
     262             :           }
     263          50 :           putchar(c);
     264          50 :           c = getc(fp);
     265             :         }
     266         471 :         putchar('\n');
     267         471 :         current_lineno++;
     268             :         {
     269         471 :           table_input input(fp);
     270         471 :           process_table(input);
     271         471 :           set_troff_location(current_filename, current_lineno);
     272         471 :           if (input.ended()) {
     273         471 :             fputs(".TE", stdout);
     274         471 :             while ((c = getc(fp)) != '\n') {
     275          15 :               if (c == EOF) {
     276          15 :                 putchar('\n');
     277          15 :                 return;
     278             :               }
     279           0 :               putchar(c);
     280             :             }
     281         456 :             putchar('\n');
     282         456 :             current_lineno++;
     283             :           }
     284             :         }
     285         456 :         state = START;
     286             :       }
     287             :       else {
     288           0 :         fputs(".TS", stdout);
     289           0 :         putchar(c);
     290           0 :         state = MIDDLE;
     291             :       }
     292         456 :       break;
     293        1523 :     case HAD_l:
     294        1523 :       if (c == 'f')
     295        1480 :         state = HAD_lf;
     296             :       else {
     297          43 :         putchar('.');
     298          43 :         putchar('l');
     299          43 :         putchar(c);
     300          43 :         if (c == '\n') {
     301           0 :           current_lineno++;
     302           0 :           state = START;
     303             :         }
     304             :         else
     305          43 :           state = MIDDLE;
     306             :       }
     307        1523 :       break;
     308        1480 :     case HAD_lf:
     309        1480 :       if (c == ' ' || c == '\n' || compatible_flag) {
     310        1480 :         string line;
     311       14961 :         while (c != EOF) {
     312       14961 :           line += c;
     313       14961 :           if (c == '\n') {
     314        1480 :             current_lineno++;
     315        1480 :             break;
     316             :           }
     317       13481 :           c = getc(fp);
     318             :         }
     319        1480 :         line += '\0';
     320        1480 :         interpret_lf_request_arguments(line.contents());
     321        1480 :         printf(".lf%s", line.contents());
     322        1480 :         state = START;
     323             :       }
     324             :       else {
     325           0 :         fputs(".lf", stdout);
     326           0 :         putchar(c);
     327           0 :         state = MIDDLE;
     328             :       }
     329        1480 :       break;
     330           0 :     default:
     331           0 :       assert(0 == "invalid `state` in switch");
     332             :     }
     333         119 :   switch(state) {
     334         118 :   case START:
     335         118 :     break;
     336           1 :   case MIDDLE:
     337           1 :     putchar('\n');
     338           1 :     break;
     339           0 :   case HAD_DOT:
     340           0 :     fputs(".\n", stdout);
     341           0 :     break;
     342           0 :   case HAD_l:
     343           0 :     fputs(".l\n", stdout);
     344           0 :     break;
     345           0 :   case HAD_T:
     346           0 :     fputs(".T\n", stdout);
     347           0 :     break;
     348           0 :   case HAD_lf:
     349           0 :     fputs(".lf\n", stdout);
     350           0 :     break;
     351           0 :   case HAD_TS:
     352           0 :     fputs(".TS\n", stdout);
     353           0 :     break;
     354             :   }
     355         119 :   if (fp != stdin)
     356           0 :     fclose(fp);
     357             : }
     358             : 
     359             : struct options {
     360             :   unsigned flags;
     361             :   int linesize;
     362             :   char delim[2];
     363             :   char tab_char;
     364             :   char decimal_point_char;
     365             : 
     366             :   options();
     367             : };
     368             : 
     369         471 : options::options()
     370         471 : : flags(0), linesize(0), tab_char('\t'), decimal_point_char('.')
     371             : {
     372         471 :   delim[0] = delim[1] = '\0';
     373         471 : }
     374             : 
     375             : // Return non-zero if p and q are the same ignoring case.
     376             : 
     377        3693 : int strieq(const char *p, const char *q)
     378             : {
     379        3693 :   for (; cmlower(*p) == cmlower(*q); p++, q++)
     380        2298 :     if (*p == '\0')
     381         444 :       return 1;
     382        1395 :   return 0;
     383             : }
     384             : 
     385             : // Handle region options.  Return a null pointer if we should give up on
     386             : // this table.
     387         471 : options *process_options(table_input &in)
     388             : {
     389         471 :   options *opt = new options;
     390         942 :   string line;
     391         471 :   int level = 0;
     392             :   for (;;) {
     393        4565 :     int c = in.get();
     394        4565 :     if (c == EOF) {
     395           0 :       int i = line.length();
     396           0 :       while (--i >= 0)
     397           0 :         in.unget(line[i]);
     398           0 :       return opt;
     399             :     }
     400        4565 :     if (c == '\n') {
     401         186 :       in.unget(c);
     402         186 :       int i = line.length();
     403        1699 :       while (--i >= 0)
     404        1513 :         in.unget(line[i]);
     405         186 :       return opt;
     406             :     }
     407        4379 :     else if (c == '(')
     408         207 :       level++;
     409        4172 :     else if (c == ')')
     410         207 :       level--;
     411        3965 :     else if (c == ';' && 0 == level) {
     412         285 :       line += '\0';
     413         285 :       break;
     414             :     }
     415        4094 :     line += c;
     416        4094 :   }
     417         285 :   if (line.empty())
     418           0 :     return opt;
     419         285 :   char *p = &line[0];
     420             :   for (;;) {
     421         767 :     while (!csalpha(*p) && *p != '\0')
     422          38 :       p++;
     423         729 :     if (*p == '\0')
     424         285 :       break;
     425         444 :     char *q = p;
     426        2270 :     while (csalpha(*q))
     427        1826 :       q++;
     428         444 :     char *arg = 0;
     429         444 :     if (*q != '(' && *q != '\0')
     430         141 :       *q++ = '\0';
     431         465 :     while (csspace(*q))
     432          21 :       q++;
     433         444 :     if (*q == '(') {
     434         185 :       *q++ = '\0';
     435         185 :       arg = q;
     436         370 :       while (*q != ')' && *q != '\0')
     437         185 :         q++;
     438         185 :       if (*q == '\0')
     439           0 :         error("'%1' region option argument missing closing parenthesis",
     440           0 :               arg);
     441             :       else
     442         185 :         *q++ = '\0';
     443             :     }
     444         444 :     if (*p == '\0') {
     445           0 :       if (arg)
     446           0 :         error("'%1' region option argument cannot be empty", arg);
     447             :     }
     448         444 :     else if (strieq(p, "tab")) {
     449         179 :       if (!arg)
     450           0 :         error("'tab' region option requires argument in parentheses");
     451             :       else {
     452         179 :         if (arg[0] == '\0' || arg[1] != '\0')
     453           0 :           error("'tab' region option argument must be a single"
     454             :                 " character");
     455             :         else
     456         179 :           opt->tab_char = arg[0];
     457             :       }
     458             :     }
     459         265 :     else if (strieq(p, "linesize")) {
     460           6 :       if (!arg)
     461           0 :         error("'linesize' region option requires argument in"
     462             :               " parentheses");
     463             :       else {
     464           6 :         if (sscanf(arg, "%d", &opt->linesize) != 1)
     465           0 :           error("invalid argument to 'linesize' region option: '%1'",
     466           0 :                 arg);
     467           6 :         else if (opt->linesize <= 0) {
     468           0 :           error("'linesize' region option argument must be positive");
     469           0 :           opt->linesize = 0;
     470             :         }
     471             :       }
     472             :     }
     473         259 :     else if (strieq(p, "delim")) {
     474           0 :       if (!arg)
     475           0 :         error("'delim' region option requires argument in parentheses");
     476           0 :       else if (arg[0] == '\0' || arg[1] == '\0' || arg[2] != '\0')
     477           0 :         error("argument to 'delim' option must be two characters");
     478             :       else {
     479           0 :         opt->delim[0] = arg[0];
     480           0 :         opt->delim[1] = arg[1];
     481             :       }
     482             :     }
     483         259 :     else if (strieq(p, "center") || strieq(p, "centre")) {
     484         105 :       if (arg)
     485           0 :         error("'center' region option does not take an argument");
     486         105 :       opt->flags |= table::CENTER;
     487             :     }
     488         154 :     else if (strieq(p, "expand")) {
     489          13 :       if (arg)
     490           0 :         error("'expand' region option does not take an argument");
     491          13 :       opt->flags |= table::EXPAND;
     492          13 :       opt->flags |= table::GAP_EXPAND;
     493             :     }
     494         141 :     else if (strieq(p, "box") || strieq(p, "frame")) {
     495         107 :       if (arg)
     496           0 :         error("'box' region option does not take an argument");
     497         107 :       opt->flags |= table::BOX;
     498             :     }
     499          34 :     else if (strieq(p, "doublebox") || strieq(p, "doubleframe")) {
     500           2 :       if (arg)
     501           0 :         error("'doublebox' region option does not take an argument");
     502           2 :       opt->flags |= table::DOUBLEBOX;
     503             :     }
     504          32 :     else if (strieq(p, "allbox")) {
     505          14 :       if (arg)
     506           0 :         error("'allbox' region option does not take an argument");
     507          14 :       opt->flags |= table::ALLBOX;
     508             :     }
     509          18 :     else if (strieq(p, "nokeep")) {
     510          11 :       if (arg)
     511           0 :         error("'nokeep' region option does not take an argument");
     512          11 :       opt->flags |= table::NOKEEP;
     513             :     }
     514           7 :     else if (strieq(p, "nospaces")) {
     515           1 :       if (arg)
     516           0 :         error("'nospaces' region option does not take an argument");
     517           1 :       opt->flags |= table::NOSPACES;
     518             :     }
     519           6 :     else if (strieq(p, "nowarn")) {
     520           6 :       if (arg)
     521           0 :         error("'nowarn' region option does not take an argument");
     522           6 :       opt->flags |= table::NOWARN;
     523             :     }
     524           0 :     else if (strieq(p, "decimalpoint")) {
     525           0 :       if (!arg)
     526           0 :         error("'decimalpoint' region option requires argument in"
     527             :               " parentheses");
     528             :       else {
     529           0 :         if (arg[0] == '\0' || arg[1] != '\0')
     530           0 :           error("'decimalpoint' region option argument must be a single"
     531             :                 " character");
     532             :         else
     533           0 :           opt->decimal_point_char = arg[0];
     534             :       }
     535             :     }
     536           0 :     else if (strieq(p, "experimental")) {
     537           0 :       opt->flags |= table::EXPERIMENTAL;
     538             :     }
     539             :     else {
     540           0 :       error("unrecognized region option '%1'", p);
     541             :       // delete opt;
     542             :       // return 0;
     543             :     }
     544         444 :     p = q;
     545         444 :   }
     546         285 :   return opt;
     547             : }
     548             : 
     549        4592 : entry_modifier::entry_modifier()
     550        4592 : : vertical_alignment(CENTER), zero_width(0), stagger(0)
     551             : {
     552        4592 :   vertical_spacing.relativity = size_expression::ABSOLUTE;
     553        4592 :   vertical_spacing.whole = 0;
     554        4592 :   type_size.relativity = size_expression::ABSOLUTE;
     555        4592 :   type_size.whole = 0;
     556        4592 : }
     557             : 
     558        4592 : entry_modifier::~entry_modifier()
     559             : {
     560        4592 : }
     561             : 
     562        2296 : entry_format::entry_format() : type(FORMAT_LEFT)
     563             : {
     564        2296 : }
     565             : 
     566        2296 : entry_format::entry_format(format_type t) : type(t)
     567             : {
     568        2296 : }
     569             : 
     570           0 : void entry_format::debug_print() const
     571             : {
     572           0 :   switch (type) {
     573           0 :   case FORMAT_LEFT:
     574           0 :     putc('l', stderr);
     575           0 :     break;
     576           0 :   case FORMAT_CENTER:
     577           0 :     putc('c', stderr);
     578           0 :     break;
     579           0 :   case FORMAT_RIGHT:
     580           0 :     putc('r', stderr);
     581           0 :     break;
     582           0 :   case FORMAT_NUMERIC:
     583           0 :     putc('n', stderr);
     584           0 :     break;
     585           0 :   case FORMAT_ALPHABETIC:
     586           0 :     putc('a', stderr);
     587           0 :     break;
     588           0 :   case FORMAT_SPAN:
     589           0 :     putc('s', stderr);
     590           0 :     break;
     591           0 :   case FORMAT_VSPAN:
     592           0 :     putc('^', stderr);
     593           0 :     break;
     594           0 :   case FORMAT_HRULE:
     595           0 :     putc('_', stderr);
     596           0 :     break;
     597           0 :   case FORMAT_DOUBLE_HRULE:
     598           0 :     putc('=', stderr);
     599           0 :     break;
     600           0 :   default:
     601           0 :     assert(0 == "invalid column classifier in switch");
     602             :     break;
     603             :   }
     604           0 :   if (type_size.whole != 0) {
     605           0 :     putc('p', stderr);
     606           0 :     if (type_size.relativity == size_expression::INCREMENT)
     607           0 :       putc('+', stderr);
     608           0 :     else if (type_size.relativity == size_expression::DECREMENT)
     609           0 :       putc('-', stderr);
     610           0 :     fprintf(stderr, "%d ", type_size.whole);
     611             :   }
     612           0 :   if (vertical_spacing.whole != 0) {
     613           0 :     putc('v', stderr);
     614           0 :     if (vertical_spacing.relativity == size_expression::INCREMENT)
     615           0 :       putc('+', stderr);
     616           0 :     else if (vertical_spacing.relativity == size_expression::DECREMENT)
     617           0 :       putc('-', stderr);
     618           0 :     fprintf(stderr, "%d ", vertical_spacing.whole);
     619             :   }
     620           0 :   if (!font.empty()) {
     621           0 :     putc('f', stderr);
     622           0 :     put_string(font, stderr);
     623           0 :     putc(' ', stderr);
     624             :   }
     625           0 :   if (!macro.empty()) {
     626           0 :     putc('m', stderr);
     627           0 :     put_string(macro, stderr);
     628           0 :     putc(' ', stderr);
     629             :   }
     630           0 :   switch (vertical_alignment) {
     631           0 :   case entry_modifier::CENTER:
     632           0 :     break;
     633           0 :   case entry_modifier::TOP:
     634           0 :     putc('t', stderr);
     635           0 :     break;
     636           0 :   case entry_modifier::BOTTOM:
     637           0 :     putc('d', stderr);
     638           0 :     break;
     639             :   }
     640           0 :   if (zero_width)
     641           0 :     putc('z', stderr);
     642           0 :   if (stagger)
     643           0 :     putc('u', stderr);
     644           0 : }
     645             : 
     646             : struct format {
     647             :   int nrows;
     648             :   int ncolumns;
     649             :   int *separation;
     650             :   string *width;
     651             :   char *equal;
     652             :   char *expand;
     653             :   entry_format **entry;
     654             :   char **vrule;
     655             : 
     656             :   format(int nr, int nc);
     657             :   ~format();
     658             :   void add_rows(int n);
     659             : };
     660             : 
     661         471 : format::format(int nr, int nc) : nrows(nr), ncolumns(nc)
     662             : {
     663             :   int i;
     664         471 :   separation = ncolumns > 1 ? new int[ncolumns - 1] : 0;
     665        1239 :   for (i = 0; i < ncolumns-1; i++)
     666         768 :     separation[i] = -1;
     667        1710 :   width = new string[ncolumns];
     668         471 :   equal = new char[ncolumns];
     669         471 :   expand = new char[ncolumns];
     670        1710 :   for (i = 0; i < ncolumns; i++) {
     671        1239 :     equal[i] = 0;
     672        1239 :     expand[i] = 0;
     673             :   }
     674         471 :   entry = new entry_format *[nrows];
     675        1210 :   for (i = 0; i < nrows; i++)
     676        2706 :     entry[i] = new entry_format[ncolumns];
     677         471 :   vrule = new char*[nrows];
     678        1210 :   for (i = 0; i < nrows; i++) {
     679         739 :     vrule[i] = new char[ncolumns+1];
     680        3445 :     for (int j = 0; j < ncolumns+1; j++)
     681        2706 :       vrule[i][j] = 0;
     682             :   }
     683         471 : }
     684             : 
     685          81 : void format::add_rows(int n)
     686             : {
     687             :   int i;
     688          81 :   char **old_vrule = vrule;
     689          81 :   vrule = new char*[nrows + n];
     690         242 :   for (i = 0; i < nrows; i++)
     691         161 :     vrule[i] = old_vrule[i];
     692          81 :   delete[] old_vrule;
     693         173 :   for (i = 0; i < n; i++) {
     694          92 :     vrule[nrows + i] = new char[ncolumns + 1];
     695         513 :     for (int j = 0; j < ncolumns + 1; j++)
     696         421 :       vrule[nrows + i][j] = 0;
     697             :   }
     698          81 :   entry_format **old_entry = entry;
     699          81 :   entry = new entry_format *[nrows + n];
     700         242 :   for (i = 0; i < nrows; i++)
     701         161 :     entry[i] = old_entry[i];
     702          81 :   delete[] old_entry;
     703         173 :   for (i = 0; i < n; i++)
     704         421 :     entry[nrows + i] = new entry_format[ncolumns];
     705          81 :   nrows += n;
     706          81 : }
     707             : 
     708         942 : format::~format()
     709             : {
     710         471 :   delete[] separation;
     711        1710 :   delete[] width;
     712         471 :   delete[] equal;
     713         471 :   delete[] expand;
     714        1302 :   for (int i = 0; i < nrows; i++) {
     715         831 :     delete[] vrule[i];
     716        3127 :     delete[] entry[i];
     717             :   }
     718         471 :   delete[] vrule;
     719         471 :   delete[] entry;
     720         471 : }
     721             : 
     722             : struct input_entry_format : public entry_format {
     723             :   input_entry_format *next;
     724             :   string width;
     725             :   int separation;
     726             :   int vrule;
     727             :   int vrule_count;
     728             :   bool is_last_column;
     729             :   bool is_equal_width;
     730             :   int expand;
     731             :   input_entry_format(format_type, input_entry_format * = 0);
     732             :   ~input_entry_format();
     733             :   void debug_print();
     734             : };
     735             : 
     736        2296 : input_entry_format::input_entry_format(format_type t, input_entry_format *p)
     737        2296 : : entry_format(t), next(p)
     738             : {
     739        2296 :   separation = -1;
     740        2296 :   is_last_column = false;
     741        2296 :   vrule = 0;
     742        2296 :   vrule_count = 0;
     743        2296 :   is_equal_width = false;
     744        2296 :   expand = 0;
     745        2296 : }
     746             : 
     747        2296 : input_entry_format::~input_entry_format()
     748             : {
     749        2296 : }
     750             : 
     751        2848 : void free_input_entry_format_list(input_entry_format *list)
     752             : {
     753        2848 :   while (list) {
     754        2296 :     input_entry_format *tem = list;
     755        2296 :     list = list->next;
     756        2296 :     delete tem;
     757             :   }
     758         552 : }
     759             : 
     760           0 : void input_entry_format::debug_print()
     761             : {
     762             :   int i;
     763           0 :   for (i = 0; i < vrule_count; i++)
     764           0 :     putc('|', stderr);
     765           0 :   entry_format::debug_print();
     766           0 :   if (!width.empty()) {
     767           0 :     putc('w', stderr);
     768           0 :     putc('(', stderr);
     769           0 :     put_string(width, stderr);
     770           0 :     putc(')', stderr);
     771             :   }
     772           0 :   if (is_equal_width)
     773           0 :     putc('e', stderr);
     774           0 :   if (expand)
     775           0 :     putc('x', stderr);
     776           0 :   if (separation >= 0)
     777           0 :     fprintf(stderr, "%d", separation);
     778           0 :   for (i = 0; i < vrule; i++)
     779           0 :     putc('|', stderr);
     780           0 :   if (is_last_column)
     781           0 :     putc(',', stderr);
     782           0 : }
     783             : 
     784             : // Interpret a table format specification, like "CC,LR.".  Return null
     785             : // pointer if we should give up on this table.  If this is a
     786             : // continuation format line, `current_format` will be the current format
     787             : // line.
     788         552 : format *process_format(table_input &in, options *opt,
     789             :                        format *current_format = 0)
     790             : {
     791         552 :   input_entry_format *list = 0 /* nullptr */;
     792         552 :   bool have_expand = false;
     793         552 :   bool is_first_row = true;
     794         552 :   int c = in.get();
     795             :   for (;;) {
     796        2848 :     int vrule_count = 0;
     797        2848 :     bool got_format = false;
     798        2848 :     bool got_period = false;
     799        2848 :     format_type t = FORMAT_LEFT;
     800             :     for (;;) {
     801        3160 :       if (c == EOF) {
     802           0 :         error("end of input while processing table format"
     803             :               " specification");
     804           0 :         free_input_entry_format_list(list);
     805           0 :         list = 0 /* nullptr */;
     806           0 :         return 0 /* nullptr */;
     807             :       }
     808        3160 :       switch (c) {
     809          37 :       case 'n':
     810             :       case 'N':
     811          37 :         t = FORMAT_NUMERIC;
     812          37 :         got_format = true;
     813          37 :         break;
     814          14 :       case 'a':
     815             :       case 'A':
     816          14 :         got_format = true;
     817          14 :         t = FORMAT_ALPHABETIC;
     818          14 :         break;
     819         394 :       case 'c':
     820             :       case 'C':
     821         394 :         got_format = true;
     822         394 :         t = FORMAT_CENTER;
     823         394 :         break;
     824        1666 :       case 'l':
     825             :       case 'L':
     826        1666 :         got_format = true;
     827        1666 :         t = FORMAT_LEFT;
     828        1666 :         break;
     829          51 :       case 'r':
     830             :       case 'R':
     831          51 :         got_format = true;
     832          51 :         t = FORMAT_RIGHT;
     833          51 :         break;
     834         123 :       case 's':
     835             :       case 'S':
     836         123 :         got_format = true;
     837         123 :         t = FORMAT_SPAN;
     838         123 :         break;
     839           7 :       case '^':
     840           7 :         got_format = true;
     841           7 :         t = FORMAT_VSPAN;
     842           7 :         break;
     843           4 :       case '_':
     844             :       case '-':                 // tbl also accepts this
     845           4 :         got_format = true;
     846           4 :         t = FORMAT_HRULE;
     847           4 :         if (is_first_row)
     848           2 :           opt->flags |= table::HAS_TOP_HRULE;
     849           4 :         break;
     850           0 :       case '=':
     851           0 :         got_format = true;
     852           0 :         t = FORMAT_DOUBLE_HRULE;
     853           0 :         break;
     854         552 :       case '.':
     855         552 :         got_period = true;
     856         552 :         break;
     857          12 :       case '|':
     858             :         // leading vertical line in row
     859          12 :         opt->flags |= table::HAS_TOP_VRULE;
     860          12 :         vrule_count++;
     861             :         // list->vrule_count is updated later
     862          12 :         break;
     863         300 :       case ' ':
     864             :       case '\t':
     865             :       case '\n':
     866         300 :         break;
     867           0 :       default:
     868           0 :         if (c == opt->tab_char)
     869           0 :           break;
     870           0 :         error("invalid column classifier '%1'", char(c));
     871           0 :         free_input_entry_format_list(list);
     872           0 :         list = 0 /* nullptr */;
     873           0 :         return 0 /* nullptr */;
     874             :       }
     875        3160 :       if (got_period)
     876         552 :         break;
     877        2608 :       c = in.get();
     878        2608 :       if (got_format)
     879        2296 :         break;
     880             :     }
     881        2848 :     if (got_period)
     882         552 :       break;
     883        2296 :     list = new input_entry_format(t, list);
     884        2296 :     if (vrule_count > 2) {
     885           0 :       vrule_count = 2;
     886           0 :       warning("ignoring excess vertical lines at beginning of row"
     887             :               " description");
     888             :     }
     889        2296 :     list->vrule_count = vrule_count;
     890             :     // Now handle modifiers.
     891        2296 :     vrule_count = 0;
     892        2296 :     bool is_valid_modifier_sequence = true;
     893        5541 :     do {
     894        5541 :       switch (c) {
     895          50 :       case '0':
     896             :       case '1':
     897             :       case '2':
     898             :       case '3':
     899             :       case '4':
     900             :       case '5':
     901             :       case '6':
     902             :       case '7':
     903             :       case '8':
     904             :       case '9':
     905             :         {
     906          50 :           int w = 0;
     907           0 :           do {
     908          50 :             w = w*10 + (c - '0');
     909          50 :             c = in.get();
     910          50 :           } while (c != EOF && csdigit(c));
     911          50 :           list->separation = w;
     912             :         }
     913          50 :         break;
     914         521 :       case 'B':
     915             :       case 'b':
     916         521 :         c = in.get();
     917         521 :         list->font = "B";
     918         521 :         break;
     919           3 :       case 'd':
     920             :       case 'D':
     921           3 :         c = in.get();
     922           3 :         list->vertical_alignment = entry_modifier::BOTTOM;
     923           3 :         break;
     924           0 :       case 'e':
     925             :       case 'E':
     926           0 :         c = in.get();
     927           0 :         list->is_equal_width = true;
     928             :         // 'e' and 'x' are mutually exclusive
     929           0 :         list->expand = 0;
     930           0 :         break;
     931         338 :       case 'f':
     932             :       case 'F':
     933           0 :         do {
     934         338 :           c = in.get();
     935         338 :         } while (c == ' ' || c == '\t');
     936         338 :         if (c == EOF) {
     937           0 :           error("'f' column modifier missing font name or mounting"
     938             :                 " position");
     939           0 :           break;
     940             :         }
     941         338 :         if (c == '(') {
     942             :           for (;;) {
     943         915 :             c = in.get();
     944         915 :             if (c == EOF || c == ' ' || c == '\t') {
     945           0 :               error("'f' column modifier missing closing parenthesis");
     946           0 :               break;
     947             :             }
     948         915 :             if (c == ')') {
     949         309 :               c = in.get();
     950         309 :               break;
     951             :             }
     952         606 :             list->font += char(c);
     953             :           }
     954             :         }
     955             :         else {
     956          29 :           list->font = c;
     957          29 :           char cc = c;
     958          29 :           c = in.get();
     959          29 :           if (!csdigit(cc)
     960          29 :               && c != EOF && c != ' ' && c != '\t' && c != '.' && c != '\n') {
     961          26 :             list->font += char(c);
     962          26 :             c = in.get();
     963             :           }
     964             :         }
     965         338 :         break;
     966          47 :       case 'I':
     967             :       case 'i':
     968          47 :         c = in.get();
     969          47 :         list->font = "I";
     970          47 :         break;
     971           0 :       case 'm':
     972             :       case 'M':
     973           0 :         do {
     974           0 :           c = in.get();
     975           0 :         } while (c == ' ' || c == '\t');
     976           0 :         if (c == EOF) {
     977           0 :           error("'m' column modifier missing macro name");
     978           0 :           break;
     979             :         }
     980           0 :         if (c == '(') {
     981             :           for (;;) {
     982           0 :             c = in.get();
     983           0 :             if (c == EOF || c == ' ' || c == '\t') {
     984           0 :               error("'m' column modifier missing closing parenthesis");
     985           0 :               break;
     986             :             }
     987           0 :             if (c == ')') {
     988           0 :               c = in.get();
     989           0 :               break;
     990             :             }
     991           0 :             list->macro += char(c);
     992             :           }
     993             :         }
     994             :         else {
     995           0 :           list->macro = c;
     996           0 :           char cc = c;
     997           0 :           c = in.get();
     998           0 :           if (!csdigit(cc)
     999           0 :               && c != EOF && c != ' ' && c != '\t' && c != '.' && c != '\n') {
    1000           0 :             list->macro += char(c);
    1001           0 :             c = in.get();
    1002             :           }
    1003             :         }
    1004           0 :         break;
    1005          24 :       case 'p':
    1006             :       case 'P':
    1007             :         {
    1008          24 :           size_expression &ps = list->type_size;
    1009          24 :           ps.relativity = size_expression::ABSOLUTE;
    1010          24 :           ps.whole = 0;
    1011           0 :           do {
    1012          24 :             c = in.get();
    1013          24 :           } while (c == ' ' || c == '\t');
    1014          24 :           if (c == EOF) {
    1015           0 :             error("'p' column modifier missing type size parameter");
    1016           0 :             break;
    1017             :           }
    1018          24 :           if (c == '+' || c == '-') {
    1019          24 :             if (c == '+')
    1020           0 :               ps.relativity = size_expression::INCREMENT;
    1021          24 :             else if (c == '-')
    1022          24 :               ps.relativity = size_expression::DECREMENT;
    1023          24 :             c = in.get();
    1024             :           }
    1025          24 :           if (c == EOF || !csdigit(c)) {
    1026           0 :             warning("'p' column modifier must be followed by"
    1027             :                     " (optionally signed) integer; ignoring");
    1028           0 :             ps.relativity = size_expression::ABSOLUTE;
    1029             :           }
    1030             :           else {
    1031           0 :             do {
    1032          24 :               ps.whole *= 10;
    1033          24 :               ps.whole += c - '0';
    1034          24 :               c = in.get();
    1035          24 :             } while (c != EOF && csdigit(c));
    1036             :           }
    1037          24 :           if (ps.whole > MAX_POINT_SIZE || ps.whole < -MAX_POINT_SIZE) {
    1038           0 :             warning("'p' column modifier argument magnitude of %1"
    1039             :                     " points out of range (> %2); ignoring", ps.whole,
    1040           0 :                     MAX_POINT_SIZE);
    1041           0 :             ps.whole = 0;
    1042           0 :             ps.relativity = size_expression::ABSOLUTE;
    1043             :           }
    1044          24 :           break;
    1045             :         }
    1046           3 :       case 't':
    1047             :       case 'T':
    1048           3 :         c = in.get();
    1049           3 :         list->vertical_alignment = entry_modifier::TOP;
    1050           3 :         break;
    1051           9 :       case 'u':
    1052             :       case 'U':
    1053           9 :         c = in.get();
    1054           9 :         list->stagger = 1;
    1055           9 :         break;
    1056           0 :       case 'v':
    1057             :       case 'V':
    1058             :         {
    1059           0 :           size_expression &vs = list->vertical_spacing;
    1060           0 :           vs.whole = 0;
    1061           0 :           vs.relativity = size_expression::ABSOLUTE;
    1062           0 :           do {
    1063           0 :             c = in.get();
    1064           0 :           } while (c == ' ' || c == '\t');
    1065           0 :           if (c == EOF) {
    1066           0 :             error("'v' column modifier missing vertical spacing"
    1067             :                   " parameter");
    1068           0 :             break;
    1069             :           }
    1070           0 :           if (c == '+' || c == '-') {
    1071           0 :             if (c == '+')
    1072           0 :               vs.relativity = size_expression::INCREMENT;
    1073           0 :             else if (c == '-')
    1074           0 :               vs.relativity = size_expression::DECREMENT;
    1075           0 :             c = in.get();
    1076             :           }
    1077           0 :           if (c == EOF || !csdigit(c)) {
    1078           0 :             warning("'v' column modifier must be followed by"
    1079             :                     " (optionally signed) integer; ignoring");
    1080           0 :             vs.whole = 0;
    1081           0 :             vs.relativity = size_expression::ABSOLUTE;
    1082             :           }
    1083             :           else {
    1084           0 :             do {
    1085           0 :               vs.whole *= 10;
    1086           0 :               vs.whole += c - '0';
    1087           0 :               c = in.get();
    1088           0 :             } while (c != EOF && csdigit(c));
    1089             :           }
    1090           0 :           if (vs.whole > MAX_VERTICAL_SPACING
    1091           0 :               || vs.whole < -MAX_VERTICAL_SPACING) {
    1092           0 :             warning("'v' column modifier argument magnitude of %1"
    1093             :                     " points out of range (> %2); ignoring", vs.whole,
    1094           0 :                     MAX_VERTICAL_SPACING);
    1095           0 :             vs.whole = 0;
    1096           0 :             vs.relativity = size_expression::ABSOLUTE;
    1097             :           }
    1098           0 :           break;
    1099             :         }
    1100          10 :       case 'w':
    1101             :       case 'W':
    1102           0 :         do {
    1103          10 :           c = in.get();
    1104          10 :         } while (c == ' ' || c == '\t');
    1105          10 :         if (c == EOF) {
    1106           0 :           error("'w' column modifier missing width parameter");
    1107           0 :           break;
    1108             :         }
    1109          10 :         if (c == '(') {
    1110          10 :           list->width = "";
    1111          10 :           c = in.get();
    1112          36 :           while (c != ')') {
    1113          26 :             if (c == EOF || c == '\n') {
    1114           0 :               error("'w' column modifier missing closing parenthesis");
    1115           0 :               free_input_entry_format_list(list);
    1116           0 :               list = 0 /* nullptr */;
    1117           0 :               return 0 /* nullptr */;
    1118             :             }
    1119          26 :             list->width += c;
    1120          26 :             c = in.get();
    1121             :           }
    1122          10 :           c = in.get();
    1123             :         }
    1124             :         else {
    1125           0 :           if (c == '+' || c == '-') {
    1126           0 :             list->width = char(c);
    1127           0 :             c = in.get();
    1128             :           }
    1129             :           else
    1130           0 :             list->width = "";
    1131           0 :           if (c == EOF || !csdigit(c))
    1132           0 :             error("invalid argument to 'w' modifier");
    1133             :           else {
    1134           0 :             do {
    1135           0 :               list->width += char(c);
    1136           0 :               c = in.get();
    1137           0 :             } while (c != EOF && csdigit(c));
    1138             :           }
    1139             :         }
    1140             :         // 'w' and 'x' are mutually exclusive
    1141          10 :         list->expand = 0;
    1142          10 :         break;
    1143         252 :       case 'x':
    1144             :       case 'X':
    1145         252 :         c = in.get();
    1146         252 :         list->expand = 1;
    1147             :         // 'x' and 'e' are mutually exclusive
    1148         252 :         list->is_equal_width = false;
    1149             :         // 'x' and 'w' are mutually exclusive
    1150         252 :         list->width = "";
    1151         252 :         break;
    1152           6 :       case 'z':
    1153             :       case 'Z':
    1154           6 :         c = in.get();
    1155           6 :         list->zero_width = 1;
    1156           6 :         break;
    1157          91 :       case '|':
    1158          91 :         if (is_first_row)
    1159          55 :           opt->flags |= table::HAS_TOP_VRULE;
    1160          91 :         c = in.get();
    1161          91 :         vrule_count++;
    1162          91 :         break;
    1163        1891 :       case ' ':
    1164             :       case '\t':
    1165        1891 :         c = in.get();
    1166        1891 :         break;
    1167        2296 :       default:
    1168        2296 :         if (c == opt->tab_char)
    1169           0 :           c = in.get();
    1170             :         else
    1171        2296 :           is_valid_modifier_sequence = false;
    1172        2296 :         break;
    1173             :       }
    1174             :     } while (is_valid_modifier_sequence);
    1175        2296 :     if (vrule_count > 2) {
    1176           0 :       vrule_count = 2;
    1177           0 :       warning("ignoring excess vertical lines after column descriptor");
    1178             :     }
    1179        2296 :     list->vrule += vrule_count;
    1180        2296 :     if (c == '\n' || c == ',') {
    1181         282 :       vrule_count = 0;
    1182         282 :       is_first_row = false;
    1183         282 :       c = in.get();
    1184         282 :       list->is_last_column = true;
    1185             :     }
    1186        2296 :   }
    1187         552 :   if (c == '.') {
    1188           0 :     do {
    1189         552 :       c = in.get();
    1190         552 :     } while (c == ' ' || c == '\t');
    1191         552 :     if (c != '\n') {
    1192           0 :       error("'.' is not the last character of the table format");
    1193           0 :       free_input_entry_format_list(list);
    1194           0 :       list = 0 /* nullptr */;
    1195           0 :       return 0 /* nullptr */;
    1196             :     }
    1197             :   }
    1198         552 :   if (!list) {
    1199           0 :     error("table format specification is empty");
    1200           0 :     free_input_entry_format_list(list);
    1201           0 :     list = 0 /* nullptr */;
    1202           0 :     return 0 /* nullptr */;
    1203             :   }
    1204         552 :   list->is_last_column = true;
    1205             :   // now reverse the list so that the first row is at the beginning
    1206         552 :   input_entry_format *rev = 0;
    1207        2848 :   while (list != 0) {
    1208        2296 :     input_entry_format *tem = list->next;
    1209        2296 :     list->next = rev;
    1210        2296 :     rev = list;
    1211        2296 :     list = tem;
    1212             :   }
    1213         552 :   list = rev;
    1214             :   input_entry_format *tem;
    1215             : 
    1216             : #if 0
    1217             :   for (tem = list; tem; tem = tem->next)
    1218             :     tem->debug_print();
    1219             :   putc('\n', stderr);
    1220             : #endif
    1221             :   // compute number of columns and rows
    1222         552 :   int ncolumns = 0;
    1223         552 :   int nrows = 0;
    1224         552 :   int col = 0;
    1225        2848 :   for (tem = list; tem; tem = tem->next) {
    1226        2296 :     if (tem->is_last_column) {
    1227         831 :       if (col >= ncolumns)
    1228         552 :         ncolumns = col + 1;
    1229         831 :       col = 0;
    1230         831 :       nrows++;
    1231             :     }
    1232             :     else
    1233        1465 :       col++;
    1234             :   }
    1235             :   int row;
    1236             :   format *f;
    1237         552 :   if (current_format) {
    1238          81 :     if (ncolumns > current_format->ncolumns) {
    1239           0 :       error("cannot increase the number of columns in a continued format");
    1240           0 :       free_input_entry_format_list(list);
    1241           0 :       list = 0 /* nullptr */;
    1242           0 :       return 0 /* nullptr */;
    1243             :     }
    1244          81 :     f = current_format;
    1245          81 :     row = f->nrows;
    1246          81 :     f->add_rows(nrows);
    1247             :   }
    1248             :   else {
    1249         471 :     f = new format(nrows, ncolumns);
    1250         471 :     row = 0;
    1251             :   }
    1252         552 :   col = 0;
    1253        2848 :   for (tem = list; tem; tem = tem->next) {
    1254        2296 :     f->entry[row][col] = *tem;
    1255        2296 :     if (col < ncolumns - 1) {
    1256             :       // use the greatest separation
    1257        1465 :       if (tem->separation > f->separation[col]) {
    1258          44 :         if (current_format)
    1259           0 :           error("cannot change column separation in continued format");
    1260             :         else
    1261          44 :           f->separation[col] = tem->separation;
    1262             :       }
    1263             :     }
    1264         831 :     else if (tem->separation >= 0)
    1265           0 :       error("column separation specified for last column");
    1266        2296 :     if (tem->is_equal_width && !f->equal[col]) {
    1267           0 :       if (current_format)
    1268           0 :         error("cannot change which columns are equal in continued format");
    1269             :       else
    1270           0 :         f->equal[col] = 1;
    1271             :     }
    1272        2296 :     if (tem->expand && !f->expand[col]) {
    1273         181 :       if (current_format)
    1274           0 :         error("cannot change which columns are expanded in continued format");
    1275             :       else {
    1276         181 :         f->expand[col] = 1;
    1277         181 :         have_expand = true;
    1278             :       }
    1279             :     }
    1280        2296 :     if (!tem->width.empty()) {
    1281             :       // use the last width
    1282          10 :       if (!f->width[col].empty() && f->width[col] != tem->width)
    1283           0 :         error("multiple widths designated for column %1", col + 1);
    1284          10 :       f->width[col] = tem->width;
    1285             :     }
    1286        2296 :     if (tem->vrule_count)
    1287          12 :       f->vrule[row][col] = tem->vrule_count;
    1288        2296 :     f->vrule[row][col + 1] = tem->vrule;
    1289        2296 :     if (tem->is_last_column) {
    1290         831 :       row++;
    1291         831 :       col = 0;
    1292             :     }
    1293             :     else
    1294        1465 :       col++;
    1295             :   }
    1296         552 :   free_input_entry_format_list(list);
    1297         552 :   list = 0 /* nullptr */;
    1298         552 :   for (col = 0; col < ncolumns; col++) {
    1299         552 :     entry_format *e = f->entry[f->nrows - 1] + col;
    1300         552 :     if (e->type != FORMAT_HRULE
    1301         552 :         && e->type != FORMAT_DOUBLE_HRULE
    1302         552 :         && e->type != FORMAT_SPAN)
    1303         552 :       break;
    1304             :   }
    1305         552 :   if (col >= ncolumns) {
    1306           0 :     error("last row of format is all lines");
    1307           0 :     delete f;
    1308           0 :     return 0 /* nullptr */;
    1309             :   }
    1310         552 :   if (have_expand && (opt->flags & table::EXPAND)) {
    1311           0 :     error("'x' column modifier encountered; ignoring region option"
    1312             :           " 'expand'");
    1313           0 :     opt->flags &= ~table::EXPAND;
    1314             :   }
    1315         552 :   return f;
    1316             : }
    1317             : 
    1318         471 : table *process_data(table_input &in, format *f, options *opt)
    1319             : {
    1320         471 :   char tab_char = opt->tab_char;
    1321         471 :   int ncolumns = f->ncolumns;
    1322         471 :   int current_row = 0;
    1323         471 :   int format_index = 0;
    1324         471 :   bool give_up = false;
    1325             :   enum { DATA_INPUT_LINE, TROFF_INPUT_LINE, SINGLE_HRULE, DOUBLE_HRULE } type;
    1326             :   table *tbl = new table(ncolumns, opt->flags, opt->linesize,
    1327         471 :                          opt->decimal_point_char);
    1328         471 :   if (opt->delim[0] != '\0')
    1329           0 :     tbl->set_delim(opt->delim[0], opt->delim[1]);
    1330             :   for (;;) {
    1331             :     // first determine what type of line this is
    1332        6044 :     int c = in.get();
    1333        6044 :     if (c == EOF)
    1334         471 :       break;
    1335        5573 :     if (c == '.') {
    1336         397 :       int d = in.get();
    1337         397 :       if (d != EOF && csdigit(d)) {
    1338           0 :         in.unget(d);
    1339           0 :         type = DATA_INPUT_LINE;
    1340             :       }
    1341             :       else {
    1342         397 :         in.unget(d);
    1343         397 :         type = TROFF_INPUT_LINE;
    1344             :       }
    1345             :     }
    1346        5176 :     else if (c == '_' || c == '=') {
    1347         333 :       int d = in.get();
    1348         333 :       if (d == '\n') {
    1349         330 :         if (c == '_')
    1350         326 :           type = SINGLE_HRULE;
    1351             :         else
    1352           4 :           type = DOUBLE_HRULE;
    1353         330 :         if (0 == current_row)
    1354           8 :           tbl->flags |= table::HAS_TOP_HRULE;
    1355         330 :         tbl->flags |= table::HAS_DATA_HRULE;
    1356             :       }
    1357             :       else {
    1358           3 :         in.unget(d);
    1359           3 :         type = DATA_INPUT_LINE;
    1360         333 :       }
    1361             :     }
    1362             :     else {
    1363        4843 :       type = DATA_INPUT_LINE;
    1364             :     }
    1365        5573 :     switch (type) {
    1366        4846 :     case DATA_INPUT_LINE:
    1367             :       {
    1368        4846 :         string input_entry;
    1369        4846 :         if (format_index >= f->nrows)
    1370        4019 :           format_index = f->nrows - 1;
    1371             :         // A format row that is all lines doesn't use up a data line.
    1372        4850 :         while (format_index < f->nrows - 1) {
    1373             :           int cnt;
    1374         283 :           for (cnt = 0; cnt < ncolumns; cnt++) {
    1375         279 :             entry_format *e = f->entry[format_index] + cnt;
    1376         279 :             if (e->type != FORMAT_HRULE
    1377         275 :                 && e->type != FORMAT_DOUBLE_HRULE
    1378             :                 // Unfortunately tbl treats a span as needing data.
    1379             :                 // && e->type != FORMAT_SPAN
    1380             :                 )
    1381         275 :               break;
    1382             :           }
    1383         279 :           if (cnt < ncolumns)
    1384         275 :             break;
    1385           8 :           for (cnt = 0; cnt < ncolumns; cnt++)
    1386           4 :             tbl->add_entry(current_row, cnt, input_entry,
    1387           4 :                            f->entry[format_index] + cnt, current_filename,
    1388             :                            current_lineno);
    1389           4 :           tbl->add_vrules(current_row, f->vrule[format_index]);
    1390           4 :           format_index++;
    1391           4 :           current_row++;
    1392             :         }
    1393        4846 :         entry_format *line_format = f->entry[format_index];
    1394        4846 :         int col = 0;
    1395        4846 :         bool seen_row_comment = false;
    1396             :         for (;;) {
    1397      157994 :           if (c == tab_char || c == '\n') {
    1398       14277 :             int ln = current_lineno;
    1399       14277 :             if (c == '\n')
    1400        4885 :               --ln;
    1401       14277 :             if ((opt->flags & table::NOSPACES))
    1402          66 :               input_entry.remove_spaces();
    1403           5 :             while (col < ncolumns
    1404       14282 :                    && line_format[col].type == FORMAT_SPAN) {
    1405           5 :               tbl->add_entry(current_row, col, "", &line_format[col],
    1406          10 :                              current_filename, ln);
    1407           5 :               col++;
    1408             :             }
    1409        4885 :             if (c == '\n' && input_entry.length() == 2
    1410       19162 :                 && input_entry[0] == 'T' && input_entry[1] == '{') {
    1411         369 :               input_entry = "";
    1412         369 :               ln++;
    1413             :               enum {
    1414             :                 START, MIDDLE, GOT_T, GOT_RIGHT_BRACE, GOT_DOT,
    1415             :                 GOT_l, GOT_lf, END
    1416         369 :               } state = START;
    1417       43650 :               while (state != END) {
    1418       43281 :                 c = in.get();
    1419       43281 :                 if (c == EOF)
    1420           0 :                   break;
    1421       43281 :                 switch (state) {
    1422        2450 :                 case START:
    1423        2450 :                   if (c == 'T')
    1424         439 :                     state = GOT_T;
    1425        2011 :                   else if (c == '.')
    1426         697 :                     state = GOT_DOT;
    1427             :                   else {
    1428        1314 :                     input_entry += c;
    1429        1314 :                     if (c != '\n')
    1430        1314 :                       state = MIDDLE;
    1431             :                   }
    1432        2450 :                   break;
    1433         439 :                 case GOT_T:
    1434         439 :                   if (c == '}')
    1435         369 :                     state = GOT_RIGHT_BRACE;
    1436             :                   else {
    1437          70 :                     input_entry += 'T';
    1438          70 :                     input_entry += c;
    1439          70 :                     state = c == '\n' ? START : MIDDLE;
    1440             :                   }
    1441         439 :                   break;
    1442         697 :                 case GOT_DOT:
    1443         697 :                   if (c == 'l')
    1444           0 :                     state = GOT_l;
    1445             :                   else {
    1446         697 :                     input_entry += '.';
    1447         697 :                     input_entry += c;
    1448         697 :                     state = c == '\n' ? START : MIDDLE;
    1449             :                   }
    1450         697 :                   break;
    1451           0 :                 case GOT_l:
    1452           0 :                   if (c == 'f')
    1453           0 :                     state = GOT_lf;
    1454             :                   else {
    1455           0 :                     input_entry += ".l";
    1456           0 :                     input_entry += c;
    1457           0 :                     state = c == '\n' ? START : MIDDLE;
    1458             :                   }
    1459           0 :                   break;
    1460           0 :                 case GOT_lf:
    1461           0 :                   if (c == ' ' || c == '\n' || compatible_flag) {
    1462           0 :                     string args;
    1463           0 :                     input_entry += ".lf";
    1464           0 :                     while (c != EOF) {
    1465           0 :                       args += c;
    1466           0 :                       if (c == '\n')
    1467           0 :                         break;
    1468           0 :                       c = in.get();
    1469             :                     }
    1470           0 :                     args += '\0';
    1471           0 :                     interpret_lf_request_arguments(args.contents());
    1472             :                     // remove the '\0'
    1473           0 :                     args.set_length(args.length() - 1);
    1474           0 :                     input_entry += args;
    1475           0 :                     state = START;
    1476             :                   }
    1477             :                   else {
    1478           0 :                     input_entry += ".lf";
    1479           0 :                     input_entry += c;
    1480           0 :                     state = MIDDLE;
    1481             :                   }
    1482           0 :                   break;
    1483         369 :                 case GOT_RIGHT_BRACE:
    1484         369 :                   if ((opt->flags & table::NOSPACES)) {
    1485           0 :                     while (c == ' ')
    1486           0 :                       c = in.get();
    1487           0 :                     if (c == EOF)
    1488           0 :                       break;
    1489             :                   }
    1490         369 :                   if (c == '\n' || c == tab_char)
    1491         369 :                     state = END;
    1492             :                   else {
    1493           0 :                     input_entry += 'T';
    1494           0 :                     input_entry += '}';
    1495           0 :                     input_entry += c;
    1496           0 :                     state = MIDDLE;
    1497             :                   }
    1498         369 :                   break;
    1499       39326 :                 case MIDDLE:
    1500       39326 :                   if (c == '\n')
    1501        1940 :                     state = START;
    1502       39326 :                   input_entry += c;
    1503       39326 :                   break;
    1504           0 :                 case END:
    1505             :                 default:
    1506           0 :                   assert(0 == "invalid `state` in switch");
    1507             :                 }
    1508             :               }
    1509         369 :               if (c == EOF) {
    1510           0 :                 error("end of input in middle of text block");
    1511           0 :                 give_up = true;
    1512           0 :                 break;
    1513             :               }
    1514             :             }
    1515       14277 :             if (col >= ncolumns) {
    1516           0 :               if (!input_entry.empty()) {
    1517           0 :                 if (input_entry.length() >= 2
    1518           0 :                     && input_entry[0] == '\\'
    1519           0 :                     && input_entry[1] == '"')
    1520           0 :                   seen_row_comment = true;
    1521           0 :                 else if (!seen_row_comment) {
    1522           0 :                   if (c == '\n')
    1523           0 :                     in.unget(c);
    1524           0 :                   input_entry += '\0';
    1525           0 :                   error("excess table entry '%1' discarded",
    1526           0 :                         input_entry.contents());
    1527           0 :                   if (c == '\n')
    1528           0 :                     (void)in.get();
    1529             :                 }
    1530             :               }
    1531             :             }
    1532             :             else
    1533       14277 :               tbl->add_entry(current_row, col, input_entry,
    1534       14277 :                              &line_format[col], current_filename, ln);
    1535       14277 :             col++;
    1536       14277 :             if (c == '\n')
    1537        4846 :               break;
    1538        9431 :             input_entry = "";
    1539             :           }
    1540             :           else
    1541      143717 :             input_entry += c;
    1542      153148 :           c = in.get();
    1543      153148 :           if (c == EOF)
    1544           0 :             break;
    1545      153148 :         }
    1546        4846 :         if (give_up)
    1547           0 :           break;
    1548        4846 :         input_entry = "";
    1549        5166 :         for (; col < ncolumns; col++)
    1550         320 :           tbl->add_entry(current_row, col, input_entry, &line_format[col],
    1551             :                          current_filename, current_lineno - 1);
    1552        4846 :         tbl->add_vrules(current_row, f->vrule[format_index]);
    1553        4846 :         current_row++;
    1554        4846 :         format_index++;
    1555             :       }
    1556        4846 :       break;
    1557         397 :     case TROFF_INPUT_LINE:
    1558             :       {
    1559         794 :         string line;
    1560         397 :         int ln = current_lineno;
    1561             :         for (;;) {
    1562        4483 :           line += c;
    1563        4483 :           if (c == '\n')
    1564         397 :             break;
    1565        4086 :           c = in.get();
    1566        4086 :           if (c == EOF) {
    1567           0 :             break;
    1568             :           }
    1569             :         }
    1570         397 :         tbl->add_text_line(current_row, line, current_filename, ln);
    1571         397 :         if (line.length() >= 4
    1572         397 :             && line[0] == '.' && line[1] == 'T' && line[2] == '&') {
    1573          81 :           format *newf = process_format(in, opt, f);
    1574          81 :           if (newf == 0)
    1575           0 :             give_up = true;
    1576             :           else
    1577          81 :             f = newf;
    1578             :         }
    1579         397 :         if (line.length() >= 3
    1580         397 :             && line[0] == '.' && line[1] == 'l' && line[2] == 'f') {
    1581           0 :           line += '\0';
    1582           0 :           interpret_lf_request_arguments(line.contents() + 3);
    1583             :         }
    1584             :       }
    1585         397 :       break;
    1586         326 :     case SINGLE_HRULE:
    1587         326 :       tbl->add_single_hrule(current_row);
    1588         326 :       break;
    1589           4 :     case DOUBLE_HRULE:
    1590           4 :       tbl->add_double_hrule(current_row);
    1591           4 :       break;
    1592           0 :     default:
    1593           0 :       assert(0 == "invalid `type` in switch");
    1594             :     }
    1595        5573 :     if (give_up)
    1596           0 :       break;
    1597        5573 :   }
    1598         471 :   if (!give_up && current_row == 0) {
    1599           0 :     error("no data in table");
    1600           0 :     give_up = true;
    1601             :   }
    1602         471 :   if (give_up) {
    1603           0 :     delete tbl;
    1604           0 :     return 0;
    1605             :   }
    1606             :   // Do this here rather than at the beginning in case continued formats
    1607             :   // change it.
    1608             :   int i;
    1609        1239 :   for (i = 0; i < ncolumns - 1; i++)
    1610         768 :     if (f->separation[i] >= 0)
    1611          44 :       tbl->set_column_separation(i, f->separation[i]);
    1612        1710 :   for (i = 0; i < ncolumns; i++)
    1613        1239 :     if (!f->width[i].empty())
    1614          10 :       tbl->set_minimum_width(i, f->width[i]);
    1615        1710 :   for (i = 0; i < ncolumns; i++)
    1616        1239 :     if (f->equal[i])
    1617           0 :       tbl->set_equal_column(i);
    1618        1710 :   for (i = 0; i < ncolumns; i++)
    1619        1239 :     if (f->expand[i])
    1620         181 :       tbl->set_expand_column(i);
    1621         471 :   return tbl;
    1622             : }
    1623             : 
    1624         471 : void process_table(table_input &in)
    1625             : {
    1626         471 :   options *opt = 0 /* nullptr */;
    1627         471 :   format *fmt = 0 /* nullptr */;
    1628         471 :   table *tbl = 0 /* nullptr */;
    1629         471 :   if ((opt = process_options(in)) != 0 /* nullptr */
    1630         471 :       && (fmt = process_format(in, opt)) != 0 /* nullptr */
    1631         942 :       && (tbl = process_data(in, fmt, opt)) != 0 /* nullptr */) {
    1632         471 :     tbl->print();
    1633         471 :     delete tbl;
    1634             :   }
    1635             :   else {
    1636           0 :     error("giving up on this table region");
    1637           0 :     while (in.get() != EOF)
    1638             :       ;
    1639             :   }
    1640         471 :   delete opt;
    1641         471 :   delete fmt;
    1642         471 :   if (!in.ended())
    1643           0 :     error("unexpected end of input");
    1644         471 : }
    1645             : 
    1646           0 : static void usage(FILE *stream)
    1647             : {
    1648           0 :   fprintf(stream,
    1649             : "usage: %s [-C] [file ...]\n"
    1650             : "usage: %s {-v | --version}\n"
    1651             : "usage: %s --help\n",
    1652             :          program_name, program_name, program_name);
    1653           0 :   if (stdout == stream)
    1654           0 :     fputs("\n"
    1655             : "GNU tbl is a filter that translates descriptions of tables embedded\n"
    1656             : "in roff(7) input into the language understood by GNU troff(1).  See\n"
    1657             : "the tbl(1) manual page.\n",
    1658             :           stream);
    1659           0 : }
    1660             : 
    1661         134 : int main(int argc, char **argv)
    1662             : {
    1663         134 :   program_name = argv[0];
    1664             :   static char stderr_buf[BUFSIZ];
    1665         134 :   setbuf(stderr, stderr_buf);
    1666             :   int opt;
    1667             :   static const struct option long_options[] = {
    1668             :     { "help", no_argument, 0 /* nullptr */, CHAR_MAX + 1 },
    1669             :     { "version", no_argument, 0 /* nullptr */, 'v' },
    1670             :     { 0 /* nullptr */, 0, 0 /* nullptr */, 0 }
    1671             :   };
    1672         134 :   while ((opt = getopt_long(argc, argv, ":vC", long_options,
    1673             :                             0 /* nullptr */))
    1674         134 :          != EOF)
    1675           0 :     switch (opt) {
    1676           0 :     case 'C':
    1677           0 :       compatible_flag = 1;
    1678           0 :       break;
    1679           0 :     case 'v':
    1680             :       {
    1681           0 :         printf("GNU tbl (groff) version %s\n", Version_string);
    1682           0 :         exit(EXIT_SUCCESS);
    1683             :         break;
    1684             :       }
    1685           0 :     case CHAR_MAX + 1: // --help
    1686           0 :       usage(stdout);
    1687           0 :       exit(EXIT_SUCCESS);
    1688             :       break;
    1689           0 :     case '?':
    1690           0 :       if (optopt != 0)
    1691           0 :         error("unrecognized command-line option '%1'", char(optopt));
    1692             :       else
    1693           0 :         error("unrecognized command-line option '%1'",
    1694           0 :               argv[(optind - 1)]);
    1695           0 :       usage(stderr);
    1696           0 :       exit(2);
    1697             :       break;
    1698             :     // in case we ever accept options that take arguments
    1699           0 :     case ':':
    1700           0 :       error("command-line option '%1' requires an argument",
    1701           0 :            char(optopt));
    1702           0 :       usage(stderr);
    1703           0 :       exit(2);
    1704             :       break;
    1705           0 :     default:
    1706           0 :       assert(0 == "unhandled getopt_long return value");
    1707             :     }
    1708         134 :   printf(".if !\\n(.g .ab GNU tbl requires groff extensions; aborting\n"
    1709             :          ".do if !dTS .ds TS\n"
    1710             :          ".do if !dT& .ds T&\n"
    1711             :          ".do if !dTE .ds TE\n");
    1712         134 :   if (argc > optind) {
    1713           0 :     for (int i = optind; i < argc; i++)
    1714           0 :       if (argv[i][0] == '-' && argv[i][1] == '\0') {
    1715           0 :         current_lineno = 1;
    1716           0 :         current_filename = "-";
    1717           0 :         (void) printf(".lf %d %s%s\n", current_lineno,
    1718           0 :                       ('"' == current_filename[0]) ? "" : "\"",
    1719             :                       current_filename);
    1720           0 :         process_input_file(stdin);
    1721             :       }
    1722             :       else {
    1723           0 :         errno = 0;
    1724           0 :         FILE *fp = fopen(argv[i], "r");
    1725           0 :         if (fp == 0) {
    1726           0 :           current_filename = 0 /* nullptr */;
    1727           0 :           fatal("cannot open '%1': %2", argv[i], strerror(errno));
    1728             :         }
    1729             :         else {
    1730           0 :           string fn(argv[i]);
    1731           0 :           fn += '\0';
    1732           0 :           normalize_file_name_for_lf_request(fn);
    1733           0 :           current_lineno = 1;
    1734           0 :           current_filename = fn.contents();
    1735           0 :           (void) printf(".lf %d %s%s\n", current_lineno,
    1736           0 :                         ('"' == current_filename[0]) ? "" : "\"",
    1737             :                         current_filename);
    1738           0 :           process_input_file(fp);
    1739             :         }
    1740             :       }
    1741             :   }
    1742             :   else {
    1743         134 :     current_lineno = 1;
    1744         134 :     current_filename = "-";
    1745         134 :     (void) printf(".lf %d %s%s\n", current_lineno,
    1746         134 :                   ('"' == current_filename[0]) ? "" : "\"",
    1747             :                   current_filename);
    1748         134 :     process_input_file(stdin);
    1749             :   }
    1750         134 :   if (ferror(stdout))
    1751           0 :     fatal("error status on standard output stream");
    1752         134 :   if (fflush(stdout) < 0)
    1753           0 :     fatal("cannot flush standard output stream: %1", strerror(errno));
    1754         134 :   return 0;
    1755             : }
    1756             : 
    1757             : // Local Variables:
    1758             : // fill-column: 72
    1759             : // mode: C++
    1760             : // End:
    1761             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14