Skip to main content

relibc/header/string/
mod.rs

1//! `string.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/string.h.html>.
4
5use core::{iter::once, mem, ptr, slice};
6
7use cbitset::BitSet256;
8
9use crate::{
10    header::{
11        errno::{ENOMEM, ERANGE, STR_ERROR, STRERROR_MAX},
12        signal,
13    },
14    iter::{NulTerminated, NulTerminatedInclusive, SrcDstPtrIter},
15    platform::{
16        self,
17        types::{c_char, c_int, c_void, size_t},
18    },
19    raw_cell::RawCell,
20};
21
22use super::{bits_locale_t::locale_t, locale::THREAD_LOCALE};
23
24/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/memccpy.html>.
25///
26/// # Safety
27/// The caller must ensure that:
28/// - `n` is not longer than the memory area pointed to by `s1`, and
29/// - `n` is not longer than the memory area pointed to by `s2`.
30#[unsafe(no_mangle)]
31pub unsafe extern "C" fn memccpy(
32    s1: *mut c_void,
33    s2: *const c_void,
34    c: c_int,
35    n: size_t,
36) -> *mut c_void {
37    let to = unsafe { memchr(s2, c, n) };
38    let dist = if to.is_null() {
39        n
40    } else {
41        ((to as usize) - (s2 as usize)) + 1
42    };
43    unsafe { memcpy(s1, s2, dist) };
44    if to.is_null() {
45        ptr::null_mut()
46    } else {
47        unsafe { s1.cast::<u8>().add(dist).cast::<c_void>() }
48    }
49}
50
51/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/memchr.html>.
52#[unsafe(no_mangle)]
53pub unsafe extern "C" fn memchr(
54    haystack: *const c_void,
55    needle: c_int,
56    len: size_t,
57) -> *mut c_void {
58    let haystack = unsafe { slice::from_raw_parts(haystack.cast::<u8>(), len) };
59
60    match memchr::memchr(needle as u8, haystack) {
61        Some(index) => haystack[index..].as_ptr() as *mut c_void,
62        None => ptr::null_mut(),
63    }
64}
65
66/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/memcmp.html>.
67#[unsafe(no_mangle)]
68pub unsafe extern "C" fn memcmp(s1: *const c_void, s2: *const c_void, n: size_t) -> c_int {
69    let (div, rem) = (n / mem::size_of::<size_t>(), n % mem::size_of::<size_t>());
70    let mut a = s1.cast::<usize>();
71    let mut b = s2.cast::<usize>();
72    for _ in 0..div {
73        // SAFETY: `s1` and `s2` are `*const c_void`, which only guarantees byte
74        // alignment. Hence `a` and `b` may be unaligned.
75        if unsafe { a.read_unaligned() } != unsafe { b.read_unaligned() } {
76            for i in 0..mem::size_of::<usize>() {
77                let c = unsafe { *(a.cast::<u8>()).add(i) };
78                let d = unsafe { *(b.cast::<u8>()).add(i) };
79                if c != d {
80                    return c_int::from(c) - c_int::from(d);
81                }
82            }
83            unreachable!()
84        }
85        a = unsafe { a.offset(1) };
86        b = unsafe { b.offset(1) };
87    }
88
89    let mut a = a.cast::<u8>();
90    let mut b = b.cast::<u8>();
91    for _ in 0..rem {
92        if unsafe { *a } != unsafe { *b } {
93            return c_int::from(unsafe { *a }) - c_int::from(unsafe { *b });
94        }
95        a = unsafe { a.offset(1) };
96        b = unsafe { b.offset(1) };
97    }
98    0
99}
100
101/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/memcpy.html>.
102///
103/// # Safety
104/// The caller must ensure that *either*:
105/// - `n` is 0, *or*
106///     - `s1` is convertible to a `&mut [MaybeUninit<u8>]` with length `n`,
107///       and
108///     - `s2` is convertible to a `&[MaybeUninit<u8>]` with length `n`.
109#[unsafe(no_mangle)]
110pub unsafe extern "C" fn memcpy(s1: *mut c_void, s2: *const c_void, n: size_t) -> *mut c_void {
111    for i in 0..n {
112        unsafe { *s1.cast::<u8>().add(i) = *s2.cast::<u8>().add(i) };
113    }
114    s1
115}
116
117/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/memmem.html>.
118///
119/// # Safety
120/// The caller must ensure that:
121/// - `haystack` is convertible to a `&[u8]` with length `haystacklen`, and
122/// - `needle` is convertible to a `&[u8]` with length `needlelen`.
123#[unsafe(no_mangle)]
124pub unsafe extern "C" fn memmem(
125    haystack: *const c_void,
126    haystacklen: size_t,
127    needle: *const c_void,
128    needlelen: size_t,
129) -> *mut c_void {
130    match needlelen {
131        // Required to satisfy spec (would otherwise cause .windows() to panic)
132        0 => haystack,
133        _ => {
134            // SAFETY: the caller is required to ensure that the provided
135            // pointers are valid.
136            let haystack_slice =
137                unsafe { slice::from_raw_parts(haystack.cast::<u8>(), haystacklen) };
138            let needle_slice = unsafe { slice::from_raw_parts(needle.cast::<u8>(), needlelen) };
139
140            // At this point, .windows() will receive a nonzero `needlelen` and
141            // thus not panic.
142            match haystack_slice
143                .windows(needlelen)
144                .find(|&haystack_window| haystack_window == needle_slice)
145            {
146                Some(match_slice) => match_slice.as_ptr().cast(),
147                None => ptr::null(),
148            }
149        }
150    }
151    .cast_mut()
152}
153
154/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/memmove.html>.
155#[unsafe(no_mangle)]
156pub unsafe extern "C" fn memmove(s1: *mut c_void, s2: *const c_void, n: size_t) -> *mut c_void {
157    if s2 < s1.cast_const() {
158        // copy from end
159        let mut i = n;
160        while i != 0 {
161            i -= 1;
162            unsafe { *s1.cast::<u8>().add(i) = *s2.cast::<u8>().add(i) };
163        }
164    } else {
165        // copy from beginning
166        let mut i = 0;
167        while i < n {
168            unsafe { *s1.cast::<u8>().add(i) = *s2.cast::<u8>().add(i) };
169            i += 1;
170        }
171    }
172    s1
173}
174
175/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/memchr.3.html>.
176#[unsafe(no_mangle)]
177pub unsafe extern "C" fn memrchr(
178    haystack: *const c_void,
179    needle: c_int,
180    len: size_t,
181) -> *mut c_void {
182    let haystack = unsafe { slice::from_raw_parts(haystack.cast::<u8>(), len) };
183
184    match memchr::memrchr(needle as u8, haystack) {
185        Some(index) => haystack[index..].as_ptr() as *mut c_void,
186        None => ptr::null_mut(),
187    }
188}
189
190/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/memset.html>.
191///
192/// Copies `c` (converted to an unsigned char) into each of the first `n` bytes
193/// of the object pointed to by `s`.
194///
195/// Returns `s`.
196///
197/// # Implementation
198/// Casting of `c` may result in truncation.
199#[unsafe(no_mangle)]
200pub unsafe extern "C" fn memset(s: *mut c_void, c: c_int, n: size_t) -> *mut c_void {
201    for i in 0..n {
202        unsafe { *s.cast::<u8>().add(i) = c as u8 };
203    }
204    s
205}
206
207/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strcpy.html>.
208#[unsafe(no_mangle)]
209pub unsafe extern "C" fn stpcpy(mut s1: *mut c_char, mut s2: *const c_char) -> *mut c_char {
210    loop {
211        unsafe { *s1 = *s2 };
212
213        if unsafe { *s1 } == 0 {
214            break;
215        }
216
217        s1 = unsafe { s1.add(1) };
218        s2 = unsafe { s2.add(1) };
219    }
220
221    s1
222}
223
224/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strncpy.html>.
225///
226/// Copies not more than `n` bytes (bytes that follow a NUL character are not
227/// copied) from the array pointed to by `s2` to the array pointed to by `s1`.
228///
229/// If the array pointed to by `s2` is a string that is shorter than `n` bytes,
230/// NUL characters shall be appended to the copy in the array pointed to by
231/// `s1`, until `n` bytes in all are written.
232///
233/// If any NUL characters are written to the destination, returns the address
234/// of the first such NUL character. If no NUL characters are written to the
235/// destination, returns `&s1[n]`.
236///
237/// # Safety
238/// If copying takes place between objects that overlap, the behaviour is
239/// undefined.
240#[unsafe(no_mangle)]
241pub unsafe extern "C" fn stpncpy(
242    mut s1: *mut c_char,
243    mut s2: *const c_char,
244    mut n: size_t,
245) -> *mut c_char {
246    while n > 0 {
247        unsafe { *s1 = *s2 };
248
249        if unsafe { *s1 } == 0 {
250            break;
251        }
252
253        n -= 1;
254        s1 = unsafe { s1.add(1) };
255        s2 = unsafe { s2.add(1) };
256    }
257
258    unsafe { memset(s1.cast(), 0, n) };
259
260    s1
261}
262
263/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/strstr.3.html>.
264#[unsafe(no_mangle)]
265pub unsafe extern "C" fn strcasestr(haystack: *const c_char, needle: *const c_char) -> *mut c_char {
266    unsafe { inner_strstr(haystack, needle, !32) }
267}
268
269/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strcat.html>.
270#[unsafe(no_mangle)]
271pub unsafe extern "C" fn strcat(s1: *mut c_char, s2: *const c_char) -> *mut c_char {
272    unsafe { strncat(s1, s2, usize::MAX) }
273}
274
275/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strchr.html>.
276///
277/// # Safety
278/// The caller is required to ensure that `s` is a valid pointer to a buffer
279/// containing at least one nul value. The pointed-to buffer must not be
280/// modified for the duration of the call.
281#[unsafe(no_mangle)]
282pub unsafe extern "C" fn strchr(s: *const c_char, c: c_int) -> *mut c_char {
283    let c_as_c_char = c as c_char;
284
285    // We iterate over non-mut references and thus need to coerce the
286    // resulting reference via a *const pointer before we can get our *mut.
287    // SAFETY: the caller is required to ensure that s points to a valid
288    // nul-terminated buffer.
289    let ptr: *const c_char =
290        match unsafe { NulTerminatedInclusive::new(s) }.find(|&&sc| sc == c_as_c_char) {
291            Some(sc_ref) => sc_ref,
292            None => ptr::null(),
293        };
294    ptr.cast_mut()
295}
296
297/// Non-POSIX, see <https://man7.org/linux/man-pages/man3/strchr.3.html>.
298#[unsafe(no_mangle)]
299pub unsafe extern "C" fn strchrnul(s: *const c_char, c: c_int) -> *mut c_char {
300    let mut s = s.cast_mut();
301    loop {
302        if unsafe { *s } == c as _ {
303            break;
304        }
305        if unsafe { *s } == 0 {
306            break;
307        }
308        s = unsafe { s.add(1) };
309    }
310    s
311}
312
313/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strcmp.html>.
314#[unsafe(no_mangle)]
315pub unsafe extern "C" fn strcmp(s1: *const c_char, s2: *const c_char) -> c_int {
316    unsafe { strncmp(s1, s2, usize::MAX) }
317}
318
319/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strcoll_l.html>.
320#[unsafe(no_mangle)]
321pub unsafe extern "C" fn strcoll_l(s1: *const c_char, s2: *const c_char, _loc: locale_t) -> c_int {
322    // relibc has no locale stuff (yet)
323    unsafe { strcmp(s1, s2) }
324}
325
326/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strcoll.html>.
327#[unsafe(no_mangle)]
328pub unsafe extern "C" fn strcoll(s1: *const c_char, s2: *const c_char) -> c_int {
329    unsafe { strcoll_l(s1, s2, THREAD_LOCALE as locale_t) }
330}
331
332/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strcpy.html>.
333#[unsafe(no_mangle)]
334pub unsafe extern "C" fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char {
335    let src_iter = unsafe { NulTerminated::new(src).unwrap() };
336    let src_dest_iter = unsafe { SrcDstPtrIter::new(src_iter.chain(once(&0)), dst) };
337    for (src_item, dst_item) in src_dest_iter {
338        dst_item.write(*src_item);
339    }
340
341    dst
342}
343
344pub unsafe fn inner_strspn(s1: *const c_char, s2: *const c_char, cmp: bool) -> size_t {
345    let mut s1 = s1.cast::<u8>();
346    let mut s2 = s2.cast::<u8>();
347
348    // The below logic is effectively ripped from the musl implementation. It
349    // works by placing each byte as it's own bit in an array of numbers. Each
350    // number can hold up to 8 * mem::size_of::<usize>() bits. We need 256 bits
351    // in total, to fit one byte.
352
353    let mut set = BitSet256::new();
354
355    while unsafe { *s2 } != 0 {
356        set.insert(unsafe { *s2 } as usize);
357        s2 = unsafe { s2.offset(1) };
358    }
359
360    let mut i = 0;
361    while unsafe { *s1 } != 0 {
362        if set.contains(unsafe { *s1 } as usize) != cmp {
363            break;
364        }
365        i += 1;
366        s1 = unsafe { s1.offset(1) };
367    }
368    i
369}
370
371/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strcspn.html>.
372#[unsafe(no_mangle)]
373pub unsafe extern "C" fn strcspn(s1: *const c_char, s2: *const c_char) -> size_t {
374    unsafe { inner_strspn(s1, s2, false) }
375}
376
377/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strdup.html>.
378#[unsafe(no_mangle)]
379pub unsafe extern "C" fn strdup(s1: *const c_char) -> *mut c_char {
380    unsafe { strndup(s1, usize::MAX) }
381}
382
383/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strerror_l.html>.
384#[unsafe(no_mangle)]
385pub unsafe extern "C" fn strerror_l(errnum: c_int, _loc: locale_t) -> *mut c_char {
386    use core::fmt::Write;
387
388    static STRERROR_BUF: RawCell<[c_char; STRERROR_MAX]> = RawCell::new([0; STRERROR_MAX]);
389    let strerror_ptr = unsafe { STRERROR_BUF.unsafe_mut().as_mut_ptr() };
390    let mut w = platform::StringWriter(strerror_ptr, STRERROR_MAX);
391
392    let _ = match STR_ERROR.get(errnum as usize) {
393        Some(e) => w.write_str(e),
394        None => w.write_fmt(format_args!("Unknown error {}", errnum)),
395    };
396
397    strerror_ptr
398}
399
400/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strerror.html>.
401#[unsafe(no_mangle)]
402pub unsafe extern "C" fn strerror(errnum: c_int) -> *mut c_char {
403    unsafe { strerror_l(errnum, THREAD_LOCALE as locale_t) }
404}
405
406/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strerror_r.html>.
407#[unsafe(no_mangle)]
408pub unsafe extern "C" fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
409    let msg = unsafe { strerror(errnum) };
410    let len = unsafe { strlen(msg) };
411
412    if len >= buflen {
413        if buflen != 0 {
414            unsafe { memcpy(buf.cast::<c_void>(), msg as *const c_void, buflen - 1) };
415            unsafe { *buf.add(buflen - 1) = 0 };
416        }
417        return ERANGE as c_int;
418    }
419    unsafe { memcpy(buf.cast::<c_void>(), msg as *const c_void, len + 1) };
420
421    0
422}
423
424/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strlcat.html>.
425#[unsafe(no_mangle)]
426pub unsafe extern "C" fn strlcat(dst: *mut c_char, src: *const c_char, dstsize: size_t) -> size_t {
427    let dst_len = unsafe { strnlen(dst, dstsize) };
428    let d = unsafe { dst.add(dst_len) };
429    let src_len = unsafe { strlcpy(d, src, dstsize - dst_len) };
430    src_len + if dst_len > dstsize { dstsize } else { dst_len }
431}
432
433/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/strsep.3.html>.
434#[unsafe(no_mangle)]
435pub unsafe extern "C" fn strsep(str_: *mut *mut c_char, sep: *const c_char) -> *mut c_char {
436    let s = unsafe { *str_ };
437    if s.is_null() {
438        return ptr::null_mut();
439    }
440    let mut end = unsafe { s.add(strcspn(s, sep)) };
441    if unsafe { *end } != 0 {
442        unsafe { *end = 0 };
443        end = unsafe { end.add(1) };
444    } else {
445        end = ptr::null_mut();
446    }
447    unsafe { *str_ = end };
448    s
449}
450
451/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strlcat.html>.
452#[unsafe(no_mangle)]
453pub unsafe extern "C" fn strlcpy(dst: *mut c_char, src: *const c_char, dstsize: size_t) -> size_t {
454    let mut i = 0;
455
456    if dstsize != 0 {
457        while unsafe { *src.add(i) } != 0 && i < dstsize - 1 {
458            unsafe {
459                *dst.add(i) = *src.add(i);
460            }
461            i += 1;
462        }
463        unsafe {
464            *dst.add(i) = 0;
465        }
466    }
467
468    while unsafe { *src.add(i) } != 0 {
469        i += 1;
470    }
471
472    i as size_t
473}
474
475/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strlen.html>.
476///
477/// Returns the number of bytes in the string to which `s` points, not
478/// including the `NUL` character.
479///
480/// Always successful. The return value never represents an error.
481#[unsafe(no_mangle)]
482pub unsafe extern "C" fn strlen(s: *const c_char) -> size_t {
483    unsafe { NulTerminated::new(s) }
484        .map(|s| s.count())
485        .unwrap_or(0)
486}
487
488/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strncat.html>.
489#[unsafe(no_mangle)]
490pub unsafe extern "C" fn strncat(s1: *mut c_char, s2: *const c_char, n: size_t) -> *mut c_char {
491    let len = unsafe { strlen(s1.cast()) };
492    let mut i = 0;
493    while i < n {
494        let b = unsafe { *s2.add(i) };
495        if b == 0 {
496            break;
497        }
498
499        unsafe { *s1.add(len + i) = b };
500        i += 1;
501    }
502    unsafe { *s1.add(len + i) = 0 };
503
504    s1
505}
506
507/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strncmp.html>.
508#[unsafe(no_mangle)]
509pub unsafe extern "C" fn strncmp(s1: *const c_char, s2: *const c_char, n: size_t) -> c_int {
510    for i in 0..n {
511        // These must be cast as u8 to have correct comparisons
512        let a = unsafe { *s1.add(i) } as u8;
513        let b = unsafe { *s2.add(i) } as u8;
514        if a != b || a == 0 {
515            return c_int::from(a) - c_int::from(b);
516        }
517    }
518
519    0
520}
521
522/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strncpy.html>.
523///
524/// Copies not more than `n` bytes (bytes that follow a NUL character are not
525/// copied) from the array pointed to by `s2` to the array pointed to by `s1`.
526///
527/// If the array pointed to by `s2` is a string that is shorter than `n` bytes,
528/// NUL characters shall be appended to the copy in the array pointed to by
529/// `s1`, until `n` bytes in all are written.
530///
531/// Returns `s1`.
532///
533/// # Implementation
534/// Simply calls `stpncpy` and returns `s1`.
535///
536/// # Safety
537/// If copying takes place between objects that overlap, the behaviour is
538/// undefined.
539#[unsafe(no_mangle)]
540pub unsafe extern "C" fn strncpy(s1: *mut c_char, s2: *const c_char, n: size_t) -> *mut c_char {
541    unsafe { stpncpy(s1, s2, n) };
542    s1
543}
544
545/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strdup.html>.
546#[unsafe(no_mangle)]
547pub unsafe extern "C" fn strndup(s1: *const c_char, size: size_t) -> *mut c_char {
548    let len = unsafe { strnlen(s1, size) };
549
550    // the "+ 1" is to account for the NUL byte
551    let buffer = unsafe { platform::alloc(len + 1) }.cast::<c_char>();
552    if buffer.is_null() {
553        platform::ERRNO.set(ENOMEM as c_int);
554    } else {
555        //memcpy(buffer, s1, len)
556        for i in 0..len {
557            unsafe { *buffer.add(i) = *s1.add(i) };
558        }
559        unsafe { *buffer.add(len) = 0 };
560    }
561
562    buffer
563}
564
565/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strlen.html>.
566#[unsafe(no_mangle)]
567pub unsafe extern "C" fn strnlen(s: *const c_char, size: size_t) -> size_t {
568    unsafe { NulTerminated::new(s).unwrap() }.take(size).count()
569}
570
571/// Non-POSIX, see <https://en.cppreference.com/w/c/string/byte/strlen>.
572#[unsafe(no_mangle)]
573pub unsafe extern "C" fn strnlen_s(s: *const c_char, size: size_t) -> size_t {
574    if s.is_null() {
575        0
576    } else {
577        unsafe { strnlen(s, size) }
578    }
579}
580
581/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strpbrk.html>.
582#[unsafe(no_mangle)]
583pub unsafe extern "C" fn strpbrk(s1: *const c_char, s2: *const c_char) -> *mut c_char {
584    let p = unsafe { s1.add(strcspn(s1, s2)) };
585    if unsafe { *p } != 0 {
586        p.cast_mut()
587    } else {
588        ptr::null_mut()
589    }
590}
591
592/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strrchr.html>.
593#[unsafe(no_mangle)]
594pub unsafe extern "C" fn strrchr(s: *const c_char, c: c_int) -> *mut c_char {
595    let len = unsafe { strlen(s) } as isize;
596    let c = c as c_char;
597    let mut i = len - 1;
598    while i >= 0 {
599        if unsafe { *s.offset(i) } == c {
600            return unsafe { s.offset(i) }.cast_mut();
601        }
602        i -= 1;
603    }
604    ptr::null_mut()
605}
606
607/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strsignal.html>.
608#[unsafe(no_mangle)]
609pub unsafe extern "C" fn strsignal(sig: c_int) -> *mut c_char {
610    signal::SIGNAL_STRINGS
611        .get(sig as usize)
612        .unwrap_or(&signal::SIGNAL_STRINGS[0]) // Unknown signal message
613        .as_ptr() as *mut c_char
614}
615
616/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strspn.html>.
617#[unsafe(no_mangle)]
618pub unsafe extern "C" fn strspn(s1: *const c_char, s2: *const c_char) -> size_t {
619    unsafe { inner_strspn(s1, s2, true) }
620}
621
622unsafe fn inner_strstr(
623    mut haystack: *const c_char,
624    needle: *const c_char,
625    mask: c_char,
626) -> *mut c_char {
627    while unsafe { *haystack } != 0 {
628        let mut i = 0;
629        loop {
630            if unsafe { *needle.offset(i) } == 0 {
631                // We reached the end of the needle, everything matches this far
632                return haystack.cast_mut();
633            }
634            if unsafe { *haystack.offset(i) } & mask != unsafe { *needle.offset(i) } & mask {
635                break;
636            }
637
638            i += 1;
639        }
640
641        haystack = unsafe { haystack.offset(1) };
642    }
643    ptr::null_mut()
644}
645
646/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strstr.html>.
647#[unsafe(no_mangle)]
648pub unsafe extern "C" fn strstr(haystack: *const c_char, needle: *const c_char) -> *mut c_char {
649    unsafe { inner_strstr(haystack, needle, !0) }
650}
651
652/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtok.html>.
653#[unsafe(no_mangle)]
654pub unsafe extern "C" fn strtok(s1: *mut c_char, delimiter: *const c_char) -> *mut c_char {
655    static mut HAYSTACK: *mut c_char = ptr::null_mut();
656    unsafe { strtok_r(s1, delimiter, &raw mut HAYSTACK) }
657}
658
659/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtok.html>.
660#[unsafe(no_mangle)]
661pub unsafe extern "C" fn strtok_r(
662    s: *mut c_char,
663    delimiter: *const c_char,
664    lasts: *mut *mut c_char,
665) -> *mut c_char {
666    // musl returns null if both s and lasts are null, it sets s to lasts otherwise
667    let mut haystack = s;
668    if haystack.is_null() {
669        if (unsafe { *lasts }).is_null() {
670            return ptr::null_mut();
671        }
672        haystack = unsafe { *lasts };
673    }
674
675    // Skip past any extra delimiter left over from previous call
676    haystack = unsafe { haystack.add(strspn(haystack, delimiter)) };
677    if unsafe { *haystack } == 0 {
678        unsafe { *lasts = haystack };
679        return ptr::null_mut();
680    }
681
682    // Build token by injecting null byte into delimiter
683    let token = haystack;
684    haystack = unsafe { strpbrk(token, delimiter) };
685    if !haystack.is_null() {
686        unsafe { haystack.write(0) };
687        haystack = unsafe { haystack.add(1) };
688        unsafe { *lasts = haystack };
689    } else {
690        unsafe { *lasts = token.add(strlen(token)) };
691    }
692
693    token
694}
695
696/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strxfrm_l.html>.
697#[unsafe(no_mangle)]
698pub unsafe extern "C" fn strxfrm_l(
699    s1: *mut c_char,
700    s2: *const c_char,
701    n: size_t,
702    _loc: locale_t,
703) -> size_t {
704    // relibc has no locale stuff (yet)
705    let len = unsafe { strlen(s2) };
706    if len < n {
707        unsafe { strcpy(s1, s2) };
708    }
709    len
710}
711
712/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strxfrm.html>.
713#[unsafe(no_mangle)]
714pub unsafe extern "C" fn strxfrm(s1: *mut c_char, s2: *const c_char, n: size_t) -> size_t {
715    unsafe { strxfrm_l(s1, s2, n, THREAD_LOCALE as locale_t) }
716}