9ns: Add fs_files and tree_files
[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 #include <ros/common.h>
5 #include <error.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <stdarg.h>
9 #include <kthread.h>
10 #include <syscall.h>
11 #include <ns.h>
12
13 /* Print a number (base <= 16) in reverse order,
14  * using specified putch function and associated pointer putdat. */
15 void printnum(void (*putch)(int, void**), void **putdat,
16               unsigned long long num, unsigned base, int width, int padc)
17 {
18         unsigned long long temp = num;
19         int nr_digits = 1;
20         /* Determine how many leading zeros we need.
21          * For every digit/nibble beyond base, we do one less width padding */
22         while ((temp /= base)) {
23                 nr_digits++;
24                 width--;
25         }
26         /* And another one less, since we'll always print the last digit */
27         while (--width > 0)
28                 putch(padc, putdat);
29         for (int i = nr_digits; i > 0; i--) {
30                 temp = num;
31                 /* To get digit i, we only div (i-1) times */
32                 for (int j = 0; j < i - 1; j++) {
33                         temp /= base;
34                 }
35                 putch("0123456789abcdef"[temp % base], putdat);
36         }
37 }
38
39 void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...);
40
41 void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_list ap)
42 {
43         register const char *p;
44         const char *last_fmt;
45         register int ch, err;
46         unsigned long long num;
47         int base, lflag, width, precision, altflag;
48         char padc;
49         uint8_t *mac, *ip, *mask;
50         struct Gas *g;
51         int i;
52         uint32_t *lp;
53
54         while (1) {
55                 while ((ch = *(unsigned char *) fmt) != '%') {
56                         if (ch == '\0')
57                                 return;
58                         fmt++;
59                         putch(ch, putdat);
60                 }
61                 fmt++;
62
63                 // Process a %-escape sequence
64                 last_fmt = fmt;
65                 padc = ' ';
66                 width = -1;
67                 precision = -1;
68                 lflag = 0;
69                 altflag = 0;
70         reswitch:
71                 switch (ch = *(unsigned char *) fmt++) {
72
73                 // flag to pad on the right
74                 case '-':
75                         padc = '-';
76                         goto reswitch;
77
78                 // flag to pad with 0's instead of spaces
79                 case '0':
80                         padc = '0';
81                         goto reswitch;
82
83                 // width field
84                 case '1':
85                 case '2':
86                 case '3':
87                 case '4':
88                 case '5':
89                 case '6':
90                 case '7':
91                 case '8':
92                 case '9':
93                         for (precision = 0; ; ++fmt) {
94                                 precision = precision * 10 + ch - '0';
95                                 ch = *fmt;
96                                 if (ch < '0' || ch > '9')
97                                         break;
98                         }
99                         goto process_precision;
100
101                 case '*':
102                         precision = va_arg(ap, int);
103                         goto process_precision;
104
105                 case '.':
106                         if (width < 0)
107                                 width = 0;
108                         goto reswitch;
109
110                 case '#':
111                         altflag = 1;
112                         goto reswitch;
113
114                 process_precision:
115                         if (width < 0)
116                                 width = precision, precision = -1;
117                         goto reswitch;
118
119                 // long flag (doubled for long long)
120                 case 'l':
121                         lflag++;
122                         goto reswitch;
123
124                 // chan
125                 case 'C':
126                         printchan(putch, putdat, va_arg(ap, void*));
127                         break;
128
129                 // character
130                 case 'c':
131                         putch(va_arg(ap, int), putdat);
132                         break;
133
134                 case 'E': // ENET MAC
135                         if ((mac = va_arg(ap, uint8_t *)) == NULL){
136                                 char *s = "00:00:00:00:00:00";
137                                 while(*s)
138                                         putch(*s++, putdat);
139                         }
140                         printemac(putch, putdat, mac);
141                         break;
142                 case 'i':
143                         /* what to do if they screw up? */
144                         if ((lp = va_arg(ap, uint32_t *)) != NULL){
145                                 uint32_t hostfmt;
146                                 for(i = 0; i < 4; i++){
147                                         hnputl(&hostfmt, lp[i]);
148                                         printfmt(putch, putdat, "%08lx", hostfmt);
149                                 }
150                         }
151                         break;
152                 case 'I':
153                         /* what to do if they screw up? */
154                         if ((ip = va_arg(ap, uint8_t *)) != NULL)
155                                 printip(putch, putdat, ip);
156                         break;
157                 case 'M':
158                         /* what to do if they screw up? */
159                         if ((mask = va_arg(ap, uint8_t *)) != NULL)
160                                 printipmask(putch, putdat, mask);
161                         break;
162                 case 'V':
163                         /* what to do if they screw up? */
164                         if ((ip = va_arg(ap, uint8_t *)) != NULL)
165                                 printipv4(putch, putdat, ip);
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                                 // zra: make sure *p isn't '\0' before inc'ing
181                                 p++;
182                         }
183                         for (; width > 0; width--)
184                                 putch(' ', putdat);
185                         break;
186
187                 case 'd': /* (signed) decimal */
188                         if (lflag >= 2)
189                                 num = va_arg(ap, long long);
190                         else if (lflag)
191                                 num = va_arg(ap, long);
192                         else
193                                 num = va_arg(ap, int);
194                         if ((long long) num < 0) {
195                                 putch('-', putdat);
196                                 num = -(long long) num;
197                         }
198                         base = 10;
199                         goto number;
200
201                 case 'u': /* unsigned decimal */
202                 case 'o': /* (unsigned) octal */
203                 case 'x': /* (unsigned) hexadecimal */
204                         if (lflag >= 2)
205                                 num = va_arg(ap, unsigned long long);
206                         else if (lflag)
207                                 num = va_arg(ap, unsigned long);
208                         else
209                                 num = va_arg(ap, unsigned int);
210                         if (ch == 'u')
211                                 base = 10;
212                         else if (ch == 'o')
213                                 base = 8;
214                         else    /* x */
215                                 base = 16;
216                         goto number;
217
218                 // pointer
219                 case 'p':
220                         putch('0', putdat);
221                         putch('x', putdat);
222                         /* automatically zero-pad pointers, out to the length of a ptr */
223                         padc = '0';
224                         width = sizeof(void*) * 2;      /* 8 bits per byte / 4 bits per char */
225                         num = (unsigned long long)
226                                 (uintptr_t) va_arg(ap, void *);
227                         base = 16;
228                         goto number;
229
230                 // qid
231                 case 'Q':
232                         printqid(putch, putdat, va_arg(ap, void*));
233                         break;
234                 number:
235                         printnum(putch, putdat, num, base, width, padc);
236                         break;
237
238                 // escaped '%' character
239                 case '%':
240                         putch(ch, putdat);
241                         break;
242
243                 // unrecognized escape sequence - just print it literally
244                 default:
245                         putch('%', putdat);
246                         fmt = last_fmt;
247                         //for (fmt--; fmt[-1] != '%'; fmt--)
248                                 /* do nothing */;
249                         break;
250                 }
251         }
252 }
253
254 void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...)
255 {
256         va_list ap;
257
258         va_start(ap, fmt);
259         vprintfmt(putch, putdat, fmt, ap);
260         va_end(ap);
261 }
262
263 typedef struct sprintbuf {
264         char *buf;
265         char *ebuf;
266         int cnt;
267 } sprintbuf_t;
268
269 static void sprintputch(int ch, sprintbuf_t **b)
270 {
271         if ((*b)->buf < (*b)->ebuf)
272                 *((*b)->buf++) = ch;
273         (*b)->cnt++;
274 }
275
276 int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap)
277 {
278         sprintbuf_t b;// = {buf, buf+n-1, 0};
279         sprintbuf_t *bp = &b;
280
281         /* this isn't quite the snprintf 'spec', but errors aren't helpful */
282         assert(buf);
283         /* We might get large, 'negative' values for code that repeatedly calls
284          * snprintf(), e.g.:
285          *              len += snprintf(buf + len, bufsz - len, "foo");
286          *              len += snprintf(buf + len, bufsz - len, "bar");
287          * If len > bufsz, that will appear as a large value.  This is not quite the
288          * glibc semantics (we aren't returning the size we would have printed), but
289          * it short circuits the rest of the function and avoids potential errors in
290          * the putch() functions. */
291         if (!n || (n > INT32_MAX))
292                 return 0;
293
294         b.buf = NULL; // zra : help out the Deputy optimizer a bit
295         b.ebuf = buf+n-1;
296         b.cnt = 0;
297         b.buf = buf;
298
299         vprintfmt((void*)sprintputch, (void*)&bp, fmt, ap);
300
301         // null terminate the buffer
302         *b.buf = '\0';
303
304         return b.cnt;
305 }
306
307 int snprintf(char *buf, size_t n, const char *fmt, ...)
308 {
309         va_list ap;
310         int rc;
311
312         va_start(ap, fmt);
313         rc = vsnprintf(buf, n, fmt, ap);
314         va_end(ap);
315
316         return rc;
317 }
318
319 /* Convenience function: do a print, return the pointer to the null at the end.
320  *
321  * Unlike snprintf(), when we overflow, this doesn't return the 'end' where we
322  * would have written to.  Instead, we'll return 'end - 1', which is the last
323  * byte, and enforce the null-termination.  */
324 char *seprintf(char *buf, char *end, const char *fmt, ...)
325 {
326         va_list ap;
327         int rc;
328         size_t n = end - buf;
329
330         va_start(ap, fmt);
331         rc = vsnprintf(buf, n, fmt, ap);
332         va_end(ap);
333
334         /* Some error - leave them where they were. */
335         if (rc < 0)
336                 return buf;
337         /* Overflow - put them at the end */
338         if (rc >= n) {
339                 *(end - 1) = '\0';
340                 return end - 1;
341         }
342         assert(buf[rc] == '\0');
343         return buf + rc;
344 }