Skip to main content

c/
stdio_extra.rs

1// fopencookie, open_wmemstream, strfmon and getdate.
2
3// ============================================================
4// fopencookie
5// ============================================================
6
7#[repr(C)]
8#[derive(Copy, Clone)]
9pub struct CookieIo {
10    pub read: usize,
11    pub write: usize,
12    pub seek: usize,
13    pub close: usize,
14}
15
16#[repr(C)]
17struct CookieFile {
18    cookie: *mut c_void,
19    io: CookieIo,
20}
21
22type CookieReadFn = unsafe extern "C" fn(*mut c_void, *mut c_char, usize) -> isize;
23type CookieWriteFn = unsafe extern "C" fn(*mut c_void, *const c_char, usize) -> isize;
24type CookieSeekFn = unsafe extern "C" fn(*mut c_void, *mut i64, c_int) -> c_int;
25type CookieCloseFn = unsafe extern "C" fn(*mut c_void) -> c_int;
26
27unsafe extern "C" fn cookie_read(f: *mut FILE, buf: *mut u8, len: usize) -> usize {
28    let cf = (*f).cookie as *mut CookieFile;
29    if cf.is_null() || (*cf).io.read == 0 {
30        (*f).flags |= F_ERR;
31        (*f)._err = 1;
32        return 0;
33    }
34    let func: CookieReadFn = core::mem::transmute((*cf).io.read);
35    let n = func((*cf).cookie, buf as *mut c_char, len);
36    if n <= 0 {
37        if n == 0 {
38            (*f).flags |= F_EOF;
39        } else {
40            (*f).flags |= F_ERR;
41            (*f)._err = 1;
42        }
43        return 0;
44    }
45    n as usize
46}
47
48unsafe extern "C" fn cookie_write(f: *mut FILE, buf: *const u8, len: usize) -> usize {
49    let cf = (*f).cookie as *mut CookieFile;
50    if cf.is_null() || (*cf).io.write == 0 { return len; }
51    let func: CookieWriteFn = core::mem::transmute((*cf).io.write);
52    let buffered = if (*f).wbase.is_null() { 0 } else { (*f).wpos as usize - (*f).wbase as usize };
53    if buffered > 0 {
54        let n = func((*cf).cookie, (*f).wbase as *const c_char, buffered);
55        if n != buffered as isize {
56            (*f).wpos = core::ptr::null_mut();
57            (*f).wbase = core::ptr::null_mut();
58            (*f).wend = core::ptr::null_mut();
59            (*f).flags |= F_ERR;
60            (*f).flags |= F_ERR;
61            return 0;
62        }
63        (*f).wbase = (*f).buf;
64        (*f).wpos = (*f).buf;
65        (*f).wend = (*f).buf.add((*f).buf_size);
66    }
67    if len > 0 {
68        let n = func((*cf).cookie, buf as *const c_char, len);
69        if n < 0 { (*f).flags |= F_ERR; return 0; }
70        return n as usize;
71    }
72    0
73}
74
75unsafe extern "C" fn cookie_seek(f: *mut FILE, off: i64, whence: c_int) -> i64 {
76    let cf = (*f).cookie as *mut CookieFile;
77    if cf.is_null() || (*cf).io.seek == 0 {
78        ERRNO = 95; // ENOTSUP
79        return -1;
80    }
81    if whence < 0 || whence > 2 {
82        ERRNO = EINVAL;
83        return -1;
84    }
85    let func: CookieSeekFn = core::mem::transmute((*cf).io.seek);
86    let mut o = off;
87    let r = func((*cf).cookie, &mut o as *mut i64, whence);
88    if r < 0 { return -1; }
89    o
90}
91
92unsafe extern "C" fn cookie_close(f: *mut FILE) -> c_int {
93    let cf = (*f).cookie as *mut CookieFile;
94    if cf.is_null() { return 0; }
95    let mut r = 0;
96    if (*cf).io.close != 0 {
97        let func: CookieCloseFn = core::mem::transmute((*cf).io.close);
98        r = func((*cf).cookie);
99    }
100    crate::free(cf as *mut c_void);
101    (*f).cookie = core::ptr::null_mut();
102    r
103}
104
105#[no_mangle]
106pub unsafe extern "C" fn fopencookie(cookie: *mut c_void, mode: *const c_char, io_funcs: CookieIo) -> *mut FILE {
107    if mode.is_null() { ERRNO = EINVAL; return core::ptr::null_mut(); }
108    let m = *mode;
109    if m != b'r' as c_char && m != b'w' as c_char && m != b'a' as c_char {
110        ERRNO = EINVAL;
111        return core::ptr::null_mut();
112    }
113    let f = crate::calloc(1, core::mem::size_of::<FILE>() + UNGET + BUFSIZ) as *mut FILE;
114    if f.is_null() { ERRNO = ENOMEM; return core::ptr::null_mut(); }
115    let cf = crate::calloc(1, core::mem::size_of::<CookieFile>()) as *mut CookieFile;
116    if cf.is_null() { crate::free(f as *mut c_void); ERRNO = ENOMEM; return core::ptr::null_mut(); }
117    (*cf).cookie = cookie;
118    (*cf).io = io_funcs;
119
120    (*f).fd = -1;
121    (*f).cookie = cf as *mut c_void;
122    (*f).buf = buf_ptr(f);
123    (*f).buf_size = BUFSIZ;
124    (*f).lbf = -1;
125    (*f).read_fn = Some(cookie_read);
126    (*f).write_fn = Some(cookie_write);
127    (*f).seek_fn = Some(cookie_seek);
128    (*f).close = Some(cookie_close);
129    if !crate::strchr(mode as *const u8, b'+' as c_int).is_null() {
130        (*f).flags = 0;
131    } else if m == b'r' as c_char {
132        (*f).flags = F_NOWR;
133    } else {
134        (*f).flags = F_NORD;
135    }
136    if m == b'a' as c_char { (*f).flags |= F_APP; }
137    f
138}
139
140// ============================================================
141// open_wmemstream
142// ============================================================
143
144#[repr(C)]
145struct WmsCookie {
146    bufp: *mut *mut wchar_t,
147    sizep: *mut usize,
148    pos: usize,
149    buf: *mut wchar_t,
150    len: usize,
151    space: usize,
152    // Incremental UTF-8 decoder.
153    need: u8,
154    have: u8,
155    cp: u32,
156}
157
158unsafe fn wms_emit(c: *mut WmsCookie, wc: u32) {
159    if (*c).pos >= (*c).space {
160        let len2 = ((*c).space * 2 + 1) | ((*c).pos + 1);
161        if len2 > (isize::MAX as usize) / 4 { return; }
162        let newbuf = crate::realloc((*c).buf as *mut c_void, len2 * 4) as *mut wchar_t;
163        if newbuf.is_null() { ERRNO = ENOMEM; return; }
164        core::ptr::write_bytes((newbuf as *mut u8).add((*c).space * 4), 0, (len2 - (*c).space) * 4);
165        (*c).buf = newbuf;
166        (*c).space = len2;
167        if !(*c).bufp.is_null() { *(*c).bufp = newbuf; }
168    }
169    *(*c).buf.add((*c).pos) = wc as wchar_t;
170    (*c).pos += 1;
171    if (*c).pos > (*c).len { (*c).len = (*c).pos; }
172    *(*c).buf.add((*c).pos) = 0;
173    if !(*c).sizep.is_null() { *(*c).sizep = (*c).pos; }
174}
175
176unsafe extern "C" fn wms_write(f: *mut FILE, buf: *const u8, len: usize) -> usize {
177    let c = (*f).cookie as *mut WmsCookie;
178    if c.is_null() { return 0; }
179    for i in 0..len {
180        let b = *buf.add(i);
181        if (*c).need == 0 {
182            if b < 0x80 {
183                wms_emit(c, b as u32);
184            } else if b >= 0xc2 && b <= 0xdf {
185                (*c).need = 1;
186                (*c).have = 0;
187                (*c).cp = (b & 0x1f) as u32;
188            } else if b >= 0xe0 && b <= 0xef {
189                (*c).need = 2;
190                (*c).have = 0;
191                (*c).cp = (b & 0x0f) as u32;
192            } else if b >= 0xf0 && b <= 0xf4 {
193                (*c).need = 3;
194                (*c).have = 0;
195                (*c).cp = (b & 0x07) as u32;
196            } else {
197                wms_emit(c, 0xfffd);
198            }
199        } else if b & 0xc0 == 0x80 {
200            (*c).cp = ((*c).cp << 6) | (b & 0x3f) as u32;
201            (*c).have += 1;
202            if (*c).have == (*c).need {
203                wms_emit(c, (*c).cp);
204                (*c).need = 0;
205                (*c).have = 0;
206                (*c).cp = 0;
207            }
208        } else {
209            // Invalid continuation: emit a replacement and treat this byte as
210            // the start of a new sequence.
211            wms_emit(c, 0xfffd);
212            (*c).need = 0;
213            (*c).have = 0;
214            (*c).cp = 0;
215            if b < 0x80 {
216                wms_emit(c, b as u32);
217            } else if b >= 0xc2 && b <= 0xdf {
218                (*c).need = 1;
219                (*c).cp = (b & 0x1f) as u32;
220            } else if b >= 0xe0 && b <= 0xef {
221                (*c).need = 2;
222                (*c).cp = (b & 0x0f) as u32;
223            } else if b >= 0xf0 && b <= 0xf4 {
224                (*c).need = 3;
225                (*c).cp = (b & 0x07) as u32;
226            } else {
227                wms_emit(c, 0xfffd);
228            }
229        }
230    }
231    len
232}
233
234unsafe extern "C" fn wms_seek(f: *mut FILE, off: i64, whence: c_int) -> i64 {
235    let c = (*f).cookie as *mut WmsCookie;
236    if c.is_null() || whence < 0 || whence > 2 {
237        ERRNO = EINVAL;
238        return -1;
239    }
240    let base: i64 = match whence {
241        0 => 0,
242        1 => (*c).pos as i64,
243        _ => (*c).len as i64,
244    };
245    if off < -base || off > (isize::MAX as i64) / 4 - base {
246        ERRNO = EINVAL;
247        return -1;
248    }
249    (*c).pos = (base + off) as usize;
250    (*c).need = 0;
251    (*c).have = 0;
252    (*c).cp = 0;
253    (*c).pos as i64
254}
255
256unsafe extern "C" fn wms_close(_f: *mut FILE) -> c_int {
257    0
258}
259
260#[no_mangle]
261pub unsafe extern "C" fn open_wmemstream(bufp: *mut *mut wchar_t, sizep: *mut usize) -> *mut FILE {
262    if bufp.is_null() || sizep.is_null() { ERRNO = EINVAL; return core::ptr::null_mut(); }
263    let f = crate::calloc(1, core::mem::size_of::<FILE>() + 1) as *mut FILE;
264    if f.is_null() { ERRNO = ENOMEM; return core::ptr::null_mut(); }
265    let c = crate::calloc(1, core::mem::size_of::<WmsCookie>()) as *mut WmsCookie;
266    if c.is_null() { crate::free(f as *mut c_void); ERRNO = ENOMEM; return core::ptr::null_mut(); }
267    let buf = crate::calloc(1, 4) as *mut wchar_t;
268    if buf.is_null() {
269        crate::free(c as *mut c_void);
270        crate::free(f as *mut c_void);
271        ERRNO = ENOMEM;
272        return core::ptr::null_mut();
273    }
274    (*c).bufp = bufp;
275    (*c).sizep = sizep;
276    (*c).buf = buf;
277    (*c).space = 1;
278    *bufp = buf;
279    *sizep = 0;
280    (*f).fd = -1;
281    (*f).cookie = c as *mut c_void;
282    let base = (f as *mut u8).add(core::mem::size_of::<FILE>());
283    (*f).buf = base;
284    (*f).buf_size = 0;
285    (*f).lbf = -1;
286    (*f).flags = F_NORD;
287    (*f).write_fn = Some(wms_write);
288    (*f).seek_fn = Some(wms_seek);
289    (*f).close = Some(wms_close);
290    (*f).mode = 1;
291    f
292}
293
294// ============================================================
295// strfmon / strfmon_l
296// ============================================================
297
298unsafe fn strfmon_impl(mut s: *mut c_char, n: usize, fmt: *const c_char, mut ap: VaList) -> isize {
299    let start = s;
300    let mut cap = n;
301    let mut f = fmt;
302    while cap > 0 && *f != 0 {
303        if *f as u8 != b'%' {
304            *s = *f;
305            s = s.add(1);
306            f = f.add(1);
307            cap -= 1;
308            continue;
309        }
310        f = f.add(1);
311        if *f as u8 == b'%' {
312            *s = *f;
313            s = s.add(1);
314            f = f.add(1);
315            cap -= 1;
316            continue;
317        }
318        let mut fill = b' ' as c_char;
319        let mut nogrp = true; // C locale has no digit grouping
320        let mut negpar = false;
321        let mut nosym = false;
322        let mut left = false;
323        let mut plus = false;
324        loop {
325            match *f as u8 {
326                b'=' => { f = f.add(1); fill = *f; f = f.add(1); }
327                b'^' => { nogrp = true; f = f.add(1); }
328                b'(' => { negpar = true; f = f.add(1); }
329                b'+' => { plus = true; f = f.add(1); }
330                b'!' => { nosym = true; f = f.add(1); }
331                b'-' => { left = true; f = f.add(1); }
332                _ => break,
333            }
334        }
335        let mut fw = 0usize;
336        while *f as u8 >= b'0' && *f as u8 <= b'9' {
337            fw = fw * 10 + (*f as u8 - b'0') as usize;
338            f = f.add(1);
339        }
340        let mut lp = 1usize;
341        if *f as u8 == b'#' {
342            f = f.add(1);
343            lp = 0;
344            while *f as u8 >= b'0' && *f as u8 <= b'9' {
345                lp = lp * 10 + (*f as u8 - b'0') as usize;
346                f = f.add(1);
347            }
348            if lp == 0 { lp = 1; }
349        }
350        let mut rp = 2usize;
351        if *f as u8 == b'.' {
352            rp = 0;
353            f = f.add(1);
354            while *f as u8 >= b'0' && *f as u8 <= b'9' {
355                rp = rp * 10 + (*f as u8 - b'0') as usize;
356                f = f.add(1);
357            }
358        }
359        let _intl = *f as u8 == b'i';
360        if *f != 0 { f = f.add(1); }
361        let x = ap.next_arg::<f64>();
362        let neg = x < 0.0;
363        let ax = if neg { -x } else { x };
364
365        // Render the absolute number without width.
366        let mut num = [0 as c_char; 256];
367        let rn = crate::snprintf(
368            num.as_mut_ptr(),
369            num.len(),
370            b"%.*f\0".as_ptr() as *const c_char,
371            rp as c_int,
372            ax,
373        );
374        let mut nlen = core::cmp::min(rn.max(0) as usize, num.len() - 1);
375
376        // Split integer/fraction parts.
377        let mut dot = nlen;
378        for i in 0..nlen {
379            if num[i] as u8 == b'.' { dot = i; break; }
380        }
381        let mut int_digits = dot;
382        // Minimum integer digits requested with '#<n>'.
383        let zero_pad = if int_digits < lp { lp - int_digits } else { 0 };
384
385        // Group integer digits (only when explicitly enabled; C locale has none).
386        let mut grouped = [0 as c_char; 320];
387        let mut glen = 0usize;
388        if !nogrp && int_digits > 3 {
389            let first = int_digits % 3;
390            let mut i = 0usize;
391            while i < int_digits {
392                if i > 0 && (i - first) % 3 == 0 { grouped[glen] = b',' as c_char; glen += 1; }
393                grouped[glen] = num[i]; glen += 1; i += 1;
394            }
395        } else {
396            for i in 0..int_digits { grouped[glen] = num[i]; glen += 1; }
397        }
398        let _ = &mut int_digits;
399        let _ = &mut nlen;
400
401        // Compose sign/parens + zeros + grouped integer + fraction.
402        let mut core = [0 as c_char; 640];
403        let mut clen = 0usize;
404        let parens = negpar && neg;
405        if parens { core[clen] = b'(' as c_char; clen += 1; }
406        else if neg { core[clen] = b'-' as c_char; clen += 1; }
407        else if plus { core[clen] = b'+' as c_char; clen += 1; }
408        for _ in 0..zero_pad {
409            core[clen] = b'0' as c_char; clen += 1;
410        }
411        for i in 0..glen { core[clen] = grouped[i]; clen += 1; }
412        for i in dot..nlen { core[clen] = num[i]; clen += 1; }
413        if parens { core[clen] = b')' as c_char; clen += 1; }
414
415        let width = if fw > clen { fw } else { clen };
416        let pad = width - clen;
417        if left {
418            for i in 0..clen {
419                if cap <= 1 { break; }
420                *s = core[i]; s = s.add(1); cap -= 1;
421            }
422            for _ in 0..pad {
423                if cap <= 1 { break; }
424                *s = b' ' as c_char; s = s.add(1); cap -= 1;
425            }
426        } else {
427            for _ in 0..pad {
428                if cap <= 1 { break; }
429                *s = fill; s = s.add(1); cap -= 1;
430            }
431            for i in 0..clen {
432                if cap <= 1 { break; }
433                *s = core[i]; s = s.add(1); cap -= 1;
434            }
435        }
436        let _ = nosym;
437    }
438    *s = 0;
439    (s as usize - start as usize) as isize
440}
441
442#[no_mangle]
443pub unsafe extern "C" fn strfmon(s: *mut c_char, n: usize, fmt: *const c_char, args: ...) -> isize {
444    strfmon_impl(s, n, fmt, args)
445}
446
447#[no_mangle]
448pub unsafe extern "C" fn strfmon_l(
449    s: *mut c_char,
450    n: usize,
451    _loc: locale_t,
452    fmt: *const c_char,
453    args: ...,
454) -> isize {
455    strfmon_impl(s, n, fmt, args)
456}
457
458// ============================================================
459// getdate
460// ============================================================
461
462#[no_mangle]
463pub static mut getdate_err: c_int = 0;
464
465static mut GETDATE_TM: crate::tm = crate::tm {
466    tm_sec: 0,
467    tm_min: 0,
468    tm_hour: 0,
469    tm_mday: 0,
470    tm_mon: 0,
471    tm_year: 0,
472    tm_wday: 0,
473    tm_yday: 0,
474    tm_isdst: 0,
475    tm_gmtoff: 0,
476    tm_zone: core::ptr::null(),
477};
478
479#[no_mangle]
480pub unsafe extern "C" fn getdate(s: *const c_char) -> *mut crate::tm {
481    if s.is_null() {
482        getdate_err = 1;
483        return core::ptr::null_mut();
484    }
485    let datemsk = crate::getenv(b"DATEMSK\0".as_ptr() as *const c_char);
486    if datemsk.is_null() {
487        getdate_err = 1;
488        return core::ptr::null_mut();
489    }
490    let f = crate::fopen(datemsk, b"r\0".as_ptr() as *const c_char);
491    if f.is_null() {
492        getdate_err = if ERRNO == ENOMEM { 6 } else { 2 };
493        return core::ptr::null_mut();
494    }
495    let mut fmt = [0 as c_char; 128];
496    let mut found = false;
497    while !crate::fgets(fmt.as_mut_ptr(), fmt.len() as c_int, f).is_null() {
498        let mut end: *mut c_char = core::ptr::null_mut();
499        let p = crate::strptime(s, fmt.as_ptr(), &raw mut GETDATE_TM);
500        if !p.is_null() && *p == 0 {
501            found = true;
502            break;
503        }
504        let _ = &mut end;
505    }
506    if !found && crate::ferror(f) != 0 { getdate_err = 5; }
507    crate::fclose(f);
508    if found {
509        getdate_err = 0;
510        return &raw mut GETDATE_TM;
511    }
512    if getdate_err == 0 { getdate_err = 7; }
513    core::ptr::null_mut()
514}