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