.
[coreutils.git] / src / paste.c
blobe798beb9a495ca3274a4e3bc57e70e32bcfe478e
1 /* paste - merge lines of files
2 Copyright (C) 1997-2004 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 2, or (at your option)
8 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, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 /* Written by David Ihnat. */
21 /* The list of valid escape sequences has been expanded over the Unix
22 version, to include \b, \f, \r, and \v.
24 POSIX changes, bug fixes, long-named options, and cleanup
25 by David MacKenzie <djm@gnu.ai.mit.edu>.
27 Options:
28 --serial
29 -s Paste one file at a time rather than
30 one line from each file.
31 --delimiters=delim-list
32 -d delim-list Consecutively use the characters in
33 DELIM-LIST instead of tab to separate
34 merged lines. When DELIM-LIST is exhausted,
35 start again at its beginning.
36 A FILE of `-' means standard input.
37 If no FILEs are given, standard input is used. */
39 #include <config.h>
41 #include <stdio.h>
42 #include <getopt.h>
43 #include <sys/types.h>
44 #include "system.h"
45 #include "error.h"
47 /* The official name of this program (e.g., no `g' prefix). */
48 #define PROGRAM_NAME "paste"
50 #define AUTHORS "David M. Ihnat", "David MacKenzie"
52 /* Indicates that no delimiter should be added in the current position. */
53 #define EMPTY_DELIM '\0'
55 static FILE dummy_closed;
56 /* Element marking a file that has reached EOF and been closed. */
57 #define CLOSED (&dummy_closed)
59 static FILE dummy_endlist;
60 /* Element marking end of list of open files. */
61 #define ENDLIST (&dummy_endlist)
63 /* Name this program was run with. */
64 char *program_name;
66 /* If nonzero, we have read standard input at some point. */
67 static bool have_read_stdin;
69 /* If nonzero, merge subsequent lines of each file rather than
70 corresponding lines from each file in parallel. */
71 static bool serial_merge;
73 /* The delimeters between lines of input files (used cyclically). */
74 static char *delims;
76 /* A pointer to the character after the end of `delims'. */
77 static char const *delim_end;
79 static struct option const longopts[] =
81 {"serial", no_argument, 0, 's'},
82 {"delimiters", required_argument, 0, 'd'},
83 {GETOPT_HELP_OPTION_DECL},
84 {GETOPT_VERSION_OPTION_DECL},
85 {0, 0, 0, 0}
88 /* Set globals delims and delim_end. Copy STRPTR to DELIMS, converting
89 backslash representations of special characters in STRPTR to their actual
90 values. The set of possible backslash characters has been expanded beyond
91 that recognized by the Unix version. */
93 static void
94 collapse_escapes (char const *strptr)
96 char *strout = xstrdup (strptr);
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 default:
136 *strout++ = *strptr;
137 break;
139 strptr++;
142 delim_end = strout;
145 /* Perform column paste on the NFILES files named in FNAMPTR.
146 Return 0 if no errors, 1 if one or more files could not be
147 opened or read. */
149 static int
150 paste_parallel (size_t nfiles, char **fnamptr)
152 int errors = 0; /* 1 if open or read errors occur. */
153 /* If all files are just ready to be closed, or will be on this
154 round, the string of delimiters must be preserved.
155 delbuf[0] through delbuf[nfiles]
156 store the delimiters for closed files. */
157 char *delbuf;
158 FILE **fileptr; /* Streams open to the files to process. */
159 size_t files_open; /* Number of files still open to process. */
160 bool opened_stdin = false; /* true if any fopen got fd == STDIN_FILENO */
162 delbuf = xmalloc (nfiles + 2);
163 fileptr = xnmalloc (nfiles + 1, sizeof *fileptr);
165 /* Attempt to open all files. This could be expanded to an infinite
166 number of files, but at the (considerable) expense of remembering
167 each file and its current offset, then opening/reading/closing. */
169 for (files_open = 0; files_open < nfiles; ++files_open)
171 if (STREQ (fnamptr[files_open], "-"))
173 have_read_stdin = true;
174 fileptr[files_open] = stdin;
176 else
178 fileptr[files_open] = fopen (fnamptr[files_open], "r");
179 if (fileptr[files_open] == NULL)
180 error (EXIT_FAILURE, errno, "%s", fnamptr[files_open]);
181 else if (fileno (fileptr[files_open]) == STDIN_FILENO)
182 opened_stdin = true;
186 fileptr[files_open] = ENDLIST;
188 if (opened_stdin && have_read_stdin)
189 error (EXIT_FAILURE, 0, _("standard input is closed"));
191 /* Read a line from each file and output it to stdout separated by a
192 delimiter, until we go through the loop without successfully
193 reading from any of the files. */
195 while (files_open)
197 /* Set up for the next line. */
198 bool somedone = false;
199 char const *delimptr = delims;
200 size_t delims_saved = 0; /* Number of delims saved in `delbuf'. */
201 size_t i;
203 for (i = 0; fileptr[i] != ENDLIST && files_open; i++)
205 int chr IF_LINT (= 0); /* Input character. */
206 size_t line_length = 0; /* Number of chars in line. */
207 if (fileptr[i] != CLOSED)
209 chr = getc (fileptr[i]);
210 if (chr != EOF && delims_saved)
212 fwrite (delbuf, sizeof (char), delims_saved, stdout);
213 delims_saved = 0;
216 while (chr != EOF)
218 line_length++;
219 if (chr == '\n')
220 break;
221 putc (chr, stdout);
222 chr = getc (fileptr[i]);
226 if (line_length == 0)
228 /* EOF, read error, or closed file.
229 If an EOF or error, close the file and mark it in the list. */
230 if (fileptr[i] != CLOSED)
232 if (ferror (fileptr[i]))
234 error (0, errno, "%s", fnamptr[i]);
235 errors = 1;
237 if (fileptr[i] == stdin)
238 clearerr (fileptr[i]); /* Also clear EOF. */
239 else if (fclose (fileptr[i]) == EOF)
241 error (0, errno, "%s", fnamptr[i]);
242 errors = 1;
245 fileptr[i] = CLOSED;
246 files_open--;
249 if (fileptr[i + 1] == ENDLIST)
251 /* End of this output line.
252 Is this the end of the whole thing? */
253 if (somedone)
255 /* No. Some files were not closed for this line. */
256 if (delims_saved)
258 fwrite (delbuf, sizeof (char), delims_saved, stdout);
259 delims_saved = 0;
261 putc ('\n', stdout);
263 continue; /* Next read of files, or exit. */
265 else
267 /* Closed file; add delimiter to `delbuf'. */
268 if (*delimptr != EMPTY_DELIM)
269 delbuf[delims_saved++] = *delimptr;
270 if (++delimptr == delim_end)
271 delimptr = delims;
274 else
276 /* Some data read. */
277 somedone = true;
279 /* Except for last file, replace last newline with delim. */
280 if (fileptr[i + 1] != ENDLIST)
282 if (chr != '\n' && chr != EOF)
283 putc (chr, stdout);
284 if (*delimptr != EMPTY_DELIM)
285 putc (*delimptr, stdout);
286 if (++delimptr == delim_end)
287 delimptr = delims;
289 else
291 /* If the last line of the last file lacks a newline,
292 print one anyhow. POSIX requires this. */
293 char c = (chr == EOF ? '\n' : chr);
294 putc (c, stdout);
299 free (fileptr);
300 free (delbuf);
301 return errors;
304 /* Perform serial paste on the NFILES files named in FNAMPTR.
305 Return 0 if no errors, 1 if one or more files could not be
306 opened or read. */
308 static int
309 paste_serial (size_t nfiles, char **fnamptr)
311 int errors = 0; /* 1 if open or read errors occur. */
312 int charnew, charold; /* Current and previous char read. */
313 char const *delimptr; /* Current delimiter char. */
314 FILE *fileptr; /* Open for reading current file. */
316 for (; nfiles; nfiles--, fnamptr++)
318 int saved_errno;
319 if (STREQ (*fnamptr, "-"))
321 have_read_stdin = true;
322 fileptr = stdin;
324 else
326 fileptr = fopen (*fnamptr, "r");
327 if (fileptr == NULL)
329 error (0, errno, "%s", *fnamptr);
330 errors = 1;
331 continue;
335 delimptr = delims; /* Set up for delimiter string. */
337 charold = getc (fileptr);
338 saved_errno = errno;
339 if (charold != EOF)
341 /* `charold' is set up. Hit it!
342 Keep reading characters, stashing them in `charnew';
343 output `charold', converting to the appropriate delimiter
344 character if needed. After the EOF, output `charold'
345 if it's a newline; otherwise, output it and then a newline. */
347 while ((charnew = getc (fileptr)) != EOF)
349 /* Process the old character. */
350 if (charold == '\n')
352 if (*delimptr != EMPTY_DELIM)
353 putc (*delimptr, stdout);
355 if (++delimptr == delim_end)
356 delimptr = delims;
358 else
359 putc (charold, stdout);
361 charold = charnew;
363 saved_errno = errno;
365 /* Hit EOF. Process that last character. */
366 putc (charold, stdout);
369 if (charold != '\n')
370 putc ('\n', stdout);
372 if (ferror (fileptr))
374 error (0, saved_errno, "%s", *fnamptr);
375 errors = 1;
377 if (fileptr == stdin)
378 clearerr (fileptr); /* Also clear EOF. */
379 else if (fclose (fileptr) == EOF)
381 error (0, errno, "%s", *fnamptr);
382 errors = 1;
385 return errors;
388 void
389 usage (int status)
391 if (status != EXIT_SUCCESS)
392 fprintf (stderr, _("Try `%s --help' for more information.\n"),
393 program_name);
394 else
396 printf (_("\
397 Usage: %s [OPTION]... [FILE]...\n\
399 program_name);
400 fputs (_("\
401 Write lines consisting of the sequentially corresponding lines from\n\
402 each FILE, separated by TABs, to standard output.\n\
403 With no FILE, or when FILE is -, read standard input.\n\
405 "), stdout);
406 fputs (_("\
407 Mandatory arguments to long options are mandatory for short options too.\n\
408 "), stdout);
409 fputs (_("\
410 -d, --delimiters=LIST reuse characters from LIST instead of TABs\n\
411 -s, --serial paste one file at a time instead of in parallel\n\
412 "), stdout);
413 fputs (HELP_OPTION_DESCRIPTION, stdout);
414 fputs (VERSION_OPTION_DESCRIPTION, stdout);
415 /* FIXME: add a couple of examples. */
416 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
418 exit (status);
422 main (int argc, char **argv)
424 int optc, exit_status;
425 char const *delim_arg = "\t";
427 initialize_main (&argc, &argv);
428 program_name = argv[0];
429 setlocale (LC_ALL, "");
430 bindtextdomain (PACKAGE, LOCALEDIR);
431 textdomain (PACKAGE);
433 atexit (close_stdout);
435 have_read_stdin = false;
436 serial_merge = false;
438 while ((optc = getopt_long (argc, argv, "d:s", longopts, NULL)) != -1)
440 switch (optc)
442 case 0:
443 break;
445 case 'd':
446 /* Delimiter character(s). */
447 if (optarg[0] == '\0')
448 optarg = "\\0";
449 delim_arg = optarg;
450 break;
452 case 's':
453 serial_merge = true;
454 break;
456 case_GETOPT_HELP_CHAR;
458 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
460 default:
461 usage (EXIT_FAILURE);
465 if (optind == argc)
466 argv[argc++] = "-";
468 collapse_escapes (delim_arg);
470 if (!serial_merge)
471 exit_status = paste_parallel (argc - optind, &argv[optind]);
472 else
473 exit_status = paste_serial (argc - optind, &argv[optind]);
475 free (delims);
477 if (have_read_stdin && fclose (stdin) == EOF)
478 error (EXIT_FAILURE, errno, "-");
479 exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);