diff --git a/lib/c/string.zig b/lib/c/string.zig index da301a19eb..ee4f7eb9b2 100644 --- a/lib/c/string.zig +++ b/lib/c/string.zig @@ -1,6 +1,7 @@ const builtin = @import("builtin"); const std = @import("std"); const symbol = @import("../c.zig").symbol; +const c = std.c; comptime { if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) { @@ -24,6 +25,8 @@ comptime { symbol(&strpbrk, "strpbrk"); symbol(&strstr, "strstr"); symbol(&strtok, "strtok"); + symbol(&strdup, "strdup"); + symbol(&strndup, "strndup"); // strlen is in compiler_rt symbol(&strtok_r, "strtok_r"); @@ -164,6 +167,23 @@ fn strtok(noalias maybe_str: ?[*:0]c_char, noalias values: [*:0]const c_char) ca return strtok_r(maybe_str, values, &state.str); } +fn strdup(str: [*:0]const c_char) callconv(.c) ?[*:0]c_char { + const len = std.mem.len(str); + const d_opaque = c.malloc(len + 1) orelse return null; + const d: [*]c_char = @ptrCast(d_opaque); + @memcpy(d[0 .. len + 1], str[0 .. len + 1]); + return @ptrCast(d); +} + +fn strndup(str: [*:0]const c_char, n: usize) callconv(.c) ?[*:0]c_char { + const len = strnlen(str, n); + const d_opaque = c.malloc(len + 1) orelse return null; + const d: [*]c_char = @ptrCast(d_opaque); + @memcpy(d[0..len], str[0..len]); + d[len] = 0; + return @ptrCast(d); +} + // strlen is in compiler_rt fn strtok_r(noalias maybe_str: ?[*:0]c_char, noalias values: [*:0]const c_char, noalias state: *?[*:0]c_char) callconv(.c) ?[*:0]c_char { diff --git a/lib/c/wchar.zig b/lib/c/wchar.zig index b85687e8a7..24aca6b0c8 100644 --- a/lib/c/wchar.zig +++ b/lib/c/wchar.zig @@ -5,6 +5,7 @@ const wint_t = std.c.wint_t; const wchar_t = std.c.wchar_t; const symbol = @import("../c.zig").symbol; +const c = std.c; comptime { if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) { @@ -30,6 +31,7 @@ comptime { symbol(&wcspbrk, "wcspbrk"); symbol(&wcstok, "wcstok"); symbol(&wcsstr, "wcsstr"); + symbol(&wcsdup, "wcsdup"); symbol(&wcswcs, "wcswcs"); } @@ -189,3 +191,11 @@ fn wcsstr(noalias haystack: [*:0]const wchar_t, noalias needle: [*:0]const wchar fn wcswcs(noalias haystack: [*:0]const wchar_t, noalias needle: [*:0]const wchar_t) callconv(.c) ?[*:0]wchar_t { return wcsstr(haystack, needle); } + +fn wcsdup(str: [*:0]const wchar_t) callconv(.c) ?[*:0]wchar_t { + const len = wcslen(str); + const size = (len + 1) * @sizeOf(wchar_t); + const d_opaque = c.malloc(size) orelse return null; + const d: [*]wchar_t = @ptrCast(@alignCast(d_opaque)); + return @ptrCast(wmemcpy(d, str, len + 1)); +} diff --git a/lib/libc/musl/src/string/strdup.c b/lib/libc/musl/src/string/strdup.c deleted file mode 100644 index d4c274494f..0000000000 --- a/lib/libc/musl/src/string/strdup.c +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include - -char *strdup(const char *s) -{ - size_t l = strlen(s); - char *d = malloc(l+1); - if (!d) return NULL; - return memcpy(d, s, l+1); -} diff --git a/lib/libc/musl/src/string/strndup.c b/lib/libc/musl/src/string/strndup.c deleted file mode 100644 index 617d27ba9e..0000000000 --- a/lib/libc/musl/src/string/strndup.c +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include - -char *strndup(const char *s, size_t n) -{ - size_t l = strnlen(s, n); - char *d = malloc(l+1); - if (!d) return NULL; - memcpy(d, s, l); - d[l] = 0; - return d; -} diff --git a/lib/libc/musl/src/string/wcsdup.c b/lib/libc/musl/src/string/wcsdup.c deleted file mode 100644 index f398e8091c..0000000000 --- a/lib/libc/musl/src/string/wcsdup.c +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include - -wchar_t *wcsdup(const wchar_t *s) -{ - size_t l = wcslen(s); - wchar_t *d = malloc((l+1)*sizeof(wchar_t)); - if (!d) return NULL; - return wmemcpy(d, s, l+1); -} diff --git a/lib/std/c.zig b/lib/std/c.zig index b9d705286e..349b38f612 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -11136,6 +11136,9 @@ pub extern "c" fn swab(noalias from: *const anyopaque, noalias to: *anyopaque, n pub extern "c" fn strncmp(a: [*:0]const c_char, b: [*:0]const c_char, max: usize) c_int; pub extern "c" fn strcasecmp(a: [*:0]const c_char, b: [*:0]const c_char) c_int; pub extern "c" fn strncasecmp(a: [*:0]const c_char, b: [*:0]const c_char, max: usize) c_int; +pub extern "c" fn strdup(s: [*:0]const c_char) ?[*:0]c_char; +pub extern "c" fn strndup(s: [*:0]const c_char, n: usize) ?[*:0]c_char; +pub extern "c" fn wcsdup(s: [*:0]const wchar_t) ?[*:0]wchar_t; pub extern "c" fn ffs(i: c_int) c_int; pub extern "c" fn ffsl(i: c_long) c_long; diff --git a/src/libs/musl.zig b/src/libs/musl.zig index 22d9fbe51f..5f04f32b96 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -1494,14 +1494,11 @@ const src_files = [_][]const u8{ "musl/src/stdlib/strtod.c", "musl/src/stdlib/wcstod.c", "musl/src/stdlib/wcstol.c", - "musl/src/string/strdup.c", "musl/src/string/strerror_r.c", - "musl/src/string/strndup.c", "musl/src/string/strsignal.c", "musl/src/string/strverscmp.c", "musl/src/string/wcscasecmp.c", "musl/src/string/wcscasecmp_l.c", - "musl/src/string/wcsdup.c", "musl/src/string/wcsncasecmp.c", "musl/src/string/wcsncasecmp_l.c", "musl/src/temp/mkdtemp.c", diff --git a/src/libs/wasi_libc.zig b/src/libs/wasi_libc.zig index d94f7f6284..07f316b635 100644 --- a/src/libs/wasi_libc.zig +++ b/src/libs/wasi_libc.zig @@ -902,13 +902,10 @@ const libc_top_half_src_files = [_][]const u8{ "musl/src/stdlib/ecvt.c", "musl/src/stdlib/fcvt.c", "musl/src/stdlib/gcvt.c", - "musl/src/string/strdup.c", "musl/src/string/strerror_r.c", - "musl/src/string/strndup.c", "musl/src/string/strverscmp.c", "musl/src/string/wcscasecmp.c", "musl/src/string/wcscasecmp_l.c", - "musl/src/string/wcsdup.c", "musl/src/string/wcsncasecmp.c", "musl/src/string/wcsncasecmp_l.c", "musl/src/thread/default_attr.c", diff --git a/test/c.zig b/test/c.zig index d07856d037..bf2c8a5fb2 100644 --- a/test/c.zig +++ b/test/c.zig @@ -10,4 +10,5 @@ test { _ = @import("c/string.zig"); _ = @import("c/strings.zig"); _ = @import("c/unistd.zig"); + _ = @import("c/wchar.zig"); } diff --git a/test/c/string.zig b/test/c/string.zig index 255c147eae..d9f048bbb6 100644 --- a/test/c/string.zig +++ b/test/c/string.zig @@ -10,3 +10,43 @@ test "strncmp" { try testing.expect(c.strncmp(@ptrCast("b"), @ptrCast("a"), 1) > 0); try testing.expect(c.strncmp(@ptrCast("\xff"), @ptrCast("\x02"), 1) > 0); } + +test "strdup" { + const org: [*:0]const u8 = "a"; + const cpy_opt = c.strdup(@ptrCast(org)); + const cpy = cpy_opt orelse return error.OutOfMemory; + defer c.free(cpy); + + const cpy_u8: [*:0]u8 = @ptrCast(cpy); + try testing.expectEqualStrings(std.mem.span(org), std.mem.span(@as([*:0]const u8, cpy_u8))); + try testing.expect(@intFromPtr(cpy_u8) != @intFromPtr(org)); + + cpy_u8[0] = 'b'; + try testing.expectEqualStrings("a", std.mem.span(org)); + try testing.expectEqualStrings("b", std.mem.span(@as([*:0]const u8, cpy_u8))); +} + +test "strndup" { + if (builtin.target.os.tag == .windows) return; // no strndup + const org1: [*:0]const u8 = "Hello"; + + const copy1_opt = c.strndup(@ptrCast(org1), 100); + const copy1 = copy1_opt orelse return error.OutOfMemory; + defer c.free(copy1); + const copy1_u8: [*:0]u8 = @ptrCast(copy1); + try testing.expectEqualStrings("Hello", std.mem.span(@as([*:0]const u8, copy1_u8))); + + const org2: [*:0]const u8 = "Hello World!"; + const copy2_opt = c.strndup(@ptrCast(org2), 5); + const copy2 = copy2_opt orelse return error.OutOfMemory; + defer c.free(copy2); + const copy2_u8: [*:0]u8 = @ptrCast(copy2); + try testing.expectEqualStrings("Hello", std.mem.span(@as([*:0]const u8, copy2_u8))); + try testing.expectEqual(@as(usize, 5), std.mem.len(copy2_u8)); + + const copy3_opt = c.strndup(@ptrCast(org1), 5); + const copy3 = copy3_opt orelse return error.OutOfMemory; + defer c.free(copy3); + const copy3_u8: [*:0]u8 = @ptrCast(copy3); + try testing.expectEqualStrings("Hello", std.mem.span(@as([*:0]const u8, copy3_u8))); +} diff --git a/test/c/wchar.zig b/test/c/wchar.zig new file mode 100644 index 0000000000..1c84bd07be --- /dev/null +++ b/test/c/wchar.zig @@ -0,0 +1,34 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +const c = std.c; +const testing = std.testing; + +test "wcsdup" { + const org: [*:0]const c.wchar_t = &[_:0]c.wchar_t{ 'H', 'e', 'l', 'l', 'o' }; + const cpy_opt = c.wcsdup(org); + const cpy = cpy_opt orelse return error.OutOfMemory; + defer c.free(cpy); + + try testing.expectEqual(@as(usize, 5), std.mem.len(@as([*:0]const c.wchar_t, cpy))); + + try testing.expectEqual(@as(c.wchar_t, 'H'), cpy[0]); + try testing.expectEqual(@as(c.wchar_t, 'e'), cpy[1]); + try testing.expectEqual(@as(c.wchar_t, 'l'), cpy[2]); + try testing.expectEqual(@as(c.wchar_t, 'l'), cpy[3]); + try testing.expectEqual(@as(c.wchar_t, 'o'), cpy[4]); + try testing.expectEqual(@as(c.wchar_t, 0), cpy[5]); + + try testing.expect(@intFromPtr(cpy) != @intFromPtr(org)); + + cpy[0] = 'B'; + try testing.expectEqual(@as(c.wchar_t, 'H'), org[0]); + try testing.expectEqual(@as(c.wchar_t, 'B'), cpy[0]); +} + +test "wcsdup empty string" { + const org: [*:0]const c.wchar_t = &[_:0]c.wchar_t{}; + const cpy = c.wcsdup(org) orelse return error.OutOfMemory; + defer c.free(cpy); + try testing.expectEqual(@as(c.wchar_t, 0), cpy[0]); +}