Serenity Operating System
at master 1397 lines 35 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <AK/BuiltinWrappers.h> 9#include <AK/DeprecatedString.h> 10#include <AK/Format.h> 11#include <AK/PrintfImplementation.h> 12#include <AK/ScopedValueRollback.h> 13#include <AK/StdLibExtras.h> 14#include <assert.h> 15#include <bits/mutex_locker.h> 16#include <bits/stdio_file_implementation.h> 17#include <errno.h> 18#include <fcntl.h> 19#include <stdarg.h> 20#include <stdio.h> 21#include <stdio_ext.h> 22#include <stdlib.h> 23#include <string.h> 24#include <sys/internals.h> 25#include <sys/types.h> 26#include <sys/wait.h> 27#include <syscall.h> 28#include <unistd.h> 29 30static constinit pthread_mutex_t s_open_streams_lock = __PTHREAD_MUTEX_INITIALIZER; 31 32// The list of open files is initialized in __stdio_init. 33// We cannot rely on global constructors to initialize it, because it must 34// be initialized before other global constructors run. Similarly, we cannot 35// allow global destructors to destruct it. 36alignas(FILE::List) static u8 s_open_streams_storage[sizeof(FILE::List)]; 37static FILE::List* const s_open_streams = reinterpret_cast<FILE::List*>(s_open_streams_storage); 38 39FILE::~FILE() 40{ 41 bool already_closed = m_fd == -1; 42 VERIFY(already_closed); 43} 44 45FILE* FILE::create(int fd, int mode) 46{ 47 void* file_location = calloc(1, sizeof(FILE)); 48 if (file_location == nullptr) 49 return nullptr; 50 auto* file = new (file_location) FILE(fd, mode); 51 LibC::MutexLocker locker(s_open_streams_lock); 52 s_open_streams->append(*file); 53 return file; 54} 55 56bool FILE::close() 57{ 58 bool flush_ok = flush(); 59 int rc = ::close(m_fd); 60 m_fd = -1; 61 if (!flush_ok) { 62 // Restore the original error from flush(). 63 errno = m_error; 64 } 65 return flush_ok && rc == 0; 66} 67 68bool FILE::flush() 69{ 70 if (m_mode & O_WRONLY && m_buffer.may_use()) { 71 // When open for writing, write out all the buffered data. 72 while (m_buffer.is_not_empty()) { 73 bool ok = write_from_buffer(); 74 if (!ok) 75 return false; 76 } 77 } 78 if (m_mode & O_RDONLY) { 79 // When open for reading, just drop the buffered data. 80 if constexpr (sizeof(size_t) >= sizeof(off_t)) 81 VERIFY(m_buffer.buffered_size() <= NumericLimits<off_t>::max()); 82 off_t had_buffered = static_cast<off_t>(m_buffer.buffered_size()); 83 m_buffer.drop(); 84 // Attempt to reset the underlying file position to what the user 85 // expects. 86 if (lseek(m_fd, -had_buffered, SEEK_CUR) < 0) { 87 if (errno == ESPIPE) { 88 // We can't set offset on this file; oh well, the user will just 89 // have to cope. 90 errno = 0; 91 } else { 92 return false; 93 } 94 } 95 } 96 97 return true; 98} 99 100void FILE::purge() 101{ 102 m_buffer.drop(); 103} 104 105size_t FILE::pending() 106{ 107 if (m_mode & O_RDONLY) { 108 return 0; 109 } 110 111 // FIXME: Check if our buffer is a write buffer, and only count those bytes. 112 return m_buffer.buffered_size(); 113} 114 115ssize_t FILE::do_read(u8* data, size_t size) 116{ 117 int nread = ::read(m_fd, data, size); 118 119 if (nread < 0) { 120 m_error = errno; 121 } else if (nread == 0) { 122 m_eof = true; 123 } 124 return nread; 125} 126 127ssize_t FILE::do_write(u8 const* data, size_t size) 128{ 129 int nwritten = ::write(m_fd, data, size); 130 131 if (nwritten < 0) 132 m_error = errno; 133 return nwritten; 134} 135 136bool FILE::read_into_buffer() 137{ 138 m_buffer.realize(m_fd); 139 140 size_t available_size; 141 u8* data = m_buffer.begin_enqueue(available_size); 142 // If we want to read, the buffer must have some space! 143 VERIFY(available_size); 144 145 ssize_t nread = do_read(data, available_size); 146 147 if (nread <= 0) 148 return false; 149 150 m_buffer.did_enqueue(nread); 151 return true; 152} 153 154bool FILE::write_from_buffer() 155{ 156 size_t size; 157 u8 const* data = m_buffer.begin_dequeue(size); 158 // If we want to write, the buffer must have something in it! 159 VERIFY(size); 160 161 ssize_t nwritten = do_write(data, size); 162 163 if (nwritten < 0) 164 return false; 165 166 m_buffer.did_dequeue(nwritten); 167 return true; 168} 169 170size_t FILE::read(u8* data, size_t size) 171{ 172 size_t total_read = 0; 173 174 m_flags |= Flags::LastRead; 175 m_flags &= ~Flags::LastWrite; 176 177 while (size > 0) { 178 size_t actual_size; 179 180 if (m_buffer.may_use()) { 181 // Let's see if the buffer has something queued for us. 182 size_t queued_size; 183 u8 const* queued_data = m_buffer.begin_dequeue(queued_size); 184 if (queued_size == 0) { 185 // Nothing buffered; we're going to have to read some. 186 bool read_some_more = read_into_buffer(); 187 if (read_some_more) { 188 // Great, now try this again. 189 continue; 190 } 191 return total_read; 192 } 193 actual_size = min(size, queued_size); 194 memcpy(data, queued_data, actual_size); 195 m_buffer.did_dequeue(actual_size); 196 } else { 197 // Read directly into the user buffer. 198 ssize_t nread = do_read(data, size); 199 if (nread <= 0) 200 return total_read; 201 actual_size = nread; 202 } 203 204 total_read += actual_size; 205 data += actual_size; 206 size -= actual_size; 207 } 208 209 return total_read; 210} 211 212size_t FILE::write(u8 const* data, size_t size) 213{ 214 size_t total_written = 0; 215 216 m_flags &= ~Flags::LastRead; 217 m_flags |= Flags::LastWrite; 218 219 while (size > 0) { 220 size_t actual_size; 221 222 if (m_buffer.may_use()) { 223 m_buffer.realize(m_fd); 224 // Try writing into the buffer. 225 size_t available_size; 226 u8* buffer_data = m_buffer.begin_enqueue(available_size); 227 if (available_size == 0) { 228 // There's no space in the buffer; we're going to free some. 229 bool freed_some_space = write_from_buffer(); 230 if (freed_some_space) { 231 // Great, now try this again. 232 continue; 233 } 234 return total_written; 235 } 236 actual_size = min(size, available_size); 237 memcpy(buffer_data, data, actual_size); 238 m_buffer.did_enqueue(actual_size); 239 // See if we have to flush it. 240 if (m_buffer.mode() == _IOLBF) { 241 bool includes_newline = memchr(data, '\n', actual_size); 242 if (includes_newline) 243 flush(); 244 } 245 } else { 246 // Write directly from the user buffer. 247 ssize_t nwritten = do_write(data, size); 248 if (nwritten < 0) 249 return total_written; 250 actual_size = nwritten; 251 } 252 253 total_written += actual_size; 254 data += actual_size; 255 size -= actual_size; 256 } 257 258 return total_written; 259} 260 261template<typename T> 262bool FILE::gets(T* data, size_t size) 263{ 264 // gets() is a lot like read(), but it is different enough in how it 265 // processes newlines and null-terminates the buffer that it deserves a 266 // separate implementation. 267 size_t total_read = 0; 268 269 if (size == 0) 270 return false; 271 272 m_flags |= Flags::LastRead; 273 m_flags &= ~Flags::LastWrite; 274 275 while (size > 1) { 276 if (m_buffer.may_use()) { 277 // Let's see if the buffer has something queued for us. 278 size_t queued_size; 279 T const* queued_data = bit_cast<T const*>(m_buffer.begin_dequeue(queued_size)); 280 queued_size /= sizeof(T); 281 if (queued_size == 0) { 282 // Nothing buffered; we're going to have to read some. 283 bool read_some_more = read_into_buffer(); 284 if (read_some_more) { 285 // Great, now try this again. 286 continue; 287 } 288 *data = 0; 289 return total_read > 0; 290 } 291 size_t actual_size = min(size - 1, queued_size); 292 T const* newline = nullptr; 293 for (size_t i = 0; i < actual_size; ++i) { 294 if (queued_data[i] != '\n') 295 continue; 296 297 newline = &queued_data[i]; 298 actual_size = i + 1; 299 break; 300 } 301 memcpy(data, queued_data, actual_size * sizeof(T)); 302 m_buffer.did_dequeue(actual_size * sizeof(T)); 303 total_read += actual_size; 304 data += actual_size; 305 size -= actual_size; 306 if (newline) 307 break; 308 } else { 309 // Sadly, we have to actually read these characters one by one. 310 T value; 311 ssize_t nread = do_read(bit_cast<u8*>(&value), sizeof(T)); 312 if (nread <= 0) { 313 *data = 0; 314 return total_read > 0; 315 } 316 VERIFY(nread == sizeof(T)); 317 *data = value; 318 total_read++; 319 data++; 320 size--; 321 if (value == '\n') 322 break; 323 } 324 } 325 326 *data = 0; 327 return total_read > 0; 328} 329 330int FILE::seek(off_t offset, int whence) 331{ 332 bool ok = flush(); 333 if (!ok) 334 return -1; 335 336 off_t off = lseek(m_fd, offset, whence); 337 if (off < 0) { 338 // Note: do not set m_error. 339 return off; 340 } 341 342 m_eof = false; 343 return 0; 344} 345 346off_t FILE::tell() 347{ 348 bool ok = flush(); 349 if (!ok) 350 return -1; 351 352 return lseek(m_fd, 0, SEEK_CUR); 353} 354 355void FILE::reopen(int fd, int mode) 356{ 357 // Dr. POSIX says: "Failure to flush or close the file descriptor 358 // successfully shall be ignored" 359 // and so we ignore any failures these two might have. 360 flush(); 361 close(); 362 363 // Just in case flush() and close() didn't drop the buffer. 364 m_buffer.drop(); 365 366 m_fd = fd; 367 m_mode = mode; 368 m_error = 0; 369 m_eof = false; 370} 371 372u8 const* FILE::readptr(size_t& available_size) 373{ 374 return m_buffer.begin_dequeue(available_size); 375} 376 377void FILE::readptr_increase(size_t increment) 378{ 379 m_buffer.did_dequeue(increment); 380} 381 382FILE::Buffer::~Buffer() 383{ 384 if (m_data_is_malloced) 385 free(m_data); 386} 387 388bool FILE::Buffer::may_use() const 389{ 390 return m_ungotten != 0u || m_mode != _IONBF; 391} 392 393void FILE::Buffer::realize(int fd) 394{ 395 if (m_mode == -1) 396 m_mode = isatty(fd) ? _IOLBF : _IOFBF; 397 398 if (m_mode != _IONBF && m_data == nullptr) { 399 m_data = reinterpret_cast<u8*>(malloc(m_capacity)); 400 m_data_is_malloced = true; 401 } 402} 403 404void FILE::Buffer::setbuf(u8* data, int mode, size_t size) 405{ 406 drop(); 407 m_mode = mode; 408 if (data != nullptr) { 409 m_data = data; 410 m_capacity = size; 411 } 412} 413 414void FILE::Buffer::drop() 415{ 416 if (m_data_is_malloced) { 417 free(m_data); 418 m_data = nullptr; 419 m_data_is_malloced = false; 420 } 421 m_begin = m_end = 0; 422 m_empty = true; 423 m_ungotten = 0u; 424} 425 426size_t FILE::Buffer::buffered_size() const 427{ 428 // Note: does not include the ungetc() buffer. 429 430 if (m_empty) 431 return 0; 432 433 if (m_begin < m_end) 434 return m_end - m_begin; 435 else 436 return m_capacity - (m_begin - m_end); 437} 438 439u8 const* FILE::Buffer::begin_dequeue(size_t& available_size) const 440{ 441 if (m_ungotten != 0u) { 442 auto available_bytes = count_trailing_zeroes(m_ungotten) + 1; 443 available_size = available_bytes; 444 return &m_unget_buffer[unget_buffer_size - available_bytes]; 445 } 446 447 if (m_empty) { 448 available_size = 0; 449 return nullptr; 450 } 451 452 if (m_begin < m_end) 453 available_size = m_end - m_begin; 454 else 455 available_size = m_capacity - m_begin; 456 457 return &m_data[m_begin]; 458} 459 460void FILE::Buffer::did_dequeue(size_t actual_size) 461{ 462 VERIFY(actual_size > 0); 463 464 if (m_ungotten != 0u) { 465 VERIFY(actual_size <= static_cast<size_t>(popcount(m_ungotten & ungotten_mask))); 466 auto available_bytes = count_trailing_zeroes(m_ungotten); 467 m_ungotten &= (0xffffffffu << (actual_size + available_bytes)); 468 return; 469 } 470 471 m_begin += actual_size; 472 473 VERIFY(m_begin <= m_capacity); 474 if (m_begin == m_capacity) { 475 // Wrap around. 476 m_begin = 0; 477 } 478 479 if (m_begin == m_end) { 480 m_empty = true; 481 // As an optimization, move both pointers to the beginning of the 482 // buffer, so that more consecutive space is available next time. 483 m_begin = m_end = 0; 484 } 485} 486 487u8* FILE::Buffer::begin_enqueue(size_t& available_size) const 488{ 489 VERIFY(m_data != nullptr); 490 491 if (m_begin < m_end || m_empty) 492 available_size = m_capacity - m_end; 493 else 494 available_size = m_begin - m_end; 495 496 return const_cast<u8*>(&m_data[m_end]); 497} 498 499void FILE::Buffer::did_enqueue(size_t actual_size) 500{ 501 VERIFY(m_data != nullptr); 502 VERIFY(actual_size > 0); 503 504 m_end += actual_size; 505 506 VERIFY(m_end <= m_capacity); 507 if (m_end == m_capacity) { 508 // Wrap around. 509 m_end = 0; 510 } 511 512 m_empty = false; 513} 514 515bool FILE::Buffer::enqueue_front(u8 byte) 516{ 517 size_t placement_index; 518 if (m_ungotten == 0u) { 519 placement_index = 3u; 520 m_ungotten = 1u; 521 } else { 522 auto first_zero_index = count_trailing_zeroes(bit_cast<u32>(~m_ungotten)); // Thanks C. 523 if (first_zero_index >= unget_buffer_size) { 524 // Sorry, the place is already taken! 525 return false; 526 } 527 placement_index = unget_buffer_size - first_zero_index - 1; 528 m_ungotten |= (1 << first_zero_index); 529 } 530 531 m_unget_buffer[placement_index] = byte; 532 return true; 533} 534 535void FILE::lock() 536{ 537 pthread_mutex_lock(&m_mutex); 538} 539 540void FILE::unlock() 541{ 542 pthread_mutex_unlock(&m_mutex); 543} 544 545extern "C" { 546 547alignas(FILE) static u8 default_streams[3][sizeof(FILE)]; 548FILE* stdin = reinterpret_cast<FILE*>(&default_streams[0]); 549FILE* stdout = reinterpret_cast<FILE*>(&default_streams[1]); 550FILE* stderr = reinterpret_cast<FILE*>(&default_streams[2]); 551 552void __stdio_init() 553{ 554 new (s_open_streams) FILE::List(); 555 new (stdin) FILE(0, O_RDONLY); 556 new (stdout) FILE(1, O_WRONLY); 557 new (stderr) FILE(2, O_WRONLY); 558 stderr->setbuf(nullptr, _IONBF, 0); 559 s_open_streams->append(*stdin); 560 s_open_streams->append(*stdout); 561 s_open_streams->append(*stderr); 562 __stdio_is_initialized = true; 563} 564 565// https://pubs.opengroup.org/onlinepubs/9699919799/functions/setvbuf.html 566int setvbuf(FILE* stream, char* buf, int mode, size_t size) 567{ 568 VERIFY(stream); 569 ScopedFileLock lock(stream); 570 if (mode != _IONBF && mode != _IOLBF && mode != _IOFBF) { 571 errno = EINVAL; 572 return -1; 573 } 574 stream->setbuf(reinterpret_cast<u8*>(buf), mode, size); 575 return 0; 576} 577 578// https://pubs.opengroup.org/onlinepubs/9699919799/functions/setbuf.html 579void setbuf(FILE* stream, char* buf) 580{ 581 setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ); 582} 583 584void setlinebuf(FILE* stream) 585{ 586 setvbuf(stream, nullptr, _IOLBF, 0); 587} 588 589// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fileno.html 590int fileno(FILE* stream) 591{ 592 VERIFY(stream); 593 ScopedFileLock lock(stream); 594 return stream->fileno(); 595} 596 597// https://pubs.opengroup.org/onlinepubs/9699919799/functions/feof.html 598int feof(FILE* stream) 599{ 600 VERIFY(stream); 601 ScopedFileLock lock(stream); 602 return stream->eof(); 603} 604 605// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html 606int fflush(FILE* stream) 607{ 608 if (!stream) { 609 int rc = 0; 610 LibC::MutexLocker locker(s_open_streams_lock); 611 for (auto& file : *s_open_streams) { 612 ScopedFileLock lock(&file); 613 rc = file.flush() ? rc : EOF; 614 } 615 return rc; 616 } 617 ScopedFileLock lock(stream); 618 return stream->flush() ? 0 : EOF; 619} 620 621// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fgets.html 622char* fgets(char* buffer, int size, FILE* stream) 623{ 624 VERIFY(stream); 625 ScopedFileLock lock(stream); 626 bool ok = stream->gets(reinterpret_cast<u8*>(buffer), size); 627 return ok ? buffer : nullptr; 628} 629 630// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fgetc.html 631int fgetc(FILE* stream) 632{ 633 VERIFY(stream); 634 unsigned char ch; 635 size_t nread = fread(&ch, sizeof(unsigned char), 1, stream); 636 if (nread == 1) { 637 return ch; 638 } 639 return EOF; 640} 641 642int fgetc_unlocked(FILE* stream) 643{ 644 VERIFY(stream); 645 unsigned char ch; 646 size_t nread = fread_unlocked(&ch, sizeof(unsigned char), 1, stream); 647 if (nread == 1) 648 return ch; 649 return EOF; 650} 651 652// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc.html 653int getc(FILE* stream) 654{ 655 return fgetc(stream); 656} 657 658int getc_unlocked(FILE* stream) 659{ 660 return fgetc_unlocked(stream); 661} 662 663// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getchar.html 664int getchar() 665{ 666 return getc(stdin); 667} 668 669// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html 670ssize_t getdelim(char** lineptr, size_t* n, int delim, FILE* stream) 671{ 672 if (!lineptr || !n) { 673 errno = EINVAL; 674 return -1; 675 } 676 677 if (*lineptr == nullptr || *n == 0) { 678 *n = BUFSIZ; 679 if ((*lineptr = static_cast<char*>(malloc(*n))) == nullptr) { 680 return -1; 681 } 682 } 683 684 char* ptr; 685 char* eptr; 686 for (ptr = *lineptr, eptr = *lineptr + *n;;) { 687 int c = fgetc(stream); 688 if (c == -1) { 689 if (feof(stream)) { 690 *ptr = '\0'; 691 return ptr == *lineptr ? -1 : ptr - *lineptr; 692 } else { 693 return -1; 694 } 695 } 696 *ptr++ = c; 697 if (c == delim) { 698 *ptr = '\0'; 699 return ptr - *lineptr; 700 } 701 if (ptr + 2 >= eptr) { 702 char* nbuf; 703 size_t nbuf_sz = *n * 2; 704 ssize_t d = ptr - *lineptr; 705 if ((nbuf = static_cast<char*>(realloc(*lineptr, nbuf_sz))) == nullptr) { 706 return -1; 707 } 708 *lineptr = nbuf; 709 *n = nbuf_sz; 710 eptr = nbuf + nbuf_sz; 711 ptr = nbuf + d; 712 } 713 } 714} 715 716// https://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html 717ssize_t getline(char** lineptr, size_t* n, FILE* stream) 718{ 719 return getdelim(lineptr, n, '\n', stream); 720} 721 722// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ungetc.html 723int ungetc(int c, FILE* stream) 724{ 725 VERIFY(stream); 726 ScopedFileLock lock(stream); 727 bool ok = stream->ungetc(c); 728 return ok ? c : EOF; 729} 730 731// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fputc.html 732int fputc(int ch, FILE* stream) 733{ 734 VERIFY(stream); 735 u8 byte = ch; 736 ScopedFileLock lock(stream); 737 size_t nwritten = stream->write(&byte, 1); 738 if (nwritten == 0) 739 return EOF; 740 VERIFY(nwritten == 1); 741 return byte; 742} 743 744// https://pubs.opengroup.org/onlinepubs/9699919799/functions/putc.html 745int putc(int ch, FILE* stream) 746{ 747 return fputc(ch, stream); 748} 749 750// https://pubs.opengroup.org/onlinepubs/9699919799/functions/putchar.html 751int putchar(int ch) 752{ 753 return putc(ch, stdout); 754} 755 756// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fputs.html 757int fputs(char const* s, FILE* stream) 758{ 759 VERIFY(stream); 760 size_t len = strlen(s); 761 ScopedFileLock lock(stream); 762 size_t nwritten = stream->write(reinterpret_cast<u8 const*>(s), len); 763 if (nwritten < len) 764 return EOF; 765 return 1; 766} 767 768// https://pubs.opengroup.org/onlinepubs/9699919799/functions/puts.html 769int puts(char const* s) 770{ 771 int rc = fputs(s, stdout); 772 if (rc == EOF) 773 return EOF; 774 return fputc('\n', stdout); 775} 776 777// https://pubs.opengroup.org/onlinepubs/9699919799/functions/clearerr.html 778void clearerr(FILE* stream) 779{ 780 VERIFY(stream); 781 ScopedFileLock lock(stream); 782 stream->clear_err(); 783} 784 785// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ferror.html 786int ferror(FILE* stream) 787{ 788 VERIFY(stream); 789 ScopedFileLock lock(stream); 790 return stream->error(); 791} 792 793size_t fread_unlocked(void* ptr, size_t size, size_t nmemb, FILE* stream) 794{ 795 VERIFY(stream); 796 VERIFY(!Checked<size_t>::multiplication_would_overflow(size, nmemb)); 797 798 size_t nread = stream->read(reinterpret_cast<u8*>(ptr), size * nmemb); 799 if (!nread) 800 return 0; 801 return nread / size; 802} 803 804// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fread.html 805size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) 806{ 807 VERIFY(stream); 808 ScopedFileLock lock(stream); 809 return fread_unlocked(ptr, size, nmemb, stream); 810} 811 812// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fwrite.html 813size_t fwrite(void const* ptr, size_t size, size_t nmemb, FILE* stream) 814{ 815 VERIFY(stream); 816 VERIFY(!Checked<size_t>::multiplication_would_overflow(size, nmemb)); 817 818 ScopedFileLock lock(stream); 819 size_t nwritten = stream->write(reinterpret_cast<u8 const*>(ptr), size * nmemb); 820 if (!nwritten) 821 return 0; 822 return nwritten / size; 823} 824 825// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fseek.html 826int fseek(FILE* stream, long offset, int whence) 827{ 828 VERIFY(stream); 829 ScopedFileLock lock(stream); 830 return stream->seek(offset, whence); 831} 832 833// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fseeko.html 834int fseeko(FILE* stream, off_t offset, int whence) 835{ 836 VERIFY(stream); 837 ScopedFileLock lock(stream); 838 return stream->seek(offset, whence); 839} 840 841// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftell.html 842long ftell(FILE* stream) 843{ 844 VERIFY(stream); 845 ScopedFileLock lock(stream); 846 return stream->tell(); 847} 848 849// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftello.html 850off_t ftello(FILE* stream) 851{ 852 VERIFY(stream); 853 ScopedFileLock lock(stream); 854 return stream->tell(); 855} 856 857// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fgetpos.html 858int fgetpos(FILE* stream, fpos_t* pos) 859{ 860 VERIFY(stream); 861 VERIFY(pos); 862 863 ScopedFileLock lock(stream); 864 off_t val = stream->tell(); 865 if (val == -1L) 866 return 1; 867 868 *pos = val; 869 return 0; 870} 871 872// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsetpos.html 873int fsetpos(FILE* stream, fpos_t const* pos) 874{ 875 VERIFY(stream); 876 VERIFY(pos); 877 878 ScopedFileLock lock(stream); 879 return stream->seek(*pos, SEEK_SET); 880} 881 882// https://pubs.opengroup.org/onlinepubs/9699919799/functions/rewind.html 883void rewind(FILE* stream) 884{ 885 fseek(stream, 0, SEEK_SET); 886 clearerr(stream); 887} 888 889ALWAYS_INLINE void stdout_putch(char*&, char ch) 890{ 891 putchar(ch); 892} 893 894// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vfprintf.html 895int vfprintf(FILE* stream, char const* fmt, va_list ap) 896{ 897 return printf_internal([stream](auto, char ch) { fputc(ch, stream); }, nullptr, fmt, ap); 898} 899 900// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html 901int fprintf(FILE* stream, char const* fmt, ...) 902{ 903 va_list ap; 904 va_start(ap, fmt); 905 int ret = vfprintf(stream, fmt, ap); 906 va_end(ap); 907 return ret; 908} 909 910// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vprintf.html 911int vprintf(char const* fmt, va_list ap) 912{ 913 return printf_internal(stdout_putch, nullptr, fmt, ap); 914} 915 916// https://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html 917int printf(char const* fmt, ...) 918{ 919 va_list ap; 920 va_start(ap, fmt); 921 int ret = vprintf(fmt, ap); 922 va_end(ap); 923 return ret; 924} 925 926// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vasprintf.html 927int vasprintf(char** strp, char const* fmt, va_list ap) 928{ 929 StringBuilder builder; 930 builder.appendvf(fmt, ap); 931 VERIFY(builder.length() <= NumericLimits<int>::max()); 932 int length = builder.length(); 933 *strp = strdup(builder.to_deprecated_string().characters()); 934 return length; 935} 936 937// https://pubs.opengroup.org/onlinepubs/9699919799/functions/asprintf.html 938int asprintf(char** strp, char const* fmt, ...) 939{ 940 StringBuilder builder; 941 va_list ap; 942 va_start(ap, fmt); 943 builder.appendvf(fmt, ap); 944 va_end(ap); 945 VERIFY(builder.length() <= NumericLimits<int>::max()); 946 int length = builder.length(); 947 *strp = strdup(builder.to_deprecated_string().characters()); 948 return length; 949} 950 951static void buffer_putch(char*& bufptr, char ch) 952{ 953 *bufptr++ = ch; 954} 955 956// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vsprintf.html 957int vsprintf(char* buffer, char const* fmt, va_list ap) 958{ 959 int ret = printf_internal(buffer_putch, buffer, fmt, ap); 960 buffer[ret] = '\0'; 961 return ret; 962} 963 964// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sprintf.html 965int sprintf(char* buffer, char const* fmt, ...) 966{ 967 va_list ap; 968 va_start(ap, fmt); 969 int ret = vsprintf(buffer, fmt, ap); 970 va_end(ap); 971 return ret; 972} 973 974// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vsnprintf.html 975int vsnprintf(char* buffer, size_t size, char const* fmt, va_list ap) 976{ 977 size_t space_remaining = 0; 978 if (size) { 979 space_remaining = size - 1; 980 } else { 981 space_remaining = 0; 982 } 983 auto sized_buffer_putch = [&](char*& bufptr, char ch) { 984 if (space_remaining) { 985 *bufptr++ = ch; 986 --space_remaining; 987 } 988 }; 989 int ret = printf_internal(sized_buffer_putch, buffer, fmt, ap); 990 if (space_remaining) { 991 buffer[ret] = '\0'; 992 } else if (size > 0) { 993 buffer[size - 1] = '\0'; 994 } 995 return ret; 996} 997 998// https://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html 999int snprintf(char* buffer, size_t size, char const* fmt, ...) 1000{ 1001 va_list ap; 1002 va_start(ap, fmt); 1003 int ret = vsnprintf(buffer, size, fmt, ap); 1004 va_end(ap); 1005 return ret; 1006} 1007 1008// https://pubs.opengroup.org/onlinepubs/9699919799/functions/perror.html 1009void perror(char const* s) 1010{ 1011 int saved_errno = errno; 1012 dbgln("perror(): {}: {}", s, strerror(saved_errno)); 1013 warnln("{}: {}", s, strerror(saved_errno)); 1014} 1015 1016static int parse_mode(char const* mode) 1017{ 1018 int flags = 0; 1019 1020 // NOTE: rt is a non-standard mode which opens a file for read, explicitly 1021 // specifying that it's a text file 1022 for (auto* ptr = mode; *ptr; ++ptr) { 1023 switch (*ptr) { 1024 case 'r': 1025 flags |= O_RDONLY; 1026 break; 1027 case 'w': 1028 flags |= O_WRONLY | O_CREAT | O_TRUNC; 1029 break; 1030 case 'a': 1031 flags |= O_WRONLY | O_APPEND | O_CREAT; 1032 break; 1033 case '+': 1034 flags |= O_RDWR; 1035 break; 1036 case 'e': 1037 flags |= O_CLOEXEC; 1038 break; 1039 case 'b': 1040 // Ok... 1041 break; 1042 case 't': 1043 // Ok... 1044 break; 1045 default: 1046 dbgln("Potentially unsupported fopen mode _{}_ (because of '{}')", mode, *ptr); 1047 } 1048 } 1049 1050 return flags; 1051} 1052 1053// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html 1054FILE* fopen(char const* pathname, char const* mode) 1055{ 1056 int flags = parse_mode(mode); 1057 int fd = open(pathname, flags, 0666); 1058 if (fd < 0) 1059 return nullptr; 1060 return FILE::create(fd, flags); 1061} 1062 1063// https://pubs.opengroup.org/onlinepubs/9699919799/functions/freopen.html 1064FILE* freopen(char const* pathname, char const* mode, FILE* stream) 1065{ 1066 VERIFY(stream); 1067 if (!pathname) { 1068 // FIXME: Someone should probably implement this path. 1069 TODO(); 1070 } 1071 1072 int flags = parse_mode(mode); 1073 int fd = open(pathname, flags, 0666); 1074 if (fd < 0) 1075 return nullptr; 1076 1077 stream->reopen(fd, flags); 1078 return stream; 1079} 1080 1081// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopen.html 1082FILE* fdopen(int fd, char const* mode) 1083{ 1084 int flags = parse_mode(mode); 1085 // FIXME: Verify that the mode matches how fd is already open. 1086 if (fd < 0) 1087 return nullptr; 1088 return FILE::create(fd, flags); 1089} 1090 1091// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fmemopen.html 1092FILE* fmemopen(void*, size_t, char const*) 1093{ 1094 // FIXME: Implement me :^) 1095 TODO(); 1096} 1097 1098static inline bool is_default_stream(FILE* stream) 1099{ 1100 return stream == stdin || stream == stdout || stream == stderr; 1101} 1102 1103// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fclose.html 1104int fclose(FILE* stream) 1105{ 1106 VERIFY(stream); 1107 bool ok; 1108 1109 { 1110 ScopedFileLock lock(stream); 1111 ok = stream->close(); 1112 } 1113 ScopedValueRollback errno_restorer(errno); 1114 1115 { 1116 LibC::MutexLocker locker(s_open_streams_lock); 1117 s_open_streams->remove(*stream); 1118 } 1119 stream->~FILE(); 1120 if (!is_default_stream(stream)) 1121 free(stream); 1122 1123 return ok ? 0 : EOF; 1124} 1125 1126// https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html 1127int rename(char const* oldpath, char const* newpath) 1128{ 1129 return renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath); 1130} 1131 1132// https://pubs.opengroup.org/onlinepubs/9699919799/functions/renameat.html 1133int renameat(int olddirfd, char const* oldpath, int newdirfd, char const* newpath) 1134{ 1135 if (!oldpath || !newpath) { 1136 errno = EFAULT; 1137 return -1; 1138 } 1139 Syscall::SC_rename_params params { olddirfd, { oldpath, strlen(oldpath) }, newdirfd, { newpath, strlen(newpath) } }; 1140 int rc = syscall(SC_rename, &params); 1141 __RETURN_WITH_ERRNO(rc, rc, -1); 1142} 1143 1144void dbgputstr(char const* characters, size_t length) 1145{ 1146 syscall(SC_dbgputstr, characters, length); 1147} 1148 1149// https://pubs.opengroup.org/onlinepubs/9699919799/functions/tmpnam.html 1150char* tmpnam(char*) 1151{ 1152 dbgln("FIXME: Implement tmpnam()"); 1153 TODO(); 1154} 1155 1156// https://pubs.opengroup.org/onlinepubs/9699919799/functions/popen.html 1157FILE* popen(char const* command, char const* type) 1158{ 1159 if (!type || (*type != 'r' && *type != 'w')) { 1160 errno = EINVAL; 1161 return nullptr; 1162 } 1163 1164 int pipe_fds[2]; 1165 1166 if (pipe(pipe_fds) < 0) { 1167 ScopedValueRollback rollback(errno); 1168 perror("pipe"); 1169 return nullptr; 1170 } 1171 1172 pid_t child_pid = fork(); 1173 if (child_pid < 0) { 1174 ScopedValueRollback rollback(errno); 1175 perror("fork"); 1176 close(pipe_fds[0]); 1177 close(pipe_fds[1]); 1178 return nullptr; 1179 } else if (child_pid == 0) { 1180 if (*type == 'r') { 1181 if (dup2(pipe_fds[1], STDOUT_FILENO) < 0) { 1182 perror("dup2"); 1183 exit(1); 1184 } 1185 close(pipe_fds[0]); 1186 close(pipe_fds[1]); 1187 } else if (*type == 'w') { 1188 if (dup2(pipe_fds[0], STDIN_FILENO) < 0) { 1189 perror("dup2"); 1190 exit(1); 1191 } 1192 close(pipe_fds[0]); 1193 close(pipe_fds[1]); 1194 } 1195 1196 if (execl("/bin/sh", "sh", "-c", command, nullptr) < 0) 1197 perror("execl"); 1198 exit(1); 1199 } 1200 1201 FILE* file = nullptr; 1202 if (*type == 'r') { 1203 file = FILE::create(pipe_fds[0], O_RDONLY); 1204 close(pipe_fds[1]); 1205 } else if (*type == 'w') { 1206 file = FILE::create(pipe_fds[1], O_WRONLY); 1207 close(pipe_fds[0]); 1208 } 1209 1210 file->set_popen_child(child_pid); 1211 return file; 1212} 1213 1214// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pclose.html 1215int pclose(FILE* stream) 1216{ 1217 VERIFY(stream); 1218 VERIFY(stream->popen_child() != 0); 1219 1220 int wstatus = 0; 1221 if (waitpid(stream->popen_child(), &wstatus, 0) < 0) 1222 return -1; 1223 1224 return wstatus; 1225} 1226 1227// https://pubs.opengroup.org/onlinepubs/9699919799/functions/remove.html 1228int remove(char const* pathname) 1229{ 1230 if (unlink(pathname) < 0) { 1231 if (errno == EISDIR) 1232 return rmdir(pathname); 1233 return -1; 1234 } 1235 return 0; 1236} 1237 1238// https://pubs.opengroup.org/onlinepubs/9699919799/functions/scanf.html 1239int scanf(char const* fmt, ...) 1240{ 1241 va_list ap; 1242 va_start(ap, fmt); 1243 int count = vfscanf(stdin, fmt, ap); 1244 va_end(ap); 1245 return count; 1246} 1247 1248// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html 1249int fscanf(FILE* stream, char const* fmt, ...) 1250{ 1251 va_list ap; 1252 va_start(ap, fmt); 1253 int count = vfscanf(stream, fmt, ap); 1254 va_end(ap); 1255 return count; 1256} 1257 1258// https://pubs.opengroup.org/onlinepubs/9699919799/functions/sscanf.html 1259int sscanf(char const* buffer, char const* fmt, ...) 1260{ 1261 va_list ap; 1262 va_start(ap, fmt); 1263 int count = vsscanf(buffer, fmt, ap); 1264 va_end(ap); 1265 return count; 1266} 1267 1268// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vfscanf.html 1269int vfscanf(FILE* stream, char const* fmt, va_list ap) 1270{ 1271 char buffer[BUFSIZ]; 1272 if (!fgets(buffer, sizeof(buffer) - 1, stream)) 1273 return -1; 1274 return vsscanf(buffer, fmt, ap); 1275} 1276 1277// https://pubs.opengroup.org/onlinepubs/9699919799/functions/vscanf.html 1278int vscanf(char const* fmt, va_list ap) 1279{ 1280 return vfscanf(stdin, fmt, ap); 1281} 1282 1283// https://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html 1284void flockfile(FILE* filehandle) 1285{ 1286 VERIFY(filehandle); 1287 filehandle->lock(); 1288} 1289 1290// https://pubs.opengroup.org/onlinepubs/9699919799/functions/funlockfile.html 1291void funlockfile(FILE* filehandle) 1292{ 1293 VERIFY(filehandle); 1294 filehandle->unlock(); 1295} 1296 1297// https://pubs.opengroup.org/onlinepubs/9699919799/functions/tmpfile.html 1298FILE* tmpfile() 1299{ 1300 char tmp_path[] = "/tmp/XXXXXX"; 1301 int fd = mkstemp(tmp_path); 1302 if (fd < 0) 1303 return nullptr; 1304 // FIXME: instead of using this hack, implement with O_TMPFILE or similar 1305 unlink(tmp_path); 1306 return fdopen(fd, "rw"); 1307} 1308 1309// https://pubs.opengroup.org/onlinepubs/9699919799/functions/ctermid.html 1310char* ctermid(char* s) 1311{ 1312 static char tty_path[L_ctermid] = "/dev/tty"; 1313 if (s) 1314 return strcpy(s, tty_path); 1315 return tty_path; 1316} 1317 1318size_t __fpending(FILE* stream) 1319{ 1320 ScopedFileLock lock(stream); 1321 return stream->pending(); 1322} 1323 1324int __freading(FILE* stream) 1325{ 1326 ScopedFileLock lock(stream); 1327 1328 if ((stream->mode() & O_RDWR) == O_RDONLY) { 1329 return 1; 1330 } 1331 1332 return (stream->flags() & FILE::Flags::LastRead); 1333} 1334 1335int __fwriting(FILE* stream) 1336{ 1337 ScopedFileLock lock(stream); 1338 1339 if ((stream->mode() & O_RDWR) == O_WRONLY) { 1340 return 1; 1341 } 1342 1343 return (stream->flags() & FILE::Flags::LastWrite); 1344} 1345 1346void __fpurge(FILE* stream) 1347{ 1348 ScopedFileLock lock(stream); 1349 stream->purge(); 1350} 1351 1352size_t __freadahead(FILE* stream) 1353{ 1354 VERIFY(stream); 1355 1356 ScopedFileLock lock(stream); 1357 1358 size_t available_size; 1359 stream->readptr(available_size); 1360 return available_size; 1361} 1362 1363char const* __freadptr(FILE* stream, size_t* sizep) 1364{ 1365 VERIFY(stream); 1366 VERIFY(sizep); 1367 1368 ScopedFileLock lock(stream); 1369 1370 size_t available_size; 1371 u8 const* ptr = stream->readptr(available_size); 1372 1373 if (available_size == 0) 1374 return nullptr; 1375 1376 *sizep = available_size; 1377 return reinterpret_cast<char const*>(ptr); 1378} 1379 1380void __freadptrinc(FILE* stream, size_t increment) 1381{ 1382 VERIFY(stream); 1383 1384 ScopedFileLock lock(stream); 1385 1386 stream->readptr_increase(increment); 1387} 1388 1389void __fseterr(FILE* stream) 1390{ 1391 ScopedFileLock lock(stream); 1392 stream->set_err(); 1393} 1394} 1395 1396template bool FILE::gets<u8>(u8*, size_t); 1397template bool FILE::gets<u32>(u32*, size_t);