From 2d48ae784ea6828e8626c32c848f64232d8f35c0 Mon Sep 17 00:00:00 2001 From: geronimo Date: Fri, 13 Jul 2012 04:26:40 +0200 Subject: initial import --- libs/vdr/src/CharsetConv.cc | 294 +++++++++++++++++++++++++++++++++ libs/vdr/src/CondWait.cc | 101 ++++++++++++ libs/vdr/src/FileNameList.cc | 70 ++++++++ libs/vdr/src/Logging.cc | 46 ++++++ libs/vdr/src/Mutex.cc | 81 +++++++++ libs/vdr/src/ReadDir.cc | 53 ++++++ libs/vdr/src/String.cc | 379 +++++++++++++++++++++++++++++++++++++++++++ libs/vdr/src/StringList.cc | 50 ++++++ libs/vdr/src/Thread.cc | 209 ++++++++++++++++++++++++ libs/vdr/src/TimeMs.cc | 101 ++++++++++++ libs/vdr/src/i18n.cc | 324 ++++++++++++++++++++++++++++++++++++ 11 files changed, 1708 insertions(+) create mode 100644 libs/vdr/src/CharsetConv.cc create mode 100644 libs/vdr/src/CondWait.cc create mode 100644 libs/vdr/src/FileNameList.cc create mode 100644 libs/vdr/src/Logging.cc create mode 100644 libs/vdr/src/Mutex.cc create mode 100644 libs/vdr/src/ReadDir.cc create mode 100644 libs/vdr/src/String.cc create mode 100644 libs/vdr/src/StringList.cc create mode 100644 libs/vdr/src/Thread.cc create mode 100644 libs/vdr/src/TimeMs.cc create mode 100644 libs/vdr/src/i18n.cc (limited to 'libs/vdr/src') diff --git a/libs/vdr/src/CharsetConv.cc b/libs/vdr/src/CharsetConv.cc new file mode 100644 index 0000000..ed50dee --- /dev/null +++ b/libs/vdr/src/CharsetConv.cc @@ -0,0 +1,294 @@ +/** + * File: CharsetConv.cc + * Project: libvdr - classes taken from vdr-project + * + * from "Video Disk Recorder": + * + * Copyright (C) 2000, 2003, 2006, 2008 Klaus Schmidinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * The original author can be reached at kls@tvdr.de + * + * The vdr project's page is at http://www.tvdr.de + * + */ +#include +#include +#include +#include +#include +#include + +static uint SystemToUtf8[128] = { 0 }; + +int Utf8CharLen(const char *s) +{ + if (cCharSetConv::SystemCharacterTable()) + return 1; +#define MT(s, m, v) ((*(s) & (m)) == (v)) // Mask Test + if (MT(s, 0xE0, 0xC0) && MT(s + 1, 0xC0, 0x80)) + return 2; + if (MT(s, 0xF0, 0xE0) && MT(s + 1, 0xC0, 0x80) && MT(s + 2, 0xC0, 0x80)) + return 3; + if (MT(s, 0xF8, 0xF0) && MT(s + 1, 0xC0, 0x80) && MT(s + 2, 0xC0, 0x80) && MT(s + 3, 0xC0, 0x80)) + return 4; + return 1; +} + +uint Utf8CharGet(const char *s, int Length) +{ + if (cCharSetConv::SystemCharacterTable()) + return (uchar)*s < 128 ? *s : SystemToUtf8[(uchar)*s - 128]; + if (!Length) + Length = Utf8CharLen(s); + switch (Length) { + case 2: return ((*s & 0x1F) << 6) | (*(s + 1) & 0x3F); + case 3: return ((*s & 0x0F) << 12) | ((*(s + 1) & 0x3F) << 6) | (*(s + 2) & 0x3F); + case 4: return ((*s & 0x07) << 18) | ((*(s + 1) & 0x3F) << 12) | ((*(s + 2) & 0x3F) << 6) | (*(s + 3) & 0x3F); + default: break; + } + return *s; +} + +int Utf8CharSet(uint c, char *s) +{ + if (c < 0x80 || cCharSetConv::SystemCharacterTable()) { + if (s) + *s = c; + return 1; + } + if (c < 0x800) { + if (s) { + *s++ = ((c >> 6) & 0x1F) | 0xC0; + *s = (c & 0x3F) | 0x80; + } + return 2; + } + if (c < 0x10000) { + if (s) { + *s++ = ((c >> 12) & 0x0F) | 0xE0; + *s++ = ((c >> 6) & 0x3F) | 0x80; + *s = (c & 0x3F) | 0x80; + } + return 3; + } + if (c < 0x110000) { + if (s) { + *s++ = ((c >> 18) & 0x07) | 0xF0; + *s++ = ((c >> 12) & 0x3F) | 0x80; + *s++ = ((c >> 6) & 0x3F) | 0x80; + *s = (c & 0x3F) | 0x80; + } + return 4; + } + return 0; // can't convert to UTF-8 +} + +int Utf8SymChars(const char *s, int Symbols) +{ + if (cCharSetConv::SystemCharacterTable()) + return Symbols; + int n = 0; + while (*s && Symbols--) { + int sl = Utf8CharLen(s); + s += sl; + n += sl; + } + return n; +} + +int Utf8StrLen(const char *s) +{ + if (cCharSetConv::SystemCharacterTable()) + return strlen(s); + int n = 0; + while (*s) { + s += Utf8CharLen(s); + n++; + } + return n; +} + +extern char *strn0cpy(char *dest, const char *src, size_t n); + +char *Utf8Strn0Cpy(char *Dest, const char *Src, int n) +{ + if (cCharSetConv::SystemCharacterTable()) + return strn0cpy(Dest, Src, n); + char *d = Dest; + while (*Src) { + int sl = Utf8CharLen(Src); + n -= sl; + if (n > 0) { + while (sl--) + *d++ = *Src++; + } + else + break; + } + *d = 0; + return Dest; +} + +int Utf8ToArray(const char *s, uint *a, int Size) +{ + int n = 0; + while (*s && --Size > 0) { + if (cCharSetConv::SystemCharacterTable()) + *a++ = (uchar)(*s++); + else { + int sl = Utf8CharLen(s); + *a++ = Utf8CharGet(s, sl); + s += sl; + } + n++; + } + if (Size > 0) + *a = 0; + return n; +} + +int Utf8FromArray(const uint *a, char *s, int Size, int Max) +{ + int NumChars = 0; + int NumSyms = 0; + while (*a && NumChars < Size) { + if (Max >= 0 && NumSyms++ >= Max) + break; + if (cCharSetConv::SystemCharacterTable()) { + *s++ = *a++; + NumChars++; + } + else { + int sl = Utf8CharSet(*a); + if (NumChars + sl <= Size) { + Utf8CharSet(*a, s); + a++; + s += sl; + NumChars += sl; + } + else + break; + } + } + if (NumChars < Size) + *s = 0; + return NumChars; +} + +// --- cCharSetConv ---------------------------------------------------------- + +char *cCharSetConv::systemCharacterTable = NULL; + +cCharSetConv::cCharSetConv(const char *FromCode, const char *ToCode) +{ + if (!FromCode) + FromCode = systemCharacterTable ? systemCharacterTable : "UTF-8"; + if (!ToCode) + ToCode = "UTF-8"; + cd = iconv_open(ToCode, FromCode); + result = NULL; + length = 0; +} + +cCharSetConv::~cCharSetConv() +{ + free(result); + iconv_close(cd); +} + +void cCharSetConv::SetSystemCharacterTable(const char *CharacterTable) +{ + free(systemCharacterTable); + systemCharacterTable = NULL; + if (!strcasestr(CharacterTable, "UTF-8")) { + // Set up a map for the character values 128...255: + char buf[129]; + for (int i = 0; i < 128; i++) + buf[i] = i + 128; + buf[128] = 0; + cCharSetConv csc(CharacterTable); + const char *s = csc.Convert(buf); + int i = 0; + while (*s) { + int sl = Utf8CharLen(s); + SystemToUtf8[i] = Utf8CharGet(s, sl); + s += sl; + i++; + } + systemCharacterTable = strdup(CharacterTable); + } +} + +const char *cCharSetConv::Convert(const char *From, char *To, size_t ToLength) +{ + if (cd != (iconv_t)-1 && From && *From) { + char *FromPtr = (char *)From; + size_t FromLength = strlen(From); + char *ToPtr = To; + if (!ToPtr) { + int NewLength = max(length, FromLength * 2); // some reserve to avoid later reallocations + if (char *NewBuffer = (char *)realloc(result, NewLength)) { + length = NewLength; + result = NewBuffer; + } + else { + esyslog("ERROR: out of memory"); + return From; + } + ToPtr = result; + ToLength = length; + } + else if (!ToLength) + return From; // can't convert into a zero sized buffer + ToLength--; // save space for terminating 0 + char *Converted = ToPtr; + while (FromLength > 0) { + if (iconv(cd, &FromPtr, &FromLength, &ToPtr, &ToLength) == size_t(-1)) { + if (errno == E2BIG || ((errno == EILSEQ) && (ToLength < 1))) { + if (To) break; // caller provided a fixed size buffer, but it was too small + // The result buffer is too small, so increase it: + size_t d = ToPtr - result; + size_t r = length / 2; + int NewLength = length + r; + if (char *NewBuffer = (char *)realloc(result, NewLength)) { + length = NewLength; + Converted = result = NewBuffer; + } + else { + esyslog("ERROR: out of memory"); + return From; + } + ToLength += r; + ToPtr = result + d; + } + if (errno == EILSEQ) { + // A character can't be converted, so mark it with '?' and proceed: + FromPtr++; + FromLength--; + *ToPtr++ = '?'; + ToLength--; + } + else if (errno != E2BIG) + return From; // unknown error, return original string + } + } + *ToPtr = 0; + return Converted; + } + return From; +} diff --git a/libs/vdr/src/CondWait.cc b/libs/vdr/src/CondWait.cc new file mode 100644 index 0000000..efa4803 --- /dev/null +++ b/libs/vdr/src/CondWait.cc @@ -0,0 +1,101 @@ +/** + * File: CondWait.cc + * Project: libvdr - classes taken from vdr-project + * + * from "Video Disk Recorder": + * + * Copyright (C) 2000, 2003, 2006, 2008 Klaus Schmidinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * The original author can be reached at kls@tvdr.de + * + * The vdr project's page is at http://www.tvdr.de + * + */ +#include +#include +#include +#include +#include +#include + +static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow) +{ + struct timeval now; + if (gettimeofday(&now, NULL) == 0) { // get current time + now.tv_sec += MillisecondsFromNow / 1000; // add full seconds + now.tv_usec += (MillisecondsFromNow % 1000) * 1000; // add microseconds + if (now.tv_usec >= 1000000) { // take care of an overflow + now.tv_sec++; + now.tv_usec -= 1000000; + } + Abstime->tv_sec = now.tv_sec; // seconds + Abstime->tv_nsec = now.tv_usec * 1000; // nano seconds + return true; + } + return false; +} + +cCondWait::cCondWait(void) +{ + signaled = false; + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cond, NULL); +} + +cCondWait::~cCondWait() +{ + pthread_cond_broadcast(&cond); // wake up any sleepers + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&mutex); +} + +void cCondWait::SleepMs(int TimeoutMs) +{ + cCondWait w; + w.Wait(max(TimeoutMs, 3)); // making sure the time is >2ms to avoid a possible busy wait +} + +bool cCondWait::Wait(int TimeoutMs) +{ + pthread_mutex_lock(&mutex); + if (!signaled) { + if (TimeoutMs) { + struct timespec abstime; + if (GetAbsTime(&abstime, TimeoutMs)) { + while (!signaled) { + if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT) + break; + } + } + } + else + pthread_cond_wait(&cond, &mutex); + } + bool r = signaled; + signaled = false; + pthread_mutex_unlock(&mutex); + return r; +} + +void cCondWait::Signal(void) +{ + pthread_mutex_lock(&mutex); + signaled = true; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); +} diff --git a/libs/vdr/src/FileNameList.cc b/libs/vdr/src/FileNameList.cc new file mode 100644 index 0000000..74dc4f2 --- /dev/null +++ b/libs/vdr/src/FileNameList.cc @@ -0,0 +1,70 @@ +/** + * File: FileNameList.cc + * Project: libvdr - classes taken from vdr-project + * + * from "Video Disk Recorder": + * + * Copyright (C) 2000, 2003, 2006, 2008 Klaus Schmidinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * The original author can be reached at kls@tvdr.de + * + * The vdr project's page is at http://www.tvdr.de + * + */ +#include +#include +#include +#include + + +cString AddDirectory(const char *DirName, const char *FileName) +{ + return cString::sprintf("%s/%s", DirName && *DirName ? DirName : ".", FileName); +} + +// TODO better GetFileNames(const char *Directory, cStringList *List)? +cFileNameList::cFileNameList(const char *Directory, bool DirsOnly) +{ + Load(Directory, DirsOnly); +} + +bool cFileNameList::Load(const char *Directory, bool DirsOnly) +{ + Clear(); + if (Directory) { + cReadDir d(Directory); + struct dirent *e; + if (d.Ok()) { + while ((e = d.Next()) != NULL) { + if (DirsOnly) { + struct stat ds; + if (stat(AddDirectory(Directory, e->d_name), &ds) == 0) { + if (!S_ISDIR(ds.st_mode)) + continue; + } + } + Append(strdup(e->d_name)); + } + Sort(); + return true; + } + else + LOG_ERROR_STR(Directory); + } + return false; +} diff --git a/libs/vdr/src/Logging.cc b/libs/vdr/src/Logging.cc new file mode 100644 index 0000000..4db516b --- /dev/null +++ b/libs/vdr/src/Logging.cc @@ -0,0 +1,46 @@ +/** + * File: Logging.cc + * Project: libvdr - classes taken from vdr-project + * + * from "Video Disk Recorder": + * + * Copyright (C) 2000, 2003, 2006, 2008 Klaus Schmidinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * The original author can be reached at kls@tvdr.de + * + * The vdr project's page is at http://www.tvdr.de + * + */ +#include +#include +#include +#include + +int SysLogLevel = 3; + +#define MAXSYSLOGBUF 256 + +void syslog_with_tid(int priority, const char *format, ...) +{ + va_list ap; + char fmt[MAXSYSLOGBUF]; + snprintf(fmt, sizeof(fmt), "[%d] %s", cThread::ThreadId(), format); + va_start(ap, format); + vsyslog(priority, fmt, ap); + va_end(ap); +} diff --git a/libs/vdr/src/Mutex.cc b/libs/vdr/src/Mutex.cc new file mode 100644 index 0000000..cb100c7 --- /dev/null +++ b/libs/vdr/src/Mutex.cc @@ -0,0 +1,81 @@ +/** + * File: Mutex.cc + * Project: libvdr - classes taken from vdr-project + * + * from "Video Disk Recorder": + * + * Copyright (C) 2000, 2003, 2006, 2008 Klaus Schmidinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * The original author can be reached at kls@tvdr.de + * + * The vdr project's page is at http://www.tvdr.de + * + */ +#include + +cMutex::cMutex(void) +{ + locked = 0; + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP); + pthread_mutex_init(&mutex, &attr); +} + +cMutex::~cMutex() +{ + pthread_mutex_destroy(&mutex); +} + +void cMutex::Lock(void) +{ + pthread_mutex_lock(&mutex); + locked++; +} + +void cMutex::Unlock(void) +{ + if (!--locked) + pthread_mutex_unlock(&mutex); +} + +// --- cMutexLock ------------------------------------------------------------ + +cMutexLock::cMutexLock(cMutex *Mutex) +{ + mutex = NULL; + locked = false; + Lock(Mutex); +} + +cMutexLock::~cMutexLock() +{ + if (mutex && locked) + mutex->Unlock(); +} + +bool cMutexLock::Lock(cMutex *Mutex) +{ + if (Mutex && !mutex) { + mutex = Mutex; + Mutex->Lock(); + locked = true; + return true; + } + return false; +} diff --git a/libs/vdr/src/ReadDir.cc b/libs/vdr/src/ReadDir.cc new file mode 100644 index 0000000..a837f35 --- /dev/null +++ b/libs/vdr/src/ReadDir.cc @@ -0,0 +1,53 @@ +/** + * File: ReadDir.cc + * Project: libvdr - classes taken from vdr-project + * + * from "Video Disk Recorder": + * + * Copyright (C) 2000, 2003, 2006, 2008 Klaus Schmidinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * The original author can be reached at kls@tvdr.de + * + * The vdr project's page is at http://www.tvdr.de + * + */ +#include +#include +#include + +cReadDir::cReadDir(const char *Directory) +{ + directory = opendir(Directory); +} + +cReadDir::~cReadDir() +{ + if (directory) + closedir(directory); +} + +struct dirent *cReadDir::Next(void) +{ + if (directory) { + while (readdir_r(directory, &u.d, &result) == 0 && result) { + if (strcmp(result->d_name, ".") && strcmp(result->d_name, "..")) + return result; + } + } + return NULL; +} diff --git a/libs/vdr/src/String.cc b/libs/vdr/src/String.cc new file mode 100644 index 0000000..0729c77 --- /dev/null +++ b/libs/vdr/src/String.cc @@ -0,0 +1,379 @@ +/** + * File: String.cc + * Project: libvdr - classes taken from vdr-project + * + * from "Video Disk Recorder": + * + * Copyright (C) 2000, 2003, 2006, 2008 Klaus Schmidinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * The original author can be reached at kls@tvdr.de + * + * The vdr project's page is at http://www.tvdr.de + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *strcpyrealloc(char *dest, const char *src) +{ + if (src) { + int l = max(dest ? strlen(dest) : 0, strlen(src)) + 1; // don't let the block get smaller! + dest = (char *)realloc(dest, l); + if (dest) + strcpy(dest, src); + else + esyslog("ERROR: out of memory"); + } + else { + free(dest); + dest = NULL; + } + return dest; +} + +char *strn0cpy(char *dest, const char *src, size_t n) +{ + char *s = dest; + for ( ; --n && (*dest = *src) != 0; dest++, src++) ; + *dest = 0; + return s; +} + +char *strreplace(char *s, char c1, char c2) +{ + if (s) { + char *p = s; + while (*p) { + if (*p == c1) + *p = c2; + p++; + } + } + return s; +} + +char *strreplace(char *s, const char *s1, const char *s2) +{ + char *p = strstr(s, s1); + if (p) { + int of = p - s; + int l = strlen(s); + int l1 = strlen(s1); + int l2 = strlen(s2); + if (l2 > l1) { + if (char *NewBuffer = (char *)realloc(s, l + l2 - l1 + 1)) + s = NewBuffer; + else { + esyslog("ERROR: out of memory"); + return s; + } + } + char *sof = s + of; + if (l2 != l1) + memmove(sof + l2, sof + l1, l - of - l1 + 1); + strncpy(sof, s2, l2); + } + return s; +} + +char *stripspace(char *s) +{ + if (s && *s) { + for (char *p = s + strlen(s) - 1; p >= s; p--) { + if (!isspace(*p)) + break; + *p = 0; + } + } + return s; +} + +char *compactspace(char *s) +{ + if (s && *s) { + char *t = stripspace(skipspace(s)); + char *p = t; + while (p && *p) { + char *q = skipspace(p); + if (q - p > 1) + memmove(p + 1, q, strlen(q) + 1); + p++; + } + if (t != s) + memmove(s, t, strlen(t) + 1); + } + return s; +} + +cString strescape(const char *s, const char *chars) +{ + char *buffer; + const char *p = s; + char *t = NULL; + while (*p) { + if (strchr(chars, *p)) { + if (!t) { + buffer = MALLOC(char, 2 * strlen(s) + 1); + t = buffer + (p - s); + s = strcpy(buffer, s); + } + *t++ = '\\'; + } + if (t) + *t++ = *p; + p++; + } + if (t) + *t = 0; + return cString(s, t != NULL); +} + +bool startswith(const char *s, const char *p) +{ + while (*p) { + if (*p++ != *s++) + return false; + } + return true; +} + +bool endswith(const char *s, const char *p) +{ + const char *se = s + strlen(s) - 1; + const char *pe = p + strlen(p) - 1; + while (pe >= p) { + if (*pe-- != *se-- || (se < s && pe >= p)) + return false; + } + return true; +} + +bool isempty(const char *s) +{ + return !(s && *skipspace(s)); +} + +int numdigits(int n) +{ + int res = 1; + while (n >= 10) { + n /= 10; + res++; + } + return res; +} + +bool isnumber(const char *s) +{ + if (!s || !*s) + return false; + do { + if (!isdigit(*s)) + return false; + } while (*++s); + return true; +} + +int64_t StrToNum(const char *s) +{ + char *t = NULL; + int64_t n = strtoll(s, &t, 10); + if (t) { + switch (*t) { + case 'T': n *= 1024; + case 'G': n *= 1024; + case 'M': n *= 1024; + case 'K': n *= 1024; + } + } + return n; +} + +cString itoa(int n) +{ + char buf[16]; + snprintf(buf, sizeof(buf), "%d", n); + return buf; +} + +// --- cString --------------------------------------------------------------- + +cString::cString(const char *S, bool TakePointer) +{ + s = TakePointer ? (char *)S : S ? strdup(S) : NULL; +} + +cString::cString(const cString &String) +{ + s = String.s ? strdup(String.s) : NULL; +} + +cString::~cString() +{ + free(s); +} + +cString &cString::operator=(const cString &String) +{ + if (this == &String) + return *this; + free(s); + s = String.s ? strdup(String.s) : NULL; + return *this; +} + +cString &cString::operator=(const char *String) +{ + if (s == String) + return *this; + free(s); + s = String ? strdup(String) : NULL; + return *this; +} + +cString &cString::Truncate(int Index) +{ + int l = strlen(s); + if (Index < 0) + Index = l + Index; + if (Index >= 0 && Index < l) + s[Index] = 0; + return *this; +} + +cString cString::sprintf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + char *buffer; + if (!fmt || vasprintf(&buffer, fmt, ap) < 0) { + esyslog("error in vasprintf('%s', ...)", fmt); + buffer = strdup("???"); + } + va_end(ap); + return cString(buffer, true); +} + +cString cString::vsprintf(const char *fmt, va_list &ap) +{ + char *buffer; + if (!fmt || vasprintf(&buffer, fmt, ap) < 0) { + esyslog("error in vasprintf('%s', ...)", fmt); + buffer = strdup("???"); + } + return cString(buffer, true); +} + +cString WeekDayName(int WeekDay) +{ + char buffer[16]; + WeekDay = WeekDay == 0 ? 6 : WeekDay - 1; // we start with Monday==0! + if (0 <= WeekDay && WeekDay <= 6) { + // TRANSLATORS: abbreviated weekdays, beginning with monday (must all be 3 letters!) + const char *day = tr("MonTueWedThuFriSatSun"); + day += Utf8SymChars(day, WeekDay * 3); + strn0cpy(buffer, day, min(Utf8SymChars(day, 3) + 1, int(sizeof(buffer)))); + return buffer; + } + else + return "???"; +} + +cString WeekDayName(time_t t) +{ + struct tm tm_r; + return WeekDayName(localtime_r(&t, &tm_r)->tm_wday); +} + +cString WeekDayNameFull(int WeekDay) +{ + WeekDay = WeekDay == 0 ? 6 : WeekDay - 1; // we start with Monday==0! + switch (WeekDay) { + case 0: return tr("Monday"); + case 1: return tr("Tuesday"); + case 2: return tr("Wednesday"); + case 3: return tr("Thursday"); + case 4: return tr("Friday"); + case 5: return tr("Saturday"); + case 6: return tr("Sunday"); + default: return "???"; + } +} + +cString WeekDayNameFull(time_t t) +{ + struct tm tm_r; + return WeekDayNameFull(localtime_r(&t, &tm_r)->tm_wday); +} + +cString DayDateTime(time_t t) +{ + char buffer[32]; + if (t == 0) + time(&t); + struct tm tm_r; + tm *tm = localtime_r(&t, &tm_r); + snprintf(buffer, sizeof(buffer), "%s %02d.%02d. %02d:%02d", *WeekDayName(tm->tm_wday), tm->tm_mday, tm->tm_mon + 1, tm->tm_hour, tm->tm_min); + return buffer; +} + +cString TimeToString(time_t t) +{ + char buffer[32]; + if (ctime_r(&t, buffer)) { + buffer[strlen(buffer) - 1] = 0; // strip trailing newline + return buffer; + } + return "???"; +} + +cString DateString(time_t t) +{ + char buf[32]; + struct tm tm_r; + tm *tm = localtime_r(&t, &tm_r); + char *p = stpcpy(buf, WeekDayName(tm->tm_wday)); + *p++ = ' '; + strftime(p, sizeof(buf) - (p - buf), "%d.%m.%Y", tm); + return buf; +} + +cString ShortDateString(time_t t) +{ + char buf[32]; + struct tm tm_r; + tm *tm = localtime_r(&t, &tm_r); + strftime(buf, sizeof(buf), "%d.%m.%y", tm); + return buf; +} + +cString TimeString(time_t t) +{ + char buf[25]; + struct tm tm_r; + strftime(buf, sizeof(buf), "%R", localtime_r(&t, &tm_r)); + return buf; +} diff --git a/libs/vdr/src/StringList.cc b/libs/vdr/src/StringList.cc new file mode 100644 index 0000000..5b3adf5 --- /dev/null +++ b/libs/vdr/src/StringList.cc @@ -0,0 +1,50 @@ +/** + * File: StringList.cc + * Project: libvdr - classes taken from vdr-project + * + * from "Video Disk Recorder": + * + * Copyright (C) 2000, 2003, 2006, 2008 Klaus Schmidinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * The original author can be reached at kls@tvdr.de + * + * The vdr project's page is at http://www.tvdr.de + * + */ +#include + +cStringList::~cStringList() +{ + Clear(); +} + +int cStringList::Find(const char *s) const +{ + for (int i = 0; i < Size(); i++) { + if (!strcmp(s, At(i))) + return i; + } + return -1; +} + +void cStringList::Clear(void) +{ + for (int i = 0; i < Size(); i++) + free(At(i)); + cVector::Clear(); +} diff --git a/libs/vdr/src/Thread.cc b/libs/vdr/src/Thread.cc new file mode 100644 index 0000000..25d23e3 --- /dev/null +++ b/libs/vdr/src/Thread.cc @@ -0,0 +1,209 @@ +/** + * File: Thread.cc + * Project: libvdr - classes taken from vdr-project + * + * from "Video Disk Recorder": + * + * Copyright (C) 2000, 2003, 2006, 2008 Klaus Schmidinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * The original author can be reached at kls@tvdr.de + * + * The vdr project's page is at http://www.tvdr.de + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +tThreadId cThread::mainThreadId = 0; + +cThread::cThread(const char *Description) + : active(false) + , running(false) + , childTid(0) + , childThreadId(0) + , description(NULL) + , threadCallback(NULL) + , opaque(NULL) +{ + if (Description) SetDescription("%s", Description); +} + +cThread::cThread(int (*ThreadCallback)(void *, cThread *), void *Opaque, const char *Description) + : active(false) + , running(false) + , childTid(0) + , childThreadId(0) + , description(NULL) + , threadCallback(ThreadCallback) + , opaque(Opaque) +{ + if (Description) SetDescription("%s", Description); +} + +cThread::~cThread() +{ + Cancel(); // just in case the derived class didn't call it + free(description); +} + +void cThread::SetPriority(int Priority) +{ + if (setpriority(PRIO_PROCESS, 0, Priority) < 0) + LOG_ERROR; +} + +void cThread::SetIOPriority(int Priority) +{ + if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (2 << 13)) < 0) // best effort class + LOG_ERROR; +} + +void cThread::SetDescription(const char *Description, ...) +{ + free(description); + description = NULL; + if (Description) { + va_list ap; + va_start(ap, Description); + vasprintf(&description, Description, ap); + va_end(ap); + } +} + +void *cThread::StartThread(cThread *Thread) +{ + Thread->childThreadId = ThreadId(); + if (Thread->description) { + dsyslog("%s thread started (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId); +#ifdef PR_SET_NAME + if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0) + esyslog("%s thread naming failed (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId); +#endif + } + Thread->Action(); + if (Thread->description) + dsyslog("%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId); + Thread->running = false; + Thread->active = false; + + return NULL; +} + +#define THREAD_STOP_TIMEOUT 3000 // ms to wait for a thread to stop before newly starting it +#define THREAD_STOP_SLEEP 30 // ms to sleep while waiting for a thread to stop + +bool cThread::Start(void) +{ + if (!running) { + if (active) { + // Wait until the previous incarnation of this thread has completely ended + // before starting it newly: + cTimeMs RestartTimeout; + while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT) + cCondWait::SleepMs(THREAD_STOP_SLEEP); + } + if (!active) { + active = running = true; + if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) { + pthread_detach(childTid); // auto-reap + } + else { + LOG_ERROR; + active = running = false; + return false; + } + } + } + return true; +} + +void cThread::Action() +{ + if (!threadCallback) *((char *)0) = 0; + ///< if not used the callback constructor, cThread must be subclassed, so stop application here! + + threadCallback(opaque, this); // passing this as parameter allows the callback + // use the public interface of this instance. +} + +bool cThread::Active(void) +{ + if (active) { + // + // Single UNIX Spec v2 says: + // + // The pthread_kill() function is used to request + // that a signal be delivered to the specified thread. + // + // As in kill(), if sig is zero, error checking is + // performed but no signal is actually sent. + // + int err; + if ((err = pthread_kill(childTid, 0)) != 0) { + if (err != ESRCH) + LOG_ERROR; + childTid = 0; + active = running = false; + } + else + return true; + } + return false; +} + +void cThread::Cancel(int WaitSeconds) +{ + running = false; + if (active && WaitSeconds > -1) { + if (WaitSeconds > 0) { + for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) { + if (!Active()) + return; + cCondWait::SleepMs(10); + } + esyslog("ERROR: %s thread %d won't end (waited %d seconds) - canceling it...", description ? description : "", childThreadId, WaitSeconds); + } + pthread_cancel(childTid); + childTid = 0; + active = false; + } +} + +tThreadId cThread::ThreadId(void) +{ + return syscall(__NR_gettid); +} + +void cThread::SetMainThreadId(void) +{ + if (mainThreadId == 0) + mainThreadId = ThreadId(); + else + esyslog("ERROR: attempt to set main thread id to %d while it already is %d", ThreadId(), mainThreadId); +} diff --git a/libs/vdr/src/TimeMs.cc b/libs/vdr/src/TimeMs.cc new file mode 100644 index 0000000..620481e --- /dev/null +++ b/libs/vdr/src/TimeMs.cc @@ -0,0 +1,101 @@ +/** + * File: TimeMs.cc + * Project: libvdr - classes taken from vdr-project + * + * from "Video Disk Recorder": + * + * Copyright (C) 2000, 2003, 2006, 2008 Klaus Schmidinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * The original author can be reached at kls@tvdr.de + * + * The vdr project's page is at http://www.tvdr.de + * + */ +#include +#include +#include +#include +#include +#include +#include + +cTimeMs::cTimeMs(int Ms) +{ + if (Ms >= 0) + Set(Ms); + else + begin = 0; +} + +uint64_t cTimeMs::Now(void) +{ +#if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK) +#define MIN_RESOLUTION 5 // ms + static bool initialized = false; + static bool monotonic = false; + struct timespec tp; + if (!initialized) { + // check if monotonic timer is available and provides enough accurate resolution: + if (clock_getres(CLOCK_MONOTONIC, &tp) == 0) { + long Resolution = tp.tv_nsec; + // require a minimum resolution: + if (tp.tv_sec == 0 && tp.tv_nsec <= MIN_RESOLUTION * 1000000) { + if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) { + dsyslog("cTimeMs: using monotonic clock (resolution is %ld ns)", Resolution); + monotonic = true; + } + else + esyslog("cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed"); + } + else + dsyslog("cTimeMs: not using monotonic clock - resolution is too bad (%ld s %ld ns)", tp.tv_sec, tp.tv_nsec); + } + else + esyslog("cTimeMs: clock_getres(CLOCK_MONOTONIC) failed"); + initialized = true; + } + if (monotonic) { + if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) + return (uint64_t(tp.tv_sec)) * 1000 + tp.tv_nsec / 1000000; + esyslog("cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed"); + monotonic = false; + // fall back to gettimeofday() + } +#else +# warning Posix monotonic clock not available +#endif + struct timeval t; + if (gettimeofday(&t, NULL) == 0) + return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000; + return 0; +} + +void cTimeMs::Set(int Ms) +{ + begin = Now() + Ms; +} + +bool cTimeMs::TimedOut(void) +{ + return Now() >= begin; +} + +uint64_t cTimeMs::Elapsed(void) +{ + return Now() - begin; +} diff --git a/libs/vdr/src/i18n.cc b/libs/vdr/src/i18n.cc new file mode 100644 index 0000000..a6d8d65 --- /dev/null +++ b/libs/vdr/src/i18n.cc @@ -0,0 +1,324 @@ +/** + * File: i18n.cc + * Project: libvdr - classes taken from vdr-project + * + * from "Video Disk Recorder": + * + * Copyright (C) 2000, 2003, 2006, 2008 Klaus Schmidinger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * + * The original author can be reached at kls@tvdr.de + * + * The vdr project's page is at http://www.tvdr.de + * + * In case an English phrase is used in more than one context (and might need + * different translations in other languages) it can be preceded with an + * arbitrary string to describe its context, separated from the actual phrase + * by a '$' character (see for instance "Button$Stop" vs. "Stop"). + * Of course this means that no English phrase may contain the '$' character! + * If this should ever become necessary, the existing '$' would have to be + * replaced with something different... + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// TRANSLATORS: The name of the language, as written natively +const char *LanguageName = trNOOP("LanguageName$English"); +// TRANSLATORS: The 3-letter code of the language +const char *LanguageCode = trNOOP("LanguageCode$eng"); + +#define AsString(s) #s + +// List of known language codes with aliases. +// Actually we could list all codes from http://www.loc.gov/standards/iso639-2 +// here, but that would be several hundreds - and for most of them it's unlikely +// they're ever going to be used... + +const char *LanguageCodeList[] = { + "eng,dos", + "deu,ger", + "slv,slo", + "ita", + "dut,nla,nld", + "prt", + "fra,fre", + "nor", + "fin,suo", + "pol", + "esl,spa", + "ell,gre", + "sve,swe", + "rom,rum", + "hun", + "cat,cln", + "rus", + "srb,srp,scr,scc", + "hrv", + "est", + "dan", + "cze,ces", + "tur", + "ukr", + "ara", + NULL + }; + +static const char *I18nLocaleDir = AsString(LOCDIR); + +static cStringList LanguageLocales; +static cStringList LanguageNames; +static cStringList LanguageCodes; + +static int NumLocales = 1; +static int CurrentLanguage = 0; + +static bool ContainsCode(const char *Codes, const char *Code) +{ + while (*Codes) { + int l = 0; + for ( ; l < 3 && Code[l]; l++) { + if (Codes[l] != tolower(Code[l])) + break; + } + if (l == 3) + return true; + Codes++; + } + return false; +} + +static const char *SkipContext(const char *s) +{ + const char *p = strchr(s, '$'); + return p ? p + 1 : s; +} + +static void SetEnvLanguage(const char *Locale) +{ + setenv("LANGUAGE", Locale, 1); + extern int _nl_msg_cat_cntr; + ++_nl_msg_cat_cntr; +} + +void I18nInitialize(const char *LocaleDir) +{ + if (LocaleDir) + I18nLocaleDir = LocaleDir; + LanguageLocales.Append(strdup(I18N_DEFAULT_LOCALE)); + LanguageNames.Append(strdup(SkipContext(LanguageName))); + LanguageCodes.Append(strdup(LanguageCodeList[0])); + textdomain("vdr"); + bindtextdomain("vdr", I18nLocaleDir); + cFileNameList Locales(I18nLocaleDir, true); + if (Locales.Size() > 0) { + char *OldLocale = strdup(setlocale(LC_MESSAGES, NULL)); + for (int i = 0; i < Locales.Size(); i++) { + cString FileName = cString::sprintf("%s/%s/LC_MESSAGES/vdr.mo", I18nLocaleDir, Locales[i]); + if (access(FileName, F_OK) == 0) { // found a locale with VDR texts + if (NumLocales < I18N_MAX_LANGUAGES - 1) { + SetEnvLanguage(Locales[i]); + const char *TranslatedLanguageName = gettext(LanguageName); + if (TranslatedLanguageName != LanguageName) { + NumLocales++; + if (strstr(OldLocale, Locales[i]) == OldLocale) + CurrentLanguage = LanguageLocales.Size(); + LanguageLocales.Append(strdup(Locales[i])); + LanguageNames.Append(strdup(TranslatedLanguageName)); + const char *Code = gettext(LanguageCode); + for (const char **lc = LanguageCodeList; *lc; lc++) { + if (ContainsCode(*lc, Code)) { + Code = *lc; + break; + } + } + LanguageCodes.Append(strdup(Code)); + } + } + else { + esyslog("ERROR: too many locales - increase I18N_MAX_LANGUAGES!"); + break; + } + } + } + SetEnvLanguage(LanguageLocales[CurrentLanguage]); + free(OldLocale); + dsyslog("found %d locales in %s", NumLocales - 1, I18nLocaleDir); + } + // Prepare any known language codes for which there was no locale: + for (const char **lc = LanguageCodeList; *lc; lc++) { + bool Found = false; + for (int i = 0; i < LanguageCodes.Size(); i++) { + if (strcmp(*lc, LanguageCodes[i]) == 0) { + Found = true; + break; + } + } + if (!Found) { + dsyslog("no locale for language code '%s'", *lc); + LanguageLocales.Append(strdup(I18N_DEFAULT_LOCALE)); + LanguageNames.Append(strdup(*lc)); + LanguageCodes.Append(strdup(*lc)); + } + } +} + +void I18nRegister(const char *Plugin) +{ + cString Domain = cString::sprintf("vdr-%s", Plugin); + bindtextdomain(Domain, I18nLocaleDir); +} + +void I18nSetLocale(const char *Locale) +{ + if (Locale && *Locale) { + int i = LanguageLocales.Find(Locale); + if (i >= 0) { + CurrentLanguage = i; + SetEnvLanguage(Locale); + } + else + dsyslog("unknown locale: '%s'", Locale); + } +} + +int I18nCurrentLanguage(void) +{ + return CurrentLanguage; +} + +void I18nSetLanguage(int Language) +{ + if (Language < LanguageNames.Size()) { + CurrentLanguage = Language; + I18nSetLocale(I18nLocale(CurrentLanguage)); + } +} + +int I18nNumLanguagesWithLocale(void) +{ + return NumLocales; +} + +const cStringList *I18nLanguages(void) +{ + return &LanguageNames; +} + +const char *I18nTranslate(const char *s, const char *Plugin) +{ + if (!s) + return s; + if (CurrentLanguage) { + const char *t = Plugin ? dgettext(Plugin, s) : gettext(s); + if (t != s) + return t; + } + return SkipContext(s); +} + +const char *I18nLocale(int Language) +{ + return 0 <= Language && Language < LanguageLocales.Size() ? LanguageLocales[Language] : NULL; +} + +const char *I18nLanguageCode(int Language) +{ + return 0 <= Language && Language < LanguageCodes.Size() ? LanguageCodes[Language] : NULL; +} + +int I18nLanguageIndex(const char *Code) +{ + for (int i = 0; i < LanguageCodes.Size(); i++) { + if (ContainsCode(LanguageCodes[i], Code)) + return i; + } + //dsyslog("unknown language code: '%s'", Code); + return -1; +} + +const char *I18nNormalizeLanguageCode(const char *Code) +{ + for (int i = 0; i < 3; i++) { + if (Code[i]) { + // ETSI EN 300 468 defines language codes as consisting of three letters + // according to ISO 639-2. This means that they are supposed to always consist + // of exactly three letters in the range a-z - no digits, UTF-8 or other + // funny characters. However, some broadcasters apparently don't have a + // copy of the DVB standard (or they do, but are perhaps unable to read it), + // so they put all sorts of non-standard stuff into the language codes, + // like nonsense as "2ch" or "A 1" (yes, they even go as far as using + // blanks!). Such things should go into the description of the EPG event's + // ComponentDescriptor. + // So, as a workaround for this broadcaster stupidity, let's ignore + // language codes with unprintable characters... + if (!isprint(Code[i])) { + //dsyslog("invalid language code: '%s'", Code); + return "???"; + } + // ...and replace blanks with underlines (ok, this breaks the 'const' + // of the Code parameter - but hey, it's them who started this): + if (Code[i] == ' ') + *((char *)&Code[i]) = '_'; + } + else + break; + } + int n = I18nLanguageIndex(Code); + return n >= 0 ? I18nLanguageCode(n) : Code; +} + +bool I18nIsPreferredLanguage(int *PreferredLanguages, const char *LanguageCode, int &OldPreference, int *Position) +{ + int pos = 1; + bool found = false; + while (LanguageCode) { + int LanguageIndex = I18nLanguageIndex(LanguageCode); + for (int i = 0; i < LanguageCodes.Size(); i++) { + if (PreferredLanguages[i] < 0) + break; // the language is not a preferred one + if (PreferredLanguages[i] == LanguageIndex) { + if (OldPreference < 0 || i < OldPreference) { + OldPreference = i; + if (Position) + *Position = pos; + found = true; + break; + } + } + } + if ((LanguageCode = strchr(LanguageCode, '+')) != NULL) { + LanguageCode++; + pos++; + } + else if (pos == 1 && Position) + *Position = 0; + } + if (OldPreference < 0) { + OldPreference = LanguageCodes.Size(); // higher than the maximum possible value + return true; // if we don't find a preferred one, we take the first one + } + return found; +} -- cgit v1.2.3