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