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