utils: Make sure the compiler can emit format warnings for stringtf

Unfortunately the compilers can't emit Wformat warnings for variadic templates
and those can't be annotated with the format attribute.
Use a variadic function which can be annotated and thus warns on format string-args mismatches.

Ref 0796a2262e
This commit is contained in:
Peter Keresztes Schmidt 2021-06-25 23:48:35 +02:00
parent 80b08a2075
commit eaf2e51b0c
5 changed files with 32 additions and 19 deletions

View File

@ -518,7 +518,7 @@ void Event::AddFrame(Image *image,
if (image) { if (image) {
if (save_jpegs & 1) { if (save_jpegs & 1) {
std::string event_file = stringtf(staticConfig.capture_file_format, path.c_str(), frames); std::string event_file = stringtf(staticConfig.capture_file_format.c_str(), path.c_str(), frames);
Debug(1, "Writing capture frame %d to %s", frames, event_file.c_str()); Debug(1, "Writing capture frame %d to %s", frames, event_file.c_str());
if (!WriteFrameImage(image, timestamp, event_file.c_str())) { if (!WriteFrameImage(image, timestamp, event_file.c_str())) {
Error("Failed to write frame image"); Error("Failed to write frame image");
@ -548,7 +548,7 @@ void Event::AddFrame(Image *image,
} }
if (alarm_image and (save_jpegs & 2)) { if (alarm_image and (save_jpegs & 2)) {
std::string event_file = stringtf(staticConfig.analyse_file_format, path.c_str(), frames); std::string event_file = stringtf(staticConfig.analyse_file_format.c_str(), path.c_str(), frames);
Debug(1, "Writing analysis frame %d", frames); Debug(1, "Writing analysis frame %d", frames);
if (!WriteFrameImage(alarm_image, timestamp, event_file.c_str(), true)) { if (!WriteFrameImage(alarm_image, timestamp, event_file.c_str(), true)) {
Error("Failed to write analysis frame image"); Error("Failed to write analysis frame image");

View File

@ -44,7 +44,7 @@ constexpr Milliseconds EventStream::STREAM_PAUSE_WAIT;
bool EventStream::loadInitialEventData(int monitor_id, SystemTimePoint event_time) { bool EventStream::loadInitialEventData(int monitor_id, SystemTimePoint event_time) {
std::string sql = stringtf("SELECT `Id` FROM `Events` WHERE " std::string sql = stringtf("SELECT `Id` FROM `Events` WHERE "
"`MonitorId` = %d AND unix_timestamp(`EndDateTime`) > %ld " "`MonitorId` = %d AND unix_timestamp(`EndDateTime`) > %ld "
"ORDER BY `Id` ASC LIMIT 1", monitor_id, event_time); "ORDER BY `Id` ASC LIMIT 1", monitor_id, std::chrono::system_clock::to_time_t(event_time));
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql.c_str());
if (!result) if (!result)
@ -687,7 +687,7 @@ bool EventStream::checkEventLoaded() {
} // void EventStream::checkEventLoaded() } // void EventStream::checkEventLoaded()
Image * EventStream::getImage( ) { Image * EventStream::getImage( ) {
std::string path = stringtf(staticConfig.capture_file_format, event_data->path.c_str(), curr_frame_id); std::string path = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
Debug(2, "EventStream::getImage path(%s) from %s frame(%ld) ", path.c_str(), event_data->path.c_str(), curr_frame_id); Debug(2, "EventStream::getImage path(%s) from %s frame(%ld) ", path.c_str(), event_data->path.c_str(), curr_frame_id);
Image *image = new Image(path.c_str()); Image *image = new Image(path.c_str());
return image; return image;
@ -702,12 +702,12 @@ bool EventStream::sendFrame(Microseconds delta_us) {
// This needs to be abstracted. If we are saving jpgs, then load the capture file. // This needs to be abstracted. If we are saving jpgs, then load the capture file.
// If we are only saving analysis frames, then send that. // If we are only saving analysis frames, then send that.
if (event_data->SaveJPEGs & 1) { if (event_data->SaveJPEGs & 1) {
filepath = stringtf(staticConfig.capture_file_format, event_data->path.c_str(), curr_frame_id); filepath = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
} else if (event_data->SaveJPEGs & 2) { } else if (event_data->SaveJPEGs & 2) {
filepath = stringtf(staticConfig.analyse_file_format, event_data->path.c_str(), curr_frame_id); filepath = stringtf(staticConfig.analyse_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
if (stat(filepath.c_str(), &filestat) < 0) { if (stat(filepath.c_str(), &filestat) < 0) {
Debug(1, "analyze file %s not found will try to stream from other", filepath.c_str()); Debug(1, "analyze file %s not found will try to stream from other", filepath.c_str());
filepath = stringtf(staticConfig.capture_file_format, event_data->path.c_str(), curr_frame_id); filepath = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
if (stat(filepath.c_str(), &filestat) < 0) { if (stat(filepath.c_str(), &filestat) < 0) {
Debug(1, "capture file %s not found either", filepath.c_str()); Debug(1, "capture file %s not found either", filepath.c_str());
filepath = ""; filepath = "";

View File

@ -528,8 +528,8 @@ void Logger::logPrint(bool hex, const char *filepath, int line, int level, const
"INSERT INTO `Logs` " "INSERT INTO `Logs` "
"( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` )" "( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` )"
" VALUES " " VALUES "
"( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", "( %ld.%06" PRIi64 ", '%s', %d, %d, %d, '%s', '%s', '%s', %d )",
now_sec, now_frac.count(), mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, now_sec, static_cast<int64>(now_frac.count()), mId.c_str(), staticConfig.SERVER_ID, tid, level, classString,
escapedString.c_str(), file, line); escapedString.c_str(), file, line);
dbQueue.push(std::move(sql_string)); dbQueue.push(std::move(sql_string));
} else { } else {

View File

@ -22,6 +22,7 @@
#include "zm_config.h" #include "zm_config.h"
#include "zm_logger.h" #include "zm_logger.h"
#include <array> #include <array>
#include <cstdarg>
#include <cstring> #include <cstring>
#include <fcntl.h> /* Definition of AT_* constants */ #include <fcntl.h> /* Definition of AT_* constants */
#include <sstream> #include <sstream>
@ -116,6 +117,26 @@ std::string Join(const StringVector &values, const std::string &delim) {
return ss.str(); return ss.str();
} }
std::string stringtf(const char* format, ...) {
va_list args;
va_start(args, format);
va_list args2;
va_copy(args2, args);
int size = vsnprintf(nullptr, 0, format, args) + 1; // Extra space for '\0'
va_end(args);
if (size <= 0) {
throw std::runtime_error("Error during formatting.");
}
std::unique_ptr<char[]> buf(new char[size]);
vsnprintf(buf.get(), size, format, args2);
va_end(args2);
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
}
std::string ByteArrayToHexString(nonstd::span<const uint8> bytes) { std::string ByteArrayToHexString(nonstd::span<const uint8> bytes) {
static constexpr char lowercase_table[] = "0123456789abcdef"; static constexpr char lowercase_table[] = "0123456789abcdef";
std::string buf; std::string buf;

View File

@ -61,16 +61,8 @@ inline bool StartsWith(const std::string &haystack, const std::string &needle) {
return (haystack.substr(0, needle.length()) == needle); return (haystack.substr(0, needle.length()) == needle);
} }
template<typename... Args> __attribute__((format(printf, 1, 2)))
std::string stringtf(const std::string &format, Args... args) { std::string stringtf(const char* format, ...);
int size = snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
if (size <= 0) {
throw std::runtime_error("Error during formatting.");
}
std::unique_ptr<char[]> buf(new char[size]);
snprintf(buf.get(), size, format.c_str(), args...);
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
}
std::string ByteArrayToHexString(nonstd::span<const uint8> bytes); std::string ByteArrayToHexString(nonstd::span<const uint8> bytes);