diff --git a/src/ls.zig b/src/ls.zig
index 57e3d6a..fd1aaa2 100644
--- a/src/ls.zig
+++ b/src/ls.zig
@@ -1,23 +1,215 @@
 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 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
 // TODO: error management.
 // TODO: verbose output (-l).
 
 pub const cwd = fs.cwd();
 
-// Either print directory's content or file.
-fn print_element(path: []const u8) !void {
-    var dir: fs.Dir = cwd.openDir(path, .{.iterate = true}) catch |err| switch (err) {
-        error.NotDir => return lib.print("{s}\n", .{path}),
+const Sorting = enum {
+    None,
+    DateCreation,   // TODO
+    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,
     };
-    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| {
-        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.
     var arg_idx: usize = 1;
+    var nb_parameters: i32 = 0;
 
     // case there are parameters
     while(arg_idx < cli.args.len) {
@@ -34,11 +227,17 @@ pub fn ls() !void {
             lib.warn("Null argument\n", .{});
             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
-    if (cli.args.len == 1) {
+    if (nb_parameters == 0) {
         try print_element(".");
     }
 }