xref: /drstd/dlibc/src/unix/header/wchar/mod.rs (revision 69bbf99969c635b975633fbae5786a97353ca9ae)
1 //! wchar implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/wchar.h.html
2 
3 use core::{char, ffi::VaList as va_list, mem, ptr, slice, usize};
4 use crate::unix::platform;
5 use crate::unix::header::{
6     ctype::isspace, errno::ERANGE, stdio::*, stdlib::MB_CUR_MAX, string, time::*, wctype::*,
7 };
8 
9 mod utf8;
10 #[repr(C)]
11 #[derive(Clone, Copy)]
12 pub struct mbstate_t;
13 
14 #[no_mangle]
15 pub unsafe extern "C" fn btowc(c: ::c_int) -> ::wint_t {
16     //Check for EOF
17     if c == EOF {
18         return WEOF;
19     }
20 
21     let uc = c as u8;
22     let c = uc as ::c_char;
23     let mut ps: mbstate_t = mbstate_t;
24     let mut wc: ::wchar_t = 0;
25     let saved_errno = platform::errno;
26     let status = mbrtowc(&mut wc, &c as *const ::c_char, 1, &mut ps);
27     if status == usize::max_value() || status == usize::max_value() - 1 {
28         platform::errno = saved_errno;
29         return WEOF;
30     }
31     wc as ::wint_t
32 }
33 
34 #[no_mangle]
35 pub unsafe extern "C" fn fgetwc(stream: *mut FILE) -> ::wint_t {
36     //TODO: Real multibyte
37     btowc(fgetc(stream))
38 }
39 
40 #[no_mangle]
41 pub unsafe extern "C" fn fgetws(ws: *mut ::wchar_t, n: ::c_int, stream: *mut FILE) -> *mut ::wchar_t {
42     //TODO: lock
43     let mut i = 0;
44     while ((i + 1) as ::c_int) < n {
45         let wc = fgetwc(stream);
46         if wc == WEOF {
47             return ptr::null_mut();
48         }
49         *ws.add(i) = wc as ::wchar_t;
50         i += 1;
51     }
52     while (i as ::c_int) < n {
53         *ws.add(i) = 0;
54         i += 1;
55     }
56     ws
57 }
58 
59 #[no_mangle]
60 pub unsafe extern "C" fn fputwc(wc: ::wchar_t, stream: *mut FILE) -> ::wint_t {
61     //Convert ::wchar_t to multibytes first
62     static mut INTERNAL: mbstate_t = mbstate_t;
63     let mut bytes: [::c_char; MB_CUR_MAX as usize] = [0; MB_CUR_MAX as usize];
64 
65     let amount = wcrtomb(bytes.as_mut_ptr(), wc, &mut INTERNAL);
66 
67     for i in 0..amount {
68         fputc(bytes[i] as ::c_int, &mut *stream);
69     }
70 
71     wc as ::wint_t
72 }
73 
74 #[no_mangle]
75 pub unsafe extern "C" fn fputws(ws: *const ::wchar_t, stream: *mut FILE) -> ::c_int {
76     let mut i = 0;
77     loop {
78         let wc = *ws.add(i);
79         if wc == 0 {
80             return 0;
81         }
82         if fputwc(wc, stream) == WEOF {
83             return -1;
84         }
85         i += 1;
86     }
87 }
88 
89 #[no_mangle]
90 pub unsafe extern "C" fn fwide(stream: *mut FILE, mode: ::c_int) -> ::c_int {
91     (*stream).try_set_orientation(mode)
92 }
93 
94 #[no_mangle]
95 pub unsafe extern "C" fn getwc(stream: *mut FILE) -> ::wint_t {
96     fgetwc(stream)
97 }
98 
99 #[no_mangle]
100 pub unsafe extern "C" fn getwchar() -> ::wint_t {
101     fgetwc(stdin)
102 }
103 
104 #[no_mangle]
105 pub unsafe extern "C" fn mbsinit(ps: *const mbstate_t) -> ::c_int {
106     //Add a check for the state maybe
107     if ps.is_null() {
108         1
109     } else {
110         0
111     }
112 }
113 
114 #[no_mangle]
115 pub unsafe extern "C" fn mbrlen(s: *const ::c_char, n: ::size_t, _ps: *mut mbstate_t) -> ::size_t {
116     static mut INTERNAL: mbstate_t = mbstate_t;
117     mbrtowc(ptr::null_mut(), s, n, &mut INTERNAL)
118 }
119 
120 //Only works for UTF8 at the moment
121 #[no_mangle]
122 pub unsafe extern "C" fn mbrtowc(
123     pwc: *mut ::wchar_t,
124     s: *const ::c_char,
125     n: ::size_t,
126     ps: *mut mbstate_t,
127 ) -> ::size_t {
128     static mut INTERNAL: mbstate_t = mbstate_t;
129 
130     if ps.is_null() {
131         let _ps = &mut INTERNAL;
132     }
133     if s.is_null() {
134         let xs: [::c_char; 1] = [0];
135         utf8::mbrtowc(pwc, &xs[0] as *const ::c_char, 1, ps)
136     } else {
137         utf8::mbrtowc(pwc, s, n, ps)
138     }
139 }
140 
141 //Convert a multibyte string to a wide string with a limited amount of bytes
142 //Required for in POSIX.1-2008
143 #[no_mangle]
144 pub unsafe extern "C" fn mbsnrtowcs(
145     dst_ptr: *mut ::wchar_t,
146     src_ptr: *mut *const ::c_char,
147     src_len: ::size_t,
148     dst_len: ::size_t,
149     ps: *mut mbstate_t,
150 ) -> ::size_t {
151     static mut INTERNAL: mbstate_t = mbstate_t;
152 
153     if ps.is_null() {
154         let _ps = &mut INTERNAL;
155     }
156 
157     let mut src = *src_ptr;
158 
159     let mut dst_offset: usize = 0;
160     let mut src_offset: usize = 0;
161 
162     while (dst_ptr.is_null() || dst_offset < dst_len) && src_offset < src_len {
163         let ps_copy = *ps;
164         let mut wc: ::wchar_t = 0;
165         let amount = mbrtowc(&mut wc, src.add(src_offset), src_len - src_offset, ps);
166 
167         // Stop in the event a decoding error occured.
168         if amount == -1isize as usize {
169             *src_ptr = src.add(src_offset);
170             return 1isize as usize;
171         }
172 
173         // Stop decoding early in the event we encountered a partial character.
174         if amount == -2isize as usize {
175             *ps = ps_copy;
176             break;
177         }
178 
179         // Store the decoded wide character in the destination buffer.
180         if !dst_ptr.is_null() {
181             *dst_ptr.add(dst_offset) = wc;
182         }
183 
184         // Stop decoding after decoding a null character and return a NULL
185         // source pointer to the caller, not including the null character in the
186         // number of characters stored in the destination buffer.
187         if wc == 0 {
188             src = ptr::null();
189             src_offset = 0;
190             break;
191         }
192 
193         dst_offset += 1;
194         src_offset += amount;
195     }
196 
197     *src_ptr = src.add(src_offset);
198     dst_offset
199 }
200 
201 //Convert a multibyte string to a wide string
202 #[no_mangle]
203 pub unsafe extern "C" fn mbsrtowcs(
204     dst: *mut ::wchar_t,
205     src: *mut *const ::c_char,
206     len: ::size_t,
207     ps: *mut mbstate_t,
208 ) -> ::size_t {
209     mbsnrtowcs(dst, src, ::size_t::max_value(), len, ps)
210 }
211 
212 #[no_mangle]
213 pub unsafe extern "C" fn putwc(wc: ::wchar_t, stream: *mut FILE) -> ::wint_t {
214     fputwc(wc, &mut *stream)
215 }
216 
217 #[no_mangle]
218 pub unsafe extern "C" fn putwchar(wc: ::wchar_t) -> ::wint_t {
219     fputwc(wc, &mut *stdout)
220 }
221 
222 // #[no_mangle]
223 pub extern "C" fn swprintf(
224     _s: *mut ::wchar_t,
225     _n: ::size_t,
226     _format: *const ::wchar_t,
227     _ap: va_list,
228 ) -> ::c_int {
229     unimplemented!();
230 }
231 
232 // #[no_mangle]
233 pub extern "C" fn swscanf(_s: *const ::wchar_t, _format: *const ::wchar_t, _ap: va_list) -> ::c_int {
234     unimplemented!();
235 }
236 
237 // #[no_mangle]
238 pub extern "C" fn ungetwc(_wc: ::wint_t, _stream: *mut FILE) -> ::wint_t {
239     unimplemented!();
240 }
241 
242 // #[no_mangle]
243 pub extern "C" fn vfwprintf(_stream: *mut FILE, _format: *const ::wchar_t, _arg: va_list) -> ::c_int {
244     unimplemented!();
245 }
246 
247 // #[no_mangle]
248 pub extern "C" fn vwprintf(_format: *const ::wchar_t, _arg: va_list) -> ::c_int {
249     unimplemented!();
250 }
251 
252 // #[no_mangle]
253 pub extern "C" fn vswprintf(
254     _s: *mut ::wchar_t,
255     _n: ::size_t,
256     _format: *const ::wchar_t,
257     _arg: va_list,
258 ) -> ::c_int {
259     unimplemented!();
260 }
261 
262 //widechar to multibyte
263 #[no_mangle]
264 pub unsafe extern "C" fn wcrtomb(s: *mut ::c_char, wc: ::wchar_t, ps: *mut mbstate_t) -> ::size_t {
265     let mut buffer: [::c_char; MB_CUR_MAX as usize] = [0; MB_CUR_MAX as usize];
266     let (s_cpy, wc_cpy) = if s.is_null() {
267         (buffer.as_mut_ptr(), 0)
268     } else {
269         (s, wc)
270     };
271 
272     utf8::wcrtomb(s_cpy, wc_cpy, ps)
273 }
274 
275 #[no_mangle]
276 pub unsafe extern "C" fn wcscat(ws1: *mut ::wchar_t, ws2: *const ::wchar_t) -> *mut ::wchar_t {
277     wcsncat(ws1, ws2, usize::MAX)
278 }
279 
280 #[no_mangle]
281 pub unsafe extern "C" fn wcschr(ws: *const ::wchar_t, wc: ::wchar_t) -> *mut ::wchar_t {
282     let mut i = 0;
283     loop {
284         if *ws.add(i) == wc {
285             return ws.add(i) as *mut ::wchar_t;
286         } else if *ws.add(i) == 0 {
287             return ptr::null_mut();
288         }
289         i += 1;
290     }
291 }
292 
293 #[no_mangle]
294 pub unsafe extern "C" fn wcscmp(ws1: *const ::wchar_t, ws2: *const ::wchar_t) -> ::c_int {
295     wcsncmp(ws1, ws2, usize::MAX)
296 }
297 
298 #[no_mangle]
299 pub unsafe extern "C" fn wcscoll(ws1: *const ::wchar_t, ws2: *const ::wchar_t) -> ::c_int {
300     //TODO: locale comparison
301     wcscmp(ws1, ws2)
302 }
303 
304 #[no_mangle]
305 pub unsafe extern "C" fn wcscpy(ws1: *mut ::wchar_t, ws2: *const ::wchar_t) -> *mut ::wchar_t {
306     let mut i = 0;
307     loop {
308         let wc = *ws2.add(i);
309         *ws1.add(i) = wc;
310         i += 1;
311         if wc == 0 {
312             return ws1;
313         }
314     }
315 }
316 
317 unsafe fn inner_wcsspn(mut wcs: *const ::wchar_t, set: *const ::wchar_t, reject: bool) -> ::size_t {
318     let mut count = 0;
319     while (*wcs) != 0 && wcschr(set, *wcs).is_null() == reject {
320         wcs = wcs.add(1);
321         count += 1;
322     }
323     count
324 }
325 
326 #[no_mangle]
327 pub unsafe extern "C" fn wcscspn(wcs: *const ::wchar_t, set: *const ::wchar_t) -> ::size_t {
328     inner_wcsspn(wcs, set, true)
329 }
330 
331 // #[no_mangle]
332 pub extern "C" fn wcsftime(
333     _wcs: *mut ::wchar_t,
334     _maxsize: ::size_t,
335     _format: *const ::wchar_t,
336     _timptr: *mut tm,
337 ) -> ::size_t {
338     unimplemented!();
339 }
340 
341 #[no_mangle]
342 pub unsafe extern "C" fn wcslen(ws: *const ::wchar_t) -> ::size_t {
343     let mut i = 0;
344     loop {
345         if *ws.add(i) == 0 {
346             return i;
347         }
348         i += 1;
349     }
350 }
351 
352 #[no_mangle]
353 pub unsafe extern "C" fn wcsncat(
354     ws1: *mut ::wchar_t,
355     ws2: *const ::wchar_t,
356     n: ::size_t,
357 ) -> *mut ::wchar_t {
358     let len = wcslen(ws1);
359     let dest = ws1.add(len);
360     let mut i = 0;
361     while i < n {
362         let wc = *ws2.add(i);
363         if wc == 0 {
364             break;
365         }
366         *dest.add(i) = wc;
367         i += 1;
368     }
369     *dest.add(i) = 0;
370     ws1
371 }
372 
373 #[no_mangle]
374 pub unsafe extern "C" fn wcsncmp(ws1: *const ::wchar_t, ws2: *const ::wchar_t, n: ::size_t) -> ::c_int {
375     for i in 0..n {
376         let wc1 = *ws1.add(i);
377         let wc2 = *ws2.add(i);
378         if wc1 != wc2 {
379             return wc1 - wc2;
380         } else if wc1 == 0 {
381             break;
382         }
383     }
384     0
385 }
386 
387 #[no_mangle]
388 pub unsafe extern "C" fn wcsncpy(
389     ws1: *mut ::wchar_t,
390     ws2: *const ::wchar_t,
391     n: ::size_t,
392 ) -> *mut ::wchar_t {
393     let mut i = 0;
394     while i < n {
395         let wc = *ws2.add(i);
396         *ws1.add(i) = wc;
397         i += 1;
398         if wc == 0 {
399             break;
400         }
401     }
402     while i < n {
403         *ws1.add(i) = 0;
404         i += 1;
405     }
406     ws1
407 }
408 
409 #[no_mangle]
410 pub unsafe extern "C" fn wcspbrk(mut wcs: *const ::wchar_t, set: *const ::wchar_t) -> *mut ::wchar_t {
411     wcs = wcs.add(wcscspn(wcs, set));
412     if *wcs == 0 {
413         ptr::null_mut()
414     } else {
415         // Once again, C wants us to transmute a const pointer to a
416         // mutable one...
417         wcs as *mut _
418     }
419 }
420 
421 #[no_mangle]
422 pub unsafe extern "C" fn wcsrchr(ws1: *const ::wchar_t, wc: ::wchar_t) -> *mut ::wchar_t {
423     let mut last_matching_wc = 0 as *const ::wchar_t;
424     let mut i = 0;
425 
426     while *ws1.add(i) != 0 {
427         if *ws1.add(i) == wc {
428             last_matching_wc = ws1.add(i);
429         }
430         i += 1;
431     }
432 
433     last_matching_wc as *mut ::wchar_t
434 }
435 
436 // #[no_mangle]
437 pub extern "C" fn wcsrtombs(
438     _dst: *mut ::c_char,
439     _src: *mut *const ::wchar_t,
440     _len: ::size_t,
441     _ps: *mut mbstate_t,
442 ) -> ::size_t {
443     unimplemented!();
444 }
445 
446 #[no_mangle]
447 pub unsafe extern "C" fn wcsspn(wcs: *const ::wchar_t, set: *const ::wchar_t) -> ::size_t {
448     inner_wcsspn(wcs, set, false)
449 }
450 
451 #[no_mangle]
452 pub unsafe extern "C" fn wcsstr(ws1: *const ::wchar_t, ws2: *const ::wchar_t) -> *mut ::wchar_t {
453     // Get length of ws2, not including null terminator
454     let ws2_len = wcslen(ws2);
455 
456     // The standard says that we must return ws1 if ws2 has length 0
457     if ws2_len == 0 {
458         ws1 as *mut ::wchar_t
459     } else {
460         let ws1_len = wcslen(ws1);
461 
462         // Construct slices without null terminator
463         let ws1_slice = slice::from_raw_parts(ws1, ws1_len);
464         let ws2_slice = slice::from_raw_parts(ws2, ws2_len);
465 
466         /* Sliding ws2-sized window iterator on ws1. The iterator
467          * returns None if ws2 is longer than ws1. */
468         let mut ws1_windows = ws1_slice.windows(ws2_len);
469 
470         /* Find the first offset into ws1 where the window is equal to
471          * the ws2 contents. Return null pointer if no match is found. */
472         match ws1_windows.position(|ws1_window| ws1_window == ws2_slice) {
473             Some(pos) => ws1.add(pos) as *mut ::wchar_t,
474             None => ptr::null_mut(),
475         }
476     }
477 }
478 
479 macro_rules! skipws {
480     ($ptr:expr) => {
481         while isspace(*$ptr) != 0 {
482             $ptr = $ptr.add(1);
483         }
484     };
485 }
486 
487 #[no_mangle]
488 pub unsafe extern "C" fn wcstod(mut ptr: *const ::wchar_t, end: *mut *mut ::wchar_t) -> ::c_double {
489     const RADIX: u32 = 10;
490 
491     skipws!(ptr);
492     let negative = *ptr == '-' as ::wchar_t;
493     if negative {
494         ptr = ptr.add(1);
495     }
496 
497     let mut result: ::c_double = 0.0;
498     while let Some(digit) = char::from_u32(*ptr as _).and_then(|c| c.to_digit(RADIX)) {
499         result *= 10.0;
500         if negative {
501             result -= digit as ::c_double;
502         } else {
503             result += digit as ::c_double;
504         }
505         ptr = ptr.add(1);
506     }
507     if *ptr == '.' as ::wchar_t {
508         ptr = ptr.add(1);
509 
510         let mut scale = 1.0;
511         while let Some(digit) = char::from_u32(*ptr as _).and_then(|c| c.to_digit(RADIX)) {
512             scale /= 10.0;
513             if negative {
514                 result -= digit as ::c_double * scale;
515             } else {
516                 result += digit as ::c_double * scale;
517             }
518             ptr = ptr.add(1);
519         }
520     }
521     if !end.is_null() {
522         *end = ptr as *mut _;
523     }
524     result
525 }
526 
527 #[no_mangle]
528 pub unsafe extern "C" fn wcstok(
529     mut wcs: *mut ::wchar_t,
530     delim: *const ::wchar_t,
531     state: *mut *mut ::wchar_t,
532 ) -> *mut ::wchar_t {
533     // Choose starting position
534     if wcs.is_null() {
535         if (*state).is_null() {
536             // There was no next token
537             return ptr::null_mut();
538         }
539         wcs = *state;
540     }
541 
542     // Advance past any delimiters
543     wcs = wcs.add(wcsspn(wcs, delim));
544 
545     // Check end
546     if *wcs == 0 {
547         *state = ptr::null_mut();
548         return ptr::null_mut();
549     }
550 
551     // Advance *to* any delimiters
552     let end = wcspbrk(wcs, delim);
553     if end.is_null() {
554         *state = ptr::null_mut();
555     } else {
556         *end = 0;
557         *state = end.add(1);
558     }
559     wcs
560 }
561 
562 macro_rules! strtou_impl {
563     ($type:ident, $ptr:expr, $base:expr) => {
564         strtou_impl!($type, $ptr, $base, false)
565     };
566     ($type:ident, $ptr:expr, $base:expr, $negative:expr) => {{
567         if $base == 16 && *$ptr == '0' as ::wchar_t && *$ptr.add(1) | 0x20 == 'x' as ::wchar_t {
568             $ptr = $ptr.add(2);
569         }
570 
571         let mut result: $type = 0;
572         while let Some(digit) = char::from_u32(*$ptr as u32).and_then(|c| c.to_digit($base as u32))
573         {
574             let new = result.checked_mul($base as $type).and_then(|result| {
575                 if $negative {
576                     result.checked_sub(digit as $type)
577                 } else {
578                     result.checked_add(digit as $type)
579                 }
580             });
581             result = match new {
582                 Some(new) => new,
583                 None => {
584                     platform::errno = ERANGE;
585                     return !0;
586                 }
587             };
588 
589             $ptr = $ptr.add(1);
590         }
591         result
592     }};
593 }
594 macro_rules! strto_impl {
595     ($type:ident, $ptr:expr, $base:expr) => {{
596         let negative = *$ptr == '-' as ::wchar_t;
597         if negative {
598             $ptr = $ptr.add(1);
599         }
600         strtou_impl!($type, $ptr, $base, negative)
601     }};
602 }
603 
604 #[no_mangle]
605 pub unsafe extern "C" fn wcstol(
606     mut ptr: *const ::wchar_t,
607     end: *mut *mut ::wchar_t,
608     base: ::c_int,
609 ) -> ::c_long {
610     skipws!(ptr);
611     let result = strto_impl!(i64, ptr, base);
612     if !end.is_null() {
613         *end = ptr as *mut _;
614     }
615     result
616 }
617 
618 #[no_mangle]
619 pub unsafe extern "C" fn wcstoul(
620     mut ptr: *const ::wchar_t,
621     end: *mut *mut ::wchar_t,
622     base: ::c_int,
623 ) -> ::c_ulong {
624     skipws!(ptr);
625     let result = strtou_impl!(u64, ptr, base);
626     if !end.is_null() {
627         *end = ptr as *mut _;
628     }
629     result
630 }
631 
632 // #[no_mangle]
633 pub extern "C" fn wcswcs(_ws1: *const ::wchar_t, _ws2: *const ::wchar_t) -> *mut ::wchar_t {
634     unimplemented!();
635 }
636 
637 #[no_mangle]
638 pub unsafe extern "C" fn wcswidth(pwcs: *const ::wchar_t, n: ::size_t) -> ::c_int {
639     let mut total_width = 0;
640     for i in 0..n {
641         let wc_width = wcwidth(*pwcs.add(i));
642         if wc_width < 0 {
643             return -1;
644         }
645         total_width += wc_width;
646     }
647     total_width
648 }
649 
650 // #[no_mangle]
651 pub extern "C" fn wcsxfrm(_ws1: *mut ::wchar_t, _ws2: *const ::wchar_t, _n: ::size_t) -> ::size_t {
652     unimplemented!();
653 }
654 
655 #[no_mangle]
656 pub extern "C" fn wctob(c: ::wint_t) -> ::c_int {
657     if c <= 0x7F {
658         c as ::c_int
659     } else {
660         EOF
661     }
662 }
663 
664 #[no_mangle]
665 pub extern "C" fn wcwidth(wc: ::wchar_t) -> ::c_int {
666     match char::from_u32(wc as u32) {
667         Some(c) => match unicode_width::UnicodeWidthChar::width(c) {
668             Some(width) => width as ::c_int,
669             None => -1,
670         },
671         None => -1,
672     }
673 }
674 
675 #[no_mangle]
676 pub unsafe extern "C" fn wmemchr(ws: *const ::wchar_t, wc: ::wchar_t, n: ::size_t) -> *mut ::wchar_t {
677     for i in 0..n {
678         if *ws.add(i) == wc {
679             return ws.add(i) as *mut ::wchar_t;
680         }
681     }
682     ptr::null_mut()
683 }
684 
685 #[no_mangle]
686 pub unsafe extern "C" fn wmemcmp(ws1: *const ::wchar_t, ws2: *const ::wchar_t, n: ::size_t) -> ::c_int {
687     for i in 0..n {
688         let wc1 = *ws1.add(i);
689         let wc2 = *ws2.add(i);
690         if wc1 != wc2 {
691             return wc1 - wc2;
692         }
693     }
694     0
695 }
696 
697 #[no_mangle]
698 pub unsafe extern "C" fn wmemcpy(
699     ws1: *mut ::wchar_t,
700     ws2: *const ::wchar_t,
701     n: ::size_t,
702 ) -> *mut ::wchar_t {
703     string::memcpy(
704         ws1 as *mut ::c_void,
705         ws2 as *const ::c_void,
706         n * mem::size_of::<::wchar_t>(),
707     ) as *mut ::wchar_t
708 }
709 
710 #[no_mangle]
711 pub unsafe extern "C" fn wmemmove(
712     ws1: *mut ::wchar_t,
713     ws2: *const ::wchar_t,
714     n: ::size_t,
715 ) -> *mut ::wchar_t {
716     string::memmove(
717         ws1 as *mut ::c_void,
718         ws2 as *const ::c_void,
719         n * mem::size_of::<::wchar_t>(),
720     ) as *mut ::wchar_t
721 }
722 
723 #[no_mangle]
724 pub unsafe extern "C" fn wmemset(ws: *mut ::wchar_t, wc: ::wchar_t, n: ::size_t) -> *mut ::wchar_t {
725     for i in 0..n {
726         *ws.add(i) = wc;
727     }
728     ws
729 }
730 
731 // #[no_mangle]
732 pub extern "C" fn wprintf(_format: *const ::wchar_t, _ap: va_list) -> ::c_int {
733     unimplemented!();
734 }
735 
736 // #[no_mangle]
737 pub extern "C" fn wscanf(_format: *const ::wchar_t, _ap: va_list) -> ::c_int {
738     unimplemented!();
739 }
740 
741 #[no_mangle]
742 pub extern "C" fn wcscasecmp(mut s1: *const ::wchar_t, mut s2: *const ::wchar_t) -> ::c_int {
743     unsafe {
744         while *s1 != 0 && *s2 != 0 {
745             if towlower(*s1 as ::wint_t) != towlower(*s2 as ::wint_t) {
746                 break;
747             }
748             s1 = s1.add(1);
749             s2 = s2.add(1);
750         }
751         let result = towlower(*s1 as ::wint_t).wrapping_sub(towlower(*s2 as ::wint_t));
752         return result as ::c_int;
753     }
754 }
755 
756 #[no_mangle]
757 pub extern "C" fn wcsncasecmp(mut s1: *const ::wchar_t, mut s2: *const ::wchar_t, n: ::size_t) -> ::c_int {
758     if n == 0 {
759         return 0;
760     }
761     unsafe {
762         for _ in 0..n {
763             if *s1 == 0 || *s2 == 0 || towlower(*s1 as ::wint_t) != towlower(*s2 as ::wint_t) {
764                 return towlower(*s1 as ::wint_t).wrapping_sub(towlower(*s2 as ::wint_t)) as ::c_int;
765             }
766             s1 = s1.add(1);
767             s2 = s2.add(1);
768         }
769         return 0;
770     }
771 }
772