xref: /f-stack/lib/ff_subr_prf.c (revision ebf5cedb)
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  * 4. 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  *  @(#)subr_prf.c  8.3 (Berkeley) 1/21/94
35  */
36 
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39 
40 #include "opt_ddb.h"
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/lock.h>
45 #include <sys/kdb.h>
46 #include <sys/mutex.h>
47 #include <sys/sx.h>
48 #include <sys/kernel.h>
49 #include <sys/msgbuf.h>
50 #include <sys/malloc.h>
51 #include <sys/priv.h>
52 #include <sys/proc.h>
53 #include <sys/stddef.h>
54 #include <sys/sysctl.h>
55 #include <sys/tty.h>
56 #include <sys/syslog.h>
57 #include <sys/cons.h>
58 #include <sys/uio.h>
59 #include <sys/ctype.h>
60 
61 #ifdef DDB
62 #include <ddb/ddb.h>
63 #endif
64 
65 /*
66  * Note that stdarg.h and the ANSI style va_start macro is used for both
67  * ANSI and traditional C compilers.
68  */
69 #include <machine/stdarg.h>
70 
71 #define TOCONS    0x01
72 #define TOTTY    0x02
73 #define TOLOG    0x04
74 
75 /* Max number conversion buffer length: a u_quad_t in base 2, plus NUL byte. */
76 #define MAXNBUF    (sizeof(intmax_t) * NBBY + 1)
77 
78 struct putchar_arg {
79     int flags;
80     int pri;
81     struct tty *tty;
82     char *p_bufr;
83     size_t n_bufr;
84     char *p_next;
85     size_t remain;
86 };
87 
88 struct snprintf_arg {
89     char *str;
90     size_t remain;
91 };
92 
93 int putchar(int c);
94 int puts(const char *str);
95 
96 
97 static char *ksprintn(char *nbuf, uintmax_t num, int base, int *len, int upper);
98 
99 /*
100  * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
101  * order; return an optional length and a pointer to the last character
102  * written in the buffer (i.e., the first character of the string).
103  * The buffer pointed to by `nbuf' must have length >= MAXNBUF.
104  */
105 static char *
106 ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
107 {
108     char *p, c;
109 
110     p = nbuf;
111     *p = '\0';
112     do {
113         c = hex2ascii(num % base);
114         *++p = upper ? toupper(c) : c;
115     } while (num /= base);
116     if (lenp)
117         *lenp = p - nbuf;
118     return (p);
119 }
120 
121 static void
122 putbuf(int c, struct putchar_arg *ap)
123 {
124     /* Check if no console output buffer was provided. */
125     if (ap->p_bufr == NULL) {
126         /* Output direct to the console. */
127         if (ap->flags & TOCONS)
128             putchar(c);
129 
130     } else {
131         /* Buffer the character: */
132         *ap->p_next++ = c;
133         ap->remain--;
134 
135         /* Always leave the buffer zero terminated. */
136         *ap->p_next = '\0';
137 
138         /* Check if the buffer needs to be flushed. */
139         if (ap->remain == 2 || c == '\n') {
140 
141             if (ap->flags & TOCONS) {
142                 puts(ap->p_bufr);
143             }
144 
145             ap->p_next = ap->p_bufr;
146             ap->remain = ap->n_bufr;
147             *ap->p_next = '\0';
148         }
149 
150         /*
151          * Since we fill the buffer up one character at a time,
152          * this should not happen.  We should always catch it when
153          * ap->remain == 2 (if not sooner due to a newline), flush
154          * the buffer and move on.  One way this could happen is
155          * if someone sets PRINTF_BUFR_SIZE to 1 or something
156          * similarly silly.
157          */
158         KASSERT(ap->remain > 2, ("Bad buffer logic, remain = %zd",
159             ap->remain));
160     }
161 }
162 
163 /*
164  * Print a character on console or users terminal.  If destination is
165  * the console then the last bunch of characters are saved in msgbuf for
166  * inspection later.
167  */
168 static void
169 kputchar(int c, void *arg)
170 {
171     struct putchar_arg *ap = (struct putchar_arg*) arg;
172     int flags = ap->flags;
173     int putbuf_done = 0;
174 
175     if (flags & TOCONS) {
176         putbuf(c, ap);
177         putbuf_done = 1;
178     }
179 
180     if ((flags & TOLOG) && (putbuf_done == 0)) {
181         if (c != '\0')
182             putbuf(c, ap);
183     }
184 }
185 
186 /*
187  * Scaled down version of printf(3).
188  *
189  * Two additional formats:
190  *
191  * The format %b is supported to decode error registers.
192  * Its usage is:
193  *
194  *    printf("reg=%b\n", regval, "<base><arg>*");
195  *
196  * where <base> is the output base expressed as a control character, e.g.
197  * \10 gives octal; \20 gives hex.  Each arg is a sequence of characters,
198  * the first of which gives the bit number to be inspected (origin 1), and
199  * the next characters (up to a control character, i.e. a character <= 32),
200  * give the name of the register.  Thus:
201  *
202  *    kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
203  *
204  * would produce output:
205  *
206  *    reg=3<BITTWO,BITONE>
207  *
208  * XXX:  %D  -- Hexdump, takes pointer and separator string:
209  *        ("%6D", ptr, ":")   -> XX:XX:XX:XX:XX:XX
210  *        ("%*D", len, ptr, " " -> XX XX XX XX ...
211  */
212 int
213 kvprintf(char const *fmt, void (*func)(int, void*), void *arg, int radix, va_list ap)
214 {
215 #define PCHAR(c) {int cc=(c); if (func) (*func)(cc,arg); else *d++ = cc; retval++; }
216     char nbuf[MAXNBUF];
217     char *d;
218     const char *p, *percent, *q;
219     u_char *up;
220     int ch, n;
221     uintmax_t num;
222     int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
223     int cflag, hflag, jflag, tflag, zflag;
224     int dwidth, upper;
225     char padc;
226     int stop = 0, retval = 0;
227 
228     num = 0;
229     if (!func)
230         d = (char *) arg;
231     else
232         d = NULL;
233 
234     if (fmt == NULL)
235         fmt = "(fmt null)\n";
236 
237     if (radix < 2 || radix > 36)
238         radix = 10;
239 
240     for (;;) {
241         padc = ' ';
242         width = 0;
243         while ((ch = (u_char)*fmt++) != '%' || stop) {
244             if (ch == '\0')
245                 return (retval);
246             PCHAR(ch);
247         }
248         percent = fmt - 1;
249         qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
250         sign = 0; dot = 0; dwidth = 0; upper = 0;
251         cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
252 reswitch:    switch (ch = (u_char)*fmt++) {
253         case '.':
254             dot = 1;
255             goto reswitch;
256         case '#':
257             sharpflag = 1;
258             goto reswitch;
259         case '+':
260             sign = 1;
261             goto reswitch;
262         case '-':
263             ladjust = 1;
264             goto reswitch;
265         case '%':
266             PCHAR(ch);
267             break;
268         case '*':
269             if (!dot) {
270                 width = va_arg(ap, int);
271                 if (width < 0) {
272                     ladjust = !ladjust;
273                     width = -width;
274                 }
275             } else {
276                 dwidth = va_arg(ap, int);
277             }
278             goto reswitch;
279         case '0':
280             if (!dot) {
281                 padc = '0';
282                 goto reswitch;
283             }
284         case '1': case '2': case '3': case '4':
285         case '5': case '6': case '7': case '8': case '9':
286                 for (n = 0;; ++fmt) {
287                     n = n * 10 + ch - '0';
288                     ch = *fmt;
289                     if (ch < '0' || ch > '9')
290                         break;
291                 }
292             if (dot)
293                 dwidth = n;
294             else
295                 width = n;
296             goto reswitch;
297         case 'b':
298             num = (u_int)va_arg(ap, int);
299             p = va_arg(ap, char *);
300             for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
301                 PCHAR(*q--);
302 
303             if (num == 0)
304                 break;
305 
306             for (tmp = 0; *p;) {
307                 n = *p++;
308                 if (num & (1 << (n - 1))) {
309                     PCHAR(tmp ? ',' : '<');
310                     for (; (n = *p) > ' '; ++p)
311                         PCHAR(n);
312                     tmp = 1;
313                 } else
314                     for (; *p > ' '; ++p)
315                         continue;
316             }
317             if (tmp)
318                 PCHAR('>');
319             break;
320         case 'c':
321             PCHAR(va_arg(ap, int));
322             break;
323         case 'D':
324             up = va_arg(ap, u_char *);
325             p = va_arg(ap, char *);
326             if (!width)
327                 width = 16;
328             while(width--) {
329                 PCHAR(hex2ascii(*up >> 4));
330                 PCHAR(hex2ascii(*up & 0x0f));
331                 up++;
332                 if (width)
333                     for (q=p;*q;q++)
334                         PCHAR(*q);
335             }
336             break;
337         case 'd':
338         case 'i':
339             base = 10;
340             sign = 1;
341             goto handle_sign;
342         case 'h':
343             if (hflag) {
344                 hflag = 0;
345                 cflag = 1;
346             } else
347                 hflag = 1;
348             goto reswitch;
349         case 'j':
350             jflag = 1;
351             goto reswitch;
352         case 'l':
353             if (lflag) {
354                 lflag = 0;
355                 qflag = 1;
356             } else
357                 lflag = 1;
358             goto reswitch;
359         case 'n':
360             if (jflag)
361                 *(va_arg(ap, intmax_t *)) = retval;
362             else if (qflag)
363                 *(va_arg(ap, quad_t *)) = retval;
364             else if (lflag)
365                 *(va_arg(ap, long *)) = retval;
366             else if (zflag)
367                 *(va_arg(ap, size_t *)) = retval;
368             else if (hflag)
369                 *(va_arg(ap, short *)) = retval;
370             else if (cflag)
371                 *(va_arg(ap, char *)) = retval;
372             else
373                 *(va_arg(ap, int *)) = retval;
374             break;
375         case 'o':
376             base = 8;
377             goto handle_nosign;
378         case 'p':
379             base = 16;
380             sharpflag = (width == 0);
381             sign = 0;
382             num = (uintptr_t)va_arg(ap, void *);
383             goto number;
384         case 'q':
385             qflag = 1;
386             goto reswitch;
387         case 'r':
388             base = radix;
389             if (sign)
390                 goto handle_sign;
391             goto handle_nosign;
392         case 's':
393             p = va_arg(ap, char *);
394             if (p == NULL)
395                 p = "(null)";
396             if (!dot)
397                 n = strlen (p);
398             else
399                 for (n = 0; n < dwidth && p[n]; n++)
400                     continue;
401 
402             width -= n;
403 
404             if (!ladjust && width > 0)
405                 while (width--)
406                     PCHAR(padc);
407             while (n--)
408                 PCHAR(*p++);
409             if (ladjust && width > 0)
410                 while (width--)
411                     PCHAR(padc);
412             break;
413         case 't':
414             tflag = 1;
415             goto reswitch;
416         case 'u':
417             base = 10;
418             goto handle_nosign;
419         case 'X':
420             upper = 1;
421         case 'x':
422             base = 16;
423             goto handle_nosign;
424         case 'y':
425             base = 16;
426             sign = 1;
427             goto handle_sign;
428         case 'z':
429             zflag = 1;
430             goto reswitch;
431 handle_nosign:
432             sign = 0;
433             if (jflag)
434                 num = va_arg(ap, uintmax_t);
435             else if (qflag)
436                 num = va_arg(ap, u_quad_t);
437             else if (tflag)
438                 num = va_arg(ap, ptrdiff_t);
439             else if (lflag)
440                 num = va_arg(ap, u_long);
441             else if (zflag)
442                 num = va_arg(ap, size_t);
443             else if (hflag)
444                 num = (u_short)va_arg(ap, int);
445             else if (cflag)
446                 num = (u_char)va_arg(ap, int);
447             else
448                 num = va_arg(ap, u_int);
449             goto number;
450 handle_sign:
451             if (jflag)
452                 num = va_arg(ap, intmax_t);
453             else if (qflag)
454                 num = va_arg(ap, quad_t);
455             else if (tflag)
456                 num = va_arg(ap, ptrdiff_t);
457             else if (lflag)
458                 num = va_arg(ap, long);
459             else if (zflag)
460                 num = va_arg(ap, ssize_t);
461             else if (hflag)
462                 num = (short)va_arg(ap, int);
463             else if (cflag)
464                 num = (char)va_arg(ap, int);
465             else
466                 num = va_arg(ap, int);
467 number:
468             if (sign && (intmax_t)num < 0) {
469                 neg = 1;
470                 num = -(intmax_t)num;
471             }
472             p = ksprintn(nbuf, num, base, &n, upper);
473             tmp = 0;
474             if (sharpflag && num != 0) {
475                 if (base == 8)
476                     tmp++;
477                 else if (base == 16)
478                     tmp += 2;
479             }
480             if (neg)
481                 tmp++;
482 
483             if (!ladjust && padc == '0')
484                 dwidth = width - tmp;
485             width -= tmp + imax(dwidth, n);
486             dwidth -= n;
487             if (!ladjust)
488                 while (width-- > 0)
489                     PCHAR(' ');
490             if (neg)
491                 PCHAR('-');
492             if (sharpflag && num != 0) {
493                 if (base == 8) {
494                     PCHAR('0');
495                 } else if (base == 16) {
496                     PCHAR('0');
497                     PCHAR('x');
498                 }
499             }
500             while (dwidth-- > 0)
501                 PCHAR('0');
502 
503             while (*p)
504                 PCHAR(*p--);
505 
506             if (ladjust)
507                 while (width-- > 0)
508                     PCHAR(' ');
509 
510             break;
511         default:
512             while (percent < fmt)
513                 PCHAR(*percent++);
514             /*
515              * Since we ignore an formatting argument it is no
516              * longer safe to obey the remaining formatting
517              * arguments as the arguments will no longer match
518              * the format specs.
519              */
520             stop = 1;
521             break;
522         }
523     }
524 #undef PCHAR
525 }
526 
527 int
528 printf(const char *fmt, ...)
529 {
530     va_list ap;
531     int retval;
532 
533     va_start(ap, fmt);
534     retval = vprintf(fmt, ap);
535     va_end(ap);
536 
537     return (retval);
538 }
539 
540 int
541 vprintf(const char *fmt, va_list ap)
542 {
543     struct putchar_arg pca;
544     int retval;
545 #ifdef PRINTF_BUFR_SIZE
546     char bufr[PRINTF_BUFR_SIZE];
547 #endif
548 
549     pca.tty = NULL;
550     pca.flags = TOCONS | TOLOG;
551     pca.pri = -1;
552 #ifdef PRINTF_BUFR_SIZE
553     pca.p_bufr = bufr;
554     pca.p_next = pca.p_bufr;
555     pca.n_bufr = sizeof(bufr);
556     pca.remain = sizeof(bufr);
557     *pca.p_next = '\0';
558 #else
559     /* Don't buffer console output. */
560     pca.p_bufr = NULL;
561 #endif
562 
563     retval = kvprintf(fmt, kputchar, &pca, 10, ap);
564 
565 #ifdef PRINTF_BUFR_SIZE
566     /* Write any buffered console/log output: */
567     if (*pca.p_bufr != '\0') {
568         cnputs(pca.p_bufr);
569         msglogstr(pca.p_bufr, pca.pri, /*filter_cr*/ 1);
570     }
571 #endif
572 
573     return (retval);
574 }
575