summaryrefslogtreecommitdiff
path: root/userland/libc/stdio
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2023-10-22 19:50:38 +0200
committerAnton Kling <anton@kling.gg>2023-10-22 19:50:38 +0200
commit4e09bca9e34c226b6d7e34b4fa11248405fd988e (patch)
tree80f156b7940d9d19971395f335530170c69516c7 /userland/libc/stdio
Move everything into a new repo.
Diffstat (limited to 'userland/libc/stdio')
-rw-r--r--userland/libc/stdio/dprintf.c9
-rw-r--r--userland/libc/stdio/fclose.c10
-rw-r--r--userland/libc/stdio/feof.c5
-rw-r--r--userland/libc/stdio/ferror.c5
-rw-r--r--userland/libc/stdio/fflush.c7
-rw-r--r--userland/libc/stdio/fgetc.c20
-rw-r--r--userland/libc/stdio/fgetpos.c7
-rw-r--r--userland/libc/stdio/fgets.c16
-rw-r--r--userland/libc/stdio/fileno.c13
-rw-r--r--userland/libc/stdio/fopen.c57
-rw-r--r--userland/libc/stdio/fprintf.c9
-rw-r--r--userland/libc/stdio/fputc.c7
-rw-r--r--userland/libc/stdio/fputs.c9
-rw-r--r--userland/libc/stdio/fread.c11
-rw-r--r--userland/libc/stdio/fscanf.c7
-rw-r--r--userland/libc/stdio/fseek.c21
-rw-r--r--userland/libc/stdio/fsetpos.c7
-rw-r--r--userland/libc/stdio/ftell.c5
-rw-r--r--userland/libc/stdio/fwrite.c12
-rw-r--r--userland/libc/stdio/getchar.c4
-rw-r--r--userland/libc/stdio/open_memstream.c108
-rw-r--r--userland/libc/stdio/printf.c9
-rw-r--r--userland/libc/stdio/putc.c3
-rw-r--r--userland/libc/stdio/putchar.c7
-rw-r--r--userland/libc/stdio/puts.c6
-rw-r--r--userland/libc/stdio/remove.c9
-rw-r--r--userland/libc/stdio/rename.c8
-rw-r--r--userland/libc/stdio/setvbuf.c6
-rw-r--r--userland/libc/stdio/snprintf.c42
-rw-r--r--userland/libc/stdio/sprintf.c33
-rw-r--r--userland/libc/stdio/stderr.c11
-rw-r--r--userland/libc/stdio/stdin.c54
-rw-r--r--userland/libc/stdio/stdout.c13
-rw-r--r--userland/libc/stdio/tmpfile.c9
-rw-r--r--userland/libc/stdio/tmpnam.c10
-rw-r--r--userland/libc/stdio/ungetc.c9
-rw-r--r--userland/libc/stdio/vdprintf.c77
-rw-r--r--userland/libc/stdio/vfprintf.c243
-rw-r--r--userland/libc/stdio/vprintf.c3
39 files changed, 901 insertions, 0 deletions
diff --git a/userland/libc/stdio/dprintf.c b/userland/libc/stdio/dprintf.c
new file mode 100644
index 0000000..2f2aadb
--- /dev/null
+++ b/userland/libc/stdio/dprintf.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int dprintf(int fd, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ int rc = vdprintf(fd, format, ap);
+ va_end(ap);
+ return rc;
+}
diff --git a/userland/libc/stdio/fclose.c b/userland/libc/stdio/fclose.c
new file mode 100644
index 0000000..02d93ae
--- /dev/null
+++ b/userland/libc/stdio/fclose.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fclose.html
+// FIXME: Do some actual error checking.
+int fclose(FILE *stream) {
+ free(stream->cookie);
+ free(stream);
+ return 0;
+}
diff --git a/userland/libc/stdio/feof.c b/userland/libc/stdio/feof.c
new file mode 100644
index 0000000..7f46301
--- /dev/null
+++ b/userland/libc/stdio/feof.c
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+int feof(FILE *stream) {
+ return stream->is_eof;
+}
diff --git a/userland/libc/stdio/ferror.c b/userland/libc/stdio/ferror.c
new file mode 100644
index 0000000..8cb46cf
--- /dev/null
+++ b/userland/libc/stdio/ferror.c
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+int ferror(FILE *stream) {
+ return stream->has_error;
+}
diff --git a/userland/libc/stdio/fflush.c b/userland/libc/stdio/fflush.c
new file mode 100644
index 0000000..7a37c79
--- /dev/null
+++ b/userland/libc/stdio/fflush.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html
+int fflush(FILE *stream) {
+ // FIXME: Implement
+ return 0;
+}
diff --git a/userland/libc/stdio/fgetc.c b/userland/libc/stdio/fgetc.c
new file mode 100644
index 0000000..c69211f
--- /dev/null
+++ b/userland/libc/stdio/fgetc.c
@@ -0,0 +1,20 @@
+#include <assert.h>
+#include <stdio.h>
+
+int fgetc(FILE *stream) {
+ if (stream->has_buffered_char) {
+ stream->has_buffered_char = 0;
+ return stream->buffered_char;
+ }
+ char c;
+ if (1 == fread(&c, 1, 1, stream))
+ return (int)c;
+ // FIXME: Should use feof and ferror
+ if (stream->has_error)
+ return EOF;
+ if (stream->is_eof)
+ return EOF;
+ // How did we get here?
+ assert(0);
+ return EOF;
+}
diff --git a/userland/libc/stdio/fgetpos.c b/userland/libc/stdio/fgetpos.c
new file mode 100644
index 0000000..7f34d6a
--- /dev/null
+++ b/userland/libc/stdio/fgetpos.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+// FIXME: Error handling
+int fgetpos(FILE *restrict stream, fpos_t *restrict pos) {
+ *pos = (fpos_t)stream->offset_in_file;
+ return 0;
+}
diff --git a/userland/libc/stdio/fgets.c b/userland/libc/stdio/fgets.c
new file mode 100644
index 0000000..8e21501
--- /dev/null
+++ b/userland/libc/stdio/fgets.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+char *fgets(char *s, int n, FILE *stream) {
+ for (int i = 0; i < n; i++) {
+ char c;
+ fread(&c, 1, 1, stream);
+ if (stream->has_error)
+ return NULL;
+ if (stream->is_eof)
+ return NULL;
+ s[i] = c;
+ if (c == '\n')
+ break;
+ }
+ return s;
+}
diff --git a/userland/libc/stdio/fileno.c b/userland/libc/stdio/fileno.c
new file mode 100644
index 0000000..246cc51
--- /dev/null
+++ b/userland/libc/stdio/fileno.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <errno.h>
+
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fileno.html
+// The fileno() function shall return the integer file descriptor associated
+// with the stream pointed to by stream.
+int fileno(FILE *stream) {
+ if (-1 == stream->fd) {
+ errno = EBADF;
+ return -1;
+ }
+ return stream->fd;
+}
diff --git a/userland/libc/stdio/fopen.c b/userland/libc/stdio/fopen.c
new file mode 100644
index 0000000..a29c7ef
--- /dev/null
+++ b/userland/libc/stdio/fopen.c
@@ -0,0 +1,57 @@
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <stdio.h>
+
+// FIXME: All modes not implemented
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html
+FILE *fopen(const char *pathname, const char *mode) {
+ uint8_t read = 0;
+ uint8_t write = 0;
+ uint8_t append = 0;
+ // FIXME: Not parsed correctly
+ for (; *mode; mode++) {
+ // r or rb
+ // Open file for reading.
+ // w or wb
+ // Truncate to zero length or create file for writing.
+ // a or ab
+ // Append; open or create file for writing at
+ // end-of-file.
+ switch (*mode) {
+ case 'r':
+ read = 1;
+ break;
+ case 'w':
+ write = 1;
+ break;
+ case 'a':
+ append = 1;
+ break;
+ }
+ }
+ int flag = 0;
+ if (read)
+ flag |= O_READ;
+ if (write)
+ flag |= O_WRITE;
+
+ int fd = open(pathname, flag, 0);
+ if (-1 == fd)
+ return NULL;
+
+ struct stat s;
+ stat(pathname, &s);
+
+ FILE *r = malloc(sizeof(FILE));
+ r->read = read_fd;
+ r->write = write_fd;
+ r->seek = seek_fd;
+ r->has_error = 0;
+ r->is_eof = 0;
+ r->offset_in_file = 0;
+ r->file_size = s.st_size;
+ r->cookie = NULL;
+ r->fd = fd;
+ return r;
+}
diff --git a/userland/libc/stdio/fprintf.c b/userland/libc/stdio/fprintf.c
new file mode 100644
index 0000000..f983065
--- /dev/null
+++ b/userland/libc/stdio/fprintf.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int fprintf(FILE *f, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ int rc = vfprintf(f, fmt, ap);
+ va_end(ap);
+ return rc;
+}
diff --git a/userland/libc/stdio/fputc.c b/userland/libc/stdio/fputc.c
new file mode 100644
index 0000000..7c8fa7c
--- /dev/null
+++ b/userland/libc/stdio/fputc.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int fputc(int c, FILE *stream) {
+ if (fwrite(&c, 1, 1, stream) > 0)
+ return c;
+ return EOF;
+}
diff --git a/userland/libc/stdio/fputs.c b/userland/libc/stdio/fputs.c
new file mode 100644
index 0000000..1b70c66
--- /dev/null
+++ b/userland/libc/stdio/fputs.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int fputs(const char *s, FILE *stream) {
+ const char *b = s;
+ for (; *s; s++)
+ if (0 == fwrite(s, 1, 1, stream))
+ return EOF;
+ return s - b;
+}
diff --git a/userland/libc/stdio/fread.c b/userland/libc/stdio/fread.c
new file mode 100644
index 0000000..1a27afa
--- /dev/null
+++ b/userland/libc/stdio/fread.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <sys/types.h>
+
+size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) {
+ // FIXME: Check for overflow
+ ssize_t bytes_to_read = nmemb * size;
+ size_t rc = stream->read(stream, ptr, bytes_to_read);
+ // On success, fread() return the number of items read
+ rc /= size;
+ return rc;
+}
diff --git a/userland/libc/stdio/fscanf.c b/userland/libc/stdio/fscanf.c
new file mode 100644
index 0000000..785ce4b
--- /dev/null
+++ b/userland/libc/stdio/fscanf.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include <assert.h>
+
+int fscanf(FILE *stream, const char *format, ...) {
+ // FIXME
+ assert(0);
+}
diff --git a/userland/libc/stdio/fseek.c b/userland/libc/stdio/fseek.c
new file mode 100644
index 0000000..fb891ec
--- /dev/null
+++ b/userland/libc/stdio/fseek.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <assert.h>
+
+int fseek(FILE *stream, long offset, int whence) {
+ return stream->seek(stream, offset, whence);
+ /*
+ switch (whence) {
+ case SEEK_SET:
+ stream->offset_in_file = offset;
+ break;
+ case SEEK_CUR:
+ stream->offset_in_file += offset;
+ break;
+ case SEEK_END:
+ // FIXME
+ assert(0);
+ break;
+ }
+ // FIXME: Error checking
+ return 0;*/
+}
diff --git a/userland/libc/stdio/fsetpos.c b/userland/libc/stdio/fsetpos.c
new file mode 100644
index 0000000..c39c545
--- /dev/null
+++ b/userland/libc/stdio/fsetpos.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+// FIXME: Error handling
+int fsetpos(FILE *stream, const fpos_t *pos) {
+ stream->offset_in_file = (long)(*pos);
+ return 0;
+}
diff --git a/userland/libc/stdio/ftell.c b/userland/libc/stdio/ftell.c
new file mode 100644
index 0000000..35076d0
--- /dev/null
+++ b/userland/libc/stdio/ftell.c
@@ -0,0 +1,5 @@
+#include <stdio.h>
+
+long ftell(FILE *stream) {
+ return stream->offset_in_file;
+}
diff --git a/userland/libc/stdio/fwrite.c b/userland/libc/stdio/fwrite.c
new file mode 100644
index 0000000..552bbd6
--- /dev/null
+++ b/userland/libc/stdio/fwrite.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <sys/types.h>
+
+size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) {
+ // FIXME: Check for overflow
+ ssize_t bytes_to_write = nmemb * size;
+ size_t rc = stream->write(stream, ptr, bytes_to_write);
+ // On success, fwrite() return the number of items
+ // written.
+ rc /= size;
+ return rc;
+}
diff --git a/userland/libc/stdio/getchar.c b/userland/libc/stdio/getchar.c
new file mode 100644
index 0000000..dad2263
--- /dev/null
+++ b/userland/libc/stdio/getchar.c
@@ -0,0 +1,4 @@
+#include <stdio.h>
+
+// The getchar() function shall be equivalent to getc(stdin).
+int getchar(void) { return fgetc(stdin); }
diff --git a/userland/libc/stdio/open_memstream.c b/userland/libc/stdio/open_memstream.c
new file mode 100644
index 0000000..8f359b9
--- /dev/null
+++ b/userland/libc/stdio/open_memstream.c
@@ -0,0 +1,108 @@
+#include <assert.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+/*
+struct __IO_FILE {
+ size_t (*write)(FILE *, const unsigned char *, size_t);
+ size_t (*read)(FILE *, unsigned char *, size_t);
+ int (*seek)(FILE *, long, int);
+ long offset_in_file;
+ int buffered_char;
+ int has_buffered_char;
+ int fd;
+ uint8_t is_eof;
+ uint8_t has_error;
+ uint64_t file_size;
+ void *cookie;
+};
+*/
+
+struct Memstream {
+ size_t buffer_usage;
+ char *buffer;
+};
+
+#define MEMSTREAM_DEF_SIZE 4096
+
+size_t memstream_write(FILE *fp, const unsigned char *buf, size_t n) {
+ struct Memstream *c = fp->cookie;
+ // FIXME: Do a reallocation
+ if (c->buffer_usage + n >= fp->file_size) {
+ n = fp->file_size - c->buffer_usage;
+ }
+
+ memcpy(c->buffer + fp->offset_in_file, buf, n);
+ fp->offset_in_file += n;
+ if (fp->offset_in_file > c->buffer_usage)
+ c->buffer_usage = fp->offset_in_file;
+ return n;
+}
+
+size_t memstream_read(FILE *fp, unsigned char *buf, size_t n) {
+ struct Memstream *c = fp->cookie;
+ size_t length_left = c->buffer_usage - fp->offset_in_file;
+ n = min(length_left, n);
+ memcpy(buf, c->buffer + fp->offset_in_file, n);
+ fp->offset_in_file += n;
+ if (0 == n)
+ fp->is_eof = 1;
+ return n;
+}
+
+int memstream_seek(FILE *stream, long offset, int whence) {
+ switch (whence) {
+ case SEEK_SET:
+ stream->offset_in_file = offset;
+ break;
+ case SEEK_CUR:
+ stream->offset_in_file += offset;
+ break;
+ case SEEK_END:
+ stream->offset_in_file = stream->file_size + offset;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ // FIXME: Error checking
+ return 0;
+}
+
+FILE *open_memstream(char **bufp, size_t *sizep) {
+ struct Memstream *c = NULL;
+ FILE *fp = malloc(sizeof(FILE));
+ if (!fp)
+ return NULL;
+
+ fp->offset_in_file = 0;
+ fp->buffered_char = 0;
+ fp->has_buffered_char = 0;
+ fp->seek = memstream_seek;
+ fp->fd = -1;
+ fp->is_eof = 0;
+ fp->has_error = 0;
+ fp->file_size = MEMSTREAM_DEF_SIZE;
+
+ fp->write = memstream_write;
+ fp->read = memstream_read;
+
+ c = malloc(sizeof(struct Memstream));
+ if (!c) {
+ goto _exit_memstream_fail;
+ }
+
+ fp->cookie = (void *)c;
+
+ c->buffer = *bufp = malloc(MEMSTREAM_DEF_SIZE);
+ if (!bufp) {
+ goto _exit_memstream_fail;
+ }
+ c->buffer_usage = 0;
+
+ return fp;
+_exit_memstream_fail:
+ free(c);
+ free(fp);
+ return NULL;
+}
diff --git a/userland/libc/stdio/printf.c b/userland/libc/stdio/printf.c
new file mode 100644
index 0000000..d26568a
--- /dev/null
+++ b/userland/libc/stdio/printf.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int printf(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ int rc = vprintf(format, ap);
+ va_end(ap);
+ return rc;
+}
diff --git a/userland/libc/stdio/putc.c b/userland/libc/stdio/putc.c
new file mode 100644
index 0000000..a468a95
--- /dev/null
+++ b/userland/libc/stdio/putc.c
@@ -0,0 +1,3 @@
+#include <stdio.h>
+
+int putc(int c, FILE *stream) { return fputc(c, stream);}
diff --git a/userland/libc/stdio/putchar.c b/userland/libc/stdio/putchar.c
new file mode 100644
index 0000000..3fcf7ca
--- /dev/null
+++ b/userland/libc/stdio/putchar.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include <unistd.h>
+
+int putchar(int c) {
+ printf("%c", (char)c);
+ return c;
+}
diff --git a/userland/libc/stdio/puts.c b/userland/libc/stdio/puts.c
new file mode 100644
index 0000000..4a72e66
--- /dev/null
+++ b/userland/libc/stdio/puts.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int puts(const char *s) {
+ int rc = printf("%s\n", s);
+ return rc;
+}
diff --git a/userland/libc/stdio/remove.c b/userland/libc/stdio/remove.c
new file mode 100644
index 0000000..35b41ad
--- /dev/null
+++ b/userland/libc/stdio/remove.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+#include <errno.h>
+
+extern int errno;
+int remove(const char *path) {
+ // FIXME
+ errno = ENAMETOOLONG;
+ return -1;
+}
diff --git a/userland/libc/stdio/rename.c b/userland/libc/stdio/rename.c
new file mode 100644
index 0000000..15d4bf5
--- /dev/null
+++ b/userland/libc/stdio/rename.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+#include <assert.h>
+
+int rename(const char *old, const char *new) {
+ (void)old;
+ (void)new;
+ assert(0); // TODO: Implement
+ }
diff --git a/userland/libc/stdio/setvbuf.c b/userland/libc/stdio/setvbuf.c
new file mode 100644
index 0000000..7f91518
--- /dev/null
+++ b/userland/libc/stdio/setvbuf.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int setvbuf(FILE *restrict stream, char *restrict buf, int type, size_t size) {
+ // TODO
+ return 0;
+}
diff --git a/userland/libc/stdio/snprintf.c b/userland/libc/stdio/snprintf.c
new file mode 100644
index 0000000..328442a
--- /dev/null
+++ b/userland/libc/stdio/snprintf.c
@@ -0,0 +1,42 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+struct sn_cookie {
+ char *s;
+ size_t n;
+};
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+size_t sn_write(FILE *f, const unsigned char *s, const size_t l) {
+ struct sn_cookie *c = f->cookie;
+ size_t k = MIN(l, c->n);
+ memcpy(c->s, s, k);
+ c->s += k;
+ c->n -= k;
+ *(c->s) = '\0';
+ // Upon successful completion, the snprintf() function shall return the number
+ // of bytes that would be written to s had n been sufficiently large excluding
+ // the terminating null byte.
+ return l;
+}
+
+int vsnprintf(char *str, size_t size, const char *format, va_list ap) {
+ char dummy[1];
+ struct sn_cookie c = {.s = (size ? str : dummy), .n = (size ? size - 1 : 0)};
+ FILE f = {
+ .write = sn_write,
+ .cookie = &c,
+ };
+ return vfprintf(&f, format, ap);
+}
+
+int snprintf(char *str, size_t size, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ int rc = vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return rc;
+}
diff --git a/userland/libc/stdio/sprintf.c b/userland/libc/stdio/sprintf.c
new file mode 100644
index 0000000..deffbbe
--- /dev/null
+++ b/userland/libc/stdio/sprintf.c
@@ -0,0 +1,33 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+struct s_cookie {
+ char *s;
+};
+
+size_t s_write(FILE *f, const unsigned char *s, size_t l) {
+ struct s_cookie *c = f->cookie;
+ memcpy(c->s, s, l);
+ c->s += l;
+ *(c->s) = '\0';
+ return l;
+}
+
+int vsprintf(char *str, const char *format, va_list ap) {
+ struct s_cookie c = {.s = str};
+ FILE f = {
+ .write = s_write,
+ .cookie = &c,
+ };
+ return vfprintf(&f, format, ap);
+}
+
+int sprintf(char *str, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ int rc = vsprintf(str, format, ap);
+ va_end(ap);
+ return rc;
+}
diff --git a/userland/libc/stdio/stderr.c b/userland/libc/stdio/stderr.c
new file mode 100644
index 0000000..76597e2
--- /dev/null
+++ b/userland/libc/stdio/stderr.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include <unistd.h>
+
+FILE __stderr_FILE = {
+ .write = write_fd,
+ .read = read_fd,
+ .is_eof = 0,
+ .has_error = 0,
+ .cookie = NULL,
+ .fd = 2,
+};
diff --git a/userland/libc/stdio/stdin.c b/userland/libc/stdio/stdin.c
new file mode 100644
index 0000000..ae3ab8d
--- /dev/null
+++ b/userland/libc/stdio/stdin.c
@@ -0,0 +1,54 @@
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+
+size_t write_fd(FILE *f, const unsigned char *s, size_t l) {
+ int rc = pwrite(f->fd, s, l, f->offset_in_file);
+ if (rc == -1) {
+ f->has_error = 1;
+ return 0;
+ }
+ f->offset_in_file += rc;
+ return rc;
+}
+
+size_t read_fd(FILE *f, unsigned char *s, size_t l) {
+ int rc = pread(f->fd, s, l, f->offset_in_file);
+ if (rc == 0)
+ f->is_eof = 1;
+ if (rc == -1) {
+ f->has_error = 1;
+ return 0;
+ }
+ f->offset_in_file += rc;
+ return rc;
+}
+
+int seek_fd(FILE *stream, long offset, int whence) {
+ switch (whence) {
+ case SEEK_SET:
+ stream->offset_in_file = offset;
+ break;
+ case SEEK_CUR:
+ stream->offset_in_file += offset;
+ break;
+ case SEEK_END:
+ stream->offset_in_file = stream->file_size + offset;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ // FIXME: Error checking
+ return 0;
+}
+
+FILE __stdin_FILE = {
+ .write = write_fd,
+ .read = read_fd,
+ .seek = NULL,
+ .is_eof = 0,
+ .has_error = 0,
+ .cookie = NULL,
+ .fd = 0,
+};
diff --git a/userland/libc/stdio/stdout.c b/userland/libc/stdio/stdout.c
new file mode 100644
index 0000000..7f4edf0
--- /dev/null
+++ b/userland/libc/stdio/stdout.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <unistd.h>
+
+FILE __stdout_FILE = {
+ .write = write_fd,
+ .read = read_fd,
+ .is_eof = 0,
+ .has_error = 0,
+ .seek = NULL,
+ .cookie = NULL,
+ .fd = 1,
+};
+FILE __stderr_FILE;
diff --git a/userland/libc/stdio/tmpfile.c b/userland/libc/stdio/tmpfile.c
new file mode 100644
index 0000000..cee6e0a
--- /dev/null
+++ b/userland/libc/stdio/tmpfile.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+#include <assert.h>
+
+FILE *tmpfile(void) {
+ // TODO
+ printf("TODO: Implement tmpfile()\n");
+ assert(0);
+ return NULL;
+}
diff --git a/userland/libc/stdio/tmpnam.c b/userland/libc/stdio/tmpnam.c
new file mode 100644
index 0000000..aafe67d
--- /dev/null
+++ b/userland/libc/stdio/tmpnam.c
@@ -0,0 +1,10 @@
+#include <assert.h>
+#include <stdio.h>
+
+char *tmpnam(char *s) {
+ assert(!s);
+ s = malloc(100);
+ strcpy(s, "/tmp.XXXXXX");
+ mkstemp(s);
+ return s;
+}
diff --git a/userland/libc/stdio/ungetc.c b/userland/libc/stdio/ungetc.c
new file mode 100644
index 0000000..8d649bc
--- /dev/null
+++ b/userland/libc/stdio/ungetc.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int ungetc(int c, FILE *stream) {
+ if (stream->has_buffered_char)
+ return EOF;
+ stream->buffered_char = c;
+ stream->has_buffered_char = 1;
+ return c;
+}
diff --git a/userland/libc/stdio/vdprintf.c b/userland/libc/stdio/vdprintf.c
new file mode 100644
index 0000000..b3fa065
--- /dev/null
+++ b/userland/libc/stdio/vdprintf.c
@@ -0,0 +1,77 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+struct vd_cookie {
+ int fd;
+ char *buffer;
+ uint8_t buf_len;
+ uint8_t buf_used;
+ int sent_bytes;
+};
+
+size_t min(size_t a, size_t b) { return (a < b) ? a : b; }
+
+size_t vd_write(FILE *f, const unsigned char *s, size_t l) {
+ struct vd_cookie *c = f->cookie;
+
+ int clear_buffer = 0;
+ size_t b_copy = min(l, c->buf_len - (c->buf_used));
+ for (int i = 0; i < b_copy; i++) {
+ c->buffer[c->buf_used + i] = s[i];
+ if (s[i] == '\n')
+ clear_buffer = 1;
+ }
+ c->buf_used += b_copy;
+
+ if (clear_buffer) {
+ int rc = write(c->fd, c->buffer, c->buf_used);
+ c->buf_used = 0;
+ if (-1 == rc) {
+ return (size_t)-1;
+ }
+ c->sent_bytes += rc;
+ }
+ return l;
+}
+
+int vdprintf(int fd, const char *format, va_list ap) {
+ FILE f = {
+ .write = write_fd,
+ .fd = fd,
+ };
+ return vfprintf(&f, format, ap);
+ // return -1;
+ /*
+char buffer[32];
+struct vd_cookie c = {.fd = fd,
+ .buffer = buffer,
+ .buf_len = 32,
+ .buf_used = 0,
+ .sent_bytes = 0};
+FILE f = {
+.write = vd_write,
+.cookie = &c,
+};
+
+// If an output error was encountered, these functions shall return a
+// negative value and set errno to indicate the error.
+if (-1 == vfprintf(&f, format, ap))
+return -1;
+
+// Upon successful completion, the dprintf(),
+// fprintf(), and printf() functions shall return the number of bytes
+// transmitted.
+
+if(0 == c.buf_used)
+return c.sent_bytes;
+
+// First the current buffer needs to be cleared
+int rc = write(fd, buffer, c.buf_used);
+if (-1 == rc) {
+return -1;
+}
+c.sent_bytes += rc;
+return c.sent_bytes;*/
+}
diff --git a/userland/libc/stdio/vfprintf.c b/userland/libc/stdio/vfprintf.c
new file mode 100644
index 0000000..79a22fb
--- /dev/null
+++ b/userland/libc/stdio/vfprintf.c
@@ -0,0 +1,243 @@
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+const char HEX_SET[0x10] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+#define FILE_WRITE(_f, _s, _l, _r) \
+ { \
+ size_t _rc = _f->write(_f, (const unsigned char *)_s, _l); \
+ if ((size_t)-1 == _rc) \
+ assert(0); \
+ *(int *)(_r) += _rc; \
+ }
+// if ((size_t)0 == _rc) \
+// assert(0); \
+
+int fprint_num(FILE *f, int n, int base, char *char_set, int prefix,
+ int zero_padding, int right_padding) {
+ int c = 0;
+ if (0 == n) {
+ zero_padding = 1;
+ prefix = 1;
+ }
+ char str[32];
+ int i = 0;
+ for (; n != 0 && i < 32; i++, n /= base)
+ // str[i] = (n % base) + '0';
+ str[i] = char_set[(n % base)];
+
+ char t = (zero_padding) ? '0' : ' ';
+ int orig_i = i;
+
+ if (!right_padding) {
+ for (; prefix - orig_i > 0; prefix--)
+ FILE_WRITE(f, &t, 1, &c);
+ }
+
+ for (i--; i >= 0; i--)
+ FILE_WRITE(f, &(str[i]), 1, &c);
+
+ if (right_padding) {
+ for (; prefix - orig_i > 0; prefix--)
+ FILE_WRITE(f, &t, 1, &c);
+ }
+ return c;
+}
+
+int fprint_int(FILE *f, int n, int prefix, int zero_padding,
+ int right_padding) {
+ return fprint_num(f, n, 10, "0123456789", prefix, zero_padding,
+ right_padding);
+}
+
+int fprint_hex(FILE *f, int n, int prefix, int zero_padding,
+ int right_padding) {
+ return fprint_num(f, n, 16, "0123456789abcdef", prefix, zero_padding,
+ right_padding);
+}
+
+int fprint_octal(FILE *f, int n, int prefix, int zero_padding,
+ int right_padding) {
+ return fprint_num(f, n, 8, "012345678", prefix, zero_padding, right_padding);
+}
+
+int print_string(FILE *f, const char *s, int *rc, int prefix, int right_padding,
+ int precision) {
+ int l = strlen(s);
+ char t = ' ';
+ int c = 0;
+ if (!right_padding) {
+ if (prefix)
+ assert(-1 == precision); // FIXME: Is this correct?
+ for (; prefix - l > 0; prefix--)
+ FILE_WRITE(f, &t, 1, &c);
+ }
+ int bl = precision;
+ for (; *s; s++, (*rc)++) {
+ if (precision != -1) {
+ if (0 == bl)
+ break;
+ bl--;
+ }
+ int r;
+ FILE_WRITE(f, (const unsigned char *)s, 1, &r);
+ assert(r != 0);
+ }
+ if (right_padding) {
+ assert(-1 == precision); // FIXME: Is this correct?
+ for (; prefix - l > 0; prefix--)
+ FILE_WRITE(f, &t, 1, &c);
+ }
+ (*rc) += c;
+ return 0;
+}
+
+int parse_precision(const char **fmt) {
+ const char *s = *fmt;
+ int rc = 0;
+ for (int i = 0;; i++, s++) {
+ if ('\0' == *s)
+ break;
+ const char c = *s;
+ if ('*' == c) {
+ assert(i == 0);
+ return -1;
+ } else if (!(c >= '0' && c <= '9')) {
+ s--;
+ break;
+ }
+ rc *= 10;
+ rc += c - '0';
+ }
+ *fmt = s;
+ return rc;
+}
+
+int vfprintf(FILE *f, const char *fmt, va_list ap) {
+ int rc = 0;
+ const char *s = fmt;
+ int prefix = 0;
+
+ int zero_padding = 0;
+ int right_padding = 0;
+
+ int cont = 0;
+ int precision = -1;
+ for (; *s; s++) {
+ if (!cont && '%' != *s) {
+ FILE_WRITE(f, (const unsigned char *)s, 1, &rc);
+ continue;
+ }
+ if (!cont) {
+ cont = 1;
+ continue;
+ }
+
+ if ('\0' == *s)
+ break;
+
+ switch (*s) {
+ case '.':
+ s++;
+ assert('\0' != *s);
+ precision = parse_precision(&s);
+ assert('\0' != *s);
+ if (-1 == precision)
+ precision = va_arg(ap, int);
+ cont = 1;
+ break;
+ case '0':
+ prefix *= 10;
+ if (0 == prefix)
+ zero_padding = 1;
+ cont = 1;
+ break;
+ case '-':
+ assert(0 == prefix);
+ right_padding = 1;
+ cont = 1;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ prefix *= 10;
+ prefix += (*s) - '0';
+ cont = 1;
+ break;
+ case 'i':
+ case 'd':
+ if(-1 != precision) {
+ zero_padding = 1;
+ prefix = precision;
+ right_padding = 0;
+ }
+ rc += fprint_int(f, va_arg(ap, int), prefix, zero_padding, right_padding);
+ cont = 0;
+ break;
+ case 'u':
+ assert(-1 == precision);
+ rc += fprint_int(f, va_arg(ap, unsigned int), prefix, zero_padding,
+ right_padding);
+ cont = 0;
+ break;
+ case 's': {
+ assert(!zero_padding); // this is not supported to strings
+ char *a = va_arg(ap, char *);
+ if (!a) {
+ if (-1 ==
+ print_string(f, "(NULL)", &rc, prefix, right_padding, precision))
+ return -1;
+ cont = 0;
+ break;
+ }
+ if (-1 == print_string(f, a, &rc, prefix, right_padding, precision))
+ return -1;
+ cont = 0;
+ break;
+ }
+ case 'p': // TODO: Print this out in a nicer way
+ case 'x':
+ assert(-1 == precision);
+ rc += fprint_hex(f, va_arg(ap, const uint32_t), prefix, zero_padding,
+ right_padding);
+ cont = 0;
+ break;
+ case 'o':
+ assert(-1 == precision);
+ rc += fprint_octal(f, va_arg(ap, const uint32_t), prefix, zero_padding,
+ right_padding);
+ cont = 0;
+ break;
+ case '%': {
+ FILE_WRITE(f, (const unsigned char *)"%", 1, &rc);
+ cont = 0;
+ break;
+ }
+ case 'c': {
+ char c = va_arg(ap, const int);
+ FILE_WRITE(f, (const unsigned char *)&c, 1, &rc);
+ cont = 0;
+ break;
+ }
+ default:
+ printf("got %c but that is not supported by printf\n", *s);
+ assert(0);
+ break;
+ }
+ if (!cont) {
+ prefix = 0;
+ zero_padding = right_padding = 0;
+ precision = -1;
+ }
+ }
+ return rc;
+}
diff --git a/userland/libc/stdio/vprintf.c b/userland/libc/stdio/vprintf.c
new file mode 100644
index 0000000..8a8dc33
--- /dev/null
+++ b/userland/libc/stdio/vprintf.c
@@ -0,0 +1,3 @@
+#include <stdio.h>
+
+int vprintf(const char *format, va_list ap) { return vdprintf(1, format, ap); }