Fixes user debugfmt printfs
[akaros.git] / user / parlib / debugfmt.c
1 #include <ros/common.h>
2 #include <ros/errno.h>
3 #include <string.h>
4 #include <ros_debug.h>
5
6 /*
7  * Print a number (base <= 16) in reverse order,
8  * using specified putch function and associated pointer putdat.
9  */
10 #ifdef __DEPUTY__
11 static void printnum(void (*putch)(int, TV(t)), TV(t) putdat,
12                          unsigned long long num, unsigned base, int width, int padc)
13 #else
14 static void printnum(void (*putch)(int, void**), void **putdat,
15                          unsigned long long num, unsigned base, int width, int padc)
16 #endif
17 {
18         // first recursively print all preceding (more significant) digits
19         if (num >= base) {
20                 printnum(putch, putdat, num / base, base, width - 1, padc);
21         } else {
22                 // print any needed pad characters before first digit
23                 while (--width > 0)
24                         putch(padc, putdat);
25         }
26
27         // then print this (the least significant) digit
28         putch("0123456789abcdef"[num % base], putdat);
29 }
30
31 // Main function to format and print a string.
32 #ifdef __DEPUTY__
33 void ros_debugfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, ...);
34 void ros_vdebugfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, va_list ap)
35 #else
36 void ros_debugfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...);
37 void ros_vdebugfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_list ap)
38 #endif
39 {
40         register const char *NTS p;
41         const char *NTS last_fmt;
42         register int ch, err;
43         unsigned long long num;
44         int base, lflag, width, precision, altflag;
45         char padc;
46
47         while (1) {
48                 while ((ch = *(unsigned char *) fmt) != '%') {
49                         if (ch == '\0')
50                                 return;
51                         fmt++;
52                         putch(ch, putdat);
53                 }
54                 fmt++;
55
56                 // Process a %-escape sequence
57                 last_fmt = fmt;
58                 padc = ' ';
59                 width = -1;
60                 precision = -1;
61                 lflag = 0;
62                 altflag = 0;
63         reswitch:
64                 switch (ch = *(unsigned char *) fmt++) {
65
66                 // flag to pad on the right
67                 case '-':
68                         padc = '-';
69                         goto reswitch;
70                         
71                 // flag to pad with 0's instead of spaces
72                 case '0':
73                         padc = '0';
74                         goto reswitch;
75
76                 // width field
77                 case '1':
78                 case '2':
79                 case '3':
80                 case '4':
81                 case '5':
82                 case '6':
83                 case '7':
84                 case '8':
85                 case '9':
86                         for (precision = 0; ; ++fmt) {
87                                 precision = precision * 10 + ch - '0';
88                                 ch = *fmt;
89                                 if (ch < '0' || ch > '9')
90                                         break;
91                         }
92                         goto process_precision;
93
94                 case '*':
95                         precision = va_arg(ap, int);
96                         goto process_precision;
97
98                 case '.':
99                         if (width < 0)
100                                 width = 0;
101                         goto reswitch;
102
103                 case '#':
104                         altflag = 1;
105                         goto reswitch;
106
107                 process_precision:
108                         if (width < 0)
109                                 width = precision, precision = -1;
110                         goto reswitch;
111
112                 // long flag (doubled for long long)
113                 case 'l':
114                         lflag++;
115                         goto reswitch;
116
117                 // character
118                 case 'c':
119                         putch(va_arg(ap, int), putdat);
120                         break;
121
122                 // string
123                 case 's':
124                         if ((p = va_arg(ap, char *NT)) == NULL)
125                                 p = "(null)";
126                         if (width > 0 && padc != '-')
127                                 for (width -= strnlen(p, precision); width > 0; width--)
128                                         putch(padc, putdat);
129                         for (; (ch = *p) != '\0' && (precision < 0 || --precision >= 0); width--) {
130                                 if (altflag && (ch < ' ' || ch > '~'))
131                                         putch('?', putdat);
132                                 else
133                                         putch(ch, putdat);
134                                 // zra: make sure *p isn't '\0' before inc'ing
135                                 p++;
136                         }
137                         for (; width > 0; width--)
138                                 putch(' ', putdat);
139                         break;
140
141                 case 'd': /* (signed) decimal */
142                         if (lflag >= 2)
143                                 num = va_arg(ap, long long);
144                         else if (lflag)
145                                 num = va_arg(ap, long);
146                         else
147                                 num = va_arg(ap, int);
148                         if ((long long) num < 0) {
149                                 putch('-', putdat);
150                                 num = -(long long) num;
151                         }
152                         base = 10;
153                         goto number;
154
155                 case 'u': /* unsigned decimal */
156                 case 'o': /* (unsigned) octal */
157                 case 'x': /* (unsigned) hexadecimal */
158                         if (lflag >= 2)
159                                 num = va_arg(ap, unsigned long long);
160                         else if (lflag)
161                                 num = va_arg(ap, unsigned long);
162                         else
163                                 num = va_arg(ap, unsigned int);
164                         if (ch == 'u')
165                                 base = 10;
166                         else if (ch == 'o')
167                                 base = 8;
168                         else    /* x */
169                                 base = 16;
170                         goto number;
171
172                 // pointer
173                 case 'p':
174                         putch('0', putdat);
175                         putch('x', putdat);
176                         num = (unsigned long long)
177                                 (uintptr_t) va_arg(ap, void *);
178                         base = 16;
179                         goto number;
180
181                 number:
182                         printnum(putch, putdat, num, base, width, padc);
183                         break;
184
185                 // escaped '%' character
186                 case '%':
187                         putch(ch, putdat);
188                         break;
189                         
190                 // unrecognized escape sequence - just print it literally
191                 default:
192                         putch('%', putdat);
193                         fmt = last_fmt;
194                         //for (fmt--; fmt[-1] != '%'; fmt--)
195                                 /* do nothing */;
196                         break;
197                 }
198         }
199 }
200
201 #ifdef __DEPUTY__
202 void ros_debugfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, ...)
203 #else
204 void ros_debugfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...)
205 #endif
206 {
207         va_list ap;
208
209         va_start(ap, fmt);
210         ros_vdebugfmt(putch, putdat, fmt, ap);
211         va_end(ap);
212 }
213