1269257Sdes/* snprintf - compatibility implementation of snprintf, vsnprintf 2269257Sdes * 3269257Sdes * Copyright (c) 2013, NLnet Labs. All rights reserved. 4269257Sdes * 5269257Sdes * This software is open source. 6269257Sdes * 7269257Sdes * Redistribution and use in source and binary forms, with or without 8269257Sdes * modification, are permitted provided that the following conditions 9269257Sdes * are met: 10269257Sdes * 11269257Sdes * Redistributions of source code must retain the above copyright notice, 12269257Sdes * this list of conditions and the following disclaimer. 13269257Sdes * 14269257Sdes * Redistributions in binary form must reproduce the above copyright notice, 15269257Sdes * this list of conditions and the following disclaimer in the documentation 16269257Sdes * and/or other materials provided with the distribution. 17269257Sdes * 18269257Sdes * Neither the name of the NLNET LABS nor the names of its contributors may 19269257Sdes * be used to endorse or promote products derived from this software without 20269257Sdes * specific prior written permission. 21269257Sdes * 22269257Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23269257Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24269257Sdes * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25269257Sdes * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26269257Sdes * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27269257Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 28269257Sdes * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 29269257Sdes * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30269257Sdes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 31269257Sdes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32269257Sdes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33269257Sdes */ 34238106Sdes 35269257Sdes#include "config.h" 36269257Sdes#include <stdio.h> 37238106Sdes#include <ctype.h> 38269257Sdes#include <string.h> 39269257Sdes#include <stdarg.h> 40269257Sdes#include <stdlib.h> 41269257Sdes#include <errno.h> 42269257Sdes#ifdef HAVE_STDINT_H 43269257Sdes#include <stdint.h> 44269257Sdes#endif 45294190Sdes#include <limits.h> 46238106Sdes 47269257Sdes/* for test */ 48269257Sdes/* #define SNPRINTF_TEST 1 */ 49269257Sdes#ifdef SNPRINTF_TEST 50269257Sdes#define snprintf my_snprintf 51269257Sdes#define vsnprintf my_vsnprintf 52269257Sdes#endif /* SNPRINTF_TEST */ 53238106Sdes 54269257Sdesint snprintf(char* str, size_t size, const char* format, ...); 55269257Sdesint vsnprintf(char* str, size_t size, const char* format, va_list arg); 56238106Sdes 57269257Sdes/** 58269257Sdes * Very portable snprintf implementation, limited in functionality, 59269257Sdes * esp. for %[capital] %[nonportable] and so on. Reduced float functionality, 60269257Sdes * mostly in formatting and range (e+-16), for %f and %g. 61238106Sdes * 62269257Sdes * %s, %d, %u, %i, %x, %c, %n and %% are fully supported. 63269257Sdes * This includes width, precision, flags 0- +, and *(arg for wid,prec). 64269257Sdes * %f, %g, %m, %p have reduced support, support for wid,prec,flags,*, but 65269257Sdes * less floating point range, no %e formatting for %g. 66269257Sdes */ 67269257Sdesint snprintf(char* str, size_t size, const char* format, ...) 68269257Sdes{ 69269257Sdes int r; 70269257Sdes va_list args; 71269257Sdes va_start(args, format); 72269257Sdes r = vsnprintf(str, size, format, args); 73269257Sdes va_end(args); 74269257Sdes return r; 75269257Sdes} 76238106Sdes 77269257Sdes/** add padding to string */ 78269257Sdesstatic void 79269257Sdesprint_pad(char** at, size_t* left, int* ret, char p, int num) 80269257Sdes{ 81269257Sdes while(num--) { 82269257Sdes if(*left > 1) { 83269257Sdes *(*at)++ = p; 84269257Sdes (*left)--; 85269257Sdes } 86269257Sdes (*ret)++; 87269257Sdes } 88269257Sdes} 89238106Sdes 90269257Sdes/** get negative symbol, 0 if none */ 91269257Sdesstatic char 92269257Sdesget_negsign(int negative, int plus, int space) 93269257Sdes{ 94269257Sdes if(negative) 95269257Sdes return '-'; 96269257Sdes if(plus) 97269257Sdes return '+'; 98269257Sdes if(space) 99269257Sdes return ' '; 100269257Sdes return 0; 101269257Sdes} 102238106Sdes 103269257Sdes#define PRINT_DEC_BUFSZ 32 /* 20 is enough for 64 bit decimals */ 104269257Sdes/** print decimal into buffer, returns length */ 105269257Sdesstatic int 106269257Sdesprint_dec(char* buf, int max, unsigned int value) 107269257Sdes{ 108269257Sdes int i = 0; 109269257Sdes if(value == 0) { 110269257Sdes if(max > 0) { 111269257Sdes buf[0] = '0'; 112269257Sdes i = 1; 113269257Sdes } 114269257Sdes } else while(value && i < max) { 115269257Sdes buf[i++] = '0' + value % 10; 116269257Sdes value /= 10; 117269257Sdes } 118269257Sdes return i; 119269257Sdes} 120238106Sdes 121269257Sdes/** print long decimal into buffer, returns length */ 122269257Sdesstatic int 123269257Sdesprint_dec_l(char* buf, int max, unsigned long value) 124269257Sdes{ 125269257Sdes int i = 0; 126269257Sdes if(value == 0) { 127269257Sdes if(max > 0) { 128269257Sdes buf[0] = '0'; 129269257Sdes i = 1; 130269257Sdes } 131269257Sdes } else while(value && i < max) { 132269257Sdes buf[i++] = '0' + value % 10; 133269257Sdes value /= 10; 134269257Sdes } 135269257Sdes return i; 136269257Sdes} 137238106Sdes 138269257Sdes/** print long decimal into buffer, returns length */ 139269257Sdesstatic int 140269257Sdesprint_dec_ll(char* buf, int max, unsigned long long value) 141269257Sdes{ 142269257Sdes int i = 0; 143269257Sdes if(value == 0) { 144269257Sdes if(max > 0) { 145269257Sdes buf[0] = '0'; 146269257Sdes i = 1; 147269257Sdes } 148269257Sdes } else while(value && i < max) { 149269257Sdes buf[i++] = '0' + value % 10; 150269257Sdes value /= 10; 151269257Sdes } 152269257Sdes return i; 153269257Sdes} 154238106Sdes 155269257Sdes/** print hex into buffer, returns length */ 156269257Sdesstatic int 157269257Sdesprint_hex(char* buf, int max, unsigned int value) 158238106Sdes{ 159269257Sdes const char* h = "0123456789abcdef"; 160269257Sdes int i = 0; 161269257Sdes if(value == 0) { 162269257Sdes if(max > 0) { 163269257Sdes buf[0] = '0'; 164269257Sdes i = 1; 165269257Sdes } 166269257Sdes } else while(value && i < max) { 167269257Sdes buf[i++] = h[value & 0x0f]; 168269257Sdes value >>= 4; 169269257Sdes } 170269257Sdes return i; 171238106Sdes} 172238106Sdes 173269257Sdes/** print long hex into buffer, returns length */ 174269257Sdesstatic int 175269257Sdesprint_hex_l(char* buf, int max, unsigned long value) 176238106Sdes{ 177269257Sdes const char* h = "0123456789abcdef"; 178269257Sdes int i = 0; 179269257Sdes if(value == 0) { 180269257Sdes if(max > 0) { 181269257Sdes buf[0] = '0'; 182269257Sdes i = 1; 183269257Sdes } 184269257Sdes } else while(value && i < max) { 185269257Sdes buf[i++] = h[value & 0x0f]; 186269257Sdes value >>= 4; 187269257Sdes } 188269257Sdes return i; 189238106Sdes} 190238106Sdes 191269257Sdes/** print long long hex into buffer, returns length */ 192269257Sdesstatic int 193269257Sdesprint_hex_ll(char* buf, int max, unsigned long long value) 194269257Sdes{ 195269257Sdes const char* h = "0123456789abcdef"; 196269257Sdes int i = 0; 197269257Sdes if(value == 0) { 198269257Sdes if(max > 0) { 199269257Sdes buf[0] = '0'; 200269257Sdes i = 1; 201269257Sdes } 202269257Sdes } else while(value && i < max) { 203269257Sdes buf[i++] = h[value & 0x0f]; 204269257Sdes value >>= 4; 205269257Sdes } 206269257Sdes return i; 207269257Sdes} 208238106Sdes 209269257Sdes/** copy string into result, reversed */ 210269257Sdesstatic void 211269257Sdesspool_str_rev(char** at, size_t* left, int* ret, const char* buf, int len) 212269257Sdes{ 213269257Sdes int i = len; 214269257Sdes while(i) { 215269257Sdes if(*left > 1) { 216269257Sdes *(*at)++ = buf[--i]; 217269257Sdes (*left)--; 218269257Sdes } else --i; 219269257Sdes (*ret)++; 220269257Sdes } 221269257Sdes} 222238106Sdes 223269257Sdes/** copy string into result */ 224269257Sdesstatic void 225269257Sdesspool_str(char** at, size_t* left, int* ret, const char* buf, int len) 226269257Sdes{ 227269257Sdes int i; 228269257Sdes for(i=0; i<len; i++) { 229269257Sdes if(*left > 1) { 230269257Sdes *(*at)++ = buf[i]; 231269257Sdes (*left)--; 232269257Sdes } 233269257Sdes (*ret)++; 234269257Sdes } 235269257Sdes} 236238106Sdes 237269257Sdes/** print number formatted */ 238269257Sdesstatic void 239269257Sdesprint_num(char** at, size_t* left, int* ret, int minw, int precision, 240269257Sdes int prgiven, int zeropad, int minus, int plus, int space, 241269257Sdes int zero, int negative, char* buf, int len) 242269257Sdes{ 243269257Sdes int w = len; /* excludes minus sign */ 244269257Sdes char s = get_negsign(negative, plus, space); 245269257Sdes if(minus) { 246269257Sdes /* left adjust the number into the field, space padding */ 247269257Sdes /* calc numw = [sign][zeroes][number] */ 248269257Sdes int numw = w; 249269257Sdes if(precision == 0 && zero) numw = 0; 250269257Sdes if(numw < precision) numw = precision; 251269257Sdes if(s) numw++; 252238106Sdes 253269257Sdes /* sign */ 254269257Sdes if(s) print_pad(at, left, ret, s, 1); 255238106Sdes 256269257Sdes /* number */ 257269257Sdes if(precision == 0 && zero) { 258269257Sdes /* "" for the number */ 259269257Sdes } else { 260269257Sdes if(w < precision) 261269257Sdes print_pad(at, left, ret, '0', precision - w); 262269257Sdes spool_str_rev(at, left, ret, buf, len); 263269257Sdes } 264269257Sdes /* spaces */ 265269257Sdes if(numw < minw) 266269257Sdes print_pad(at, left, ret, ' ', minw - numw); 267269257Sdes } else { 268269257Sdes /* pad on the left of the number */ 269269257Sdes /* calculate numw has width of [sign][zeroes][number] */ 270269257Sdes int numw = w; 271269257Sdes if(precision == 0 && zero) numw = 0; 272269257Sdes if(numw < precision) numw = precision; 273269257Sdes if(!prgiven && zeropad && numw < minw) numw = minw; 274269257Sdes else if(s) numw++; 275269257Sdes 276269257Sdes /* pad with spaces */ 277269257Sdes if(numw < minw) 278269257Sdes print_pad(at, left, ret, ' ', minw - numw); 279269257Sdes /* print sign (and one less zeropad if so) */ 280269257Sdes if(s) { 281269257Sdes print_pad(at, left, ret, s, 1); 282269257Sdes numw--; 283269257Sdes } 284269257Sdes /* pad with zeroes */ 285269257Sdes if(w < numw) 286269257Sdes print_pad(at, left, ret, '0', numw - w); 287269257Sdes if(precision == 0 && zero) 288269257Sdes return; 289269257Sdes /* print the characters for the value */ 290269257Sdes spool_str_rev(at, left, ret, buf, len); 291269257Sdes } 292269257Sdes} 293269257Sdes 294269257Sdes/** print %d and %i */ 295269257Sdesstatic void 296269257Sdesprint_num_d(char** at, size_t* left, int* ret, int value, 297269257Sdes int minw, int precision, int prgiven, int zeropad, int minus, 298269257Sdes int plus, int space) 299238106Sdes{ 300269257Sdes char buf[PRINT_DEC_BUFSZ]; 301269257Sdes int negative = (value < 0); 302269257Sdes int zero = (value == 0); 303269257Sdes int len = print_dec(buf, (int)sizeof(buf), 304269257Sdes (unsigned int)(negative?-value:value)); 305269257Sdes print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 306269257Sdes plus, space, zero, negative, buf, len); 307269257Sdes} 308238106Sdes 309269257Sdes/** print %ld and %li */ 310269257Sdesstatic void 311269257Sdesprint_num_ld(char** at, size_t* left, int* ret, long value, 312269257Sdes int minw, int precision, int prgiven, int zeropad, int minus, 313269257Sdes int plus, int space) 314269257Sdes{ 315269257Sdes char buf[PRINT_DEC_BUFSZ]; 316269257Sdes int negative = (value < 0); 317269257Sdes int zero = (value == 0); 318269257Sdes int len = print_dec_l(buf, (int)sizeof(buf), 319269257Sdes (unsigned long)(negative?-value:value)); 320269257Sdes print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 321269257Sdes plus, space, zero, negative, buf, len); 322269257Sdes} 323238106Sdes 324269257Sdes/** print %lld and %lli */ 325269257Sdesstatic void 326269257Sdesprint_num_lld(char** at, size_t* left, int* ret, long long value, 327269257Sdes int minw, int precision, int prgiven, int zeropad, int minus, 328269257Sdes int plus, int space) 329269257Sdes{ 330269257Sdes char buf[PRINT_DEC_BUFSZ]; 331269257Sdes int negative = (value < 0); 332269257Sdes int zero = (value == 0); 333269257Sdes int len = print_dec_ll(buf, (int)sizeof(buf), 334269257Sdes (unsigned long long)(negative?-value:value)); 335269257Sdes print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 336269257Sdes plus, space, zero, negative, buf, len); 337238106Sdes} 338238106Sdes 339269257Sdes/** print %u */ 340269257Sdesstatic void 341269257Sdesprint_num_u(char** at, size_t* left, int* ret, unsigned int value, 342269257Sdes int minw, int precision, int prgiven, int zeropad, int minus, 343269257Sdes int plus, int space) 344238106Sdes{ 345269257Sdes char buf[PRINT_DEC_BUFSZ]; 346269257Sdes int negative = 0; 347269257Sdes int zero = (value == 0); 348269257Sdes int len = print_dec(buf, (int)sizeof(buf), value); 349269257Sdes print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 350269257Sdes plus, space, zero, negative, buf, len); 351269257Sdes} 352238106Sdes 353269257Sdes/** print %lu */ 354269257Sdesstatic void 355269257Sdesprint_num_lu(char** at, size_t* left, int* ret, unsigned long value, 356269257Sdes int minw, int precision, int prgiven, int zeropad, int minus, 357269257Sdes int plus, int space) 358269257Sdes{ 359269257Sdes char buf[PRINT_DEC_BUFSZ]; 360269257Sdes int negative = 0; 361269257Sdes int zero = (value == 0); 362269257Sdes int len = print_dec_l(buf, (int)sizeof(buf), value); 363269257Sdes print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 364269257Sdes plus, space, zero, negative, buf, len); 365269257Sdes} 366238106Sdes 367269257Sdes/** print %llu */ 368269257Sdesstatic void 369269257Sdesprint_num_llu(char** at, size_t* left, int* ret, unsigned long long value, 370269257Sdes int minw, int precision, int prgiven, int zeropad, int minus, 371269257Sdes int plus, int space) 372269257Sdes{ 373269257Sdes char buf[PRINT_DEC_BUFSZ]; 374269257Sdes int negative = 0; 375269257Sdes int zero = (value == 0); 376269257Sdes int len = print_dec_ll(buf, (int)sizeof(buf), value); 377269257Sdes print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 378269257Sdes plus, space, zero, negative, buf, len); 379238106Sdes} 380238106Sdes 381269257Sdes/** print %x */ 382269257Sdesstatic void 383269257Sdesprint_num_x(char** at, size_t* left, int* ret, unsigned int value, 384269257Sdes int minw, int precision, int prgiven, int zeropad, int minus, 385269257Sdes int plus, int space) 386269257Sdes{ 387269257Sdes char buf[PRINT_DEC_BUFSZ]; 388269257Sdes int negative = 0; 389269257Sdes int zero = (value == 0); 390269257Sdes int len = print_hex(buf, (int)sizeof(buf), value); 391269257Sdes print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 392269257Sdes plus, space, zero, negative, buf, len); 393269257Sdes} 394238106Sdes 395269257Sdes/** print %lx */ 396269257Sdesstatic void 397269257Sdesprint_num_lx(char** at, size_t* left, int* ret, unsigned long value, 398269257Sdes int minw, int precision, int prgiven, int zeropad, int minus, 399269257Sdes int plus, int space) 400238106Sdes{ 401269257Sdes char buf[PRINT_DEC_BUFSZ]; 402269257Sdes int negative = 0; 403269257Sdes int zero = (value == 0); 404269257Sdes int len = print_hex_l(buf, (int)sizeof(buf), value); 405269257Sdes print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 406269257Sdes plus, space, zero, negative, buf, len); 407269257Sdes} 408238106Sdes 409269257Sdes/** print %llx */ 410269257Sdesstatic void 411269257Sdesprint_num_llx(char** at, size_t* left, int* ret, unsigned long long value, 412269257Sdes int minw, int precision, int prgiven, int zeropad, int minus, 413269257Sdes int plus, int space) 414269257Sdes{ 415269257Sdes char buf[PRINT_DEC_BUFSZ]; 416269257Sdes int negative = 0; 417269257Sdes int zero = (value == 0); 418269257Sdes int len = print_hex_ll(buf, (int)sizeof(buf), value); 419269257Sdes print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 420269257Sdes plus, space, zero, negative, buf, len); 421269257Sdes} 422238106Sdes 423269257Sdes/** print %llp */ 424269257Sdesstatic void 425269257Sdesprint_num_llp(char** at, size_t* left, int* ret, void* value, 426269257Sdes int minw, int precision, int prgiven, int zeropad, int minus, 427269257Sdes int plus, int space) 428269257Sdes{ 429269257Sdes char buf[PRINT_DEC_BUFSZ]; 430269257Sdes int negative = 0; 431269257Sdes int zero = (value == 0); 432294190Sdes#if defined(SIZE_MAX) && defined(UINT32_MAX) && (UINT32_MAX == SIZE_MAX || INT32_MAX == SIZE_MAX) 433269257Sdes /* avoid warning about upcast on 32bit systems */ 434269257Sdes unsigned long long llvalue = (unsigned long)value; 435269257Sdes#else 436269257Sdes unsigned long long llvalue = (unsigned long long)value; 437269257Sdes#endif 438269257Sdes int len = print_hex_ll(buf, (int)sizeof(buf), llvalue); 439269257Sdes if(zero) { 440269257Sdes buf[0]=')'; 441269257Sdes buf[1]='l'; 442269257Sdes buf[2]='i'; 443269257Sdes buf[3]='n'; 444269257Sdes buf[4]='('; 445269257Sdes len = 5; 446269257Sdes } else { 447269257Sdes /* put '0x' in front of the (reversed) buffer result */ 448269257Sdes if(len < PRINT_DEC_BUFSZ) 449269257Sdes buf[len++] = 'x'; 450269257Sdes if(len < PRINT_DEC_BUFSZ) 451269257Sdes buf[len++] = '0'; 452269257Sdes } 453269257Sdes print_num(at, left, ret, minw, precision, prgiven, zeropad, minus, 454269257Sdes plus, space, zero, negative, buf, len); 455269257Sdes} 456238106Sdes 457269257Sdes#define PRINT_FLOAT_BUFSZ 64 /* xx.yy with 20.20 about the max */ 458269257Sdes/** spool remainder after the decimal point to buffer, in reverse */ 459269257Sdesstatic int 460269257Sdesprint_remainder(char* buf, int max, double r, int prec) 461269257Sdes{ 462269257Sdes unsigned long long cap = 1; 463269257Sdes unsigned long long value; 464269257Sdes int len, i; 465269257Sdes if(prec > 19) prec = 19; /* max we can do */ 466269257Sdes if(max < prec) return 0; 467269257Sdes for(i=0; i<prec; i++) { 468269257Sdes cap *= 10; 469269257Sdes } 470269257Sdes r *= (double)cap; 471269257Sdes value = (unsigned long long)r; 472269257Sdes /* see if we need to round up */ 473269257Sdes if(((unsigned long long)((r - (double)value)*10.0)) >= 5) { 474269257Sdes value++; 475269257Sdes /* that might carry to numbers before the comma, if so, 476269257Sdes * just ignore that rounding. failure because 64bitprintout */ 477269257Sdes if(value >= cap) 478269257Sdes value = cap-1; 479269257Sdes } 480269257Sdes len = print_dec_ll(buf, max, value); 481269257Sdes while(len < prec) { /* pad with zeroes, e.g. if 0.0012 */ 482269257Sdes buf[len++] = '0'; 483269257Sdes } 484269257Sdes if(len < max) 485269257Sdes buf[len++] = '.'; 486269257Sdes return len; 487269257Sdes} 488238106Sdes 489269257Sdes/** spool floating point to buffer */ 490269257Sdesstatic int 491269257Sdesprint_float(char* buf, int max, double value, int prec) 492269257Sdes{ 493269257Sdes /* as xxx.xxx if prec==0, no '.', with prec decimals after . */ 494269257Sdes /* no conversion for NAN and INF, because we do not want to require 495269257Sdes linking with -lm. */ 496269257Sdes /* Thus, the conversions use 64bit integers to convert the numbers, 497269257Sdes * which makes 19 digits before and after the decimal point the max */ 498269257Sdes unsigned long long whole = (unsigned long long)value; 499269257Sdes double remain = value - (double)whole; 500269257Sdes int len = 0; 501269257Sdes if(prec != 0) 502269257Sdes len = print_remainder(buf, max, remain, prec); 503269257Sdes len += print_dec_ll(buf+len, max-len, whole); 504269257Sdes return len; 505269257Sdes} 506238106Sdes 507269257Sdes/** print %f */ 508269257Sdesstatic void 509269257Sdesprint_num_f(char** at, size_t* left, int* ret, double value, 510269257Sdes int minw, int precision, int prgiven, int zeropad, int minus, 511269257Sdes int plus, int space) 512269257Sdes{ 513269257Sdes char buf[PRINT_FLOAT_BUFSZ]; 514269257Sdes int negative = (value < 0); 515269257Sdes int zero = 0; 516269257Sdes int len; 517269257Sdes if(!prgiven) precision = 6; 518269257Sdes len = print_float(buf, (int)sizeof(buf), negative?-value:value, 519269257Sdes precision); 520269257Sdes print_num(at, left, ret, minw, 1, 0, zeropad, minus, 521269257Sdes plus, space, zero, negative, buf, len); 522269257Sdes} 523238106Sdes 524269257Sdes/* rudimentary %g support */ 525269257Sdesstatic int 526269257Sdesprint_float_g(char* buf, int max, double value, int prec) 527269257Sdes{ 528269257Sdes unsigned long long whole = (unsigned long long)value; 529269257Sdes double remain = value - (double)whole; 530269257Sdes int before = 0; 531269257Sdes int len = 0; 532238106Sdes 533269257Sdes /* number of digits before the decimal point */ 534269257Sdes while(whole > 0) { 535269257Sdes before++; 536269257Sdes whole /= 10; 537269257Sdes } 538269257Sdes whole = (unsigned long long)value; 539238106Sdes 540269257Sdes if(prec > before && remain != 0.0) { 541269257Sdes /* see if the last decimals are zero, if so, skip them */ 542269257Sdes len = print_remainder(buf, max, remain, prec-before); 543269257Sdes while(len > 0 && buf[0]=='0') { 544269257Sdes memmove(buf, buf+1, --len); 545269257Sdes } 546269257Sdes } 547269257Sdes len += print_dec_ll(buf+len, max-len, whole); 548269257Sdes return len; 549238106Sdes} 550238106Sdes 551269257Sdes 552269257Sdes/** print %g */ 553269257Sdesstatic void 554269257Sdesprint_num_g(char** at, size_t* left, int* ret, double value, 555269257Sdes int minw, int precision, int prgiven, int zeropad, int minus, 556269257Sdes int plus, int space) 557238106Sdes{ 558269257Sdes char buf[PRINT_FLOAT_BUFSZ]; 559269257Sdes int negative = (value < 0); 560269257Sdes int zero = 0; 561269257Sdes int len; 562269257Sdes if(!prgiven) precision = 6; 563269257Sdes if(precision == 0) precision = 1; 564269257Sdes len = print_float_g(buf, (int)sizeof(buf), negative?-value:value, 565269257Sdes precision); 566269257Sdes print_num(at, left, ret, minw, 1, 0, zeropad, minus, 567269257Sdes plus, space, zero, negative, buf, len); 568269257Sdes} 569238106Sdes 570238106Sdes 571269257Sdes/** strnlen (compat implementation) */ 572269257Sdesstatic int 573269257Sdesmy_strnlen(const char* s, int max) 574269257Sdes{ 575269257Sdes int i; 576269257Sdes for(i=0; i<max; i++) 577269257Sdes if(s[i]==0) 578269257Sdes return i; 579269257Sdes return max; 580238106Sdes} 581238106Sdes 582269257Sdes/** print %s */ 583269257Sdesstatic void 584269257Sdesprint_str(char** at, size_t* left, int* ret, char* s, 585269257Sdes int minw, int precision, int prgiven, int minus) 586238106Sdes{ 587269257Sdes int w; 588269257Sdes /* with prec: no more than x characters from this string, stop at 0 */ 589269257Sdes if(prgiven) 590269257Sdes w = my_strnlen(s, precision); 591269257Sdes else w = (int)strlen(s); /* up to the nul */ 592269257Sdes if(w < minw && !minus) 593269257Sdes print_pad(at, left, ret, ' ', minw - w); 594269257Sdes spool_str(at, left, ret, s, w); 595269257Sdes if(w < minw && minus) 596269257Sdes print_pad(at, left, ret, ' ', minw - w); 597269257Sdes} 598238106Sdes 599269257Sdes/** print %c */ 600269257Sdesstatic void 601269257Sdesprint_char(char** at, size_t* left, int* ret, int c, 602269257Sdes int minw, int minus) 603269257Sdes{ 604269257Sdes if(1 < minw && !minus) 605269257Sdes print_pad(at, left, ret, ' ', minw - 1); 606269257Sdes print_pad(at, left, ret, c, 1); 607269257Sdes if(1 < minw && minus) 608269257Sdes print_pad(at, left, ret, ' ', minw - 1); 609238106Sdes} 610238106Sdes 611269257Sdes 612269257Sdes/** 613269257Sdes * Print to string. 614269257Sdes * str: string buffer for result. result will be null terminated. 615269257Sdes * size: size of the buffer. null is put inside buffer. 616269257Sdes * format: printf format string. 617269257Sdes * arg: '...' arguments to print. 618269257Sdes * returns number of characters. a null is printed after this. 619269257Sdes * return number of bytes that would have been written 620269257Sdes * if the buffer had been large enough. 621269257Sdes * 622269257Sdes * supported format specifiers: 623269257Sdes * %s, %u, %d, %x, %i, %f, %g, %c, %p, %n. 624269257Sdes * length: l, ll (for d, u, x). 625269257Sdes * precision: 6.6d (for d, u, x) 626269257Sdes * %f, %g precisions, 0.3f 627269257Sdes * %20s, '.*s' 628269257Sdes * and %%. 629269257Sdes */ 630269257Sdesint vsnprintf(char* str, size_t size, const char* format, va_list arg) 631238106Sdes{ 632269257Sdes char* at = str; 633269257Sdes size_t left = size; 634269257Sdes int ret = 0; 635269257Sdes const char* fmt = format; 636269257Sdes int conv, minw, precision, prgiven, zeropad, minus, plus, space, length; 637269257Sdes while(*fmt) { 638269257Sdes /* copy string before % */ 639269257Sdes while(*fmt && *fmt!='%') { 640269257Sdes if(left > 1) { 641269257Sdes *at++ = *fmt++; 642269257Sdes left--; 643269257Sdes } else fmt++; 644269257Sdes ret++; 645269257Sdes } 646269257Sdes 647269257Sdes /* see if we are at end */ 648269257Sdes if(!*fmt) break; 649238106Sdes 650269257Sdes /* fetch next argument % designation from format string */ 651269257Sdes fmt++; /* skip the '%' */ 652238106Sdes 653269257Sdes /********************************/ 654269257Sdes /* get the argument designation */ 655269257Sdes /********************************/ 656269257Sdes /* we must do this vararg stuff inside this function for 657269257Sdes * portability. Hence, get_designation, and print_designation 658269257Sdes * are not their own functions. */ 659269257Sdes 660269257Sdes /* printout designation: 661269257Sdes * conversion specifier: x, d, u, s, c, n, m, p 662269257Sdes * flags: # not supported 663269257Sdes * 0 zeropad (on the left) 664269257Sdes * - left adjust (right by default) 665269257Sdes * ' ' printspace for positive number (in - position). 666269257Sdes * + alwayssign 667269257Sdes * fieldwidth: [1-9][0-9]* minimum field width. 668269257Sdes * if this is * then type int next argument specifies the minwidth. 669269257Sdes * if this is negative, the - flag is set (with positive width). 670269257Sdes * precision: period[digits]*, %.2x. 671269257Sdes * if this is * then type int next argument specifies the precision. 672269257Sdes * just '.' or negative value means precision=0. 673269257Sdes * this is mindigits to print for d, i, u, x 674269257Sdes * this is aftercomma digits for f 675269257Sdes * this is max number significant digits for g 676269257Sdes * maxnumber characters to be printed for s 677269257Sdes * length: 0-none (int), 1-l (long), 2-ll (long long) 678269257Sdes * notsupported: hh (char), h (short), L (long double), q, j, z, t 679269257Sdes * Does not support %m$ and *m$ argument designation as array indices. 680269257Sdes * Does not support %#x 681269257Sdes * 682269257Sdes */ 683269257Sdes minw = 0; 684269257Sdes precision = 1; 685269257Sdes prgiven = 0; 686269257Sdes zeropad = 0; 687269257Sdes minus = 0; 688269257Sdes plus = 0; 689269257Sdes space = 0; 690269257Sdes length = 0; 691269257Sdes 692269257Sdes /* get flags in any order */ 693269257Sdes for(;;) { 694269257Sdes if(*fmt == '0') 695269257Sdes zeropad = 1; 696269257Sdes else if(*fmt == '-') 697269257Sdes minus = 1; 698269257Sdes else if(*fmt == '+') 699269257Sdes plus = 1; 700269257Sdes else if(*fmt == ' ') 701269257Sdes space = 1; 702269257Sdes else break; 703269257Sdes fmt++; 704269257Sdes } 705269257Sdes 706269257Sdes /* field width */ 707269257Sdes if(*fmt == '*') { 708269257Sdes fmt++; /* skip char */ 709269257Sdes minw = va_arg(arg, int); 710269257Sdes if(minw < 0) { 711269257Sdes minus = 1; 712269257Sdes minw = -minw; 713269257Sdes } 714269257Sdes } else while(*fmt >= '0' && *fmt <= '9') { 715269257Sdes minw = minw*10 + (*fmt++)-'0'; 716269257Sdes } 717269257Sdes 718269257Sdes /* precision */ 719269257Sdes if(*fmt == '.') { 720269257Sdes fmt++; /* skip period */ 721269257Sdes prgiven = 1; 722269257Sdes precision = 0; 723269257Sdes if(*fmt == '*') { 724269257Sdes fmt++; /* skip char */ 725269257Sdes precision = va_arg(arg, int); 726269257Sdes if(precision < 0) 727269257Sdes precision = 0; 728269257Sdes } else while(*fmt >= '0' && *fmt <= '9') { 729269257Sdes precision = precision*10 + (*fmt++)-'0'; 730269257Sdes } 731269257Sdes } 732269257Sdes 733269257Sdes /* length */ 734269257Sdes if(*fmt == 'l') { 735269257Sdes fmt++; /* skip char */ 736269257Sdes length = 1; 737269257Sdes if(*fmt == 'l') { 738269257Sdes fmt++; /* skip char */ 739269257Sdes length = 2; 740269257Sdes } 741269257Sdes } 742269257Sdes 743269257Sdes /* get the conversion */ 744269257Sdes if(!*fmt) conv = 0; 745269257Sdes else conv = *fmt++; 746269257Sdes 747269257Sdes /***********************************/ 748269257Sdes /* print that argument designation */ 749269257Sdes /***********************************/ 750269257Sdes switch(conv) { 751269257Sdes case 'i': 752269257Sdes case 'd': 753269257Sdes if(length == 0) 754269257Sdes print_num_d(&at, &left, &ret, va_arg(arg, int), 755269257Sdes minw, precision, prgiven, zeropad, minus, plus, space); 756269257Sdes else if(length == 1) 757269257Sdes print_num_ld(&at, &left, &ret, va_arg(arg, long), 758269257Sdes minw, precision, prgiven, zeropad, minus, plus, space); 759269257Sdes else if(length == 2) 760269257Sdes print_num_lld(&at, &left, &ret, 761269257Sdes va_arg(arg, long long), 762269257Sdes minw, precision, prgiven, zeropad, minus, plus, space); 763269257Sdes break; 764269257Sdes case 'u': 765269257Sdes if(length == 0) 766269257Sdes print_num_u(&at, &left, &ret, 767269257Sdes va_arg(arg, unsigned int), 768269257Sdes minw, precision, prgiven, zeropad, minus, plus, space); 769269257Sdes else if(length == 1) 770269257Sdes print_num_lu(&at, &left, &ret, 771269257Sdes va_arg(arg, unsigned long), 772269257Sdes minw, precision, prgiven, zeropad, minus, plus, space); 773269257Sdes else if(length == 2) 774269257Sdes print_num_llu(&at, &left, &ret, 775269257Sdes va_arg(arg, unsigned long long), 776269257Sdes minw, precision, prgiven, zeropad, minus, plus, space); 777269257Sdes break; 778269257Sdes case 'x': 779269257Sdes if(length == 0) 780269257Sdes print_num_x(&at, &left, &ret, 781269257Sdes va_arg(arg, unsigned int), 782269257Sdes minw, precision, prgiven, zeropad, minus, plus, space); 783269257Sdes else if(length == 1) 784269257Sdes print_num_lx(&at, &left, &ret, 785269257Sdes va_arg(arg, unsigned long), 786269257Sdes minw, precision, prgiven, zeropad, minus, plus, space); 787269257Sdes else if(length == 2) 788269257Sdes print_num_llx(&at, &left, &ret, 789269257Sdes va_arg(arg, unsigned long long), 790269257Sdes minw, precision, prgiven, zeropad, minus, plus, space); 791269257Sdes break; 792269257Sdes case 's': 793269257Sdes print_str(&at, &left, &ret, va_arg(arg, char*), 794269257Sdes minw, precision, prgiven, minus); 795269257Sdes break; 796269257Sdes case 'c': 797269257Sdes print_char(&at, &left, &ret, va_arg(arg, int), 798269257Sdes minw, minus); 799269257Sdes break; 800269257Sdes case 'n': 801269257Sdes *va_arg(arg, int*) = ret; 802269257Sdes break; 803269257Sdes case 'm': 804269257Sdes print_str(&at, &left, &ret, strerror(errno), 805269257Sdes minw, precision, prgiven, minus); 806269257Sdes break; 807269257Sdes case 'p': 808269257Sdes print_num_llp(&at, &left, &ret, va_arg(arg, void*), 809269257Sdes minw, precision, prgiven, zeropad, minus, plus, space); 810269257Sdes break; 811269257Sdes case '%': 812269257Sdes print_pad(&at, &left, &ret, '%', 1); 813269257Sdes break; 814269257Sdes case 'f': 815269257Sdes print_num_f(&at, &left, &ret, va_arg(arg, double), 816269257Sdes minw, precision, prgiven, zeropad, minus, plus, space); 817269257Sdes break; 818269257Sdes case 'g': 819269257Sdes print_num_g(&at, &left, &ret, va_arg(arg, double), 820269257Sdes minw, precision, prgiven, zeropad, minus, plus, space); 821269257Sdes break; 822269257Sdes /* unknown */ 823269257Sdes default: 824269257Sdes case 0: break; 825269257Sdes } 826269257Sdes } 827269257Sdes 828269257Sdes /* zero terminate */ 829269257Sdes if(left > 0) 830269257Sdes *at = 0; 831269257Sdes return ret; 832238106Sdes} 833238106Sdes 834269257Sdes#ifdef SNPRINTF_TEST 835238106Sdes 836269257Sdes/** do tests */ 837269257Sdes#undef snprintf 838269257Sdes#define DOTEST(bufsz, result, retval, ...) do { \ 839269257Sdes char buf[bufsz]; \ 840269257Sdes printf("now test %s\n", #__VA_ARGS__); \ 841269257Sdes int r=my_snprintf(buf, sizeof(buf), __VA_ARGS__); \ 842269257Sdes if(r != retval || strcmp(buf, result) != 0) { \ 843269257Sdes printf("error test(%s) was \"%s\":%d\n", \ 844269257Sdes ""#bufsz", "#result", "#retval", "#__VA_ARGS__, \ 845269257Sdes buf, r); \ 846269257Sdes exit(1); \ 847269257Sdes } \ 848269257Sdes r=snprintf(buf, sizeof(buf), __VA_ARGS__); \ 849269257Sdes if(r != retval || strcmp(buf, result) != 0) { \ 850269257Sdes printf("error test(%s) differs with system, \"%s\":%d\n", \ 851269257Sdes ""#bufsz", "#result", "#retval", "#__VA_ARGS__, \ 852269257Sdes buf, r); \ 853269257Sdes exit(1); \ 854269257Sdes } \ 855269257Sdes printf("test(\"%s\":%d) passed\n", buf, r); \ 856269257Sdes } while(0); 857238106Sdes 858269257Sdes/** test program */ 859269257Sdesint main(void) 860269257Sdes{ 861269257Sdes int x = 0; 862238106Sdes 863269257Sdes /* bufsize, expectedstring, expectedretval, snprintf arguments */ 864269257Sdes DOTEST(1024, "hello", 5, "hello"); 865269257Sdes DOTEST(1024, "h", 1, "h"); 866269257Sdes /* warning from gcc for format string, but it does work 867269257Sdes * DOTEST(1024, "", 0, ""); */ 868238106Sdes 869269257Sdes DOTEST(3, "he", 5, "hello"); 870269257Sdes DOTEST(1, "", 7, "%d", 7823089); 871238106Sdes 872269257Sdes /* test positive numbers */ 873269257Sdes DOTEST(1024, "0", 1, "%d", 0); 874269257Sdes DOTEST(1024, "1", 1, "%d", 1); 875269257Sdes DOTEST(1024, "9", 1, "%d", 9); 876269257Sdes DOTEST(1024, "15", 2, "%d", 15); 877269257Sdes DOTEST(1024, "ab15cd", 6, "ab%dcd", 15); 878269257Sdes DOTEST(1024, "167", 3, "%d", 167); 879269257Sdes DOTEST(1024, "7823089", 7, "%d", 7823089); 880269257Sdes DOTEST(1024, " 12", 3, "%3d", 12); 881269257Sdes DOTEST(1024, "012", 3, "%.3d", 12); 882269257Sdes DOTEST(1024, "012", 3, "%3.3d", 12); 883269257Sdes DOTEST(1024, "012", 3, "%03d", 12); 884269257Sdes DOTEST(1024, " 012", 4, "%4.3d", 12); 885269257Sdes DOTEST(1024, "", 0, "%.0d", 0); 886238106Sdes 887269257Sdes /* test negative numbers */ 888269257Sdes DOTEST(1024, "-1", 2, "%d", -1); 889269257Sdes DOTEST(1024, "-12", 3, "%3d", -12); 890269257Sdes DOTEST(1024, " -2", 3, "%3d", -2); 891269257Sdes DOTEST(1024, "-012", 4, "%.3d", -12); 892269257Sdes DOTEST(1024, "-012", 4, "%3.3d", -12); 893269257Sdes DOTEST(1024, "-012", 4, "%4.3d", -12); 894269257Sdes DOTEST(1024, " -012", 5, "%5.3d", -12); 895269257Sdes DOTEST(1024, "-12", 3, "%03d", -12); 896269257Sdes DOTEST(1024, "-02", 3, "%03d", -2); 897269257Sdes DOTEST(1024, "-15", 3, "%d", -15); 898269257Sdes DOTEST(1024, "-7307", 5, "%d", -7307); 899269257Sdes DOTEST(1024, "-12 ", 5, "%-5d", -12); 900269257Sdes DOTEST(1024, "-00012", 6, "%-.5d", -12); 901238106Sdes 902269257Sdes /* test + and space flags */ 903269257Sdes DOTEST(1024, "+12", 3, "%+d", 12); 904269257Sdes DOTEST(1024, " 12", 3, "% d", 12); 905238106Sdes 906269257Sdes /* test %u */ 907269257Sdes DOTEST(1024, "12", 2, "%u", 12); 908269257Sdes DOTEST(1024, "0", 1, "%u", 0); 909269257Sdes DOTEST(1024, "4294967295", 10, "%u", 0xffffffff); 910238106Sdes 911269257Sdes /* test %x */ 912269257Sdes DOTEST(1024, "0", 1, "%x", 0); 913269257Sdes DOTEST(1024, "c", 1, "%x", 12); 914269257Sdes DOTEST(1024, "12ab34cd", 8, "%x", 0x12ab34cd); 915238106Sdes 916269257Sdes /* test %llu, %lld */ 917269257Sdes DOTEST(1024, "18446744073709551615", 20, "%llu", 918269257Sdes (long long)0xffffffffffffffff); 919269257Sdes DOTEST(1024, "-9223372036854775808", 20, "%lld", 920269257Sdes (long long)0x8000000000000000); 921269257Sdes DOTEST(1024, "9223372036854775808", 19, "%llu", 922269257Sdes (long long)0x8000000000000000); 923238106Sdes 924269257Sdes /* test %s */ 925269257Sdes DOTEST(1024, "hello", 5, "%s", "hello"); 926269257Sdes DOTEST(1024, " hello", 10, "%10s", "hello"); 927269257Sdes DOTEST(1024, "hello ", 10, "%-10s", "hello"); 928269257Sdes DOTEST(1024, "he", 2, "%.2s", "hello"); 929269257Sdes DOTEST(1024, " he", 4, "%4.2s", "hello"); 930269257Sdes DOTEST(1024, " h", 4, "%4.2s", "h"); 931238106Sdes 932269257Sdes /* test %c */ 933269257Sdes DOTEST(1024, "a", 1, "%c", 'a'); 934269257Sdes /* warning from gcc for format string, but it does work 935269257Sdes DOTEST(1024, " a", 5, "%5c", 'a'); 936269257Sdes DOTEST(1024, "a", 1, "%.0c", 'a'); */ 937238106Sdes 938269257Sdes /* test %n */ 939269257Sdes DOTEST(1024, "hello", 5, "hello%n", &x); 940269257Sdes if(x != 5) { printf("the %%n failed\n"); exit(1); } 941238106Sdes 942269257Sdes /* test %m */ 943269257Sdes errno = 0; 944269257Sdes DOTEST(1024, "Success", 7, "%m"); 945238106Sdes 946269257Sdes /* test %p */ 947269257Sdes DOTEST(1024, "0x10", 4, "%p", (void*)0x10); 948269257Sdes DOTEST(1024, "(nil)", 5, "%p", (void*)0x0); 949238106Sdes 950269257Sdes /* test %% */ 951269257Sdes DOTEST(1024, "%", 1, "%%"); 952238106Sdes 953269257Sdes /* test %f */ 954269257Sdes DOTEST(1024, "0.000000", 8, "%f", 0.0); 955269257Sdes DOTEST(1024, "0.00", 4, "%.2f", 0.0); 956269257Sdes /* differs, "-0.00" DOTEST(1024, "0.00", 4, "%.2f", -0.0); */ 957269257Sdes DOTEST(1024, "234.00", 6, "%.2f", 234.005); 958269257Sdes DOTEST(1024, "8973497.1246", 12, "%.4f", 8973497.12456); 959269257Sdes DOTEST(1024, "-12.000000", 10, "%f", -12.0); 960269257Sdes DOTEST(1024, "6", 1, "%.0f", 6.0); 961238106Sdes 962269257Sdes DOTEST(1024, "6", 1, "%g", 6.0); 963269257Sdes DOTEST(1024, "6.1", 3, "%g", 6.1); 964269257Sdes DOTEST(1024, "6.15", 4, "%g", 6.15); 965238106Sdes 966269257Sdes /* These format strings are from the code of NSD, Unbound, ldns */ 967238106Sdes 968269257Sdes DOTEST(1024, "abcdef", 6, "%s", "abcdef"); 969269257Sdes DOTEST(1024, "005", 3, "%03u", 5); 970269257Sdes DOTEST(1024, "12345", 5, "%03u", 12345); 971269257Sdes DOTEST(1024, "5", 1, "%d", 5); 972269257Sdes DOTEST(1024, "(nil)", 5, "%p", NULL); 973269257Sdes DOTEST(1024, "12345", 5, "%ld", (long)12345); 974269257Sdes DOTEST(1024, "12345", 5, "%lu", (long)12345); 975269257Sdes DOTEST(1024, " 12345", 12, "%12u", (unsigned)12345); 976269257Sdes DOTEST(1024, "12345", 5, "%u", (unsigned)12345); 977269257Sdes DOTEST(1024, "12345", 5, "%llu", (unsigned long long)12345); 978269257Sdes DOTEST(1024, "12345", 5, "%x", 0x12345); 979269257Sdes DOTEST(1024, "12345", 5, "%llx", (long long)0x12345); 980269257Sdes DOTEST(1024, "012345", 6, "%6.6d", 12345); 981269257Sdes DOTEST(1024, "012345", 6, "%6.6u", 12345); 982269257Sdes DOTEST(1024, "1234.54", 7, "%g", 1234.54); 983269257Sdes DOTEST(1024, "123456789.54", 12, "%.12g", 123456789.54); 984269257Sdes DOTEST(1024, "3456789123456.54", 16, "%.16g", 3456789123456.54); 985269257Sdes /* %24g does not work with 24 digits, not enough accuracy, 986269257Sdes * the first 16 digits are correct */ 987269257Sdes DOTEST(1024, "12345", 5, "%3.3d", 12345); 988269257Sdes DOTEST(1024, "000", 3, "%3.3d", 0); 989269257Sdes DOTEST(1024, "001", 3, "%3.3d", 1); 990269257Sdes DOTEST(1024, "012", 3, "%3.3d", 12); 991269257Sdes DOTEST(1024, "-012", 4, "%3.3d", -12); 992269257Sdes DOTEST(1024, "he", 2, "%.2s", "hello"); 993269257Sdes DOTEST(1024, "helloworld", 10, "%s%s", "hello", "world"); 994269257Sdes DOTEST(1024, "he", 2, "%.*s", 2, "hello"); 995269257Sdes DOTEST(1024, " hello", 7, "%*s", 7, "hello"); 996269257Sdes DOTEST(1024, "hello ", 7, "%*s", -7, "hello"); 997269257Sdes DOTEST(1024, "0", 1, "%c", '0'); 998269257Sdes DOTEST(1024, "A", 1, "%c", 'A'); 999269257Sdes DOTEST(1024, "", 1, "%c", 0); 1000269257Sdes DOTEST(1024, "\010", 1, "%c", 8); 1001269257Sdes DOTEST(1024, "%", 1, "%%"); 1002269257Sdes DOTEST(1024, "0a", 2, "%02x", 0x0a); 1003269257Sdes DOTEST(1024, "bd", 2, "%02x", 0xbd); 1004269257Sdes DOTEST(1024, "12", 2, "%02ld", (long)12); 1005269257Sdes DOTEST(1024, "02", 2, "%02ld", (long)2); 1006269257Sdes DOTEST(1024, "02", 2, "%02u", (unsigned)2); 1007269257Sdes DOTEST(1024, "765432", 6, "%05u", (unsigned)765432); 1008269257Sdes DOTEST(1024, "10.234", 6, "%0.3f", 10.23421); 1009269257Sdes DOTEST(1024, "123456.234", 10, "%0.3f", 123456.23421); 1010269257Sdes DOTEST(1024, "123456789.234", 13, "%0.3f", 123456789.23421); 1011269257Sdes DOTEST(1024, "123456.23", 9, "%.2f", 123456.23421); 1012269257Sdes DOTEST(1024, "123456", 6, "%.0f", 123456.23421); 1013269257Sdes DOTEST(1024, "0123", 4, "%.4x", 0x0123); 1014269257Sdes DOTEST(1024, "00000123", 8, "%.8x", 0x0123); 1015269257Sdes DOTEST(1024, "ffeb0cde", 8, "%.8x", 0xffeb0cde); 1016269257Sdes DOTEST(1024, " 987654321", 10, "%10lu", (unsigned long)987654321); 1017269257Sdes DOTEST(1024, " 987654321", 12, "%12lu", (unsigned long)987654321); 1018269257Sdes DOTEST(1024, "987654321", 9, "%i", 987654321); 1019269257Sdes DOTEST(1024, "-87654321", 9, "%i", -87654321); 1020269257Sdes DOTEST(1024, "hello ", 16, "%-16s", "hello"); 1021269257Sdes DOTEST(1024, " ", 16, "%-16s", ""); 1022269257Sdes DOTEST(1024, "a ", 16, "%-16s", "a"); 1023269257Sdes DOTEST(1024, "foobarfoobar ", 16, "%-16s", "foobarfoobar"); 1024269257Sdes DOTEST(1024, "foobarfoobarfoobar", 18, "%-16s", "foobarfoobarfoobar"); 1025238106Sdes 1026269257Sdes /* combined expressions */ 1027269257Sdes DOTEST(1024, "foo 1.0 size 512 edns", 21, 1028269257Sdes "foo %s size %d %s%s", "1.0", 512, "", "edns"); 1029269257Sdes DOTEST(15, "foo 1.0 size 5", 21, 1030269257Sdes "foo %s size %d %s%s", "1.0", 512, "", "edns"); 1031269257Sdes DOTEST(1024, "packet 1203ceff id", 18, 1032269257Sdes "packet %2.2x%2.2x%2.2x%2.2x id", 0x12, 0x03, 0xce, 0xff); 1033269257Sdes DOTEST(1024, "/tmp/testbound_123abcd.tmp", 26, "/tmp/testbound_%u%s%s.tmp", 123, "ab", "cd"); 1034269257Sdes 1035269257Sdes return 0; 1036238106Sdes} 1037238106Sdes#endif /* SNPRINTF_TEST */ 1038