aboutsummaryrefslogtreecommitdiff
path: root/std/os
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2017-04-21 01:56:12 -0400
committerAndrew Kelley <superjoe30@gmail.com>2017-04-21 01:56:12 -0400
commitfb492d19ebb56466f04a2a88c7d3c0a9833f2e0d (patch)
tree2d87121db6510c014c34d9a8f7071eb1805ba52a /std/os
parent599215cee46b83d052f57a3e6566098900332e38 (diff)
downloadzig-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.zig65
-rw-r--r--std/os/path.zig140
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));
}