Serenity Operating System
at portability 709 lines 16 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/LogStream.h> 28#include <AK/PrintfImplementation.h> 29#include <AK/ScopedValueRollback.h> 30#include <AK/StdLibExtras.h> 31#include <Kernel/Syscall.h> 32#include <assert.h> 33#include <errno.h> 34#include <fcntl.h> 35#include <stdarg.h> 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39#include <sys/types.h> 40#include <sys/wait.h> 41#include <unistd.h> 42 43extern "C" { 44 45static FILE __default_streams[3]; 46FILE* stdin; 47FILE* stdout; 48FILE* stderr; 49 50void init_FILE(FILE& fp, int fd, int mode) 51{ 52 fp.fd = fd; 53 fp.buffer = fp.default_buffer; 54 fp.buffer_size = BUFSIZ; 55 fp.mode = mode; 56} 57 58static FILE* make_FILE(int fd) 59{ 60 auto* fp = (FILE*)malloc(sizeof(FILE)); 61 memset(fp, 0, sizeof(FILE)); 62 init_FILE(*fp, fd, isatty(fd)); 63 return fp; 64} 65 66void __stdio_init() 67{ 68 stdin = &__default_streams[0]; 69 stdout = &__default_streams[1]; 70 stderr = &__default_streams[2]; 71 init_FILE(*stdin, 0, isatty(0) ? _IOLBF : _IOFBF); 72 init_FILE(*stdout, 1, isatty(1) ? _IOLBF : _IOFBF); 73 init_FILE(*stderr, 2, _IONBF); 74} 75 76int setvbuf(FILE* stream, char* buf, int mode, size_t size) 77{ 78 if (mode != _IONBF && mode != _IOLBF && mode != _IOFBF) { 79 errno = EINVAL; 80 return -1; 81 } 82 stream->mode = mode; 83 if (buf) { 84 stream->buffer = buf; 85 stream->buffer_size = size; 86 } else { 87 stream->buffer = stream->default_buffer; 88 stream->buffer_size = BUFSIZ; 89 } 90 stream->buffer_index = 0; 91 return 0; 92} 93 94void setbuf(FILE* stream, char* buf) 95{ 96 setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ); 97} 98 99void setlinebuf(FILE* stream) 100{ 101 setvbuf(stream, nullptr, _IOLBF, 0); 102} 103 104int fileno(FILE* stream) 105{ 106 assert(stream); 107 return stream->fd; 108} 109 110int feof(FILE* stream) 111{ 112 assert(stream); 113 return stream->eof; 114} 115 116int fflush(FILE* stream) 117{ 118 if (!stream) { 119 dbg() << "FIXME: fflush(nullptr) should flush all open streams"; 120 return 0; 121 } 122 if (!stream->buffer_index) 123 return 0; 124 int rc = write(stream->fd, stream->buffer, stream->buffer_index); 125 stream->buffer_index = 0; 126 stream->error = 0; 127 stream->eof = 0; 128 stream->have_ungotten = false; 129 stream->ungotten = 0; 130 if (rc < 0) { 131 stream->error = errno; 132 return EOF; 133 } 134 return 0; 135} 136 137char* fgets(char* buffer, int size, FILE* stream) 138{ 139 ASSERT(stream); 140 ASSERT(size); 141 ssize_t nread = 0; 142 while (nread < (size - 1)) { 143 int ch = fgetc(stream); 144 if (ch == EOF) 145 break; 146 buffer[nread++] = ch; 147 if (ch == '\n') 148 break; 149 } 150 if (nread) { 151 buffer[nread] = '\0'; 152 return buffer; 153 } 154 return nullptr; 155} 156 157int fgetc(FILE* stream) 158{ 159 assert(stream); 160 char ch; 161 size_t nread = fread(&ch, sizeof(char), 1, stream); 162 if (nread == 1) 163 return ch; 164 return EOF; 165} 166 167int getc(FILE* stream) 168{ 169 return fgetc(stream); 170} 171 172int getc_unlocked(FILE* stream) 173{ 174 return fgetc(stream); 175} 176 177int getchar() 178{ 179 return getc(stdin); 180} 181 182ssize_t getdelim(char** lineptr, size_t* n, int delim, FILE* stream) 183{ 184 char *ptr, *eptr; 185 if (*lineptr == nullptr || *n == 0) { 186 *n = BUFSIZ; 187 if ((*lineptr = static_cast<char*>(malloc(*n))) == nullptr) { 188 return -1; 189 } 190 } 191 192 for (ptr = *lineptr, eptr = *lineptr + *n;;) { 193 int c = fgetc(stream); 194 if (c == -1) { 195 if (feof(stream)) { 196 return ptr == *lineptr ? -1 : ptr - *lineptr; 197 } else { 198 return -1; 199 } 200 } 201 *ptr++ = c; 202 if (c == delim) { 203 *ptr = '\0'; 204 return ptr - *lineptr; 205 } 206 if (ptr + 2 >= eptr) { 207 char* nbuf; 208 size_t nbuf_sz = *n * 2; 209 ssize_t d = ptr - *lineptr; 210 if ((nbuf = static_cast<char*>(realloc(*lineptr, nbuf_sz))) == nullptr) { 211 return -1; 212 } 213 *lineptr = nbuf; 214 *n = nbuf_sz; 215 eptr = nbuf + nbuf_sz; 216 ptr = nbuf + d; 217 } 218 } 219} 220 221ssize_t getline(char** lineptr, size_t* n, FILE* stream) 222{ 223 return getdelim(lineptr, n, '\n', stream); 224} 225 226int ungetc(int c, FILE* stream) 227{ 228 ASSERT(stream); 229 if (c == EOF) 230 return EOF; 231 if (stream->have_ungotten) 232 return EOF; 233 stream->have_ungotten = true; 234 stream->ungotten = c; 235 stream->eof = false; 236 return c; 237} 238 239int fputc(int ch, FILE* stream) 240{ 241 assert(stream); 242 assert(stream->buffer_index < stream->buffer_size); 243 stream->buffer[stream->buffer_index++] = ch; 244 if (stream->buffer_index >= stream->buffer_size) 245 fflush(stream); 246 else if (stream->mode == _IONBF || (stream->mode == _IOLBF && ch == '\n')) 247 fflush(stream); 248 if (stream->eof || stream->error) 249 return EOF; 250 return (u8)ch; 251} 252 253int putc(int ch, FILE* stream) 254{ 255 return fputc(ch, stream); 256} 257 258int putchar(int ch) 259{ 260 return putc(ch, stdout); 261} 262 263int fputs(const char* s, FILE* stream) 264{ 265 for (; *s; ++s) { 266 int rc = putc(*s, stream); 267 if (rc == EOF) 268 return EOF; 269 } 270 return 1; 271} 272 273int puts(const char* s) 274{ 275 int rc = fputs(s, stdout); 276 if (rc == EOF) 277 return EOF; 278 return fputc('\n', stdout); 279} 280 281void clearerr(FILE* stream) 282{ 283 assert(stream); 284 stream->eof = false; 285 stream->error = 0; 286} 287 288int ferror(FILE* stream) 289{ 290 return stream->error; 291} 292 293size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) 294{ 295 assert(stream); 296 if (!size) 297 return 0; 298 299 ssize_t nread = 0; 300 301 if (stream->have_ungotten) { 302 // FIXME: Support ungotten character even if size != 1. 303 ASSERT(size == 1); 304 ((char*)ptr)[0] = stream->ungotten; 305 stream->have_ungotten = false; 306 --nmemb; 307 if (!nmemb) 308 return 1; 309 ptr = &((char*)ptr)[1]; 310 ++nread; 311 } 312 313 ssize_t rc = read(stream->fd, ptr, nmemb * size); 314 if (rc < 0) { 315 stream->error = errno; 316 return 0; 317 } 318 if (rc == 0) 319 stream->eof = true; 320 nread += rc; 321 return nread / size; 322} 323 324size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream) 325{ 326 assert(stream); 327 auto* bytes = (const u8*)ptr; 328 ssize_t nwritten = 0; 329 for (size_t i = 0; i < (size * nmemb); ++i) { 330 int rc = fputc(bytes[i], stream); 331 if (rc == EOF) 332 break; 333 ++nwritten; 334 } 335 return nwritten / size; 336} 337 338int fseek(FILE* stream, long offset, int whence) 339{ 340 assert(stream); 341 fflush(stream); 342 off_t off = lseek(stream->fd, offset, whence); 343 if (off < 0) 344 return off; 345 stream->eof = false; 346 stream->error = 0; 347 stream->have_ungotten = false; 348 stream->ungotten = 0; 349 return 0; 350} 351 352long ftell(FILE* stream) 353{ 354 assert(stream); 355 fflush(stream); 356 return lseek(stream->fd, 0, SEEK_CUR); 357} 358 359int fgetpos(FILE* stream, fpos_t* pos) 360{ 361 assert(stream); 362 assert(pos); 363 364 long val = ftell(stream); 365 if (val == -1L) 366 return 1; 367 368 *pos = val; 369 return 0; 370} 371 372int fsetpos(FILE* stream, const fpos_t* pos) 373{ 374 assert(stream); 375 assert(pos); 376 return fseek(stream, (long) *pos, SEEK_SET); 377} 378 379void rewind(FILE* stream) 380{ 381 ASSERT(stream); 382 int rc = fseek(stream, 0, SEEK_SET); 383 ASSERT(rc == 0); 384} 385 386int dbgprintf(const char* fmt, ...) 387{ 388 va_list ap; 389 va_start(ap, fmt); 390 int ret = printf_internal([](char*&, char ch) { dbgputch(ch); }, nullptr, fmt, ap); 391 va_end(ap); 392 return ret; 393} 394 395[[gnu::always_inline]] inline void stdout_putch(char*&, char ch) 396{ 397 putchar(ch); 398} 399 400static FILE* __current_stream = nullptr; 401[[gnu::always_inline]] inline static void stream_putch(char*&, char ch) 402{ 403 fputc(ch, __current_stream); 404} 405 406int vfprintf(FILE* stream, const char* fmt, va_list ap) 407{ 408 __current_stream = stream; 409 return printf_internal(stream_putch, nullptr, fmt, ap); 410} 411 412int fprintf(FILE* stream, const char* fmt, ...) 413{ 414 va_list ap; 415 va_start(ap, fmt); 416 int ret = vfprintf(stream, fmt, ap); 417 va_end(ap); 418 return ret; 419} 420 421int vprintf(const char* fmt, va_list ap) 422{ 423 return printf_internal(stdout_putch, nullptr, fmt, ap); 424} 425 426int printf(const char* fmt, ...) 427{ 428 va_list ap; 429 va_start(ap, fmt); 430 int ret = vprintf(fmt, ap); 431 va_end(ap); 432 return ret; 433} 434 435static void buffer_putch(char*& bufptr, char ch) 436{ 437 *bufptr++ = ch; 438} 439 440int vsprintf(char* buffer, const char* fmt, va_list ap) 441{ 442 int ret = printf_internal(buffer_putch, buffer, fmt, ap); 443 buffer[ret] = '\0'; 444 return ret; 445} 446 447int sprintf(char* buffer, const char* fmt, ...) 448{ 449 va_list ap; 450 va_start(ap, fmt); 451 int ret = vsprintf(buffer, fmt, ap); 452 va_end(ap); 453 return ret; 454} 455 456static size_t __vsnprintf_space_remaining; 457[[gnu::always_inline]] inline void sized_buffer_putch(char*& bufptr, char ch) 458{ 459 if (__vsnprintf_space_remaining) { 460 *bufptr++ = ch; 461 --__vsnprintf_space_remaining; 462 } 463} 464 465int vsnprintf(char* buffer, size_t size, const char* fmt, va_list ap) 466{ 467 __vsnprintf_space_remaining = size; 468 int ret = printf_internal(sized_buffer_putch, buffer, fmt, ap); 469 if (__vsnprintf_space_remaining) { 470 buffer[ret] = '\0'; 471 } 472 return ret; 473} 474 475int snprintf(char* buffer, size_t size, const char* fmt, ...) 476{ 477 va_list ap; 478 va_start(ap, fmt); 479 int ret = vsnprintf(buffer, size, fmt, ap); 480 va_end(ap); 481 return ret; 482} 483 484void perror(const char* s) 485{ 486 int saved_errno = errno; 487 dbg() << "perror(): " << s << ": " << strerror(saved_errno); 488 fprintf(stderr, "%s: %s\n", s, strerror(saved_errno)); 489} 490 491FILE* fopen(const char* pathname, const char* mode) 492{ 493 int flags = 0; 494 // NOTE: rt is a non-standard mode which opens a file for read, explicitly 495 // specifying that it's a text file 496 if (!strcmp(mode, "r") || !strcmp(mode, "rb") || !strcmp(mode, "rt")) 497 flags = O_RDONLY; 498 else if (!strcmp(mode, "r+") || !strcmp(mode, "rb+")) 499 flags = O_RDWR; 500 else if (!strcmp(mode, "w") || !strcmp(mode, "wb")) 501 flags = O_WRONLY | O_CREAT | O_TRUNC; 502 else if (!strcmp(mode, "w+") || !strcmp(mode, "wb+")) 503 flags = O_RDWR | O_CREAT | O_TRUNC; 504 else if (!strcmp(mode, "a") || !strcmp(mode, "ab")) 505 flags = O_WRONLY | O_APPEND | O_CREAT; 506 else if (!strcmp(mode, "a+") || !strcmp(mode, "ab+")) 507 flags = O_RDWR | O_APPEND | O_CREAT; 508 else { 509 fprintf(stderr, "FIXME(LibC): fopen('%s', '%s')\n", pathname, mode); 510 ASSERT_NOT_REACHED(); 511 } 512 int fd = open(pathname, flags, 0666); 513 if (fd < 0) 514 return nullptr; 515 return make_FILE(fd); 516} 517 518FILE* freopen(const char* pathname, const char* mode, FILE* stream) 519{ 520 (void)pathname; 521 (void)mode; 522 (void)stream; 523 ASSERT_NOT_REACHED(); 524} 525 526FILE* fdopen(int fd, const char* mode) 527{ 528 UNUSED_PARAM(mode); 529 // FIXME: Verify that the mode matches how fd is already open. 530 if (fd < 0) 531 return nullptr; 532 return make_FILE(fd); 533} 534 535int fclose(FILE* stream) 536{ 537 fflush(stream); 538 int rc = close(stream->fd); 539 if (stream != &__default_streams[0] && stream != &__default_streams[1] && stream != &__default_streams[2]) 540 free(stream); 541 return rc; 542} 543 544int rename(const char* oldpath, const char* newpath) 545{ 546 if (!oldpath || !newpath) { 547 errno = EFAULT; 548 return -1; 549 } 550 Syscall::SC_rename_params params { { oldpath, strlen(oldpath) }, { newpath, strlen(newpath) } }; 551 int rc = syscall(SC_rename, &params); 552 __RETURN_WITH_ERRNO(rc, rc, -1); 553} 554 555void dbgputch(char ch) 556{ 557 syscall(SC_dbgputch, ch); 558} 559 560int dbgputstr(const char* characters, int length) 561{ 562 int rc = syscall(SC_dbgputstr, characters, length); 563 __RETURN_WITH_ERRNO(rc, rc, -1); 564} 565 566char* tmpnam(char*) 567{ 568 ASSERT_NOT_REACHED(); 569} 570 571FILE* popen(const char* command, const char* type) 572{ 573 if (!type || (*type != 'r' && *type != 'w')) { 574 errno = EINVAL; 575 return nullptr; 576 } 577 578 int pipe_fds[2]; 579 580 int rc = pipe(pipe_fds); 581 if (rc < 0) { 582 ScopedValueRollback rollback(errno); 583 perror("pipe"); 584 return nullptr; 585 } 586 587 pid_t child_pid = fork(); 588 if (!child_pid) { 589 if (*type == 'r') { 590 int rc = dup2(pipe_fds[1], STDOUT_FILENO); 591 if (rc < 0) { 592 perror("dup2"); 593 exit(1); 594 } 595 close(pipe_fds[0]); 596 close(pipe_fds[1]); 597 } else if (*type == 'w') { 598 int rc = dup2(pipe_fds[0], STDIN_FILENO); 599 if (rc < 0) { 600 perror("dup2"); 601 exit(1); 602 } 603 close(pipe_fds[0]); 604 close(pipe_fds[1]); 605 } 606 607 int rc = execl("/bin/sh", "sh", "-c", command, nullptr); 608 if (rc < 0) 609 perror("execl"); 610 exit(1); 611 } 612 613 FILE* fp = nullptr; 614 if (*type == 'r') { 615 fp = make_FILE(pipe_fds[0]); 616 close(pipe_fds[1]); 617 } else if (*type == 'w') { 618 fp = make_FILE(pipe_fds[1]); 619 close(pipe_fds[0]); 620 } 621 622 fp->popen_child = child_pid; 623 return fp; 624} 625 626int pclose(FILE* fp) 627{ 628 ASSERT(fp); 629 ASSERT(fp->popen_child != 0); 630 631 int wstatus = 0; 632 int rc = waitpid(fp->popen_child, &wstatus, 0); 633 if (rc < 0) 634 return rc; 635 636 return wstatus; 637} 638 639int remove(const char* pathname) 640{ 641 int rc = unlink(pathname); 642 if (rc < 0 && errno != EISDIR) 643 return -1; 644 return rmdir(pathname); 645} 646 647int scanf(const char* fmt, ...) 648{ 649 va_list ap; 650 va_start(ap, fmt); 651 int count = vfscanf(stdin, fmt, ap); 652 va_end(ap); 653 return count; 654} 655 656int fscanf(FILE* stream, const char* fmt, ...) 657{ 658 va_list ap; 659 va_start(ap, fmt); 660 int count = vfscanf(stream, fmt, ap); 661 va_end(ap); 662 return count; 663} 664 665int sscanf(const char* buffer, const char* fmt, ...) 666{ 667 va_list ap; 668 va_start(ap, fmt); 669 int count = vsscanf(buffer, fmt, ap); 670 va_end(ap); 671 return count; 672} 673 674int vfscanf(FILE* stream, const char* fmt, va_list ap) 675{ 676 char buffer[BUFSIZ]; 677 if (!fgets(buffer, sizeof(buffer) - 1, stream)) 678 return -1; 679 return vsscanf(buffer, fmt, ap); 680} 681 682void flockfile(FILE* filehandle) 683{ 684 (void)filehandle; 685 dbgprintf("FIXME: Implement flockfile()\n"); 686} 687 688void funlockfile(FILE* filehandle) 689{ 690 (void)filehandle; 691 dbgprintf("FIXME: Implement funlockfile()\n"); 692} 693 694FILE* tmpfile() 695{ 696 char tmp_path[] = "/tmp/XXXXXX"; 697 if (__generate_unique_filename(tmp_path) < 0) 698 return nullptr; 699 700 int fd = open(tmp_path, O_CREAT | O_EXCL | O_RDWR, S_IWUSR | S_IRUSR); 701 if (fd < 0) 702 return nullptr; 703 704 // FIXME: instead of using this hack, implement with O_TMPFILE or similar 705 unlink(tmp_path); 706 707 return make_FILE(fd); 708} 709}