_M procs start up at _start/hart_entry for vcore0
[akaros.git] / kern / src / printfmt.c
1 // Stripped-down primitive printf-style formatting routines,
2 // used in common by printf, sprintf, fprintf, etc.
3 // This code is also used by both the kernel and user programs.
4
5 #ifdef __SHARC__
6 #pragma nosharc
7 #endif
8
9 #include <ros/common.h>
10 #include <error.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdarg.h>
14
15 /*
16  * Print a number (base <= 16) in reverse order,
17  * using specified putch function and associated pointer putdat.
18  */
19 #ifdef __DEPUTY__
20 void printnum(void (*putch)(int, TV(t)), TV(t) putdat,
21                          unsigned long long num, unsigned base, int width, int padc)
22 #else
23 void printnum(void (*putch)(int, void**), void **putdat,
24                          unsigned long long num, unsigned base, int width, int padc)
25 #endif
26 {
27         // first recursively print all preceding (more significant) digits
28         if (num >= base) {
29                 printnum(putch, putdat, num / base, base, width - 1, padc);
30         } else {
31                 // print any needed pad characters before first digit
32                 while (--width > 0)
33                         putch(padc, putdat);
34         }
35
36         // then print this (the least significant) digit
37         putch("0123456789abcdef"[num % base], putdat);
38 }
39
40 // Get an unsigned int of various possible sizes from a varargs list,
41 // depending on the lflag parameter.
42 static unsigned long long getuint(va_list *ap, int lflag)
43 {
44         if (lflag >= 2)
45                 return va_arg(*ap, unsigned long long);
46         else if (lflag)
47                 return va_arg(*ap, unsigned long);
48         else
49                 return va_arg(*ap, unsigned int);
50 }
51
52 // Same as getuint but signed - can't use getuint
53 // because of sign extension
54 static long long getint(va_list *ap, int lflag)
55 {
56         if (lflag >= 2)
57                 return va_arg(*ap, long long);
58         else if (lflag)
59                 return va_arg(*ap, long);
60         else
61                 return va_arg(*ap, int);
62 }
63
64
65 // Main function to format and print a string.
66 #ifdef __DEPUTY__
67 void printfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, ...);
68 #else
69 void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...);
70 #endif
71
72 #ifdef __DEPUTY__
73 void vprintfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, va_list ap)
74 #else
75 void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_list ap)
76 #endif
77 {
78         register const char *NTS p;
79         const char *NTS last_fmt;
80         register int ch, err;
81         unsigned long long num;
82         int base, lflag, width, precision, altflag;
83         char padc;
84
85         while (1) {
86                 while ((ch = *(unsigned char *) fmt) != '%') {
87                         if (ch == '\0')
88                                 return;
89                         fmt++;
90                         putch(ch, putdat);
91                 }
92                 fmt++;
93
94                 // Process a %-escape sequence
95                 last_fmt = fmt;
96                 padc = ' ';
97                 width = -1;
98                 precision = -1;
99                 lflag = 0;
100                 altflag = 0;
101         reswitch:
102                 switch (ch = *(unsigned char *) fmt++) {
103
104                 // flag to pad on the right
105                 case '-':
106                         padc = '-';
107                         goto reswitch;
108                         
109                 // flag to pad with 0's instead of spaces
110                 case '0':
111                         padc = '0';
112                         goto reswitch;
113
114                 // width field
115                 case '1':
116                 case '2':
117                 case '3':
118                 case '4':
119                 case '5':
120                 case '6':
121                 case '7':
122                 case '8':
123                 case '9':
124                         for (precision = 0; ; ++fmt) {
125                                 precision = precision * 10 + ch - '0';
126                                 ch = *fmt;
127                                 if (ch < '0' || ch > '9')
128                                         break;
129                         }
130                         goto process_precision;
131
132                 case '*':
133                         precision = va_arg(ap, int);
134                         goto process_precision;
135
136                 case '.':
137                         if (width < 0)
138                                 width = 0;
139                         goto reswitch;
140
141                 case '#':
142                         altflag = 1;
143                         goto reswitch;
144
145                 process_precision:
146                         if (width < 0)
147                                 width = precision, precision = -1;
148                         goto reswitch;
149
150                 // long flag (doubled for long long)
151                 case 'l':
152                         lflag++;
153                         goto reswitch;
154
155                 // character
156                 case 'c':
157                         putch(va_arg(ap, int), putdat);
158                         break;
159
160                 // error message
161                 case 'e':
162                         err = va_arg(ap, int);
163                         if (err < 0)
164                                 err = -err;
165                         if (err >= NUMERRORS)
166                                 printfmt(putch, putdat, "error %d", err);
167                         else
168                                 printfmt(putch, putdat, "%s", error_string[err]);
169                         break;
170
171                 // string
172                 case 's':
173                         if ((p = va_arg(ap, char *)) == NULL)
174                                 p = "(null)";
175                         if (width > 0 && padc != '-')
176                                 for (width -= strnlen(p, precision); width > 0; width--)
177                                         putch(padc, putdat);
178                         for (; (ch = *p) != '\0' && (precision < 0 || --precision >= 0); width--) {
179                                 if (altflag && (ch < ' ' || ch > '~'))
180                                         putch('?', putdat);
181                                 else
182                                         putch(ch, putdat);
183                                 // zra: make sure *p isn't '\0' before inc'ing
184                                 p++;
185                         }
186                         for (; width > 0; width--)
187                                 putch(' ', putdat);
188                         break;
189
190                 // (signed) decimal
191                 case 'd':
192                         num = getint(&ap, lflag);
193                         if ((long long) num < 0) {
194                                 putch('-', putdat);
195                                 num = -(long long) num;
196                         }
197                         base = 10;
198                         goto number;
199
200                 // unsigned decimal
201                 case 'u':
202                         num = getuint(&ap, lflag);
203                         base = 10;
204                         goto number;
205
206                 // (unsigned) octal
207                 case 'o':
208                         // should do something with padding so it's always 3 octits
209                         num = getuint(&ap, lflag);
210                         base = 8;
211                         goto number;
212
213                 // pointer
214                 case 'p':
215                         putch('0', putdat);
216                         putch('x', putdat);
217                         num = (unsigned long long)
218                                 (uintptr_t) va_arg(ap, void *);
219                         base = 16;
220                         goto number;
221
222                 // (unsigned) hexadecimal
223                 case 'x':
224                         num = getuint(&ap, lflag);
225                         base = 16;
226                 number:
227                         printnum(putch, putdat, num, base, width, padc);
228                         break;
229
230                 // escaped '%' character
231                 case '%':
232                         putch(ch, putdat);
233                         break;
234                         
235                 // unrecognized escape sequence - just print it literally
236                 default:
237                         putch('%', putdat);
238                         fmt = last_fmt;
239                         //for (fmt--; fmt[-1] != '%'; fmt--)
240                                 /* do nothing */;
241                         break;
242                 }
243         }
244 }
245
246 #ifdef __DEPUTY__
247 void printfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, ...)
248 #else
249 void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...)
250 #endif
251 {
252         va_list ap;
253
254         va_start(ap, fmt);
255         vprintfmt(putch, putdat, fmt, ap);
256         va_end(ap);
257 }
258
259 typedef struct sprintbuf {
260         char *BND(__this,ebuf) buf;
261         char *SNT ebuf;
262         int cnt;
263 } sprintbuf_t;
264
265 static void sprintputch(int ch, sprintbuf_t *NONNULL *NONNULL b)
266 {
267         (*b)->cnt++;
268         if ((*b)->buf < (*b)->ebuf)
269                 *((*b)->buf++) = ch;
270 }
271
272 int vsnprintf(char *buf, int n, const char *fmt, va_list ap)
273 {
274         sprintbuf_t b;// = {buf, buf+n-1, 0};
275         sprintbuf_t *COUNT(1) NONNULL bp = &b;
276
277         if (buf == NULL || n < 1)
278                 return -EINVAL;
279
280         b.buf = NULL; // zra : help out the Deputy optimizer a bit
281         b.ebuf = buf+n-1;
282         b.cnt = 0;
283         b.buf = buf;
284
285         // print the string to the buffer
286         #ifdef __DEPUTY__
287         vprintfmt((void*)sprintputch, (sprintbuf_t *NONNULL*NONNULL)&bp, fmt, ap);
288         #else
289         vprintfmt((void*)sprintputch, (void*)&bp, fmt, ap);
290         #endif
291
292         // null terminate the buffer
293         *b.buf = '\0';
294
295         return b.cnt;
296 }
297
298 int snprintf(char *buf, int n, const char *fmt, ...)
299 {
300         va_list ap;
301         int rc;
302
303         va_start(ap, fmt);
304         rc = vsnprintf(buf, n, fmt, ap);
305         va_end(ap);
306
307         return rc;
308 }
309
310