paste.c   [plain text]


/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1992-2007 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                  David Korn <dgk@research.att.com>                   *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * David Korn
 * AT&T Bell Laboratories
 *
 * paste [-s] [-d delim] [file] ...
 *
 * paste lines from files together
 */

static const char usage[] =
"[-?\n@(#)$Id: paste (AT&T Research) 1999-06-22 $\n]"
USAGE_LICENSE
"[+NAME?paste - merge lines of files]"
"[+DESCRIPTION?\bpaste\b concatenates the corresponding lines of a "
	"given input file and writes the resulting lines to standard "
	"output.  By default \bpaste\b replaces the newline character of "
	"every line other than the last input file with the TAB character.]"
"[+?Unless the \b-s\b option is specified, if an end-of-file is encountered "
	"on one or more input files, but not all input files, \bpaste\b "
	"behaves as if empty lines were read from the file(s) on which "
	"end-of-file was detected.]"
"[+?Unless the \b-s\b option is specified, \bpaste\b is limited by "
	"the underlying operating system on how many \afile\a operands "
	"can be specified.]"
"[+?If no \afile\a operands are given or if the \afile\a is \b-\b, \bpaste\b "
	"reads from standard input. The start of the file is defined as the "
	"current offset.]"

"[s:serial?Paste the lines of one file at a time rather than one line "
	"from each file.  In this case if the \b-d\b option is "
	"specified the delimiter will be reset to the first in the "
	"list at the beginning of each file.]"
"[d:delimiters]:[list?\alist\a specifies a list of delimiters.  These "
	"delimiters are used circularly instead of TAB to replace "
	"the newline character of the input lines. Unless the \b-s\b "
	"option is specified, the delimiter will be reset to the first "
	"element of \alist\a each time a line is processed from each file.  "
	"The delimiter characters corresponding to \alist\a will be found "
	"by treating \alist\a as an ANSI-C string, except that the \b\\0\b "
	"sequence will insert the empty string instead of the null character.]"
"\n"
"\n[file ...]\n"
"\n"
"[+EXIT STATUS?]{"
	"[+0?All files processed successfully.]"
	"[+>0?An error occurred.]"
"}"
"[+SEE ALSO?\bcut\b(1), \bcat\b(1), \bjoin\b(1)]"
;


#include <cmd.h>

/*
 * paste the lines of the <nstreams> defined in <streams> and put results
 * to <out>
 */

static int paste(int nstream,Sfio_t* streams[],Sfio_t *out, register const char *delim,int dlen)
{
	register const char *cp;
	register int d, n, more=1;
	register Sfio_t *fp;
	do
	{
		d = (dlen>0?0:-1);
		for(n=more-1,more=0; n < nstream;)
		{
			if(fp=streams[n])
			{
				if(cp = sfgetr(fp,'\n',0))
				{
					if(n==0)
						more = 1;
					else if(!more) /* first stream with output */
					{
						if(dlen==1)
							sfnputc(out, *delim, n);
						else if(dlen>0)
						{
							for(d=n; d>dlen; d-=dlen)
								sfwrite(out,delim,dlen);
							if(d)
								sfwrite(out,delim,d);
						}
						more = n+1;
					}
					if(sfwrite(out,cp,sfvalue(fp)-((n+1)<nstream)) < 0)
						return(-1);
				}
				else
					streams[n] = 0;
			}
			if(++n<nstream && more && d>=0)
			{
				register int c;
				if(d >= dlen)
					d = 0;
				if(c=delim[d++])
					sfputc(out,c);
			}
			else if(n==nstream && !streams[n-1] && more)
				sfputc(out,'\n');
		}
	}
	while(more);
	return(0);
}

/*
 * Handles paste -s, for file <in> to file <out> using delimiters <delim>
 */
static int spaste(Sfio_t *in,register Sfio_t* out,register const char *delim,int dlen)
{
	register const char *cp;
	register int d=0;
	if(cp = sfgetr(in,'\n',0))
	{
		if(sfwrite(out,cp,sfvalue(in)-1) < 0)
			return(-1);
	}
	while(cp=sfgetr(in, '\n',0)) 
	{
		if(dlen)
		{
			register int c;
			if(d >= dlen)
				d = 0;
			if(c=delim[d++])
				sfputc(out,c);
		}
		if(sfwrite(out,cp,sfvalue(in)-1) < 0)
			return(-1);
	}
	sfputc(out,'\n');
	return(0);
}

int
b_paste(int argc,register char *argv[], void* context)
{
	register int		n, sflag=0;
	register Sfio_t		*fp, **streams;
	register char 		*cp, *delim;
	int			dlen;
	char			defdelim[2];

	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
	delim = 0;
	while (n = optget(argv, usage)) switch (n)
	{
	    case 'd':
		delim = opt_info.arg;
		break;
	    case 's':
		sflag++;
		break;
	    case ':':
		error(2, "%s", opt_info.arg);
		break;
	    case '?':
		error(ERROR_usage(2), "%s", opt_info.arg);
		break;
	}
	argv += opt_info.index;
	if(error_info.errors)
		error(ERROR_usage(2),"%s", optusage(NiL));
	if(delim)
		dlen = stresc(delim);
	else
	{
		*(delim = defdelim) = '\t';
		dlen = 1;
	}
	if(cp = *argv)
	{
		n = argc - opt_info.index;
		argv++;
	}
	else
		n = 1;
	if(!sflag)
	{
		if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*))))
			error(ERROR_exit(1), "out of space");
		n = 0;
	}
	do
	{
		if(!cp || streq(cp,"-"))
			fp = sfstdin;
		else if(!(fp = sfopen(NiL,cp,"r")))
		{
			error(ERROR_system(0),"%s: cannot open",cp);
			error_info.errors = 1;
		}
		if(fp && sflag)
		{
			if(spaste(fp,sfstdout,delim,dlen) < 0)
			{
				error(ERROR_system(0),"write failed");
				error_info.errors = 1;
			}
			if(fp!=sfstdin)
				sfclose(fp);
		}
		else
			streams[n++] = fp;
	}
	while(cp= *argv++);
	if(!sflag)
	{
		if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dlen) < 0)
		{
			error(ERROR_system(0),"write failed");
			error_info.errors = 1;
		}
		while(--n>=0)
		{
			if((fp=streams[n]) && fp!=sfstdin)
				sfclose(fp);
		}
	}
	return(error_info.errors);
}