xref: /titanic_50/usr/src/lib/libcmd/common/paste.c (revision 2ca5b6595b95478e6568b0e77c6c83c8a870867a)
1  /***********************************************************************
2  *                                                                      *
3  *               This software is part of the ast package               *
4  *          Copyright (c) 1992-2008 AT&T Intellectual Property          *
5  *                      and is licensed under the                       *
6  *                  Common Public License, Version 1.0                  *
7  *                    by AT&T Intellectual Property                     *
8  *                                                                      *
9  *                A copy of the License is available at                 *
10  *            http://www.opensource.org/licenses/cpl1.0.txt             *
11  *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
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) 2008-04-01 $\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  
73  #include <cmd.h>
74  
75  /*
76   * paste the lines of the <nstreams> defined in <streams> and put results
77   * to <out>
78   */
79  
80  static int paste(int nstream,Sfio_t* streams[],Sfio_t *out, register const char *delim,int dlen)
81  {
82  	register const char *cp;
83  	register int d, n, more=1;
84  	register Sfio_t *fp;
85  	do
86  	{
87  		d = (dlen>0?0:-1);
88  		for(n=more-1,more=0; n < nstream;)
89  		{
90  			if(fp=streams[n])
91  			{
92  				if(cp = sfgetr(fp,'\n',0))
93  				{
94  					if(n==0)
95  						more = 1;
96  					else if(!more) /* first stream with output */
97  					{
98  						if(dlen==1)
99  							sfnputc(out, *delim, n);
100  						else if(dlen>0)
101  						{
102  							for(d=n; d>dlen; d-=dlen)
103  								sfwrite(out,delim,dlen);
104  							if(d)
105  								sfwrite(out,delim,d);
106  						}
107  						more = n+1;
108  					}
109  					if(sfwrite(out,cp,sfvalue(fp)-((n+1)<nstream)) < 0)
110  						return(-1);
111  				}
112  				else
113  					streams[n] = 0;
114  			}
115  			if(++n<nstream && more && d>=0)
116  			{
117  				register int c;
118  				if(d >= dlen)
119  					d = 0;
120  				if(c=delim[d++])
121  					sfputc(out,c);
122  			}
123  			else if(n==nstream && !streams[n-1] && more)
124  				sfputc(out,'\n');
125  		}
126  	}
127  	while(more);
128  	return(0);
129  }
130  
131  /*
132   * Handles paste -s, for file <in> to file <out> using delimiters <delim>
133   */
134  static int spaste(Sfio_t *in,register Sfio_t* out,register const char *delim,int dlen)
135  {
136  	register const char *cp;
137  	register int d=0;
138  	if(cp = sfgetr(in,'\n',0))
139  	{
140  		if(sfwrite(out,cp,sfvalue(in)-1) < 0)
141  			return(-1);
142  	}
143  	while(cp=sfgetr(in, '\n',0))
144  	{
145  		if(dlen)
146  		{
147  			register int c;
148  			if(d >= dlen)
149  				d = 0;
150  			if(c=delim[d++])
151  				sfputc(out,c);
152  		}
153  		if(sfwrite(out,cp,sfvalue(in)-1) < 0)
154  			return(-1);
155  	}
156  	sfputc(out,'\n');
157  	return(0);
158  }
159  
160  int
161  b_paste(int argc,register char *argv[], void* context)
162  {
163  	register int		n, sflag=0;
164  	register Sfio_t		*fp, **streams;
165  	register char 		*cp, *delim;
166  	int			dlen;
167  	char			defdelim[2];
168  
169  	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
170  	delim = 0;
171  	while (n = optget(argv, usage)) switch (n)
172  	{
173  	    case 'd':
174  		delim = opt_info.arg;
175  		break;
176  	    case 's':
177  		sflag++;
178  		break;
179  	    case ':':
180  		error(2, "%s", opt_info.arg);
181  		break;
182  	    case '?':
183  		error(ERROR_usage(2), "%s", opt_info.arg);
184  		break;
185  	}
186  	argv += opt_info.index;
187  	if(error_info.errors)
188  		error(ERROR_usage(2),"%s", optusage(NiL));
189  	if(delim)
190  		dlen = stresc(delim);
191  	else
192  	{
193  		*(delim = defdelim) = '\t';
194  		dlen = 1;
195  	}
196  	if(cp = *argv)
197  	{
198  		n = argc - opt_info.index;
199  		argv++;
200  	}
201  	else
202  		n = 1;
203  	if(!sflag)
204  	{
205  		if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*))))
206  			error(ERROR_exit(1), "out of space");
207  		n = 0;
208  	}
209  	do
210  	{
211  		if(!cp || streq(cp,"-"))
212  			fp = sfstdin;
213  		else if(!(fp = sfopen(NiL,cp,"r")))
214  			error(ERROR_system(0),"%s: cannot open",cp);
215  		if(fp && sflag)
216  		{
217  			if(spaste(fp,sfstdout,delim,dlen) < 0)
218  				error(ERROR_system(0),"write failed");
219  			if(fp!=sfstdin)
220  				sfclose(fp);
221  		}
222  		else if(!sflag)
223  			streams[n++] = fp;
224  	} while(cp= *argv++);
225  	if(!sflag)
226  	{
227  		if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dlen) < 0)
228  			error(ERROR_system(0),"write failed");
229  		while(--n>=0)
230  			if((fp=streams[n]) && fp!=sfstdin)
231  				sfclose(fp);
232  	}
233  	return(error_info.errors);
234  }
235  
236