/* * 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 #include #include #include #include #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("Id") */ 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; }