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