1 module filemanager; 2 import nspire; 3 import nspire.device; 4 import std.stdio; 5 6 int filemanagerFun(NSpire nspire, DeviceInfo deviceInfo, string[] args) { 7 int error; 8 9 if (args.length < 1) { 10 help(); 11 return 1; 12 } 13 14 switch (args[0]) { 15 case "ls" : { 16 error = ls(nspire, args[1..$]); 17 break; 18 } 19 20 case "info" : { 21 error = info(nspire, args[1..$]); 22 break; 23 } 24 25 case "cp" : { 26 error = cp(nspire, deviceInfo, args[1..$]); 27 break; 28 } 29 30 case "mv" : { 31 error = mv(nspire, deviceInfo, args[1..$]); 32 break; 33 } 34 35 case "rm" : { 36 error = rm(nspire, deviceInfo, args[1..$]); 37 break; 38 } 39 40 case "mkdir" : { 41 error = mkdir(nspire, args[1..$]); 42 break; 43 } 44 45 case "rmdir" : { 46 error = rmdir(nspire, args[1..$]); 47 break; 48 } 49 50 case "push" : { 51 error = push(nspire, deviceInfo, args[1..$]); 52 break; 53 } 54 55 case "pull" : { 56 error = pull(nspire, deviceInfo, args[1..$]); 57 break; 58 } 59 60 default : { 61 help(); 62 return 1; 63 } 64 } 65 66 return error; 67 } 68 69 int ls(NSpire nspire, string[] args) { 70 import std.datetime.systime; 71 72 int error; 73 74 if (args.length < 1) { 75 args ~= "/"; 76 } 77 78 auto dirs = nspire.dirList(args[0], error); 79 80 if (error != ErrorCodes.NSPIRE_ERR_SUCCESS) { 81 writeln("Error: ", NSpire.errorStr(error)); 82 return error; 83 } 84 85 foreach (dir; dirs) { 86 auto name = dir.type == DirType.NSPIRE_DIR ? "\033[1m\x1B[94m" ~ dir.name ~ "\x1B[0m\033[22m" : dir.name; 87 writeln(dir.size, "\t", SysTime.fromUnixTime(dir.date), "\t", name); 88 } 89 90 return 0; 91 } 92 93 int info(NSpire nspire, string[] args) { 94 import std.datetime.systime; 95 import std.math : pow; 96 97 int error; 98 99 if (args.length < 1) { 100 help(); 101 return 1; 102 } 103 104 auto attr = nspire.getAttr(args[0], error); 105 106 if (error != ErrorCodes.NSPIRE_ERR_SUCCESS) { 107 writeln("Error: ", NSpire.errorStr(error)); 108 return error; 109 } 110 111 writeln("Name: ", attr.name); 112 writeln("Size: ", attr.size, " bytes or ", cast(double) attr.size / pow(2, 10), " KiB"); 113 writeln("Date: ", SysTime.fromUnixTime(attr.date)); 114 writeln("Type: ", attr.type == DirType.NSPIRE_DIR ? "directory" : "file"); 115 116 return 0; 117 } 118 119 int cp(NSpire nspire, DeviceInfo deviceInfo, string[] args) { 120 if (args.length < 2) { 121 help(); 122 return 1; 123 } 124 125 if (!checkExtFile(deviceInfo, args[0], args[1])) { 126 return ErrorCodes.NSPIRE_ERR_INVALID; 127 } 128 129 auto error = nspire.copyFile(args[0], args[1]); 130 131 if (error != ErrorCodes.NSPIRE_ERR_SUCCESS) { 132 writeln("Error: ", NSpire.errorStr(error)); 133 return error; 134 } 135 136 return 0; 137 } 138 139 int mv(NSpire nspire, DeviceInfo deviceInfo, string[] args) { 140 import std.algorithm.searching : endsWith; 141 142 if (args.length < 2) { 143 help(); 144 return 1; 145 } 146 147 if (!checkExtFile(deviceInfo, args[0], args[1])) { 148 return ErrorCodes.NSPIRE_ERR_INVALID; 149 } 150 151 auto error = nspire.moveFile(args[0], args[1]); 152 153 if (error != ErrorCodes.NSPIRE_ERR_SUCCESS) { 154 writeln("Error: ", NSpire.errorStr(error)); 155 return error; 156 } 157 158 return 0; 159 } 160 161 int rm(NSpire nspire, DeviceInfo deviceInfo, string[] args) { 162 import std.algorithm.searching : endsWith; 163 164 if (args.length < 1) { 165 help(); 166 return 1; 167 } 168 169 if (!checkExtFile(deviceInfo, args[0])) { 170 return ErrorCodes.NSPIRE_ERR_INVALID; 171 } 172 173 auto error = nspire.deleteFile(args[0]); 174 175 if (error != ErrorCodes.NSPIRE_ERR_SUCCESS) { 176 writeln("Error: ", NSpire.errorStr(error)); 177 return error; 178 } 179 180 return 0; 181 } 182 183 int mkdir(NSpire nspire, string[] args) { 184 if (args.length < 1) { 185 help(); 186 return 1; 187 } 188 189 auto error = nspire.createDirectory(args[0]); 190 191 if (error != ErrorCodes.NSPIRE_ERR_SUCCESS) { 192 writeln("Error: ", NSpire.errorStr(error)); 193 return error; 194 } 195 196 return 0; 197 } 198 199 int rmdir(NSpire nspire, string[] args) { 200 if (args.length < 1) { 201 help(); 202 return 1; 203 } 204 205 auto error = nspire.deleteDirectory(args[0]); 206 207 if (error != ErrorCodes.NSPIRE_ERR_SUCCESS) { 208 writeln("Error: ", NSpire.errorStr(error)); 209 return error; 210 } 211 212 return 0; 213 } 214 215 int push(NSpire nspire, DeviceInfo deviceInfo, string[] args) { 216 import std.algorithm.searching : endsWith; 217 import std.file : read, FileException; 218 219 if (args.length < 2) { 220 help(); 221 return 1; 222 } 223 224 void[] fileBuf; 225 226 if (!checkExtFile(deviceInfo, args[0], args[1])) { 227 return ErrorCodes.NSPIRE_ERR_INVALID; 228 } 229 230 try { 231 fileBuf = read(args[0]); 232 } catch (FileException e) { 233 writeln("Error: failed file read on local side"); 234 return ErrorCodes.NSPIRE_ERR_INVALID; 235 } 236 237 auto error = nspire.writeFile(args[1], fileBuf); 238 239 if (error != ErrorCodes.NSPIRE_ERR_SUCCESS) { 240 writeln("Error: ", NSpire.errorStr(error)); 241 return error; 242 } 243 244 return 0; 245 } 246 247 int pull(NSpire nspire, DeviceInfo deviceInfo, string[] args) { 248 import std.algorithm.searching : endsWith; 249 import std.file : write, FileException; 250 251 if (args.length < 2) { 252 help(); 253 return 1; 254 } 255 256 void[] fileBuf; 257 int error; 258 size_t readBytes; 259 260 if (!checkExtFile(deviceInfo, args[0], args[1])) { 261 return ErrorCodes.NSPIRE_ERR_INVALID; 262 } 263 264 auto attr = nspire.getAttr(args[0], error); 265 266 if (error != ErrorCodes.NSPIRE_ERR_SUCCESS) { 267 writeln("Error: ", NSpire.errorStr(error)); 268 return error; 269 } 270 271 fileBuf = new void[attr.size]; 272 273 error = nspire.readFile(args[0], fileBuf, readBytes); 274 275 if (error != ErrorCodes.NSPIRE_ERR_SUCCESS) { 276 writeln("Error: ", NSpire.errorStr(error)); 277 return error; 278 } 279 280 if (readBytes < attr.size) { 281 writeln("033[1mWarning: the received file is smaller than expected, it may be corrupt\033[22m"); 282 } 283 284 if (readBytes > attr.size) { 285 writeln("Error: ", NSpire.errorStr(ErrorCodes.NSPIRE_ERR_NOMEM)); 286 return ErrorCodes.NSPIRE_ERR_NOMEM; 287 } 288 289 try { 290 write(args[1], fileBuf); 291 } catch (FileException e) { 292 writeln("Error: failed file write on local side"); 293 return ErrorCodes.NSPIRE_ERR_INVALID; 294 } 295 296 return 0; 297 } 298 299 void help() { 300 writeln("NSpire Tools - filemanager\n"); 301 writeln("Usage: nspire-tools filemanager [function [arguments]...]"); 302 writeln("\tor: nspire-filemanager [function [arguments]...]...\n"); 303 writeln("Currently defined functions:"); 304 writeln("\tls (default /)"); 305 writeln("\tls <path>"); 306 writeln("\tinfo <path>"); 307 writeln("\tcp <source path> <destination path>"); 308 writeln("\tmv <source path> <destination path>"); 309 writeln("\trm <path>"); 310 writeln("\tmkdir <path>"); 311 writeln("\trmdir <path>"); 312 writeln("\tpush <local path> <remote path>"); 313 writeln("\tpull <remote path> <local path>"); 314 } 315 316 bool checkExtFile(DeviceInfo deviceInfo, string[] paths...) { 317 import std.string : fromStringz; 318 return checkExt(deviceInfo.fileExtensions.file.ptr.fromStringz.idup, paths); 319 } 320 321 bool checkExtOs(DeviceInfo deviceInfo, string[] paths...) { 322 import std.string : fromStringz; 323 return checkExt(deviceInfo.fileExtensions.os.ptr.fromStringz.idup, paths); 324 } 325 326 private bool checkExt(string ext, string[] paths...) { 327 foreach (path; paths) { 328 if (path.length < 4 || path[$-ext.length..$] != ext) { 329 writeln("Error: files must have an ", ext, " extension"); 330 return false; 331 } 332 } 333 334 return true; 335 }