Support to multi-threading and pthread interface (POSIX.1-2008) (#23)
[lunaix-os.git] / lunaix-os / libs / klibc / ksprintf.c
1 #define __LUNAIX_LIBC
2 #include <klibc/stdlib.h>
3 #include <klibc/strfmt.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 unsigned long
23 ksnprintfv(char* buffer, const char* fmt, unsigned long max_len, va_list vargs)
24 {
25     // This sprintf just a random implementation I found it on Internet . lol.
26     //      Of course, with some modifications for porting to LunaixOS :)
27
28     char numbuf[NUMBUFSIZ];
29     u32_t ptr = 0;
30     for (; *fmt; ++fmt) {
31         if (max_len && ptr >= max_len - 1) {
32             break;
33         }
34
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)
102                              : va_arg(vargs, unsigned);
103                 flags |= FLAG_NUMERIC;
104                 break;
105             case 'b':
106                 base = 2;
107                 goto format_unsigned;
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 = (ptr_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                 data[i] = c & ~((c & 0x40) >> 1);
144                 i++;
145             }
146         }
147
148         const char* prefix = "";
149         if ((flags & FLAG_NUMERIC) && (flags & FLAG_SIGNED)) {
150             if (flags & FLAG_NEGATIVE) {
151                 prefix = "-";
152             } else if (flags & FLAG_PLUSPOSITIVE) {
153                 prefix = "+";
154             } else if (flags & FLAG_SPACEPOSITIVE) {
155                 prefix = " ";
156             }
157         } else if ((flags & FLAG_NUMERIC) && (flags & FLAG_ALT) &&
158                    (base == 16 || base == -16) &&
159                    (num || (flags & FLAG_ALT2))) {
160             prefix = "0x";
161         }
162
163         int len;
164         if (precision >= 0 && !(flags & FLAG_NUMERIC)) {
165             len = strnlen(data, precision);
166         } else {
167             len = strlen(data);
168         }
169         int zeros;
170         if ((flags & FLAG_NUMERIC) && precision >= 0) {
171             zeros = precision > len ? precision - len : 0;
172         } else if ((flags & FLAG_NUMERIC) && (flags & FLAG_ZERO) &&
173                    !(flags & FLAG_LEFTJUSTIFY) &&
174                    len + (int)strlen(prefix) < width) {
175             zeros = width - len - strlen(prefix);
176         } else {
177             zeros = 0;
178         }
179         width -= len + zeros + strlen(prefix);
180         for (; !(flags & FLAG_LEFTJUSTIFY) && width > 0; --width) {
181             buffer[ptr++] = ' ';
182         }
183         for (; *prefix; ++prefix) {
184             buffer[ptr++] = *prefix;
185         }
186         for (; zeros > 0; --zeros) {
187             buffer[ptr++] = '0';
188         }
189         for (; len > 0; ++data, --len) {
190             buffer[ptr++] = *data;
191         }
192         for (; width > 0; --width) {
193             buffer[ptr++] = ' ';
194         }
195     }
196     buffer[ptr++] = '\0';
197
198     return ptr;
199 }
200
201 unsigned long
202 ksprintf(char* buffer, char* fmt, ...)
203 {
204     va_list args;
205     va_start(args, fmt);
206     unsigned long len = ksnprintfv(buffer, fmt, 0, args);
207     va_end(args);
208     return len;
209 }
210
211 unsigned long
212 ksnprintf(char* buffer, unsigned long n, char* fmt, ...)
213 {
214     va_list args;
215     va_start(args, fmt);
216     unsigned long len = ksnprintfv(buffer, fmt, n, args);
217     va_end(args);
218     return len;
219 }