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