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