ls.ha (3397B)
1 use fmt; 2 use os; 3 use getopt; 4 use fs; 5 use strings; 6 use io; 7 8 use color; 9 use util; 10 11 type mode = enum uint { 12 DIR = 1 << 0, 13 COLOR = 1 << 1, 14 ALL = 1 << 2, 15 }; 16 17 let script = false; 18 let color = true; 19 let all = false; 20 let showdirs = true; 21 let path = ""; 22 23 fn bit(ent: uint, m: uint) bool = ((ent & m) == m); 24 25 fn getcol(ent: str, link: bool) color::col = { 26 let m = os::stat(ent)!.mode; 27 28 if (bit(m, fs::mode::BLK) || bit(m, fs::mode::CHR)) 29 return (color::colors::YELLOW, color::mode::BOLD) 30 31 else if (bit(m, fs::mode::DIR)) { 32 if (link) 33 return (color::colors::BLUE, color::mode::ITAL); 34 return (color::colors::BLUE, color::mode::BOLD); 35 } else if (link) 36 return (color::colors::NORM, color::mode::ITAL) 37 38 else if (bit(m, fs::mode::OTHER_X) || 39 bit(m, fs::mode::USER_X) || 40 bit(m, fs::mode::GROUP_X)) return (color::colors::GREEN, color::mode::BOLD) 41 42 43 44 else return (color::colors::NORM, color::mode::NORM); 45 }; 46 47 fn printent(ent: fs::dirent, opts: mode, file: str) void = { 48 if (!bit(opts, mode::ALL)) { 49 match (strings::index(ent.name, '.')) { 50 case void => yield; 51 case let i: size => if (i == 0) return; 52 }; 53 }; 54 55 let res = fmt::asprint(ent.name)!; 56 defer free(res); 57 58 if (bit(opts, mode::DIR) && bit(ent.ftype, fs::mode::DIR) && !bit(ent.ftype, fs::mode::BLK)) 59 res = fmt::asprintf("{}{}", res, "/")!; 60 61 let link = false; 62 if (bit(ent.ftype, fs::mode::LINK)) { 63 link = true; 64 file = os::readlink(ent.name)!; 65 if (bit(opts, mode::DIR) && bit(os::stat(file)!.mode, fs::mode::DIR)) 66 res = fmt::asprintf("{}{}", res, "/")!; 67 }; 68 69 70 if (bit(opts, mode::COLOR)) 71 color::println(res, getcol(file, link)) 72 else 73 fmt::println(res)!; 74 }; 75 76 export fn main() void = { 77 const cmd = getopt::parse(os::args, 78 "list directory contents", 79 ('s', "scriptable output, equivelant to -aCD"), 80 ('a', "show all files, including hidden files"), 81 ('c', "color output (default)"), 82 ('d', "show dirs with '/' (default)"), 83 ('A', "don't show all files (default)"), 84 ('C', "no color output"), 85 ('D', "don't show dirs with '/'"), 86 "where" 87 ); 88 defer getopt::finish(&cmd); 89 90 for (let opt .. cmd.opts) { 91 switch (opt.0) { 92 case 's' => script = true; 93 case 'a' => all = true; 94 case 'c' => color = true; 95 case 'A' => all = false; 96 case 'C' => color = false; 97 case 'd' => showdirs = true; 98 case 'D' => showdirs = false; 99 case => abort(); 100 }; 101 }; 102 103 if (len(cmd.args) > 1) util::die("can only list one directory") 104 else if (len(cmd.args) == 1) path = cmd.args[0] 105 else path = "./"; 106 107 if (script) { 108 all = true; 109 color = false; 110 showdirs = false; 111 }; 112 let dir = match (os::diropen(path)) { 113 case let fs: *fs::fs => os::chdir(fs)!; 114 case let e: fs::error => util::die(fs::strerror(e)); 115 }; 116 117 let dir = match (os::diropen("./")) { 118 case let fs: *fs::fs => yield fs; 119 case let e: fs::error => util::die(fs::strerror(e)); 120 }; 121 defer fs::close(dir); 122 123 let iter = match(fs::iter(dir, "./")) { 124 case let iter: *fs::iterator => yield iter; 125 case let e: fs::error => util::die(fs::strerror(e)); 126 }; 127 defer fs::finish(iter); 128 129 let dirs: []fs::dirent = []; 130 defer free(dirs); 131 132 for (let dirent = fs::next(iter)!; dirent is fs::dirent; dirent = fs::next(iter)!) { 133 let d = dirent as fs::dirent; 134 append(dirs, d)!; 135 }; 136 137 for (let ent .. dirs) { 138 let opts: mode = 0; 139 140 if (all) opts |= mode::ALL; 141 if (color) opts |= mode::COLOR; 142 if (showdirs) opts |= mode::DIR; 143 144 printent(ent, opts, ent.name); 145 }; 146 };