diff --git a/base/CLog.cpp b/base/CLog.cpp index 38bcdfd6..b104e5cc 100644 --- a/base/CLog.cpp +++ b/base/CLog.cpp @@ -1,4 +1,5 @@ #include "CLog.h" +#include "CString.h" #include #include #include @@ -82,8 +83,9 @@ CLog::print(const char* fmt, ...) char stack[1024]; va_list args; va_start(args, fmt); - char* buffer = vsprint(pad, stack, - sizeof(stack) / sizeof(stack[0]), fmt, args); + char* buffer = CStringUtil::vsprint(stack, + sizeof(stack) / sizeof(stack[0]), + pad, g_newlineLength, fmt, args); va_end(args); // output buffer @@ -119,8 +121,9 @@ CLog::printt(const char* file, int line, const char* fmt, ...) // print to buffer, leaving space for a newline at the end va_list args; va_start(args, fmt); - char* buffer = vsprint(pad, stack, - sizeof(stack) / sizeof(stack[0]), fmt, args); + char* buffer = CStringUtil::vsprint(stack, + sizeof(stack) / sizeof(stack[0]), + pad, g_newlineLength, fmt, args); va_end(args); // print the prefix to the buffer. leave space for priority label. @@ -227,8 +230,9 @@ CLog::output(int priority, char* msg) int n = -g_prioritySuffixLength; if (priority >= 0) { n = strlen(g_priority[priority]); - sprintf(msg + g_maxPriorityLength - n, - "%s:", g_priority[priority]); + strcpy(msg + g_maxPriorityLength - n, g_priority[priority]); + msg[g_maxPriorityLength + 0] = ':'; + msg[g_maxPriorityLength + 1] = ' '; msg[g_maxPriorityLength + 1] = ' '; } @@ -246,31 +250,6 @@ CLog::output(int priority, char* msg) } } -char* -CLog::vsprint(int pad, char* buffer, int len, const char* fmt, va_list args) -{ - assert(len > 0); - - // try writing to input buffer - int n; - if (len >= pad) { - n = vsnprintf(buffer + pad, len - pad, fmt, args); - if (n >= 0 && n <= len - pad + g_newlineLength) - return buffer; - } - - // start allocating buffers until we write the whole string - buffer = NULL; - do { - delete[] buffer; - len *= 2; - buffer = new char[len + pad]; - n = vsnprintf(buffer + pad, len - pad, fmt, args); - } while (n < 0 || n > len - pad + g_newlineLength); - - return buffer; -} - #if WINDOWS_LIKE static DWORD s_thread = 0; diff --git a/base/CLog.h b/base/CLog.h index 64fd9976..834b5bec 100644 --- a/base/CLog.h +++ b/base/CLog.h @@ -65,8 +65,6 @@ private: static void dummyLock(bool); static int getMaxPriority(); static void output(int priority, char* msg); - static char* vsprint(int pad, char*, int len, const char*, va_list); - static int nprint(const char*, va_list); #if WINDOWS_LIKE static void openConsole(); #endif diff --git a/base/CString.cpp b/base/CString.cpp index 775fe912..f6d30794 100644 --- a/base/CString.cpp +++ b/base/CString.cpp @@ -1,7 +1,159 @@ #include "CString.h" +#include "stdvector.h" #include +#include +#include #include +#if WINDOWS_LIKE +#define WIN32_LEAN_AND_MEAN +#include +#define vsnprintf _vsnprintf +#endif + +// +// CStringUtil +// + +CString +CStringUtil::format(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + CString result = vformat(fmt, args); + va_end(args); + return result; +} + +CString +CStringUtil::vformat(const char* fmt, va_list args) +{ + // find highest indexed substitution and the locations of substitutions + std::vector pos; + std::vector width; + std::vector index; + int maxIndex = 0; + for (const char* scan = fmt; *scan != '\0'; ++scan) { + if (*scan == '%') { + ++scan; + if (*scan == '\0') { + break; + } + else if (*scan == '%') { + // literal + index.push_back(0); + pos.push_back(static_cast(scan - 1 - fmt)); + width.push_back(2); + } + else if (*scan == '{') { + // get argument index + char* end; + int i = static_cast(strtol(scan + 1, &end, 10)); + if (*end != '}') { + // invalid index -- ignore + scan = end - 1; + } + else { + index.push_back(i); + pos.push_back(static_cast(scan - 1 - fmt)); + width.push_back(static_cast(end - scan + 2)); + if (i > maxIndex) { + maxIndex = i; + } + scan = end; + } + } + else { + // improper escape -- ignore + } + } + } + + // get args + std::vector value; + std::vector length; + value.push_back("%"); + length.push_back(1); + for (int i = 0; i < maxIndex; ++i) { + const char* arg = va_arg(args, const char*); + value.push_back(arg); + length.push_back(strlen(arg)); + } + + // compute final length + size_t resultLength = strlen(fmt); + const int n = static_cast(pos.size()); + for (int i = 0; i < n; ++i) { + resultLength -= width[i]; + resultLength += length[index[i]]; + } + + // substitute + CString result; + result.reserve(resultLength); + size_t src = 0; + for (int i = 0; i < n; ++i) { + result.append(fmt + src, pos[i] - src); + result.append(value[index[i]]); + src = pos[i] + width[i]; + } + result.append(fmt + src); + + return result; +} + +CString +CStringUtil::print(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + CString result = vprint(fmt, args); + va_end(args); + return result; +} + +CString +CStringUtil::vprint(const char* fmt, va_list args) +{ + char tmp[1024]; + char* buffer = vsprint(tmp, sizeof(tmp) / sizeof(tmp[0]), 0, 0, fmt, args); + if (buffer == tmp) { + return buffer; + } + else { + CString result(buffer); + delete[] buffer; + return result; + } +} + +char* +CStringUtil::vsprint(char* buffer, int len, + int prefix, int suffix, const char* fmt, va_list args) +{ + assert(len > 0); + + // try writing to input buffer + int n; + if (buffer != NULL && len >= prefix + suffix) { + n = vsnprintf(buffer + prefix, len - (prefix + suffix), fmt, args); + if (n >= 0 && n <= len - (prefix + suffix)) + return buffer; + } + + // start allocating buffers until we write the whole string + buffer = NULL; + do { + delete[] buffer; + len *= 2; + buffer = new char[len + (prefix + suffix)]; + n = vsnprintf(buffer + prefix, len - (prefix + suffix), fmt, args); + } while (n < 0 || n > len - (prefix + suffix)); + + return buffer; +} + + // // CStringUtil::CaselessCmp // @@ -11,7 +163,7 @@ CStringUtil::CaselessCmp::cmpEqual( const CString::value_type& a, const CString::value_type& b) { - // FIXME -- use std::tolower but not in all versions of libstdc++ + // FIXME -- use std::tolower but not in all versions of libstdc++ have it return tolower(a) == tolower(b); } @@ -20,7 +172,7 @@ CStringUtil::CaselessCmp::cmpLess( const CString::value_type& a, const CString::value_type& b) { - // FIXME -- use std::tolower but not in all versions of libstdc++ + // FIXME -- use std::tolower but not in all versions of libstdc++ have it return tolower(a) < tolower(b); } diff --git a/base/CString.h b/base/CString.h index 78fd9ad2..56c3a690 100644 --- a/base/CString.h +++ b/base/CString.h @@ -1,18 +1,41 @@ #ifndef CSTRING_H #define CSTRING_H +#include #include "stdpre.h" #include #include "stdpost.h" -// use to get appropriate type for string constants. it depends on -// the internal representation type of CString. -#define _CS(_x) _x - typedef std::string CString; class CStringUtil { public: + // format a string using positional arguments. fmt has literal + // characters and conversion specifications introduced by `%': + // %% literal `%' + // %{n} positional element n, n a positive integer, {} are literal + // all arguments in the variable list are const char*. positional + // elements are indexed from 1. + static CString format(const char* fmt, ...); + static CString vformat(const char* fmt, va_list); + + // print a string using printf-style formatting + static CString print(const char* fmt, ...); + static CString vprint(const char* fmt, va_list); + + // like print but print into a given buffer. if the resulting string + // will not fit into the buffer then a new buffer is allocated and + // returned, otherwise the input buffer is returned. the caller must + // delete[] the returned buffer if is not the passed-in buffer. + // + // prefix and suffix must be >= 0. exactly prefix characters and + // at least suffix characters are available in the buffer before + // and after the printed string, respectively. bufferLength is the + // length of buffer and should not be adjusted by the caller to + // account for prefix or suffix. + static char* vsprint(char* buffer, int bufferLength, + int prefix, int suffix, const char* fmt, va_list); + class CaselessCmp { public: bool operator()(const CString&, const CString&) const;