From f25404f2f2ebf75faef2523a7be28ecf437b7f5c Mon Sep 17 00:00:00 2001 From: Philippe Pittoli Date: Sun, 1 May 2022 15:31:23 +0200 Subject: [PATCH] ls: fewer syscalls --- src/ls.zig | 158 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 125 insertions(+), 33 deletions(-) diff --git a/src/ls.zig b/src/ls.zig index fd1aaa2..8a66424 100644 --- a/src/ls.zig +++ b/src/ls.zig @@ -5,6 +5,9 @@ const path = std.fs.path; const fs = std.fs; const lib = @import("./lib.zig"); +const fmt = std.fmt; +const depth = 3; + const Kind = std.fs.File.Kind; const PermissionsUnix = std.fs.File.PermissionsUnix; @@ -44,6 +47,7 @@ const Options = struct { var options: Options = Options {}; fn print_stats(fpath: []const u8, stats: std.fs.File.Stat) void { + // lib.print("FS STATS: {}\n", .{stats}); // print kind (directory, file, pipe, etc.) switch (stats.kind) { .File => lib.print("-", .{}), @@ -98,12 +102,7 @@ fn print_stats(fpath: []const u8, stats: std.fs.File.Stat) void { return lib.print(" {s}\n", .{fpath}); } -// mode = type (directory, file, symlink, etc.) + rights -pub fn linuxstat_to_fsstat(st: std.os.linux.Stat) std.fs.File.StatError!std.fs.File.Stat { - const atime = st.atime(); - const mtime = st.mtime(); - const ctime = st.ctime(); - +fn fs_kind_from_linux_stat (st: std.os.linux.Stat) Kind { const kind: Kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) { .BLOCK_DEVICE => Kind.BlockDevice, .CHARACTER_DEVICE => Kind.CharacterDevice, @@ -131,6 +130,18 @@ pub fn linuxstat_to_fsstat(st: std.os.linux.Stat) std.fs.File.StatError!std.fs.F }; break :blk .Unknown; }; + return kind; +} + + +// mode = type (directory, file, symlink, etc.) + rights +pub fn linuxstat_to_fsstat(st: std.os.linux.Stat) std.fs.File.StatError!std.fs.File.Stat { + // lib.print("LINUX STATS: {}\n", .{st}); + const atime = st.atime(); + const mtime = st.mtime(); + const ctime = st.ctime(); + + const kind = fs_kind_from_linux_stat (st); const stats = std.fs.File.Stat{ .inode = st.ino, @@ -146,28 +157,113 @@ pub fn linuxstat_to_fsstat(st: std.os.linux.Stat) std.fs.File.StatError!std.fs.F return stats; } +fn print_linux_stats (fpath: []const u8, stats: std.os.linux.Stat) !void { + var buffer = [_]u8 {0} ** (std.fs.MAX_PATH_BYTES + 100); + // var it: u16 = 0; + var fbs = std.io.fixedBufferStream(&buffer); + var writer = fbs.writer(); + + const fmtopts = fmt.FormatOptions{}; + // try std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234")); + // fbs.reset(); + + // print kind (directory, file, pipe, etc.) + const kind = fs_kind_from_linux_stat(stats); + switch (kind) { + .File => try fmt.formatType('-', "c", fmtopts, writer, depth), + .Directory => try fmt.formatType('d', "c", fmtopts, writer, depth), + .CharacterDevice => try fmt.formatType('c', "c", fmtopts, writer, depth), + .BlockDevice => try fmt.formatType('b', "c", fmtopts, writer, depth), + .NamedPipe => try fmt.formatType('p', "c", fmtopts, writer, depth), + .SymLink => try fmt.formatType('s', "c", fmtopts, writer, depth), + .UnixDomainSocket => try fmt.formatType('s', "c", fmtopts, writer, depth), + else => { + if (builtin.os.tag == .solaris) switch (kind) { + .Door => try fmt.formatType('D', "c", fmtopts, writer, depth), // TODO: what to print here? + .EventPort => try fmt.formatType('e', "c", fmtopts, writer, depth), // TODO: what to print here? + else => try fmt.formatType('?', "c", fmtopts, writer, depth), + }; + }, + } + + // 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 fmt.formatType('r', "c", fmtopts, writer, depth), + .write => try fmt.formatType('w', "c", fmtopts, writer, depth), + .execute => try fmt.formatType('x', "c", fmtopts, writer, depth), + } + } + else { + try fmt.formatType('-', "c", fmtopts, writer, depth); + } + } + } + + // TODO: print user & group + try fmt.formatType('\t', "c", fmtopts, writer, depth); + try fmt.formatType(stats.uid, "", fmtopts, writer, depth); + try fmt.formatType('\t', "c", fmtopts, writer, depth); + try fmt.formatType(stats.gid, "", fmtopts, writer, depth); + + // TODO: print size + // TODO: print date last write + + // lib.print("{s}: {}\n", .{fpath, stats}); + + // std.mem.copy (u8, buffer[it..], fpath); + try fmt.formatType('\t', "c", fmtopts, writer, depth); + try fmt.formatType(fpath, "s", fmtopts, writer, depth); + try fmt.formatType('\n', "c", fmtopts, writer, depth); + + return lib.print("{s}", .{fbs.getWritten()}); +} + + fn print_file (fpath: []const u8) !void { if (options.verbose == false) { return lib.print("{s}\n", .{fpath}); } - // First, try to open the file to stat it. - var f: std.fs.File = undefined; - f = switch (path.isAbsolute(fpath)) { - true => std.fs.openFileAbsolute(fpath, .{.mode = .read_only}), - else => cwd.openFile(fpath, .{.mode = .read_only}), - } catch |err| switch(err) { - error.AccessDenied => { - lib.print("access denied\n", .{}); - // try to read the content of the parent directory - return err; - }, + const dname = path.dirname(fpath); + var dir: fs.Dir = cwd.openDir(dname.?, .{}) catch |err| switch (err) { + error.AccessDenied => return lib.print("{s}: access denied\n", .{dname}), else => return err, }; - defer f.close(); - const stats = try f.stat(); - return print_stats (fpath, stats); + const bname = path.basename(fpath); + try print_file_in_dir (dir, bname); + +// // First, try to open the file to stat it. +// var f: std.fs.File = undefined; +// f = switch (path.isAbsolute(fpath)) { +// true => std.fs.openFileAbsolute(fpath, .{.mode = .read_only}), +// else => cwd.openFile(fpath, .{.mode = .read_only}), +// } catch |err| switch(err) { +// error.AccessDenied => { +// lib.print("access denied\n", .{}); +// // try to read the content of the parent directory +// return err; +// }, +// else => return err, +// }; +// defer f.close(); +// +// const stats = try f.stat(); +// return print_stats (fpath, stats); } fn print_file_in_dir (dir: fs.Dir, entryname: []const u8) !void { @@ -176,37 +272,33 @@ fn print_file_in_dir (dir: fs.Dir, entryname: []const u8) !void { } const stats = statFile(dir, entryname); - return print_stats (entryname, try linuxstat_to_fsstat(stats)); + // return print_stats (entryname, try linuxstat_to_fsstat(stats)); + return try print_linux_stats (entryname, stats); } // TODO: improve this to use the fstatat syscall instead of making 2 syscalls here. pub fn statFile(self: fs.Dir, sub_path: []const u8) std.os.linux.Stat { var stat: std.os.linux.Stat = undefined; - var t = [_:0]u8{0} ** 1000; + var t = [_:0]u8{0} ** std.fs.MAX_PATH_BYTES; std.mem.copy (u8, t[0..], sub_path); - var p = t[0..sub_path.len+1:0]; - // var p = sub_path[0..sub_path.len :0]; + var p = t[0..sub_path.len+1:0]; // add a final \0 at the end of the string, and make the type checker happy _ = std.os.linux.fstatat(self.fd, p, &stat, 0); // returns usize return stat; } // Either print directory's content or file. -fn print_element(fpath: []const u8) !void { - - // First: - // if directory: try to open the directory. - // else: try to open the parent directory. - +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. + // 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); @@ -232,13 +324,13 @@ pub fn ls() !void { } else { nb_parameters += 1; - try print_element(element); + try print_directory(element); } } // case there were no parameter, print current directory if (nb_parameters == 0) { - try print_element("."); + try print_directory("."); } }