cleanup
[rpikern] / src / libc / stdio.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include <errno.h>
5 #include "contty.h"
6 #include "serial.h"
7 #include "panic.h"
8
9 enum {
10         OUT_DEF,
11         OUT_BUF,
12         OUT_SCR,
13         OUT_SER
14 };
15
16 static int intern_printf(int out, char *buf, size_t sz, const char *fmt, va_list ap);
17 static int intern_scanf(const char *instr, FILE *infile, const char *fmt, va_list ap);
18 static void bwrite(int out, char *buf, size_t buf_sz, char *str, int sz);
19 /*static int readchar(const char *str, FILE *fp);*/
20
21 int putchar(int c)
22 {
23         con_putchar(c);
24         return c;
25 }
26
27 int puts(const char *s)
28 {
29         while(*s) {
30                 putchar(*s++);
31         }
32         putchar('\n');
33         return 0;
34 }
35
36 /* -- printf and friends -- */
37
38 int printf(const char *fmt, ...)
39 {
40         int res;
41         va_list ap;
42
43         va_start(ap, fmt);
44         res = intern_printf(OUT_DEF, 0, 0, fmt, ap);
45         va_end(ap);
46         return res;
47 }
48
49 int vprintf(const char *fmt, va_list ap)
50 {
51         return intern_printf(OUT_DEF, 0, 0, fmt, ap);
52 }
53
54 int sprintf(char *buf, const char *fmt, ...)
55 {
56         int res;
57         va_list ap;
58
59         va_start(ap, fmt);
60         res = intern_printf(OUT_BUF, buf, 0, fmt, ap);
61         va_end(ap);
62         return res;
63 }
64
65 int vsprintf(char *buf, const char *fmt, va_list ap)
66 {
67         return intern_printf(OUT_BUF, buf, 0, fmt, ap);
68 }
69
70 int snprintf(char *buf, size_t sz, const char *fmt, ...)
71 {
72         int res;
73         va_list ap;
74
75         va_start(ap, fmt);
76         res = intern_printf(OUT_BUF, buf, sz, fmt, ap);
77         va_end(ap);
78         return res;
79 }
80
81 int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap)
82 {
83         return intern_printf(OUT_BUF, buf, sz, fmt, ap);
84 }
85
86 int fprintf(FILE *fp, const char *fmt, ...)
87 {
88         int res;
89         va_list ap;
90
91         va_start(ap, fmt);
92         res = vfprintf(fp, fmt, ap);
93         va_end(ap);
94         return res;
95 }
96
97 int vfprintf(FILE *fp, const char *fmt, va_list ap)
98 {
99         if(fp == stdout || fp == stderr) {
100                 return vprintf(fmt, ap);
101         }
102
103         panic("*fprintf for anything other than stdout/stderr, not implemented yet\n");
104         return 0;
105 }
106
107 int ser_printf(const char *fmt, ...)
108 {
109         int res;
110         va_list ap;
111
112         va_start(ap, fmt);
113         res = intern_printf(OUT_SER, 0, 0, fmt, ap);
114         va_end(ap);
115         return res;
116 }
117
118 int ser_vprintf(const char *fmt, va_list ap)
119 {
120         return intern_printf(OUT_SER, 0, 0, fmt, ap);
121 }
122
123 void perror(const char *s)
124 {
125         printf("%s: %s\n", s, strerror(errno));
126 }
127
128 int getchar(void)
129 {
130         return con_getchar();
131 }
132
133 /* intern_printf provides all the functionality needed by all the printf
134  * variants.
135  * - buf: optional buffer onto which the formatted results are written. If null
136  *   then the output goes to the terminal through putchar calls. This is used
137  *   by the (v)sprintf variants which write to an array of char.
138  * - sz: optional maximum size of the output, 0 means unlimited. This is used
139  *   by the (v)snprintf variants to avoid buffer overflows.
140  * The rest are obvious, format string and variable argument list.
141  */
142 static char *convc = "dioxXucsfeEgGpn%";
143
144 #define IS_CONV(c)      strchr(convc, c)
145
146 #define BUF(x)  ((x) ? (x) + cnum : (x))
147 #define SZ(x)   ((x) ? (x) - cnum : (x))
148
149 static int intern_printf(int out, char *buf, size_t sz, const char *fmt, va_list ap)
150 {
151         char conv_buf[32];
152         char *str;
153         int i, slen;
154         const char *fstart = 0;
155
156         /* state */
157         int cnum = 0;
158         int base = 10;
159         int alt = 0;
160         int fwidth = 0;
161         int padc = ' ';
162         int sign = 0;
163         int left_align = 0;
164         int hex_caps = 0;
165         int unsig = 0;
166         int num, unum;
167
168         while(*fmt) {
169                 if(*fmt == '%') {
170                         fstart = fmt++;
171                         continue;
172                 }
173
174                 if(fstart) {
175                         if(IS_CONV(*fmt)) {
176                                 switch(*fmt) {
177                                 case 'X':
178                                         hex_caps = 1;
179
180                                 case 'x':
181                                 case 'p':
182                                         base = 16;
183
184                                         if(alt) {
185                                                 bwrite(out, BUF(buf), SZ(sz), "0x", 2);
186                                                 cnum += 2;
187                                         }
188
189                                 case 'u':
190                                         unsig = 1;
191
192                                         if(0) {
193                                 case 'o':
194                                                 base = 8;
195
196                                                 if(alt) {
197                                                         bwrite(out, BUF(buf), SZ(sz), "0", 1);
198                                                         cnum++;
199                                                 }
200                                         }
201
202                                 case 'd':
203                                 case 'i':
204                                         if(unsig) {
205                                                 unum = va_arg(ap, unsigned int);
206                                                 utoa(unum, conv_buf, base);
207                                         } else {
208                                                 num = va_arg(ap, int);
209                                                 itoa(num, conv_buf, base);
210                                         }
211                                         if(hex_caps) {
212                                                 for(i=0; conv_buf[i]; i++) {
213                                                         conv_buf[i] = toupper(conv_buf[i]);
214                                                 }
215                                         }
216
217                                         slen = strlen(conv_buf);
218
219                                         if(left_align) {
220                                                 if(!unsig && sign && num >= 0) {
221                                                         bwrite(out, BUF(buf), SZ(sz), "+", 1);
222                                                         cnum++;
223                                                 }
224                                                 bwrite(out, BUF(buf), SZ(sz), conv_buf, slen);
225                                                 cnum += slen;
226                                                 padc = ' ';
227                                         }
228                                         for(i=slen; i<fwidth; i++) {
229                                                 bwrite(out, BUF(buf), SZ(sz), (char*)&padc, 1);
230                                                 cnum++;
231                                         }
232                                         if(!left_align) {
233                                                 if(!unsig && sign && num >= 0) {
234                                                         bwrite(out, BUF(buf), SZ(sz), "+", 1);
235                                                         cnum++;
236                                                 }
237                                                 bwrite(out, BUF(buf), SZ(sz), conv_buf, slen);
238                                                 cnum += slen;
239                                         }
240                                         break;
241
242                                 case 'c':
243                                         {
244                                                 char c = va_arg(ap, int);
245                                                 bwrite(out, BUF(buf), SZ(sz), &c, 1);
246                                                 cnum++;
247                                         }
248                                         break;
249
250                                 case 's':
251                                         str = va_arg(ap, char*);
252                                         slen = strlen(str);
253
254                                         if(left_align) {
255                                                 bwrite(out, BUF(buf), SZ(sz), str, slen);
256                                                 cnum += slen;
257                                                 padc = ' ';
258                                         }
259                                         for(i=slen; i<fwidth; i++) {
260                                                 bwrite(out, BUF(buf), SZ(sz), (char*)&padc, 1);
261                                                 cnum++;
262                                         }
263                                         if(!left_align) {
264                                                 bwrite(out, BUF(buf), SZ(sz), str, slen);
265                                                 cnum += slen;
266                                         }
267                                         break;
268
269                                 case 'n':
270                                         *va_arg(ap, int*) = cnum;
271                                         break;
272
273                                 default:
274                                         break;
275                                 }
276
277                                 /* restore default conversion state */
278                                 base = 10;
279                                 alt = 0;
280                                 fwidth = 0;
281                                 padc = ' ';
282                                 hex_caps = 0;
283
284                                 fstart = 0;
285                                 fmt++;
286                         } else {
287                                 switch(*fmt) {
288                                 case '#':
289                                         alt = 1;
290                                         break;
291
292                                 case '+':
293                                         sign = 1;
294                                         break;
295
296                                 case '-':
297                                         left_align = 1;
298                                         break;
299
300                                 case 'l':
301                                 case 'L':
302                                         break;
303
304                                 case '0':
305                                         padc = '0';
306                                         break;
307
308                                 default:
309                                         if(isdigit(*fmt)) {
310                                                 const char *fw = fmt;
311                                                 while(*fmt && isdigit(*fmt)) fmt++;
312
313                                                 fwidth = atoi(fw);
314                                                 continue;
315                                         }
316                                 }
317                                 fmt++;
318                         }
319                 } else {
320                         bwrite(out, BUF(buf), SZ(sz), (char*)fmt++, 1);
321                         cnum++;
322                 }
323         }
324
325         return cnum;
326 }
327
328
329 #if 0
330 static char *sconvc = "diouxcsefg%";
331
332 #define IS_SCONV(c)     strchr(sconvc, c)
333
334 static int intern_scanf(const char *instr, FILE *infile, const char *fmt, va_list ap)
335 {
336         return -1;      /* TODO */
337 }
338 #endif
339
340
341 /* bwrite is called by intern_printf to transparently handle writing into a
342  * buffer or to the terminal
343  */
344 static void bwrite(int out, char *buf, size_t buf_sz, char *str, int sz)
345 {
346         int i;
347
348         if(out == OUT_BUF) {
349                 if(buf_sz && buf_sz <= sz) sz = buf_sz;
350                 buf[sz] = 0;
351                 memcpy(buf, str, sz);
352         } else {
353                 switch(out) {
354                 case OUT_DEF:
355                         for(i=0; i<sz; i++) {
356                                 putchar(*str++);
357                         }
358                         break;
359
360                 case OUT_SER:
361                         for(i=0; i<sz; i++) {
362                                 ser_putchar(*str++);
363                         }
364                         break;
365
366                 default:
367                         /* TODO: OUT_SCR */
368                         break;
369                 }
370         }
371 }
372
373 /*
374 static int readchar(const char *str, FILE *fp)
375 {
376         static const char *orig_str;
377         static const char *sptr;
378
379         if(str) {
380                 if(str == orig_str) {
381                         if(!*sptr) return -1;
382                         return *sptr++;
383                 } else {
384                         orig_str = sptr = str;
385                         return readchar(str, fp);
386                 }
387         } else {
388                 return fgetc(fp);
389         }
390
391         return -1;
392 }
393 */