/*
* Tcl command procedure to compare two files and report whether
* they are identical.
*
* P. Mackerras 17/7/96.
*
* $Id: filecmp.c,v 1.3 2001/08/15 11:12:44 paulus Exp $
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <tcl.h>
#include <sys/fcntl.h>
#define BSIZE 32768
#define MAXTAGLEN 512 /* max tag length for sanity, < BSIZE */
#ifdef NO_MEMMOVE
#define memmove(t, f, n) bcopy(f, t, n)
#endif
char *rcs_ignores[] = {
"Log", /* must be at index 0! */
"Id",
"Revision",
"Source",
"Author",
"Date",
"State",
"Header",
NULL
};
char *comment_leaders[] = {
" *",
"#",
NULL
};
/*
* See if the text starting at p, for n chars, looks like an RCS tag.
* We already know p[0] == '$' and n >= 4.
* Return 0 if it isn't a tag, -1 if it looks like a tag but is incomplete,
* or the length of the tag.
*/
int
tag_length(p, n)
char *p;
int n;
{
int i, j, k, l;
int maybe;
char *t, *cl;
/* check through list of tags */
for (i = 0; (t = rcs_ignores[i]) != NULL; ++i) {
for (j = 1; *t != 0; ++j, ++t) {
if (j >= n)
return -1;
if (p[j] != *t)
break;
}
if (*t == 0)
break;
}
if (t == NULL) /* if tag wasn't in list */
return 0;
if (j >= n) /* if we got to the end of the buffer */
return -1;
if (p[j] == '$') /* for the \dollar Id\dollar case */
return j + 1;
if (p[j] != ':') /* tag word must be followed by $ or : */
return 0;
while (++j < n) { /* if by :, matching $ must appear before \n */
if (p[j] == '\n')
return 0; /* got \n without $: not a tag */
if (p[j] == '$') {
if (i != 0) /* got $: if the tag wasn't \dollar Log, */
return j + 1; /* that's the end of the tag */
/* here we skip lines that start with ' * ' or '# '. */
for (;;) {
/* skip to \n */
while (++j < n && p[j] != '\n')
;
if (++j >= n)
break;
maybe = 0;
l = n - j;
if (l >= 3 && p[j] == ' ' && p[j+1] == '*' && p[j+2] != '/')
continue; /* C-style comment leader */
if (p[j] == '#')
continue; /* Script comment leader */
if (l < 3 && p[j] == ' ' && (l < 2 || p[j] == '*'))
maybe = 1; /* may be C-style comment leader */
if (maybe)
break;
return j; /* we've found the end of the tag */
}
/* Incomplete tag following \dollar Log;
the complete tag could be quite long. */
if (n >= BSIZE)
return 0;
return -1;
}
}
if (n > MAXTAGLEN) /* incomplete tag too long: not a tag */
return 0;
return -1; /* incomplete tag */
}
int rcscmp(char *p1, int *k1p, char *p2, int *k2p, int e1, int e2)
{
int k1 = *k1p, k2 = *k2p;
int i, t1, t2;
for (;;) {
for (i = 0; i < k1 && i < k2; ++i) {
if (p1[i] != p2[i])
return 0;
if (p1[i] == '$')
break;
}
p1 += i;
k1 -= i;
p2 += i;
k2 -= i;
/* 4 == strlen("<dollar>Id<dollar>") */
if (k1 < 4 && !e1 || k2 < 4 && !e2)
break;
if (k1 < 4 || k2 < 4) {
/* near the end of one or both files */
if (k1 != k2 || memcmp(p1, p2, k1) != 0)
return 0;
k1 = k2 = 0;
break;
}
/* check that the tags look the same (1st 2 chars at least) */
if (p1[1] != p2[1] || p1[2] != p2[2])
return 0;
t1 = tag_length(p1, k1);
if (t1 < 0 && e1)
t1 = 0;
if (t1 != 0) {
t2 = tag_length(p2, k2);
if (t2 < 0 && e2)
t2 = 0;
}
if (t1 == 0 || t2 == 0) {
/* one or other string isn't a tag */
p1 += 3;
k1 -= 3;
p2 += 3;
k2 -= 3;
continue;
}
if (t1 < 0 || t2 < 0)
break; /* one or other tag is incomplete */
p1 += t1;
k1 -= t1;
p2 += t2;
k2 -= t2;
if (k1 == 0 || k2 == 0)
break;
}
*k1p = k1;
*k2p = k2;
return 1;
}
static char bktag[] = "BK Id: ";
#define BKTAGLEN 7
int bkcmp(char *p1, int *k1p, char *p2, int *k2p, int e1, int e2)
{
int k1 = *k1p, k2 = *k2p;
int i, match, t1, t2;
for (;;) {
match = 0;
for (i = 0; i < k1 && i < k2; ++i) {
if (p1[i] != p2[i])
return 0;
if (p1[i] == bktag[match]) {
if (++match == BKTAGLEN) {
++i;
break;
}
} else {
match = (p1[i] == bktag[0]);
}
}
p1 += i;
k1 -= i;
p2 += i;
k2 -= i;
if (match < BKTAGLEN) {
/* we have run out of one or other buffer */
if (k1 == 0 && e1 || k2 == 0 && e2) {
if (k1 == k2)
break;
return 0;
}
k1 += match;
k2 += match;
break;
}
/* found a tag, skip to eol on both files */
for (t1 = 0; t1 < k1; ++t1)
if (p1[t1] == '\n')
break;
for (t2 = 0; t2 < k2; ++t2)
if (p2[t2] == '\n')
break;
if (t1 < k1 && t2 < k2) {
p1 += t1;
k1 -= t1;
p2 += t2;
k2 -= t2;
continue;
}
/* ran out before eol on one or both files */
if (t1 == k1 && e1 || t2 == k2 && e2) {
k1 -= t1;
k2 -= t2;
if (k1 == k2)
break;
return 0;
}
/* not at eof on either file */
k1 += BKTAGLEN;
k2 += BKTAGLEN;
break;
}
*k1p = k1;
*k2p = k2;
return 1;
}
int
FileCmpCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int i, fi;
int f1, f2;
char *b1, *b2;
int n1, n2, same;
int k1, k2, t1, t2;
int rcsflag, bkflag;
int e1, e2;
fi = 1;
rcsflag = 0;
bkflag = 0;
if (argc > 1 && strcmp(argv[1], "-rcs") == 0) {
++fi;
rcsflag = 1;
} else if (argc > 1 && strcmp(argv[1], "-bk") == 0) {
++fi;
bkflag = 1;
}
if (argc != fi + 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ?-rcs? file1 file2\"", (char *) NULL);
return TCL_ERROR;
}
if ((f1 = open(argv[fi], O_RDONLY)) == -1) {
Tcl_AppendResult(interp, "can't open \"", argv[fi], "\": ",
Tcl_PosixError(interp), (char *) NULL);
return TCL_ERROR;
}
if ((f2 = open(argv[fi+1], O_RDONLY)) == -1) {
Tcl_AppendResult(interp, "can't open \"", argv[fi+1], "\": ",
Tcl_PosixError(interp), (char *) NULL);
close(f1);
return TCL_ERROR;
}
b1 = (char *) ckalloc(BSIZE);
b2 = (char *) ckalloc(BSIZE);
same = 1;
k1 = k2 = 0;
e1 = e2 = 0;
for (;;) {
/* Here we already have k1 chars in b1 and k2 chars in b2. */
if (!e1 && k1 < BSIZE) {
n1 = read(f1, b1 + k1, BSIZE - k1);
if (n1 < 0) {
Tcl_AppendResult(interp, "error reading \"", argv[fi],
"\": ", Tcl_PosixError(interp),
(char *) NULL);
break;
}
k1 += n1;
if (k1 < BSIZE)
e1 = 1;
}
if (!e2 && k2 < BSIZE) {
n2 = read(f2, b2 + k2, BSIZE - k2);
if (n2 < 0) {
Tcl_AppendResult(interp, "error reading \"", argv[fi+1],
"\": ", Tcl_PosixError(interp),
(char *) NULL);
break;
}
k2 += n2;
if (k2 < BSIZE)
e2 = 1;
}
if (k1 == 0 && k2 == 0)
break;
t1 = k1;
t2 = k2;
if (rcsflag)
same = rcscmp(b1, &k1, b2, &k2, e1, e2);
else if (bkflag)
same = bkcmp(b1, &k1, b2, &k2, e1, e2);
else {
if (k1 != k2 || memcmp(b1, b2, k1) != 0)
same = 0;
k1 = k2 = 0;
}
if (!same || (e1 && e2))
break;
if (k1 > 0)
memmove(b1, b1 + t1 - k1, k1);
if (k2 > 0)
memmove(b2, b2 + t2 - k2, k2);
}
close(f1);
close(f2);
ckfree(b1);
ckfree(b2);
if (n1 < 0 || n2 < 0)
return TCL_ERROR;
sprintf(interp->result, "%d", same);
return TCL_OK;
}
int
Filecmp_Init(interp)
Tcl_Interp *interp;
{
Tcl_CreateCommand(interp, "filecmp", FileCmpCmd, NULL, NULL);
return TCL_OK;
}
syntax highlighted by Code2HTML, v. 0.9.1