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