1b0104773SPascal Brand /* 2b0104773SPascal Brand * Imported from NetBSD 5.1 with modifications to make it a vsnprintf(3) 3b0104773SPascal Brand * function 4b0104773SPascal Brand */ 5b0104773SPascal Brand 6*d8516202SJens Wiklander /* $NetBSD: subr_prf.c,v 1.156 2014/08/15 11:05:35 apb Exp $ */ 7b0104773SPascal Brand 8b0104773SPascal Brand /*- 9b0104773SPascal Brand * Copyright (c) 1986, 1988, 1991, 1993 10b0104773SPascal Brand * The Regents of the University of California. All rights reserved. 11b0104773SPascal Brand * (c) UNIX System Laboratories, Inc. 12b0104773SPascal Brand * All or some portions of this file are derived from material licensed 13b0104773SPascal Brand * to the University of California by American Telephone and Telegraph 14b0104773SPascal Brand * Co. or Unix System Laboratories, Inc. and are reproduced herein with 15b0104773SPascal Brand * the permission of UNIX System Laboratories, Inc. 16b0104773SPascal Brand * 17b0104773SPascal Brand * Redistribution and use in source and binary forms, with or without 18b0104773SPascal Brand * modification, are permitted provided that the following conditions 19b0104773SPascal Brand * are met: 20b0104773SPascal Brand * 1. Redistributions of source code must retain the above copyright 21b0104773SPascal Brand * notice, this list of conditions and the following disclaimer. 22b0104773SPascal Brand * 2. Redistributions in binary form must reproduce the above copyright 23b0104773SPascal Brand * notice, this list of conditions and the following disclaimer in the 24b0104773SPascal Brand * documentation and/or other materials provided with the distribution. 25b0104773SPascal Brand * 3. Neither the name of the University nor the names of its contributors 26b0104773SPascal Brand * may be used to endorse or promote products derived from this software 27b0104773SPascal Brand * without specific prior written permission. 28b0104773SPascal Brand * 29b0104773SPascal Brand * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30b0104773SPascal Brand * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31b0104773SPascal Brand * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32b0104773SPascal Brand * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33b0104773SPascal Brand * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34b0104773SPascal Brand * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35b0104773SPascal Brand * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36b0104773SPascal Brand * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37b0104773SPascal Brand * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38b0104773SPascal Brand * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39b0104773SPascal Brand * SUCH DAMAGE. 40b0104773SPascal Brand * 41b0104773SPascal Brand * @(#)subr_prf.c 8.4 (Berkeley) 5/4/95 42b0104773SPascal Brand */ 43b0104773SPascal Brand 44b0104773SPascal Brand #include <unistd.h> 45b0104773SPascal Brand #include <stdint.h> 46b0104773SPascal Brand #include <string.h> 47b0104773SPascal Brand #include <stdio.h> 48b0104773SPascal Brand #include <stdarg.h> 49b0104773SPascal Brand 50b0104773SPascal Brand /* flags for kprintf */ 51b0104773SPascal Brand #define TOCONS 0x0001 /* to the console */ 52b0104773SPascal Brand #define TOTTY 0x0002 /* to the process' tty */ 53b0104773SPascal Brand #define TOLOG 0x0004 /* to the kernel message buffer */ 54b0104773SPascal Brand #define TOBUFONLY 0x0008 /* to the buffer (only) [for snprintf] */ 55b0104773SPascal Brand #define TODDB 0x0010 /* to ddb console */ 56b0104773SPascal Brand #define NOLOCK 0x1000 /* don't acquire a tty lock */ 57b0104773SPascal Brand 58b0104773SPascal Brand /* max size buffer kprintf needs to print quad_t [size in base 8 + \0] */ 59b0104773SPascal Brand #define KPRINTF_BUFSIZE 23 /*(8 * 8 / 3 + 2) */ 60b0104773SPascal Brand 61b0104773SPascal Brand /* 62b0104773SPascal Brand * The following macro is used to remove const cast-away warnings 63b0104773SPascal Brand * from gcc -Wcast-qual; it should be used with caution because it 64b0104773SPascal Brand * can hide valid errors; in particular most valid uses are in 65b0104773SPascal Brand * situations where the API requires it, not to cast away string 66b0104773SPascal Brand * constants. We don't use *intptr_t on purpose here and we are 67b0104773SPascal Brand * explicit about unsigned long so that we don't have additional 68b0104773SPascal Brand * dependencies. 69b0104773SPascal Brand */ 70b0104773SPascal Brand #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) 71b0104773SPascal Brand 72b0104773SPascal Brand #define putchar(c, flags, tty) \ 73b0104773SPascal Brand do { (void)(c); (void)(flags); (void)(tty); } while(0) 74b0104773SPascal Brand 75b0104773SPascal Brand static int kprintf(const char *fmt0, int oflags, void *vp, char *sbuf, 76b0104773SPascal Brand va_list ap); 77b0104773SPascal Brand 78b0104773SPascal Brand static const char hexdigits[] = "0123456789abcdef"; 79b0104773SPascal Brand static const char HEXDIGITS[] = "0123456789ABCDEF"; 80b0104773SPascal Brand 81b0104773SPascal Brand /* 82b0104773SPascal Brand * snprintf: print a message to a buffer 83b0104773SPascal Brand */ 84*d8516202SJens Wiklander int 85*d8516202SJens Wiklander snprintf(char *bf, size_t size, const char *fmt, ...) 86b0104773SPascal Brand { 87b0104773SPascal Brand int retval; 88b0104773SPascal Brand va_list ap; 89b0104773SPascal Brand 90b0104773SPascal Brand va_start(ap, fmt); 91*d8516202SJens Wiklander retval = vsnprintf(bf, size, fmt, ap); 92b0104773SPascal Brand va_end(ap); 93*d8516202SJens Wiklander 94b0104773SPascal Brand return retval; 95b0104773SPascal Brand } 96b0104773SPascal Brand 97b0104773SPascal Brand /* 98*d8516202SJens Wiklander * vsnprintf: print a message to a buffer [already have va_list] 99b0104773SPascal Brand */ 100*d8516202SJens Wiklander int 101*d8516202SJens Wiklander vsnprintf(char *bf, size_t size, const char *fmt, va_list ap) 102b0104773SPascal Brand { 103b0104773SPascal Brand int retval; 104b0104773SPascal Brand char *p; 105b0104773SPascal Brand 106*d8516202SJens Wiklander p = bf + size; 107b0104773SPascal Brand retval = kprintf(fmt, TOBUFONLY, &p, bf, ap); 108*d8516202SJens Wiklander if (bf && size > 0) { 109*d8516202SJens Wiklander /* nul terminate */ 110*d8516202SJens Wiklander if (size <= (size_t)retval) 111*d8516202SJens Wiklander bf[size - 1] = '\0'; 112*d8516202SJens Wiklander else 113*d8516202SJens Wiklander bf[retval] = '\0'; 114*d8516202SJens Wiklander } 115b0104773SPascal Brand return retval; 116b0104773SPascal Brand } 117b0104773SPascal Brand 118b0104773SPascal Brand /* 119b0104773SPascal Brand * kprintf: scaled down version of printf(3). 120b0104773SPascal Brand * 121b0104773SPascal Brand * this version based on vfprintf() from libc which was derived from 122b0104773SPascal Brand * software contributed to Berkeley by Chris Torek. 123b0104773SPascal Brand * 124b0104773SPascal Brand */ 125b0104773SPascal Brand 126b0104773SPascal Brand /* 127b0104773SPascal Brand * macros for converting digits to letters and vice versa 128b0104773SPascal Brand */ 129b0104773SPascal Brand #define to_digit(c) ((c) - '0') 130b0104773SPascal Brand #define is_digit(c) ((unsigned)to_digit(c) <= 9) 131b0104773SPascal Brand #define to_char(n) ((n) + '0') 132b0104773SPascal Brand 133b0104773SPascal Brand /* 134b0104773SPascal Brand * flags used during conversion. 135b0104773SPascal Brand */ 136b0104773SPascal Brand #define ALT 0x001 /* alternate form */ 137b0104773SPascal Brand #define HEXPREFIX 0x002 /* add 0x or 0X prefix */ 138b0104773SPascal Brand #define LADJUST 0x004 /* left adjustment */ 139b0104773SPascal Brand #define LONGDBL 0x008 /* long double; unimplemented */ 140b0104773SPascal Brand #define LONGINT 0x010 /* long integer */ 141*d8516202SJens Wiklander #define QUADINT 0x020 /* quad integer */ 142b0104773SPascal Brand #define SHORTINT 0x040 /* short integer */ 143b0104773SPascal Brand #define MAXINT 0x080 /* intmax_t */ 144b0104773SPascal Brand #define PTRINT 0x100 /* intptr_t */ 145b0104773SPascal Brand #define SIZEINT 0x200 /* size_t */ 146b0104773SPascal Brand #define ZEROPAD 0x400 /* zero (as opposed to blank) pad */ 147b0104773SPascal Brand #define FPT 0x800 /* Floating point number */ 148b0104773SPascal Brand 149b0104773SPascal Brand /* 150b0104773SPascal Brand * To extend shorts properly, we need both signed and unsigned 151b0104773SPascal Brand * argument extraction methods. 152b0104773SPascal Brand */ 153b0104773SPascal Brand #define SARG() \ 154b0104773SPascal Brand (flags&MAXINT ? va_arg(ap, intmax_t) : \ 155b0104773SPascal Brand flags&PTRINT ? va_arg(ap, intptr_t) : \ 156b0104773SPascal Brand flags&SIZEINT ? va_arg(ap, ssize_t) : /* XXX */ \ 157*d8516202SJens Wiklander flags&QUADINT ? va_arg(ap, int64_t) : \ 158b0104773SPascal Brand flags&LONGINT ? va_arg(ap, long) : \ 159b0104773SPascal Brand flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ 160b0104773SPascal Brand (long)va_arg(ap, int)) 161b0104773SPascal Brand #define UARG() \ 162b0104773SPascal Brand (flags&MAXINT ? va_arg(ap, uintmax_t) : \ 163b0104773SPascal Brand flags&PTRINT ? va_arg(ap, uintptr_t) : \ 164b0104773SPascal Brand flags&SIZEINT ? va_arg(ap, size_t) : \ 165*d8516202SJens Wiklander flags&QUADINT ? va_arg(ap, uint64_t) : \ 166b0104773SPascal Brand flags&LONGINT ? va_arg(ap, unsigned long) : \ 167b0104773SPascal Brand flags&SHORTINT ? (unsigned long)(unsigned short)va_arg(ap, int) : \ 168*d8516202SJens Wiklander (unsigned long)va_arg(ap, unsigned int)) 169b0104773SPascal Brand 170b0104773SPascal Brand #define KPRINTF_PUTCHAR(C) { \ 171b0104773SPascal Brand if (oflags == TOBUFONLY) { \ 172*d8516202SJens Wiklander if (sbuf && ((vp == NULL) || (sbuf < tailp))) \ 173b0104773SPascal Brand *sbuf++ = (C); \ 174b0104773SPascal Brand } else { \ 175*d8516202SJens Wiklander putchar((C), oflags, vp); \ 176b0104773SPascal Brand } \ 177b0104773SPascal Brand } 178b0104773SPascal Brand 179b0104773SPascal Brand /* 180b0104773SPascal Brand * Guts of kernel printf. Note, we already expect to be in a mutex! 181b0104773SPascal Brand */ 182b0104773SPascal Brand static int 183b0104773SPascal Brand kprintf(const char *fmt0, int oflags, void *vp, char *sbuf, va_list ap) 184b0104773SPascal Brand { 185b0104773SPascal Brand const char *fmt; /* format string */ 186b0104773SPascal Brand int ch; /* character from fmt */ 187b0104773SPascal Brand int n; /* handy integer (short term usage) */ 188b0104773SPascal Brand char *cp; /* handy char pointer (short term usage) */ 189b0104773SPascal Brand int flags; /* flags as above */ 190b0104773SPascal Brand int ret; /* return value accumulator */ 191b0104773SPascal Brand int width; /* width from format (%8d), or 0 */ 192b0104773SPascal Brand int prec; /* precision from format (%.3d), or -1 */ 193b0104773SPascal Brand char sign; /* sign prefix (' ', '+', '-', or \0) */ 194b0104773SPascal Brand 195*d8516202SJens Wiklander uint64_t _uquad; /* integer arguments %[diouxX] */ 196b0104773SPascal Brand enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ 197b0104773SPascal Brand int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 198b0104773SPascal Brand int realsz; /* field size expanded by dprec */ 199b0104773SPascal Brand int size; /* size of converted field or string */ 200b0104773SPascal Brand const char *xdigs; /* digits for [xX] conversion */ 201b0104773SPascal Brand char bf[KPRINTF_BUFSIZE]; /* space for %c, %[diouxX] */ 202b0104773SPascal Brand char *tailp; /* tail pointer for snprintf */ 203b0104773SPascal Brand 204b0104773SPascal Brand if (oflags == TOBUFONLY && (vp != NULL)) 205b0104773SPascal Brand tailp = *(char **)vp; 206*d8516202SJens Wiklander else 207*d8516202SJens Wiklander tailp = NULL; 208b0104773SPascal Brand 209b0104773SPascal Brand cp = NULL; /* XXX: shutup gcc */ 210b0104773SPascal Brand size = 0; /* XXX: shutup gcc */ 211b0104773SPascal Brand 212b0104773SPascal Brand fmt = fmt0; 213b0104773SPascal Brand ret = 0; 214b0104773SPascal Brand 215b0104773SPascal Brand xdigs = NULL; /* XXX: shut up gcc warning */ 216b0104773SPascal Brand 217b0104773SPascal Brand /* 218b0104773SPascal Brand * Scan the format for conversions (`%' character). 219b0104773SPascal Brand */ 220b0104773SPascal Brand for (;;) { 221*d8516202SJens Wiklander for (; *fmt != '%' && *fmt; fmt++) { 222b0104773SPascal Brand ret++; 223*d8516202SJens Wiklander KPRINTF_PUTCHAR(*fmt); 224b0104773SPascal Brand } 225b0104773SPascal Brand if (*fmt == 0) 226b0104773SPascal Brand goto done; 227b0104773SPascal Brand 228b0104773SPascal Brand fmt++; /* skip over '%' */ 229b0104773SPascal Brand 230b0104773SPascal Brand flags = 0; 231b0104773SPascal Brand dprec = 0; 232b0104773SPascal Brand width = 0; 233b0104773SPascal Brand prec = -1; 234b0104773SPascal Brand sign = '\0'; 235b0104773SPascal Brand 236b0104773SPascal Brand rflag: ch = *fmt++; 237b0104773SPascal Brand reswitch: switch (ch) { 238b0104773SPascal Brand case ' ': 239b0104773SPascal Brand /* 240b0104773SPascal Brand * ``If the space and + flags both appear, the space 241b0104773SPascal Brand * flag will be ignored.'' 242b0104773SPascal Brand * -- ANSI X3J11 243b0104773SPascal Brand */ 244b0104773SPascal Brand if (!sign) 245b0104773SPascal Brand sign = ' '; 246b0104773SPascal Brand goto rflag; 247b0104773SPascal Brand case '#': 248b0104773SPascal Brand flags |= ALT; 249b0104773SPascal Brand goto rflag; 250b0104773SPascal Brand case '*': 251b0104773SPascal Brand /* 252b0104773SPascal Brand * ``A negative field width argument is taken as a 253b0104773SPascal Brand * - flag followed by a positive field width.'' 254b0104773SPascal Brand * -- ANSI X3J11 255b0104773SPascal Brand * They don't exclude field widths read from args. 256b0104773SPascal Brand */ 257b0104773SPascal Brand if ((width = va_arg(ap, int)) >= 0) 258b0104773SPascal Brand goto rflag; 259b0104773SPascal Brand width = -width; 260b0104773SPascal Brand /* FALLTHROUGH */ 261b0104773SPascal Brand case '-': 262b0104773SPascal Brand flags |= LADJUST; 263b0104773SPascal Brand goto rflag; 264b0104773SPascal Brand case '+': 265b0104773SPascal Brand sign = '+'; 266b0104773SPascal Brand goto rflag; 267b0104773SPascal Brand case '.': 268b0104773SPascal Brand if ((ch = *fmt++) == '*') { 269b0104773SPascal Brand n = va_arg(ap, int); 270b0104773SPascal Brand prec = n < 0 ? -1 : n; 271b0104773SPascal Brand goto rflag; 272b0104773SPascal Brand } 273b0104773SPascal Brand n = 0; 274b0104773SPascal Brand while (is_digit(ch)) { 275b0104773SPascal Brand n = 10 * n + to_digit(ch); 276b0104773SPascal Brand ch = *fmt++; 277b0104773SPascal Brand } 278b0104773SPascal Brand prec = n < 0 ? -1 : n; 279b0104773SPascal Brand goto reswitch; 280b0104773SPascal Brand case '0': 281b0104773SPascal Brand /* 282b0104773SPascal Brand * ``Note that 0 is taken as a flag, not as the 283b0104773SPascal Brand * beginning of a field width.'' 284b0104773SPascal Brand * -- ANSI X3J11 285b0104773SPascal Brand */ 286b0104773SPascal Brand flags |= ZEROPAD; 287b0104773SPascal Brand goto rflag; 288*d8516202SJens Wiklander case '1': case '2': case '3': case '4': 289*d8516202SJens Wiklander case '5': case '6': case '7': case '8': case '9': 290b0104773SPascal Brand n = 0; 291b0104773SPascal Brand do { 292b0104773SPascal Brand n = 10 * n + to_digit(ch); 293b0104773SPascal Brand ch = *fmt++; 294b0104773SPascal Brand } while (is_digit(ch)); 295b0104773SPascal Brand width = n; 296b0104773SPascal Brand goto reswitch; 297b0104773SPascal Brand case 'h': 298b0104773SPascal Brand flags |= SHORTINT; 299b0104773SPascal Brand goto rflag; 300b0104773SPascal Brand case 'j': 301b0104773SPascal Brand flags |= MAXINT; 302b0104773SPascal Brand goto rflag; 303b0104773SPascal Brand case 'l': 304*d8516202SJens Wiklander if (*fmt == 'l') { 305*d8516202SJens Wiklander fmt++; 306*d8516202SJens Wiklander flags |= QUADINT; 307*d8516202SJens Wiklander } else { 308b0104773SPascal Brand flags |= LONGINT; 309*d8516202SJens Wiklander } 310*d8516202SJens Wiklander goto rflag; 311*d8516202SJens Wiklander case 'q': 312*d8516202SJens Wiklander flags |= QUADINT; 313b0104773SPascal Brand goto rflag; 314b0104773SPascal Brand case 't': 315b0104773SPascal Brand flags |= PTRINT; 316b0104773SPascal Brand goto rflag; 317b0104773SPascal Brand case 'z': 318b0104773SPascal Brand flags |= SIZEINT; 319b0104773SPascal Brand goto rflag; 320b0104773SPascal Brand case 'c': 321b0104773SPascal Brand *(cp = bf) = va_arg(ap, int); 322b0104773SPascal Brand size = 1; 323b0104773SPascal Brand sign = '\0'; 324b0104773SPascal Brand break; 325b0104773SPascal Brand case 'D': 326b0104773SPascal Brand flags |= LONGINT; 327*d8516202SJens Wiklander /*FALLTHROUGH*/ 328*d8516202SJens Wiklander case 'd': 329b0104773SPascal Brand case 'i': 330b0104773SPascal Brand _uquad = SARG(); 331*d8516202SJens Wiklander if ((int64_t)_uquad < 0) { 332b0104773SPascal Brand _uquad = -_uquad; 333b0104773SPascal Brand sign = '-'; 334b0104773SPascal Brand } 335b0104773SPascal Brand base = DEC; 336b0104773SPascal Brand goto number; 337b0104773SPascal Brand case 'n': 338b0104773SPascal Brand if (flags & MAXINT) 339b0104773SPascal Brand *va_arg(ap, intmax_t *) = ret; 340b0104773SPascal Brand else if (flags & PTRINT) 341b0104773SPascal Brand *va_arg(ap, intptr_t *) = ret; 342b0104773SPascal Brand else if (flags & SIZEINT) 343b0104773SPascal Brand *va_arg(ap, ssize_t *) = ret; 344*d8516202SJens Wiklander else if (flags & QUADINT) 345*d8516202SJens Wiklander *va_arg(ap, int64_t *) = ret; 346b0104773SPascal Brand else if (flags & LONGINT) 347b0104773SPascal Brand *va_arg(ap, long *) = ret; 348b0104773SPascal Brand else if (flags & SHORTINT) 349b0104773SPascal Brand *va_arg(ap, short *) = ret; 350b0104773SPascal Brand else 351b0104773SPascal Brand *va_arg(ap, int *) = ret; 352b0104773SPascal Brand continue; /* no output */ 353b0104773SPascal Brand case 'O': 354b0104773SPascal Brand flags |= LONGINT; 355*d8516202SJens Wiklander /*FALLTHROUGH*/ 356*d8516202SJens Wiklander case 'o': 357b0104773SPascal Brand _uquad = UARG(); 358b0104773SPascal Brand base = OCT; 359b0104773SPascal Brand goto nosign; 360b0104773SPascal Brand case 'p': 361b0104773SPascal Brand /* 362b0104773SPascal Brand * ``The argument shall be a pointer to void. The 363b0104773SPascal Brand * value of the pointer is converted to a sequence 364b0104773SPascal Brand * of printable characters, in an implementation- 365b0104773SPascal Brand * defined manner.'' 366b0104773SPascal Brand * -- ANSI X3J11 367b0104773SPascal Brand */ 368b0104773SPascal Brand /* NOSTRICT */ 369b0104773SPascal Brand _uquad = (unsigned long)va_arg(ap, void *); 370b0104773SPascal Brand base = HEX; 371b0104773SPascal Brand xdigs = hexdigits; 372b0104773SPascal Brand flags |= HEXPREFIX; 373b0104773SPascal Brand ch = 'x'; 374b0104773SPascal Brand goto nosign; 375b0104773SPascal Brand case 's': 376b0104773SPascal Brand if ((cp = va_arg(ap, char *)) == NULL) 377*d8516202SJens Wiklander /*XXXUNCONST*/ 378*d8516202SJens Wiklander cp = __UNCONST("(null)"); 379b0104773SPascal Brand if (prec >= 0) { 380b0104773SPascal Brand /* 381b0104773SPascal Brand * can't use strlen; can only look for the 382b0104773SPascal Brand * NUL in the first `prec' characters, and 383b0104773SPascal Brand * strlen() will go further. 384b0104773SPascal Brand */ 385b0104773SPascal Brand char *p = memchr(cp, 0, prec); 386b0104773SPascal Brand 387b0104773SPascal Brand if (p != NULL) { 388b0104773SPascal Brand size = p - cp; 389b0104773SPascal Brand if (size > prec) 390b0104773SPascal Brand size = prec; 391*d8516202SJens Wiklander } else 392b0104773SPascal Brand size = prec; 393*d8516202SJens Wiklander } else 394b0104773SPascal Brand size = strlen(cp); 395b0104773SPascal Brand sign = '\0'; 396b0104773SPascal Brand break; 397b0104773SPascal Brand case 'U': 398b0104773SPascal Brand flags |= LONGINT; 399*d8516202SJens Wiklander /*FALLTHROUGH*/ 400*d8516202SJens Wiklander case 'u': 401b0104773SPascal Brand _uquad = UARG(); 402b0104773SPascal Brand base = DEC; 403b0104773SPascal Brand goto nosign; 404b0104773SPascal Brand case 'X': 405b0104773SPascal Brand xdigs = HEXDIGITS; 406b0104773SPascal Brand goto hex; 407b0104773SPascal Brand case 'x': 408b0104773SPascal Brand xdigs = hexdigits; 409b0104773SPascal Brand hex: _uquad = UARG(); 410b0104773SPascal Brand base = HEX; 411b0104773SPascal Brand /* leading 0x/X only if non-zero */ 412b0104773SPascal Brand if (flags & ALT && _uquad != 0) 413b0104773SPascal Brand flags |= HEXPREFIX; 414b0104773SPascal Brand 415b0104773SPascal Brand /* unsigned conversions */ 416b0104773SPascal Brand nosign: sign = '\0'; 417b0104773SPascal Brand /* 418b0104773SPascal Brand * ``... diouXx conversions ... if a precision is 419b0104773SPascal Brand * specified, the 0 flag will be ignored.'' 420b0104773SPascal Brand * -- ANSI X3J11 421b0104773SPascal Brand */ 422b0104773SPascal Brand number: if ((dprec = prec) >= 0) 423b0104773SPascal Brand flags &= ~ZEROPAD; 424b0104773SPascal Brand 425b0104773SPascal Brand /* 426b0104773SPascal Brand * ``The result of converting a zero value with an 427b0104773SPascal Brand * explicit precision of zero is no characters.'' 428b0104773SPascal Brand * -- ANSI X3J11 429b0104773SPascal Brand */ 430b0104773SPascal Brand cp = bf + KPRINTF_BUFSIZE; 431b0104773SPascal Brand if (_uquad != 0 || prec != 0) { 432b0104773SPascal Brand /* 433b0104773SPascal Brand * Unsigned mod is hard, and unsigned mod 434b0104773SPascal Brand * by a constant is easier than that by 435b0104773SPascal Brand * a variable; hence this switch. 436b0104773SPascal Brand */ 437b0104773SPascal Brand switch (base) { 438b0104773SPascal Brand case OCT: 439b0104773SPascal Brand do { 440b0104773SPascal Brand *--cp = to_char(_uquad & 7); 441b0104773SPascal Brand _uquad >>= 3; 442b0104773SPascal Brand } while (_uquad); 443b0104773SPascal Brand /* handle octal leading 0 */ 444b0104773SPascal Brand if (flags & ALT && *cp != '0') 445b0104773SPascal Brand *--cp = '0'; 446b0104773SPascal Brand break; 447b0104773SPascal Brand 448b0104773SPascal Brand case DEC: 449b0104773SPascal Brand /* many numbers are 1 digit */ 450b0104773SPascal Brand while (_uquad >= 10) { 451b0104773SPascal Brand *--cp = to_char(_uquad % 10); 452b0104773SPascal Brand _uquad /= 10; 453b0104773SPascal Brand } 454b0104773SPascal Brand *--cp = to_char(_uquad); 455b0104773SPascal Brand break; 456b0104773SPascal Brand 457b0104773SPascal Brand case HEX: 458b0104773SPascal Brand do { 459b0104773SPascal Brand *--cp = xdigs[_uquad & 15]; 460b0104773SPascal Brand _uquad >>= 4; 461b0104773SPascal Brand } while (_uquad); 462b0104773SPascal Brand break; 463b0104773SPascal Brand 464b0104773SPascal Brand default: 465b0104773SPascal Brand /*XXXUNCONST*/ 466*d8516202SJens Wiklander cp = __UNCONST("bug in kprintf: bad base"); 467b0104773SPascal Brand size = strlen(cp); 468b0104773SPascal Brand goto skipsize; 469b0104773SPascal Brand } 470b0104773SPascal Brand } 471b0104773SPascal Brand size = bf + KPRINTF_BUFSIZE - cp; 472b0104773SPascal Brand skipsize: 473b0104773SPascal Brand break; 474b0104773SPascal Brand default: /* "%?" prints ?, unless ? is NUL */ 475b0104773SPascal Brand if (ch == '\0') 476b0104773SPascal Brand goto done; 477b0104773SPascal Brand /* pretend it was %c with argument ch */ 478b0104773SPascal Brand cp = bf; 479b0104773SPascal Brand *cp = ch; 480b0104773SPascal Brand size = 1; 481b0104773SPascal Brand sign = '\0'; 482b0104773SPascal Brand break; 483b0104773SPascal Brand } 484b0104773SPascal Brand 485b0104773SPascal Brand /* 486b0104773SPascal Brand * All reasonable formats wind up here. At this point, `cp' 487b0104773SPascal Brand * points to a string which (if not flags&LADJUST) should be 488b0104773SPascal Brand * padded out to `width' places. If flags&ZEROPAD, it should 489b0104773SPascal Brand * first be prefixed by any sign or other prefix; otherwise, 490b0104773SPascal Brand * it should be blank padded before the prefix is emitted. 491b0104773SPascal Brand * After any left-hand padding and prefixing, emit zeroes 492b0104773SPascal Brand * required by a decimal [diouxX] precision, then print the 493b0104773SPascal Brand * string proper, then emit zeroes required by any leftover 494b0104773SPascal Brand * floating precision; finally, if LADJUST, pad with blanks. 495b0104773SPascal Brand * 496b0104773SPascal Brand * Compute actual size, so we know how much to pad. 497b0104773SPascal Brand * size excludes decimal prec; realsz includes it. 498b0104773SPascal Brand */ 499b0104773SPascal Brand realsz = dprec > size ? dprec : size; 500b0104773SPascal Brand if (sign) 501b0104773SPascal Brand realsz++; 502b0104773SPascal Brand else if (flags & HEXPREFIX) 503b0104773SPascal Brand realsz+= 2; 504b0104773SPascal Brand 505b0104773SPascal Brand /* adjust ret */ 506b0104773SPascal Brand ret += width > realsz ? width : realsz; 507b0104773SPascal Brand 508b0104773SPascal Brand /* right-adjusting blank padding */ 509b0104773SPascal Brand if ((flags & (LADJUST|ZEROPAD)) == 0) { 510b0104773SPascal Brand n = width - realsz; 511b0104773SPascal Brand while (n-- > 0) 512b0104773SPascal Brand KPRINTF_PUTCHAR(' '); 513b0104773SPascal Brand } 514b0104773SPascal Brand 515b0104773SPascal Brand /* prefix */ 516b0104773SPascal Brand if (sign) { 517b0104773SPascal Brand KPRINTF_PUTCHAR(sign); 518b0104773SPascal Brand } else if (flags & HEXPREFIX) { 519b0104773SPascal Brand KPRINTF_PUTCHAR('0'); 520b0104773SPascal Brand KPRINTF_PUTCHAR(ch); 521b0104773SPascal Brand } 522b0104773SPascal Brand 523b0104773SPascal Brand /* right-adjusting zero padding */ 524b0104773SPascal Brand if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) { 525b0104773SPascal Brand n = width - realsz; 526b0104773SPascal Brand while (n-- > 0) 527b0104773SPascal Brand KPRINTF_PUTCHAR('0'); 528b0104773SPascal Brand } 529b0104773SPascal Brand 530b0104773SPascal Brand /* leading zeroes from decimal precision */ 531b0104773SPascal Brand n = dprec - size; 532b0104773SPascal Brand while (n-- > 0) 533b0104773SPascal Brand KPRINTF_PUTCHAR('0'); 534b0104773SPascal Brand 535b0104773SPascal Brand /* the string or number proper */ 536*d8516202SJens Wiklander for (; size--; cp++) 537*d8516202SJens Wiklander KPRINTF_PUTCHAR(*cp); 538b0104773SPascal Brand /* left-adjusting padding (always blank) */ 539b0104773SPascal Brand if (flags & LADJUST) { 540b0104773SPascal Brand n = width - realsz; 541b0104773SPascal Brand while (n-- > 0) 542b0104773SPascal Brand KPRINTF_PUTCHAR(' '); 543b0104773SPascal Brand } 544b0104773SPascal Brand } 545b0104773SPascal Brand 546b0104773SPascal Brand done: 547b0104773SPascal Brand if ((oflags == TOBUFONLY) && (vp != NULL)) 548b0104773SPascal Brand *(char **)vp = sbuf; 549b0104773SPascal Brand return ret; 550b0104773SPascal Brand } 551