const std = @import("std"); const os = std.os; const builtin = @import("builtin"); const path = std.fs.path; const fs = std.fs; const lib = @import("./lib.zig"); const fmt = std.fmt; const Kind = std.fs.File.Kind; const PermissionsUnix = std.fs.File.PermissionsUnix; // WON'T: human output => use human command instead // TODO: error management. pub const cwd = fs.cwd(); const Options = struct { verbose: bool = false, }; var options: Options = Options {}; fn print_stats(fpath: []const u8, stats: os.system.Stat) !void { var buffer = [_]u8 {0} ** (std.fs.MAX_PATH_BYTES + 100); var writer = lib.FixedBufferWriter.init(&buffer); // print kind (directory, file, pipe, etc.) const kind = fs.File.Stat.systemStatKindToFsKind(stats); switch (kind) { .File => try writer.put("-"), .Directory => try writer.put("d"), .CharacterDevice => try writer.put("c"), .BlockDevice => try writer.put("b"), .NamedPipe => try writer.put("p"), .SymLink => try writer.put("s"), .UnixDomainSocket => try writer.put("s"), else => { if (builtin.os.tag == .solaris) switch (kind) { .Door => try writer.put("D"), // TODO: what to print here? .EventPort => try writer.put("e"), // TODO: what to print here? else => try writer.put("?"), }; }, } // print rights const perms = PermissionsUnix.unixNew(stats.mode); const classes = [_]PermissionsUnix.Class { PermissionsUnix.Class.user, PermissionsUnix.Class.group, PermissionsUnix.Class.other }; const rights = [_]PermissionsUnix.Permission { PermissionsUnix.Permission.read, PermissionsUnix.Permission.write, PermissionsUnix.Permission.execute }; for (classes) |c| { for (rights) |r| { if (perms.unixHas(c,r)) { switch(r) { .read => try writer.put("r"), .write => try writer.put("w"), .execute => try writer.put("x"), } } else { try writer.put("-"); } } } // TODO: print user & group, not just uid and gid try writer.putf("\t{}\t{}\t", .{stats.uid, stats.gid}); try writer.putf("{}\t", .{stats.size}); try writer.putf("{}\t", .{stats.mtim.tv_sec}); try writer.putf("{s}\n", .{fpath}); return lib.print("{s}", .{writer.getWritten()}); } fn print_file (fpath: []const u8) !void { if (options.verbose == false) { return lib.print("{s}\n", .{fpath}); } const dname = path.dirname(fpath); var dpath = dname orelse "."; var dir: fs.Dir = cwd.openDir(dpath, .{}) catch |err| switch (err) { error.AccessDenied => return lib.print("{s}: access denied\n", .{dname}), else => return err, }; const bname = path.basename(fpath); try print_file_in_dir (dir, bname); } fn print_file_in_dir (dir: fs.Dir, entryname: []const u8) !void { if (options.verbose == false) { return lib.print("{s}\n", .{entryname}); } return try print_stats (entryname, try dir.fstatat(entryname)); } // Either print directory's content or file. fn print_directory(fpath: []const u8) !void { var dir: fs.Dir = cwd.openDir(fpath, .{.iterate = true}) catch |err| switch (err) { error.AccessDenied => return lib.print("{s}: access denied\n", .{fpath}), error.NotDir => { // fpath is a simple file, just print the file infos return try print_file(fpath); }, else => return err, }; // fpath is a directory: print info on each file var dir_it = dir.iterate(); while (try dir_it.next()) |entry| { try print_file_in_dir (dir, entry.name); } } pub fn ls() !void { var cli = try lib.CLI.init(); defer cli.deinit(); // Skipping the executable binary name. var arg_idx: usize = 1; var nb_parameters: i32 = 0; // case there are parameters while(arg_idx < cli.args.len) { const element = cli.nextArg(&arg_idx) orelse { lib.warn("Null argument\n", .{}); return error.InvalidArgs; }; if (std.mem.eql(u8, element, "-l")) { options.verbose = true; } else { nb_parameters += 1; try print_directory(element); } } // case there were no parameter, print current directory if (nb_parameters == 0) { try print_directory("."); } } pub fn main() !void { try ls(); }