Skip to main content

relibc/header/wchar/
mod.rs

1//! `wchar.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/wchar.h.html>.
4
5use core::{char, ffi::VaList as va_list, mem, ptr, slice};
6
7use crate::{
8    c_str::{WStr, Wide},
9    header::{
10        ctype::isspace,
11        errno::{EILSEQ, ENOMEM, ERANGE},
12        stdio::*,
13        stdlib::{MB_CUR_MAX, MB_LEN_MAX, malloc},
14        string,
15        time::*,
16        wchar::reader::Reader,
17        wctype::*,
18    },
19    iter::{NulTerminated, NulTerminatedInclusive},
20    platform::{
21        self, ERRNO,
22        types::{
23            c_char, c_double, c_int, c_long, c_longlong, c_uchar, c_ulong, c_ulonglong, c_void,
24            size_t, wchar_t, wint_t,
25        },
26    },
27};
28
29mod utf8;
30mod wprintf;
31mod wscanf;
32
33pub use utf8::get_char_encoded_length;
34
35/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/wchar.h.html>.
36#[repr(C)]
37#[derive(Clone, Copy)]
38pub struct mbstate_t;
39
40/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/btowc.html>.
41#[unsafe(no_mangle)]
42pub unsafe extern "C" fn btowc(c: c_int) -> wint_t {
43    //Check for EOF
44    if c == EOF {
45        return WEOF;
46    }
47
48    let uc = c as u8;
49    let c = uc as c_char;
50    let mut ps: mbstate_t = mbstate_t;
51    let mut wc: wchar_t = 0;
52    let saved_errno = platform::ERRNO.get();
53    let status = unsafe { mbrtowc(&raw mut wc, ptr::from_ref::<c_char>(&c), 1, &raw mut ps) };
54    if status == usize::MAX || status == usize::MAX - 1 {
55        platform::ERRNO.set(saved_errno);
56        return WEOF;
57    }
58    wc as wint_t
59}
60
61// not in POSIX.
62pub unsafe fn fgetwc_unlocked(stream: *mut FILE) -> wint_t {
63    // TODO: Process locale
64    let mut buf: [c_uchar; MB_CUR_MAX as usize] = [0; MB_CUR_MAX as usize];
65    let mut encoded_length = 0;
66    let mut bytes_read = 0;
67    let mut wc: wchar_t = 0;
68
69    loop {
70        unsafe {
71            let ret = getc_unlocked(stream);
72            if ret == EOF {
73                return WEOF;
74            }
75            *buf.as_mut_ptr().add(bytes_read) = ret as c_uchar;
76        }
77
78        bytes_read += 1;
79
80        if bytes_read == 1 {
81            encoded_length = if let Some(el) = get_char_encoded_length(buf[0]) {
82                el
83            } else {
84                unsafe {
85                    (*stream).flags |= F_ERR;
86                }
87                ERRNO.set(EILSEQ);
88                return WEOF;
89            };
90        }
91
92        if bytes_read >= encoded_length {
93            break;
94        }
95    }
96
97    unsafe {
98        mbrtowc(
99            &raw mut wc,
100            buf.as_ptr().cast::<c_char>(),
101            encoded_length,
102            ptr::null_mut(),
103        )
104    };
105
106    wc as wint_t
107}
108
109/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fgetwc.html>.
110#[unsafe(no_mangle)]
111pub unsafe extern "C" fn fgetwc(stream: *mut FILE) -> wint_t {
112    let mut stream = unsafe { (*stream).lock() };
113    unsafe { fgetwc_unlocked(&raw mut *stream) }
114}
115
116/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fgetws.html>.
117#[unsafe(no_mangle)]
118pub unsafe extern "C" fn fgetws(ws: *mut wchar_t, n: c_int, stream: *mut FILE) -> *mut wchar_t {
119    let mut i = 0;
120    let mut stream = unsafe { (*stream).lock() };
121    while ((i + 1) as c_int) < n {
122        let wc = unsafe { fgetwc_unlocked(&raw mut *stream) };
123        if wc == WEOF {
124            break;
125        }
126        unsafe { *ws.add(i) = wc as wchar_t };
127        i += 1;
128        if wc as wchar_t == '\n' as wchar_t {
129            break;
130        }
131    }
132    // NUL-terminate result
133    unsafe { *ws.add(i) = 0 };
134    if i == 0 || unsafe { ferror(&raw mut *stream) != 0 } {
135        core::ptr::null_mut()
136    } else {
137        ws
138    }
139}
140
141/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fputwc.html>.
142#[unsafe(no_mangle)]
143pub unsafe extern "C" fn fputwc(wc: wchar_t, stream: *mut FILE) -> wint_t {
144    //Convert wchar_t to multibytes first
145    static mut INTERNAL: mbstate_t = mbstate_t;
146    let mut bytes: [c_char; MB_CUR_MAX as usize] = [0; MB_CUR_MAX as usize];
147
148    let amount = unsafe { wcrtomb(bytes.as_mut_ptr(), wc, &raw mut INTERNAL) };
149
150    for b in bytes.iter().take(amount) {
151        unsafe { fputc(c_int::from(*b), &raw mut *stream) };
152    }
153
154    wc as wint_t
155}
156
157/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fputws.html>.
158#[unsafe(no_mangle)]
159pub unsafe extern "C" fn fputws(ws: *const wchar_t, stream: *mut FILE) -> c_int {
160    let mut i = 0;
161    loop {
162        let wc = unsafe { *ws.add(i) };
163        if wc == 0 {
164            return 0;
165        }
166        if unsafe { fputwc(wc, stream) } == WEOF {
167            return -1;
168        }
169        i += 1;
170    }
171}
172
173/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fwide.html>.
174#[unsafe(no_mangle)]
175pub unsafe extern "C" fn fwide(stream: *mut FILE, mode: c_int) -> c_int {
176    unsafe { (*stream).try_set_orientation(mode) }
177}
178
179/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fwscanf.html>.
180#[unsafe(no_mangle)]
181pub unsafe extern "C" fn fwscanf(
182    stream: *mut FILE,
183    format: *const wchar_t,
184    __valist: ...
185) -> c_int {
186    unsafe { vfwscanf(stream, format, __valist) }
187}
188
189/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getwc.html>.
190#[unsafe(no_mangle)]
191pub unsafe extern "C" fn getwc(stream: *mut FILE) -> wint_t {
192    unsafe { fgetwc(stream) }
193}
194
195/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getwchar.html>.
196#[unsafe(no_mangle)]
197pub unsafe extern "C" fn getwchar() -> wint_t {
198    unsafe { fgetwc(stdin) }
199}
200
201/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mbsinit.html>.
202#[unsafe(no_mangle)]
203pub unsafe extern "C" fn mbsinit(ps: *const mbstate_t) -> c_int {
204    //Add a check for the state maybe
205    if ps.is_null() { 1 } else { 0 }
206}
207
208/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mbrlen.html>.
209#[unsafe(no_mangle)]
210pub unsafe extern "C" fn mbrlen(s: *const c_char, n: size_t, ps: *mut mbstate_t) -> size_t {
211    static mut INTERNAL: mbstate_t = mbstate_t;
212    unsafe { mbrtowc(ptr::null_mut(), s, n, &raw mut INTERNAL) }
213}
214
215/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mbrtowc.html>.
216///
217/// Only works for UTF8 at the moment.
218#[unsafe(no_mangle)]
219pub unsafe extern "C" fn mbrtowc(
220    pwc: *mut wchar_t,
221    s: *const c_char,
222    n: size_t,
223    ps: *mut mbstate_t,
224) -> size_t {
225    static mut INTERNAL: mbstate_t = mbstate_t;
226
227    if ps.is_null() {
228        let ps = &raw mut INTERNAL;
229    }
230    if s.is_null() {
231        let xs: [c_char; 1] = [0];
232        unsafe { utf8::mbrtowc(pwc, ptr::from_ref::<c_char>(&xs[0]), 1, ps) }
233    } else {
234        unsafe { utf8::mbrtowc(pwc, s, n, ps) }
235    }
236}
237
238/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mbsnrtowcs.html>.
239///
240/// Convert a multibyte string to a wide string with a limited amount of bytes.
241#[unsafe(no_mangle)]
242pub unsafe extern "C" fn mbsnrtowcs(
243    dst_ptr: *mut wchar_t,
244    src_ptr: *mut *const c_char,
245    src_len: size_t,
246    dst_len: size_t,
247    ps: *mut mbstate_t,
248) -> size_t {
249    static mut INTERNAL: mbstate_t = mbstate_t;
250
251    if ps.is_null() {
252        let ps = &raw mut INTERNAL;
253    }
254
255    let mut src = unsafe { *src_ptr };
256
257    let mut dst_offset: usize = 0;
258    let mut src_offset: usize = 0;
259
260    while (dst_ptr.is_null() || dst_offset < dst_len) && src_offset < src_len {
261        let ps_copy = unsafe { *ps };
262        let mut wc: wchar_t = 0;
263        let amount = unsafe { mbrtowc(&raw mut wc, src.add(src_offset), src_len - src_offset, ps) };
264
265        // Stop in the event a decoding error occured.
266        if amount == -1isize as usize {
267            unsafe { *src_ptr = src.add(src_offset) };
268            return 1isize as usize;
269        }
270
271        // Stop decoding early in the event we encountered a partial character.
272        if amount == -2isize as usize {
273            unsafe { *ps = ps_copy };
274            break;
275        }
276
277        // Store the decoded wide character in the destination buffer.
278        if !dst_ptr.is_null() {
279            unsafe { *dst_ptr.add(dst_offset) = wc };
280        }
281
282        // Stop decoding after decoding a null character and return a NULL
283        // source pointer to the caller, not including the null character in the
284        // number of characters stored in the destination buffer.
285        if wc == 0 {
286            src = ptr::null();
287            src_offset = 0;
288            break;
289        }
290
291        dst_offset += 1;
292        src_offset += amount;
293    }
294
295    unsafe { *src_ptr = src.add(src_offset) };
296    dst_offset
297}
298
299/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/mbsrtowcs.html>.
300///
301/// Convert a multibyte string to a wide string.
302#[unsafe(no_mangle)]
303pub unsafe extern "C" fn mbsrtowcs(
304    dst: *mut wchar_t,
305    src: *mut *const c_char,
306    len: size_t,
307    ps: *mut mbstate_t,
308) -> size_t {
309    unsafe { mbsnrtowcs(dst, src, size_t::MAX, len, ps) }
310}
311
312/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/putwc.html>.
313#[unsafe(no_mangle)]
314pub unsafe extern "C" fn putwc(wc: wchar_t, stream: *mut FILE) -> wint_t {
315    unsafe { fputwc(wc, &raw mut *stream) }
316}
317
318/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/putwchar.html>.
319#[unsafe(no_mangle)]
320pub unsafe extern "C" fn putwchar(wc: wchar_t) -> wint_t {
321    unsafe { fputwc(wc, &raw mut *stdout) }
322}
323
324/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vswscanf.html>.
325#[unsafe(no_mangle)]
326pub unsafe extern "C" fn vswscanf(
327    s: *const wchar_t,
328    format: *const wchar_t,
329    __valist: va_list,
330) -> c_int {
331    unsafe {
332        let format = WStr::from_ptr(format);
333        let s = WStr::from_ptr(s);
334        wscanf::scanf(s.into(), format, __valist)
335    }
336}
337
338/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fwscanf.html>.
339#[unsafe(no_mangle)]
340pub unsafe extern "C" fn swscanf(
341    s: *const wchar_t,
342    format: *const wchar_t,
343    __valist: ...
344) -> c_int {
345    unsafe { vswscanf(s, format, __valist) }
346}
347
348/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ungetwc.html>.
349///
350/// Push wide character `wc` back onto `stream` so it'll be read next
351#[unsafe(no_mangle)]
352pub unsafe extern "C" fn ungetwc(wc: wint_t, stream: &mut FILE) -> wint_t {
353    if wc == WEOF {
354        return wc;
355    }
356    static mut INTERNAL: mbstate_t = mbstate_t;
357    let mut bytes: [c_char; MB_CUR_MAX as usize] = [0; MB_CUR_MAX as usize];
358
359    let amount = unsafe { wcrtomb(bytes.as_mut_ptr(), wc as wchar_t, &raw mut INTERNAL) };
360    if amount == usize::MAX {
361        return WEOF;
362    }
363
364    /*
365    We might have unget multiple bytes for a single wchar, eg, `รง` is [195, 167].
366    We need to unget them in reversed, so they are pused as [..., 167, 195, ...]
367    When we do fgetwc, we pop from the Vec, getting the write order of bytes [195, 167].
368    If we called ungetc in the non-reversed order, we would get [167, 195]
369    */
370    for i in 0..amount {
371        unsafe { ungetc(c_int::from(bytes[amount - 1 - i]), &raw mut *stream) };
372    }
373
374    wc
375}
376
377/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfwprintf.html>.
378#[unsafe(no_mangle)]
379pub unsafe extern "C" fn vfwprintf(
380    stream: *mut FILE,
381    format: *const wchar_t,
382    arg: va_list,
383) -> c_int {
384    let mut stream = unsafe { (*stream).lock() };
385    if (*stream).try_set_wide_orientation_unlocked().is_err() {
386        return -1;
387    }
388
389    unsafe { wprintf::wprintf(&mut *stream, WStr::from_ptr(format), arg) }
390}
391
392/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fwprintf.html>.
393#[unsafe(no_mangle)]
394pub unsafe extern "C" fn fwprintf(
395    stream: *mut FILE,
396    format: *const wchar_t,
397    __valist: ...
398) -> c_int {
399    unsafe { vfwprintf(stream, format, __valist) }
400}
401
402/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfwprintf.html>.
403#[unsafe(no_mangle)]
404pub unsafe extern "C" fn vwprintf(format: *const wchar_t, arg: va_list) -> c_int {
405    unsafe { vfwprintf(&raw mut *stdout, format, arg) }
406}
407
408/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fwprintf.html>.
409#[unsafe(no_mangle)]
410pub unsafe extern "C" fn wprintf(format: *const wchar_t, __valist: ...) -> c_int {
411    unsafe { vfwprintf(&raw mut *stdout, format, __valist) }
412}
413
414/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfwprintf.html>.
415#[unsafe(no_mangle)]
416pub unsafe extern "C" fn vswprintf(
417    s: *mut wchar_t,
418    n: size_t,
419    format: *const wchar_t,
420    arg: va_list,
421) -> c_int {
422    //TODO: implement vswprintf. This is not as simple as wprintf, since the output is not UTF-8
423    // but instead is a wchar array.
424    todo_skip!(0, "vswprintf not implemented");
425    -1
426}
427
428/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fwprintf.html>.
429#[unsafe(no_mangle)]
430pub unsafe extern "C" fn swprintf(
431    s: *mut wchar_t,
432    n: size_t,
433    format: *const wchar_t,
434    __valist: ...
435) -> c_int {
436    unsafe { vswprintf(s, n, format, __valist) }
437}
438
439/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcpcpy.html>.
440#[unsafe(no_mangle)]
441pub unsafe extern "C" fn wcpcpy(d: *mut wchar_t, s: *const wchar_t) -> *mut wchar_t {
442    unsafe { (wcscpy(d, s)).add(wcslen(s)) }
443}
444
445/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcpncpy.html>.
446#[unsafe(no_mangle)]
447pub unsafe extern "C" fn wcpncpy(d: *mut wchar_t, s: *const wchar_t, n: size_t) -> *mut wchar_t {
448    unsafe { (wcsncpy(d, s, n)).add(wcsnlen(s, n)) }
449}
450
451/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcrtomb.html>.
452///
453/// widechar to multibyte.
454#[unsafe(no_mangle)]
455pub unsafe extern "C" fn wcrtomb(s: *mut c_char, wc: wchar_t, ps: *mut mbstate_t) -> size_t {
456    let mut buffer: [c_char; MB_CUR_MAX as usize] = [0; MB_CUR_MAX as usize];
457    let (s_cpy, wc_cpy) = if s.is_null() {
458        (buffer.as_mut_ptr(), 0)
459    } else {
460        (s, wc)
461    };
462
463    unsafe { utf8::wcrtomb(s_cpy, wc_cpy, ps) }
464}
465
466/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsdup.html>.
467#[unsafe(no_mangle)]
468pub unsafe extern "C" fn wcsdup(s: *const wchar_t) -> *mut wchar_t {
469    let l = unsafe { wcslen(s) };
470
471    let d = unsafe { malloc((l + 1) * mem::size_of::<wchar_t>()) }.cast::<wchar_t>();
472
473    if d.is_null() {
474        ERRNO.set(ENOMEM);
475        return ptr::null_mut();
476    }
477
478    unsafe { wmemcpy(d, s, l + 1) }
479}
480
481/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsrtombs.html>.
482#[unsafe(no_mangle)]
483pub unsafe extern "C" fn wcsrtombs(
484    s: *mut c_char,
485    ws: *mut *const wchar_t,
486    n: size_t,
487    mut st: *mut mbstate_t,
488) -> size_t {
489    let mut mbs = mbstate_t {};
490    if st.is_null() {
491        st = &raw mut mbs;
492    }
493    unsafe { wcsnrtombs(s, ws, size_t::MAX, n, st) }
494}
495
496/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcscat.html>.
497#[unsafe(no_mangle)]
498pub unsafe extern "C" fn wcscat(ws1: *mut wchar_t, ws2: *const wchar_t) -> *mut wchar_t {
499    unsafe { wcsncat(ws1, ws2, usize::MAX) }
500}
501
502/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcschr.html>.
503///
504/// # Safety
505/// The caller is required to ensure that `ws` is a valid pointer to a buffer
506/// containing at least one nul value. The pointed-to buffer must not be
507/// modified for the duration of the call.
508#[unsafe(no_mangle)]
509pub unsafe extern "C" fn wcschr(ws: *const wchar_t, wc: wchar_t) -> *mut wchar_t {
510    // We iterate over non-mut references and thus need to coerce the
511    // resulting reference via a *const pointer before we can get our *mut.
512    // SAFETY: the caller is required to ensure that ws points to a valid
513    // nul-terminated buffer.
514    let ptr: *const wchar_t =
515        match unsafe { NulTerminatedInclusive::new(ws) }.find(|&&wsc| wsc == wc) {
516            Some(wsc_ref) => wsc_ref,
517            None => ptr::null(),
518        };
519    ptr.cast_mut()
520}
521
522/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcscmp.html>.
523#[unsafe(no_mangle)]
524pub unsafe extern "C" fn wcscmp(ws1: *const wchar_t, ws2: *const wchar_t) -> c_int {
525    unsafe { wcsncmp(ws1, ws2, usize::MAX) }
526}
527
528/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcscoll.html>.
529#[unsafe(no_mangle)]
530pub unsafe extern "C" fn wcscoll(ws1: *const wchar_t, ws2: *const wchar_t) -> c_int {
531    //TODO: locale comparison
532    unsafe { wcscmp(ws1, ws2) }
533}
534
535/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcscpy.html>.
536#[unsafe(no_mangle)]
537pub unsafe extern "C" fn wcscpy(ws1: *mut wchar_t, ws2: *const wchar_t) -> *mut wchar_t {
538    let mut i = 0;
539    loop {
540        let wc = unsafe { *ws2.add(i) };
541        unsafe { *ws1.add(i) = wc };
542        i += 1;
543        if wc == 0 {
544            return ws1;
545        }
546    }
547}
548
549unsafe fn inner_wcsspn(mut wcs: *const wchar_t, set: *const wchar_t, reject: bool) -> size_t {
550    let mut count = 0;
551    while unsafe { *wcs } != 0 && unsafe { wcschr(set, *wcs).is_null() } == reject {
552        wcs = unsafe { wcs.add(1) };
553        count += 1;
554    }
555    count
556}
557
558/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcscspn.html>.
559#[unsafe(no_mangle)]
560pub unsafe extern "C" fn wcscspn(wcs: *const wchar_t, set: *const wchar_t) -> size_t {
561    unsafe { inner_wcsspn(wcs, set, true) }
562}
563
564/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsftime.html>.
565#[unsafe(no_mangle)]
566pub extern "C" fn wcsftime(
567    wcs: *mut wchar_t,
568    maxsize: size_t,
569    format: *const wchar_t,
570    timptr: *const tm,
571) -> size_t {
572    todo_skip!(0, "wcsftime is not implemented");
573    0
574}
575
576/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcslen.html>.
577#[unsafe(no_mangle)]
578pub unsafe extern "C" fn wcslen(ws: *const wchar_t) -> size_t {
579    unsafe { NulTerminated::new(ws).unwrap() }.count()
580}
581
582/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsncat.html>.
583#[unsafe(no_mangle)]
584pub unsafe extern "C" fn wcsncat(
585    ws1: *mut wchar_t,
586    ws2: *const wchar_t,
587    n: size_t,
588) -> *mut wchar_t {
589    let len = unsafe { wcslen(ws1) };
590    let dest = unsafe { ws1.add(len) };
591    let mut i = 0;
592    while i < n {
593        let wc = unsafe { *ws2.add(i) };
594        if wc == 0 {
595            break;
596        }
597        unsafe { *dest.add(i) = wc };
598        i += 1;
599    }
600    unsafe { *dest.add(i) = 0 };
601    ws1
602}
603
604/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsncmp.html>.
605#[unsafe(no_mangle)]
606pub unsafe extern "C" fn wcsncmp(ws1: *const wchar_t, ws2: *const wchar_t, n: size_t) -> c_int {
607    for i in 0..n {
608        let wc1 = unsafe { *ws1.add(i) };
609        let wc2 = unsafe { *ws2.add(i) };
610        if wc1 != wc2 {
611            return wc1 - wc2;
612        } else if wc1 == 0 {
613            break;
614        }
615    }
616    0
617}
618
619/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsncpy.html>.
620#[unsafe(no_mangle)]
621pub unsafe extern "C" fn wcsncpy(
622    ws1: *mut wchar_t,
623    ws2: *const wchar_t,
624    n: size_t,
625) -> *mut wchar_t {
626    let mut i = 0;
627    while i < n {
628        let wc = unsafe { *ws2.add(i) };
629        unsafe { *ws1.add(i) = wc };
630        i += 1;
631        if wc == 0 {
632            break;
633        }
634    }
635    while i < n {
636        unsafe { *ws1.add(i) = 0 };
637        i += 1;
638    }
639    ws1
640}
641
642/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsnlen.html>.
643#[unsafe(no_mangle)]
644pub unsafe extern "C" fn wcsnlen(mut s: *const wchar_t, maxlen: size_t) -> size_t {
645    let mut len = 0;
646
647    while len < maxlen {
648        if unsafe { *s } == 0 {
649            break;
650        }
651
652        len += 1;
653        s = unsafe { s.offset(1) };
654    }
655
656    len
657}
658
659/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsnrtombs.html>.
660#[unsafe(no_mangle)]
661pub unsafe extern "C" fn wcsnrtombs(
662    mut dest: *mut c_char,
663    src: *mut *const wchar_t,
664    nwc: size_t,
665    len: size_t,
666    mut ps: *mut mbstate_t,
667) -> size_t {
668    let mut written = 0;
669    let mut read = 0;
670    let mut buf: [c_char; MB_LEN_MAX as usize] = [0; MB_LEN_MAX as usize];
671    let mut mbs = mbstate_t {};
672
673    if ps.is_null() {
674        ps = &raw mut mbs;
675    }
676
677    while read < nwc {
678        buf.fill(0);
679
680        let ret = unsafe { wcrtomb(buf.as_mut_ptr(), **src, ps) };
681
682        if ret == size_t::MAX {
683            ERRNO.set(EILSEQ);
684            return size_t::MAX;
685        }
686
687        if !dest.is_null() && len < written + ret {
688            return written;
689        }
690
691        if !dest.is_null() {
692            unsafe { ptr::copy_nonoverlapping(buf.as_ptr(), dest, ret) };
693            dest = unsafe { dest.add(ret) };
694        }
695
696        if unsafe { **src } == '\0' as wchar_t {
697            unsafe { *src = ptr::null() };
698            return written;
699        }
700
701        unsafe { *src = (*src).add(1) };
702        read += 1;
703        written += ret;
704    }
705    written
706}
707
708/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcspbrk.html>.
709#[unsafe(no_mangle)]
710pub unsafe extern "C" fn wcspbrk(mut wcs: *const wchar_t, set: *const wchar_t) -> *mut wchar_t {
711    wcs = unsafe { wcs.add(wcscspn(wcs, set)) };
712    if unsafe { *wcs } == 0 {
713        ptr::null_mut()
714    } else {
715        // Once again, C wants us to transmute a const pointer to a
716        // mutable one...
717        wcs.cast_mut()
718    }
719}
720
721/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsrchr.html>.
722#[unsafe(no_mangle)]
723pub unsafe extern "C" fn wcsrchr(ws1: *const wchar_t, wc: wchar_t) -> *mut wchar_t {
724    let mut last_matching_wc = ptr::null::<wchar_t>();
725    let mut i = 0;
726
727    while unsafe { *ws1.add(i) } != 0 {
728        if unsafe { *ws1.add(i) } == wc {
729            last_matching_wc = unsafe { ws1.add(i) };
730        }
731        i += 1;
732    }
733
734    last_matching_wc.cast_mut()
735}
736
737/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsspn.html>.
738#[unsafe(no_mangle)]
739pub unsafe extern "C" fn wcsspn(wcs: *const wchar_t, set: *const wchar_t) -> size_t {
740    unsafe { inner_wcsspn(wcs, set, false) }
741}
742
743/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsstr.html>.
744#[unsafe(no_mangle)]
745pub unsafe extern "C" fn wcsstr(ws1: *const wchar_t, ws2: *const wchar_t) -> *mut wchar_t {
746    // Get length of ws2, not including null terminator
747    let ws2_len = unsafe { wcslen(ws2) };
748
749    // The standard says that we must return ws1 if ws2 has length 0
750    if ws2_len == 0 {
751        ws1.cast_mut()
752    } else {
753        let ws1_len = unsafe { wcslen(ws1) };
754
755        // Construct slices without null terminator
756        let ws1_slice = unsafe { slice::from_raw_parts(ws1, ws1_len) };
757        let ws2_slice = unsafe { slice::from_raw_parts(ws2, ws2_len) };
758
759        /* Sliding ws2-sized window iterator on ws1. The iterator
760         * returns None if ws2 is longer than ws1. */
761        let mut ws1_windows = ws1_slice.windows(ws2_len);
762
763        /* Find the first offset into ws1 where the window is equal to
764         * the ws2 contents. Return null pointer if no match is found. */
765        match ws1_windows.position(|ws1_window| ws1_window == ws2_slice) {
766            Some(pos) => unsafe { ws1.add(pos).cast_mut() },
767            None => ptr::null_mut(),
768        }
769    }
770}
771
772/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcstod.html>.
773#[unsafe(no_mangle)]
774pub unsafe extern "C" fn wcstod(mut ptr: *const wchar_t, end: *mut *mut wchar_t) -> c_double {
775    const RADIX: u32 = 10;
776
777    skipws!(ptr);
778    let negative = unsafe { *ptr } == '-' as wchar_t;
779    if negative {
780        ptr = unsafe { ptr.add(1) };
781    }
782
783    let mut result: c_double = 0.0;
784    while let Some(digit) = char::from_u32(unsafe { *ptr } as _).and_then(|c| c.to_digit(RADIX)) {
785        result *= 10.0;
786        if negative {
787            result -= c_double::from(digit);
788        } else {
789            result += c_double::from(digit);
790        }
791        ptr = unsafe { ptr.add(1) };
792    }
793    if unsafe { *ptr } == '.' as wchar_t {
794        ptr = unsafe { ptr.add(1) };
795
796        let mut scale = 1.0;
797        while let Some(digit) = char::from_u32(unsafe { *ptr } as _).and_then(|c| c.to_digit(RADIX))
798        {
799            scale /= 10.0;
800            if negative {
801                result -= c_double::from(digit) * scale;
802            } else {
803                result += c_double::from(digit) * scale;
804            }
805            ptr = unsafe { ptr.add(1) };
806        }
807    }
808    if !end.is_null() {
809        unsafe { *end = ptr.cast_mut() };
810    }
811    result
812}
813
814/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcstok.html>.
815#[unsafe(no_mangle)]
816pub unsafe extern "C" fn wcstok(
817    mut wcs: *mut wchar_t,
818    delim: *const wchar_t,
819    state: *mut *mut wchar_t,
820) -> *mut wchar_t {
821    // Choose starting position
822    if wcs.is_null() {
823        if (unsafe { *state }).is_null() {
824            // There was no next token
825            return ptr::null_mut();
826        }
827        wcs = unsafe { *state };
828    }
829
830    // Advance past any delimiters
831    wcs = unsafe { wcs.add(wcsspn(wcs, delim)) };
832
833    // Check end
834    if unsafe { *wcs } == 0 {
835        unsafe { *state = ptr::null_mut() };
836        return ptr::null_mut();
837    }
838
839    // Advance *to* any delimiters
840    let end = unsafe { wcspbrk(wcs, delim) };
841    if end.is_null() {
842        unsafe { *state = ptr::null_mut() };
843    } else {
844        unsafe { *end = 0 };
845        unsafe { *state = end.add(1) };
846    }
847    wcs
848}
849
850/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcstol.html>.
851///
852/// Converts the initial portion of the wide-character string pointed to by
853/// `nptr` to a type `long`.
854///
855/// Upon success, returns the converted value. If no conversion could be
856/// performed, returns `0`.
857#[unsafe(no_mangle)]
858pub unsafe extern "C" fn wcstol(
859    mut nptr: *const wchar_t,
860    endptr: *mut *mut wchar_t,
861    base: c_int,
862) -> c_long {
863    skipws!(nptr);
864    let result = wcsto_impl!(c_long, nptr, base);
865    if !endptr.is_null() {
866        unsafe { *endptr = nptr.cast_mut() };
867    }
868    result
869}
870
871/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcstoll.html>.
872///
873/// Converts the initial portion of the wide-character string pointed to by
874/// `nptr` to a type `long long`.
875///
876/// Upon success, returns the converted value. If no conversion could be
877/// performed, returns `0`.
878#[expect(clippy::cast_lossless)] // not all users of `wcsto_impl!` are lossless
879#[unsafe(no_mangle)]
880pub unsafe extern "C" fn wcstoll(
881    mut nptr: *const wchar_t,
882    endptr: *mut *mut wchar_t,
883    base: c_int,
884) -> c_longlong {
885    skipws!(nptr);
886    let result = wcsto_impl!(c_longlong, nptr, base);
887    if !endptr.is_null() {
888        unsafe { *endptr = nptr.cast_mut() };
889    }
890    result
891}
892
893/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcstoul.html>.
894///
895/// Converts the initial portion of the wide-character string pointed to by
896/// `nptr` to a type `unsigned long`.
897///
898/// Upon success, returns the converted value. If no conversion could be
899/// performed, returns `0`.
900#[unsafe(no_mangle)]
901pub unsafe extern "C" fn wcstoul(
902    mut nptr: *const wchar_t,
903    endptr: *mut *mut wchar_t,
904    base: c_int,
905) -> c_ulong {
906    skipws!(nptr);
907    let result = wcsto_impl!(c_ulong, nptr, base);
908    if !endptr.is_null() {
909        unsafe { *endptr = nptr.cast_mut() };
910    }
911    result
912}
913
914/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcstoull.html>.
915///
916/// Converts the initial portion of the wide-character string pointed to by
917/// `nptr` to a type `unsigned long long`.
918///
919/// Upon success, returns the converted value. If no conversion could be
920/// performed, returns `0`.
921#[unsafe(no_mangle)]
922pub unsafe extern "C" fn wcstoull(
923    mut nptr: *const wchar_t,
924    endptr: *mut *mut wchar_t,
925    base: c_int,
926) -> c_ulonglong {
927    skipws!(nptr);
928    let result = wcsto_impl!(c_ulonglong, nptr, base);
929    if !endptr.is_null() {
930        unsafe { *endptr = nptr.cast_mut() };
931    }
932    result
933}
934
935/// See <https://pubs.opengroup.org/onlinepubs/009604499/functions/wcswcs.html>.
936///
937/// Marked legacy in issue 6.
938/// Encouraged to use `wcsstr` instead, which this implementation simply forwards to.
939#[deprecated]
940#[unsafe(no_mangle)]
941pub unsafe extern "C" fn wcswcs(ws1: *const wchar_t, ws2: *const wchar_t) -> *mut wchar_t {
942    unsafe { wcsstr(ws1, ws2) }
943}
944
945/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcswidth.html>.
946#[unsafe(no_mangle)]
947pub unsafe extern "C" fn wcswidth(pwcs: *const wchar_t, n: size_t) -> c_int {
948    let mut total_width = 0;
949    for i in 0..n {
950        let wc_width = wcwidth(unsafe { *pwcs.add(i) });
951        if wc_width < 0 {
952            return -1;
953        }
954        total_width += wc_width;
955    }
956    total_width
957}
958
959/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsxfrm.html>.
960#[unsafe(no_mangle)]
961pub extern "C" fn wcsxfrm(ws1: *mut wchar_t, ws2: *const wchar_t, n: size_t) -> size_t {
962    todo_skip!(0, "wcsxfrm is not implemented");
963    0
964}
965
966/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wctob.html>.
967#[unsafe(no_mangle)]
968pub extern "C" fn wctob(c: wint_t) -> c_int {
969    if c <= 0x7F { c as c_int } else { EOF }
970}
971
972/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcwidth.html>.
973#[unsafe(no_mangle)]
974pub extern "C" fn wcwidth(wc: wchar_t) -> c_int {
975    match char::from_u32(wc as u32) {
976        Some(c) => match unicode_width::UnicodeWidthChar::width(c) {
977            Some(width) => width as c_int,
978            None => -1,
979        },
980        None => -1,
981    }
982}
983
984/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wmemchr.html>.
985#[unsafe(no_mangle)]
986pub unsafe extern "C" fn wmemchr(ws: *const wchar_t, wc: wchar_t, n: size_t) -> *mut wchar_t {
987    for i in 0..n {
988        if unsafe { *ws.add(i) } == wc {
989            return unsafe { ws.add(i) }.cast_mut();
990        }
991    }
992    ptr::null_mut()
993}
994
995/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wmemcmp.html>.
996#[unsafe(no_mangle)]
997pub unsafe extern "C" fn wmemcmp(ws1: *const wchar_t, ws2: *const wchar_t, n: size_t) -> c_int {
998    for i in 0..n {
999        let wc1 = unsafe { *ws1.add(i) };
1000        let wc2 = unsafe { *ws2.add(i) };
1001        if wc1 != wc2 {
1002            return wc1 - wc2;
1003        }
1004    }
1005    0
1006}
1007
1008/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wmemcpy.html>.
1009#[unsafe(no_mangle)]
1010pub unsafe extern "C" fn wmemcpy(
1011    ws1: *mut wchar_t,
1012    ws2: *const wchar_t,
1013    n: size_t,
1014) -> *mut wchar_t {
1015    (unsafe {
1016        string::memcpy(
1017            ws1.cast::<c_void>(),
1018            ws2.cast::<c_void>(),
1019            n * mem::size_of::<wchar_t>(),
1020        )
1021    })
1022    .cast::<wchar_t>()
1023}
1024
1025/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wmemmove.html>.
1026#[unsafe(no_mangle)]
1027pub unsafe extern "C" fn wmemmove(
1028    ws1: *mut wchar_t,
1029    ws2: *const wchar_t,
1030    n: size_t,
1031) -> *mut wchar_t {
1032    (unsafe {
1033        string::memmove(
1034            ws1.cast::<c_void>(),
1035            ws2.cast::<c_void>(),
1036            n * mem::size_of::<wchar_t>(),
1037        )
1038    })
1039    .cast::<wchar_t>()
1040}
1041
1042/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wmemset.html>.
1043#[unsafe(no_mangle)]
1044pub unsafe extern "C" fn wmemset(ws: *mut wchar_t, wc: wchar_t, n: size_t) -> *mut wchar_t {
1045    for i in 0..n {
1046        unsafe { *ws.add(i) = wc };
1047    }
1048    ws
1049}
1050
1051/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfwscanf.html>.
1052#[unsafe(no_mangle)]
1053pub unsafe extern "C" fn vfwscanf(
1054    stream: *mut FILE,
1055    format: *const wchar_t,
1056    __valist: va_list,
1057) -> c_int {
1058    let mut file = unsafe { (*stream).lock() };
1059    if file.try_set_byte_orientation_unlocked().is_err() {
1060        return -1;
1061    }
1062
1063    let f: &mut FILE = &mut file;
1064    let reader: Reader<Wide> = f.into();
1065
1066    unsafe {
1067        let format = WStr::from_ptr(format);
1068        wscanf::scanf(reader, format, __valist)
1069    }
1070}
1071
1072/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vwscanf.html>.
1073#[unsafe(no_mangle)]
1074pub unsafe extern "C" fn vwscanf(format: *const wchar_t, __valist: va_list) -> c_int {
1075    unsafe { vfwscanf(stdin, format, __valist) }
1076}
1077
1078/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wscanf.html>.
1079#[unsafe(no_mangle)]
1080pub unsafe extern "C" fn wscanf(format: *const wchar_t, __valist: ...) -> c_int {
1081    unsafe { vfwscanf(stdin, format, __valist) }
1082}
1083
1084/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcscasecmp.html>.
1085#[unsafe(no_mangle)]
1086pub unsafe extern "C" fn wcscasecmp(mut s1: *const wchar_t, mut s2: *const wchar_t) -> c_int {
1087    unsafe {
1088        while *s1 != 0 && *s2 != 0 {
1089            if towlower(*s1 as wint_t) != towlower(*s2 as wint_t) {
1090                break;
1091            }
1092            s1 = s1.add(1);
1093            s2 = s2.add(1);
1094        }
1095        let result = towlower(*s1 as wint_t).wrapping_sub(towlower(*s2 as wint_t));
1096        result as c_int
1097    }
1098}
1099
1100/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsncasecmp.html>.
1101#[unsafe(no_mangle)]
1102pub unsafe extern "C" fn wcsncasecmp(
1103    mut s1: *const wchar_t,
1104    mut s2: *const wchar_t,
1105    n: size_t,
1106) -> c_int {
1107    if n == 0 {
1108        return 0;
1109    }
1110    unsafe {
1111        for _ in 0..n {
1112            if *s1 == 0 || *s2 == 0 || towlower(*s1 as wint_t) != towlower(*s2 as wint_t) {
1113                return towlower(*s1 as wint_t).wrapping_sub(towlower(*s2 as wint_t)) as c_int;
1114            }
1115            s1 = s1.add(1);
1116            s2 = s2.add(1);
1117        }
1118        0
1119    }
1120}