1use 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#[repr(C)]
37#[derive(Clone, Copy)]
38pub struct mbstate_t;
39
40#[unsafe(no_mangle)]
42pub unsafe extern "C" fn btowc(c: c_int) -> wint_t {
43 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
61pub unsafe fn fgetwc_unlocked(stream: *mut FILE) -> wint_t {
63 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#[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#[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 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#[unsafe(no_mangle)]
143pub unsafe extern "C" fn fputwc(wc: wchar_t, stream: *mut FILE) -> wint_t {
144 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#[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#[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#[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#[unsafe(no_mangle)]
191pub unsafe extern "C" fn getwc(stream: *mut FILE) -> wint_t {
192 unsafe { fgetwc(stream) }
193}
194
195#[unsafe(no_mangle)]
197pub unsafe extern "C" fn getwchar() -> wint_t {
198 unsafe { fgetwc(stdin) }
199}
200
201#[unsafe(no_mangle)]
203pub unsafe extern "C" fn mbsinit(ps: *const mbstate_t) -> c_int {
204 if ps.is_null() { 1 } else { 0 }
206}
207
208#[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#[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#[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 if amount == -1isize as usize {
267 unsafe { *src_ptr = src.add(src_offset) };
268 return 1isize as usize;
269 }
270
271 if amount == -2isize as usize {
273 unsafe { *ps = ps_copy };
274 break;
275 }
276
277 if !dst_ptr.is_null() {
279 unsafe { *dst_ptr.add(dst_offset) = wc };
280 }
281
282 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#[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#[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#[unsafe(no_mangle)]
320pub unsafe extern "C" fn putwchar(wc: wchar_t) -> wint_t {
321 unsafe { fputwc(wc, &raw mut *stdout) }
322}
323
324#[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#[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#[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 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#[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#[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#[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#[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#[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_skip!(0, "vswprintf not implemented");
425 -1
426}
427
428#[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#[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#[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#[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#[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#[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#[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#[unsafe(no_mangle)]
509pub unsafe extern "C" fn wcschr(ws: *const wchar_t, wc: wchar_t) -> *mut wchar_t {
510 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#[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#[unsafe(no_mangle)]
530pub unsafe extern "C" fn wcscoll(ws1: *const wchar_t, ws2: *const wchar_t) -> c_int {
531 unsafe { wcscmp(ws1, ws2) }
533}
534
535#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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 wcs.cast_mut()
718 }
719}
720
721#[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#[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#[unsafe(no_mangle)]
745pub unsafe extern "C" fn wcsstr(ws1: *const wchar_t, ws2: *const wchar_t) -> *mut wchar_t {
746 let ws2_len = unsafe { wcslen(ws2) };
748
749 if ws2_len == 0 {
751 ws1.cast_mut()
752 } else {
753 let ws1_len = unsafe { wcslen(ws1) };
754
755 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 let mut ws1_windows = ws1_slice.windows(ws2_len);
762
763 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#[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#[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 if wcs.is_null() {
823 if (unsafe { *state }).is_null() {
824 return ptr::null_mut();
826 }
827 wcs = unsafe { *state };
828 }
829
830 wcs = unsafe { wcs.add(wcsspn(wcs, delim)) };
832
833 if unsafe { *wcs } == 0 {
835 unsafe { *state = ptr::null_mut() };
836 return ptr::null_mut();
837 }
838
839 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#[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#[expect(clippy::cast_lossless)] #[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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}