update project structures
[lunaix-os.git] / lunaix-os / libs / libc / stdio / sprintf.c
index 51466cd59483035be7e76e10f68c5a9b96e13888..61772676c3aeff4bcc4da7ea10c96f704d37738c 100644 (file)
 #include <libc/string.h>
 #include <stdint.h>
 
+#define NUMBUFSIZ 24
+
+static const char flag_chars[] = "#0- +";
+
+#define FLAG_ALT                (1<<0)
+#define FLAG_ZERO               (1<<1)
+#define FLAG_LEFTJUSTIFY        (1<<2)
+#define FLAG_SPACEPOSITIVE      (1<<3)
+#define FLAG_PLUSPOSITIVE       (1<<4)
+#define FLAG_NUMERIC            (1<<5)
+#define FLAG_SIGNED             (1<<6)
+#define FLAG_NEGATIVE           (1<<7)
+#define FLAG_ALT2               (1<<8)
+#define FLAG_CAPS               (1<<9)
+
+
+// FIXME: use something like IO_FILE to abstract this into a more flexible, stream based, vprintf
 void
-__sprintf_internal(char* buffer, char* fmt, va_list args)
+__sprintf_internal(char* buffer, char* fmt, va_list vargs)
 {
-    char c;
-    int num;
-    unsigned int ptr = 0;
-    unsigned int adv = 0;
-    while ((c = *fmt)) {
-        if (c != '%') {
-            buffer[ptr] = c;
-            adv = 1;
-        } else {
-            adv = 0;
-            fmt++;
-            switch (c = *fmt) {
-                case 'c': {
-                    buffer[ptr] = (char)va_arg(args, int);
-                    adv = 1;
-                    break;
-                }
-                case 's': {
-                    char* str = va_arg(args, char*);
-                    strcpy(buffer + ptr, str);
-                    adv = strlen(str);
-                    break;
-                }
-                case 'i':
-                // fall through
-                case 'd': {
-                    num = va_arg(args, int);
-                    __itoa_internal(num, buffer + ptr, 10, &adv);
-                    break;
-                }
-                case 'u': {
-                    unsigned int unum = va_arg(args, unsigned int);
-                    __uitoa_internal(unum, buffer + ptr, 10, &adv);
-                    break;
-                }
-                case 'o': {
-                    num = va_arg(args, int);
-                    __itoa_internal(num, buffer + ptr, 8, &adv);
-                    break;
-                }
-                case 'x':
-                // fall through
-                case 'X': {
-                    num = va_arg(args, int);
-                    __itoa_internal(num, buffer + ptr, 16, &adv);
-                    if (c == 'X') {
-                        int p = ptr;
-                        char c_;
-                        while ((c_ = buffer[p])) {
-                            buffer[p] = c_ & ~((c_ & 0x40) >> 1);
-                            p++;
-                        }
-                    }
-                    break;
-                }
-                case 'p': {
-                    uintptr_t dptr = va_arg(args, uintptr_t);
-                    buffer[ptr] = '0';
-                    buffer[ptr+1] = 'x';
-                    __itoa_internal((int)dptr, buffer + ptr + 2, 16, &adv);
-                    adv+=2;
-                    break;
-                }
-                case '%': {
-                    buffer[ptr] = c;
-                    adv = 1;
-                    break;
+    // This sprintf just a random implementation I found it on Internet . lol.
+    //      Of course, with some modifications for porting to LunaixOS :)
+
+    // TODO: support floating point.
+
+    char numbuf[NUMBUFSIZ];
+    uint32_t ptr = 0;
+    for (; *fmt; ++fmt) {
+        if (*fmt != '%') {
+            buffer[ptr++] = *fmt;
+            continue;
+        }
+
+        // process flags
+        int flags = 0;
+        for (++fmt; *fmt; ++fmt) {
+            const char* flagc = strchr(flag_chars, *fmt);
+            if (flagc) {
+                flags |= 1 << (flagc - flag_chars);
+            } else {
+                break;
+            }
+        }
+
+        // process width
+        int width = -1;
+        if (*fmt >= '1' && *fmt <= '9') {
+            for (width = 0; *fmt >= '0' && *fmt <= '9'; ) {
+                width = 10 * width + *fmt++ - '0';
+            }
+        } else if (*fmt == '*') {
+            width = va_arg(vargs, int);
+            ++fmt;
+        }
+
+        // process precision
+        int precision = -1;
+        if (*fmt == '.') {
+            ++fmt;
+            if (*fmt >= '0' && *fmt <= '9') {
+                for (precision = 0; *fmt >= '0' && *fmt <= '9'; ) {
+                    precision = 10 * precision + *fmt++ - '0';
                 }
-                default:
-                    // unknown
-                    break;
+            } else if (*fmt == '*') {
+                precision = va_arg(vargs, int);
+                ++fmt;
             }
+            if (precision < 0) {
+                precision = 0;
+            }
+        }
+
+        // process main conversion character
+        int base = 10;
+        unsigned long num = 0;
+        int length = 0;
+        char* data = "";
+    again:
+        switch (*fmt) {
+        case 'l':
+        case 'z':
+            length = 1;
+            ++fmt;
+            goto again;
+        case 'd':
+        case 'i': {
+            long x = length ? va_arg(vargs, long) : va_arg(vargs, int);
+            int negative = x < 0 ? FLAG_NEGATIVE : 0;
+            num = negative ? -x : x;
+            flags |= FLAG_NUMERIC | FLAG_SIGNED | negative;
+            break;
+        }
+        case 'u':
+        format_unsigned:
+            num = length ? va_arg(vargs, unsigned long) : va_arg(vargs, unsigned);
+            flags |= FLAG_NUMERIC;
+            break;
+        case 'x':
+            base = 16;
+            goto format_unsigned;
+        case 'X':
+            flags = flags | FLAG_CAPS;
+            base = 16;
+            goto format_unsigned;
+        case 'p':
+            num = (uintptr_t) va_arg(vargs, void*);
+            base = 16;
+            flags |= FLAG_ALT | FLAG_ALT2 | FLAG_NUMERIC;
+            break;
+        case 's':
+            data = va_arg(vargs, char*);
+            break;
+        case 'c':
+            data = numbuf;
+            numbuf[0] = va_arg(vargs, int);
+            numbuf[1] = '\0';
+            break;
+        default:
+            data = numbuf;
+            numbuf[0] = (*fmt ? *fmt : '%');
+            numbuf[1] = '\0';
+            if (!*fmt) {
+                fmt--;
+            }
+            break;
+        }
+
+        if (flags & FLAG_NUMERIC) {
+            data = itoa(num, numbuf, base);
+            int i = 0;
+            char c;
+            while ((flags & FLAG_CAPS) && (c = data[i]))
+            {
+                data[i] = c & ~((c & 0x40) >> 1);
+                i++;
+            }
+        }
+
+        const char* prefix = "";
+        if ((flags & FLAG_NUMERIC) && (flags & FLAG_SIGNED)) {
+            if (flags & FLAG_NEGATIVE) {
+                prefix = "-";
+            } else if (flags & FLAG_PLUSPOSITIVE) {
+                prefix = "+";
+            } else if (flags & FLAG_SPACEPOSITIVE) {
+                prefix = " ";
+            }
+        } else if ((flags & FLAG_NUMERIC) && (flags & FLAG_ALT)
+                   && (base == 16 || base == -16)
+                   && (num || (flags & FLAG_ALT2))) {
+            prefix = "0x";
+        }
+
+        int len;
+        if (precision >= 0 && !(flags & FLAG_NUMERIC)) {
+            len = strnlen(data, precision);
+        } else {
+            len = strlen(data);
+        }
+        int zeros;
+        if ((flags & FLAG_NUMERIC) && precision >= 0) {
+            zeros = precision > len ? precision - len : 0;
+        } else if ((flags & FLAG_NUMERIC) && (flags & FLAG_ZERO)
+                   && !(flags & FLAG_LEFTJUSTIFY)
+                   && len + (int) strlen(prefix) < width) {
+            zeros = width - len - strlen(prefix);
+        } else {
+            zeros = 0;
+        }
+        width -= len + zeros + strlen(prefix);
+        for (; !(flags & FLAG_LEFTJUSTIFY) && width > 0; --width) {
+            buffer[ptr++] = ' ';
+        }
+        for (; *prefix; ++prefix) {
+            buffer[ptr++] = *prefix;
+        }
+        for (; zeros > 0; --zeros) {
+            buffer[ptr++] = '0';
+        }
+        for (; len > 0; ++data, --len) {
+            buffer[ptr++] = *data;
+        }
+        for (; width > 0; --width) {
+            buffer[ptr++] = ' ';
         }
-        fmt++;
-        ptr += adv;
     }
-    buffer[ptr] = '\0';
+    buffer[ptr++] = '\0';
 }
 
 void