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