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