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/* 41 * Standaloneified version of the FreeBSD kernel printf family. 42 */ 43 44#include <sys/types.h> 45#include <sys/stddef.h> 46#include <sys/stdint.h> 47#include <limits.h> 48#include <string.h> 49#include "stand.h" 50 51/* 52 * Note that stdarg.h and the ANSI style va_start macro is used for both 53 * ANSI and traditional C compilers. 54 */ 55#include <machine/stdarg.h> 56 57#define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1) 58 59static char *ksprintn (char *buf, uintmax_t num, int base, int *len, int upper); 60static int kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap); 61 62int 63printf(const char *fmt, ...) 64{ 65 va_list ap; 66 int retval; 67 68 va_start(ap, fmt); 69 retval = kvprintf(fmt, putchar, NULL, 10, ap); 70 va_end(ap); 71 return retval; 72} 73 74void 75vprintf(const char *fmt, va_list ap) 76{ 77 78 kvprintf(fmt, putchar, NULL, 10, ap); 79} 80 81int 82sprintf(char *buf, const char *cfmt, ...) 83{ 84 int retval; 85 va_list ap; 86 87 va_start(ap, cfmt); 88 retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap); 89 buf[retval] = '\0'; 90 va_end(ap); 91 return retval; 92} 93 94void 95vsprintf(char *buf, const char *cfmt, va_list ap) 96{ 97 int retval; 98 99 retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap); 100 buf[retval] = '\0'; 101} 102 103/* 104 * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse 105 * order; return an optional length and a pointer to the last character 106 * written in the buffer (i.e., the first character of the string). 107 * The buffer pointed to by `nbuf' must have length >= MAXNBUF. 108 */ 109static char * 110ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) 111{ 112 char *p, c; 113 114 p = nbuf; 115 *p = '\0'; 116 do { 117 c = hex2ascii(num % base); 118 *++p = upper ? toupper(c) : c; 119 } while (num /= base); 120 if (lenp) 121 *lenp = p - nbuf; 122 return (p); 123} 124 125/* 126 * Scaled down version of printf(3). 127 * 128 * Two additional formats: 129 * 130 * The format %b is supported to decode error registers. 131 * Its usage is: 132 * 133 * printf("reg=%b\n", regval, "<base><arg>*"); 134 * 135 * where <base> is the output base expressed as a control character, e.g. 136 * \10 gives octal; \20 gives hex. Each arg is a sequence of characters, 137 * the first of which gives the bit number to be inspected (origin 1), and 138 * the next characters (up to a control character, i.e. a character <= 32), 139 * give the name of the register. Thus: 140 * 141 * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n"); 142 * 143 * would produce output: 144 * 145 * reg=3<BITTWO,BITONE> 146 * 147 * XXX: %D -- Hexdump, takes pointer and separator string: 148 * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX 149 * ("%*D", len, ptr, " " -> XX XX XX XX ... 150 */ 151static int 152kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap) 153{ 154#define PCHAR(c) {int cc=(c); if (func) (*func)(cc); else *d++ = cc; retval++; } 155 char nbuf[MAXNBUF]; 156 char *d; 157 const char *p, *percent, *q; 158 u_char *up; 159 int ch, n; 160 uintmax_t num; 161 int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; 162 int cflag, hflag, jflag, tflag, zflag; 163 int dwidth, upper; 164 char padc; 165 int stop = 0, retval = 0; 166 167 num = 0; 168 if (!func) 169 d = (char *) arg; 170 else 171 d = NULL; 172 173 if (fmt == NULL) 174 fmt = "(fmt null)\n"; 175 176 if (radix < 2 || radix > 36) 177 radix = 10; 178 179 for (;;) { 180 padc = ' '; 181 width = 0; 182 while ((ch = (u_char)*fmt++) != '%' || stop) { 183 if (ch == '\0') 184 return (retval); 185 PCHAR(ch); 186 } 187 percent = fmt - 1; 188 qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; 189 sign = 0; dot = 0; dwidth = 0; upper = 0; 190 cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; 191reswitch: switch (ch = (u_char)*fmt++) { 192 case '.': 193 dot = 1; 194 goto reswitch; 195 case '#': 196 sharpflag = 1; 197 goto reswitch; 198 case '+': 199 sign = 1; 200 goto reswitch; 201 case '-': 202 ladjust = 1; 203 goto reswitch; 204 case '%': 205 PCHAR(ch); 206 break; 207 case '*': 208 if (!dot) { 209 width = va_arg(ap, int); 210 if (width < 0) { 211 ladjust = !ladjust; 212 width = -width; 213 } 214 } else { 215 dwidth = va_arg(ap, int); 216 } 217 goto reswitch; 218 case '0': 219 if (!dot) { 220 padc = '0'; 221 goto reswitch; 222 } 223 case '1': case '2': case '3': case '4': 224 case '5': case '6': case '7': case '8': case '9': 225 for (n = 0;; ++fmt) { 226 n = n * 10 + ch - '0'; 227 ch = *fmt; 228 if (ch < '0' || ch > '9') 229 break; 230 } 231 if (dot) 232 dwidth = n; 233 else 234 width = n; 235 goto reswitch; 236 case 'b': 237 num = (u_int)va_arg(ap, int); 238 p = va_arg(ap, char *); 239 for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;) 240 PCHAR(*q--); 241 242 if (num == 0) 243 break; 244 245 for (tmp = 0; *p;) { 246 n = *p++; 247 if (num & (1 << (n - 1))) { 248 PCHAR(tmp ? ',' : '<'); 249 for (; (n = *p) > ' '; ++p) 250 PCHAR(n); 251 tmp = 1; 252 } else 253 for (; *p > ' '; ++p) 254 continue; 255 } 256 if (tmp) 257 PCHAR('>'); 258 break; 259 case 'c': 260 PCHAR(va_arg(ap, int)); 261 break; 262 case 'D': 263 up = va_arg(ap, u_char *); 264 p = va_arg(ap, char *); 265 if (!width) 266 width = 16; 267 while(width--) { 268 PCHAR(hex2ascii(*up >> 4)); 269 PCHAR(hex2ascii(*up & 0x0f)); 270 up++; 271 if (width) 272 for (q=p;*q;q++) 273 PCHAR(*q); 274 } 275 break; 276 case 'd': 277 case 'i': 278 base = 10; 279 sign = 1; 280 goto handle_sign; 281 case 'h': 282 if (hflag) { 283 hflag = 0; 284 cflag = 1; 285 } else 286 hflag = 1; 287 goto reswitch; 288 case 'j': 289 jflag = 1; 290 goto reswitch; 291 case 'l': 292 if (lflag) { 293 lflag = 0; 294 qflag = 1; 295 } else 296 lflag = 1; 297 goto reswitch; 298 case 'n': 299 if (jflag) 300 *(va_arg(ap, intmax_t *)) = retval; 301 else if (qflag) 302 *(va_arg(ap, quad_t *)) = retval; 303 else if (lflag) 304 *(va_arg(ap, long *)) = retval; 305 else if (zflag) 306 *(va_arg(ap, size_t *)) = retval; 307 else if (hflag) 308 *(va_arg(ap, short *)) = retval; 309 else if (cflag) 310 *(va_arg(ap, char *)) = retval; 311 else 312 *(va_arg(ap, int *)) = retval; 313 break; 314 case 'o': 315 base = 8; 316 goto handle_nosign; 317 case 'p': 318 base = 16; 319 sharpflag = (width == 0); 320 sign = 0; 321 num = (uintptr_t)va_arg(ap, void *); 322 goto number; 323 case 'q': 324 qflag = 1; 325 goto reswitch; 326 case 'r': 327 base = radix; 328 if (sign) 329 goto handle_sign; 330 goto handle_nosign; 331 case 's': 332 p = va_arg(ap, char *); 333 if (p == NULL) 334 p = "(null)"; 335 if (!dot) 336 n = strlen (p); 337 else 338 for (n = 0; n < dwidth && p[n]; n++) 339 continue; 340 341 width -= n; 342 343 if (!ladjust && width > 0) 344 while (width--) 345 PCHAR(padc); 346 while (n--) 347 PCHAR(*p++); 348 if (ladjust && width > 0) 349 while (width--) 350 PCHAR(padc); 351 break; 352 case 't': 353 tflag = 1; 354 goto reswitch; 355 case 'u': 356 base = 10; 357 goto handle_nosign; 358 case 'X': 359 upper = 1; 360 case 'x': 361 base = 16; 362 goto handle_nosign; 363 case 'y': 364 base = 16; 365 sign = 1; 366 goto handle_sign; 367 case 'z': 368 zflag = 1; 369 goto reswitch; 370handle_nosign: 371 sign = 0; 372 if (jflag) 373 num = va_arg(ap, uintmax_t); 374 else if (qflag) 375 num = va_arg(ap, u_quad_t); 376 else if (tflag) 377 num = va_arg(ap, ptrdiff_t); 378 else if (lflag) 379 num = va_arg(ap, u_long); 380 else if (zflag) 381 num = va_arg(ap, size_t); 382 else if (hflag) 383 num = (u_short)va_arg(ap, int); 384 else if (cflag) 385 num = (u_char)va_arg(ap, int); 386 else 387 num = va_arg(ap, u_int); 388 goto number; 389handle_sign: 390 if (jflag) 391 num = va_arg(ap, intmax_t); 392 else if (qflag) 393 num = va_arg(ap, quad_t); 394 else if (tflag) 395 num = va_arg(ap, ptrdiff_t); 396 else if (lflag) 397 num = va_arg(ap, long); 398 else if (zflag) 399 num = va_arg(ap, ssize_t); 400 else if (hflag) 401 num = (short)va_arg(ap, int); 402 else if (cflag) 403 num = (char)va_arg(ap, int); 404 else 405 num = va_arg(ap, int); 406number: 407 if (sign && (intmax_t)num < 0) { 408 neg = 1; 409 num = -(intmax_t)num; 410 } 411 p = ksprintn(nbuf, num, base, &n, upper); 412 tmp = 0; 413 if (sharpflag && num != 0) { 414 if (base == 8) 415 tmp++; 416 else if (base == 16) 417 tmp += 2; 418 } 419 if (neg) 420 tmp++; 421 422 if (!ladjust && padc == '0') 423 dwidth = width - tmp; 424 width -= tmp + imax(dwidth, n); 425 dwidth -= n; 426 if (!ladjust) 427 while (width-- > 0) 428 PCHAR(' '); 429 if (neg) 430 PCHAR('-'); 431 if (sharpflag && num != 0) { 432 if (base == 8) { 433 PCHAR('0'); 434 } else if (base == 16) { 435 PCHAR('0'); 436 PCHAR('x'); 437 } 438 } 439 while (dwidth-- > 0) 440 PCHAR('0'); 441 442 while (*p) 443 PCHAR(*p--); 444 445 if (ladjust) 446 while (width-- > 0) 447 PCHAR(' '); 448 449 break; 450 default: 451 while (percent < fmt) 452 PCHAR(*percent++); 453 /* 454 * Since we ignore an formatting argument it is no 455 * longer safe to obey the remaining formatting 456 * arguments as the arguments will no longer match 457 * the format specs. 458 */ 459 stop = 1; 460 break; 461 } 462 } 463#undef PCHAR 464} 465