1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1992-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                                                                      *
20 ***********************************************************************/
21 #pragma prototyped
22 /*
23  * David Korn
24  * AT&T Bell Laboratories
25  *
26  * paste [-s] [-d delim] [file] ...
27  *
28  * paste lines from files together
29  */
30 
31 static const char usage[] =
32 "[-?\n@(#)$Id: paste (AT&T Research) 2010-06-12 $\n]"
33 USAGE_LICENSE
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 "
45 	"can be specified.]"
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 "
48 	"current offset.]"
49 
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.]"
62 "\n"
63 "\n[file ...]\n"
64 "\n"
65 "[+EXIT STATUS?]{"
66 	"[+0?All files processed successfully.]"
67 	"[+>0?An error occurred.]"
68 "}"
69 "[+SEE ALSO?\bcut\b(1), \bcat\b(1), \bjoin\b(1)]"
70 ;
71 
72 #include <cmd.h>
73 
74 typedef struct Delim_s
75 {
76 	const char*	chr;
77 	size_t		len;
78 } Delim_t;
79 
80 /*
81  * paste the lines of the <nstreams> defined in <streams> and put results
82  * to <out>
83  */
84 
paste(int nstream,Sfio_t * streams[],Sfio_t * out,register const char * delim,int dsiz,int dlen,Delim_t * mp)85 static int paste(int nstream,Sfio_t* streams[],Sfio_t *out, register const char *delim, int dsiz, int dlen, Delim_t* mp)
86 {
87 	register const char *cp;
88 	register int d, n, i, z, more=1;
89 	register Sfio_t *fp;
90 	do
91 	{
92 		d = (dlen>0?0:-1);
93 		for(n=more-1,more=0; n < nstream;)
94 		{
95 			if(fp=streams[n])
96 			{
97 				if(cp = sfgetr(fp,'\n',0))
98 				{
99 					if(n==0)
100 						more = 1;
101 					else if(!more) /* first stream with output */
102 					{
103 						if(dsiz == 1)
104 							sfnputc(out, *delim, n);
105 						else if(dlen>0)
106 						{
107 							for(d=n; d>dlen; d-=dlen)
108 								sfwrite(out,delim,dsiz);
109 							if(d)
110 							{
111 								if(mp)
112 									for (i = z = 0; i < d; i++)
113 										z += mp[i].len;
114 								else
115 									z = d;
116 								sfwrite(out,delim,z);
117 							}
118 						}
119 						more = n+1;
120 					}
121 					if(sfwrite(out,cp,sfvalue(fp)-((n+1)<nstream)) < 0)
122 						return(-1);
123 				}
124 				else
125 					streams[n] = 0;
126 			}
127 			if(++n<nstream && more && d>=0)
128 			{
129 				register int c;
130 				if(d >= dlen)
131 					d = 0;
132 				if(mp)
133 					sfwrite(out,mp[d].chr,mp[d].len);
134 				else if(c=delim[d])
135 					sfputc(out,c);
136 				d++;
137 			}
138 			else if(n==nstream && !streams[n-1] && more)
139 				sfputc(out,'\n');
140 		}
141 	} while(more);
142 	return(0);
143 }
144 
145 /*
146  * Handles paste -s, for file <in> to file <out> using delimiters <delim>
147  */
spaste(Sfio_t * in,register Sfio_t * out,register const char * delim,int dsiz,int dlen,Delim_t * mp)148 static int spaste(Sfio_t *in,register Sfio_t* out,register const char *delim,int dsiz,int dlen,Delim_t* mp)
149 {
150 	register const char *cp;
151 	register int d=0;
152 	if((cp = sfgetr(in,'\n',0)) && sfwrite(out,cp,sfvalue(in)-1) < 0)
153 		return(-1);
154 	while(cp=sfgetr(in, '\n',0))
155 	{
156 		if(dlen)
157 		{
158 			register int c;
159 			if(d >= dlen)
160 				d = 0;
161 			if(mp)
162 				sfwrite(out,mp[d].chr,mp[d].len);
163 			else if(c=delim[d])
164 				sfputc(out,c);
165 			d++;
166 		}
167 		if(sfwrite(out,cp,sfvalue(in)-1) < 0)
168 			return(-1);
169 	}
170 	sfputc(out,'\n');
171 	return(0);
172 }
173 
174 int
b_paste(int argc,char ** argv,Shbltin_t * context)175 b_paste(int argc, char** argv, Shbltin_t* context)
176 {
177 	register int		n, sflag=0;
178 	register Sfio_t		*fp, **streams;
179 	register char 		*cp, *delim;
180 	char			*ep;
181 	Delim_t			*mp;
182 	int			dlen, dsiz;
183 	char			defdelim[2];
184 
185 	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
186 	delim = 0;
187 	for (;;)
188 	{
189 		switch (optget(argv, usage))
190 		{
191 		case 'd':
192 			delim = opt_info.arg;
193 			continue;
194 		case 's':
195 			sflag++;
196 			continue;
197 		case ':':
198 			error(2, "%s", opt_info.arg);
199 			break;
200 		case '?':
201 			error(ERROR_usage(2), "%s", opt_info.arg);
202 			break;
203 		}
204 		break;
205 	}
206 	argv += opt_info.index;
207 	if(error_info.errors)
208 		error(ERROR_usage(2),"%s", optusage(NiL));
209 	if(!delim || !*delim)
210 	{
211 		delim = defdelim;
212 		delim[0] = '\t';
213 		delim[1] = 0;
214 	}
215 	if (!(delim = strdup(delim)))
216 		error(ERROR_system(1), "out of space");
217 	dlen = dsiz = stresc(delim);
218 	mp = 0;
219 	if (mbwide())
220 	{
221 		cp = delim;
222 		ep = delim + dlen;
223 		dlen = 0;
224 		while (cp < ep)
225 		{
226 			mbchar(cp);
227 			dlen++;
228 		}
229 		if(dlen < dsiz)
230 		{
231 			if (!(mp = newof(0, Delim_t, dlen, 0)))
232 			{
233 				free(delim);
234 				error(ERROR_system(1), "out of space");
235 			}
236 			cp = delim;
237 			dlen = 0;
238 			while (cp < ep)
239 			{
240 				mp[dlen].chr = cp;
241 				mbchar(cp);
242 				mp[dlen].len = cp - mp[dlen].chr;
243 				dlen++;
244 			}
245 		}
246 	}
247 	if(cp = *argv)
248 	{
249 		n = argc - opt_info.index;
250 		argv++;
251 	}
252 	else
253 		n = 1;
254 	if(!sflag)
255 	{
256 		if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*))))
257 			error(ERROR_exit(1), "out of space");
258 		n = 0;
259 	}
260 	do
261 	{
262 		if(!cp || streq(cp,"-"))
263 			fp = sfstdin;
264 		else if(!(fp = sfopen(NiL,cp,"r")))
265 			error(ERROR_system(0),"%s: cannot open",cp);
266 		if(fp && sflag)
267 		{
268 			if(spaste(fp,sfstdout,delim,dsiz,dlen,mp) < 0)
269 				error(ERROR_system(0),"write failed");
270 			if(fp!=sfstdin)
271 				sfclose(fp);
272 		}
273 		else if(!sflag)
274 			streams[n++] = fp;
275 	} while(cp= *argv++);
276 	if(!sflag)
277 	{
278 		if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dsiz,dlen,mp) < 0)
279 			error(ERROR_system(0),"write failed");
280 		while(--n>=0)
281 			if((fp=streams[n]) && fp!=sfstdin)
282 				sfclose(fp);
283 	}
284 	if (mp)
285 		free(mp);
286 	free(delim);
287 	return(error_info.errors);
288 }
289