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