| author | |
| committer | |
| log | 46aa416c48c283849059292267ac25a6d0db76d6 |
| tree | ce260c8f27835122f09c010387e7c59aabe3a59d |
| parent | 8c31eaf2a87d39fe2f9ed8f5af2a059048bfffb3 |
* move std.io.File to std.os.File
* add `zig fmt` to self hosted compiler
* introduce std.io.BufferedAtomicFile API
* introduce std.os.AtomicFile API
* add `std.os.default_file_mode`
* change FileMode on posix from being a usize to a u32
* add std.os.File.mode to return mode of an open file
* std.os.copyFile copies the mode from the source file instead of
using the default file mode for the dest file
* move `std.os.line_sep` to `std.cstr.line_sep`15 files changed, 543 insertions(+), 351 deletions(-)
CMakeLists.txt+2-1| ... | @@ -437,11 +437,12 @@ set(ZIG_STD_FILES | ... | @@ -437,11 +437,12 @@ set(ZIG_STD_FILES |
| 437 | "os/child_process.zig" | 437 | "os/child_process.zig" |
| 438 | "os/darwin.zig" | 438 | "os/darwin.zig" |
| 439 | "os/darwin_errno.zig" | 439 | "os/darwin_errno.zig" |
| 440 | "os/file.zig" | ||
| 440 | "os/get_user_id.zig" | 441 | "os/get_user_id.zig" |
| 441 | "os/index.zig" | 442 | "os/index.zig" |
| 442 | "os/linux/index.zig" | ||
| 443 | "os/linux/errno.zig" | 443 | "os/linux/errno.zig" |
| 444 | "os/linux/i386.zig" | 444 | "os/linux/i386.zig" |
| 445 | "os/linux/index.zig" | ||
| 445 | "os/linux/x86_64.zig" | 446 | "os/linux/x86_64.zig" |
| 446 | "os/path.zig" | 447 | "os/path.zig" |
| 447 | "os/windows/error.zig" | 448 | "os/windows/error.zig" |
doc/docgen.zig+2-2| ... | @@ -31,10 +31,10 @@ pub fn main() !void { | ... | @@ -31,10 +31,10 @@ pub fn main() !void { |
| 31 | const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg")); | 31 | const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg")); |
| 32 | defer allocator.free(out_file_name); | 32 | defer allocator.free(out_file_name); |
| 33 | 33 | ||
| 34 | var in_file = try io.File.openRead(allocator, in_file_name); | 34 | var in_file = try os.File.openRead(allocator, in_file_name); |
| 35 | defer in_file.close(); | 35 | defer in_file.close(); |
| 36 | 36 | ||
| 37 | var out_file = try io.File.openWrite(allocator, out_file_name); | 37 | var out_file = try os.File.openWrite(allocator, out_file_name); |
| 38 | defer out_file.close(); | 38 | defer out_file.close(); |
| 39 | 39 | ||
| 40 | var file_in_stream = io.FileInStream.init(&in_file); | 40 | var file_in_stream = io.FileInStream.init(&in_file); |
example/cat/main.zig+2-2| ... | @@ -20,7 +20,7 @@ pub fn main() !void { | ... | @@ -20,7 +20,7 @@ pub fn main() !void { |
| 20 | } else if (arg[0] == '-') { | 20 | } else if (arg[0] == '-') { |
| 21 | return usage(exe); | 21 | return usage(exe); |
| 22 | } else { | 22 | } else { |
| 23 | var file = io.File.openRead(allocator, arg) catch |err| { | 23 | var file = os.File.openRead(allocator, arg) catch |err| { |
| 24 | warn("Unable to open file: {}\n", @errorName(err)); | 24 | warn("Unable to open file: {}\n", @errorName(err)); |
| 25 | return err; | 25 | return err; |
| 26 | }; | 26 | }; |
| ... | @@ -41,7 +41,7 @@ fn usage(exe: []const u8) !void { | ... | @@ -41,7 +41,7 @@ fn usage(exe: []const u8) !void { |
| 41 | return error.Invalid; | 41 | return error.Invalid; |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | fn cat_file(stdout: &io.File, file: &io.File) !void { | 44 | fn cat_file(stdout: &os.File, file: &os.File) !void { |
| 45 | var buf: [1024 * 4]u8 = undefined; | 45 | var buf: [1024 * 4]u8 = undefined; |
| 46 | 46 | ||
| 47 | while (true) { | 47 | while (true) { |
src-self-hosted/main.zig+10-3| ... | @@ -562,7 +562,7 @@ fn printZen() !void { | ... | @@ -562,7 +562,7 @@ fn printZen() !void { |
| 562 | 562 | ||
| 563 | fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void { | 563 | fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void { |
| 564 | for (file_paths) |file_path| { | 564 | for (file_paths) |file_path| { |
| 565 | var file = try io.File.openRead(allocator, file_path); | 565 | var file = try os.File.openRead(allocator, file_path); |
| 566 | defer file.close(); | 566 | defer file.close(); |
| 567 | 567 | ||
| 568 | const source_code = io.readFileAlloc(allocator, file_path) catch |err| { | 568 | const source_code = io.readFileAlloc(allocator, file_path) catch |err| { |
| ... | @@ -574,7 +574,14 @@ fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void { | ... | @@ -574,7 +574,14 @@ fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void { |
| 574 | var tokenizer = std.zig.Tokenizer.init(source_code); | 574 | var tokenizer = std.zig.Tokenizer.init(source_code); |
| 575 | var parser = std.zig.Parser.init(&tokenizer, allocator, file_path); | 575 | var parser = std.zig.Parser.init(&tokenizer, allocator, file_path); |
| 576 | defer parser.deinit(); | 576 | defer parser.deinit(); |
| 577 | warn("opened {} (todo tokenize and parse and render)\n", file_path); | 577 | |
| 578 | const tree = try parser.parse(); | ||
| 579 | defer tree.deinit(); | ||
| 580 | |||
| 581 | const baf = try io.BufferedAtomicFile.create(allocator, file_path); | ||
| 582 | defer baf.destroy(); | ||
| 583 | |||
| 584 | try parser.renderSource(baf.stream(), tree.root_node); | ||
| 578 | } | 585 | } |
| 579 | } | 586 | } |
| 580 | 587 | ||
| ... | @@ -602,7 +609,7 @@ fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 | ... | @@ -602,7 +609,7 @@ fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 |
| 602 | const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig"); | 609 | const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig"); |
| 603 | defer allocator.free(test_index_file); | 610 | defer allocator.free(test_index_file); |
| 604 | 611 | ||
| 605 | var file = try io.File.openRead(allocator, test_index_file); | 612 | var file = try os.File.openRead(allocator, test_index_file); |
| 606 | file.close(); | 613 | file.close(); |
| 607 | 614 | ||
| 608 | return test_zig_dir; | 615 | return test_zig_dir; |
std/build.zig+9-6| ... | @@ -624,10 +624,10 @@ pub const Builder = struct { | ... | @@ -624,10 +624,10 @@ pub const Builder = struct { |
| 624 | } | 624 | } |
| 625 | 625 | ||
| 626 | fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) !void { | 626 | fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) !void { |
| 627 | return self.copyFileMode(source_path, dest_path, 0o666); | 627 | return self.copyFileMode(source_path, dest_path, os.default_file_mode); |
| 628 | } | 628 | } |
| 629 | 629 | ||
| 630 | fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: usize) !void { | 630 | fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: os.FileMode) !void { |
| 631 | if (self.verbose) { | 631 | if (self.verbose) { |
| 632 | warn("cp {} {}\n", source_path, dest_path); | 632 | warn("cp {} {}\n", source_path, dest_path); |
| 633 | } | 633 | } |
| ... | @@ -1833,10 +1833,13 @@ const InstallArtifactStep = struct { | ... | @@ -1833,10 +1833,13 @@ const InstallArtifactStep = struct { |
| 1833 | const self = @fieldParentPtr(Self, "step", step); | 1833 | const self = @fieldParentPtr(Self, "step", step); |
| 1834 | const builder = self.builder; | 1834 | const builder = self.builder; |
| 1835 | 1835 | ||
| 1836 | const mode = switch (self.artifact.kind) { | 1836 | const mode = switch (builtin.os) { |
| 1837 | LibExeObjStep.Kind.Obj => unreachable, | 1837 | builtin.Os.windows => {}, |
| 1838 | LibExeObjStep.Kind.Exe => usize(0o755), | 1838 | else => switch (self.artifact.kind) { |
| 1839 | LibExeObjStep.Kind.Lib => if (self.artifact.static) usize(0o666) else usize(0o755), | 1839 | LibExeObjStep.Kind.Obj => unreachable, |
| 1840 | LibExeObjStep.Kind.Exe => u32(0o755), | ||
| 1841 | LibExeObjStep.Kind.Lib => if (self.artifact.static) u32(0o666) else u32(0o755), | ||
| 1842 | }, | ||
| 1840 | }; | 1843 | }; |
| 1841 | try builder.copyFileMode(self.artifact.getOutputPath(), self.dest_file, mode); | 1844 | try builder.copyFileMode(self.artifact.getOutputPath(), self.dest_file, mode); |
| 1842 | if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) { | 1845 | if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) { |
std/cstr.zig+7| ... | @@ -1,8 +1,15 @@ | ... | @@ -1,8 +1,15 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("index.zig"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const debug = std.debug; | 3 | const debug = std.debug; |
| 3 | const mem = std.mem; | 4 | const mem = std.mem; |
| 4 | const assert = debug.assert; | 5 | const assert = debug.assert; |
| 5 | 6 | ||
| 7 | pub const line_sep = switch (builtin.os) { | ||
| 8 | builtin.Os.windows => "\r\n", | ||
| 9 | else => "\n", | ||
| 10 | }; | ||
| 11 | |||
| 12 | |||
| 6 | pub fn len(ptr: &const u8) usize { | 13 | pub fn len(ptr: &const u8) usize { |
| 7 | var count: usize = 0; | 14 | var count: usize = 0; |
| 8 | while (ptr[count] != 0) : (count += 1) {} | 15 | while (ptr[count] != 0) : (count += 1) {} |
std/debug/index.zig+3-3| ... | @@ -13,7 +13,7 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; | ... | @@ -13,7 +13,7 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; |
| 13 | /// Tries to write to stderr, unbuffered, and ignores any error returned. | 13 | /// Tries to write to stderr, unbuffered, and ignores any error returned. |
| 14 | /// Does not append a newline. | 14 | /// Does not append a newline. |
| 15 | /// TODO atomic/multithread support | 15 | /// TODO atomic/multithread support |
| 16 | var stderr_file: io.File = undefined; | 16 | var stderr_file: os.File = undefined; |
| 17 | var stderr_file_out_stream: io.FileOutStream = undefined; | 17 | var stderr_file_out_stream: io.FileOutStream = undefined; |
| 18 | var stderr_stream: ?&io.OutStream(io.FileOutStream.Error) = null; | 18 | var stderr_stream: ?&io.OutStream(io.FileOutStream.Error) = null; |
| 19 | pub fn warn(comptime fmt: []const u8, args: ...) void { | 19 | pub fn warn(comptime fmt: []const u8, args: ...) void { |
| ... | @@ -265,7 +265,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { | ... | @@ -265,7 +265,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { |
| 265 | } | 265 | } |
| 266 | 266 | ||
| 267 | fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &const LineInfo) !void { | 267 | fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &const LineInfo) !void { |
| 268 | var f = try io.File.openRead(allocator, line_info.file_name); | 268 | var f = try os.File.openRead(allocator, line_info.file_name); |
| 269 | defer f.close(); | 269 | defer f.close(); |
| 270 | // TODO fstat and make sure that the file has the correct size | 270 | // TODO fstat and make sure that the file has the correct size |
| 271 | 271 | ||
| ... | @@ -298,7 +298,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con | ... | @@ -298,7 +298,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con |
| 298 | } | 298 | } |
| 299 | 299 | ||
| 300 | pub const ElfStackTrace = struct { | 300 | pub const ElfStackTrace = struct { |
| 301 | self_exe_file: io.File, | 301 | self_exe_file: os.File, |
| 302 | elf: elf.Elf, | 302 | elf: elf.Elf, |
| 303 | debug_info: &elf.SectionHeader, | 303 | debug_info: &elf.SectionHeader, |
| 304 | debug_abbrev: &elf.SectionHeader, | 304 | debug_abbrev: &elf.SectionHeader, |
std/elf.zig+4-3| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("index.zig"); | 2 | const std = @import("index.zig"); |
| 3 | const io = std.io; | 3 | const io = std.io; |
| 4 | const os = std.os; | ||
| 4 | const math = std.math; | 5 | const math = std.math; |
| 5 | const mem = std.mem; | 6 | const mem = std.mem; |
| 6 | const debug = std.debug; | 7 | const debug = std.debug; |
| ... | @@ -63,7 +64,7 @@ pub const SectionHeader = struct { | ... | @@ -63,7 +64,7 @@ pub const SectionHeader = struct { |
| 63 | }; | 64 | }; |
| 64 | 65 | ||
| 65 | pub const Elf = struct { | 66 | pub const Elf = struct { |
| 66 | in_file: &io.File, | 67 | in_file: &os.File, |
| 67 | auto_close_stream: bool, | 68 | auto_close_stream: bool, |
| 68 | is_64: bool, | 69 | is_64: bool, |
| 69 | endian: builtin.Endian, | 70 | endian: builtin.Endian, |
| ... | @@ -76,7 +77,7 @@ pub const Elf = struct { | ... | @@ -76,7 +77,7 @@ pub const Elf = struct { |
| 76 | string_section: &SectionHeader, | 77 | string_section: &SectionHeader, |
| 77 | section_headers: []SectionHeader, | 78 | section_headers: []SectionHeader, |
| 78 | allocator: &mem.Allocator, | 79 | allocator: &mem.Allocator, |
| 79 | prealloc_file: io.File, | 80 | prealloc_file: os.File, |
| 80 | 81 | ||
| 81 | /// Call close when done. | 82 | /// Call close when done. |
| 82 | pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) !void { | 83 | pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) !void { |
| ... | @@ -86,7 +87,7 @@ pub const Elf = struct { | ... | @@ -86,7 +87,7 @@ pub const Elf = struct { |
| 86 | } | 87 | } |
| 87 | 88 | ||
| 88 | /// Call close when done. | 89 | /// Call close when done. |
| 89 | pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &io.File) !void { | 90 | pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &os.File) !void { |
| 90 | elf.allocator = allocator; | 91 | elf.allocator = allocator; |
| 91 | elf.in_file = file; | 92 | elf.in_file = file; |
| 92 | elf.auto_close_stream = false; | 93 | elf.auto_close_stream = false; |
std/io.zig+62-276| ... | @@ -1,12 +1,6 @@ | ... | @@ -1,12 +1,6 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("index.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Os = builtin.Os; | 3 | const Os = builtin.Os; |
| 4 | const system = switch(builtin.os) { | ||
| 5 | Os.linux => @import("os/linux/index.zig"), | ||
| 6 | Os.macosx, Os.ios => @import("os/darwin.zig"), | ||
| 7 | Os.windows => @import("os/windows/index.zig"), | ||
| 8 | else => @compileError("Unsupported OS"), | ||
| 9 | }; | ||
| 10 | const c = std.c; | 4 | const c = std.c; |
| 11 | 5 | ||
| 12 | const math = std.math; | 6 | const math = std.math; |
| ... | @@ -16,23 +10,18 @@ const os = std.os; | ... | @@ -16,23 +10,18 @@ const os = std.os; |
| 16 | const mem = std.mem; | 10 | const mem = std.mem; |
| 17 | const Buffer = std.Buffer; | 11 | const Buffer = std.Buffer; |
| 18 | const fmt = std.fmt; | 12 | const fmt = std.fmt; |
| 13 | const File = std.os.File; | ||
| 19 | 14 | ||
| 20 | const is_posix = builtin.os != builtin.Os.windows; | 15 | const is_posix = builtin.os != builtin.Os.windows; |
| 21 | const is_windows = builtin.os == builtin.Os.windows; | 16 | const is_windows = builtin.os == builtin.Os.windows; |
| 22 | 17 | ||
| 23 | test "import io tests" { | ||
| 24 | comptime { | ||
| 25 | _ = @import("io_test.zig"); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | const GetStdIoErrs = os.WindowsGetStdHandleErrs; | 18 | const GetStdIoErrs = os.WindowsGetStdHandleErrs; |
| 30 | 19 | ||
| 31 | pub fn getStdErr() GetStdIoErrs!File { | 20 | pub fn getStdErr() GetStdIoErrs!File { |
| 32 | const handle = if (is_windows) | 21 | const handle = if (is_windows) |
| 33 | try os.windowsGetStdHandle(system.STD_ERROR_HANDLE) | 22 | try os.windowsGetStdHandle(os.windows.STD_ERROR_HANDLE) |
| 34 | else if (is_posix) | 23 | else if (is_posix) |
| 35 | system.STDERR_FILENO | 24 | os.posix.STDERR_FILENO |
| 36 | else | 25 | else |
| 37 | unreachable; | 26 | unreachable; |
| 38 | return File.openHandle(handle); | 27 | return File.openHandle(handle); |
| ... | @@ -40,9 +29,9 @@ pub fn getStdErr() GetStdIoErrs!File { | ... | @@ -40,9 +29,9 @@ pub fn getStdErr() GetStdIoErrs!File { |
| 40 | 29 | ||
| 41 | pub fn getStdOut() GetStdIoErrs!File { | 30 | pub fn getStdOut() GetStdIoErrs!File { |
| 42 | const handle = if (is_windows) | 31 | const handle = if (is_windows) |
| 43 | try os.windowsGetStdHandle(system.STD_OUTPUT_HANDLE) | 32 | try os.windowsGetStdHandle(os.windows.STD_OUTPUT_HANDLE) |
| 44 | else if (is_posix) | 33 | else if (is_posix) |
| 45 | system.STDOUT_FILENO | 34 | os.posix.STDOUT_FILENO |
| 46 | else | 35 | else |
| 47 | unreachable; | 36 | unreachable; |
| 48 | return File.openHandle(handle); | 37 | return File.openHandle(handle); |
| ... | @@ -50,9 +39,9 @@ pub fn getStdOut() GetStdIoErrs!File { | ... | @@ -50,9 +39,9 @@ pub fn getStdOut() GetStdIoErrs!File { |
| 50 | 39 | ||
| 51 | pub fn getStdIn() GetStdIoErrs!File { | 40 | pub fn getStdIn() GetStdIoErrs!File { |
| 52 | const handle = if (is_windows) | 41 | const handle = if (is_windows) |
| 53 | try os.windowsGetStdHandle(system.STD_INPUT_HANDLE) | 42 | try os.windowsGetStdHandle(os.windows.STD_INPUT_HANDLE) |
| 54 | else if (is_posix) | 43 | else if (is_posix) |
| 55 | system.STDIN_FILENO | 44 | os.posix.STDIN_FILENO |
| 56 | else | 45 | else |
| 57 | unreachable; | 46 | unreachable; |
| 58 | return File.openHandle(handle); | 47 | return File.openHandle(handle); |
| ... | @@ -104,260 +93,10 @@ pub const FileOutStream = struct { | ... | @@ -104,260 +93,10 @@ pub const FileOutStream = struct { |
| 104 | } | 93 | } |
| 105 | }; | 94 | }; |
| 106 | 95 | ||
| 107 | pub const File = struct { | 96 | pub fn InStream(comptime ReadError: type) type { |
| 108 | /// The OS-specific file descriptor or file handle. | ||
| 109 | handle: os.FileHandle, | ||
| 110 | |||
| 111 | const OpenError = os.WindowsOpenError || os.PosixOpenError; | ||
| 112 | |||
| 113 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. | ||
| 114 | /// Call close to clean up. | ||
| 115 | pub fn openRead(allocator: &mem.Allocator, path: []const u8) OpenError!File { | ||
| 116 | if (is_posix) { | ||
| 117 | const flags = system.O_LARGEFILE|system.O_RDONLY; | ||
| 118 | const fd = try os.posixOpen(allocator, path, flags, 0); | ||
| 119 | return openHandle(fd); | ||
| 120 | } else if (is_windows) { | ||
| 121 | const handle = try os.windowsOpen(allocator, path, system.GENERIC_READ, system.FILE_SHARE_READ, | ||
| 122 | system.OPEN_EXISTING, system.FILE_ATTRIBUTE_NORMAL); | ||
| 123 | return openHandle(handle); | ||
| 124 | } else { | ||
| 125 | unreachable; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Calls `openWriteMode` with 0o666 for the mode. | ||
| 130 | pub fn openWrite(allocator: &mem.Allocator, path: []const u8) !File { | ||
| 131 | return openWriteMode(allocator, path, 0o666); | ||
| 132 | |||
| 133 | } | ||
| 134 | |||
| 135 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. | ||
| 136 | /// Call close to clean up. | ||
| 137 | pub fn openWriteMode(allocator: &mem.Allocator, path: []const u8, mode: usize) !File { | ||
| 138 | if (is_posix) { | ||
| 139 | const flags = system.O_LARGEFILE|system.O_WRONLY|system.O_CREAT|system.O_CLOEXEC|system.O_TRUNC; | ||
| 140 | const fd = try os.posixOpen(allocator, path, flags, mode); | ||
| 141 | return openHandle(fd); | ||
| 142 | } else if (is_windows) { | ||
| 143 | const handle = try os.windowsOpen(allocator, path, system.GENERIC_WRITE, | ||
| 144 | system.FILE_SHARE_WRITE|system.FILE_SHARE_READ|system.FILE_SHARE_DELETE, | ||
| 145 | system.CREATE_ALWAYS, system.FILE_ATTRIBUTE_NORMAL); | ||
| 146 | return openHandle(handle); | ||
| 147 | } else { | ||
| 148 | unreachable; | ||
| 149 | } | ||
| 150 | |||
| 151 | } | ||
| 152 | |||
| 153 | pub fn openHandle(handle: os.FileHandle) File { | ||
| 154 | return File { | ||
| 155 | .handle = handle, | ||
| 156 | }; | ||
| 157 | } | ||
| 158 | |||
| 159 | |||
| 160 | /// Upon success, the stream is in an uninitialized state. To continue using it, | ||
| 161 | /// you must use the open() function. | ||
| 162 | pub fn close(self: &File) void { | ||
| 163 | os.close(self.handle); | ||
| 164 | self.handle = undefined; | ||
| 165 | } | ||
| 166 | |||
| 167 | /// Calls `os.isTty` on `self.handle`. | ||
| 168 | pub fn isTty(self: &File) bool { | ||
| 169 | return os.isTty(self.handle); | ||
| 170 | } | ||
| 171 | |||
| 172 | pub fn seekForward(self: &File, amount: isize) !void { | ||
| 173 | switch (builtin.os) { | ||
| 174 | Os.linux, Os.macosx, Os.ios => { | ||
| 175 | const result = system.lseek(self.handle, amount, system.SEEK_CUR); | ||
| 176 | const err = system.getErrno(result); | ||
| 177 | if (err > 0) { | ||
| 178 | return switch (err) { | ||
| 179 | system.EBADF => error.BadFd, | ||
| 180 | system.EINVAL => error.Unseekable, | ||
| 181 | system.EOVERFLOW => error.Unseekable, | ||
| 182 | system.ESPIPE => error.Unseekable, | ||
| 183 | system.ENXIO => error.Unseekable, | ||
| 184 | else => os.unexpectedErrorPosix(err), | ||
| 185 | }; | ||
| 186 | } | ||
| 187 | }, | ||
| 188 | Os.windows => { | ||
| 189 | if (system.SetFilePointerEx(self.handle, amount, null, system.FILE_CURRENT) == 0) { | ||
| 190 | const err = system.GetLastError(); | ||
| 191 | return switch (err) { | ||
| 192 | system.ERROR.INVALID_PARAMETER => error.BadFd, | ||
| 193 | else => os.unexpectedErrorWindows(err), | ||
| 194 | }; | ||
| 195 | } | ||
| 196 | }, | ||
| 197 | else => @compileError("unsupported OS"), | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | pub fn seekTo(self: &File, pos: usize) !void { | ||
| 202 | switch (builtin.os) { | ||
| 203 | Os.linux, Os.macosx, Os.ios => { | ||
| 204 | const ipos = try math.cast(isize, pos); | ||
| 205 | const result = system.lseek(self.handle, ipos, system.SEEK_SET); | ||
| 206 | const err = system.getErrno(result); | ||
| 207 | if (err > 0) { | ||
| 208 | return switch (err) { | ||
| 209 | system.EBADF => error.BadFd, | ||
| 210 | system.EINVAL => error.Unseekable, | ||
| 211 | system.EOVERFLOW => error.Unseekable, | ||
| 212 | system.ESPIPE => error.Unseekable, | ||
| 213 | system.ENXIO => error.Unseekable, | ||
| 214 | else => os.unexpectedErrorPosix(err), | ||
| 215 | }; | ||
| 216 | } | ||
| 217 | }, | ||
| 218 | Os.windows => { | ||
| 219 | const ipos = try math.cast(isize, pos); | ||
| 220 | if (system.SetFilePointerEx(self.handle, ipos, null, system.FILE_BEGIN) == 0) { | ||
| 221 | const err = system.GetLastError(); | ||
| 222 | return switch (err) { | ||
| 223 | system.ERROR.INVALID_PARAMETER => error.BadFd, | ||
| 224 | else => os.unexpectedErrorWindows(err), | ||
| 225 | }; | ||
| 226 | } | ||
| 227 | }, | ||
| 228 | else => @compileError("unsupported OS: " ++ @tagName(builtin.os)), | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | pub fn getPos(self: &File) !usize { | ||
| 233 | switch (builtin.os) { | ||
| 234 | Os.linux, Os.macosx, Os.ios => { | ||
| 235 | const result = system.lseek(self.handle, 0, system.SEEK_CUR); | ||
| 236 | const err = system.getErrno(result); | ||
| 237 | if (err > 0) { | ||
| 238 | return switch (err) { | ||
| 239 | system.EBADF => error.BadFd, | ||
| 240 | system.EINVAL => error.Unseekable, | ||
| 241 | system.EOVERFLOW => error.Unseekable, | ||
| 242 | system.ESPIPE => error.Unseekable, | ||
| 243 | system.ENXIO => error.Unseekable, | ||
| 244 | else => os.unexpectedErrorPosix(err), | ||
| 245 | }; | ||
| 246 | } | ||
| 247 | return result; | ||
| 248 | }, | ||
| 249 | Os.windows => { | ||
| 250 | var pos : system.LARGE_INTEGER = undefined; | ||
| 251 | if (system.SetFilePointerEx(self.handle, 0, &pos, system.FILE_CURRENT) == 0) { | ||
| 252 | const err = system.GetLastError(); | ||
| 253 | return switch (err) { | ||
| 254 | system.ERROR.INVALID_PARAMETER => error.BadFd, | ||
| 255 | else => os.unexpectedErrorWindows(err), | ||
| 256 | }; | ||
| 257 | } | ||
| 258 | |||
| 259 | assert(pos >= 0); | ||
| 260 | if (@sizeOf(@typeOf(pos)) > @sizeOf(usize)) { | ||
| 261 | if (pos > @maxValue(usize)) { | ||
| 262 | return error.FilePosLargerThanPointerRange; | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | return usize(pos); | ||
| 267 | }, | ||
| 268 | else => @compileError("unsupported OS"), | ||
| 269 | } | ||
| 270 | } | ||
| 271 | |||
| 272 | pub fn getEndPos(self: &File) !usize { | ||
| 273 | if (is_posix) { | ||
| 274 | var stat: system.Stat = undefined; | ||
| 275 | const err = system.getErrno(system.fstat(self.handle, &stat)); | ||
| 276 | if (err > 0) { | ||
| 277 | return switch (err) { | ||
| 278 | system.EBADF => error.BadFd, | ||
| 279 | system.ENOMEM => error.SystemResources, | ||
| 280 | else => os.unexpectedErrorPosix(err), | ||
| 281 | }; | ||
| 282 | } | ||
| 283 | |||
| 284 | return usize(stat.size); | ||
| 285 | } else if (is_windows) { | ||
| 286 | var file_size: system.LARGE_INTEGER = undefined; | ||
| 287 | if (system.GetFileSizeEx(self.handle, &file_size) == 0) { | ||
| 288 | const err = system.GetLastError(); | ||
| 289 | return switch (err) { | ||
| 290 | else => os.unexpectedErrorWindows(err), | ||
| 291 | }; | ||
| 292 | } | ||
| 293 | if (file_size < 0) | ||
| 294 | return error.Overflow; | ||
| 295 | return math.cast(usize, u64(file_size)); | ||
| 296 | } else { | ||
| 297 | unreachable; | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | pub const ReadError = error {}; | ||
| 302 | |||
| 303 | pub fn read(self: &File, buffer: []u8) !usize { | ||
| 304 | if (is_posix) { | ||
| 305 | var index: usize = 0; | ||
| 306 | while (index < buffer.len) { | ||
| 307 | const amt_read = system.read(self.handle, &buffer[index], buffer.len - index); | ||
| 308 | const read_err = system.getErrno(amt_read); | ||
| 309 | if (read_err > 0) { | ||
| 310 | switch (read_err) { | ||
| 311 | system.EINTR => continue, | ||
| 312 | system.EINVAL => unreachable, | ||
| 313 | system.EFAULT => unreachable, | ||
| 314 | system.EBADF => return error.BadFd, | ||
| 315 | system.EIO => return error.Io, | ||
| 316 | else => return os.unexpectedErrorPosix(read_err), | ||
| 317 | } | ||
| 318 | } | ||
| 319 | if (amt_read == 0) return index; | ||
| 320 | index += amt_read; | ||
| 321 | } | ||
| 322 | return index; | ||
| 323 | } else if (is_windows) { | ||
| 324 | var index: usize = 0; | ||
| 325 | while (index < buffer.len) { | ||
| 326 | const want_read_count = system.DWORD(math.min(system.DWORD(@maxValue(system.DWORD)), buffer.len - index)); | ||
| 327 | var amt_read: system.DWORD = undefined; | ||
| 328 | if (system.ReadFile(self.handle, @ptrCast(&c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) { | ||
| 329 | const err = system.GetLastError(); | ||
| 330 | return switch (err) { | ||
| 331 | system.ERROR.OPERATION_ABORTED => continue, | ||
| 332 | system.ERROR.BROKEN_PIPE => return index, | ||
| 333 | else => os.unexpectedErrorWindows(err), | ||
| 334 | }; | ||
| 335 | } | ||
| 336 | if (amt_read == 0) return index; | ||
| 337 | index += amt_read; | ||
| 338 | } | ||
| 339 | return index; | ||
| 340 | } else { | ||
| 341 | unreachable; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | pub const WriteError = os.WindowsWriteError || os.PosixWriteError; | ||
| 346 | |||
| 347 | fn write(self: &File, bytes: []const u8) WriteError!void { | ||
| 348 | if (is_posix) { | ||
| 349 | try os.posixWrite(self.handle, bytes); | ||
| 350 | } else if (is_windows) { | ||
| 351 | try os.windowsWrite(self.handle, bytes); | ||
| 352 | } else { | ||
| 353 | @compileError("Unsupported OS"); | ||
| 354 | } | ||
| 355 | } | ||
| 356 | }; | ||
| 357 | |||
| 358 | pub fn InStream(comptime Error: type) type { | ||
| 359 | return struct { | 97 | return struct { |
| 360 | const Self = this; | 98 | const Self = this; |
| 99 | pub const Error = ReadError; | ||
| 361 | 100 | ||
| 362 | /// Return the number of bytes read. If the number read is smaller than buf.len, it | 101 | /// Return the number of bytes read. If the number read is smaller than buf.len, it |
| 363 | /// means the stream reached the end. Reaching the end of a stream is not an error | 102 | /// means the stream reached the end. Reaching the end of a stream is not an error |
| ... | @@ -486,9 +225,10 @@ pub fn InStream(comptime Error: type) type { | ... | @@ -486,9 +225,10 @@ pub fn InStream(comptime Error: type) type { |
| 486 | }; | 225 | }; |
| 487 | } | 226 | } |
| 488 | 227 | ||
| 489 | pub fn OutStream(comptime Error: type) type { | 228 | pub fn OutStream(comptime WriteError: type) type { |
| 490 | return struct { | 229 | return struct { |
| 491 | const Self = this; | 230 | const Self = this; |
| 231 | pub const Error = WriteError; | ||
| 492 | 232 | ||
| 493 | writeFn: fn(self: &Self, bytes: []const u8) Error!void, | 233 | writeFn: fn(self: &Self, bytes: []const u8) Error!void, |
| 494 | 234 | ||
| ... | @@ -614,10 +354,11 @@ pub fn BufferedOutStream(comptime Error: type) type { | ... | @@ -614,10 +354,11 @@ pub fn BufferedOutStream(comptime Error: type) type { |
| 614 | return BufferedOutStreamCustom(os.page_size, Error); | 354 | return BufferedOutStreamCustom(os.page_size, Error); |
| 615 | } | 355 | } |
| 616 | 356 | ||
| 617 | pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime Error: type) type { | 357 | pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamError: type) type { |
| 618 | return struct { | 358 | return struct { |
| 619 | const Self = this; | 359 | const Self = this; |
| 620 | const Stream = OutStream(Error); | 360 | pub const Stream = OutStream(Error); |
| 361 | pub const Error = OutStreamError; | ||
| 621 | 362 | ||
| 622 | pub stream: Stream, | 363 | pub stream: Stream, |
| 623 | 364 | ||
| ... | @@ -638,9 +379,6 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime Error: type | ... | @@ -638,9 +379,6 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime Error: type |
| 638 | } | 379 | } |
| 639 | 380 | ||
| 640 | pub fn flush(self: &Self) !void { | 381 | pub fn flush(self: &Self) !void { |
| 641 | if (self.index == 0) | ||
| 642 | return; | ||
| 643 | |||
| 644 | try self.unbuffered_out_stream.write(self.buffer[0..self.index]); | 382 | try self.unbuffered_out_stream.write(self.buffer[0..self.index]); |
| 645 | self.index = 0; | 383 | self.index = 0; |
| 646 | } | 384 | } |
| ... | @@ -692,3 +430,51 @@ pub const BufferOutStream = struct { | ... | @@ -692,3 +430,51 @@ pub const BufferOutStream = struct { |
| 692 | } | 430 | } |
| 693 | }; | 431 | }; |
| 694 | 432 | ||
| 433 | |||
| 434 | pub const BufferedAtomicFile = struct { | ||
| 435 | atomic_file: os.AtomicFile, | ||
| 436 | file_stream: FileOutStream, | ||
| 437 | buffered_stream: BufferedOutStream(FileOutStream.Error), | ||
| 438 | |||
| 439 | pub fn create(allocator: &mem.Allocator, dest_path: []const u8) !&BufferedAtomicFile { | ||
| 440 | // TODO with well defined copy elision we don't need this allocation | ||
| 441 | var self = try allocator.create(BufferedAtomicFile); | ||
| 442 | errdefer allocator.destroy(self); | ||
| 443 | |||
| 444 | *self = BufferedAtomicFile { | ||
| 445 | .atomic_file = undefined, | ||
| 446 | .file_stream = undefined, | ||
| 447 | .buffered_stream = undefined, | ||
| 448 | }; | ||
| 449 | |||
| 450 | self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.default_file_mode); | ||
| 451 | errdefer self.atomic_file.deinit(); | ||
| 452 | |||
| 453 | self.file_stream = FileOutStream.init(&self.atomic_file.file); | ||
| 454 | self.buffered_stream = BufferedOutStream(FileOutStream.Error).init(&self.file_stream.stream); | ||
| 455 | return self; | ||
| 456 | } | ||
| 457 | |||
| 458 | /// always call destroy, even after successful finish() | ||
| 459 | pub fn destroy(self: &BufferedAtomicFile) void { | ||
| 460 | const allocator = self.atomic_file.allocator; | ||
| 461 | self.atomic_file.deinit(); | ||
| 462 | allocator.destroy(self); | ||
| 463 | } | ||
| 464 | |||
| 465 | pub fn finish(self: &BufferedAtomicFile) !void { | ||
| 466 | try self.buffered_stream.flush(); | ||
| 467 | try self.atomic_file.finish(); | ||
| 468 | } | ||
| 469 | |||
| 470 | pub fn stream(self: &BufferedAtomicFile) &OutStream(FileOutStream.Error) { | ||
| 471 | return &self.buffered_stream.stream; | ||
| 472 | } | ||
| 473 | }; | ||
| 474 | |||
| 475 | test "import io tests" { | ||
| 476 | comptime { | ||
| 477 | _ = @import("io_test.zig"); | ||
| 478 | } | ||
| 479 | } | ||
| 480 |
std/io_test.zig+2-2| ... | @@ -13,7 +13,7 @@ test "write a file, read it, then delete it" { | ... | @@ -13,7 +13,7 @@ test "write a file, read it, then delete it" { |
| 13 | rng.fillBytes(data[0..]); | 13 | rng.fillBytes(data[0..]); |
| 14 | const tmp_file_name = "temp_test_file.txt"; | 14 | const tmp_file_name = "temp_test_file.txt"; |
| 15 | { | 15 | { |
| 16 | var file = try io.File.openWrite(allocator, tmp_file_name); | 16 | var file = try os.File.openWrite(allocator, tmp_file_name); |
| 17 | defer file.close(); | 17 | defer file.close(); |
| 18 | 18 | ||
| 19 | var file_out_stream = io.FileOutStream.init(&file); | 19 | var file_out_stream = io.FileOutStream.init(&file); |
| ... | @@ -25,7 +25,7 @@ test "write a file, read it, then delete it" { | ... | @@ -25,7 +25,7 @@ test "write a file, read it, then delete it" { |
| 25 | try buf_stream.flush(); | 25 | try buf_stream.flush(); |
| 26 | } | 26 | } |
| 27 | { | 27 | { |
| 28 | var file = try io.File.openRead(allocator, tmp_file_name); | 28 | var file = try os.File.openRead(allocator, tmp_file_name); |
| 29 | defer file.close(); | 29 | defer file.close(); |
| 30 | 30 | ||
| 31 | const file_size = try file.getEndPos(); | 31 | const file_size = try file.getEndPos(); |
std/os/child_process.zig+9-9| ... | @@ -24,9 +24,9 @@ pub const ChildProcess = struct { | ... | @@ -24,9 +24,9 @@ pub const ChildProcess = struct { |
| 24 | 24 | ||
| 25 | pub allocator: &mem.Allocator, | 25 | pub allocator: &mem.Allocator, |
| 26 | 26 | ||
| 27 | pub stdin: ?io.File, | 27 | pub stdin: ?os.File, |
| 28 | pub stdout: ?io.File, | 28 | pub stdout: ?os.File, |
| 29 | pub stderr: ?io.File, | 29 | pub stderr: ?os.File, |
| 30 | 30 | ||
| 31 | pub term: ?(SpawnError!Term), | 31 | pub term: ?(SpawnError!Term), |
| 32 | 32 | ||
| ... | @@ -428,17 +428,17 @@ pub const ChildProcess = struct { | ... | @@ -428,17 +428,17 @@ pub const ChildProcess = struct { |
| 428 | // we are the parent | 428 | // we are the parent |
| 429 | const pid = i32(pid_result); | 429 | const pid = i32(pid_result); |
| 430 | if (self.stdin_behavior == StdIo.Pipe) { | 430 | if (self.stdin_behavior == StdIo.Pipe) { |
| 431 | self.stdin = io.File.openHandle(stdin_pipe[1]); | 431 | self.stdin = os.File.openHandle(stdin_pipe[1]); |
| 432 | } else { | 432 | } else { |
| 433 | self.stdin = null; | 433 | self.stdin = null; |
| 434 | } | 434 | } |
| 435 | if (self.stdout_behavior == StdIo.Pipe) { | 435 | if (self.stdout_behavior == StdIo.Pipe) { |
| 436 | self.stdout = io.File.openHandle(stdout_pipe[0]); | 436 | self.stdout = os.File.openHandle(stdout_pipe[0]); |
| 437 | } else { | 437 | } else { |
| 438 | self.stdout = null; | 438 | self.stdout = null; |
| 439 | } | 439 | } |
| 440 | if (self.stderr_behavior == StdIo.Pipe) { | 440 | if (self.stderr_behavior == StdIo.Pipe) { |
| 441 | self.stderr = io.File.openHandle(stderr_pipe[0]); | 441 | self.stderr = os.File.openHandle(stderr_pipe[0]); |
| 442 | } else { | 442 | } else { |
| 443 | self.stderr = null; | 443 | self.stderr = null; |
| 444 | } | 444 | } |
| ... | @@ -620,17 +620,17 @@ pub const ChildProcess = struct { | ... | @@ -620,17 +620,17 @@ pub const ChildProcess = struct { |
| 620 | }; | 620 | }; |
| 621 | 621 | ||
| 622 | if (g_hChildStd_IN_Wr) |h| { | 622 | if (g_hChildStd_IN_Wr) |h| { |
| 623 | self.stdin = io.File.openHandle(h); | 623 | self.stdin = os.File.openHandle(h); |
| 624 | } else { | 624 | } else { |
| 625 | self.stdin = null; | 625 | self.stdin = null; |
| 626 | } | 626 | } |
| 627 | if (g_hChildStd_OUT_Rd) |h| { | 627 | if (g_hChildStd_OUT_Rd) |h| { |
| 628 | self.stdout = io.File.openHandle(h); | 628 | self.stdout = os.File.openHandle(h); |
| 629 | } else { | 629 | } else { |
| 630 | self.stdout = null; | 630 | self.stdout = null; |
| 631 | } | 631 | } |
| 632 | if (g_hChildStd_ERR_Rd) |h| { | 632 | if (g_hChildStd_ERR_Rd) |h| { |
| 633 | self.stderr = io.File.openHandle(h); | 633 | self.stderr = os.File.openHandle(h); |
| 634 | } else { | 634 | } else { |
| 635 | self.stderr = null; | 635 | self.stderr = null; |
| 636 | } | 636 | } |
std/os/file.zig created+311| ... | @@ -0,0 +1,311 @@ | ||
| 1 | const std = @import("../index.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const os = std.os; | ||
| 4 | const mem = std.mem; | ||
| 5 | const math = std.math; | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | const posix = os.posix; | ||
| 8 | const windows = os.windows; | ||
| 9 | const Os = builtin.Os; | ||
| 10 | |||
| 11 | const is_posix = builtin.os != builtin.Os.windows; | ||
| 12 | const is_windows = builtin.os == builtin.Os.windows; | ||
| 13 | |||
| 14 | pub const File = struct { | ||
| 15 | /// The OS-specific file descriptor or file handle. | ||
| 16 | handle: os.FileHandle, | ||
| 17 | |||
| 18 | const OpenError = os.WindowsOpenError || os.PosixOpenError; | ||
| 19 | |||
| 20 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. | ||
| 21 | /// Call close to clean up. | ||
| 22 | pub fn openRead(allocator: &mem.Allocator, path: []const u8) OpenError!File { | ||
| 23 | if (is_posix) { | ||
| 24 | const flags = posix.O_LARGEFILE|posix.O_RDONLY; | ||
| 25 | const fd = try os.posixOpen(allocator, path, flags, 0); | ||
| 26 | return openHandle(fd); | ||
| 27 | } else if (is_windows) { | ||
| 28 | const handle = try os.windowsOpen(allocator, path, windows.GENERIC_READ, windows.FILE_SHARE_READ, | ||
| 29 | windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL); | ||
| 30 | return openHandle(handle); | ||
| 31 | } else { | ||
| 32 | @compileError("TODO implement openRead for this OS"); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | /// Calls `openWriteMode` with os.default_file_mode for the mode. | ||
| 37 | pub fn openWrite(allocator: &mem.Allocator, path: []const u8) OpenError!File { | ||
| 38 | return openWriteMode(allocator, path, os.default_file_mode); | ||
| 39 | |||
| 40 | } | ||
| 41 | |||
| 42 | /// If the path does not exist it will be created. | ||
| 43 | /// If a file already exists in the destination it will be truncated. | ||
| 44 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. | ||
| 45 | /// Call close to clean up. | ||
| 46 | pub fn openWriteMode(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { | ||
| 47 | if (is_posix) { | ||
| 48 | const flags = posix.O_LARGEFILE|posix.O_WRONLY|posix.O_CREAT|posix.O_CLOEXEC|posix.O_TRUNC; | ||
| 49 | const fd = try os.posixOpen(allocator, path, flags, file_mode); | ||
| 50 | return openHandle(fd); | ||
| 51 | } else if (is_windows) { | ||
| 52 | const handle = try os.windowsOpen(allocator, path, windows.GENERIC_WRITE, | ||
| 53 | windows.FILE_SHARE_WRITE|windows.FILE_SHARE_READ|windows.FILE_SHARE_DELETE, | ||
| 54 | windows.CREATE_ALWAYS, windows.FILE_ATTRIBUTE_NORMAL); | ||
| 55 | return openHandle(handle); | ||
| 56 | } else { | ||
| 57 | @compileError("TODO implement openWriteMode for this OS"); | ||
| 58 | } | ||
| 59 | |||
| 60 | } | ||
| 61 | |||
| 62 | /// If the path does not exist it will be created. | ||
| 63 | /// If a file already exists in the destination this returns OpenError.PathAlreadyExists | ||
| 64 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. | ||
| 65 | /// Call close to clean up. | ||
| 66 | pub fn openWriteNoClobber(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { | ||
| 67 | if (is_posix) { | ||
| 68 | const flags = posix.O_LARGEFILE|posix.O_WRONLY|posix.O_CREAT|posix.O_CLOEXEC|posix.O_EXCL; | ||
| 69 | const fd = try os.posixOpen(allocator, path, flags, file_mode); | ||
| 70 | return openHandle(fd); | ||
| 71 | } else if (is_windows) { | ||
| 72 | const handle = try os.windowsOpen(allocator, path, windows.GENERIC_WRITE, | ||
| 73 | windows.FILE_SHARE_WRITE|windows.FILE_SHARE_READ|windows.FILE_SHARE_DELETE, | ||
| 74 | windows.CREATE_NEW, windows.FILE_ATTRIBUTE_NORMAL); | ||
| 75 | return openHandle(handle); | ||
| 76 | } else { | ||
| 77 | @compileError("TODO implement openWriteMode for this OS"); | ||
| 78 | } | ||
| 79 | |||
| 80 | } | ||
| 81 | |||
| 82 | pub fn openHandle(handle: os.FileHandle) File { | ||
| 83 | return File { | ||
| 84 | .handle = handle, | ||
| 85 | }; | ||
| 86 | } | ||
| 87 | |||
| 88 | |||
| 89 | /// Upon success, the stream is in an uninitialized state. To continue using it, | ||
| 90 | /// you must use the open() function. | ||
| 91 | pub fn close(self: &File) void { | ||
| 92 | os.close(self.handle); | ||
| 93 | self.handle = undefined; | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Calls `os.isTty` on `self.handle`. | ||
| 97 | pub fn isTty(self: &File) bool { | ||
| 98 | return os.isTty(self.handle); | ||
| 99 | } | ||
| 100 | |||
| 101 | pub fn seekForward(self: &File, amount: isize) !void { | ||
| 102 | switch (builtin.os) { | ||
| 103 | Os.linux, Os.macosx, Os.ios => { | ||
| 104 | const result = posix.lseek(self.handle, amount, posix.SEEK_CUR); | ||
| 105 | const err = posix.getErrno(result); | ||
| 106 | if (err > 0) { | ||
| 107 | return switch (err) { | ||
| 108 | posix.EBADF => error.BadFd, | ||
| 109 | posix.EINVAL => error.Unseekable, | ||
| 110 | posix.EOVERFLOW => error.Unseekable, | ||
| 111 | posix.ESPIPE => error.Unseekable, | ||
| 112 | posix.ENXIO => error.Unseekable, | ||
| 113 | else => os.unexpectedErrorPosix(err), | ||
| 114 | }; | ||
| 115 | } | ||
| 116 | }, | ||
| 117 | Os.windows => { | ||
| 118 | if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) { | ||
| 119 | const err = windows.GetLastError(); | ||
| 120 | return switch (err) { | ||
| 121 | windows.ERROR.INVALID_PARAMETER => error.BadFd, | ||
| 122 | else => os.unexpectedErrorWindows(err), | ||
| 123 | }; | ||
| 124 | } | ||
| 125 | }, | ||
| 126 | else => @compileError("unsupported OS"), | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | pub fn seekTo(self: &File, pos: usize) !void { | ||
| 131 | switch (builtin.os) { | ||
| 132 | Os.linux, Os.macosx, Os.ios => { | ||
| 133 | const ipos = try math.cast(isize, pos); | ||
| 134 | const result = posix.lseek(self.handle, ipos, posix.SEEK_SET); | ||
| 135 | const err = posix.getErrno(result); | ||
| 136 | if (err > 0) { | ||
| 137 | return switch (err) { | ||
| 138 | posix.EBADF => error.BadFd, | ||
| 139 | posix.EINVAL => error.Unseekable, | ||
| 140 | posix.EOVERFLOW => error.Unseekable, | ||
| 141 | posix.ESPIPE => error.Unseekable, | ||
| 142 | posix.ENXIO => error.Unseekable, | ||
| 143 | else => os.unexpectedErrorPosix(err), | ||
| 144 | }; | ||
| 145 | } | ||
| 146 | }, | ||
| 147 | Os.windows => { | ||
| 148 | const ipos = try math.cast(isize, pos); | ||
| 149 | if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) { | ||
| 150 | const err = windows.GetLastError(); | ||
| 151 | return switch (err) { | ||
| 152 | windows.ERROR.INVALID_PARAMETER => error.BadFd, | ||
| 153 | else => os.unexpectedErrorWindows(err), | ||
| 154 | }; | ||
| 155 | } | ||
| 156 | }, | ||
| 157 | else => @compileError("unsupported OS: " ++ @tagName(builtin.os)), | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | pub fn getPos(self: &File) !usize { | ||
| 162 | switch (builtin.os) { | ||
| 163 | Os.linux, Os.macosx, Os.ios => { | ||
| 164 | const result = posix.lseek(self.handle, 0, posix.SEEK_CUR); | ||
| 165 | const err = posix.getErrno(result); | ||
| 166 | if (err > 0) { | ||
| 167 | return switch (err) { | ||
| 168 | posix.EBADF => error.BadFd, | ||
| 169 | posix.EINVAL => error.Unseekable, | ||
| 170 | posix.EOVERFLOW => error.Unseekable, | ||
| 171 | posix.ESPIPE => error.Unseekable, | ||
| 172 | posix.ENXIO => error.Unseekable, | ||
| 173 | else => os.unexpectedErrorPosix(err), | ||
| 174 | }; | ||
| 175 | } | ||
| 176 | return result; | ||
| 177 | }, | ||
| 178 | Os.windows => { | ||
| 179 | var pos : windows.LARGE_INTEGER = undefined; | ||
| 180 | if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) { | ||
| 181 | const err = windows.GetLastError(); | ||
| 182 | return switch (err) { | ||
| 183 | windows.ERROR.INVALID_PARAMETER => error.BadFd, | ||
| 184 | else => os.unexpectedErrorWindows(err), | ||
| 185 | }; | ||
| 186 | } | ||
| 187 | |||
| 188 | assert(pos >= 0); | ||
| 189 | if (@sizeOf(@typeOf(pos)) > @sizeOf(usize)) { | ||
| 190 | if (pos > @maxValue(usize)) { | ||
| 191 | return error.FilePosLargerThanPointerRange; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | return usize(pos); | ||
| 196 | }, | ||
| 197 | else => @compileError("unsupported OS"), | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | pub fn getEndPos(self: &File) !usize { | ||
| 202 | if (is_posix) { | ||
| 203 | var stat: posix.Stat = undefined; | ||
| 204 | const err = posix.getErrno(posix.fstat(self.handle, &stat)); | ||
| 205 | if (err > 0) { | ||
| 206 | return switch (err) { | ||
| 207 | posix.EBADF => error.BadFd, | ||
| 208 | posix.ENOMEM => error.SystemResources, | ||
| 209 | else => os.unexpectedErrorPosix(err), | ||
| 210 | }; | ||
| 211 | } | ||
| 212 | |||
| 213 | return usize(stat.size); | ||
| 214 | } else if (is_windows) { | ||
| 215 | var file_size: windows.LARGE_INTEGER = undefined; | ||
| 216 | if (windows.GetFileSizeEx(self.handle, &file_size) == 0) { | ||
| 217 | const err = windows.GetLastError(); | ||
| 218 | return switch (err) { | ||
| 219 | else => os.unexpectedErrorWindows(err), | ||
| 220 | }; | ||
| 221 | } | ||
| 222 | if (file_size < 0) | ||
| 223 | return error.Overflow; | ||
| 224 | return math.cast(usize, u64(file_size)); | ||
| 225 | } else { | ||
| 226 | @compileError("TODO support getEndPos on this OS"); | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | pub const ModeError = error { | ||
| 231 | BadFd, | ||
| 232 | SystemResources, | ||
| 233 | Unexpected, | ||
| 234 | }; | ||
| 235 | |||
| 236 | fn mode(self: &File) ModeError!FileMode { | ||
| 237 | if (is_posix) { | ||
| 238 | var stat: posix.Stat = undefined; | ||
| 239 | const err = posix.getErrno(posix.fstat(self.handle, &stat)); | ||
| 240 | if (err > 0) { | ||
| 241 | return switch (err) { | ||
| 242 | posix.EBADF => error.BadFd, | ||
| 243 | posix.ENOMEM => error.SystemResources, | ||
| 244 | else => os.unexpectedErrorPosix(err), | ||
| 245 | }; | ||
| 246 | } | ||
| 247 | |||
| 248 | return stat.mode; | ||
| 249 | } else if (is_windows) { | ||
| 250 | return {}; | ||
| 251 | } else { | ||
| 252 | @compileError("TODO support file mode on this OS"); | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | pub const ReadError = error {}; | ||
| 257 | |||
| 258 | pub fn read(self: &File, buffer: []u8) !usize { | ||
| 259 | if (is_posix) { | ||
| 260 | var index: usize = 0; | ||
| 261 | while (index < buffer.len) { | ||
| 262 | const amt_read = posix.read(self.handle, &buffer[index], buffer.len - index); | ||
| 263 | const read_err = posix.getErrno(amt_read); | ||
| 264 | if (read_err > 0) { | ||
| 265 | switch (read_err) { | ||
| 266 | posix.EINTR => continue, | ||
| 267 | posix.EINVAL => unreachable, | ||
| 268 | posix.EFAULT => unreachable, | ||
| 269 | posix.EBADF => return error.BadFd, | ||
| 270 | posix.EIO => return error.Io, | ||
| 271 | else => return os.unexpectedErrorPosix(read_err), | ||
| 272 | } | ||
| 273 | } | ||
| 274 | if (amt_read == 0) return index; | ||
| 275 | index += amt_read; | ||
| 276 | } | ||
| 277 | return index; | ||
| 278 | } else if (is_windows) { | ||
| 279 | var index: usize = 0; | ||
| 280 | while (index < buffer.len) { | ||
| 281 | const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); | ||
| 282 | var amt_read: windows.DWORD = undefined; | ||
| 283 | if (windows.ReadFile(self.handle, @ptrCast(&c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) { | ||
| 284 | const err = windows.GetLastError(); | ||
| 285 | return switch (err) { | ||
| 286 | windows.ERROR.OPERATION_ABORTED => continue, | ||
| 287 | windows.ERROR.BROKEN_PIPE => return index, | ||
| 288 | else => os.unexpectedErrorWindows(err), | ||
| 289 | }; | ||
| 290 | } | ||
| 291 | if (amt_read == 0) return index; | ||
| 292 | index += amt_read; | ||
| 293 | } | ||
| 294 | return index; | ||
| 295 | } else { | ||
| 296 | unreachable; | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | pub const WriteError = os.WindowsWriteError || os.PosixWriteError; | ||
| 301 | |||
| 302 | fn write(self: &File, bytes: []const u8) WriteError!void { | ||
| 303 | if (is_posix) { | ||
| 304 | try os.posixWrite(self.handle, bytes); | ||
| 305 | } else if (is_windows) { | ||
| 306 | try os.windowsWrite(self.handle, bytes); | ||
| 307 | } else { | ||
| 308 | @compileError("Unsupported OS"); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | }; | ||
std/os/index.zig+115-38| ... | @@ -17,10 +17,16 @@ pub const posix = switch(builtin.os) { | ... | @@ -17,10 +17,16 @@ pub const posix = switch(builtin.os) { |
| 17 | 17 | ||
| 18 | pub const ChildProcess = @import("child_process.zig").ChildProcess; | 18 | pub const ChildProcess = @import("child_process.zig").ChildProcess; |
| 19 | pub const path = @import("path.zig"); | 19 | pub const path = @import("path.zig"); |
| 20 | pub const File = @import("file.zig").File; | ||
| 20 | 21 | ||
| 21 | pub const line_sep = switch (builtin.os) { | 22 | pub const FileMode = switch (builtin.os) { |
| 22 | Os.windows => "\r\n", | 23 | Os.windows => void, |
| 23 | else => "\n", | 24 | else => u32, |
| 25 | }; | ||
| 26 | |||
| 27 | pub const default_file_mode = switch (builtin.os) { | ||
| 28 | Os.windows => {}, | ||
| 29 | else => 0o666, | ||
| 24 | }; | 30 | }; |
| 25 | 31 | ||
| 26 | pub const page_size = 4 * 1024; | 32 | pub const page_size = 4 * 1024; |
| ... | @@ -672,27 +678,27 @@ const b64_fs_encoder = base64.Base64Encoder.init( | ... | @@ -672,27 +678,27 @@ const b64_fs_encoder = base64.Base64Encoder.init( |
| 672 | pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { | 678 | pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { |
| 673 | if (symLink(allocator, existing_path, new_path)) { | 679 | if (symLink(allocator, existing_path, new_path)) { |
| 674 | return; | 680 | return; |
| 675 | } else |err| { | 681 | } else |err| switch (err) { |
| 676 | if (err != error.PathAlreadyExists) { | 682 | error.PathAlreadyExists => {}, |
| 677 | return err; | 683 | else => return err, // TODO zig should know this set does not include PathAlreadyExists |
| 678 | } | ||
| 679 | } | 684 | } |
| 680 | 685 | ||
| 686 | const dirname = os.path.dirname(new_path); | ||
| 687 | |||
| 681 | var rand_buf: [12]u8 = undefined; | 688 | var rand_buf: [12]u8 = undefined; |
| 682 | const tmp_path = try allocator.alloc(u8, new_path.len + base64.Base64Encoder.calcSize(rand_buf.len)); | 689 | const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len)); |
| 683 | defer allocator.free(tmp_path); | 690 | defer allocator.free(tmp_path); |
| 684 | mem.copy(u8, tmp_path[0..], new_path); | 691 | mem.copy(u8, tmp_path[0..], dirname); |
| 692 | tmp_path[dirname.len] = os.path.sep; | ||
| 685 | while (true) { | 693 | while (true) { |
| 686 | try getRandomBytes(rand_buf[0..]); | 694 | try getRandomBytes(rand_buf[0..]); |
| 687 | b64_fs_encoder.encode(tmp_path[new_path.len..], rand_buf); | 695 | b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); |
| 696 | |||
| 688 | if (symLink(allocator, existing_path, tmp_path)) { | 697 | if (symLink(allocator, existing_path, tmp_path)) { |
| 689 | return rename(allocator, tmp_path, new_path); | 698 | return rename(allocator, tmp_path, new_path); |
| 690 | } else |err| { | 699 | } else |err| switch (err) { |
| 691 | if (err == error.PathAlreadyExists) { | 700 | error.PathAlreadyExists => continue, |
| 692 | continue; | 701 | else => return err, // TODO zig should know this set does not include PathAlreadyExists |
| 693 | } else { | ||
| 694 | return err; | ||
| 695 | } | ||
| 696 | } | 702 | } |
| 697 | } | 703 | } |
| 698 | 704 | ||
| ... | @@ -750,37 +756,108 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void { | ... | @@ -750,37 +756,108 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void { |
| 750 | } | 756 | } |
| 751 | } | 757 | } |
| 752 | 758 | ||
| 753 | /// Calls ::copyFileMode with 0o666 for the mode. | 759 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is |
| 760 | /// merged and readily available, | ||
| 761 | /// there is a possibility of power loss or application termination leaving temporary files present | ||
| 762 | /// in the same directory as dest_path. | ||
| 763 | /// Destination file will have the same mode as the source file. | ||
| 754 | pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) !void { | 764 | pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) !void { |
| 755 | return copyFileMode(allocator, source_path, dest_path, 0o666); | 765 | var in_file = try os.File.openRead(allocator, source_path); |
| 756 | } | 766 | defer in_file.close(); |
| 757 | 767 | ||
| 758 | // TODO instead of accepting a mode argument, use the mode from fstat'ing the source path once open | 768 | const mode = try in_file.mode(); |
| 759 | /// Guaranteed to be atomic. | ||
| 760 | pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: usize) !void { | ||
| 761 | var rand_buf: [12]u8 = undefined; | ||
| 762 | const tmp_path = try allocator.alloc(u8, dest_path.len + base64.Base64Encoder.calcSize(rand_buf.len)); | ||
| 763 | defer allocator.free(tmp_path); | ||
| 764 | mem.copy(u8, tmp_path[0..], dest_path); | ||
| 765 | try getRandomBytes(rand_buf[0..]); | ||
| 766 | b64_fs_encoder.encode(tmp_path[dest_path.len..], rand_buf); | ||
| 767 | 769 | ||
| 768 | var out_file = try io.File.openWriteMode(allocator, tmp_path, mode); | 770 | var atomic_file = try AtomicFile.init(allocator, dest_path, mode); |
| 769 | defer out_file.close(); | 771 | defer atomic_file.deinit(); |
| 770 | errdefer _ = deleteFile(allocator, tmp_path); | ||
| 771 | 772 | ||
| 772 | var in_file = try io.File.openRead(allocator, source_path); | 773 | var buf: [page_size]u8 = undefined; |
| 774 | while (true) { | ||
| 775 | const amt = try in_file.read(buf[0..]); | ||
| 776 | try atomic_file.file.write(buf[0..amt]); | ||
| 777 | if (amt != buf.len) { | ||
| 778 | return atomic_file.finish(); | ||
| 779 | } | ||
| 780 | } | ||
| 781 | } | ||
| 782 | |||
| 783 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | ||
| 784 | /// merged and readily available, | ||
| 785 | /// there is a possibility of power loss or application termination leaving temporary files present | ||
| 786 | pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void { | ||
| 787 | var in_file = try os.File.openRead(allocator, source_path); | ||
| 773 | defer in_file.close(); | 788 | defer in_file.close(); |
| 774 | 789 | ||
| 790 | var atomic_file = try AtomicFile.init(allocator, dest_path, mode); | ||
| 791 | defer atomic_file.deinit(); | ||
| 792 | |||
| 775 | var buf: [page_size]u8 = undefined; | 793 | var buf: [page_size]u8 = undefined; |
| 776 | while (true) { | 794 | while (true) { |
| 777 | const amt = try in_file.read(buf[0..]); | 795 | const amt = try in_file.read(buf[0..]); |
| 778 | try out_file.write(buf[0..amt]); | 796 | try atomic_file.file.write(buf[0..amt]); |
| 779 | if (amt != buf.len) | 797 | if (amt != buf.len) { |
| 780 | return rename(allocator, tmp_path, dest_path); | 798 | return atomic_file.finish(); |
| 799 | } | ||
| 781 | } | 800 | } |
| 782 | } | 801 | } |
| 783 | 802 | ||
| 803 | pub const AtomicFile = struct { | ||
| 804 | allocator: &Allocator, | ||
| 805 | file: os.File, | ||
| 806 | tmp_path: []u8, | ||
| 807 | dest_path: []const u8, | ||
| 808 | finished: bool, | ||
| 809 | |||
| 810 | /// dest_path must remain valid for the lifetime of AtomicFile | ||
| 811 | /// call finish to atomically replace dest_path with contents | ||
| 812 | pub fn init(allocator: &Allocator, dest_path: []const u8, mode: FileMode) !AtomicFile { | ||
| 813 | const dirname = os.path.dirname(dest_path); | ||
| 814 | |||
| 815 | var rand_buf: [12]u8 = undefined; | ||
| 816 | const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len)); | ||
| 817 | errdefer allocator.free(tmp_path); | ||
| 818 | mem.copy(u8, tmp_path[0..], dirname); | ||
| 819 | tmp_path[dirname.len] = os.path.sep; | ||
| 820 | |||
| 821 | while (true) { | ||
| 822 | try getRandomBytes(rand_buf[0..]); | ||
| 823 | b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); | ||
| 824 | |||
| 825 | const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) { | ||
| 826 | error.PathAlreadyExists => continue, | ||
| 827 | // TODO zig should figure out that this error set does not include PathAlreadyExists since | ||
| 828 | // it is handled in the above switch | ||
| 829 | else => return err, | ||
| 830 | }; | ||
| 831 | |||
| 832 | return AtomicFile { | ||
| 833 | .allocator = allocator, | ||
| 834 | .file = file, | ||
| 835 | .tmp_path = tmp_path, | ||
| 836 | .dest_path = dest_path, | ||
| 837 | .finished = false, | ||
| 838 | }; | ||
| 839 | } | ||
| 840 | } | ||
| 841 | |||
| 842 | /// always call deinit, even after successful finish() | ||
| 843 | pub fn deinit(self: &AtomicFile) void { | ||
| 844 | if (!self.finished) { | ||
| 845 | self.file.close(); | ||
| 846 | deleteFile(self.allocator, self.tmp_path) catch {}; | ||
| 847 | self.allocator.free(self.tmp_path); | ||
| 848 | self.finished = true; | ||
| 849 | } | ||
| 850 | } | ||
| 851 | |||
| 852 | pub fn finish(self: &AtomicFile) !void { | ||
| 853 | assert(!self.finished); | ||
| 854 | self.file.close(); | ||
| 855 | try rename(self.allocator, self.tmp_path, self.dest_path); | ||
| 856 | self.allocator.free(self.tmp_path); | ||
| 857 | self.finished = true; | ||
| 858 | } | ||
| 859 | }; | ||
| 860 | |||
| 784 | pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) !void { | 861 | pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) !void { |
| 785 | const full_buf = try allocator.alloc(u8, old_path.len + new_path.len + 2); | 862 | const full_buf = try allocator.alloc(u8, old_path.len + new_path.len + 2); |
| 786 | defer allocator.free(full_buf); | 863 | defer allocator.free(full_buf); |
| ... | @@ -1620,19 +1697,19 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) (error{Unexpected}) { | ... | @@ -1620,19 +1697,19 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) (error{Unexpected}) { |
| 1620 | return error.Unexpected; | 1697 | return error.Unexpected; |
| 1621 | } | 1698 | } |
| 1622 | 1699 | ||
| 1623 | pub fn openSelfExe() !io.File { | 1700 | pub fn openSelfExe() !os.File { |
| 1624 | switch (builtin.os) { | 1701 | switch (builtin.os) { |
| 1625 | Os.linux => { | 1702 | Os.linux => { |
| 1626 | const proc_file_path = "/proc/self/exe"; | 1703 | const proc_file_path = "/proc/self/exe"; |
| 1627 | var fixed_buffer_mem: [proc_file_path.len + 1]u8 = undefined; | 1704 | var fixed_buffer_mem: [proc_file_path.len + 1]u8 = undefined; |
| 1628 | var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | 1705 | var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]); |
| 1629 | return io.File.openRead(&fixed_allocator.allocator, proc_file_path); | 1706 | return os.File.openRead(&fixed_allocator.allocator, proc_file_path); |
| 1630 | }, | 1707 | }, |
| 1631 | Os.macosx, Os.ios => { | 1708 | Os.macosx, Os.ios => { |
| 1632 | var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined; | 1709 | var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined; |
| 1633 | var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | 1710 | var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]); |
| 1634 | const self_exe_path = try selfExePath(&fixed_allocator.allocator); | 1711 | const self_exe_path = try selfExePath(&fixed_allocator.allocator); |
| 1635 | return io.File.openRead(&fixed_allocator.allocator, self_exe_path); | 1712 | return os.File.openRead(&fixed_allocator.allocator, self_exe_path); |
| 1636 | }, | 1713 | }, |
| 1637 | else => @compileError("Unsupported OS"), | 1714 | else => @compileError("Unsupported OS"), |
| 1638 | } | 1715 | } |
std/zig/parser.zig+1-4| ... | @@ -1038,11 +1038,8 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; | ... | @@ -1038,11 +1038,8 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; |
| 1038 | fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { | 1038 | fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { |
| 1039 | var padded_source: [0x100]u8 = undefined; | 1039 | var padded_source: [0x100]u8 = undefined; |
| 1040 | std.mem.copy(u8, padded_source[0..source.len], source); | 1040 | std.mem.copy(u8, padded_source[0..source.len], source); |
| 1041 | padded_source[source.len + 0] = '\n'; | ||
| 1042 | padded_source[source.len + 1] = '\n'; | ||
| 1043 | padded_source[source.len + 2] = '\n'; | ||
| 1044 | 1041 | ||
| 1045 | var tokenizer = Tokenizer.init(padded_source[0..source.len + 3]); | 1042 | var tokenizer = Tokenizer.init(padded_source[0..source.len]); |
| 1046 | var parser = Parser.init(&tokenizer, allocator, "(memory buffer)"); | 1043 | var parser = Parser.init(&tokenizer, allocator, "(memory buffer)"); |
| 1047 | defer parser.deinit(); | 1044 | defer parser.deinit(); |
| 1048 | 1045 |
test/compare_output.zig+4-2| ... | @@ -1,4 +1,6 @@ | ... | @@ -1,4 +1,6 @@ |
| 1 | const os = @import("std").os; | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); | ||
| 3 | const os = std.os; | ||
| 2 | const tests = @import("tests.zig"); | 4 | const tests = @import("tests.zig"); |
| 3 | 5 | ||
| 4 | pub fn addCases(cases: &tests.CompareOutputContext) void { | 6 | pub fn addCases(cases: &tests.CompareOutputContext) void { |
| ... | @@ -8,7 +10,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { | ... | @@ -8,7 +10,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { |
| 8 | \\ _ = c.puts(c"Hello, world!"); | 10 | \\ _ = c.puts(c"Hello, world!"); |
| 9 | \\ return 0; | 11 | \\ return 0; |
| 10 | \\} | 12 | \\} |
| 11 | , "Hello, world!" ++ os.line_sep); | 13 | , "Hello, world!" ++ std.cstr.line_sep); |
| 12 | 14 | ||
| 13 | cases.addCase(x: { | 15 | cases.addCase(x: { |
| 14 | var tc = cases.create("multiple files with private function", | 16 | var tc = cases.create("multiple files with private function", |