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