/* paste - merge lines of files This is the paste utility
Copyright (C) 1997-2018 Free Software Foundation, Inc.
Copyright (C) 1984 David M. Ihnat
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ The GNUv3 license
/* Written by David Ihnat. */
/* The list of valid escape sequences has been expanded over the Unix
version, to include \b, \f, \r, and \v.
POSIX changes, bug fixes, long-named options, and cleanup
by David MacKenzie <djm@gnu.ai.mit.edu>.
Options:
--serial
-s Paste one file at a time rather than
one line from each file.
--delimiters=delim-list
-d delim-list Consecutively use the characters in
DELIM-LIST instead of tab to separate
merged lines. When DELIM-LIST is exhausted,
start again at its beginning.
A FILE of '-' means standard input.
If no FILEs are given, standard input is used. */
#include <config.h> Provides system specific information
#include <stdio.h> Provides standard I/O capability
#include <getopt.h> ...!includes auto-comment...
#include <sys/types.h> Provides system data types
#include "system.h" ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "fadvise.h" ...!includes auto-comment...
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "paste" Line 49
#define AUTHORS \ Line 51
proper_name ("David M. Ihnat"), \ Line 52
proper_name ("David MacKenzie") Line 53
/* Indicates that no delimiter should be added in the current position. */
#define EMPTY_DELIM '\0' Line 56
/* If nonzero, we have read standard input at some point. */
static bool have_read_stdin; Line 59
/* If nonzero, merge subsequent lines of each file rather than
corresponding lines from each file in parallel. */
static bool serial_merge; Line 63
/* The delimiters between lines of input files (used cyclically). */
static char *delims; Line 66
/* A pointer to the character after the end of 'delims'. */
static char const *delim_end; Line 69
static unsigned char line_delim = '\n'; Line 71
static struct option const longopts[] = Line 73
{
{"serial", no_argument, NULL, 's'}, Line 75
{"delimiters", required_argument, NULL, 'd'}, Line 76
{"zero-terminated", no_argument, NULL, 'z'}, Line 77
{GETOPT_HELP_OPTION_DECL}, Line 78
{GETOPT_VERSION_OPTION_DECL}, Line 79
{NULL, 0, NULL, 0} Line 80
}; Block 1
/* Set globals delims and delim_end. Copy STRPTR to DELIMS, converting
backslash representations of special characters in STRPTR to their actual
values. The set of possible backslash characters has been expanded beyond
that recognized by the Unix version.
Return 0 upon success.
If the string ends in an odd number of backslashes, ignore the
final backslash and return nonzero. */
static int Line 91
collapse_escapes (char const *strptr) Line 92
{
char *strout = xstrdup (strptr); Line 94
bool backslash_at_end = false; Line 95
delims = strout; Line 97
while (*strptr) Line 99
{
if (*strptr != '\\') /* Is it an escape character? */ Line 101
*strout++ = *strptr++; /* No, just transfer it. */ Line 102
else Line 103
{
switch (*++strptr) Line 105
{
case '0': Line 107
*strout++ = EMPTY_DELIM; Line 108
break; Line 109
case 'b': Line 111
*strout++ = '\b'; Line 112
break; Line 113
case 'f': Line 115
*strout++ = '\f'; Line 116
break; Line 117
case 'n': Line 119
*strout++ = '\n'; Line 120
break; Line 121
case 'r': Line 123
*strout++ = '\r'; Line 124
break; Line 125
case 't': Line 127
*strout++ = '\t'; Line 128
break; Line 129
case 'v': Line 131
*strout++ = '\v'; Line 132
break; Line 133
case '\\': Line 135
*strout++ = '\\'; Line 136
break; Line 137
case '\0': Line 139
backslash_at_end = true; Line 140
goto done; Line 141
default: Line 143
*strout++ = *strptr; Line 144
break; Line 145
}
strptr++; Line 147
}
}
done: Line 151
delim_end = strout; Line 153
return backslash_at_end ? 1 : 0; Line 154
} Block 2
/* Report a write error and exit. */
static void write_error (void) ATTRIBUTE_NORETURN; Line 159
static void Line 160
write_error (void) Line 161
{
die (EXIT_FAILURE, errno, _("write error")); Line 163
} Block 3
/* Output a single byte, reporting any write errors. */
static inline void Line 168
xputchar (char c) Line 169
{
if (putchar (c) < 0) Line 171
write_error (); Line 172
} Block 4
/* Perform column paste on the NFILES files named in FNAMPTR.
Return true if successful, false if one or more files could not be
opened or read. */
static bool Line 179
paste_parallel (size_t nfiles, char **fnamptr) Line 180
{
bool ok = true; Line 182
/* If all files are just ready to be closed, or will be on this
round, the string of delimiters must be preserved.
delbuf[0] through delbuf[nfiles]
store the delimiters for closed files. */
char *delbuf = xmalloc (nfiles + 2); Line 187
/* Streams open to the files to process; NULL if the corresponding
stream is closed. */
FILE **fileptr = xnmalloc (nfiles + 1, sizeof *fileptr); Line 191
/* Number of files still open to process. */
size_t files_open; Line 194
/* True if any fopen got fd == STDIN_FILENO. */
bool opened_stdin = false; Line 197
/* Attempt to open all files. This could be expanded to an infinite
number of files, but at the (considerable) expense of remembering
each file and its current offset, then opening/reading/closing. */
for (files_open = 0; files_open < nfiles; ++files_open) Line 203
{
if (STREQ (fnamptr[files_open], "-")) Line 205
{
have_read_stdin = true; Line 207
fileptr[files_open] = stdin; Line 208
}
else Line 210
{
fileptr[files_open] = fopen (fnamptr[files_open], "r"); Line 212...!syscalls auto-comment...
if (fileptr[files_open] == NULL) Line 213
die (EXIT_FAILURE, errno, "%s", quotef (fnamptr[files_open])); Line 214
else if (fileno (fileptr[files_open]) == STDIN_FILENO) Line 215
opened_stdin = true; Line 216
fadvise (fileptr[files_open], FADVISE_SEQUENTIAL); Line 217...!syscalls auto-comment...
}
}
if (opened_stdin && have_read_stdin) Line 221
die (EXIT_FAILURE, 0, _("standard input is closed")); Line 222
/* Read a line from each file and output it to stdout separated by a
delimiter, until we go through the loop without successfully
reading from any of the files. */
while (files_open) Line 228
{
/* Set up for the next line. */
bool somedone = false; Line 231
char const *delimptr = delims; Line 232
size_t delims_saved = 0; /* Number of delims saved in 'delbuf'. */ Line 233
for (size_t i = 0; i < nfiles && files_open; i++) Line 235
{
int chr IF_LINT ( = 0); /* Input character. */ Line 237
int err IF_LINT ( = 0); /* Input errno value. */ Line 238
bool sometodo = false; /* Input chars to process. */ Line 239
if (fileptr[i]) Line 241
{
chr = getc (fileptr[i]); Line 243
err = errno; Line 244
if (chr != EOF && delims_saved) Line 245
{
if (fwrite (delbuf, 1, delims_saved, stdout) != delims_saved) Line 247...!syscalls auto-comment...
write_error (); Line 248
delims_saved = 0; Line 249
}
while (chr != EOF) Line 252
{
sometodo = true; Line 254
if (chr == line_delim) Line 255
break; Line 256
xputchar (chr); Line 257
chr = getc (fileptr[i]); Line 258
err = errno; Line 259
}
}
if (! sometodo) Line 263
{
/* EOF, read error, or closed file.
If an EOF or error, close the file. */
if (fileptr[i]) Line 267
{
if (ferror (fileptr[i])) Line 269
{
error (0, err, "%s", quotef (fnamptr[i])); Line 271
ok = false; Line 272
}
if (fileptr[i] == stdin) Line 274
clearerr (fileptr[i]); /* Also clear EOF. */ Line 275
else if (fclose (fileptr[i]) == EOF) Line 276...!syscalls auto-comment...
{
error (0, errno, "%s", quotef (fnamptr[i])); Line 278
ok = false; Line 279
}
fileptr[i] = NULL; Line 282
files_open--; Line 283
}
if (i + 1 == nfiles) Line 286
{
/* End of this output line.
Is this the end of the whole thing? */
if (somedone) Line 290
{
/* No. Some files were not closed for this line. */
if (delims_saved) Line 293
{
if (fwrite (delbuf, 1, delims_saved, stdout) Line 295...!syscalls auto-comment...
!= delims_saved) Line 296
write_error (); Line 297
delims_saved = 0; Line 298
}
xputchar (line_delim); Line 300
}
continue; /* Next read of files, or exit. */ Line 302
}
else Line 304
{
/* Closed file; add delimiter to 'delbuf'. */
if (*delimptr != EMPTY_DELIM) Line 307
delbuf[delims_saved++] = *delimptr; Line 308
if (++delimptr == delim_end) Line 309
delimptr = delims; Line 310
}
}
else Line 313
{
/* Some data read. */
somedone = true; Line 316
/* Except for last file, replace last newline with delim. */
if (i + 1 != nfiles) Line 319
{
if (chr != line_delim && chr != EOF) Line 321
xputchar (chr); Line 322
if (*delimptr != EMPTY_DELIM) Line 323
xputchar (*delimptr); Line 324
if (++delimptr == delim_end) Line 325
delimptr = delims; Line 326
}
else Line 328
{
/* If the last line of the last file lacks a newline,
print one anyhow. POSIX requires this. */
char c = (chr == EOF ? line_delim : chr); Line 332
xputchar (c); Line 333
}
}
}
}
free (fileptr); Line 338
free (delbuf); Line 339
return ok; Line 340
} Block 5
/* Perform serial paste on the NFILES files named in FNAMPTR.
Return true if no errors, false if one or more files could not be
opened or read. */
static bool Line 347
paste_serial (size_t nfiles, char **fnamptr) Line 348
{
bool ok = true; /* false if open or read errors occur. */ Line 350
int charnew, charold; /* Current and previous char read. */ Line 351
char const *delimptr; /* Current delimiter char. */ Line 352
FILE *fileptr; /* Open for reading current file. */ Line 353
for (; nfiles; nfiles--, fnamptr++) Line 355
{
int saved_errno; Line 357
bool is_stdin = STREQ (*fnamptr, "-"); Line 358
if (is_stdin) Line 359
{
have_read_stdin = true; Line 361
fileptr = stdin; Line 362
}
else Line 364
{
fileptr = fopen (*fnamptr, "r"); Line 366...!syscalls auto-comment...
if (fileptr == NULL) Line 367
{
error (0, errno, "%s", quotef (*fnamptr)); Line 369
ok = false; Line 370
continue; Line 371
}
fadvise (fileptr, FADVISE_SEQUENTIAL); Line 373...!syscalls auto-comment...
}
delimptr = delims; /* Set up for delimiter string. */ Line 376
charold = getc (fileptr); Line 378
saved_errno = errno; Line 379
if (charold != EOF) Line 380
{
/* 'charold' is set up. Hit it!
Keep reading characters, stashing them in 'charnew';
output 'charold', converting to the appropriate delimiter
character if needed. After the EOF, output 'charold'
if it's a newline; otherwise, output it and then a newline. */
while ((charnew = getc (fileptr)) != EOF) Line 388
{
/* Process the old character. */
if (charold == line_delim) Line 391
{
if (*delimptr != EMPTY_DELIM) Line 393
xputchar (*delimptr); Line 394
if (++delimptr == delim_end) Line 396
delimptr = delims; Line 397
}
else Line 399
xputchar (charold); Line 400
charold = charnew; Line 402
}
saved_errno = errno; Line 404
/* Hit EOF. Process that last character. */
xputchar (charold); Line 407
}
if (charold != line_delim) Line 410
xputchar (line_delim); Line 411
if (ferror (fileptr)) Line 413
{
error (0, saved_errno, "%s", quotef (*fnamptr)); Line 415
ok = false; Line 416
}
if (is_stdin) Line 418
clearerr (fileptr); /* Also clear EOF. */ Line 419
else if (fclose (fileptr) == EOF) Line 420...!syscalls auto-comment...
{
error (0, errno, "%s", quotef (*fnamptr)); Line 422
ok = false; Line 423
}
}
return ok; Line 426
} Block 6
void Line 429
usage (int status) Line 430
{
if (status != EXIT_SUCCESS) Line 432
emit_try_help (); ...!common auto-comment...
else Line 434
{
printf (_("\ Line 436
Usage: %s [OPTION]... [FILE]...\n\ Line 437
"), Line 438
program_name); Line 439
fputs (_("\ Line 440
Write lines consisting of the sequentially corresponding lines from\n\ Line 441
each FILE, separated by TABs, to standard output.\n\ Line 442
"), stdout); Line 443
emit_stdin_note (); ...!common auto-comment...
emit_mandatory_arg_note (); ...!common auto-comment...
fputs (_("\ Line 448
-d, --delimiters=LIST reuse characters from LIST instead of TABs\n\ Line 449
-s, --serial paste one file at a time instead of in parallel\n\ Line 450
"), stdout); Line 451
fputs (_("\ Line 452
-z, --zero-terminated line delimiter is NUL, not newline\n\ Line 453
"), stdout); Line 454
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 455
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 456
/* FIXME: add a couple of examples. */
emit_ancillary_info (PROGRAM_NAME); Line 458
}
exit (status); Line 460
} Block 7
int
main (int argc, char **argv) Line 464
{
int optc; Line 466
char const *delim_arg = "\t"; Line 467
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
atexit (close_stdout); Close stdout on exit (see gnulib)
have_read_stdin = false; Line 477
serial_merge = false; Line 478
while ((optc = getopt_long (argc, argv, "d:sz", longopts, NULL)) != -1) Line 480
{
switch (optc) Line 482
{
case 'd': Line 484
/* Delimiter character(s). */
delim_arg = (optarg[0] == '\0' ? "\\0" : optarg); Line 486
break; Line 487
case 's': Line 489
serial_merge = true; Line 490
break; Line 491
case 'z': Line 493
line_delim = '\0'; Line 494
break; Line 495
case_GETOPT_HELP_CHAR; Line 497
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 499
default: Line 501
usage (EXIT_FAILURE); Line 502
}
}
int nfiles = argc - optind; Line 506
if (nfiles == 0) Line 507
{
argv[optind] = bad_cast ("-"); Line 509
nfiles++; Line 510
}
if (collapse_escapes (delim_arg)) Line 513
{
/* Don't use the quote() quoting style, because that would double the
number of displayed backslashes, making the diagnostic look bogus. */
die (EXIT_FAILURE, 0, Line 517
_("delimiter list ends with an unescaped backslash: %s"), Line 518
quotearg_n_style_colon (0, c_maybe_quoting_style, delim_arg)); Line 519
}
bool ok = ((serial_merge ? paste_serial : paste_parallel) Line 522
(nfiles, &argv[optind])); Line 523
free (delims); Line 525
if (have_read_stdin && fclose (stdin) == EOF) Line 527...!syscalls auto-comment...
die (EXIT_FAILURE, errno, "-"); Line 528
return ok ? EXIT_SUCCESS : EXIT_FAILURE; Line 529
} Block 8