Implement shift+<key> support, and ...
[lunaix-os.git] / lunaix-os / libs / klibc / stdio / sprintf.c
1 #define __LUNAIX_LIBC
2 #include <klibc/stdio.h>
3 #include <klibc/stdlib.h>
4 #include <klibc/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, size_t max_len, 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 (max_len && ptr >= max_len - 1) {
36             break;
37         }
38         
39         if (*fmt != '%') {
40             buffer[ptr++] = *fmt;
41             continue;
42         }
43
44         // process flags
45         int flags = 0;
46         for (++fmt; *fmt; ++fmt) {
47             const char* flagc = strchr(flag_chars, *fmt);
48             if (flagc) {
49                 flags |= 1 << (flagc - flag_chars);
50             } else {
51                 break;
52             }
53         }
54
55         // process width
56         int width = -1;
57         if (*fmt >= '1' && *fmt <= '9') {
58             for (width = 0; *fmt >= '0' && *fmt <= '9'; ) {
59                 width = 10 * width + *fmt++ - '0';
60             }
61         } else if (*fmt == '*') {
62             width = va_arg(vargs, int);
63             ++fmt;
64         }
65
66         // process precision
67         int precision = -1;
68         if (*fmt == '.') {
69             ++fmt;
70             if (*fmt >= '0' && *fmt <= '9') {
71                 for (precision = 0; *fmt >= '0' && *fmt <= '9'; ) {
72                     precision = 10 * precision + *fmt++ - '0';
73                 }
74             } else if (*fmt == '*') {
75                 precision = va_arg(vargs, int);
76                 ++fmt;
77             }
78             if (precision < 0) {
79                 precision = 0;
80             }
81         }
82
83         // process main conversion character
84         int base = 10;
85         unsigned long num = 0;
86         int length = 0;
87         char* data = "";
88     again:
89         switch (*fmt) {
90         case 'l':
91         case 'z':
92             length = 1;
93             ++fmt;
94             goto again;
95         case 'd':
96         case 'i': {
97             long x = length ? va_arg(vargs, long) : va_arg(vargs, int);
98             int negative = x < 0 ? FLAG_NEGATIVE : 0;
99             num = negative ? -x : x;
100             flags |= FLAG_NUMERIC | FLAG_SIGNED | negative;
101             break;
102         }
103         case 'u':
104         format_unsigned:
105             num = length ? va_arg(vargs, unsigned long) : va_arg(vargs, unsigned);
106             flags |= FLAG_NUMERIC;
107             break;
108         case 'x':
109             base = 16;
110             goto format_unsigned;
111         case 'X':
112             flags = flags | FLAG_CAPS;
113             base = 16;
114             goto format_unsigned;
115         case 'p':
116             num = (uintptr_t) va_arg(vargs, void*);
117             base = 16;
118             flags |= FLAG_ALT | FLAG_ALT2 | FLAG_NUMERIC;
119             break;
120         case 's':
121             data = va_arg(vargs, char*);
122             break;
123         case 'c':
124             data = numbuf;
125             numbuf[0] = va_arg(vargs, int);
126             numbuf[1] = '\0';
127             break;
128         default:
129             data = numbuf;
130             numbuf[0] = (*fmt ? *fmt : '%');
131             numbuf[1] = '\0';
132             if (!*fmt) {
133                 fmt--;
134             }
135             break;
136         }
137
138         if (flags & FLAG_NUMERIC) {
139             data = itoa(num, numbuf, base);
140             int i = 0;
141             char c;
142             while ((flags & FLAG_CAPS) && (c = data[i]))
143             {
144                 data[i] = c & ~((c & 0x40) >> 1);
145                 i++;
146             }
147         }
148
149         const char* prefix = "";
150         if ((flags & FLAG_NUMERIC) && (flags & FLAG_SIGNED)) {
151             if (flags & FLAG_NEGATIVE) {
152                 prefix = "-";
153             } else if (flags & FLAG_PLUSPOSITIVE) {
154                 prefix = "+";
155             } else if (flags & FLAG_SPACEPOSITIVE) {
156                 prefix = " ";
157             }
158         } else if ((flags & FLAG_NUMERIC) && (flags & FLAG_ALT)
159                    && (base == 16 || base == -16)
160                    && (num || (flags & FLAG_ALT2))) {
161             prefix = "0x";
162         }
163
164         int len;
165         if (precision >= 0 && !(flags & FLAG_NUMERIC)) {
166             len = strnlen(data, precision);
167         } else {
168             len = strlen(data);
169         }
170         int zeros;
171         if ((flags & FLAG_NUMERIC) && precision >= 0) {
172             zeros = precision > len ? precision - len : 0;
173         } else if ((flags & FLAG_NUMERIC) && (flags & FLAG_ZERO)
174                    && !(flags & FLAG_LEFTJUSTIFY)
175                    && len + (int) strlen(prefix) < width) {
176             zeros = width - len - strlen(prefix);
177         } else {
178             zeros = 0;
179         }
180         width -= len + zeros + strlen(prefix);
181         for (; !(flags & FLAG_LEFTJUSTIFY) && width > 0; --width) {
182             buffer[ptr++] = ' ';
183         }
184         for (; *prefix; ++prefix) {
185             buffer[ptr++] = *prefix;
186         }
187         for (; zeros > 0; --zeros) {
188             buffer[ptr++] = '0';
189         }
190         for (; len > 0; ++data, --len) {
191             buffer[ptr++] = *data;
192         }
193         for (; width > 0; --width) {
194             buffer[ptr++] = ' ';
195         }
196     }
197     buffer[ptr++] = '\0';
198 }
199
200 void
201 sprintf(char* buffer, char* fmt, ...)
202 {
203     va_list args;
204     va_start(args, fmt);
205     __sprintf_internal(buffer, fmt, 0, args);
206     va_end(args);
207 }
208
209 void
210 snprintf(char* buffer, size_t n, char* fmt, ...)
211 {
212     va_list args;
213     va_start(args, fmt);
214     __sprintf_internal(buffer, fmt, n, args);
215     va_end(args);
216 }