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