diff --git a/largeString.cpp b/largeString.cpp index 1c820e3d..7997e008 100644 --- a/largeString.cpp +++ b/largeString.cpp @@ -3,55 +3,79 @@ #include #include #include -#include "largeString.h" +#include #include +#include "largeString.h" -cLargeString::cLargeString(const char *name, size_t initialSize, size_t increaseSize, bool debugBufferSize) { - if (name) m_name = name; +void cLargeString::init(size_t initialSize, size_t increaseSize, bool debugBufferSize) { m_debugBufferSize = debugBufferSize; - if (initialSize <= 0) { - esyslog("cLargeString::cLargeString, ERROR name = %s, initialSize = %zu", m_name.c_str(), initialSize); - initialSize = 100; - } if (increaseSize > 0) m_increaseSize = increaseSize; - else m_increaseSize = std::max((size_t)10, initialSize / 2); + else m_increaseSize = std::max((size_t)100, initialSize / 2); + if (initialSize == 0) return; m_s = (char *) malloc(initialSize * sizeof(char)); if (!m_s) { - esyslog("cLargeString::cLargeString, ERROR out of memory, name = %s, initialSize = %zu", m_name.c_str(), initialSize); + esyslog("cLargeString::init, ERROR out of memory, name = %.*s, initialSize = %zu", nameLen(), nameData(), initialSize); throw std::runtime_error("cLargeString::cLargeString, ERROR out of memory"); return; } m_buffer_end = m_s + initialSize; m_string_end = m_s; } +void cLargeString::loadFile(const char *filename, bool *exists) { + if (exists) *exists = false; + if (!filename) return; + struct stat buffer; + if (stat(filename, &buffer) != 0) return; + +// file exists, length buffer.st_size + if (exists) *exists = true; + if (buffer.st_size == 0) return; // empty file + m_s = (char *) malloc((size_t)(buffer.st_size + 1) * sizeof(char)); // add one. So we can add the 0 string terminator + if (!m_s) { + esyslog("cLargeString::loadFile, ERROR out of memory, name = %.*s, filename = %s, requested size = %zu", nameLen(), nameData(), filename, (size_t)(buffer.st_size + 1)); + throw std::runtime_error("cLargeString::cLargeString, ERROR out of memory (file)"); + return; + } + m_buffer_end = m_s + buffer.st_size + 1; + m_string_end = m_s; + FILE *f = fopen(filename, "rb"); + if (!f) { + esyslog("cLargeString::loadFile, ERROR: stat OK, fopen fails, filename %s", filename); + return; + } + size_t num_read = fread (m_s, 1, (size_t)buffer.st_size, f); + fclose(f); + m_string_end = m_s + num_read; + if (num_read != (size_t)buffer.st_size) { + esyslog("cLargeString::loadFile, ERROR: num_read = %zu, buffer.st_size = %zu, ferror %i, name = %.*s, filename %s", num_read, (size_t)buffer.st_size, ferror(f), nameLen(), nameData(), filename); + m_string_end = m_s + std::min(num_read, (size_t)buffer.st_size); + } +} + cLargeString::~cLargeString() { - if (!m_s) return; + if (m_s == m_s_initial) return; free (m_s); setMaxSize(); size_t buffer_len = m_buffer_end - m_s; - if (m_debugBufferSize && buffer_len > m_maxSize * 2) esyslog("cLargeString::cLargeString WARNING too large buffer, name = %s, buffer %zu, len %zu", m_name.c_str(), buffer_len, m_maxSize); + if (m_debugBufferSize && buffer_len > m_maxSize * 2) esyslog("cLargeString::cLargeString WARNING too large buffer, name = %.*s, buffer %zu, len %zu", nameLen(), nameData(), buffer_len, m_maxSize); } -bool cLargeString::clear() { - if (!m_s) return false; +void cLargeString::clear() { setMaxSize(); m_string_end = m_s; m_endBorrowed = false; - return true; } char *cLargeString::borrowEnd(size_t len) { - if (!m_s) return NULL; - if (!appendLen(len)) return NULL; + appendLen(len); m_endBorrowed = true; return m_string_end; } bool cLargeString::finishBorrow(size_t len) { - if (!m_s) return false; if (!m_endBorrowed) return true; m_endBorrowed = false; if (m_string_end + len >= m_buffer_end) { - esyslog("cLargeString::finishBorrow(size_t len), ERROR name = %s, len %zu too large, available %zu", m_name.c_str(), len, m_buffer_end - m_string_end - 1); + esyslog("cLargeString::finishBorrow(size_t len), ERROR name = %.*s, len %zu too large, available %zu", nameLen(), nameData(), len, m_buffer_end - m_string_end - 1); m_string_end = m_buffer_end - 1; return false; } @@ -60,13 +84,12 @@ bool cLargeString::finishBorrow(size_t len) { } bool cLargeString::finishBorrow() { - if (!m_s) return false; if (!m_endBorrowed) return true; m_endBorrowed = false; char *end = strchr(m_string_end, 0); if (!end) return false; if (end >= m_buffer_end) { - esyslog("cLargeString::finishBorrow(), ERROR name = %s, end >= m_buffer_end, available %zu", m_name.c_str(), m_buffer_end - m_string_end - 1); + esyslog("cLargeString::finishBorrow(), ERROR name = %.*s, end >= m_buffer_end, available %zu", nameLen(), nameData(), m_buffer_end - m_string_end - 1); m_string_end = m_buffer_end - 1; return false; } @@ -74,71 +97,58 @@ bool cLargeString::finishBorrow() { return true; } -bool cLargeString::append(char c) { - if (!m_s) return false; - if (!appendLen(1)) return false; +cLargeString &cLargeString::append(char c) { + appendLen(1); *(m_string_end++) = c; - return true; + return *this; } -bool cLargeString::append(int i) { - if (!m_s) return false; +cLargeString &cLargeString::append(int i) { if (i < 0) { - if(!appendLen(2)) return false; + appendLen(2); *(m_string_end++) = '-'; i *= -1; } if (i < 10) return append((char)('0' + i)); - char *buf; - for (int j = i; j > 0;) { - for (j = i, buf = m_buffer_end - 2; j > 0 && buf >= m_string_end; j /= 10, buf--) *buf = j%10 + '0'; - if (j > 0 && !enlarge()) return false; - } - if (buf >= m_string_end) for (buf++; buf < m_buffer_end - 1;m_string_end++, buf++) *m_string_end = *buf; - else m_string_end = m_buffer_end - 1; - return true; + char buf[21]; // unsigned int 64: max. 20. (18446744073709551615) signed int64: max. 19 (+ sign) + char *bufferEnd = buf+20; + *bufferEnd = 0; + for (; i; i /= 10) *(--bufferEnd) = '0' + (i%10); + return appendS(bufferEnd); } -bool cLargeString::appendLen(size_t len) { - char *newStringEnd = m_string_end + len; - if (newStringEnd < m_buffer_end) return true; - return enlarge(newStringEnd + 1 - m_buffer_end); +cLargeString &cLargeString::append(const char *s, size_t len) { + if (!s || len == 0) return *this; + appendLen(len); + memcpy(m_string_end, s, len); + m_string_end += len; + return *this; } -bool cLargeString::append(const char *s, size_t len) { - if (!m_s) return false; - if (!s || len == 0) return true; - if (!appendLen(len)) return false; - for (char *newStringEnd = m_string_end + len; m_string_end < newStringEnd; s++, m_string_end++) *m_string_end = *s; - return true; -} - -bool cLargeString::append(const char *s) { - if (!m_s) return false; - if (!s || !*s) return true; - for (; *s && m_string_end < m_buffer_end; s++, m_string_end++) *m_string_end = *s; - while(m_string_end == m_buffer_end) { - if (!enlarge()) return false; - for (; *s && m_string_end < m_buffer_end; s++, m_string_end++) *m_string_end = *s; - } - return true; +cLargeString &cLargeString::appendS(const char *s) { + if (!s || !*s) return *this; + size_t len = strlen(s); + appendLen(len); + memcpy(m_string_end, s, len); + m_string_end += len; + return *this; } - -bool cLargeString::enlarge(size_t increaseSize) { - if (!m_s) return false; +void cLargeString::enlarge(size_t increaseSize) { increaseSize = std::max(increaseSize, m_increaseSize); increaseSize = std::max(increaseSize, (size_t)((m_buffer_end - m_s)/2) ); size_t stringLength = m_string_end - m_s; size_t newSize = m_buffer_end - m_s + increaseSize; - if (m_debugBufferSize) esyslog("cLargeString::cLargeString, WARNING realloc required!!!, name = %s, new Size = %zu", m_name.c_str(), newSize); - char *tmp = (char *)realloc(m_s, newSize * sizeof(char)); - if (!tmp) { - esyslog("cLargeString::cLargeString, ERROR out of memory, name = %s, new Size = %zu", m_name.c_str(), newSize); + if (m_s == m_s_initial) + m_s = (char *) malloc(newSize * sizeof(char)); + else { + if (m_debugBufferSize) esyslog("cLargeString::cLargeString, WARNING realloc required!!!, name = %.*s, new Size = %zu", nameLen(), nameData(), newSize); + m_s = (char *)realloc(m_s, newSize * sizeof(char)); + } + if (!m_s) { + esyslog("cLargeString::cLargeString, ERROR out of memory, name = %.*s, new Size = %zu", nameLen(), nameData(), newSize); throw std::runtime_error("cLargeString::cLargeString, ERROR out of memory (enlarge)"); - return false; + return; } - m_s = tmp; m_buffer_end = m_s + newSize; m_string_end = m_s + stringLength; - return true; } diff --git a/largeString.h b/largeString.h index 60b7c8a9..9d0c0929 100644 --- a/largeString.h +++ b/largeString.h @@ -8,35 +8,81 @@ class cLargeString { private: - char *m_s = NULL; - char *m_string_end = NULL; // *m_string_end = 0; m_string_end must be < m_buffer_end, because m_buffer_end is excluded - char *m_buffer_end = NULL; // buffer is between [m_s;m_buffer_end), i.e. m_buffer_end is excluded + char m_s_initial[1] = ""; + char *m_s = m_s_initial; + char *m_string_end = m_s; // *m_string_end = 0; m_string_end must be < m_buffer_end, because m_buffer_end is excluded + char *m_buffer_end = m_s + 1; // buffer is between [m_s;m_buffer_end), i.e. m_buffer_end is excluded size_t m_increaseSize = 0; size_t m_maxSize = 0; - std::string m_name = ""; + const char *m_nameData; + int m_nameLen; bool m_debugBufferSize = false; bool m_endBorrowed = false; - bool enlarge(size_t increaseSize = 0); - bool appendLen(size_t len); // enure buffer is large enough to append len characters + void enlarge(size_t increaseSize = 0); + inline void appendLen(size_t len) { // enure buffer is large enough to append len characters + if (m_string_end + len < m_buffer_end) return; + enlarge(m_string_end + len + 1 - m_buffer_end); + } void setMaxSize() { m_maxSize = std::max(m_maxSize, (size_t)(m_string_end - m_s)); } + void init(size_t initialSize, size_t increaseSize, bool debugBufferSize); + void loadFile(const char *filename, bool *exists); public: - cLargeString(const char *name, size_t initialSize, size_t increaseSize = 0, bool debugBufferSize = false); + cLargeString(const cLargeString& o) = delete; + cLargeString &operator= (const cLargeString &) = delete; + cLargeString(cLargeString&& o) = default; + cLargeString &operator= (cLargeString &&) = default; + template + cLargeString(const char (&name)[N], size_t initialSize = 0, size_t increaseSize = 0, bool debugBufferSize = false) { + m_nameData = name; + m_nameLen = static_cast(N) - 1; + init(initialSize, increaseSize, debugBufferSize); + } + template + cLargeString(const char (&name)[N], const char *filename, bool *exists = NULL) { + m_nameData = name; + m_nameLen = static_cast(N) - 1; + loadFile(filename, exists); + } ~cLargeString(); char *data() { *m_string_end = 0; return m_s; } const char *c_str() const { *m_string_end = 0; return m_s; } - bool append(char c); - bool append(const char *s); - bool append(const char *s, size_t len); - bool append(std::string s) { return append(s.c_str(), s.length()); } - bool append(int i); + cLargeString &append(const char (&s)[1]) { +// note: every other specific implementation is too slow. memcpy is too fast :) +// std::cout << "append template xx1xx !!!!\n"; + return *this; + } + template cLargeString &append(const char (&s)[N]) { +// std::cout << "append template!!!!\n"; + appendLen(N-1); + memcpy(m_string_end, s, N-1); + m_string_end += N-1; + return *this; + } + cLargeString &append(char c); + cLargeString &append(const char *s, size_t len); + cLargeString &append(const std::string &s) { return append(s.c_str(), s.length()); } + cLargeString &append(int i); + cLargeString &appendS(const char *s); + template cLargeString &appendFormated(char const* format, Args&&... args) { + size_t avail = m_buffer_end - m_string_end; + size_t numNeeded = snprintf(m_string_end, avail, format, std::forward(args)...); + if (numNeeded >= avail) { + appendLen(numNeeded + 1); + sprintf(m_string_end, format, std::forward(args)...); + } + m_string_end += numNeeded; + return *this; + } char *borrowEnd(size_t len); // len: upper limit of characters that can be written bool finishBorrow(size_t len); // len: number of actually written characters bool finishBorrow(); // number of actually written characters: zero terminated - bool clear(); - size_t length() const { return m_string_end - m_s; } - bool empty() const { return m_string_end == m_s; } + void clear(); + inline size_t length() const { return m_string_end - m_s; } + inline bool empty() const { return m_string_end == m_s; } cLargeString &erase(size_t index = 0) { setMaxSize(); m_string_end = std::min(m_string_end, m_s + index); return *this;} char operator[](size_t i) const { return *(m_s + i); } + const char *nameData() const { return m_nameData; } + int nameLen() const { return m_nameLen; } }; #endif // __LARGE_STRING_H diff --git a/live/img/rd.png b/live/img/rd.png new file mode 100644 index 00000000..20f3b913 Binary files /dev/null and b/live/img/rd.png differ diff --git a/live/js/live/createHtml.js b/live/js/live/createHtml.js index 696c0279..f1496c95 100644 --- a/live/js/live/createHtml.js +++ b/live/js/live/createHtml.js @@ -144,10 +144,10 @@ function injectHdSdIcon(elementId, sdhd, channelName) { if (typeof liveEnhanced !== 'undefined') liveEnhanced.domReadySetup(); } -function injectErrorHdSdIcon(elementId, numErrors, durationDeviation, sdhd, channelName) { +function injectErrorHdSdIcon(elementId, numErrors, durationDeviation, sdhd, channelName, duration) { const s = Object.create(null); s.a = ""; - addErrorIcon(s, numErrors, durationDeviation); + addErrorIcon(s, numErrors, durationDeviation, duration); addHdSdIcon(s, sdhd, channelName); document.getElementById(elementId).innerHTML = s.a; if (typeof liveEnhanced !== 'undefined') liveEnhanced.domReadySetup(); diff --git a/pages/recordings.ecpp b/pages/recordings.ecpp index 85708c13..979b2c6e 100644 --- a/pages/recordings.ecpp +++ b/pages/recordings.ecpp @@ -152,7 +152,7 @@ function RecordingActionS(s, id, A, Img, Title) { // [1] : ArchiveDescr() // scraper data // [2] : IMDB ID -// [3] : image.path (nach "/tvscraper/") +// [3] : image.path (behind "/tvscraper/") // [4] : "pt" if m_s_image.width <= m_s_image.height, otherwise= "" // [5] : title (scraper) // [6] : season / episode (scraper, for tv shows, if available. Otherwise: Empty) @@ -589,6 +589,9 @@ if (recording && recording->Info() ) { char sdhd = video_SD_HD == 0 ? 's': video_SD_HD == 1 ? 'h': 'u'; CONVERT(b_recordingErrors, recordingErrors, toCharsI); CONVERT(b_duration_deviation, duration_deviation, toCharsI); + std::string duration; + if (recording->FileName() ) + AppendDuration(duration, tr("%d:%02d"), recording->LengthInSeconds() ); @@ -596,7 +599,7 @@ if (recording && recording->Info() ) { ")"/> #> <%cpp> } diff --git a/pages/timers.ecpp b/pages/timers.ecpp index 8daa80cc..282ea631 100644 --- a/pages/timers.ecpp +++ b/pages/timers.ecpp @@ -155,7 +155,7 @@ using namespace vdrlive; timerStateHint = tr("Timer is recording."); } else if (timer->Flags() & tfActive) { - if (timerConflicts.HasConflict(*timer)) { + if (timerConflicts.HasConflict(timer->Id() )) { timerStateImg = "timerconflict.gif"; timerStateHint = tr("Timer has a conflict."); } diff --git a/po/de_DE.po b/po/de_DE.po index 427a56ea..7892837f 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -507,6 +507,10 @@ msgstr "Fehler beim Zugriff auf Programinfos" msgid "%d:%02d" msgstr "%d:%02d" +#, c-format +msgid "%'d MB" +msgstr "%'d MB" + msgid "Recording length" msgstr "Länge der Aufzeichnung" @@ -1025,6 +1029,9 @@ msgstr "Timer löschen" msgid "No timer defined" msgstr "Keine Timer vorhanden" +msgid "Duration" +msgstr "Dauer" + msgid "Timer is recording." msgstr "Timer zeichnet auf." diff --git a/recman.cpp b/recman.cpp index 3745c1d2..0bf1f24c 100644 --- a/recman.cpp +++ b/recman.cpp @@ -756,13 +756,17 @@ template void RecordingsItem::AppendShortTextOrDesc(cLargeString & int RecordingsItemRec::SD_HD() { - if ( m_video_SD_HD >= 0 ) return m_video_SD_HD; + if (m_video_SD_HD >= -1) return m_video_SD_HD; // < -1: not checked. -1: Radio. 0 is SD, 1 is HD, >1 is UHD or better +// see ETSI EN 300 468, V1.15.1 (2016-03) or later, Chapter "6.2.8 Component Descriptor" const cComponents *components = RecInfo()->Components(); + bool videoStreamFound = false; + bool audioStreamFound = false; if(components) for( int ix = 0; ix < components->NumComponents(); ix++) { tComponent * component = components->Component(ix); switch (component->stream) { case 1: // MPEG2 case 5: // H.264 + videoStreamFound = true; switch (component->type) { case 1: case 5: m_video_SD_HD = 0; break; // 4:3 @@ -784,13 +788,14 @@ template void RecordingsItem::AppendShortTextOrDesc(cLargeString & break; case 9: // HEVC // stream_content_ext is missing from VDR info - // => use only the first HEVC entry for video, else probably audio track - if (m_video_SD_HD >= 1) - goto audio; + // stream_content_ext == 0 -> video + // stream_content_ext == 1 -> audio + // we assume stream_content_ext == 0 (video). This might be wrong :( . On the other hand side, all this data might be wrong as broadcasters often ignore this standard + videoStreamFound = true; switch (component->type) { - case 0: - case 1: - case 2: + case 0: // stream_content_ext == 0 -> HD. stream_content_ext == 1 -> AC-4 main audio, mono + case 1: // stream_content_ext == 0 -> HD. stream_content_ext == 1 -> AC-4 main audio, mono, dialogue enhancement enabled + case 2: // stream_content_ext == 0 -> HD. stream_content_ext == 1 -> AC-4 main audio, stereo case 3: m_video_SD_HD = 1; break; // HD case 4: case 5: @@ -800,15 +805,19 @@ template void RecordingsItem::AppendShortTextOrDesc(cLargeString & break; case 2: // MPEG2 Audio case 4: // AC3 Audio - case 6: // HEAAC Audio - audio: - if (m_video_SD_HD == -1) - m_video_SD_HD = 9; + case 6: // HE-AAC Audio + case 7: // reserved for DTS audio modes + audioStreamFound = true; + break; + case 11: + videoStreamFound = true; // guess as stream_content_ext is missing, assume stream_content_ext = 0xF + break; } } - if(m_video_SD_HD == -1) + if(m_video_SD_HD < -1) // nothing known found { - m_video_SD_HD = 0; + if (!videoStreamFound && audioStreamFound) m_video_SD_HD = -1; // radio + else m_video_SD_HD = 0; // no information -> SD as default if(RecInfo()->ChannelName() ) { size_t l = strlen(RecInfo()->ChannelName() ); if( l > 3 && RecInfo()->ChannelName()[l-2] == 'H' && RecInfo()->ChannelName()[l-1] == 'D') m_video_SD_HD = 1; @@ -852,7 +861,7 @@ void AppendScraperData(cLargeString &target, const std::string &s_IMDB_ID, const void RecordingsItemRec::AppendAsJSArray(cLargeString &target, bool displayFolder){ target.append("\""); // [0] : ID - target.append(Id().c_str() + 10); + target.appendS(Id().c_str() + 10); target.append("\",\""); // [1] : ArchiveDescr() if (IsArchived()) AppendHtmlEscapedAndCorrectNonUTF8(target, ArchiveDescr().c_str() ); @@ -876,7 +885,7 @@ void AppendScraperData(cLargeString &target, const std::string &s_IMDB_ID, const #endif target.append(", \""); // [11] HD_SD - target.append(SD_HD() == 0 ? 's': SD_HD() == 1 ? 'h': SD_HD() == 2 ? 'u': 'r'); + target.append(SD_HD() == 0 ? 's': SD_HD() == 1 ? 'h': SD_HD() >= 2 ? 'u': 'r'); target.append("\", \""); // [12] channel name AppendHtmlEscapedAndCorrectNonUTF8(target, RecInfo()->ChannelName() ); @@ -911,8 +920,12 @@ void AppendScraperData(cLargeString &target, const std::string &s_IMDB_ID, const target.append(" "); target.append("\",\""); // [20] size - if(FileSizeMB() >= 0) - target.append(FormatInt(tr("%'d MB"), FileSizeMB())); + int fileSizeMB = FileSizeMB(); + if(fileSizeMB >= 0) + if (fileSizeMB >= 1000) + AppendFormated(target, tr("%.1f GB"), (double)fileSizeMB / 1000.); + else + AppendFormated(target, tr("%'d MB"), fileSizeMB); else target.append(" "); target.append("\""); diff --git a/recman.h b/recman.h index 8c66bea5..b60c04d1 100644 --- a/recman.h +++ b/recman.h @@ -239,7 +239,7 @@ template virtual const std::string ArchiveDescr() const { return "" ; } virtual const char *NewR() const { return "" ; } virtual const int RecordingErrors() const { return -1; } - virtual int SD_HD() { return m_video_SD_HD; } + virtual int SD_HD() { return m_video_SD_HD; } // < -1: not checked. -1: Radio. 0: SD. 1: HD. >1 UHD or better virtual void AppendAsJSArray(cLargeString &target, bool displayFolder) { } bool recEntriesSorted() { return m_cmp_rec != NULL; } bool dirEntriesSorted() { return m_cmp_dir != NULL; } @@ -270,7 +270,8 @@ template int m_s_episode_number = 0; int m_s_season_number = 0; int m_language = 0; - int m_video_SD_HD = -1; // 0 is SD, 1 is HD, 2 is UHD, 9 is radio + int m_video_SD_HD = -2; // < -1: not checked. -1: Radio. 0 is SD, 1 is HD, >1 is UHD or better + int m_duration_deviation = 0; }; diff --git a/timerconflict.cpp b/timerconflict.cpp index 4b2bfc6b..b72ca0a5 100644 --- a/timerconflict.cpp +++ b/timerconflict.cpp @@ -7,6 +7,7 @@ // STL headers need to be before VDR tools.h (included by ) #include +#include #include #include @@ -149,11 +150,11 @@ namespace vdrlive { svdrpServerNames.Clear(); } - bool TimerConflicts::HasConflict(const cTimer& timer) + bool TimerConflicts::HasConflict(int timerId) { for (const auto& conflict : *this) for (const auto& tic : conflict.ConflictingTimers()) - if (tic.timerIndex == timer.Id()) + if (tic.timerIndex == timerId) return true; return false; } diff --git a/timerconflict.h b/timerconflict.h index f394f0bf..c8647cb4 100644 --- a/timerconflict.h +++ b/timerconflict.h @@ -7,7 +7,7 @@ #include #include -#include +// #include avoid this to ensure the correct order of vdr includes / cxx includes, see https://www.vdr-portal.de/forum/index.php?thread/135369-live-3-1-12/&postID=1359294#post1359294 namespace vdrlive { @@ -62,7 +62,7 @@ namespace vdrlive { iterator end() { return m_conflicts.end(); } const_iterator end() const { return m_conflicts.end(); } - bool HasConflict(const cTimer& timer); + bool HasConflict(int timerId); static bool CheckAdvised(); private: void GetRemote(std::list & conflicts); diff --git a/tools.cpp b/tools.cpp index 4570de72..3c368cbd 100644 --- a/tools.cpp +++ b/tools.cpp @@ -71,7 +71,7 @@ template size_t i = 0; // number of not yet appended chars const char* notAppended = s; // position of the first character which is not yet appended for (const char* current = s; *current && current < end; current+=l) { -l = utf8CodepointIsValid(current); + l = utf8CodepointIsValid(current); switch(l) { case 1: switch(*current) { @@ -180,34 +180,18 @@ template void AppendHtmlEscapedAndCorrectNonUTF8(cLargeString &tar return; } - void AppendDuration(cLargeString &target, char const* format, int duration) +template + void AppendDuration(T &target, char const* format, int duration) { int minutes = (duration + 30) / 60; int hours = minutes / 60; minutes %= 60; - int numChars = snprintf(target.borrowEnd(32), 32, format, hours, minutes); - if (numChars < 0) { - target.finishBorrow(0); - std::stringstream builder; - builder << "cannot represent duration " << hours << ":" << minutes << " as requested"; - throw std::runtime_error( builder.str() ); - } - target.finishBorrow(numChars); - } - void AppendDuration(std::string &target, char const* format, int duration) - { - int minutes = (duration + 30) / 60; - int hours = minutes / 60; - minutes %= 60; - char result[ 32 ]; - if ( snprintf(result, sizeof(result), format, hours, minutes) < 0 ) { - std::stringstream builder; - builder << "cannot represent duration " << hours << ":" << minutes << " as requested"; - throw std::runtime_error( builder.str() ); - } - target.append(result); + AppendFormated(target, format, hours, minutes); } - std::string FormatDuration( char const* format, int duration) +template void AppendDuration(std::string &target, char const* format, int duration); +template void AppendDuration(cLargeString &target, char const* format, int duration); + + std::string FormatDuration(char const* format, int duration) { std::string result; AppendDuration(result, format, duration); @@ -250,17 +234,6 @@ template void AppendHtmlEscapedAndCorrectNonUTF8(cLargeString &tar return result; } - std::string FormatInt(char const* format, int size) - { - char result[16]; - if ( snprintf(result, sizeof(result), format, size) < 0 ) { - std::stringstream builder; - builder << "cannot represent integer " << size << " as requested"; - throw std::runtime_error( builder.str() ); - } - return result; - } - std::string StringReplace( std::string const& text, std::string const& substring, std::string const& replacement ) { std::string result = text; diff --git a/tools.h b/tools.h index 2c378801..4a185b24 100644 --- a/tools.h +++ b/tools.h @@ -57,28 +57,55 @@ namespace vdrlive { extern const std::locale g_locale; extern const std::collate& g_collate_char; - void AppendHtmlEscaped(std::string &target, const char* s); + void AppendHtmlEscaped(std::string &target, const char* s); template - void AppendHtmlEscapedAndCorrectNonUTF8(T &target, const char* s, const char *end = NULL, bool tooltip = false); + void AppendHtmlEscapedAndCorrectNonUTF8(T &target, const char* s, const char *end = NULL, bool tooltip = false); template void AppendTextTruncateOnWord(T &target, const char *text, int max_len, bool tooltip = false); - void AppendCorrectNonUTF8(std::string &target, const char* s); + void AppendCorrectNonUTF8(std::string &target, const char* s); - wint_t getNextUtfCodepoint(const char *&p); - int utf8CodepointIsValid(const char *p); // In case of invalid UTF8, return 0. Otherwise: Number of characters - wint_t Utf8ToUtf32(const char *&p, int len); // assumes, that uft8 validity checks have already been done. len must be provided. call utf8CodepointIsValid first + wint_t getNextUtfCodepoint(const char *&p); + int utf8CodepointIsValid(const char *p); // In case of invalid UTF8, return 0. Otherwise: Number of characters + wint_t Utf8ToUtf32(const char *&p, int len); // assumes, that uft8 validity checks have already been done. len must be provided. call utf8CodepointIsValid first void AppendUtfCodepoint(std::string &target, wint_t codepoint); - void AppendDuration(cLargeString &target, char const* format, int duration ); - void AppendDuration(std::string &target, char const* format, int duration ); + template void AppendFormated(std::string &target, char const* format, Args&&... args) { + char result[30]; + size_t numNeeded = snprintf(result, sizeof(result), format, std::forward(args)...); + + if (numNeeded < sizeof(result)) { + target.append(result); + } else { + char buf2[numNeeded+1]; + sprintf(buf2, format, std::forward(args)...); + target.append(buf2); + } + } + template void AppendFormated(cLargeString &target, char const* format, Args&&... args) { + target.appendFormated(format, std::forward(args)...); +/* + size_t numRes= 30; + size_t numNeeded = snprintf(target.borrowEnd(numRes), numRes, format, std::forward(args)...); + if (numNeeded >= numRes) { + target.finishBorrow(0); + sprintf(target.borrowEnd(numNeeded + 1), format, std::forward(args)...); + } + target.finishBorrow(numNeeded); +*/ + } + template std::string Format(char const* format, Args&&... args) { + std::string result; + AppendFormated(result, format, std::forward(args)...); + return result; + } + + template void AppendDuration(T &target, char const* format, int duration); std::string FormatDuration( char const* format, int duration ); - void AppendDateTime(cLargeString &target, char const* format, time_t time ); - void AppendDateTime(std::string &target, char const* format, time_t time ); + void AppendDateTime(cLargeString &target, char const* format, time_t time ); + void AppendDateTime(std::string &target, char const* format, time_t time ); std::string FormatDateTime( char const* format, time_t time ); - std::string FormatInt(char const* format, int size); - std::string StringReplace( std::string const& text, std::string const& substring, std::string const& replacement ); std::vector StringSplit( std::string const& text, char delimiter ); @@ -88,7 +115,7 @@ template std::string StringWordTruncate(const std::string& input, size_t maxLen, bool& truncated); inline std::string StringWordTruncate(const std::string& input, size_t maxLen) { bool dummy; return StringWordTruncate(input, maxLen, dummy); } - std::string StringEscapeAndBreak(std::string const& input, const char* nl = "
"); + std::string StringEscapeAndBreak(std::string const& input, const char* nl = "
"); std::string StringFormatBreak(std::string const& input); @@ -97,7 +124,7 @@ template const char *getText(const char *shortText, const char *description); template - void AppendTextMaxLen(T &target, const char *text); + void AppendTextMaxLen(T &target, const char *text); std::string MD5Hash(std::string const& str); std::string xxHash32(std::string const& str);