rm.ha (1363B)
1 use fmt; 2 use fs; 3 use getopt; 4 use main; 5 use os; 6 7 type config = struct { 8 status: int, 9 force: bool, 10 recur: bool, 11 }; 12 13 export fn utilmain() (void | main::error) = { 14 const cmd = getopt::parse(os::args, 15 "remove files", 16 ('f', "remove read-only files and ignore errors"), 17 ('r', "remove directories and their contents"), 18 "files..."); 19 defer getopt::finish(&cmd); 20 21 let conf = config { ... }; 22 for (let i = 0z; i < len(cmd.opts); i += 1) { 23 switch (cmd.opts[i].0) { 24 case 'f' => 25 conf.force = true; 26 case 'r' => 27 conf.recur = true; 28 case => abort(); 29 }; 30 }; 31 32 for (let i = 0z; i < len(cmd.args); i += 1) { 33 const target = cmd.args[i]; 34 match (remove(&conf, target)) { 35 case let err: fs::error => 36 conf.status = 1; 37 if (!conf.force) { 38 fmt::errorfln("{}: Error: {}", 39 target, fs::strerror(err))!; 40 }; 41 case void => void; 42 }; 43 }; 44 45 os::exit(conf.status); 46 }; 47 48 fn remove(conf: *config, path: str) (void | fs::error) = { 49 const st = os::stat(path)?; 50 if (fs::isdir(st.mode) && !conf.recur) { 51 if (!conf.force) { 52 conf.status = 1; 53 fmt::errorfln("{}: skipping directory", path)?; 54 }; 55 return; 56 }; 57 58 if (!os::access(path, os::amode::W_OK)? && !conf.force) { 59 conf.status = 1; 60 fmt::errorfln("{}: skipping read-only file", path)?; 61 return; 62 }; 63 64 if (fs::isdir(st.mode)) { 65 os::rmdirall(path)?; 66 } else { 67 os::remove(path)?; 68 }; 69 };