14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*8462SApril.Chin@Sun.COM *          Copyright (c) 1992-2008 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
7*8462SApril.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*8462SApril.Chin@Sun.COM "[-?\n@(#)$Id: paste (AT&T Research) 2008-04-01 $\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 
724887Schin 
734887Schin #include <cmd.h>
744887Schin 
754887Schin /*
764887Schin  * paste the lines of the <nstreams> defined in <streams> and put results
774887Schin  * to <out>
784887Schin  */
794887Schin 
804887Schin static int paste(int nstream,Sfio_t* streams[],Sfio_t *out, register const char *delim,int dlen)
814887Schin {
824887Schin 	register const char *cp;
834887Schin 	register int d, n, more=1;
844887Schin 	register Sfio_t *fp;
854887Schin 	do
864887Schin 	{
874887Schin 		d = (dlen>0?0:-1);
884887Schin 		for(n=more-1,more=0; n < nstream;)
894887Schin 		{
904887Schin 			if(fp=streams[n])
914887Schin 			{
924887Schin 				if(cp = sfgetr(fp,'\n',0))
934887Schin 				{
944887Schin 					if(n==0)
954887Schin 						more = 1;
964887Schin 					else if(!more) /* first stream with output */
974887Schin 					{
984887Schin 						if(dlen==1)
994887Schin 							sfnputc(out, *delim, n);
1004887Schin 						else if(dlen>0)
1014887Schin 						{
1024887Schin 							for(d=n; d>dlen; d-=dlen)
1034887Schin 								sfwrite(out,delim,dlen);
1044887Schin 							if(d)
1054887Schin 								sfwrite(out,delim,d);
1064887Schin 						}
1074887Schin 						more = n+1;
1084887Schin 					}
1094887Schin 					if(sfwrite(out,cp,sfvalue(fp)-((n+1)<nstream)) < 0)
1104887Schin 						return(-1);
1114887Schin 				}
1124887Schin 				else
1134887Schin 					streams[n] = 0;
1144887Schin 			}
1154887Schin 			if(++n<nstream && more && d>=0)
1164887Schin 			{
1174887Schin 				register int c;
1184887Schin 				if(d >= dlen)
1194887Schin 					d = 0;
1204887Schin 				if(c=delim[d++])
1214887Schin 					sfputc(out,c);
1224887Schin 			}
1234887Schin 			else if(n==nstream && !streams[n-1] && more)
1244887Schin 				sfputc(out,'\n');
1254887Schin 		}
1264887Schin 	}
1274887Schin 	while(more);
1284887Schin 	return(0);
1294887Schin }
1304887Schin 
1314887Schin /*
1324887Schin  * Handles paste -s, for file <in> to file <out> using delimiters <delim>
1334887Schin  */
1344887Schin static int spaste(Sfio_t *in,register Sfio_t* out,register const char *delim,int dlen)
1354887Schin {
1364887Schin 	register const char *cp;
1374887Schin 	register int d=0;
1384887Schin 	if(cp = sfgetr(in,'\n',0))
1394887Schin 	{
1404887Schin 		if(sfwrite(out,cp,sfvalue(in)-1) < 0)
1414887Schin 			return(-1);
1424887Schin 	}
1434887Schin 	while(cp=sfgetr(in, '\n',0))
1444887Schin 	{
1454887Schin 		if(dlen)
1464887Schin 		{
1474887Schin 			register int c;
1484887Schin 			if(d >= dlen)
1494887Schin 				d = 0;
1504887Schin 			if(c=delim[d++])
1514887Schin 				sfputc(out,c);
1524887Schin 		}
1534887Schin 		if(sfwrite(out,cp,sfvalue(in)-1) < 0)
1544887Schin 			return(-1);
1554887Schin 	}
1564887Schin 	sfputc(out,'\n');
1574887Schin 	return(0);
1584887Schin }
1594887Schin 
1604887Schin int
1614887Schin b_paste(int argc,register char *argv[], void* context)
1624887Schin {
1634887Schin 	register int		n, sflag=0;
1644887Schin 	register Sfio_t		*fp, **streams;
1654887Schin 	register char 		*cp, *delim;
1664887Schin 	int			dlen;
1674887Schin 	char			defdelim[2];
1684887Schin 
1694887Schin 	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
1704887Schin 	delim = 0;
1714887Schin 	while (n = optget(argv, usage)) switch (n)
1724887Schin 	{
1734887Schin 	    case 'd':
1744887Schin 		delim = opt_info.arg;
1754887Schin 		break;
1764887Schin 	    case 's':
1774887Schin 		sflag++;
1784887Schin 		break;
1794887Schin 	    case ':':
1804887Schin 		error(2, "%s", opt_info.arg);
1814887Schin 		break;
1824887Schin 	    case '?':
1834887Schin 		error(ERROR_usage(2), "%s", opt_info.arg);
1844887Schin 		break;
1854887Schin 	}
1864887Schin 	argv += opt_info.index;
1874887Schin 	if(error_info.errors)
1884887Schin 		error(ERROR_usage(2),"%s", optusage(NiL));
1894887Schin 	if(delim)
1904887Schin 		dlen = stresc(delim);
1914887Schin 	else
1924887Schin 	{
1934887Schin 		*(delim = defdelim) = '\t';
1944887Schin 		dlen = 1;
1954887Schin 	}
1964887Schin 	if(cp = *argv)
1974887Schin 	{
1984887Schin 		n = argc - opt_info.index;
1994887Schin 		argv++;
2004887Schin 	}
2014887Schin 	else
2024887Schin 		n = 1;
2034887Schin 	if(!sflag)
2044887Schin 	{
2054887Schin 		if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*))))
2064887Schin 			error(ERROR_exit(1), "out of space");
2074887Schin 		n = 0;
2084887Schin 	}
2094887Schin 	do
2104887Schin 	{
2114887Schin 		if(!cp || streq(cp,"-"))
2124887Schin 			fp = sfstdin;
2134887Schin 		else if(!(fp = sfopen(NiL,cp,"r")))
2144887Schin 			error(ERROR_system(0),"%s: cannot open",cp);
2154887Schin 		if(fp && sflag)
2164887Schin 		{
2174887Schin 			if(spaste(fp,sfstdout,delim,dlen) < 0)
2184887Schin 				error(ERROR_system(0),"write failed");
2194887Schin 			if(fp!=sfstdin)
2204887Schin 				sfclose(fp);
2214887Schin 		}
222*8462SApril.Chin@Sun.COM 		else if(!sflag)
2234887Schin 			streams[n++] = fp;
224*8462SApril.Chin@Sun.COM 	} while(cp= *argv++);
2254887Schin 	if(!sflag)
2264887Schin 	{
2274887Schin 		if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dlen) < 0)
2284887Schin 			error(ERROR_system(0),"write failed");
2294887Schin 		while(--n>=0)
2304887Schin 			if((fp=streams[n]) && fp!=sfstdin)
2314887Schin 				sfclose(fp);
2324887Schin 	}
2334887Schin 	return(error_info.errors);
2344887Schin }
2354887Schin 
236