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)
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>.
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. */
43 #include <sys/types.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 /* Name this program was run with. */
58 /* If nonzero, we have read standard input at some point. */
59 static bool have_read_stdin
;
61 /* If nonzero, merge subsequent lines of each file rather than
62 corresponding lines from each file in parallel. */
63 static bool serial_merge
;
65 /* The delimeters between lines of input files (used cyclically). */
68 /* A pointer to the character after the end of `delims'. */
69 static char const *delim_end
;
71 static struct option
const longopts
[] =
73 {"serial", no_argument
, 0, 's'},
74 {"delimiters", required_argument
, 0, 'd'},
75 {GETOPT_HELP_OPTION_DECL
},
76 {GETOPT_VERSION_OPTION_DECL
},
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. */
86 collapse_escapes (char const *strptr
)
88 char *strout
= xstrdup (strptr
);
93 if (*strptr
!= '\\') /* Is it an escape character? */
94 *strout
++ = *strptr
++; /* No, just transfer it. */
100 *strout
++ = EMPTY_DELIM
;
137 /* Report a write error and exit. */
139 static void write_error (void) ATTRIBUTE_NORETURN
;
143 error (EXIT_FAILURE
, errno
, _("write error"));
147 /* Output a single byte, reporting any write errors. */
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
161 paste_parallel (size_t nfiles
, char **fnamptr
)
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);
170 /* Streams open to the files to process; NULL if the corresponding
172 FILE **fileptr
= xnmalloc (nfiles
+ 1, sizeof *fileptr
);
174 /* Number of files still open to process. */
177 /* True if any fopen got fd == STDIN_FILENO. */
178 bool opened_stdin
= false;
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. */
184 for (files_open
= 0; files_open
< nfiles
; ++files_open
)
186 if (STREQ (fnamptr
[files_open
], "-"))
188 have_read_stdin
= true;
189 fileptr
[files_open
] = stdin
;
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
)
201 if (opened_stdin
&& have_read_stdin
)
202 error (EXIT_FAILURE
, 0, _("standard input is closed"));
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. */
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'. */
216 for (i
= 0; i
< nfiles
&& files_open
; i
++)
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. */
224 chr
= getc (fileptr
[i
]);
226 if (chr
!= EOF
&& delims_saved
)
228 if (fwrite (delbuf
, 1, delims_saved
, stdout
) != delims_saved
)
239 chr
= getc (fileptr
[i
]);
244 if (line_length
== 0)
246 /* EOF, read error, or closed file.
247 If an EOF or error, close the file. */
250 if (ferror (fileptr
[i
]))
252 error (0, err
, "%s", fnamptr
[i
]);
255 if (fileptr
[i
] == stdin
)
256 clearerr (fileptr
[i
]); /* Also clear EOF. */
257 else if (fclose (fileptr
[i
]) == EOF
)
259 error (0, errno
, "%s", fnamptr
[i
]);
269 /* End of this output line.
270 Is this the end of the whole thing? */
273 /* No. Some files were not closed for this line. */
276 if (fwrite (delbuf
, 1, delims_saved
, stdout
)
283 continue; /* Next read of files, or exit. */
287 /* Closed file; add delimiter to `delbuf'. */
288 if (*delimptr
!= EMPTY_DELIM
)
289 delbuf
[delims_saved
++] = *delimptr
;
290 if (++delimptr
== delim_end
)
296 /* Some data read. */
299 /* Except for last file, replace last newline with delim. */
302 if (chr
!= '\n' && chr
!= EOF
)
304 if (*delimptr
!= EMPTY_DELIM
)
305 xputchar (*delimptr
);
306 if (++delimptr
== delim_end
)
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
);
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
329 paste_serial (size_t nfiles
, char **fnamptr
)
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. */
336 for (; nfiles
; nfiles
--, fnamptr
++)
339 if (STREQ (*fnamptr
, "-"))
341 have_read_stdin
= true;
346 fileptr
= fopen (*fnamptr
, "r");
349 error (0, errno
, "%s", *fnamptr
);
355 delimptr
= delims
; /* Set up for delimiter string. */
357 charold
= getc (fileptr
);
361 /* `charold' is set up. Hit it!
362 Keep reading characters, stashing them in `charnew';
363 output `charold', converting to the appropriate delimiter
364 character if needed. After the EOF, output `charold'
365 if it's a newline; otherwise, output it and then a newline. */
367 while ((charnew
= getc (fileptr
)) != EOF
)
369 /* Process the old character. */
372 if (*delimptr
!= EMPTY_DELIM
)
373 xputchar (*delimptr
);
375 if (++delimptr
== delim_end
)
385 /* Hit EOF. Process that last character. */
392 if (ferror (fileptr
))
394 error (0, saved_errno
, "%s", *fnamptr
);
397 if (fileptr
== stdin
)
398 clearerr (fileptr
); /* Also clear EOF. */
399 else if (fclose (fileptr
) == EOF
)
401 error (0, errno
, "%s", *fnamptr
);
411 if (status
!= EXIT_SUCCESS
)
412 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
417 Usage: %s [OPTION]... [FILE]...\n\
421 Write lines consisting of the sequentially corresponding lines from\n\
422 each FILE, separated by TABs, to standard output.\n\
423 With no FILE, or when FILE is -, read standard input.\n\
427 Mandatory arguments to long options are mandatory for short options too.\n\
430 -d, --delimiters=LIST reuse characters from LIST instead of TABs\n\
431 -s, --serial paste one file at a time instead of in parallel\n\
433 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
434 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
435 /* FIXME: add a couple of examples. */
436 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT
);
442 main (int argc
, char **argv
)
446 char const *delim_arg
= "\t";
448 initialize_main (&argc
, &argv
);
449 program_name
= argv
[0];
450 setlocale (LC_ALL
, "");
451 bindtextdomain (PACKAGE
, LOCALEDIR
);
452 textdomain (PACKAGE
);
454 atexit (close_stdout
);
456 have_read_stdin
= false;
457 serial_merge
= false;
459 while ((optc
= getopt_long (argc
, argv
, "d:s", longopts
, NULL
)) != -1)
464 /* Delimiter character(s). */
465 if (optarg
[0] == '\0')
474 case_GETOPT_HELP_CHAR
;
476 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
479 usage (EXIT_FAILURE
);
486 collapse_escapes (delim_arg
);
489 ok
= paste_parallel (argc
- optind
, &argv
[optind
]);
491 ok
= paste_serial (argc
- optind
, &argv
[optind
]);
495 if (have_read_stdin
&& fclose (stdin
) == EOF
)
496 error (EXIT_FAILURE
, errno
, "-");
497 exit (ok
? EXIT_SUCCESS
: EXIT_FAILURE
);