main kernel startup, libc, console tty, asmops, build flags fixes
[bootcensus] / src / libc / stdio.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include "contty.h"
5
6 extern void pcboot_putchar(int c);
7
8 static void bwrite(char *buf, size_t buf_sz, char *str, int sz);
9 static int intern_printf(char *buf, size_t sz, const char *fmt, va_list ap);
10
11 int putchar(int c)
12 {
13         con_putchar(c);
14         return c;
15 }
16
17 int puts(const char *s)
18 {
19         while(*s) {
20                 putchar(*s++);
21         }
22         putchar('\n');
23         return 0;
24 }
25
26 /* -- printf and friends -- */
27
28 static char *convc = "dioxXucsfeEgGpn%";
29
30 #define IS_CONV(c)      strchr(convc, c)
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(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(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(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(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(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(buf, sz, fmt, ap);
78 }
79
80
81 /* intern_printf provides all the functionality needed by all the printf
82  * variants.
83  * - buf: optional buffer onto which the formatted results are written. If null
84  *   then the output goes to the terminal through putchar calls. This is used
85  *   by the (v)sprintf variants which write to an array of char.
86  * - sz: optional maximum size of the output, 0 means unlimited. This is used
87  *   by the (v)snprintf variants to avoid buffer overflows.
88  * The rest are obvious, format string and variable argument list.
89  */
90
91 #define BUF(x)  ((x) ? (x) + cnum : (x))
92 #define SZ(x)   ((x) ? (x) - cnum : (x))
93
94 static int intern_printf(char *buf, size_t sz, const char *fmt, va_list ap)
95 {
96         char conv_buf[32];
97         char *str;
98         int i, slen;
99         const char *fstart = 0;
100
101         /* state */
102         int cnum = 0;
103         int base = 10;
104         int alt = 0;
105         int fwidth = 0;
106         int padc = ' ';
107         int sign = 0;
108         int left_align = 0;     /* not implemented yet */
109         int hex_caps = 0;
110         int unsig = 0;
111
112         while(*fmt) {
113                 if(*fmt == '%') {
114                         fstart = fmt++;
115                         continue;
116                 }
117
118                 if(fstart) {
119                         if(IS_CONV(*fmt)) {
120                                 switch(*fmt) {
121                                 case 'X':
122                                         hex_caps = 1;
123
124                                 case 'x':
125                                 case 'p':
126                                         base = 16;
127
128                                         if(alt) {
129                                                 bwrite(BUF(buf), SZ(sz), "0x", 2);
130                                         }
131
132                                 case 'u':
133                                         unsig = 1;
134
135                                         if(0) {
136                                 case 'o':
137                                                 base = 8;
138
139                                                 if(alt) {
140                                                         bwrite(BUF(buf), SZ(sz), "0", 1);
141                                                 }
142                                         }
143
144                                 case 'd':
145                                 case 'i':
146                                         if(unsig) {
147                                                 utoa(va_arg(ap, unsigned int), conv_buf, base);
148                                         } else {
149                                                 itoa(va_arg(ap, int), conv_buf, base);
150                                         }
151                                         if(hex_caps) {
152                                                 for(i=0; conv_buf[i]; i++) {
153                                                         conv_buf[i] = toupper(conv_buf[i]);
154                                                 }
155                                         }
156
157                                         slen = strlen(conv_buf);
158                                         for(i=slen; i<fwidth; i++) {
159                                                 bwrite(BUF(buf), SZ(sz), (char*)&padc, 1);
160                                                 cnum++;
161                                         }
162
163                                         bwrite(BUF(buf), SZ(sz), conv_buf, strlen(conv_buf));
164                                         cnum += slen;
165                                         break;
166
167                                 case 'c':
168                                         {
169                                                 char c = va_arg(ap, int);
170                                                 bwrite(BUF(buf), SZ(sz), &c, 1);
171                                                 cnum++;
172                                         }
173                                         break;
174
175                                 case 's':
176                                         str = va_arg(ap, char*);
177                                         slen = strlen(str);
178
179                                         for(i=slen; i<fwidth; i++) {
180                                                 bwrite(BUF(buf), SZ(sz), (char*)&padc, 1);
181                                                 cnum++;
182                                         }
183                                         bwrite(BUF(buf), SZ(sz), str, slen);
184                                         cnum += slen;
185                                         break;
186
187                                 case 'n':
188                                         *va_arg(ap, int*) = cnum;
189                                         break;
190
191                                 default:
192                                         break;
193                                 }
194
195                                 /* restore default conversion state */
196                                 base = 10;
197                                 alt = 0;
198                                 fwidth = 0;
199                                 padc = ' ';
200                                 hex_caps = 0;
201
202                                 fstart = 0;
203                                 fmt++;
204                         } else {
205                                 switch(*fmt) {
206                                 case '#':
207                                         alt = 1;
208                                         break;
209
210                                 case '+':
211                                         sign = 1;
212                                         break;
213
214                                 case '-':
215                                         left_align = 1;
216                                         break;
217
218                                 case 'l':
219                                 case 'L':
220                                         break;
221
222                                 case '0':
223                                         padc = '0';
224                                         break;
225
226                                 default:
227                                         if(isdigit(*fmt)) {
228                                                 const char *fw = fmt;
229                                                 while(*fmt && isdigit(*fmt)) fmt++;
230
231                                                 fwidth = atoi(fw);
232                                                 continue;
233                                         }
234                                 }
235                                 fmt++;
236                         }
237                 } else {
238                         bwrite(BUF(buf), SZ(sz), (char*)fmt++, 1);
239                         cnum++;
240                 }
241         }
242
243         return 0;
244 }
245
246
247 /* bwrite is called by intern_printf to transparently handle writing into a
248  * buffer (if buf is non-null) or to the terminal (if buf is null).
249  */
250 static void bwrite(char *buf, size_t buf_sz, char *str, int sz)
251 {
252         if(buf) {
253                 if(buf_sz && buf_sz <= sz) sz = buf_sz - 1;
254                 memcpy(buf, str, sz);
255
256                 buf[sz] = 0;
257         } else {
258                 int i;
259                 for(i=0; i<sz; i++) {
260                         putchar(*str++);
261                 }
262         }
263 }
264