Application Context Implementation #4
|
@ -10,15 +10,7 @@
|
|||
"valuesFormatting": "parseText",
|
||||
"preLaunchTask": "Build Debug",
|
||||
},
|
||||
{
|
||||
"name": "Oar",
|
||||
"type": "gdb",
|
||||
"request": "launch",
|
||||
"target": "${workspaceFolder}/zig-out/bin/oar",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"valuesFormatting": "parseText",
|
||||
"preLaunchTask": "Build Debug",
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Test",
|
||||
"type": "gdb",
|
||||
|
|
253
src/oar/main.zig
253
src/oar/main.zig
|
@ -1,253 +0,0 @@
|
|||
const core = @import("core");
|
||||
const std = @import("std");
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub const Archive = struct {
|
||||
pub fn deinit(archive: *Archive) {
|
||||
|
||||
}
|
||||
|
||||
pub fn init(file_system: *const sys.FileSystem, file_path: sys.Path) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// An entry block of an Oar archive file.
|
||||
///
|
||||
/// Typically, following the block in memory is the file data it holds the meta-information for.
|
||||
///
|
||||
pub const Entry = extern struct {
|
||||
signature: [signature_magic.len]u8 = signature_magic,
|
||||
revision: u8,
|
||||
path: Path,
|
||||
file_size: u64,
|
||||
absolute_offset: u64,
|
||||
padding: [232]u8 = std.mem.zeroes([232]u8),
|
||||
|
||||
comptime {
|
||||
const entry_size = @sizeOf(Entry);
|
||||
|
||||
if (entry_size != 512)
|
||||
@compileError("Entry is " ++
|
||||
std.fmt.comptimePrint("{d}", .{entry_size}) ++ " bytes");
|
||||
}
|
||||
|
||||
///
|
||||
/// Attempts to read the next [Entry] from `file_access`.
|
||||
///
|
||||
/// Returns the read [Entry], `null` if there is no more to read, or a
|
||||
/// [core.io.FileAccess.Error] if it failed.
|
||||
///
|
||||
pub fn next(file_access: core.io.FileAccess) core.io.FileAccess.Error!?Entry {
|
||||
const mem = std.mem;
|
||||
var entry = mem.zeroes(Entry);
|
||||
const origin = try file_access.queryCursor();
|
||||
|
||||
if (((try file_access.read(mem.asBytes(&entry))) != @sizeOf(Entry)) and
|
||||
core.io.equals(u8, &entry.signature, &signature_magic)) {
|
||||
|
||||
try file_access.seek(origin);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
///
|
||||
/// Magic identifier used to validate [Entry] data.
|
||||
///
|
||||
pub const signature_magic = [3]u8{'o', 'a', 'r'};
|
||||
};
|
||||
|
||||
///
|
||||
/// Unique identifier pointing to an entry within an archive.
|
||||
///
|
||||
/// A path does not do any verification that the given entry pointed to actually exists.
|
||||
///
|
||||
pub const Path = extern struct {
|
||||
buffer: [255]u8,
|
||||
length: u8,
|
||||
|
||||
///
|
||||
/// [Error.TooLong] occurs when creating a path that is greater than the maximum path size **in
|
||||
/// bytes**.
|
||||
///
|
||||
pub const Error = error {
|
||||
TooLong,
|
||||
};
|
||||
|
||||
///
|
||||
/// An empty [Path] with a length of `0`.
|
||||
///
|
||||
pub const empty = std.mem.zeroes(Path);
|
||||
|
||||
///
|
||||
/// Returns `true` if `this_path` is equal to `that_path, otherwise `false`.
|
||||
///
|
||||
pub fn equals(this_path: Path, that_path: Path) bool {
|
||||
return core.io.equals(u8, this_path.buffer[0 ..this_path.
|
||||
length], that_path.buffer[0 .. that_path.length]);
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the hash of the text in `path`.
|
||||
///
|
||||
pub fn hash(path: Path) usize {
|
||||
return core.io.hashBytes(path.buffer[0 .. path.length]);
|
||||
}
|
||||
|
||||
///
|
||||
/// Attempts to create a [Path] with the path components in `sequences` as a fully qualified
|
||||
/// path from root.
|
||||
///
|
||||
/// A [Path] value is returned containing the fully qualified path from the file-system root or
|
||||
/// a [Error] if it could not be created.
|
||||
///
|
||||
pub fn joined(sequences: []const []const u8) Error!Path {
|
||||
var path = empty;
|
||||
|
||||
if (sequences.len != 0) {
|
||||
const last_sequence_index = sequences.len - 1;
|
||||
|
||||
for (sequences) |sequence, index| if (sequence.len != 0) {
|
||||
var components = core.io.Spliterator(u8){
|
||||
.source = sequence,
|
||||
.delimiter = "/",
|
||||
};
|
||||
|
||||
while (components.next()) |component| if (component.len != 0) {
|
||||
for (component) |byte| {
|
||||
if (path.length == max) return error.TooLong;
|
||||
|
||||
path.buffer[path.length] = byte;
|
||||
path.length += 1;
|
||||
}
|
||||
|
||||
if (components.hasNext()) {
|
||||
if (path.length == max) return error.TooLong;
|
||||
|
||||
path.buffer[path.length] = '/';
|
||||
path.length += 1;
|
||||
}
|
||||
};
|
||||
|
||||
if (index < last_sequence_index) {
|
||||
if (path.length == max) return error.TooLong;
|
||||
|
||||
path.buffer[path.length] = '/';
|
||||
path.length += 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
///
|
||||
/// Maximum number of **bytes** in a [Path].
|
||||
///
|
||||
pub const max = 255;
|
||||
|
||||
///
|
||||
/// Textual separator between components of a [Path].
|
||||
///
|
||||
pub const seperator = '/';
|
||||
};
|
||||
|
||||
test "Path" {
|
||||
const testing = std.testing;
|
||||
const empty_path = Path.empty;
|
||||
|
||||
try testing.expectEqual(empty_path.length, 0);
|
||||
try testing.expect(empty_path.equals(Path.empty));
|
||||
|
||||
const joined_component_path = try Path.joined(&.{"path", "to/my", "/file"});
|
||||
const joined_normalized_path = try Path.joined(&.{"path/to/my/file"});
|
||||
|
||||
try testing.expectEqual(joined_component_path.length, joined_normalized_path.length);
|
||||
try testing.expect(joined_component_path.equals(joined_normalized_path));
|
||||
}
|
||||
|
||||
///
|
||||
/// Starts the **O**na **Ar**chive packer utility.
|
||||
///
|
||||
pub fn main() u8 {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
|
||||
defer std.debug.assert(!gpa.deinit());
|
||||
|
||||
const allocator = gpa.allocator();
|
||||
const out_writer = std.io.getStdOut().writer();
|
||||
const process = std.process;
|
||||
|
||||
const args = process.argsAlloc(allocator) catch {
|
||||
out_writer.print("Failed to allocate args memory\n", .{}) catch undefined;
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
defer process.argsFree(allocator, args);
|
||||
|
||||
if (args.len < 2) {
|
||||
out_writer.print("Usage: oar [OPTION]... [FILE]...\n", .{}) catch undefined;
|
||||
out_writer.print("Options and arguments\n", .{}) catch undefined;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const arg = std.mem.sliceTo(args[1], 0);
|
||||
|
||||
if (core.io.equals(u8, arg, "--create")) {
|
||||
if (args.len < 3) {
|
||||
out_writer.print("Expected output file specified after `--create`\n", .{}) catch undefined;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
var archive = Archive.init(allocator, Path.joined(&.{args[2]})) catch {
|
||||
out_writer.print("Failed to initialize archive for create\n", .{}) catch undefined;
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
defer archive.deinit();
|
||||
|
||||
for (args[3 .. ]) |input_file_path| {
|
||||
const file = std.fs.cwd().openFile(input_file_path) catch {
|
||||
out_writer.print("Failed to open {s}\n", .{input_file_path}) catch undefined;
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
defer file.close();
|
||||
|
||||
var entry = archive.open(Path.joined(&.{input_file_path})) catch {
|
||||
out_writer.print("Failed to open {s}\n", .{input_file_path}) catch undefined;
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
defer archive.close(entry);
|
||||
|
||||
var copy_buffer = std.mem.zeroes([4096]u8);
|
||||
|
||||
while (true) {
|
||||
const read = try file.read(©_buffer);
|
||||
|
||||
if (read == 0) break;
|
||||
|
||||
try entry.write(copy_buffer[read ..]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
out_writer.print("Unrecognized command-line option `{s}`\n", .{arg}) catch undefined;
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue