strconv.c 2.7 KB

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