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