xref: /onnv-gate/usr/src/lib/libcmd/common/paste.c (revision 12068:08a39a083754)
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