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