Compare commits

..

10 commits

7 changed files with 246 additions and 34 deletions

30
Makefile Normal file
View file

@ -0,0 +1,30 @@
all: global-compilation
OPT_CACHE = --global-cache-dir /tmp/.global-cache-dir
OPT_MODE = ReleaseSmall
#OPT_MODE = ReleaseFast
#OPT_MODE = ReleaseSafe
#OPT_MODE = Debug
OPT_STRIP = --strip
opts = -O $(OPT_MODE) $(OPT_CACHE) $(OPT_STRIP)
individual_apps != ls src/*.zig |\
grep -v lib.zig |\
grep -v zig-cache |\
grep -v test.zig |\
sed "s_src/__" |\
sed "s/.zig$$//"
$(individual_apps):
@echo $@
@zig build-exe $(opts) src/$@.zig
individual-compilation:
@echo "individual apps: $(individual_apps)"
@for i in $(individual_apps); do echo "$${i}"; zig build-exe $(opts) src/$${i}.zig; done
global-compilation: src/main.zig
zig build-exe $(opts) src/main.zig
.PHONY: $(global-compilation) $(individual-compilation)

View file

@ -1,5 +1,5 @@
const std = @import("std");
const lib = @import("./lib.zig");
const stdout = std.io.getStdOut().writer();
pub fn main() anyerror!void {
const args = try std.process.argsAlloc(std.heap.page_allocator);
@ -8,34 +8,27 @@ pub fn main() anyerror!void {
try print_input();
}
const files = args[1..];
for (files) |f| {
if (std.mem.eql(u8, f, "-")) { try print_input(); }
for (args[1..]) |f| {
if (std.mem.eql(u8, f, "-")) { try print_input(); }
else { try print_file (f); }
}
}
fn print_input() !void {
const stdin = std.io.getStdIn().reader();
var buffer: [4096]u8 = undefined;
while (true) {
const nbytes = try stdin.read(&buffer);
lib.print("{s}", .{buffer[0..nbytes]});
if (nbytes == 0) break;
}
try print_all (std.io.getStdIn());
}
fn print_file(dest: []const u8) !void {
// open file and defer closing
var file = try std.fs.cwd().openFile(dest, .{ .mode = .read_only });
defer file.close();
try print_all (file);
}
// read file content and print everything
fn print_all(reader: std.fs.File) !void {
var buffer: [4096]u8 = undefined;
var nbytes : u64 = 0;
while (true) {
nbytes = try file.read(&buffer);
lib.print("{s}", .{buffer[0..nbytes]});
const nbytes = try reader.read(&buffer);
try stdout.print("{s}", .{buffer[0..nbytes]});
if (nbytes == 0) break;
}
}

View file

@ -1,6 +0,0 @@
pub fn nextArg(args: [][]const u8, idx: *usize) ?[]const u8 {
if (idx.* >= args.len) return null;
defer idx.* += 1;
return args[idx.*];
}

View file

@ -2,15 +2,46 @@ const std = @import("std");
const mem = std.mem;
const stdout = std.io.getStdOut().writer();
const process = std.process;
const fmt = std.fmt;
const fs = std.fs;
const io = std.io;
pub const warn = std.debug.print;
pub fn print(comptime format: []const u8, args: anytype) void {
nosuspend stdout.print(format, args) catch return;
}
const cli_arguments = @import("./cli_arguments.zig");
// const depth = 3;
pub const FixedBufferWriter = struct {
const Self = @This();
depth: u32 = 3,
fbs: io.FixedBufferStream([]u8),
fmtopts: fmt.FormatOptions = fmt.FormatOptions{},
pub fn put(self: *Self, str: []const u8) !void {
var writer = self.fbs.writer();
try fmt.formatType(str, "s", self.fmtopts, writer, self.depth);
}
pub fn putf(self: *Self, comptime format: []const u8, args: anytype) !void {
var writer = self.fbs.writer();
try fmt.format(writer, format, args);
}
pub fn init(buffer: []u8) Self {
return Self{
.fbs = io.fixedBufferStream(buffer),
};
}
pub fn getWritten(self: *Self) []u8 {
defer self.fbs.reset();
return self.fbs.getWritten();
}
};
pub const CLI = struct {
const Self = @This();

View file

@ -1,23 +1,124 @@
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 fmt = std.fmt;
const Kind = std.fs.File.Kind;
const PermissionsUnix = std.fs.File.PermissionsUnix;
// 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 Options = struct {
verbose: bool = false,
};
var options: Options = Options {};
fn print_stats(fpath: []const u8, stats: os.system.Stat) !void {
var buffer = [_]u8 {0} ** (std.fs.MAX_PATH_BYTES + 100);
var writer = lib.FixedBufferWriter.init(&buffer);
// print kind (directory, file, pipe, etc.)
const kind = fs.File.Stat.systemStatKindToFsKind(stats);
switch (kind) {
.File => try writer.put("-"),
.Directory => try writer.put("d"),
.CharacterDevice => try writer.put("c"),
.BlockDevice => try writer.put("b"),
.NamedPipe => try writer.put("p"),
.SymLink => try writer.put("s"),
.UnixDomainSocket => try writer.put("s"),
else => {
if (builtin.os.tag == .solaris) switch (kind) {
.Door => try writer.put("D"), // TODO: what to print here?
.EventPort => try writer.put("e"), // TODO: what to print here?
else => try writer.put("?"),
};
},
}
// 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 writer.put("r"),
.write => try writer.put("w"),
.execute => try writer.put("x"),
}
}
else {
try writer.put("-");
}
}
}
// TODO: print user & group, not just uid and gid
try writer.putf("\t{}\t{}\t", .{stats.uid, stats.gid});
try writer.putf("{}\t", .{stats.size});
try writer.putf("{}\t", .{stats.mtim.tv_sec});
try writer.putf("{s}\n", .{fpath});
return lib.print("{s}", .{writer.getWritten()});
}
fn print_file (fpath: []const u8) !void {
if (options.verbose == false) {
return lib.print("{s}\n", .{fpath});
}
const dname = path.dirname(fpath);
var dpath = dname orelse ".";
var dir: fs.Dir = cwd.openDir(dpath, .{}) catch |err| switch (err) {
error.AccessDenied => return lib.print("{s}: access denied\n", .{dname}),
else => return err,
};
var dir_it = dir.iterate();
const bname = path.basename(fpath);
try print_file_in_dir (dir, bname);
}
fn print_file_in_dir (dir: fs.Dir, entryname: []const u8) !void {
if (options.verbose == false) {
return lib.print("{s}\n", .{entryname});
}
return try print_stats (entryname, try dir.fstatat(entryname));
}
// Either print directory's content or file.
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: print info on each file
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 +128,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,12 +136,18 @@ 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_directory(element);
}
}
// case there were no parameter, print current directory
if (cli.args.len == 1) {
try print_element(".");
if (nb_parameters == 0) {
try print_directory(".");
}
}

30
src/main.zig Normal file
View file

@ -0,0 +1,30 @@
const std = @import("std");
const eq = std.mem.eql;
const ls = @import("ls.zig");
const cat = @import("cat.zig");
const lib = @import("lib.zig");
fn help() void {
const help_string =
\\
\\ toying with a single tool: zig build-exe ls.zig
\\ (currently ± working tools: cat, ls)
\\
\\ global compilation: zig build-exe main.zig
\\ -> produces "main" that includes all tools
\\ you can invoke each tool by creating a symbolic link to "main"
\\ ex: ln -s main ls <- now you have "ls" working as your well-known "ls" :)
;
lib.print("{s}\n", .{help_string});
}
pub fn main() !void {
const args = try std.process.argsAlloc(std.heap.page_allocator);
const bname = std.fs.path.basename(args[0]);
if (eq(u8, bname, "ls")) { try ls.main(); }
else if (eq(u8, bname, "cat")) { try cat.main(); }
else { help(); }
}

26
src/test.zig Normal file
View file

@ -0,0 +1,26 @@
const std = @import("std");
const path = std.fs.path;
const lib = @import("./lib.zig");
const fmt = std.fmt;
const default_max_depth = 3;
test "print type" {
lib.print("hello\n", .{});
var buffer = [_]u8{0} ** 3;
const T = @TypeOf(std.io.fixedBufferStream(&buffer));
lib.print("hello: {}\n", .{T});
}
test "buffer stuff" {
var buffer = [_]u8 {0} ** (std.fs.MAX_PATH_BYTES + 100);
var fbs = std.io.fixedBufferStream(&buffer);
var writer = fbs.writer();
try fmt.formatType('W', "c", fmt.FormatOptions{}, writer, default_max_depth);
try fmt.formatType("ritten", "s", fmt.FormatOptions{}, writer, default_max_depth);
lib.print("{s}\n", .{fbs.getWritten()});
fbs.reset();
}