ls: print rights.
parent
9109646d8f
commit
7ce6b0651c
213
src/ls.zig
213
src/ls.zig
|
@ -1,23 +1,215 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const os = std.os;
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const path = std.fs.path;
|
||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
const lib = @import("./lib.zig");
|
const lib = @import("./lib.zig");
|
||||||
|
|
||||||
|
const Kind = std.fs.File.Kind;
|
||||||
|
const PermissionsUnix = std.fs.File.PermissionsUnix;
|
||||||
|
|
||||||
|
// // /absolute/path/file => fpath + sep + entry.name
|
||||||
|
// std.mem.copy (u8, fullname[0..], fpath);
|
||||||
|
// fullname[fpath.len] = path.sep; // '/' on Unix-like systems, '\' on Windows.
|
||||||
|
// std.mem.copy (u8, fullname[fpath.len+1..], entry.name);
|
||||||
|
// try print_file(fullname[0..fpath.len+1+entry.name.len]);
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
// .size = @bitCast(u64, info.StandardInformation.EndOfFile),
|
||||||
|
// .mode = 0,
|
||||||
|
// .atime = LastAccessTime
|
||||||
|
// .mtime = LastWriteTime
|
||||||
|
// .ctime = CreationTime
|
||||||
|
|
||||||
|
|
||||||
// WON'T: human output => use human command instead
|
// WON'T: human output => use human command instead
|
||||||
// TODO: error management.
|
// TODO: error management.
|
||||||
// TODO: verbose output (-l).
|
// TODO: verbose output (-l).
|
||||||
|
|
||||||
pub const cwd = fs.cwd();
|
pub const cwd = fs.cwd();
|
||||||
|
|
||||||
// Either print directory's content or file.
|
const Sorting = enum {
|
||||||
fn print_element(path: []const u8) !void {
|
None,
|
||||||
var dir: fs.Dir = cwd.openDir(path, .{.iterate = true}) catch |err| switch (err) {
|
DateCreation, // TODO
|
||||||
error.NotDir => return lib.print("{s}\n", .{path}),
|
DateLastAccess, // TODO
|
||||||
|
DateLastWrite, // TODO
|
||||||
|
Size, // TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
const Options = struct {
|
||||||
|
sorting: Sorting = Sorting.None,
|
||||||
|
verbose: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
var options: Options = Options {};
|
||||||
|
|
||||||
|
fn print_stats(fpath: []const u8, stats: std.fs.File.Stat) void {
|
||||||
|
// print kind (directory, file, pipe, etc.)
|
||||||
|
switch (stats.kind) {
|
||||||
|
.File => lib.print("-", .{}),
|
||||||
|
.Directory => lib.print("d", .{}),
|
||||||
|
.CharacterDevice => lib.print("c", .{}),
|
||||||
|
.BlockDevice => lib.print("b", .{}),
|
||||||
|
.NamedPipe => lib.print("p", .{}),
|
||||||
|
.SymLink => lib.print("s", .{}),
|
||||||
|
.UnixDomainSocket => lib.print("s", .{}),
|
||||||
|
else => {
|
||||||
|
if (builtin.os.tag == .solaris) switch (stats.kind) {
|
||||||
|
.Door => lib.print("D", .{}), // TODO: what to print here?
|
||||||
|
.EventPort => lib.print("e", .{}), // TODO: what to print here?
|
||||||
|
else => lib.print("?", .{}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 => lib.print("r", .{}),
|
||||||
|
.write => lib.print("w", .{}),
|
||||||
|
.execute => lib.print("x", .{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lib.print("-", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: print user & group
|
||||||
|
// TODO: print size
|
||||||
|
// TODO: print date last write
|
||||||
|
|
||||||
|
// lib.print("{s}: {}\n", .{fpath, stats});
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
const kind: Kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) {
|
||||||
|
.BLOCK_DEVICE => Kind.BlockDevice,
|
||||||
|
.CHARACTER_DEVICE => Kind.CharacterDevice,
|
||||||
|
.DIRECTORY => Kind.Directory,
|
||||||
|
.SYMBOLIC_LINK => Kind.SymLink,
|
||||||
|
.REGULAR_FILE => Kind.File,
|
||||||
|
.SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket,
|
||||||
|
else => Kind.Unknown,
|
||||||
|
} else blk: {
|
||||||
|
const m = st.mode & os.S.IFMT;
|
||||||
|
switch (m) {
|
||||||
|
os.S.IFBLK => break :blk Kind.BlockDevice,
|
||||||
|
os.S.IFCHR => break :blk Kind.CharacterDevice,
|
||||||
|
os.S.IFDIR => break :blk Kind.Directory,
|
||||||
|
os.S.IFIFO => break :blk Kind.NamedPipe,
|
||||||
|
os.S.IFLNK => break :blk Kind.SymLink,
|
||||||
|
os.S.IFREG => break :blk Kind.File,
|
||||||
|
os.S.IFSOCK => break :blk Kind.UnixDomainSocket,
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
if (builtin.os.tag == .solaris) switch (m) {
|
||||||
|
os.S.IFDOOR => break :blk Kind.Door,
|
||||||
|
os.S.IFPORT => break :blk Kind.EventPort,
|
||||||
|
else => {},
|
||||||
|
};
|
||||||
|
break :blk .Unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
const stats = std.fs.File.Stat{
|
||||||
|
.inode = st.ino,
|
||||||
|
.size = @bitCast(u64, st.size),
|
||||||
|
.mode = st.mode,
|
||||||
|
.kind = kind,
|
||||||
|
.atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
|
||||||
|
.mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
|
||||||
|
.ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
|
||||||
|
};
|
||||||
|
|
||||||
|
// lib.print("{}\n", .{stats});
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
},
|
||||||
else => return err,
|
else => return err,
|
||||||
};
|
};
|
||||||
var dir_it = dir.iterate();
|
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 {
|
||||||
|
if (options.verbose == false) {
|
||||||
|
return lib.print("{s}\n", .{entryname});
|
||||||
|
}
|
||||||
|
|
||||||
|
const stats = statFile(dir, entryname);
|
||||||
|
return print_stats (entryname, try linuxstat_to_fsstat(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;
|
||||||
|
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];
|
||||||
|
_ = 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.
|
||||||
|
|
||||||
|
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 => {
|
||||||
|
return try print_file(fpath);
|
||||||
|
},
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
|
||||||
|
// fpath is a directory.
|
||||||
|
var dir_it = dir.iterate();
|
||||||
while (try dir_it.next()) |entry| {
|
while (try dir_it.next()) |entry| {
|
||||||
lib.print("{s}\n", .{entry.name});
|
try print_file_in_dir (dir, entry.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +219,7 @@ pub fn ls() !void {
|
||||||
|
|
||||||
// Skipping the executable binary name.
|
// Skipping the executable binary name.
|
||||||
var arg_idx: usize = 1;
|
var arg_idx: usize = 1;
|
||||||
|
var nb_parameters: i32 = 0;
|
||||||
|
|
||||||
// case there are parameters
|
// case there are parameters
|
||||||
while(arg_idx < cli.args.len) {
|
while(arg_idx < cli.args.len) {
|
||||||
|
@ -34,11 +227,17 @@ pub fn ls() !void {
|
||||||
lib.warn("Null argument\n", .{});
|
lib.warn("Null argument\n", .{});
|
||||||
return error.InvalidArgs;
|
return error.InvalidArgs;
|
||||||
};
|
};
|
||||||
|
if (std.mem.eql(u8, element, "-l")) {
|
||||||
|
options.verbose = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nb_parameters += 1;
|
||||||
try print_element(element);
|
try print_element(element);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// case there were no parameter, print current directory
|
// case there were no parameter, print current directory
|
||||||
if (cli.args.len == 1) {
|
if (nb_parameters == 0) {
|
||||||
try print_element(".");
|
try print_element(".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue