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