LCOV - code coverage report
Current view: top level - src - pr.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 611 940 65.0 %
Date: 2018-01-30 Functions: 34 38 89.5 %

          Line data    Source code
       1             : /* pr -- convert text files for printing.
       2             :    Copyright (C) 88, 91, 1995-2008 Free Software Foundation, Inc.
       3             : 
       4             :    This program is free software: you can redistribute it and/or modify
       5             :    it under the terms of the GNU General Public License as published by
       6             :    the Free Software Foundation, either version 3 of the License, or
       7             :    (at your option) any later version.
       8             : 
       9             :    This program is distributed in the hope that it will be useful,
      10             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :    GNU General Public License for more details.
      13             : 
      14             :    You should have received a copy of the GNU General Public License
      15             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      16             : 
      17             : /*  By Pete TerMaat, with considerable refinement by Roland Huebner.  */
      18             : 
      19             : /* Things to watch: Sys V screws up on ...
      20             :    pr -n -3 -s: /usr/dict/words
      21             :    pr -m -o10 -n /usr/dict/words{,,,}
      22             :    pr -6 -a -n -o5 /usr/dict/words
      23             : 
      24             :    Ideas:
      25             : 
      26             :    Keep a things_to_do list of functions to call when we know we have
      27             :    something to print.  Cleaner than current series of checks.
      28             : 
      29             :    Improve the printing of control prefixes.
      30             : 
      31             :    Expand the file name in the centered header line to a full file name.
      32             : 
      33             : 
      34             :    Concept:
      35             : 
      36             :    If the input_tab_char differs from the default value TAB
      37             :    (`-e[CHAR[...]]' is used), any input text tab is expanded to the
      38             :    default width of 8 spaces (compare char_to_clump). - Same as SunOS
      39             :    does.
      40             : 
      41             :    The treatment of the number_separator (compare add_line_number):
      42             :    The default value TAB of the number_separator (`-n[SEP[...]]') doesn't
      43             :    be thought to be an input character. An optional `-e'-input has no
      44             :    effect.
      45             :    -  With single column output
      46             :       only one POSIX requirement has to be met:
      47             :    The default n-separator should be a TAB. The consequence is a
      48             :    different width between the number and the text if the output position
      49             :    of the separator changes, i.e. it depends upon the left margin used.
      50             :    That's not nice but easy-to-use together with the defaults of other
      51             :    utilities, e.g. sort or cut. - Same as SunOS does.
      52             :    -  With multicolumn output
      53             :       two conflicting POSIX requirements exist:
      54             :    First `default n-separator is TAB', second `output text columns shall
      55             :    be of equal width'. Moreover POSIX specifies the number+separator a
      56             :    part of the column, together with `-COLUMN' and `-a -COLUMN'.
      57             :    (With -m output the number shall occupy each line only once. Exactly
      58             :    the same situation as single column output exists.)
      59             :       GNU pr gives priority to the 2nd requirement and observes POSIX
      60             :    column definition. The n-separator TAB is expanded to the same number
      61             :    of spaces in each column using the default value 8. Tabification is
      62             :    only performed if it is compatible with the output position.
      63             :    Consequence: The output text columns are of equal width. The layout
      64             :    of a page does not change if the left margin varies. - Looks better
      65             :    than the SunOS approach.
      66             :       SunOS pr gives priority to the 1st requirement. n-separator TAB
      67             :    width varies with each column. Only the width of text part of the
      68             :    column is fixed.
      69             :    Consequence: The output text columns don't have equal width. The
      70             :    widths and the layout of the whole page varies with the left margin.
      71             :    An overflow of the line length (without margin) over the input value
      72             :    PAGE_WIDTH may occur.
      73             : 
      74             :    The interference of the POSIX-compliant small letter options -w and -s:
      75             :    (`interference' means `setting a _separator_ with -s switches off the
      76             :    column sturctur and the default - not generally - page_width,
      77             :    acts on -w option')
      78             :        options:       text form  / separator:     equivalent new options:
      79             :        -w l   -s[x]
      80             :     --------------------------------------------------------------------
      81             :     1.  --     --     columns    / space          --
      82             :                       trunc. to page_width = 72
      83             :     2.  --    -s[:]   full lines / TAB[:]         -J  --sep-string[="<TAB>"|:]
      84             :                       no truncation
      85             :     3.  -w l   --     columns    / space          -W l
      86             :                       trunc. to page_width = l
      87             :     4.  -w l  -s[:]   columns    / no sep.[:]     -W l  --sep-string[=:]
      88             :                       trunc. to page_width = l
      89             :     --------------------------------------------------------------------
      90             : 
      91             : 
      92             :    Options:
      93             : 
      94             :    Including version 1.22i:
      95             :    Some SMALL LETTER options has been redefined with the object of a
      96             :    better POSIX compliance. The output of some further cases has been
      97             :    adapted to other UNIXes. A violation of downward compatibility has to
      98             :    be accepted.
      99             :    Some NEW CAPITAL LETTER options ( -J, -S, -W) has been introduced to
     100             :    turn off unexpected interferences of small letter options (-s and -w
     101             :    together with the three column options).
     102             :    -N option and the second argument LAST_PAGE of +FIRST_PAGE offer more
     103             :    flexibility; The detailed handling of form feeds set in the input
     104             :    files requires -T option.
     105             : 
     106             :    Capital letter options dominate small letter ones.
     107             : 
     108             :    Some of the option-arguments cannot be specified as separate arguments
     109             :    from the preceding option letter (already stated in POSIX specification).
     110             : 
     111             :    Form feeds in the input cause page breaks in the output. Multiple
     112             :    form feeds produce empty pages.
     113             : 
     114             :    +FIRST_PAGE[:LAST_PAGE], --pages=FIRST_PAGE[:LAST_PAGE]
     115             :                 begin [stop] printing with page FIRST_[LAST_]PAGE
     116             : 
     117             :    -COLUMN, --columns=COLUMN
     118             :                 Produce output that is COLUMN columns wide and
     119             :                 print columns down, unless -a is used. Balance number of
     120             :                 lines in the columns on each page.
     121             : 
     122             :    -a, --across         Print columns across rather than down, used
     123             :                 together with -COLUMN. The input
     124             :                 one
     125             :                 two
     126             :                 three
     127             :                 four
     128             :                 will be printed with `-a -3' as
     129             :                 one     two     three
     130             :                 four
     131             : 
     132             :    -b           Balance columns on the last page.
     133             :                 -b is no longer an independent option. It's always used
     134             :                 together with -COLUMN (unless -a is used) to get a
     135             :                 consistent formulation with "FF set by hand" in input
     136             :                 files. Each formfeed found terminates the number of lines
     137             :                 to be read with the actual page. The situation for
     138             :                 printing columns down is equivalent to that on the last
     139             :                 page. So we need a balancing.
     140             : 
     141             :                 Keeping -b as an underground option guarantees some
     142             :                 downward compatibility. Utilities using pr with -b
     143             :                 (a most frequently used form) still work as usual.
     144             : 
     145             :    -c, --show-control-chars
     146             :                 Print unprintable characters as control prefixes.
     147             :                 Control-g is printed as ^G (use hat notation) and
     148             :                 octal backslash notation.
     149             : 
     150             :    -d, --double-space   Double space the output.
     151             : 
     152             :    -D FORMAT, --date-format=FORMAT  Use FORMAT for the header date.
     153             : 
     154             :    -e[CHAR[WIDTH]], --expand-tabs[=CHAR[WIDTH]]
     155             :                 Expand tabs to spaces on input.  Optional argument CHAR
     156             :                 is the input TAB character. (Default is TAB).  Optional
     157             :                 argument WIDTH is the input TAB character's width.
     158             :                 (Default is 8.)
     159             : 
     160             :    -F, -f, --form-feed  Use formfeeds instead of newlines to separate
     161             :                 pages. A three line HEADER is used, no TRAILER with -F,
     162             :                 without -F both HEADER and TRAILER are made of five lines.
     163             : 
     164             :    -h HEADER, --header=HEADER
     165             :                 Replace the filename in the header with the string HEADER.
     166             :                 A centered header is used.
     167             : 
     168             :    -i[CHAR[WIDTH]], --output-tabs[=CHAR[WIDTH]]
     169             :                 Replace spaces with tabs on output.  Optional argument
     170             :                 CHAR is the output TAB character. (Default is TAB).
     171             :                 Optional argument WIDTH is the output TAB character's
     172             :                 width. (Default is 8)
     173             : 
     174             :    -J, --join-lines     Merge lines of full length, turns off -W/-w
     175             :                 line truncation, no column alignment, --sep-string[=STRING]
     176             :                 sets separators, works with all column options
     177             :                 (-COLUMN | -a -COLUMN | -m).
     178             :                 -J has been introduced (together with -W and --sep-string) to
     179             :                 disentangle the old (POSIX compliant) options -w, -s
     180             :                 along with the 3 column options.
     181             : 
     182             :    -l PAGE_LENGTH, --length=PAGE_LENGTH
     183             :                 Set the page length to PAGE_LENGTH lines. Default is 66,
     184             :                 including 5 lines of HEADER and 5 lines of TRAILER
     185             :                 without -F, but only 3 lines of HEADER and no TRAILER
     186             :                 with -F (i.e the number of text lines defaults to 56 or
     187             :                 63 respectively).
     188             : 
     189             :    -m, --merge          Print files in parallel; pad_across_to align
     190             :                 columns; truncate lines and print separator strings;
     191             :                 Do it also with empty columns to get a continuous line
     192             :                 numbering and column marking by separators throughout
     193             :                 the whole merged file.
     194             : 
     195             :                 Empty pages in some input files produce empty columns
     196             :                 [marked by separators] in the merged pages. Completely
     197             :                 empty merged pages show no column separators at all.
     198             : 
     199             :                 The layout of a merged page is ruled by the largest form
     200             :                 feed distance of the single pages at that page. Shorter
     201             :                 columns will be filled up with empty lines.
     202             : 
     203             :                 Together with -J option join lines of full length and
     204             :                 set separators when -S option is used.
     205             : 
     206             :    -n[SEP[DIGITS]], --number-lines[=SEP[DIGITS]]
     207             :                 Provide DIGITS digit line numbering (default for DIGITS
     208             :                 is 5). With multicolumn output the number occupies the
     209             :                 first DIGITS column positions of each text column or only
     210             :                 each line of -m output.
     211             :                 With single column output the number precedes each line
     212             :                 just as -m output.
     213             :                 Optional argument SEP is the character appended to the
     214             :                 line number to separate it from the text followed.
     215             :                 The default separator is a TAB. In a strict sense a TAB
     216             :                 is always printed with single column output only. The
     217             :                 TAB-width varies with the TAB-position, e.g. with the
     218             :                 left margin specified by -o option.
     219             :                 With multicolumn output priority is given to `equal width
     220             :                 of output columns' (a POSIX specification). The TAB-width
     221             :                 is fixed to the value of the 1st column and does not
     222             :                 change with different values of left margin. That means a
     223             :                 fixed number of spaces is always printed in the place of
     224             :                 a TAB. The tabification depends upon the output
     225             :                 position.
     226             : 
     227             :                 Default counting of the line numbers starts with 1st
     228             :                 line of the input file (not the 1st line printed,
     229             :                 compare the --page option and -N option).
     230             : 
     231             :    -N NUMBER, --first-line-number=NUMBER
     232             :                 Start line counting with the number NUMBER at the 1st
     233             :                 line of first page printed (mostly not the 1st line of
     234             :                 the input file).
     235             : 
     236             :    -o MARGIN, --indent=MARGIN
     237             :                 Offset each line with a margin MARGIN spaces wide.
     238             :                 Total page width is the size of the margin plus the
     239             :                 PAGE_WIDTH set with -W/-w option.
     240             : 
     241             :    -r, --no-file-warnings
     242             :                 Omit warning when a file cannot be opened.
     243             : 
     244             :    -s[CHAR], --separator[=CHAR]
     245             :                 Separate columns by a single character CHAR, default for
     246             :                 CHAR is the TAB character without -w and 'no char' with -w.
     247             :                 Without `-s' default separator `space' is set.
     248             :                 -s[CHAR] turns off line truncation of all 3 column options
     249             :                 (-COLUMN|-a -COLUMN|-m) except -w is set. That is a POSIX
     250             :                 compliant formulation. The source code translates -s into
     251             :                 the new options -S and -J, also -W if required.
     252             : 
     253             :    -S STRING, --sep-string[=STRING]
     254             :                 Separate columns by any string STRING. The -S option
     255             :                 doesn't react upon the -W/-w option (unlike -s option
     256             :                 does). It defines a separator nothing else.
     257             :                 Without -S: Default separator TAB is used with -J and
     258             :                 `space' otherwise (same as -S" ").
     259             :                 With -S "": No separator is used.
     260             :                 Quotes should be used with blanks and some shell active
     261             :                 characters.
     262             :                 -S is problematic because in its obsolete form you
     263             :                 cannot use -S "STRING", but in its standard form you
     264             :                 must use -S "STRING" if STRING is empty.  Use
     265             :                 --sep-string to avoid the ambiguity.
     266             : 
     267             :    -t, --omit-header    Do not print headers or footers but retain form
     268             :                 feeds set in the input files.
     269             : 
     270             :    -T, --omit-pagination
     271             :                 Do not print headers or footers, eliminate any pagination
     272             :                 by form feeds set in the input files.
     273             : 
     274             :    -v, --show-nonprinting
     275             :                 Print unprintable characters as escape sequences. Use
     276             :                 octal backslash notation. Control-G becomes \007.
     277             : 
     278             :    -w PAGE_WIDTH, --width=PAGE_WIDTH
     279             :                 Set page width to PAGE_WIDTH characters for multiple
     280             :                 text-column output only (default for PAGE_WIDTH is 72).
     281             :                 -s[CHAR] turns off the default page width and any line
     282             :                 truncation. Lines of full length will be merged,
     283             :                 regardless of the column options set. A POSIX compliant
     284             :                 formulation.
     285             : 
     286             :    -W PAGE_WIDTH, --page-width=PAGE_WIDTH
     287             :                 Set the page width to PAGE_WIDTH characters. That's valid
     288             :                 with and without a column option. Text lines will be
     289             :                 truncated, unless -J is used. Together with one of the
     290             :                 column options (-COLUMN| -a -COLUMN| -m) column alignment
     291             :                 is always used.
     292             :                 Default is 72 characters.
     293             :                 Without -W PAGE_WIDTH
     294             :                 - but with one of the column options default truncation of
     295             :                   72 characters is used (to keep downward compatibility
     296             :                   and to simplify most frequently met column tasks).
     297             :                   Column alignment and column separators are used.
     298             :                 - and without any of the column options NO line truncation
     299             :                   is used (to keep downward compatibility and to meet most
     300             :                   frequent tasks). That's equivalent to  -W 72 -J .
     301             : 
     302             :                 With/without  -W PAGE_WIDTH  the header line is always
     303             :                 truncated to avoid line overflow.
     304             : 
     305             :                 (In pr versions newer than 1.14 -S option does no longer
     306             :                 affect -W option.)
     307             : 
     308             : */
     309             : 
     310             : 
     311             : #include <config.h>
     312             : 
     313             : #include <getopt.h>
     314             : #include <sys/types.h>
     315             : #include "system.h"
     316             : #include "error.h"
     317             : #include "hard-locale.h"
     318             : #include "mbswidth.h"
     319             : #include "quote.h"
     320             : #include "stat-time.h"
     321             : #include "stdio--.h"
     322             : #include "strftime.h"
     323             : #include "xstrtol.h"
     324             : 
     325             : /* The official name of this program (e.g., no `g' prefix).  */
     326             : #define PROGRAM_NAME "pr"
     327             : 
     328             : #define AUTHORS "Pete TerMaat", "Roland Huebner"
     329             : 
     330             : /* Used with start_position in the struct COLUMN described below.
     331             :    If start_position == ANYWHERE, we aren't truncating columns and
     332             :    can begin printing a column anywhere.  Otherwise we must pad to
     333             :    the horizontal position start_position. */
     334             : #define ANYWHERE        0
     335             : 
     336             : /* Each column has one of these structures allocated for it.
     337             :    If we're only dealing with one file, fp is the same for all
     338             :    columns.
     339             : 
     340             :    The general strategy is to spend time setting up these column
     341             :    structures (storing columns if necessary), after which printing
     342             :    is a matter of flitting from column to column and calling
     343             :    print_func.
     344             : 
     345             :    Parallel files, single files printing across in multiple
     346             :    columns, and single files printing down in multiple columns all
     347             :    fit the same printing loop.
     348             : 
     349             :    print_func           Function used to print lines in this column.
     350             :                         If we're storing this column it will be
     351             :                         print_stored(), Otherwise it will be read_line().
     352             : 
     353             :    char_func            Function used to process characters in this column.
     354             :                         If we're storing this column it will be store_char(),
     355             :                         otherwise it will be print_char().
     356             : 
     357             :    current_line         Index of the current entry in line_vector, which
     358             :                         contains the index of the first character of the
     359             :                         current line in buff[].
     360             : 
     361             :    lines_stored         Number of lines in this column which are stored in
     362             :                         buff.
     363             : 
     364             :    lines_to_print       If we're storing this column, lines_to_print is
     365             :                         the number of stored_lines which remain to be
     366             :                         printed.  Otherwise it is the number of lines
     367             :                         we can print without exceeding lines_per_body.
     368             : 
     369             :    start_position       The horizontal position we want to be in before we
     370             :                         print the first character in this column.
     371             : 
     372             :    numbered             True means precede this column with a line number. */
     373             : 
     374             : /* FIXME: There are many unchecked integer overflows in this file,
     375             :    that will cause this command to misbehave given large inputs or
     376             :    options.  Many of the "int" values below should be "size_t" or
     377             :    something else like that.  */
     378             : 
     379             : struct COLUMN;
     380             : struct COLUMN
     381             :   {
     382             :     FILE *fp;                   /* Input stream for this column. */
     383             :     char const *name;           /* File name. */
     384             :     enum
     385             :       {
     386             :         OPEN,
     387             :         FF_FOUND,               /* used with -b option, set with \f, changed
     388             :                                    to ON_HOLD after print_header */
     389             :         ON_HOLD,                /* Hit a form feed. */
     390             :         CLOSED
     391             :       }
     392             :     status;                     /* Status of the file pointer. */
     393             : 
     394             :     /* Func to print lines in this col. */
     395             :     bool (*print_func) (struct COLUMN *);
     396             : 
     397             :     /* Func to print/store chars in this col. */
     398             :     void (*char_func) (char);
     399             : 
     400             :     int current_line;           /* Index of current place in line_vector. */
     401             :     int lines_stored;           /* Number of lines stored in buff. */
     402             :     int lines_to_print;         /* No. lines stored or space left on page. */
     403             :     int start_position;         /* Horizontal position of first char. */
     404             :     bool numbered;
     405             :     bool full_page_printed;     /* True means printed without a FF found. */
     406             : 
     407             :     /* p->full_page_printed  controls a special case of "FF set by hand":
     408             :        True means a full page has been printed without FF found. To avoid an
     409             :        additional empty page we have to ignore a FF immediately following in
     410             :        the next line. */
     411             :   };
     412             : 
     413             : typedef struct COLUMN COLUMN;
     414             : 
     415             : #define NULLCOL (COLUMN *)0
     416             : 
     417             : static int char_to_clump (char c);
     418             : static bool read_line (COLUMN *p);
     419             : static bool print_page (void);
     420             : static bool print_stored (COLUMN *p);
     421             : static bool open_file (char *name, COLUMN *p);
     422             : static bool skip_to_page (uintmax_t page);
     423             : static void print_header (void);
     424             : static void pad_across_to (int position);
     425             : static void add_line_number (COLUMN *p);
     426             : static void getoptarg (char *arg, char switch_char, char *character,
     427             :                        int *number);
     428             : void usage (int status);
     429             : static void print_files (int number_of_files, char **av);
     430             : static void init_parameters (int number_of_files);
     431             : static void init_header (char const *filename, int desc);
     432             : static bool init_fps (int number_of_files, char **av);
     433             : static void init_funcs (void);
     434             : static void init_store_cols (void);
     435             : static void store_columns (void);
     436             : static void balance (int total_stored);
     437             : static void store_char (char c);
     438             : static void pad_down (int lines);
     439             : static void read_rest_of_line (COLUMN *p);
     440             : static void skip_read (COLUMN *p, int column_number);
     441             : static void print_char (char c);
     442             : static void cleanup (void);
     443             : static void print_sep_string (void);
     444             : static void separator_string (const char *optarg_S);
     445             : 
     446             : /* The name under which this program was invoked. */
     447             : char *program_name;
     448             : 
     449             : /* All of the columns to print.  */
     450             : static COLUMN *column_vector;
     451             : 
     452             : /* When printing a single file in multiple downward columns,
     453             :    we store the leftmost columns contiguously in buff.
     454             :    To print a line from buff, get the index of the first character
     455             :    from line_vector[i], and print up to line_vector[i + 1]. */
     456             : static char *buff;
     457             : 
     458             : /* Index of the position in buff where the next character
     459             :    will be stored. */
     460             : static int buff_current;
     461             : 
     462             : /* The number of characters in buff.
     463             :    Used for allocation of buff and to detect overflow of buff. */
     464             : static size_t buff_allocated;
     465             : 
     466             : /* Array of indices into buff.
     467             :    Each entry is an index of the first character of a line.
     468             :    This is used when storing lines to facilitate shuffling when
     469             :    we do column balancing on the last page. */
     470             : static int *line_vector;
     471             : 
     472             : /* Array of horizonal positions.
     473             :    For each line in line_vector, end_vector[line] is the horizontal
     474             :    position we are in after printing that line.  We keep track of this
     475             :    so that we know how much we need to pad to prepare for the next
     476             :    column. */
     477             : static int *end_vector;
     478             : 
     479             : /* (-m) True means we're printing multiple files in parallel. */
     480             : static bool parallel_files = false;
     481             : 
     482             : /* (-m) True means a line starts with some empty columns (some files
     483             :    already CLOSED or ON_HOLD) which we have to align. */
     484             : static bool align_empty_cols;
     485             : 
     486             : /* (-m) True means we have not yet found any printable column in a line.
     487             :    align_empty_cols = true  has to be maintained. */
     488             : static bool empty_line;
     489             : 
     490             : /* (-m) False means printable column output precedes a form feed found.
     491             :    Column alignment is done only once. No additional action with that form
     492             :    feed.
     493             :    True means we found only a form feed in a column. Maybe we have to do
     494             :    some column alignment with that form feed. */
     495             : static bool FF_only;
     496             : 
     497             : /* (-[0-9]+) True means we're given an option explicitly specifying
     498             :    number of columns.  Used to detect when this option is used with -m
     499             :    and when translating old options to new/long options. */
     500             : static bool explicit_columns = false;
     501             : 
     502             : /* (-t|-T) False means we aren't printing headers and footers. */
     503             : static bool extremities = true;
     504             : 
     505             : /* (-t) True means we retain all FF set by hand in input files.
     506             :    False is set with -T option. */
     507             : static bool keep_FF = false;
     508             : static bool print_a_FF = false;
     509             : 
     510             : /* True means we need to print a header as soon as we know we've got input
     511             :    to print after it. */
     512             : static bool print_a_header;
     513             : 
     514             : /* (-f) True means use formfeeds instead of newlines to separate pages. */
     515             : static bool use_form_feed = false;
     516             : 
     517             : /* True means we have read the standard input. */
     518             : static bool have_read_stdin = false;
     519             : 
     520             : /* True means the -a flag has been given. */
     521             : static bool print_across_flag = false;
     522             : 
     523             : /* True means we're printing one file in multiple (>1) downward columns. */
     524             : static bool storing_columns = true;
     525             : 
     526             : /* (-b) True means balance columns on the last page as Sys V does. */
     527             : /* That's no longer an independent option. With storing_columns = true
     528             :    balance_columns = true is used too (s. function init_parameters).
     529             :    We get a consistent formulation with "FF set by hand" in input files. */
     530             : static bool balance_columns = false;
     531             : 
     532             : /* (-l) Number of lines on a page, including header and footer lines. */
     533             : static int lines_per_page = 66;
     534             : 
     535             : /* Number of lines in the header and footer can be reset to 0 using
     536             :    the -t flag. */
     537             : enum { lines_per_header = 5 };
     538             : static int lines_per_body;
     539             : enum { lines_per_footer = 5 };
     540             : 
     541             : /* (-w|-W) Width in characters of the page.  Does not include the width of
     542             :    the margin. */
     543             : static int chars_per_line = 72;
     544             : 
     545             : /* (-w|W) True means we truncate lines longer than chars_per_column. */
     546             : static bool truncate_lines = false;
     547             : 
     548             : /* (-J) True means we join lines without any line truncation. -J
     549             :    dominates -w option. */
     550             : static bool join_lines = false;
     551             : 
     552             : /* Number of characters in a column.  Based on col_sep_length and
     553             :    page width. */
     554             : static int chars_per_column;
     555             : 
     556             : /* (-e) True means convert tabs to spaces on input. */
     557             : static bool untabify_input = false;
     558             : 
     559             : /* (-e) The input tab character. */
     560             : static char input_tab_char = '\t';
     561             : 
     562             : /* (-e) Tabstops are at chars_per_tab, 2*chars_per_tab, 3*chars_per_tab, ...
     563             :    where the leftmost column is 1. */
     564             : static int chars_per_input_tab = 8;
     565             : 
     566             : /* (-i) True means convert spaces to tabs on output. */
     567             : static bool tabify_output = false;
     568             : 
     569             : /* (-i) The output tab character. */
     570             : static char output_tab_char = '\t';
     571             : 
     572             : /* (-i) The width of the output tab. */
     573             : static int chars_per_output_tab = 8;
     574             : 
     575             : /* Keeps track of pending white space.  When we hit a nonspace
     576             :    character after some whitespace, we print whitespace, tabbing
     577             :    if necessary to get to output_position + spaces_not_printed. */
     578             : static int spaces_not_printed;
     579             : 
     580             : /* (-o) Number of spaces in the left margin (tabs used when possible). */
     581             : static int chars_per_margin = 0;
     582             : 
     583             : /* Position where the next character will fall.
     584             :    Leftmost position is 0 + chars_per_margin.
     585             :    Rightmost position is chars_per_margin + chars_per_line - 1.
     586             :    This is important for converting spaces to tabs on output. */
     587             : static int output_position;
     588             : 
     589             : /* Horizontal position relative to the current file.
     590             :    (output_position depends on where we are on the page;
     591             :    input_position depends on where we are in the file.)
     592             :    Important for converting tabs to spaces on input. */
     593             : static int input_position;
     594             : 
     595             : /* True if there were any failed opens so we can exit with nonzero
     596             :    status.  */
     597             : static bool failed_opens = false;
     598             : 
     599             : /* The number of spaces taken up if we print a tab character with width
     600             :    c_ from position h_. */
     601             : #define TAB_WIDTH(c_, h_) ((c_) - ((h_) % (c_)))
     602             : 
     603             : /* The horizontal position we'll be at after printing a tab character
     604             :    of width c_ from the position h_. */
     605             : #define POS_AFTER_TAB(c_, h_) ((h_) + TAB_WIDTH (c_, h_))
     606             : 
     607             : /* (-NNN) Number of columns of text to print. */
     608             : static int columns = 1;
     609             : 
     610             : /* (+NNN:MMM) Page numbers on which to begin and stop printing.
     611             :    first_page_number = 0  will be used to check input only. */
     612             : static uintmax_t first_page_number = 0;
     613             : static uintmax_t last_page_number = UINTMAX_MAX;
     614             : 
     615             : /* Number of files open (not closed, not on hold). */
     616             : static int files_ready_to_read = 0;
     617             : 
     618             : /* Current page number.  Displayed in header. */
     619             : static uintmax_t page_number;
     620             : 
     621             : /* Current line number.  Displayed when -n flag is specified.
     622             : 
     623             :    When printing files in parallel (-m flag), line numbering is as follows:
     624             :    1    foo     goo     moo
     625             :    2    hoo     too     zoo
     626             : 
     627             :    When printing files across (-a flag), ...
     628             :    1    foo     2       moo     3       goo
     629             :    4    hoo     5       too     6       zoo
     630             : 
     631             :    Otherwise, line numbering is as follows:
     632             :    1    foo     3       goo     5       too
     633             :    2    moo     4       hoo     6       zoo */
     634             : static int line_number;
     635             : 
     636             : /* With line_number overflow, we use power_10 to cut off the higher-order
     637             :    digits of the line_number */
     638             : static int power_10;
     639             : 
     640             : /* (-n) True means lines should be preceded by numbers. */
     641             : static bool numbered_lines = false;
     642             : 
     643             : /* (-n) Character which follows each line number. */
     644             : static char number_separator = '\t';
     645             : 
     646             : /* (-n) line counting starts with 1st line of input file (not with 1st
     647             :    line of 1st page printed). */
     648             : static int line_count = 1;
     649             : 
     650             : /* (-n) True means counting of skipped lines starts with 1st line of
     651             :    input file. False means -N option is used in addition, counting of
     652             :    skipped lines not required. */
     653             : static bool skip_count = true;
     654             : 
     655             : /* (-N) Counting starts with start_line_number = NUMBER at 1st line of
     656             :    first page printed, usually not 1st page of input file. */
     657             : static int start_line_num = 1;
     658             : 
     659             : /* (-n) Width in characters of a line number. */
     660             : static int chars_per_number = 5;
     661             : 
     662             : /* Used when widening the first column to accommodate numbers -- only
     663             :    needed when printing files in parallel.  Includes width of both the
     664             :    number and the number_separator. */
     665             : static int number_width;
     666             : 
     667             : /* Buffer sprintf uses to format a line number. */
     668             : static char *number_buff;
     669             : 
     670             : /* (-v) True means unprintable characters are printed as escape sequences.
     671             :    control-g becomes \007. */
     672             : static bool use_esc_sequence = false;
     673             : 
     674             : /* (-c) True means unprintable characters are printed as control prefixes.
     675             :    control-g becomes ^G. */
     676             : static bool use_cntrl_prefix = false;
     677             : 
     678             : /* (-d) True means output is double spaced. */
     679             : static bool double_space = false;
     680             : 
     681             : /* Number of files opened initially in init_files.  Should be 1
     682             :    unless we're printing multiple files in parallel. */
     683             : static int total_files = 0;
     684             : 
     685             : /* (-r) True means don't complain if we can't open a file. */
     686             : static bool ignore_failed_opens = false;
     687             : 
     688             : /* (-S) True means we separate columns with a specified string.
     689             :    -S option does not affect line truncation nor column alignment. */
     690             : static bool use_col_separator = false;
     691             : 
     692             : /* String used to separate columns if the -S option has been specified.
     693             :    Default without -S but together with one of the column options
     694             :    -a|COLUMN|-m is a `space' and with the -J option a `tab'. */
     695             : static char *col_sep_string = "";
     696             : static int col_sep_length = 0;
     697             : static char *column_separator = " ";
     698             : static char *line_separator = "\t";
     699             : 
     700             : /* Number of separator characters waiting to be printed as soon as we
     701             :    know that we have any input remaining to be printed. */
     702             : static int separators_not_printed;
     703             : 
     704             : /* Position we need to pad to, as soon as we know that we have input
     705             :    remaining to be printed. */
     706             : static int padding_not_printed;
     707             : 
     708             : /* True means we should pad the end of the page.  Remains false until we
     709             :    know we have a page to print. */
     710             : static bool pad_vertically;
     711             : 
     712             : /* (-h) String of characters used in place of the filename in the header. */
     713             : static char *custom_header;
     714             : 
     715             : /* (-D) Date format for the header.  */
     716             : static char const *date_format;
     717             : 
     718             : /* Date and file name for the header.  */
     719             : static char *date_text;
     720             : static char const *file_text;
     721             : 
     722             : /* Output columns available, not counting the date and file name.  */
     723             : static int header_width_available;
     724             : 
     725             : static char *clump_buff;
     726             : 
     727             : /* True means we read the line no. lines_per_body in skip_read
     728             :    called by skip_to_page. That variable controls the coincidence of a
     729             :    "FF set by hand" and "full_page_printed", see above the definition of
     730             :    structure COLUMN. */
     731             : static bool last_line = false;
     732             : 
     733             : /* For long options that have no equivalent short option, use a
     734             :    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
     735             : enum
     736             : {
     737             :   COLUMNS_OPTION = CHAR_MAX + 1,
     738             :   PAGES_OPTION
     739             : };
     740             : 
     741             : static char const short_options[] =
     742             :   "-0123456789D:FJN:S::TW:abcde::fh:i::l:mn::o:rs::tvw:";
     743             : 
     744             : static struct option const long_options[] =
     745             : {
     746             :   {"pages", required_argument, NULL, PAGES_OPTION},
     747             :   {"columns", required_argument, NULL, COLUMNS_OPTION},
     748             :   {"across", no_argument, NULL, 'a'},
     749             :   {"show-control-chars", no_argument, NULL, 'c'},
     750             :   {"double-space", no_argument, NULL, 'd'},
     751             :   {"date-format", required_argument, NULL, 'D'},
     752             :   {"expand-tabs", optional_argument, NULL, 'e'},
     753             :   {"form-feed", no_argument, NULL, 'f'},
     754             :   {"header", required_argument, NULL, 'h'},
     755             :   {"output-tabs", optional_argument, NULL, 'i'},
     756             :   {"join-lines", no_argument, NULL, 'J'},
     757             :   {"length", required_argument, NULL, 'l'},
     758             :   {"merge", no_argument, NULL, 'm'},
     759             :   {"number-lines", optional_argument, NULL, 'n'},
     760             :   {"first-line-number", required_argument, NULL, 'N'},
     761             :   {"indent", required_argument, NULL, 'o'},
     762             :   {"no-file-warnings", no_argument, NULL, 'r'},
     763             :   {"separator", optional_argument, NULL, 's'},
     764             :   {"sep-string", optional_argument, NULL, 'S'},
     765             :   {"omit-header", no_argument, NULL, 't'},
     766             :   {"omit-pagination", no_argument, NULL, 'T'},
     767             :   {"show-nonprinting", no_argument, NULL, 'v'},
     768             :   {"width", required_argument, NULL, 'w'},
     769             :   {"page-width", required_argument, NULL, 'W'},
     770             :   {GETOPT_HELP_OPTION_DECL},
     771             :   {GETOPT_VERSION_OPTION_DECL},
     772             :   {NULL, 0, NULL, 0}
     773             : };
     774             : 
     775             : /* Return the number of columns that have either an open file or
     776             :    stored lines. */
     777             : 
     778             : static int
     779         534 : cols_ready_to_print (void)
     780             : {
     781             :   COLUMN *q;
     782             :   int i;
     783             :   int n;
     784             : 
     785         534 :   n = 0;
     786        1449 :   for (q = column_vector, i = 0; i < columns; ++q, ++i)
     787        1654 :     if (q->status == OPEN ||
     788        1390 :         q->status == FF_FOUND ||     /* With -b: To print a header only */
     789        1055 :         (storing_columns && q->lines_stored > 0 && q->lines_to_print > 0))
     790         285 :       ++n;
     791         534 :   return n;
     792             : }
     793             : 
     794             : /* Estimate first_ / last_page_number
     795             :    using option +FIRST_PAGE:LAST_PAGE */
     796             : 
     797             : static bool
     798          27 : first_last_page (int oi, char c, char const *pages)
     799             : {
     800             :   char *p;
     801             :   uintmax_t first;
     802          27 :   uintmax_t last = UINTMAX_MAX;
     803          27 :   strtol_error err = xstrtoumax (pages, &p, 10, &first, "");
     804          27 :   if (err != LONGINT_OK && err != LONGINT_INVALID_SUFFIX_CHAR)
     805          12 :     xstrtol_fatal (err, oi, c, long_options, pages);
     806             : 
     807          15 :   if (p == pages || !first)
     808           4 :     return false;
     809             : 
     810          11 :   if (*p == ':')
     811             :     {
     812           1 :       char const *p1 = p + 1;
     813           1 :       err = xstrtoumax (p1, &p, 10, &last, "");
     814           1 :       if (err != LONGINT_OK)
     815           1 :         xstrtol_fatal (err, oi, c, long_options, pages);
     816           0 :       if (p1 == p || last < first)
     817           0 :         return false;
     818             :     }
     819             : 
     820          10 :   if (*p)
     821           1 :     return false;
     822             : 
     823           9 :   first_page_number = first;
     824           9 :   last_page_number = last;
     825           9 :   return true;
     826             : }
     827             : 
     828             : /* Parse column count string S, and if it's valid (1 or larger and
     829             :    within range of the type of `columns') set the global variables
     830             :    columns and explicit_columns and return true.
     831             :    Otherwise, exit with a diagnostic.  */
     832             : static void
     833          21 : parse_column_count (char const *s)
     834             : {
     835             :   long int tmp_long;
     836          21 :   if (xstrtol (s, NULL, 10, &tmp_long, "") != LONGINT_OK
     837          15 :       || !(1 <= tmp_long && tmp_long <= INT_MAX))
     838           9 :     error (EXIT_FAILURE, 0,
     839             :            _("invalid number of columns: %s"), quote (s));
     840             : 
     841          12 :   columns = tmp_long;
     842          12 :   explicit_columns = true;
     843          12 : }
     844             : 
     845             : /* Estimate length of col_sep_string with option -S.  */
     846             : 
     847             : static void
     848           0 : separator_string (const char *optarg_S)
     849             : {
     850           0 :   col_sep_length = (int) strlen (optarg_S);
     851           0 :   col_sep_string = xmalloc (col_sep_length + 1);
     852           0 :   strcpy (col_sep_string, optarg_S);
     853           0 : }
     854             : 
     855             : int
     856          88 : main (int argc, char **argv)
     857             : {
     858             :   int n_files;
     859          88 :   bool old_options = false;
     860          88 :   bool old_w = false;
     861          88 :   bool old_s = false;
     862             :   char **file_names;
     863             : 
     864             :   /* Accumulate the digits of old-style options like -99.  */
     865          88 :   char *column_count_string = NULL;
     866          88 :   size_t n_digits = 0;
     867          88 :   size_t n_alloc = 0;
     868             : 
     869             :   initialize_main (&argc, &argv);
     870          88 :   program_name = argv[0];
     871          88 :   setlocale (LC_ALL, "");
     872             :   bindtextdomain (PACKAGE, LOCALEDIR);
     873             :   textdomain (PACKAGE);
     874             : 
     875          88 :   atexit (close_stdout);
     876             : 
     877          88 :   n_files = 0;
     878          88 :   file_names = (argc > 1
     879          86 :                 ? xmalloc ((argc - 1) * sizeof (char *))
     880         174 :                 : NULL);
     881             : 
     882             :   for (;;)
     883          49 :     {
     884         137 :       int oi = -1;
     885         137 :       int c = getopt_long (argc, argv, short_options, long_options, &oi);
     886         137 :       if (c == -1)
     887          61 :         break;
     888             : 
     889          76 :       if (ISDIGIT (c))
     890             :         {
     891             :           /* Accumulate column-count digits specified via old-style options. */
     892          15 :           if (n_digits + 1 >= n_alloc)
     893             :             column_count_string
     894          11 :               = X2REALLOC (column_count_string, &n_alloc);
     895          15 :           column_count_string[n_digits++] = c;
     896          15 :           column_count_string[n_digits] = '\0';
     897          15 :           continue;
     898             :         }
     899             : 
     900          61 :       n_digits = 0;
     901             : 
     902          61 :       switch (c)
     903             :         {
     904          41 :         case 1:                 /* Non-option argument. */
     905             :           /* long option --page dominates old `+FIRST_PAGE ...'.  */
     906          55 :           if (! (first_page_number == 0
     907          41 :                  && *optarg == '+' && first_last_page (-2, '+', optarg + 1)))
     908          19 :             file_names[n_files++] = optarg;
     909          28 :           break;
     910             : 
     911           0 :         case PAGES_OPTION:      /* --pages=FIRST_PAGE[:LAST_PAGE] */
     912             :           {                     /* dominates old opt +... */
     913           0 :             if (! optarg)
     914           0 :               error (EXIT_FAILURE, 0,
     915             :                      _("`--pages=FIRST_PAGE[:LAST_PAGE]' missing argument"));
     916           0 :             else if (! first_last_page (oi, 0, optarg))
     917           0 :               error (EXIT_FAILURE, 0, _("Invalid page range %s"),
     918             :                      quote (optarg));
     919           0 :             break;
     920             :           }
     921             : 
     922          11 :         case COLUMNS_OPTION:    /* --columns=COLUMN */
     923             :           {
     924          11 :             parse_column_count (optarg);
     925             : 
     926             :             /* If there was a prior column count specified via the
     927             :                short-named option syntax, e.g., -9, ensure that this
     928             :                long-name-specified value overrides it.  */
     929           4 :             free (column_count_string);
     930           4 :             column_count_string = NULL;
     931           4 :             n_alloc = 0;
     932           4 :             break;
     933             :           }
     934             : 
     935           1 :         case 'a':
     936           1 :           print_across_flag = true;
     937           1 :           storing_columns = false;
     938           1 :           break;
     939           0 :         case 'b':
     940           0 :           balance_columns = true;
     941           0 :           break;
     942           0 :         case 'c':
     943           0 :           use_cntrl_prefix = true;
     944           0 :           break;
     945           0 :         case 'd':
     946           0 :           double_space = true;
     947           0 :           break;
     948           0 :         case 'D':
     949           0 :           date_format = optarg;
     950           0 :           break;
     951           1 :         case 'e':
     952           1 :           if (optarg)
     953           0 :             getoptarg (optarg, 'e', &input_tab_char,
     954             :                        &chars_per_input_tab);
     955             :           /* Could check tab width > 0. */
     956           1 :           untabify_input = true;
     957           1 :           break;
     958           0 :         case 'f':
     959             :         case 'F':
     960           0 :           use_form_feed = true;
     961           0 :           break;
     962           0 :         case 'h':
     963           0 :           custom_header = optarg;
     964           0 :           break;
     965           0 :         case 'i':
     966           0 :           if (optarg)
     967           0 :             getoptarg (optarg, 'i', &output_tab_char,
     968             :                        &chars_per_output_tab);
     969             :           /* Could check tab width > 0. */
     970           0 :           tabify_output = true;
     971           0 :           break;
     972           0 :         case 'J':
     973           0 :           join_lines = true;
     974           0 :           break;
     975           0 :         case 'l':
     976             :           {
     977             :             long int tmp_long;
     978           0 :             if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
     979           0 :                 || tmp_long <= 0 || tmp_long > INT_MAX)
     980             :               {
     981           0 :                 error (EXIT_FAILURE, 0,
     982             :                        _("`-l PAGE_LENGTH' invalid number of lines: %s"),
     983             :                        quote (optarg));
     984             :               }
     985           0 :             lines_per_page = tmp_long;
     986           0 :             break;
     987             :           }
     988           0 :         case 'm':
     989           0 :           parallel_files = true;
     990           0 :           storing_columns = false;
     991           0 :           break;
     992           0 :         case 'n':
     993           0 :           numbered_lines = true;
     994           0 :           if (optarg)
     995           0 :             getoptarg (optarg, 'n', &number_separator,
     996             :                        &chars_per_number);
     997           0 :           break;
     998           0 :         case 'N':
     999           0 :           skip_count = false;
    1000             :           {
    1001             :             long int tmp_long;
    1002           0 :             if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
    1003           0 :                 || tmp_long > INT_MAX)
    1004             :               {
    1005           0 :                 error (EXIT_FAILURE, 0,
    1006             :                        _("`-N NUMBER' invalid starting line number: %s"),
    1007             :                        quote (optarg));
    1008             :               }
    1009           0 :             start_line_num = tmp_long;
    1010           0 :             break;
    1011             :           }
    1012           0 :         case 'o':
    1013             :           {
    1014             :             long int tmp_long;
    1015           0 :             if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
    1016           0 :                 || tmp_long < 0 || tmp_long > INT_MAX)
    1017           0 :               error (EXIT_FAILURE, 0,
    1018             :                      _("`-o MARGIN' invalid line offset: %s"), quote (optarg));
    1019           0 :             chars_per_margin = tmp_long;
    1020           0 :             break;
    1021             :           }
    1022           0 :         case 'r':
    1023           0 :           ignore_failed_opens = true;
    1024           0 :           break;
    1025           0 :         case 's':
    1026           0 :           old_options = true;
    1027           0 :           old_s = true;
    1028           0 :           if (!use_col_separator && optarg)
    1029           0 :             separator_string (optarg);
    1030           0 :           break;
    1031           0 :         case 'S':
    1032           0 :           old_s = false;
    1033             :           /* Reset an additional input of -s, -S dominates -s */
    1034           0 :           col_sep_string = "";
    1035           0 :           col_sep_length = 0;
    1036           0 :           use_col_separator = true;
    1037           0 :           if (optarg)
    1038           0 :             separator_string (optarg);
    1039           0 :           break;
    1040           0 :         case 't':
    1041           0 :           extremities = false;
    1042           0 :           keep_FF = true;
    1043           0 :           break;
    1044           0 :         case 'T':
    1045           0 :           extremities = false;
    1046           0 :           keep_FF = false;
    1047           0 :           break;
    1048           0 :         case 'v':
    1049           0 :           use_esc_sequence = true;
    1050           0 :           break;
    1051           0 :         case 'w':
    1052           0 :           old_options = true;
    1053           0 :           old_w = true;
    1054             :           {
    1055             :             long int tmp_long;
    1056           0 :             if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
    1057           0 :                 || tmp_long <= 0 || tmp_long > INT_MAX)
    1058           0 :               error (EXIT_FAILURE, 0,
    1059             :                      _("`-w PAGE_WIDTH' invalid number of characters: %s"),
    1060             :                      quote (optarg));
    1061           0 :             if (!truncate_lines)
    1062           0 :               chars_per_line = tmp_long;
    1063           0 :             break;
    1064             :           }
    1065           0 :         case 'W':
    1066           0 :           old_w = false;                        /* dominates -w */
    1067           0 :           truncate_lines = true;
    1068             :           {
    1069             :             long int tmp_long;
    1070           0 :             if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
    1071           0 :                 || tmp_long <= 0 || tmp_long > INT_MAX)
    1072           0 :               error (EXIT_FAILURE, 0,
    1073             :                      _("`-W PAGE_WIDTH' invalid number of characters: %s"),
    1074             :                      quote (optarg));
    1075           0 :             chars_per_line = tmp_long;
    1076           0 :             break;
    1077             :           }
    1078           0 :         case_GETOPT_HELP_CHAR;
    1079           0 :         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    1080           7 :         default:
    1081           7 :           usage (EXIT_FAILURE);
    1082           0 :           break;
    1083             :         }
    1084             :     }
    1085             : 
    1086          61 :   if (column_count_string)
    1087             :     {
    1088          10 :       parse_column_count (column_count_string);
    1089           8 :       free (column_count_string);
    1090             :     }
    1091             : 
    1092          59 :   if (! date_format)
    1093          59 :     date_format = (getenv ("POSIXLY_CORRECT") && !hard_locale (LC_TIME)
    1094             :                    ? "%b %e %H:%M %Y"
    1095             :                    : "%Y-%m-%d %H:%M");
    1096             : 
    1097             :   /* Now we can set a reasonable initial value: */
    1098          59 :   if (first_page_number == 0)
    1099          50 :     first_page_number = 1;
    1100             : 
    1101          59 :   if (parallel_files & explicit_columns)
    1102           0 :     error (EXIT_FAILURE, 0,
    1103             :          _("Cannot specify number of columns when printing in parallel."));
    1104             : 
    1105          59 :   if (parallel_files & print_across_flag)
    1106           0 :     error (EXIT_FAILURE, 0,
    1107             :        _("Cannot specify both printing across and printing in parallel."));
    1108             : 
    1109             : /* Translate some old short options to new/long options.
    1110             :    To meet downward compatibility with other UNIX pr utilities
    1111             :    and some POSIX specifications. */
    1112             : 
    1113          59 :   if (old_options)
    1114             :     {
    1115           0 :       if (old_w)
    1116             :         {
    1117           0 :           if (parallel_files | explicit_columns)
    1118             :             {
    1119             :               /* activate -W */
    1120           0 :               truncate_lines = true;
    1121           0 :               if (old_s)
    1122             :                 /* adapt HP-UX and SunOS: -s = no separator;
    1123             :                    activate -S */
    1124           0 :                 use_col_separator = true;
    1125             :             }
    1126             :           else
    1127             :             /* old -w sets width with columns only
    1128             :                activate -J */
    1129           0 :             join_lines = true;
    1130             :         }
    1131           0 :       else if (!use_col_separator)
    1132             :         {
    1133             :           /* No -S option read */
    1134           0 :           if (old_s & (parallel_files | explicit_columns))
    1135             :             {
    1136           0 :               if (!truncate_lines)
    1137             :                 {
    1138             :                   /* old -s (without -w and -W) annuls column alignment,
    1139             :                   uses fields, activate -J */
    1140           0 :                   join_lines = true;
    1141           0 :                   if (col_sep_length > 0)
    1142             :                     /* activate -S */
    1143           0 :                     use_col_separator = true;
    1144             :                 }
    1145             :               else
    1146             :                 /* with -W */
    1147             :                 /* adapt HP-UX and SunOS: -s = no separator;
    1148             :                    activate -S */
    1149           0 :                 use_col_separator = true;
    1150             :             }
    1151             :         }
    1152             :     }
    1153             : 
    1154          80 :   for (; optind < argc; optind++)
    1155             :     {
    1156          21 :       file_names[n_files++] = argv[optind];
    1157             :     }
    1158             : 
    1159          59 :   if (n_files == 0)
    1160             :     {
    1161             :       /* No file arguments specified;  read from standard input.  */
    1162          25 :       print_files (0, NULL);
    1163             :     }
    1164             :   else
    1165             :     {
    1166          34 :       if (parallel_files)
    1167           0 :         print_files (n_files, file_names);
    1168             :       else
    1169             :         {
    1170             :           int i;
    1171          67 :           for (i = 0; i < n_files; i++)
    1172          39 :             print_files (1, &file_names[i]);
    1173             :         }
    1174             :     }
    1175             : 
    1176          53 :   cleanup ();
    1177             : 
    1178          53 :   if (have_read_stdin && fclose (stdin) == EOF)
    1179           0 :     error (EXIT_FAILURE, errno, _("standard input"));
    1180          53 :   if (failed_opens)
    1181          12 :     exit (EXIT_FAILURE);
    1182          41 :   exit (EXIT_SUCCESS);
    1183             : }
    1184             : 
    1185             : /* Parse options of the form -scNNN.
    1186             : 
    1187             :    Example: -nck, where 'n' is the option, c is the optional number
    1188             :    separator, and k is the optional width of the field used when printing
    1189             :    a number. */
    1190             : 
    1191             : static void
    1192           0 : getoptarg (char *arg, char switch_char, char *character, int *number)
    1193             : {
    1194           0 :   if (!ISDIGIT (*arg))
    1195           0 :     *character = *arg++;
    1196           0 :   if (*arg)
    1197             :     {
    1198             :       long int tmp_long;
    1199           0 :       if (xstrtol (arg, NULL, 10, &tmp_long, "") != LONGINT_OK
    1200           0 :           || tmp_long <= 0 || tmp_long > INT_MAX)
    1201             :         {
    1202           0 :           error (0, 0,
    1203             :              _("`-%c' extra characters or invalid number in the argument: %s"),
    1204             :                  switch_char, quote (arg));
    1205           0 :           usage (EXIT_FAILURE);
    1206             :         }
    1207           0 :       *number = tmp_long;
    1208             :     }
    1209           0 : }
    1210             : 
    1211             : /* Set parameters related to formatting. */
    1212             : 
    1213             : static void
    1214          64 : init_parameters (int number_of_files)
    1215             : {
    1216          64 :   int chars_used_by_number = 0;
    1217             : 
    1218          64 :   lines_per_body = lines_per_page - lines_per_header - lines_per_footer;
    1219          64 :   if (lines_per_body <= 0)
    1220             :     {
    1221           0 :       extremities = false;
    1222           0 :       keep_FF = true;
    1223             :     }
    1224          64 :   if (extremities == false)
    1225           0 :     lines_per_body = lines_per_page;
    1226             : 
    1227          64 :   if (double_space)
    1228           0 :     lines_per_body = lines_per_body / 2;
    1229             : 
    1230             :   /* If input is stdin, cannot print parallel files.  BSD dumps core
    1231             :      on this. */
    1232          64 :   if (number_of_files == 0)
    1233          25 :     parallel_files = false;
    1234             : 
    1235          64 :   if (parallel_files)
    1236           0 :     columns = number_of_files;
    1237             : 
    1238             :   /* One file, multi columns down: -b option is set to get a consistent
    1239             :      formulation with "FF set by hand" in input files. */
    1240          64 :   if (storing_columns)
    1241          59 :     balance_columns = true;
    1242             : 
    1243             :   /* Tabification is assumed for multiple columns. */
    1244          64 :   if (columns > 1)
    1245             :     {
    1246          12 :       if (!use_col_separator)
    1247             :         {
    1248             :           /* Use default separator */
    1249          11 :           if (join_lines)
    1250           0 :             col_sep_string = line_separator;
    1251             :           else
    1252          11 :             col_sep_string = column_separator;
    1253             : 
    1254          11 :           col_sep_length = 1;
    1255          11 :           use_col_separator = true;
    1256             :         }
    1257             :       /* It's rather pointless to define a TAB separator with column
    1258             :          alignment */
    1259           1 :       else if (!join_lines && *col_sep_string == '\t')
    1260           0 :         col_sep_string = column_separator;
    1261             : 
    1262          12 :       truncate_lines = true;
    1263          12 :       tabify_output = true;
    1264             :     }
    1265             :   else
    1266          52 :     storing_columns = false;
    1267             : 
    1268             :   /* -J dominates -w in any case */
    1269          64 :   if (join_lines)
    1270           0 :     truncate_lines = false;
    1271             : 
    1272          64 :   if (numbered_lines)
    1273             :     {
    1274             :       int tmp_i;
    1275           0 :       int chars_per_default_tab = 8;
    1276             : 
    1277           0 :       line_count = start_line_num;
    1278             : 
    1279             :       /* To allow input tab-expansion (-e sensitive) use:
    1280             :          if (number_separator == input_tab_char)
    1281             :            number_width = chars_per_number +
    1282             :              TAB_WIDTH (chars_per_input_tab, chars_per_number);   */
    1283             : 
    1284             :       /* Estimate chars_per_text without any margin and keep it constant. */
    1285           0 :       if (number_separator == '\t')
    1286           0 :         number_width = chars_per_number +
    1287           0 :           TAB_WIDTH (chars_per_default_tab, chars_per_number);
    1288             :       else
    1289           0 :         number_width = chars_per_number + 1;
    1290             : 
    1291             :       /* The number is part of the column width unless we are
    1292             :          printing files in parallel. */
    1293           0 :       if (parallel_files)
    1294           0 :         chars_used_by_number = number_width;
    1295             : 
    1296             :       /* We use power_10 to cut off the higher-order digits of the
    1297             :          line_number in function add_line_number */
    1298           0 :       tmp_i = chars_per_number;
    1299           0 :       for (power_10 = 1; tmp_i > 0; --tmp_i)
    1300           0 :         power_10 = 10 * power_10;
    1301             :     }
    1302             : 
    1303         192 :   chars_per_column = (chars_per_line - chars_used_by_number -
    1304         128 :                      (columns - 1) * col_sep_length) / columns;
    1305             : 
    1306          64 :   if (chars_per_column < 1)
    1307           0 :     error (EXIT_FAILURE, 0, _("page width too narrow"));
    1308             : 
    1309          64 :   if (numbered_lines)
    1310             :     {
    1311           0 :       free (number_buff);
    1312           0 :       number_buff = xmalloc (2 * chars_per_number);
    1313             :     }
    1314             : 
    1315             :   /* Pick the maximum between the tab width and the width of an
    1316             :      escape sequence.
    1317             :      The width of an escape sequence (4) isn't the lower limit any longer.
    1318             :      We've to use 8 as the lower limit, if we use chars_per_default_tab = 8
    1319             :      to expand a tab which is not an input_tab-char. */
    1320          64 :   free (clump_buff);
    1321          64 :   clump_buff = xmalloc (MAX (8, chars_per_input_tab));
    1322          64 : }
    1323             : 
    1324             : /* Open the necessary files,
    1325             :    maintaining a COLUMN structure for each column.
    1326             : 
    1327             :    With multiple files, each column p has a different p->fp.
    1328             :    With single files, each column p has the same p->fp.
    1329             :    Return false if (number_of_files > 0) and no files can be opened,
    1330             :    true otherwise.
    1331             : 
    1332             :    With each column/file p, p->full_page_printed is initialized,
    1333             :    see also open_file.  */
    1334             : 
    1335             : static bool
    1336          64 : init_fps (int number_of_files, char **av)
    1337             : {
    1338             :   int i, files_left;
    1339             :   COLUMN *p;
    1340             :   FILE *firstfp;
    1341             :   char const *firstname;
    1342             : 
    1343          64 :   total_files = 0;
    1344             : 
    1345          64 :   free (column_vector);
    1346          64 :   column_vector = xnmalloc (columns, sizeof (COLUMN));
    1347             : 
    1348          64 :   if (parallel_files)
    1349             :     {
    1350           0 :       files_left = number_of_files;
    1351           0 :       for (p = column_vector; files_left--; ++p, ++av)
    1352             :         {
    1353           0 :           if (! open_file (*av, p))
    1354             :             {
    1355           0 :               --p;
    1356           0 :               --columns;
    1357             :             }
    1358             :         }
    1359           0 :       if (columns == 0)
    1360           0 :         return false;
    1361           0 :       init_header ("", -1);
    1362             :     }
    1363             :   else
    1364             :     {
    1365          64 :       p = column_vector;
    1366          64 :       if (number_of_files > 0)
    1367             :         {
    1368          39 :           if (! open_file (*av, p))
    1369          13 :             return false;
    1370          26 :           init_header (*av, fileno (p->fp));
    1371          26 :           p->lines_stored = 0;
    1372             :         }
    1373             :       else
    1374             :         {
    1375          25 :           p->name = _("standard input");
    1376          25 :           p->fp = stdin;
    1377          25 :           have_read_stdin = true;
    1378          25 :           p->status = OPEN;
    1379          25 :           p->full_page_printed = false;
    1380          25 :           ++total_files;
    1381          25 :           init_header ("", -1);
    1382          25 :           p->lines_stored = 0;
    1383             :         }
    1384             : 
    1385          51 :       firstname = p->name;
    1386          51 :       firstfp = p->fp;
    1387          86 :       for (i = columns - 1, ++p; i; --i, ++p)
    1388             :         {
    1389          35 :           p->name = firstname;
    1390          35 :           p->fp = firstfp;
    1391          35 :           p->status = OPEN;
    1392          35 :           p->full_page_printed = false;
    1393          35 :           p->lines_stored = 0;
    1394             :         }
    1395             :     }
    1396          51 :   files_ready_to_read = total_files;
    1397          51 :   return true;
    1398             : }
    1399             : 
    1400             : /* Determine print_func and char_func, the functions
    1401             :    used by each column for printing and/or storing.
    1402             : 
    1403             :    Determine the horizontal position desired when we begin
    1404             :    printing a column (p->start_position). */
    1405             : 
    1406             : static void
    1407          44 : init_funcs (void)
    1408             : {
    1409             :   int i, h, h_next;
    1410             :   COLUMN *p;
    1411             : 
    1412          44 :   h = chars_per_margin;
    1413             : 
    1414          44 :   if (!truncate_lines)
    1415          34 :     h_next = ANYWHERE;
    1416             :   else
    1417             :     {
    1418             :       /* When numbering lines of parallel files, we enlarge the
    1419             :          first column to accomodate the number.  Looks better than
    1420             :          the Sys V approach. */
    1421          10 :       if (parallel_files & numbered_lines)
    1422           0 :         h_next = h + chars_per_column + number_width;
    1423             :       else
    1424          10 :         h_next = h + chars_per_column;
    1425             :     }
    1426             : 
    1427             :   /* Enlarge p->start_position of first column to use the same form of
    1428             :      padding_not_printed with all columns. */
    1429          44 :   h = h + col_sep_length;
    1430             : 
    1431             :   /* This loop takes care of all but the rightmost column. */
    1432             : 
    1433          79 :   for (p = column_vector, i = 1; i < columns; ++p, ++i)
    1434             :     {
    1435          35 :       if (storing_columns)      /* One file, multi columns down. */
    1436             :         {
    1437          35 :           p->char_func = store_char;
    1438          35 :           p->print_func = print_stored;
    1439             :         }
    1440             :       else
    1441             :         /* One file, multi columns across; or parallel files.  */
    1442             :         {
    1443           0 :           p->char_func = print_char;
    1444           0 :           p->print_func = read_line;
    1445             :         }
    1446             : 
    1447             :       /* Number only the first column when printing files in
    1448             :          parallel. */
    1449          35 :       p->numbered = numbered_lines && (!parallel_files || i == 1);
    1450          35 :       p->start_position = h;
    1451             : 
    1452             :       /* If we don't truncate lines, all start_positions are
    1453             :          ANYWHERE, except the first column's start_position when
    1454             :          using a margin. */
    1455             : 
    1456          35 :       if (!truncate_lines)
    1457             :         {
    1458           0 :           h = ANYWHERE;
    1459           0 :           h_next = ANYWHERE;
    1460             :         }
    1461             :       else
    1462             :         {
    1463          35 :           h = h_next + col_sep_length;
    1464          35 :           h_next = h + chars_per_column;
    1465             :         }
    1466             :     }
    1467             : 
    1468             :   /* The rightmost column.
    1469             : 
    1470             :      Doesn't need to be stored unless we intend to balance
    1471             :      columns on the last page. */
    1472          44 :   if (storing_columns & balance_columns)
    1473             :     {
    1474          10 :       p->char_func = store_char;
    1475          10 :       p->print_func = print_stored;
    1476             :     }
    1477             :   else
    1478             :     {
    1479          34 :       p->char_func = print_char;
    1480          34 :       p->print_func = read_line;
    1481             :     }
    1482             : 
    1483          44 :   p->numbered = numbered_lines && (!parallel_files || i == 1);
    1484          44 :   p->start_position = h;
    1485          44 : }
    1486             : 
    1487             : /* Open a file.  Return true if successful.
    1488             : 
    1489             :    With each file p, p->full_page_printed is initialized,
    1490             :    see also init_fps. */
    1491             : 
    1492             : static bool
    1493          39 : open_file (char *name, COLUMN *p)
    1494             : {
    1495          39 :   if (STREQ (name, "-"))
    1496             :     {
    1497          18 :       p->name = _("standard input");
    1498          18 :       p->fp = stdin;
    1499          18 :       have_read_stdin = true;
    1500             :     }
    1501             :   else
    1502             :     {
    1503          21 :       p->name = name;
    1504          21 :       p->fp = fopen (name, "r");
    1505             :     }
    1506          39 :   if (p->fp == NULL)
    1507             :     {
    1508          13 :       failed_opens = true;
    1509          13 :       if (!ignore_failed_opens)
    1510          13 :         error (0, errno, "%s", name);
    1511          13 :       return false;
    1512             :     }
    1513          26 :   p->status = OPEN;
    1514          26 :   p->full_page_printed = false;
    1515          26 :   ++total_files;
    1516          26 :   return true;
    1517             : }
    1518             : 
    1519             : /* Close the file in P.
    1520             : 
    1521             :    If we aren't dealing with multiple files in parallel, we change
    1522             :    the status of all columns in the column list to reflect the close. */
    1523             : 
    1524             : static void
    1525          51 : close_file (COLUMN *p)
    1526             : {
    1527             :   COLUMN *q;
    1528             :   int i;
    1529             : 
    1530          51 :   if (p->status == CLOSED)
    1531           0 :     return;
    1532          51 :   if (ferror (p->fp))
    1533           6 :     error (EXIT_FAILURE, errno, "%s", p->name);
    1534          45 :   if (fileno (p->fp) != STDIN_FILENO && fclose (p->fp) != 0)
    1535           0 :     error (EXIT_FAILURE, errno, "%s", p->name);
    1536             : 
    1537          45 :   if (!parallel_files)
    1538             :     {
    1539         125 :       for (q = column_vector, i = columns; i; ++q, --i)
    1540             :         {
    1541          80 :           q->status = CLOSED;
    1542          80 :           if (q->lines_stored == 0)
    1543             :             {
    1544          80 :               q->lines_to_print = 0;
    1545             :             }
    1546             :         }
    1547             :     }
    1548             :   else
    1549             :     {
    1550           0 :       p->status = CLOSED;
    1551           0 :       p->lines_to_print = 0;
    1552             :     }
    1553             : 
    1554          45 :   --files_ready_to_read;
    1555             : }
    1556             : 
    1557             : /* Put a file on hold until we start a new page,
    1558             :    since we've hit a form feed.
    1559             : 
    1560             :    If we aren't dealing with parallel files, we must change the
    1561             :    status of all columns in the column list. */
    1562             : 
    1563             : static void
    1564          89 : hold_file (COLUMN *p)
    1565             : {
    1566             :   COLUMN *q;
    1567             :   int i;
    1568             : 
    1569          89 :   if (!parallel_files)
    1570         212 :     for (q = column_vector, i = columns; i; ++q, --i)
    1571             :       {
    1572         123 :         if (storing_columns)
    1573          44 :           q->status = FF_FOUND;
    1574             :         else
    1575          79 :           q->status = ON_HOLD;
    1576             :       }
    1577             :   else
    1578           0 :     p->status = ON_HOLD;
    1579             : 
    1580          89 :   p->lines_to_print = 0;
    1581          89 :   --files_ready_to_read;
    1582          89 : }
    1583             : 
    1584             : /* Undo hold_file -- go through the column list and change any
    1585             :    ON_HOLD columns to OPEN.  Used at the end of each page. */
    1586             : 
    1587             : static void
    1588         134 : reset_status (void)
    1589             : {
    1590         134 :   int i = columns;
    1591             :   COLUMN *p;
    1592             : 
    1593         337 :   for (p = column_vector; i; --i, ++p)
    1594         203 :     if (p->status == ON_HOLD)
    1595             :       {
    1596         123 :         p->status = OPEN;
    1597         123 :         files_ready_to_read++;
    1598             :       }
    1599             : 
    1600         134 :   if (storing_columns)
    1601             :     {
    1602          20 :       if (column_vector->status == CLOSED)
    1603             :         /* We use the info to output an error message in  skip_to_page. */
    1604          10 :         files_ready_to_read = 0;
    1605             :       else
    1606          10 :         files_ready_to_read = 1;
    1607             :     }
    1608         134 : }
    1609             : 
    1610             : /* Print a single file, or multiple files in parallel.
    1611             : 
    1612             :    Set up the list of columns, opening the necessary files.
    1613             :    Allocate space for storing columns, if necessary.
    1614             :    Skip to first_page_number, if user has asked to skip leading pages.
    1615             :    Determine which functions are appropriate to store/print lines
    1616             :    in each column.
    1617             :    Print the file(s). */
    1618             : 
    1619             : static void
    1620          64 : print_files (int number_of_files, char **av)
    1621             : {
    1622          64 :   init_parameters (number_of_files);
    1623          64 :   if (! init_fps (number_of_files, av))
    1624          13 :     return;
    1625          51 :   if (storing_columns)
    1626          10 :     init_store_cols ();
    1627             : 
    1628          51 :   if (first_page_number > 1)
    1629             :     {
    1630           9 :       if (!skip_to_page (first_page_number))
    1631           7 :         return;
    1632             :       else
    1633           2 :         page_number = first_page_number;
    1634             :     }
    1635             :   else
    1636          42 :     page_number = 1;
    1637             : 
    1638          44 :   init_funcs ();
    1639             : 
    1640          44 :   line_number = line_count;
    1641          44 :   while (print_page ())
    1642             :     ;
    1643             : }
    1644             : 
    1645             : /* Initialize header information.
    1646             :    If DESC is non-negative, it is a file descriptor open to
    1647             :    FILENAME for reading.  */
    1648             : 
    1649             : static void
    1650          51 : init_header (char const *filename, int desc)
    1651             : {
    1652          51 :   char *buf = NULL;
    1653             :   struct stat st;
    1654             :   struct timespec t;
    1655             :   int ns;
    1656             :   struct tm *tm;
    1657             : 
    1658             :   /* If parallel files or standard input, use current date. */
    1659          51 :   if (STREQ (filename, "-"))
    1660          18 :     desc = -1;
    1661          51 :   if (0 <= desc && fstat (desc, &st) == 0)
    1662           8 :     t = get_stat_mtime (&st);
    1663             :   else
    1664             :     {
    1665             :       static struct timespec timespec;
    1666          43 :       if (! timespec.tv_sec)
    1667          43 :         gettime (&timespec);
    1668          43 :       t = timespec;
    1669             :     }
    1670             : 
    1671          51 :   ns = t.tv_nsec;
    1672          51 :   tm = localtime (&t.tv_sec);
    1673          51 :   if (tm == NULL)
    1674             :     {
    1675           2 :       buf = xmalloc (INT_BUFSIZE_BOUND (long int)
    1676             :                      + MAX (10, INT_BUFSIZE_BOUND (int)));
    1677           2 :       sprintf (buf, "%ld.%09d", (long int) t.tv_sec, ns);
    1678             :     }
    1679             :   else
    1680             :     {
    1681          49 :       size_t bufsize = nstrftime (NULL, SIZE_MAX, date_format, tm, 0, ns) + 1;
    1682          49 :       buf = xmalloc (bufsize);
    1683          49 :       nstrftime (buf, bufsize, date_format, tm, 0, ns);
    1684             :     }
    1685             : 
    1686          51 :   free (date_text);
    1687          51 :   date_text = buf;
    1688          51 :   file_text = custom_header ? custom_header : desc < 0 ? "" : filename;
    1689          51 :   header_width_available = (chars_per_line
    1690          51 :                             - mbswidth (date_text, 0)
    1691          51 :                             - mbswidth (file_text, 0));
    1692          51 : }
    1693             : 
    1694             : /* Set things up for printing a page
    1695             : 
    1696             :    Scan through the columns ...
    1697             :    Determine which are ready to print
    1698             :    (i.e., which have lines stored or open files)
    1699             :    Set p->lines_to_print appropriately
    1700             :    (to p->lines_stored if we're storing, or lines_per_body
    1701             :    if we're reading straight from the file)
    1702             :    Keep track of this total so we know when to stop printing */
    1703             : 
    1704             : static void
    1705         137 : init_page (void)
    1706             : {
    1707             :   int j;
    1708             :   COLUMN *p;
    1709             : 
    1710         137 :   if (storing_columns)
    1711             :     {
    1712          30 :       store_columns ();
    1713         134 :       for (j = columns - 1, p = column_vector; j; --j, ++p)
    1714             :         {
    1715         104 :           p->lines_to_print = p->lines_stored;
    1716             :         }
    1717             : 
    1718             :       /* Last column. */
    1719          30 :       if (balance_columns)
    1720             :         {
    1721          30 :           p->lines_to_print = p->lines_stored;
    1722             :         }
    1723             :       /* Since we're not balancing columns, we don't need to store
    1724             :          the rightmost column.   Read it straight from the file. */
    1725             :       else
    1726             :         {
    1727           0 :           if (p->status == OPEN)
    1728             :             {
    1729           0 :               p->lines_to_print = lines_per_body;
    1730             :             }
    1731             :           else
    1732           0 :             p->lines_to_print = 0;
    1733             :         }
    1734             :     }
    1735             :   else
    1736         214 :     for (j = columns, p = column_vector; j; --j, ++p)
    1737         107 :       if (p->status == OPEN)
    1738             :         {
    1739          79 :           p->lines_to_print = lines_per_body;
    1740             :         }
    1741             :       else
    1742          28 :         p->lines_to_print = 0;
    1743         137 : }
    1744             : 
    1745             : /* Align empty columns and print separators.
    1746             :    Empty columns will be formed by files with status ON_HOLD or CLOSED
    1747             :    when printing multiple files in parallel. */
    1748             : 
    1749             : static void
    1750           0 : align_column (COLUMN *p)
    1751             : {
    1752           0 :   padding_not_printed = p->start_position;
    1753           0 :   if (padding_not_printed - col_sep_length > 0)
    1754             :     {
    1755           0 :       pad_across_to (padding_not_printed - col_sep_length);
    1756           0 :       padding_not_printed = ANYWHERE;
    1757             :     }
    1758             : 
    1759           0 :   if (use_col_separator)
    1760           0 :     print_sep_string ();
    1761             : 
    1762           0 :   if (p->numbered)
    1763           0 :     add_line_number (p);
    1764           0 : }
    1765             : 
    1766             : /* Print one page.
    1767             : 
    1768             :    As long as there are lines left on the page and columns ready to print,
    1769             :    Scan across the column list
    1770             :    if the column has stored lines or the file is open
    1771             :    pad to the appropriate spot
    1772             :    print the column
    1773             :    pad the remainder of the page with \n or \f as requested
    1774             :    reset the status of all files -- any files which where on hold because
    1775             :    of formfeeds are now put back into the lineup. */
    1776             : 
    1777             : static bool
    1778         137 : print_page (void)
    1779             : {
    1780             :   int j;
    1781             :   int lines_left_on_page;
    1782             :   COLUMN *p;
    1783             : 
    1784             :   /* Used as an accumulator (with | operator) of successive values of
    1785             :      pad_vertically.  The trick is to set pad_vertically
    1786             :      to false before each run through the inner loop, then after that
    1787             :      loop, it tells us whether a line was actually printed (whether a
    1788             :      newline needs to be output -- or two for double spacing).  But those
    1789             :      values have to be accumulated (in pv) so we can invoke pad_down
    1790             :      properly after the outer loop completes. */
    1791             :   bool pv;
    1792             : 
    1793         137 :   init_page ();
    1794             : 
    1795         137 :   if (cols_ready_to_print () == 0)
    1796          38 :     return false;
    1797             : 
    1798          99 :   if (extremities)
    1799          99 :     print_a_header = true;
    1800             : 
    1801             :   /* Don't pad unless we know a page was printed. */
    1802          99 :   pad_vertically = false;
    1803          99 :   pv = false;
    1804             : 
    1805          99 :   lines_left_on_page = lines_per_body;
    1806          99 :   if (double_space)
    1807           0 :     lines_left_on_page *= 2;
    1808             : 
    1809         300 :   while (lines_left_on_page > 0 && cols_ready_to_print () > 0)
    1810             :     {
    1811         108 :       output_position = 0;
    1812         108 :       spaces_not_printed = 0;
    1813         108 :       separators_not_printed = 0;
    1814         108 :       pad_vertically = false;
    1815         108 :       align_empty_cols = false;
    1816         108 :       empty_line = true;
    1817             : 
    1818         118 :       for (j = 1, p = column_vector; j <= columns; ++j, ++p)
    1819             :         {
    1820         109 :           input_position = 0;
    1821         109 :           if (p->lines_to_print > 0 || p->status == FF_FOUND)
    1822             :             {
    1823         109 :               FF_only = false;
    1824         109 :               padding_not_printed = p->start_position;
    1825         109 :               if (!(p->print_func) (p))
    1826           0 :                 read_rest_of_line (p);
    1827         103 :               pv |= pad_vertically;
    1828             : 
    1829         103 :               --p->lines_to_print;
    1830         103 :               if (p->lines_to_print <= 0)
    1831             :                 {
    1832          94 :                   if (cols_ready_to_print () <= 0)
    1833          93 :                     break;
    1834             :                 }
    1835             : 
    1836             :               /* File p changed its status to ON_HOLD or CLOSED */
    1837          20 :               if (parallel_files && p->status != OPEN)
    1838             :                 {
    1839           0 :                   if (empty_line)
    1840           0 :                     align_empty_cols = true;
    1841           0 :                   else if (p->status == CLOSED ||
    1842           0 :                            (p->status == ON_HOLD && FF_only))
    1843           0 :                     align_column (p);
    1844             :                 }
    1845             :             }
    1846           0 :           else if (parallel_files)
    1847             :             {
    1848             :               /* File status ON_HOLD or CLOSED */
    1849           0 :               if (empty_line)
    1850           0 :                 align_empty_cols = true;
    1851             :               else
    1852           0 :                 align_column (p);
    1853             :             }
    1854             : 
    1855             :           /* We need it also with an empty column */
    1856          10 :           if (use_col_separator)
    1857           1 :             ++separators_not_printed;
    1858             :         }
    1859             : 
    1860         102 :       if (pad_vertically)
    1861             :         {
    1862          95 :           putchar ('\n');
    1863          95 :           --lines_left_on_page;
    1864             :         }
    1865             : 
    1866         102 :       if (cols_ready_to_print () <= 0 && !extremities)
    1867           0 :         break;
    1868             : 
    1869         102 :       if (double_space & pv)
    1870             :         {
    1871           0 :           putchar ('\n');
    1872           0 :           --lines_left_on_page;
    1873             :         }
    1874             :     }
    1875             : 
    1876          93 :   if (lines_left_on_page == 0)
    1877           0 :     for (j = 1, p = column_vector; j <= columns; ++j, ++p)
    1878           0 :       if (p->status == OPEN)
    1879           0 :         p->full_page_printed = true;
    1880             : 
    1881          93 :   pad_vertically = pv;
    1882             : 
    1883          93 :   if (pad_vertically & extremities)
    1884          90 :     pad_down (lines_left_on_page + lines_per_footer);
    1885           3 :   else if (keep_FF & print_a_FF)
    1886             :     {
    1887           0 :       putchar ('\f');
    1888           0 :       print_a_FF = false;
    1889             :     }
    1890             : 
    1891          93 :   if (last_page_number < page_number)
    1892           0 :     return false;               /* Stop printing with LAST_PAGE */
    1893             : 
    1894          93 :   reset_status ();              /* Change ON_HOLD to OPEN. */
    1895             : 
    1896          93 :   return true;                  /* More pages to go. */
    1897             : }
    1898             : 
    1899             : /* Allocate space for storing columns.
    1900             : 
    1901             :    This is necessary when printing multiple columns from a single file.
    1902             :    Lines are stored consecutively in buff, separated by '\0'.
    1903             : 
    1904             :    The following doesn't apply any longer - any tuning possible?
    1905             :    (We can't use a fixed offset since with the '-s' flag lines aren't
    1906             :    truncated.)
    1907             : 
    1908             :    We maintain a list (line_vector) of pointers to the beginnings
    1909             :    of lines in buff.  We allocate one more than the number of lines
    1910             :    because the last entry tells us the index of the last character,
    1911             :    which we need to know in order to print the last line in buff. */
    1912             : 
    1913             : static void
    1914          10 : init_store_cols (void)
    1915             : {
    1916          10 :   int total_lines = lines_per_body * columns;
    1917          10 :   int chars_if_truncate = total_lines * (chars_per_column + 1);
    1918             : 
    1919          10 :   free (line_vector);
    1920             :   /* FIXME: here's where it was allocated.  */
    1921          10 :   line_vector = xmalloc ((total_lines + 1) * sizeof (int *));
    1922             : 
    1923          10 :   free (end_vector);
    1924          10 :   end_vector = xmalloc (total_lines * sizeof (int *));
    1925             : 
    1926          10 :   free (buff);
    1927          10 :   buff_allocated = (use_col_separator
    1928          20 :                     ? 2 * chars_if_truncate
    1929          20 :                     : chars_if_truncate);       /* Tune this. */
    1930          10 :   buff = xmalloc (buff_allocated);
    1931          10 : }
    1932             : 
    1933             : /* Store all but the rightmost column.
    1934             :    (Used when printing a single file in multiple downward columns)
    1935             : 
    1936             :    For each column
    1937             :    set p->current_line to be the index in line_vector of the
    1938             :    first line in the column
    1939             :    For each line in the column
    1940             :    store the line in buff
    1941             :    add to line_vector the index of the line's first char
    1942             :    buff_start is the index in buff of the first character in the
    1943             :    current line. */
    1944             : 
    1945             : static void
    1946          30 : store_columns (void)
    1947             : {
    1948             :   int i, j;
    1949          30 :   int line = 0;
    1950             :   int buff_start;
    1951             :   int last_col;         /* The rightmost column which will be saved in buff */
    1952             :   COLUMN *p;
    1953             : 
    1954          30 :   buff_current = 0;
    1955          30 :   buff_start = 0;
    1956             : 
    1957          30 :   if (balance_columns)
    1958          30 :     last_col = columns;
    1959             :   else
    1960           0 :     last_col = columns - 1;
    1961             : 
    1962         164 :   for (i = 1, p = column_vector; i <= last_col; ++i, ++p)
    1963         134 :     p->lines_stored = 0;
    1964             : 
    1965          80 :   for (i = 1, p = column_vector; i <= last_col && files_ready_to_read;
    1966          20 :        ++i, ++p)
    1967             :     {
    1968          20 :       p->current_line = line;
    1969          43 :       for (j = lines_per_body; j && files_ready_to_read; --j)
    1970             : 
    1971          23 :         if (p->status == OPEN)       /* Redundant.  Clean up. */
    1972             :           {
    1973          23 :             input_position = 0;
    1974             : 
    1975          23 :             if (!read_line (p))
    1976           2 :               read_rest_of_line (p);
    1977             : 
    1978          23 :             if (p->status == OPEN
    1979          20 :                 || buff_start != buff_current)
    1980             :               {
    1981          14 :                 ++p->lines_stored;
    1982          14 :                 line_vector[line] = buff_start;
    1983          14 :                 end_vector[line++] = input_position;
    1984          14 :                 buff_start = buff_current;
    1985             :               }
    1986             :           }
    1987             :     }
    1988             : 
    1989             :   /* Keep track of the location of the last char in buff. */
    1990          30 :   line_vector[line] = buff_start;
    1991             : 
    1992          30 :   if (balance_columns)
    1993          30 :     balance (line);
    1994          30 : }
    1995             : 
    1996             : static void
    1997          30 : balance (int total_stored)
    1998             : {
    1999             :   COLUMN *p;
    2000             :   int i, lines;
    2001          30 :   int first_line = 0;
    2002             : 
    2003         164 :   for (i = 1, p = column_vector; i <= columns; ++i, ++p)
    2004             :     {
    2005         134 :       lines = total_stored / columns;
    2006         134 :       if (i <= total_stored % columns)
    2007          12 :         ++lines;
    2008             : 
    2009         134 :       p->lines_stored = lines;
    2010         134 :       p->current_line = first_line;
    2011             : 
    2012         134 :       first_line += lines;
    2013             :     }
    2014          30 : }
    2015             : 
    2016             : /* Store a character in the buffer. */
    2017             : 
    2018             : static void
    2019          57 : store_char (char c)
    2020             : {
    2021          57 :   if (buff_current >= buff_allocated)
    2022             :     {
    2023             :       /* May be too generous. */
    2024           0 :       buff = X2REALLOC (buff, &buff_allocated);
    2025             :     }
    2026          57 :   buff[buff_current++] = c;
    2027          57 : }
    2028             : 
    2029             : static void
    2030           0 : add_line_number (COLUMN *p)
    2031             : {
    2032             :   int i;
    2033             :   char *s;
    2034             :   int left_cut;
    2035             : 
    2036             :   /* Cutting off the higher-order digits is more informative than
    2037             :      lower-order cut off*/
    2038           0 :   if (line_number < power_10)
    2039           0 :     sprintf (number_buff, "%*d", chars_per_number, line_number);
    2040             :   else
    2041             :     {
    2042           0 :       left_cut = line_number % power_10;
    2043           0 :       sprintf (number_buff, "%0*d", chars_per_number, left_cut);
    2044             :     }
    2045           0 :   line_number++;
    2046           0 :   s = number_buff;
    2047           0 :   for (i = chars_per_number; i > 0; i--)
    2048           0 :     (p->char_func) (*s++);
    2049             : 
    2050           0 :   if (columns > 1)
    2051             :     {
    2052             :       /* Tabification is assumed for multiple columns, also for n-separators,
    2053             :          but `default n-separator = TAB' hasn't been given priority over
    2054             :          equal column_width also specified by POSIX. */
    2055           0 :       if (number_separator == '\t')
    2056             :         {
    2057           0 :           i = number_width - chars_per_number;
    2058           0 :           while (i-- > 0)
    2059           0 :             (p->char_func) (' ');
    2060             :         }
    2061             :       else
    2062           0 :         (p->char_func) (number_separator);
    2063             :     }
    2064             :   else
    2065             :     /* To comply with POSIX, we avoid any expansion of default TAB
    2066             :        separator with a single column output. No column_width requirement
    2067             :        has to be considered. */
    2068             :     {
    2069           0 :       (p->char_func) (number_separator);
    2070           0 :       if (number_separator == '\t')
    2071           0 :         output_position = POS_AFTER_TAB (chars_per_output_tab,
    2072             :                           output_position);
    2073             :     }
    2074             : 
    2075           0 :   if (truncate_lines & !parallel_files)
    2076           0 :     input_position += number_width;
    2077           0 : }
    2078             : 
    2079             : /* Print (or store) padding until the current horizontal position
    2080             :    is position. */
    2081             : 
    2082             : static void
    2083          94 : pad_across_to (int position)
    2084             : {
    2085          94 :   int h = output_position;
    2086             : 
    2087          94 :   if (tabify_output)
    2088          21 :     spaces_not_printed = position - output_position;
    2089             :   else
    2090             :     {
    2091         146 :       while (++h <= position)
    2092           0 :         putchar (' ');
    2093          73 :       output_position = position;
    2094             :     }
    2095          94 : }
    2096             : 
    2097             : /* Pad to the bottom of the page.
    2098             : 
    2099             :    If the user has requested a formfeed, use one.
    2100             :    Otherwise, use newlines. */
    2101             : 
    2102             : static void
    2103          90 : pad_down (int lines)
    2104             : {
    2105             :   int i;
    2106             : 
    2107          90 :   if (use_form_feed)
    2108           0 :     putchar ('\f');
    2109             :   else
    2110        5485 :     for (i = lines; i; --i)
    2111        5395 :       putchar ('\n');
    2112          90 : }
    2113             : 
    2114             : /* Read the rest of the line.
    2115             : 
    2116             :    Read from the current column's file until an end of line is
    2117             :    hit.  Used when we've truncated a line and we no longer need
    2118             :    to print or store its characters. */
    2119             : 
    2120             : static void
    2121           2 : read_rest_of_line (COLUMN *p)
    2122             : {
    2123             :   int c;
    2124           2 :   FILE *f = p->fp;
    2125             : 
    2126           4 :   while ((c = getc (f)) != '\n')
    2127             :     {
    2128           2 :       if (c == '\f')
    2129             :         {
    2130           2 :           if ((c = getc (f)) != '\n')
    2131           1 :             ungetc (c, f);
    2132           2 :           if (keep_FF)
    2133           0 :             print_a_FF = true;
    2134           2 :           hold_file (p);
    2135           2 :           break;
    2136             :         }
    2137           0 :       else if (c == EOF)
    2138             :         {
    2139           0 :           close_file (p);
    2140           0 :           break;
    2141             :         }
    2142             :     }
    2143           2 : }
    2144             : 
    2145             : /* Read a line with skip_to_page.
    2146             : 
    2147             :    Read from the current column's file until an end of line is
    2148             :    hit.  Used when we read full lines to skip pages.
    2149             :    With skip_to_page we have to check for FF-coincidence which is done
    2150             :    in function read_line otherwise.
    2151             :    Count lines of skipped pages to find the line number of 1st page
    2152             :    printed relative to 1st line of input file (start_line_num). */
    2153             : 
    2154             : static void
    2155          42 : skip_read (COLUMN *p, int column_number)
    2156             : {
    2157             :   int c;
    2158          42 :   FILE *f = p->fp;
    2159             :   int i;
    2160          42 :   bool single_ff = false;
    2161             :   COLUMN *q;
    2162             : 
    2163             :   /* Read 1st character in a line or any character succeeding a FF */
    2164          42 :   if ((c = getc (f)) == '\f' && p->full_page_printed)
    2165             :     /* A FF-coincidence with a previous full_page_printed.
    2166             :        To avoid an additional empty page, eliminate the FF */
    2167           0 :     if ((c = getc (f)) == '\n')
    2168           0 :       c = getc (f);
    2169             : 
    2170          42 :   p->full_page_printed = false;
    2171             : 
    2172             :   /* 1st character a FF means a single FF without any printable
    2173             :      characters. Don't count it as a line with -n option. */
    2174          42 :   if (c == '\f')
    2175          33 :     single_ff = true;
    2176             : 
    2177             :   /* Preparing for a FF-coincidence: Maybe we finish that page
    2178             :      without a FF found */
    2179          42 :   if (last_line)
    2180           0 :     p->full_page_printed = true;
    2181             : 
    2182          89 :   while (c != '\n')
    2183             :     {
    2184          46 :       if (c == '\f')
    2185             :         {
    2186             :           /* No FF-coincidence possible,
    2187             :              no catching up of a FF-coincidence with next page */
    2188          34 :           if (last_line)
    2189             :             {
    2190           0 :               if (!parallel_files)
    2191           0 :                 for (q = column_vector, i = columns; i; ++q, --i)
    2192           0 :                   q->full_page_printed = false;
    2193             :               else
    2194           0 :                 p->full_page_printed = false;
    2195             :             }
    2196             : 
    2197          34 :           if ((c = getc (f)) != '\n')
    2198           8 :             ungetc (c, f);
    2199          34 :           hold_file (p);
    2200          34 :           break;
    2201             :         }
    2202          12 :       else if (c == EOF)
    2203             :         {
    2204           7 :           close_file (p);
    2205           7 :           break;
    2206             :         }
    2207           5 :       c = getc (f);
    2208             :     }
    2209             : 
    2210          42 :   if (skip_count)
    2211          42 :     if ((!parallel_files || column_number == 1) && !single_ff)
    2212           9 :       ++line_count;
    2213          42 : }
    2214             : 
    2215             : /* If we're tabifying output,
    2216             : 
    2217             :    When print_char encounters white space it keeps track
    2218             :    of our desired horizontal position and delays printing
    2219             :    until this function is called. */
    2220             : 
    2221             : static void
    2222          94 : print_white_space (void)
    2223             : {
    2224             :   int h_new;
    2225          94 :   int h_old = output_position;
    2226          94 :   int goal = h_old + spaces_not_printed;
    2227             : 
    2228         192 :   while (goal - h_old > 1
    2229           5 :          && (h_new = POS_AFTER_TAB (chars_per_output_tab, h_old)) <= goal)
    2230             :     {
    2231           4 :       putchar (output_tab_char);
    2232           4 :       h_old = h_new;
    2233             :     }
    2234         192 :   while (++h_old <= goal)
    2235           4 :     putchar (' ');
    2236             : 
    2237          94 :   output_position = goal;
    2238          94 :   spaces_not_printed = 0;
    2239          94 : }
    2240             : 
    2241             : /* Print column separators.
    2242             : 
    2243             :    We keep a count until we know that we'll be printing a line,
    2244             :    then print_sep_string() is called. */
    2245             : 
    2246             : static void
    2247          14 : print_sep_string (void)
    2248             : {
    2249             :   char *s;
    2250          14 :   int l = col_sep_length;
    2251             : 
    2252          14 :   s = col_sep_string;
    2253             : 
    2254          14 :   if (separators_not_printed <= 0)
    2255             :     {
    2256             :       /* We'll be starting a line with chars_per_margin, anything else? */
    2257          13 :       if (spaces_not_printed > 0)
    2258           0 :         print_white_space ();
    2259             :     }
    2260             :   else
    2261             :     {
    2262           2 :       for (; separators_not_printed > 0; --separators_not_printed)
    2263             :         {
    2264           3 :           while (l-- > 0)
    2265             :             {
    2266             :               /* 3 types of sep_strings: spaces only, spaces and chars,
    2267             :               chars only */
    2268           1 :               if (*s == ' ')
    2269             :                 {
    2270             :                   /* We're tabifying output; consecutive spaces in
    2271             :                   sep_string may have to be converted to tabs */
    2272           1 :                   s++;
    2273           1 :                   ++spaces_not_printed;
    2274             :                 }
    2275             :               else
    2276             :                 {
    2277           0 :                   if (spaces_not_printed > 0)
    2278           0 :                     print_white_space ();
    2279           0 :                   putchar (*s++);
    2280           0 :                   ++output_position;
    2281             :                 }
    2282             :             }
    2283             :           /* sep_string ends with some spaces */
    2284           1 :           if (spaces_not_printed > 0)
    2285           1 :             print_white_space ();
    2286             :         }
    2287             :     }
    2288          14 : }
    2289             : 
    2290             : /* Print (or store, depending on p->char_func) a clump of N
    2291             :    characters. */
    2292             : 
    2293             : static void
    2294         200 : print_clump (COLUMN *p, int n, char *clump)
    2295             : {
    2296         598 :   while (n--)
    2297         198 :     (p->char_func) (*clump++);
    2298         200 : }
    2299             : 
    2300             : /* Print a character.
    2301             : 
    2302             :    Update the following comment: process-char hasn't been used any
    2303             :    longer.
    2304             :    If we're tabifying, all tabs have been converted to spaces by
    2305             :    process_char().  Keep a count of consecutive spaces, and when
    2306             :    a nonspace is encountered, call print_white_space() to print the
    2307             :    required number of tabs and spaces. */
    2308             : 
    2309             : static void
    2310         198 : print_char (char c)
    2311             : {
    2312         198 :   if (tabify_output)
    2313             :     {
    2314          57 :       if (c == ' ')
    2315             :         {
    2316           0 :           ++spaces_not_printed;
    2317           0 :           return;
    2318             :         }
    2319          57 :       else if (spaces_not_printed > 0)
    2320           0 :         print_white_space ();
    2321             : 
    2322             :       /* Nonprintables are assumed to have width 0, except '\b'. */
    2323          57 :       if (! isprint (to_uchar (c)))
    2324             :         {
    2325          57 :           if (c == '\b')
    2326           0 :             --output_position;
    2327             :         }
    2328             :       else
    2329           0 :         ++output_position;
    2330             :     }
    2331         198 :   putchar (c);
    2332             : }
    2333             : 
    2334             : /* Skip to page PAGE before printing.
    2335             :    PAGE may be larger than total number of pages. */
    2336             : 
    2337             : static bool
    2338           9 : skip_to_page (uintmax_t page)
    2339             : {
    2340             :   uintmax_t n;
    2341             :   int i;
    2342             :   int j;
    2343             :   COLUMN *p;
    2344             : 
    2345          43 :   for (n = 1; n < page; ++n)
    2346             :     {
    2347        2296 :       for (i = 1; i < lines_per_body; ++i)
    2348             :         {
    2349        4510 :           for (j = 1, p = column_vector; j <= columns; ++j, ++p)
    2350        2255 :             if (p->status == OPEN)
    2351          42 :               skip_read (p, j);
    2352             :         }
    2353          41 :       last_line = true;
    2354          82 :       for (j = 1, p = column_vector; j <= columns; ++j, ++p)
    2355          41 :         if (p->status == OPEN)
    2356           0 :           skip_read (p, j);
    2357             : 
    2358          41 :       if (storing_columns)      /* change FF_FOUND to ON_HOLD */
    2359           0 :         for (j = 1, p = column_vector; j <= columns; ++j, ++p)
    2360           0 :           if (p->status != CLOSED)
    2361           0 :             p->status = ON_HOLD;
    2362             : 
    2363          41 :       reset_status ();
    2364          41 :       last_line = false;
    2365             : 
    2366          41 :       if (files_ready_to_read < 1)
    2367             :         {
    2368             :           /* It's very helpful, normally the total number of pages is
    2369             :              not known in advance.  */
    2370           7 :           error (0, 0,
    2371             :                  _("starting page number %"PRIuMAX
    2372             :                    " exceeds page count %"PRIuMAX),
    2373             :                  page, n);
    2374           7 :           break;
    2375             :         }
    2376             :     }
    2377           9 :   return files_ready_to_read > 0;
    2378             : }
    2379             : 
    2380             : /* Print a header.
    2381             : 
    2382             :    Formfeeds are assumed to use up two lines at the beginning of
    2383             :    the page. */
    2384             : 
    2385             : static void
    2386          93 : print_header (void)
    2387             : {
    2388             :   char page_text[256 + INT_STRLEN_BOUND (page_number)];
    2389             :   int available_width;
    2390             :   int lhs_spaces;
    2391             :   int rhs_spaces;
    2392             : 
    2393          93 :   output_position = 0;
    2394          93 :   pad_across_to (chars_per_margin);
    2395          93 :   print_white_space ();
    2396             : 
    2397          93 :   if (page_number == 0)
    2398           0 :     error (EXIT_FAILURE, 0, _("Page number overflow"));
    2399             : 
    2400             :   /* The translator must ensure that formatting the translation of
    2401             :      "Page %"PRIuMAX does not generate more than (sizeof page_text - 1)
    2402             :      bytes.  */
    2403          93 :   sprintf (page_text, _("Page %"PRIuMAX), page_number++);
    2404          93 :   available_width = header_width_available - mbswidth (page_text, 0);
    2405          93 :   available_width = MAX (0, available_width);
    2406          93 :   lhs_spaces = available_width >> 1;
    2407          93 :   rhs_spaces = available_width - lhs_spaces;
    2408             : 
    2409          93 :   printf ("\n\n%s%*s%s%*s%s\n\n\n",
    2410             :           date_text, lhs_spaces, " ", file_text, rhs_spaces, " ", page_text);
    2411             : 
    2412          93 :   print_a_header = false;
    2413          93 :   output_position = 0;
    2414          93 : }
    2415             : 
    2416             : /* Print (or store, if p->char_func is store_char()) a line.
    2417             : 
    2418             :    Read a character to determine whether we have a line or not.
    2419             :    (We may hit EOF, \n, or \f)
    2420             : 
    2421             :    Once we know we have a line,
    2422             :    set pad_vertically = true, meaning it's safe
    2423             :    to pad down at the end of the page, since we do have a page.
    2424             :    print a header if needed.
    2425             :    pad across to padding_not_printed if needed.
    2426             :    print any separators which need to be printed.
    2427             :    print a line number if it needs to be printed.
    2428             : 
    2429             :    Print the clump which corresponds to the first character.
    2430             : 
    2431             :    Enter a loop and keep printing until an end of line condition
    2432             :    exists, or until we exceed chars_per_column.
    2433             : 
    2434             :    Return false if we exceed chars_per_column before reading
    2435             :    an end of line character, true otherwise. */
    2436             : 
    2437             : static bool
    2438         111 : read_line (COLUMN *p)
    2439             : {
    2440             :   int c;
    2441             :   int chars IF_LINT (= 0);
    2442             :   int last_input_position;
    2443             :   int j, k;
    2444             :   COLUMN *q;
    2445             : 
    2446             :   /* read 1st character in each line or any character succeeding a FF: */
    2447         111 :   c = getc (p->fp);
    2448             : 
    2449         111 :   last_input_position = input_position;
    2450             : 
    2451         111 :   if (c == '\f' && p->full_page_printed)
    2452           0 :     if ((c = getc (p->fp)) == '\n')
    2453           0 :       c = getc (p->fp);
    2454         111 :   p->full_page_printed = false;
    2455             : 
    2456         111 :   switch (c)
    2457             :     {
    2458          43 :     case '\f':
    2459          43 :       if ((c = getc (p->fp)) != '\n')
    2460           3 :         ungetc (c, p->fp);
    2461          43 :       FF_only = true;
    2462          43 :       if (print_a_header & !storing_columns)
    2463             :         {
    2464          32 :           pad_vertically = true;
    2465          32 :           print_header ();
    2466             :         }
    2467          11 :       else if (keep_FF)
    2468           0 :         print_a_FF = true;
    2469          43 :       hold_file (p);
    2470          43 :       return true;
    2471           6 :     case EOF:
    2472           6 :       close_file (p);
    2473           3 :       return true;
    2474          12 :     case '\n':
    2475          12 :       break;
    2476          50 :     default:
    2477          50 :       chars = char_to_clump (c);
    2478             :     }
    2479             : 
    2480          62 :   if (truncate_lines && input_position > chars_per_column)
    2481             :     {
    2482           2 :       input_position = last_input_position;
    2483           2 :       return false;
    2484             :     }
    2485             : 
    2486          60 :   if (p->char_func != store_char)
    2487             :     {
    2488          46 :       pad_vertically = true;
    2489             : 
    2490          46 :       if (print_a_header & !storing_columns)
    2491          41 :         print_header ();
    2492             : 
    2493          46 :       if (parallel_files & align_empty_cols)
    2494             :         {
    2495             :           /* We have to align empty columns at the beginning of a line. */
    2496           0 :           k = separators_not_printed;
    2497           0 :           separators_not_printed = 0;
    2498           0 :           for (j = 1, q = column_vector; j <= k; ++j, ++q)
    2499             :             {
    2500           0 :               align_column (q);
    2501           0 :               separators_not_printed += 1;
    2502             :             }
    2503           0 :           padding_not_printed = p->start_position;
    2504           0 :           if (truncate_lines)
    2505           0 :             spaces_not_printed = chars_per_column;
    2506             :           else
    2507           0 :             spaces_not_printed = 0;
    2508           0 :           align_empty_cols = false;
    2509             :         }
    2510             : 
    2511          46 :       if (padding_not_printed - col_sep_length > 0)
    2512             :         {
    2513           0 :           pad_across_to (padding_not_printed - col_sep_length);
    2514           0 :           padding_not_printed = ANYWHERE;
    2515             :         }
    2516             : 
    2517          46 :       if (use_col_separator)
    2518           0 :         print_sep_string ();
    2519             :     }
    2520             : 
    2521          60 :   if (p->numbered)
    2522           0 :     add_line_number (p);
    2523             : 
    2524          60 :   empty_line = false;
    2525          60 :   if (c == '\n')
    2526          12 :     return true;
    2527             : 
    2528          48 :   print_clump (p, chars, clump_buff);
    2529             : 
    2530             :   for (;;)
    2531             :     {
    2532         352 :       c = getc (p->fp);
    2533             : 
    2534         200 :       switch (c)
    2535             :         {
    2536           0 :         case '\n':
    2537           0 :           return true;
    2538          10 :         case '\f':
    2539          10 :           if ((c = getc (p->fp)) != '\n')
    2540           2 :             ungetc (c, p->fp);
    2541          10 :           if (keep_FF)
    2542           0 :             print_a_FF = true;
    2543          10 :           hold_file (p);
    2544          10 :           return true;
    2545          38 :         case EOF:
    2546          38 :           close_file (p);
    2547          35 :           return true;
    2548             :         }
    2549             : 
    2550         152 :       last_input_position = input_position;
    2551         152 :       chars = char_to_clump (c);
    2552         152 :       if (truncate_lines && input_position > chars_per_column)
    2553             :         {
    2554           0 :           input_position = last_input_position;
    2555           0 :           return false;
    2556             :         }
    2557             : 
    2558         152 :       print_clump (p, chars, clump_buff);
    2559             :     }
    2560             : }
    2561             : 
    2562             : /* Print a line from buff.
    2563             : 
    2564             :    If this function has been called, we know we have "something to
    2565             :    print". But it remains to be seen whether we have a real text page
    2566             :    or an empty page (a single form feed) with/without a header only.
    2567             :    Therefore first we set pad_vertically to true and print a header
    2568             :    if necessary.
    2569             :    If FF_FOUND and we are using -t|-T option we omit any newline by
    2570             :    setting pad_vertically to false (see print_page).
    2571             :    Otherwise we pad across if necessary, print separators if necessary
    2572             :    and text of COLUMN *p.
    2573             : 
    2574             :    Return true, meaning there is no need to call read_rest_of_line. */
    2575             : 
    2576             : static bool
    2577          21 : print_stored (COLUMN *p)
    2578             : {
    2579             :   COLUMN *q;
    2580             :   int i;
    2581             : 
    2582          21 :   int line = p->current_line++;
    2583          21 :   char *first = &buff[line_vector[line]];
    2584             :   /* FIXME
    2585             :      UMR: Uninitialized memory read:
    2586             :      * This is occurring while in:
    2587             :      print_stored   [pr.c:2239]
    2588             :      * Reading 4 bytes from 0x5148c in the heap.
    2589             :      * Address 0x5148c is 4 bytes into a malloc'd block at 0x51488 of 676 bytes
    2590             :      * This block was allocated from:
    2591             :      malloc         [rtlib.o]
    2592             :      xmalloc        [xmalloc.c:94]
    2593             :      init_store_cols [pr.c:1648]
    2594             :      */
    2595          21 :   char *last = &buff[line_vector[line + 1]];
    2596             : 
    2597          21 :   pad_vertically = true;
    2598             : 
    2599          21 :   if (print_a_header)
    2600          20 :     print_header ();
    2601             : 
    2602          21 :   if (p->status == FF_FOUND)
    2603             :     {
    2604          54 :       for (i = 1, q = column_vector; i <= columns; ++i, ++q)
    2605          44 :         q->status = ON_HOLD;
    2606          10 :       if (column_vector->lines_to_print <= 0)
    2607             :         {
    2608           7 :           if (!extremities)
    2609           0 :             pad_vertically = false;
    2610           7 :           return true;          /* print a header only */
    2611             :         }
    2612             :     }
    2613             : 
    2614          14 :   if (padding_not_printed - col_sep_length > 0)
    2615             :     {
    2616           1 :       pad_across_to (padding_not_printed - col_sep_length);
    2617           1 :       padding_not_printed = ANYWHERE;
    2618             :     }
    2619             : 
    2620          14 :   if (use_col_separator)
    2621          14 :     print_sep_string ();
    2622             : 
    2623          85 :   while (first != last)
    2624          57 :     print_char (*first++);
    2625             : 
    2626          14 :   if (spaces_not_printed == 0)
    2627             :     {
    2628          14 :       output_position = p->start_position + end_vector[line];
    2629          14 :       if (p->start_position - col_sep_length == chars_per_margin)
    2630          13 :         output_position -= col_sep_length;
    2631             :     }
    2632             : 
    2633          14 :   return true;
    2634             : }
    2635             : 
    2636             : /* Convert a character to the proper format and return the number of
    2637             :    characters in the resulting clump.  Increment input_position by
    2638             :    the width of the clump.
    2639             : 
    2640             :    Tabs are converted to clumps of spaces.
    2641             :    Nonprintable characters may be converted to clumps of escape
    2642             :    sequences or control prefixes.
    2643             : 
    2644             :    Note: the width of a clump is not necessarily equal to the number of
    2645             :    characters in clump_buff.  (e.g, the width of '\b' is -1, while the
    2646             :    number of characters is 1.) */
    2647             : 
    2648             : static int
    2649         202 : char_to_clump (char c)
    2650             : {
    2651         202 :   unsigned char uc = c;
    2652         202 :   char *s = clump_buff;
    2653             :   int i;
    2654             :   char esc_buff[4];
    2655             :   int width;
    2656             :   int chars;
    2657         202 :   int chars_per_c = 8;
    2658             : 
    2659         202 :   if (c == input_tab_char)
    2660          10 :     chars_per_c = chars_per_input_tab;
    2661             : 
    2662         202 :   if (c == input_tab_char || c == '\t')
    2663             :     {
    2664          10 :       width = TAB_WIDTH (chars_per_c, input_position);
    2665             : 
    2666          20 :       if (untabify_input)
    2667             :         {
    2668           0 :           for (i = width; i; --i)
    2669           0 :             *s++ = ' ';
    2670           0 :           chars = width;
    2671             :         }
    2672             :       else
    2673             :         {
    2674          10 :           *s = c;
    2675          10 :           chars = 1;
    2676             :         }
    2677             : 
    2678             :     }
    2679         192 :   else if (! isprint (uc))
    2680             :     {
    2681         189 :       if (use_esc_sequence)
    2682             :         {
    2683           0 :           width = 4;
    2684           0 :           chars = 4;
    2685           0 :           *s++ = '\\';
    2686           0 :           sprintf (esc_buff, "%03o", uc);
    2687           0 :           for (i = 0; i <= 2; ++i)
    2688           0 :             *s++ = esc_buff[i];
    2689             :         }
    2690         189 :       else if (use_cntrl_prefix)
    2691             :         {
    2692           0 :           if (uc < 0200)
    2693             :             {
    2694           0 :               width = 2;
    2695           0 :               chars = 2;
    2696           0 :               *s++ = '^';
    2697           0 :               *s++ = c ^ 0100;
    2698             :             }
    2699             :           else
    2700             :             {
    2701           0 :               width = 4;
    2702           0 :               chars = 4;
    2703           0 :               *s++ = '\\';
    2704           0 :               sprintf (esc_buff, "%03o", uc);
    2705           0 :               for (i = 0; i <= 2; ++i)
    2706           0 :                 *s++ = esc_buff[i];
    2707             :             }
    2708             :         }
    2709         189 :       else if (c == '\b')
    2710             :         {
    2711           4 :           width = -1;
    2712           4 :           chars = 1;
    2713           4 :           *s = c;
    2714             :         }
    2715             :       else
    2716             :         {
    2717         185 :           width = 0;
    2718         185 :           chars = 1;
    2719         185 :           *s = c;
    2720             :         }
    2721             :     }
    2722             :   else
    2723             :     {
    2724           3 :       width = 1;
    2725           3 :       chars = 1;
    2726           3 :       *s = c;
    2727             :     }
    2728             : 
    2729             :   /* Too many backspaces must put us in position 0 -- never negative.  */
    2730         202 :   if (width < 0 && input_position == 0)
    2731             :     {
    2732           2 :       chars = 0;
    2733           2 :       input_position = 0;
    2734             :     }
    2735         200 :   else if (width < 0 && input_position <= -width)
    2736           1 :     input_position = 0;
    2737             :   else
    2738         199 :     input_position += width;
    2739             : 
    2740         202 :   return chars;
    2741             : }
    2742             : 
    2743             : /* We've just printed some files and need to clean up things before
    2744             :    looking for more options and printing the next batch of files.
    2745             : 
    2746             :    Free everything we've xmalloc'ed, except `header'. */
    2747             : 
    2748             : static void
    2749          53 : cleanup (void)
    2750             : {
    2751          53 :   free (number_buff);
    2752          53 :   free (clump_buff);
    2753          53 :   free (column_vector);
    2754          53 :   free (line_vector);
    2755          53 :   free (end_vector);
    2756          53 :   free (buff);
    2757          53 : }
    2758             : 
    2759             : /* Complain, print a usage message, and die. */
    2760             : 
    2761             : void
    2762           7 : usage (int status)
    2763             : {
    2764           7 :   if (status != EXIT_SUCCESS)
    2765           7 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
    2766             :              program_name);
    2767             :   else
    2768             :     {
    2769           0 :       printf (_("\
    2770             : Usage: %s [OPTION]... [FILE]...\n\
    2771             : "),
    2772             :               program_name);
    2773             : 
    2774           0 :       fputs (_("\
    2775             : Paginate or columnate FILE(s) for printing.\n\
    2776             : \n\
    2777             : "), stdout);
    2778           0 :       fputs (_("\
    2779             : Mandatory arguments to long options are mandatory for short options too.\n\
    2780             : "), stdout);
    2781           0 :       fputs (_("\
    2782             :   +FIRST_PAGE[:LAST_PAGE], --pages=FIRST_PAGE[:LAST_PAGE]\n\
    2783             :                     begin [stop] printing with page FIRST_[LAST_]PAGE\n\
    2784             :   -COLUMN, --columns=COLUMN\n\
    2785             :                     output COLUMN columns and print columns down,\n\
    2786             :                     unless -a is used. Balance number of lines in the\n\
    2787             :                     columns on each page.\n\
    2788             : "), stdout);
    2789           0 :       fputs (_("\
    2790             :   -a, --across      print columns across rather than down, used together\n\
    2791             :                     with -COLUMN\n\
    2792             :   -c, --show-control-chars\n\
    2793             :                     use hat notation (^G) and octal backslash notation\n\
    2794             :   -d, --double-space\n\
    2795             :                     double space the output\n\
    2796             : "), stdout);
    2797           0 :       fputs (_("\
    2798             :   -D, --date-format=FORMAT\n\
    2799             :                     use FORMAT for the header date\n\
    2800             :   -e[CHAR[WIDTH]], --expand-tabs[=CHAR[WIDTH]]\n\
    2801             :                     expand input CHARs (TABs) to tab WIDTH (8)\n\
    2802             :   -F, -f, --form-feed\n\
    2803             :                     use form feeds instead of newlines to separate pages\n\
    2804             :                     (by a 3-line page header with -F or a 5-line header\n\
    2805             :                     and trailer without -F)\n\
    2806             : "), stdout);
    2807           0 :       fputs (_("\
    2808             :   -h HEADER, --header=HEADER\n\
    2809             :                     use a centered HEADER instead of filename in page header,\n\
    2810             :                     -h \"\" prints a blank line, don't use -h\"\"\n\
    2811             :   -i[CHAR[WIDTH]], --output-tabs[=CHAR[WIDTH]]\n\
    2812             :                     replace spaces with CHARs (TABs) to tab WIDTH (8)\n\
    2813             :   -J, --join-lines  merge full lines, turns off -W line truncation, no column\n\
    2814             :                     alignment, --sep-string[=STRING] sets separators\n\
    2815             : "), stdout);
    2816           0 :       fputs (_("\
    2817             :   -l PAGE_LENGTH, --length=PAGE_LENGTH\n\
    2818             :                     set the page length to PAGE_LENGTH (66) lines\n\
    2819             :                     (default number of lines of text 56, and with -F 63)\n\
    2820             :   -m, --merge       print all files in parallel, one in each column,\n\
    2821             :                     truncate lines, but join lines of full length with -J\n\
    2822             : "), stdout);
    2823           0 :       fputs (_("\
    2824             :   -n[SEP[DIGITS]], --number-lines[=SEP[DIGITS]]\n\
    2825             :                     number lines, use DIGITS (5) digits, then SEP (TAB),\n\
    2826             :                     default counting starts with 1st line of input file\n\
    2827             :   -N NUMBER, --first-line-number=NUMBER\n\
    2828             :                     start counting with NUMBER at 1st line of first\n\
    2829             :                     page printed (see +FIRST_PAGE)\n\
    2830             : "), stdout);
    2831           0 :       fputs (_("\
    2832             :   -o MARGIN, --indent=MARGIN\n\
    2833             :                     offset each line with MARGIN (zero) spaces, do not\n\
    2834             :                     affect -w or -W, MARGIN will be added to PAGE_WIDTH\n\
    2835             :   -r, --no-file-warnings\n\
    2836             :                     omit warning when a file cannot be opened\n\
    2837             : "), stdout);
    2838           0 :       fputs (_("\
    2839             :   -s[CHAR],--separator[=CHAR]\n\
    2840             :                     separate columns by a single character, default for CHAR\n\
    2841             :                     is the <TAB> character without -w and \'no char\' with -w\n\
    2842             :                     -s[CHAR] turns off line truncation of all 3 column\n\
    2843             :                     options (-COLUMN|-a -COLUMN|-m) except -w is set\n\
    2844             : "), stdout);
    2845           0 :       fputs (_("\
    2846             :   -SSTRING, --sep-string[=STRING]\n\
    2847             : "), stdout);
    2848           0 :       fputs (_("\
    2849             :                     separate columns by STRING,\n\
    2850             :                     without -S: Default separator <TAB> with -J and <space>\n\
    2851             :                     otherwise (same as -S\" \"), no effect on column options\n\
    2852             :   -t, --omit-header  omit page headers and trailers\n\
    2853             : "), stdout);
    2854           0 :       fputs (_("\
    2855             :   -T, --omit-pagination\n\
    2856             :                     omit page headers and trailers, eliminate any pagination\n\
    2857             :                     by form feeds set in input files\n\
    2858             :   -v, --show-nonprinting\n\
    2859             :                     use octal backslash notation\n\
    2860             :   -w PAGE_WIDTH, --width=PAGE_WIDTH\n\
    2861             :                     set page width to PAGE_WIDTH (72) characters for\n\
    2862             :                     multiple text-column output only, -s[char] turns off (72)\n\
    2863             : "), stdout);
    2864           0 :       fputs (_("\
    2865             :   -W PAGE_WIDTH, --page-width=PAGE_WIDTH\n\
    2866             :                     set page width to PAGE_WIDTH (72) characters always,\n\
    2867             :                     truncate lines, except -J option is set, no interference\n\
    2868             :                     with -S or -s\n\
    2869             : "), stdout);
    2870           0 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
    2871           0 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
    2872           0 :       fputs (_("\
    2873             : \n\
    2874             : -t is implied if PAGE_LENGTH <= 10.  With no FILE, or when\n\
    2875             : FILE is -, read standard input.\n\
    2876             : "), stdout);
    2877           0 :       emit_bug_reporting_address ();
    2878             :     }
    2879           7 :   exit (status);
    2880             : }

Generated by: LCOV version 1.10