1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*b30d1939SAndy Fiddaman *          Copyright (c) 1992-2012 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6*b30d1939SAndy Fiddaman *                 Eclipse Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10*b30d1939SAndy Fiddaman *          http://www.eclipse.org/org/documents/epl-v10.html           *
11*b30d1939SAndy Fiddaman *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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*b30d1939SAndy Fiddaman "[-?\n@(#)$Id: paste (AT&T Research) 2010-06-12 $\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 
743e14f97fSRoger A. Faulkner typedef struct Delim_s
753e14f97fSRoger A. Faulkner {
763e14f97fSRoger A. Faulkner 	const char*	chr;
773e14f97fSRoger A. Faulkner 	size_t		len;
783e14f97fSRoger A. Faulkner } Delim_t;
793e14f97fSRoger 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)853e14f97fSRoger 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;
883e14f97fSRoger 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 					{
1033e14f97fSRoger A. Faulkner 						if(dsiz == 1)
104da2e3ebdSchin 							sfnputc(out, *delim, n);
105da2e3ebdSchin 						else if(dlen>0)
106da2e3ebdSchin 						{
107da2e3ebdSchin 							for(d=n; d>dlen; d-=dlen)
1083e14f97fSRoger A. Faulkner 								sfwrite(out,delim,dsiz);
109da2e3ebdSchin 							if(d)
1103e14f97fSRoger A. Faulkner 							{
1113e14f97fSRoger A. Faulkner 								if(mp)
1123e14f97fSRoger A. Faulkner 									for (i = z = 0; i < d; i++)
1133e14f97fSRoger A. Faulkner 										z += mp[i].len;
1143e14f97fSRoger A. Faulkner 								else
1153e14f97fSRoger A. Faulkner 									z = d;
1163e14f97fSRoger A. Faulkner 								sfwrite(out,delim,z);
1173e14f97fSRoger 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;
1323e14f97fSRoger A. Faulkner 				if(mp)
1333e14f97fSRoger A. Faulkner 					sfwrite(out,mp[d].chr,mp[d].len);
1343e14f97fSRoger A. Faulkner 				else if(c=delim[d])
135da2e3ebdSchin 					sfputc(out,c);
1363e14f97fSRoger A. Faulkner 				d++;
137da2e3ebdSchin 			}
138da2e3ebdSchin 			else if(n==nstream && !streams[n-1] && more)
139da2e3ebdSchin 				sfputc(out,'\n');
140da2e3ebdSchin 		}
1413e14f97fSRoger 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)1483e14f97fSRoger 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;
1523e14f97fSRoger A. Faulkner 	if((cp = sfgetr(in,'\n',0)) && sfwrite(out,cp,sfvalue(in)-1) < 0)
1533e14f97fSRoger A. Faulkner 		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;
1613e14f97fSRoger A. Faulkner 			if(mp)
1623e14f97fSRoger A. Faulkner 				sfwrite(out,mp[d].chr,mp[d].len);
1633e14f97fSRoger A. Faulkner 			else if(c=delim[d])
164da2e3ebdSchin 				sfputc(out,c);
1653e14f97fSRoger 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,char ** argv,Shbltin_t * context)175*b30d1939SAndy Fiddaman b_paste(int argc, char** argv, Shbltin_t* context)
176da2e3ebdSchin {
177da2e3ebdSchin 	register int		n, sflag=0;
178da2e3ebdSchin 	register Sfio_t		*fp, **streams;
179da2e3ebdSchin 	register char 		*cp, *delim;
1803e14f97fSRoger A. Faulkner 	char			*ep;
1813e14f97fSRoger A. Faulkner 	Delim_t			*mp;
1823e14f97fSRoger A. Faulkner 	int			dlen, dsiz;
183da2e3ebdSchin 	char			defdelim[2];
184da2e3ebdSchin 
185da2e3ebdSchin 	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
186da2e3ebdSchin 	delim = 0;
187*b30d1939SAndy Fiddaman 	for (;;)
188da2e3ebdSchin 	{
189*b30d1939SAndy Fiddaman 		switch (optget(argv, usage))
190*b30d1939SAndy Fiddaman 		{
191*b30d1939SAndy Fiddaman 		case 'd':
192*b30d1939SAndy Fiddaman 			delim = opt_info.arg;
193*b30d1939SAndy Fiddaman 			continue;
194*b30d1939SAndy Fiddaman 		case 's':
195*b30d1939SAndy Fiddaman 			sflag++;
196*b30d1939SAndy Fiddaman 			continue;
197*b30d1939SAndy Fiddaman 		case ':':
198*b30d1939SAndy Fiddaman 			error(2, "%s", opt_info.arg);
199*b30d1939SAndy Fiddaman 			break;
200*b30d1939SAndy Fiddaman 		case '?':
201*b30d1939SAndy Fiddaman 			error(ERROR_usage(2), "%s", opt_info.arg);
202*b30d1939SAndy Fiddaman 			break;
203*b30d1939SAndy Fiddaman 		}
204da2e3ebdSchin 		break;
205da2e3ebdSchin 	}
206da2e3ebdSchin 	argv += opt_info.index;
207da2e3ebdSchin 	if(error_info.errors)
208da2e3ebdSchin 		error(ERROR_usage(2),"%s", optusage(NiL));
2093e14f97fSRoger A. Faulkner 	if(!delim || !*delim)
210da2e3ebdSchin 	{
2113e14f97fSRoger A. Faulkner 		delim = defdelim;
2123e14f97fSRoger A. Faulkner 		delim[0] = '\t';
2133e14f97fSRoger A. Faulkner 		delim[1] = 0;
2143e14f97fSRoger A. Faulkner 	}
215*b30d1939SAndy Fiddaman 	if (!(delim = strdup(delim)))
216*b30d1939SAndy Fiddaman 		error(ERROR_system(1), "out of space");
2173e14f97fSRoger A. Faulkner 	dlen = dsiz = stresc(delim);
2183e14f97fSRoger A. Faulkner 	mp = 0;
2193e14f97fSRoger A. Faulkner 	if (mbwide())
2203e14f97fSRoger A. Faulkner 	{
2213e14f97fSRoger A. Faulkner 		cp = delim;
2223e14f97fSRoger A. Faulkner 		ep = delim + dlen;
2233e14f97fSRoger A. Faulkner 		dlen = 0;
2243e14f97fSRoger A. Faulkner 		while (cp < ep)
2253e14f97fSRoger A. Faulkner 		{
2263e14f97fSRoger A. Faulkner 			mbchar(cp);
2273e14f97fSRoger A. Faulkner 			dlen++;
2283e14f97fSRoger A. Faulkner 		}
2293e14f97fSRoger A. Faulkner 		if(dlen < dsiz)
2303e14f97fSRoger A. Faulkner 		{
2313e14f97fSRoger A. Faulkner 			if (!(mp = newof(0, Delim_t, dlen, 0)))
232*b30d1939SAndy Fiddaman 			{
233*b30d1939SAndy Fiddaman 				free(delim);
2343e14f97fSRoger A. Faulkner 				error(ERROR_system(1), "out of space");
235*b30d1939SAndy Fiddaman 			}
2363e14f97fSRoger A. Faulkner 			cp = delim;
2373e14f97fSRoger A. Faulkner 			dlen = 0;
2383e14f97fSRoger A. Faulkner 			while (cp < ep)
2393e14f97fSRoger A. Faulkner 			{
2403e14f97fSRoger A. Faulkner 				mp[dlen].chr = cp;
2413e14f97fSRoger A. Faulkner 				mbchar(cp);
2423e14f97fSRoger A. Faulkner 				mp[dlen].len = cp - mp[dlen].chr;
2433e14f97fSRoger A. Faulkner 				dlen++;
2443e14f97fSRoger A. Faulkner 			}
2453e14f97fSRoger A. Faulkner 		}
246da2e3ebdSchin 	}
247da2e3ebdSchin 	if(cp = *argv)
248da2e3ebdSchin 	{
249da2e3ebdSchin 		n = argc - opt_info.index;
250da2e3ebdSchin 		argv++;
251da2e3ebdSchin 	}
252da2e3ebdSchin 	else
253da2e3ebdSchin 		n = 1;
254da2e3ebdSchin 	if(!sflag)
255da2e3ebdSchin 	{
256da2e3ebdSchin 		if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*))))
257da2e3ebdSchin 			error(ERROR_exit(1), "out of space");
258da2e3ebdSchin 		n = 0;
259da2e3ebdSchin 	}
260da2e3ebdSchin 	do
261da2e3ebdSchin 	{
262da2e3ebdSchin 		if(!cp || streq(cp,"-"))
263da2e3ebdSchin 			fp = sfstdin;
264da2e3ebdSchin 		else if(!(fp = sfopen(NiL,cp,"r")))
265da2e3ebdSchin 			error(ERROR_system(0),"%s: cannot open",cp);
266da2e3ebdSchin 		if(fp && sflag)
267da2e3ebdSchin 		{
2683e14f97fSRoger A. Faulkner 			if(spaste(fp,sfstdout,delim,dsiz,dlen,mp) < 0)
269da2e3ebdSchin 				error(ERROR_system(0),"write failed");
270da2e3ebdSchin 			if(fp!=sfstdin)
271da2e3ebdSchin 				sfclose(fp);
272da2e3ebdSchin 		}
2737c2fbfb3SApril Chin 		else if(!sflag)
274da2e3ebdSchin 			streams[n++] = fp;
2757c2fbfb3SApril Chin 	} while(cp= *argv++);
276da2e3ebdSchin 	if(!sflag)
277da2e3ebdSchin 	{
2783e14f97fSRoger A. Faulkner 		if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dsiz,dlen,mp) < 0)
279da2e3ebdSchin 			error(ERROR_system(0),"write failed");
280da2e3ebdSchin 		while(--n>=0)
281da2e3ebdSchin 			if((fp=streams[n]) && fp!=sfstdin)
282da2e3ebdSchin 				sfclose(fp);
283da2e3ebdSchin 	}
2843e14f97fSRoger A. Faulkner 	if (mp)
2853e14f97fSRoger A. Faulkner 		free(mp);
286*b30d1939SAndy Fiddaman 	free(delim);
287da2e3ebdSchin 	return(error_info.errors);
288da2e3ebdSchin }
289