xref: /titanic_44/usr/src/lib/libcmd/common/cat.c (revision da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968)
1*da2e3ebdSchin /***********************************************************************
2*da2e3ebdSchin *                                                                      *
3*da2e3ebdSchin *               This software is part of the ast package               *
4*da2e3ebdSchin *           Copyright (c) 1992-2007 AT&T Knowledge Ventures            *
5*da2e3ebdSchin *                      and is licensed under the                       *
6*da2e3ebdSchin *                  Common Public License, Version 1.0                  *
7*da2e3ebdSchin *                      by AT&T Knowledge Ventures                      *
8*da2e3ebdSchin *                                                                      *
9*da2e3ebdSchin *                A copy of the License is available at                 *
10*da2e3ebdSchin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*da2e3ebdSchin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*da2e3ebdSchin *                                                                      *
13*da2e3ebdSchin *              Information and Software Systems Research               *
14*da2e3ebdSchin *                            AT&T Research                             *
15*da2e3ebdSchin *                           Florham Park NJ                            *
16*da2e3ebdSchin *                                                                      *
17*da2e3ebdSchin *                 Glenn Fowler <gsf@research.att.com>                  *
18*da2e3ebdSchin *                  David Korn <dgk@research.att.com>                   *
19*da2e3ebdSchin *                                                                      *
20*da2e3ebdSchin ***********************************************************************/
21*da2e3ebdSchin #pragma prototyped
22*da2e3ebdSchin /*
23*da2e3ebdSchin  * David Korn
24*da2e3ebdSchin  * Glenn Fowler
25*da2e3ebdSchin  * AT&T Bell Laboratories
26*da2e3ebdSchin  *
27*da2e3ebdSchin  * cat
28*da2e3ebdSchin  */
29*da2e3ebdSchin 
30*da2e3ebdSchin #include <cmd.h>
31*da2e3ebdSchin #include <fcntl.h>
32*da2e3ebdSchin 
33*da2e3ebdSchin static const char usage[] =
34*da2e3ebdSchin "[-?\n@(#)$Id: cat (AT&T Research) 2006-05-17 $\n]"
35*da2e3ebdSchin USAGE_LICENSE
36*da2e3ebdSchin "[+NAME?cat - concatenate files]"
37*da2e3ebdSchin "[+DESCRIPTION?\bcat\b copies each \afile\a in sequence to the standard"
38*da2e3ebdSchin "	output. If no \afile\a is given, or if the \afile\a is \b-\b,"
39*da2e3ebdSchin "	\bcat\b copies from standard input starting at the current location.]"
40*da2e3ebdSchin 
41*da2e3ebdSchin "[b:number-nonblank?Number lines as with \b-n\b but omit line numbers from"
42*da2e3ebdSchin "	blank lines.]"
43*da2e3ebdSchin "[d:dos-input?Input files are opened in \atext\amode which removes carriage"
44*da2e3ebdSchin "	returns in front of new-lines on some systems.]"
45*da2e3ebdSchin "[e?Equivalent to \b-vE\b.]"
46*da2e3ebdSchin "[n:number?Causes a line number to be inserted at the beginning of each line.]"
47*da2e3ebdSchin "[s?Equivalent to \b-S\b for \aatt\a universe and \b-B\b otherwise.]"
48*da2e3ebdSchin "[t?Equivalent to \b-vT\b.]"
49*da2e3ebdSchin "[u:unbuffer?The output is not delayed by buffering.]"
50*da2e3ebdSchin "[v:show-nonprinting?Causes non-printing characters (whith the exception of"
51*da2e3ebdSchin "	tabs, new-lines, and form-feeds) to be output as printable charater"
52*da2e3ebdSchin "	sequences. ASCII control characters are printed as \b^\b\an\a,"
53*da2e3ebdSchin "	where \an\a is the corresponding ASCII character in the range"
54*da2e3ebdSchin "	octal 100-137. The DEL character (octal 0177) is copied"
55*da2e3ebdSchin "	as \b^?\b. Other non-printable characters are copied as \bM-\b\ax\a"
56*da2e3ebdSchin "	where \ax\a is the ASCII character specified by the low-order seven"
57*da2e3ebdSchin "	bits.  Multibyte characters in the current locale are treated as"
58*da2e3ebdSchin "	printable characters.]"
59*da2e3ebdSchin "[A:show-all?Equivalent to \b-vET\b.]"
60*da2e3ebdSchin "[B:squeeze-blank?Multiple adjacent new-line characters are replace by one"
61*da2e3ebdSchin "	new-line.]"
62*da2e3ebdSchin "[D:dos-output?Output files are opened in \atext\amode which inserts carriage"
63*da2e3ebdSchin "	returns in front of new-lines on some systems.]"
64*da2e3ebdSchin "[E:show-ends?Causes a \b$\b to be inserted before each new-line.]"
65*da2e3ebdSchin "[S:silent?\bcat\b is silent about non-existent files.]"
66*da2e3ebdSchin "[T:show-blank?Causes tabs to be copied as \b^I\b and formfeeds as \b^L\b.]"
67*da2e3ebdSchin 
68*da2e3ebdSchin "\n"
69*da2e3ebdSchin "\n[file ...]\n"
70*da2e3ebdSchin "\n"
71*da2e3ebdSchin 
72*da2e3ebdSchin "[+SEE ALSO?\bcp\b(1), \bgetconf\b(1), \bpr\b(1)]"
73*da2e3ebdSchin ;
74*da2e3ebdSchin 
75*da2e3ebdSchin #define RUBOUT	0177
76*da2e3ebdSchin 
77*da2e3ebdSchin /* control flags */
78*da2e3ebdSchin #define B_FLAG		(1<<0)
79*da2e3ebdSchin #define E_FLAG		(1<<1)
80*da2e3ebdSchin #define F_FLAG		(1<<2)
81*da2e3ebdSchin #define N_FLAG		(1<<3)
82*da2e3ebdSchin #define S_FLAG		(1<<4)
83*da2e3ebdSchin #define T_FLAG		(1<<5)
84*da2e3ebdSchin #define U_FLAG		(1<<6)
85*da2e3ebdSchin #define V_FLAG		(1<<7)
86*da2e3ebdSchin #define D_FLAG		(1<<8)
87*da2e3ebdSchin #define d_FLAG		(1<<9)
88*da2e3ebdSchin 
89*da2e3ebdSchin /* character types */
90*da2e3ebdSchin #define T_ENDBUF	1
91*da2e3ebdSchin #define T_CONTROL	2
92*da2e3ebdSchin #define T_NEWLINE	3
93*da2e3ebdSchin #define T_EIGHTBIT	4
94*da2e3ebdSchin #define T_CNTL8BIT	5
95*da2e3ebdSchin 
96*da2e3ebdSchin #define printof(c)	((c)^0100)
97*da2e3ebdSchin 
98*da2e3ebdSchin /*
99*da2e3ebdSchin  * called for any special output processing
100*da2e3ebdSchin  */
101*da2e3ebdSchin 
102*da2e3ebdSchin static int
103*da2e3ebdSchin vcat(register char* states, Sfio_t *fdin, Sfio_t *fdout, int flags)
104*da2e3ebdSchin {
105*da2e3ebdSchin 	register unsigned char*	cp;
106*da2e3ebdSchin 	register unsigned char*	cpold;
107*da2e3ebdSchin 	register int		n;
108*da2e3ebdSchin 	register int		m;
109*da2e3ebdSchin 	register int		line = 1;
110*da2e3ebdSchin 	register unsigned char*	endbuff;
111*da2e3ebdSchin 	unsigned char*		inbuff;
112*da2e3ebdSchin 	int			printdefer = (flags&(B_FLAG|N_FLAG));
113*da2e3ebdSchin 	int			lastchar;
114*da2e3ebdSchin 
115*da2e3ebdSchin 	unsigned char		meta[4];
116*da2e3ebdSchin 
117*da2e3ebdSchin 	meta[0] = 'M';
118*da2e3ebdSchin 	meta[1] = '-';
119*da2e3ebdSchin 	for (;;)
120*da2e3ebdSchin 	{
121*da2e3ebdSchin 		/* read in a buffer full */
122*da2e3ebdSchin 		if (!(inbuff = (unsigned char*)sfreserve(fdin, SF_UNBOUND, 0)))
123*da2e3ebdSchin 			return sfvalue(fdin) ? -1 : 0;
124*da2e3ebdSchin 		if ((n = sfvalue(fdin)) <= 0)
125*da2e3ebdSchin 			return n;
126*da2e3ebdSchin 		cp = inbuff;
127*da2e3ebdSchin 		lastchar = *(endbuff = cp + --n);
128*da2e3ebdSchin 		*endbuff = 0;
129*da2e3ebdSchin 		if (printdefer)
130*da2e3ebdSchin 		{
131*da2e3ebdSchin 			if (states[*cp]!=T_NEWLINE || !(flags&B_FLAG))
132*da2e3ebdSchin 				sfprintf(fdout,"%6d\t",line);
133*da2e3ebdSchin 			printdefer = 0;
134*da2e3ebdSchin 		}
135*da2e3ebdSchin 		while (endbuff)
136*da2e3ebdSchin 		{
137*da2e3ebdSchin 			cpold = cp;
138*da2e3ebdSchin 			/* skip over printable characters */
139*da2e3ebdSchin 			if (mbwide())
140*da2e3ebdSchin 				while ((n = (m = mbsize(cp)) < 2 ? states[*cp++] : (cp += m, states['a'])) == 0);
141*da2e3ebdSchin 			else
142*da2e3ebdSchin 				while ((n = states[*cp++]) == 0);
143*da2e3ebdSchin 			if (n==T_ENDBUF)
144*da2e3ebdSchin 			{
145*da2e3ebdSchin 				if (cp>endbuff)
146*da2e3ebdSchin 				{
147*da2e3ebdSchin 					if (!(n = states[lastchar]))
148*da2e3ebdSchin 					{
149*da2e3ebdSchin 						*endbuff = lastchar;
150*da2e3ebdSchin 						cp++;
151*da2e3ebdSchin 					}
152*da2e3ebdSchin 					else
153*da2e3ebdSchin 					{
154*da2e3ebdSchin 						if (--cp > cpold)
155*da2e3ebdSchin 							sfwrite(fdout,(char*)cpold,cp-cpold);
156*da2e3ebdSchin 						if (endbuff==inbuff)
157*da2e3ebdSchin 							*++endbuff = 0;
158*da2e3ebdSchin 						cp = cpold = endbuff;
159*da2e3ebdSchin 						cp[-1] = lastchar;
160*da2e3ebdSchin 						if (n==T_ENDBUF)
161*da2e3ebdSchin 							n = T_CONTROL;
162*da2e3ebdSchin 
163*da2e3ebdSchin 					}
164*da2e3ebdSchin 					endbuff = 0;
165*da2e3ebdSchin 				}
166*da2e3ebdSchin 				else n = T_CONTROL;
167*da2e3ebdSchin 			}
168*da2e3ebdSchin 			if (--cp>cpold)
169*da2e3ebdSchin 				sfwrite(fdout,(char*)cpold,cp-cpold);
170*da2e3ebdSchin 			switch(n)
171*da2e3ebdSchin 			{
172*da2e3ebdSchin 				case T_CNTL8BIT:
173*da2e3ebdSchin 					meta[2] = '^';
174*da2e3ebdSchin 					do
175*da2e3ebdSchin 					{
176*da2e3ebdSchin 						n = (*cp++)&~0200;
177*da2e3ebdSchin 						meta[3] = printof(n);
178*da2e3ebdSchin 						sfwrite(fdout,(char*)meta,4);
179*da2e3ebdSchin 					}
180*da2e3ebdSchin 					while ((n=states[*cp])==T_CNTL8BIT);
181*da2e3ebdSchin 					break;
182*da2e3ebdSchin 				case T_EIGHTBIT:
183*da2e3ebdSchin 					do
184*da2e3ebdSchin 					{
185*da2e3ebdSchin 						meta[2] = (*cp++)&~0200;
186*da2e3ebdSchin 						sfwrite(fdout,(char*)meta,3);
187*da2e3ebdSchin 					}
188*da2e3ebdSchin 					while ((n=states[*cp])==T_EIGHTBIT);
189*da2e3ebdSchin 					break;
190*da2e3ebdSchin 				case T_CONTROL:
191*da2e3ebdSchin 					do
192*da2e3ebdSchin 					{
193*da2e3ebdSchin 						n = *cp++;
194*da2e3ebdSchin 						sfputc(fdout,'^');
195*da2e3ebdSchin 						sfputc(fdout,printof(n));
196*da2e3ebdSchin 					}
197*da2e3ebdSchin 					while ((n=states[*cp])==T_CONTROL);
198*da2e3ebdSchin 					break;
199*da2e3ebdSchin 				case T_NEWLINE:
200*da2e3ebdSchin 					if (flags&S_FLAG)
201*da2e3ebdSchin 					{
202*da2e3ebdSchin 						while (states[*++cp]==T_NEWLINE)
203*da2e3ebdSchin 							line++;
204*da2e3ebdSchin 						cp--;
205*da2e3ebdSchin 					}
206*da2e3ebdSchin 					do
207*da2e3ebdSchin 					{
208*da2e3ebdSchin 						cp++;
209*da2e3ebdSchin 						if (flags&E_FLAG)
210*da2e3ebdSchin 							sfputc(fdout,'$');
211*da2e3ebdSchin 						sfputc(fdout,'\n');
212*da2e3ebdSchin 						if (!(flags&(N_FLAG|B_FLAG)))
213*da2e3ebdSchin 							continue;
214*da2e3ebdSchin 						line++;
215*da2e3ebdSchin 						if (cp < endbuff)
216*da2e3ebdSchin 							sfprintf(fdout,"%6d\t",line);
217*da2e3ebdSchin 						else printdefer = 1;
218*da2e3ebdSchin 					}
219*da2e3ebdSchin 					while (states[*cp]==T_NEWLINE);
220*da2e3ebdSchin 					break;
221*da2e3ebdSchin 			}
222*da2e3ebdSchin 		}
223*da2e3ebdSchin 	}
224*da2e3ebdSchin }
225*da2e3ebdSchin 
226*da2e3ebdSchin int
227*da2e3ebdSchin b_cat(int argc, char** argv, void* context)
228*da2e3ebdSchin {
229*da2e3ebdSchin 	register int		n;
230*da2e3ebdSchin 	register int		flags = 0;
231*da2e3ebdSchin 	register char*		cp;
232*da2e3ebdSchin 	register Sfio_t*	fp;
233*da2e3ebdSchin 	char*			mode;
234*da2e3ebdSchin 	int			att;
235*da2e3ebdSchin 	int			dovcat=0;
236*da2e3ebdSchin 	char			states[UCHAR_MAX+1];
237*da2e3ebdSchin 
238*da2e3ebdSchin 	NoP(argc);
239*da2e3ebdSchin 	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
240*da2e3ebdSchin 	att = !strcmp(astconf("UNIVERSE", NiL, NiL), "att");
241*da2e3ebdSchin 	mode = "r";
242*da2e3ebdSchin 	for (;;)
243*da2e3ebdSchin 	{
244*da2e3ebdSchin 		switch (optget(argv, usage))
245*da2e3ebdSchin 		{
246*da2e3ebdSchin 		case 'A':
247*da2e3ebdSchin 			flags |= T_FLAG|E_FLAG|V_FLAG;
248*da2e3ebdSchin 			continue;
249*da2e3ebdSchin 		case 'B':
250*da2e3ebdSchin 			flags |= S_FLAG;
251*da2e3ebdSchin 			continue;
252*da2e3ebdSchin 		case 'b':
253*da2e3ebdSchin 			flags |= B_FLAG;
254*da2e3ebdSchin 			continue;
255*da2e3ebdSchin 		case 'E':
256*da2e3ebdSchin 			flags |= E_FLAG;
257*da2e3ebdSchin 			continue;
258*da2e3ebdSchin 		case 'e':
259*da2e3ebdSchin 			flags |= E_FLAG|V_FLAG;
260*da2e3ebdSchin 			continue;
261*da2e3ebdSchin 		case 'n':
262*da2e3ebdSchin 			flags |= N_FLAG;
263*da2e3ebdSchin 			continue;
264*da2e3ebdSchin 		case 's':
265*da2e3ebdSchin 			flags |= att ? F_FLAG : S_FLAG;
266*da2e3ebdSchin 			continue;
267*da2e3ebdSchin 		case 'S':
268*da2e3ebdSchin 			flags |= F_FLAG;
269*da2e3ebdSchin 			continue;
270*da2e3ebdSchin 		case 'T':
271*da2e3ebdSchin 			flags |= T_FLAG;
272*da2e3ebdSchin 			continue;
273*da2e3ebdSchin 		case 't':
274*da2e3ebdSchin 			flags |= T_FLAG|V_FLAG;
275*da2e3ebdSchin 			continue;
276*da2e3ebdSchin 		case 'u':
277*da2e3ebdSchin 			flags |= U_FLAG;
278*da2e3ebdSchin 			continue;
279*da2e3ebdSchin 		case 'v':
280*da2e3ebdSchin 			flags |= V_FLAG;
281*da2e3ebdSchin 			continue;
282*da2e3ebdSchin 		case 'd':
283*da2e3ebdSchin 			mode = "rt";
284*da2e3ebdSchin 			continue;
285*da2e3ebdSchin 		case 'D':
286*da2e3ebdSchin 			flags |= d_FLAG;
287*da2e3ebdSchin 			continue;
288*da2e3ebdSchin 		case ':':
289*da2e3ebdSchin 			error(2, "%s", opt_info.arg);
290*da2e3ebdSchin 			break;
291*da2e3ebdSchin 		case '?':
292*da2e3ebdSchin 			error(ERROR_usage(2), "%s", opt_info.arg);
293*da2e3ebdSchin 			break;
294*da2e3ebdSchin 		}
295*da2e3ebdSchin 		break;
296*da2e3ebdSchin 	}
297*da2e3ebdSchin 	argv += opt_info.index;
298*da2e3ebdSchin 	if (error_info.errors)
299*da2e3ebdSchin 		error(ERROR_usage(2), "%s", optusage(NiL));
300*da2e3ebdSchin 	memset(states, 0, sizeof(states));
301*da2e3ebdSchin 	if (flags&V_FLAG)
302*da2e3ebdSchin 	{
303*da2e3ebdSchin 		memset(states, T_CONTROL, ' ');
304*da2e3ebdSchin 		states[RUBOUT] = T_CONTROL;
305*da2e3ebdSchin 		memset(states+0200, T_EIGHTBIT, 0200);
306*da2e3ebdSchin 		memset(states+0200, T_CNTL8BIT, ' ');
307*da2e3ebdSchin 		states[RUBOUT|0200] = T_CNTL8BIT;
308*da2e3ebdSchin 		states['\n'] = 0;
309*da2e3ebdSchin 	}
310*da2e3ebdSchin 	if (flags&T_FLAG)
311*da2e3ebdSchin 		states['\t'] = T_CONTROL;
312*da2e3ebdSchin 	states[0] = T_ENDBUF;
313*da2e3ebdSchin 	if (att)
314*da2e3ebdSchin 	{
315*da2e3ebdSchin 		if (flags&V_FLAG)
316*da2e3ebdSchin 		{
317*da2e3ebdSchin 			states['\n'|0200] = T_EIGHTBIT;
318*da2e3ebdSchin 			if (!(flags&T_FLAG))
319*da2e3ebdSchin 			{
320*da2e3ebdSchin 				states['\t'] = states['\f'] = 0;
321*da2e3ebdSchin 				states['\t'|0200] = states['\f'|0200] = T_EIGHTBIT;
322*da2e3ebdSchin 			}
323*da2e3ebdSchin 		}
324*da2e3ebdSchin 	}
325*da2e3ebdSchin 	else if (flags)
326*da2e3ebdSchin 	{
327*da2e3ebdSchin 		if (!(flags&T_FLAG))
328*da2e3ebdSchin 			states['\t'] = 0;
329*da2e3ebdSchin 	}
330*da2e3ebdSchin 	if (flags&(V_FLAG|T_FLAG|N_FLAG|E_FLAG|B_FLAG))
331*da2e3ebdSchin 	{
332*da2e3ebdSchin 		states['\n'] = T_NEWLINE;
333*da2e3ebdSchin 		dovcat = 1;
334*da2e3ebdSchin 	}
335*da2e3ebdSchin 	if (flags&B_FLAG)
336*da2e3ebdSchin 		flags |= S_FLAG;
337*da2e3ebdSchin 	if (flags&d_FLAG)
338*da2e3ebdSchin 		sfopen(sfstdout, NiL, "wt");
339*da2e3ebdSchin 	if (cp = *argv)
340*da2e3ebdSchin 		argv++;
341*da2e3ebdSchin 	do
342*da2e3ebdSchin 	{
343*da2e3ebdSchin 		if (!cp || streq(cp,"-"))
344*da2e3ebdSchin 		{
345*da2e3ebdSchin 			fp = sfstdin;
346*da2e3ebdSchin 			if (flags&D_FLAG)
347*da2e3ebdSchin 				sfopen(fp, NiL, mode);
348*da2e3ebdSchin 		}
349*da2e3ebdSchin 		else if (!(fp = sfopen(NiL, cp, mode)))
350*da2e3ebdSchin 		{
351*da2e3ebdSchin 			if (!(flags&F_FLAG))
352*da2e3ebdSchin 				error(ERROR_system(0), "%s: cannot open", cp);
353*da2e3ebdSchin 			error_info.errors = 1;
354*da2e3ebdSchin 			continue;
355*da2e3ebdSchin 		}
356*da2e3ebdSchin 		if (flags&U_FLAG)
357*da2e3ebdSchin 			sfsetbuf(fp, (void*)fp, -1);
358*da2e3ebdSchin 		if (dovcat)
359*da2e3ebdSchin 			n = vcat(states, fp, sfstdout, flags);
360*da2e3ebdSchin 		else if (sfmove(fp, sfstdout, SF_UNBOUND, -1) >= 0 && sfeof(fp))
361*da2e3ebdSchin 			n = 0;
362*da2e3ebdSchin 		else
363*da2e3ebdSchin 			n = -1;
364*da2e3ebdSchin 		if (fp != sfstdin)
365*da2e3ebdSchin 			sfclose(fp);
366*da2e3ebdSchin 		if (n < 0 && errno != EPIPE)
367*da2e3ebdSchin 		{
368*da2e3ebdSchin 			if (cp)
369*da2e3ebdSchin 				error(ERROR_system(0), "%s: read error", cp);
370*da2e3ebdSchin 			else
371*da2e3ebdSchin 				error(ERROR_system(0), "read error");
372*da2e3ebdSchin 		}
373*da2e3ebdSchin 		if (sferror(sfstdout))
374*da2e3ebdSchin 			break;
375*da2e3ebdSchin 	} while (cp = *argv++);
376*da2e3ebdSchin 	if (sfsync(sfstdout))
377*da2e3ebdSchin 		error(ERROR_system(0), "write error");
378*da2e3ebdSchin 	if (flags&d_FLAG)
379*da2e3ebdSchin 		sfopen(sfstdout, NiL, "w");
380*da2e3ebdSchin 	return error_info.errors;
381*da2e3ebdSchin }
382