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