add %r format
[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 /*
124                 case 'r':
125                         p = current_errstr();
126                         /* oh, barf. Now we look like glibc. * /
127                         goto putstring;
128 */
129                 case 's':
130                         if ((p = va_arg(ap, char *NT)) == NULL)
131                                 p = "(null)";
132 //putstring:
133                         if (width > 0 && padc != '-')
134                                 for (width -= strnlen(p, precision); width > 0; width--)
135                                         putch(padc, putdat);
136                         for (; (ch = *p) != '\0' && (precision < 0 || --precision >= 0); width--) {
137                                 if (altflag && (ch < ' ' || ch > '~'))
138                                         putch('?', putdat);
139                                 else
140                                         putch(ch, putdat);
141                                 // zra: make sure *p isn't '\0' before inc'ing
142                                 p++;
143                         }
144                         for (; width > 0; width--)
145                                 putch(' ', putdat);
146                         break;
147
148                 case 'd': /* (signed) decimal */
149                         if (lflag >= 2)
150                                 num = va_arg(ap, long long);
151                         else if (lflag)
152                                 num = va_arg(ap, long);
153                         else
154                                 num = va_arg(ap, int);
155                         if ((long long) num < 0) {
156                                 putch('-', putdat);
157                                 num = -(long long) num;
158                         }
159                         base = 10;
160                         goto number;
161
162                 case 'u': /* unsigned decimal */
163                 case 'o': /* (unsigned) octal */
164                 case 'x': /* (unsigned) hexadecimal */
165                         if (lflag >= 2)
166                                 num = va_arg(ap, unsigned long long);
167                         else if (lflag)
168                                 num = va_arg(ap, unsigned long);
169                         else
170                                 num = va_arg(ap, unsigned int);
171                         if (ch == 'u')
172                                 base = 10;
173                         else if (ch == 'o')
174                                 base = 8;
175                         else    /* x */
176                                 base = 16;
177                         goto number;
178
179                 // pointer
180                 case 'p':
181                         putch('0', putdat);
182                         putch('x', putdat);
183                         num = (unsigned long long)
184                                 (uintptr_t) va_arg(ap, void *);
185                         base = 16;
186                         goto number;
187
188                 number:
189                         printnum(putch, putdat, num, base, width, padc);
190                         break;
191
192                 // escaped '%' character
193                 case '%':
194                         putch(ch, putdat);
195                         break;
196                         
197                 // unrecognized escape sequence - just print it literally
198                 default:
199                         putch('%', putdat);
200                         fmt = last_fmt;
201                         //for (fmt--; fmt[-1] != '%'; fmt--)
202                                 /* do nothing */;
203                         break;
204                 }
205         }
206 }
207
208 #ifdef __DEPUTY__
209 void ros_debugfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, ...)
210 #else
211 void ros_debugfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...)
212 #endif
213 {
214         va_list ap;
215
216         va_start(ap, fmt);
217         ros_vdebugfmt(putch, putdat, fmt, ap);
218         va_end(ap);
219 }
220