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