ptx: fix an invalid heap reference with short --width
[coreutils.git] / src / paste.c
blob0cf5d2fc54b124095f642af60766dc92180158b3
1 /* paste - merge lines of files
2 Copyright (C) 1997-2016 Free Software Foundation, Inc.
3 Copyright (C) 1984 David M. Ihnat
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 /* Written by David Ihnat. */
20 /* The list of valid escape sequences has been expanded over the Unix
21 version, to include \b, \f, \r, and \v.
23 POSIX changes, bug fixes, long-named options, and cleanup
24 by David MacKenzie <djm@gnu.ai.mit.edu>.
26 Options:
27 --serial
28 -s Paste one file at a time rather than
29 one line from each file.
30 --delimiters=delim-list
31 -d delim-list Consecutively use the characters in
32 DELIM-LIST instead of tab to separate
33 merged lines. When DELIM-LIST is exhausted,
34 start again at its beginning.
35 A FILE of '-' means standard input.
36 If no FILEs are given, standard input is used. */
38 #include <config.h>
40 #include <stdio.h>
41 #include <getopt.h>
42 #include <sys/types.h>
43 #include "system.h"
44 #include "die.h"
45 #include "error.h"
46 #include "fadvise.h"
48 /* The official name of this program (e.g., no 'g' prefix). */
49 #define PROGRAM_NAME "paste"
51 #define AUTHORS \
52 proper_name ("David M. Ihnat"), \
53 proper_name ("David MacKenzie")
55 /* Indicates that no delimiter should be added in the current position. */
56 #define EMPTY_DELIM '\0'
58 /* If nonzero, we have read standard input at some point. */
59 static bool have_read_stdin;
61 /* If nonzero, merge subsequent lines of each file rather than
62 corresponding lines from each file in parallel. */
63 static bool serial_merge;
65 /* The delimiters between lines of input files (used cyclically). */
66 static char *delims;
68 /* A pointer to the character after the end of 'delims'. */
69 static char const *delim_end;
71 static unsigned char line_delim = '\n';
73 static struct option const longopts[] =
75 {"serial", no_argument, NULL, 's'},
76 {"delimiters", required_argument, NULL, 'd'},
77 {"zero-terminated", no_argument, NULL, 'z'},
78 {GETOPT_HELP_OPTION_DECL},
79 {GETOPT_VERSION_OPTION_DECL},
80 {NULL, 0, NULL, 0}
83 /* Set globals delims and delim_end. Copy STRPTR to DELIMS, converting
84 backslash representations of special characters in STRPTR to their actual
85 values. The set of possible backslash characters has been expanded beyond
86 that recognized by the Unix version.
87 Return 0 upon success.
88 If the string ends in an odd number of backslashes, ignore the
89 final backslash and return nonzero. */
91 static int
92 collapse_escapes (char const *strptr)
94 char *strout = xstrdup (strptr);
95 bool backslash_at_end = false;
97 delims = strout;
99 while (*strptr)
101 if (*strptr != '\\') /* Is it an escape character? */
102 *strout++ = *strptr++; /* No, just transfer it. */
103 else
105 switch (*++strptr)
107 case '0':
108 *strout++ = EMPTY_DELIM;
109 break;
111 case 'b':
112 *strout++ = '\b';
113 break;
115 case 'f':
116 *strout++ = '\f';
117 break;
119 case 'n':
120 *strout++ = '\n';
121 break;
123 case 'r':
124 *strout++ = '\r';
125 break;
127 case 't':
128 *strout++ = '\t';
129 break;
131 case 'v':
132 *strout++ = '\v';
133 break;
135 case '\\':
136 *strout++ = '\\';
137 break;
139 case '\0':
140 backslash_at_end = true;
141 goto done;
143 default:
144 *strout++ = *strptr;
145 break;
147 strptr++;
151 done:
153 delim_end = strout;
154 return backslash_at_end ? 1 : 0;
157 /* Report a write error and exit. */
159 static void write_error (void) ATTRIBUTE_NORETURN;
160 static void
161 write_error (void)
163 die (EXIT_FAILURE, errno, _("write error"));
166 /* Output a single byte, reporting any write errors. */
168 static inline void
169 xputchar (char c)
171 if (putchar (c) < 0)
172 write_error ();
175 /* Perform column paste on the NFILES files named in FNAMPTR.
176 Return true if successful, false if one or more files could not be
177 opened or read. */
179 static bool
180 paste_parallel (size_t nfiles, char **fnamptr)
182 bool ok = true;
183 /* If all files are just ready to be closed, or will be on this
184 round, the string of delimiters must be preserved.
185 delbuf[0] through delbuf[nfiles]
186 store the delimiters for closed files. */
187 char *delbuf = xmalloc (nfiles + 2);
189 /* Streams open to the files to process; NULL if the corresponding
190 stream is closed. */
191 FILE **fileptr = xnmalloc (nfiles + 1, sizeof *fileptr);
193 /* Number of files still open to process. */
194 size_t files_open;
196 /* True if any fopen got fd == STDIN_FILENO. */
197 bool opened_stdin = false;
199 /* Attempt to open all files. This could be expanded to an infinite
200 number of files, but at the (considerable) expense of remembering
201 each file and its current offset, then opening/reading/closing. */
203 for (files_open = 0; files_open < nfiles; ++files_open)
205 if (STREQ (fnamptr[files_open], "-"))
207 have_read_stdin = true;
208 fileptr[files_open] = stdin;
210 else
212 fileptr[files_open] = fopen (fnamptr[files_open], "r");
213 if (fileptr[files_open] == NULL)
214 die (EXIT_FAILURE, errno, "%s", quotef (fnamptr[files_open]));
215 else if (fileno (fileptr[files_open]) == STDIN_FILENO)
216 opened_stdin = true;
217 fadvise (fileptr[files_open], FADVISE_SEQUENTIAL);
221 if (opened_stdin && have_read_stdin)
222 die (EXIT_FAILURE, 0, _("standard input is closed"));
224 /* Read a line from each file and output it to stdout separated by a
225 delimiter, until we go through the loop without successfully
226 reading from any of the files. */
228 while (files_open)
230 /* Set up for the next line. */
231 bool somedone = false;
232 char const *delimptr = delims;
233 size_t delims_saved = 0; /* Number of delims saved in 'delbuf'. */
234 size_t i;
236 for (i = 0; i < nfiles && files_open; i++)
238 int chr IF_LINT ( = 0); /* Input character. */
239 int err IF_LINT ( = 0); /* Input errno value. */
240 bool sometodo = false; /* Input chars to process. */
242 if (fileptr[i])
244 chr = getc (fileptr[i]);
245 err = errno;
246 if (chr != EOF && delims_saved)
248 if (fwrite (delbuf, 1, delims_saved, stdout) != delims_saved)
249 write_error ();
250 delims_saved = 0;
253 while (chr != EOF)
255 sometodo = true;
256 if (chr == line_delim)
257 break;
258 xputchar (chr);
259 chr = getc (fileptr[i]);
260 err = errno;
264 if (! sometodo)
266 /* EOF, read error, or closed file.
267 If an EOF or error, close the file. */
268 if (fileptr[i])
270 if (ferror (fileptr[i]))
272 error (0, err, "%s", quotef (fnamptr[i]));
273 ok = false;
275 if (fileptr[i] == stdin)
276 clearerr (fileptr[i]); /* Also clear EOF. */
277 else if (fclose (fileptr[i]) == EOF)
279 error (0, errno, "%s", quotef (fnamptr[i]));
280 ok = false;
283 fileptr[i] = NULL;
284 files_open--;
287 if (i + 1 == nfiles)
289 /* End of this output line.
290 Is this the end of the whole thing? */
291 if (somedone)
293 /* No. Some files were not closed for this line. */
294 if (delims_saved)
296 if (fwrite (delbuf, 1, delims_saved, stdout)
297 != delims_saved)
298 write_error ();
299 delims_saved = 0;
301 xputchar (line_delim);
303 continue; /* Next read of files, or exit. */
305 else
307 /* Closed file; add delimiter to 'delbuf'. */
308 if (*delimptr != EMPTY_DELIM)
309 delbuf[delims_saved++] = *delimptr;
310 if (++delimptr == delim_end)
311 delimptr = delims;
314 else
316 /* Some data read. */
317 somedone = true;
319 /* Except for last file, replace last newline with delim. */
320 if (i + 1 != nfiles)
322 if (chr != line_delim && chr != EOF)
323 xputchar (chr);
324 if (*delimptr != EMPTY_DELIM)
325 xputchar (*delimptr);
326 if (++delimptr == delim_end)
327 delimptr = delims;
329 else
331 /* If the last line of the last file lacks a newline,
332 print one anyhow. POSIX requires this. */
333 char c = (chr == EOF ? line_delim : chr);
334 xputchar (c);
339 free (fileptr);
340 free (delbuf);
341 return ok;
344 /* Perform serial paste on the NFILES files named in FNAMPTR.
345 Return true if no errors, false if one or more files could not be
346 opened or read. */
348 static bool
349 paste_serial (size_t nfiles, char **fnamptr)
351 bool ok = true; /* false if open or read errors occur. */
352 int charnew, charold; /* Current and previous char read. */
353 char const *delimptr; /* Current delimiter char. */
354 FILE *fileptr; /* Open for reading current file. */
356 for (; nfiles; nfiles--, fnamptr++)
358 int saved_errno;
359 bool is_stdin = STREQ (*fnamptr, "-");
360 if (is_stdin)
362 have_read_stdin = true;
363 fileptr = stdin;
365 else
367 fileptr = fopen (*fnamptr, "r");
368 if (fileptr == NULL)
370 error (0, errno, "%s", quotef (*fnamptr));
371 ok = false;
372 continue;
374 fadvise (fileptr, FADVISE_SEQUENTIAL);
377 delimptr = delims; /* Set up for delimiter string. */
379 charold = getc (fileptr);
380 saved_errno = errno;
381 if (charold != EOF)
383 /* 'charold' is set up. Hit it!
384 Keep reading characters, stashing them in 'charnew';
385 output 'charold', converting to the appropriate delimiter
386 character if needed. After the EOF, output 'charold'
387 if it's a newline; otherwise, output it and then a newline. */
389 while ((charnew = getc (fileptr)) != EOF)
391 /* Process the old character. */
392 if (charold == line_delim)
394 if (*delimptr != EMPTY_DELIM)
395 xputchar (*delimptr);
397 if (++delimptr == delim_end)
398 delimptr = delims;
400 else
401 xputchar (charold);
403 charold = charnew;
405 saved_errno = errno;
407 /* Hit EOF. Process that last character. */
408 xputchar (charold);
411 if (charold != line_delim)
412 xputchar (line_delim);
414 if (ferror (fileptr))
416 error (0, saved_errno, "%s", quotef (*fnamptr));
417 ok = false;
419 if (is_stdin)
420 clearerr (fileptr); /* Also clear EOF. */
421 else if (fclose (fileptr) == EOF)
423 error (0, errno, "%s", quotef (*fnamptr));
424 ok = false;
427 return ok;
430 void
431 usage (int status)
433 if (status != EXIT_SUCCESS)
434 emit_try_help ();
435 else
437 printf (_("\
438 Usage: %s [OPTION]... [FILE]...\n\
440 program_name);
441 fputs (_("\
442 Write lines consisting of the sequentially corresponding lines from\n\
443 each FILE, separated by TABs, to standard output.\n\
444 "), stdout);
446 emit_stdin_note ();
447 emit_mandatory_arg_note ();
449 fputs (_("\
450 -d, --delimiters=LIST reuse characters from LIST instead of TABs\n\
451 -s, --serial paste one file at a time instead of in parallel\n\
452 "), stdout);
453 fputs (_("\
454 -z, --zero-terminated line delimiter is NUL, not newline\n\
455 "), stdout);
456 fputs (HELP_OPTION_DESCRIPTION, stdout);
457 fputs (VERSION_OPTION_DESCRIPTION, stdout);
458 /* FIXME: add a couple of examples. */
459 emit_ancillary_info (PROGRAM_NAME);
461 exit (status);
465 main (int argc, char **argv)
467 int optc;
468 char const *delim_arg = "\t";
470 initialize_main (&argc, &argv);
471 set_program_name (argv[0]);
472 setlocale (LC_ALL, "");
473 bindtextdomain (PACKAGE, LOCALEDIR);
474 textdomain (PACKAGE);
476 atexit (close_stdout);
478 have_read_stdin = false;
479 serial_merge = false;
481 while ((optc = getopt_long (argc, argv, "d:sz", longopts, NULL)) != -1)
483 switch (optc)
485 case 'd':
486 /* Delimiter character(s). */
487 delim_arg = (optarg[0] == '\0' ? "\\0" : optarg);
488 break;
490 case 's':
491 serial_merge = true;
492 break;
494 case 'z':
495 line_delim = '\0';
496 break;
498 case_GETOPT_HELP_CHAR;
500 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
502 default:
503 usage (EXIT_FAILURE);
507 int nfiles = argc - optind;
508 if (nfiles == 0)
510 argv[optind] = bad_cast ("-");
511 nfiles++;
514 if (collapse_escapes (delim_arg))
516 /* Don't use the quote() quoting style, because that would double the
517 number of displayed backslashes, making the diagnostic look bogus. */
518 die (EXIT_FAILURE, 0,
519 _("delimiter list ends with an unescaped backslash: %s"),
520 quotearg_n_style_colon (0, c_maybe_quoting_style, delim_arg));
523 bool ok = ((serial_merge ? paste_serial : paste_parallel)
524 (nfiles, &argv[optind]));
526 free (delims);
528 if (have_read_stdin && fclose (stdin) == EOF)
529 die (EXIT_FAILURE, errno, "-");
530 return ok ? EXIT_SUCCESS : EXIT_FAILURE;