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