summaryrefslogtreecommitdiff
path: root/libs/vdr/src
diff options
context:
space:
mode:
authorgeronimo <geronimo013@gmx.de>2012-07-13 04:26:40 +0200
committergeronimo <geronimo013@gmx.de>2012-07-13 04:26:40 +0200
commit2d48ae784ea6828e8626c32c848f64232d8f35c0 (patch)
treefab114b03e91125783a778b835dd1913b039cebe /libs/vdr/src
downloadcmp-2d48ae784ea6828e8626c32c848f64232d8f35c0.tar.gz
cmp-2d48ae784ea6828e8626c32c848f64232d8f35c0.tar.bz2
initial import
Diffstat (limited to 'libs/vdr/src')
-rw-r--r--libs/vdr/src/CharsetConv.cc294
-rw-r--r--libs/vdr/src/CondWait.cc101
-rw-r--r--libs/vdr/src/FileNameList.cc70
-rw-r--r--libs/vdr/src/Logging.cc46
-rw-r--r--libs/vdr/src/Mutex.cc81
-rw-r--r--libs/vdr/src/ReadDir.cc53
-rw-r--r--libs/vdr/src/String.cc379
-rw-r--r--libs/vdr/src/StringList.cc50
-rw-r--r--libs/vdr/src/Thread.cc209
-rw-r--r--libs/vdr/src/TimeMs.cc101
-rw-r--r--libs/vdr/src/i18n.cc324
11 files changed, 1708 insertions, 0 deletions
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 <CharsetConv.h>
+#include <Logging.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <tools.h>
+
+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 <CondWait.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <errno.h>
+#include <tools.h>
+
+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 <FileNameList.h>
+#include <ReadDir.h>
+#include <String.h>
+#include <sys/stat.h>
+
+
+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 <Logging.h>
+#include <Thread.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+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 <Mutex.h>
+
+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 <ReadDir.h>
+#include <stddef.h>
+#include <string.h>
+
+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 <String.h>
+#include <Logging.h>
+#include <i18n.h>
+#include <tools.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+#include <ctype.h>
+
+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 <StringList.h>
+
+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<char *>::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 <Thread.h>
+#include <TimeMs.h>
+#include <CondWait.h>
+#include <Logging.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/resource.h>
+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 <TimeMs.h>
+#include <Logging.h>
+#include <stddef.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+
+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 <i18n.h>
+#include <String.h>
+#include <StringList.h>
+#include <FileNameList.h>
+#include <Logging.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <libintl.h>
+#include <locale.h>
+#include <unistd.h>
+
+// 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;
+}