1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*3e14f97fSRoger A. Faulkner * Copyright (c) 1992-2010 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt *
11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12da2e3ebdSchin * *
13da2e3ebdSchin * Information and Software Systems Research *
14da2e3ebdSchin * AT&T Research *
15da2e3ebdSchin * Florham Park NJ *
16da2e3ebdSchin * *
17da2e3ebdSchin * Glenn Fowler <gsf@research.att.com> *
18da2e3ebdSchin * David Korn <dgk@research.att.com> *
19da2e3ebdSchin * *
20da2e3ebdSchin ***********************************************************************/
21da2e3ebdSchin #pragma prototyped
22da2e3ebdSchin /*
23da2e3ebdSchin * David Korn
24da2e3ebdSchin * AT&T Bell Laboratories
25da2e3ebdSchin *
26da2e3ebdSchin * paste [-s] [-d delim] [file] ...
27da2e3ebdSchin *
28da2e3ebdSchin * paste lines from files together
29da2e3ebdSchin */
30da2e3ebdSchin
31da2e3ebdSchin static const char usage[] =
32*3e14f97fSRoger A. Faulkner "[-?\n@(#)$Id: paste (AT&T Research) 2009-11-28 $\n]"
33da2e3ebdSchin USAGE_LICENSE
34da2e3ebdSchin "[+NAME?paste - merge lines of files]"
35da2e3ebdSchin "[+DESCRIPTION?\bpaste\b concatenates the corresponding lines of a "
36da2e3ebdSchin "given input file and writes the resulting lines to standard "
37da2e3ebdSchin "output. By default \bpaste\b replaces the newline character of "
38da2e3ebdSchin "every line other than the last input file with the TAB character.]"
39da2e3ebdSchin "[+?Unless the \b-s\b option is specified, if an end-of-file is encountered "
40da2e3ebdSchin "on one or more input files, but not all input files, \bpaste\b "
41da2e3ebdSchin "behaves as if empty lines were read from the file(s) on which "
42da2e3ebdSchin "end-of-file was detected.]"
43da2e3ebdSchin "[+?Unless the \b-s\b option is specified, \bpaste\b is limited by "
44da2e3ebdSchin "the underlying operating system on how many \afile\a operands "
45da2e3ebdSchin "can be specified.]"
46da2e3ebdSchin "[+?If no \afile\a operands are given or if the \afile\a is \b-\b, \bpaste\b "
47da2e3ebdSchin "reads from standard input. The start of the file is defined as the "
48da2e3ebdSchin "current offset.]"
49da2e3ebdSchin
50da2e3ebdSchin "[s:serial?Paste the lines of one file at a time rather than one line "
51da2e3ebdSchin "from each file. In this case if the \b-d\b option is "
52da2e3ebdSchin "specified the delimiter will be reset to the first in the "
53da2e3ebdSchin "list at the beginning of each file.]"
54da2e3ebdSchin "[d:delimiters]:[list?\alist\a specifies a list of delimiters. These "
55da2e3ebdSchin "delimiters are used circularly instead of TAB to replace "
56da2e3ebdSchin "the newline character of the input lines. Unless the \b-s\b "
57da2e3ebdSchin "option is specified, the delimiter will be reset to the first "
58da2e3ebdSchin "element of \alist\a each time a line is processed from each file. "
59da2e3ebdSchin "The delimiter characters corresponding to \alist\a will be found "
60da2e3ebdSchin "by treating \alist\a as an ANSI-C string, except that the \b\\0\b "
61da2e3ebdSchin "sequence will insert the empty string instead of the null character.]"
62da2e3ebdSchin "\n"
63da2e3ebdSchin "\n[file ...]\n"
64da2e3ebdSchin "\n"
65da2e3ebdSchin "[+EXIT STATUS?]{"
66da2e3ebdSchin "[+0?All files processed successfully.]"
67da2e3ebdSchin "[+>0?An error occurred.]"
68da2e3ebdSchin "}"
69da2e3ebdSchin "[+SEE ALSO?\bcut\b(1), \bcat\b(1), \bjoin\b(1)]"
70da2e3ebdSchin ;
71da2e3ebdSchin
72da2e3ebdSchin #include <cmd.h>
73da2e3ebdSchin
74*3e14f97fSRoger A. Faulkner typedef struct Delim_s
75*3e14f97fSRoger A. Faulkner {
76*3e14f97fSRoger A. Faulkner const char* chr;
77*3e14f97fSRoger A. Faulkner size_t len;
78*3e14f97fSRoger A. Faulkner } Delim_t;
79*3e14f97fSRoger A. Faulkner
80da2e3ebdSchin /*
81da2e3ebdSchin * paste the lines of the <nstreams> defined in <streams> and put results
82da2e3ebdSchin * to <out>
83da2e3ebdSchin */
84da2e3ebdSchin
paste(int nstream,Sfio_t * streams[],Sfio_t * out,register const char * delim,int dsiz,int dlen,Delim_t * mp)85*3e14f97fSRoger A. Faulkner static int paste(int nstream,Sfio_t* streams[],Sfio_t *out, register const char *delim, int dsiz, int dlen, Delim_t* mp)
86da2e3ebdSchin {
87da2e3ebdSchin register const char *cp;
88*3e14f97fSRoger A. Faulkner register int d, n, i, z, more=1;
89da2e3ebdSchin register Sfio_t *fp;
90da2e3ebdSchin do
91da2e3ebdSchin {
92da2e3ebdSchin d = (dlen>0?0:-1);
93da2e3ebdSchin for(n=more-1,more=0; n < nstream;)
94da2e3ebdSchin {
95da2e3ebdSchin if(fp=streams[n])
96da2e3ebdSchin {
97da2e3ebdSchin if(cp = sfgetr(fp,'\n',0))
98da2e3ebdSchin {
99da2e3ebdSchin if(n==0)
100da2e3ebdSchin more = 1;
101da2e3ebdSchin else if(!more) /* first stream with output */
102da2e3ebdSchin {
103*3e14f97fSRoger A. Faulkner if(dsiz == 1)
104da2e3ebdSchin sfnputc(out, *delim, n);
105da2e3ebdSchin else if(dlen>0)
106da2e3ebdSchin {
107da2e3ebdSchin for(d=n; d>dlen; d-=dlen)
108*3e14f97fSRoger A. Faulkner sfwrite(out,delim,dsiz);
109da2e3ebdSchin if(d)
110*3e14f97fSRoger A. Faulkner {
111*3e14f97fSRoger A. Faulkner if(mp)
112*3e14f97fSRoger A. Faulkner for (i = z = 0; i < d; i++)
113*3e14f97fSRoger A. Faulkner z += mp[i].len;
114*3e14f97fSRoger A. Faulkner else
115*3e14f97fSRoger A. Faulkner z = d;
116*3e14f97fSRoger A. Faulkner sfwrite(out,delim,z);
117*3e14f97fSRoger A. Faulkner }
118da2e3ebdSchin }
119da2e3ebdSchin more = n+1;
120da2e3ebdSchin }
121da2e3ebdSchin if(sfwrite(out,cp,sfvalue(fp)-((n+1)<nstream)) < 0)
122da2e3ebdSchin return(-1);
123da2e3ebdSchin }
124da2e3ebdSchin else
125da2e3ebdSchin streams[n] = 0;
126da2e3ebdSchin }
127da2e3ebdSchin if(++n<nstream && more && d>=0)
128da2e3ebdSchin {
129da2e3ebdSchin register int c;
130da2e3ebdSchin if(d >= dlen)
131da2e3ebdSchin d = 0;
132*3e14f97fSRoger A. Faulkner if(mp)
133*3e14f97fSRoger A. Faulkner sfwrite(out,mp[d].chr,mp[d].len);
134*3e14f97fSRoger A. Faulkner else if(c=delim[d])
135da2e3ebdSchin sfputc(out,c);
136*3e14f97fSRoger A. Faulkner d++;
137da2e3ebdSchin }
138da2e3ebdSchin else if(n==nstream && !streams[n-1] && more)
139da2e3ebdSchin sfputc(out,'\n');
140da2e3ebdSchin }
141*3e14f97fSRoger A. Faulkner } while(more);
142da2e3ebdSchin return(0);
143da2e3ebdSchin }
144da2e3ebdSchin
145da2e3ebdSchin /*
146da2e3ebdSchin * Handles paste -s, for file <in> to file <out> using delimiters <delim>
147da2e3ebdSchin */
spaste(Sfio_t * in,register Sfio_t * out,register const char * delim,int dsiz,int dlen,Delim_t * mp)148*3e14f97fSRoger A. Faulkner static int spaste(Sfio_t *in,register Sfio_t* out,register const char *delim,int dsiz,int dlen,Delim_t* mp)
149da2e3ebdSchin {
150da2e3ebdSchin register const char *cp;
151da2e3ebdSchin register int d=0;
152*3e14f97fSRoger A. Faulkner if((cp = sfgetr(in,'\n',0)) && sfwrite(out,cp,sfvalue(in)-1) < 0)
153da2e3ebdSchin return(-1);
154da2e3ebdSchin while(cp=sfgetr(in, '\n',0))
155da2e3ebdSchin {
156da2e3ebdSchin if(dlen)
157da2e3ebdSchin {
158da2e3ebdSchin register int c;
159da2e3ebdSchin if(d >= dlen)
160da2e3ebdSchin d = 0;
161*3e14f97fSRoger A. Faulkner if(mp)
162*3e14f97fSRoger A. Faulkner sfwrite(out,mp[d].chr,mp[d].len);
163*3e14f97fSRoger A. Faulkner else if(c=delim[d])
164da2e3ebdSchin sfputc(out,c);
165*3e14f97fSRoger A. Faulkner d++;
166da2e3ebdSchin }
167da2e3ebdSchin if(sfwrite(out,cp,sfvalue(in)-1) < 0)
168da2e3ebdSchin return(-1);
169da2e3ebdSchin }
170da2e3ebdSchin sfputc(out,'\n');
171da2e3ebdSchin return(0);
172da2e3ebdSchin }
173da2e3ebdSchin
174da2e3ebdSchin int
b_paste(int argc,register char * argv[],void * context)175da2e3ebdSchin b_paste(int argc,register char *argv[], void* context)
176da2e3ebdSchin {
177da2e3ebdSchin register int n, sflag=0;
178da2e3ebdSchin register Sfio_t *fp, **streams;
179da2e3ebdSchin register char *cp, *delim;
180*3e14f97fSRoger A. Faulkner char *ep;
181*3e14f97fSRoger A. Faulkner Delim_t *mp;
182*3e14f97fSRoger A. Faulkner int dlen, dsiz;
183da2e3ebdSchin char defdelim[2];
184da2e3ebdSchin
185da2e3ebdSchin cmdinit(argc, argv, context, ERROR_CATALOG, 0);
186da2e3ebdSchin delim = 0;
187da2e3ebdSchin while (n = optget(argv, usage)) switch (n)
188da2e3ebdSchin {
189da2e3ebdSchin case 'd':
190da2e3ebdSchin delim = opt_info.arg;
191da2e3ebdSchin break;
192da2e3ebdSchin case 's':
193da2e3ebdSchin sflag++;
194da2e3ebdSchin break;
195da2e3ebdSchin case ':':
196da2e3ebdSchin error(2, "%s", opt_info.arg);
197da2e3ebdSchin break;
198da2e3ebdSchin case '?':
199da2e3ebdSchin error(ERROR_usage(2), "%s", opt_info.arg);
200da2e3ebdSchin break;
201da2e3ebdSchin }
202da2e3ebdSchin argv += opt_info.index;
203da2e3ebdSchin if(error_info.errors)
204da2e3ebdSchin error(ERROR_usage(2),"%s", optusage(NiL));
205*3e14f97fSRoger A. Faulkner if(!delim || !*delim)
206da2e3ebdSchin {
207*3e14f97fSRoger A. Faulkner delim = defdelim;
208*3e14f97fSRoger A. Faulkner delim[0] = '\t';
209*3e14f97fSRoger A. Faulkner delim[1] = 0;
210*3e14f97fSRoger A. Faulkner }
211*3e14f97fSRoger A. Faulkner dlen = dsiz = stresc(delim);
212*3e14f97fSRoger A. Faulkner mp = 0;
213*3e14f97fSRoger A. Faulkner if (mbwide())
214*3e14f97fSRoger A. Faulkner {
215*3e14f97fSRoger A. Faulkner cp = delim;
216*3e14f97fSRoger A. Faulkner ep = delim + dlen;
217*3e14f97fSRoger A. Faulkner dlen = 0;
218*3e14f97fSRoger A. Faulkner while (cp < ep)
219*3e14f97fSRoger A. Faulkner {
220*3e14f97fSRoger A. Faulkner mbchar(cp);
221*3e14f97fSRoger A. Faulkner dlen++;
222*3e14f97fSRoger A. Faulkner }
223*3e14f97fSRoger A. Faulkner if(dlen < dsiz)
224*3e14f97fSRoger A. Faulkner {
225*3e14f97fSRoger A. Faulkner if (!(mp = newof(0, Delim_t, dlen, 0)))
226*3e14f97fSRoger A. Faulkner error(ERROR_system(1), "out of space");
227*3e14f97fSRoger A. Faulkner cp = delim;
228*3e14f97fSRoger A. Faulkner dlen = 0;
229*3e14f97fSRoger A. Faulkner while (cp < ep)
230*3e14f97fSRoger A. Faulkner {
231*3e14f97fSRoger A. Faulkner mp[dlen].chr = cp;
232*3e14f97fSRoger A. Faulkner mbchar(cp);
233*3e14f97fSRoger A. Faulkner mp[dlen].len = cp - mp[dlen].chr;
234*3e14f97fSRoger A. Faulkner dlen++;
235*3e14f97fSRoger A. Faulkner }
236*3e14f97fSRoger A. Faulkner }
237da2e3ebdSchin }
238da2e3ebdSchin if(cp = *argv)
239da2e3ebdSchin {
240da2e3ebdSchin n = argc - opt_info.index;
241da2e3ebdSchin argv++;
242da2e3ebdSchin }
243da2e3ebdSchin else
244da2e3ebdSchin n = 1;
245da2e3ebdSchin if(!sflag)
246da2e3ebdSchin {
247da2e3ebdSchin if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*))))
248da2e3ebdSchin error(ERROR_exit(1), "out of space");
249da2e3ebdSchin n = 0;
250da2e3ebdSchin }
251da2e3ebdSchin do
252da2e3ebdSchin {
253da2e3ebdSchin if(!cp || streq(cp,"-"))
254da2e3ebdSchin fp = sfstdin;
255da2e3ebdSchin else if(!(fp = sfopen(NiL,cp,"r")))
256da2e3ebdSchin error(ERROR_system(0),"%s: cannot open",cp);
257da2e3ebdSchin if(fp && sflag)
258da2e3ebdSchin {
259*3e14f97fSRoger A. Faulkner if(spaste(fp,sfstdout,delim,dsiz,dlen,mp) < 0)
260da2e3ebdSchin error(ERROR_system(0),"write failed");
261da2e3ebdSchin if(fp!=sfstdin)
262da2e3ebdSchin sfclose(fp);
263da2e3ebdSchin }
2647c2fbfb3SApril Chin else if(!sflag)
265da2e3ebdSchin streams[n++] = fp;
2667c2fbfb3SApril Chin } while(cp= *argv++);
267da2e3ebdSchin if(!sflag)
268da2e3ebdSchin {
269*3e14f97fSRoger A. Faulkner if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dsiz,dlen,mp) < 0)
270da2e3ebdSchin error(ERROR_system(0),"write failed");
271da2e3ebdSchin while(--n>=0)
272da2e3ebdSchin if((fp=streams[n]) && fp!=sfstdin)
273da2e3ebdSchin sfclose(fp);
274da2e3ebdSchin }
275*3e14f97fSRoger A. Faulkner if (mp)
276*3e14f97fSRoger A. Faulkner free(mp);
277da2e3ebdSchin return(error_info.errors);
278da2e3ebdSchin }
279