zig-toybox/src/ls.zig

157 lines
4.6 KiB
Zig

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();
}