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