1 /* $NetBSD: vsnprintf_ss.c,v 1.6 2007/02/03 22:26:55 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Chris Torek. 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 #if defined(LIBC_SCCS) && !defined(lint) 37 #if 0 38 static char sccsid[] = "@(#)vsnprintf.c 8.1 (Berkeley) 6/4/93"; 39 #else 40 __RCSID("$NetBSD: vsnprintf_ss.c,v 1.6 2007/02/03 22:26:55 christos Exp $"); 41 #endif 42 #endif /* LIBC_SCCS and not lint */ 43 44 #include "namespace.h" 45 46 #include <sys/types.h> 47 #include <inttypes.h> 48 #include <assert.h> 49 #include <stdio.h> 50 #include <errno.h> 51 #include <stdarg.h> 52 #include <string.h> 53 #include "reentrant.h" 54 #include "extern.h" 55 #include "local.h" 56 57 #ifdef __weak_alias 58 __weak_alias(vsnprintf_ss,_vsnprintf_ss) 59 #endif 60 61 /* 62 * vsnprintf_ss: scaled down version of printf(3). 63 * 64 * this version based on vfprintf() from libc which was derived from 65 * software contributed to Berkeley by Chris Torek. 66 * 67 */ 68 69 /* 70 * macros for converting digits to letters and vice versa 71 */ 72 #define to_digit(c) ((c) - '0') 73 #define is_digit(c) ((unsigned)to_digit(c) <= 9) 74 #define to_char(n) (char)((n) + '0') 75 76 /* 77 * flags used during conversion. 78 */ 79 #define ALT 0x001 /* alternate form */ 80 #define HEXPREFIX 0x002 /* add 0x or 0X prefix */ 81 #define LADJUST 0x004 /* left adjustment */ 82 #define LONGDBL 0x008 /* long double; unimplemented */ 83 #define LONGINT 0x010 /* long integer */ 84 #define QUADINT 0x020 /* quad integer */ 85 #define SHORTINT 0x040 /* short integer */ 86 #define MAXINT 0x080 /* intmax_t */ 87 #define PTRINT 0x100 /* intptr_t */ 88 #define SIZEINT 0x200 /* size_t */ 89 #define ZEROPAD 0x400 /* zero (as opposed to blank) pad */ 90 #define FPT 0x800 /* Floating point number */ 91 92 /* 93 * To extend shorts properly, we need both signed and unsigned 94 * argument extraction methods. 95 */ 96 #define SARG() \ 97 (flags&MAXINT ? va_arg(ap, intmax_t) : \ 98 flags&PTRINT ? va_arg(ap, intptr_t) : \ 99 flags&SIZEINT ? va_arg(ap, ssize_t) : /* XXX */ \ 100 flags&QUADINT ? va_arg(ap, quad_t) : \ 101 flags&LONGINT ? va_arg(ap, long) : \ 102 flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ 103 (long)va_arg(ap, int)) 104 #define UARG() \ 105 (flags&MAXINT ? va_arg(ap, uintmax_t) : \ 106 flags&PTRINT ? va_arg(ap, uintptr_t) : \ 107 flags&SIZEINT ? va_arg(ap, size_t) : \ 108 flags&QUADINT ? va_arg(ap, u_quad_t) : \ 109 flags&LONGINT ? va_arg(ap, u_long) : \ 110 flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \ 111 (u_long)va_arg(ap, u_int)) 112 113 #define PUTCHAR(C) do { \ 114 if (sbuf < tailp) \ 115 *sbuf++ = (C); \ 116 } while (/*CONSTCOND*/0) 117 118 int 119 vsnprintf_ss(char *sbuf, size_t slen, const char *fmt0, _BSD_VA_LIST_ ap) 120 { 121 const char *fmt; /* format string */ 122 int ch; /* character from fmt */ 123 int n; /* handy integer (short term usage) */ 124 char *cp; /* handy char pointer (short term usage) */ 125 int flags; /* flags as above */ 126 int ret; /* return value accumulator */ 127 int width; /* width from format (%8d), or 0 */ 128 int prec; /* precision from format (%.3d), or -1 */ 129 char sign; /* sign prefix (' ', '+', '-', or \0) */ 130 131 u_quad_t _uquad; /* integer arguments %[diouxX] */ 132 enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ 133 int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 134 int realsz; /* field size expanded by dprec */ 135 int size; /* size of converted field or string */ 136 const char *xdigs; /* digits for [xX] conversion */ 137 char bf[128]; /* space for %c, %[diouxX] */ 138 char *tailp; /* tail pointer for snprintf */ 139 140 static const char xdigs_lower[16] = "0123456789abcdef"; 141 static const char xdigs_upper[16] = "0123456789ABCDEF"; 142 143 _DIAGASSERT(slen == 0 || sbuf != NULL); 144 _DIAGASSERT(fmt0 != NULL); 145 146 if ((int)slen < 0) { 147 errno = EINVAL; 148 return (-1); 149 } 150 151 tailp = sbuf + slen; 152 153 cp = NULL; /* XXX: shutup gcc */ 154 size = 0; /* XXX: shutup gcc */ 155 156 fmt = fmt0; 157 ret = 0; 158 159 xdigs = NULL; /* XXX: shut up gcc warning */ 160 161 /* 162 * Scan the format for conversions (`%' character). 163 */ 164 for (;;) { 165 while (*fmt != '%' && *fmt) { 166 ret++; 167 PUTCHAR(*fmt++); 168 } 169 if (*fmt == 0) 170 goto done; 171 172 fmt++; /* skip over '%' */ 173 174 flags = 0; 175 dprec = 0; 176 width = 0; 177 prec = -1; 178 sign = '\0'; 179 180 rflag: ch = *fmt++; 181 reswitch: switch (ch) { 182 case ' ': 183 /* 184 * ``If the space and + flags both appear, the space 185 * flag will be ignored.'' 186 * -- ANSI X3J11 187 */ 188 if (!sign) 189 sign = ' '; 190 goto rflag; 191 case '#': 192 flags |= ALT; 193 goto rflag; 194 case '*': 195 /* 196 * ``A negative field width argument is taken as a 197 * - flag followed by a positive field width.'' 198 * -- ANSI X3J11 199 * They don't exclude field widths read from args. 200 */ 201 if ((width = va_arg(ap, int)) >= 0) 202 goto rflag; 203 width = -width; 204 /* FALLTHROUGH */ 205 case '-': 206 flags |= LADJUST; 207 goto rflag; 208 case '+': 209 sign = '+'; 210 goto rflag; 211 case '.': 212 if ((ch = *fmt++) == '*') { 213 n = va_arg(ap, int); 214 prec = n < 0 ? -1 : n; 215 goto rflag; 216 } 217 n = 0; 218 while (is_digit(ch)) { 219 n = 10 * n + to_digit(ch); 220 ch = *fmt++; 221 } 222 prec = n < 0 ? -1 : n; 223 goto reswitch; 224 case '0': 225 /* 226 * ``Note that 0 is taken as a flag, not as the 227 * beginning of a field width.'' 228 * -- ANSI X3J11 229 */ 230 flags |= ZEROPAD; 231 goto rflag; 232 case '1': case '2': case '3': case '4': 233 case '5': case '6': case '7': case '8': case '9': 234 n = 0; 235 do { 236 n = 10 * n + to_digit(ch); 237 ch = *fmt++; 238 } while (is_digit(ch)); 239 width = n; 240 goto reswitch; 241 case 'h': 242 flags |= SHORTINT; 243 goto rflag; 244 case 'j': 245 flags |= MAXINT; 246 goto rflag; 247 case 'l': 248 if (*fmt == 'l') { 249 fmt++; 250 flags |= QUADINT; 251 } else { 252 flags |= LONGINT; 253 } 254 goto rflag; 255 case 'q': 256 flags |= QUADINT; 257 goto rflag; 258 case 't': 259 flags |= PTRINT; 260 goto rflag; 261 case 'z': 262 flags |= SIZEINT; 263 goto rflag; 264 case 'c': 265 *(cp = bf) = va_arg(ap, int); 266 size = 1; 267 sign = '\0'; 268 break; 269 case 'D': 270 flags |= LONGINT; 271 /*FALLTHROUGH*/ 272 case 'd': 273 case 'i': 274 _uquad = SARG(); 275 if ((quad_t)_uquad < 0) { 276 _uquad = -_uquad; 277 sign = '-'; 278 } 279 base = DEC; 280 goto number; 281 case 'n': 282 if (flags & MAXINT) 283 *va_arg(ap, intmax_t *) = ret; 284 else if (flags & PTRINT) 285 *va_arg(ap, intptr_t *) = ret; 286 else if (flags & SIZEINT) 287 *va_arg(ap, ssize_t *) = ret; 288 else if (flags & QUADINT) 289 *va_arg(ap, quad_t *) = ret; 290 else if (flags & LONGINT) 291 *va_arg(ap, long *) = ret; 292 else if (flags & SHORTINT) 293 *va_arg(ap, short *) = ret; 294 else 295 *va_arg(ap, int *) = ret; 296 continue; /* no output */ 297 case 'O': 298 flags |= LONGINT; 299 /*FALLTHROUGH*/ 300 case 'o': 301 _uquad = UARG(); 302 base = OCT; 303 goto nosign; 304 case 'p': 305 /* 306 * ``The argument shall be a pointer to void. The 307 * value of the pointer is converted to a sequence 308 * of printable characters, in an implementation- 309 * defined manner.'' 310 * -- ANSI X3J11 311 */ 312 /* NOSTRICT */ 313 _uquad = (u_long)va_arg(ap, void *); 314 base = HEX; 315 xdigs = xdigs_lower; 316 flags |= HEXPREFIX; 317 ch = 'x'; 318 goto nosign; 319 case 's': 320 if ((cp = va_arg(ap, char *)) == NULL) 321 /*XXXUNCONST*/ 322 cp = __UNCONST("(null)"); 323 if (prec >= 0) { 324 /* 325 * can't use strlen; can only look for the 326 * NUL in the first `prec' characters, and 327 * strlen() will go further. 328 */ 329 char *p = memchr(cp, 0, (size_t)prec); 330 331 if (p != NULL) { 332 size = p - cp; 333 if (size > prec) 334 size = prec; 335 } else 336 size = prec; 337 } else 338 size = strlen(cp); 339 sign = '\0'; 340 break; 341 case 'U': 342 flags |= LONGINT; 343 /*FALLTHROUGH*/ 344 case 'u': 345 _uquad = UARG(); 346 base = DEC; 347 goto nosign; 348 case 'X': 349 xdigs = xdigs_upper; 350 goto hex; 351 case 'x': 352 xdigs = xdigs_lower; 353 hex: _uquad = UARG(); 354 base = HEX; 355 /* leading 0x/X only if non-zero */ 356 if (flags & ALT && _uquad != 0) 357 flags |= HEXPREFIX; 358 359 /* unsigned conversions */ 360 nosign: sign = '\0'; 361 /* 362 * ``... diouXx conversions ... if a precision is 363 * specified, the 0 flag will be ignored.'' 364 * -- ANSI X3J11 365 */ 366 number: if ((dprec = prec) >= 0) 367 flags &= ~ZEROPAD; 368 369 /* 370 * ``The result of converting a zero value with an 371 * explicit precision of zero is no characters.'' 372 * -- ANSI X3J11 373 */ 374 cp = bf + sizeof(bf); 375 if (_uquad != 0 || prec != 0) { 376 /* 377 * Unsigned mod is hard, and unsigned mod 378 * by a constant is easier than that by 379 * a variable; hence this switch. 380 */ 381 switch (base) { 382 case OCT: 383 do { 384 *--cp = to_char(_uquad & 7); 385 _uquad >>= 3; 386 } while (_uquad); 387 /* handle octal leading 0 */ 388 if (flags & ALT && *cp != '0') 389 *--cp = '0'; 390 break; 391 392 case DEC: 393 /* many numbers are 1 digit */ 394 while (_uquad >= 10) { 395 *--cp = to_char(_uquad % 10); 396 _uquad /= 10; 397 } 398 *--cp = to_char(_uquad); 399 break; 400 401 case HEX: 402 do { 403 *--cp = xdigs[(size_t)_uquad & 15]; 404 _uquad >>= 4; 405 } while (_uquad); 406 break; 407 408 default: 409 /*XXXUNCONST*/ 410 cp = __UNCONST("bug bad base"); 411 size = strlen(cp); 412 goto skipsize; 413 } 414 } 415 size = bf + sizeof(bf) - cp; 416 skipsize: 417 break; 418 default: /* "%?" prints ?, unless ? is NUL */ 419 if (ch == '\0') 420 goto done; 421 /* pretend it was %c with argument ch */ 422 cp = bf; 423 *cp = ch; 424 size = 1; 425 sign = '\0'; 426 break; 427 } 428 429 /* 430 * All reasonable formats wind up here. At this point, `cp' 431 * points to a string which (if not flags&LADJUST) should be 432 * padded out to `width' places. If flags&ZEROPAD, it should 433 * first be prefixed by any sign or other prefix; otherwise, 434 * it should be blank padded before the prefix is emitted. 435 * After any left-hand padding and prefixing, emit zeroes 436 * required by a decimal [diouxX] precision, then print the 437 * string proper, then emit zeroes required by any leftover 438 * floating precision; finally, if LADJUST, pad with blanks. 439 * 440 * Compute actual size, so we know how much to pad. 441 * size excludes decimal prec; realsz includes it. 442 */ 443 realsz = dprec > size ? dprec : size; 444 if (sign) 445 realsz++; 446 else if (flags & HEXPREFIX) 447 realsz+= 2; 448 449 /* adjust ret */ 450 ret += width > realsz ? width : realsz; 451 452 /* right-adjusting blank padding */ 453 if ((flags & (LADJUST|ZEROPAD)) == 0) { 454 n = width - realsz; 455 while (n-- > 0) 456 PUTCHAR(' '); 457 } 458 459 /* prefix */ 460 if (sign) { 461 PUTCHAR(sign); 462 } else if (flags & HEXPREFIX) { 463 PUTCHAR('0'); 464 PUTCHAR(ch); 465 } 466 467 /* right-adjusting zero padding */ 468 if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) { 469 n = width - realsz; 470 while (n-- > 0) 471 PUTCHAR('0'); 472 } 473 474 /* leading zeroes from decimal precision */ 475 n = dprec - size; 476 while (n-- > 0) 477 PUTCHAR('0'); 478 479 /* the string or number proper */ 480 while (size--) 481 PUTCHAR(*cp++); 482 /* left-adjusting padding (always blank) */ 483 if (flags & LADJUST) { 484 n = width - realsz; 485 while (n-- > 0) 486 PUTCHAR(' '); 487 } 488 } 489 490 done: 491 if (sbuf == tailp) 492 sbuf[-1] = '\0'; 493 else 494 *sbuf = '\0'; 495 return (ret); 496 /* NOTREACHED */ 497 } 498