Skip to main content

relibc/header/stdio/
mod.rs

1//! `stdio.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdio.h.html>.
4
5use alloc::{
6    borrow::{Borrow, BorrowMut},
7    boxed::Box,
8    vec::Vec,
9};
10use core::{
11    cmp,
12    ffi::VaList as va_list,
13    fmt::{self, Write as WriteFmt},
14    mem,
15    ops::{Deref, DerefMut},
16    ptr, slice, str,
17};
18
19use crate::{
20    byte_literal::ByteLiteral,
21    c_str::{CStr, Thin},
22    c_vec::CVec,
23    error::{ResultExt, ResultExtPtrMut},
24    fs::File,
25    header::{
26        errno::{self, STR_ERROR},
27        fcntl,
28        pthread::RlctMutex,
29        pwd, stdlib,
30        string::{self, strlen, strncpy},
31        unistd,
32    },
33    io::{self, BufRead, BufWriter, LineWriter, Read, Write},
34    out::Out,
35    platform::{
36        self, ERRNO, Pal, Sys, WriteByte,
37        types::{c_char, c_int, c_long, c_uint, c_ulonglong, c_void, off_t, size_t},
38    },
39};
40use reader::Reader;
41
42pub use self::constants::*;
43pub use crate::header::bits_fcntl::{SEEK_CUR, SEEK_END, SEEK_SET};
44mod constants;
45
46pub use self::default::*;
47mod default;
48
49pub use self::getdelim::*;
50mod getdelim;
51
52mod ext;
53mod helpers;
54pub mod printf;
55pub mod reader;
56pub mod scanf;
57static mut TMPNAM_BUF: [c_char; L_tmpnam as usize + 1] = [0; L_tmpnam as usize + 1];
58
59enum Buffer<'a> {
60    Borrowed(&'a mut [u8]),
61    Owned(Vec<u8>),
62}
63
64impl<'a> Deref for Buffer<'a> {
65    type Target = [u8];
66
67    fn deref(&self) -> &Self::Target {
68        match self {
69            Buffer::Borrowed(inner) => inner,
70            Buffer::Owned(inner) => inner.borrow(),
71        }
72    }
73}
74
75impl<'a> DerefMut for Buffer<'a> {
76    fn deref_mut(&mut self) -> &mut Self::Target {
77        match self {
78            Buffer::Borrowed(inner) => inner,
79            Buffer::Owned(inner) => inner.borrow_mut(),
80        }
81    }
82}
83
84pub trait Pending {
85    fn pending(&self) -> size_t;
86}
87
88impl<W: crate::io::Write> Pending for BufWriter<W> {
89    fn pending(&self) -> size_t {
90        self.buf.len() as size_t
91    }
92}
93
94impl<W: crate::io::Write> Pending for LineWriter<W> {
95    fn pending(&self) -> size_t {
96        self.inner.buf.len() as size_t
97    }
98}
99
100pub trait Writer: Write + Pending {
101    fn purge(&mut self);
102}
103
104impl<W: crate::io::Write> Writer for BufWriter<W> {
105    fn purge(&mut self) {
106        self.buf.clear();
107    }
108}
109
110impl<W: crate::io::Write> Writer for LineWriter<W> {
111    fn purge(&mut self) {
112        self.inner.buf.clear();
113    }
114}
115
116/// This struct gets exposed to the C API.
117pub struct FILE {
118    lock: RlctMutex,
119
120    file: File,
121    // pub for stdio_ext
122    pub(crate) flags: c_int,
123
124    // TODO: Is the read_buf dropped?
125    read_buf: Buffer<'static>,
126
127    read_pos: usize,
128    read_size: usize,
129    unget: Vec<u8>,
130    // pub for stdio_ext
131
132    // TODO: To support const fn initialization, use static dispatch (perhaps partially)?
133    pub(crate) writer: Box<dyn Writer + Send>,
134
135    // Optional pid for use with popen/pclose
136    pid: Option<c_int>,
137
138    // wchar support
139    pub(crate) orientation: c_int,
140}
141
142impl Read for FILE {
143    fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
144        let unget_read_size = cmp::min(out.len(), self.unget.len());
145        for inner in out.iter_mut().take(unget_read_size) {
146            *inner = self.unget.pop().unwrap();
147        }
148        if unget_read_size != 0 {
149            return Ok(unget_read_size);
150        }
151
152        let len = {
153            let buf = self.fill_buf()?;
154            let len = buf.len().min(out.len());
155
156            out[..len].copy_from_slice(&buf[..len]);
157            len
158        };
159        self.consume(len);
160        Ok(len)
161    }
162}
163
164impl BufRead for FILE {
165    fn fill_buf(&mut self) -> io::Result<&[u8]> {
166        if self.read_pos == self.read_size {
167            self.read_size = match self.file.read(&mut self.read_buf) {
168                Ok(0) => {
169                    self.flags |= F_EOF;
170                    0
171                }
172                Ok(n) => n,
173                Err(err) => {
174                    self.flags |= F_ERR;
175                    return Err(err);
176                }
177            };
178            self.read_pos = 0;
179        }
180        Ok(&self.read_buf[self.read_pos..self.read_size])
181    }
182    fn consume(&mut self, i: usize) {
183        self.read_pos = (self.read_pos + i).min(self.read_size);
184    }
185}
186
187impl Write for FILE {
188    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
189        match self.writer.write(buf) {
190            Ok(n) => Ok(n),
191            Err(err) => {
192                self.flags |= F_ERR;
193                Err(err)
194            }
195        }
196    }
197    fn flush(&mut self) -> io::Result<()> {
198        match self.writer.flush() {
199            Ok(()) => Ok(()),
200            Err(err) => {
201                self.flags |= F_ERR;
202                Err(err)
203            }
204        }
205    }
206}
207
208impl WriteFmt for FILE {
209    fn write_str(&mut self, s: &str) -> fmt::Result {
210        self.write_all(s.as_bytes()).map_err(|_| fmt::Error)
211    }
212}
213
214impl WriteByte for FILE {
215    fn write_u8(&mut self, c: u8) -> fmt::Result {
216        self.write_all(&[c]).map_err(|_| fmt::Error)
217    }
218}
219
220impl FILE {
221    pub fn lock(&mut self) -> LockGuard<'_> {
222        unsafe {
223            flockfile(self);
224        }
225        LockGuard(self)
226    }
227
228    pub fn try_set_orientation(&mut self, mode: c_int) -> c_int {
229        let stream = self.lock();
230        stream.0.try_set_orientation_unlocked(mode)
231    }
232
233    pub fn try_set_orientation_unlocked(&mut self, mode: c_int) -> c_int {
234        if self.orientation == 0 {
235            self.orientation = match mode {
236                1..=i32::MAX => 1,
237                i32::MIN..=-1 => -1,
238                0 => self.orientation,
239            };
240        }
241        self.orientation
242    }
243
244    pub fn try_set_byte_orientation_unlocked(&mut self) -> core::result::Result<(), c_int> {
245        match self.try_set_orientation_unlocked(-1) {
246            i32::MIN..=-1 => Ok(()),
247            x => Err(x),
248        }
249    }
250
251    pub fn try_set_wide_orientation_unlocked(&mut self) -> core::result::Result<(), c_int> {
252        match self.try_set_orientation_unlocked(1) {
253            1..=i32::MAX => Ok(()),
254            x => Err(x),
255        }
256    }
257
258    pub fn purge(&mut self) {
259        // Purge read buffer
260        self.read_pos = 0;
261        self.read_size = 0;
262        // Purge unget
263        self.unget.clear();
264        // Purge write buffer
265        self.writer.purge();
266    }
267}
268
269pub struct LockGuard<'a>(&'a mut FILE);
270
271impl<'a> Deref for LockGuard<'a> {
272    type Target = FILE;
273
274    fn deref(&self) -> &Self::Target {
275        self.0
276    }
277}
278
279impl<'a> DerefMut for LockGuard<'a> {
280    fn deref_mut(&mut self) -> &mut Self::Target {
281        self.0
282    }
283}
284
285impl<'a> Drop for LockGuard<'a> {
286    fn drop(&mut self) {
287        unsafe {
288            funlockfile(self.0);
289        }
290    }
291}
292
293/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/clearerr.html>.
294///
295/// Clears EOF and ERR indicators on a stream
296#[unsafe(no_mangle)]
297pub unsafe extern "C" fn clearerr(stream: *mut FILE) {
298    let mut stream = unsafe { (*stream).lock() };
299    stream.flags &= !(F_EOF | F_ERR);
300}
301
302/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ctermid.html>.
303#[unsafe(no_mangle)]
304pub unsafe extern "C" fn ctermid(s: *mut c_char) -> *mut c_char {
305    static mut TERMID: [u8; L_ctermid] = *b"/dev/tty\0";
306
307    if s.is_null() {
308        return (&raw mut TERMID).cast::<c_char>();
309    }
310
311    unsafe { strncpy(s, (&raw mut TERMID).cast::<c_char>(), L_ctermid) }
312}
313
314/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/cuserid.html>
315///
316/// Marked legacy in SUS Version 2.
317// #[unsafe(no_mangle)]
318#[deprecated]
319pub unsafe extern "C" fn cuserid(s: *mut c_char) -> *mut c_char {
320    let mut buf: Vec<c_char> = vec![0; 256];
321    let mut pwd: pwd::passwd = unsafe { mem::zeroed() };
322    let mut pwdbuf: *mut pwd::passwd = unsafe { mem::zeroed() };
323    if !s.is_null() {
324        unsafe {
325            *s.add(0) = 0;
326        }
327    }
328    unsafe {
329        pwd::getpwuid_r(
330            unistd::geteuid(),
331            &raw mut pwd,
332            buf.as_mut_ptr(),
333            buf.len(),
334            &raw mut pwdbuf,
335        )
336    };
337    if pwdbuf.is_null() {
338        return s;
339    }
340
341    if !s.is_null() {
342        unsafe { strncpy(s, (*pwdbuf).pw_name, unistd::L_cuserid) };
343        return s;
344    }
345
346    unsafe { (*pwdbuf).pw_name }
347}
348
349/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fclose.html>.
350///
351/// Close a file
352/// This function does not guarentee that the file buffer will be flushed or that the file
353/// descriptor will be closed, so if it is important that the file be written to, use `fflush()`
354/// prior to using this function.
355#[unsafe(no_mangle)]
356pub unsafe extern "C" fn fclose(stream: *mut FILE) -> c_int {
357    let stream = unsafe { &mut *stream };
358    unsafe { flockfile(stream) };
359
360    let mut r = stream.flush().is_err();
361    // TODO: better error handling
362    let close = Sys::close(*stream.file).map(|()| 0).or_minus_one_errno() == -1;
363    r = r || close;
364
365    if stream.flags & constants::F_PERM == 0 {
366        // Not one of stdin, stdout or stderr
367        let mut stream = unsafe { Box::from_raw(stream) };
368        // Reference files aren't closed on drop, so pretend to be a reference
369        stream.file.reference = true;
370    } else {
371        unsafe { funlockfile(stream) };
372    }
373
374    c_int::from(r)
375}
376
377/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdopen.html>.
378///
379/// Open a file from a file descriptor
380#[unsafe(no_mangle)]
381pub unsafe extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE {
382    helpers::_fdopen(fildes, unsafe { CStr::from_ptr(mode) })
383        .map(Box::into_raw)
384        .or_errno_null_mut()
385}
386
387/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feof.html>.
388///
389/// Check for EOF
390#[unsafe(no_mangle)]
391pub unsafe extern "C" fn feof(stream: *mut FILE) -> c_int {
392    let stream = unsafe { (*stream).lock() };
393    stream.flags & F_EOF
394}
395
396/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ferror.html>.
397///
398/// Check for ERR
399#[unsafe(no_mangle)]
400pub unsafe extern "C" fn ferror(stream: *mut FILE) -> c_int {
401    let stream = unsafe { (*stream).lock() };
402    stream.flags & F_ERR
403}
404
405/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fflush.html>.
406///
407/// Flush output to stream, or sync read position
408/// Ensure the file is unlocked before calling this function, as it will attempt to lock the file
409/// itself.
410#[unsafe(no_mangle)]
411pub unsafe extern "C" fn fflush(stream: *mut FILE) -> c_int {
412    if stream.is_null() {
413        //TODO: flush all files!
414
415        if unsafe { fflush(stdout) } != 0 {
416            return EOF;
417        }
418
419        if unsafe { fflush(stderr) } != 0 {
420            return EOF;
421        }
422    } else {
423        let mut stream = unsafe { (*stream).lock() };
424        if stream.flush().is_err() {
425            return EOF;
426        }
427    }
428
429    0
430}
431
432/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fgetc.html>.
433///
434/// Get a single char from a stream
435#[unsafe(no_mangle)]
436pub unsafe extern "C" fn fgetc(stream: *mut FILE) -> c_int {
437    let mut stream = unsafe { (*stream).lock() };
438    if (*stream).try_set_byte_orientation_unlocked().is_err() {
439        return -1;
440    }
441
442    unsafe { getc_unlocked(&raw mut *stream) }
443}
444
445/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fgetpos.html>.
446///
447/// Get the position of the stream and store it in pos
448#[unsafe(no_mangle)]
449pub unsafe extern "C" fn fgetpos(stream: *mut FILE, pos: *mut fpos_t) -> c_int {
450    let off = unsafe { ftello(stream) };
451    if off < 0 {
452        return -1;
453    }
454    unsafe { *pos = off };
455    0
456}
457
458/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fgets.html>.
459///
460/// Get a string from the stream
461#[unsafe(no_mangle)]
462pub unsafe extern "C" fn fgets(
463    original: *mut c_char,
464    max: c_int,
465    stream: *mut FILE,
466) -> *mut c_char {
467    let mut stream = unsafe { (*stream).lock() };
468    if (*stream).try_set_byte_orientation_unlocked().is_err() {
469        return ptr::null_mut();
470    }
471
472    let mut out = original;
473    let max = max as usize;
474    let mut left = max.saturating_sub(1); // Make space for the terminating NUL-byte
475    let mut wrote = false;
476
477    if left >= 1 {
478        let unget_read_size = cmp::min(left, stream.unget.len());
479        for _ in 0..unget_read_size {
480            unsafe { *out = stream.unget.pop().unwrap() as c_char };
481            out = unsafe { out.offset(1) };
482        }
483        left -= unget_read_size;
484    }
485
486    loop {
487        if left == 0 {
488            break;
489        }
490
491        // TODO: When NLL is a thing, this block can be flattened out
492        let (read, exit) = {
493            let buf = match stream.fill_buf() {
494                Ok(buf) => buf,
495                Err(_) => return ptr::null_mut(),
496            };
497            if buf.is_empty() {
498                break;
499            }
500            wrote = true;
501            let len = buf.len().min(left);
502
503            let newline = buf[..len].iter().position(|&c| c == b'\n');
504            let len = newline.map(|i| i + 1).unwrap_or(len);
505
506            unsafe { ptr::copy_nonoverlapping(buf.as_ptr(), out.cast::<u8>(), len) };
507
508            (len, newline.is_some())
509        };
510
511        stream.consume(read);
512
513        out = unsafe { out.add(read) };
514        left -= read;
515
516        if exit {
517            break;
518        }
519    }
520
521    if max >= 1 {
522        // Write the NUL byte
523        unsafe { *out = 0 };
524    }
525    if wrote { original } else { ptr::null_mut() }
526}
527
528/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fileno.html>.
529///
530/// Get the underlying file descriptor
531#[unsafe(no_mangle)]
532pub unsafe extern "C" fn fileno(stream: *mut FILE) -> c_int {
533    let stream = unsafe { (*stream).lock() };
534    *stream.file
535}
536
537/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/flockfile.html>.
538///
539/// Lock the file
540/// Do not call any functions other than those with the `_unlocked` postfix while the file is
541/// locked
542#[unsafe(no_mangle)]
543pub unsafe extern "C" fn flockfile(file: *mut FILE) {
544    if let Err(e) = unsafe { (*file).lock.lock() } {
545        todo_error!(0, e, "flockfile error")
546    }
547}
548
549/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fopen.html>.
550///
551/// Open the file in mode `mode`
552#[unsafe(no_mangle)]
553pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE {
554    let initial_mode = unsafe { *mode };
555    if initial_mode != ByteLiteral::cast_cchar(b'r')
556        && initial_mode != ByteLiteral::cast_cchar(b'w')
557        && initial_mode != ByteLiteral::cast_cchar(b'a')
558    {
559        platform::ERRNO.set(errno::EINVAL);
560        return ptr::null_mut();
561    }
562
563    let flags = helpers::parse_mode_flags(unsafe { CStr::from_ptr(mode) });
564
565    let new_mode = if flags & fcntl::O_CREAT == fcntl::O_CREAT {
566        0o666
567    } else {
568        0
569    };
570
571    let fd = unsafe { fcntl::open(filename, flags, new_mode) };
572    if fd < 0 {
573        return ptr::null_mut();
574    }
575
576    if flags & fcntl::O_CLOEXEC > 0 {
577        unsafe { fcntl::fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC as c_ulonglong) };
578    }
579
580    helpers::_fdopen(fd, unsafe { CStr::from_ptr(mode) })
581        .map(Box::into_raw)
582        .inspect_err(|err| {
583            // TODO: guard type
584            if let Ok(()) = Sys::close(fd) {}; // TODO handle error
585        })
586        .or_errno_null_mut()
587}
588
589/// See <https://www.man7.org/linux/man-pages/man3/fpurge.3.html>.
590///
591/// Non-POSIX. From Solaris.
592///
593/// Clear the buffers of a stream
594/// Ensure the file is unlocked before calling this function, as it will attempt to lock the file
595/// itself.
596#[unsafe(no_mangle)]
597pub unsafe extern "C" fn __fpurge(stream: *mut FILE) {
598    if !stream.is_null() {
599        let mut stream = unsafe { (*stream).lock() };
600        stream.purge();
601    }
602}
603
604/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fputc.html>.
605///
606/// Insert a character into the stream
607#[unsafe(no_mangle)]
608pub unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int {
609    let mut stream = unsafe { (*stream).lock() };
610    if (*stream).try_set_byte_orientation_unlocked().is_err() {
611        return -1;
612    }
613
614    unsafe { putc_unlocked(c, &raw mut *stream) }
615}
616
617/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fputs.html>.
618///
619/// Insert a string into a stream
620#[unsafe(no_mangle)]
621pub unsafe extern "C" fn fputs(s: *const c_char, stream: *mut FILE) -> c_int {
622    let mut stream = unsafe { (*stream).lock() };
623    if (*stream).try_set_byte_orientation_unlocked().is_err() {
624        return -1;
625    }
626
627    let buf = unsafe { slice::from_raw_parts(s as *mut u8, strlen(s)) };
628
629    if stream.write_all(buf).is_ok() { 0 } else { -1 }
630}
631
632/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fread.html>.
633///
634/// Read `nitems` of size `size` into `ptr` from `stream`
635#[unsafe(no_mangle)]
636pub unsafe extern "C" fn fread(
637    ptr: *mut c_void,
638    size: size_t,
639    nitems: size_t,
640    stream: *mut FILE,
641) -> size_t {
642    if size == 0 || nitems == 0 {
643        return 0;
644    }
645
646    let mut stream = unsafe { (*stream).lock() };
647    if (*stream).try_set_byte_orientation_unlocked().is_err() {
648        return 0;
649    }
650
651    let buf = unsafe { slice::from_raw_parts_mut(ptr.cast::<u8>(), size * nitems) };
652    let mut read = 0;
653    while read < buf.len() {
654        match stream.read(&mut buf[read..]) {
655            Ok(0) | Err(_) => break,
656            Ok(n) => read += n,
657        }
658    }
659    (read / size) as size_t
660}
661
662/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/freopen.html>.
663#[unsafe(no_mangle)]
664pub unsafe extern "C" fn freopen(
665    filename: *const c_char,
666    mode: *const c_char,
667    stream: &mut FILE,
668) -> *mut FILE {
669    let mut flags = helpers::parse_mode_flags(unsafe { CStr::from_ptr(mode) });
670    unsafe { flockfile(stream) };
671
672    let _ = stream.flush();
673    if filename.is_null() {
674        // Reopen stream in new mode
675        if flags & fcntl::O_CLOEXEC > 0 {
676            unsafe {
677                fcntl::fcntl(
678                    *stream.file,
679                    fcntl::F_SETFD,
680                    fcntl::FD_CLOEXEC as c_ulonglong,
681                )
682            };
683        }
684        flags &= !(fcntl::O_CREAT | fcntl::O_EXCL | fcntl::O_CLOEXEC);
685        if unsafe { fcntl::fcntl(*stream.file, fcntl::F_SETFL, flags as c_ulonglong) } < 0 {
686            unsafe { funlockfile(stream) };
687            unsafe { fclose(stream) };
688            return ptr::null_mut();
689        }
690    } else {
691        let new = unsafe { fopen(filename, mode) };
692        if new.is_null() {
693            unsafe { funlockfile(stream) };
694            unsafe { fclose(stream) };
695            return ptr::null_mut();
696        }
697        let new = unsafe { &mut *new }; // Should be safe, new is not null
698        if *new.file == *stream.file {
699            new.file.fd = -1;
700        } else if Sys::dup2(*new.file, *stream.file).or_minus_one_errno() == -1
701            || unsafe {
702                fcntl::fcntl(
703                    *stream.file,
704                    fcntl::F_SETFL,
705                    (flags & fcntl::O_CLOEXEC) as c_ulonglong,
706                )
707            } < 0
708        {
709            unsafe { funlockfile(stream) };
710            unsafe { fclose(new) };
711            unsafe { fclose(stream) };
712            return ptr::null_mut();
713        }
714        stream.flags = (stream.flags & constants::F_PERM) | new.flags;
715        unsafe { fclose(new) };
716    }
717    stream.orientation = 0;
718    unsafe { funlockfile(stream) };
719    stream
720}
721
722/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fseek.html>.
723///
724/// Seek to an offset `offset` from `whence`
725#[unsafe(no_mangle)]
726pub unsafe extern "C" fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int {
727    unsafe { fseeko(stream, offset as off_t, whence) }
728}
729
730/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fseek.html>.
731///
732/// Seek to an offset `offset` from `whence`
733#[unsafe(no_mangle)]
734pub unsafe extern "C" fn fseeko(stream: *mut FILE, off: off_t, whence: c_int) -> c_int {
735    let mut stream = unsafe { (*stream).lock() };
736    unsafe { fseek_locked(&mut stream, off, whence) }
737}
738
739pub unsafe fn fseek_locked(stream: &mut FILE, mut off: off_t, whence: c_int) -> c_int {
740    if whence == SEEK_CUR {
741        // Since it's a buffered writer, our actual cursor isn't where the user
742        // thinks
743        off -= (stream.read_size - stream.read_pos) as off_t;
744    }
745
746    // Flush write buffer before seek
747    if stream.flush().is_err() {
748        return -1;
749    }
750
751    let err = Sys::lseek(*stream.file, off, whence).or_minus_one_errno();
752    if err < 0 {
753        return err as c_int;
754    }
755
756    stream.flags &= !(F_EOF | F_ERR);
757    stream.read_pos = 0;
758    stream.read_size = 0;
759    stream.unget = Vec::new();
760    0
761}
762
763/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fsetpos.html>.
764///
765/// Seek to a position `pos` in the file from the beginning of the file
766#[unsafe(no_mangle)]
767pub unsafe extern "C" fn fsetpos(stream: *mut FILE, pos: *const fpos_t) -> c_int {
768    unsafe { fseeko(stream, *pos, SEEK_SET) }
769}
770
771/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ftell.html>.
772///
773/// Get the current position of the cursor in the file
774#[unsafe(no_mangle)]
775pub unsafe extern "C" fn ftell(stream: *mut FILE) -> c_long {
776    unsafe { ftello(stream) as c_long }
777}
778
779/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ftell.html>.
780///
781/// Get the current position of the cursor in the file
782#[unsafe(no_mangle)]
783pub unsafe extern "C" fn ftello(stream: *mut FILE) -> off_t {
784    let mut stream = unsafe { (*stream).lock() };
785    unsafe { ftell_locked(&mut stream) }
786}
787
788pub unsafe extern "C" fn ftell_locked(stream: &mut FILE) -> off_t {
789    let pos = Sys::lseek(*stream.file, 0, SEEK_CUR).or_minus_one_errno();
790    if pos < 0 {
791        return -1;
792    }
793
794    // Adjust for read buffer, ungetc, and write buffer
795    pos - (stream.read_size - stream.read_pos) as off_t - stream.unget.len() as off_t
796        + stream.writer.pending() as off_t
797}
798
799/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/flockfile.html>.
800///
801/// Try to lock the file. Returns 0 for success, 1 for failure
802#[unsafe(no_mangle)]
803pub unsafe extern "C" fn ftrylockfile(file: *mut FILE) -> c_int {
804    if unsafe { (*file).lock.try_lock() }.is_ok() {
805        0
806    } else {
807        1
808    }
809}
810
811/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/flockfile.html>.
812///
813/// Unlock the file
814#[unsafe(no_mangle)]
815pub unsafe extern "C" fn funlockfile(file: *mut FILE) {
816    if let Err(e) = unsafe { (*file).lock.unlock() } {
817        todo_error!(0, e, "RELIBC: funlockfile error")
818    }
819}
820
821/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fwrite.html>.
822///
823/// Write `nitems` of size `size` from `ptr` to `stream`
824#[unsafe(no_mangle)]
825pub unsafe extern "C" fn fwrite(
826    ptr: *const c_void,
827    size: size_t,
828    nitems: size_t,
829    stream: *mut FILE,
830) -> size_t {
831    if size == 0 || nitems == 0 {
832        return 0;
833    }
834    let mut stream = unsafe { (*stream).lock() };
835    if (*stream).try_set_byte_orientation_unlocked().is_err() {
836        return 0;
837    }
838
839    let buf = unsafe { slice::from_raw_parts(ptr.cast::<u8>(), size * nitems) };
840    let mut written = 0;
841    while written < buf.len() {
842        match stream.write(&buf[written..]) {
843            Ok(0) | Err(_) => break,
844            Ok(n) => written += n,
845        }
846    }
847    (written / size) as size_t
848}
849
850/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getc.html>.
851///
852/// Get a single char from a stream
853#[unsafe(no_mangle)]
854pub unsafe extern "C" fn getc(stream: *mut FILE) -> c_int {
855    let mut stream = unsafe { (*stream).lock() };
856    unsafe { getc_unlocked(&raw mut *stream) }
857}
858
859/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getchar.html>.
860///
861/// Get a single char from `stdin`
862#[unsafe(no_mangle)]
863pub unsafe extern "C" fn getchar() -> c_int {
864    unsafe { fgetc(&raw mut *stdin) }
865}
866
867/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getc_unlocked.html>.
868///
869/// Get a char from a stream without locking the stream
870#[unsafe(no_mangle)]
871pub unsafe extern "C" fn getc_unlocked(stream: *mut FILE) -> c_int {
872    if unsafe { (*stream).try_set_byte_orientation_unlocked() }.is_err() {
873        return -1;
874    }
875
876    let mut buf = [0];
877
878    match unsafe { (*stream).read(&mut buf) } {
879        Ok(0) | Err(_) => EOF,
880        Ok(_) => c_int::from(buf[0]),
881    }
882}
883
884/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getc_unlocked.html>.
885///
886/// Get a char from `stdin` without locking `stdin`
887#[unsafe(no_mangle)]
888pub unsafe extern "C" fn getchar_unlocked() -> c_int {
889    unsafe { getc_unlocked(&raw mut *stdin) }
890}
891
892/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/gets.html>.
893///
894/// Marked obsolescent in issue 7.
895/// `fgets` is recommended instead, which is what this implementation calls.
896///
897/// Get a string from `stdin`
898#[deprecated]
899#[unsafe(no_mangle)]
900pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char {
901    unsafe { fgets(s, c_int::MAX, &raw mut *stdin) }
902}
903
904/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/getw.html>.
905///
906/// Was marked legacy and removed in issue 6.
907///
908/// Get an integer from `stream`
909#[deprecated]
910#[unsafe(no_mangle)]
911pub unsafe extern "C" fn getw(stream: *mut FILE) -> c_int {
912    let mut ret: c_int = 0;
913    if unsafe {
914        fread(
915            ptr::from_mut(&mut ret).cast::<c_void>(),
916            mem::size_of_val(&ret),
917            1,
918            stream,
919        )
920    } > 0
921    {
922        ret
923    } else {
924        -1
925    }
926}
927
928/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pclose.html>.
929#[unsafe(no_mangle)]
930pub unsafe extern "C" fn pclose(stream: *mut FILE) -> c_int {
931    // TODO: rusty error handling?
932    let pid = {
933        let mut stream = unsafe { (*stream).lock() };
934
935        if let Some(pid) = stream.pid.take() {
936            pid
937        } else {
938            ERRNO.set(errno::ECHILD);
939            return -1;
940        }
941    };
942
943    unsafe { fclose(stream) };
944
945    let mut wstatus = 0;
946    if Sys::waitpid(pid, Some(Out::from_mut(&mut wstatus)), 0).or_minus_one_errno() == -1 {
947        return -1;
948    }
949
950    wstatus
951}
952
953/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/perror.html>.
954#[unsafe(no_mangle)]
955pub unsafe extern "C" fn perror(s: *const c_char) {
956    let err = ERRNO.get();
957    let err_str = if err >= 0 && err < STR_ERROR.len() as c_int {
958        STR_ERROR[err as usize]
959    } else {
960        "Unknown error"
961    };
962    let mut w = platform::FileWriter::new(2);
963
964    // The prefix, `s`, is optional (empty or NULL) according to the spec
965    match unsafe { CStr::from_nullable_ptr(s) }
966        .and_then(|s_cstr| str::from_utf8(s_cstr.to_bytes()).ok())
967    {
968        Some(s_str) if !s_str.is_empty() => w
969            .write_fmt(format_args!("{}: {}\n", s_str, err_str))
970            .unwrap(),
971        _ => w.write_fmt(format_args!("{}\n", err_str)).unwrap(),
972    }
973}
974
975/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/popen.html>.
976#[unsafe(no_mangle)]
977pub unsafe extern "C" fn popen(command: *const c_char, mode: *const c_char) -> *mut FILE {
978    //TODO: share code with system
979
980    let mode = unsafe { CStr::from_ptr(mode) };
981
982    let mut cloexec = false;
983    let mut write_opt = None;
984    for b in mode.to_bytes().iter() {
985        match b {
986            b'e' => cloexec = true,
987            b'r' if write_opt.is_none() => write_opt = Some(false),
988            b'w' if write_opt.is_none() => write_opt = Some(true),
989            _ => {
990                ERRNO.set(errno::EINVAL);
991                return ptr::null_mut();
992            }
993        }
994    }
995
996    let write = match write_opt {
997        Some(some) => some,
998        None => {
999            ERRNO.set(errno::EINVAL);
1000            return ptr::null_mut();
1001        }
1002    };
1003
1004    let mut pipes = [-1, -1];
1005    if unsafe { unistd::pipe(pipes.as_mut_ptr()) } != 0 {
1006        return ptr::null_mut();
1007    }
1008
1009    let child_pid = unsafe { unistd::fork() };
1010    if child_pid == 0 {
1011        let command_nonnull = if command.is_null() {
1012            c"exit 0".as_ptr()
1013        } else {
1014            command.cast::<c_char>()
1015        };
1016
1017        let shell = c"/bin/sh".as_ptr();
1018
1019        let args = [c"sh".as_ptr(), c"-c".as_ptr(), command_nonnull, ptr::null()];
1020
1021        // Setup up stdin or stdout
1022        //TODO: dup errors are ignored, should they be?
1023        {
1024            if write {
1025                match unistd::dup2(pipes[0], 0) {
1026                    0 => {}
1027                    e => unsafe { stdlib::exit(127) },
1028                }
1029            } else {
1030                match unistd::dup2(pipes[1], 1) {
1031                    1 => {}
1032                    e => unsafe { stdlib::exit(127) },
1033                }
1034            }
1035
1036            unistd::close(pipes[0]);
1037            unistd::close(pipes[1]);
1038        }
1039
1040        unsafe { unistd::execv(shell.cast::<c_char>(), args.as_ptr().cast::<*mut c_char>()) };
1041
1042        unsafe { stdlib::exit(127) };
1043
1044        unreachable!();
1045    } else if child_pid > 0 {
1046        let (fd, fd_mode): (_, CStr) = if write {
1047            unistd::close(pipes[0]);
1048            (pipes[1], if cloexec { c"we".into() } else { c"w".into() })
1049        } else {
1050            unistd::close(pipes[1]);
1051            (pipes[0], if cloexec { c"re".into() } else { c"r".into() })
1052        };
1053
1054        helpers::_fdopen(fd, fd_mode)
1055            .map(|mut f| {
1056                f.pid = Some(child_pid);
1057                Box::into_raw(f)
1058            })
1059            .or_errno_null_mut()
1060    } else {
1061        ptr::null_mut()
1062    }
1063}
1064
1065/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/putc.html>.
1066///
1067/// Put a character `c` into `stream`
1068#[unsafe(no_mangle)]
1069pub unsafe extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int {
1070    let mut stream = unsafe { (*stream).lock() };
1071    unsafe { putc_unlocked(c, &raw mut *stream) }
1072}
1073
1074/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/putchar.html>.
1075///
1076/// Put a character `c` into `stdout`
1077#[unsafe(no_mangle)]
1078pub unsafe extern "C" fn putchar(c: c_int) -> c_int {
1079    unsafe { fputc(c, &raw mut *stdout) }
1080}
1081
1082/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getc_unlocked.html>.
1083///
1084/// Put a character `c` into `stream` without locking `stream`
1085#[unsafe(no_mangle)]
1086pub unsafe extern "C" fn putc_unlocked(c: c_int, stream: *mut FILE) -> c_int {
1087    if unsafe { (*stream).try_set_byte_orientation_unlocked() }.is_err() {
1088        return -1;
1089    }
1090
1091    match unsafe { (*stream).write(&[c as u8]) } {
1092        Ok(0) | Err(_) => EOF,
1093        Ok(_) => c,
1094    }
1095}
1096
1097/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getc_unlocked.html>.
1098///
1099/// Put a character `c` into `stdout` without locking `stdout`
1100#[unsafe(no_mangle)]
1101pub unsafe extern "C" fn putchar_unlocked(c: c_int) -> c_int {
1102    unsafe { putc_unlocked(c, stdout) }
1103}
1104
1105/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/puts.html>.
1106///
1107/// Put a string `s` into `stdout`
1108#[unsafe(no_mangle)]
1109pub unsafe extern "C" fn puts(s: *const c_char) -> c_int {
1110    let mut stream = unsafe { (&mut *stdout).lock() };
1111    if (*stream).try_set_byte_orientation_unlocked().is_err() {
1112        return -1;
1113    }
1114
1115    let buf = unsafe { slice::from_raw_parts(s as *mut u8, strlen(s)) };
1116
1117    if stream.write_all(buf).is_err() {
1118        return -1;
1119    }
1120    if stream.write(b"\n").is_err() {
1121        return -1;
1122    }
1123    0
1124}
1125
1126/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/putw.html>.
1127///
1128/// Marked legacy in SUS Version 2.
1129///
1130/// Put an integer `w` into `stream`
1131#[deprecated]
1132#[unsafe(no_mangle)]
1133pub unsafe extern "C" fn putw(w: c_int, stream: *mut FILE) -> c_int {
1134    (unsafe {
1135        fwrite(
1136            ptr::from_ref::<c_int>(&w).cast(),
1137            mem::size_of_val(&w),
1138            1,
1139            stream,
1140        )
1141    }) as i32
1142        - 1
1143}
1144
1145/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/remove.html>.
1146///
1147/// Delete file or directory `path`
1148#[unsafe(no_mangle)]
1149pub unsafe extern "C" fn remove(path: *const c_char) -> c_int {
1150    let path = unsafe { CStr::from_ptr(path) };
1151    Sys::unlink(path)
1152        .or_else(|_err| Sys::rmdir(path))
1153        .map(|()| 0)
1154        .or_minus_one_errno()
1155}
1156
1157/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/rename.html>.
1158#[unsafe(no_mangle)]
1159pub unsafe extern "C" fn rename(oldpath: *const c_char, newpath: *const c_char) -> c_int {
1160    let oldpath = unsafe { CStr::from_ptr(oldpath) };
1161    let newpath = unsafe { CStr::from_ptr(newpath) };
1162    Sys::rename(oldpath, newpath)
1163        .map(|()| 0)
1164        .or_minus_one_errno()
1165}
1166
1167/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/renameat.html>.
1168#[unsafe(no_mangle)]
1169pub unsafe extern "C" fn renameat(
1170    old_dir: c_int,
1171    old_path: *const c_char,
1172    new_dir: c_int,
1173    new_path: *const c_char,
1174) -> c_int {
1175    let old_path = unsafe { CStr::from_ptr(old_path) };
1176    let new_path = unsafe { CStr::from_ptr(new_path) };
1177    Sys::renameat(old_dir, old_path, new_dir, new_path)
1178        .map(|()| 0)
1179        .or_minus_one_errno()
1180}
1181
1182/// See <https://www.man7.org/linux/man-pages/man2/rename.2.html>.
1183///
1184/// Non-POSIX. Seems to be a GNU extension.
1185#[unsafe(no_mangle)]
1186pub unsafe extern "C" fn renameat2(
1187    old_dir: c_int,
1188    old_path: *const c_char,
1189    new_dir: c_int,
1190    new_path: *const c_char,
1191    flags: c_uint,
1192) -> c_int {
1193    let old_path = unsafe { CStr::from_ptr(old_path) };
1194    let new_path = unsafe { CStr::from_ptr(new_path) };
1195    Sys::renameat2(old_dir, old_path, new_dir, new_path, flags)
1196        .map(|()| 0)
1197        .or_minus_one_errno()
1198}
1199
1200/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/rewind.html>.
1201///
1202/// Rewind `stream` back to the beginning of it
1203#[unsafe(no_mangle)]
1204pub unsafe extern "C" fn rewind(stream: *mut FILE) {
1205    unsafe { fseeko(stream, 0, SEEK_SET) };
1206}
1207
1208/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setbuf.html>.
1209///
1210/// Reset `stream` to use buffer `buf`. Buffer must be `BUFSIZ` in length
1211#[unsafe(no_mangle)]
1212pub unsafe extern "C" fn setbuf(stream: *mut FILE, buf: *mut c_char) {
1213    unsafe {
1214        setvbuf(
1215            stream,
1216            buf,
1217            if buf.is_null() { _IONBF } else { _IOFBF },
1218            BUFSIZ as usize,
1219        )
1220    };
1221}
1222
1223/// See <https://www.man7.org/linux/man-pages/man3/setlinebuf.3.html>.
1224///
1225/// Non-POSIX.
1226///
1227/// Set buffering of `stream` to line buffered
1228#[unsafe(no_mangle)]
1229pub unsafe extern "C" fn setlinebuf(stream: *mut FILE) {
1230    unsafe { setvbuf(stream, ptr::null_mut(), _IOLBF, 0) };
1231}
1232
1233/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setvbuf.html>.
1234///
1235/// Reset `stream` to use buffer `buf` of size `size`
1236/// If this isn't the meaning of unsafe, idk what is
1237#[unsafe(no_mangle)]
1238pub unsafe extern "C" fn setvbuf(
1239    stream: *mut FILE,
1240    buf: *mut c_char,
1241    mode: c_int,
1242    mut size: size_t,
1243) -> c_int {
1244    let mut stream = unsafe { (*stream).lock() };
1245    // Set a buffer of size `size` if no buffer is given
1246    stream.read_buf = if buf.is_null() || size == 0 {
1247        if size == 0 {
1248            size = BUFSIZ as usize;
1249        }
1250        // TODO: Make it unbuffered if _IONBF
1251        // if mode == _IONBF {
1252        // } else {
1253        Buffer::Owned(vec![0; size])
1254    // }
1255    } else {
1256        Buffer::Borrowed(unsafe { slice::from_raw_parts_mut(buf.cast::<u8>(), size) })
1257    };
1258    stream.flags |= F_SVB;
1259    0
1260}
1261
1262/// See <https://pubs.opengroup.org/onlinepubs/009604599/functions/tempnam.html>.
1263///
1264/// Marked obsolescent in issue 7.
1265#[deprecated]
1266#[unsafe(no_mangle)]
1267pub unsafe extern "C" fn tempnam(dir: *const c_char, pfx: *const c_char) -> *mut c_char {
1268    unsafe fn is_appropriate(pos_dir: *const c_char) -> bool {
1269        !pos_dir.is_null() && unsafe { unistd::access(pos_dir, unistd::W_OK) } == 0
1270    }
1271
1272    // directory search order is env!(TMPDIR), dir, P_tmpdir, "/tmp"
1273    let dirname = {
1274        let tmpdir = unsafe { stdlib::getenv(c"TMPDIR".as_ptr().cast()) };
1275        [tmpdir, dir, P_tmpdir.as_ptr().cast()]
1276            .iter()
1277            .copied()
1278            .find(|&d| unsafe { is_appropriate(d) })
1279            .unwrap_or(c"/tmp".as_ptr().cast())
1280    };
1281    let dirname_len = unsafe { string::strlen(dirname) };
1282
1283    let prefix_len = unsafe { string::strnlen_s(pfx, 5) };
1284
1285    // allocate enough for dirname "/" prefix "XXXXXX\0"
1286    let mut out_buf =
1287        unsafe { platform::alloc(dirname_len + 1 + prefix_len + L_tmpnam as usize + 1) }
1288            .cast::<c_char>();
1289
1290    if !out_buf.is_null() {
1291        // copy the directory name and prefix into the allocated buffer
1292        unsafe { out_buf.copy_from_nonoverlapping(dirname, dirname_len) };
1293        unsafe { *out_buf.add(dirname_len) = ByteLiteral::cast_cchar(b'/') };
1294        unsafe {
1295            out_buf
1296                .add(dirname_len + 1)
1297                .copy_from_nonoverlapping(pfx, prefix_len)
1298        };
1299
1300        // use the same mechanism as tmpnam to get the file name
1301        if unsafe {
1302            #[allow(deprecated)]
1303            tmpnam_inner(out_buf, dirname_len + 1 + prefix_len)
1304        }
1305        .is_null()
1306        {
1307            // failed to find a valid file name, so we need to free the buffer
1308            unsafe { platform::free(out_buf.cast()) };
1309            out_buf = ptr::null_mut();
1310        }
1311    }
1312
1313    out_buf
1314}
1315
1316/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tmpfile.html>.
1317#[unsafe(no_mangle)]
1318pub unsafe extern "C" fn tmpfile() -> *mut FILE {
1319    let mut file_name = *b"/tmp/tmpfileXXXXXX\0";
1320    let file_name = file_name.as_mut_ptr().cast::<c_char>();
1321    let fd = unsafe { stdlib::mkstemp(file_name) };
1322
1323    if fd < 0 {
1324        return ptr::null_mut();
1325    }
1326
1327    let fp = unsafe { fdopen(fd, c"w+".as_ptr()) };
1328    {
1329        let file_name = unsafe { CStr::from_ptr(file_name) };
1330        if let Ok(()) = Sys::unlink(file_name) {}; // TODO handle error
1331    }
1332
1333    if fp.is_null()
1334        && let Ok(()) = Sys::close(fd)
1335    {}; // TODO handle error
1336
1337    fp
1338}
1339
1340/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tmpnam.html>.
1341///
1342/// Marked obsolescent in issue 7.
1343#[deprecated]
1344#[unsafe(no_mangle)]
1345pub unsafe extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char {
1346    let buf = if s.is_null() {
1347        (&raw mut TMPNAM_BUF).cast()
1348    } else {
1349        s
1350    };
1351
1352    unsafe { *buf = ByteLiteral::cast_cchar(b'/') };
1353    unsafe {
1354        #[allow(deprecated)]
1355        tmpnam_inner(buf, 1)
1356    }
1357}
1358
1359#[deprecated]
1360unsafe extern "C" fn tmpnam_inner(buf: *mut c_char, offset: usize) -> *mut c_char {
1361    const TEMPLATE: &[u8] = b"XXXXXX\0";
1362
1363    unsafe {
1364        buf.add(offset)
1365            .copy_from_nonoverlapping(TEMPLATE.as_ptr().cast(), TEMPLATE.len())
1366    };
1367
1368    let err = platform::ERRNO.get();
1369    unsafe {
1370        #[allow(deprecated)]
1371        stdlib::mktemp(buf)
1372    };
1373    platform::ERRNO.set(err);
1374
1375    if unsafe { *buf } == 0 {
1376        ptr::null_mut()
1377    } else {
1378        buf
1379    }
1380}
1381
1382/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ungetc.html>.
1383///
1384/// Push character `c` back onto `stream` so it'll be read next
1385#[unsafe(no_mangle)]
1386pub unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int {
1387    let mut stream = unsafe { (*stream).lock() };
1388    if (*stream).try_set_byte_orientation_unlocked().is_err() {
1389        return -1;
1390    }
1391
1392    stream.unget.push(c as u8);
1393    c
1394}
1395
1396/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfprintf.html>.
1397#[unsafe(no_mangle)]
1398pub unsafe extern "C" fn vfprintf(file: *mut FILE, format: *const c_char, ap: va_list) -> c_int {
1399    let mut file = unsafe { (*file).lock() };
1400    if file.try_set_byte_orientation_unlocked().is_err() {
1401        return -1;
1402    }
1403
1404    unsafe { printf::printf(&mut *file, CStr::from_ptr(format), ap) }
1405}
1406
1407/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fprintf.html>.
1408#[unsafe(no_mangle)]
1409pub unsafe extern "C" fn fprintf(file: *mut FILE, format: *const c_char, __valist: ...) -> c_int {
1410    unsafe { vfprintf(file, format, __valist) }
1411}
1412
1413/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vdprintf.html>.
1414#[unsafe(no_mangle)]
1415pub unsafe extern "C" fn vdprintf(fd: c_int, format: *const c_char, ap: va_list) -> c_int {
1416    let mut f = File::new(fd);
1417
1418    // We don't want to close the file on drop; we're merely
1419    // borrowing the file descriptor here
1420    f.reference = true;
1421
1422    unsafe { printf::printf(f, CStr::from_ptr(format), ap) }
1423}
1424
1425/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dprintf.html>.
1426#[unsafe(no_mangle)]
1427pub unsafe extern "C" fn dprintf(fd: c_int, format: *const c_char, __valist: ...) -> c_int {
1428    unsafe { vdprintf(fd, format, __valist) }
1429}
1430
1431/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfprintf.html>.
1432#[unsafe(no_mangle)]
1433pub unsafe extern "C" fn vprintf(format: *const c_char, ap: va_list) -> c_int {
1434    unsafe { vfprintf(&raw mut *stdout, format, ap) }
1435}
1436
1437/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fprintf.html>.
1438#[unsafe(no_mangle)]
1439pub unsafe extern "C" fn printf(format: *const c_char, __valist: ...) -> c_int {
1440    unsafe { vfprintf(&raw mut *stdout, format, __valist) }
1441}
1442
1443/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfprintf.html>.
1444#[unsafe(no_mangle)]
1445pub unsafe extern "C" fn vasprintf(
1446    strp: *mut *mut c_char,
1447    format: *const c_char,
1448    ap: va_list,
1449) -> c_int {
1450    let mut alloc_writer = CVec::new();
1451    let ret = unsafe { printf::printf(&mut alloc_writer, CStr::from_ptr(format), ap) };
1452    alloc_writer.push(0).unwrap();
1453    alloc_writer.shrink_to_fit().unwrap();
1454    unsafe { *strp = alloc_writer.leak().cast::<c_char>() };
1455    ret
1456}
1457
1458/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fprintf.html>.
1459#[unsafe(no_mangle)]
1460pub unsafe extern "C" fn asprintf(
1461    strp: *mut *mut c_char,
1462    format: *const c_char,
1463    __valist: ...
1464) -> c_int {
1465    unsafe { vasprintf(strp, format, __valist) }
1466}
1467
1468/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfprintf.html>.
1469#[unsafe(no_mangle)]
1470pub unsafe extern "C" fn vsnprintf(
1471    s: *mut c_char,
1472    n: size_t,
1473    format: *const c_char,
1474    ap: va_list,
1475) -> c_int {
1476    unsafe {
1477        printf::printf(
1478            &mut platform::StringWriter(s, n),
1479            CStr::from_ptr(format),
1480            ap,
1481        )
1482    }
1483}
1484
1485/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fprintf.html>.
1486#[unsafe(no_mangle)]
1487pub unsafe extern "C" fn snprintf(
1488    s: *mut c_char,
1489    n: size_t,
1490    format: *const c_char,
1491    __valist: ...
1492) -> c_int {
1493    unsafe {
1494        printf::printf(
1495            &mut platform::StringWriter(s, n),
1496            CStr::from_ptr(format),
1497            __valist,
1498        )
1499    }
1500}
1501
1502/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfprintf.html>.
1503#[unsafe(no_mangle)]
1504pub unsafe extern "C" fn vsprintf(s: *mut c_char, format: *const c_char, ap: va_list) -> c_int {
1505    unsafe {
1506        printf::printf(
1507            &mut platform::UnsafeStringWriter(s.cast::<u8>()),
1508            CStr::from_ptr(format),
1509            ap,
1510        )
1511    }
1512}
1513
1514/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fprintf.html>.
1515#[unsafe(no_mangle)]
1516pub unsafe extern "C" fn sprintf(s: *mut c_char, format: *const c_char, __valist: ...) -> c_int {
1517    unsafe {
1518        printf::printf(
1519            &mut platform::UnsafeStringWriter(s.cast::<u8>()),
1520            CStr::from_ptr(format),
1521            __valist,
1522        )
1523    }
1524}
1525
1526/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfscanf.html>.
1527#[unsafe(no_mangle)]
1528pub unsafe extern "C" fn vfscanf(file: *mut FILE, format: *const c_char, ap: va_list) -> c_int {
1529    let mut file = unsafe { (*file).lock() };
1530    if file.try_set_byte_orientation_unlocked().is_err() {
1531        return -1;
1532    }
1533
1534    let f: &mut FILE = &mut file;
1535    let reader: Reader<Thin> = f.into();
1536    unsafe {
1537        let format = CStr::from_ptr(format);
1538        scanf::scanf(reader, format.into(), ap)
1539    }
1540}
1541
1542/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fscanf.html>.
1543#[unsafe(no_mangle)]
1544pub unsafe extern "C" fn fscanf(file: *mut FILE, format: *const c_char, __valist: ...) -> c_int {
1545    unsafe { vfscanf(file, format, __valist) }
1546}
1547
1548/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfscanf.html>.
1549#[unsafe(no_mangle)]
1550pub unsafe extern "C" fn vscanf(format: *const c_char, ap: va_list) -> c_int {
1551    unsafe { vfscanf(&raw mut *stdin, format, ap) }
1552}
1553
1554/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fscanf.html>.
1555#[unsafe(no_mangle)]
1556pub unsafe extern "C" fn scanf(format: *const c_char, __valist: ...) -> c_int {
1557    unsafe { vfscanf(&raw mut *stdin, format, __valist) }
1558}
1559
1560/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfscanf.html>.
1561#[unsafe(no_mangle)]
1562pub unsafe extern "C" fn vsscanf(s: *const c_char, format: *const c_char, ap: va_list) -> c_int {
1563    unsafe {
1564        let format = CStr::from_ptr(format);
1565        let s = CStr::from_ptr(s);
1566        scanf::scanf(s.into(), format.into(), ap)
1567    }
1568}
1569
1570/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fscanf.html>.
1571#[unsafe(no_mangle)]
1572pub unsafe extern "C" fn sscanf(s: *const c_char, format: *const c_char, __valist: ...) -> c_int {
1573    unsafe {
1574        let format = CStr::from_ptr(format);
1575        let s = CStr::from_ptr(s);
1576        scanf::scanf(s.into(), format.into(), __valist)
1577    }
1578}
1579
1580pub unsafe fn flush_io_streams() {
1581    let flush = |stream: *mut FILE| {
1582        let stream = unsafe { &mut *stream };
1583        let _ = stream.flush();
1584    };
1585    flush(unsafe { stdout });
1586    flush(unsafe { stderr });
1587}