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