Remove errstrings.h, in favor of error.c
[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                 // error message
135                 case 'e':
136                         err = va_arg(ap, int);
137                         if (err < 0)
138                                 err = -err;
139                         if (err >= MAX_ERRNO)
140                                 printfmt(putch, putdat, "error %d(%s)", err, get_cur_errbuf());
141                         else
142                                 printfmt(putch, putdat, "%s (%s)", errno_strings[err], get_cur_errbuf());
143                         break;
144
145                 case 'E': // ENET MAC
146                         if ((mac = va_arg(ap, uint8_t *)) == NULL){
147                                 char *s = "00:00:00:00:00:00";
148                                 while(*s)
149                                         putch(*s++, putdat);
150                         }
151                         printemac(putch, putdat, mac);
152                         break;
153                 case 'i':
154                         /* what to do if they screw up? */
155                         if ((lp = va_arg(ap, uint32_t *)) != NULL){
156                                 uint32_t hostfmt;
157                                 for(i = 0; i < 4; i++){
158                                         hnputl(&hostfmt, lp[i]);
159                                         printfmt(putch, putdat, "%08lx", hostfmt);
160                                 }
161                         }
162                         break;
163                 case 'I':
164                         /* what to do if they screw up? */
165                         if ((ip = va_arg(ap, uint8_t *)) != NULL)
166                                 printip(putch, putdat, ip);
167                         break;
168                 case 'M':
169                         /* what to do if they screw up? */
170                         if ((mask = va_arg(ap, uint8_t *)) != NULL)
171                                 printipmask(putch, putdat, mask);
172                         break;
173                 case 'V':
174                         /* what to do if they screw up? */
175                         if ((ip = va_arg(ap, uint8_t *)) != NULL)
176                                 printipv4(putch, putdat, ip);
177                         break;
178
179                 // string
180                 case 's':
181                         if ((p = va_arg(ap, char *)) == NULL)
182                                 p = "(null)";
183                         if (width > 0 && padc != '-')
184                                 for (width -= strnlen(p, precision); width > 0; width--)
185                                         putch(padc, putdat);
186                         for (; (ch = *p) != '\0' && (precision < 0 || --precision >= 0); width--) {
187                                 if (altflag && (ch < ' ' || ch > '~'))
188                                         putch('?', putdat);
189                                 else
190                                         putch(ch, putdat);
191                                 // zra: make sure *p isn't '\0' before inc'ing
192                                 p++;
193                         }
194                         for (; width > 0; width--)
195                                 putch(' ', putdat);
196                         break;
197
198                 case 'd': /* (signed) decimal */
199                         if (lflag >= 2)
200                                 num = va_arg(ap, long long);
201                         else if (lflag)
202                                 num = va_arg(ap, long);
203                         else
204                                 num = va_arg(ap, int);
205                         if ((long long) num < 0) {
206                                 putch('-', putdat);
207                                 num = -(long long) num;
208                         }
209                         base = 10;
210                         goto number;
211
212                 case 'u': /* unsigned decimal */
213                 case 'o': /* (unsigned) octal */
214                 case 'x': /* (unsigned) hexadecimal */
215                         if (lflag >= 2)
216                                 num = va_arg(ap, unsigned long long);
217                         else if (lflag)
218                                 num = va_arg(ap, unsigned long);
219                         else
220                                 num = va_arg(ap, unsigned int);
221                         if (ch == 'u')
222                                 base = 10;
223                         else if (ch == 'o')
224                                 base = 8;
225                         else    /* x */
226                                 base = 16;
227                         goto number;
228
229                 // pointer
230                 case 'p':
231                         putch('0', putdat);
232                         putch('x', putdat);
233                         /* automatically zero-pad pointers, out to the length of a ptr */
234                         padc = '0';
235                         width = sizeof(void*) * 2;      /* 8 bits per byte / 4 bits per char */
236                         num = (unsigned long long)
237                                 (uintptr_t) va_arg(ap, void *);
238                         base = 16;
239                         goto number;
240
241                 // qid
242                 case 'Q':
243                         printqid(putch, putdat, va_arg(ap, void*));
244                         break;
245                 number:
246                         printnum(putch, putdat, num, base, width, padc);
247                         break;
248
249                 // escaped '%' character
250                 case '%':
251                         putch(ch, putdat);
252                         break;
253
254                 // unrecognized escape sequence - just print it literally
255                 default:
256                         putch('%', putdat);
257                         fmt = last_fmt;
258                         //for (fmt--; fmt[-1] != '%'; fmt--)
259                                 /* do nothing */;
260                         break;
261                 }
262         }
263 }
264
265 void printfmt(void (*putch)(int, void**), void **putdat, const char *fmt, ...)
266 {
267         va_list ap;
268
269         va_start(ap, fmt);
270         vprintfmt(putch, putdat, fmt, ap);
271         va_end(ap);
272         check_poison("printfmt");
273 }
274
275 typedef struct sprintbuf {
276         char *buf;
277         char *ebuf;
278         int cnt;
279 } sprintbuf_t;
280
281 static void sprintputch(int ch, sprintbuf_t **b)
282 {
283         if ((*b)->buf < (*b)->ebuf)
284                 *((*b)->buf++) = ch;
285         (*b)->cnt++;
286 }
287
288 int vsnprintf(char *buf, int n, const char *fmt, va_list ap)
289 {
290         sprintbuf_t b;// = {buf, buf+n-1, 0};
291         sprintbuf_t *bp = &b;
292
293         /* this isn't quite the snprintf 'spec', but errors aren't helpful */
294         if (buf == NULL || n < 1)
295                 return 0;
296
297         b.buf = NULL; // zra : help out the Deputy optimizer a bit
298         b.ebuf = buf+n-1;
299         b.cnt = 0;
300         b.buf = buf;
301
302         vprintfmt((void*)sprintputch, (void*)&bp, fmt, ap);
303
304         // null terminate the buffer
305         *b.buf = '\0';
306
307         return b.cnt;
308 }
309
310 int snprintf(char *buf, int n, const char *fmt, ...)
311 {
312         va_list ap;
313         int rc;
314
315         va_start(ap, fmt);
316         rc = vsnprintf(buf, n, fmt, ap);
317         va_end(ap);
318
319         check_poison("snprintf");
320         return rc;
321 }
322
323 /* convenience function: do a print, return the pointer to the end. */
324 char *seprintf(char *buf, char *end, const char *fmt, ...)
325 {
326         va_list ap;
327         int rc;
328         int n = end - buf;
329
330         if (n <= 0)
331                 return buf;
332
333         va_start(ap, fmt);
334         rc = vsnprintf(buf, n, fmt, ap);
335         va_end(ap);
336         check_poison("seprintf");
337
338         if (rc >= 0)
339                 return buf + rc;
340         else
341                 return buf;
342 }