xref: /onnv-gate/usr/src/lib/libcmd/common/cat.c (revision 8462)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*8462SApril.Chin@Sun.COM *          Copyright (c) 1992-2008 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
7*8462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
184887Schin *                  David Korn <dgk@research.att.com>                   *
194887Schin *                                                                      *
204887Schin ***********************************************************************/
214887Schin #pragma prototyped
224887Schin /*
234887Schin  * David Korn
244887Schin  * Glenn Fowler
254887Schin  * AT&T Bell Laboratories
264887Schin  *
274887Schin  * cat
284887Schin  */
294887Schin 
304887Schin #include <cmd.h>
314887Schin #include <fcntl.h>
324887Schin 
334887Schin static const char usage[] =
34*8462SApril.Chin@Sun.COM "[-?\n@(#)$Id: cat (AT&T Research) 2007-07-17 $\n]"
354887Schin USAGE_LICENSE
364887Schin "[+NAME?cat - concatenate files]"
374887Schin "[+DESCRIPTION?\bcat\b copies each \afile\a in sequence to the standard"
384887Schin "	output. If no \afile\a is given, or if the \afile\a is \b-\b,"
394887Schin "	\bcat\b copies from standard input starting at the current location.]"
404887Schin 
414887Schin "[b:number-nonblank?Number lines as with \b-n\b but omit line numbers from"
424887Schin "	blank lines.]"
434887Schin "[d:dos-input?Input files are opened in \atext\amode which removes carriage"
444887Schin "	returns in front of new-lines on some systems.]"
454887Schin "[e?Equivalent to \b-vE\b.]"
464887Schin "[n:number?Causes a line number to be inserted at the beginning of each line.]"
474887Schin "[s?Equivalent to \b-S\b for \aatt\a universe and \b-B\b otherwise.]"
484887Schin "[t?Equivalent to \b-vT\b.]"
494887Schin "[u:unbuffer?The output is not delayed by buffering.]"
504887Schin "[v:show-nonprinting?Causes non-printing characters (whith the exception of"
514887Schin "	tabs, new-lines, and form-feeds) to be output as printable charater"
524887Schin "	sequences. ASCII control characters are printed as \b^\b\an\a,"
534887Schin "	where \an\a is the corresponding ASCII character in the range"
544887Schin "	octal 100-137. The DEL character (octal 0177) is copied"
554887Schin "	as \b^?\b. Other non-printable characters are copied as \bM-\b\ax\a"
564887Schin "	where \ax\a is the ASCII character specified by the low-order seven"
574887Schin "	bits.  Multibyte characters in the current locale are treated as"
584887Schin "	printable characters.]"
594887Schin "[A:show-all?Equivalent to \b-vET\b.]"
604887Schin "[B:squeeze-blank?Multiple adjacent new-line characters are replace by one"
614887Schin "	new-line.]"
624887Schin "[D:dos-output?Output files are opened in \atext\amode which inserts carriage"
634887Schin "	returns in front of new-lines on some systems.]"
644887Schin "[E:show-ends?Causes a \b$\b to be inserted before each new-line.]"
654887Schin "[S:silent?\bcat\b is silent about non-existent files.]"
664887Schin "[T:show-blank?Causes tabs to be copied as \b^I\b and formfeeds as \b^L\b.]"
674887Schin 
684887Schin "\n"
694887Schin "\n[file ...]\n"
704887Schin "\n"
714887Schin 
724887Schin "[+SEE ALSO?\bcp\b(1), \bgetconf\b(1), \bpr\b(1)]"
734887Schin ;
744887Schin 
754887Schin #define RUBOUT	0177
764887Schin 
774887Schin /* control flags */
784887Schin #define B_FLAG		(1<<0)
794887Schin #define E_FLAG		(1<<1)
804887Schin #define F_FLAG		(1<<2)
814887Schin #define N_FLAG		(1<<3)
824887Schin #define S_FLAG		(1<<4)
834887Schin #define T_FLAG		(1<<5)
844887Schin #define U_FLAG		(1<<6)
854887Schin #define V_FLAG		(1<<7)
864887Schin #define D_FLAG		(1<<8)
874887Schin #define d_FLAG		(1<<9)
884887Schin 
894887Schin /* character types */
904887Schin #define T_ENDBUF	1
914887Schin #define T_CONTROL	2
924887Schin #define T_NEWLINE	3
934887Schin #define T_EIGHTBIT	4
944887Schin #define T_CNTL8BIT	5
954887Schin 
964887Schin #define printof(c)	((c)^0100)
974887Schin 
984887Schin /*
994887Schin  * called for any special output processing
1004887Schin  */
1014887Schin 
1024887Schin static int
1034887Schin vcat(register char* states, Sfio_t *fdin, Sfio_t *fdout, int flags)
1044887Schin {
1054887Schin 	register unsigned char*	cp;
1064887Schin 	register unsigned char*	cpold;
1074887Schin 	register int		n;
1084887Schin 	register int		m;
1094887Schin 	register int		line = 1;
1104887Schin 	register unsigned char*	endbuff;
1114887Schin 	unsigned char*		inbuff;
1124887Schin 	int			printdefer = (flags&(B_FLAG|N_FLAG));
1134887Schin 	int			lastchar;
114*8462SApril.Chin@Sun.COM 	int			lastline;
1154887Schin 
1164887Schin 	unsigned char		meta[4];
1174887Schin 
1184887Schin 	meta[0] = 'M';
1194887Schin 	meta[1] = '-';
1204887Schin 	for (;;)
1214887Schin 	{
1224887Schin 		/* read in a buffer full */
1234887Schin 		if (!(inbuff = (unsigned char*)sfreserve(fdin, SF_UNBOUND, 0)))
1244887Schin 			return sfvalue(fdin) ? -1 : 0;
1254887Schin 		if ((n = sfvalue(fdin)) <= 0)
1264887Schin 			return n;
1274887Schin 		cp = inbuff;
1284887Schin 		lastchar = *(endbuff = cp + --n);
1294887Schin 		*endbuff = 0;
1304887Schin 		if (printdefer)
1314887Schin 		{
1324887Schin 			if (states[*cp]!=T_NEWLINE || !(flags&B_FLAG))
1334887Schin 				sfprintf(fdout,"%6d\t",line);
1344887Schin 			printdefer = 0;
1354887Schin 		}
1364887Schin 		while (endbuff)
1374887Schin 		{
1384887Schin 			cpold = cp;
1394887Schin 			/* skip over printable characters */
1404887Schin 			if (mbwide())
1414887Schin 				while ((n = (m = mbsize(cp)) < 2 ? states[*cp++] : (cp += m, states['a'])) == 0);
1424887Schin 			else
1434887Schin 				while ((n = states[*cp++]) == 0);
1444887Schin 			if (n==T_ENDBUF)
1454887Schin 			{
1464887Schin 				if (cp>endbuff)
1474887Schin 				{
1484887Schin 					if (!(n = states[lastchar]))
1494887Schin 					{
1504887Schin 						*endbuff = lastchar;
1514887Schin 						cp++;
1524887Schin 					}
1534887Schin 					else
1544887Schin 					{
1554887Schin 						if (--cp > cpold)
1564887Schin 							sfwrite(fdout,(char*)cpold,cp-cpold);
1574887Schin 						if (endbuff==inbuff)
1584887Schin 							*++endbuff = 0;
1594887Schin 						cp = cpold = endbuff;
1604887Schin 						cp[-1] = lastchar;
1614887Schin 						if (n==T_ENDBUF)
1624887Schin 							n = T_CONTROL;
1634887Schin 
1644887Schin 					}
1654887Schin 					endbuff = 0;
1664887Schin 				}
1674887Schin 				else n = T_CONTROL;
1684887Schin 			}
1694887Schin 			if (--cp>cpold)
1704887Schin 				sfwrite(fdout,(char*)cpold,cp-cpold);
1714887Schin 			switch(n)
1724887Schin 			{
1734887Schin 				case T_CNTL8BIT:
1744887Schin 					meta[2] = '^';
1754887Schin 					do
1764887Schin 					{
1774887Schin 						n = (*cp++)&~0200;
1784887Schin 						meta[3] = printof(n);
1794887Schin 						sfwrite(fdout,(char*)meta,4);
1804887Schin 					}
1814887Schin 					while ((n=states[*cp])==T_CNTL8BIT);
1824887Schin 					break;
1834887Schin 				case T_EIGHTBIT:
1844887Schin 					do
1854887Schin 					{
1864887Schin 						meta[2] = (*cp++)&~0200;
1874887Schin 						sfwrite(fdout,(char*)meta,3);
1884887Schin 					}
1894887Schin 					while ((n=states[*cp])==T_EIGHTBIT);
1904887Schin 					break;
1914887Schin 				case T_CONTROL:
1924887Schin 					do
1934887Schin 					{
1944887Schin 						n = *cp++;
1954887Schin 						sfputc(fdout,'^');
1964887Schin 						sfputc(fdout,printof(n));
1974887Schin 					}
1984887Schin 					while ((n=states[*cp])==T_CONTROL);
1994887Schin 					break;
2004887Schin 				case T_NEWLINE:
201*8462SApril.Chin@Sun.COM 					lastline = line;
2024887Schin 					if (flags&S_FLAG)
2034887Schin 					{
2044887Schin 						while (states[*++cp]==T_NEWLINE)
2054887Schin 							line++;
2064887Schin 						cp--;
2074887Schin 					}
2084887Schin 					do
2094887Schin 					{
2104887Schin 						cp++;
2114887Schin 						if (flags&E_FLAG)
2124887Schin 							sfputc(fdout,'$');
2134887Schin 						sfputc(fdout,'\n');
214*8462SApril.Chin@Sun.COM 						if(line > lastline)
215*8462SApril.Chin@Sun.COM 						{
216*8462SApril.Chin@Sun.COM 							if (flags&E_FLAG)
217*8462SApril.Chin@Sun.COM 								sfputc(fdout,'$');
218*8462SApril.Chin@Sun.COM 							sfputc(fdout,'\n');
219*8462SApril.Chin@Sun.COM 						}
2204887Schin 						if (!(flags&(N_FLAG|B_FLAG)))
2214887Schin 							continue;
2224887Schin 						line++;
2234887Schin 						if (cp < endbuff)
2244887Schin 							sfprintf(fdout,"%6d\t",line);
2254887Schin 						else printdefer = 1;
2264887Schin 					}
2274887Schin 					while (states[*cp]==T_NEWLINE);
2284887Schin 					break;
2294887Schin 			}
2304887Schin 		}
2314887Schin 	}
2324887Schin }
2334887Schin 
2344887Schin int
2354887Schin b_cat(int argc, char** argv, void* context)
2364887Schin {
2374887Schin 	register int		n;
2384887Schin 	register int		flags = 0;
2394887Schin 	register char*		cp;
2404887Schin 	register Sfio_t*	fp;
2414887Schin 	char*			mode;
2424887Schin 	int			att;
2434887Schin 	int			dovcat=0;
2444887Schin 	char			states[UCHAR_MAX+1];
2454887Schin 
2464887Schin 	NoP(argc);
2474887Schin 	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
2484887Schin 	att = !strcmp(astconf("UNIVERSE", NiL, NiL), "att");
2494887Schin 	mode = "r";
2504887Schin 	for (;;)
2514887Schin 	{
2524887Schin 		switch (optget(argv, usage))
2534887Schin 		{
2544887Schin 		case 'A':
2554887Schin 			flags |= T_FLAG|E_FLAG|V_FLAG;
2564887Schin 			continue;
2574887Schin 		case 'B':
2584887Schin 			flags |= S_FLAG;
2594887Schin 			continue;
2604887Schin 		case 'b':
2614887Schin 			flags |= B_FLAG;
2624887Schin 			continue;
2634887Schin 		case 'E':
2644887Schin 			flags |= E_FLAG;
2654887Schin 			continue;
2664887Schin 		case 'e':
2674887Schin 			flags |= E_FLAG|V_FLAG;
2684887Schin 			continue;
2694887Schin 		case 'n':
2704887Schin 			flags |= N_FLAG;
2714887Schin 			continue;
2724887Schin 		case 's':
2734887Schin 			flags |= att ? F_FLAG : S_FLAG;
2744887Schin 			continue;
2754887Schin 		case 'S':
2764887Schin 			flags |= F_FLAG;
2774887Schin 			continue;
2784887Schin 		case 'T':
2794887Schin 			flags |= T_FLAG;
2804887Schin 			continue;
2814887Schin 		case 't':
2824887Schin 			flags |= T_FLAG|V_FLAG;
2834887Schin 			continue;
2844887Schin 		case 'u':
2854887Schin 			flags |= U_FLAG;
2864887Schin 			continue;
2874887Schin 		case 'v':
2884887Schin 			flags |= V_FLAG;
2894887Schin 			continue;
2904887Schin 		case 'd':
2914887Schin 			mode = "rt";
2924887Schin 			continue;
2934887Schin 		case 'D':
2944887Schin 			flags |= d_FLAG;
2954887Schin 			continue;
2964887Schin 		case ':':
2974887Schin 			error(2, "%s", opt_info.arg);
2984887Schin 			break;
2994887Schin 		case '?':
3004887Schin 			error(ERROR_usage(2), "%s", opt_info.arg);
3014887Schin 			break;
3024887Schin 		}
3034887Schin 		break;
3044887Schin 	}
3054887Schin 	argv += opt_info.index;
3064887Schin 	if (error_info.errors)
3074887Schin 		error(ERROR_usage(2), "%s", optusage(NiL));
3084887Schin 	memset(states, 0, sizeof(states));
3094887Schin 	if (flags&V_FLAG)
3104887Schin 	{
3114887Schin 		memset(states, T_CONTROL, ' ');
3124887Schin 		states[RUBOUT] = T_CONTROL;
3134887Schin 		memset(states+0200, T_EIGHTBIT, 0200);
3144887Schin 		memset(states+0200, T_CNTL8BIT, ' ');
3154887Schin 		states[RUBOUT|0200] = T_CNTL8BIT;
3164887Schin 		states['\n'] = 0;
3174887Schin 	}
3184887Schin 	if (flags&T_FLAG)
3194887Schin 		states['\t'] = T_CONTROL;
3204887Schin 	states[0] = T_ENDBUF;
3214887Schin 	if (att)
3224887Schin 	{
3234887Schin 		if (flags&V_FLAG)
3244887Schin 		{
3254887Schin 			states['\n'|0200] = T_EIGHTBIT;
3264887Schin 			if (!(flags&T_FLAG))
3274887Schin 			{
3284887Schin 				states['\t'] = states['\f'] = 0;
3294887Schin 				states['\t'|0200] = states['\f'|0200] = T_EIGHTBIT;
3304887Schin 			}
3314887Schin 		}
3324887Schin 	}
3334887Schin 	else if (flags)
3344887Schin 	{
3354887Schin 		if (!(flags&T_FLAG))
3364887Schin 			states['\t'] = 0;
3374887Schin 	}
338*8462SApril.Chin@Sun.COM 	if (flags&(V_FLAG|T_FLAG|N_FLAG|E_FLAG|B_FLAG|S_FLAG))
3394887Schin 	{
3404887Schin 		states['\n'] = T_NEWLINE;
3414887Schin 		dovcat = 1;
3424887Schin 	}
3434887Schin 	if (flags&B_FLAG)
3444887Schin 		flags |= S_FLAG;
3454887Schin 	if (flags&d_FLAG)
3464887Schin 		sfopen(sfstdout, NiL, "wt");
3474887Schin 	if (cp = *argv)
3484887Schin 		argv++;
3494887Schin 	do
3504887Schin 	{
3514887Schin 		if (!cp || streq(cp,"-"))
3524887Schin 		{
3534887Schin 			fp = sfstdin;
3544887Schin 			if (flags&D_FLAG)
3554887Schin 				sfopen(fp, NiL, mode);
3564887Schin 		}
3574887Schin 		else if (!(fp = sfopen(NiL, cp, mode)))
3584887Schin 		{
3594887Schin 			if (!(flags&F_FLAG))
3604887Schin 				error(ERROR_system(0), "%s: cannot open", cp);
3614887Schin 			error_info.errors = 1;
3624887Schin 			continue;
3634887Schin 		}
3644887Schin 		if (flags&U_FLAG)
3654887Schin 			sfsetbuf(fp, (void*)fp, -1);
3664887Schin 		if (dovcat)
3674887Schin 			n = vcat(states, fp, sfstdout, flags);
3684887Schin 		else if (sfmove(fp, sfstdout, SF_UNBOUND, -1) >= 0 && sfeof(fp))
3694887Schin 			n = 0;
3704887Schin 		else
3714887Schin 			n = -1;
3724887Schin 		if (fp != sfstdin)
3734887Schin 			sfclose(fp);
3744887Schin 		if (n < 0 && errno != EPIPE)
3754887Schin 		{
3764887Schin 			if (cp)
3774887Schin 				error(ERROR_system(0), "%s: read error", cp);
3784887Schin 			else
3794887Schin 				error(ERROR_system(0), "read error");
3804887Schin 		}
3814887Schin 		if (sferror(sfstdout))
3824887Schin 			break;
3834887Schin 	} while (cp = *argv++);
3844887Schin 	if (sfsync(sfstdout))
3854887Schin 		error(ERROR_system(0), "write error");
3864887Schin 	if (flags&d_FLAG)
3874887Schin 		sfopen(sfstdout, NiL, "w");
3884887Schin 	return error_info.errors;
3894887Schin }
390