dupprintf.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. /*
  2. * Do an sprintf(), but into a custom-allocated buffer.
  3. *
  4. * Currently I'm doing this via vsnprintf. This has worked so far,
  5. * but it's not good, because vsnprintf is not available on all
  6. * platforms. There's an ifdef to use `_vsnprintf', which seems
  7. * to be the local name for it on Windows. Other platforms may
  8. * lack it completely, in which case it'll be time to rewrite
  9. * this function in a totally different way.
  10. *
  11. * The only `properly' portable solution I can think of is to
  12. * implement my own format string scanner, which figures out an
  13. * upper bound for the length of each formatting directive,
  14. * allocates the buffer as it goes along, and calls sprintf() to
  15. * actually process each directive. If I ever need to actually do
  16. * this, some caveats:
  17. *
  18. * - It's very hard to find a reliable upper bound for
  19. * floating-point values. %f, in particular, when supplied with
  20. * a number near to the upper or lower limit of representable
  21. * numbers, could easily take several hundred characters. It's
  22. * probably feasible to predict this statically using the
  23. * constants in <float.h>, or even to predict it dynamically by
  24. * looking at the exponent of the specific float provided, but
  25. * it won't be fun.
  26. *
  27. * - Don't forget to _check_, after calling sprintf, that it's
  28. * used at most the amount of space we had available.
  29. *
  30. * - Fault any formatting directive we don't fully understand. The
  31. * aim here is to _guarantee_ that we never overflow the buffer,
  32. * because this is a security-critical function. If we see a
  33. * directive we don't know about, we should panic and die rather
  34. * than run any risk.
  35. */
  36. #include <stdio.h>
  37. #include "defs.h"
  38. #include "misc.h"
  39. #include "utils/utils.h"
  40. /* Work around lack of va_copy in old MSC */
  41. #if defined _MSC_VER && !defined va_copy
  42. #define va_copy(a, b) TYPECHECK( \
  43. (va_list *)0 == &(a) && (va_list *)0 == &(b), \
  44. memcpy(&a, &b, sizeof(va_list)))
  45. #endif
  46. /* Also lack of vsnprintf before VS2015 */
  47. #if defined _WINDOWS && \
  48. !defined __MINGW32__ && \
  49. !defined __WINE__ && \
  50. _MSC_VER < 1900
  51. #define vsnprintf _vsnprintf
  52. #endif
  53. char *dupvprintf_inner(char *buf, size_t oldlen, size_t *sizeptr,
  54. const char *fmt, va_list ap)
  55. {
  56. size_t size = *sizeptr;
  57. sgrowarrayn_nm(buf, size, oldlen, 512);
  58. while (1) {
  59. va_list aq;
  60. va_copy(aq, ap);
  61. int len = vsnprintf(buf + oldlen, size - oldlen, fmt, aq);
  62. va_end(aq);
  63. if (len >= 0 && len < size) {
  64. /* This is the C99-specified criterion for snprintf to have
  65. * been completely successful. */
  66. *sizeptr = size;
  67. return buf;
  68. } else if (len > 0) {
  69. /* This is the C99 error condition: the returned length is
  70. * the required buffer size not counting the NUL. */
  71. sgrowarrayn_nm(buf, size, oldlen + 1, len);
  72. } else {
  73. /* This is the pre-C99 glibc error condition: <0 means the
  74. * buffer wasn't big enough, so we enlarge it a bit and hope. */
  75. sgrowarray_nm(buf, size, size);
  76. }
  77. }
  78. }
  79. char *dupvprintf(const char *fmt, va_list ap)
  80. {
  81. size_t size = 0;
  82. return dupvprintf_inner(NULL, 0, &size, fmt, ap);
  83. }
  84. char *dupprintf(const char *fmt, ...)
  85. {
  86. char *ret;
  87. va_list ap;
  88. va_start(ap, fmt);
  89. ret = dupvprintf(fmt, ap);
  90. va_end(ap);
  91. return ret;
  92. }