Amend sanitization of format strings. Support ' without stb_printf. Simpler loops, will also be used for ImStrv branch. (8ee77f1) (#3604)
Widgets: Sliders: Fix a bug where numbers after format specifier (eg. %d123) would cause RoundScalarWithFormatT() return incorrect value.
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index fdf65a0..380bb2a 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -2077,37 +2077,36 @@
return src;
}
+// Sanitize format
+// - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi
+// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi.
+static void SanitizeFormatString(const char* fmt, char* fmt_out, size_t fmt_out_size)
+{
+ const char* fmt_end = ImParseFormatFindEnd(fmt);
+ IM_ASSERT((size_t)(fmt_end - fmt + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!
+ while (fmt < fmt_end)
+ {
+ char c = *(fmt++);
+ if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.
+ *(fmt_out++) = c;
+ }
+ *fmt_out = 0; // Zero-terminate
+}
+
template<typename TYPE, typename SIGNEDTYPE>
TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v)
{
const char* fmt_start = ImParseFormatFindStart(format);
if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string
return v;
+
+ // Sanitize format
+ char fmt_sanitized[32];
+ SanitizeFormatString(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized));
+ fmt_start = fmt_sanitized;
+
+ // Format value with our rounding, and read back
char v_str[64];
- char fmt[32];
- const char* fmt_end = ImParseFormatFindEnd(fmt_start);
- IM_ASSERT(fmt_end - fmt_start < IM_ARRAYSIZE(fmt) && "Number format is too long!");
-#ifdef IMGUI_USE_STB_SPRINTF
- // stb_sprintf.h supports several new modifiers which format numbers in a way that makes them incompatible with
- // ImAtof()/ImAtoi(). Copy format string omitting incompatible modifiers and anything past the end of format specifier.
- int fmt_len = 0;
- for (int i = 0, end = fmt_end - fmt_start; i < end; i++)
- {
- char c = fmt_start[i];
- if (c == '\'' || c == '$' || c == '_') // Custom flags provided by stb_sprintf.h
- continue;
- fmt[fmt_len++] = c;
- }
- fmt[fmt_len] = 0;
- fmt_start = fmt;
-#else
- // Extra characters after format specifier may confuse ImAtof()/ImAtoi(), therefore copying is performed, excluding anything beyond.
- if (*fmt_end != 0)
- {
- ImStrncpy(fmt, fmt_start, fmt_end - fmt_start + 1);
- fmt_start = fmt;
- }
-#endif
ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v);
const char* p = v_str;
while (*p == ' ')