1N/A/*
1N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
1N/A */
1N/A
1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1992-2011 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* Glenn Fowler <gsf@research.att.com> *
1N/A* David Korn <dgk@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A/*
1N/A * David Korn
1N/A * Glenn Fowler
1N/A * AT&T Bell Laboratories
1N/A *
1N/A * cmp
1N/A */
1N/A
1N/Astatic const char usage[] =
1N/A"[-?\n@(#)$Id: cmp (AT&T Research) 2010-04-11 $\n]"
1N/AUSAGE_LICENSE
1N/A"[+NAME?cmp - compare two files]"
1N/A"[+DESCRIPTION?\bcmp\b compares two files \afile1\a and \afile2\a. "
1N/A "\bcmp\b writes no output if the files are the same. By default, if the "
1N/A "files differ, the byte and line number at which the first difference "
1N/A "occurred are written to standard output. Bytes and lines are numbered "
1N/A "beginning with 1.]"
1N/A"[+?If \askip1\a or \askip2\a are specified, or the \b-i\b option is "
1N/A "specified, initial bytes of the corresponding file are skipped before "
1N/A "beginning the compare. The skip values are in bytes or can have a "
1N/A "suffix of \bk\b for kilobytes or \bm\b for megabytes.]"
1N/A"[+?If either \afile1\a or \afiles2\a is \b-\b, \bcmp\b uses standard "
1N/A "input starting at the current location.]"
1N/A"[b:print-bytes?Print differing bytes as 3 digit octal values.]"
1N/A"[c:print-chars?Print differing bytes as follows: non-space printable "
1N/A "characters as themselves; space and control characters as \b^\b "
1N/A "followed by a letter of the alphabet; and characters with the high bit "
1N/A "set as the lower 7 bit character prefixed by \bM^\b for 7 bit space and "
1N/A "non-printable characters and \bM-\b for all other characters. If the 7 "
1N/A "bit character encoding is not ASCII then the characters are converted "
1N/A "to ASCII to determine \ahigh bit set\a, and if set it is cleared and "
1N/A "converted back to the native encoding. Multibyte characters in the "
1N/A "current locale are treated as printable characters.]"
1N/A"[d:differences?Print at most \adifferences\a differences using "
1N/A "\b--verbose\b output format. \b--differences=0\b is equivalent to "
1N/A "\b--silent\b.]#[differences]"
1N/A"[i:ignore-initial|skip?Skip the the first \askip1\a bytes in \afile1\a "
1N/A "and the first \askip2\a bytes in \afile2\a. If \askip2\a is omitted "
1N/A "then \askip1\a is used.]:[skip1[::skip2]]:=0::0]"
1N/A"[l:verbose?Write the decimal byte number and the differing bytes (in "
1N/A "octal) for each difference.]"
1N/A"[n:count|bytes?Compare at most \acount\a bytes.]#[count]"
1N/A"[s:quiet|silent?Write nothing for differing files; return non-zero exit "
1N/A "status only.]"
1N/A"\n"
1N/A"\nfile1 file2 [skip1 [skip2]]\n"
1N/A"\n"
1N/A"[+EXIT STATUS?]"
1N/A "{"
1N/A "[+0?The files or portions compared are identical.]"
1N/A "[+1?The files are different.]"
1N/A "[+>1?An error occurred.]"
1N/A "}"
1N/A"[+SEE ALSO?\bcomm\b(1), \bdiff\b(1), \bcat\b(1)]"
1N/A;
1N/A
1N/A#include <cmd.h>
1N/A#include <ls.h>
1N/A#include <ctype.h>
1N/A#include <ccode.h>
1N/A
1N/A#define CMP_VERBOSE 0x01
1N/A#define CMP_SILENT 0x02
1N/A#define CMP_CHARS 0x04
1N/A#define CMP_BYTES 0x08
1N/A
1N/Astatic void
1N/Apretty(Sfio_t *out, int o, int delim, int flags)
1N/A{
1N/A int c;
1N/A int m;
1N/A char* s;
1N/A char buf[10];
1N/A
1N/A s = buf;
1N/A if ((flags & CMP_BYTES) || !(flags & CMP_CHARS))
1N/A {
1N/A *s++ = ' ';
1N/A if ((flags & CMP_CHARS) && delim != -1)
1N/A *s++ = ' ';
1N/A *s++ = '0' + ((o >> 6) & 07);
1N/A *s++ = '0' + ((o >> 3) & 07);
1N/A *s++ = '0' + (o & 07);
1N/A }
1N/A if (flags & CMP_CHARS)
1N/A {
1N/A *s++ = ' ';
1N/A c = ccmapc(o, CC_NATIVE, CC_ASCII);
1N/A if (c & 0x80)
1N/A {
1N/A m = 1;
1N/A *s++ = 'M';
1N/A c &= 0x7f;
1N/A o = ccmapc(c, CC_ASCII, CC_NATIVE);
1N/A }
1N/A else
1N/A m = 0;
1N/A if (isspace(o) || !isprint(o))
1N/A {
1N/A if (!m)
1N/A *s++ = ' ';
1N/A *s++ = '^';
1N/A c ^= 0x40;
1N/A o = ccmapc(c, CC_ASCII, CC_NATIVE);
1N/A }
1N/A else if (m)
1N/A *s++ = '-';
1N/A else
1N/A {
1N/A *s++ = ' ';
1N/A *s++ = ' ';
1N/A }
1N/A *s++ = o;
1N/A }
1N/A *s = 0;
1N/A sfputr(out, buf, delim);
1N/A}
1N/A
1N/A/*
1N/A * compare two files
1N/A */
1N/A
1N/Astatic int
1N/Acmp(const char* file1, Sfio_t* f1, const char* file2, Sfio_t* f2, int flags, Sfoff_t count, Sfoff_t differences)
1N/A{
1N/A register int c1;
1N/A register int c2;
1N/A register unsigned char* p1 = 0;
1N/A register unsigned char* p2 = 0;
1N/A register Sfoff_t lines = 1;
1N/A register unsigned char* e1 = 0;
1N/A register unsigned char* e2 = 0;
1N/A Sfoff_t pos = 0;
1N/A int n1 = 0;
1N/A int ret = 0;
1N/A unsigned char* last;
1N/A
1N/A for (;;)
1N/A {
1N/A if ((c1 = e1 - p1) <= 0)
1N/A {
1N/A if (count > 0 && !(count -= n1))
1N/A return ret;
1N/A if (!(p1 = (unsigned char*)sfreserve(f1, SF_UNBOUND, 0)) || (c1 = sfvalue(f1)) <= 0)
1N/A {
1N/A if (sferror(f1)) {
1N/A error(ERROR_exit(2),
1N/A "read error on %s", file1);
1N/A }
1N/A if ((e2 - p2) > 0 || sfreserve(f2, SF_UNBOUND, 0) && sfvalue(f2) > 0)
1N/A {
1N/A ret = 1;
1N/A if (!(flags & CMP_SILENT))
1N/A error(ERROR_exit(1), "EOF on %s", file1);
1N/A }
1N/A if (sferror(f2)) {
1N/A error(ERROR_exit(2),
1N/A "read error on %s", file2);
1N/A }
1N/A return ret;
1N/A }
1N/A if (count > 0 && c1 > count)
1N/A c1 = (int)count;
1N/A e1 = p1 + c1;
1N/A n1 = c1;
1N/A }
1N/A if ((c2 = e2 - p2) <= 0)
1N/A {
1N/A if (!(p2 = (unsigned char*)sfreserve(f2, SF_UNBOUND, 0)) || (c2 = sfvalue(f2)) <= 0)
1N/A {
1N/A if (sferror(f2)) {
1N/A error(ERROR_exit(2),
1N/A "read error on %s", file2);
1N/A }
1N/A if (!(flags & CMP_SILENT))
1N/A error(ERROR_exit(1), "EOF on %s", file2);
1N/A return 1;
1N/A }
1N/A e2 = p2 + c2;
1N/A }
1N/A if (c1 > c2)
1N/A c1 = c2;
1N/A pos += c1;
1N/A if (flags & CMP_SILENT)
1N/A {
1N/A if (memcmp(p1, p2, c1))
1N/A return 1;
1N/A p1 += c1;
1N/A p2 += c1;
1N/A }
1N/A else
1N/A {
1N/A last = p1 + c1;
1N/A while (p1 < last)
1N/A {
1N/A if ((c1 = *p1++) != *p2++)
1N/A {
1N/A if (differences >= 0)
1N/A {
1N/A if (!differences)
1N/A return 1;
1N/A differences--;
1N/A }
1N/A#if 0
1N/A if (!flags)
1N/A sfprintf(sfstdout, "%s %s differ: char %I*d, line %I*u\n", file1, file2, sizeof(pos), pos - (last - p1), sizeof(lines), lines);
1N/A else
1N/A {
1N/A sfprintf(sfstdout, "%6I*d", sizeof(pos), pos - (last - p1));
1N/A pretty(sfstdout, c1, -1, flags);
1N/A pretty(sfstdout, *(p2-1), '\n', flags);
1N/A }
1N/A#else
1N/A if (flags & CMP_VERBOSE)
1N/A sfprintf(sfstdout, "%6I*d", sizeof(pos), pos - (last - p1));
1N/A else
1N/A sfprintf(sfstdout, "%s %s differ: char %I*d, line %I*u", file1, file2, sizeof(pos), pos - (last - p1), sizeof(lines), lines);
1N/A if (flags & (CMP_BYTES|CMP_CHARS|CMP_VERBOSE))
1N/A {
1N/A sfputc(sfstdout, (flags & CMP_VERBOSE) ? ' ' : ',');
1N/A pretty(sfstdout, c1, -1, flags);
1N/A pretty(sfstdout, *(p2-1), '\n', flags);
1N/A }
1N/A else
1N/A sfputc(sfstdout, '\n');
1N/A#endif
1N/A if (!differences || differences < 0 && !(flags & CMP_VERBOSE))
1N/A return 1;
1N/A ret = 1;
1N/A }
1N/A if (c1 == '\n')
1N/A lines++;
1N/A }
1N/A }
1N/A }
1N/A}
1N/A
1N/Aint
1N/Ab_cmp(int argc, register char** argv, void* context)
1N/A{
1N/A char* s;
1N/A char* e;
1N/A char* file1;
1N/A char* file2;
1N/A int n;
1N/A struct stat s1;
1N/A struct stat s2;
1N/A
1N/A Sfio_t* f1 = 0;
1N/A Sfio_t* f2 = 0;
1N/A Sfoff_t o1 = 0;
1N/A Sfoff_t o2 = 0;
1N/A Sfoff_t count = -1;
1N/A Sfoff_t differences = -1;
1N/A int flags = 0;
1N/A
1N/A NoP(argc);
1N/A cmdinit(argc, argv, context, ERROR_CATALOG, 0);
1N/A for (;;)
1N/A {
1N/A switch (optget(argv, usage))
1N/A {
1N/A case 'b':
1N/A flags |= CMP_BYTES;
1N/A continue;
1N/A case 'c':
1N/A flags |= CMP_CHARS;
1N/A continue;
1N/A case 'd':
1N/A flags |= CMP_VERBOSE;
1N/A differences = opt_info.number;
1N/A continue;
1N/A case 'i':
1N/A o1 = strtoll(opt_info.arg, &e, 0);
1N/A if (*e == ':')
1N/A o2 = strtoll(e + 1, &e, 0);
1N/A else
1N/A o2 = o1;
1N/A if (*e)
1N/A {
1N/A error(2, "%s: skip1:skip2 expected", opt_info.arg);
1N/A break;
1N/A }
1N/A continue;
1N/A case 'l':
1N/A flags |= CMP_VERBOSE;
1N/A continue;
1N/A case 'n':
1N/A count = opt_info.number;
1N/A continue;
1N/A case 's':
1N/A flags |= CMP_SILENT;
1N/A continue;
1N/A case ':':
1N/A error(2, "%s", opt_info.arg);
1N/A break;
1N/A case '?':
1N/A error(ERROR_usage(2), "%s", opt_info.arg);
1N/A break;
1N/A }
1N/A break;
1N/A }
1N/A argv += opt_info.index;
1N/A if (error_info.errors || !(file1 = *argv++) || !(file2 = *argv++))
1N/A error(ERROR_usage(2), "%s", optusage(NiL));
1N/A n = 2;
1N/A if (streq(file1, "-"))
1N/A f1 = sfstdin;
1N/A else if (!(f1 = sfopen(NiL, file1, "r")))
1N/A {
1N/A if (!(flags & CMP_SILENT))
1N/A error(ERROR_system(0), "%s: cannot open", file1);
1N/A goto done;
1N/A }
1N/A if (streq(file2, "-"))
1N/A f2 = sfstdin;
1N/A else if (!(f2 = sfopen(NiL, file2, "r")))
1N/A {
1N/A if (!(flags & CMP_SILENT))
1N/A error(ERROR_system(0), "%s: cannot open", file2);
1N/A goto done;
1N/A }
1N/A if (s = *argv++)
1N/A {
1N/A o1 = strtoll(s, &e, 0);
1N/A if (*e)
1N/A {
1N/A error(ERROR_exit(0), "%s: %s: invalid skip", file1, s);
1N/A goto done;
1N/A }
1N/A if (s = *argv++)
1N/A {
1N/A o2 = strtoll(s, &e, 0);
1N/A if (*e)
1N/A {
1N/A error(ERROR_exit(0), "%s: %s: invalid skip", file2, s);
1N/A goto done;
1N/A }
1N/A }
1N/A if (*argv)
1N/A {
1N/A error(ERROR_usage(0), "%s", optusage(NiL));
1N/A goto done;
1N/A }
1N/A }
1N/A if (o1 && sfseek(f1, o1, SEEK_SET) != o1)
1N/A {
1N/A if (!(flags & CMP_SILENT))
1N/A error(ERROR_exit(0), "EOF on %s", file1);
1N/A n = 1;
1N/A goto done;
1N/A }
1N/A if (o2 && sfseek(f2, o2, SEEK_SET) != o2)
1N/A {
1N/A if (!(flags & CMP_SILENT))
1N/A error(ERROR_exit(0), "EOF on %s", file2);
1N/A n = 1;
1N/A goto done;
1N/A }
1N/A if (fstat(sffileno(f1), &s1))
1N/A error(ERROR_system(0), "%s: cannot stat", file1);
1N/A else if (fstat(sffileno(f2), &s2))
1N/A error(ERROR_system(0), "%s: cannot stat", file1);
1N/A else if (s1.st_ino == s2.st_ino && s1.st_dev == s2.st_dev && o1 == o2)
1N/A n = 0;
1N/A else
1N/A 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, count, differences);
1N/A done:
1N/A if (f1 && f1 != sfstdin)
1N/A sfclose(f1);
1N/A if (f2 && f2 != sfstdin)
1N/A sfclose(f2);
1N/A return n;
1N/A}