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