| 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); |
| 3 | const symbol = @import("../c.zig").symbol; |
| 4 | const c = std.c; |
| 5 | |
| 6 | comptime { |
| 7 | symbol(&strndup, "strndup"); |
| 8 | |
| 9 | if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) { |
| 10 | // memcpy implemented in compiler_rt |
| 11 | // memmove implemented in compiler_rt |
| 12 | // memset implemented in compiler_rt |
| 13 | // memcmp implemented in compiler_rt |
| 14 | symbol(&memchr, "memchr"); |
| 15 | symbol(&strcpy, "strcpy"); |
| 16 | symbol(&strncpy, "strncpy"); |
| 17 | symbol(&strcat, "strcat"); |
| 18 | symbol(&strncat, "strncat"); |
| 19 | symbol(&strcmp, "strcmp"); |
| 20 | symbol(&strncmp, "strncmp"); |
| 21 | symbol(&strcoll, "strcoll"); |
| 22 | symbol(&strxfrm, "strxfrm"); |
| 23 | symbol(&strchr, "strchr"); |
| 24 | symbol(&strrchr, "strrchr"); |
| 25 | symbol(&strcspn, "strcspn"); |
| 26 | symbol(&strspn, "strspn"); |
| 27 | symbol(&strpbrk, "strpbrk"); |
| 28 | symbol(&strstr, "strstr"); |
| 29 | symbol(&strtok, "strtok"); |
| 30 | symbol(&strdup, "strdup"); |
| 31 | // strlen is in compiler_rt |
| 32 | |
| 33 | symbol(&strtok_r, "strtok_r"); |
| 34 | symbol(&stpcpy, "stpcpy"); |
| 35 | symbol(&stpncpy, "stpncpy"); |
| 36 | symbol(&strnlen, "strnlen"); |
| 37 | symbol(&memmem, "memmem"); |
| 38 | |
| 39 | symbol(&memccpy, "memccpy"); |
| 40 | |
| 41 | symbol(&strsep, "strsep"); |
| 42 | symbol(&strlcat, "strlcat"); |
| 43 | symbol(&strlcpy, "strlcpy"); |
| 44 | symbol(&explicit_bzero, "explicit_bzero"); |
| 45 | |
| 46 | symbol(&strchrnul, "strchrnul"); |
| 47 | symbol(&strcasestr, "strcasestr"); |
| 48 | symbol(&memrchr, "memrchr"); |
| 49 | symbol(&mempcpy, "mempcpy"); |
| 50 | |
| 51 | symbol(&__strcoll_l, "__strcoll_l"); |
| 52 | symbol(&__strxfrm_l, "__strxfrm_l"); |
| 53 | symbol(&__strcoll_l, "strcoll_l"); |
| 54 | symbol(&__strxfrm_l, "strxfrm_l"); |
| 55 | |
| 56 | // These symbols are not in the public ABI of musl/wasi. However they depend on these exports internally. |
| 57 | symbol(&stpcpy, "__stpcpy"); |
| 58 | symbol(&stpncpy, "__stpncpy"); |
| 59 | symbol(&strchrnul, "__strchrnul"); |
| 60 | symbol(&memrchr, "__memrchr"); |
| 61 | } |
| 62 | |
| 63 | if (builtin.target.isMinGW()) { |
| 64 | symbol(&strnlen, "strnlen"); |
| 65 | symbol(&mempcpy, "mempcpy"); |
| 66 | symbol(&strtok_r, "strtok_r"); |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | fn memchr(ptr: *const anyopaque, value: c_int, len: usize) callconv(.c) ?*anyopaque { |
| 71 | const bytes: [*]const u8 = @ptrCast(ptr); |
| 72 | return @constCast(bytes[std.mem.findScalar(u8, bytes[0..len], @truncate(@as(c_uint, @bitCast(value)))) orelse return null ..]); |
| 73 | } |
| 74 | |
| 75 | fn strcpy(noalias dst: [*]c_char, noalias src: [*:0]const c_char) callconv(.c) [*]c_char { |
| 76 | _ = stpcpy(dst, src); |
| 77 | return dst; |
| 78 | } |
| 79 | |
| 80 | fn strncpy(noalias dst: [*]c_char, noalias src: [*:0]const c_char, max: usize) callconv(.c) [*]c_char { |
| 81 | _ = stpncpy(dst, src, max); |
| 82 | return dst; |
| 83 | } |
| 84 | |
| 85 | fn strcat(noalias dst: [*:0]c_char, noalias src: [*:0]const c_char) callconv(.c) [*:0]c_char { |
| 86 | return strncat(dst, src, std.math.maxInt(usize)); |
| 87 | } |
| 88 | |
| 89 | fn strncat(noalias dst: [*:0]c_char, noalias src: [*:0]const c_char, max: usize) callconv(.c) [*:0]c_char { |
| 90 | const dst_len = std.mem.len(@as([*:0]u8, @ptrCast(dst))); |
| 91 | const src_len = strnlen(src, max); |
| 92 | |
| 93 | @memcpy(dst[dst_len..][0..src_len], src[0..src_len]); |
| 94 | dst[dst_len + src_len] = 0; |
| 95 | return dst[0..(dst_len + src_len) :0].ptr; |
| 96 | } |
| 97 | |
| 98 | fn strcmp(a: [*:0]const c_char, b: [*:0]const c_char) callconv(.c) c_int { |
| 99 | return strncmp(a, b, std.math.maxInt(usize)); |
| 100 | } |
| 101 | |
| 102 | fn strncmp(a: [*:0]const c_char, b: [*:0]const c_char, max: usize) callconv(.c) c_int { |
| 103 | return switch (std.mem.boundedOrderZ(u8, @ptrCast(a), @ptrCast(b), max)) { |
| 104 | .eq => 0, |
| 105 | .gt => 1, |
| 106 | .lt => -1, |
| 107 | }; |
| 108 | } |
| 109 | |
| 110 | fn strcoll(a: [*:0]const c_char, b: [*:0]const c_char) callconv(.c) c_int { |
| 111 | return strcmp(a, b); |
| 112 | } |
| 113 | |
| 114 | fn __strcoll_l(a: [*:0]const c_char, b: [*:0]const c_char, locale: *anyopaque) callconv(.c) c_int { |
| 115 | _ = locale; |
| 116 | return strcoll(a, b); |
| 117 | } |
| 118 | |
| 119 | // NOTE: If 'max' is 0, 'dst' is allowed to be a null pointer |
| 120 | fn strxfrm(noalias dst: ?[*]c_char, noalias src: [*:0]const c_char, max: usize) callconv(.c) usize { |
| 121 | const src_len = std.mem.len(@as([*:0]const u8, @ptrCast(src))); |
| 122 | if (src_len < max) @memcpy(dst.?[0 .. src_len + 1], src[0 .. src_len + 1]); |
| 123 | return src_len; |
| 124 | } |
| 125 | |
| 126 | fn __strxfrm_l(noalias dst: ?[*]c_char, noalias src: [*:0]const c_char, max: usize, locale: *anyopaque) callconv(.c) usize { |
| 127 | _ = locale; |
| 128 | return strxfrm(dst, src, max); |
| 129 | } |
| 130 | |
| 131 | fn strchr(str: [*:0]const c_char, value: c_int) callconv(.c) ?[*:0]c_char { |
| 132 | const str_u8: [*:0]const u8 = @ptrCast(str); |
| 133 | const len = std.mem.len(str_u8); |
| 134 | |
| 135 | if (value == 0) return @constCast(str + len); |
| 136 | return @constCast(str[std.mem.findScalar(u8, str_u8[0..len], @truncate(@as(c_uint, @bitCast(value)))) orelse return null ..]); |
| 137 | } |
| 138 | |
| 139 | fn strrchr(str: [*:0]const c_char, value: c_int) callconv(.c) ?[*:0]c_char { |
| 140 | const str_u8: [*:0]const u8 = @ptrCast(str); |
| 141 | // std.mem.len(str) + 1 to not special case '\0' |
| 142 | return @constCast(str[std.mem.findScalarLast(u8, str_u8[0 .. std.mem.len(str_u8) + 1], @truncate(@as(c_uint, @bitCast(value)))) orelse return null ..]); |
| 143 | } |
| 144 | |
| 145 | fn strcspn(dst: [*:0]const c_char, values: [*:0]const c_char) callconv(.c) usize { |
| 146 | const dst_slice = std.mem.span(@as([*:0]const u8, @ptrCast(dst))); |
| 147 | return std.mem.findAny(u8, dst_slice, std.mem.span(@as([*:0]const u8, @ptrCast(values)))) orelse dst_slice.len; |
| 148 | } |
| 149 | |
| 150 | fn strspn(dst: [*:0]const c_char, values: [*:0]const c_char) callconv(.c) usize { |
| 151 | const dst_slice = std.mem.span(@as([*:0]const u8, @ptrCast(dst))); |
| 152 | return std.mem.findNone(u8, dst_slice, std.mem.span(@as([*:0]const u8, @ptrCast(values)))) orelse dst_slice.len; |
| 153 | } |
| 154 | |
| 155 | fn strpbrk(haystack: [*:0]const c_char, needle: [*:0]const c_char) callconv(.c) ?[*:0]c_char { |
| 156 | return @constCast(haystack[std.mem.findAny(u8, std.mem.span(@as([*:0]const u8, @ptrCast(haystack))), std.mem.span(@as([*:0]const u8, @ptrCast(needle)))) orelse return null ..]); |
| 157 | } |
| 158 | |
| 159 | fn strstr(haystack: [*:0]const c_char, needle: [*:0]const c_char) callconv(.c) ?[*:0]c_char { |
| 160 | return @constCast(haystack[std.mem.find(u8, std.mem.span(@as([*:0]const u8, @ptrCast(haystack))), std.mem.span(@as([*:0]const u8, @ptrCast(needle)))) orelse return null ..]); |
| 161 | } |
| 162 | |
| 163 | fn strtok(noalias maybe_str: ?[*:0]c_char, noalias values: [*:0]const c_char) callconv(.c) ?[*:0]c_char { |
| 164 | const state = struct { |
| 165 | var str: ?[*:0]c_char = null; |
| 166 | }; |
| 167 | |
| 168 | return strtok_r(maybe_str, values, &state.str); |
| 169 | } |
| 170 | |
| 171 | fn strdup(str: [*:0]const c_char) callconv(.c) ?[*:0]c_char { |
| 172 | const len = std.mem.len(str); |
| 173 | const d_opaque = c.malloc(len + 1) orelse return null; |
| 174 | const d: [*]c_char = @ptrCast(d_opaque); |
| 175 | @memcpy(d[0 .. len + 1], str[0 .. len + 1]); |
| 176 | return @ptrCast(d); |
| 177 | } |
| 178 | |
| 179 | fn strndup(str: [*:0]const c_char, n: usize) callconv(.c) ?[*:0]c_char { |
| 180 | const len = strnlen(str, n); |
| 181 | const d_opaque = c.malloc(len + 1) orelse return null; |
| 182 | const d: [*]c_char = @ptrCast(d_opaque); |
| 183 | @memcpy(d[0..len], str[0..len]); |
| 184 | d[len] = 0; |
| 185 | return @ptrCast(d); |
| 186 | } |
| 187 | |
| 188 | // strlen is in compiler_rt |
| 189 | |
| 190 | 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 { |
| 191 | const str = if (maybe_str) |str| |
| 192 | str |
| 193 | else if (state.*) |state_str| |
| 194 | state_str |
| 195 | else |
| 196 | return null; |
| 197 | |
| 198 | const str_bytes = std.mem.span(@as([*:0]u8, @ptrCast(str))); |
| 199 | const values_bytes = std.mem.span(@as([*:0]const u8, @ptrCast(values))); |
| 200 | const tok_start = std.mem.findNone(u8, str_bytes, values_bytes) orelse return null; |
| 201 | |
| 202 | if (std.mem.findAnyPos(u8, str_bytes, tok_start, values_bytes)) |tok_end| { |
| 203 | str[tok_end] = 0; |
| 204 | state.* = str[tok_end + 1 ..]; |
| 205 | } else { |
| 206 | state.* = str[str_bytes.len..]; |
| 207 | } |
| 208 | |
| 209 | return str[tok_start..]; |
| 210 | } |
| 211 | |
| 212 | fn stpcpy(noalias dst: [*]c_char, noalias src: [*:0]const c_char) callconv(.c) [*]c_char { |
| 213 | const src_len = std.mem.len(@as([*:0]const u8, @ptrCast(src))); |
| 214 | @memcpy(dst[0 .. src_len + 1], src[0 .. src_len + 1]); |
| 215 | return dst + src_len; |
| 216 | } |
| 217 | |
| 218 | fn stpncpy(noalias dst: [*]c_char, noalias src: [*:0]const c_char, max: usize) callconv(.c) [*]c_char { |
| 219 | const src_len = strnlen(src, max); |
| 220 | const copying_len = @min(max, src_len); |
| 221 | @memcpy(dst[0..copying_len], src[0..copying_len]); |
| 222 | @memset(dst[copying_len..][0 .. max - copying_len], 0x00); |
| 223 | return dst + copying_len; |
| 224 | } |
| 225 | |
| 226 | fn strnlen(str: [*:0]const c_char, max: usize) callconv(.c) usize { |
| 227 | return std.mem.findScalar(u8, @ptrCast(str[0..max]), 0) orelse max; |
| 228 | } |
| 229 | |
| 230 | fn memmem(haystack: *const anyopaque, haystack_len: usize, needle: *const anyopaque, needle_len: usize) callconv(.c) ?*anyopaque { |
| 231 | const haystack_bytes: [*:0]const u8 = @ptrCast(haystack); |
| 232 | const needle_bytes: [*:0]const u8 = @ptrCast(needle); |
| 233 | |
| 234 | return @constCast(haystack_bytes[std.mem.find(u8, haystack_bytes[0..haystack_len], needle_bytes[0..needle_len]) orelse return null ..]); |
| 235 | } |
| 236 | |
| 237 | fn strsep(maybe_str: *?[*:0]c_char, values: [*:0]const c_char) callconv(.c) ?[*]c_char { |
| 238 | if (maybe_str.*) |str| { |
| 239 | const values_bytes = std.mem.span(@as([*:0]const u8, @ptrCast(values))); |
| 240 | const str_bytes = std.mem.span(@as([*:0]u8, @ptrCast(str))); |
| 241 | const found = std.mem.findAny(u8, str_bytes, values_bytes) orelse { |
| 242 | maybe_str.* = null; |
| 243 | return str; |
| 244 | }; |
| 245 | |
| 246 | str[found] = 0; |
| 247 | maybe_str.* = str[found + 1 ..]; |
| 248 | return str; |
| 249 | } |
| 250 | |
| 251 | return null; |
| 252 | } |
| 253 | |
| 254 | fn strlcat(dst: [*:0]c_char, src: [*:0]const c_char, dst_total_len: usize) callconv(.c) usize { |
| 255 | const dst_len = strnlen(dst, dst_total_len); |
| 256 | const src_bytes = std.mem.span(@as([*:0]const u8, @ptrCast(src))); |
| 257 | |
| 258 | if (dst_total_len == dst_len) return dst_len + src_bytes.len; |
| 259 | |
| 260 | const copying_len = @min(dst_total_len - (dst_len + 1), src_bytes.len); |
| 261 | |
| 262 | @memcpy(dst[dst_len..][0..copying_len], src[0..copying_len]); |
| 263 | dst[dst_len + copying_len] = 0; |
| 264 | return dst_len + src_bytes.len; |
| 265 | } |
| 266 | |
| 267 | fn strlcpy(dst: [*]c_char, src: [*:0]const c_char, dst_total_len: usize) callconv(.c) usize { |
| 268 | const src_bytes = std.mem.span(@as([*:0]const u8, @ptrCast(src))); |
| 269 | if (dst_total_len != 0) { |
| 270 | const copying_len = @min(src_bytes.len, dst_total_len - 1); |
| 271 | @memcpy(dst[0..copying_len], src[0..copying_len]); |
| 272 | dst[copying_len] = 0; |
| 273 | } |
| 274 | return src_bytes.len; |
| 275 | } |
| 276 | |
| 277 | fn memccpy(noalias dst: *anyopaque, noalias src: *const anyopaque, value: c_int, len: usize) callconv(.c) *anyopaque { |
| 278 | const dst_bytes: [*]u8 = @ptrCast(dst); |
| 279 | const src_bytes: [*]const u8 = @ptrCast(src); |
| 280 | const value_u8: u8 = @truncate(@as(c_uint, @bitCast(value))); |
| 281 | const copying_len = std.mem.findScalar(u8, src_bytes[0..len], value_u8) orelse len; |
| 282 | @memcpy(dst_bytes[0..copying_len], src_bytes[0..copying_len]); |
| 283 | return dst_bytes + copying_len; |
| 284 | } |
| 285 | |
| 286 | fn explicit_bzero(ptr: *anyopaque, len: usize) callconv(.c) void { |
| 287 | const bytes: [*]u8 = @ptrCast(ptr); |
| 288 | std.crypto.secureZero(u8, bytes[0..len]); |
| 289 | } |
| 290 | |
| 291 | fn strchrnul(str: [*:0]const c_char, value: c_int) callconv(.c) [*:0]c_char { |
| 292 | const str_u8: [*:0]const u8 = @ptrCast(str); |
| 293 | const len = std.mem.len(str_u8); |
| 294 | |
| 295 | if (value == 0) return @constCast(str + len); |
| 296 | return @constCast(str[std.mem.findScalar(u8, str_u8[0..len], @truncate(@as(c_uint, @bitCast(value)))) orelse len ..]); |
| 297 | } |
| 298 | |
| 299 | fn strcasestr(haystack: [*:0]const c_char, needle: [*:0]const c_char) callconv(.c) ?[*:0]c_char { |
| 300 | return @constCast(haystack[std.ascii.findIgnoreCase(std.mem.span(@as([*:0]const u8, @ptrCast(haystack))), std.mem.span(@as([*:0]const u8, @ptrCast(needle)))) orelse return null ..]); |
| 301 | } |
| 302 | |
| 303 | fn memrchr(ptr: *const anyopaque, value: c_int, len: usize) callconv(.c) ?*anyopaque { |
| 304 | const bytes: [*]const u8 = @ptrCast(ptr); |
| 305 | return @constCast(bytes[std.mem.findScalarLast(u8, bytes[0..len], @truncate(@as(c_uint, @bitCast(value)))) orelse return null ..]); |
| 306 | } |
| 307 | |
| 308 | fn mempcpy(noalias dst: *anyopaque, noalias src: *const anyopaque, len: usize) callconv(.c) *anyopaque { |
| 309 | const dst_bytes: [*]u8 = @ptrCast(dst); |
| 310 | const src_bytes: [*]const u8 = @ptrCast(src); |
| 311 | @memcpy(dst_bytes[0..len], src_bytes[0..len]); |
| 312 | return dst_bytes + len; |
| 313 | } |