convert 2-author programs to use proper_name
[platform/upstream/coreutils.git] / src / paste.c
1 /* paste - merge lines of files
2    Copyright (C) 1997-2005, 2008 Free Software Foundation, Inc.
3    Copyright (C) 1984 David M. Ihnat
4
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.
9
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.
14
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/>.  */
17
18 /* Written by David Ihnat.  */
19
20 /* The list of valid escape sequences has been expanded over the Unix
21    version, to include \b, \f, \r, and \v.
22
23    POSIX changes, bug fixes, long-named options, and cleanup
24    by David MacKenzie <djm@gnu.ai.mit.edu>.
25
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. */
37
38 #include <config.h>
39
40 #include <stdio.h>
41 #include <getopt.h>
42 #include <sys/types.h>
43 #include "system.h"
44 #include "error.h"
45 #include "quotearg.h"
46
47 /* The official name of this program (e.g., no `g' prefix).  */
48 #define PROGRAM_NAME "paste"
49
50 #define AUTHORS \
51   proper_name ("David M. Ihnat"), \
52   proper_name ("David MacKenzie")
53
54 /* Indicates that no delimiter should be added in the current position. */
55 #define EMPTY_DELIM '\0'
56
57 /* Name this program was run with. */
58 char *program_name;
59
60 /* If nonzero, we have read standard input at some point. */
61 static bool have_read_stdin;
62
63 /* If nonzero, merge subsequent lines of each file rather than
64    corresponding lines from each file in parallel. */
65 static bool serial_merge;
66
67 /* The delimeters between lines of input files (used cyclically). */
68 static char *delims;
69
70 /* A pointer to the character after the end of `delims'. */
71 static char const *delim_end;
72
73 static struct option const longopts[] =
74 {
75   {"serial", no_argument, NULL, 's'},
76   {"delimiters", required_argument, NULL, 'd'},
77   {GETOPT_HELP_OPTION_DECL},
78   {GETOPT_VERSION_OPTION_DECL},
79   {NULL, 0, NULL, 0}
80 };
81
82 /* Set globals delims and delim_end.  Copy STRPTR to DELIMS, converting
83    backslash representations of special characters in STRPTR to their actual
84    values. The set of possible backslash characters has been expanded beyond
85    that recognized by the Unix version.
86    Return 0 upon success.
87    If the string ends in an odd number of backslashes, ignore the
88    final backslash and return nonzero.  */
89
90 static int
91 collapse_escapes (char const *strptr)
92 {
93   char *strout = xstrdup (strptr);
94   bool backslash_at_end = false;
95
96   delims = strout;
97
98   while (*strptr)
99     {
100       if (*strptr != '\\')      /* Is it an escape character? */
101         *strout++ = *strptr++;  /* No, just transfer it. */
102       else
103         {
104           switch (*++strptr)
105             {
106             case '0':
107               *strout++ = EMPTY_DELIM;
108               break;
109
110             case 'b':
111               *strout++ = '\b';
112               break;
113
114             case 'f':
115               *strout++ = '\f';
116               break;
117
118             case 'n':
119               *strout++ = '\n';
120               break;
121
122             case 'r':
123               *strout++ = '\r';
124               break;
125
126             case 't':
127               *strout++ = '\t';
128               break;
129
130             case 'v':
131               *strout++ = '\v';
132               break;
133
134             case '\\':
135               *strout++ = '\\';
136               break;
137
138             case '\0':
139               backslash_at_end = true;
140               goto done;
141
142             default:
143               *strout++ = *strptr;
144               break;
145             }
146           strptr++;
147         }
148     }
149
150  done:;
151
152   delim_end = strout;
153   return backslash_at_end ? 1 : 0;
154 }
155
156 /* Report a write error and exit.  */
157
158 static void write_error (void) ATTRIBUTE_NORETURN;
159 static void
160 write_error (void)
161 {
162   error (EXIT_FAILURE, errno, _("write error"));
163   abort ();
164 }
165
166 /* Output a single byte, reporting any write errors.  */
167
168 static inline void
169 xputchar (char c)
170 {
171   if (putchar (c) < 0)
172     write_error ();
173 }
174
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. */
178
179 static bool
180 paste_parallel (size_t nfiles, char **fnamptr)
181 {
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);
188
189   /* Streams open to the files to process; NULL if the corresponding
190      stream is closed.  */
191   FILE **fileptr = xnmalloc (nfiles + 1, sizeof *fileptr);
192
193   /* Number of files still open to process.  */
194   size_t files_open;
195
196   /* True if any fopen got fd == STDIN_FILENO.  */
197   bool opened_stdin = false;
198
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.  */
202
203   for (files_open = 0; files_open < nfiles; ++files_open)
204     {
205       if (STREQ (fnamptr[files_open], "-"))
206         {
207           have_read_stdin = true;
208           fileptr[files_open] = stdin;
209         }
210       else
211         {
212           fileptr[files_open] = fopen (fnamptr[files_open], "r");
213           if (fileptr[files_open] == NULL)
214             error (EXIT_FAILURE, errno, "%s", fnamptr[files_open]);
215           else if (fileno (fileptr[files_open]) == STDIN_FILENO)
216             opened_stdin = true;
217         }
218     }
219
220   if (opened_stdin && have_read_stdin)
221     error (EXIT_FAILURE, 0, _("standard input is closed"));
222
223   /* Read a line from each file and output it to stdout separated by a
224      delimiter, until we go through the loop without successfully
225      reading from any of the files. */
226
227   while (files_open)
228     {
229       /* Set up for the next line. */
230       bool somedone = false;
231       char const *delimptr = delims;
232       size_t delims_saved = 0;  /* Number of delims saved in `delbuf'. */
233       size_t i;
234
235       for (i = 0; i < nfiles && files_open; i++)
236         {
237           int chr IF_LINT (= 0);        /* Input character. */
238           int err IF_LINT (= 0);        /* Input errno value.  */
239           size_t line_length = 0;       /* Number of chars in line. */
240
241           if (fileptr[i])
242             {
243               chr = getc (fileptr[i]);
244               err = errno;
245               if (chr != EOF && delims_saved)
246                 {
247                   if (fwrite (delbuf, 1, delims_saved, stdout) != delims_saved)
248                     write_error ();
249                   delims_saved = 0;
250                 }
251
252               while (chr != EOF)
253                 {
254                   line_length++;
255                   if (chr == '\n')
256                     break;
257                   xputchar (chr);
258                   chr = getc (fileptr[i]);
259                   err = errno;
260                 }
261             }
262
263           if (line_length == 0)
264             {
265               /* EOF, read error, or closed file.
266                  If an EOF or error, close the file.  */
267               if (fileptr[i])
268                 {
269                   if (ferror (fileptr[i]))
270                     {
271                       error (0, err, "%s", fnamptr[i]);
272                       ok = false;
273                     }
274                   if (fileptr[i] == stdin)
275                     clearerr (fileptr[i]); /* Also clear EOF. */
276                   else if (fclose (fileptr[i]) == EOF)
277                     {
278                       error (0, errno, "%s", fnamptr[i]);
279                       ok = false;
280                     }
281
282                   fileptr[i] = NULL;
283                   files_open--;
284                 }
285
286               if (i + 1 == nfiles)
287                 {
288                   /* End of this output line.
289                      Is this the end of the whole thing? */
290                   if (somedone)
291                     {
292                       /* No.  Some files were not closed for this line. */
293                       if (delims_saved)
294                         {
295                           if (fwrite (delbuf, 1, delims_saved, stdout)
296                               != delims_saved)
297                             write_error ();
298                           delims_saved = 0;
299                         }
300                       xputchar ('\n');
301                     }
302                   continue;     /* Next read of files, or exit. */
303                 }
304               else
305                 {
306                   /* Closed file; add delimiter to `delbuf'. */
307                   if (*delimptr != EMPTY_DELIM)
308                     delbuf[delims_saved++] = *delimptr;
309                   if (++delimptr == delim_end)
310                     delimptr = delims;
311                 }
312             }
313           else
314             {
315               /* Some data read. */
316               somedone = true;
317
318               /* Except for last file, replace last newline with delim. */
319               if (i + 1 != nfiles)
320                 {
321                   if (chr != '\n' && chr != EOF)
322                     xputchar (chr);
323                   if (*delimptr != EMPTY_DELIM)
324                     xputchar (*delimptr);
325                   if (++delimptr == delim_end)
326                     delimptr = delims;
327                 }
328               else
329                 {
330                   /* If the last line of the last file lacks a newline,
331                      print one anyhow.  POSIX requires this.  */
332                   char c = (chr == EOF ? '\n' : chr);
333                   xputchar (c);
334                 }
335             }
336         }
337     }
338   free (fileptr);
339   free (delbuf);
340   return ok;
341 }
342
343 /* Perform serial paste on the NFILES files named in FNAMPTR.
344    Return true if no errors, false if one or more files could not be
345    opened or read. */
346
347 static bool
348 paste_serial (size_t nfiles, char **fnamptr)
349 {
350   bool ok = true;       /* false if open or read errors occur. */
351   int charnew, charold; /* Current and previous char read. */
352   char const *delimptr; /* Current delimiter char. */
353   FILE *fileptr;        /* Open for reading current file. */
354
355   for (; nfiles; nfiles--, fnamptr++)
356     {
357       int saved_errno;
358       bool is_stdin = STREQ (*fnamptr, "-");
359       if (is_stdin)
360         {
361           have_read_stdin = true;
362           fileptr = stdin;
363         }
364       else
365         {
366           fileptr = fopen (*fnamptr, "r");
367           if (fileptr == NULL)
368             {
369               error (0, errno, "%s", *fnamptr);
370               ok = false;
371               continue;
372             }
373         }
374
375       delimptr = delims;        /* Set up for delimiter string. */
376
377       charold = getc (fileptr);
378       saved_errno = errno;
379       if (charold != EOF)
380         {
381           /* `charold' is set up.  Hit it!
382              Keep reading characters, stashing them in `charnew';
383              output `charold', converting to the appropriate delimiter
384              character if needed.  After the EOF, output `charold'
385              if it's a newline; otherwise, output it and then a newline. */
386
387           while ((charnew = getc (fileptr)) != EOF)
388             {
389               /* Process the old character. */
390               if (charold == '\n')
391                 {
392                   if (*delimptr != EMPTY_DELIM)
393                     xputchar (*delimptr);
394
395                   if (++delimptr == delim_end)
396                     delimptr = delims;
397                 }
398               else
399                 xputchar (charold);
400
401               charold = charnew;
402             }
403           saved_errno = errno;
404
405           /* Hit EOF.  Process that last character. */
406           xputchar (charold);
407         }
408
409       if (charold != '\n')
410         xputchar ('\n');
411
412       if (ferror (fileptr))
413         {
414           error (0, saved_errno, "%s", *fnamptr);
415           ok = false;
416         }
417       if (is_stdin)
418         clearerr (fileptr);     /* Also clear EOF. */
419       else if (fclose (fileptr) == EOF)
420         {
421           error (0, errno, "%s", *fnamptr);
422           ok = false;
423         }
424     }
425   return ok;
426 }
427
428 void
429 usage (int status)
430 {
431   if (status != EXIT_SUCCESS)
432     fprintf (stderr, _("Try `%s --help' for more information.\n"),
433              program_name);
434   else
435     {
436       printf (_("\
437 Usage: %s [OPTION]... [FILE]...\n\
438 "),
439               program_name);
440       fputs (_("\
441 Write lines consisting of the sequentially corresponding lines from\n\
442 each FILE, separated by TABs, to standard output.\n\
443 With no FILE, or when FILE is -, read standard input.\n\
444 \n\
445 "), stdout);
446       fputs (_("\
447 Mandatory arguments to long options are mandatory for short options too.\n\
448 "), stdout);
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 (HELP_OPTION_DESCRIPTION, stdout);
454       fputs (VERSION_OPTION_DESCRIPTION, stdout);
455       /* FIXME: add a couple of examples.  */
456       emit_bug_reporting_address ();
457     }
458   exit (status);
459 }
460
461 int
462 main (int argc, char **argv)
463 {
464   int optc;
465   bool ok;
466   char const *delim_arg = "\t";
467
468   initialize_main (&argc, &argv);
469   program_name = argv[0];
470   setlocale (LC_ALL, "");
471   bindtextdomain (PACKAGE, LOCALEDIR);
472   textdomain (PACKAGE);
473
474   atexit (close_stdout);
475
476   have_read_stdin = false;
477   serial_merge = false;
478
479   while ((optc = getopt_long (argc, argv, "d:s", longopts, NULL)) != -1)
480     {
481       switch (optc)
482         {
483         case 'd':
484           /* Delimiter character(s). */
485           delim_arg = (optarg[0] == '\0' ? "\\0" : optarg);
486           break;
487
488         case 's':
489           serial_merge = true;
490           break;
491
492         case_GETOPT_HELP_CHAR;
493
494         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
495
496         default:
497           usage (EXIT_FAILURE);
498         }
499     }
500
501   if (optind == argc)
502     argv[argc++] = "-";
503
504   if (collapse_escapes (delim_arg))
505     {
506       /* Don't use the default quoting style, because that would double the
507          number of displayed backslashes, making the diagnostic look bogus.  */
508       set_quoting_style (NULL, escape_quoting_style);
509       error (EXIT_FAILURE, 0,
510              _("delimiter list ends with an unescaped backslash: %s"),
511              quotearg_colon (delim_arg));
512     }
513
514   if (!serial_merge)
515     ok = paste_parallel (argc - optind, &argv[optind]);
516   else
517     ok = paste_serial (argc - optind, &argv[optind]);
518
519   free (delims);
520
521   if (have_read_stdin && fclose (stdin) == EOF)
522     error (EXIT_FAILURE, errno, "-");
523   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
524 }