ls: fewer syscalls

master
Philippe Pittoli 2022-05-01 15:31:23 +02:00
parent 7ce6b0651c
commit f25404f2f2
1 changed files with 125 additions and 33 deletions

View File

@ -5,6 +5,9 @@ const path = std.fs.path;
const fs = std.fs; const fs = std.fs;
const lib = @import("./lib.zig"); const lib = @import("./lib.zig");
const fmt = std.fmt;
const depth = 3;
const Kind = std.fs.File.Kind; const Kind = std.fs.File.Kind;
const PermissionsUnix = std.fs.File.PermissionsUnix; const PermissionsUnix = std.fs.File.PermissionsUnix;
@ -44,6 +47,7 @@ const Options = struct {
var options: Options = Options {}; var options: Options = Options {};
fn print_stats(fpath: []const u8, stats: std.fs.File.Stat) void { fn print_stats(fpath: []const u8, stats: std.fs.File.Stat) void {
// lib.print("FS STATS: {}\n", .{stats});
// print kind (directory, file, pipe, etc.) // print kind (directory, file, pipe, etc.)
switch (stats.kind) { switch (stats.kind) {
.File => lib.print("-", .{}), .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}); return lib.print(" {s}\n", .{fpath});
} }
// mode = type (directory, file, symlink, etc.) + rights fn fs_kind_from_linux_stat (st: std.os.linux.Stat) Kind {
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();
const kind: Kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) { const kind: Kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) {
.BLOCK_DEVICE => Kind.BlockDevice, .BLOCK_DEVICE => Kind.BlockDevice,
.CHARACTER_DEVICE => Kind.CharacterDevice, .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; 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{ const stats = std.fs.File.Stat{
.inode = st.ino, .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; 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 { fn print_file (fpath: []const u8) !void {
if (options.verbose == false) { if (options.verbose == false) {
return lib.print("{s}\n", .{fpath}); return lib.print("{s}\n", .{fpath});
} }
// First, try to open the file to stat it. const dname = path.dirname(fpath);
var f: std.fs.File = undefined; var dir: fs.Dir = cwd.openDir(dname.?, .{}) catch |err| switch (err) {
f = switch (path.isAbsolute(fpath)) { error.AccessDenied => return lib.print("{s}: access denied\n", .{dname}),
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, else => return err,
}; };
defer f.close();
const stats = try f.stat(); const bname = path.basename(fpath);
return print_stats (fpath, stats); 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 { 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); 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. // 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 { pub fn statFile(self: fs.Dir, sub_path: []const u8) std.os.linux.Stat {
var stat: std.os.linux.Stat = undefined; 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); std.mem.copy (u8, t[0..], sub_path);
var p = t[0..sub_path.len+1: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
// var p = sub_path[0..sub_path.len :0];
_ = std.os.linux.fstatat(self.fd, p, &stat, 0); // returns usize _ = std.os.linux.fstatat(self.fd, p, &stat, 0); // returns usize
return stat; return stat;
} }
// Either print directory's content or file. // Either print directory's content or file.
fn print_element(fpath: []const u8) !void { fn print_directory(fpath: []const u8) !void {
// First:
// if directory: try to open the directory.
// else: try to open the parent directory.
var dir: fs.Dir = cwd.openDir(fpath, .{.iterate = true}) catch |err| switch (err) { var dir: fs.Dir = cwd.openDir(fpath, .{.iterate = true}) catch |err| switch (err) {
error.AccessDenied => return lib.print("{s}: access denied\n", .{fpath}), error.AccessDenied => return lib.print("{s}: access denied\n", .{fpath}),
error.NotDir => { error.NotDir => {
// fpath is a simple file, just print the file infos
return try print_file(fpath); return try print_file(fpath);
}, },
else => return err, else => return err,
}; };
// fpath is a directory. // fpath is a directory: print info on each file
var dir_it = dir.iterate(); var dir_it = dir.iterate();
while (try dir_it.next()) |entry| { while (try dir_it.next()) |entry| {
try print_file_in_dir (dir, entry.name); try print_file_in_dir (dir, entry.name);
@ -232,13 +324,13 @@ pub fn ls() !void {
} }
else { else {
nb_parameters += 1; nb_parameters += 1;
try print_element(element); try print_directory(element);
} }
} }
// case there were no parameter, print current directory // case there were no parameter, print current directory
if (nb_parameters == 0) { if (nb_parameters == 0) {
try print_element("."); try print_directory(".");
} }
} }