printnum() is no longer recursive
[akaros.git] / kern / src / printfmt.c
1 // Stripped-down primitive printf-style formatting routines,
2 // used in common by printf, sprintf, fprintf, etc.
3 // This code is also used by both the kernel and user programs.
4
5 #ifdef __SHARC__
6 #pragma nosharc
7 #endif
8
9 #include <ros/common.h>
10 #include <error.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <stdarg.h>
14
15 /* Print a number (base <= 16) in reverse order,
16  * using specified putch function and associated pointer putdat. */
17 void printnum(void (*putch)(int, void**), void **putdat,
18               unsigned long long num, unsigned base, int width, int padc)
19 {
20         unsigned long long temp = num;
21         int nr_digits = 1;
22         /* Determine how many leading zeros we need.
23          * For every digit/nibble beyond base, we do one less width padding */
24         while ((temp /= base)) {
25                 nr_digits++;
26                 width--;
27         }
28         /* And another one less, since we'll always print the last digit */
29         while (--width > 0)
30                 putch(padc, putdat);
31         for (int i = nr_digits; i > 0; i--) {
32                 temp = num;
33                 /* To get digit i, we only div (i-1) times */
34                 for (int j = 0; j < i - 1; j++) {
35                         temp /= base;
36                 }
37                 putch("0123456789abcdef"[temp % base], putdat);
38         }
39 }
40
41 // Main function to format and print a string.
42 #ifdef __DEPUTY__
43 void printfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, ...);
44 #else
45 void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...);
46 #endif
47
48 #ifdef __DEPUTY__
49 void vprintfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, va_list ap)
50 #else
51 void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_list ap)
52 #endif
53 {
54         register const char *NTS p;
55         const char *NTS last_fmt;
56         register int ch, err;
57         unsigned long long num;
58         int base, lflag, width, precision, altflag;
59         char padc;
60
61         while (1) {
62                 while ((ch = *(unsigned char *) fmt) != '%') {
63                         if (ch == '\0')
64                                 return;
65                         fmt++;
66                         putch(ch, putdat);
67                 }
68                 fmt++;
69
70                 // Process a %-escape sequence
71                 last_fmt = fmt;
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                                 printfmt(putch, putdat, "error %d", err);
143                         else
144                                 printfmt(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                                 // zra: make sure *p isn't '\0' before inc'ing
160                                 p++;
161                         }
162                         for (; width > 0; width--)
163                                 putch(' ', putdat);
164                         break;
165
166                 case 'd': /* (signed) decimal */
167                         if (lflag >= 2)
168                                 num = va_arg(ap, long long);
169                         else if (lflag)
170                                 num = va_arg(ap, long);
171                         else
172                                 num = va_arg(ap, int);
173                         if ((long long) num < 0) {
174                                 putch('-', putdat);
175                                 num = -(long long) num;
176                         }
177                         base = 10;
178                         goto number;
179
180                 case 'u': /* unsigned decimal */
181                 case 'o': /* (unsigned) octal */
182                 case 'x': /* (unsigned) hexadecimal */
183                         if (lflag >= 2)
184                                 num = va_arg(ap, unsigned long long);
185                         else if (lflag)
186                                 num = va_arg(ap, unsigned long);
187                         else
188                                 num = va_arg(ap, unsigned int);
189                         if (ch == 'u')
190                                 base = 10;
191                         else if (ch == 'o')
192                                 base = 8;
193                         else    /* x */
194                                 base = 16;
195                         goto number;
196
197                 // pointer
198                 case 'p':
199                         putch('0', putdat);
200                         putch('x', putdat);
201                         /* automatically zero-pad pointers, out to the length of a ptr */
202                         padc = '0';
203                         width = sizeof(void*) * 2;      /* 8 bits per byte / 4 bits per char */
204                         num = (unsigned long long)
205                                 (uintptr_t) va_arg(ap, void *);
206                         base = 16;
207                         goto number;
208
209                 number:
210                         printnum(putch, putdat, num, base, width, padc);
211                         break;
212
213                 // escaped '%' character
214                 case '%':
215                         putch(ch, putdat);
216                         break;
217                         
218                 // unrecognized escape sequence - just print it literally
219                 default:
220                         putch('%', putdat);
221                         fmt = last_fmt;
222                         //for (fmt--; fmt[-1] != '%'; fmt--)
223                                 /* do nothing */;
224                         break;
225                 }
226         }
227 }
228
229 #ifdef __DEPUTY__
230 void printfmt(void (*putch)(int, TV(t)), TV(t) putdat, const char *fmt, ...)
231 #else
232 void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...)
233 #endif
234 {
235         va_list ap;
236
237         va_start(ap, fmt);
238         vprintfmt(putch, putdat, fmt, ap);
239         va_end(ap);
240 }
241
242 typedef struct sprintbuf {
243         char *BND(__this,ebuf) buf;
244         char *SNT ebuf;
245         int cnt;
246 } sprintbuf_t;
247
248 static void sprintputch(int ch, sprintbuf_t *NONNULL *NONNULL b)
249 {
250         (*b)->cnt++;
251         if ((*b)->buf < (*b)->ebuf)
252                 *((*b)->buf++) = ch;
253 }
254
255 int vsnprintf(char *buf, int n, const char *fmt, va_list ap)
256 {
257         sprintbuf_t b;// = {buf, buf+n-1, 0};
258         sprintbuf_t *COUNT(1) NONNULL bp = &b;
259
260         if (buf == NULL || n < 1)
261                 return -EINVAL;
262
263         b.buf = NULL; // zra : help out the Deputy optimizer a bit
264         b.ebuf = buf+n-1;
265         b.cnt = 0;
266         b.buf = buf;
267
268         // print the string to the buffer
269         #ifdef __DEPUTY__
270         vprintfmt((void*)sprintputch, (sprintbuf_t *NONNULL*NONNULL)&bp, fmt, ap);
271         #else
272         vprintfmt((void*)sprintputch, (void*)&bp, fmt, ap);
273         #endif
274
275         // null terminate the buffer
276         *b.buf = '\0';
277
278         return b.cnt;
279 }
280
281 int snprintf(char *buf, int n, const char *fmt, ...)
282 {
283         va_list ap;
284         int rc;
285
286         va_start(ap, fmt);
287         rc = vsnprintf(buf, n, fmt, ap);
288         va_end(ap);
289
290         return rc;
291 }
292
293