Implement printf in the CRT
authorcoderain <coderain@sdf.org>
Thu, 8 Mar 2018 20:38:18 +0000 (21:38 +0100)
committercoderain <coderain@sdf.org>
Thu, 8 Mar 2018 20:38:18 +0000 (21:38 +0100)
crt/include/stdio.h
crt/include/stdlib.h
crt/src/io.c
crt/src/io_priv.h [new file with mode: 0644]
crt/src/malloc.c
crt/src/numconv.c
crt/src/printf.c [new file with mode: 0644]

index aa09f56549cb10b30c2d8fefb7a41825b15483c0..467956d652b30d5f08fc5b8eab61b61dc1c49232 100644 (file)
@@ -55,6 +55,8 @@ int fputc_unlocked(int c, FILE *stream);
 char *fgets_unlocked(char *s, int size, FILE *stream);
 int fputs_unlocked(const char *s, FILE *stream);
 
+FILE *fopen(const char *pathname, const char *mode);
+FILE *fdopen(int fd, const char *mode);
 FILE *fmemopen(void *buf, size_t size, const char *mode);
 int fclose(FILE *stream);
 
@@ -68,4 +70,16 @@ void setbuf(FILE *stream, char *buf);
 void setbuffer(FILE *stream, char *buf, size_t size);
 void setlinebuf(FILE *stream);
 
+int printf(const char *format, ...);
+int fprintf(FILE *stream, const char *format, ...);
+int dprintf(int fd, const char *format, ...);
+int sprintf(char *str, const char *format, ...);
+int snprintf(char *str, size_t size, const char *format, ...);
+int vprintf(const char *format, va_list ap);
+int vfprintf(FILE *stream, const char *format, va_list ap);
+int vdprintf(int fd, const char *format, va_list ap);
+int vsprintf(char *str, const char *format, va_list ap);
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+
+
 #endif
index e20268ef676f9ff81d714b1458bf9d0d4df4ea15..f1a9524cf684c5efacc27fcf20dc3ec140022d42 100644 (file)
 #include <errno.h>
 
 char *itoa(int value, char *str, int base);
-int atoi(char *str);
-int strtol(const char *str, char **endptr, int base);
+char *ltoa(long value, char *str, int base);
+char *lltoa(long long value, char *str, int base);
+char *uitoa(unsigned int value, char *str, int base);
+char *ultoa(unsigned long value, char *str, int base);
+char *ulltoa(unsigned long long value, char *str, int base);
+int atoi(const char *str);
+long atol(const char *str);
+long long atoll(const char *str);
+long strtol(const char *str, char **endptr, int base);
+long long strtoll(const char *str, char **endptr, int base);
 unsigned long strtoul(const char *str, char **endptr, int base);
 unsigned long long strtoull(const char *str, char **endptr, int base);
 
index f4338b9ad68507f9df49c7d5e66baf3b90e65c1e..2b461679844b9cc1e7e7bb44f433fe36c5e42c98 100644 (file)
 
 #include <stdio.h>
 #include <unistd.h>
-#include <monolithium.h>
-
-#define FILE_READ        (1 << 0)
-#define FILE_WRITE       (1 << 1)
-#define FILE_APPEND      (1 << 2)
-#define FILE_BUFFER_LINE (1 << 3)
-#define FILE_BUFFER_DIR  (1 << 4)
-#define FILE_BUFFER_FULL (1 << 31)
-
-struct __crt_file
-{
-    list_entry_t link;
-    handle_t mutex;
-    uint32_t flags;
-
-    int fd;
-    size_t size;
-
-    char *buffer;
-    size_t buffer_size;
-    size_t buffer_start, buffer_end;
-};
+#include "io_priv.h"
 
 static list_entry_t open_files;
 
@@ -388,9 +367,61 @@ int puts(const char *s)
     return 0;
 }
 
+FILE *fdopen(int fd, const char *mode)
+{
+    FILE *stream = (FILE*)malloc(sizeof(FILE) + BUFSIZ);
+
+    if (stream == NULL)
+    {
+        errno = ENOMEM;
+        return NULL;
+    }
+
+    sysret_t status = syscall_create_mutex(NULL, TRUE, &stream->mutex);
+    if (status != ERR_SUCCESS)
+    {
+        free(stream);
+        errno = __crt_translate_error(status);
+        return NULL;
+    }
+
+    stream->flags = 0;
+
+    const char *ptr;
+    for (ptr = mode; *ptr; ptr++)
+    {
+        switch (*ptr)
+        {
+        case 'r':
+            stream->flags |= FILE_READ;
+            break;
+        case 'w':
+            stream->flags |= FILE_WRITE;
+            break;
+        case 'a':
+            stream->flags |= FILE_APPEND;
+            break;
+        case '+':
+            stream->flags |= FILE_READ | FILE_WRITE;
+            break;
+        }
+    }
+
+    stream->fd = fd;
+
+    char *buffer = (char*)((uintptr_t)stream + sizeof(FILE));
+    setbuf(stream, buffer);
+    return stream;
+}
+
 FILE *fmemopen(void *buf, size_t size, const char *mode)
 {
     FILE *stream = (FILE*)malloc(sizeof(FILE));
+    if (stream == NULL)
+    {
+        errno = ENOMEM;
+        return NULL;
+    }
 
     sysret_t status = syscall_create_mutex(NULL, TRUE, &stream->mutex);
     if (status != ERR_SUCCESS)
@@ -423,7 +454,6 @@ FILE *fmemopen(void *buf, size_t size, const char *mode)
     }
 
     stream->fd = -1;
-    stream->size = size;
     stream->buffer = buf;
     stream->buffer_size = size;
     stream->buffer_start = stream->buffer_end = 0;
@@ -432,9 +462,47 @@ FILE *fmemopen(void *buf, size_t size, const char *mode)
     return stream;
 }
 
+FILE *fopen(const char *pathname, const char *mode)
+{
+    int open_flags = 0;
+
+    const char *ptr;
+    for (ptr = mode; *ptr; ptr++)
+    {
+        switch (*ptr)
+        {
+        case 'r':
+            open_flags = (open_flags & ~3) | O_RDONLY;
+            break;
+        case 'w':
+            open_flags = (open_flags & ~3) | O_WRONLY;
+            break;
+        case 'a':
+            open_flags |= O_APPEND;
+            break;
+        case '+':
+            open_flags = (open_flags & ~3) | O_RDWR;
+            break;
+        }
+    }
+
+    int fd = open(pathname, open_flags);
+    if (fd < 0) return NULL;
+
+    FILE *stream = fdopen(fd, mode);
+    if (!stream) close(fd);
+    return NULL;
+}
+
 int fclose(FILE *stream)
 {
+    fflush(stream);
     list_remove(&stream->link);
+    syscall_wait_mutex(stream->mutex, NO_TIMEOUT);
+
+    if (stream->fd) close(stream->fd);
+
+    syscall_close_object(stream->mutex);
     free(stream);
     return 0;
 }
diff --git a/crt/src/io_priv.h b/crt/src/io_priv.h
new file mode 100644 (file)
index 0000000..c5258da
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * io_priv.h
+ *
+ * Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _IO_PRIV_H_
+#define _IO_PRIV_H_
+
+#include <monolithium.h>
+
+#define FILE_READ        (1 << 0)
+#define FILE_WRITE       (1 << 1)
+#define FILE_APPEND      (1 << 2)
+#define FILE_BUFFER_LINE (1 << 3)
+#define FILE_BUFFER_DIR  (1 << 4)
+#define FILE_BUFFER_FULL (1 << 31)
+
+struct __crt_file
+{
+    list_entry_t link;
+    handle_t mutex;
+    uint32_t flags;
+
+    int fd;
+
+    char *buffer;
+    size_t buffer_size;
+    size_t buffer_start, buffer_end;
+};
+
+#endif
index 5d09e0ccd3f2db4d539bdec1edd27534590dbad0..077878f67b4996c5455a12d76bac7d3479bc8010 100644 (file)
@@ -30,7 +30,7 @@ typedef struct
     size_t size;
 } heap_header_t;
 
-__crt_heap_t *__crt_default_heap;
+__crt_heap_t *__crt_default_heap = NULL;
 
 static void __crt_heap_coalesce(__crt_heap_t *heap)
 {
index 1d89453b4e936433e6c81234c0a2e7c0f49735ed..2b425e1f9af757860264f401ec641c5c743bf8ac 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 
-char *itoa(int value, char *str, int base)
-{
-    int cnt = 0;
-    const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-    int append_sign = 0;
-
-    if (base < 2 || base > 36) return NULL;
-
-    if (value == 0)
-    {
-        str[0] = '0';
-        str[1] = 0;
-        return str;
+#define DEFINE_XTOA(t, p, s)                                            \
+    char *p##toa(t value, char *str, int base)                          \
+    {                                                                   \
+        char *ptr = str;                                                \
+        if (base < 2 || base > 36) return NULL;                         \
+                                                                        \
+        if (value == 0##s)                                              \
+        {                                                               \
+            *ptr++ = '0';                                               \
+            *ptr++ = '\0';                                              \
+            return str;                                                 \
+        }                                                               \
+                                                                        \
+        if (value < 0##s)                                               \
+        {                                                               \
+            *ptr++ = '-';                                               \
+            value = -value;                                             \
+        }                                                               \
+                                                                        \
+        t temp;                                                         \
+        for (temp = value; temp > 0##s; temp /= (t)base) ptr++;         \
+        *ptr = '\0';                                                    \
+                                                                        \
+        while (value > 0##s)                                            \
+        {                                                               \
+            *--ptr = all_digits[value % (t)base];                       \
+            value /= (t)base;                                           \
+        }                                                               \
+                                                                        \
+        return str;                                                     \
+    }                                                                   \
+                                                                        \
+    char *u##p##toa(unsigned t value, char *str, int base)              \
+    {                                                                   \
+        char *ptr = str;                                                \
+        if (base < 2 || base > 36) return NULL;                         \
+                                                                        \
+        if (value == 0U##s)                                             \
+        {                                                               \
+            *ptr++ = '0';                                               \
+            *ptr++ = '\0';                                              \
+            return str;                                                 \
+        }                                                               \
+                                                                        \
+        unsigned t temp;                                                \
+        for (temp = value; temp > 0U##s; temp /= (unsigned t)base) ptr++; \
+        *ptr = '\0';                                                    \
+                                                                        \
+        while (value > 0U##s)                                           \
+        {                                                               \
+            *--ptr = all_digits[value % (unsigned t)base];              \
+            value /= (unsigned t)base;                                  \
+        }                                                               \
+                                                                        \
+        return str;                                                     \
     }
 
-    if (value < 0 && base == 10)
-    {
-        value = -value;
-        append_sign = 1;
+#define DEFINE_ATOX(t, n, p, s)                                         \
+    t ato##p(const char *str)                                           \
+    {                                                                   \
+        return strto##p(str, NULL, 10);                                 \
+    }                                                                   \
+                                                                        \
+    t strto##p(const char *str, char **endptr, int base)                \
+    {                                                                   \
+        const char *ptr;                                                \
+        t result = 0##s;                                                \
+        int overflow = 0, negative = 0;                                 \
+                                                                        \
+        if (base < 2 || base > 36) return 0;                            \
+                                                                        \
+        switch (*str)                                                   \
+        {                                                               \
+        case '-':                                                       \
+            negative = 1;                                               \
+        case '+':                                                       \
+            str++;                                                      \
+            break;                                                      \
+        }                                                               \
+                                                                        \
+        for (ptr = str; *ptr; ptr++)                                    \
+        {                                                               \
+            char *digit_ptr = strchr(all_digits, toupper(*ptr));        \
+            if (digit_ptr == NULL) break;                               \
+                                                                        \
+            t digit = (t)(digit_ptr - all_digits);                      \
+            if (digit >= base) break;                                   \
+                                                                        \
+            t new_result = result * (t)base + digit;                    \
+            if (new_result < result) overflow = 1;                      \
+            result = new_result;                                        \
+        }                                                               \
+                                                                        \
+        if (overflow)                                                   \
+        {                                                               \
+            errno = ERANGE;                                             \
+            return negative ? n##_MIN : n##_MAX;                        \
+        }                                                               \
+                                                                        \
+        if (negative) result = -result;                                 \
+        if (endptr) *endptr = (char*)ptr;                               \
+        return result;                                                  \
+    }                                                                   \
+                                                                        \
+    unsigned t strtou##p(const char *str, char **endptr, int base)      \
+    {                                                                   \
+        const char *ptr;                                                \
+        unsigned t result = 0UL;                                        \
+        int overflow = 0;                                               \
+                                                                        \
+        if (base < 2 || base > 36) return 0;                            \
+                                                                        \
+        for (ptr = str; *ptr; ptr++)                                    \
+        {                                                               \
+            char *digit_ptr = strchr(all_digits, toupper(*ptr));        \
+            if (digit_ptr == NULL) break;                               \
+                                                                        \
+            unsigned t digit = (unsigned t)(digit_ptr - all_digits);    \
+            if (digit >= base) break;                                   \
+                                                                        \
+            unsigned t new_result = result * (unsigned t)base + digit;  \
+            if (new_result < result) overflow = 1;                      \
+            result = new_result;                                        \
+        }                                                               \
+                                                                        \
+        if (overflow)                                                   \
+        {                                                               \
+            errno = ERANGE;                                             \
+            return U##n##_MAX;                                          \
+        }                                                               \
+                                                                        \
+        if (endptr) *endptr = (char*)ptr;                               \
+        return result;                                                  \
     }
 
-    uint32_t u_value = (uint32_t) value;
-
-    while (u_value > 0)
-    {
-        str[cnt++] = digits[u_value % base];
-        u_value /= base;
-    }
-
-    if (append_sign) str[cnt++] = '-';
-    str[cnt] = 0;
-    strrev(str);
-
-    return str;
-}
-
-int atoi(char *str)
-{
-    int i, len = strlen(str);
-    int ret = 0, z = 1;
-    int negative = (str[0] == '-');
+static const char all_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
-    if (negative) str[0] = '0';
+DEFINE_XTOA(int, i, );
+DEFINE_XTOA(long, l, L);
+DEFINE_XTOA(long long, ll, LL);
 
-    for (i = len - 1; i >= 0; i--)
-    {
-        if (str[i] >= '0' && str[i] <= '9')
-        {
-            int prev_ret = ret;
-            ret += (str[i] - '0') * z;
-            if (ret < prev_ret)
-            {
-                ret = 2147483647;
-                break;
-            }
-            z *= 10;
-        }
-        else return 0;
-    }
-    if (negative) return -ret;
-    else return ret;
-}
+DEFINE_ATOX(long, LONG, l, L);
+DEFINE_ATOX(long long, LLONG, ll, LL);
 
-int strtol(const char *str, char **endptr, int base)
+int atoi(const char *str)
 {
-    int result = 0;
-    const char *ptr;
-    int negative = 0;
-    const char *digits = "0123456789abcdefghijklmnopqrstuvxyz";
-    if (base < 2 || base > 36) return 0;
-
-    switch (*str)
-    {
-    case '-':
-        negative = 1;
-    case '+':
-        str++;
-        break;
-    }
-
-    for (ptr = str; *ptr; ptr++)
-    {
-        char *digit_ptr = strchr(digits, tolower(*ptr));
-        if (digit_ptr == NULL) break;
-
-        int digit = (int)(digit_ptr - digits);
-        if (digit >= base) break;
-
-        result *= base;
-        result += digit;
-    }
-
-    if (negative) result = -result;
-    if (endptr) *endptr = (char*)ptr;
-    return result;
-}
-
-unsigned long strtoul(const char *str, char **endptr, int base)
-{
-    unsigned long result = 0;
-    const char *ptr;
-    const char *digits = "0123456789abcdefghijklmnopqrstuvxyz";
-    if (base < 2 || base > 36) return 0;
-
-    for (ptr = str; *ptr; ptr++)
-    {
-        char *digit_ptr = strchr(digits, tolower(*ptr));
-        if (digit_ptr == NULL) break;
-
-        int digit = (int)(digit_ptr - digits);
-        if (digit >= base || result > (ULONG_MAX / base)) break;
-
-        result *= base;
-        result += digit;
-    }
-
-    if (endptr) *endptr = (char*)ptr;
-    return result;
-}
-
-unsigned long long strtoull(const char *str, char **endptr, int base)
-{
-    unsigned long long result = 0;
-    const char *ptr;
-    const char *digits = "0123456789abcdefghijklmnopqrstuvxyz";
-    if (base < 2 || base > 36) return 0;
-
-    for (ptr = str; *ptr; ptr++)
-    {
-        char *digit_ptr = strchr(digits, tolower(*ptr));
-        if (digit_ptr == NULL) break;
-
-        int digit = (int)(digit_ptr - digits);
-        if (digit >= base) break;
-        if (result > (ULONG_LONG_MAX / base)) break;
-
-        result *= (unsigned long long)base;
-        result += (unsigned long long)digit;
-    }
-
-    if (endptr) *endptr = (char*)ptr;
-    return result;
+    return (int)strtol(str, NULL, 10);
 }
diff --git a/crt/src/printf.c b/crt/src/printf.c
new file mode 100644 (file)
index 0000000..c7fae78
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * printf.c
+ *
+ * Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <monolithium.h>
+#include "io_priv.h"
+
+#define __CRT_PRINTF_FLAG_ALIGN_LEFT         (1 << 0)
+#define __CRT_PRINTF_FLAG_PLUS               (1 << 1)
+#define __CRT_PRINTF_FLAG_SPACE              (1 << 2)
+#define __CRT_PRINTF_FLAG_EXTRA              (1 << 3)
+#define __CRT_PRINTF_FLAG_ZERO_PAD           (1 << 4)
+#define __CRT_PRINTF_FLAG_EXTERNAL_WIDTH     (1 << 5)
+#define __CRT_PRINTF_FLAG_EXTERNAL_PRECISION (1 << 6)
+#define __CRT_PRINTF_FLAG_DEFAULT_WIDTH      (1 << 7)
+#define __CRT_PRINTF_FLAG_DEFAULT_PRECISION  (1 << 8)
+
+typedef struct
+{
+    FILE *stream;
+    char *string;
+    size_t size;
+} __crt_stream_or_string_t;
+
+static int __crt_strputc(__crt_stream_or_string_t *str, char c)
+{
+    if (str->stream)
+    {
+        return fputc_unlocked(c, str->stream) != EOF;
+    }
+    else if (str->size > 1)
+    {
+        *str->string++ = c;
+        str->size--;
+        return 1;
+    }
+
+    return 0;
+}
+
+static int __crt_strputs(__crt_stream_or_string_t *str, const char *s)
+{
+    if (str->stream)
+    {
+        return fputs_unlocked(s, str->stream) != EOF ? strlen(s) : 0;
+    }
+    else if (str->size > 1)
+    {
+        int length = strlen(s);
+        strncpy(str->string, s, str->size - 1);
+        if (length > (str->size - 1)) length = str->size - 1;
+        str->string += length;
+        str->size -= length;
+    }
+
+    return 0;
+}
+
+static inline int __crt_vstrprintf(__crt_stream_or_string_t *str, const char *format, va_list ap)
+{
+    int ret = 0;
+    if (str->stream) syscall_wait_mutex(str->stream->mutex, NO_TIMEOUT);
+
+    const char *ptr;
+    for (ptr = format; *ptr; ptr++)
+    {
+        if (*ptr == '%')
+        {
+            unsigned long flags = 0;
+            unsigned int width = 0, precision = 0;
+
+            ptr++;
+
+            while (*ptr)
+            {
+                if (*ptr == '-') flags |= __CRT_PRINTF_FLAG_ALIGN_LEFT;
+                else if (*ptr == '+') flags |= __CRT_PRINTF_FLAG_PLUS;
+                else if (*ptr == ' ') flags |= __CRT_PRINTF_FLAG_SPACE;
+                else if (*ptr == '#') flags |= __CRT_PRINTF_FLAG_EXTRA;
+                else if (*ptr == '0') flags |= __CRT_PRINTF_FLAG_ZERO_PAD;
+                else break;
+
+                ptr++;
+            }
+
+            if (*ptr == '*') flags |= __CRT_PRINTF_FLAG_EXTERNAL_WIDTH;
+            else if (isdigit(*ptr)) width = strtoul(ptr, (char**)&ptr, 10);
+            else flags |= __CRT_PRINTF_FLAG_DEFAULT_WIDTH;
+
+            if (*ptr == '.')
+            {
+                ptr++;
+                if (*ptr == '*') flags |= __CRT_PRINTF_FLAG_EXTERNAL_PRECISION;
+                else if (isdigit(*ptr)) precision = strtoul(ptr, (char**)&ptr, 10);
+                else flags |= __CRT_PRINTF_FLAG_DEFAULT_PRECISION;
+            }
+            else
+            {
+                flags |= __CRT_PRINTF_FLAG_DEFAULT_PRECISION;
+            }
+
+            int variable_size = 0;
+
+            if (*ptr == 'h')
+            {
+                variable_size--;
+
+                if (*++ptr == 'h')
+                {
+                    variable_size--;
+                    ptr++;
+                }
+            }
+            else if (*ptr == 'l')
+            {
+                variable_size--;
+
+                if (*++ptr == 'l')
+                {
+                    variable_size--;
+                    ptr++;
+                }
+            }
+
+            const char *data = NULL;
+            char buffer[512];
+            int radix = 10;
+
+            switch (*ptr)
+            {
+            case 's':
+                data = va_arg(ap, const char*);
+                break;
+
+            case 'c':
+                data = buffer;
+                buffer[0] = (char)va_arg(ap, int);
+                buffer[1] = '\0';
+                break;
+
+            case 'd':
+            case 'i':
+                if (flags & __CRT_PRINTF_FLAG_DEFAULT_WIDTH) width = 0;
+                if (flags & __CRT_PRINTF_FLAG_DEFAULT_PRECISION) precision = 1;
+                data = buffer;
+
+                switch (variable_size)
+                {
+                case -2: itoa((char)va_arg(ap, int), buffer, 10); break;
+                case -1: itoa((short)va_arg(ap, int), buffer, 10); break;
+                case  0: itoa(va_arg(ap, int), buffer, 10); break;
+                case  1: ltoa(va_arg(ap, long), buffer, 10); break;
+                case  2: lltoa(va_arg(ap, long long), buffer, 10); break;
+                }
+                break;
+
+            case 'o':
+                radix = 8;
+                goto read_variable;
+            case 'x':
+            case 'X':
+                radix = 16;
+            case 'u':
+            read_variable:
+                if (flags & __CRT_PRINTF_FLAG_DEFAULT_WIDTH) width = 0;
+                if (flags & __CRT_PRINTF_FLAG_DEFAULT_PRECISION) precision = 1;
+                data = buffer;
+
+                switch (variable_size)
+                {
+                case -2: uitoa((unsigned char)va_arg(ap, unsigned int), buffer, radix); break;
+                case -1: uitoa((unsigned short)va_arg(ap, unsigned int), buffer, radix); break;
+                case  0: uitoa(va_arg(ap, unsigned int), buffer, radix); break;
+                case  1: ultoa(va_arg(ap, unsigned long), buffer, radix); break;
+                case  2: ulltoa(va_arg(ap, unsigned long long), buffer, radix); break;
+                }
+                break;
+
+            case '%':
+                data = buffer;
+                strcpy(buffer, "%");
+                break;
+            }
+
+            if (flags & __CRT_PRINTF_FLAG_EXTERNAL_WIDTH) width = va_arg(ap, unsigned int);
+            if (flags & __CRT_PRINTF_FLAG_EXTERNAL_PRECISION) precision = va_arg(ap, unsigned int);
+
+            int length = strlen(data);
+            char padding = (flags &__CRT_PRINTF_FLAG_ZERO_PAD) ? '0' : ' ';
+
+            switch (*ptr)
+            {
+            case 's':
+                if (!(flags & __CRT_PRINTF_FLAG_ALIGN_LEFT))
+                {
+                    while (length < width)
+                    {
+                        __crt_strputc(str, padding);
+                        length++;
+                    }
+                }
+
+                if (precision)
+                {
+                    while (*data && precision > 0)
+                    {
+                        __crt_strputc(str, *data++);
+                        precision--;
+                    }
+                }
+                else
+                {
+                    __crt_strputs(str, data ? data : "(null)");
+                }
+                break;
+
+            case 'd':
+            case 'i':
+            case 'o':
+            case 'u':
+            case 'x':
+            case 'X':
+                if (flags & (__CRT_PRINTF_FLAG_PLUS | __CRT_PRINTF_FLAG_SPACE))
+                {
+                    if (*data == '-')
+                    {
+                        __crt_strputc(str, '-');
+                        data++;
+                    }
+                    else
+                    {
+                        __crt_strputc(str, (flags & __CRT_PRINTF_FLAG_PLUS) ? '+' : ' ');
+                        length++;
+                    }
+                }
+
+                if (!(flags & __CRT_PRINTF_FLAG_ALIGN_LEFT) && width > precision)
+                {
+                    while (length < width - precision)
+                    {
+                        __crt_strputc(str, padding);
+                        length++;
+                    }
+
+                    while (length < width)
+                    {
+                        __crt_strputc(str, '0');
+                        length++;
+                    }
+                }
+                else
+                {
+                    while (length < precision)
+                    {
+                        __crt_strputc(str, '0');
+                        length++;
+                    }
+                }
+
+                __crt_strputs(str, data);
+                break;
+            }
+
+            while (length < width)
+            {
+                __crt_strputc(str, padding);
+                length++;
+            }
+        }
+        else
+        {
+            ret += __crt_strputc(str, *ptr);
+        }
+    }
+
+    if (str->string) *str->string = '\0';
+    if (str->stream) syscall_release_mutex(str->stream->mutex);
+    return ret;
+}
+
+int vsnprintf(char *string, size_t size, const char *format, va_list ap)
+{
+    __crt_stream_or_string_t str = { .stream = NULL, .string = string, .size = size };
+    return __crt_vstrprintf(&str, format, ap);
+}
+
+int vsprintf(char *str, const char *format, va_list ap)
+{
+    return vsnprintf(str, -1, format, ap);
+}
+
+int vprintf(const char *format, va_list ap)
+{
+    return vfprintf(stdout, format, ap);
+}
+
+int vfprintf(FILE *stream, const char *format, va_list ap)
+{
+    __crt_stream_or_string_t str = { .stream = stream, .string = NULL, .size = 0 };
+    return __crt_vstrprintf(&str, format, ap);
+}
+
+int fprintf(FILE *stream, const char *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    int ret = vfprintf(stream, format, ap);
+    va_end(ap);
+    return ret;
+}
+
+int snprintf(char *str, size_t size, const char *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    int ret = vsnprintf(str, size, format, ap);
+    va_end(ap);
+    return ret;
+}
+
+int sprintf(char *str, const char *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    int ret = vsprintf(str, format, ap);
+    va_end(ap);
+    return ret;
+}
+
+int printf(const char *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    int ret = vprintf(format, ap);
+    va_end(ap);
+    return ret;
+}