summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-13 19:22:06 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2025-08-13 19:22:06 +0200
commit98c4176b91ee08106d7cc988f364825c7956722e (patch)
tree5c2212d427dee56de78e9f7e9ccf73e32098052d /src
parent621eaa97e417ce329c65ebf23d555ce34c39dddf (diff)
Reformat.
Diffstat (limited to 'src')
-rw-r--r--src/main.zig179
1 files changed, 87 insertions, 92 deletions
diff --git a/src/main.zig b/src/main.zig
index 3e2c266..f2a7726 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -11,7 +11,7 @@ const STDERR_BUFFER_SIZE: usize = 1024;
const FileSpec = union(enum) {
Con,
Lpt1,
- Lpt2,
+ Lpt2,
Lpt3,
Prn,
Path: []const u8,
@@ -19,8 +19,8 @@ const FileSpec = union(enum) {
const RedirectType = enum {
OutputOverwrite, // >
- OutputAppend, // >>
- InputFrom, // <
+ OutputAppend, // >>
+ InputFrom, // <
};
const Redirect = struct {
@@ -214,9 +214,9 @@ const CommandStatus = union(enum) {
const Token = union(enum) {
Word: []const u8,
Pipe,
- RedirectOut, // >
+ RedirectOut, // >
RedirectAppend, // >>
- RedirectIn, // <
+ RedirectIn, // <
Newline,
Eof,
};
@@ -257,7 +257,7 @@ const Lexer = struct {
fn readWord(self: *Lexer, allocator: Allocator) ![]const u8 {
var word = ArrayList(u8).init(allocator);
defer word.deinit();
-
+
var in_quotes = false;
var quote_char: u8 = '"';
@@ -337,7 +337,7 @@ const Lexer = struct {
pub fn tokenize(self: *Lexer, allocator: Allocator) !ArrayList(Token) {
var tokens = ArrayList(Token).init(allocator);
-
+
while (true) {
const token = try self.nextToken(allocator);
const is_eof = switch (token) {
@@ -480,7 +480,7 @@ const Parser = struct {
} else {
const first_arg_upper = try std.ascii.allocUpperString(self.allocator, args.items[0]);
defer self.allocator.free(first_arg_upper);
-
+
if (std.mem.eql(u8, first_arg_upper, "ON") and args.items.len == 1) {
return Command{ .Builtin = BuiltinCommand.EchoOn };
} else if (std.mem.eql(u8, first_arg_upper, "OFF") and args.items.len == 1) {
@@ -592,21 +592,21 @@ fn parseAndExecute(input: []const u8, allocator: Allocator) !CommandStatus {
const OutputCapture = struct {
buffer: ArrayList(u8),
-
+
pub fn init(allocator: Allocator) OutputCapture {
return OutputCapture{
.buffer = ArrayList(u8).init(allocator),
};
}
-
+
pub fn deinit(self: *OutputCapture) void {
self.buffer.deinit();
}
-
+
pub fn write(self: *OutputCapture, data: []const u8) !void {
try self.buffer.appendSlice(data);
}
-
+
pub fn getContents(self: *const OutputCapture) []const u8 {
return self.buffer.items;
}
@@ -615,30 +615,30 @@ const OutputCapture = struct {
const InputSource = struct {
data: []const u8,
position: usize,
-
+
pub fn init(data: []const u8) InputSource {
return InputSource{
.data = data,
.position = 0,
};
}
-
+
pub fn readLine(self: *InputSource, allocator: Allocator) !?[]const u8 {
if (self.position >= self.data.len) {
return null; // EOF
}
-
+
var line_end = self.position;
while (line_end < self.data.len and self.data[line_end] != '\n') {
line_end += 1;
}
-
+
const line = self.data[self.position..line_end];
self.position = if (line_end < self.data.len) line_end + 1 else self.data.len;
-
+
// Remove trailing \r if present (DOS line endings)
if (line.len > 0 and line[line.len - 1] == '\r') {
- return try allocator.dupe(u8, line[0..line.len - 1]);
+ return try allocator.dupe(u8, line[0 .. line.len - 1]);
} else {
return try allocator.dupe(u8, line);
}
@@ -648,7 +648,7 @@ const InputSource = struct {
fn executeCommandWithOutput(command: Command, allocator: Allocator, output_capture: ?*OutputCapture, input_source: ?*InputSource) !CommandStatus {
switch (command) {
.Empty => return CommandStatus{ .Code = 0 },
-
+
.Builtin => |builtin_cmd| {
switch (builtin_cmd) {
.EchoText => |echo| {
@@ -705,15 +705,15 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
const timestamp = std.time.timestamp();
const epoch_seconds = @as(u64, @intCast(timestamp));
const epoch_day = @divFloor(epoch_seconds, std.time.s_per_day);
-
+
// Calculate days since Unix epoch (1970-01-01)
// Unix epoch is 719163 days since year 1 AD
const days_since_year_1 = epoch_day + 719163;
-
+
// Simple algorithm to convert days to year/month/day
var year: u32 = 1;
var remaining_days = days_since_year_1;
-
+
// Find the year
while (true) {
const days_in_year: u64 = if (isLeapYear(year)) 366 else 365;
@@ -721,10 +721,10 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
remaining_days -= days_in_year;
year += 1;
}
-
+
// Days in each month (non-leap year)
const days_in_month = [_]u32{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-
+
var month: u32 = 1;
for (days_in_month, 1..) |days, m| {
var month_days = days;
@@ -732,16 +732,16 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
if (m == 2 and isLeapYear(year)) {
month_days = 29;
}
-
+
if (remaining_days < month_days) {
month = @intCast(m);
break;
}
remaining_days -= month_days;
}
-
+
const day = remaining_days + 1; // Days are 1-indexed
-
+
const output = try std.fmt.allocPrint(allocator, "Current date is {d:0>2}/{d:0>2}/{d}\n", .{ month, day, year });
defer allocator.free(output);
if (output_capture) |capture| {
@@ -758,7 +758,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
const hours = day_seconds / std.time.s_per_hour;
const minutes = (day_seconds % std.time.s_per_hour) / std.time.s_per_min;
const seconds = day_seconds % std.time.s_per_min;
-
+
const output = try std.fmt.allocPrint(allocator, "Current time is {d:0>2}:{d:0>2}:{d:0>2}\n", .{ hours, minutes, seconds });
defer allocator.free(output);
if (output_capture) |capture| {
@@ -771,9 +771,9 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
.Dir => |dir| {
var output_buffer = ArrayList(u8).init(allocator);
defer output_buffer.deinit();
-
+
try output_buffer.writer().print("Directory of {s}\n\n", .{dir.path});
-
+
var dir_iterator = std.fs.cwd().openDir(dir.path, .{ .iterate = true }) catch {
const error_msg = "File not found\n";
if (output_capture) |capture| {
@@ -784,11 +784,11 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
return CommandStatus{ .Code = 1 };
};
defer dir_iterator.close();
-
+
var iterator = dir_iterator.iterate();
var file_count: u32 = 0;
var dir_count: u32 = 0;
-
+
while (try iterator.next()) |entry| {
switch (entry.kind) {
.directory => {
@@ -803,10 +803,10 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
else => {},
}
}
-
+
try output_buffer.writer().print("\n{d} File(s)\n", .{file_count});
try output_buffer.writer().print("{d} Dir(s)\n", .{dir_count});
-
+
if (output_capture) |capture| {
try capture.write(output_buffer.items);
} else {
@@ -836,7 +836,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
},
.Path => |path| path,
};
-
+
const file = std.fs.cwd().openFile(file_path, .{}) catch |err| {
const error_msg = switch (err) {
error.FileNotFound => "The system cannot find the file specified.\n",
@@ -852,7 +852,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
return CommandStatus{ .Code = 1 };
};
defer file.close();
-
+
// Read and display file contents
var buffer: [4096]u8 = undefined;
while (true) {
@@ -868,13 +868,13 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
}
return CommandStatus{ .Code = 1 };
};
-
+
if (bytes_read == 0) break;
-
+
// Process buffer contents for output
var processed_output = ArrayList(u8).init(allocator);
defer processed_output.deinit();
-
+
for (buffer[0..bytes_read]) |byte| {
// Convert to printable characters, similar to DOS TYPE behavior
if (byte >= 32 and byte <= 126) {
@@ -891,17 +891,17 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
try processed_output.append('?');
}
}
-
+
if (output_capture) |capture| {
try capture.write(processed_output.items);
} else {
print("{s}", .{processed_output.items});
}
-
+
// If we read less than the buffer size, we're done
if (bytes_read < buffer.len) break;
}
-
+
return CommandStatus{ .Code = 0 };
},
.Sort => {
@@ -912,7 +912,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
}
lines.deinit();
}
-
+
// Read input lines
if (input_source) |source| {
// Read from input redirection
@@ -929,28 +929,28 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
}
return CommandStatus{ .Code = 0 };
}
-
+
// Sort the lines
std.mem.sort([]const u8, lines.items, {}, struct {
fn lessThan(_: void, lhs: []const u8, rhs: []const u8) bool {
return std.mem.order(u8, lhs, rhs) == .lt;
}
}.lessThan);
-
+
// Output sorted lines
var output_buffer = ArrayList(u8).init(allocator);
defer output_buffer.deinit();
-
+
for (lines.items) |line| {
try output_buffer.writer().print("{s}\n", .{line});
}
-
+
if (output_capture) |capture| {
try capture.write(output_buffer.items);
} else {
print("{s}", .{output_buffer.items});
}
-
+
return CommandStatus{ .Code = 0 };
},
else => {
@@ -965,24 +965,24 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
},
}
},
-
+
.External => |external| {
// Try to execute external command
var child_args = ArrayList([]const u8).init(allocator);
defer child_args.deinit();
-
+
try child_args.append(external.program);
for (external.args.items) |arg| {
try child_args.append(arg);
}
-
+
var child = std.process.Child.init(child_args.items, allocator);
-
+
// Set up pipes for capturing output
child.stdin_behavior = if (input_source != null) .Pipe else .Inherit;
child.stdout_behavior = if (output_capture != null) .Pipe else .Inherit;
child.stderr_behavior = if (output_capture != null) .Pipe else .Inherit;
-
+
const spawn_result = child.spawn();
if (spawn_result) |_| {
// Spawn succeeded, continue with execution
@@ -990,7 +990,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
error.FileNotFound => {
const error_msg = try std.fmt.allocPrint(allocator, "'{s}' is not recognized as an internal or external command,\noperable program or batch file.\n", .{external.program});
defer allocator.free(error_msg);
-
+
if (output_capture) |capture| {
try capture.write(error_msg);
} else {
@@ -1010,25 +1010,25 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
else => {
const error_msg = try std.fmt.allocPrint(allocator, "Cannot execute '{s}': {}\n", .{ external.program, err });
defer allocator.free(error_msg);
-
+
if (output_capture) |capture| {
try capture.write(error_msg);
} else {
print("{s}", .{error_msg});
}
return CommandStatus{ .Code = 1 };
- }
+ },
}
-
+
// Handle input redirection
if (input_source) |source| {
if (child.stdin) |stdin| {
const writer = stdin.writer();
-
+
// Reset source position for reading
var temp_source = source.*;
temp_source.position = 0;
-
+
while (try temp_source.readLine(allocator)) |line| {
defer allocator.free(line);
try writer.print("{s}\n", .{line});
@@ -1037,7 +1037,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
child.stdin = null;
}
}
-
+
// Handle output capture
if (output_capture) |capture| {
// Read stdout
@@ -1049,8 +1049,8 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
try capture.write(buffer[0..bytes_read]);
}
}
-
- // Read stderr
+
+ // Read stderr
if (child.stderr) |stderr| {
var buffer: [4096]u8 = undefined;
while (true) {
@@ -1060,7 +1060,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
}
}
}
-
+
// Wait for process to complete
const term = child.wait() catch |err| {
const error_msg = switch (err) {
@@ -1068,7 +1068,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
else => try std.fmt.allocPrint(allocator, "Error waiting for command: {}\n", .{err}),
};
defer allocator.free(error_msg);
-
+
if (output_capture) |capture| {
try capture.write(error_msg);
} else {
@@ -1076,7 +1076,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
}
return CommandStatus{ .Code = 1 };
};
-
+
// Return exit code
switch (term) {
.Exited => |code| return CommandStatus{ .Code = @intCast(code) },
@@ -1085,7 +1085,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
.Unknown => |_| return CommandStatus{ .Code = 1 },
}
},
-
+
.Redirect => |redirect| {
// Check if we have any output redirections
var has_output_redirect = false;
@@ -1095,15 +1095,15 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
break;
}
}
-
+
var captured_output = OutputCapture.init(allocator);
defer captured_output.deinit();
-
+
// Prepare input redirection if needed
var input_data: ?[]const u8 = null;
var redirect_input_source: ?InputSource = null;
defer if (input_data) |data| allocator.free(data);
-
+
// Process input redirections first
for (redirect.redirects.items) |redir| {
if (redir.redirect_type == .InputFrom) {
@@ -1118,7 +1118,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
},
.Path => |path| path,
};
-
+
// Read input file
const file = std.fs.cwd().openFile(file_path, .{}) catch |err| {
switch (err) {
@@ -1129,7 +1129,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
return CommandStatus{ .Code = 1 };
};
defer file.close();
-
+
input_data = file.readToEndAlloc(allocator, std.math.maxInt(usize)) catch |err| {
switch (err) {
error.AccessDenied => print("Access is denied.\n", .{}),
@@ -1137,20 +1137,15 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
}
return CommandStatus{ .Code = 1 };
};
-
+
redirect_input_source = InputSource.init(input_data.?);
break; // Only handle first input redirection
}
}
-
+
// Execute the command with input and output capture (only capture output if needed)
- const status = try executeCommandWithOutput(
- redirect.command.*,
- allocator,
- if (has_output_redirect) &captured_output else null,
- if (redirect_input_source) |*source| source else null
- );
-
+ const status = try executeCommandWithOutput(redirect.command.*, allocator, if (has_output_redirect) &captured_output else null, if (redirect_input_source) |*source| source else null);
+
// Handle output redirections
for (redirect.redirects.items) |redir| {
if (redir.redirect_type == .InputFrom) continue; // Already handled
@@ -1166,7 +1161,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
},
.Path => |path| path,
};
-
+
// Handle different redirect types
switch (redir.redirect_type) {
.OutputOverwrite => {
@@ -1179,7 +1174,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
return CommandStatus{ .Code = 1 };
};
defer file.close();
-
+
file.writeAll(captured_output.getContents()) catch |err| {
switch (err) {
error.AccessDenied => print("Access is denied.\n", .{}),
@@ -1202,7 +1197,7 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
return CommandStatus{ .Code = 1 };
};
defer new_file.close();
-
+
new_file.writeAll(captured_output.getContents()) catch {
print("Cannot write to file.\n", .{});
return CommandStatus{ .Code = 1 };
@@ -1220,13 +1215,13 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
}
};
defer file.close();
-
+
// Seek to end for append
file.seekFromEnd(0) catch {
print("Cannot seek to end of file.\n", .{});
return CommandStatus{ .Code = 1 };
};
-
+
file.writeAll(captured_output.getContents()) catch |err| {
switch (err) {
error.AccessDenied => print("Access is denied.\n", .{}),
@@ -1241,10 +1236,10 @@ fn executeCommandWithOutput(command: Command, allocator: Allocator, output_captu
},
}
}
-
+
return status;
},
-
+
else => {
const error_msg = "Command type not implemented\n";
if (output_capture) |capture| {
@@ -1264,11 +1259,11 @@ fn executeCommand(command: Command, allocator: Allocator) !CommandStatus {
fn readLine(allocator: Allocator, prompt_text: []const u8) !?[]const u8 {
const stdin = std.io.getStdIn().reader();
print("{s}", .{prompt_text});
-
+
if (try stdin.readUntilDelimiterOrEofAlloc(allocator, '\n', 4096)) |input| {
// Remove trailing \r on Windows
if (input.len > 0 and input[input.len - 1] == '\r') {
- return input[0..input.len - 1];
+ return input[0 .. input.len - 1];
}
return input;
}
@@ -1294,13 +1289,13 @@ pub fn main() !void {
const interpolated_prompt = try std.mem.replaceOwned(u8, allocator, prompt_spec, "$p", full_cwd);
defer allocator.free(interpolated_prompt);
-
+
const final_prompt = try std.mem.replaceOwned(u8, allocator, interpolated_prompt, "$g", ">");
defer allocator.free(final_prompt);
if (try readLine(allocator, final_prompt)) |line| {
defer allocator.free(line);
-
+
const command_result = parseAndExecute(line, allocator) catch |err| {
switch (err) {
error.ExpectedWord, error.UnexpectedToken => {
@@ -1319,4 +1314,4 @@ pub fn main() !void {
break; // EOF
}
}
-} \ No newline at end of file
+}