315a7e1594563ffac4ddd816b82a0222157cbae3
[visor] / libvisor / src / viprintf.c
1 #include "vilibc.h"
2
3 enum {
4         OUT_BUF
5 };
6
7 static int intern_printf(int out, char *buf, unsigned long sz, const char *fmt, va_list ap);
8 static void bwrite(int out, char *buf, unsigned long buf_sz, char *str, int sz);
9 static void utoa(unsigned int val, char *buf, int base);
10 static void itoa(int val, char *buf, int base);
11
12 int sprintf(char *buf, const char *fmt, ...)
13 {
14         int res;
15         va_list ap;
16
17         va_start(ap, fmt);
18         res = intern_printf(OUT_BUF, buf, 0, fmt, ap);
19         va_end(ap);
20         return res;
21 }
22
23 int vsprintf(char *buf, const char *fmt, va_list ap)
24 {
25         return intern_printf(OUT_BUF, buf, 0, fmt, ap);
26 }
27
28 int snprintf(char *buf, unsigned long sz, const char *fmt, ...)
29 {
30         int res;
31         va_list ap;
32
33         va_start(ap, fmt);
34         res = intern_printf(OUT_BUF, buf, sz, fmt, ap);
35         va_end(ap);
36         return res;
37 }
38
39 int vsnprintf(char *buf, unsigned long sz, const char *fmt, va_list ap)
40 {
41         return intern_printf(OUT_BUF, buf, sz, fmt, ap);
42 }
43
44 /* intern_printf provides all the functionality needed by all the printf
45  * variants.
46  * - buf: optional buffer onto which the formatted results are written. If null
47  *   then the output goes to the terminal through putchar calls. This is used
48  *   by the (v)sprintf variants which write to an array of char.
49  * - sz: optional maximum size of the output, 0 means unlimited. This is used
50  *   by the (v)snprintf variants to avoid buffer overflows.
51  * The rest are obvious, format string and variable argument list.
52  */
53 static char *convc = "dioxXucsfeEgGpn%";
54
55 #define IS_CONV(c)      strchr(convc, c)
56
57 #define BUF(x)  ((x) ? (x) + cnum : (x))
58 #define SZ(x)   ((x) ? (x) - cnum : (x))
59
60 static int intern_printf(int out, char *buf, unsigned long sz, const char *fmt, va_list ap)
61 {
62         char conv_buf[32];
63         char *str;
64         int i, slen;
65         const char *fstart = 0;
66
67         /* state */
68         int cnum = 0;
69         int base = 10;
70         int alt = 0;
71         int fwidth = 0;
72         int padc = ' ';
73         int sign = 0;
74         int left_align = 0;
75         int hex_caps = 0;
76         int unsig = 0;
77         int num, unum;
78
79         while(*fmt) {
80                 if(*fmt == '%') {
81                         fstart = fmt++;
82                         continue;
83                 }
84
85                 if(fstart) {
86                         if(IS_CONV(*fmt)) {
87                                 switch(*fmt) {
88                                 case 'X':
89                                         hex_caps = 1;
90
91                                 case 'x':
92                                 case 'p':
93                                         base = 16;
94
95                                         if(alt) {
96                                                 bwrite(out, BUF(buf), SZ(sz), "0x", 2);
97                                                 cnum += 2;
98                                         }
99
100                                 case 'u':
101                                         unsig = 1;
102
103                                         if(0) {
104                                 case 'o':
105                                                 base = 8;
106
107                                                 if(alt) {
108                                                         bwrite(out, BUF(buf), SZ(sz), "0", 1);
109                                                         cnum++;
110                                                 }
111                                         }
112
113                                 case 'd':
114                                 case 'i':
115                                         if(unsig) {
116                                                 unum = va_arg(ap, unsigned int);
117                                                 utoa(unum, conv_buf, base);
118                                         } else {
119                                                 num = va_arg(ap, int);
120                                                 itoa(num, conv_buf, base);
121                                         }
122                                         if(hex_caps) {
123                                                 for(i=0; conv_buf[i]; i++) {
124                                                         conv_buf[i] = toupper(conv_buf[i]);
125                                                 }
126                                         }
127
128                                         slen = strlen(conv_buf);
129
130                                         if(left_align) {
131                                                 if(!unsig && sign && num >= 0) {
132                                                         bwrite(out, BUF(buf), SZ(sz), "+", 1);
133                                                         cnum++;
134                                                 }
135                                                 bwrite(out, BUF(buf), SZ(sz), conv_buf, slen);
136                                                 cnum += slen;
137                                                 padc = ' ';
138                                         }
139                                         for(i=slen; i<fwidth; i++) {
140                                                 bwrite(out, BUF(buf), SZ(sz), (char*)&padc, 1);
141                                                 cnum++;
142                                         }
143                                         if(!left_align) {
144                                                 if(!unsig && sign && num >= 0) {
145                                                         bwrite(out, BUF(buf), SZ(sz), "+", 1);
146                                                         cnum++;
147                                                 }
148                                                 bwrite(out, BUF(buf), SZ(sz), conv_buf, slen);
149                                                 cnum += slen;
150                                         }
151                                         break;
152
153                                 case 'c':
154                                         {
155                                                 char c = va_arg(ap, int);
156                                                 bwrite(out, BUF(buf), SZ(sz), &c, 1);
157                                                 cnum++;
158                                         }
159                                         break;
160
161                                 case 's':
162                                         str = va_arg(ap, char*);
163                                         slen = strlen(str);
164
165                                         if(left_align) {
166                                                 bwrite(out, BUF(buf), SZ(sz), str, slen);
167                                                 cnum += slen;
168                                                 padc = ' ';
169                                         }
170                                         for(i=slen; i<fwidth; i++) {
171                                                 bwrite(out, BUF(buf), SZ(sz), (char*)&padc, 1);
172                                                 cnum++;
173                                         }
174                                         if(!left_align) {
175                                                 bwrite(out, BUF(buf), SZ(sz), str, slen);
176                                                 cnum += slen;
177                                         }
178                                         break;
179
180                                 case 'n':
181                                         *va_arg(ap, int*) = cnum;
182                                         break;
183
184                                 default:
185                                         break;
186                                 }
187
188                                 /* restore default conversion state */
189                                 base = 10;
190                                 alt = 0;
191                                 fwidth = 0;
192                                 padc = ' ';
193                                 hex_caps = 0;
194
195                                 fstart = 0;
196                                 fmt++;
197                         } else {
198                                 switch(*fmt) {
199                                 case '#':
200                                         alt = 1;
201                                         break;
202
203                                 case '+':
204                                         sign = 1;
205                                         break;
206
207                                 case '-':
208                                         left_align = 1;
209                                         break;
210
211                                 case 'l':
212                                 case 'L':
213                                         break;
214
215                                 case '0':
216                                         padc = '0';
217                                         break;
218
219                                 default:
220                                         if(isdigit(*fmt)) {
221                                                 const char *fw = fmt;
222                                                 while(*fmt && isdigit(*fmt)) fmt++;
223
224                                                 fwidth = atoi(fw);
225                                                 continue;
226                                         }
227                                 }
228                                 fmt++;
229                         }
230                 } else {
231                         bwrite(out, BUF(buf), SZ(sz), (char*)fmt++, 1);
232                         cnum++;
233                 }
234         }
235
236         return cnum;
237 }
238
239 /* bwrite is called by intern_printf to transparently handle writing into a
240  * buffer or to the terminal
241  */
242 static void bwrite(int out, char *buf, unsigned long buf_sz, char *str, int sz)
243 {
244         if(out == OUT_BUF) {
245                 if(buf_sz && buf_sz <= sz) sz = buf_sz;
246                 buf[sz] = 0;
247                 memcpy(buf, str, sz);
248         }
249 }
250
251 static void utoa(unsigned int val, char *buf, int base)
252 {
253         static char rbuf[16];
254         char *ptr = rbuf;
255
256         if(val == 0) {
257                 *ptr++ = '0';
258         }
259
260         while(val) {
261                 unsigned int digit = val % base;
262                 *ptr++ = digit < 10 ? (digit + '0') : (digit - 10 + 'a');
263                 val /= base;
264         }
265
266         ptr--;
267
268         while(ptr >= rbuf) {
269                 *buf++ = *ptr--;
270         }
271         *buf = 0;
272 }
273
274 static void itoa(int val, char *buf, int base)
275 {
276         static char rbuf[16];
277         char *ptr = rbuf;
278         int neg = 0;
279
280         if(val < 0) {
281                 neg = 1;
282                 val = -val;
283         }
284
285         if(val == 0) {
286                 *ptr++ = '0';
287         }
288
289         while(val) {
290                 int digit = val % base;
291                 *ptr++ = digit < 10 ? (digit + '0') : (digit - 10 + 'a');
292                 val /= base;
293         }
294
295         if(neg) {
296                 *ptr++ = '-';
297         }
298
299         ptr--;
300
301         while(ptr >= rbuf) {
302                 *buf++ = *ptr--;
303         }
304         *buf = 0;
305 }
306