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