xref: /relibc/src/header/stdio/mod.rs (revision be35961d82cd98f2a2e61c4f1869271b9f4af571)
1 //! stdio implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/stdio.h.html
2 
3 use alloc::{
4     borrow::{Borrow, BorrowMut},
5     boxed::Box,
6     vec::Vec,
7 };
8 use core::{
9     cmp,
10     ffi::VaList as va_list,
11     fmt::{self, Write as WriteFmt},
12     i32, mem,
13     ops::{Deref, DerefMut},
14     ptr, slice, str,
15 };
16 
17 use crate::{
18     c_str::CStr,
19     c_vec::CVec,
20     fs::File,
21     header::{
22         errno::{self, STR_ERROR},
23         fcntl, stdlib,
24         string::{self, strlen},
25         unistd,
26     },
27     io::{self, BufRead, BufWriter, LineWriter, Read, Write},
28     platform::{self, errno, types::*, Pal, Sys, WriteByte},
29     sync::Mutex,
30 };
31 
32 pub use self::constants::*;
33 mod constants;
34 
35 pub use self::default::*;
36 mod default;
37 
38 pub use self::getdelim::*;
39 mod getdelim;
40 
41 mod ext;
42 mod helpers;
43 mod lookaheadreader;
44 mod printf;
45 mod scanf;
46 use lookaheadreader::LookAheadReader;
47 static mut TMPNAM_BUF: [c_char; L_tmpnam as usize + 1] = [0; L_tmpnam as usize + 1];
48 
49 enum Buffer<'a> {
50     Borrowed(&'a mut [u8]),
51     Owned(Vec<u8>),
52 }
53 impl<'a> Deref for Buffer<'a> {
54     type Target = [u8];
55 
56     fn deref(&self) -> &Self::Target {
57         match self {
58             Buffer::Borrowed(inner) => inner,
59             Buffer::Owned(inner) => inner.borrow(),
60         }
61     }
62 }
63 impl<'a> DerefMut for Buffer<'a> {
64     fn deref_mut(&mut self) -> &mut Self::Target {
65         match self {
66             Buffer::Borrowed(inner) => inner,
67             Buffer::Owned(inner) => inner.borrow_mut(),
68         }
69     }
70 }
71 
72 pub trait Pending {
73     fn pending(&self) -> size_t;
74 }
75 
76 impl<W: core_io::Write> Pending for BufWriter<W> {
77     fn pending(&self) -> size_t {
78         self.buf.len() as size_t
79     }
80 }
81 
82 impl<W: core_io::Write> Pending for LineWriter<W> {
83     fn pending(&self) -> size_t {
84         self.inner.buf.len() as size_t
85     }
86 }
87 
88 pub trait Writer: Write + Pending {
89     fn purge(&mut self);
90 }
91 
92 impl<W: core_io::Write> Writer for BufWriter<W> {
93     fn purge(&mut self) {
94         self.buf.clear();
95     }
96 }
97 impl<W: core_io::Write> Writer for LineWriter<W> {
98     fn purge(&mut self) {
99         self.inner.buf.clear();
100     }
101 }
102 
103 /// This struct gets exposed to the C API.
104 pub struct FILE {
105     lock: Mutex<()>,
106 
107     file: File,
108     // pub for stdio_ext
109     pub(crate) flags: c_int,
110     read_buf: Buffer<'static>,
111     read_pos: usize,
112     read_size: usize,
113     unget: Vec<u8>,
114     // pub for stdio_ext
115     pub(crate) writer: Box<dyn Writer + Send>,
116 
117     // Optional pid for use with popen/pclose
118     pid: Option<c_int>,
119 
120     // wchar support
121     pub(crate) orientation: c_int,
122 }
123 
124 impl Read for FILE {
125     fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
126         let unget_read_size = cmp::min(out.len(), self.unget.len());
127         for i in 0..unget_read_size {
128             out[i] = self.unget.pop().unwrap();
129         }
130         if unget_read_size != 0 {
131             return Ok(unget_read_size);
132         }
133 
134         let len = {
135             let buf = self.fill_buf()?;
136             let len = buf.len().min(out.len());
137 
138             out[..len].copy_from_slice(&buf[..len]);
139             len
140         };
141         self.consume(len);
142         Ok(len)
143     }
144 }
145 impl BufRead for FILE {
146     fn fill_buf(&mut self) -> io::Result<&[u8]> {
147         if self.read_pos == self.read_size {
148             self.read_size = match self.file.read(&mut self.read_buf) {
149                 Ok(0) => {
150                     self.flags |= F_EOF;
151                     0
152                 }
153                 Ok(n) => n,
154                 Err(err) => {
155                     self.flags |= F_ERR;
156                     return Err(err);
157                 }
158             };
159             self.read_pos = 0;
160         }
161         Ok(&self.read_buf[self.read_pos..self.read_size])
162     }
163     fn consume(&mut self, i: usize) {
164         self.read_pos = (self.read_pos + i).min(self.read_size);
165     }
166 }
167 impl Write for FILE {
168     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
169         match self.writer.write(buf) {
170             Ok(n) => Ok(n),
171             Err(err) => {
172                 self.flags |= F_ERR;
173                 Err(err)
174             }
175         }
176     }
177     fn flush(&mut self) -> io::Result<()> {
178         match self.writer.flush() {
179             Ok(()) => Ok(()),
180             Err(err) => {
181                 self.flags |= F_ERR;
182                 Err(err)
183             }
184         }
185     }
186 }
187 impl WriteFmt for FILE {
188     fn write_str(&mut self, s: &str) -> fmt::Result {
189         self.write_all(s.as_bytes())
190             .map(|_| ())
191             .map_err(|_| fmt::Error)
192     }
193 }
194 impl WriteByte for FILE {
195     fn write_u8(&mut self, c: u8) -> fmt::Result {
196         self.write_all(&[c]).map(|_| ()).map_err(|_| fmt::Error)
197     }
198 }
199 impl FILE {
200     pub fn lock(&mut self) -> LockGuard {
201         unsafe {
202             flockfile(self);
203         }
204         LockGuard(self)
205     }
206 
207     pub fn try_set_orientation(&mut self, mode: c_int) -> c_int {
208         let stream = self.lock();
209         stream.0.try_set_orientation_unlocked(mode)
210     }
211 
212     pub fn try_set_orientation_unlocked(&mut self, mode: c_int) -> c_int {
213         if self.orientation == 0 {
214             self.orientation = match mode {
215                 1..=i32::MAX => 1,
216                 i32::MIN..=-1 => -1,
217                 0 => self.orientation,
218             };
219         }
220         self.orientation
221     }
222 
223     pub fn try_set_byte_orientation_unlocked(&mut self) -> core::result::Result<(), c_int> {
224         match self.try_set_orientation_unlocked(-1) {
225             i32::MIN..=-1 => Ok(()),
226             x => Err(x),
227         }
228     }
229 
230     pub fn purge(&mut self) {
231         // Purge read buffer
232         self.read_pos = 0;
233         self.read_size = 0;
234         // Purge unget
235         self.unget.clear();
236         // Purge write buffer
237         self.writer.purge();
238     }
239 }
240 
241 pub struct LockGuard<'a>(&'a mut FILE);
242 impl<'a> Deref for LockGuard<'a> {
243     type Target = FILE;
244 
245     fn deref(&self) -> &Self::Target {
246         &self.0
247     }
248 }
249 impl<'a> DerefMut for LockGuard<'a> {
250     fn deref_mut(&mut self) -> &mut Self::Target {
251         self.0
252     }
253 }
254 impl<'a> Drop for LockGuard<'a> {
255     fn drop(&mut self) {
256         unsafe {
257             funlockfile(self.0);
258         }
259     }
260 }
261 
262 /// Clears EOF and ERR indicators on a stream
263 #[no_mangle]
264 pub unsafe extern "C" fn clearerr(stream: *mut FILE) {
265     let mut stream = (*stream).lock();
266     stream.flags &= !(F_EOF | F_ERR);
267 }
268 
269 // #[no_mangle]
270 pub extern "C" fn ctermid(_s: *mut c_char) -> *mut c_char {
271     unimplemented!();
272 }
273 
274 // #[no_mangle]
275 pub extern "C" fn cuserid(_s: *mut c_char) -> *mut c_char {
276     unimplemented!();
277 }
278 
279 /// Close a file
280 /// This function does not guarentee that the file buffer will be flushed or that the file
281 /// descriptor will be closed, so if it is important that the file be written to, use `fflush()`
282 /// prior to using this function.
283 #[no_mangle]
284 pub unsafe extern "C" fn fclose(stream: *mut FILE) -> c_int {
285     let stream = &mut *stream;
286     flockfile(stream);
287 
288     let mut r = stream.flush().is_err();
289     let close = Sys::close(*stream.file) < 0;
290     r = r || close;
291 
292     if stream.flags & constants::F_PERM == 0 {
293         // Not one of stdin, stdout or stderr
294         let mut stream = Box::from_raw(stream);
295         // Reference files aren't closed on drop, so pretend to be a reference
296         stream.file.reference = true;
297     } else {
298         funlockfile(stream);
299     }
300 
301     r as c_int
302 }
303 
304 /// Open a file from a file descriptor
305 #[no_mangle]
306 pub unsafe extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE {
307     if let Some(f) = helpers::_fdopen(fildes, mode) {
308         f
309     } else {
310         ptr::null_mut()
311     }
312 }
313 
314 /// Check for EOF
315 #[no_mangle]
316 pub unsafe extern "C" fn feof(stream: *mut FILE) -> c_int {
317     let stream = (*stream).lock();
318     stream.flags & F_EOF
319 }
320 
321 /// Check for ERR
322 #[no_mangle]
323 pub unsafe extern "C" fn ferror(stream: *mut FILE) -> c_int {
324     let stream = (*stream).lock();
325     stream.flags & F_ERR
326 }
327 
328 /// Flush output to stream, or sync read position
329 /// Ensure the file is unlocked before calling this function, as it will attempt to lock the file
330 /// itself.
331 #[no_mangle]
332 pub unsafe extern "C" fn fflush(stream: *mut FILE) -> c_int {
333     if stream.is_null() {
334         //TODO: flush all files!
335 
336         if fflush(stdout) != 0 {
337             return EOF;
338         }
339 
340         if fflush(stderr) != 0 {
341             return EOF;
342         }
343     } else {
344         let mut stream = (*stream).lock();
345         if stream.flush().is_err() {
346             return EOF;
347         }
348     }
349 
350     0
351 }
352 
353 /// Get a single char from a stream
354 #[no_mangle]
355 pub unsafe extern "C" fn fgetc(stream: *mut FILE) -> c_int {
356     let mut stream = (*stream).lock();
357     if let Err(_) = (*stream).try_set_byte_orientation_unlocked() {
358         return -1;
359     }
360 
361     getc_unlocked(&mut *stream)
362 }
363 
364 /// Get the position of the stream and store it in pos
365 #[no_mangle]
366 pub unsafe extern "C" fn fgetpos(stream: *mut FILE, pos: *mut fpos_t) -> c_int {
367     let off = ftello(stream);
368     if off < 0 {
369         return -1;
370     }
371     *pos = off;
372     0
373 }
374 
375 /// Get a string from the stream
376 #[no_mangle]
377 pub unsafe extern "C" fn fgets(
378     original: *mut c_char,
379     max: c_int,
380     stream: *mut FILE,
381 ) -> *mut c_char {
382     let mut stream = (*stream).lock();
383     if let Err(_) = (*stream).try_set_byte_orientation_unlocked() {
384         return ptr::null_mut();
385     }
386 
387     let mut out = original;
388     let max = max as usize;
389     let mut left = max.saturating_sub(1); // Make space for the terminating NUL-byte
390     let mut wrote = false;
391 
392     if left >= 1 {
393         let unget_read_size = cmp::min(left, stream.unget.len());
394         for _ in 0..unget_read_size {
395             *out = stream.unget.pop().unwrap() as i8;
396             out = out.offset(1);
397         }
398         left -= unget_read_size;
399     }
400 
401     loop {
402         if left == 0 {
403             break;
404         }
405 
406         // TODO: When NLL is a thing, this block can be flattened out
407         let (read, exit) = {
408             let buf = match stream.fill_buf() {
409                 Ok(buf) => buf,
410                 Err(_) => return ptr::null_mut(),
411             };
412             if buf.is_empty() {
413                 break;
414             }
415             wrote = true;
416             let len = buf.len().min(left);
417 
418             let newline = buf[..len].iter().position(|&c| c == b'\n');
419             let len = newline.map(|i| i + 1).unwrap_or(len);
420 
421             ptr::copy_nonoverlapping(buf.as_ptr(), out as *mut u8, len);
422 
423             (len, newline.is_some())
424         };
425 
426         stream.consume(read);
427 
428         out = out.add(read);
429         left -= read;
430 
431         if exit {
432             break;
433         }
434     }
435 
436     if max >= 1 {
437         // Write the NUL byte
438         *out = 0;
439     }
440     if wrote {
441         original
442     } else {
443         ptr::null_mut()
444     }
445 }
446 
447 /// Get the underlying file descriptor
448 #[no_mangle]
449 pub unsafe extern "C" fn fileno(stream: *mut FILE) -> c_int {
450     let stream = (*stream).lock();
451     *stream.file
452 }
453 
454 /// Lock the file
455 /// Do not call any functions other than those with the `_unlocked` postfix while the file is
456 /// locked
457 #[no_mangle]
458 pub unsafe extern "C" fn flockfile(file: *mut FILE) {
459     (*file).lock.manual_lock();
460 }
461 
462 /// Open the file in mode `mode`
463 #[no_mangle]
464 pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE {
465     let initial_mode = *mode;
466     if initial_mode != b'r' as i8 && initial_mode != b'w' as i8 && initial_mode != b'a' as i8 {
467         platform::errno = errno::EINVAL;
468         return ptr::null_mut();
469     }
470 
471     let flags = helpers::parse_mode_flags(mode);
472 
473     let new_mode = if flags & fcntl::O_CREAT == fcntl::O_CREAT {
474         0o666
475     } else {
476         0
477     };
478 
479     let fd = fcntl::sys_open(filename, flags, new_mode);
480     if fd < 0 {
481         return ptr::null_mut();
482     }
483 
484     if flags & fcntl::O_CLOEXEC > 0 {
485         fcntl::sys_fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC);
486     }
487 
488     if let Some(f) = helpers::_fdopen(fd, mode) {
489         f
490     } else {
491         Sys::close(fd);
492         ptr::null_mut()
493     }
494 }
495 
496 /// Clear the buffers of a stream
497 /// Ensure the file is unlocked before calling this function, as it will attempt to lock the file
498 /// itself.
499 #[no_mangle]
500 pub unsafe extern "C" fn __fpurge(stream: *mut FILE) {
501     if ! stream.is_null() {
502         let mut stream = (*stream).lock();
503         stream.purge();
504     }
505 }
506 
507 /// Insert a character into the stream
508 #[no_mangle]
509 pub unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int {
510     let mut stream = (*stream).lock();
511     if let Err(_) = (*stream).try_set_byte_orientation_unlocked() {
512         return -1;
513     }
514 
515     putc_unlocked(c, &mut *stream)
516 }
517 
518 /// Insert a string into a stream
519 #[no_mangle]
520 pub unsafe extern "C" fn fputs(s: *const c_char, stream: *mut FILE) -> c_int {
521     let mut stream = (*stream).lock();
522     if let Err(_) = (*stream).try_set_byte_orientation_unlocked() {
523         return -1;
524     }
525 
526     let buf = slice::from_raw_parts(s as *mut u8, strlen(s));
527 
528     if stream.write_all(&buf).is_ok() {
529         0
530     } else {
531         -1
532     }
533 }
534 
535 /// Read `nitems` of size `size` into `ptr` from `stream`
536 #[no_mangle]
537 pub unsafe extern "C" fn fread(
538     ptr: *mut c_void,
539     size: size_t,
540     nitems: size_t,
541     stream: *mut FILE,
542 ) -> size_t {
543     if size == 0 || nitems == 0 {
544         return 0;
545     }
546 
547     let mut stream = (*stream).lock();
548     if let Err(_) = (*stream).try_set_byte_orientation_unlocked() {
549         return 0;
550     }
551 
552     let buf = slice::from_raw_parts_mut(ptr as *mut u8, size as usize * nitems as usize);
553     let mut read = 0;
554     while read < buf.len() {
555         match stream.read(&mut buf[read..]) {
556             Ok(0) | Err(_) => break,
557             Ok(n) => read += n,
558         }
559     }
560     (read / size as usize) as size_t
561 }
562 
563 #[no_mangle]
564 pub unsafe extern "C" fn freopen(
565     filename: *const c_char,
566     mode: *const c_char,
567     stream: &mut FILE,
568 ) -> *mut FILE {
569     let mut flags = helpers::parse_mode_flags(mode);
570     flockfile(stream);
571 
572     let _ = stream.flush();
573     if filename.is_null() {
574         // Reopen stream in new mode
575         if flags & fcntl::O_CLOEXEC > 0 {
576             fcntl::sys_fcntl(*stream.file, fcntl::F_SETFD, fcntl::FD_CLOEXEC);
577         }
578         flags &= !(fcntl::O_CREAT | fcntl::O_EXCL | fcntl::O_CLOEXEC);
579         if fcntl::sys_fcntl(*stream.file, fcntl::F_SETFL, flags) < 0 {
580             funlockfile(stream);
581             fclose(stream);
582             return ptr::null_mut();
583         }
584     } else {
585         let new = fopen(filename, mode);
586         if new.is_null() {
587             funlockfile(stream);
588             fclose(stream);
589             return ptr::null_mut();
590         }
591         let new = &mut *new; // Should be safe, new is not null
592         if *new.file == *stream.file {
593             new.file.fd = -1;
594         } else if Sys::dup2(*new.file, *stream.file) < 0
595             || fcntl::sys_fcntl(*stream.file, fcntl::F_SETFL, flags & fcntl::O_CLOEXEC) < 0
596         {
597             funlockfile(stream);
598             fclose(new);
599             fclose(stream);
600             return ptr::null_mut();
601         }
602         stream.flags = (stream.flags & constants::F_PERM) | new.flags;
603         fclose(new);
604     }
605     stream.orientation = 0;
606     funlockfile(stream);
607     stream
608 }
609 
610 /// Seek to an offset `offset` from `whence`
611 #[no_mangle]
612 pub unsafe extern "C" fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int {
613     fseeko(stream, offset as off_t, whence)
614 }
615 
616 /// Seek to an offset `offset` from `whence`
617 #[no_mangle]
618 pub unsafe extern "C" fn fseeko(stream: *mut FILE, off: off_t, whence: c_int) -> c_int {
619     let mut stream = (*stream).lock();
620     fseek_locked(&mut *stream, off, whence)
621 }
622 
623 pub unsafe fn fseek_locked(stream: &mut FILE, mut off: off_t, whence: c_int) -> c_int {
624     if whence == SEEK_CUR {
625         // Since it's a buffered writer, our actual cursor isn't where the user
626         // thinks
627         off -= (stream.read_size - stream.read_pos) as off_t;
628     }
629 
630     // Flush write buffer before seek
631     if stream.flush().is_err() {
632         return -1;
633     }
634 
635     let err = Sys::lseek(*stream.file, off, whence);
636     if err < 0 {
637         return err as c_int;
638     }
639 
640     stream.flags &= !(F_EOF | F_ERR);
641     stream.read_pos = 0;
642     stream.read_size = 0;
643     stream.unget = Vec::new();
644     0
645 }
646 
647 /// Seek to a position `pos` in the file from the beginning of the file
648 #[no_mangle]
649 pub unsafe extern "C" fn fsetpos(stream: *mut FILE, pos: *const fpos_t) -> c_int {
650     fseeko(stream, *pos, SEEK_SET)
651 }
652 
653 /// Get the current position of the cursor in the file
654 #[no_mangle]
655 pub unsafe extern "C" fn ftell(stream: *mut FILE) -> c_long {
656     ftello(stream) as c_long
657 }
658 
659 /// Get the current position of the cursor in the file
660 #[no_mangle]
661 pub unsafe extern "C" fn ftello(stream: *mut FILE) -> off_t {
662     let mut stream = (*stream).lock();
663     ftell_locked(&mut *stream)
664 }
665 pub unsafe extern "C" fn ftell_locked(stream: &mut FILE) -> off_t {
666     let pos = Sys::lseek(*stream.file, 0, SEEK_CUR);
667     if pos < 0 {
668         return -1;
669     }
670 
671     pos - (stream.read_size - stream.read_pos) as off_t - stream.unget.len() as off_t
672 }
673 
674 /// Try to lock the file. Returns 0 for success, 1 for failure
675 #[no_mangle]
676 pub unsafe extern "C" fn ftrylockfile(file: *mut FILE) -> c_int {
677     if (*file).lock.manual_try_lock().is_ok() {
678         0
679     } else {
680         1
681     }
682 }
683 
684 /// Unlock the file
685 #[no_mangle]
686 pub unsafe extern "C" fn funlockfile(file: *mut FILE) {
687     (*file).lock.manual_unlock();
688 }
689 
690 /// Write `nitems` of size `size` from `ptr` to `stream`
691 #[no_mangle]
692 pub unsafe extern "C" fn fwrite(
693     ptr: *const c_void,
694     size: size_t,
695     nitems: size_t,
696     stream: *mut FILE,
697 ) -> size_t {
698     if size == 0 || nitems == 0 {
699         return 0;
700     }
701     let mut stream = (*stream).lock();
702     if let Err(_) = (*stream).try_set_byte_orientation_unlocked() {
703         return 0;
704     }
705 
706     let buf = slice::from_raw_parts(ptr as *const u8, size as usize * nitems as usize);
707     let mut written = 0;
708     while written < buf.len() {
709         match stream.write(&buf[written..]) {
710             Ok(0) | Err(_) => break,
711             Ok(n) => written += n,
712         }
713     }
714     (written / size as usize) as size_t
715 }
716 
717 /// Get a single char from a stream
718 #[no_mangle]
719 pub unsafe extern "C" fn getc(stream: *mut FILE) -> c_int {
720     let mut stream = (*stream).lock();
721     getc_unlocked(&mut *stream)
722 }
723 
724 /// Get a single char from `stdin`
725 #[no_mangle]
726 pub unsafe extern "C" fn getchar() -> c_int {
727     fgetc(&mut *stdin)
728 }
729 
730 /// Get a char from a stream without locking the stream
731 #[no_mangle]
732 pub unsafe extern "C" fn getc_unlocked(stream: *mut FILE) -> c_int {
733     if let Err(_) = (*stream).try_set_byte_orientation_unlocked() {
734         return -1;
735     }
736 
737     let mut buf = [0];
738 
739     match (*stream).read(&mut buf) {
740         Ok(0) | Err(_) => EOF,
741         Ok(_) => buf[0] as c_int,
742     }
743 }
744 
745 /// Get a char from `stdin` without locking `stdin`
746 #[no_mangle]
747 pub unsafe extern "C" fn getchar_unlocked() -> c_int {
748     getc_unlocked(&mut *stdin)
749 }
750 
751 /// Get a string from `stdin`
752 #[no_mangle]
753 pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char {
754     fgets(s, c_int::max_value(), &mut *stdin)
755 }
756 
757 /// Get an integer from `stream`
758 #[no_mangle]
759 pub unsafe extern "C" fn getw(stream: *mut FILE) -> c_int {
760     let mut ret: c_int = 0;
761     if fread(
762         &mut ret as *mut _ as *mut c_void,
763         mem::size_of_val(&ret),
764         1,
765         stream,
766     ) > 0
767     {
768         ret
769     } else {
770         -1
771     }
772 }
773 
774 #[no_mangle]
775 pub unsafe extern "C" fn pclose(stream: *mut FILE) -> c_int {
776     let pid = {
777         let mut stream = (*stream).lock();
778 
779         if let Some(pid) = stream.pid.take() {
780             pid
781         } else {
782             errno = errno::ECHILD;
783             return -1;
784         }
785     };
786 
787     fclose(stream);
788 
789     let mut wstatus = 0;
790     if Sys::waitpid(pid, &mut wstatus, 0) < 0 {
791         return -1;
792     }
793 
794     wstatus
795 }
796 
797 #[no_mangle]
798 pub unsafe extern "C" fn perror(s: *const c_char) {
799     let s_cstr = CStr::from_ptr(s);
800     let s_str = str::from_utf8_unchecked(s_cstr.to_bytes());
801 
802     let mut w = platform::FileWriter(2);
803     if errno >= 0 && errno < STR_ERROR.len() as c_int {
804         w.write_fmt(format_args!("{}: {}\n", s_str, STR_ERROR[errno as usize]))
805             .unwrap();
806     } else {
807         w.write_fmt(format_args!("{}: Unknown error {}\n", s_str, errno))
808             .unwrap();
809     }
810 }
811 
812 #[no_mangle]
813 pub unsafe extern "C" fn popen(command: *const c_char, mode: *const c_char) -> *mut FILE {
814     //TODO: share code with system
815 
816     let mode = CStr::from_ptr(mode);
817 
818     let mut cloexec = false;
819     let mut write_opt = None;
820     for b in mode.to_bytes().iter() {
821         match b {
822             b'e' => cloexec = true,
823             b'r' if write_opt.is_none() => write_opt = Some(false),
824             b'w' if write_opt.is_none() => write_opt = Some(true),
825             _ => {
826                 errno = errno::EINVAL;
827                 return ptr::null_mut();
828             }
829         }
830     }
831 
832     let write = match write_opt {
833         Some(some) => some,
834         None => {
835             errno = errno::EINVAL;
836             return ptr::null_mut();
837         }
838     };
839 
840     let mut pipes = [-1, -1];
841     if unistd::pipe(pipes.as_mut_ptr()) != 0 {
842         return ptr::null_mut();
843     }
844 
845     let child_pid = unistd::fork();
846     if child_pid == 0 {
847         let command_nonnull = if command.is_null() {
848             "exit 0\0".as_ptr()
849         } else {
850             command as *const u8
851         };
852 
853         let shell = "/bin/sh\0".as_ptr();
854 
855         let args = [
856             "sh\0".as_ptr(),
857             "-c\0".as_ptr(),
858             command_nonnull,
859             ptr::null(),
860         ];
861 
862         // Setup up stdin or stdout
863         //TODO: dup errors are ignored, should they be?
864         {
865             if write {
866                 unistd::dup2(0, pipes[0]);
867             } else {
868                 unistd::dup2(1, pipes[1]);
869             }
870 
871             unistd::close(pipes[0]);
872             unistd::close(pipes[1]);
873         }
874 
875         unistd::execv(shell as *const c_char, args.as_ptr() as *const *mut c_char);
876 
877         stdlib::exit(127);
878 
879         unreachable!();
880     } else if child_pid > 0 {
881         let (fd, fd_mode) = if write {
882             unistd::close(pipes[0]);
883             (pipes[1], if cloexec { c_str!("we") } else { c_str!("w") })
884         } else {
885             unistd::close(pipes[1]);
886             (pipes[0], if cloexec { c_str!("re") } else { c_str!("r") })
887         };
888 
889         if let Some(f) = helpers::_fdopen(fd, fd_mode.as_ptr()) {
890             (*f).pid = Some(child_pid);
891             f
892         } else {
893             ptr::null_mut()
894         }
895     } else {
896         ptr::null_mut()
897     }
898 }
899 
900 /// Put a character `c` into `stream`
901 #[no_mangle]
902 pub unsafe extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int {
903     let mut stream = (*stream).lock();
904     putc_unlocked(c, &mut *stream)
905 }
906 
907 /// Put a character `c` into `stdout`
908 #[no_mangle]
909 pub unsafe extern "C" fn putchar(c: c_int) -> c_int {
910     fputc(c, &mut *stdout)
911 }
912 
913 /// Put a character `c` into `stream` without locking `stream`
914 #[no_mangle]
915 pub unsafe extern "C" fn putc_unlocked(c: c_int, stream: *mut FILE) -> c_int {
916     if let Err(_) = (*stream).try_set_byte_orientation_unlocked() {
917         return -1;
918     }
919 
920     match (*stream).write(&[c as u8]) {
921         Ok(0) | Err(_) => EOF,
922         Ok(_) => c,
923     }
924 }
925 
926 /// Put a character `c` into `stdout` without locking `stdout`
927 #[no_mangle]
928 pub unsafe extern "C" fn putchar_unlocked(c: c_int) -> c_int {
929     putc_unlocked(c, stdout)
930 }
931 
932 /// Put a string `s` into `stdout`
933 #[no_mangle]
934 pub unsafe extern "C" fn puts(s: *const c_char) -> c_int {
935     let mut stream = (&mut *stdout).lock();
936     if let Err(_) = (*stream).try_set_byte_orientation_unlocked() {
937         return -1;
938     }
939 
940     let buf = slice::from_raw_parts(s as *mut u8, strlen(s));
941 
942     if stream.write_all(&buf).is_err() {
943         return -1;
944     }
945     if stream.write(&[b'\n']).is_err() {
946         return -1;
947     }
948     0
949 }
950 
951 /// Put an integer `w` into `stream`
952 #[no_mangle]
953 pub unsafe extern "C" fn putw(w: c_int, stream: *mut FILE) -> c_int {
954     fwrite(&w as *const c_int as _, mem::size_of_val(&w), 1, stream) as i32 - 1
955 }
956 
957 /// Delete file or directory `path`
958 #[no_mangle]
959 pub unsafe extern "C" fn remove(path: *const c_char) -> c_int {
960     let path = CStr::from_ptr(path);
961     let r = Sys::unlink(path);
962     if r == -errno::EISDIR {
963         Sys::rmdir(path)
964     } else {
965         r
966     }
967 }
968 
969 #[no_mangle]
970 pub unsafe extern "C" fn rename(oldpath: *const c_char, newpath: *const c_char) -> c_int {
971     let oldpath = CStr::from_ptr(oldpath);
972     let newpath = CStr::from_ptr(newpath);
973     Sys::rename(oldpath, newpath)
974 }
975 
976 /// Rewind `stream` back to the beginning of it
977 #[no_mangle]
978 pub unsafe extern "C" fn rewind(stream: *mut FILE) {
979     fseeko(stream, 0, SEEK_SET);
980 }
981 
982 /// Reset `stream` to use buffer `buf`. Buffer must be `BUFSIZ` in length
983 #[no_mangle]
984 pub unsafe extern "C" fn setbuf(stream: *mut FILE, buf: *mut c_char) {
985     setvbuf(
986         stream,
987         buf,
988         if buf.is_null() { _IONBF } else { _IOFBF },
989         BUFSIZ as usize,
990     );
991 }
992 
993 /// Reset `stream` to use buffer `buf` of size `size`
994 /// If this isn't the meaning of unsafe, idk what is
995 #[no_mangle]
996 pub unsafe extern "C" fn setvbuf(
997     stream: *mut FILE,
998     buf: *mut c_char,
999     mode: c_int,
1000     mut size: size_t,
1001 ) -> c_int {
1002     let mut stream = (*stream).lock();
1003     // Set a buffer of size `size` if no buffer is given
1004     stream.read_buf = if buf.is_null() || size == 0 {
1005         if size == 0 {
1006             size = BUFSIZ as usize;
1007         }
1008         // TODO: Make it unbuffered if _IONBF
1009         // if mode == _IONBF {
1010         // } else {
1011         Buffer::Owned(vec![0; size as usize])
1012     // }
1013     } else {
1014         Buffer::Borrowed(slice::from_raw_parts_mut(buf as *mut u8, size))
1015     };
1016     stream.flags |= F_SVB;
1017     0
1018 }
1019 
1020 #[no_mangle]
1021 pub unsafe extern "C" fn tempnam(dir: *const c_char, pfx: *const c_char) -> *mut c_char {
1022     unsafe fn is_appropriate(pos_dir: *const c_char) -> bool {
1023         !pos_dir.is_null() && unistd::access(pos_dir, unistd::W_OK) == 0
1024     }
1025 
1026     // directory search order is env!(TMPDIR), dir, P_tmpdir, "/tmp"
1027     let dirname = {
1028         let tmpdir = stdlib::getenv(b"TMPDIR\0".as_ptr() as _);
1029         [tmpdir, dir, P_tmpdir.as_ptr() as _]
1030             .iter()
1031             .copied()
1032             .skip_while(|&d| !is_appropriate(d))
1033             .next()
1034             .unwrap_or(b"/tmp\0".as_ptr() as _)
1035     };
1036     let dirname_len = string::strlen(dirname);
1037 
1038     let prefix_len = string::strnlen_s(pfx, 5);
1039 
1040     // allocate enough for dirname "/" prefix "XXXXXX\0"
1041     let mut out_buf =
1042         platform::alloc(dirname_len + 1 + prefix_len + L_tmpnam as usize + 1) as *mut c_char;
1043 
1044     if !out_buf.is_null() {
1045         // copy the directory name and prefix into the allocated buffer
1046         out_buf.copy_from_nonoverlapping(dirname, dirname_len);
1047         *out_buf.add(dirname_len) = b'/' as _;
1048         out_buf
1049             .add(dirname_len + 1)
1050             .copy_from_nonoverlapping(pfx, prefix_len);
1051 
1052         // use the same mechanism as tmpnam to get the file name
1053         if tmpnam_inner(out_buf, dirname_len + 1 + prefix_len).is_null() {
1054             // failed to find a valid file name, so we need to free the buffer
1055             platform::free(out_buf as _);
1056             out_buf = ptr::null_mut();
1057         }
1058     }
1059 
1060     out_buf
1061 }
1062 
1063 #[no_mangle]
1064 pub unsafe extern "C" fn tmpfile() -> *mut FILE {
1065     let mut file_name = *b"/tmp/tmpfileXXXXXX\0";
1066     let file_name = file_name.as_mut_ptr() as *mut c_char;
1067     let fd = stdlib::mkstemp(file_name);
1068 
1069     if fd < 0 {
1070         return ptr::null_mut();
1071     }
1072 
1073     let fp = fdopen(fd, c_str!("w+").as_ptr());
1074     {
1075         let file_name = CStr::from_ptr(file_name);
1076         Sys::unlink(file_name);
1077     }
1078 
1079     if fp.is_null() {
1080         Sys::close(fd);
1081     }
1082 
1083     fp
1084 }
1085 
1086 #[no_mangle]
1087 pub unsafe extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char {
1088     let buf = if s.is_null() {
1089         TMPNAM_BUF.as_mut_ptr()
1090     } else {
1091         s
1092     };
1093 
1094     *buf = b'/' as _;
1095     tmpnam_inner(buf, 1)
1096 }
1097 
1098 unsafe extern "C" fn tmpnam_inner(buf: *mut c_char, offset: usize) -> *mut c_char {
1099     const TEMPLATE: &[u8] = b"XXXXXX\0";
1100 
1101     buf.add(offset)
1102         .copy_from_nonoverlapping(TEMPLATE.as_ptr() as _, TEMPLATE.len());
1103 
1104     let err = platform::errno;
1105     stdlib::mktemp(buf);
1106     platform::errno = err;
1107 
1108     if *buf == 0 {
1109         ptr::null_mut()
1110     } else {
1111         buf
1112     }
1113 }
1114 
1115 /// Push character `c` back onto `stream` so it'll be read next
1116 #[no_mangle]
1117 pub unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int {
1118     let mut stream = (*stream).lock();
1119     if let Err(_) = (*stream).try_set_byte_orientation_unlocked() {
1120         return -1;
1121     }
1122 
1123     stream.unget.push(c as u8);
1124     c
1125 }
1126 
1127 #[no_mangle]
1128 pub unsafe extern "C" fn vfprintf(file: *mut FILE, format: *const c_char, ap: va_list) -> c_int {
1129     let mut file = (*file).lock();
1130     if let Err(_) = file.try_set_byte_orientation_unlocked() {
1131         return -1;
1132     }
1133 
1134     printf::printf(&mut *file, format, ap)
1135 }
1136 
1137 #[no_mangle]
1138 pub unsafe extern "C" fn vprintf(format: *const c_char, ap: va_list) -> c_int {
1139     vfprintf(&mut *stdout, format, ap)
1140 }
1141 
1142 #[no_mangle]
1143 pub unsafe extern "C" fn vasprintf(
1144     strp: *mut *mut c_char,
1145     format: *const c_char,
1146     ap: va_list,
1147 ) -> c_int {
1148     let mut alloc_writer = CVec::new();
1149     let ret = printf::printf(&mut alloc_writer, format, ap);
1150     alloc_writer.push(0).unwrap();
1151     alloc_writer.shrink_to_fit().unwrap();
1152     *strp = alloc_writer.leak() as *mut c_char;
1153     ret
1154 }
1155 
1156 #[no_mangle]
1157 pub unsafe extern "C" fn vsnprintf(
1158     s: *mut c_char,
1159     n: size_t,
1160     format: *const c_char,
1161     ap: va_list,
1162 ) -> c_int {
1163     printf::printf(
1164         &mut platform::StringWriter(s as *mut u8, n as usize),
1165         format,
1166         ap,
1167     )
1168 }
1169 
1170 #[no_mangle]
1171 pub unsafe extern "C" fn vsprintf(s: *mut c_char, format: *const c_char, ap: va_list) -> c_int {
1172     printf::printf(&mut platform::UnsafeStringWriter(s as *mut u8), format, ap)
1173 }
1174 
1175 #[no_mangle]
1176 pub unsafe extern "C" fn vfscanf(file: *mut FILE, format: *const c_char, ap: va_list) -> c_int {
1177     let ret = {
1178         let mut file = (*file).lock();
1179         if let Err(_) = file.try_set_byte_orientation_unlocked() {
1180             return -1;
1181         }
1182 
1183         let f: &mut FILE = &mut *file;
1184         let reader: LookAheadReader = f.into();
1185         scanf::scanf(reader, format, ap)
1186     };
1187     ret
1188 }
1189 
1190 #[no_mangle]
1191 pub unsafe extern "C" fn vscanf(format: *const c_char, ap: va_list) -> c_int {
1192     vfscanf(&mut *stdin, format, ap)
1193 }
1194 
1195 #[no_mangle]
1196 pub unsafe extern "C" fn vsscanf(s: *const c_char, format: *const c_char, ap: va_list) -> c_int {
1197     let reader = (s as *const u8).into();
1198     scanf::scanf(reader, format, ap)
1199 }
1200 
1201 pub unsafe fn flush_io_streams() {
1202     let flush = |stream: *mut FILE| {
1203         let stream = &mut *stream;
1204         stream.flush()
1205     };
1206     flush(stdout);
1207     flush(stderr);
1208 }
1209