/* pr -- convert text files for printing. This is the pr utility
Copyright (C) 1988-2018 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ The GNUv3 license
/* By Pete TerMaat, with considerable refinement by Roland Huebner. */
/* Things to watch: Sys V screws up on ...
pr -n -3 -s: /usr/dict/words
pr -m -o10 -n /usr/dict/words{,,,}
pr -6 -a -n -o5 /usr/dict/words
Ideas:
Keep a things_to_do list of functions to call when we know we have
something to print. Cleaner than current series of checks.
Improve the printing of control prefixes.
Expand the file name in the centered header line to a full file name.
Concept:
If the input_tab_char differs from the default value TAB
('-e[CHAR[...]]' is used), any input text tab is expanded to the
default width of 8 spaces (compare char_to_clump). - Same as SunOS
does.
The treatment of the number_separator (compare add_line_number):
The default value TAB of the number_separator ('-n[SEP[...]]') doesn't
be thought to be an input character. An optional '-e'-input has no
effect.
- With single column output
only one POSIX requirement has to be met:
The default n-separator should be a TAB. The consequence is a
different width between the number and the text if the output position
of the separator changes, i.e., it depends upon the left margin used.
That's not nice but easy-to-use together with the defaults of other
utilities, e.g. sort or cut. - Same as SunOS does.
- With multicolumn output
two conflicting POSIX requirements exist:
First "default n-separator is TAB", second "output text columns shall
be of equal width". Moreover POSIX specifies the number+separator a
part of the column, together with '-COLUMN' and '-a -COLUMN'.
(With -m output the number shall occupy each line only once. Exactly
the same situation as single column output exists.)
GNU pr gives priority to the 2nd requirement and observes POSIX
column definition. The n-separator TAB is expanded to the same number
of spaces in each column using the default value 8. Tabification is
only performed if it is compatible with the output position.
Consequence: The output text columns are of equal width. The layout
of a page does not change if the left margin varies. - Looks better
than the SunOS approach.
SunOS pr gives priority to the 1st requirement. n-separator TAB
width varies with each column. Only the width of text part of the
column is fixed.
Consequence: The output text columns don't have equal width. The
widths and the layout of the whole page varies with the left margin.
An overflow of the line length (without margin) over the input value
PAGE_WIDTH may occur.
The interference of the POSIX-compliant small letter options -w and -s:
("interference" means "setting a _separator_ with -s switches off the
column structure and the default - not generally - page_width,
acts on -w option")
options: text form / separator: equivalent new options:
-w l -s[x]
--------------------------------------------------------------------
1. -- -- columns / space --
trunc. to page_width = 72
2. -- -s[:] full lines / TAB[:] -J --sep-string[="<TAB>"|:]
no truncation
3. -w l -- columns / space -W l
trunc. to page_width = l
4. -w l -s[:] columns / no sep.[:] -W l --sep-string[=:]
trunc. to page_width = l
--------------------------------------------------------------------
Options:
Including version 1.22i:
Some SMALL LETTER options have been redefined with the object of a
better POSIX compliance. The output of some further cases has been
adapted to other UNIXes. A violation of downward compatibility has to
be accepted.
Some NEW CAPITAL LETTER options ( -J, -S, -W) has been introduced to
turn off unexpected interferences of small letter options (-s and -w
together with the three column options).
-N option and the second argument LAST_PAGE of +FIRST_PAGE offer more
flexibility; The detailed handling of form feeds set in the input
files requires -T option.
Capital letter options dominate small letter ones.
Some of the option-arguments cannot be specified as separate arguments
from the preceding option letter (already stated in POSIX specification).
Form feeds in the input cause page breaks in the output. Multiple
form feeds produce empty pages.
+FIRST_PAGE[:LAST_PAGE], --pages=FIRST_PAGE[:LAST_PAGE]
begin [stop] printing with page FIRST_[LAST_]PAGE
-COLUMN, --columns=COLUMN
Produce output that is COLUMN columns wide and
print columns down, unless -a is used. Balance number of
lines in the columns on each page.
-a, --across Print columns across rather than down, used
together with -COLUMN. The input
one
two
three
four
will be printed with '-a -3' as
one two three
four
-b Balance columns on the last page.
-b is no longer an independent option. It's always used
together with -COLUMN (unless -a is used) to get a
consistent formulation with "FF set by hand" in input
files. Each formfeed found terminates the number of lines
to be read with the actual page. The situation for
printing columns down is equivalent to that on the last
page. So we need a balancing.
Keeping -b as an underground option guarantees some
downward compatibility. Utilities using pr with -b
(a most frequently used form) still work as usual.
-c, --show-control-chars
Print unprintable characters as control prefixes.
Control-g is printed as ^G (use hat notation) and
octal backslash notation.
-d, --double-space Double space the output.
-D FORMAT, --date-format=FORMAT Use FORMAT for the header date.
-e[CHAR[WIDTH]], --expand-tabs[=CHAR[WIDTH]]
Expand tabs to spaces on input. Optional argument CHAR
is the input TAB character. (Default is TAB). Optional
argument WIDTH is the input TAB character's width.
(Default is 8.)
-F, -f, --form-feed Use formfeeds instead of newlines to separate
pages. A three line HEADER is used, no TRAILER with -F,
without -F both HEADER and TRAILER are made of five lines.
-h HEADER, --header=HEADER
Replace the filename in the header with the string HEADER.
A centered header is used.
-i[CHAR[WIDTH]], --output-tabs[=CHAR[WIDTH]]
Replace spaces with tabs on output. Optional argument
CHAR is the output TAB character. (Default is TAB).
Optional argument WIDTH is the output TAB character's
width. (Default is 8)
-J, --join-lines Merge lines of full length, turns off -W/-w
line truncation, no column alignment, --sep-string[=STRING]
sets separators, works with all column options
(-COLUMN | -a -COLUMN | -m).
-J has been introduced (together with -W and --sep-string) to
disentangle the old (POSIX compliant) options -w, -s
along with the 3 column options.
-l PAGE_LENGTH, --length=PAGE_LENGTH
Set the page length to PAGE_LENGTH lines. Default is 66,
including 5 lines of HEADER and 5 lines of TRAILER
without -F, but only 3 lines of HEADER and no TRAILER
with -F (i.e the number of text lines defaults to 56 or
63 respectively).
-m, --merge Print files in parallel; pad_across_to align
columns; truncate lines and print separator strings;
Do it also with empty columns to get a continuous line
numbering and column marking by separators throughout
the whole merged file.
Empty pages in some input files produce empty columns
[marked by separators] in the merged pages. Completely
empty merged pages show no column separators at all.
The layout of a merged page is ruled by the largest form
feed distance of the single pages at that page. Shorter
columns will be filled up with empty lines.
Together with -J option join lines of full length and
set separators when -S option is used.
-n[SEP[DIGITS]], --number-lines[=SEP[DIGITS]]
Provide DIGITS digit line numbering (default for DIGITS
is 5). With multicolumn output the number occupies the
first DIGITS column positions of each text column or only
each line of -m output.
With single column output the number precedes each line
just as -m output.
Optional argument SEP is the character appended to the
line number to separate it from the text followed.
The default separator is a TAB. In a strict sense a TAB
is always printed with single column output only. The
TAB-width varies with the TAB-position, e.g. with the
left margin specified by -o option.
With multicolumn output priority is given to "equal width
of output columns" (a POSIX specification). The TAB-width
is fixed to the value of the 1st column and does not
change with different values of left margin. That means a
fixed number of spaces is always printed in the place of
a TAB. The tabification depends upon the output
position.
Default counting of the line numbers starts with 1st
line of the input file (not the 1st line printed,
compare the --page option and -N option).
-N NUMBER, --first-line-number=NUMBER
Start line counting with the number NUMBER at the 1st
line of first page printed (mostly not the 1st line of
the input file).
-o MARGIN, --indent=MARGIN
Offset each line with a margin MARGIN spaces wide.
Total page width is the size of the margin plus the
PAGE_WIDTH set with -W/-w option.
-r, --no-file-warnings
Omit warning when a file cannot be opened.
-s[CHAR], --separator[=CHAR]
Separate columns by a single character CHAR, default for
CHAR is the TAB character without -w and 'no char' with -w.
Without '-s' default separator 'space' is set.
-s[CHAR] turns off line truncation of all 3 column options
(-COLUMN|-a -COLUMN|-m) except -w is set. That is a POSIX
compliant formulation. The source code translates -s into
the new options -S and -J, also -W if required.
-S[STRING], --sep-string[=STRING]
Separate columns by any string STRING. The -S option
doesn't react upon the -W/-w option (unlike -s option
does). It defines a separator nothing else.
Without -S: Default separator TAB is used with -J and
'space' otherwise (same as -S" ").
With -S "": No separator is used.
Quotes should be used with blanks and some shell active
characters.
-S is problematic because in its obsolete form you
cannot use -S "STRING", but in its standard form you
must use -S "STRING" if STRING is empty. Use
--sep-string to avoid the ambiguity.
-t, --omit-header Do not print headers or footers but retain form
feeds set in the input files.
-T, --omit-pagination
Do not print headers or footers, eliminate any pagination
by form feeds set in the input files.
-v, --show-nonprinting
Print unprintable characters as escape sequences. Use
octal backslash notation. Control-G becomes \007.
-w PAGE_WIDTH, --width=PAGE_WIDTH
Set page width to PAGE_WIDTH characters for multiple
text-column output only (default for PAGE_WIDTH is 72).
-s[CHAR] turns off the default page width and any line
truncation. Lines of full length will be merged,
regardless of the column options set. A POSIX compliant
formulation.
-W PAGE_WIDTH, --page-width=PAGE_WIDTH
Set the page width to PAGE_WIDTH characters. That's valid
with and without a column option. Text lines will be
truncated, unless -J is used. Together with one of the
column options (-COLUMN| -a -COLUMN| -m) column alignment
is always used.
Default is 72 characters.
Without -W PAGE_WIDTH
- but with one of the column options default truncation of
72 characters is used (to keep downward compatibility
and to simplify most frequently met column tasks).
Column alignment and column separators are used.
- and without any of the column options NO line truncation
is used (to keep downward compatibility and to meet most
frequent tasks). That's equivalent to -W 72 -J .
With/without -W PAGE_WIDTH the header line is always
truncated to avoid line overflow.
(In pr versions newer than 1.14 -S option does no longer
affect -W option.)
*/
#include <config.h> Provides system specific information
#include <getopt.h> ...!includes auto-comment...
#include <sys/types.h> Provides system data types
#include "system.h" ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "fadvise.h" ...!includes auto-comment...
#include "hard-locale.h" ...!includes auto-comment...
#include "mbswidth.h" ...!includes auto-comment...
#include "quote.h" ...!includes auto-comment...
#include "stat-time.h" ...!includes auto-comment...
#include "stdio--.h" ...!includes auto-comment...
#include "strftime.h" ...!includes auto-comment...
#include "xstrtol.h" ...!includes auto-comment...
#include "xdectoint.h" ...!includes auto-comment...
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "pr" Line 328
#define AUTHORS \ Line 330
proper_name ("Pete TerMaat"), \ Line 331
proper_name ("Roland Huebner") Line 332
/* Used with start_position in the struct COLUMN described below.
If start_position == ANYWHERE, we aren't truncating columns and
can begin printing a column anywhere. Otherwise we must pad to
the horizontal position start_position. */
#define ANYWHERE 0 Line 338
/* Each column has one of these structures allocated for it.
If we're only dealing with one file, fp is the same for all
columns.
The general strategy is to spend time setting up these column
structures (storing columns if necessary), after which printing
is a matter of flitting from column to column and calling
print_func.
Parallel files, single files printing across in multiple
columns, and single files printing down in multiple columns all
fit the same printing loop.
print_func Function used to print lines in this column.
If we're storing this column it will be
print_stored(), Otherwise it will be read_line().
char_func Function used to process characters in this column.
If we're storing this column it will be store_char(),
otherwise it will be print_char().
current_line Index of the current entry in line_vector, which
contains the index of the first character of the
current line in buff[].
lines_stored Number of lines in this column which are stored in
buff.
lines_to_print If we're storing this column, lines_to_print is
the number of stored_lines which remain to be
printed. Otherwise it is the number of lines
we can print without exceeding lines_per_body.
start_position The horizontal position we want to be in before we
print the first character in this column.
numbered True means precede this column with a line number. */
/* FIXME: There are many unchecked integer overflows in this file,
that will cause this command to misbehave given large inputs or
options. Many of the "int" values below should be "size_t" or
something else like that. */
struct COLUMN; Line 383
struct COLUMN Line 384
{
FILE *fp; /* Input stream for this column. */ Line 386
char const *name; /* File name. */ Line 387
enum Line 388
{
OPEN, Line 390
FF_FOUND, /* used with -b option, set with \f, changed Line 391
to ON_HOLD after print_header */
ON_HOLD, /* Hit a form feed. */ Line 393
CLOSED Line 394
}
status; /* Status of the file pointer. */ Line 396
/* Func to print lines in this col. */
bool (*print_func) (struct COLUMN *); Line 399
/* Func to print/store chars in this col. */
void (*char_func) (char); Line 402
int current_line; /* Index of current place in line_vector. */ Line 404
int lines_stored; /* Number of lines stored in buff. */ Line 405
int lines_to_print; /* No. lines stored or space left on page. */ Line 406
int start_position; /* Horizontal position of first char. */ Line 407
bool numbered; Line 408
bool full_page_printed; /* True means printed without a FF found. */ Line 409
/* p->full_page_printed controls a special case of "FF set by hand":
True means a full page has been printed without FF found. To avoid an
additional empty page we have to ignore a FF immediately following in
the next line. */
}; Block 1
typedef struct COLUMN COLUMN; Line 417
static int char_to_clump (char c); Line 419
static bool read_line (COLUMN *p); Line 420
static bool print_page (void); Line 421
static bool print_stored (COLUMN *p); Line 422
static bool open_file (char *name, COLUMN *p); Line 423
static bool skip_to_page (uintmax_t page); Line 424
static void print_header (void); Line 425
static void pad_across_to (int position); Line 426
static void add_line_number (COLUMN *p); Line 427
static void getoptnum (const char *n_str, int min, int *num, Line 428
const char *errfmt); Line 429
static void getoptarg (char *arg, char switch_char, char *character, Line 430
int *number); Line 431
static void print_files (int number_of_files, char **av); Line 432
static void init_parameters (int number_of_files); Line 433
static void init_header (char const *filename, int desc); Line 434
static bool init_fps (int number_of_files, char **av); Line 435
static void init_funcs (void); Line 436
static void init_store_cols (void); Line 437
static void store_columns (void); Line 438
static void balance (int total_stored); Line 439
static void store_char (char c); Line 440
static void pad_down (unsigned int lines); Line 441
static void read_rest_of_line (COLUMN *p); Line 442
static void skip_read (COLUMN *p, int column_number); Line 443...!syscalls auto-comment...
static void print_char (char c); Line 444
static void cleanup (void); Line 445
static void print_sep_string (void); Line 446
static void separator_string (const char *optarg_S); Line 447
/* All of the columns to print. */
static COLUMN *column_vector; Line 450
/* When printing a single file in multiple downward columns,
we store the leftmost columns contiguously in buff.
To print a line from buff, get the index of the first character
from line_vector[i], and print up to line_vector[i + 1]. */
static char *buff; Line 456
/* Index of the position in buff where the next character
will be stored. */
static unsigned int buff_current; Line 460
/* The number of characters in buff.
Used for allocation of buff and to detect overflow of buff. */
static size_t buff_allocated; Line 464
/* Array of indices into buff.
Each entry is an index of the first character of a line.
This is used when storing lines to facilitate shuffling when
we do column balancing on the last page. */
static int *line_vector; Line 470
/* Array of horizontal positions.
For each line in line_vector, end_vector[line] is the horizontal
position we are in after printing that line. We keep track of this
so that we know how much we need to pad to prepare for the next
column. */
static int *end_vector; Line 477
/* (-m) True means we're printing multiple files in parallel. */
static bool parallel_files = false; Line 480
/* (-m) True means a line starts with some empty columns (some files
already CLOSED or ON_HOLD) which we have to align. */
static bool align_empty_cols; Line 484
/* (-m) True means we have not yet found any printable column in a line.
align_empty_cols = true has to be maintained. */
static bool empty_line; Line 488
/* (-m) False means printable column output precedes a form feed found.
Column alignment is done only once. No additional action with that form
feed.
True means we found only a form feed in a column. Maybe we have to do
some column alignment with that form feed. */
static bool FF_only; Line 495
/* (-[0-9]+) True means we're given an option explicitly specifying
number of columns. Used to detect when this option is used with -m
and when translating old options to new/long options. */
static bool explicit_columns = false; Line 500
/* (-t|-T) False means we aren't printing headers and footers. */
static bool extremities = true; Line 503
/* (-t) True means we retain all FF set by hand in input files.
False is set with -T option. */
static bool keep_FF = false; Line 507
static bool print_a_FF = false; Line 508
/* True means we need to print a header as soon as we know we've got input
to print after it. */
static bool print_a_header; Line 512
/* (-f) True means use formfeeds instead of newlines to separate pages. */
static bool use_form_feed = false; Line 515
/* True means we have read the standard input. */
static bool have_read_stdin = false; Line 518
/* True means the -a flag has been given. */
static bool print_across_flag = false; Line 521
/* True means we're printing one file in multiple (>1) downward columns. */
static bool storing_columns = true; Line 524
/* (-b) True means balance columns on the last page as Sys V does. */
/* That's no longer an independent option. With storing_columns = true
balance_columns = true is used too (s. function init_parameters).
We get a consistent formulation with "FF set by hand" in input files. */
static bool balance_columns = false; Line 530
/* (-l) Number of lines on a page, including header and footer lines. */
static int lines_per_page = 66; Line 533
/* Number of lines in the header and footer can be reset to 0 using
the -t flag. */
enum { lines_per_header = 5 }; Line 537Block 2
static int lines_per_body; Line 538
enum { lines_per_footer = 5 }; Line 539
/* (-w|-W) Width in characters of the page. Does not include the width of
the margin. */
static int chars_per_line = 72; Line 543
/* (-w|W) True means we truncate lines longer than chars_per_column. */
static bool truncate_lines = false; Line 546
/* (-J) True means we join lines without any line truncation. -J
dominates -w option. */
static bool join_lines = false; Line 550
/* Number of characters in a column. Based on col_sep_length and
page width. */
static int chars_per_column; Line 554
/* (-e) True means convert tabs to spaces on input. */
static bool untabify_input = false; Line 557
/* (-e) The input tab character. */
static char input_tab_char = '\t'; Line 560
/* (-e) Tabstops are at chars_per_tab, 2*chars_per_tab, 3*chars_per_tab, ...
where the leftmost column is 1. */
static int chars_per_input_tab = 8; Line 564
/* (-i) True means convert spaces to tabs on output. */
static bool tabify_output = false; Line 567
/* (-i) The output tab character. */
static char output_tab_char = '\t'; Line 570
/* (-i) The width of the output tab. */
static int chars_per_output_tab = 8; Line 573
/* Keeps track of pending white space. When we hit a nonspace
character after some whitespace, we print whitespace, tabbing
if necessary to get to output_position + spaces_not_printed. */
static int spaces_not_printed; Line 578
/* (-o) Number of spaces in the left margin (tabs used when possible). */
static int chars_per_margin = 0; Line 581
/* Position where the next character will fall.
Leftmost position is 0 + chars_per_margin.
Rightmost position is chars_per_margin + chars_per_line - 1.
This is important for converting spaces to tabs on output. */
static int output_position; Line 587
/* Horizontal position relative to the current file.
(output_position depends on where we are on the page;
input_position depends on where we are in the file.)
Important for converting tabs to spaces on input. */
static int input_position; Line 593
/* True if there were any failed opens so we can exit with nonzero
status. */
static bool failed_opens = false; Line 597
/* The number of spaces taken up if we print a tab character with width
c_ from position h_. */
#define TAB_WIDTH(c_, h_) ((c_) - ((h_) % (c_))) Line 601
/* The horizontal position we'll be at after printing a tab character
of width c_ from the position h_. */
#define POS_AFTER_TAB(c_, h_) ((h_) + TAB_WIDTH (c_, h_)) Line 605
/* (-NNN) Number of columns of text to print. */
static int columns = 1; Line 608
/* (+NNN:MMM) Page numbers on which to begin and stop printing.
first_page_number = 0 will be used to check input only. */
static uintmax_t first_page_number = 0; Line 612
static uintmax_t last_page_number = UINTMAX_MAX; Line 613
/* Number of files open (not closed, not on hold). */
static int files_ready_to_read = 0; Line 616
/* Current page number. Displayed in header. */
static uintmax_t page_number; Line 619
/* Current line number. Displayed when -n flag is specified.
When printing files in parallel (-m flag), line numbering is as follows:
1 foo goo moo
2 hoo too zoo
When printing files across (-a flag), ...
1 foo 2 moo 3 goo
4 hoo 5 too 6 zoo
Otherwise, line numbering is as follows:
1 foo 3 goo 5 too
2 moo 4 hoo 6 zoo */
static int line_number; Line 634
/* (-n) True means lines should be preceded by numbers. */
static bool numbered_lines = false; Line 637
/* (-n) Character which follows each line number. */
static char number_separator = '\t'; Line 640
/* (-n) line counting starts with 1st line of input file (not with 1st
line of 1st page printed). */
static int line_count = 1; Line 644
/* (-n) True means counting of skipped lines starts with 1st line of
input file. False means -N option is used in addition, counting of
skipped lines not required. */
static bool skip_count = true; Line 649
/* (-N) Counting starts with start_line_number = NUMBER at 1st line of
first page printed, usually not 1st page of input file. */
static int start_line_num = 1; Line 653
/* (-n) Width in characters of a line number. */
static int chars_per_number = 5; Line 656
/* Used when widening the first column to accommodate numbers -- only
needed when printing files in parallel. Includes width of both the
number and the number_separator. */
static int number_width; Line 661
/* Buffer sprintf uses to format a line number. */
static char *number_buff; Line 664
/* (-v) True means unprintable characters are printed as escape sequences.
control-g becomes \007. */
static bool use_esc_sequence = false; Line 668
/* (-c) True means unprintable characters are printed as control prefixes.
control-g becomes ^G. */
static bool use_cntrl_prefix = false; Line 672
/* (-d) True means output is double spaced. */
static bool double_space = false; Line 675
/* Number of files opened initially in init_files. Should be 1
unless we're printing multiple files in parallel. */
static int total_files = 0; Line 679
/* (-r) True means don't complain if we can't open a file. */
static bool ignore_failed_opens = false; Line 682
/* (-S) True means we separate columns with a specified string.
-S option does not affect line truncation nor column alignment. */
static bool use_col_separator = false; Line 686
/* String used to separate columns if the -S option has been specified.
Default without -S but together with one of the column options
-a|COLUMN|-m is a 'space' and with the -J option a 'tab'. */
static char const *col_sep_string = ""; Line 691
static int col_sep_length = 0; Line 692
static char *column_separator = (char *) " "; Line 693
static char *line_separator = (char *) "\t"; Line 694
/* Number of separator characters waiting to be printed as soon as we
know that we have any input remaining to be printed. */
static int separators_not_printed; Line 698
/* Position we need to pad to, as soon as we know that we have input
remaining to be printed. */
static int padding_not_printed; Line 702
/* True means we should pad the end of the page. Remains false until we
know we have a page to print. */
static bool pad_vertically; Line 706
/* (-h) String of characters used in place of the filename in the header. */
static char *custom_header; Line 709
/* (-D) Date format for the header. */
static char const *date_format; Line 712
/* The local time zone rules, as per the TZ environment variable. */
static timezone_t localtz; Line 715
/* Date and file name for the header. */
static char *date_text; Line 718
static char const *file_text; Line 719
/* Output columns available, not counting the date and file name. */
static int header_width_available; Line 722
static char *clump_buff; Line 724
/* True means we read the line no. lines_per_body in skip_read
called by skip_to_page. That variable controls the coincidence of a
"FF set by hand" and "full_page_printed", see above the definition of
structure COLUMN. */
static bool last_line = false; Line 730
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum Line 734
{
COLUMNS_OPTION = CHAR_MAX + 1, Line 736
PAGES_OPTION Line 737
}; Block 4
static char const short_options[] = Line 740
"-0123456789D:FJN:S::TW:abcde::fh:i::l:mn::o:rs::tvw:"; Line 741
static struct option const long_options[] = Line 743
{
{"pages", required_argument, NULL, PAGES_OPTION}, Line 745
{"columns", required_argument, NULL, COLUMNS_OPTION}, Line 746
{"across", no_argument, NULL, 'a'}, Line 747
{"show-control-chars", no_argument, NULL, 'c'}, Line 748
{"double-space", no_argument, NULL, 'd'}, Line 749
{"date-format", required_argument, NULL, 'D'}, Line 750
{"expand-tabs", optional_argument, NULL, 'e'}, Line 751
{"form-feed", no_argument, NULL, 'f'}, Line 752
{"header", required_argument, NULL, 'h'}, Line 753
{"output-tabs", optional_argument, NULL, 'i'}, Line 754
{"join-lines", no_argument, NULL, 'J'}, Line 755
{"length", required_argument, NULL, 'l'}, Line 756
{"merge", no_argument, NULL, 'm'}, Line 757
{"number-lines", optional_argument, NULL, 'n'}, Line 758
{"first-line-number", required_argument, NULL, 'N'}, Line 759
{"indent", required_argument, NULL, 'o'}, Line 760
{"no-file-warnings", no_argument, NULL, 'r'}, Line 761
{"separator", optional_argument, NULL, 's'}, Line 762
{"sep-string", optional_argument, NULL, 'S'}, Line 763
{"omit-header", no_argument, NULL, 't'}, Line 764
{"omit-pagination", no_argument, NULL, 'T'}, Line 765
{"show-nonprinting", no_argument, NULL, 'v'}, Line 766
{"width", required_argument, NULL, 'w'}, Line 767
{"page-width", required_argument, NULL, 'W'}, Line 768
{GETOPT_HELP_OPTION_DECL}, Line 769
{GETOPT_VERSION_OPTION_DECL}, Line 770
{NULL, 0, NULL, 0} Line 771
}; Block 5
static void Line 774
integer_overflow (void) Line 775
{
die (EXIT_FAILURE, 0, _("integer overflow")); Line 777
} Block 6
/* Return the number of columns that have either an open file or
stored lines. */
static unsigned int _GL_ATTRIBUTE_PURE Line 783
cols_ready_to_print (void) Line 784
{
COLUMN *q; Line 786
unsigned int i; Line 787
unsigned int n; Line 788
n = 0; Line 790
for (q = column_vector, i = 0; i < columns; ++q, ++i) Line 791
if (q->status == OPEN Line 792
|| q->status == FF_FOUND /* With -b: To print a header only */ Line 793
|| (storing_columns && q->lines_stored > 0 && q->lines_to_print > 0)) Line 794
++n; Line 795
return n; Line 796
} Block 7
/* Estimate first_ / last_page_number
using option +FIRST_PAGE:LAST_PAGE */
static bool Line 802
first_last_page (int oi, char c, char const *pages) Line 803
{
char *p; Line 805
uintmax_t first; Line 806
uintmax_t last = UINTMAX_MAX; Line 807
strtol_error err = xstrtoumax (pages, &p, 10, &first, ""); Line 808
if (err != LONGINT_OK && err != LONGINT_INVALID_SUFFIX_CHAR) Line 809
xstrtol_fatal (err, oi, c, long_options, pages); Line 810
if (p == pages || !first) Line 812
return false; Line 813
if (*p == ':') Line 815
{
char const *p1 = p + 1; Line 817
err = xstrtoumax (p1, &p, 10, &last, ""); Line 818
if (err != LONGINT_OK) Line 819
xstrtol_fatal (err, oi, c, long_options, pages); Line 820
if (p1 == p || last < first) Line 821
return false; Line 822
}
if (*p) Line 825
return false; Line 826
first_page_number = first; Line 828
last_page_number = last; Line 829
return true; Line 830
} Block 8
/* Parse column count string S, and if it's valid (1 or larger and
within range of the type of 'columns') set the global variables
columns and explicit_columns. Otherwise, exit with a diagnostic. */
static void Line 837
parse_column_count (char const *s) Line 838
{
getoptnum (s, 1, &columns, _("invalid number of columns")); Line 840
explicit_columns = true; Line 841
} Block 9
/* Estimate length of col_sep_string with option -S. */
static void Line 846
separator_string (const char *optarg_S) Line 847
{
size_t len = strlen (optarg_S); Line 849
if (INT_MAX < len) Line 850
integer_overflow (); Line 851
col_sep_length = len; Line 852
col_sep_string = optarg_S; Line 853
} Block 10
int
main (int argc, char **argv) Line 857
{
unsigned int n_files; Line 859
bool old_options = false; Line 860
bool old_w = false; Line 861
bool old_s = false; Line 862
char **file_names; Line 863
/* Accumulate the digits of old-style options like -99. */
char *column_count_string = NULL; Line 866
size_t n_digits = 0; Line 867
size_t n_alloc = 0; Line 868
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
atexit (close_stdout); Close stdout on exit (see gnulib)
n_files = 0; Line 878
file_names = (argc > 1 Line 879
? xnmalloc (argc - 1, sizeof (char *)) Line 880
: NULL); Line 881
while (true) Line 883
{
int oi = -1; Line 885
int c = getopt_long (argc, argv, short_options, long_options, &oi); Line 886
if (c == -1) Line 887
break; Line 888
if (ISDIGIT (c)) Line 890
{
/* Accumulate column-count digits specified via old-style options. */
if (n_digits + 1 >= n_alloc) Line 893
column_count_string Line 894
= X2REALLOC (column_count_string, &n_alloc); Line 895
column_count_string[n_digits++] = c; Line 896
column_count_string[n_digits] = '\0'; Line 897
continue; Line 898
}
n_digits = 0; Line 901
switch (c) Line 903
{
case 1: /* Non-option argument. */ Line 905
/* long option --page dominates old '+FIRST_PAGE ...'. */
if (! (first_page_number == 0 Line 907
&& *optarg == '+' && first_last_page (-2, '+', optarg + 1))) Line 908
file_names[n_files++] = optarg; Line 909
break; Line 910
case PAGES_OPTION: /* --pages=FIRST_PAGE[:LAST_PAGE] */ Line 912
{ /* dominates old opt +... */ Line 913
if (! optarg) Line 914
die (EXIT_FAILURE, 0, Line 915
_("'--pages=FIRST_PAGE[:LAST_PAGE]' missing argument")); Line 916
else if (! first_last_page (oi, 0, optarg)) Line 917
die (EXIT_FAILURE, 0, _("invalid page range %s"), Line 918
quote (optarg)); Line 919
break; Line 920
}
case COLUMNS_OPTION: /* --columns=COLUMN */ Line 923
{
parse_column_count (optarg); Line 925
/* If there was a prior column count specified via the
short-named option syntax, e.g., -9, ensure that this
long-name-specified value overrides it. */
free (column_count_string); Line 930
column_count_string = NULL; Line 931
n_alloc = 0; Line 932
break; Line 933
}
case 'a': Line 936
print_across_flag = true; Line 937
storing_columns = false; Line 938
break; Line 939
case 'b': Line 940
balance_columns = true; Line 941
break; Line 942
case 'c': Line 943
use_cntrl_prefix = true; Line 944
break; Line 945
case 'd': Line 946
double_space = true; Line 947
break; Line 948
case 'D': Line 949
date_format = optarg; Line 950
break; Line 951
case 'e': Line 952
if (optarg) Line 953
getoptarg (optarg, 'e', &input_tab_char, Line 954
&chars_per_input_tab); Line 955
/* Could check tab width > 0. */
untabify_input = true; Line 957
break; Line 958
case 'f': Line 959
case 'F': Line 960
use_form_feed = true; Line 961
break; Line 962
case 'h': Line 963
custom_header = optarg; Line 964
break; Line 965
case 'i': Line 966
if (optarg) Line 967
getoptarg (optarg, 'i', &output_tab_char, Line 968
&chars_per_output_tab); Line 969
/* Could check tab width > 0. */
tabify_output = true; Line 971
break; Line 972
case 'J': Line 973
join_lines = true; Line 974
break; Line 975
case 'l': Line 976
getoptnum (optarg, 1, &lines_per_page, Line 977
_("'-l PAGE_LENGTH' invalid number of lines")); Line 978
break; Line 979
case 'm': Line 980
parallel_files = true; Line 981
storing_columns = false; Line 982
break; Line 983
case 'n': Line 984
numbered_lines = true; Line 985
if (optarg) Line 986
getoptarg (optarg, 'n', &number_separator, Line 987
&chars_per_number); Line 988
break; Line 989
case 'N': Line 990
skip_count = false; Line 991
getoptnum (optarg, INT_MIN, &start_line_num, Line 992
_("'-N NUMBER' invalid starting line number")); Line 993
break; Line 994
case 'o': Line 995
getoptnum (optarg, 0, &chars_per_margin, Line 996
_("'-o MARGIN' invalid line offset")); Line 997
break; Line 998
case 'r': Line 999
ignore_failed_opens = true; Line 1000
break; Line 1001
case 's': Line 1002
old_options = true; Line 1003
old_s = true; Line 1004
if (!use_col_separator && optarg) Line 1005
separator_string (optarg); Line 1006
break; Line 1007
case 'S': Line 1008
old_s = false; Line 1009
/* Reset an additional input of -s, -S dominates -s */
col_sep_string = ""; Line 1011
col_sep_length = 0; Line 1012
use_col_separator = true; Line 1013
if (optarg) Line 1014
separator_string (optarg); Line 1015
break; Line 1016
case 't': Line 1017
extremities = false; Line 1018
keep_FF = true; Line 1019
break; Line 1020
case 'T': Line 1021
extremities = false; Line 1022
keep_FF = false; Line 1023
break; Line 1024
case 'v': Line 1025
use_esc_sequence = true; Line 1026
break; Line 1027
case 'w': Line 1028
old_options = true; Line 1029
old_w = true; Line 1030
{
int tmp_cpl; Line 1032
getoptnum (optarg, 1, &tmp_cpl, Line 1033
_("'-w PAGE_WIDTH' invalid number of characters")); Line 1034
if (! truncate_lines) Line 1035
chars_per_line = tmp_cpl; Line 1036
}
break; Line 1038
case 'W': Line 1039
old_w = false; /* dominates -w */ Line 1040
truncate_lines = true; Line 1041
getoptnum (optarg, 1, &chars_per_line, Line 1042
_("'-W PAGE_WIDTH' invalid number of characters")); Line 1043
break; Line 1044
case_GETOPT_HELP_CHAR; Line 1045
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 1046
default: Line 1047
usage (EXIT_FAILURE); Line 1048
break; Line 1049
}
}
if (column_count_string) Line 1053
{
parse_column_count (column_count_string); Line 1055
free (column_count_string); Line 1056
}
if (! date_format) Line 1059
date_format = (getenv ("POSIXLY_CORRECT") && !hard_locale (LC_TIME) Line 1060
? "%b %e %H:%M %Y" Line 1061
: "%Y-%m-%d %H:%M"); Line 1062
localtz = tzalloc (getenv ("TZ")); Line 1064
/* Now we can set a reasonable initial value: */
if (first_page_number == 0) Line 1067
first_page_number = 1; Line 1068
if (parallel_files && explicit_columns) Line 1070
die (EXIT_FAILURE, 0, Line 1071
_("cannot specify number of columns when printing in parallel")); Line 1072
if (parallel_files && print_across_flag) Line 1074
die (EXIT_FAILURE, 0, Line 1075
_("cannot specify both printing across and printing in parallel")); Line 1076
/* Translate some old short options to new/long options.
To meet downward compatibility with other UNIX pr utilities
and some POSIX specifications. */
if (old_options) Line 1082
{
if (old_w) Line 1084
{
if (parallel_files || explicit_columns) Line 1086
{
/* activate -W */
truncate_lines = true; Line 1089
if (old_s) Line 1090
/* adapt HP-UX and SunOS: -s = no separator;
activate -S */
use_col_separator = true; Line 1093
}
else Line 1095
/* old -w sets width with columns only
activate -J */
join_lines = true; Line 1098
}
else if (!use_col_separator) Line 1100
{
/* No -S option read */
if (old_s && (parallel_files || explicit_columns)) Line 1103
{
if (!truncate_lines) Line 1105
{
/* old -s (without -w and -W) annuls column alignment,
uses fields, activate -J */
join_lines = true; Line 1109
if (col_sep_length > 0) Line 1110
/* activate -S */
use_col_separator = true; Line 1112
}
else Line 1114
/* with -W */
/* adapt HP-UX and SunOS: -s = no separator;
activate -S */
use_col_separator = true; Line 1118
}
}
}
for (; optind < argc; optind++) Line 1123
{
file_names[n_files++] = argv[optind]; Line 1125
}
if (n_files == 0) Line 1128
{
/* No file arguments specified; read from standard input. */
print_files (0, NULL); Line 1131
}
else Line 1133
{
if (parallel_files) Line 1135
print_files (n_files, file_names); Line 1136
else Line 1137
{
for (unsigned int i = 0; i < n_files; i++) Line 1139
print_files (1, &file_names[i]); Line 1140
}
}
cleanup (); Line 1144
IF_LINT (free (file_names)); Line 1145
if (have_read_stdin && fclose (stdin) == EOF) Line 1147...!syscalls auto-comment...
die (EXIT_FAILURE, errno, _("standard input")); Line 1148
return failed_opens ? EXIT_FAILURE : EXIT_SUCCESS; Line 1149
} Block 11
/* Parse numeric arguments, ensuring MIN <= number <= INT_MAX. */
static void Line 1154
getoptnum (const char *n_str, int min, int *num, const char *err) Line 1155
{
intmax_t tnum = xdectoimax (n_str, min, INT_MAX, "", err, 0); Line 1157
*num = tnum; Line 1158
} Block 12
/* Parse options of the form -scNNN.
Example: -nck, where 'n' is the option, c is the optional number
separator, and k is the optional width of the field used when printing
a number. */
static void Line 1167
getoptarg (char *arg, char switch_char, char *character, int *number) Line 1168
{
if (!ISDIGIT (*arg)) Line 1170
*character = *arg++; Line 1171
if (*arg) Line 1172
{
long int tmp_long; Line 1174
if (xstrtol (arg, NULL, 10, &tmp_long, "") != LONGINT_OK Line 1175
|| tmp_long <= 0 || INT_MAX < tmp_long) Line 1176
{
error (0, INT_MAX < tmp_long ? EOVERFLOW : errno, Line 1178
_("'-%c' extra characters or invalid number in the argument: %s"), Line 1179
switch_char, quote (arg)); Line 1180
usage (EXIT_FAILURE); Line 1181
}
*number = tmp_long; Line 1183
}
} Block 13
/* Set parameters related to formatting. */
static void Line 1189
init_parameters (int number_of_files) Line 1190
{
int chars_used_by_number = 0; Line 1192
lines_per_body = lines_per_page - lines_per_header - lines_per_footer; Line 1194
if (lines_per_body <= 0) Line 1195
{
extremities = false; Line 1197
keep_FF = true; Line 1198
}
if (extremities == false) Line 1200
lines_per_body = lines_per_page; Line 1201
if (double_space) Line 1203
lines_per_body = lines_per_body / 2; Line 1204
/* If input is stdin, cannot print parallel files. BSD dumps core
on this. */
if (number_of_files == 0) Line 1208
parallel_files = false; Line 1209
if (parallel_files) Line 1211
columns = number_of_files; Line 1212
/* One file, multi columns down: -b option is set to get a consistent
formulation with "FF set by hand" in input files. */
if (storing_columns) Line 1216
balance_columns = true; Line 1217
/* Tabification is assumed for multiple columns. */
if (columns > 1) Line 1220
{
if (!use_col_separator) Line 1222
{
/* Use default separator */
if (join_lines) Line 1225
col_sep_string = line_separator; Line 1226
else Line 1227
col_sep_string = column_separator; Line 1228
col_sep_length = 1; Line 1230
use_col_separator = true; Line 1231
}
/* It's rather pointless to define a TAB separator with column
alignment */
else if (!join_lines && col_sep_length == 1 && *col_sep_string == '\t') Line 1235
col_sep_string = column_separator; Line 1236
truncate_lines = true; Line 1238
tabify_output = true; Line 1239
}
else Line 1241
storing_columns = false; Line 1242
/* -J dominates -w in any case */
if (join_lines) Line 1245
truncate_lines = false; Line 1246
if (numbered_lines) Line 1248
{
int chars_per_default_tab = 8; Line 1250
line_count = start_line_num; Line 1252
/* To allow input tab-expansion (-e sensitive) use:
if (number_separator == input_tab_char)
number_width = chars_per_number
+ TAB_WIDTH (chars_per_input_tab, chars_per_number); */
/* Estimate chars_per_text without any margin and keep it constant. */
if (number_separator == '\t') Line 1260
number_width = (chars_per_number Line 1261
+ TAB_WIDTH (chars_per_default_tab, chars_per_number)); Line 1262
else Line 1263
number_width = chars_per_number + 1; Line 1264
/* The number is part of the column width unless we are
printing files in parallel. */
if (parallel_files) Line 1268
chars_used_by_number = number_width; Line 1269
}
int sep_chars, useful_chars; Line 1272
if (INT_MULTIPLY_WRAPV (columns - 1, col_sep_length, &sep_chars)) Line 1273
sep_chars = INT_MAX; Line 1274
if (INT_SUBTRACT_WRAPV (chars_per_line - chars_used_by_number, sep_chars, Line 1275
&useful_chars)) Line 1276
useful_chars = 0; Line 1277
chars_per_column = useful_chars / columns; Line 1278
if (chars_per_column < 1) Line 1280
die (EXIT_FAILURE, 0, _("page width too narrow")); Line 1281
if (numbered_lines) Line 1283
{
free (number_buff); Line 1285
number_buff = xmalloc (MAX (chars_per_number, Line 1286
INT_STRLEN_BOUND (line_number)) + 1); Line 1287
}
/* Pick the maximum between the tab width and the width of an
escape sequence.
The width of an escape sequence (4) isn't the lower limit any longer.
We've to use 8 as the lower limit, if we use chars_per_default_tab = 8
to expand a tab which is not an input_tab-char. */
free (clump_buff); Line 1295
clump_buff = xmalloc (MAX (8, chars_per_input_tab)); Line 1296
} Block 14
/* Open the necessary files,
maintaining a COLUMN structure for each column.
With multiple files, each column p has a different p->fp.
With single files, each column p has the same p->fp.
Return false if (number_of_files > 0) and no files can be opened,
true otherwise.
With each column/file p, p->full_page_printed is initialized,
see also open_file. */
static bool Line 1310
init_fps (int number_of_files, char **av) Line 1311
{
COLUMN *p; Line 1313
total_files = 0; Line 1315
free (column_vector); Line 1317
column_vector = xnmalloc (columns, sizeof (COLUMN)); Line 1318
if (parallel_files) Line 1320
{
int files_left = number_of_files; Line 1322
for (p = column_vector; files_left--; ++p, ++av) Line 1323
{
if (! open_file (*av, p)) Line 1325
{
--p; Line 1327
--columns; Line 1328
}
}
if (columns == 0) Line 1331
return false; Line 1332
init_header ("", -1); Line 1333
}
else Line 1335
{
p = column_vector; Line 1337
if (number_of_files > 0) Line 1338
{
if (! open_file (*av, p)) Line 1340
return false; Line 1341
init_header (*av, fileno (p->fp)); Line 1342
p->lines_stored = 0; Line 1343
}
else Line 1345
{
p->name = _("standard input"); Line 1347
p->fp = stdin; Line 1348
have_read_stdin = true; Line 1349
p->status = OPEN; Line 1350
p->full_page_printed = false; Line 1351
++total_files; Line 1352
init_header ("", -1); Line 1353
p->lines_stored = 0; Line 1354
}
char const *firstname = p->name; Line 1357
FILE *firstfp = p->fp; Line 1358
int i; Line 1359
for (i = columns - 1, ++p; i; --i, ++p) Line 1360
{
p->name = firstname; Line 1362
p->fp = firstfp; Line 1363
p->status = OPEN; Line 1364
p->full_page_printed = false; Line 1365
p->lines_stored = 0; Line 1366
}
}
files_ready_to_read = total_files; Line 1369
return true; Line 1370
} Block 15
/* Determine print_func and char_func, the functions
used by each column for printing and/or storing.
Determine the horizontal position desired when we begin
printing a column (p->start_position). */
static void Line 1379
init_funcs (void) Line 1380
{
int i, h, h_next; Line 1382
COLUMN *p; Line 1383
h = chars_per_margin; Line 1385
if (!truncate_lines) Line 1387
h_next = ANYWHERE; Line 1388
else Line 1389
{
/* When numbering lines of parallel files, we enlarge the
first column to accommodate the number. Looks better than
the Sys V approach. */
if (parallel_files && numbered_lines) Line 1394
h_next = h + chars_per_column + number_width; Line 1395
else Line 1396
h_next = h + chars_per_column; Line 1397
}
/* Enlarge p->start_position of first column to use the same form of
padding_not_printed with all columns. */
h = h + col_sep_length; Line 1402
/* This loop takes care of all but the rightmost column. */
for (p = column_vector, i = 1; i < columns; ++p, ++i) Line 1406
{
if (storing_columns) /* One file, multi columns down. */ Line 1408
{
p->char_func = store_char; Line 1410
p->print_func = print_stored; Line 1411
}
else Line 1413
/* One file, multi columns across; or parallel files. */
{
p->char_func = print_char; Line 1416
p->print_func = read_line; Line 1417
}
/* Number only the first column when printing files in
parallel. */
p->numbered = numbered_lines && (!parallel_files || i == 1); Line 1422
p->start_position = h; Line 1423
/* If we don't truncate lines, all start_positions are
ANYWHERE, except the first column's start_position when
using a margin. */
if (!truncate_lines) Line 1429
{
h = ANYWHERE; Line 1431
h_next = ANYWHERE; Line 1432
}
else Line 1434
{
h = h_next + col_sep_length; Line 1436
h_next = h + chars_per_column; Line 1437
}
}
/* The rightmost column.
Doesn't need to be stored unless we intend to balance
columns on the last page. */
if (storing_columns && balance_columns) Line 1445
{
p->char_func = store_char; Line 1447
p->print_func = print_stored; Line 1448
}
else Line 1450
{
p->char_func = print_char; Line 1452
p->print_func = read_line; Line 1453
}
p->numbered = numbered_lines && (!parallel_files || i == 1); Line 1456
p->start_position = h; Line 1457
} Block 16
/* Open a file. Return true if successful.
With each file p, p->full_page_printed is initialized,
see also init_fps. */
static bool Line 1465
open_file (char *name, COLUMN *p) Line 1466
{
if (STREQ (name, "-")) Line 1468
{
p->name = _("standard input"); Line 1470
p->fp = stdin; Line 1471
have_read_stdin = true; Line 1472
}
else Line 1474
{
p->name = name; Line 1476
p->fp = fopen (name, "r"); Line 1477...!syscalls auto-comment...
}
if (p->fp == NULL) Line 1479
{
failed_opens = true; Line 1481
if (!ignore_failed_opens) Line 1482
error (0, errno, "%s", quotef (name)); Line 1483
return false; Line 1484
}
fadvise (p->fp, FADVISE_SEQUENTIAL); Line 1486...!syscalls auto-comment...
p->status = OPEN; Line 1487
p->full_page_printed = false; Line 1488
++total_files; Line 1489
return true; Line 1490
} Block 17
/* Close the file in P.
If we aren't dealing with multiple files in parallel, we change
the status of all columns in the column list to reflect the close. */
static void Line 1498
close_file (COLUMN *p) Line 1499
{
COLUMN *q; Line 1501
int i; Line 1502
if (p->status == CLOSED) Line 1504
return; Line 1505
if (ferror (p->fp)) Line 1506
die (EXIT_FAILURE, errno, "%s", quotef (p->name)); Line 1507
if (fileno (p->fp) != STDIN_FILENO && fclose (p->fp) != 0) Line 1508...!syscalls auto-comment...
die (EXIT_FAILURE, errno, "%s", quotef (p->name)); Line 1509
if (!parallel_files) Line 1511
{
for (q = column_vector, i = columns; i; ++q, --i) Line 1513
{
q->status = CLOSED; Line 1515
if (q->lines_stored == 0) Line 1516
{
q->lines_to_print = 0; Line 1518
}
}
}
else Line 1522
{
p->status = CLOSED; Line 1524
p->lines_to_print = 0; Line 1525
}
--files_ready_to_read; Line 1528
} Block 18
/* Put a file on hold until we start a new page,
since we've hit a form feed.
If we aren't dealing with parallel files, we must change the
status of all columns in the column list. */
static void Line 1537
hold_file (COLUMN *p) Line 1538
{
COLUMN *q; Line 1540
int i; Line 1541
if (!parallel_files) Line 1543
for (q = column_vector, i = columns; i; ++q, --i) Line 1544
{
if (storing_columns) Line 1546
q->status = FF_FOUND; Line 1547
else Line 1548
q->status = ON_HOLD; Line 1549
}
else Line 1551
p->status = ON_HOLD; Line 1552
p->lines_to_print = 0; Line 1554
--files_ready_to_read; Line 1555
} Block 19
/* Undo hold_file -- go through the column list and change any
ON_HOLD columns to OPEN. Used at the end of each page. */
static void Line 1561
reset_status (void) Line 1562
{
int i = columns; Line 1564
COLUMN *p; Line 1565
for (p = column_vector; i; --i, ++p) Line 1567
if (p->status == ON_HOLD) Line 1568
{
p->status = OPEN; Line 1570
files_ready_to_read++; Line 1571
}
if (storing_columns) Line 1574
{
if (column_vector->status == CLOSED) Line 1576
/* We use the info to output an error message in skip_to_page. */
files_ready_to_read = 0; Line 1578
else Line 1579
files_ready_to_read = 1; Line 1580
}
} Block 20
/* Print a single file, or multiple files in parallel.
Set up the list of columns, opening the necessary files.
Allocate space for storing columns, if necessary.
Skip to first_page_number, if user has asked to skip leading pages.
Determine which functions are appropriate to store/print lines
in each column.
Print the file(s). */
static void Line 1593
print_files (int number_of_files, char **av) Line 1594
{
init_parameters (number_of_files); Line 1596
if (! init_fps (number_of_files, av)) Line 1597
return; Line 1598
if (storing_columns) Line 1599
init_store_cols (); Line 1600
if (first_page_number > 1) Line 1602
{
if (!skip_to_page (first_page_number)) Line 1604
return; Line 1605
else Line 1606
page_number = first_page_number; Line 1607
}
else Line 1609
page_number = 1; Line 1610
init_funcs (); Line 1612
line_number = line_count; Line 1614
while (print_page ()) Line 1615
;
} Block 21
/* Initialize header information.
If DESC is non-negative, it is a file descriptor open to
FILENAME for reading. */
static void Line 1623
init_header (char const *filename, int desc) Line 1624
{
char *buf = NULL; Line 1626
struct stat st; Line 1627
struct timespec t; Line 1628
int ns; Line 1629
struct tm tm; Line 1630
/* If parallel files or standard input, use current date. */
if (STREQ (filename, "-")) Line 1633
desc = -1; Line 1634
if (0 <= desc && fstat (desc, &st) == 0) Line 1635...!syscalls auto-comment......!syscalls auto-comment...
t = get_stat_mtime (&st); Line 1636
else Line 1637
{
static struct timespec timespec; Line 1639
if (! timespec.tv_sec) Line 1640
gettime (×pec); Line 1641
t = timespec; Line 1642
}
ns = t.tv_nsec; Line 1645
if (localtime_rz (localtz, &t.tv_sec, &tm)) Line 1646
{
size_t bufsize Line 1648
= nstrftime (NULL, SIZE_MAX, date_format, &tm, localtz, ns) + 1; Line 1649
buf = xmalloc (bufsize); Line 1650
nstrftime (buf, bufsize, date_format, &tm, localtz, ns); Line 1651
}
else Line 1653
{
char secbuf[INT_BUFSIZE_BOUND (intmax_t)]; Line 1655
buf = xmalloc (sizeof secbuf + MAX (10, INT_BUFSIZE_BOUND (int))); Line 1656
sprintf (buf, "%s.%09d", timetostr (t.tv_sec, secbuf), ns); Line 1657
}
free (date_text); Line 1660
date_text = buf; Line 1661
file_text = custom_header ? custom_header : desc < 0 ? "" : filename; Line 1662
header_width_available = (chars_per_line Line 1663
- mbswidth (date_text, 0) Line 1664
- mbswidth (file_text, 0)); Line 1665
} Block 22
/* Set things up for printing a page
Scan through the columns ...
Determine which are ready to print
(i.e., which have lines stored or open files)
Set p->lines_to_print appropriately
(to p->lines_stored if we're storing, or lines_per_body
if we're reading straight from the file)
Keep track of this total so we know when to stop printing */
static void Line 1678
init_page (void) Line 1679
{
int j; Line 1681
COLUMN *p; Line 1682
if (storing_columns) Line 1684
{
store_columns (); Line 1686
for (j = columns - 1, p = column_vector; j; --j, ++p) Line 1687
{
p->lines_to_print = p->lines_stored; Line 1689
}
/* Last column. */
if (balance_columns) Line 1693
{
p->lines_to_print = p->lines_stored; Line 1695
}
/* Since we're not balancing columns, we don't need to store
the rightmost column. Read it straight from the file. */
else Line 1699
{
if (p->status == OPEN) Line 1701
{
p->lines_to_print = lines_per_body; Line 1703
}
else Line 1705
p->lines_to_print = 0; Line 1706
}
}
else Line 1709
for (j = columns, p = column_vector; j; --j, ++p) Line 1710
if (p->status == OPEN) Line 1711
{
p->lines_to_print = lines_per_body; Line 1713
}
else Line 1715
p->lines_to_print = 0; Line 1716
} Block 23
/* Align empty columns and print separators.
Empty columns will be formed by files with status ON_HOLD or CLOSED
when printing multiple files in parallel. */
static void Line 1723
align_column (COLUMN *p) Line 1724
{
padding_not_printed = p->start_position; Line 1726
if (col_sep_length < padding_not_printed) Line 1727
{
pad_across_to (padding_not_printed - col_sep_length); Line 1729
padding_not_printed = ANYWHERE; Line 1730
}
if (use_col_separator) Line 1733
print_sep_string (); Line 1734
if (p->numbered) Line 1736
add_line_number (p); Line 1737
} Block 24
/* Print one page.
As long as there are lines left on the page and columns ready to print,
Scan across the column list
if the column has stored lines or the file is open
pad to the appropriate spot
print the column
pad the remainder of the page with \n or \f as requested
reset the status of all files -- any files which where on hold because
of formfeeds are now put back into the lineup. */
static bool Line 1751
print_page (void) Line 1752
{
int j; Line 1754
int lines_left_on_page; Line 1755
COLUMN *p; Line 1756
/* Used as an accumulator (with | operator) of successive values of
pad_vertically. The trick is to set pad_vertically
to false before each run through the inner loop, then after that
loop, it tells us whether a line was actually printed (whether a
newline needs to be output -- or two for double spacing). But those
values have to be accumulated (in pv) so we can invoke pad_down
properly after the outer loop completes. */
bool pv; Line 1765
init_page (); Line 1767
if (cols_ready_to_print () == 0) Line 1769
return false; Line 1770
if (extremities) Line 1772
print_a_header = true; Line 1773
/* Don't pad unless we know a page was printed. */
pad_vertically = false; Line 1776
pv = false; Line 1777
lines_left_on_page = lines_per_body; Line 1779
if (double_space) Line 1780
lines_left_on_page *= 2; Line 1781
while (lines_left_on_page > 0 && cols_ready_to_print () > 0) Line 1783
{
output_position = 0; Line 1785
spaces_not_printed = 0; Line 1786
separators_not_printed = 0; Line 1787
pad_vertically = false; Line 1788
align_empty_cols = false; Line 1789
empty_line = true; Line 1790
for (j = 1, p = column_vector; j <= columns; ++j, ++p) Line 1792
{
input_position = 0; Line 1794
if (p->lines_to_print > 0 || p->status == FF_FOUND) Line 1795
{
FF_only = false; Line 1797
padding_not_printed = p->start_position; Line 1798
if (!(p->print_func) (p)) Line 1799
read_rest_of_line (p); Line 1800
pv |= pad_vertically; Line 1801
--p->lines_to_print; Line 1803
if (p->lines_to_print <= 0) Line 1804
{
if (cols_ready_to_print () == 0) Line 1806
break; Line 1807
}
/* File p changed its status to ON_HOLD or CLOSED */
if (parallel_files && p->status != OPEN) Line 1811
{
if (empty_line) Line 1813
align_empty_cols = true; Line 1814
else if (p->status == CLOSED Line 1815
|| (p->status == ON_HOLD && FF_only)) Line 1816
align_column (p); Line 1817
}
}
else if (parallel_files) Line 1820
{
/* File status ON_HOLD or CLOSED */
if (empty_line) Line 1823
align_empty_cols = true; Line 1824
else Line 1825
align_column (p); Line 1826
}
/* We need it also with an empty column */
if (use_col_separator) Line 1830
++separators_not_printed; Line 1831
}
if (pad_vertically) Line 1834
{
putchar ('\n'); Line 1836
--lines_left_on_page; Line 1837
}
if (cols_ready_to_print () == 0 && !extremities) Line 1840
break; Line 1841
if (double_space && pv) Line 1843
{
putchar ('\n'); Line 1845
--lines_left_on_page; Line 1846
}
}
if (lines_left_on_page == 0) Line 1850
for (j = 1, p = column_vector; j <= columns; ++j, ++p) Line 1851
if (p->status == OPEN) Line 1852
p->full_page_printed = true; Line 1853
pad_vertically = pv; Line 1855
if (pad_vertically && extremities) Line 1857
pad_down (lines_left_on_page + lines_per_footer); Line 1858
else if (keep_FF && print_a_FF) Line 1859
{
putchar ('\f'); Line 1861
print_a_FF = false; Line 1862
}
if (last_page_number < ++page_number) Line 1865
return false; /* Stop printing with LAST_PAGE */ Line 1866
reset_status (); /* Change ON_HOLD to OPEN. */ Line 1868
return true; /* More pages to go. */ Line 1870
} Block 25
/* Allocate space for storing columns.
This is necessary when printing multiple columns from a single file.
Lines are stored consecutively in buff, separated by '\0'.
The following doesn't apply any longer - any tuning possible?
(We can't use a fixed offset since with the '-s' flag lines aren't
truncated.)
We maintain a list (line_vector) of pointers to the beginnings
of lines in buff. We allocate one more than the number of lines
because the last entry tells us the index of the last character,
which we need to know in order to print the last line in buff. */
static void Line 1887
init_store_cols (void) Line 1888
{
int total_lines, total_lines_1, chars_per_column_1, chars_if_truncate; Line 1890
if (INT_MULTIPLY_WRAPV (lines_per_body, columns, &total_lines) Line 1891
|| INT_ADD_WRAPV (total_lines, 1, &total_lines_1) Line 1892
|| INT_ADD_WRAPV (chars_per_column, 1, &chars_per_column_1) Line 1893
|| INT_MULTIPLY_WRAPV (total_lines, chars_per_column_1, Line 1894
&chars_if_truncate)) Line 1895
integer_overflow (); Line 1896
free (line_vector); Line 1898
/* FIXME: here's where it was allocated. */
line_vector = xnmalloc (total_lines_1, sizeof *line_vector); Line 1900
free (end_vector); Line 1902
end_vector = xnmalloc (total_lines, sizeof *end_vector); Line 1903
free (buff); Line 1905
buff = xnmalloc (chars_if_truncate, use_col_separator + 1); Line 1906
buff_allocated = chars_if_truncate; /* Tune this. */ Line 1907
buff_allocated *= use_col_separator + 1; Line 1908
} Block 26
/* Store all but the rightmost column.
(Used when printing a single file in multiple downward columns)
For each column
set p->current_line to be the index in line_vector of the
first line in the column
For each line in the column
store the line in buff
add to line_vector the index of the line's first char
buff_start is the index in buff of the first character in the
current line. */
static void Line 1923
store_columns (void) Line 1924
{
int i, j; Line 1926
unsigned int line = 0; Line 1927
unsigned int buff_start; Line 1928
int last_col; /* The rightmost column which will be saved in buff */ Line 1929
COLUMN *p; Line 1930
buff_current = 0; Line 1932
buff_start = 0; Line 1933
if (balance_columns) Line 1935
last_col = columns; Line 1936
else Line 1937
last_col = columns - 1; Line 1938
for (i = 1, p = column_vector; i <= last_col; ++i, ++p) Line 1940
p->lines_stored = 0; Line 1941
for (i = 1, p = column_vector; i <= last_col && files_ready_to_read; Line 1943
++i, ++p) Line 1944
{
p->current_line = line; Line 1946
for (j = lines_per_body; j && files_ready_to_read; --j) Line 1947
if (p->status == OPEN) /* Redundant. Clean up. */ Line 1949
{
input_position = 0; Line 1951
if (!read_line (p)) Line 1953
read_rest_of_line (p); Line 1954
if (p->status == OPEN Line 1956
|| buff_start != buff_current) Line 1957
{
++p->lines_stored; Line 1959
line_vector[line] = buff_start; Line 1960
end_vector[line++] = input_position; Line 1961
buff_start = buff_current; Line 1962
}
}
}
/* Keep track of the location of the last char in buff. */
line_vector[line] = buff_start; Line 1968
if (balance_columns) Line 1970
balance (line); Line 1971
} Block 27
static void Line 1974
balance (int total_stored) Line 1975
{
COLUMN *p; Line 1977
int i, lines; Line 1978
int first_line = 0; Line 1979
for (i = 1, p = column_vector; i <= columns; ++i, ++p) Line 1981
{
lines = total_stored / columns; Line 1983
if (i <= total_stored % columns) Line 1984
++lines; Line 1985
p->lines_stored = lines; Line 1987
p->current_line = first_line; Line 1988
first_line += lines; Line 1990
}
} Block 28
/* Store a character in the buffer. */
static void Line 1996
store_char (char c) Line 1997
{
if (buff_current >= buff_allocated) Line 1999
{
/* May be too generous. */
buff = X2REALLOC (buff, &buff_allocated); Line 2002
}
buff[buff_current++] = c; Line 2004
} Block 29
static void Line 2007
add_line_number (COLUMN *p) Line 2008
{
int i; Line 2010
char *s; Line 2011
int num_width; Line 2012
/* Cutting off the higher-order digits is more informative than
lower-order cut off. */
num_width = sprintf (number_buff, "%*d", chars_per_number, line_number); Line 2016
line_number++; Line 2017
s = number_buff + (num_width - chars_per_number); Line 2018
for (i = chars_per_number; i > 0; i--) Line 2019
(p->char_func) (*s++); Line 2020
if (columns > 1) Line 2022
{
/* Tabification is assumed for multiple columns, also for n-separators,
but 'default n-separator = TAB' hasn't been given priority over
equal column_width also specified by POSIX. */
if (number_separator == '\t') Line 2027
{
i = number_width - chars_per_number; Line 2029
while (i-- > 0) Line 2030
(p->char_func) (' '); Line 2031
}
else Line 2033
(p->char_func) (number_separator); Line 2034
}
else Line 2036
/* To comply with POSIX, we avoid any expansion of default TAB
separator with a single column output. No column_width requirement
has to be considered. */
{
(p->char_func) (number_separator); Line 2041
if (number_separator == '\t') Line 2042
output_position = POS_AFTER_TAB (chars_per_output_tab, Line 2043
output_position); Line 2044
}
if (truncate_lines && !parallel_files) Line 2047
input_position += number_width; Line 2048
} Block 30
/* Print (or store) padding until the current horizontal position
is position. */
static void Line 2054
pad_across_to (int position) Line 2055
{
int h = output_position; Line 2057
if (tabify_output) Line 2059
spaces_not_printed = position - output_position; Line 2060
else Line 2061
{
while (++h <= position) Line 2063
putchar (' '); Line 2064
output_position = position; Line 2065
}
} Block 31
/* Pad to the bottom of the page.
If the user has requested a formfeed, use one.
Otherwise, use newlines. */
static void Line 2074
pad_down (unsigned int lines) Line 2075
{
if (use_form_feed) Line 2077
putchar ('\f'); Line 2078
else Line 2079
for (unsigned int i = lines; i; --i) Line 2080
putchar ('\n'); Line 2081
} Block 32
/* Read the rest of the line.
Read from the current column's file until an end of line is
hit. Used when we've truncated a line and we no longer need
to print or store its characters. */
static void Line 2090
read_rest_of_line (COLUMN *p) Line 2091
{
int c; Line 2093
FILE *f = p->fp; Line 2094
while ((c = getc (f)) != '\n') Line 2096
{
if (c == '\f') Line 2098
{
if ((c = getc (f)) != '\n') Line 2100
ungetc (c, f); Line 2101
if (keep_FF) Line 2102
print_a_FF = true; Line 2103
hold_file (p); Line 2104
break; Line 2105
}
else if (c == EOF) Line 2107
{
close_file (p); Line 2109
break; Line 2110
}
}
} Block 33
/* Read a line with skip_to_page.
Read from the current column's file until an end of line is
hit. Used when we read full lines to skip pages.
With skip_to_page we have to check for FF-coincidence which is done
in function read_line otherwise.
Count lines of skipped pages to find the line number of 1st page
printed relative to 1st line of input file (start_line_num). */
static void Line 2124
skip_read (COLUMN *p, int column_number) Line 2125...!syscalls auto-comment...
{
int c; Line 2127
FILE *f = p->fp; Line 2128
int i; Line 2129
bool single_ff = false; Line 2130
COLUMN *q; Line 2131
/* Read 1st character in a line or any character succeeding a FF */
if ((c = getc (f)) == '\f' && p->full_page_printed) Line 2134
/* A FF-coincidence with a previous full_page_printed.
To avoid an additional empty page, eliminate the FF */
if ((c = getc (f)) == '\n') Line 2137
c = getc (f); Line 2138
p->full_page_printed = false; Line 2140
/* 1st character a FF means a single FF without any printable
characters. Don't count it as a line with -n option. */
if (c == '\f') Line 2144
single_ff = true; Line 2145
/* Preparing for a FF-coincidence: Maybe we finish that page
without a FF found */
if (last_line) Line 2149
p->full_page_printed = true; Line 2150
while (c != '\n') Line 2152
{
if (c == '\f') Line 2154
{
/* No FF-coincidence possible,
no catching up of a FF-coincidence with next page */
if (last_line) Line 2158
{
if (!parallel_files) Line 2160
for (q = column_vector, i = columns; i; ++q, --i) Line 2161
q->full_page_printed = false; Line 2162
else Line 2163
p->full_page_printed = false; Line 2164
}
if ((c = getc (f)) != '\n') Line 2167
ungetc (c, f); Line 2168
hold_file (p); Line 2169
break; Line 2170
}
else if (c == EOF) Line 2172
{
close_file (p); Line 2174
break; Line 2175
}
c = getc (f); Line 2177
}
if (skip_count) Line 2180
if ((!parallel_files || column_number == 1) && !single_ff) Line 2181
++line_count; Line 2182
} Block 34
/* If we're tabifying output,
When print_char encounters white space it keeps track
of our desired horizontal position and delays printing
until this function is called. */
static void Line 2191
print_white_space (void) Line 2192
{
int h_new; Line 2194
int h_old = output_position; Line 2195
int goal = h_old + spaces_not_printed; Line 2196
while (goal - h_old > 1 Line 2198
&& (h_new = POS_AFTER_TAB (chars_per_output_tab, h_old)) <= goal) Line 2199
{
putchar (output_tab_char); Line 2201
h_old = h_new; Line 2202
}
while (++h_old <= goal) Line 2204
putchar (' '); Line 2205
output_position = goal; Line 2207
spaces_not_printed = 0; Line 2208
} Block 35
/* Print column separators.
We keep a count until we know that we'll be printing a line,
then print_sep_string() is called. */
static void Line 2216
print_sep_string (void) Line 2217
{
char const *s = col_sep_string; Line 2219
int l = col_sep_length; Line 2220
if (separators_not_printed <= 0) Line 2222
{
/* We'll be starting a line with chars_per_margin, anything else? */
if (spaces_not_printed > 0) Line 2225
print_white_space (); Line 2226
}
else Line 2228
{
for (; separators_not_printed > 0; --separators_not_printed) Line 2230
{
while (l-- > 0) Line 2232
{
/* 3 types of sep_strings: spaces only, spaces and chars,
chars only */
if (*s == ' ') Line 2236
{
/* We're tabifying output; consecutive spaces in
sep_string may have to be converted to tabs */
s++; Line 2240
++spaces_not_printed; Line 2241
}
else Line 2243
{
if (spaces_not_printed > 0) Line 2245
print_white_space (); Line 2246
putchar (*s++); Line 2247
++output_position; Line 2248
}
}
/* sep_string ends with some spaces */
if (spaces_not_printed > 0) Line 2252
print_white_space (); Line 2253
}
}
} Block 36
/* Print (or store, depending on p->char_func) a clump of N
characters. */
static void Line 2261
print_clump (COLUMN *p, int n, char *clump) Line 2262
{
while (n--) Line 2264
(p->char_func) (*clump++); Line 2265
} Block 37
/* Print a character.
Update the following comment: process-char hasn't been used any
longer.
If we're tabifying, all tabs have been converted to spaces by
process_char(). Keep a count of consecutive spaces, and when
a nonspace is encountered, call print_white_space() to print the
required number of tabs and spaces. */
static void Line 2277
print_char (char c) Line 2278
{
if (tabify_output) Line 2280
{
if (c == ' ') Line 2282
{
++spaces_not_printed; Line 2284
return; Line 2285
}
else if (spaces_not_printed > 0) Line 2287
print_white_space (); Line 2288
/* Nonprintables are assumed to have width 0, except '\b'. */
if (! isprint (to_uchar (c))) Line 2291
{
if (c == '\b') Line 2293
--output_position; Line 2294
}
else Line 2296
++output_position; Line 2297
}
putchar (c); Line 2299
} Block 38
/* Skip to page PAGE before printing.
PAGE may be larger than total number of pages. */
static bool Line 2305
skip_to_page (uintmax_t page) Line 2306
{
for (uintmax_t n = 1; n < page; ++n) Line 2308
{
COLUMN *p; Line 2310
int j; Line 2311
for (int i = 1; i < lines_per_body; ++i) Line 2313
{
for (j = 1, p = column_vector; j <= columns; ++j, ++p) Line 2315
if (p->status == OPEN) Line 2316
skip_read (p, j); Line 2317...!syscalls auto-comment...
}
last_line = true; Line 2319
for (j = 1, p = column_vector; j <= columns; ++j, ++p) Line 2320
if (p->status == OPEN) Line 2321
skip_read (p, j); Line 2322...!syscalls auto-comment...
if (storing_columns) /* change FF_FOUND to ON_HOLD */ Line 2324
for (j = 1, p = column_vector; j <= columns; ++j, ++p) Line 2325
if (p->status != CLOSED) Line 2326
p->status = ON_HOLD; Line 2327
reset_status (); Line 2329
last_line = false; Line 2330
if (files_ready_to_read < 1) Line 2332
{
/* It's very helpful, normally the total number of pages is
not known in advance. */
error (0, 0, Line 2336
_("starting page number %"PRIuMAX Line 2337
" exceeds page count %"PRIuMAX), Line 2338
page, n); Line 2339
break; Line 2340
}
}
return files_ready_to_read > 0; Line 2343
} Block 39
/* Print a header.
Formfeeds are assumed to use up two lines at the beginning of
the page. */
static void Line 2351
print_header (void) Line 2352
{
char page_text[256 + INT_STRLEN_BOUND (page_number)]; Line 2354
int available_width; Line 2355
int lhs_spaces; Line 2356
int rhs_spaces; Line 2357
output_position = 0; Line 2359
pad_across_to (chars_per_margin); Line 2360
print_white_space (); Line 2361
if (page_number == 0) Line 2363
die (EXIT_FAILURE, 0, _("page number overflow")); Line 2364
/* The translator must ensure that formatting the translation of
"Page %"PRIuMAX does not generate more than (sizeof page_text - 1)
bytes. */
sprintf (page_text, _("Page %"PRIuMAX), page_number); Line 2369
available_width = header_width_available - mbswidth (page_text, 0); Line 2370
available_width = MAX (0, available_width); Line 2371
lhs_spaces = available_width >> 1; Line 2372
rhs_spaces = available_width - lhs_spaces; Line 2373
printf ("\n\n%*s%s%*s%s%*s%s\n\n\n", Line 2375
chars_per_margin, "", Line 2376
date_text, lhs_spaces, " ", Line 2377
file_text, rhs_spaces, " ", page_text); Line 2378
print_a_header = false; Line 2380
output_position = 0; Line 2381
} Block 40
/* Print (or store, if p->char_func is store_char()) a line.
Read a character to determine whether we have a line or not.
(We may hit EOF, \n, or \f)
Once we know we have a line,
set pad_vertically = true, meaning it's safe
to pad down at the end of the page, since we do have a page.
print a header if needed.
pad across to padding_not_printed if needed.
print any separators which need to be printed.
print a line number if it needs to be printed.
Print the clump which corresponds to the first character.
Enter a loop and keep printing until an end of line condition
exists, or until we exceed chars_per_column.
Return false if we exceed chars_per_column before reading
an end of line character, true otherwise. */
static bool Line 2405
read_line (COLUMN *p) Line 2406
{
int c; Line 2408
int chars IF_LINT ( = 0); Line 2409
int last_input_position; Line 2410
int j, k; Line 2411
COLUMN *q; Line 2412
/* read 1st character in each line or any character succeeding a FF: */
c = getc (p->fp); Line 2415
last_input_position = input_position; Line 2417
if (c == '\f' && p->full_page_printed) Line 2419
if ((c = getc (p->fp)) == '\n') Line 2420
c = getc (p->fp); Line 2421
p->full_page_printed = false; Line 2422
switch (c) Line 2424
{
case '\f': Line 2426
if ((c = getc (p->fp)) != '\n') Line 2427
ungetc (c, p->fp); Line 2428
FF_only = true; Line 2429
if (print_a_header && !storing_columns) Line 2430
{
pad_vertically = true; Line 2432
print_header (); Line 2433
}
else if (keep_FF) Line 2435
print_a_FF = true; Line 2436
hold_file (p); Line 2437
return true; Line 2438
case EOF: Line 2439
close_file (p); Line 2440
return true; Line 2441
case '\n': Line 2442
break; Line 2443
default: Line 2444
chars = char_to_clump (c); Line 2445
}
if (truncate_lines && input_position > chars_per_column) Line 2448
{
input_position = last_input_position; Line 2450
return false; Line 2451
}
if (p->char_func != store_char) Line 2454
{
pad_vertically = true; Line 2456
if (print_a_header && !storing_columns) Line 2458
print_header (); Line 2459
if (parallel_files && align_empty_cols) Line 2461
{
/* We have to align empty columns at the beginning of a line. */
k = separators_not_printed; Line 2464
separators_not_printed = 0; Line 2465
for (j = 1, q = column_vector; j <= k; ++j, ++q) Line 2466
{
align_column (q); Line 2468
separators_not_printed += 1; Line 2469
}
padding_not_printed = p->start_position; Line 2471
if (truncate_lines) Line 2472
spaces_not_printed = chars_per_column; Line 2473
else Line 2474
spaces_not_printed = 0; Line 2475
align_empty_cols = false; Line 2476
}
if (col_sep_length < padding_not_printed) Line 2479
{
pad_across_to (padding_not_printed - col_sep_length); Line 2481
padding_not_printed = ANYWHERE; Line 2482
}
if (use_col_separator) Line 2485
print_sep_string (); Line 2486
}
if (p->numbered) Line 2489
add_line_number (p); Line 2490
empty_line = false; Line 2492
if (c == '\n') Line 2493
return true; Line 2494
print_clump (p, chars, clump_buff); Line 2496
while (true) Line 2498
{
c = getc (p->fp); Line 2500
switch (c) Line 2502
{
case '\n': Line 2504
return true; Line 2505
case '\f': Line 2506
if ((c = getc (p->fp)) != '\n') Line 2507
ungetc (c, p->fp); Line 2508
if (keep_FF) Line 2509
print_a_FF = true; Line 2510
hold_file (p); Line 2511
return true; Line 2512
case EOF: Line 2513
close_file (p); Line 2514
return true; Line 2515
}
last_input_position = input_position; Line 2518
chars = char_to_clump (c); Line 2519
if (truncate_lines && input_position > chars_per_column) Line 2520
{
input_position = last_input_position; Line 2522
return false; Line 2523
}
print_clump (p, chars, clump_buff); Line 2526
}
} Block 41
/* Print a line from buff.
If this function has been called, we know we have "something to
print". But it remains to be seen whether we have a real text page
or an empty page (a single form feed) with/without a header only.
Therefore first we set pad_vertically to true and print a header
if necessary.
If FF_FOUND and we are using -t|-T option we omit any newline by
setting pad_vertically to false (see print_page).
Otherwise we pad across if necessary, print separators if necessary
and text of COLUMN *p.
Return true, meaning there is no need to call read_rest_of_line. */
static bool Line 2544
print_stored (COLUMN *p) Line 2545
{
COLUMN *q; Line 2547
int line = p->current_line++; Line 2549
char *first = &buff[line_vector[line]]; Line 2550
/* FIXME
UMR: Uninitialized memory read:
* This is occurring while in:
print_stored [pr.c:2239]
* Reading 4 bytes from 0x5148c in the heap.
* Address 0x5148c is 4 bytes into a malloc'd block at 0x51488 of 676 bytes
* This block was allocated from:
malloc [rtlib.o]
xmalloc [xmalloc.c:94]
init_store_cols [pr.c:1648]
*/
char *last = &buff[line_vector[line + 1]]; Line 2562
pad_vertically = true; Line 2564
if (print_a_header) Line 2566
print_header (); Line 2567
if (p->status == FF_FOUND) Line 2569
{
int i; Line 2571
for (i = 1, q = column_vector; i <= columns; ++i, ++q) Line 2572
q->status = ON_HOLD; Line 2573
if (column_vector->lines_to_print <= 0) Line 2574
{
if (!extremities) Line 2576
pad_vertically = false; Line 2577
return true; /* print a header only */ Line 2578
}
}
if (col_sep_length < padding_not_printed) Line 2582
{
pad_across_to (padding_not_printed - col_sep_length); Line 2584
padding_not_printed = ANYWHERE; Line 2585
}
if (use_col_separator) Line 2588
print_sep_string (); Line 2589
while (first != last) Line 2591
print_char (*first++); Line 2592
if (spaces_not_printed == 0) Line 2594
{
output_position = p->start_position + end_vector[line]; Line 2596
if (p->start_position - col_sep_length == chars_per_margin) Line 2597
output_position -= col_sep_length; Line 2598
}
return true; Line 2601
} Block 42
/* Convert a character to the proper format and return the number of
characters in the resulting clump. Increment input_position by
the width of the clump.
Tabs are converted to clumps of spaces.
Nonprintable characters may be converted to clumps of escape
sequences or control prefixes.
Note: the width of a clump is not necessarily equal to the number of
characters in clump_buff. (e.g, the width of '\b' is -1, while the
number of characters is 1.) */
static int Line 2616
char_to_clump (char c) Line 2617
{
unsigned char uc = c; Line 2619
char *s = clump_buff; Line 2620
int i; Line 2621
char esc_buff[4]; Line 2622
int width; Line 2623
int chars; Line 2624
int chars_per_c = 8; Line 2625
if (c == input_tab_char) Line 2627
chars_per_c = chars_per_input_tab; Line 2628
if (c == input_tab_char || c == '\t') Line 2630
{
width = TAB_WIDTH (chars_per_c, input_position); Line 2632
if (untabify_input) Line 2634
{
for (i = width; i; --i) Line 2636
*s++ = ' '; Line 2637
chars = width; Line 2638
}
else Line 2640
{
*s = c; Line 2642
chars = 1; Line 2643
}
}
else if (! isprint (uc)) Line 2647
{
if (use_esc_sequence) Line 2649
{
width = 4; Line 2651
chars = 4; Line 2652
*s++ = '\\'; Line 2653
sprintf (esc_buff, "%03o", uc); Line 2654
for (i = 0; i <= 2; ++i) Line 2655
*s++ = esc_buff[i]; Line 2656
}
else if (use_cntrl_prefix) Line 2658
{
if (uc < 0200) Line 2660
{
width = 2; Line 2662
chars = 2; Line 2663
*s++ = '^'; Line 2664
*s = c ^ 0100; Line 2665
}
else Line 2667
{
width = 4; Line 2669
chars = 4; Line 2670
*s++ = '\\'; Line 2671
sprintf (esc_buff, "%03o", uc); Line 2672
for (i = 0; i <= 2; ++i) Line 2673
*s++ = esc_buff[i]; Line 2674
}
}
else if (c == '\b') Line 2677
{
width = -1; Line 2679
chars = 1; Line 2680
*s = c; Line 2681
}
else Line 2683
{
width = 0; Line 2685
chars = 1; Line 2686
*s = c; Line 2687
}
}
else Line 2690
{
width = 1; Line 2692
chars = 1; Line 2693
*s = c; Line 2694
}
/* Too many backspaces must put us in position 0 -- never negative. */
if (width < 0 && input_position == 0) Line 2698
{
chars = 0; Line 2700
input_position = 0; Line 2701
}
else if (width < 0 && input_position <= -width) Line 2703
input_position = 0; Line 2704
else Line 2705
input_position += width; Line 2706
return chars; Line 2708
} Block 43
/* We've just printed some files and need to clean up things before
looking for more options and printing the next batch of files.
Free everything we've xmalloc'ed, except 'header'. */
static void Line 2716
cleanup (void) Line 2717
{
free (number_buff); Line 2719
free (clump_buff); Line 2720
free (column_vector); Line 2721
free (line_vector); Line 2722
free (end_vector); Line 2723
free (buff); Line 2724
} Block 44
/* Complain, print a usage message, and die. */
void Line 2729
usage (int status) Line 2730
{
if (status != EXIT_SUCCESS) Line 2732
emit_try_help (); ...!common auto-comment...
else Line 2734
{
printf (_("\ Line 2736
Usage: %s [OPTION]... [FILE]...\n\ Line 2737
"), Line 2738
program_name); Line 2739
fputs (_("\ Line 2741
Paginate or columnate FILE(s) for printing.\n\ Line 2742
"), stdout); Line 2743
emit_stdin_note (); ...!common auto-comment...
emit_mandatory_arg_note (); ...!common auto-comment...
fputs (_("\ Line 2748
+FIRST_PAGE[:LAST_PAGE], --pages=FIRST_PAGE[:LAST_PAGE]\n\ Line 2749
begin [stop] printing with page FIRST_[LAST_]PAGE\n\ Line 2750
-COLUMN, --columns=COLUMN\n\ Line 2751
output COLUMN columns and print columns down,\n\ Line 2752
unless -a is used. Balance number of lines in the\n\ Line 2753
columns on each page\n\ Line 2754
"), stdout); Line 2755
fputs (_("\ Line 2756
-a, --across print columns across rather than down, used together\n\ Line 2757
with -COLUMN\n\ Line 2758
-c, --show-control-chars\n\ Line 2759
use hat notation (^G) and octal backslash notation\n\ Line 2760
-d, --double-space\n\ Line 2761
double space the output\n\ Line 2762
"), stdout); Line 2763
fputs (_("\ Line 2764
-D, --date-format=FORMAT\n\ Line 2765
use FORMAT for the header date\n\ Line 2766
-e[CHAR[WIDTH]], --expand-tabs[=CHAR[WIDTH]]\n\ Line 2767
expand input CHARs (TABs) to tab WIDTH (8)\n\ Line 2768
-F, -f, --form-feed\n\ Line 2769
use form feeds instead of newlines to separate pages\n\ Line 2770
(by a 3-line page header with -F or a 5-line header\n\ Line 2771
and trailer without -F)\n\ Line 2772
"), stdout); Line 2773
fputs (_("\ Line 2774
-h, --header=HEADER\n\ Line 2775
use a centered HEADER instead of filename in page header,\n\Line 2776
-h \"\" prints a blank line, don't use -h\"\"\n\ Line 2777
-i[CHAR[WIDTH]], --output-tabs[=CHAR[WIDTH]]\n\ Line 2778
replace spaces with CHARs (TABs) to tab WIDTH (8)\n\ Line 2779
-J, --join-lines merge full lines, turns off -W line truncation, no column\n\Line 2780
alignment, --sep-string[=STRING] sets separators\n\ Line 2781
"), stdout); Line 2782
fputs (_("\ Line 2783
-l, --length=PAGE_LENGTH\n\ Line 2784
set the page length to PAGE_LENGTH (66) lines\n\ Line 2785
(default number of lines of text 56, and with -F 63).\n\ Line 2786
implies -t if PAGE_LENGTH <= 10\n\ Line 2787
"), stdout); Line 2788
fputs (_("\ Line 2789
-m, --merge print all files in parallel, one in each column,\n\ Line 2790
truncate lines, but join lines of full length with -J\n\ Line 2791
"), stdout); Line 2792
fputs (_("\ Line 2793
-n[SEP[DIGITS]], --number-lines[=SEP[DIGITS]]\n\ Line 2794
number lines, use DIGITS (5) digits, then SEP (TAB),\n\ Line 2795
default counting starts with 1st line of input file\n\ Line 2796
-N, --first-line-number=NUMBER\n\ Line 2797
start counting with NUMBER at 1st line of first\n\ Line 2798
page printed (see +FIRST_PAGE)\n\ Line 2799
"), stdout); Line 2800
fputs (_("\ Line 2801
-o, --indent=MARGIN\n\ Line 2802
offset each line with MARGIN (zero) spaces, do not\n\ Line 2803
affect -w or -W, MARGIN will be added to PAGE_WIDTH\n\ Line 2804
-r, --no-file-warnings\n\ Line 2805
omit warning when a file cannot be opened\n\ Line 2806
"), stdout); Line 2807
fputs (_("\ Line 2808
-s[CHAR], --separator[=CHAR]\n\ Line 2809
separate columns by a single character, default for CHAR\n\ Line 2810
is the <TAB> character without -w and \'no char\' with -w.\ Line 2811
\n\
-s[CHAR] turns off line truncation of all 3 column\n\ Line 2813
options (-COLUMN|-a -COLUMN|-m) except -w is set\n\ Line 2814
"), stdout); Line 2815
fputs (_("\ Line 2816
-S[STRING], --sep-string[=STRING]\n\ Line 2817
separate columns by STRING,\n\ Line 2818
without -S: Default separator <TAB> with -J and <space>\n\ Line 2819
otherwise (same as -S\" \"), no effect on column options\n\ Line 2820
"), stdout); Line 2821
fputs (_("\ Line 2822
-t, --omit-header omit page headers and trailers;\n\ Line 2823
implied if PAGE_LENGTH <= 10\n\ Line 2824
"), stdout); Line 2825
fputs (_("\ Line 2826
-T, --omit-pagination\n\ Line 2827
omit page headers and trailers, eliminate any pagination\n\ Line 2828
by form feeds set in input files\n\ Line 2829
-v, --show-nonprinting\n\ Line 2830
use octal backslash notation\n\ Line 2831
-w, --width=PAGE_WIDTH\n\ Line 2832
set page width to PAGE_WIDTH (72) characters for\n\ Line 2833
multiple text-column output only, -s[char] turns off (72)\n\Line 2834
"), stdout); Line 2835
fputs (_("\ Line 2836
-W, --page-width=PAGE_WIDTH\n\ Line 2837
set page width to PAGE_WIDTH (72) characters always,\n\ Line 2838
truncate lines, except -J option is set, no interference\n\ Line 2839
with -S or -s\n\ Line 2840
"), stdout); Line 2841
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 2842
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 2843
emit_ancillary_info (PROGRAM_NAME); Line 2844
}
exit (status); Line 2846
} Block 45