diff --git a/dub.json b/dub.json index d9c5233..33c9c1e 100644 --- a/dub.json +++ b/dub.json @@ -12,9 +12,9 @@ "xdiff" ], "dependencies": { - "xdiff": { - "repository": "git+https://git.zhirov.kz/dlang/xdiff.git", - "version": "e2396bc172eba813cdcd1a96c494e35d687f576a" + "libxdiff": { + "repository": "git+https://git.zhirov.kz/dlang/libxdiff.git", + "version": "143f39005c0e89cc57d564365e6ea61427928bc3" } } } \ No newline at end of file diff --git a/dub.selections.json b/dub.selections.json new file mode 100644 index 0000000..c3c6475 --- /dev/null +++ b/dub.selections.json @@ -0,0 +1,6 @@ +{ + "fileVersion": 1, + "versions": { + "libxdiff": {"version":"143f39005c0e89cc57d564365e6ea61427928bc3","repository":"git+https://git.zhirov.kz/dlang/libxdiff.git"} + } +} diff --git a/source/app.d b/source/app.d index 7b3a7ed..9101d29 100644 --- a/source/app.d +++ b/source/app.d @@ -1,322 +1,17 @@ -import std.stdio : writeln, stderr, File; -import std.getopt; -import std.file : read, exists; -import std.exception : enforce, collectException; -import core.stdc.stdlib : malloc, free, realloc; -import core.stdc.string : memcpy; -import core.sys.posix.unistd : posix_write = write; +import std.stdio; +import libxdiff : MMFile; -import xdiff; - -// ------------------------------ -// RAII для mmfile_t -// ------------------------------ -struct MmFile +void main() { - mmfile_t mf; + auto a = MMFile.fromBytes(cast(ubyte[]) "hello world\n"); + auto b = MMFile.fromBytes(cast(ubyte[]) "hello world!\n"); - // фабрика вместо конструктора по умолчанию - static MmFile create(long bsize = 8 * 1024, ulong flags = XDL_MMF_ATOMIC) - { - MmFile m; - enforce(xdl_init_mmfile(&m.mf, bsize, flags) == 0, "xdl_init_mmfile failed"); - return m; - } + auto patch = a.computePatch(b); + writeln("patch size: ", patch.size()); - void deinit() - { - xdl_free_mmfile(&mf); - } -} - -// ------------------------------ -// Коллбэки вывода -// ------------------------------ -extern (C) int mmfileOut(void* priv, mmbuffer_t* mb, int nbuf) -{ - auto mmfOut = cast(mmfile_t*) priv; - return xdl_writem_mmfile(mmfOut, mb, nbuf) < 0 ? -1 : 0; -} - -extern (C) int fdOut(void* priv, mmbuffer_t* mb, int nbuf) -{ - const fd = cast(int) cast(size_t) priv; // 1=stdout, 2=stderr - foreach (i; 0 .. nbuf) - { - auto ptr = mb[i].ptr; - auto left = cast(size_t) mb[i].size; - while (left) - { - auto n = posix_write(fd, ptr, left); - if (n < 0) - return -1; - ptr += n; - left -= cast(size_t) n; - } - } - return 0; -} - -// ------------------------------ -// Загрузка/выгрузка -// ------------------------------ -void loadMmFile(ref MmFile m, string path) -{ - try - { - enforce(exists(path), "no such file: " ~ path); - auto data = cast(const(char)[]) read(path); // читает весь файл - auto dst = cast(char*) xdl_mmfile_writeallocate(&m.mf, data.length); - enforce(dst !is null, "xdl_mmfile_writeallocate failed (null)"); - if (data.length) - memcpy(dst, data.ptr, data.length); - } - catch (Exception e) - { - // пробрасываем с контекстом пути - throw new Exception("loadMmFile(" ~ path ~ "): " ~ e.msg); - } -} - -void dumpMmFile(const ref MmFile m, string path) -{ - File f = File(path, "wb"); - scope (exit) - f.close(); - long sz = 0; - auto blk = cast(char*) xdl_mmfile_first(cast(mmfile_t*)&m.mf, &sz); - while (blk !is null) - { - f.rawWrite(blk[0 .. cast(size_t) sz]); - blk = cast(char*) xdl_mmfile_next(cast(mmfile_t*)&m.mf, &sz); - } -} - -// ------------------------------ -// Аллокатор -// ------------------------------ -extern (C) void* wrap_malloc(void*, uint size) -{ - return malloc(size); -} - -extern (C) void wrap_free(void*, void* ptr) -{ - free(ptr); -} - -extern (C) void* wrap_realloc(void*, void* ptr, uint size) -{ - return realloc(ptr, size); -} - -void installAllocator() -{ - memallocator_t malt; - malt.priv = null; - malt.malloc = &wrap_malloc; - malt.free = &wrap_free; - malt.realloc = &wrap_realloc; - enforce(xdl_set_allocator(&malt) == 0, "xdl_set_allocator failed"); -} - -// ------------------------------ -// Команды -// ------------------------------ -int doDiff(ref MmFile a, ref MmFile b, long ctxlen) -{ - xpparam_t xpp; - xpp.flags = 0; - xdemitconf_t xecfg; - xecfg.ctxlen = ctxlen; - xdemitcb_t ecb; - ecb.priv = cast(void*) cast(size_t) 1; - ecb.outf = &fdOut; - return xdl_diff(&a.mf, &b.mf, &xpp, &xecfg, &ecb); -} - -int doPatch(ref MmFile orig, ref MmFile patch, int mode) -{ - xdemitcb_t outcb; - outcb.priv = cast(void*) cast(size_t) 1; - outcb.outf = &fdOut; - xdemitcb_t rejcb; - rejcb.priv = cast(void*) cast(size_t) 2; - rejcb.outf = &fdOut; - return xdl_patch(&orig.mf, &patch.mf, mode, &outcb, &rejcb); -} - -int doBDiff(ref MmFile a, ref MmFile b, long bsize) -{ - bdiffparam_t bdp; - bdp.bsize = bsize; - xdemitcb_t ecb; - ecb.priv = cast(void*) cast(size_t) 1; - ecb.outf = &fdOut; - return xdl_bdiff(&a.mf, &b.mf, &bdp, &ecb); -} - -int doRaBDiff(ref MmFile a, ref MmFile b) -{ - xdemitcb_t ecb; - ecb.priv = cast(void*) cast(size_t) 1; - ecb.outf = &fdOut; - return xdl_rabdiff(&a.mf, &b.mf, &ecb); -} - -int doBPatch(ref MmFile base, ref MmFile binPatch) -{ - xdemitcb_t ecb; - ecb.priv = cast(void*) cast(size_t) 1; - ecb.outf = &fdOut; - return xdl_bpatch(&base.mf, &binPatch.mf, &ecb); -} - -// ------------------------------ -// CLI -// ------------------------------ -enum Cmd -{ - diff, - patch, - bdiff, - rabdiff, - bpatch -} - -private Cmd parseCmd(string s) -{ - final switch (s) - { - case "diff": - return Cmd.diff; - case "patch": - return Cmd.patch; - case "bdiff": - return Cmd.bdiff; - case "rabdiff": - return Cmd.rabdiff; - case "bpatch": - return Cmd.bpatch; - } -} - -/// Короткая справка -void printUsage(string prg) -{ - stderr.writefln( - "use: %1$s diff [-C N] FROM TO\n" ~ - " %1$s patch ORIG PATCH\n" ~ - " %1$s bdiff [-B N] FROM TO\n" ~ - " %1$s rabdiff FROM TO\n" ~ - " %1$s bpatch ORIG PATCH\n", - prg); -} - -/// Парсинг подкоманды + её опций. Возвращаем уже «очищенный» список позиционных. -struct Parsed -{ - Cmd cmd; - long ctxlen = 3; // для diff - long bsize = 16; // для bdiff - string[] positional; // оставшиеся позиционные (пути) -} - -Parsed parseArgs(string[] args) -{ - enforce(args.length >= 2, "no subcommand"); - auto sub = args[1]; - auto cmd = parseCmd(sub); - - long ctxlen = 3; - long bsize = 16; - bool helpWanted = false; - - // Опции и позиционные ТОЛЬКО после подкоманды - string[] optArgs = args.length > 2 ? args[2 .. $].dup : []; - - // getopt выкинет опции из optArgs и оставит только позиционные - auto r = getopt(optArgs, - "h|help", &helpWanted, - "C", &ctxlen, // актуально для `diff` - "B", &bsize // актуально для `bdiff` - - - - ); - enforce(!helpWanted, "help"); - - Parsed p; - p.cmd = cmd; - p.ctxlen = ctxlen; - p.bsize = bsize; - p.positional = optArgs; // здесь уже только пути - return p; -} - -int main(string[] args) -{ - // 1) Парсим - Parsed p; - try - { - p = parseArgs(args); - } - catch (Exception) - { - printUsage(args[0]); - return 1; - } - if (p.positional.length != 2) - { - // каждая подкоманда требует ровно 2 пути - printUsage(args[0]); - return 1; - } - auto p1 = p.positional[0]; - auto p2 = p.positional[1]; - - // 2) Аллокатор libxdiff - installAllocator(); - - // 3) Готовим входы - auto mf1 = MmFile.create(); - scope (exit) - mf1.deinit(); - auto mf2 = MmFile.create(); - scope (exit) - mf2.deinit(); - - try - { - loadMmFile(mf1, p1); - loadMmFile(mf2, p2); - } - catch (Exception e) - { - stderr.writeln(e.msg); - return 1; - } - - // 4) Запускаем действие - int rc = 0; - final switch (p.cmd) - { - case Cmd.diff: - rc = doDiff(mf1, mf2, p.ctxlen); - break; - case Cmd.patch: - rc = doPatch(mf1, mf2, XDL_PATCH_NORMAL); - break; - case Cmd.bdiff: - rc = doBDiff(mf1, mf2, p.bsize); - break; - case Cmd.rabdiff: - rc = doRaBDiff(mf1, mf2); - break; - case Cmd.bpatch: - rc = doBPatch(mf1, mf2); - break; - } - return rc < 0 ? 2 : 0; + auto res = a.applyPatch(patch); + writeln("apply success: ", res.success); + writeln(res.patched.asSlice()); // печатаем результат + // печатаем как есть (включая заголовок @@ и строки с '-'/'+' и '\n') + write(cast(string) MMFile.fromBlocksMoved(patch).asSlice()); }