1use 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
116pub struct FILE {
118 lock: RlctMutex,
119
120 file: File,
121 pub(crate) flags: c_int,
123
124 read_buf: Buffer<'static>,
126
127 read_pos: usize,
128 read_size: usize,
129 unget: Vec<u8>,
130 pub(crate) writer: Box<dyn Writer + Send>,
134
135 pid: Option<c_int>,
137
138 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 self.read_pos = 0;
261 self.read_size = 0;
262 self.unget.clear();
264 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#[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#[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#[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#[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 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 let mut stream = unsafe { Box::from_raw(stream) };
368 stream.file.reference = true;
370 } else {
371 unsafe { funlockfile(stream) };
372 }
373
374 c_int::from(r)
375}
376
377#[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#[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#[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#[unsafe(no_mangle)]
411pub unsafe extern "C" fn fflush(stream: *mut FILE) -> c_int {
412 if stream.is_null() {
413 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#[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#[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#[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); 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 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 unsafe { *out = 0 };
524 }
525 if wrote { original } else { ptr::null_mut() }
526}
527
528#[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#[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#[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 if let Ok(()) = Sys::close(fd) {}; })
586 .or_errno_null_mut()
587}
588
589#[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#[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#[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#[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#[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 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 }; 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#[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#[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 off -= (stream.read_size - stream.read_pos) as off_t;
744 }
745
746 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#[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#[unsafe(no_mangle)]
775pub unsafe extern "C" fn ftell(stream: *mut FILE) -> c_long {
776 unsafe { ftello(stream) as c_long }
777}
778
779#[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 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#[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#[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#[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#[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#[unsafe(no_mangle)]
863pub unsafe extern "C" fn getchar() -> c_int {
864 unsafe { fgetc(&raw mut *stdin) }
865}
866
867#[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#[unsafe(no_mangle)]
888pub unsafe extern "C" fn getchar_unlocked() -> c_int {
889 unsafe { getc_unlocked(&raw mut *stdin) }
890}
891
892#[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#[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#[unsafe(no_mangle)]
930pub unsafe extern "C" fn pclose(stream: *mut FILE) -> c_int {
931 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#[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 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#[unsafe(no_mangle)]
977pub unsafe extern "C" fn popen(command: *const c_char, mode: *const c_char) -> *mut FILE {
978 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 {
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#[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#[unsafe(no_mangle)]
1078pub unsafe extern "C" fn putchar(c: c_int) -> c_int {
1079 unsafe { fputc(c, &raw mut *stdout) }
1080}
1081
1082#[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#[unsafe(no_mangle)]
1101pub unsafe extern "C" fn putchar_unlocked(c: c_int) -> c_int {
1102 unsafe { putc_unlocked(c, stdout) }
1103}
1104
1105#[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#[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#[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#[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#[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#[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#[unsafe(no_mangle)]
1204pub unsafe extern "C" fn rewind(stream: *mut FILE) {
1205 unsafe { fseeko(stream, 0, SEEK_SET) };
1206}
1207
1208#[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#[unsafe(no_mangle)]
1229pub unsafe extern "C" fn setlinebuf(stream: *mut FILE) {
1230 unsafe { setvbuf(stream, ptr::null_mut(), _IOLBF, 0) };
1231}
1232
1233#[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 stream.read_buf = if buf.is_null() || size == 0 {
1247 if size == 0 {
1248 size = BUFSIZ as usize;
1249 }
1250 Buffer::Owned(vec![0; size])
1254 } 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#[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 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 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 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 if unsafe {
1302 #[allow(deprecated)]
1303 tmpnam_inner(out_buf, dirname_len + 1 + prefix_len)
1304 }
1305 .is_null()
1306 {
1307 unsafe { platform::free(out_buf.cast()) };
1309 out_buf = ptr::null_mut();
1310 }
1311 }
1312
1313 out_buf
1314}
1315
1316#[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) {}; }
1332
1333 if fp.is_null()
1334 && let Ok(()) = Sys::close(fd)
1335 {}; fp
1338}
1339
1340#[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#[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#[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#[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#[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 f.reference = true;
1421
1422 unsafe { printf::printf(f, CStr::from_ptr(format), ap) }
1423}
1424
1425#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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}