2 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * Copyright (C) 1989 - 1992, 2000 - 2005, 2007, 2008
5 * Free Software Foundation, Inc.
6 * Written by James Clark (jjc@jclark.com)
8 * This is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2, or (at your option) any later
13 * This is distributed in the hope that it will be useful, but WITHOUT ANY
14 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 * You should have received a copy of the GNU General Public License along
19 * with groff; see the file COPYING. If not, write to the Free Software
20 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "tbl-config.h"
26 #include "file_case.h"
30 int compatible_flag
= 0;
36 REREAD_T
, REREAD_TE
, REREAD_E
,
37 LEADER_1
, LEADER_2
, LEADER_3
, LEADER_4
,
42 table_input(file_case
*);
44 int ended() { return unget_stack
.empty() && state
== END
; }
48 table_input::table_input(file_case
*fcp
)
49 : _fcp(fcp
), state(START
)
53 void table_input::unget(char c
)
61 int table_input::get()
63 int len
= unget_stack
.length();
65 unsigned char c
= unget_stack
[len
- 1];
66 unget_stack
.set_length(len
- 1);
75 if ((c
= _fcp
->get_c()) == '.') {
76 if ((c
= _fcp
->get_c()) == 'T') {
77 if ((c
= _fcp
->get_c()) == 'E') {
78 if (compatible_flag
) {
86 if (c
== EOF
|| c
== ' ' || c
== '\n') {
118 error("invalid input character code 0");
126 // handle line continuation and uninterpreted leader character
127 if ((c
= _fcp
->get_c()) == '\\') {
130 c
= _fcp
->get_c(); // perhaps state ought to be START now
131 else if (c
== 'a' && compatible_flag
) {
150 else if (c
== '\0') {
151 error("invalid input character code 0");
184 void process_input_file(file_case
*);
185 void process_table(table_input
&in
);
187 void process_input_file(file_case
*fcp
)
189 enum { START
, MIDDLE
, HAD_DOT
, HAD_T
, HAD_TS
, HAD_l
, HAD_lf
} state
;
192 while ((c
= fcp
->get_c()) != EOF
)
244 if (c
== ' ' || c
== '\n' || compatible_flag
) {
250 error("end of file at beginning of table");
259 table_input
input(fcp
);
260 process_table(input
);
261 set_troff_location(current_filename
, current_lineno
);
263 fputs(".TE", stdout
);
264 while ((c
= fcp
->get_c()) != '\n') {
278 fputs(".TS", stdout
);
299 if (c
== ' ' || c
== '\n' || compatible_flag
) {
310 interpret_lf_args(line
.contents());
311 printf(".lf%s", line
.contents());
315 fputs(".lf", stdout
);
330 fputs(".\n", stdout
);
333 fputs(".l\n", stdout
);
336 fputs(".T\n", stdout
);
339 fputs(".lf\n", stdout
);
342 fputs(".TS\n", stdout
);
354 char decimal_point_char
;
360 : flags(0), linesize(0), tab_char('\t'), decimal_point_char('.')
362 delim
[0] = delim
[1] = '\0';
365 // Return non-zero if p and q are the same ignoring case.
367 int strieq(const char *p
, const char *q
)
369 for (; cmlower(*p
) == cmlower(*q
); p
++, q
++)
375 // return 0 if we should give up in this table
377 options
*process_options(table_input
&in
)
379 options
*opt
= new options
;
385 int i
= line
.length();
392 int i
= line
.length();
401 else if (c
== ';' && level
== 0) {
411 while (!csalpha(*p
) && *p
!= '\0')
419 if (*q
!= '(' && *q
!= '\0')
426 while (*q
!= ')' && *q
!= '\0')
429 error("missing `)'");
435 error("argument without option");
437 else if (strieq(p
, "tab")) {
439 error("`tab' option requires argument in parentheses");
441 if (arg
[0] == '\0' || arg
[1] != '\0')
442 error("argument to `tab' option must be a single character");
444 opt
->tab_char
= arg
[0];
447 else if (strieq(p
, "linesize")) {
449 error("`linesize' option requires argument in parentheses");
451 if (sscanf(arg
, "%d", &opt
->linesize
) != 1)
452 error("bad linesize `%s'", arg
);
453 else if (opt
->linesize
<= 0) {
454 error("linesize must be positive");
459 else if (strieq(p
, "delim")) {
461 error("`delim' option requires argument in parentheses");
462 else if (arg
[0] == '\0' || arg
[1] == '\0' || arg
[2] != '\0')
463 error("argument to `delim' option must be two characters");
465 opt
->delim
[0] = arg
[0];
466 opt
->delim
[1] = arg
[1];
469 else if (strieq(p
, "center") || strieq(p
, "centre")) {
471 error("`center' option does not take an argument");
472 opt
->flags
|= table::CENTER
;
474 else if (strieq(p
, "expand")) {
476 error("`expand' option does not take an argument");
477 opt
->flags
|= table::EXPAND
;
479 else if (strieq(p
, "box") || strieq(p
, "frame")) {
481 error("`box' option does not take an argument");
482 opt
->flags
|= table::BOX
;
484 else if (strieq(p
, "doublebox") || strieq(p
, "doubleframe")) {
486 error("`doublebox' option does not take an argument");
487 opt
->flags
|= table::DOUBLEBOX
;
489 else if (strieq(p
, "allbox")) {
491 error("`allbox' option does not take an argument");
492 opt
->flags
|= table::ALLBOX
;
494 else if (strieq(p
, "nokeep")) {
496 error("`nokeep' option does not take an argument");
497 opt
->flags
|= table::NOKEEP
;
499 else if (strieq(p
, "nospaces")) {
501 error("`nospaces' option does not take an argument");
502 opt
->flags
|= table::NOSPACES
;
503 } else if (strieq(p
, "nowarn")) { // TODO if(arg)goto jerr; anywhere here
505 error("`nowarn' option does not take an argument");
506 opt
->flags
|= table::NOWARN
;
508 else if (strieq(p
, "decimalpoint")) {
510 error("`decimalpoint' option requires argument in parentheses");
512 if (arg
[0] == '\0' || arg
[1] != '\0')
513 error("argument to `decimalpoint' option must be a single character");
515 opt
->decimal_point_char
= arg
[0];
518 else if (strieq(p
, "experimental")) {
519 opt
->flags
|= table::EXPERIMENTAL
;
522 error("unrecognised global option `%1'", p
);
531 entry_modifier::entry_modifier()
532 : vertical_alignment(CENTER
), zero_width(0), stagger(0)
534 vertical_spacing
.inc
= vertical_spacing
.val
= 0;
535 point_size
.inc
= point_size
.val
= 0;
538 entry_modifier::~entry_modifier()
542 entry_format::entry_format() : type(FORMAT_LEFT
)
546 entry_format::entry_format(format_type t
) : type(t
)
550 void entry_format::debug_print() const
565 case FORMAT_ALPHABETIC
:
577 case FORMAT_DOUBLE_HLINE
:
584 if (point_size
.val
!= 0) {
586 if (point_size
.inc
> 0)
588 else if (point_size
.inc
< 0)
590 fprintf(stderr
, "%d ", point_size
.val
);
592 if (vertical_spacing
.val
!= 0) {
594 if (vertical_spacing
.inc
> 0)
596 else if (vertical_spacing
.inc
< 0)
598 fprintf(stderr
, "%d ", vertical_spacing
.val
);
602 put_string(font
, stderr
);
605 if (!macro
.empty()) {
607 put_string(macro
, stderr
);
610 switch (vertical_alignment
) {
611 case entry_modifier::CENTER
:
613 case entry_modifier::TOP
:
616 case entry_modifier::BOTTOM
:
635 entry_format
**entry
;
638 format(int nr
, int nc
);
640 void add_rows(int n
);
643 format::format(int nr
, int nc
) : nrows(nr
), ncolumns(nc
)
646 separation
= ncolumns
> 1 ? new int[ncolumns
- 1] : 0;
647 for (i
= 0; i
< ncolumns
-1; i
++)
649 width
= new string
[ncolumns
];
650 equal
= new char[ncolumns
];
651 expand
= new char[ncolumns
];
652 for (i
= 0; i
< ncolumns
; i
++) {
656 entry
= new entry_format
*[nrows
];
657 for (i
= 0; i
< nrows
; i
++)
658 entry
[i
] = new entry_format
[ncolumns
];
659 vline
= new char*[nrows
];
660 for (i
= 0; i
< nrows
; i
++) {
661 vline
[i
] = new char[ncolumns
+1];
662 for (int j
= 0; j
< ncolumns
+1; j
++)
667 void format::add_rows(int n
)
670 char **old_vline
= vline
;
671 vline
= new char*[nrows
+ n
];
672 for (i
= 0; i
< nrows
; i
++)
673 vline
[i
] = old_vline
[i
];
675 for (i
= 0; i
< n
; i
++) {
676 vline
[nrows
+ i
] = new char[ncolumns
+ 1];
677 for (int j
= 0; j
< ncolumns
+ 1; j
++)
678 vline
[nrows
+ i
][j
] = 0;
680 entry_format
**old_entry
= entry
;
681 entry
= new entry_format
*[nrows
+ n
];
682 for (i
= 0; i
< nrows
; i
++)
683 entry
[i
] = old_entry
[i
];
685 for (i
= 0; i
< n
; i
++)
686 entry
[nrows
+ i
] = new entry_format
[ncolumns
];
693 ad_delete(ncolumns
) width
;
696 for (int i
= 0; i
< nrows
; i
++) {
698 ad_delete(ncolumns
) entry
[i
];
704 class input_entry_format
705 : public entry_format
708 input_entry_format
*next
;
717 input_entry_format(format_type
, input_entry_format
* = 0);
718 ~input_entry_format();
722 input_entry_format::input_entry_format(format_type t
, input_entry_format
*p
)
723 : entry_format(t
), next(p
)
733 input_entry_format::~input_entry_format()
737 void free_input_entry_format_list(input_entry_format
*list
)
740 input_entry_format
*tem
= list
;
746 void input_entry_format::debug_print()
749 for (i
= 0; i
< pre_vline
; i
++)
751 entry_format::debug_print();
752 if (!width
.empty()) {
755 put_string(width
, stderr
);
763 fprintf(stderr
, "%d", separation
);
764 for (i
= 0; i
< vline
; i
++)
770 // Return zero if we should give up on this table.
771 // If this is a continuation format line, current_format will be the current
774 format
*process_format(table_input
&in
, options
*opt
,
775 format
*current_format
= 0)
777 input_entry_format
*list
= 0;
784 format_type t
= FORMAT_LEFT
;
787 error("end of input while processing format");
788 free_input_entry_format_list(list
);
800 t
= FORMAT_ALPHABETIC
;
827 case '-': // tbl also accepts this
833 t
= FORMAT_DOUBLE_HLINE
;
846 if (c
== opt
->tab_char
)
848 error("unrecognised format `%1'", char(c
));
849 free_input_entry_format_list(list
);
860 list
= new input_entry_format(t
, list
);
862 list
->pre_vline
= pre_vline
;
879 w
= w
*10 + (c
- '0');
881 } while (c
!= EOF
&& csdigit(c
));
882 list
->separation
= w
;
893 list
->vertical_alignment
= entry_modifier::BOTTOM
;
899 // `e' and `x' are mutually exclusive
906 } while (c
== ' ' || c
== '\t');
908 error("missing font name");
914 if (c
== EOF
|| c
== ' ' || c
== '\t') {
915 error("missing `)'");
922 list
->font
+= char(c
);
930 && c
!= EOF
&& c
!= ' ' && c
!= '\t' && c
!= '.' && c
!= '\n') {
931 list
->font
+= char(c
);
945 } while (c
== ' ' || c
== '\t');
947 error("missing macro name");
953 if (c
== EOF
|| c
== ' ' || c
== '\t') {
954 error("missing `)'");
961 list
->macro
+= char(c
);
969 && c
!= EOF
&& c
!= ' ' && c
!= '\t' && c
!= '.' && c
!= '\n') {
970 list
->macro
+= char(c
);
978 list
->point_size
.val
= 0;
979 list
->point_size
.inc
= 0;
980 if (c
== '+' || c
== '-') {
981 list
->point_size
.inc
= (c
== '+' ? 1 : -1);
984 if (c
== EOF
|| !csdigit(c
)) {
985 error("`p' modifier must be followed by number");
986 list
->point_size
.inc
= 0;
990 list
->point_size
.val
*= 10;
991 list
->point_size
.val
+= c
- '0';
993 } while (c
!= EOF
&& csdigit(c
));
995 if (list
->point_size
.val
> MAX_POINT_SIZE
996 || list
->point_size
.val
< -MAX_POINT_SIZE
) {
997 error("unreasonable point size");
998 list
->point_size
.val
= 0;
999 list
->point_size
.inc
= 0;
1005 list
->vertical_alignment
= entry_modifier::TOP
;
1015 list
->vertical_spacing
.val
= 0;
1016 list
->vertical_spacing
.inc
= 0;
1017 if (c
== '+' || c
== '-') {
1018 list
->vertical_spacing
.inc
= (c
== '+' ? 1 : -1);
1021 if (c
== EOF
|| !csdigit(c
)) {
1022 error("`v' modifier must be followed by number");
1023 list
->vertical_spacing
.inc
= 0;
1027 list
->vertical_spacing
.val
*= 10;
1028 list
->vertical_spacing
.val
+= c
- '0';
1030 } while (c
!= EOF
&& csdigit(c
));
1032 if (list
->vertical_spacing
.val
> MAX_VERTICAL_SPACING
1033 || list
->vertical_spacing
.val
< -MAX_VERTICAL_SPACING
) {
1034 error("unreasonable vertical spacing");
1035 list
->vertical_spacing
.val
= 0;
1036 list
->vertical_spacing
.inc
= 0;
1042 while (c
== ' ' || c
== '\t')
1048 if (c
== EOF
|| c
== '\n') {
1049 error("missing `)'");
1050 free_input_entry_format_list(list
);
1059 if (c
== '+' || c
== '-') {
1060 list
->width
= char(c
);
1065 if (c
== EOF
|| !csdigit(c
))
1066 error("bad argument for `w' modifier");
1069 list
->width
+= char(c
);
1071 } while (c
!= EOF
&& csdigit(c
));
1074 // `w' and `x' are mutually exclusive
1081 // `x' and `e' are mutually exclusive
1083 // `x' and `w' are mutually exclusive
1089 list
->zero_width
= 1;
1100 if (c
== opt
->tab_char
)
1107 if (list
->vline
> 2) {
1109 error("more than 2 vertical bars between key letters");
1111 if (c
== '\n' || c
== ',') {
1113 list
->last_column
= 1;
1119 } while (c
== ' ' || c
== '\t');
1121 error("`.' not last character on line");
1122 free_input_entry_format_list(list
);
1128 free_input_entry_format_list(list
);
1131 list
->last_column
= 1;
1132 // now reverse the list so that the first row is at the beginning
1133 input_entry_format
*rev
= 0;
1135 input_entry_format
*tem
= list
->next
;
1141 input_entry_format
*tem
;
1144 for (tem
= list
; tem
; tem
= tem
->next
)
1148 // compute number of columns and rows
1152 for (tem
= list
; tem
; tem
= tem
->next
) {
1153 if (tem
->last_column
) {
1154 if (col
>= ncolumns
)
1164 if (current_format
) {
1165 if (ncolumns
> current_format
->ncolumns
) {
1166 error("cannot increase the number of columns in a continued format");
1167 free_input_entry_format_list(list
);
1175 f
= new format(nrows
, ncolumns
);
1179 for (tem
= list
; tem
; tem
= tem
->next
) {
1180 f
->entry
[row
][col
] = *tem
;
1181 if (col
< ncolumns
- 1) {
1182 // use the greatest separation
1183 if (tem
->separation
> f
->separation
[col
]) {
1185 error("cannot change column separation in continued format");
1187 f
->separation
[col
] = tem
->separation
;
1190 else if (tem
->separation
>= 0)
1191 error("column separation specified for last column");
1192 if (tem
->equal
&& !f
->equal
[col
]) {
1194 error("cannot change which columns are equal in continued format");
1198 if (tem
->expand
&& !f
->expand
[col
]) {
1200 error("cannot change which columns are expanded in continued format");
1206 if (!tem
->width
.empty()) {
1207 // use the last width
1208 if (!f
->width
[col
].empty() && f
->width
[col
] != tem
->width
)
1209 error("multiple widths for column %1", col
+ 1);
1210 f
->width
[col
] = tem
->width
;
1212 if (tem
->pre_vline
) {
1214 f
->vline
[row
][col
] = tem
->pre_vline
;
1216 f
->vline
[row
][col
+ 1] = tem
->vline
;
1217 if (tem
->last_column
) {
1224 free_input_entry_format_list(list
);
1225 for (col
= 0; col
< ncolumns
; col
++) {
1226 entry_format
*e
= f
->entry
[f
->nrows
- 1] + col
;
1227 if (e
->type
!= FORMAT_HLINE
1228 && e
->type
!= FORMAT_DOUBLE_HLINE
1229 && e
->type
!= FORMAT_SPAN
)
1232 if (col
>= ncolumns
) {
1233 error("last row of format is all lines");
1237 if (have_expand
&& (opt
->flags
& table::EXPAND
)) {
1238 error("ignoring global `expand' option because of `x' specifiers");
1239 opt
->flags
&= ~table::EXPAND
;
1244 table
*process_data(table_input
&in
, format
*f
, options
*opt
)
1246 char tab_char
= opt
->tab_char
;
1247 int ncolumns
= f
->ncolumns
;
1248 int current_row
= 0;
1249 int format_index
= 0;
1251 enum { DATA_INPUT_LINE
, TROFF_INPUT_LINE
, SINGLE_HLINE
, DOUBLE_HLINE
} type
;
1252 table
*tbl
= new table(ncolumns
, opt
->flags
, opt
->linesize
,
1253 opt
->decimal_point_char
);
1254 if (opt
->delim
[0] != '\0')
1255 tbl
->set_delim(opt
->delim
[0], opt
->delim
[1]);
1257 // first determine what type of line this is
1263 if (d
!= EOF
&& csdigit(d
)) {
1265 type
= DATA_INPUT_LINE
;
1269 type
= TROFF_INPUT_LINE
;
1272 else if (c
== '_' || c
== '=') {
1276 type
= SINGLE_HLINE
;
1278 type
= DOUBLE_HLINE
;
1282 type
= DATA_INPUT_LINE
;
1286 type
= DATA_INPUT_LINE
;
1289 case DATA_INPUT_LINE
:
1292 if (format_index
>= f
->nrows
)
1293 format_index
= f
->nrows
- 1;
1294 // A format row that is all lines doesn't use up a data line.
1295 while (format_index
< f
->nrows
- 1) {
1297 for (cnt
= 0; cnt
< ncolumns
; cnt
++) {
1298 entry_format
*e
= f
->entry
[format_index
] + cnt
;
1299 if (e
->type
!= FORMAT_HLINE
1300 && e
->type
!= FORMAT_DOUBLE_HLINE
1301 // Unfortunately tbl treats a span as needing data.
1302 // && e->type != FORMAT_SPAN
1308 for (cnt
= 0; cnt
< ncolumns
; cnt
++)
1309 tbl
->add_entry(current_row
, cnt
, input_entry
,
1310 f
->entry
[format_index
] + cnt
, current_filename
,
1312 tbl
->add_vlines(current_row
, f
->vline
[format_index
]);
1316 entry_format
*line_format
= f
->entry
[format_index
];
1318 int row_comment
= 0;
1320 if (c
== tab_char
|| c
== '\n') {
1321 int ln
= current_lineno
;
1324 if ((opt
->flags
& table::NOSPACES
))
1325 input_entry
.remove_spaces();
1326 while (col
< ncolumns
1327 && line_format
[col
].type
== FORMAT_SPAN
) {
1328 tbl
->add_entry(current_row
, col
, "", &line_format
[col
],
1329 current_filename
, ln
);
1332 if (c
== '\n' && input_entry
.length() == 2
1333 && input_entry
[0] == 'T' && input_entry
[1] == '{') {
1337 START
, MIDDLE
, GOT_T
, GOT_RIGHT_BRACE
, GOT_DOT
,
1340 while (state
!= END
) {
1358 state
= GOT_RIGHT_BRACE
;
1362 state
= c
== '\n' ? START
: MIDDLE
;
1371 state
= c
== '\n' ? START
: MIDDLE
;
1378 input_entry
+= ".l";
1380 state
= c
== '\n' ? START
: MIDDLE
;
1384 if (c
== ' ' || c
== '\n' || compatible_flag
) {
1386 input_entry
+= ".lf";
1394 interpret_lf_args(args
.contents());
1396 args
.set_length(args
.length() - 1);
1397 input_entry
+= args
;
1401 input_entry
+= ".lf";
1406 case GOT_RIGHT_BRACE
:
1407 if ((opt
->flags
& table::NOSPACES
)) {
1413 if (c
== '\n' || c
== tab_char
)
1433 error("end of data in middle of text block");
1438 if (col
>= ncolumns
) {
1439 if (!input_entry
.empty()) {
1440 if (input_entry
.length() >= 2
1441 && input_entry
[0] == '\\'
1442 && input_entry
[1] == '"')
1444 else if (!row_comment
) {
1447 input_entry
+= '\0';
1448 error("excess data entry `%1' discarded",
1449 input_entry
.contents());
1456 tbl
->add_entry(current_row
, col
, input_entry
,
1457 &line_format
[col
], current_filename
, ln
);
1472 for (; col
< ncolumns
; col
++)
1473 tbl
->add_entry(current_row
, col
, input_entry
, &line_format
[col
],
1474 current_filename
, current_lineno
- 1);
1475 tbl
->add_vlines(current_row
, f
->vline
[format_index
]);
1480 case TROFF_INPUT_LINE
:
1483 int ln
= current_lineno
;
1493 tbl
->add_text_line(current_row
, line
, current_filename
, ln
);
1494 if (line
.length() >= 4
1495 && line
[0] == '.' && line
[1] == 'T' && line
[2] == '&') {
1496 format
*newf
= process_format(in
, opt
, f
);
1502 if (line
.length() >= 3
1503 && line
[0] == '.' && line
[1] == 'l' && line
[2] == 'f') {
1505 interpret_lf_args(line
.contents() + 3);
1510 tbl
->add_single_hline(current_row
);
1513 tbl
->add_double_hline(current_row
);
1521 if (!give_up
&& current_row
== 0) {
1522 error("no real data");
1529 // Do this here rather than at the beginning in case continued formats
1532 for (i
= 0; i
< ncolumns
- 1; i
++)
1533 if (f
->separation
[i
] >= 0)
1534 tbl
->set_column_separation(i
, f
->separation
[i
]);
1535 for (i
= 0; i
< ncolumns
; i
++)
1536 if (!f
->width
[i
].empty())
1537 tbl
->set_minimum_width(i
, f
->width
[i
]);
1538 for (i
= 0; i
< ncolumns
; i
++)
1540 tbl
->set_equal_column(i
);
1541 for (i
= 0; i
< ncolumns
; i
++)
1543 tbl
->set_expand_column(i
);
1547 void process_table(table_input
&in
)
1552 if ((opt
= process_options(in
)) != 0
1553 && (form
= process_format(in
, opt
)) != 0
1554 && (tbl
= process_data(in
, form
, opt
)) != 0) {
1559 error("giving up on this table");
1560 while (in
.get() != EOF
)
1566 error("premature end of file");
1569 static void usage(FILE *stream
)
1571 fprintf(stream
, "Synopsis: %s [ -vC ] [ files... ]\n", program_name
);
1574 int main(int argc
, char **argv
)
1576 program_name
= argv
[0];
1577 static char stderr_buf
[BUFSIZ
];
1578 setbuf(stderr
, stderr_buf
);
1580 static const struct option long_options
[] = {
1581 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
1582 { "version", no_argument
, 0, 'v' },
1585 while ((opt
= getopt_long(argc
, argv
, "vCT:", long_options
, NULL
)) != EOF
)
1588 compatible_flag
= 1;
1592 puts(L_P_TBL
" (" T_ROFF
") v" VERSION
);
1597 // I'm sick of getting bug reports from IRIX users
1599 case CHAR_MAX
+ 1: // --help
1610 printf(".if !\\n(.g .ab " L_P_TBL
" requires " L_TROFF
".\n"
1612 ".if !dTE .ds TE\n");
1615 do /*while (optind < argc)*/ {
1616 if ((current_filename
= argv
[optind
++]) == NULL
)
1617 current_filename
= "-";
1618 fcp
= file_case::muxer(current_filename
);
1620 assert(strcmp(current_filename
, "-"));
1621 fatal("can't open `%1': %2", current_filename
, strerror(errno
));
1625 printf(".lf 1 %s\n", current_filename
);
1626 process_input_file(fcp
);
1629 } while (optind
< argc
);
1631 if (ferror(stdout
) || fflush(stdout
) < 0)
1632 fatal("output error");