Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 441 lines (362 sloc) 11.982 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
/*
* This file is in the Public Domain
*
* Based on code from Public Domain snprintf.c from mutt
* http://dev.mutt.org/hg/mutt/file/55cd4cb611d9/snprintf.c
* Tue Aug 08 22:49:12 2006 +0000
*
*/

#ifdef HAVE_CONFIG_H
#include <raptor_config.h>
#endif

#ifdef HAVE_VASPRINTF
#define _GNU_SOURCE /* to get vasprintf() available */
#endif
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

#include "raptor2.h"
#include "raptor_internal.h"



/*
* Thanks to the patch in this Debian bug for the solution
* to the crash inside vsnprintf on some architectures.
*
* "reuse of args inside the while(1) loop is in violation of the
* specs and only happens to work by accident on other systems."
*
* http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=104325
*/

#ifndef va_copy
#ifdef __va_copy
#define va_copy(dest, src) __va_copy(dest,src)
#else
#define va_copy(dest, src) (dest) = (src)
#endif
#endif


#ifdef CHECK_VSNPRINTF_RUNTIME
static int vsnprintf_checked = -1;

static int
vsnprintf_check_is_c99(char *buf, const char *s, ...)
{
  va_list args;
  int r;
  va_start(args, s);
  r = vsnprintf(buf, buf ? 5 : 0, s, args);
  va_end(args);

  return (r == 7);
}

static int
vsnprintf_is_c99(void)
{
  if(vsnprintf_checked < 0) {
    char buffer[32];
    vsnprintf_checked = (vsnprintf_check_is_c99(NULL, "1234567") &&
                         vsnprintf_check_is_c99(buffer, "1234567"))
                        ? 1 : 0;
  }

  return vsnprintf_checked;
}
#endif /* CHECK_VSNPRINTF_RUNTIME */


#define VSNPRINTF_C99_BLOCK(len, buffer, size, format, arguments) \
do { \
len = vsnprintf(buffer, size, format, arguments); \
} while(0)

#define VSNPRINTF_NOT_C99_BLOCK(len, buffer, size, format, arguments) \
do { \
if(!buffer || !size) { \
/* This vsnprintf doesn't return number of bytes required */ \
size = 2 + strlen(format); \
while(1) { \
va_list args_copy; \
char* tmp_buffer = RAPTOR_MALLOC(char*, size + 1); \
\
if(!tmp_buffer) \
break; \
\
/* copy for re-use */ \
va_copy(args_copy, arguments); \
len = vsnprintf(tmp_buffer, size, format, args_copy); \
va_end(args_copy); \
\
/* On windows, vsnprintf() returns -1 if the buffer does not \
* fit. If the buffer exactly fits the string without a NULL \
* terminator, it returns the string length and it ends up \
* with an unterminated string. The added check makes sure \
* the string returned is terminated - otherwise more buffer \
* space is allocated and the while() loop retries. \
*/ \
if((len >= 0) && \
(RAPTOR_GOOD_CAST(size_t, len) < size) && \
(tmp_buffer[len] == '\0')) { \
len = RAPTOR_BAD_CAST(int, strlen(tmp_buffer)); \
RAPTOR_FREE(char*, tmp_buffer); \
break; \
} \
RAPTOR_FREE(char*, tmp_buffer); \
size += (size >> 1); \
} \
} \
\
if(buffer) \
len = vsnprintf(buffer, size, format, arguments); \
} while(0)

#ifndef STANDALONE

/**
* raptor_vsnprintf2:
* @buffer: buffer (or NULL)
* @size: size of buffer (or 0)
* @format: printf-style format string
* @arguments: variable arguments list
*
* Format output for a variable arguments list into an allocated sized buffer.
*
* This is a wrapper around system versions of vsnprintf with
* different call and return conventions.
*
* If @buffer is NULL or size is 0 or the buffer size is too small,
* returns the number of bytes that would be needed for buffer
*
* Return value: number of bytes allocated (excluding NUL) or <0 on failure
**/
int
raptor_vsnprintf2(char *buffer, size_t size,
                  const char *format, va_list arguments)
{
  int len = -1;

  RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(format, char*, -1);

#ifdef CHECK_VSNPRINTF_RUNTIME

  if(vsnprintf_is_c99())
    VSNPRINTF_C99_BLOCK(len, buffer, size, format, arguments) ;
  else
    VSNPRINTF_NOT_C99_BLOCK(len, buffer, size, format, arguments) ;

#else

#ifdef HAVE_C99_VSNPRINTF
  VSNPRINTF_C99_BLOCK(len, buffer, size, format, arguments) ;
#else
  VSNPRINTF_NOT_C99_BLOCK(len, buffer, size, format, arguments) ;
#endif

#endif
  
  return len;
}


/**
* raptor_vsnprintf:
* @format: printf-style format string
* @arguments: variable arguments list
*
* Format output for a variable arguments list into a newly allocated buffer
*
* @Deprecated: This does not actually conform to vsnprintf's calling
* convention and does not return the allocated buffer length. Use
* raptor_vsnprintf2() or raptor_vasprintf() instead.
*
* Return value: a newly allocated string as the formatted result or NULL on failure
**/
char*
raptor_vsnprintf(const char *format, va_list arguments)
{
  int len;
  char *buffer = NULL;

  RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(format, char*, NULL);

  len = raptor_vasprintf(&buffer, format, arguments);
  if(len < 0)
    return NULL;
  
  return buffer;
}


/**
* raptor_snprintf:
* @buffer: buffer (or NULL)
* @size: bufer size (or 0)
* @format: printf-style format string
* @...: format arguments
*
* Format output into an allocated sized buffer
*
* This provides a portable version snprintf() over variants on
* different systems.
*
* If @buffer is NULL, calculates the number of bytes needed to
* allocate for buffer and do no formatting.
*
* Return value: number of bytes allocated (excluding NUL) or 0 on failure
**/
int
raptor_snprintf(char *buffer, size_t size, const char *format, ...)
{
  va_list arguments;
  int length;

  RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(format, char*, 0);

  va_start(arguments, format);
  length = raptor_vsnprintf2(buffer, size, format, arguments);
  va_end(arguments);

  return length;
}


/**
* raptor_vasprintf:
* @ret: pointer to store buffer
* @format: printf-style format string
* @arguments: format arguments list
*
* Format output into a new buffer and return it
*
* This is a wrapper around the (GNU) vasprintf function that is not
* always avaiable.
*
* Return value: number of bytes allocated (excluding NUL) or < 0 on failure
**/
int
raptor_vasprintf(char **ret, const char *format, va_list arguments)
{
  int length;
#ifndef HAVE_VASPRINTF
  va_list args_copy;
#endif

  RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(ret, char**, -1);
  RAPTOR_ASSERT_OBJECT_POINTER_RETURN_VALUE(format, char*, -1);

#ifdef HAVE_VASPRINTF
  length = vasprintf(ret, format, arguments);
#else
  va_copy(args_copy, arguments);
  length = raptor_vsnprintf2(NULL, 0, format, args_copy);
  va_end(args_copy);
  if(length < 0) {
    *ret = NULL;
    return length;
  }
  *ret = RAPTOR_MALLOC(char*, length + 1);
  if(!*ret)
    return -1;

  va_copy(args_copy, arguments);
  length = raptor_vsnprintf2(*ret, length + 1, format, args_copy);
  va_end(args_copy);
#endif

  return length;
}


static const char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

/**
* raptor_format_integer:
* @buffer: buffer (or NULL)
* @bufsize: size of above (or 0)
* @integer: integer value to format
* @base: numeric base up to 36
* @width: field width (or -1)
* @padding: padding char (or \0)
*
* INTERNAL - Format an integer as a decimal into a buffer or
* calculate the size needed.
*
* Works Like the C99 snprintf() but just for integers.
*
* If @buffer is NULL or the @bufsize is too small, the number of
* bytes needed (excluding NUL) is returned and no formatting is done.
*
* Return value: number of bytes needed or written (excluding NUL) or 0 on failure
*/
size_t
raptor_format_integer(char* buffer, size_t bufsize, int integer,
                      unsigned int base, int width, char padding)
{
  size_t len = 1;
  char *p;
  unsigned int value;

  if(integer < 0) {
    value = (unsigned int)-integer;
    len++;
    width++;
  } else
    value = (unsigned int)integer;
  while(value /= base)
    len++;

  if(width > 0 && RAPTOR_GOOD_CAST(size_t, width) > len)
    len = width;

  if(!buffer || bufsize < RAPTOR_GOOD_CAST(size_t, (len + 1))) /* +1 for NUL */
    return len;

  if(!padding)
    padding = ' ';

  if(integer < 0)
    value = (unsigned int)-integer;
  else
    value = (unsigned int)integer;

  p = &buffer[len];
  *p-- = '\0';
  while(value > 0 && p >= buffer) {
    *p-- = digits[value % base];
    value /= base;
  }
  while(p >= buffer)
    *p-- = padding;
  if(integer < 0)
    *buffer = '-';

  return len;
}


#else /* STANDALONE */


int main(int argc, char *argv[]);
static int test_snprintf_real(int len_ref, const char *format, va_list arguments) RAPTOR_PRINTF_FORMAT(2, 0);
static int test_snprintf(size_t len_ref, const char *format, ...) RAPTOR_PRINTF_FORMAT(2, 3);

static const char* program;


static int
test_snprintf_real(int len_ref, const char *format, va_list arguments)
{
  int len = -2;
  size_t size = 0;

  VSNPRINTF_NOT_C99_BLOCK(len, NULL, size, format, arguments);

  if(len != len_ref) {
    fprintf(stderr,
            "%s: VSNPRINTF_NOT_C99_BLOCK(len=%d, size=%d, format=\"%s\") failed : expected %d, got %d\n",
            program, len, (int)size, format, (int)len_ref, (int)len);
    return 1;
  }

  return 0;
}


static int
test_snprintf(size_t len_ref, const char *format, ...)
{
  va_list arguments;
  int rc;

  va_start(arguments, format);
  rc = test_snprintf_real(RAPTOR_BAD_CAST(int, len_ref), format, arguments);
  va_end(arguments);

  return rc;
}


#define FMT_LEN_MAX 128
#define ARG_LEN_MAX 128

int
main(int argc, char *argv[])
{
  char fmt[FMT_LEN_MAX + 1];
  char arg[ARG_LEN_MAX + 1];
  size_t x, y;
  int errors = 0;

  program = raptor_basename(argv[0]);

  for(x = 2; x < FMT_LEN_MAX; x++) {
    for(y = 0; y < ARG_LEN_MAX; y++) {
      size_t len_ref = x + y - 2;

      /* fmt = "xxxxxxxx%s"
* (number of 'x' characters varies)
*/
      memset(fmt, 'x', x - 2);
      fmt[x - 2] = '%';
      fmt[x - 1] = 's';
      fmt[x] = '\0';

      /* arg = "yyyyyyyy"
* (number of 'y' characters varies)
*/
      memset(arg, 'y', y);
      arg[y] = '\0';

      /* assert(strlen(fmt) == x); */
      /* assert(strlen(arg) == y); */

      /* len_ref = sprintf(buf_ref, fmt, arg);
assert((size_t)len_ref == x + y - 2); */

      if(test_snprintf(len_ref, fmt, arg))
        errors++;
    }
  }

  return errors;
}


#endif /* STANDALONE */
Something went wrong with that request. Please try again.