ls: print rights.
This commit is contained in:
		
							parent
							
								
									9109646d8f
								
							
						
					
					
						commit
						7ce6b0651c
					
				
					 1 changed files with 207 additions and 8 deletions
				
			
		
							
								
								
									
										215
									
								
								src/ls.zig
									
										
									
									
									
								
							
							
						
						
									
										215
									
								
								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;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        try print_element(element);
 | 
					        if (std.mem.eql(u8, element, "-l")) {
 | 
				
			||||||
 | 
					            options.verbose = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            nb_parameters += 1;
 | 
				
			||||||
 | 
					            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…
	
	Add table
		
		Reference in a new issue