brought the code over from the amiga test
[lugburz] / src / amiga / libc / stdio.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include "serial.h"
5
6 static void bwrite(char *buf, size_t buf_sz, char *str, int sz);
7 static int intern_printf(char *buf, size_t sz, const char *fmt, va_list ap);
8
9 int putchar(int c)
10 {
11         if(c == '\n') {
12                 ser_putchar('\r');
13         }
14         ser_putchar(c);
15         return c;
16 }
17
18 int puts(const char *s)
19 {
20         while(*s) {
21                 putchar(*s++);
22         }
23         putchar('\n');
24         return 0;
25 }
26
27 /* -- printf and friends -- */
28
29 static char *convc = "dioxXucsfeEgGpn%";
30
31 #define IS_CONV(c)      strchr(convc, c)
32
33 int printf(const char *fmt, ...)
34 {
35         int res;
36         va_list ap;
37
38         va_start(ap, fmt);
39         res = intern_printf(0, 0, fmt, ap);
40         va_end(ap);
41         return res;
42 }
43
44 int vprintf(const char *fmt, va_list ap)
45 {
46         return intern_printf(0, 0, fmt, ap);
47 }
48
49 int sprintf(char *buf, const char *fmt, ...)
50 {
51         int res;
52         va_list ap;
53
54         va_start(ap, fmt);
55         res = intern_printf(buf, 0, fmt, ap);
56         va_end(ap);
57         return res;
58 }
59
60 int vsprintf(char *buf, const char *fmt, va_list ap)
61 {
62         return intern_printf(buf, 0, fmt, ap);
63 }
64
65 int snprintf(char *buf, size_t sz, const char *fmt, ...)
66 {
67         int res;
68         va_list ap;
69
70         va_start(ap, fmt);
71         res = intern_printf(buf, sz, fmt, ap);
72         va_end(ap);
73         return res;
74 }
75
76 int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap)
77 {
78         return intern_printf(buf, sz, fmt, ap);
79 }
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
92 #define BUF(x)  ((x) ? (x) + cnum : (x))
93 #define SZ(x)   ((x) ? (x) - cnum : (x))
94
95 static int intern_printf(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;     /* not implemented yet */
110         int hex_caps = 0;
111         int unsig = 0;
112
113         while(*fmt) {
114                 if(*fmt == '%') {
115                         fstart = fmt++;
116                         continue;
117                 }
118
119                 if(fstart) {
120                         if(IS_CONV(*fmt)) {
121                                 switch(*fmt) {
122                                 case 'X':
123                                         hex_caps = 1;
124
125                                 case 'x':
126                                 case 'p':
127                                         base = 16;
128
129                                         if(alt) {
130                                                 bwrite(BUF(buf), SZ(sz), "0x", 2);
131                                         }
132
133                                 case 'u':
134                                         unsig = 1;
135
136                                         if(0) {
137                                 case 'o':
138                                                 base = 8;
139
140                                                 if(alt) {
141                                                         bwrite(BUF(buf), SZ(sz), "0", 1);
142                                                 }
143                                         }
144
145                                 case 'd':
146                                 case 'i':
147                                         if(unsig) {
148                                                 utoa(va_arg(ap, unsigned int), conv_buf, base);
149                                         } else {
150                                                 itoa(va_arg(ap, int), conv_buf, base);
151                                         }
152                                         if(hex_caps) {
153                                                 for(i=0; conv_buf[i]; i++) {
154                                                         conv_buf[i] = toupper(conv_buf[i]);
155                                                 }
156                                         }
157
158                                         slen = strlen(conv_buf);
159                                         for(i=slen; i<fwidth; i++) {
160                                                 bwrite(BUF(buf), SZ(sz), (char*)&padc, 1);
161                                                 cnum++;
162                                         }
163
164                                         bwrite(BUF(buf), SZ(sz), conv_buf, strlen(conv_buf));
165                                         cnum += slen;
166                                         break;
167
168                                 case 'c':
169                                         {
170                                                 char c = va_arg(ap, int);
171                                                 bwrite(BUF(buf), SZ(sz), &c, 1);
172                                                 cnum++;
173                                         }
174                                         break;
175
176                                 case 's':
177                                         str = va_arg(ap, char*);
178                                         slen = strlen(str);
179
180                                         for(i=slen; i<fwidth; i++) {
181                                                 bwrite(BUF(buf), SZ(sz), (char*)&padc, 1);
182                                                 cnum++;
183                                         }
184                                         bwrite(BUF(buf), SZ(sz), str, slen);
185                                         cnum += slen;
186                                         break;
187
188                                 case 'n':
189                                         *va_arg(ap, int*) = cnum;
190                                         break;
191
192                                 default:
193                                         break;
194                                 }
195
196                                 /* restore default conversion state */
197                                 base = 10;
198                                 alt = 0;
199                                 fwidth = 0;
200                                 padc = ' ';
201                                 hex_caps = 0;
202
203                                 fstart = 0;
204                                 fmt++;
205                         } else {
206                                 switch(*fmt) {
207                                 case '#':
208                                         alt = 1;
209                                         break;
210
211                                 case '+':
212                                         sign = 1;
213                                         break;
214
215                                 case '-':
216                                         left_align = 1;
217                                         break;
218
219                                 case 'l':
220                                 case 'L':
221                                         break;
222
223                                 case '0':
224                                         padc = '0';
225                                         break;
226
227                                 default:
228                                         if(isdigit(*fmt)) {
229                                                 const char *fw = fmt;
230                                                 while(*fmt && isdigit(*fmt)) fmt++;
231
232                                                 fwidth = atoi(fw);
233                                                 continue;
234                                         }
235                                 }
236                                 fmt++;
237                         }
238                 } else {
239                         bwrite(BUF(buf), SZ(sz), (char*)fmt++, 1);
240                         cnum++;
241                 }
242         }
243
244         return 0;
245 }
246
247
248 /* bwrite is called by intern_printf to transparently handle writing into a
249  * buffer (if buf is non-null) or to the terminal (if buf is null).
250  */
251 static void bwrite(char *buf, size_t buf_sz, char *str, int sz)
252 {
253         if(buf) {
254                 if(buf_sz && buf_sz <= sz) sz = buf_sz - 1;
255                 memcpy(buf, str, sz);
256
257                 buf[sz] = 0;
258         } else {
259                 int i;
260                 for(i=0; i<sz; i++) {
261                         putchar(*str++);
262                 }
263         }
264 }
265