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