Serenity Operating System
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, ¶ms);
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}