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