diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2017-04-21 01:56:12 -0400 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2017-04-21 01:56:12 -0400 |
| commit | fb492d19ebb56466f04a2a88c7d3c0a9833f2e0d (patch) | |
| tree | 2d87121db6510c014c34d9a8f7071eb1805ba52a /std/os | |
| parent | 599215cee46b83d052f57a3e6566098900332e38 (diff) | |
| download | zig-fb492d19ebb56466f04a2a88c7d3c0a9833f2e0d.tar.gz zig-fb492d19ebb56466f04a2a88c7d3c0a9833f2e0d.zip | |
zig build system supports building a library
See #329
Supporting work:
* move std.cstr.Buffer0 to std.buffer.Buffer
* add build.zig to example/shared_library/ and add an automated test
for it
* add std.list.List.resizeDown
* improve std.os.makePath
- no longer recursive
- takes into account . and ..
* add std.os.path.isAbsolute
* add std.os.path.resolve
* reimplement std.os.path.dirname
- no longer requires an allocator
- handles edge cases correctly
Diffstat (limited to 'std/os')
| -rw-r--r-- | std/os/index.zig | 65 | ||||
| -rw-r--r-- | std/os/path.zig | 140 |
2 files changed, 166 insertions, 39 deletions
diff --git a/std/os/index.zig b/std/os/index.zig index 0113706796..52e26d9e2b 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -226,11 +226,11 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void { pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &const BufMap, allocator: &Allocator) -> %void { - const argv_buf = %return allocator.alloc(?&const u8, argv.len + 2); - mem.set(?&const u8, argv_buf, null); + const argv_buf = %return allocator.alloc(?&u8, argv.len + 2); + mem.set(?&u8, argv_buf, null); defer { for (argv_buf) |arg| { - const arg_buf = if (const ptr ?= arg) ptr[0...cstr.len(ptr)] else break; + const arg_buf = if (const ptr ?= arg) cstr.toSlice(ptr) else break; allocator.free(arg_buf); } allocator.free(argv_buf); @@ -253,11 +253,11 @@ pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &con argv_buf[argv.len + 1] = null; const envp_count = env_map.count(); - const envp_buf = %return allocator.alloc(?&const u8, envp_count + 1); - mem.set(?&const u8, envp_buf, null); + const envp_buf = %return allocator.alloc(?&u8, envp_count + 1); + mem.set(?&u8, envp_buf, null); defer { for (envp_buf) |env| { - const env_buf = if (const ptr ?= env) ptr[0...cstr.len(ptr)] else break; + const env_buf = if (const ptr ?= env) cstr.toSlice(ptr) else break; allocator.free(env_buf); } allocator.free(envp_buf); @@ -380,7 +380,7 @@ pub const args = struct { } pub fn at(i: usize) -> []const u8 { const s = raw[i]; - return s[0...cstr.len(s)]; + return cstr.toSlice(s); } }; @@ -397,7 +397,7 @@ pub fn getCwd(allocator: &Allocator) -> %[]u8 { return error.Unexpected; } - return buf; + return cstr.toSlice(buf.ptr); } } @@ -572,22 +572,39 @@ pub fn makeDir(allocator: &Allocator, dir_path: []const u8) -> %void { /// Calls makeDir recursively to make an entire path. Returns success if the path /// already exists and is a directory. pub fn makePath(allocator: &Allocator, full_path: []const u8) -> %void { - const child_dir = %return path.dirname(allocator, full_path); - defer allocator.free(child_dir); + const resolved_path = %return path.resolve(allocator, full_path); + defer allocator.free(resolved_path); - if (mem.eql(u8, child_dir, full_path)) - return; - - makePath(allocator, child_dir) %% |err| { - if (err != error.PathAlreadyExists) - return err; - }; - - makeDir(allocator, full_path) %% |err| { - if (err != error.PathAlreadyExists) - return err; - // TODO stat the file and return an error if it's not a directory - }; + var end_index: usize = resolved_path.len; + while (true) { + makeDir(allocator, resolved_path[0...end_index]) %% |err| { + if (err == error.PathAlreadyExists) { + // TODO stat the file and return an error if it's not a directory + // this is important because otherwise a dangling symlink + // could cause an infinite loop + if (end_index == resolved_path.len) + return; + } else if (err == error.FileNotFound) { + // march end_index backward until next path component + while (true) { + end_index -= 1; + if (resolved_path[end_index] == '/') + break; + } + continue; + } else { + return err; + } + }; + if (end_index == resolved_path.len) + return; + // march end_index forward until next path component + while (true) { + end_index += 1; + if (end_index == resolved_path.len or resolved_path[end_index] == '/') + break; + } + } } /// Returns ::error.DirNotEmpty if the directory is not empty. @@ -739,7 +756,7 @@ pub const Dir = struct { const next_index = self.index + linux_entry.d_reclen; self.index = next_index; - const name = (&linux_entry.d_name)[0...cstr.len(&linux_entry.d_name)]; + const name = cstr.toSlice(&linux_entry.d_name); // skip . and .. entries if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { diff --git a/std/os/path.zig b/std/os/path.zig index 66c07ad6de..1f82153a89 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -2,7 +2,11 @@ const debug = @import("../debug.zig"); const assert = debug.assert; const mem = @import("../mem.zig"); const Allocator = mem.Allocator; +const os = @import("index.zig"); +pub const sep = '/'; + +/// Naively combines a series of paths with the native path seperator. /// Allocates memory for the result, which must be freed by the caller. pub fn join(allocator: &Allocator, paths: ...) -> %[]u8 { assert(paths.len >= 2); @@ -26,8 +30,8 @@ pub fn join(allocator: &Allocator, paths: ...) -> %[]u8 { mem.copy(u8, buf[buf_index...], arg); buf_index += arg.len; if (path_i >= paths.len) break; - if (arg[arg.len - 1] != '/') { - buf[buf_index] = '/'; + if (arg[arg.len - 1] != sep) { + buf[buf_index] = sep; buf_index += 1; } } @@ -43,22 +47,128 @@ test "os.path.join" { assert(mem.eql(u8, %%join(&debug.global_allocator, "/a/", "b/", "c"), "/a/b/c")); } -pub fn dirname(allocator: &Allocator, path: []const u8) -> %[]u8 { - if (path.len != 0) { - var last_index: usize = path.len - 1; - if (path[last_index] == '/') - last_index -= 1; +pub fn isAbsolute(path: []const u8) -> bool { + switch (@compileVar("os")) { + Os.windows => @compileError("Unsupported OS"), + else => return path[0] == sep, + } +} + +/// This function is like a series of `cd` statements executed one after another. +/// The result does not have a trailing path separator. +pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 { + var paths: [args.len][]const u8 = undefined; + comptime var arg_i = 0; + inline while (arg_i < args.len; arg_i += 1) { + paths[arg_i] = args[arg_i]; + } + return resolveSlice(allocator, paths); +} + +pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { + if (paths.len == 0) + return os.getCwd(allocator); - var i: usize = last_index; + var first_index: usize = 0; + var have_abs = false; + var max_size: usize = 0; + for (paths) |p, i| { + if (isAbsolute(p)) { + first_index = i; + have_abs = true; + max_size = 0; + } + max_size += p.len + 1; + } + + var result: []u8 = undefined; + var result_index: usize = 0; + + if (have_abs) { + result = %return allocator.alloc(u8, max_size); + } else { + const cwd = %return os.getCwd(allocator); + defer allocator.free(cwd); + result = %return allocator.alloc(u8, max_size + cwd.len + 1); + mem.copy(u8, result, cwd); + result_index += cwd.len; + } + %defer allocator.free(result); + + for (paths[first_index...]) |p, i| { + var it = mem.split(p, '/'); while (true) { - const c = path[i]; - if (c == '/') - return mem.dupe(allocator, u8, path[0...i]); - if (i == 0) - break; - i -= 1; + const component = it.next() ?? break; + if (mem.eql(u8, component, ".")) { + continue; + } else if (mem.eql(u8, component, "..")) { + while (true) { + if (result_index == 0) + break; + result_index -= 1; + if (result[result_index] == '/') + break; + } + } else { + result[result_index] = '/'; + result_index += 1; + mem.copy(u8, result[result_index...], component); + result_index += component.len; + } } } - return mem.dupe(allocator, u8, "."); + if (result_index == 0) { + result[0] = '/'; + result_index += 1; + } + + return result[0...result_index]; +} + +test "os.path.resolve" { + assert(mem.eql(u8, testResolve("/a/b", "c"), "/a/b/c")); + assert(mem.eql(u8, testResolve("/a/b", "c", "//d", "e///"), "/d/e")); + assert(mem.eql(u8, testResolve("/a/b/c", "..", "../"), "/a")); + assert(mem.eql(u8, testResolve("/", "..", ".."), "/")); +} +fn testResolve(args: ...) -> []u8 { + return %%resolve(&debug.global_allocator, args); +} + +pub fn dirname(path: []const u8) -> []const u8 { + if (path.len == 0) + return path[0...0]; + var end_index: usize = path.len - 1; + while (path[end_index] == '/') { + if (end_index == 0) + return path[0...1]; + end_index -= 1; + } + + while (path[end_index] != '/') { + if (end_index == 0) + return path[0...0]; + end_index -= 1; + } + + if (end_index == 0 and path[end_index] == '/') + return path[0...1]; + + return path[0...end_index]; +} + +test "os.path.dirname" { + testDirname("/a/b/c", "/a/b"); + testDirname("/a/b/c///", "/a/b"); + testDirname("/a", "/"); + testDirname("/", "/"); + testDirname("////", "/"); + testDirname("", ""); + testDirname("a", ""); + testDirname("a/", ""); + testDirname("a//", ""); +} +fn testDirname(input: []const u8, expected_output: []const u8) { + assert(mem.eql(u8, dirname(input), expected_output)); } |
