Don't include version.h.
[platform/upstream/coreutils.git] / src / paste.c
1 /* paste - merge lines of files
2    Copyright (C) 1984 by David M. Ihnat
3
4    This program is a total rewrite of the Bell Laboratories Unix(Tm)
5    command of the same name, as of System V.  It contains no proprietary
6    code, and therefore may be used without violation of any proprietary
7    agreements whatsoever.  However, you will notice that the program is
8    copyrighted by me.  This is to assure the program does *not* fall
9    into the public domain.  Thus, I may specify just what I am now:
10    This program may be freely copied and distributed, provided this notice
11    remains; it may not be sold for profit without express written consent of
12    the author.
13    Please note that I recreated the behavior of the Unix(Tm) 'paste' command
14    as faithfully as possible, with minor exceptions; however,
15    I haven't run a full set of regression tests.  Thus, the user of
16    this program accepts full responsibility for any effects or loss;
17    in particular, the author is not responsible for any losses,
18    explicit or incidental, that may be incurred through use of this program.
19
20    I ask that any bugs (and, if possible, fixes) be reported to me when
21    possible.  -David Ihnat (312) 784-4544 ignatz@homebru.chi.il.us
22
23    The list of valid escape sequences has been expanded over the Unix
24    version, to include \b, \f, \r, and \v.
25
26    POSIX changes, bug fixes, long-named options, and cleanup
27    by David MacKenzie <djm@gnu.ai.mit.edu>.
28
29    Options:
30    --serial
31    -s                           Paste one file at a time rather than
32                                 one line from each file.
33    --delimiters=delim-list
34    -d delim-list                Consecutively use the characters in
35                                 DELIM-LIST instead of tab to separate
36                                 merged lines.  When DELIM-LIST is exhausted,
37                                 start again at its beginning.
38    A FILE of `-' means standard input.
39    If no FILEs are given, standard input is used. */
40
41 #include <config.h>
42
43 #include <stdio.h>
44 #include <getopt.h>
45 #include <sys/types.h>
46 #include "system.h"
47 #include "error.h"
48
49 char *xmalloc ();
50 char *xrealloc ();
51
52 /* Indicates that no delimiter should be added in the current position. */
53 #define EMPTY_DELIM '\0'
54
55 static FILE dummy_closed;
56 /* Element marking a file that has reached EOF and been closed. */
57 #define CLOSED (&dummy_closed)
58
59 static FILE dummy_endlist;
60 /* Element marking end of list of open files. */
61 #define ENDLIST (&dummy_endlist)
62
63 /* Name this program was run with. */
64 char *program_name;
65
66 /* If nonzero, we have read standard input at some point. */
67 static int have_read_stdin;
68
69 /* If nonzero, merge subsequent lines of each file rather than
70    corresponding lines from each file in parallel. */
71 static int serial_merge;
72
73 /* The delimeters between lines of input files (used cyclically). */
74 static char *delims;
75
76 /* A pointer to the character after the end of `delims'. */
77 static char *delim_end;
78
79 /* If nonzero, display usage information and exit.  */
80 static int show_help;
81
82 /* If nonzero, print the version on standard output then exit.  */
83 static int show_version;
84
85 static struct option const longopts[] =
86 {
87   {"serial", no_argument, 0, 's'},
88   {"delimiters", required_argument, 0, 'd'},
89   {"help", no_argument, &show_help, 1},
90   {"version", no_argument, &show_version, 1},
91   {0, 0, 0, 0}
92 };
93
94 /* Replace backslash representations of special characters in
95    STRPTR with their actual values.
96    The set of possible backslash characters has been expanded beyond
97    that recognized by the Unix version.
98
99    Return a pointer to the character after the new end of STRPTR. */
100
101 static char *
102 collapse_escapes (char *strptr)
103 {
104   register char *strout;
105
106   strout = strptr;              /* Start at the same place, anyway. */
107
108   while (*strptr)
109     {
110       if (*strptr != '\\')      /* Is it an escape character? */
111         *strout++ = *strptr++;  /* No, just transfer it. */
112       else
113         {
114           switch (*++strptr)
115             {
116             case '0':
117               *strout++ = EMPTY_DELIM;
118               break;
119
120             case 'b':
121               *strout++ = '\b';
122               break;
123
124             case 'f':
125               *strout++ = '\f';
126               break;
127
128             case 'n':
129               *strout++ = '\n';
130               break;
131
132             case 'r':
133               *strout++ = '\r';
134               break;
135
136             case 't':
137               *strout++ = '\t';
138               break;
139
140             case 'v':
141               *strout++ = '\v';
142               break;
143
144             default:
145               *strout++ = *strptr;
146               break;
147             }
148           strptr++;
149         }
150     }
151   return strout;
152 }
153
154 /* Perform column paste on the NFILES files named in FNAMPTR.
155    Return 0 if no errors, 1 if one or more files could not be
156    opened or read. */
157
158 static int
159 paste_parallel (int nfiles, char **fnamptr)
160 {
161   int errors = 0;               /* 1 if open or read errors occur. */
162   /* Number of files for which space is allocated in `delbuf' and `fileptr'.
163      Enlarged as necessary. */
164   int file_list_size = 12;
165   int chr;                      /* Input character. */
166   int line_length;              /* Number of chars in line. */
167   int somedone;                 /* 0 if all files empty for this line. */
168   /* If all files are just ready to be closed, or will be on this
169      round, the string of delimiters must be preserved.
170      delbuf[0] through delbuf[file_list_size]
171      store the delimiters for closed files. */
172   char *delbuf;
173   int delims_saved;             /* Number of delims saved in `delbuf'. */
174   register char *delimptr;      /* Cycling pointer into `delims'. */
175   FILE **fileptr;               /* Streams open to the files to process. */
176   int files_open;               /* Number of files still open to process. */
177   int i;                        /* Loop index. */
178   int opened_stdin = 0;         /* Nonzero if any fopen got fd 0. */
179
180 #ifdef lint  /* Suppress `used before initialized' warning.  */
181   chr = 0;
182 #endif
183
184   delbuf = (char *) xmalloc (file_list_size + 2);
185   fileptr = (FILE **) xmalloc ((file_list_size + 1) * sizeof (FILE *));
186
187   /* Attempt to open all files.  This could be expanded to an infinite
188      number of files, but at the (considerable) expense of remembering
189      each file and its current offset, then opening/reading/closing.  */
190
191   for (files_open = 0; files_open < nfiles; ++files_open)
192     {
193       if (files_open == file_list_size - 2)
194         {
195           file_list_size += 12;
196           delbuf = (char *) xrealloc (delbuf, file_list_size + 2);
197           fileptr = (FILE **) xrealloc (fileptr, (file_list_size + 1)
198                                         * sizeof (FILE *));
199         }
200       if (!strcmp (fnamptr[files_open], "-"))
201         {
202           have_read_stdin = 1;
203           fileptr[files_open] = stdin;
204         }
205       else
206         {
207           fileptr[files_open] = fopen (fnamptr[files_open], "r");
208           if (fileptr[files_open] == NULL)
209             error (1, errno, "%s", fnamptr[files_open]);
210           else if (fileno (fileptr[files_open]) == 0)
211             opened_stdin = 1;
212         }
213     }
214
215   fileptr[files_open] = ENDLIST;
216
217   if (opened_stdin && have_read_stdin)
218     error (1, 0, _("standard input is closed"));
219
220   /* Read a line from each file and output it to stdout separated by a
221      delimiter, until we go through the loop without successfully
222      reading from any of the files. */
223
224   while (files_open)
225     {
226       /* Set up for the next line. */
227       somedone = 0;
228       delimptr = delims;
229       delims_saved = 0;
230
231       for (i = 0; fileptr[i] != ENDLIST && files_open; i++)
232         {
233           line_length = 0;      /* Clear so we can easily detect EOF. */
234           if (fileptr[i] != CLOSED)
235             {
236               chr = getc (fileptr[i]);
237               if (chr != EOF && delims_saved)
238                 {
239                   fwrite (delbuf, sizeof (char), delims_saved, stdout);
240                   delims_saved = 0;
241                 }
242
243               while (chr != EOF)
244                 {
245                   line_length++;
246                   if (chr == '\n')
247                     break;
248                   putc (chr, stdout);
249                   chr = getc (fileptr[i]);
250                 }
251             }
252
253           if (line_length == 0)
254             {
255               /* EOF, read error, or closed file.
256                  If an EOF or error, close the file and mark it in the list. */
257               if (fileptr[i] != CLOSED)
258                 {
259                   if (ferror (fileptr[i]))
260                     {
261                       error (0, errno, "%s", fnamptr[i]);
262                       errors = 1;
263                     }
264                   if (fileptr[i] == stdin)
265                     clearerr (fileptr[i]); /* Also clear EOF. */
266                   else if (fclose (fileptr[i]) == EOF)
267                     {
268                       error (0, errno, "%s", fnamptr[i]);
269                       errors = 1;
270                     }
271
272                   fileptr[i] = CLOSED;
273                   files_open--;
274                 }
275
276               if (fileptr[i + 1] == ENDLIST)
277                 {
278                   /* End of this output line.
279                      Is this the end of the whole thing? */
280                   if (somedone)
281                     {
282                       /* No.  Some files were not closed for this line. */
283                       if (delims_saved)
284                         {
285                           fwrite (delbuf, sizeof (char), delims_saved, stdout);
286                           delims_saved = 0;
287                         }
288                       putc ('\n', stdout);
289                     }
290                   continue;     /* Next read of files, or exit. */
291                 }
292               else
293                 {
294                   /* Closed file; add delimiter to `delbuf'. */
295                   if (*delimptr != EMPTY_DELIM)
296                     delbuf[delims_saved++] = *delimptr;
297                   if (++delimptr == delim_end)
298                     delimptr = delims;
299                 }
300             }
301           else
302             {
303               /* Some data read. */
304               somedone++;
305
306               /* Except for last file, replace last newline with delim. */
307               if (fileptr[i + 1] != ENDLIST)
308                 {
309                   if (chr != '\n')
310                     putc (chr, stdout);
311                   if (*delimptr != EMPTY_DELIM)
312                     putc (*delimptr, stdout);
313                   if (++delimptr == delim_end)
314                     delimptr = delims;
315                 }
316               else
317                 putc (chr, stdout);
318             }
319         }
320     }
321   return errors;
322 }
323
324 /* Perform serial paste on the NFILES files named in FNAMPTR.
325    Return 0 if no errors, 1 if one or more files could not be
326    opened or read. */
327
328 static int
329 paste_serial (int nfiles, char **fnamptr)
330 {
331   int errors = 0;               /* 1 if open or read errors occur. */
332   register int charnew, charold; /* Current and previous char read. */
333   register char *delimptr;      /* Current delimiter char. */
334   register FILE *fileptr;       /* Open for reading current file. */
335
336   for (; nfiles; nfiles--, fnamptr++)
337     {
338       if (!strcmp (*fnamptr, "-"))
339         {
340           have_read_stdin = 1;
341           fileptr = stdin;
342         }
343       else
344         {
345           fileptr = fopen (*fnamptr, "r");
346           if (fileptr == NULL)
347             {
348               error (0, errno, "%s", *fnamptr);
349               errors = 1;
350               continue;
351             }
352         }
353
354       delimptr = delims;        /* Set up for delimiter string. */
355
356       charold = getc (fileptr);
357       if (charold != EOF)
358         {
359           /* `charold' is set up.  Hit it!
360              Keep reading characters, stashing them in `charnew';
361              output `charold', converting to the appropriate delimiter
362              character if needed.  After the EOF, output `charold'
363              if it's a newline; otherwise, output it and then a newline. */
364
365           while ((charnew = getc (fileptr)) != EOF)
366             {
367               /* Process the old character. */
368               if (charold == '\n')
369                 {
370                   if (*delimptr != EMPTY_DELIM)
371                     putc (*delimptr, stdout);
372
373                   if (++delimptr == delim_end)
374                     delimptr = delims;
375                 }
376               else
377                 putc (charold, stdout);
378
379               charold = charnew;
380             }
381
382           /* Hit EOF.  Process that last character. */
383           putc (charold, stdout);
384         }
385
386       if (charold != '\n')
387         putc ('\n', stdout);
388
389       if (ferror (fileptr))
390         {
391           error (0, errno, "%s", *fnamptr);
392           errors = 1;
393         }
394       if (fileptr == stdin)
395         clearerr (fileptr);     /* Also clear EOF. */
396       else if (fclose (fileptr) == EOF)
397         {
398           error (0, errno, "%s", *fnamptr);
399           errors = 1;
400         }
401     }
402   return errors;
403 }
404
405 static void
406 usage (int status)
407 {
408   if (status != 0)
409     fprintf (stderr, _("Try `%s --help' for more information.\n"),
410              program_name);
411   else
412     {
413       printf (_("\
414 Usage: %s [OPTION]... [FILE]...\n\
415 "),
416               program_name);
417       printf (_("\
418 Write lines consisting of the sequentially corresponding lines from\n\
419 each FILE, separated by TABs, to standard output.\n\
420 With no FILE, or when FILE is -, read standard input.\n\
421 \n\
422   -d, --delimiters=LIST   reuse characters from LIST instead of TABs\n\
423   -s, --serial            paste one file at a time instead of in parallel\n\
424       --help              display this help and exit\n\
425       --version           output version information and exit\n\
426 \n\
427 "));
428     }
429   exit (status);
430 }
431
432 void
433 main (int argc, char **argv)
434 {
435   int optc, exit_status;
436   char default_delims[2], zero_delims[3];
437
438   program_name = argv[0];
439   setlocale (LC_ALL, "");
440   bindtextdomain (PACKAGE, LOCALEDIR);
441   textdomain (PACKAGE);
442
443   have_read_stdin = 0;
444   serial_merge = 0;
445   delims = default_delims;
446   strcpy (delims, "\t");
447   strcpy (zero_delims, "\\0");
448
449   while ((optc = getopt_long (argc, argv, "d:s", longopts, (int *) 0))
450          != EOF)
451     {
452       switch (optc)
453         {
454         case 0:
455           break;
456
457         case 'd':
458           /* Delimiter character(s). */
459           if (optarg[0] == '\0')
460             optarg = zero_delims;
461           delims = optarg;
462           break;
463
464         case 's':
465           serial_merge++;
466           break;
467
468         default:
469           usage (1);
470         }
471     }
472
473   if (show_version)
474     {
475       printf ("paste - %s\n", PACKAGE_VERSION);
476       exit (0);
477     }
478
479   if (show_help)
480     usage (0);
481
482   if (optind == argc)
483     argv[argc++] = "-";
484
485   delim_end = collapse_escapes (delims);
486
487   if (!serial_merge)
488     exit_status = paste_parallel (argc - optind, &argv[optind]);
489   else
490     exit_status = paste_serial (argc - optind, &argv[optind]);
491   if (have_read_stdin && fclose (stdin) == EOF)
492     error (1, errno, "-");
493   if (ferror (stdout) || fclose (stdout) == EOF)
494     error (1, errno, _("write error"));
495   exit (exit_status);
496 }