1 module tagion.tools.ifiler.ifiler; 2 import std.process; 3 import std.stdio : writeln, writefln; 4 import core.sys.linux.sys.inotify; 5 import core.sys.posix.unistd; 6 import core.stdc.stdio; 7 import core.stdc.stdlib; 8 import std.stdio; 9 import std.algorithm; 10 import tools = tagion.tools.toolsexception; 11 import tagion.tools.Basic; 12 import std.string : representation; 13 import std.getopt; 14 import tagion.tools.revision; 15 import std.format; 16 import std.array; 17 18 mixin Main!_main; 19 20 struct Inotify { 21 protected { 22 ubyte[] buffer; 23 int fd; 24 int wd; 25 inotify_event* event; 26 bool _empty; 27 } 28 enum EVENT_BUF_LEN = 0x100 * (inotify_event.sizeof + size_t.sizeof); 29 this(string file_name, uint mask, const size_t buf_size = EVENT_BUF_LEN) nothrow @trusted { 30 buffer.length = EVENT_BUF_LEN; 31 file_name ~= '\0'; 32 fd = inotify_init(); 33 wd = inotify_add_watch(fd, &file_name[0], mask); 34 event = cast(inotify_event*)&buffer[0]; 35 } 36 37 const(inotify_event*) wait() nothrow { 38 const length = read(fd, &buffer[0], cast(int) buffer.length); 39 /*checking for error*/ 40 if (length < 0) { 41 enum error_text = "# inotify error #".representation; 42 event.len = cast(uint) error_text.length; 43 auto error = buffer[inotify_event.sizeof .. $]; 44 error[0 .. error_text.length] = error_text; 45 } 46 return event; 47 } 48 49 void close() nothrow @nogc { 50 inotify_rm_watch(fd, wd); 51 52 53 54 .close(fd); 55 } 56 57 const pure { 58 const(char[]) name() const pure @trusted @nogc { 59 60 const result = (cast(char*) event.name.ptr)[0 .. event.len]; 61 const len = result.countUntil(0); 62 return result[0 .. len]; 63 } 64 } 65 } 66 67 import tagion.dart.BlockFile : truncate; 68 69 void icopy(string src, string dest, const size_t block_size) { 70 import std.file; 71 import std.path; 72 73 tools.check(src.exists, format("%s should exist", src)); 74 tools.check(src.isFile, format("%s should be a file", src)); 75 tools.check(dest.extension.empty && dest.exists && dest.isDir, 76 format("%s path does not exists", dest)); 77 if (dest.exists && dest.isDir) { 78 dest = buildPath(dest, src.baseName); 79 } 80 File fin, fout; 81 scope (exit) { 82 fout.flush; 83 fout.close; 84 fin.close; 85 } 86 enum one_MiB = 1 << 20; 87 88 size_t block_count; 89 ubyte[] fin_buf; 90 ubyte[] fout_buf; 91 fin_buf.length = fout_buf.length = block_size; 92 fin = File(src, "r"); 93 if (dest.exists) { 94 95 fout = File(dest, "r+"); 96 while (!fin.eof) { 97 verbose("Verify block %d %f.6MiB", block_count, double(fin.tell) / one_MiB); 98 const fin_tell = fin.tell; 99 const fin_current = fin.rawRead(fin_buf); 100 const fout_tell = fout.tell; 101 const fout_current = fout.rawRead(fout_buf); 102 if (fin_current != fout_current) { 103 fin.seek(fin_tell); 104 fout.seek(fin_tell); 105 break; 106 } 107 block_count++; 108 } 109 } 110 else { 111 fout = File(dest, "w"); 112 } 113 while (!fin.eof) { 114 verbose("Update block %d %f.6MiB", block_count, double(fin.tell) / one_MiB); 115 const fin_tell = fin.tell; 116 const fout_tell = fout.tell; 117 const fin_current = fin.rawRead(fin_buf); 118 fout.rawWrite(fin_current); 119 block_count++; 120 } 121 fout.flush; 122 auto inotify = Inotify(src, IN_CLOSE_WRITE | IN_MODIFY); 123 for (;;) { 124 const event = inotify.wait; 125 const fin_tell = fin.tell; 126 fin.reopen(null, "r"); 127 fin.seek(fin_tell); 128 verbose("Reopen %s %d", src, fin.tell); 129 while (!fin.eof) { 130 verbose("Copy block %d %f.6MiB", block_count, double(fin.tell) / one_MiB); 131 const fout_tell = fout.tell; 132 const fin_current = fin.rawRead(fin_buf); 133 fout.rawWrite(fin_current); 134 fout.flush; 135 block_count++; 136 } 137 if ((event.mask & IN_MODIFY) == 0) { 138 verbose("CLosed"); 139 break; 140 } 141 } 142 143 } 144 145 int _main(string[] args) { 146 immutable program = args[0]; 147 bool version_switch; 148 size_t block_size = 0x400; 149 // auto logo = import("logo.txt"); 150 GetoptResult main_args; 151 try { 152 main_args = getopt(args, 153 std.getopt.config.caseSensitive, 154 std.getopt.config.bundling, 155 "version", "display the version", &version_switch, 156 "v|verbose", "Prints more debug information", &__verbose_switch, 157 "b|block", "Set the block size (Default %s)", &block_size, 158 ); 159 if (version_switch) { 160 revision_text.writeln; 161 return 0; 162 } 163 164 if (main_args.helpWanted) { 165 // writeln(logo); 166 defaultGetoptPrinter( 167 [ 168 "Documentation: https://tagion.org/", 169 "", 170 "Usage:", 171 format("%s [<option>...] <src-file> <dst-file> ", program), 172 "", 173 174 "<option>:", 175 176 ].join("\n"), 177 main_args.options); 178 return 0; 179 } 180 tools.check(args.length == 3, format("%s should have two arguments", program)); 181 icopy(args[1], args[2], block_size); 182 } 183 catch (Exception e) { 184 error(e); 185 return 1; 186 } 187 return 0; 188 }