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 *ptr = '\0';
197 return ptr == *lineptr ? -1 : ptr - *lineptr;
198 } else {
199 return -1;
200 }
201 }
202 *ptr++ = c;
203 if (c == delim) {
204 *ptr = '\0';
205 return ptr - *lineptr;
206 }
207 if (ptr + 2 >= eptr) {
208 char* nbuf;
209 size_t nbuf_sz = *n * 2;
210 ssize_t d = ptr - *lineptr;
211 if ((nbuf = static_cast<char*>(realloc(*lineptr, nbuf_sz))) == nullptr) {
212 return -1;
213 }
214 *lineptr = nbuf;
215 *n = nbuf_sz;
216 eptr = nbuf + nbuf_sz;
217 ptr = nbuf + d;
218 }
219 }
220}
221
222ssize_t getline(char** lineptr, size_t* n, FILE* stream)
223{
224 return getdelim(lineptr, n, '\n', stream);
225}
226
227int ungetc(int c, FILE* stream)
228{
229 ASSERT(stream);
230 if (c == EOF)
231 return EOF;
232 if (stream->have_ungotten)
233 return EOF;
234 stream->have_ungotten = true;
235 stream->ungotten = c;
236 stream->eof = false;
237 return c;
238}
239
240int fputc(int ch, FILE* stream)
241{
242 assert(stream);
243 assert(stream->buffer_index < stream->buffer_size);
244 stream->buffer[stream->buffer_index++] = ch;
245 if (stream->buffer_index >= stream->buffer_size)
246 fflush(stream);
247 else if (stream->mode == _IONBF || (stream->mode == _IOLBF && ch == '\n'))
248 fflush(stream);
249 if (stream->eof || stream->error)
250 return EOF;
251 return (u8)ch;
252}
253
254int putc(int ch, FILE* stream)
255{
256 return fputc(ch, stream);
257}
258
259int putchar(int ch)
260{
261 return putc(ch, stdout);
262}
263
264int fputs(const char* s, FILE* stream)
265{
266 for (; *s; ++s) {
267 int rc = putc(*s, stream);
268 if (rc == EOF)
269 return EOF;
270 }
271 return 1;
272}
273
274int puts(const char* s)
275{
276 int rc = fputs(s, stdout);
277 if (rc == EOF)
278 return EOF;
279 return fputc('\n', stdout);
280}
281
282void clearerr(FILE* stream)
283{
284 assert(stream);
285 stream->eof = false;
286 stream->error = 0;
287}
288
289int ferror(FILE* stream)
290{
291 return stream->error;
292}
293
294size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream)
295{
296 assert(stream);
297 if (!size)
298 return 0;
299
300 ssize_t nread = 0;
301
302 if (stream->have_ungotten) {
303 // FIXME: Support ungotten character even if size != 1.
304 ASSERT(size == 1);
305 ((char*)ptr)[0] = stream->ungotten;
306 stream->have_ungotten = false;
307 --nmemb;
308 if (!nmemb)
309 return 1;
310 ptr = &((char*)ptr)[1];
311 ++nread;
312 }
313
314 ssize_t rc = read(stream->fd, ptr, nmemb * size);
315 if (rc < 0) {
316 stream->error = errno;
317 return 0;
318 }
319 if (rc == 0)
320 stream->eof = true;
321 nread += rc;
322 return nread / size;
323}
324
325size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream)
326{
327 assert(stream);
328 auto* bytes = (const u8*)ptr;
329 ssize_t nwritten = 0;
330 for (size_t i = 0; i < (size * nmemb); ++i) {
331 int rc = fputc(bytes[i], stream);
332 if (rc == EOF)
333 break;
334 ++nwritten;
335 }
336 return nwritten / size;
337}
338
339int fseek(FILE* stream, long offset, int whence)
340{
341 assert(stream);
342 fflush(stream);
343 off_t off = lseek(stream->fd, offset, whence);
344 if (off < 0)
345 return off;
346 stream->eof = false;
347 stream->error = 0;
348 stream->have_ungotten = false;
349 stream->ungotten = 0;
350 return 0;
351}
352
353long ftell(FILE* stream)
354{
355 assert(stream);
356 fflush(stream);
357 return lseek(stream->fd, 0, SEEK_CUR);
358}
359
360int fgetpos(FILE* stream, fpos_t* pos)
361{
362 assert(stream);
363 assert(pos);
364
365 long val = ftell(stream);
366 if (val == -1L)
367 return 1;
368
369 *pos = val;
370 return 0;
371}
372
373int fsetpos(FILE* stream, const fpos_t* pos)
374{
375 assert(stream);
376 assert(pos);
377 return fseek(stream, (long) *pos, SEEK_SET);
378}
379
380void rewind(FILE* stream)
381{
382 ASSERT(stream);
383 int rc = fseek(stream, 0, SEEK_SET);
384 ASSERT(rc == 0);
385}
386
387int dbgprintf(const char* fmt, ...)
388{
389 va_list ap;
390 va_start(ap, fmt);
391 int ret = printf_internal([](char*&, char ch) { dbgputch(ch); }, nullptr, fmt, ap);
392 va_end(ap);
393 return ret;
394}
395
396[[gnu::always_inline]] inline void stdout_putch(char*&, char ch)
397{
398 putchar(ch);
399}
400
401static FILE* __current_stream = nullptr;
402[[gnu::always_inline]] inline static void stream_putch(char*&, char ch)
403{
404 fputc(ch, __current_stream);
405}
406
407int vfprintf(FILE* stream, const char* fmt, va_list ap)
408{
409 __current_stream = stream;
410 return printf_internal(stream_putch, nullptr, fmt, ap);
411}
412
413int fprintf(FILE* stream, const char* fmt, ...)
414{
415 va_list ap;
416 va_start(ap, fmt);
417 int ret = vfprintf(stream, fmt, ap);
418 va_end(ap);
419 return ret;
420}
421
422int vprintf(const char* fmt, va_list ap)
423{
424 return printf_internal(stdout_putch, nullptr, fmt, ap);
425}
426
427int printf(const char* fmt, ...)
428{
429 va_list ap;
430 va_start(ap, fmt);
431 int ret = vprintf(fmt, ap);
432 va_end(ap);
433 return ret;
434}
435
436static void buffer_putch(char*& bufptr, char ch)
437{
438 *bufptr++ = ch;
439}
440
441int vsprintf(char* buffer, const char* fmt, va_list ap)
442{
443 int ret = printf_internal(buffer_putch, buffer, fmt, ap);
444 buffer[ret] = '\0';
445 return ret;
446}
447
448int sprintf(char* buffer, const char* fmt, ...)
449{
450 va_list ap;
451 va_start(ap, fmt);
452 int ret = vsprintf(buffer, fmt, ap);
453 va_end(ap);
454 return ret;
455}
456
457static size_t __vsnprintf_space_remaining;
458[[gnu::always_inline]] inline void sized_buffer_putch(char*& bufptr, char ch)
459{
460 if (__vsnprintf_space_remaining) {
461 *bufptr++ = ch;
462 --__vsnprintf_space_remaining;
463 }
464}
465
466int vsnprintf(char* buffer, size_t size, const char* fmt, va_list ap)
467{
468 __vsnprintf_space_remaining = size;
469 int ret = printf_internal(sized_buffer_putch, buffer, fmt, ap);
470 if (__vsnprintf_space_remaining) {
471 buffer[ret] = '\0';
472 }
473 return ret;
474}
475
476int snprintf(char* buffer, size_t size, const char* fmt, ...)
477{
478 va_list ap;
479 va_start(ap, fmt);
480 int ret = vsnprintf(buffer, size, fmt, ap);
481 va_end(ap);
482 return ret;
483}
484
485void perror(const char* s)
486{
487 int saved_errno = errno;
488 dbg() << "perror(): " << s << ": " << strerror(saved_errno);
489 fprintf(stderr, "%s: %s\n", s, strerror(saved_errno));
490}
491
492FILE* fopen(const char* pathname, const char* mode)
493{
494 int flags = 0;
495 // NOTE: rt is a non-standard mode which opens a file for read, explicitly
496 // specifying that it's a text file
497 if (!strcmp(mode, "r") || !strcmp(mode, "rb") || !strcmp(mode, "rt"))
498 flags = O_RDONLY;
499 else if (!strcmp(mode, "r+") || !strcmp(mode, "rb+"))
500 flags = O_RDWR;
501 else if (!strcmp(mode, "w") || !strcmp(mode, "wb"))
502 flags = O_WRONLY | O_CREAT | O_TRUNC;
503 else if (!strcmp(mode, "w+") || !strcmp(mode, "wb+"))
504 flags = O_RDWR | O_CREAT | O_TRUNC;
505 else if (!strcmp(mode, "a") || !strcmp(mode, "ab"))
506 flags = O_WRONLY | O_APPEND | O_CREAT;
507 else if (!strcmp(mode, "a+") || !strcmp(mode, "ab+"))
508 flags = O_RDWR | O_APPEND | O_CREAT;
509 else {
510 fprintf(stderr, "FIXME(LibC): fopen('%s', '%s')\n", pathname, mode);
511 ASSERT_NOT_REACHED();
512 }
513 int fd = open(pathname, flags, 0666);
514 if (fd < 0)
515 return nullptr;
516 return make_FILE(fd);
517}
518
519FILE* freopen(const char* pathname, const char* mode, FILE* stream)
520{
521 (void)pathname;
522 (void)mode;
523 (void)stream;
524 ASSERT_NOT_REACHED();
525}
526
527FILE* fdopen(int fd, const char* mode)
528{
529 UNUSED_PARAM(mode);
530 // FIXME: Verify that the mode matches how fd is already open.
531 if (fd < 0)
532 return nullptr;
533 return make_FILE(fd);
534}
535
536int fclose(FILE* stream)
537{
538 fflush(stream);
539 int rc = close(stream->fd);
540 if (stream != &__default_streams[0] && stream != &__default_streams[1] && stream != &__default_streams[2])
541 free(stream);
542 return rc;
543}
544
545int rename(const char* oldpath, const char* newpath)
546{
547 if (!oldpath || !newpath) {
548 errno = EFAULT;
549 return -1;
550 }
551 Syscall::SC_rename_params params { { oldpath, strlen(oldpath) }, { newpath, strlen(newpath) } };
552 int rc = syscall(SC_rename, ¶ms);
553 __RETURN_WITH_ERRNO(rc, rc, -1);
554}
555
556void dbgputch(char ch)
557{
558 syscall(SC_dbgputch, ch);
559}
560
561int dbgputstr(const char* characters, int length)
562{
563 int rc = syscall(SC_dbgputstr, characters, length);
564 __RETURN_WITH_ERRNO(rc, rc, -1);
565}
566
567char* tmpnam(char*)
568{
569 ASSERT_NOT_REACHED();
570}
571
572FILE* popen(const char* command, const char* type)
573{
574 if (!type || (*type != 'r' && *type != 'w')) {
575 errno = EINVAL;
576 return nullptr;
577 }
578
579 int pipe_fds[2];
580
581 int rc = pipe(pipe_fds);
582 if (rc < 0) {
583 ScopedValueRollback rollback(errno);
584 perror("pipe");
585 return nullptr;
586 }
587
588 pid_t child_pid = fork();
589 if (!child_pid) {
590 if (*type == 'r') {
591 int rc = dup2(pipe_fds[1], STDOUT_FILENO);
592 if (rc < 0) {
593 perror("dup2");
594 exit(1);
595 }
596 close(pipe_fds[0]);
597 close(pipe_fds[1]);
598 } else if (*type == 'w') {
599 int rc = dup2(pipe_fds[0], STDIN_FILENO);
600 if (rc < 0) {
601 perror("dup2");
602 exit(1);
603 }
604 close(pipe_fds[0]);
605 close(pipe_fds[1]);
606 }
607
608 int rc = execl("/bin/sh", "sh", "-c", command, nullptr);
609 if (rc < 0)
610 perror("execl");
611 exit(1);
612 }
613
614 FILE* fp = nullptr;
615 if (*type == 'r') {
616 fp = make_FILE(pipe_fds[0]);
617 close(pipe_fds[1]);
618 } else if (*type == 'w') {
619 fp = make_FILE(pipe_fds[1]);
620 close(pipe_fds[0]);
621 }
622
623 fp->popen_child = child_pid;
624 return fp;
625}
626
627int pclose(FILE* fp)
628{
629 ASSERT(fp);
630 ASSERT(fp->popen_child != 0);
631
632 int wstatus = 0;
633 int rc = waitpid(fp->popen_child, &wstatus, 0);
634 if (rc < 0)
635 return rc;
636
637 return wstatus;
638}
639
640int remove(const char* pathname)
641{
642 int rc = unlink(pathname);
643 if (rc < 0 && errno != EISDIR)
644 return -1;
645 return rmdir(pathname);
646}
647
648int scanf(const char* fmt, ...)
649{
650 va_list ap;
651 va_start(ap, fmt);
652 int count = vfscanf(stdin, fmt, ap);
653 va_end(ap);
654 return count;
655}
656
657int fscanf(FILE* stream, const char* fmt, ...)
658{
659 va_list ap;
660 va_start(ap, fmt);
661 int count = vfscanf(stream, fmt, ap);
662 va_end(ap);
663 return count;
664}
665
666int sscanf(const char* buffer, const char* fmt, ...)
667{
668 va_list ap;
669 va_start(ap, fmt);
670 int count = vsscanf(buffer, fmt, ap);
671 va_end(ap);
672 return count;
673}
674
675int vfscanf(FILE* stream, const char* fmt, va_list ap)
676{
677 char buffer[BUFSIZ];
678 if (!fgets(buffer, sizeof(buffer) - 1, stream))
679 return -1;
680 return vsscanf(buffer, fmt, ap);
681}
682
683void flockfile(FILE* filehandle)
684{
685 (void)filehandle;
686 dbgprintf("FIXME: Implement flockfile()\n");
687}
688
689void funlockfile(FILE* filehandle)
690{
691 (void)filehandle;
692 dbgprintf("FIXME: Implement funlockfile()\n");
693}
694
695FILE* tmpfile()
696{
697 char tmp_path[] = "/tmp/XXXXXX";
698 if (__generate_unique_filename(tmp_path) < 0)
699 return nullptr;
700
701 int fd = open(tmp_path, O_CREAT | O_EXCL | O_RDWR, S_IWUSR | S_IRUSR);
702 if (fd < 0)
703 return nullptr;
704
705 // FIXME: instead of using this hack, implement with O_TMPFILE or similar
706 unlink(tmp_path);
707
708 return make_FILE(fd);
709}
710}