dead simple rtc code for getting current datetime.
[lunaix-os.git] / lunaix-os / libs / libc / stdio / sprintf.c
1 #define __LUNAIX_LIBC
2 #include <libc/stdio.h>
3 #include <libc/stdlib.h>
4 #include <libc/string.h>
5 #include <stdint.h>
6
7 #define NUMBUFSIZ 24
8
9 static const char flag_chars[] = "#0- +";
10
11 #define FLAG_ALT                (1<<0)
12 #define FLAG_ZERO               (1<<1)
13 #define FLAG_LEFTJUSTIFY        (1<<2)
14 #define FLAG_SPACEPOSITIVE      (1<<3)
15 #define FLAG_PLUSPOSITIVE       (1<<4)
16 #define FLAG_NUMERIC            (1<<5)
17 #define FLAG_SIGNED             (1<<6)
18 #define FLAG_NEGATIVE           (1<<7)
19 #define FLAG_ALT2               (1<<8)
20 #define FLAG_CAPS               (1<<9)
21
22
23 // FIXME: use something like IO_FILE to abstract this into a more flexible, stream based, vprintf
24 void
25 __sprintf_internal(char* buffer, char* fmt, va_list vargs)
26 {
27     // This sprintf just a random implementation I found it on Internet . lol.
28     //      Of course, with some modifications for porting to LunaixOS :)
29
30     // TODO: support floating point.
31
32     char numbuf[NUMBUFSIZ];
33     uint32_t ptr = 0;
34     for (; *fmt; ++fmt) {
35         if (*fmt != '%') {
36             buffer[ptr++] = *fmt;
37             continue;
38         }
39
40         // process flags
41         int flags = 0;
42         for (++fmt; *fmt; ++fmt) {
43             const char* flagc = strchr(flag_chars, *fmt);
44             if (flagc) {
45                 flags |= 1 << (flagc - flag_chars);
46             } else {
47                 break;
48             }
49         }
50
51         // process width
52         int width = -1;
53         if (*fmt >= '1' && *fmt <= '9') {
54             for (width = 0; *fmt >= '0' && *fmt <= '9'; ) {
55                 width = 10 * width + *fmt++ - '0';
56             }
57         } else if (*fmt == '*') {
58             width = va_arg(vargs, int);
59             ++fmt;
60         }
61
62         // process precision
63         int precision = -1;
64         if (*fmt == '.') {
65             ++fmt;
66             if (*fmt >= '0' && *fmt <= '9') {
67                 for (precision = 0; *fmt >= '0' && *fmt <= '9'; ) {
68                     precision = 10 * precision + *fmt++ - '0';
69                 }
70             } else if (*fmt == '*') {
71                 precision = va_arg(vargs, int);
72                 ++fmt;
73             }
74             if (precision < 0) {
75                 precision = 0;
76             }
77         }
78
79         // process main conversion character
80         int base = 10;
81         unsigned long num = 0;
82         int length = 0;
83         char* data = "";
84     again:
85         switch (*fmt) {
86         case 'l':
87         case 'z':
88             length = 1;
89             ++fmt;
90             goto again;
91         case 'd':
92         case 'i': {
93             long x = length ? va_arg(vargs, long) : va_arg(vargs, int);
94             int negative = x < 0 ? FLAG_NEGATIVE : 0;
95             num = negative ? -x : x;
96             flags |= FLAG_NUMERIC | FLAG_SIGNED | negative;
97             break;
98         }
99         case 'u':
100         format_unsigned:
101             num = length ? va_arg(vargs, unsigned long) : va_arg(vargs, unsigned);
102             flags |= FLAG_NUMERIC;
103             break;
104         case 'x':
105             base = 16;
106             goto format_unsigned;
107         case 'X':
108             flags = flags | FLAG_CAPS;
109             base = 16;
110             goto format_unsigned;
111         case 'p':
112             num = (uintptr_t) va_arg(vargs, void*);
113             base = 16;
114             flags |= FLAG_ALT | FLAG_ALT2 | FLAG_NUMERIC;
115             break;
116         case 's':
117             data = va_arg(vargs, char*);
118             break;
119         case 'c':
120             data = numbuf;
121             numbuf[0] = va_arg(vargs, int);
122             numbuf[1] = '\0';
123             break;
124         default:
125             data = numbuf;
126             numbuf[0] = (*fmt ? *fmt : '%');
127             numbuf[1] = '\0';
128             if (!*fmt) {
129                 fmt--;
130             }
131             break;
132         }
133
134         if (flags & FLAG_NUMERIC) {
135             data = itoa(num, numbuf, base);
136             int i = 0;
137             char c;
138             while ((flags & FLAG_CAPS) && (c = data[i]))
139             {
140                 data[i] = c & ~((c & 0x40) >> 1);
141                 i++;
142             }
143         }
144
145         const char* prefix = "";
146         if ((flags & FLAG_NUMERIC) && (flags & FLAG_SIGNED)) {
147             if (flags & FLAG_NEGATIVE) {
148                 prefix = "-";
149             } else if (flags & FLAG_PLUSPOSITIVE) {
150                 prefix = "+";
151             } else if (flags & FLAG_SPACEPOSITIVE) {
152                 prefix = " ";
153             }
154         } else if ((flags & FLAG_NUMERIC) && (flags & FLAG_ALT)
155                    && (base == 16 || base == -16)
156                    && (num || (flags & FLAG_ALT2))) {
157             prefix = "0x";
158         }
159
160         int len;
161         if (precision >= 0 && !(flags & FLAG_NUMERIC)) {
162             len = strnlen(data, precision);
163         } else {
164             len = strlen(data);
165         }
166         int zeros;
167         if ((flags & FLAG_NUMERIC) && precision >= 0) {
168             zeros = precision > len ? precision - len : 0;
169         } else if ((flags & FLAG_NUMERIC) && (flags & FLAG_ZERO)
170                    && !(flags & FLAG_LEFTJUSTIFY)
171                    && len + (int) strlen(prefix) < width) {
172             zeros = width - len - strlen(prefix);
173         } else {
174             zeros = 0;
175         }
176         width -= len + zeros + strlen(prefix);
177         for (; !(flags & FLAG_LEFTJUSTIFY) && width > 0; --width) {
178             buffer[ptr++] = ' ';
179         }
180         for (; *prefix; ++prefix) {
181             buffer[ptr++] = *prefix;
182         }
183         for (; zeros > 0; --zeros) {
184             buffer[ptr++] = '0';
185         }
186         for (; len > 0; ++data, --len) {
187             buffer[ptr++] = *data;
188         }
189         for (; width > 0; --width) {
190             buffer[ptr++] = ' ';
191         }
192     }
193     buffer[ptr++] = '\0';
194 }
195
196 void
197 sprintf(char* buffer, char* fmt, ...)
198 {
199     va_list args;
200     va_start(args, fmt);
201     __sprintf_internal(buffer, fmt, args);
202     va_end(args);
203 }