xref: /titanic_51/usr/src/lib/libcmd/common/paste.c (revision a799b1e741b6f59012a469e6b57c40cb8061127b)
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