srctree

Gregory Mullen parent 552e5bd5 c79d8ed3
use vars for set instead of multiple data locations

High chance we'll want to do something different for posix but #yeet
src/builtins.zig added: 138, removed: 185, total 0
@@ -1,10 +1,11 @@
const std = @import("std");
const Token = @import("tokenizer.zig").Token;
const HSH = @import("hsh.zig").HSH;
const jobs_ = @import("jobs.zig");
const ParsedIterator = @import("parse.zig").ParsedIterator;
const log = @import("log");
const hsh_build = @import("hsh_build");
pub const Token = @import("tokenizer.zig").Token;
pub const ParsedIterator = @import("parse.zig").ParsedIterator;
pub const Variables = @import("variables.zig");
 
// files should be lowercased, but #YOLO
pub const Aliases = @import("builtins/alias.zig");
 
src/builtins/set.zig added: 138, removed: 185, total 0
@@ -1,13 +1,13 @@
const std = @import("std");
const hsh = @import("../hsh.zig");
const HSH = hsh.HSH;
const tokenizer = @import("../tokenizer.zig");
const Token = tokenizer.Token;
const bi = @import("../builtins.zig");
const print = bi.print;
const Err = bi.Err;
const ParsedIterator = @import("../parse.zig").ParsedIterator;
const Token = bi.Token;
const ParsedIterator = bi.ParsedIterator;
const State = bi.State;
const Vars = bi.Variables;
 
pub const Set = @This();
 
@@ -31,8 +31,7 @@ pub const Opts = enum(u8) {
}
};
 
pub const OOptions = enum {
// posix magic
pub const PosixOpts = enum {
allexport,
errexit,
ignoreeof,
@@ -46,10 +45,9 @@ pub const OOptions = enum {
verbose,
vi,
xtrace,
// hsh magic
};
 
const OptState = union(OOptions) {
const PosixState = union(PosixOpts) {
allexport: ?bool,
errexit: ?bool,
ignoreeof: ?bool,
@@ -65,49 +63,14 @@ const OptState = union(OOptions) {
xtrace: ?bool,
};
 
pub const KnOptions = struct {
allexport: ?bool,
errexit: ?bool,
ignoreeof: ?bool,
monitor: ?bool,
noclobber: ?bool,
noglob: ?bool,
noexec: ?bool,
nolog: ?bool,
notify: ?bool,
nounset: ?bool,
verbose: ?bool,
vi: ?bool,
xtrace: ?bool,
 
pub fn init() KnOptions {
return KnOptions {
.allexport = false,
.errexit = false,
.ignoreeof = false,
.monitor = false,
.noclobber = false,
.noglob = false,
.noexec = false,
.nolog = false,
.notify = false,
.nounset = false,
.verbose = false,
.vi = false,
.xtrace = false,
};
}
};
 
var known_options: KnOptions = undefined;
 
pub fn init() void {
known_options = KnOptions.init();
hsh.addState(State{
.name = "set",
.ctx = &known_options,
.ctx = &.{},
.api = &.{ .save = save },
}) catch unreachable;
 
enable(.NoClobber) catch unreachable;
}
 
pub fn raze() void {
@@ -120,12 +83,11 @@ fn save(_: *HSH, _: *anyopaque) ?[][]const u8 {
 
fn nop() void {}
 
fn enable(h: *HSH, o: Opts) !void {
_ = h;
fn enable(o: Opts) !void {
switch (o) {
.Export => return nop(),
.BgJob => return nop(),
.NoClobber => known_options.noclobber = true,
.NoClobber => try Vars.putKind("noclobber", "true", .internal),
.ErrExit => return nop(),
.PathExpan => return nop(),
.HashAll => return nop(),
@@ -136,12 +98,11 @@ fn enable(h: *HSH, o: Opts) !void {
}
}
 
fn disable(h: *HSH, o: Opts) !void {
_ = h;
fn disable(o: Opts) !void {
switch (o) {
.Export => return nop(),
.BgJob => return nop(),
.NoClobber => known_options.noclobber = false,
.NoClobber => try Vars.putKind("noclobber", "false", .internal),
.ErrExit => return nop(),
.PathExpan => return nop(),
.HashAll => return nop(),
@@ -158,7 +119,7 @@ fn special(h: *HSH, titr: *ParsedIterator) Err!u8 {
return 0;
}
 
fn posix(h: *HSH, opt: []const u8, titr: *ParsedIterator) Err!u8 {
fn posix(opt: []const u8, titr: *ParsedIterator) Err!u8 {
_ = titr;
const mode = if (opt[0] == '-')
true
@@ -168,7 +129,7 @@ fn posix(h: *HSH, opt: []const u8, titr: *ParsedIterator) Err!u8 {
return Err.InvalidCommand;
for (opt[1..]) |opt_c| {
const o = try Opts.find(opt_c);
if (mode) try enable(h, o) else try disable(h, o);
if (mode) try enable(o) else try disable(o);
}
return 0;
}
@@ -185,10 +146,6 @@ fn dump(h: *HSH) Err!u8 {
return 0;
}
 
pub fn get_noclobber() bool {
return known_options.noclobber;
}
 
pub fn set(h: *HSH, titr: *ParsedIterator) Err!u8 {
if (!std.mem.eql(u8, titr.first().cannon(), "set")) return Err.InvalidCommand;
 
@@ -208,7 +165,7 @@ pub fn set(h: *HSH, titr: *ParsedIterator) Err!u8 {
if (opt[0] == '-' or opt[0] == '+') {
switch (opt[1]) {
'o' => {
return posix(h, arg.cannon(), titr);
return posix(arg.cannon(), titr);
},
'-' => {
if (opt.len == 2) return special(h, titr);
 
src/exec.zig added: 138, removed: 185, total 0
@@ -16,14 +16,11 @@ const log = @import("log");
const mem = std.mem;
const fd_t = std.os.fd_t;
const bi = @import("builtins.zig");
const fs = @import("fs.zig");
const signal = @import("signals.zig");
const logic_ = @import("logic.zig");
const Variables = @import("variables.zig");
 
const fs = @import("fs.zig");
const noclobberError = fs.Error.Noclobber;
const clobberMode = fs.clobberMode;
 
const STDIN_FILENO = std.os.STDIN_FILENO;
const STDOUT_FILENO = std.os.STDOUT_FILENO;
const STDERR_FILENO = std.os.STDERR_FILENO;
@@ -266,21 +263,16 @@ fn mkCallableStack(a: Allocator, itr: *TokenIterator) Error![]CallableStack {
for (eslice) |maybeio| {
if (maybeio.kind == .io) {
switch (maybeio.kind.io) {
.Out => {
const f = fs.openStdoutFile(maybeio.cannon(), true, clobberMode.maybeClobber) catch |err| {
.Out, .Append => |appnd| {
const f = fs.openFileStdout(maybeio.cannon(), appnd == .Append) catch |err| {
switch (err) {
noclobberError => log.err("Noclobber is enabled.\n", .{}),
fs.Error.NoClobber => log.err("Noclobber is enabled.\n", .{}),
else => log.err("Failed to open file {s}\n", .{maybeio.cannon()}),
}
return Error.StdIOError;
};
io.out = f.handle;
},
.Append => {
const f = fs.openStdoutFile(maybeio.cannon(), true, clobberMode.append) catch return Error.StdIOError;
io.out = f.handle;
f.seekFromEnd(0) catch return Error.StdIOError;
},
.In, .HDoc => {
if (prev_stdout) |out| {
std.os.close(out);
 
src/fs.zig added: 138, removed: 185, total 0
@@ -4,15 +4,20 @@ const mem = @import("mem.zig");
const Allocator = mem.Allocator;
const log = @import("log");
const INotify = @import("inotify.zig");
const hsh = @import("hsh.zig");
const HSH = @import("hsh.zig").HSH;
const rand = @import("random.zig");
const set = @import("builtins/set.zig");
const KnOptions = set.KnOptions;
const PathAlreadyExists = std.fs.File.OpenError.PathAlreadyExists;
const vars = @import("variables.zig");
 
pub const fs = @This();
 
pub const Error = error{
System,
Missing,
Perm,
NoClobber,
Other,
};
 
const Names = struct {
cwd: []u8,
cwd_short: []u8,
@@ -45,6 +50,7 @@ const Names = struct {
self.paths.clearAndFree();
}
};
 
const Dirs = struct {
cwd: std.fs.IterableDir,
conf: ?std.fs.IterableDir = null,
@@ -66,20 +72,6 @@ names: Names,
inotify_fd: ?i32,
watches: [1]?INotify,
 
pub const Error = error{
System,
Missing,
Perm,
Noclobber,
Other,
};
 
pub const clobberMode = enum {
append, // Append to file, so noclobber option is irrelevent,
maybeClobber, // Check noclobber option before writing.
forceClobber, // Ignore noclobber option.
};
 
pub fn init(a: mem.Allocator, env: std.process.EnvMap) !fs {
var paths = std.ArrayList([]const u8).init(a);
if (env.get("PATH")) |penv| {
@@ -218,10 +210,10 @@ pub fn mktemp(a: std.mem.Allocator, data: ?[]const u8) ![]u8 {
fn fileAt(
dir: std.fs.Dir,
name: []const u8,
comptime create: bool,
comptime ccreate: bool,
comptime rw: bool,
) ?std.fs.File {
if (create) {
if (ccreate) {
return dir.createFile(
name,
.{ .read = true, .truncate = false },
@@ -234,20 +226,24 @@ fn fileAt(
}
}
 
pub fn writeFileAt(dir: std.fs.Dir, name: []const u8, comptime create: bool) ?std.fs.File {
return fileAt(dir, name, create, true);
pub fn writeFileAt(dir: std.fs.Dir, name: []const u8, comptime ccreate: bool) ?std.fs.File {
return fileAt(dir, name, ccreate, true);
}
 
pub fn writeFile(name: []const u8, comptime create: bool) ?std.fs.File {
return fileAt(std.fs.cwd(), name, create, true);
pub fn writeFile(name: []const u8, comptime ccreate: bool) ?std.fs.File {
return fileAt(std.fs.cwd(), name, ccreate, true);
}
 
pub fn openFileAt(dir: std.fs.Dir, name: []const u8, comptime create: bool) ?std.fs.File {
return fileAt(dir, name, create, false);
pub fn openFileAt(dir: std.fs.Dir, name: []const u8, comptime ccreate: bool) ?std.fs.File {
return fileAt(dir, name, ccreate, false);
}
 
pub fn openFile(name: []const u8, comptime create: bool) ?std.fs.File {
return fileAt(std.fs.cwd(), name, create, false);
pub fn openFile(name: []const u8, comptime ccreate: bool) ?std.fs.File {
return fileAt(std.fs.cwd(), name, ccreate, false);
}
 
pub fn create(name: []const u8) ?std.fs.File {
return fileAt(std.fs.cwd(), name, true, false);
}
 
pub fn globCwd(a: Allocator, search: []const u8) ![][]u8 {
@@ -290,14 +286,14 @@ pub fn findPath(
a: Allocator,
env: *const std.process.EnvMap,
name: []const u8,
comptime create: bool,
comptime ccreate: bool,
) !std.fs.File {
if (env.get("XDG_CONFIG_HOME")) |xdg| {
var out = try a.dupe(u8, xdg);
out = try mem.concatPath(a, out, "hsh");
defer a.free(out);
if (std.fs.openDirAbsolute(out, .{})) |d| {
if (writeFileAt(d, name, create)) |file| return file;
if (writeFileAt(d, name, ccreate)) |file| return file;
} else |_| {
log.debug("unable to open {s}\n", .{out});
}
@@ -307,13 +303,13 @@ pub fn findPath(
if (std.fs.openDirAbsolute(home, .{})) |h| {
if (h.openDir(".config", .{})) |hc| {
if (hc.openDir("hsh", .{})) |hch| {
if (writeFileAt(hch, name[1..], create)) |file| {
if (writeFileAt(hch, name[1..], ccreate)) |file| {
return file;
}
} else |e| log.debug("unable to open {s} {}\n", .{ "hsh", e });
//return hc;
} else |e| log.debug("unable to open {s} {}\n", .{ "conf", e });
if (writeFileAt(h, name, create)) |file| {
if (writeFileAt(h, name, ccreate)) |file| {
return file;
}
} else |e| log.debug("unable to open {s} {}\n", .{ "home", e });
@@ -322,50 +318,37 @@ pub fn findPath(
return Error.Missing;
}
 
pub fn openStdoutFile(name: []const u8, comptime create: bool, clobber: clobberMode) !std.fs.File {
switch (clobber) {
.append, .forceClobber => {
if (openFile(name, create)) |file| {
return file;
}
},
.maybeClobber => {
// Check for noclobber
if (hsh.getState("set")) |ctx| {
const opts: *KnOptions = @ptrCast(ctx);
if (opts.noclobber) |noclobber| {
// noclobber enabled
if (noclobber) {
// Check for existing file
if (std.fs.cwd().openFile(name, .{ .mode = .read_only })) |file| {
file.close();
return Error.Noclobber;
} else |err| {
switch (err) {
std.fs.File.OpenError.FileNotFound => {
if (openFile(name, true)) |file| {
return file;
}
},
else => return err,
}
}
return Error.Noclobber;
}
// noclobber disabled
else {
if (openFile(name, create)) |file| {
pub fn openFileStdout(name: []const u8, append: bool) !std.fs.File {
if (append) {
var file = openFile(name, true) orelse unreachable;
file.seekFromEnd(0) catch unreachable;
return file;
}
 
// TODO don't use string here
if (vars.getKind("noclobber", .internal)) |noclobber| {
if (std.mem.eql(u8, noclobber.str, "true")) {
if (std.fs.cwd().openFile(name, .{ .mode = .read_only })) |file| {
file.close();
return Error.NoClobber;
} else |err| {
switch (err) {
std.fs.File.OpenError.FileNotFound => {
if (openFile(name, true)) |file| {
return file;
}
}
},
else => return err,
}
} else |_| {
log.err("set state not found.\n", .{});
return Error.Noclobber;
}
},
return Error.NoClobber;
}
}
return Error.Noclobber;
 
if (create(name)) |file| {
return file;
}
unreachable;
}
 
pub const CoreFiles = enum {
 
src/hsh.zig added: 138, removed: 185, total 0
@@ -137,18 +137,19 @@ fn readFromRC(hsh: *HSH) E!void {
}
 
fn initHSH(hsh: *HSH) !void {
Variables.init(hsh.alloc);
 
try initBuiltins(hsh);
try Context.init(&hsh.alloc);
try readFromRC(hsh);
 
Variables.init(hsh.alloc);
Variables.load(hsh.env) catch return E.Memory;
}
 
fn razeHSH(h: *HSH) void {
Variables.raze();
Context.raze();
razeBuiltins(h);
Variables.raze();
}
 
var savestates: ArrayList(State) = undefined;
@@ -157,15 +158,6 @@ pub fn addState(s: State) E!void {
try savestates.append(s);
}
 
pub fn getState(name: []const u8) !*anyopaque {
for (savestates.items) |*s| {
if (std.mem.eql(u8, name, s.getName())) {
return s.getCtx();
}
}
return E.Other;
}
 
fn writeLine(f: std.fs.File, line: []const u8) !usize {
const size = try f.write(line);
return size;
 
src/state.zig added: 138, removed: 185, total 0
@@ -18,11 +18,3 @@ pub const API = struct {
pub fn save(self: *State, h: *HSH) ?[][]const u8 {
return self.api.save(h, self.ctx);
}
 
pub fn getName(self: *State) []const u8 {
return self.name;
}
 
pub fn getCtx(self: *State) *anyopaque {
return self.ctx;
}
 
src/variables.zig added: 138, removed: 185, total 0
@@ -7,16 +7,33 @@ const Kind = enum(u4) {
nos,
internal,
sysenv,
special,
 
const len = @typeInfo(@This()).Enum.fields.len;
};
 
const Var = struct {
pub const SysEnv = struct {
value: []const u8,
kind: Kind,
exported: bool = false,
};
 
var variables: [4]std.StringHashMap(Var) = undefined;
const Var = union(Kind) {
nos: []const u8,
internal: union(enum) {
int: usize,
str: []const u8,
},
sysenv: SysEnv,
 
pub fn getType(comptime G: Kind) type {
inline for (@typeInfo(Var).Union.fields) |each| {
if (std.mem.eql(u8, @tagName(G), each.name))
return each.type;
}
unreachable;
}
};
 
var variables: [Kind.len]std.StringHashMap(Var) = undefined;
 
var environ: [:null]?[*:0]u8 = undefined;
var environ_alloc: std.mem.Allocator = undefined;
@@ -71,7 +88,14 @@ fn environBuild() ![:null]?[*:0]u8 {
var itr = variables[@intFromEnum(Kind.sysenv)].iterator();
while (itr.next()) |ent| {
const k = ent.key_ptr.*;
const v = ent.value_ptr.*.value;
const v = switch (ent.value_ptr.*) {
.nos => |n| n,
.sysenv => |s| s.value,
.internal => |i| switch (i) {
.int => continue,
.str => |s| s,
},
};
var str = try environ_alloc.alloc(u8, k.len + v.len + 2);
@memcpy(str[0..k.len], k);
str[k.len] = '=';
@@ -91,23 +115,34 @@ pub fn henviron() [:null]?[*:0]u8 {
return environBuild() catch @panic("unable to build environ");
}
 
pub fn getKind(k: []const u8, comptime g: Kind) ?Var {
return variables[@intFromEnum(g)].get(k);
pub fn getKind(k: []const u8, comptime G: Kind) ?std.meta.FieldType(Var, G) {
var vs = variables[@intFromEnum(G)].get(k) orelse return null;
return switch (G) {
.nos => vs.nos,
.sysenv => vs.sysenv,
.internal => vs.internal,
};
}
 
pub fn get(k: []const u8) ?Var {
pub fn get(k: []const u8) ?SysEnv {
return getKind(k, .sysenv);
}
 
pub fn getStr(k: []const u8) ?[]const u8 {
if (get(k)) |v| return v.value else return null;
if (get(k)) |v| {
return v.value;
}
return null;
}
 
pub fn putKind(k: []const u8, v: []const u8, comptime g: Kind) !void {
return variables[@intFromEnum(g)].put(k, Var{
.kind = g,
.value = v,
});
pub fn putKind(k: []const u8, v: []const u8, comptime G: Kind) !void {
var vs = &variables[@intFromEnum(G)];
var ret = switch (G) {
.nos => vs.put(k, Var{ .nos = v }),
.sysenv => vs.put(k, Var{ .sysenv = .{ .value = v } }),
.internal => vs.put(k, Var{ .internal = .{ .str = v } }),
};
return ret;
}
 
pub fn put(k: []const u8, v: []const u8) !void {
@@ -126,13 +161,13 @@ pub fn del(k: []const u8) !void {
/// named exports because I don't want to fight the compiler over the keyword
pub fn exports(k: []const u8) !void {
if (variables[@intFromEnum(Kind.sysenv)].getPtr(k)) |v| {
v.exported = true;
v.sysenv.exported = true;
}
}
 
pub fn unexport(k: []const u8) !void {
if (variables[@intFromEnum(Kind.sysenv)].getPtr(k)) |v| {
v.exported = false;
v.sysenv.exported = false;
}
}
 
@@ -161,6 +196,7 @@ test "variables standard usage" {
 
var x = get("key").?;
try std.testing.expectEqual(x.exported, false);
try std.testing.expectEqual(@TypeOf(get("str")), ?SysEnv);
try exports("key");
x = get("key").?;
try std.testing.expectEqual(x.exported, true);