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