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