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