xref: /onnv-gate/usr/src/lib/libcmd/common/cmp.c (revision 12068:08a39a083754)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*12068SRoger.Faulkner@Oracle.COM *          Copyright (c) 1992-2010 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.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  * cmp
284887Schin  */
294887Schin 
304887Schin static const char usage[] =
3110898Sroland.mainz@nrubsig.org "[-?\n@(#)$Id: cmp (AT&T Research) 2009-01-05 $\n]"
324887Schin USAGE_LICENSE
334887Schin "[+NAME?cmp - compare two files]"
344887Schin "[+DESCRIPTION?\bcmp\b compares two files \afile1\a and \afile2\a.  "
354887Schin 	"\bcmp\b writes no output if the files are the same.  By default, "
364887Schin 	"if the files differ, the byte and line number at which the "
374887Schin 	"first difference occurred are written to standard output.  Bytes "
384887Schin 	"and lines are numbered beginning with 1.]"
394887Schin "[+?If \askip1\a or \askip2\a are specified, or the \b-i\b option is "
404887Schin 	"specified, initial bytes of the corresponding file are skipped "
414887Schin 	"before beginning the compare.  The skip values are in bytes or "
424887Schin 	"can have a suffix of \bk\b for kilobytes or \bm\b for megabytes.]"
434887Schin "[+?If either \afile1\a or \afiles2\a is \b-\b, \bcmp\b "
444887Schin         "uses standard input starting at the current location.]"
454887Schin "[c:print-chars?Writes control characters as a \b^\b followed by a letter of "
464887Schin 	"the alphabet and precede characters that have the high bit set with "
474887Schin 	"\bM-\b as with \bcat\b(1).]"
484887Schin "[i:ignore-initial]#[skip:=0?Sets default skip values for the operands "
494887Schin 	"\askip1\a and \askip2\a to \askip\a.]"
504887Schin "[l:verbose?Write the decimal byte number and the differing bytes (in octal) "
514887Schin 	"for each difference.]"
524887Schin "[s:quiet|silent?Write nothing for differing files; return non-zero "
534887Schin 	"exit status only.] ]"
544887Schin "\n"
554887Schin "\nfile1 file2 [skip1 [skip2]]\n"
564887Schin "\n"
574887Schin "[+EXIT STATUS?]{"
584887Schin 	"[+0?The files or portions compared are identical.]"
594887Schin 	"[+1?The files are different.]"
604887Schin 	"[+>1?An error occurred.]"
614887Schin "}"
624887Schin "[+SEE ALSO?\bcomm\b(1), \bdiff\b(1), \bcat\b(1)]"
634887Schin ;
644887Schin 
654887Schin 
664887Schin #include <cmd.h>
674887Schin #include <ls.h>
684887Schin #include <ctype.h>
694887Schin 
704887Schin #define CMP_VERBOSE	1
714887Schin #define CMP_SILENT	2
724887Schin #define CMP_CHARS	4
734887Schin 
744887Schin #define cntl(x)		(x&037)
754887Schin #define printchar(c)	((c) ^ ('A'-cntl('A')))
764887Schin 
outchar(Sfio_t * out,register int c,int delim)774887Schin static void outchar(Sfio_t *out, register int c, int delim)
784887Schin {
794887Schin 	if(c&0200)
804887Schin 	{
814887Schin 		sfputc(out,'M');
824887Schin 		sfputc(out,'-');
834887Schin 		c &= ~0200;
844887Schin 	}
854887Schin 	else if(!isprint(c))
864887Schin 	{
874887Schin 		sfputc(out,'^');
884887Schin 		c = printchar(c);
894887Schin 	}
904887Schin 	sfputc(out,c);
914887Schin 	sfputc(out,delim);
924887Schin }
934887Schin 
944887Schin /*
954887Schin  * compare two files
964887Schin  */
974887Schin 
984887Schin static int
cmp(const char * file1,Sfio_t * f1,const char * file2,Sfio_t * f2,int flags)994887Schin cmp(const char* file1, Sfio_t* f1, const char* file2, Sfio_t* f2, int flags)
1004887Schin {
1014887Schin 	register int		c1;
1024887Schin 	register int		c2;
1034887Schin 	register unsigned char*	p1 = 0;
1044887Schin 	register unsigned char*	p2 = 0;
1054887Schin 	register Sfoff_t	lines = 1;
1064887Schin 	register unsigned char*	e1 = 0;
1074887Schin 	register unsigned char*	e2 = 0;
1084887Schin 	Sfoff_t			pos = 0;
1094887Schin 	int			ret = 0;
1104887Schin 	unsigned char*		last;
1114887Schin 
1124887Schin 	for (;;)
1134887Schin 	{
1144887Schin 		if ((c1 = e1 - p1) <= 0)
1154887Schin 		{
1164887Schin 			if (!(p1 = (unsigned char*)sfreserve(f1, SF_UNBOUND, 0)) || (c1 = sfvalue(f1)) <= 0)
1174887Schin 			{
1184887Schin 				if ((e2 - p2) > 0 || sfreserve(f2, SF_UNBOUND, 0) && sfvalue(f2) > 0)
1194887Schin 				{
1204887Schin 					ret = 1;
1214887Schin 					if (!(flags & CMP_SILENT))
12210898Sroland.mainz@nrubsig.org 						error(ERROR_exit(1), "EOF on %s", file1);
1234887Schin 				}
1244887Schin 				return(ret);
1254887Schin 			}
1264887Schin 			e1 = p1 + c1;
1274887Schin 		}
1284887Schin 		if ((c2 = e2 - p2) <= 0)
1294887Schin 		{
1304887Schin 			if (!(p2 = (unsigned char*)sfreserve(f2, SF_UNBOUND, 0)) || (c2 = sfvalue(f2)) <= 0)
1314887Schin 			{
1324887Schin 				if (!(flags & CMP_SILENT))
13310898Sroland.mainz@nrubsig.org 					error(ERROR_exit(1), "EOF on %s", file2);
1344887Schin 				return(1);
1354887Schin 			}
1364887Schin 			e2 = p2 + c2;
1374887Schin 		}
1384887Schin 		if (c1 > c2)
1394887Schin 			c1 = c2;
1404887Schin 		pos += c1;
1414887Schin 		if (flags & CMP_SILENT)
1424887Schin 		{
1434887Schin 			if (memcmp(p1, p2, c1))
1444887Schin 				return(1);
1454887Schin 			p1 += c1;
1464887Schin 			p2 += c1;
1474887Schin 		}
1484887Schin 		else
1494887Schin 		{
1504887Schin 			last = p1 + c1;
1514887Schin 			while (p1 < last)
1524887Schin 			{
1534887Schin 				if ((c1 = *p1++) != *p2++)
1544887Schin 				{
1554887Schin 					if (flags)
1564887Schin 					{
1574887Schin 						ret = 1;
1584887Schin 						if(flags&CMP_CHARS)
1594887Schin 						{
1604887Schin 							sfprintf(sfstdout, "%6I*d ", sizeof(pos), pos - (last - p1));
1614887Schin 							outchar(sfstdout,c1,' ');
1624887Schin 							outchar(sfstdout,*(p2-1),'\n');
1634887Schin 						}
1644887Schin 						else
1654887Schin 							sfprintf(sfstdout, "%6I*d %3o %3o\n", sizeof(pos), pos - (last - p1), c1, *(p2 - 1));
1664887Schin 					}
1674887Schin 					else
1684887Schin 					{
1694887Schin 						sfprintf(sfstdout, "%s %s differ: char %I*d, line %I*u\n", file1, file2, sizeof(pos), pos - (last - p1), sizeof(lines), lines);
1704887Schin 						return(1);
1714887Schin 					}
1724887Schin 				}
1734887Schin 				if (c1 == '\n')
1744887Schin 					lines++;
1754887Schin 			}
1764887Schin 		}
1774887Schin 	}
1784887Schin }
1794887Schin 
1804887Schin int
b_cmp(int argc,register char ** argv,void * context)1814887Schin b_cmp(int argc, register char** argv, void* context)
1824887Schin {
1834887Schin 	char*		s;
1844887Schin 	char*		e;
1854887Schin 	Sfio_t*		f1 = 0;
1864887Schin 	Sfio_t*		f2 = 0;
1874887Schin 	char*		file1;
1884887Schin 	char*		file2;
1894887Schin 	int		n;
1904887Schin 	off_t		o1 = 0;
1914887Schin 	off_t		o2 = 0;
1924887Schin 	struct stat	s1;
1934887Schin 	struct stat	s2;
1944887Schin 
1954887Schin 	int		flags = 0;
1964887Schin 
1974887Schin 	NoP(argc);
1984887Schin 	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
1994887Schin 	while (n = optget(argv, usage)) switch (n)
2004887Schin 	{
2014887Schin 	case 'l':
2024887Schin 		flags |= CMP_VERBOSE;
2034887Schin 		break;
2044887Schin 	case 's':
2054887Schin 		flags |= CMP_SILENT;
2064887Schin 		break;
2074887Schin 	case 'c':
2084887Schin 		flags |= CMP_CHARS;
2094887Schin 		break;
2104887Schin 	case 'i':
2114887Schin 		o1 = o2 = opt_info.num;
2124887Schin 		break;
2134887Schin 	case ':':
2144887Schin 		error(2, "%s", opt_info.arg);
2154887Schin 		break;
2164887Schin 	case '?':
2174887Schin 		error(ERROR_usage(2), "%s", opt_info.arg);
2184887Schin 		break;
2194887Schin 	}
2204887Schin 	argv += opt_info.index;
2214887Schin 	if (error_info.errors || !(file1 = *argv++) || !(file2 = *argv++))
2224887Schin 		error(ERROR_usage(2), "%s", optusage(NiL));
2234887Schin 	n = 2;
2244887Schin 	if (streq(file1, "-"))
2254887Schin 		f1 = sfstdin;
2264887Schin 	else if (!(f1 = sfopen(NiL, file1, "r")))
2274887Schin 	{
2284887Schin 		if (!(flags & CMP_SILENT))
2294887Schin 			error(ERROR_system(0), "%s: cannot open", file1);
2304887Schin 		goto done;
2314887Schin 	}
2324887Schin 	if (streq(file2, "-"))
2334887Schin 		f2 = sfstdin;
2344887Schin 	else if (!(f2 = sfopen(NiL, file2, "r")))
2354887Schin 	{
2364887Schin 		if (!(flags & CMP_SILENT))
2374887Schin 			error(ERROR_system(0), "%s: cannot open", file2);
2384887Schin 		goto done;
2394887Schin 	}
2404887Schin 	if (s = *argv++)
2414887Schin 	{
2424887Schin 		o1 = strtol(s, &e, 0);
2434887Schin 		if (*e)
2444887Schin 		{
2454887Schin 			error(ERROR_exit(0), "%s: %s: invalid skip", file1, s);
2464887Schin 			goto done;
2474887Schin 		}
2484887Schin 		if (s = *argv++)
2494887Schin 		{
2504887Schin 			o2 = strtol(s, &e, 0);
2514887Schin 			if (*e)
2524887Schin 			{
2534887Schin 				error(ERROR_exit(0), "%s: %s: invalid skip", file2, s);
2544887Schin 				goto done;
2554887Schin 			}
2564887Schin 		}
2574887Schin 		if (*argv)
2584887Schin 		{
2594887Schin 			error(ERROR_usage(0), "%s", optusage(NiL));
2604887Schin 			goto done;
2614887Schin 		}
2624887Schin 	}
2634887Schin 	if (o1 && sfseek(f1, o1, SEEK_SET) != o1)
2644887Schin 	{
2654887Schin 		if (!(flags & CMP_SILENT))
26610898Sroland.mainz@nrubsig.org 			error(ERROR_exit(0), "EOF on %s", file1);
2674887Schin 		n = 1;
2684887Schin 		goto done;
2694887Schin 	}
2704887Schin 	if (o2 && sfseek(f2, o2, SEEK_SET) != o2)
2714887Schin 	{
2724887Schin 		if (!(flags & CMP_SILENT))
27310898Sroland.mainz@nrubsig.org 			error(ERROR_exit(0), "EOF on %s", file2);
2744887Schin 		n = 1;
2754887Schin 		goto done;
2764887Schin 	}
2774887Schin 	if (fstat(sffileno(f1), &s1))
2784887Schin 		error(ERROR_system(0), "%s: cannot stat", file1);
2794887Schin 	else if (fstat(sffileno(f2), &s2))
2804887Schin 		error(ERROR_system(0), "%s: cannot stat", file1);
2814887Schin 	else if (s1.st_ino == s2.st_ino && s1.st_dev == s2.st_dev && o1 == o2)
2824887Schin 		n = 0;
28310898Sroland.mainz@nrubsig.org 	else
28410898Sroland.mainz@nrubsig.org 		n = ((flags & CMP_SILENT) && S_ISREG(s1.st_mode) && S_ISREG(s2.st_mode) && (s1.st_size - o1) != (s2.st_size - o2)) ? 1 : cmp(file1, f1, file2, f2, flags);
2854887Schin  done:
2864887Schin 	if (f1 && f1 != sfstdin) sfclose(f1);
2874887Schin 	if (f2 && f2 != sfstdin) sfclose(f2);
2884887Schin 	return(n);
2894887Schin }
290