#include #include #include #include #include #include #include #include #include #include #include #include /* bindiff format is as follows: * * struct bdiff_patch { * struct bdiff_header { * uint16_t magic = 0xBD1F; * uint16_t version; * uint32_t num_edits; * }; * struct { * unsigned short op; * unsigned short hash; * uint32_t off; * uint32_t len; * } edits[num_edits]; * uint8_t payload[sum of DIFF_INSERT lengths]; * }; */ #if defined(__sparc__) #include #else #include #endif /* encode/decode integers */ size_t enc_uint16be(uint16_t s, unsigned char *dst) { dst[0] = (s >> 8) & 0xFF; dst[1] = s & 0xFF; return 2; } size_t enc_uint32be(uint32_t i, unsigned char *dst) { dst[0] = (i >> 24) & 0xFF; dst[1] = (i >> 16) & 0xFF; dst[2] = (i >> 8) & 0xFF; dst[3] = i & 0xFF; return 4; } uint16_t dec_uint16be(const unsigned char *src) { return ((unsigned)src[0] << 8) | src[1]; } uint32_t dec_uint32be(const unsigned char *src) { return ((unsigned)src[0] << 24) | ((unsigned)src[1] << 16) | ((unsigned)src[2] << 8) | src[3]; } /* mmap an entire file into memory */ void * mapfile(const char *filename, int *size) { void *ret = NULL; int fd; struct stat st; if ((fd = open(filename, O_RDONLY)) == -1 || fstat(fd, &st) == -1) { PMNO(errno); return NULL; } if ((ret = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { PMNO(errno); return NULL; } *size = st.st_size; return ret; } int do_patch(unsigned char *p, int pn, unsigned char *a, int n, FILE *stream) { unsigned char *start = p; uint32_t hdr; int sn, i; if (pn < 8) { PMSG("invalid patch file"); return -1; } /* decode header */ hdr = dec_uint32be(p); p += 4; if ((hdr & 0xFFFF0000) != 0xBD1F0000) { PMSG("invalid bindiff patch file header: 0x%8x", hdr); return -1; } else if ((hdr & 0xFFFF) > 0x0001) { PMSG("incompatible bindiff patch file version"); return -1; } sn = dec_uint32be(p); p += 4; if (pn < (8 + sn * 12)) { PMSG("bindiff patch file corrupted"); return -1; } /* decode/write edits */ for (i = 0; i < sn; i++) { int op, off, len; op = dec_uint16be(p); p += 4; off = dec_uint32be(p); p += 4; len = dec_uint32be(p); p += 4; switch (op) { case DIFF_MATCH: if ((off + len) > n) { PMSG("file being patched is truncated or patch file corrupted"); return -1; } fwrite(a + off, 1, len, stream); break; case DIFF_INSERT: if ((off + len) > pn) { PMSG("patch file is truncated or corrupted"); return -1; } fwrite(start + off, 1, len, stream); break; } } return 0; } int do_diff(unsigned char *a, int n, unsigned char *b, int m, FILE *stream) { int d; int sn, i; struct varray *ses = varray_new(sizeof(struct diff_edit), NULL); unsigned char buf[12]; size_t off; if ((d = diff(a, 0, n, b, 0, m, NULL, NULL, NULL, 0, ses, &sn, NULL)) == -1) { MMNO(errno); return EXIT_FAILURE; } /* encode/write header */ enc_uint32be(0xBD1F0001, buf); /* magic and version */ enc_uint32be(sn, buf + 4); /* num_edits */ if (fwrite(buf, 8, 1, stream) != 1) { PMNO(errno); return -1; } /* encode/write edits */ off = 8 + sn * 12; for (i = 0; i < sn; i++) { struct diff_edit *e = varray_get(ses, i); enc_uint16be(e->op, buf); enc_uint16be(0xab, buf + 2); switch (e->op) { case DIFF_MATCH: enc_uint32be(e->off, buf + 4); break; case DIFF_INSERT: enc_uint32be(off, buf + 4); off += e->len; break; } enc_uint32be(e->len, buf + 8); if (fwrite(buf, 12, 1, stream) != 1) { PMNO(errno); return -1; } } /* write payload */ for (i = 0; i < sn; i++) { struct diff_edit *e = varray_get(ses, i); if (e->op == DIFF_INSERT) { size_t n, len = e->len; while ((n = fwrite(b + e->off, 1, len, stream)) < len) { if (ferror(stream)) { PMNO(errno); return -1; } len -= n; } } } return d; } int run(const char *file1, const char *file2, int patch) { unsigned char *a, *b; int n, m; if ((a = mapfile(file1, &n)) == NULL || (b = mapfile(file2, &m)) == NULL) { AMSG(""); return -1; } if (patch) { if (do_patch(a, n, b, m, stdout) == -1) { AMSG("failed to patch %s with %s", file2, file1); return -1; } } else if (do_diff(a, n, b, m, stdout) == -1) { AMSG("failed to diff %s with %s", file1, file2); return -1; } fflush(stdout); return 0; } int main(int argc, char *argv[]) { char **args; char *file1 = NULL, *file2 = NULL; int patch = 0; if (argc < 3) { usage: fprintf(stderr, "usage: %s [-p ] []\n", argv[0]); return EXIT_FAILURE; } errno = 0; args = argv; args++; argc--; while (argc) { if (!patch && strcmp(*args, "-p") == 0) { patch = 1; args++; argc--; if (argc == 0) { fprintf(stderr, "-p requires a patch filename argument\n"); goto usage; } file1 = *args; } else if (file2) { fprintf(stderr, "invalid arguments\n"); goto usage; } else if (file1) { file2 = *args; } else { file1 = *args; } args++; argc--; } if (file1 == NULL || file2 == NULL) { goto usage; } if (run(file1, file2, patch) == -1) { MMSG(""); return EXIT_FAILURE; } return EXIT_SUCCESS; }