strconv.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #include <assert.h>
  2. #include <errno.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include "jansson_private.h"
  6. #include "strbuffer.h"
  7. /* need config.h to get the correct snprintf */
  8. #ifdef HAVE_CONFIG_H
  9. #include <config.h>
  10. #endif
  11. #if JSON_HAVE_LOCALECONV
  12. #include <locale.h>
  13. /*
  14. - This code assumes that the decimal separator is exactly one
  15. character.
  16. - If setlocale() is called by another thread between the call to
  17. localeconv() and the call to sprintf() or strtod(), the result may
  18. be wrong. setlocale() is not thread-safe and should not be used
  19. this way. Multi-threaded programs should use uselocale() instead.
  20. */
  21. static void to_locale(strbuffer_t *strbuffer)
  22. {
  23. const char *point;
  24. char *pos;
  25. point = localeconv()->decimal_point;
  26. if(*point == '.') {
  27. /* No conversion needed */
  28. return;
  29. }
  30. pos = strchr(strbuffer->value, '.');
  31. if(pos)
  32. *pos = *point;
  33. }
  34. static void from_locale(char *buffer)
  35. {
  36. const char *point;
  37. char *pos;
  38. point = localeconv()->decimal_point;
  39. if(*point == '.') {
  40. /* No conversion needed */
  41. return;
  42. }
  43. pos = strchr(buffer, *point);
  44. if(pos)
  45. *pos = '.';
  46. }
  47. #endif
  48. int jsonp_strtod(strbuffer_t *strbuffer, double *out)
  49. {
  50. double value;
  51. char *end;
  52. #if JSON_HAVE_LOCALECONV
  53. to_locale(strbuffer);
  54. #endif
  55. errno = 0;
  56. value = strtod(strbuffer->value, &end);
  57. assert(end == strbuffer->value + strbuffer->length);
  58. if(errno == ERANGE && value != 0) {
  59. /* Overflow */
  60. return -1;
  61. }
  62. *out = value;
  63. return 0;
  64. }
  65. int jsonp_dtostr(char *buffer, size_t size, double value)
  66. {
  67. int ret;
  68. char *start, *end;
  69. size_t length;
  70. ret = snprintf(buffer, size, "%.17g", value);
  71. if(ret < 0)
  72. return -1;
  73. length = (size_t)ret;
  74. if(length >= size)
  75. return -1;
  76. #if JSON_HAVE_LOCALECONV
  77. from_locale(buffer);
  78. #endif
  79. /* Make sure there's a dot or 'e' in the output. Otherwise
  80. a real is converted to an integer when decoding */
  81. if(strchr(buffer, '.') == NULL &&
  82. strchr(buffer, 'e') == NULL)
  83. {
  84. if(length + 3 >= size) {
  85. /* No space to append ".0" */
  86. return -1;
  87. }
  88. buffer[length] = '.';
  89. buffer[length + 1] = '0';
  90. buffer[length + 2] = '\0';
  91. length += 2;
  92. }
  93. /* Remove leading '+' from positive exponent. Also remove leading
  94. zeros from exponents (added by some printf() implementations) */
  95. start = strchr(buffer, 'e');
  96. if(start) {
  97. start++;
  98. end = start + 1;
  99. if(*start == '-')
  100. start++;
  101. while(*end == '0')
  102. end++;
  103. if(end != start) {
  104. memmove(start, end, length - (size_t)(end - buffer));
  105. length -= (size_t)(end - start);
  106. }
  107. }
  108. return (int)length;
  109. }