snprintf.c revision 72445
118730Ssteve/* 294589Sobrien * Copyright (c) 1995-2000 Kungliga Tekniska H�gskolan 350477Speter * (Royal Institute of Technology, Stockholm, Sweden). 41590Srgrimes * All rights reserved. 51590Srgrimes * 6143657Sharti * Redistribution and use in source and binary forms, with or without 7146056Sharti * modification, are permitted provided that the following conditions 8146574Sharti * are met: 9146574Sharti * 101590Srgrimes * 1. Redistributions of source code must retain the above copyright 11139112Sru * notice, this list of conditions and the following disclaimer. 1276801Ssobomax * 13228157Sfjoe * 2. Redistributions in binary form must reproduce the above copyright 14228157Sfjoe * notice, this list of conditions and the following disclaimer in the 15228157Sfjoe * documentation and/or other materials provided with the distribution. 16134843Sjmg * 17104475Sphk * 3. Neither the name of the Institute nor the names of its contributors 18104475Sphk * may be used to endorse or promote products derived from this software 19146608Sharti * without specific prior written permission. 20146608Sharti * 21146608Sharti * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22146608Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23146608Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24146608Sharti * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25146579Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2697121Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27145616Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28150598Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29145616Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30145616Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31145612Sharti * SUCH DAMAGE. 32145612Sharti */ 33145612Sharti 34145616Sharti#ifdef HAVE_CONFIG_H 35145616Sharti#include <config.h> 36145612ShartiRCSID("$Id: snprintf.c,v 1.28 2000/12/15 14:04:42 joda Exp $"); 37145612Sharti#endif 38145612Sharti#include <stdio.h> 39145612Sharti#include <stdarg.h> 40145612Sharti#include <stdlib.h> 41145616Sharti#include <string.h> 42145616Sharti#include <ctype.h> 43145612Sharti#include <roken.h> 44145612Sharti 45145612Shartienum format_flags { 46145612Sharti minus_flag = 1, 47145612Sharti plus_flag = 2, 48145612Sharti space_flag = 4, 49145616Sharti alternate_flag = 8, 50145612Sharti zero_flag = 16 51145612Sharti}; 52145612Sharti 53145614Sharti/* 54145612Sharti * Common state 55145612Sharti */ 56145612Sharti 57145612Shartistruct state { 58145612Sharti unsigned char *str; 59145612Sharti unsigned char *s; 60145612Sharti unsigned char *theend; 61145683Sharti size_t sz; 62145612Sharti size_t max_sz; 63145612Sharti int (*append_char)(struct state *, unsigned char); 64145612Sharti int (*reserve)(struct state *, size_t); 65145612Sharti /* XXX - methods */ 66145612Sharti}; 67145612Sharti 68145612Sharti#ifndef HAVE_VSNPRINTF 69145612Shartistatic int 70145612Shartisn_reserve (struct state *state, size_t n) 71145612Sharti{ 72145612Sharti return state->s + n > state->theend; 73145612Sharti} 74145612Sharti 75145612Shartistatic int 76145612Shartisn_append_char (struct state *state, unsigned char c) 77145612Sharti{ 78145612Sharti if (sn_reserve (state, 1)) { 79145616Sharti return 1; 80145616Sharti } else { 81145616Sharti *state->s++ = c; 82145616Sharti return 0; 83145616Sharti } 84145616Sharti} 85145616Sharti#endif 86145616Sharti 87145616Shartistatic int 88145616Shartias_reserve (struct state *state, size_t n) 89145616Sharti{ 90145616Sharti if (state->s + n > state->theend) { 91145616Sharti int off = state->s - state->str; 92145616Sharti unsigned char *tmp; 93145616Sharti 94145616Sharti if (state->max_sz && state->sz >= state->max_sz) 95145616Sharti return 1; 96145683Sharti 97145612Sharti state->sz = max(state->sz * 2, state->sz + n); 9864739Sgreen if (state->max_sz) 9964739Sgreen state->sz = min(state->sz, state->max_sz); 10064739Sgreen tmp = realloc (state->str, state->sz); 10164739Sgreen if (tmp == NULL) 10264739Sgreen return 1; 10364739Sgreen state->str = tmp; 104146571Sharti state->s = state->str + off; 105146571Sharti state->theend = state->str + state->sz - 1; 10664739Sgreen } 10764739Sgreen return 0; 10864739Sgreen} 10964739Sgreen 1101590Srgrimesstatic int 111as_append_char (struct state *state, unsigned char c) 112{ 113 if(as_reserve (state, 1)) 114 return 1; 115 else { 116 *state->s++ = c; 117 return 0; 118 } 119} 120 121static int 122append_number(struct state *state, 123 unsigned long num, unsigned base, char *rep, 124 int width, int prec, int flags, int minusp) 125{ 126 int len = 0; 127 int i; 128 129 /* given precision, ignore zero flag */ 130 if(prec != -1) 131 flags &= ~zero_flag; 132 else 133 prec = 1; 134 /* zero value with zero precision -> "" */ 135 if(prec == 0 && num == 0) 136 return 0; 137 do{ 138 if((*state->append_char)(state, rep[num % base])) 139 return 1; 140 len++; 141 num /= base; 142 }while(num); 143 prec -= len; 144 /* pad with prec zeros */ 145 while(prec-- > 0){ 146 if((*state->append_char)(state, '0')) 147 return 1; 148 len++; 149 } 150 /* add length of alternate prefix (added later) to len */ 151 if(flags & alternate_flag && (base == 16 || base == 8)) 152 len += base / 8; 153 /* pad with zeros */ 154 if(flags & zero_flag){ 155 width -= len; 156 if(minusp || (flags & space_flag) || (flags & plus_flag)) 157 width--; 158 while(width-- > 0){ 159 if((*state->append_char)(state, '0')) 160 return 1; 161 len++; 162 } 163 } 164 /* add alternate prefix */ 165 if(flags & alternate_flag && (base == 16 || base == 8)){ 166 if(base == 16) 167 if((*state->append_char)(state, rep[10] + 23)) /* XXX */ 168 return 1; 169 if((*state->append_char)(state, '0')) 170 return 1; 171 } 172 /* add sign */ 173 if(minusp){ 174 if((*state->append_char)(state, '-')) 175 return 1; 176 len++; 177 } else if(flags & plus_flag) { 178 if((*state->append_char)(state, '+')) 179 return 1; 180 len++; 181 } else if(flags & space_flag) { 182 if((*state->append_char)(state, ' ')) 183 return 1; 184 len++; 185 } 186 if(flags & minus_flag) 187 /* swap before padding with spaces */ 188 for(i = 0; i < len / 2; i++){ 189 char c = state->s[-i-1]; 190 state->s[-i-1] = state->s[-len+i]; 191 state->s[-len+i] = c; 192 } 193 width -= len; 194 while(width-- > 0){ 195 if((*state->append_char)(state, ' ')) 196 return 1; 197 len++; 198 } 199 if(!(flags & minus_flag)) 200 /* swap after padding with spaces */ 201 for(i = 0; i < len / 2; i++){ 202 char c = state->s[-i-1]; 203 state->s[-i-1] = state->s[-len+i]; 204 state->s[-len+i] = c; 205 } 206 207 return 0; 208} 209 210static int 211append_string (struct state *state, 212 unsigned char *arg, 213 int width, 214 int prec, 215 int flags) 216{ 217 if(arg == NULL) 218 arg = (unsigned char*)"(null)"; 219 220 if(prec != -1) 221 width -= prec; 222 else 223 width -= strlen((char *)arg); 224 if(!(flags & minus_flag)) 225 while(width-- > 0) 226 if((*state->append_char) (state, ' ')) 227 return 1; 228 if (prec != -1) { 229 while (*arg && prec--) 230 if ((*state->append_char) (state, *arg++)) 231 return 1; 232 } else { 233 while (*arg) 234 if ((*state->append_char) (state, *arg++)) 235 return 1; 236 } 237 if(flags & minus_flag) 238 while(width-- > 0) 239 if((*state->append_char) (state, ' ')) 240 return 1; 241 return 0; 242} 243 244static int 245append_char(struct state *state, 246 unsigned char arg, 247 int width, 248 int flags) 249{ 250 while(!(flags & minus_flag) && --width > 0) 251 if((*state->append_char) (state, ' ')) 252 return 1; 253 254 if((*state->append_char) (state, arg)) 255 return 1; 256 while((flags & minus_flag) && --width > 0) 257 if((*state->append_char) (state, ' ')) 258 return 1; 259 260 return 0; 261} 262 263/* 264 * This can't be made into a function... 265 */ 266 267#define PARSE_INT_FORMAT(res, arg, unsig) \ 268if (long_flag) \ 269 res = (unsig long)va_arg(arg, unsig long); \ 270else if (short_flag) \ 271 res = (unsig short)va_arg(arg, unsig int); \ 272else \ 273 res = (unsig int)va_arg(arg, unsig int) 274 275/* 276 * zyxprintf - return 0 or -1 277 */ 278 279static int 280xyzprintf (struct state *state, const char *char_format, va_list ap) 281{ 282 const unsigned char *format = (const unsigned char *)char_format; 283 unsigned char c; 284 285 while((c = *format++)) { 286 if (c == '%') { 287 int flags = 0; 288 int width = 0; 289 int prec = -1; 290 int long_flag = 0; 291 int short_flag = 0; 292 293 /* flags */ 294 while((c = *format++)){ 295 if(c == '-') 296 flags |= minus_flag; 297 else if(c == '+') 298 flags |= plus_flag; 299 else if(c == ' ') 300 flags |= space_flag; 301 else if(c == '#') 302 flags |= alternate_flag; 303 else if(c == '0') 304 flags |= zero_flag; 305 else 306 break; 307 } 308 309 if((flags & space_flag) && (flags & plus_flag)) 310 flags ^= space_flag; 311 312 if((flags & minus_flag) && (flags & zero_flag)) 313 flags ^= zero_flag; 314 315 /* width */ 316 if (isdigit(c)) 317 do { 318 width = width * 10 + c - '0'; 319 c = *format++; 320 } while(isdigit(c)); 321 else if(c == '*') { 322 width = va_arg(ap, int); 323 c = *format++; 324 } 325 326 /* precision */ 327 if (c == '.') { 328 prec = 0; 329 c = *format++; 330 if (isdigit(c)) 331 do { 332 prec = prec * 10 + c - '0'; 333 c = *format++; 334 } while(isdigit(c)); 335 else if (c == '*') { 336 prec = va_arg(ap, int); 337 c = *format++; 338 } 339 } 340 341 /* size */ 342 343 if (c == 'h') { 344 short_flag = 1; 345 c = *format++; 346 } else if (c == 'l') { 347 long_flag = 1; 348 c = *format++; 349 } 350 351 switch (c) { 352 case 'c' : 353 if(append_char(state, va_arg(ap, int), width, flags)) 354 return -1; 355 break; 356 case 's' : 357 if (append_string(state, 358 va_arg(ap, unsigned char*), 359 width, 360 prec, 361 flags)) 362 return -1; 363 break; 364 case 'd' : 365 case 'i' : { 366 long arg; 367 unsigned long num; 368 int minusp = 0; 369 370 PARSE_INT_FORMAT(arg, ap, signed); 371 372 if (arg < 0) { 373 minusp = 1; 374 num = -arg; 375 } else 376 num = arg; 377 378 if (append_number (state, num, 10, "0123456789", 379 width, prec, flags, minusp)) 380 return -1; 381 break; 382 } 383 case 'u' : { 384 unsigned long arg; 385 386 PARSE_INT_FORMAT(arg, ap, unsigned); 387 388 if (append_number (state, arg, 10, "0123456789", 389 width, prec, flags, 0)) 390 return -1; 391 break; 392 } 393 case 'o' : { 394 unsigned long arg; 395 396 PARSE_INT_FORMAT(arg, ap, unsigned); 397 398 if (append_number (state, arg, 010, "01234567", 399 width, prec, flags, 0)) 400 return -1; 401 break; 402 } 403 case 'x' : { 404 unsigned long arg; 405 406 PARSE_INT_FORMAT(arg, ap, unsigned); 407 408 if (append_number (state, arg, 0x10, "0123456789abcdef", 409 width, prec, flags, 0)) 410 return -1; 411 break; 412 } 413 case 'X' :{ 414 unsigned long arg; 415 416 PARSE_INT_FORMAT(arg, ap, unsigned); 417 418 if (append_number (state, arg, 0x10, "0123456789ABCDEF", 419 width, prec, flags, 0)) 420 return -1; 421 break; 422 } 423 case 'p' : { 424 unsigned long arg = (unsigned long)va_arg(ap, void*); 425 426 if (append_number (state, arg, 0x10, "0123456789ABCDEF", 427 width, prec, flags, 0)) 428 return -1; 429 break; 430 } 431 case 'n' : { 432 int *arg = va_arg(ap, int*); 433 *arg = state->s - state->str; 434 break; 435 } 436 case '\0' : 437 --format; 438 /* FALLTHROUGH */ 439 case '%' : 440 if ((*state->append_char)(state, c)) 441 return -1; 442 break; 443 default : 444 if ( (*state->append_char)(state, '%') 445 || (*state->append_char)(state, c)) 446 return -1; 447 break; 448 } 449 } else 450 if ((*state->append_char) (state, c)) 451 return -1; 452 } 453 return 0; 454} 455 456#ifndef HAVE_SNPRINTF 457int 458snprintf (char *str, size_t sz, const char *format, ...) 459{ 460 va_list args; 461 int ret; 462 463 va_start(args, format); 464 ret = vsnprintf (str, sz, format, args); 465 466#ifdef PARANOIA 467 { 468 int ret2; 469 char *tmp; 470 471 tmp = malloc (sz); 472 if (tmp == NULL) 473 abort (); 474 475 ret2 = vsprintf (tmp, format, args); 476 if (ret != ret2 || strcmp(str, tmp)) 477 abort (); 478 free (tmp); 479 } 480#endif 481 482 va_end(args); 483 return ret; 484} 485#endif 486 487#ifndef HAVE_ASPRINTF 488int 489asprintf (char **ret, const char *format, ...) 490{ 491 va_list args; 492 int val; 493 494 va_start(args, format); 495 val = vasprintf (ret, format, args); 496 497#ifdef PARANOIA 498 { 499 int ret2; 500 char *tmp; 501 tmp = malloc (val + 1); 502 if (tmp == NULL) 503 abort (); 504 505 ret2 = vsprintf (tmp, format, args); 506 if (val != ret2 || strcmp(*ret, tmp)) 507 abort (); 508 free (tmp); 509 } 510#endif 511 512 va_end(args); 513 return val; 514} 515#endif 516 517#ifndef HAVE_ASNPRINTF 518int 519asnprintf (char **ret, size_t max_sz, const char *format, ...) 520{ 521 va_list args; 522 int val; 523 524 va_start(args, format); 525 val = vasnprintf (ret, max_sz, format, args); 526 527#ifdef PARANOIA 528 { 529 int ret2; 530 char *tmp; 531 tmp = malloc (val + 1); 532 if (tmp == NULL) 533 abort (); 534 535 ret2 = vsprintf (tmp, format, args); 536 if (val != ret2 || strcmp(*ret, tmp)) 537 abort (); 538 free (tmp); 539 } 540#endif 541 542 va_end(args); 543 return val; 544} 545#endif 546 547#ifndef HAVE_VASPRINTF 548int 549vasprintf (char **ret, const char *format, va_list args) 550{ 551 return vasnprintf (ret, 0, format, args); 552} 553#endif 554 555 556#ifndef HAVE_VASNPRINTF 557int 558vasnprintf (char **ret, size_t max_sz, const char *format, va_list args) 559{ 560 int st; 561 size_t len; 562 struct state state; 563 564 state.max_sz = max_sz; 565 state.sz = 1; 566 state.str = malloc(state.sz); 567 if (state.str == NULL) { 568 *ret = NULL; 569 return -1; 570 } 571 state.s = state.str; 572 state.theend = state.s + state.sz - 1; 573 state.append_char = as_append_char; 574 state.reserve = as_reserve; 575 576 st = xyzprintf (&state, format, args); 577 if (st) { 578 free (state.str); 579 *ret = NULL; 580 return -1; 581 } else { 582 char *tmp; 583 584 *state.s = '\0'; 585 len = state.s - state.str; 586 tmp = realloc (state.str, len+1); 587 if (tmp == NULL) { 588 free (state.str); 589 *ret = NULL; 590 return -1; 591 } 592 *ret = tmp; 593 return len; 594 } 595} 596#endif 597 598#ifndef HAVE_VSNPRINTF 599int 600vsnprintf (char *str, size_t sz, const char *format, va_list args) 601{ 602 struct state state; 603 int ret; 604 unsigned char *ustr = (unsigned char *)str; 605 606 state.max_sz = 0; 607 state.sz = sz; 608 state.str = ustr; 609 state.s = ustr; 610 state.theend = ustr + sz - 1; 611 state.append_char = sn_append_char; 612 state.reserve = sn_reserve; 613 614 ret = xyzprintf (&state, format, args); 615 *state.s = '\0'; 616 if (ret) 617 return sz; 618 else 619 return state.s - state.str; 620} 621#endif 622 623