14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1992-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * *
204887Schin ***********************************************************************/
214887Schin #pragma prototyped
224887Schin /*
234887Schin * David Korn
244887Schin * AT&T Bell Laboratories
254887Schin *
264887Schin * paste [-s] [-d delim] [file] ...
274887Schin *
284887Schin * paste lines from files together
294887Schin */
304887Schin
314887Schin static const char usage[] =
32*12068SRoger.Faulkner@Oracle.COM "[-?\n@(#)$Id: paste (AT&T Research) 2009-11-28 $\n]"
334887Schin USAGE_LICENSE
344887Schin "[+NAME?paste - merge lines of files]"
354887Schin "[+DESCRIPTION?\bpaste\b concatenates the corresponding lines of a "
364887Schin "given input file and writes the resulting lines to standard "
374887Schin "output. By default \bpaste\b replaces the newline character of "
384887Schin "every line other than the last input file with the TAB character.]"
394887Schin "[+?Unless the \b-s\b option is specified, if an end-of-file is encountered "
404887Schin "on one or more input files, but not all input files, \bpaste\b "
414887Schin "behaves as if empty lines were read from the file(s) on which "
424887Schin "end-of-file was detected.]"
434887Schin "[+?Unless the \b-s\b option is specified, \bpaste\b is limited by "
444887Schin "the underlying operating system on how many \afile\a operands "
454887Schin "can be specified.]"
464887Schin "[+?If no \afile\a operands are given or if the \afile\a is \b-\b, \bpaste\b "
474887Schin "reads from standard input. The start of the file is defined as the "
484887Schin "current offset.]"
494887Schin
504887Schin "[s:serial?Paste the lines of one file at a time rather than one line "
514887Schin "from each file. In this case if the \b-d\b option is "
524887Schin "specified the delimiter will be reset to the first in the "
534887Schin "list at the beginning of each file.]"
544887Schin "[d:delimiters]:[list?\alist\a specifies a list of delimiters. These "
554887Schin "delimiters are used circularly instead of TAB to replace "
564887Schin "the newline character of the input lines. Unless the \b-s\b "
574887Schin "option is specified, the delimiter will be reset to the first "
584887Schin "element of \alist\a each time a line is processed from each file. "
594887Schin "The delimiter characters corresponding to \alist\a will be found "
604887Schin "by treating \alist\a as an ANSI-C string, except that the \b\\0\b "
614887Schin "sequence will insert the empty string instead of the null character.]"
624887Schin "\n"
634887Schin "\n[file ...]\n"
644887Schin "\n"
654887Schin "[+EXIT STATUS?]{"
664887Schin "[+0?All files processed successfully.]"
674887Schin "[+>0?An error occurred.]"
684887Schin "}"
694887Schin "[+SEE ALSO?\bcut\b(1), \bcat\b(1), \bjoin\b(1)]"
704887Schin ;
714887Schin
72*12068SRoger.Faulkner@Oracle.COM #include <cmd.h>
734887Schin
74*12068SRoger.Faulkner@Oracle.COM typedef struct Delim_s
75*12068SRoger.Faulkner@Oracle.COM {
76*12068SRoger.Faulkner@Oracle.COM const char* chr;
77*12068SRoger.Faulkner@Oracle.COM size_t len;
78*12068SRoger.Faulkner@Oracle.COM } Delim_t;
794887Schin
804887Schin /*
814887Schin * paste the lines of the <nstreams> defined in <streams> and put results
824887Schin * to <out>
834887Schin */
844887Schin
paste(int nstream,Sfio_t * streams[],Sfio_t * out,register const char * delim,int dsiz,int dlen,Delim_t * mp)85*12068SRoger.Faulkner@Oracle.COM static int paste(int nstream,Sfio_t* streams[],Sfio_t *out, register const char *delim, int dsiz, int dlen, Delim_t* mp)
864887Schin {
874887Schin register const char *cp;
88*12068SRoger.Faulkner@Oracle.COM register int d, n, i, z, more=1;
894887Schin register Sfio_t *fp;
904887Schin do
914887Schin {
924887Schin d = (dlen>0?0:-1);
934887Schin for(n=more-1,more=0; n < nstream;)
944887Schin {
954887Schin if(fp=streams[n])
964887Schin {
974887Schin if(cp = sfgetr(fp,'\n',0))
984887Schin {
994887Schin if(n==0)
1004887Schin more = 1;
1014887Schin else if(!more) /* first stream with output */
1024887Schin {
103*12068SRoger.Faulkner@Oracle.COM if(dsiz == 1)
1044887Schin sfnputc(out, *delim, n);
1054887Schin else if(dlen>0)
1064887Schin {
1074887Schin for(d=n; d>dlen; d-=dlen)
108*12068SRoger.Faulkner@Oracle.COM sfwrite(out,delim,dsiz);
1094887Schin if(d)
110*12068SRoger.Faulkner@Oracle.COM {
111*12068SRoger.Faulkner@Oracle.COM if(mp)
112*12068SRoger.Faulkner@Oracle.COM for (i = z = 0; i < d; i++)
113*12068SRoger.Faulkner@Oracle.COM z += mp[i].len;
114*12068SRoger.Faulkner@Oracle.COM else
115*12068SRoger.Faulkner@Oracle.COM z = d;
116*12068SRoger.Faulkner@Oracle.COM sfwrite(out,delim,z);
117*12068SRoger.Faulkner@Oracle.COM }
1184887Schin }
1194887Schin more = n+1;
1204887Schin }
1214887Schin if(sfwrite(out,cp,sfvalue(fp)-((n+1)<nstream)) < 0)
1224887Schin return(-1);
1234887Schin }
1244887Schin else
1254887Schin streams[n] = 0;
1264887Schin }
1274887Schin if(++n<nstream && more && d>=0)
1284887Schin {
1294887Schin register int c;
1304887Schin if(d >= dlen)
1314887Schin d = 0;
132*12068SRoger.Faulkner@Oracle.COM if(mp)
133*12068SRoger.Faulkner@Oracle.COM sfwrite(out,mp[d].chr,mp[d].len);
134*12068SRoger.Faulkner@Oracle.COM else if(c=delim[d])
1354887Schin sfputc(out,c);
136*12068SRoger.Faulkner@Oracle.COM d++;
1374887Schin }
1384887Schin else if(n==nstream && !streams[n-1] && more)
1394887Schin sfputc(out,'\n');
1404887Schin }
141*12068SRoger.Faulkner@Oracle.COM } while(more);
1424887Schin return(0);
1434887Schin }
1444887Schin
1454887Schin /*
1464887Schin * Handles paste -s, for file <in> to file <out> using delimiters <delim>
1474887Schin */
spaste(Sfio_t * in,register Sfio_t * out,register const char * delim,int dsiz,int dlen,Delim_t * mp)148*12068SRoger.Faulkner@Oracle.COM static int spaste(Sfio_t *in,register Sfio_t* out,register const char *delim,int dsiz,int dlen,Delim_t* mp)
1494887Schin {
1504887Schin register const char *cp;
1514887Schin register int d=0;
152*12068SRoger.Faulkner@Oracle.COM if((cp = sfgetr(in,'\n',0)) && sfwrite(out,cp,sfvalue(in)-1) < 0)
153*12068SRoger.Faulkner@Oracle.COM return(-1);
1544887Schin while(cp=sfgetr(in, '\n',0))
1554887Schin {
1564887Schin if(dlen)
1574887Schin {
1584887Schin register int c;
1594887Schin if(d >= dlen)
1604887Schin d = 0;
161*12068SRoger.Faulkner@Oracle.COM if(mp)
162*12068SRoger.Faulkner@Oracle.COM sfwrite(out,mp[d].chr,mp[d].len);
163*12068SRoger.Faulkner@Oracle.COM else if(c=delim[d])
1644887Schin sfputc(out,c);
165*12068SRoger.Faulkner@Oracle.COM d++;
1664887Schin }
1674887Schin if(sfwrite(out,cp,sfvalue(in)-1) < 0)
1684887Schin return(-1);
1694887Schin }
1704887Schin sfputc(out,'\n');
1714887Schin return(0);
1724887Schin }
1734887Schin
1744887Schin int
b_paste(int argc,register char * argv[],void * context)1754887Schin b_paste(int argc,register char *argv[], void* context)
1764887Schin {
1774887Schin register int n, sflag=0;
1784887Schin register Sfio_t *fp, **streams;
1794887Schin register char *cp, *delim;
180*12068SRoger.Faulkner@Oracle.COM char *ep;
181*12068SRoger.Faulkner@Oracle.COM Delim_t *mp;
182*12068SRoger.Faulkner@Oracle.COM int dlen, dsiz;
1834887Schin char defdelim[2];
1844887Schin
1854887Schin cmdinit(argc, argv, context, ERROR_CATALOG, 0);
1864887Schin delim = 0;
1874887Schin while (n = optget(argv, usage)) switch (n)
1884887Schin {
1894887Schin case 'd':
1904887Schin delim = opt_info.arg;
1914887Schin break;
1924887Schin case 's':
1934887Schin sflag++;
1944887Schin break;
1954887Schin case ':':
1964887Schin error(2, "%s", opt_info.arg);
1974887Schin break;
1984887Schin case '?':
1994887Schin error(ERROR_usage(2), "%s", opt_info.arg);
2004887Schin break;
2014887Schin }
2024887Schin argv += opt_info.index;
2034887Schin if(error_info.errors)
2044887Schin error(ERROR_usage(2),"%s", optusage(NiL));
205*12068SRoger.Faulkner@Oracle.COM if(!delim || !*delim)
206*12068SRoger.Faulkner@Oracle.COM {
207*12068SRoger.Faulkner@Oracle.COM delim = defdelim;
208*12068SRoger.Faulkner@Oracle.COM delim[0] = '\t';
209*12068SRoger.Faulkner@Oracle.COM delim[1] = 0;
210*12068SRoger.Faulkner@Oracle.COM }
211*12068SRoger.Faulkner@Oracle.COM dlen = dsiz = stresc(delim);
212*12068SRoger.Faulkner@Oracle.COM mp = 0;
213*12068SRoger.Faulkner@Oracle.COM if (mbwide())
2144887Schin {
215*12068SRoger.Faulkner@Oracle.COM cp = delim;
216*12068SRoger.Faulkner@Oracle.COM ep = delim + dlen;
217*12068SRoger.Faulkner@Oracle.COM dlen = 0;
218*12068SRoger.Faulkner@Oracle.COM while (cp < ep)
219*12068SRoger.Faulkner@Oracle.COM {
220*12068SRoger.Faulkner@Oracle.COM mbchar(cp);
221*12068SRoger.Faulkner@Oracle.COM dlen++;
222*12068SRoger.Faulkner@Oracle.COM }
223*12068SRoger.Faulkner@Oracle.COM if(dlen < dsiz)
224*12068SRoger.Faulkner@Oracle.COM {
225*12068SRoger.Faulkner@Oracle.COM if (!(mp = newof(0, Delim_t, dlen, 0)))
226*12068SRoger.Faulkner@Oracle.COM error(ERROR_system(1), "out of space");
227*12068SRoger.Faulkner@Oracle.COM cp = delim;
228*12068SRoger.Faulkner@Oracle.COM dlen = 0;
229*12068SRoger.Faulkner@Oracle.COM while (cp < ep)
230*12068SRoger.Faulkner@Oracle.COM {
231*12068SRoger.Faulkner@Oracle.COM mp[dlen].chr = cp;
232*12068SRoger.Faulkner@Oracle.COM mbchar(cp);
233*12068SRoger.Faulkner@Oracle.COM mp[dlen].len = cp - mp[dlen].chr;
234*12068SRoger.Faulkner@Oracle.COM dlen++;
235*12068SRoger.Faulkner@Oracle.COM }
236*12068SRoger.Faulkner@Oracle.COM }
2374887Schin }
2384887Schin if(cp = *argv)
2394887Schin {
2404887Schin n = argc - opt_info.index;
2414887Schin argv++;
2424887Schin }
2434887Schin else
2444887Schin n = 1;
2454887Schin if(!sflag)
2464887Schin {
2474887Schin if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*))))
2484887Schin error(ERROR_exit(1), "out of space");
2494887Schin n = 0;
2504887Schin }
2514887Schin do
2524887Schin {
2534887Schin if(!cp || streq(cp,"-"))
2544887Schin fp = sfstdin;
2554887Schin else if(!(fp = sfopen(NiL,cp,"r")))
2564887Schin error(ERROR_system(0),"%s: cannot open",cp);
2574887Schin if(fp && sflag)
2584887Schin {
259*12068SRoger.Faulkner@Oracle.COM if(spaste(fp,sfstdout,delim,dsiz,dlen,mp) < 0)
2604887Schin error(ERROR_system(0),"write failed");
2614887Schin if(fp!=sfstdin)
2624887Schin sfclose(fp);
2634887Schin }
2648462SApril.Chin@Sun.COM else if(!sflag)
2654887Schin streams[n++] = fp;
2668462SApril.Chin@Sun.COM } while(cp= *argv++);
2674887Schin if(!sflag)
2684887Schin {
269*12068SRoger.Faulkner@Oracle.COM if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dsiz,dlen,mp) < 0)
2704887Schin error(ERROR_system(0),"write failed");
2714887Schin while(--n>=0)
2724887Schin if((fp=streams[n]) && fp!=sfstdin)
2734887Schin sfclose(fp);
2744887Schin }
275*12068SRoger.Faulkner@Oracle.COM if (mp)
276*12068SRoger.Faulkner@Oracle.COM free(mp);
2774887Schin return(error_info.errors);
2784887Schin }
279