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