1const builtin = @import("builtin");
2const std = @import("std");
3const symbol = @import("../c.zig").symbol;
4const c = std.c;
5
6comptime {
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
70fn 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
75fn strcpy(noalias dst: [*]c_char, noalias src: [*:0]const c_char) callconv(.c) [*]c_char {
76 _ = stpcpy(dst, src);
77 return dst;
78}
79
80fn 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
85fn 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
89fn 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
98fn 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
102fn 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
110fn strcoll(a: [*:0]const c_char, b: [*:0]const c_char) callconv(.c) c_int {
111 return strcmp(a, b);
112}
113
114fn __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
120fn 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
126fn __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
131fn 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
139fn 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
145fn 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
150fn 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
155fn 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
159fn 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
163fn 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
171fn 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
179fn 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
190fn 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
212fn 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
218fn 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
226fn 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
230fn 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
237fn 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
254fn 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
267fn 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
277fn 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
286fn 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
291fn 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
299fn 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
303fn 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
308fn 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}