authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-21 00:46:42-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-21 00:46:42-04:00
logbda5539e9d8b5f15b8165393e4118c8601188276
tree017556e65eaa6abb24bfe414760f258f6aacff62
parent302936309a30c9c0bcfe222ec1de470b36c18a06

*WIP* std.os assumes comptime-known max path size

this allows us to remove the requirement of allocators for a lot of functions See #1392

8 files changed, 254 insertions(+), 251 deletions(-)

std/debug/index.zig+1-1
......@@ -340,7 +340,7 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace {
340340 }
341341}
342342
343fn printLineFromFile(allocator: *mem.Allocator, out_stream: var, line_info: *const LineInfo) !void {
343fn printLineFromFile(out_stream: var, line_info: *const LineInfo) !void {
344344 var f = try os.File.openRead(line_info.file_name);
345345 defer f.close();
346346 // TODO fstat and make sure that the file has the correct size
std/io_test.zig+3-3
......@@ -16,7 +16,7 @@ test "write a file, read it, then delete it" {
1616 prng.random.bytes(data[0..]);
1717 const tmp_file_name = "temp_test_file.txt";
1818 {
19 var file = try os.File.openWrite(allocator, tmp_file_name);
19 var file = try os.File.openWrite(tmp_file_name);
2020 defer file.close();
2121
2222 var file_out_stream = io.FileOutStream.init(&file);
......@@ -63,7 +63,7 @@ test "BufferOutStream" {
6363}
6464
6565test "SliceInStream" {
66 const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7 };
66 const bytes = []const u8{ 1, 2, 3, 4, 5, 6, 7 };
6767 var ss = io.SliceInStream.init(bytes);
6868
6969 var dest: [4]u8 = undefined;
......@@ -81,7 +81,7 @@ test "SliceInStream" {
8181}
8282
8383test "PeekStream" {
84 const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7, 8 };
84 const bytes = []const u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
8585 var ss = io.SliceInStream.init(bytes);
8686 var ps = io.PeekStream(2, io.SliceInStream.Error).init(&ss.stream);
8787
std/os/file.zig+27-15
......@@ -27,7 +27,6 @@ pub const File = struct {
2727
2828 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
2929
30 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
3130 /// Call close to clean up.
3231 pub fn openRead(path: []const u8) OpenError!File {
3332 if (is_posix) {
......@@ -49,15 +48,14 @@ pub const File = struct {
4948 }
5049
5150 /// Calls `openWriteMode` with os.File.default_mode for the mode.
52 pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {
53 return openWriteMode(allocator, path, os.File.default_mode);
51 pub fn openWrite(path: []const u8) OpenError!File {
52 return openWriteMode(path, os.File.default_mode);
5453 }
5554
5655 /// If the path does not exist it will be created.
5756 /// If a file already exists in the destination it will be truncated.
58 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
5957 /// Call close to clean up.
60 pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
58 pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File {
6159 if (is_posix) {
6260 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
6361 const fd = try os.posixOpen(path, flags, file_mode);
......@@ -78,16 +76,14 @@ pub const File = struct {
7876
7977 /// If the path does not exist it will be created.
8078 /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
81 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
8279 /// Call close to clean up.
83 pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File {
80 pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File {
8481 if (is_posix) {
8582 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
86 const fd = try os.posixOpen(allocator, path, flags, file_mode);
83 const fd = try os.posixOpen(path, flags, file_mode);
8784 return openHandle(fd);
8885 } else if (is_windows) {
8986 const handle = try os.windowsOpen(
90 allocator,
9187 path,
9288 windows.GENERIC_WRITE,
9389 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
......@@ -117,12 +113,13 @@ pub const File = struct {
117113 Unexpected,
118114 };
119115
120 pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void {
121 const path_with_null = try std.cstr.addNullByte(allocator, path);
122 defer allocator.free(path_with_null);
123
116 pub fn accessC(path: [*]const u8) AccessError!void {
117 if (is_windows) {
118 // this needs to convert to UTF-16LE and call accessW
119 @compileError("TODO support windows");
120 }
124121 if (is_posix) {
125 const result = posix.access(path_with_null.ptr, posix.F_OK);
122 const result = posix.access(path, posix.F_OK);
126123 const err = posix.getErrno(result);
127124 switch (err) {
128125 0 => return,
......@@ -141,7 +138,7 @@ pub const File = struct {
141138 else => return os.unexpectedErrorPosix(err),
142139 }
143140 } else if (is_windows) {
144 if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
141 if (os.windows.GetFileAttributesA(path) != os.windows.INVALID_FILE_ATTRIBUTES) {
145142 return;
146143 }
147144
......@@ -158,6 +155,21 @@ pub const File = struct {
158155 }
159156 }
160157
158 pub fn access(path: []const u8) AccessError!void {
159 if (is_windows) {
160 // this needs to convert to UTF-16LE and call accessW
161 @compileError("TODO support windows");
162 }
163 if (is_posix) {
164 var path_with_null: [posix.PATH_MAX]u8 = undefined;
165 if (path.len >= posix.PATH_MAX) return error.NameTooLong;
166 mem.copy(u8, path_with_null[0..], path);
167 path_with_null[path.len] = 0;
168 return accessC(&path_with_null);
169 }
170 @compileError("TODO implement access for this OS");
171 }
172
161173 /// Upon success, the stream is in an uninitialized state. To continue using it,
162174 /// you must use the open() function.
163175 pub fn close(self: *File) void {
std/os/index.zig+168-178
......@@ -39,11 +39,14 @@ pub const File = @import("file.zig").File;
3939pub const time = @import("time.zig");
4040
4141pub const page_size = 4 * 1024;
42pub const PATH_MAX = switch (builtin.os) {
43 Os.linux => linux.PATH_MAX,
44 Os.macosx, Os.ios => darwin.PATH_MAX,
42pub const MAX_PATH_BYTES = switch (builtin.os) {
43 Os.linux, Os.macosx, Os.ios => posix.PATH_MAX,
44 // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
45 // If it would require 4 UTF-8 bytes, then there would be a surrogate
46 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
47 // +1 for the null byte at the end, which can be encoded in 1 byte.
48 Os.windows => 32767 * 3 + 1,
4549 else => @compileError("Unsupported OS"),
46 // https://msdn.microsoft.com/en-us/library/930f87yf.aspx
4750};
4851
4952pub const UserInfo = @import("get_user_id.zig").UserInfo;
......@@ -423,7 +426,6 @@ pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, off
423426}
424427
425428pub const PosixOpenError = error{
426 OutOfMemory,
427429 AccessDenied,
428430 FileTooBig,
429431 IsDir,
......@@ -444,12 +446,10 @@ pub const PosixOpenError = error{
444446/// Calls POSIX open, keeps trying if it gets interrupted, and translates
445447/// the return value into zig errors.
446448pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 {
447 var path_with_null: [PATH_MAX]u8 = undefined;
448 if (file_path.len > PATH_MAX - 1)
449 return error.NameTooLong;
450 mem.copy(u8, path_with_null[0..PATH_MAX - 1], file_path);
451 path_with_null[file_path.len] = '\x00';
452
449 var path_with_null: [posix.PATH_MAX]u8 = undefined;
450 if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;
451 mem.copy(u8, path_with_null[0..], file_path);
452 path_with_null[file_path.len] = 0;
453453 return posixOpenC(&path_with_null, flags, perm);
454454}
455455
......@@ -728,43 +728,35 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned
728728}
729729
730730/// Caller must free the returned memory.
731pub fn getCwd(allocator: *Allocator) ![]u8 {
732 switch (builtin.os) {
733 Os.windows => {
734 var buf = try allocator.alloc(u8, 256);
735 errdefer allocator.free(buf);
736
737 while (true) {
738 const result = windows.GetCurrentDirectoryA(@intCast(windows.WORD, buf.len), buf.ptr);
731pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
732 var buf: [MAX_PATH_BYTES]u8 = undefined;
733 return mem.dupe(allocator, u8, try getCwd(&buf));
734}
739735
740 if (result == 0) {
741 const err = windows.GetLastError();
742 return switch (err) {
743 else => unexpectedErrorWindows(err),
744 };
745 }
736pub const GetCwdError = error{Unexpected};
746737
747 if (result > buf.len) {
748 buf = try allocator.realloc(u8, buf, result);
749 continue;
738/// The result is a slice of out_buffer.
739pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 {
740 switch (builtin.os) {
741 Os.windows => {
742 var utf16le_buf: [windows_util.PATH_MAX_UTF16]u16 = undefined;
743 const result = windows.GetCurrentDirectoryW(utf16le_buf.len, &utf16le_buf);
744 if (result == 0) {
745 const err = windows.GetLastError();
746 switch (err) {
747 else => return unexpectedErrorWindows(err),
750748 }
751
752 return allocator.shrink(u8, buf, result);
753749 }
750 assert(result <= buf.len);
751 const utf16le_slice = utf16le_buf[0..result];
752 return std.unicode.utf16leToUtf8(out_buffer, utf16le_buf);
754753 },
755754 else => {
756 var buf = try allocator.alloc(u8, 1024);
757 errdefer allocator.free(buf);
758 while (true) {
759 const err = posix.getErrno(posix.getcwd(buf.ptr, buf.len));
760 if (err == posix.ERANGE) {
761 buf = try allocator.realloc(u8, buf, buf.len * 2);
762 continue;
763 } else if (err > 0) {
764 return unexpectedErrorPosix(err);
765 }
766
767 return allocator.shrink(u8, buf, cstr.len(buf.ptr));
755 const err = posix.getErrno(posix.getcwd(out_buffer, out_buffer.len));
756 switch (err) {
757 0 => return cstr.toSlice(out_buffer),
758 posix.ERANGE => unreachable,
759 else => return unexpectedErrorPosix(err),
768760 }
769761 },
770762 }
......@@ -899,56 +891,45 @@ pub const DeleteFileError = error{
899891 Unexpected,
900892};
901893
902pub fn deleteFile(allocator: *Allocator, file_path: []const u8) DeleteFileError!void {
894pub fn deleteFile(file_path: []const u8) DeleteFileError!void {
903895 if (builtin.os == Os.windows) {
904 return deleteFileWindows(allocator, file_path);
896 return deleteFileWindows(file_path);
905897 } else {
906 return deleteFilePosix(allocator, file_path);
898 return deleteFilePosix(file_path);
907899 }
908900}
909901
910pub fn deleteFileWindows(allocator: *Allocator, file_path: []const u8) !void {
911 const buf = try allocator.alloc(u8, file_path.len + 1);
912 defer allocator.free(buf);
913
914 mem.copy(u8, buf, file_path);
915 buf[file_path.len] = 0;
902pub fn deleteFileWindows(file_path: []const u8) !void {
903 @compileError("TODO rewrite with DeleteFileW and no allocator");
904}
916905
917 if (windows.DeleteFileA(buf.ptr) == 0) {
918 const err = windows.GetLastError();
919 return switch (err) {
920 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
921 windows.ERROR.ACCESS_DENIED => error.AccessDenied,
922 windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
923 else => unexpectedErrorWindows(err),
924 };
906pub fn deleteFilePosixC(file_path: [*]const u8) !void {
907 const err = posix.getErrno(posix.unlink(file_path));
908 switch (err) {
909 0 => return,
910 posix.EACCES => return error.AccessDenied,
911 posix.EPERM => return error.AccessDenied,
912 posix.EBUSY => return error.FileBusy,
913 posix.EFAULT => unreachable,
914 posix.EINVAL => unreachable,
915 posix.EIO => return error.FileSystem,
916 posix.EISDIR => return error.IsDir,
917 posix.ELOOP => return error.SymLinkLoop,
918 posix.ENAMETOOLONG => return error.NameTooLong,
919 posix.ENOENT => return error.FileNotFound,
920 posix.ENOTDIR => return error.NotDir,
921 posix.ENOMEM => return error.SystemResources,
922 posix.EROFS => return error.ReadOnlyFileSystem,
923 else => return unexpectedErrorPosix(err),
925924 }
926925}
927926
928pub fn deleteFilePosix(allocator: *Allocator, file_path: []const u8) !void {
929 const buf = try allocator.alloc(u8, file_path.len + 1);
930 defer allocator.free(buf);
931
932 mem.copy(u8, buf, file_path);
933 buf[file_path.len] = 0;
934
935 const err = posix.getErrno(posix.unlink(buf.ptr));
936 if (err > 0) {
937 return switch (err) {
938 posix.EACCES, posix.EPERM => error.AccessDenied,
939 posix.EBUSY => error.FileBusy,
940 posix.EFAULT, posix.EINVAL => unreachable,
941 posix.EIO => error.FileSystem,
942 posix.EISDIR => error.IsDir,
943 posix.ELOOP => error.SymLinkLoop,
944 posix.ENAMETOOLONG => error.NameTooLong,
945 posix.ENOENT => error.FileNotFound,
946 posix.ENOTDIR => error.NotDir,
947 posix.ENOMEM => error.SystemResources,
948 posix.EROFS => error.ReadOnlyFileSystem,
949 else => unexpectedErrorPosix(err),
950 };
951 }
927pub fn deleteFilePosix(file_path: []const u8) !void {
928 var path_with_null: [posix.PATH_MAX]u8 = undefined;
929 if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;
930 mem.copy(u8, path_with_null[0..], file_path);
931 path_with_null[file_path.len] = 0;
932 return deleteFilePosixC(&path_with_null);
952933}
953934
954935/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
......@@ -956,6 +937,7 @@ pub fn deleteFilePosix(allocator: *Allocator, file_path: []const u8) !void {
956937/// there is a possibility of power loss or application termination leaving temporary files present
957938/// in the same directory as dest_path.
958939/// Destination file will have the same mode as the source file.
940/// TODO investigate if this can work with no allocator
959941pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []const u8) !void {
960942 var in_file = try os.File.openRead(source_path);
961943 defer in_file.close();
......@@ -978,6 +960,7 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con
978960/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
979961/// merged and readily available,
980962/// there is a possibility of power loss or application termination leaving temporary files present
963/// TODO investigate if this can work with no allocator
981964pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
982965 var in_file = try os.File.openRead(source_path);
983966 defer in_file.close();
......@@ -996,6 +979,7 @@ pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: [
996979}
997980
998981pub const AtomicFile = struct {
982 /// TODO investigate if we can make this work with no allocator
999983 allocator: *Allocator,
1000984 file: os.File,
1001985 tmp_path: []u8,
......@@ -1023,7 +1007,7 @@ pub const AtomicFile = struct {
10231007 try getRandomBytes(rand_buf[0..]);
10241008 b64_fs_encoder.encode(tmp_path[dirname_component_len..], rand_buf);
10251009
1026 const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) {
1010 const file = os.File.openWriteNoClobber(tmp_path, mode) catch |err| switch (err) {
10271011 error.PathAlreadyExists => continue,
10281012 // TODO zig should figure out that this error set does not include PathAlreadyExists since
10291013 // it is handled in the above switch
......@@ -1059,56 +1043,59 @@ pub const AtomicFile = struct {
10591043 }
10601044};
10611045
1062pub fn rename(allocator: *Allocator, old_path: []const u8, new_path: []const u8) !void {
1063 const full_buf = try allocator.alloc(u8, old_path.len + new_path.len + 2);
1064 defer allocator.free(full_buf);
1065
1066 const old_buf = full_buf;
1067 mem.copy(u8, old_buf, old_path);
1068 old_buf[old_path.len] = 0;
1069
1070 const new_buf = full_buf[old_path.len + 1 ..];
1071 mem.copy(u8, new_buf, new_path);
1072 new_buf[new_path.len] = 0;
1073
1046pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void {
10741047 if (is_windows) {
1075 const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
1076 if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) {
1077 const err = windows.GetLastError();
1078 return switch (err) {
1079 else => unexpectedErrorWindows(err),
1080 };
1081 }
1048 @compileError("TODO implement for windows");
10821049 } else {
1083 const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));
1084 if (err > 0) {
1085 return switch (err) {
1086 posix.EACCES, posix.EPERM => error.AccessDenied,
1087 posix.EBUSY => error.FileBusy,
1088 posix.EDQUOT => error.DiskQuota,
1089 posix.EFAULT, posix.EINVAL => unreachable,
1090 posix.EISDIR => error.IsDir,
1091 posix.ELOOP => error.SymLinkLoop,
1092 posix.EMLINK => error.LinkQuotaExceeded,
1093 posix.ENAMETOOLONG => error.NameTooLong,
1094 posix.ENOENT => error.FileNotFound,
1095 posix.ENOTDIR => error.NotDir,
1096 posix.ENOMEM => error.SystemResources,
1097 posix.ENOSPC => error.NoSpaceLeft,
1098 posix.EEXIST, posix.ENOTEMPTY => error.PathAlreadyExists,
1099 posix.EROFS => error.ReadOnlyFileSystem,
1100 posix.EXDEV => error.RenameAcrossMountPoints,
1101 else => unexpectedErrorPosix(err),
1102 };
1050 const err = posix.getErrno(posix.rename(old_path, new_path));
1051 switch (err) {
1052 0 => return,
1053 posix.EACCES => return error.AccessDenied,
1054 posix.EPERM => return error.AccessDenied,
1055 posix.EBUSY => return error.FileBusy,
1056 posix.EDQUOT => return error.DiskQuota,
1057 posix.EFAULT => unreachable,
1058 posix.EINVAL => unreachable,
1059 posix.EISDIR => return error.IsDir,
1060 posix.ELOOP => return error.SymLinkLoop,
1061 posix.EMLINK => return error.LinkQuotaExceeded,
1062 posix.ENAMETOOLONG => return error.NameTooLong,
1063 posix.ENOENT => return error.FileNotFound,
1064 posix.ENOTDIR => return error.NotDir,
1065 posix.ENOMEM => return error.SystemResources,
1066 posix.ENOSPC => return error.NoSpaceLeft,
1067 posix.EEXIST => return error.PathAlreadyExists,
1068 posix.ENOTEMPTY => return error.PathAlreadyExists,
1069 posix.EROFS => return error.ReadOnlyFileSystem,
1070 posix.EXDEV => return error.RenameAcrossMountPoints,
1071 else => return unexpectedErrorPosix(err),
11031072 }
11041073 }
11051074}
11061075
1107pub fn makeDir(allocator: *Allocator, dir_path: []const u8) !void {
1076pub fn rename(old_path: []const u8, new_path: []const u8) !void {
1077 if (is_windows) {
1078 @compileError("TODO rewrite with MoveFileExW and no allocator");
1079 } else {
1080 var old_path_with_null: [posix.PATH_MAX]u8 = undefined;
1081 if (old_path.len >= posix.PATH_MAX) return error.NameTooLong;
1082 mem.copy(u8, old_path_with_null[0..], old_path);
1083 old_path_with_null[old_path.len] = 0;
1084
1085 var new_path_with_null: [posix.PATH_MAX]u8 = undefined;
1086 if (new_path.len >= posix.PATH_MAX) return error.NameTooLong;
1087 mem.copy(u8, new_path_with_null[0..], new_path);
1088 new_path_with_null[new_path.len] = 0;
1089
1090 return renameC(&old_path_with_null, &new_path_with_null);
1091 }
1092}
1093
1094pub fn makeDir(dir_path: []const u8) !void {
11081095 if (is_windows) {
1109 return makeDirWindows(allocator, dir_path);
1096 return makeDirWindows(dir_path);
11101097 } else {
1111 return makeDirPosix(allocator, dir_path);
1098 return makeDirPosix(dir_path);
11121099 }
11131100}
11141101
......@@ -1126,30 +1113,35 @@ pub fn makeDirWindows(allocator: *Allocator, dir_path: []const u8) !void {
11261113 }
11271114}
11281115
1129pub fn makeDirPosix(allocator: *Allocator, dir_path: []const u8) !void {
1130 const path_buf = try cstr.addNullByte(allocator, dir_path);
1131 defer allocator.free(path_buf);
1132
1116pub fn makeDirPosixC(dir_path: [*]const u8) !void {
11331117 const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));
1134 if (err > 0) {
1135 return switch (err) {
1136 posix.EACCES, posix.EPERM => error.AccessDenied,
1137 posix.EDQUOT => error.DiskQuota,
1138 posix.EEXIST => error.PathAlreadyExists,
1139 posix.EFAULT => unreachable,
1140 posix.ELOOP => error.SymLinkLoop,
1141 posix.EMLINK => error.LinkQuotaExceeded,
1142 posix.ENAMETOOLONG => error.NameTooLong,
1143 posix.ENOENT => error.FileNotFound,
1144 posix.ENOMEM => error.SystemResources,
1145 posix.ENOSPC => error.NoSpaceLeft,
1146 posix.ENOTDIR => error.NotDir,
1147 posix.EROFS => error.ReadOnlyFileSystem,
1148 else => unexpectedErrorPosix(err),
1149 };
1118 switch (err) {
1119 0 => return,
1120 posix.EACCES => return error.AccessDenied,
1121 posix.EPERM => return error.AccessDenied,
1122 posix.EDQUOT => return error.DiskQuota,
1123 posix.EEXIST => return error.PathAlreadyExists,
1124 posix.EFAULT => unreachable,
1125 posix.ELOOP => return error.SymLinkLoop,
1126 posix.EMLINK => return error.LinkQuotaExceeded,
1127 posix.ENAMETOOLONG => return error.NameTooLong,
1128 posix.ENOENT => return error.FileNotFound,
1129 posix.ENOMEM => return error.SystemResources,
1130 posix.ENOSPC => return error.NoSpaceLeft,
1131 posix.ENOTDIR => return error.NotDir,
1132 posix.EROFS => return error.ReadOnlyFileSystem,
1133 else => return unexpectedErrorPosix(err),
11501134 }
11511135}
11521136
1137pub fn makeDirPosix(dir_path: []const u8) !void {
1138 var path_with_null: [posix.PATH_MAX]u8 = undefined;
1139 if (dir_path.len >= posix.PATH_MAX) return error.NameTooLong;
1140 mem.copy(u8, path_with_null[0..], dir_path);
1141 path_with_null[dir_path.len] = 0;
1142 return makeDirPosixC(&path_with_null);
1143}
1144
11531145/// Calls makeDir recursively to make an entire path. Returns success if the path
11541146/// already exists and is a directory.
11551147pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
......@@ -1409,6 +1401,7 @@ pub const Dir = struct {
14091401 },
14101402 Os.macosx, Os.ios => Handle{
14111403 .fd = try posixOpen(
1404 allocator,
14121405 dir_path,
14131406 posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,
14141407 0,
......@@ -1420,6 +1413,7 @@ pub const Dir = struct {
14201413 },
14211414 Os.linux => Handle{
14221415 .fd = try posixOpen(
1416 allocator,
14231417 dir_path,
14241418 posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC,
14251419 0,
......@@ -1616,39 +1610,35 @@ pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void {
16161610}
16171611
16181612/// Read value of a symbolic link.
1619pub fn readLink(allocator: *Allocator, file_path: []const u8) ![]u8 {
1620 var path_with_null: [PATH_MAX]u8 = undefined;
1621 if (file_path.len > PATH_MAX - 1)
1622 return error.NameTooLong;
1623 mem.copy(u8, path_with_null[0..PATH_MAX - 1], file_path);
1624 path_with_null[file_path.len] = '\x00';
1625
1626 var result_buf = try allocator.alloc(u8, 1024);
1627 errdefer allocator.free(result_buf);
1628 while (true) {
1629 const ret_val = posix.readlink(&path_with_null, result_buf.ptr, result_buf.len);
1630 const err = posix.getErrno(ret_val);
1631 if (err > 0) {
1632 return switch (err) {
1633 posix.EACCES => error.AccessDenied,
1634 posix.EFAULT, posix.EINVAL => unreachable,
1635 posix.EIO => error.FileSystem,
1636 posix.ELOOP => error.SymLinkLoop,
1637 posix.ENAMETOOLONG => error.NameTooLong,
1638 posix.ENOENT => error.FileNotFound,
1639 posix.ENOMEM => error.SystemResources,
1640 posix.ENOTDIR => error.NotDir,
1641 else => unexpectedErrorPosix(err),
1642 };
1643 }
1644 if (ret_val == result_buf.len) {
1645 result_buf = try allocator.realloc(u8, result_buf, result_buf.len * 2);
1646 continue;
1647 }
1648 return allocator.shrink(u8, result_buf, ret_val);
1613/// The return value is a slice of out_buffer.
1614pub fn readLinkC(pathname: [*]const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 {
1615 const rc = posix.readlink(pathname, out_buffer, out_buffer.len);
1616 const err = posix.getErrno(rc);
1617 switch (err) {
1618 0 => return out_buffer[0..rc],
1619 posix.EACCES => error.AccessDenied,
1620 posix.EFAULT => unreachable,
1621 posix.EINVAL => unreachable,
1622 posix.EIO => return error.FileSystem,
1623 posix.ELOOP => return error.SymLinkLoop,
1624 posix.ENAMETOOLONG => unreachable, // out_buffer is at least PATH_MAX
1625 posix.ENOENT => return error.FileNotFound,
1626 posix.ENOMEM => return error.SystemResources,
1627 posix.ENOTDIR => return error.NotDir,
1628 else => return unexpectedErrorPosix(err),
16491629 }
16501630}
16511631
1632/// Read value of a symbolic link.
1633/// The return value is a slice of out_buffer.
1634pub fn readLink(file_path: []const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 {
1635 var path_with_null: [posix.PATH_MAX]u8 = undefined;
1636 if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;
1637 mem.copy(u8, path_with_null[0..], file_path);
1638 path_with_null[file_path.len] = 0;
1639 return readLinkC(&path_with_null, out_buffer);
1640}
1641
16521642pub fn posix_setuid(uid: u32) !void {
16531643 const err = posix.getErrno(posix.setuid(uid));
16541644 if (err == 0) return;
......@@ -2035,13 +2025,13 @@ pub fn openSelfExe() !os.File {
20352025 const proc_file_path = "/proc/self/exe";
20362026 var fixed_buffer_mem: [proc_file_path.len + 1]u8 = undefined;
20372027 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
2038 return os.File.openRead(proc_file_path);
2028 return os.File.openRead(&fixed_allocator.allocator, proc_file_path);
20392029 },
20402030 Os.macosx, Os.ios => {
20412031 var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;
20422032 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
20432033 const self_exe_path = try selfExePath(&fixed_allocator.allocator);
2044 return os.File.openRead(self_exe_path);
2034 return os.File.openRead(&fixed_allocator.allocator, self_exe_path);
20452035 },
20462036 else => @compileError("Unsupported OS"),
20472037 }
std/os/path.zig+3-2
......@@ -573,7 +573,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
573573 result_index += 1;
574574 }
575575
576 return result[0..result_index];
576 return allocator.shrink(u8, result, result_index);
577577}
578578
579579test "os.path.resolve" {
......@@ -1077,6 +1077,7 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons
10771077/// Expands all symbolic links and resolves references to `.`, `..`, and
10781078/// extra `/` characters in ::pathname.
10791079/// Caller must deallocate result.
1080/// TODO rename this to realAlloc and provide real with no allocator. See #1392
10801081pub fn real(allocator: *Allocator, pathname: []const u8) ![]u8 {
10811082 switch (builtin.os) {
10821083 Os.windows => {
......@@ -1166,7 +1167,7 @@ pub fn real(allocator: *Allocator, pathname: []const u8) ![]u8 {
11661167 return allocator.shrink(u8, result_buf, cstr.len(result_buf.ptr));
11671168 },
11681169 Os.linux => {
1169 const fd = try os.posixOpen(pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0);
1170 const fd = try os.posixOpen(allocator, pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0);
11701171 defer os.close(fd);
11711172
11721173 var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
std/os/windows/kernel32.zig+3-5
......@@ -1,6 +1,5 @@
11use @import("index.zig");
22
3
43pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL;
54
65pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
......@@ -74,7 +73,8 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
7473
7574pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
7675
77pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
76pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?[*]CHAR) DWORD;
77pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: WORD, lpBuffer: ?[*]WCHAR) DWORD;
7878
7979pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;
8080pub extern "kernel32" stdcallcc fn GetCurrentThreadId() DWORD;
......@@ -107,7 +107,6 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
107107 dwFlags: DWORD,
108108) DWORD;
109109
110
111110pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL;
112111
113112pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
......@@ -194,7 +193,6 @@ pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
194193
195194pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
196195
197
198196pub const FILE_NOTIFY_INFORMATION = extern struct {
199197 NextEntryOffset: DWORD,
200198 Action: DWORD,
......@@ -208,7 +206,7 @@ pub const FILE_ACTION_MODIFIED = 0x00000003;
208206pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
209207pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
210208
211pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn(DWORD, DWORD, *OVERLAPPED) void;
209pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn (DWORD, DWORD, *OVERLAPPED) void;
212210
213211pub const FILE_LIST_DIRECTORY = 1;
214212
std/os/windows/util.zig+5-22
......@@ -7,6 +7,8 @@ const mem = std.mem;
77const BufMap = std.BufMap;
88const cstr = std.cstr;
99
10pub const PATH_MAX_UTF16 = 32767;
11
1012pub const WaitError = error{
1113 WaitAbandoned,
1214 WaitTimeOut,
......@@ -90,36 +92,17 @@ pub const OpenError = error{
9092 AccessDenied,
9193 PipeBusy,
9294 Unexpected,
93 OutOfMemory,
9495};
9596
9697/// `file_path` needs to be copied in memory to add a null terminating byte, hence the allocator.
9798pub fn windowsOpen(
98 allocator: *mem.Allocator,
9999 file_path: []const u8,
100100 desired_access: windows.DWORD,
101101 share_mode: windows.DWORD,
102102 creation_disposition: windows.DWORD,
103103 flags_and_attrs: windows.DWORD,
104104) OpenError!windows.HANDLE {
105 const path_with_null = try cstr.addNullByte(allocator, file_path);
106 defer allocator.free(path_with_null);
107
108 const result = windows.CreateFileA(path_with_null.ptr, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
109
110 if (result == windows.INVALID_HANDLE_VALUE) {
111 const err = windows.GetLastError();
112 return switch (err) {
113 windows.ERROR.SHARING_VIOLATION => OpenError.SharingViolation,
114 windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => OpenError.PathAlreadyExists,
115 windows.ERROR.FILE_NOT_FOUND => OpenError.FileNotFound,
116 windows.ERROR.ACCESS_DENIED => OpenError.AccessDenied,
117 windows.ERROR.PIPE_BUSY => OpenError.PipeBusy,
118 else => os.unexpectedErrorWindows(err),
119 };
120 }
121
122 return result;
105 @compileError("TODO rewrite with CreateFileW and no allocator");
123106}
124107
125108/// Caller must free result.
......@@ -238,7 +221,7 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_
238221 }
239222}
240223
241pub const WindowsWaitResult = enum{
224pub const WindowsWaitResult = enum {
242225 Normal,
243226 Aborted,
244227 Cancelled,
......@@ -254,7 +237,7 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t
254237 if (std.debug.runtime_safety) {
255238 std.debug.panic("unexpected error: {}\n", err);
256239 }
257 }
240 },
258241 }
259242 }
260243 return WindowsWaitResult.Normal;
std/unicode.zig+44-25
......@@ -218,7 +218,6 @@ const Utf8Iterator = struct {
218218 }
219219
220220 const cp_len = utf8ByteSequenceLength(it.bytes[it.i]) catch unreachable;
221
222221 it.i += cp_len;
223222 return it.bytes[it.i - cp_len .. it.i];
224223 }
......@@ -236,6 +235,34 @@ const Utf8Iterator = struct {
236235 }
237236};
238237
238pub const Utf16LeIterator = struct {
239 bytes: []const u8,
240 i: usize,
241
242 pub fn init(s: []const u16) Utf16LeIterator {
243 return Utf16LeIterator{
244 .bytes = @sliceToBytes(s),
245 .i = 0,
246 };
247 }
248
249 pub fn nextCodepoint(it: *Utf16LeIterator) !?u32 {
250 const c0: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]);
251 if (c0 & ~u32(0x03ff) == 0xd800) {
252 // surrogate pair
253 it.i += 2;
254 if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf;
255 const c1: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]);
256 if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
257 return 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
258 } else if (c0 & ~u32(0x03ff) == 0xdc00) {
259 return error.UnexpectedSecondSurrogateHalf;
260 } else {
261 return c0;
262 }
263 }
264};
265
239266test "utf8 encode" {
240267 comptime testUtf8Encode() catch unreachable;
241268 try testUtf8Encode();
......@@ -446,42 +473,34 @@ fn testDecode(bytes: []const u8) !u32 {
446473 return utf8Decode(bytes);
447474}
448475
449// TODO: make this API on top of a non-allocating Utf16LeView
450pub fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
476/// Caller must free returned memory.
477pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
451478 var result = std.ArrayList(u8).init(allocator);
452479 // optimistically guess that it will all be ascii.
453480 try result.ensureCapacity(utf16le.len);
454
455 const utf16le_as_bytes = @sliceToBytes(utf16le);
456 var i: usize = 0;
457481 var out_index: usize = 0;
458 while (i < utf16le_as_bytes.len) : (i += 2) {
459 // decode
460 const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
461 var codepoint: u32 = undefined;
462 if (c0 & ~u32(0x03ff) == 0xd800) {
463 // surrogate pair
464 i += 2;
465 if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf;
466 const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
467 if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
468 codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
469 } else if (c0 & ~u32(0x03ff) == 0xdc00) {
470 return error.UnexpectedSecondSurrogateHalf;
471 } else {
472 codepoint = c0;
473 }
474
475 // encode
482 var it = Utf16LeIterator.init(utf16le);
483 while (try it.nextCodepoint()) |codepoint| {
476484 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;
477485 try result.resize(result.len + utf8_len);
478 _ = utf8Encode(codepoint, result.items[out_index..]) catch unreachable;
486 assert((utf8Encode(codepoint, result.items[out_index..]) catch unreachable) == utf8_len);
479487 out_index += utf8_len;
480488 }
481489
482490 return result.toOwnedSlice();
483491}
484492
493pub fn utf16leToUtf8(utf8: []u8, utf16le: []const u16) !void {
494 var out_index: usize = 0;
495 var it = Utf16LeIterator.init(utf16le);
496 while (try it.nextCodepoint()) |codepoint| {
497 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;
498 try result.resize(result.len + utf8_len);
499 assert((utf8Encode(codepoint, result.items[out_index..]) catch unreachable) == utf8_len);
500 out_index += utf8_len;
501 }
502}
503
485504test "utf16leToUtf8" {
486505 var utf16le: [2]u16 = undefined;
487506 const utf16le_as_bytes = @sliceToBytes(utf16le[0..]);