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