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