1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1992-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
20 ***********************************************************************/
24 * AT&T Bell Laboratories
26 * paste [-s] [-d delim] [file] ...
28 * paste lines from files together
31 static const char usage
[] =
32 "[-?\n@(#)$Id: paste (AT&T Research) 2009-11-28 $\n]"
34 "[+NAME?paste - merge lines of files]"
35 "[+DESCRIPTION?\bpaste\b concatenates the corresponding lines of a "
36 "given input file and writes the resulting lines to standard "
37 "output. By default \bpaste\b replaces the newline character of "
38 "every line other than the last input file with the TAB character.]"
39 "[+?Unless the \b-s\b option is specified, if an end-of-file is encountered "
40 "on one or more input files, but not all input files, \bpaste\b "
41 "behaves as if empty lines were read from the file(s) on which "
42 "end-of-file was detected.]"
43 "[+?Unless the \b-s\b option is specified, \bpaste\b is limited by "
44 "the underlying operating system on how many \afile\a operands "
46 "[+?If no \afile\a operands are given or if the \afile\a is \b-\b, \bpaste\b "
47 "reads from standard input. The start of the file is defined as the "
50 "[s:serial?Paste the lines of one file at a time rather than one line "
51 "from each file. In this case if the \b-d\b option is "
52 "specified the delimiter will be reset to the first in the "
53 "list at the beginning of each file.]"
54 "[d:delimiters]:[list?\alist\a specifies a list of delimiters. These "
55 "delimiters are used circularly instead of TAB to replace "
56 "the newline character of the input lines. Unless the \b-s\b "
57 "option is specified, the delimiter will be reset to the first "
58 "element of \alist\a each time a line is processed from each file. "
59 "The delimiter characters corresponding to \alist\a will be found "
60 "by treating \alist\a as an ANSI-C string, except that the \b\\0\b "
61 "sequence will insert the empty string instead of the null character.]"
66 "[+0?All files processed successfully.]"
67 "[+>0?An error occurred.]"
69 "[+SEE ALSO?\bcut\b(1), \bcat\b(1), \bjoin\b(1)]"
74 typedef struct Delim_s
81 * paste the lines of the <nstreams> defined in <streams> and put results
85 static int paste(int nstream
,Sfio_t
* streams
[],Sfio_t
*out
, register const char *delim
, int dsiz
, int dlen
, Delim_t
* mp
)
87 register const char *cp
;
88 register int d
, n
, i
, z
, more
=1;
93 for(n
=more
-1,more
=0; n
< nstream
;)
97 if(cp
= sfgetr(fp
,'\n',0))
101 else if(!more
) /* first stream with output */
104 sfnputc(out
, *delim
, n
);
107 for(d
=n
; d
>dlen
; d
-=dlen
)
108 sfwrite(out
,delim
,dsiz
);
112 for (i
= z
= 0; i
< d
; i
++)
116 sfwrite(out
,delim
,z
);
121 if(sfwrite(out
,cp
,sfvalue(fp
)-((n
+1)<nstream
)) < 0)
127 if(++n
<nstream
&& more
&& d
>=0)
133 sfwrite(out
,mp
[d
].chr
,mp
[d
].len
);
138 else if(n
==nstream
&& !streams
[n
-1] && more
)
146 * Handles paste -s, for file <in> to file <out> using delimiters <delim>
148 static int spaste(Sfio_t
*in
,register Sfio_t
* out
,register const char *delim
,int dsiz
,int dlen
,Delim_t
* mp
)
150 register const char *cp
;
152 if((cp
= sfgetr(in
,'\n',0)) && sfwrite(out
,cp
,sfvalue(in
)-1) < 0)
154 while(cp
=sfgetr(in
, '\n',0))
162 sfwrite(out
,mp
[d
].chr
,mp
[d
].len
);
167 if(sfwrite(out
,cp
,sfvalue(in
)-1) < 0)
175 b_paste(int argc
,register char *argv
[], void* context
)
177 register int n
, sflag
=0;
178 register Sfio_t
*fp
, **streams
;
179 register char *cp
, *delim
;
185 cmdinit(argc
, argv
, context
, ERROR_CATALOG
, 0);
187 while (n
= optget(argv
, usage
)) switch (n
)
190 delim
= opt_info
.arg
;
196 error(2, "%s", opt_info
.arg
);
199 error(ERROR_usage(2), "%s", opt_info
.arg
);
202 argv
+= opt_info
.index
;
203 if(error_info
.errors
)
204 error(ERROR_usage(2),"%s", optusage(NiL
));
205 if(!delim
|| !*delim
)
211 dlen
= dsiz
= stresc(delim
);
225 if (!(mp
= newof(0, Delim_t
, dlen
, 0)))
226 error(ERROR_system(1), "out of space");
233 mp
[dlen
].len
= cp
- mp
[dlen
].chr
;
240 n
= argc
- opt_info
.index
;
247 if (!(streams
= (Sfio_t
**)stakalloc(n
*sizeof(Sfio_t
*))))
248 error(ERROR_exit(1), "out of space");
253 if(!cp
|| streq(cp
,"-"))
255 else if(!(fp
= sfopen(NiL
,cp
,"r")))
256 error(ERROR_system(0),"%s: cannot open",cp
);
259 if(spaste(fp
,sfstdout
,delim
,dsiz
,dlen
,mp
) < 0)
260 error(ERROR_system(0),"write failed");
266 } while(cp
= *argv
++);
269 if(error_info
.errors
==0 && paste(n
,streams
,sfstdout
,delim
,dsiz
,dlen
,mp
) < 0)
270 error(ERROR_system(0),"write failed");
272 if((fp
=streams
[n
]) && fp
!=sfstdin
)
276 return(error_info
.errors
);