]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/libsa/printf.c
stand: Remove ancient SCCS tags.
[FreeBSD/FreeBSD.git] / stand / libsa / printf.c
1 /*-
2  * Copyright (c) 1986, 1988, 1991, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  * (c) UNIX System Laboratories, Inc.
5  * All or some portions of this file are derived from material licensed
6  * to the University of California by American Telephone and Telegraph
7  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8  * the permission of UNIX System Laboratories, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/cdefs.h>
36 /*
37  * Standaloneified version of the FreeBSD kernel printf family.
38  */
39
40 #include <sys/types.h>
41 #include <sys/stddef.h>
42 #include <sys/stdint.h>
43 #include <limits.h>
44 #include <string.h>
45 #include "stand.h"
46
47 /*
48  * Note that stdarg.h and the ANSI style va_start macro is used for both
49  * ANSI and traditional C compilers.
50  */
51 #include <machine/stdarg.h>
52
53 #define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1)
54
55 typedef void (kvprintf_fn_t)(int, void *);
56
57 static char     *ksprintn (char *buf, uintmax_t num, int base, int *len, int upper);
58 static int      kvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap);
59
60 static void
61 putchar_wrapper(int cc, void *arg)
62 {
63
64         putchar(cc);
65 }
66
67 int
68 printf(const char *fmt, ...)
69 {
70         va_list ap;
71         int retval;
72
73         va_start(ap, fmt);
74         retval = kvprintf(fmt, putchar_wrapper, NULL, 10, ap);
75         va_end(ap);
76         return retval;
77 }
78
79 int
80 vprintf(const char *fmt, va_list ap)
81 {
82
83         return (kvprintf(fmt, putchar_wrapper, NULL, 10, ap));
84 }
85
86 int
87 sprintf(char *buf, const char *cfmt, ...)
88 {
89         int retval;
90         va_list ap;
91
92         va_start(ap, cfmt);
93         retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
94         buf[retval] = '\0';
95         va_end(ap);
96         return retval;
97 }
98
99 struct print_buf {
100         char *buf;
101         size_t size;
102 };
103
104 static void
105 snprint_func(int ch, void *arg)
106 {
107         struct print_buf *pbuf = arg;
108
109         if (pbuf->size < 2) {
110                 /*
111                  * Reserve last buffer position for the terminating
112                  * character:
113                  */
114                 return;
115         }
116         *(pbuf->buf)++ = ch;
117         pbuf->size--;
118 }
119
120 int
121 asprintf(char **buf, const char *cfmt, ...)
122 {
123         int retval;
124         struct print_buf arg;
125         va_list ap;
126
127         *buf = NULL;
128         va_start(ap, cfmt);
129         retval = kvprintf(cfmt, NULL, NULL, 10, ap);
130         va_end(ap);
131         if (retval <= 0)
132                 return (-1);
133
134         arg.size = retval + 1;
135         arg.buf = *buf = malloc(arg.size);
136         if (*buf == NULL)
137                 return (-1);
138
139         va_start(ap, cfmt);
140         retval = kvprintf(cfmt, &snprint_func, &arg, 10, ap);
141         va_end(ap);
142
143         if (arg.size >= 1)
144                 *(arg.buf)++ = 0;
145         return (retval);
146 }
147
148 int
149 snprintf(char *buf, size_t size, const char *cfmt, ...)
150 {
151         int retval;
152         va_list ap;
153         struct print_buf arg;
154
155         arg.buf = buf;
156         arg.size = size;
157
158         va_start(ap, cfmt);
159         retval = kvprintf(cfmt, &snprint_func, &arg, 10, ap);
160         va_end(ap);
161
162         if (arg.size >= 1)
163                 *(arg.buf)++ = 0;
164         return retval;
165 }
166
167 int
168 vsnprintf(char *buf, size_t size, const char *cfmt, va_list ap)
169 {
170         struct print_buf arg;
171         int retval;
172
173         arg.buf = buf;
174         arg.size = size;
175
176         retval = kvprintf(cfmt, &snprint_func, &arg, 10, ap);
177
178         if (arg.size >= 1)
179                 *(arg.buf)++ = 0;
180
181         return (retval);
182 }
183
184 int
185 vsprintf(char *buf, const char *cfmt, va_list ap)
186 {
187         int     retval;
188         
189         retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
190         buf[retval] = '\0';
191
192         return (retval);
193 }
194
195 /*
196  * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
197  * order; return an optional length and a pointer to the last character
198  * written in the buffer (i.e., the first character of the string).
199  * The buffer pointed to by `nbuf' must have length >= MAXNBUF.
200  */
201 static char *
202 ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
203 {
204         char *p, c;
205
206         p = nbuf;
207         *p = '\0';
208         do {
209                 c = hex2ascii(num % base);
210                 *++p = upper ? toupper(c) : c;
211         } while (num /= base);
212         if (lenp)
213                 *lenp = p - nbuf;
214         return (p);
215 }
216
217 /*
218  * Scaled down version of printf(3).
219  *
220  * Two additional formats:
221  *
222  * The format %b is supported to decode error registers.
223  * Its usage is:
224  *
225  *      printf("reg=%b\n", regval, "<base><arg>*");
226  *
227  * where <base> is the output base expressed as a control character, e.g.
228  * \10 gives octal; \20 gives hex.  Each arg is a sequence of characters,
229  * the first of which gives the bit number to be inspected (origin 1), and
230  * the next characters (up to a control character, i.e. a character <= 32),
231  * give the name of the register.  Thus:
232  *
233  *      kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE");
234  *
235  * would produce output:
236  *
237  *      reg=3<BITTWO,BITONE>
238  *
239  * XXX:  %D  -- Hexdump, takes pointer and separator string:
240  *              ("%6D", ptr, ":")   -> XX:XX:XX:XX:XX:XX
241  *              ("%*D", len, ptr, " " -> XX XX XX XX ...
242  */
243 static int
244 kvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap)
245 {
246 #define PCHAR(c) { \
247         int cc = (c);                           \
248                                                 \
249         if (func) {                             \
250                 (*func)(cc, arg);               \
251         } else if (d != NULL) {                 \
252                 *d++ = cc;                      \
253         }                                       \
254         retval++;                               \
255         }
256
257         char nbuf[MAXNBUF];
258         char *d;
259         const char *p, *percent, *q;
260         uint16_t *S;
261         u_char *up;
262         int ch, n;
263         uintmax_t num;
264         int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
265         int cflag, hflag, jflag, tflag, zflag;
266         int dwidth, upper;
267         char padc;
268         int stop = 0, retval = 0;
269
270         TSENTER();
271         num = 0;
272         if (!func)
273                 d = (char *) arg;
274         else
275                 d = NULL;
276
277         if (fmt == NULL)
278                 fmt = "(fmt null)\n";
279
280         if (radix < 2 || radix > 36)
281                 radix = 10;
282
283         for (;;) {
284                 padc = ' ';
285                 width = 0;
286                 while ((ch = (u_char)*fmt++) != '%' || stop) {
287                         if (ch == '\0') {
288                                 TSEXIT();
289                                 return (retval);
290                         }
291                         PCHAR(ch);
292                 }
293                 percent = fmt - 1;
294                 qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
295                 sign = 0; dot = 0; dwidth = 0; upper = 0;
296                 cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
297 reswitch:       switch (ch = (u_char)*fmt++) {
298                 case '.':
299                         dot = 1;
300                         goto reswitch;
301                 case '#':
302                         sharpflag = 1;
303                         goto reswitch;
304                 case '+':
305                         sign = 1;
306                         goto reswitch;
307                 case '-':
308                         ladjust = 1;
309                         goto reswitch;
310                 case '%':
311                         PCHAR(ch);
312                         break;
313                 case '*':
314                         if (!dot) {
315                                 width = va_arg(ap, int);
316                                 if (width < 0) {
317                                         ladjust = !ladjust;
318                                         width = -width;
319                                 }
320                         } else {
321                                 dwidth = va_arg(ap, int);
322                         }
323                         goto reswitch;
324                 case '0':
325                         if (!dot) {
326                                 padc = '0';
327                                 goto reswitch;
328                         }
329                 case '1': case '2': case '3': case '4':
330                 case '5': case '6': case '7': case '8': case '9':
331                                 for (n = 0;; ++fmt) {
332                                         n = n * 10 + ch - '0';
333                                         ch = *fmt;
334                                         if (ch < '0' || ch > '9')
335                                                 break;
336                                 }
337                         if (dot)
338                                 dwidth = n;
339                         else
340                                 width = n;
341                         goto reswitch;
342                 case 'b':
343                         num = (u_int)va_arg(ap, int);
344                         p = va_arg(ap, char *);
345                         for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
346                                 PCHAR(*q--);
347
348                         if (num == 0)
349                                 break;
350
351                         for (tmp = 0; *p;) {
352                                 n = *p++;
353                                 if (num & (1 << (n - 1))) {
354                                         PCHAR(tmp ? ',' : '<');
355                                         for (; (n = *p) > ' '; ++p)
356                                                 PCHAR(n);
357                                         tmp = 1;
358                                 } else
359                                         for (; *p > ' '; ++p)
360                                                 continue;
361                         }
362                         if (tmp)
363                                 PCHAR('>');
364                         break;
365                 case 'c':
366                         PCHAR(va_arg(ap, int));
367                         break;
368                 case 'D':
369                         up = va_arg(ap, u_char *);
370                         p = va_arg(ap, char *);
371                         if (!width)
372                                 width = 16;
373                         while(width--) {
374                                 PCHAR(hex2ascii(*up >> 4));
375                                 PCHAR(hex2ascii(*up & 0x0f));
376                                 up++;
377                                 if (width)
378                                         for (q=p;*q;q++)
379                                                 PCHAR(*q);
380                         }
381                         break;
382                 case 'd':
383                 case 'i':
384                         base = 10;
385                         sign = 1;
386                         goto handle_sign;
387                 case 'h':
388                         if (hflag) {
389                                 hflag = 0;
390                                 cflag = 1;
391                         } else
392                                 hflag = 1;
393                         goto reswitch;
394                 case 'j':
395                         jflag = 1;
396                         goto reswitch;
397                 case 'l':
398                         if (lflag) {
399                                 lflag = 0;
400                                 qflag = 1;
401                         } else
402                                 lflag = 1;
403                         goto reswitch;
404                 case 'n':
405                         if (jflag)
406                                 *(va_arg(ap, intmax_t *)) = retval;
407                         else if (qflag)
408                                 *(va_arg(ap, quad_t *)) = retval;
409                         else if (lflag)
410                                 *(va_arg(ap, long *)) = retval;
411                         else if (zflag)
412                                 *(va_arg(ap, size_t *)) = retval;
413                         else if (hflag)
414                                 *(va_arg(ap, short *)) = retval;
415                         else if (cflag)
416                                 *(va_arg(ap, char *)) = retval;
417                         else
418                                 *(va_arg(ap, int *)) = retval;
419                         break;
420                 case 'o':
421                         base = 8;
422                         goto handle_nosign;
423                 case 'p':
424                         base = 16;
425                         sharpflag = (width == 0);
426                         sign = 0;
427                         num = (uintptr_t)va_arg(ap, void *);
428                         goto number;
429                 case 'q':
430                         qflag = 1;
431                         goto reswitch;
432                 case 'r':
433                         base = radix;
434                         if (sign)
435                                 goto handle_sign;
436                         goto handle_nosign;
437                 case 's':
438                         p = va_arg(ap, char *);
439                         if (p == NULL)
440                                 p = "(null)";
441                         if (!dot)
442                                 n = strlen (p);
443                         else
444                                 for (n = 0; n < dwidth && p[n]; n++)
445                                         continue;
446
447                         width -= n;
448
449                         if (!ladjust && width > 0)
450                                 while (width--)
451                                         PCHAR(padc);
452                         while (n--)
453                                 PCHAR(*p++);
454                         if (ladjust && width > 0)
455                                 while (width--)
456                                         PCHAR(padc);
457                         break;
458                 case 'S':       /* Assume console can cope with wide chars */
459                         for (S = va_arg(ap, uint16_t *); *S != 0; S++)
460                                 PCHAR(*S);
461                         break;
462                 case 't':
463                         tflag = 1;
464                         goto reswitch;
465                 case 'u':
466                         base = 10;
467                         goto handle_nosign;
468                 case 'X':
469                         upper = 1;
470                 case 'x':
471                         base = 16;
472                         goto handle_nosign;
473                 case 'y':
474                         base = 16;
475                         sign = 1;
476                         goto handle_sign;
477                 case 'z':
478                         zflag = 1;
479                         goto reswitch;
480 handle_nosign:
481                         sign = 0;
482                         if (jflag)
483                                 num = va_arg(ap, uintmax_t);
484                         else if (qflag)
485                                 num = va_arg(ap, u_quad_t);
486                         else if (tflag)
487                                 num = va_arg(ap, ptrdiff_t);
488                         else if (lflag)
489                                 num = va_arg(ap, u_long);
490                         else if (zflag)
491                                 num = va_arg(ap, size_t);
492                         else if (hflag)
493                                 num = (u_short)va_arg(ap, int);
494                         else if (cflag)
495                                 num = (u_char)va_arg(ap, int);
496                         else
497                                 num = va_arg(ap, u_int);
498                         goto number;
499 handle_sign:
500                         if (jflag)
501                                 num = va_arg(ap, intmax_t);
502                         else if (qflag)
503                                 num = va_arg(ap, quad_t);
504                         else if (tflag)
505                                 num = va_arg(ap, ptrdiff_t);
506                         else if (lflag)
507                                 num = va_arg(ap, long);
508                         else if (zflag)
509                                 num = va_arg(ap, ssize_t);
510                         else if (hflag)
511                                 num = (short)va_arg(ap, int);
512                         else if (cflag)
513                                 num = (char)va_arg(ap, int);
514                         else
515                                 num = va_arg(ap, int);
516 number:
517                         if (sign && (intmax_t)num < 0) {
518                                 neg = 1;
519                                 num = -(intmax_t)num;
520                         }
521                         p = ksprintn(nbuf, num, base, &n, upper);
522                         tmp = 0;
523                         if (sharpflag && num != 0) {
524                                 if (base == 8)
525                                         tmp++;
526                                 else if (base == 16)
527                                         tmp += 2;
528                         }
529                         if (neg)
530                                 tmp++;
531
532                         if (!ladjust && padc == '0')
533                                 dwidth = width - tmp;
534                         width -= tmp + imax(dwidth, n);
535                         dwidth -= n;
536                         if (!ladjust)
537                                 while (width-- > 0)
538                                         PCHAR(' ');
539                         if (neg)
540                                 PCHAR('-');
541                         if (sharpflag && num != 0) {
542                                 if (base == 8) {
543                                         PCHAR('0');
544                                 } else if (base == 16) {
545                                         PCHAR('0');
546                                         PCHAR('x');
547                                 }
548                         }
549                         while (dwidth-- > 0)
550                                 PCHAR('0');
551
552                         while (*p)
553                                 PCHAR(*p--);
554
555                         if (ladjust)
556                                 while (width-- > 0)
557                                         PCHAR(' ');
558
559                         break;
560                 default:
561                         while (percent < fmt)
562                                 PCHAR(*percent++);
563                         /*
564                          * Since we ignore a formatting argument it is no
565                          * longer safe to obey the remaining formatting
566                          * arguments as the arguments will no longer match
567                          * the format specs.
568                          */
569                         stop = 1;
570                         break;
571                 }
572         }
573 #undef PCHAR
574 }