vdr  2.7.6
tools.c
Go to the documentation of this file.
1 /*
2  * tools.c: Various tools
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: tools.c 5.17 2025/06/18 08:44:47 kls Exp $
8  */
9 
10 #include "tools.h"
11 #include <ctype.h>
12 #include <dirent.h>
13 #include <errno.h>
14 extern "C" {
15 #ifdef boolean
16 #define HAVE_BOOLEAN
17 #endif
18 #include <jpeglib.h>
19 #undef boolean
20 }
21 #include <locale.h>
22 #include <stdlib.h>
23 #include <sys/time.h>
24 #include <sys/vfs.h>
25 #include <time.h>
26 #include <unistd.h>
27 #include <utime.h>
28 #include "i18n.h"
29 #include "thread.h"
30 
31 int SysLogLevel = 3;
32 
33 #define MAXSYSLOGBUF 256
34 
35 void syslog_with_tid(int priority, const char *format, ...)
36 {
37  va_list ap;
38  char fmt[MAXSYSLOGBUF];
39  snprintf(fmt, sizeof(fmt), "[%d] %s", cThread::ThreadId(), format);
40  va_start(ap, format);
41  vsyslog(priority, fmt, ap);
42  va_end(ap);
43 }
44 
45 int BCD2INT(int x)
46 {
47  return ((1000000 * BCDCHARTOINT((x >> 24) & 0xFF)) +
48  (10000 * BCDCHARTOINT((x >> 16) & 0xFF)) +
49  (100 * BCDCHARTOINT((x >> 8) & 0xFF)) +
50  BCDCHARTOINT( x & 0xFF));
51 }
52 
53 ssize_t safe_read(int filedes, void *buffer, size_t size)
54 {
55  for (;;) {
56  ssize_t p = read(filedes, buffer, size);
57  if (p < 0 && errno == EINTR) {
58  dsyslog("EINTR while reading from file handle %d - retrying", filedes);
59  continue;
60  }
61  return p;
62  }
63 }
64 
65 ssize_t safe_write(int filedes, const void *buffer, size_t size)
66 {
67  ssize_t p = 0;
68  ssize_t written = size;
69  const unsigned char *ptr = (const unsigned char *)buffer;
70  while (size > 0) {
71  p = write(filedes, ptr, size);
72  if (p < 0) {
73  if (errno == EINTR) {
74  dsyslog("EINTR while writing to file handle %d - retrying", filedes);
75  continue;
76  }
77  break;
78  }
79  ptr += p;
80  size -= p;
81  }
82  return p < 0 ? p : written;
83 }
84 
85 void writechar(int filedes, char c)
86 {
87  safe_write(filedes, &c, sizeof(c));
88 }
89 
90 int WriteAllOrNothing(int fd, const uchar *Data, int Length, int TimeoutMs, int RetryMs)
91 {
92  int written = 0;
93  while (Length > 0) {
94  int w = write(fd, Data + written, Length);
95  if (w > 0) {
96  Length -= w;
97  written += w;
98  }
99  else if (written > 0 && !FATALERRNO) {
100  // we've started writing, so we must finish it!
101  cTimeMs t;
102  cPoller Poller(fd, true);
103  Poller.Poll(RetryMs);
104  if (TimeoutMs > 0 && (TimeoutMs -= t.Elapsed()) <= 0)
105  break;
106  }
107  else
108  // nothing written yet (or fatal error), so we can just return the error code:
109  return w;
110  }
111  return written;
112 }
113 
114 char *strcpyrealloc(char *dest, const char *src)
115 {
116  if (src) {
117  int l = max(dest ? strlen(dest) : 0, strlen(src)) + 1; // don't let the block get smaller!
118  dest = (char *)realloc(dest, l);
119  if (dest)
120  strcpy(dest, src);
121  else
122  esyslog("ERROR: out of memory");
123  }
124  else {
125  free(dest);
126  dest = NULL;
127  }
128  return dest;
129 }
130 
131 char *strn0cpy(char *dest, const char *src, size_t n)
132 {
133  char *s = dest;
134  if (dest && n) {
135  if (src)
136  for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
137  *dest = 0;
138  }
139  return s;
140 }
141 
142 char *strreplace(char *s, char c1, char c2)
143 {
144  if (s) {
145  char *p = s;
146  while (*p) {
147  if (*p == c1)
148  *p = c2;
149  p++;
150  }
151  }
152  return s;
153 }
154 
155 char *strreplace(char *s, const char *s1, const char *s2)
156 {
157  if (!s || !s1 || !*s1 || !s2 || strcmp(s1, s2) == 0)
158  return s;
159  char *q = s;
160  if (char *p = strstr(s, s1)) {
161  int l = strlen(s);
162  int l1 = strlen(s1);
163  int l2 = strlen(s2);
164  do {
165  int of = p - s;
166  if (l2 > l1) {
167  if (char *NewBuffer = (char *)realloc(s, l + l2 - l1 + 1))
168  s = NewBuffer;
169  else {
170  esyslog("ERROR: out of memory");
171  return s;
172  }
173  }
174  char *sof = s + of;
175  if (l2 != l1) {
176  memmove(sof + l2, sof + l1, l - of - l1 + 1);
177  l += l2 - l1;
178  }
179  memcpy(sof, s2, l2);
180  q = sof + l2;
181  } while (p = strstr(q, s1));
182  }
183  return s;
184 }
185 
186 const char *strchrn(const char *s, char c, size_t n)
187 {
188  if (n == 0)
189  return s;
190  if (s) {
191  for ( ; *s; s++) {
192  if (*s == c && --n == 0)
193  return s;
194  }
195  }
196  return NULL;
197 }
198 
199 int strcountchr(const char *s, char c)
200 {
201  int n = 0;
202  if (s && c) {
203  for ( ; *s; s++) {
204  if (*s == c)
205  n++;
206  }
207  }
208  return n;
209 }
210 
211 cString strgetbefore(const char *s, char c, int n)
212 {
213  const char *p = strrchr(s, 0); // points to the terminating 0 of s
214  while (--p >= s) {
215  if (*p == c && --n == 0)
216  break;
217  }
218  return cString(s, p);
219 }
220 
221 const char *strgetlast(const char *s, char c)
222 {
223  const char *p = strrchr(s, c);
224  return p ? p + 1 : s;
225 }
226 
227 char *stripspace(char *s)
228 {
229  if (s && *s) {
230  for (char *p = s + strlen(s) - 1; p >= s; p--) {
231  if (!isspace(*p))
232  break;
233  *p = 0;
234  }
235  }
236  return s;
237 }
238 
239 char *compactspace(char *s)
240 {
241  if (s && *s) {
242  char *t = stripspace(skipspace(s));
243  char *p = t;
244  while (p && *p) {
245  char *q = skipspace(p);
246  if (q - p > 1)
247  memmove(p + 1, q, strlen(q) + 1);
248  p++;
249  }
250  if (t != s)
251  memmove(s, t, strlen(t) + 1);
252  }
253  return s;
254 }
255 
256 char *compactchars(char *s, char c)
257 {
258  if (s && *s && c) {
259  char *t = s;
260  char *p = s;
261  int n = 0;
262  while (*p) {
263  if (*p != c) {
264  *t++ = *p;
265  n = 0;
266  }
267  else if (t != s && n == 0) {
268  *t++ = *p;
269  n++;
270  }
271  p++;
272  }
273  if (n)
274  t--; // the last character was c
275  *t = 0;
276  }
277  return s;
278 }
279 
280 cString strescape(const char *s, const char *chars)
281 {
282  char *buffer;
283  const char *p = s;
284  char *t = NULL;
285  while (*p) {
286  if (strchr(chars, *p)) {
287  if (!t) {
288  buffer = MALLOC(char, 2 * strlen(s) + 1);
289  t = buffer + (p - s);
290  s = strcpy(buffer, s);
291  }
292  *t++ = '\\';
293  }
294  if (t)
295  *t++ = *p;
296  p++;
297  }
298  if (t)
299  *t = 0;
300  return cString(s, t != NULL);
301 }
302 
303 cString strgetval(const char *s, const char *name, char d)
304 {
305  if (s && name) {
306  int l = strlen(name);
307  const char *t = s;
308  while (const char *p = strstr(t, name)) {
309  t = skipspace(p + l);
310  if (p == s || *(p - 1) <= ' ') {
311  if (*t == d) {
312  t = skipspace(t + 1);
313  const char *v = t;
314  while (*t > ' ')
315  t++;
316  return cString(v, t);
317  break;
318  }
319  }
320  }
321  }
322  return NULL;
323 }
324 
325 char *strshift(char *s, int n)
326 {
327  if (s && n > 0) {
328  int l = strlen(s);
329  if (n < l)
330  memmove(s, s + n, l - n + 1); // we also copy the terminating 0!
331  else
332  *s = 0;
333  }
334  return s;
335 }
336 
337 bool startswith(const char *s, const char *p)
338 {
339  while (*p) {
340  if (*p++ != *s++)
341  return false;
342  }
343  return true;
344 }
345 
346 bool endswith(const char *s, const char *p)
347 {
348  const char *se = s + strlen(s) - 1;
349  const char *pe = p + strlen(p) - 1;
350  while (pe >= p) {
351  if (*pe-- != *se-- || (se < s && pe >= p))
352  return false;
353  }
354  return true;
355 }
356 
357 bool isempty(const char *s)
358 {
359  return !(s && *skipspace(s));
360 }
361 
362 int numdigits(int n)
363 {
364  int res = 1;
365  while (n >= 10) {
366  n /= 10;
367  res++;
368  }
369  return res;
370 }
371 
372 bool isnumber(const char *s)
373 {
374  if (!s || !*s)
375  return false;
376  do {
377  if (!isdigit(*s))
378  return false;
379  } while (*++s);
380  return true;
381 }
382 
383 int64_t StrToNum(const char *s)
384 {
385  char *t = NULL;
386  int64_t n = strtoll(s, &t, 10);
387  if (t) {
388  switch (*t) {
389  case 'T': n *= 1024;
390  case 'G': n *= 1024;
391  case 'M': n *= 1024;
392  case 'K': n *= 1024;
393  }
394  }
395  return n;
396 }
397 
398 bool StrInArray(const char *a[], const char *s)
399 {
400  if (a) {
401  while (*a) {
402  if (strcmp(*a, s) == 0)
403  return true;
404  a++;
405  }
406  }
407  return false;
408 }
409 
410 cString AddDirectory(const char *DirName, const char *FileName)
411 {
412  if (*FileName == '/')
413  FileName++;
414  return cString::sprintf("%s/%s", DirName && *DirName ? DirName : ".", FileName);
415 }
416 
417 #define DECIMAL_POINT_C '.'
418 
419 double atod(const char *s)
420 {
421  static lconv *loc = localeconv();
422  if (*loc->decimal_point != DECIMAL_POINT_C) {
423  char buf[strlen(s) + 1];
424  char *p = buf;
425  while (*s) {
426  if (*s == DECIMAL_POINT_C)
427  *p = *loc->decimal_point;
428  else
429  *p = *s;
430  p++;
431  s++;
432  }
433  *p = 0;
434  return atof(buf);
435  }
436  else
437  return atof(s);
438 }
439 
440 cString dtoa(double d, const char *Format)
441 {
442  static lconv *loc = localeconv();
443  char buf[16];
444  snprintf(buf, sizeof(buf), Format, d);
445  if (*loc->decimal_point != DECIMAL_POINT_C)
446  strreplace(buf, *loc->decimal_point, DECIMAL_POINT_C);
447  return buf;
448 }
449 
450 cString itoa(int n)
451 {
452  char buf[16];
453  snprintf(buf, sizeof(buf), "%d", n);
454  return buf;
455 }
456 
457 bool EntriesOnSameFileSystem(const char *File1, const char *File2)
458 {
459  struct stat st;
460  if (stat(File1, &st) == 0) {
461  dev_t dev1 = st.st_dev;
462  if (stat(File2, &st) == 0)
463  return st.st_dev == dev1;
464  else
465  LOG_ERROR_STR(File2);
466  }
467  else
468  LOG_ERROR_STR(File1);
469  return true; // we only return false if both files actually exist and are in different file systems!
470 }
471 
472 int FreeDiskSpaceMB(const char *Directory, int *UsedMB)
473 {
474  if (UsedMB)
475  *UsedMB = 0;
476  int Free = 0;
477  struct statfs statFs;
478  if (statfs(Directory, &statFs) == 0) {
479  double blocksPerMeg = 1024.0 * 1024.0 / statFs.f_bsize;
480  if (UsedMB)
481  *UsedMB = int((statFs.f_blocks - statFs.f_bfree) / blocksPerMeg);
482  Free = int(statFs.f_bavail / blocksPerMeg);
483  }
484  else
485  LOG_ERROR_STR(Directory);
486  return Free;
487 }
488 
489 bool DirectoryOk(const char *DirName, bool LogErrors)
490 {
491  struct stat ds;
492  if (stat(DirName, &ds) == 0) {
493  if (S_ISDIR(ds.st_mode)) {
494  if (access(DirName, R_OK | W_OK | X_OK) == 0)
495  return true;
496  else if (LogErrors)
497  esyslog("ERROR: can't access %s", DirName);
498  }
499  else if (LogErrors)
500  esyslog("ERROR: %s is not a directory", DirName);
501  }
502  else if (LogErrors)
503  LOG_ERROR_STR(DirName);
504  return false;
505 }
506 
507 bool MakeDirs(const char *FileName, bool IsDirectory)
508 {
509  bool result = true;
510  char *s = strdup(FileName);
511  char *p = s;
512  if (*p == '/')
513  p++;
514  while ((p = strchr(p, '/')) != NULL || IsDirectory) {
515  if (p)
516  *p = 0;
517  struct stat fs;
518  if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
519  dsyslog("creating directory %s", s);
520  if (mkdir(s, ACCESSPERMS) == -1) {
521  LOG_ERROR_STR(s);
522  result = false;
523  break;
524  }
525  }
526  if (p)
527  *p++ = '/';
528  else
529  break;
530  }
531  free(s);
532  return result;
533 }
534 
535 bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
536 {
537  struct stat st;
538  if (stat(FileName, &st) == 0) {
539  if (S_ISDIR(st.st_mode)) {
540  cReadDir d(FileName);
541  if (d.Ok()) {
542  struct dirent *e;
543  while ((e = d.Next()) != NULL) {
544  cString buffer = AddDirectory(FileName, e->d_name);
545  if (FollowSymlinks) {
546  struct stat st2;
547  if (lstat(buffer, &st2) == 0) {
548  if (S_ISLNK(st2.st_mode)) {
549  int size = st2.st_size + 1;
550  char *l = MALLOC(char, size);
551  int n = readlink(buffer, l, size - 1);
552  if (n < 0) {
553  if (errno != EINVAL)
554  LOG_ERROR_STR(*buffer);
555  }
556  else {
557  l[n] = 0;
558  dsyslog("removing %s", l);
559  if (remove(l) < 0)
560  LOG_ERROR_STR(l);
561  }
562  free(l);
563  }
564  }
565  else if (errno != ENOENT) {
566  LOG_ERROR_STR(FileName);
567  return false;
568  }
569  }
570  dsyslog("removing %s", *buffer);
571  if (remove(buffer) < 0)
572  LOG_ERROR_STR(*buffer);
573  }
574  }
575  else {
576  LOG_ERROR_STR(FileName);
577  return false;
578  }
579  }
580  dsyslog("removing %s", FileName);
581  if (remove(FileName) < 0) {
582  LOG_ERROR_STR(FileName);
583  return false;
584  }
585  }
586  else if (errno != ENOENT) {
587  LOG_ERROR_STR(FileName);
588  return false;
589  }
590  return true;
591 }
592 
593 bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis, const char *IgnoreFiles[])
594 {
595  bool HasIgnoredFiles = false;
596  cReadDir d(DirName);
597  if (d.Ok()) {
598  bool empty = true;
599  struct dirent *e;
600  while ((e = d.Next()) != NULL) {
601  if (strcmp(e->d_name, "lost+found")) {
602  cString buffer = AddDirectory(DirName, e->d_name);
603  struct stat st;
604  if (stat(buffer, &st) == 0) {
605  if (S_ISDIR(st.st_mode)) {
606  if (!RemoveEmptyDirectories(buffer, true, IgnoreFiles))
607  empty = false;
608  }
609  else if (RemoveThis && IgnoreFiles && StrInArray(IgnoreFiles, e->d_name))
610  HasIgnoredFiles = true;
611  else
612  empty = false;
613  }
614  else {
615  LOG_ERROR_STR(*buffer);
616  empty = false;
617  }
618  }
619  }
620  if (RemoveThis && empty) {
621  if (HasIgnoredFiles) {
622  while (*IgnoreFiles) {
623  cString buffer = AddDirectory(DirName, *IgnoreFiles);
624  if (access(buffer, F_OK) == 0) {
625  dsyslog("removing %s", *buffer);
626  if (remove(buffer) < 0) {
627  LOG_ERROR_STR(*buffer);
628  return false;
629  }
630  }
631  IgnoreFiles++;
632  }
633  }
634  dsyslog("removing %s", DirName);
635  if (remove(DirName) < 0) {
636  LOG_ERROR_STR(DirName);
637  return false;
638  }
639  }
640  return empty;
641  }
642  else
643  LOG_ERROR_STR(DirName);
644  return false;
645 }
646 
647 int DirSizeMB(const char *DirName)
648 {
649  cReadDir d(DirName);
650  if (d.Ok()) {
651  int size = 0;
652  struct dirent *e;
653  while (size >= 0 && (e = d.Next()) != NULL) {
654  cString buffer = AddDirectory(DirName, e->d_name);
655  struct stat st;
656  if (stat(buffer, &st) == 0) {
657  if (S_ISDIR(st.st_mode)) {
658  int n = DirSizeMB(buffer);
659  if (n >= 0)
660  size += n;
661  else
662  size = -1;
663  }
664  else
665  size += st.st_size / MEGABYTE(1);
666  }
667  else {
668  LOG_ERROR_STR(*buffer);
669  size = -1;
670  }
671  }
672  return size;
673  }
674  else if (errno != ENOENT)
675  LOG_ERROR_STR(DirName);
676  return -1;
677 }
678 
679 char *ReadLink(const char *FileName)
680 {
681  if (!FileName)
682  return NULL;
683  char *TargetName = canonicalize_file_name(FileName);
684  if (!TargetName) {
685  if (errno == ENOENT) // file doesn't exist
686  TargetName = strdup(FileName);
687  else // some other error occurred
688  LOG_ERROR_STR(FileName);
689  }
690  return TargetName;
691 }
692 
693 bool SpinUpDisk(const char *FileName)
694 {
695  for (int n = 0; n < 10; n++) {
696  cString buf;
697  if (DirectoryOk(FileName))
698  buf = cString::sprintf("%s/vdr-%06d", *FileName ? FileName : ".", n);
699  else
700  buf = cString::sprintf("%s.vdr-%06d", FileName, n);
701  if (access(buf, F_OK) != 0) { // the file does not exist
702  timeval tp1, tp2;
703  gettimeofday(&tp1, NULL);
704  int f = open(buf, O_WRONLY | O_CREAT, DEFFILEMODE);
705  // O_SYNC doesn't work on all file systems
706  if (f >= 0) {
707  if (fdatasync(f) < 0)
708  LOG_ERROR_STR(*buf);
709  close(f);
710  remove(buf);
711  gettimeofday(&tp2, NULL);
712  double seconds = (((long long)tp2.tv_sec * 1000000 + tp2.tv_usec) - ((long long)tp1.tv_sec * 1000000 + tp1.tv_usec)) / 1000000.0;
713  if (seconds > 0.5)
714  dsyslog("SpinUpDisk took %.2f seconds", seconds);
715  return true;
716  }
717  else
718  LOG_ERROR_STR(*buf);
719  }
720  }
721  esyslog("ERROR: SpinUpDisk failed");
722  return false;
723 }
724 
725 void TouchFile(const char *FileName, bool Create)
726 {
727  if (Create && access(FileName, F_OK) != 0) { // the file does not exist
728  isyslog("creating file '%s'", FileName);
729  int f = open(FileName, O_WRONLY | O_CREAT, DEFFILEMODE);
730  if (f >= 0)
731  close(f);
732  else
733  LOG_ERROR_STR(FileName);
734  }
735  if (utime(FileName, NULL) == -1 && errno != ENOENT)
736  LOG_ERROR_STR(FileName);
737 }
738 
739 time_t LastModifiedTime(const char *FileName)
740 {
741  struct stat fs;
742  if (stat(FileName, &fs) == 0)
743  return fs.st_mtime;
744  return 0;
745 }
746 
747 off_t FileSize(const char *FileName)
748 {
749  struct stat fs;
750  if (stat(FileName, &fs) == 0)
751  return fs.st_size;
752  return -1;
753 }
754 
755 // --- cTimeMs ---------------------------------------------------------------
756 
758 {
759  if (Ms >= 0)
760  Set(Ms);
761  else
762  begin = 0;
763 }
764 
765 uint64_t cTimeMs::Now(void)
766 {
767 #if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK)
768 #define MIN_RESOLUTION 5 // ms
769  static bool initialized = false;
770  static bool monotonic = false;
771  struct timespec tp;
772  if (!initialized) {
773  // check if monotonic timer is available and provides enough accurate resolution:
774  if (clock_getres(CLOCK_MONOTONIC, &tp) == 0) {
775  long Resolution = tp.tv_nsec;
776  // require a minimum resolution:
777  if (tp.tv_sec == 0 && tp.tv_nsec <= MIN_RESOLUTION * 1000000) {
778  if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
779  dsyslog("cTimeMs: using monotonic clock (resolution is %ld ns)", Resolution);
780  monotonic = true;
781  }
782  else
783  esyslog("cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
784  }
785  else
786  dsyslog("cTimeMs: not using monotonic clock - resolution is too bad (%jd s %ld ns)", intmax_t(tp.tv_sec), tp.tv_nsec);
787  }
788  else
789  esyslog("cTimeMs: clock_getres(CLOCK_MONOTONIC) failed");
790  initialized = true;
791  }
792  if (monotonic) {
793  if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
794  return (uint64_t(tp.tv_sec)) * 1000 + tp.tv_nsec / 1000000;
795  esyslog("cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
796  monotonic = false;
797  // fall back to gettimeofday()
798  }
799 #else
800 # warning Posix monotonic clock not available
801 #endif
802  struct timeval t;
803  if (gettimeofday(&t, NULL) == 0)
804  return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000;
805  return 0;
806 }
807 
808 void cTimeMs::Set(int Ms)
809 {
810  begin = Now() + Ms;
811 }
812 
813 bool cTimeMs::TimedOut(void) const
814 {
815  return Now() >= begin;
816 }
817 
818 uint64_t cTimeMs::Elapsed(void) const
819 {
820  return Now() - begin;
821 }
822 
823 // --- UTF-8 support ---------------------------------------------------------
824 
825 static uint SystemToUtf8[128] = { 0 };
826 
827 int Utf8CharLen(const char *s)
828 {
830  return 1;
831 #define MT(s, m, v) ((*(s) & (m)) == (v)) // Mask Test
832  if (MT(s, 0xE0, 0xC0) && MT(s + 1, 0xC0, 0x80))
833  return 2;
834  if (MT(s, 0xF0, 0xE0) && MT(s + 1, 0xC0, 0x80) && MT(s + 2, 0xC0, 0x80))
835  return 3;
836  if (MT(s, 0xF8, 0xF0) && MT(s + 1, 0xC0, 0x80) && MT(s + 2, 0xC0, 0x80) && MT(s + 3, 0xC0, 0x80))
837  return 4;
838  return 1;
839 }
840 
841 uint Utf8CharGet(const char *s, int Length)
842 {
844  return (uchar)*s < 128 ? *s : SystemToUtf8[(uchar)*s - 128];
845  if (!Length)
846  Length = Utf8CharLen(s);
847  switch (Length) {
848  case 2: return ((*s & 0x1F) << 6) | (*(s + 1) & 0x3F);
849  case 3: return ((*s & 0x0F) << 12) | ((*(s + 1) & 0x3F) << 6) | (*(s + 2) & 0x3F);
850  case 4: return ((*s & 0x07) << 18) | ((*(s + 1) & 0x3F) << 12) | ((*(s + 2) & 0x3F) << 6) | (*(s + 3) & 0x3F);
851  default: ;
852  }
853  return *s;
854 }
855 
856 int Utf8CharSet(uint c, char *s)
857 {
858  if (c < 0x80 || cCharSetConv::SystemCharacterTable()) {
859  if (s)
860  *s = c;
861  return 1;
862  }
863  if (c < 0x800) {
864  if (s) {
865  *s++ = ((c >> 6) & 0x1F) | 0xC0;
866  *s = (c & 0x3F) | 0x80;
867  }
868  return 2;
869  }
870  if (c < 0x10000) {
871  if (s) {
872  *s++ = ((c >> 12) & 0x0F) | 0xE0;
873  *s++ = ((c >> 6) & 0x3F) | 0x80;
874  *s = (c & 0x3F) | 0x80;
875  }
876  return 3;
877  }
878  if (c < 0x110000) {
879  if (s) {
880  *s++ = ((c >> 18) & 0x07) | 0xF0;
881  *s++ = ((c >> 12) & 0x3F) | 0x80;
882  *s++ = ((c >> 6) & 0x3F) | 0x80;
883  *s = (c & 0x3F) | 0x80;
884  }
885  return 4;
886  }
887  return 0; // can't convert to UTF-8
888 }
889 
890 int Utf8SymChars(const char *s, int Symbols)
891 {
893  return Symbols;
894  int n = 0;
895  while (*s && Symbols--) {
896  int sl = Utf8CharLen(s);
897  s += sl;
898  n += sl;
899  }
900  return n;
901 }
902 
903 int Utf8StrLen(const char *s)
904 {
906  return strlen(s);
907  int n = 0;
908  while (*s) {
909  s += Utf8CharLen(s);
910  n++;
911  }
912  return n;
913 }
914 
915 char *Utf8Strn0Cpy(char *Dest, const char *Src, int n)
916 {
918  return strn0cpy(Dest, Src, n);
919  char *d = Dest;
920  if (Dest && n > 0) {
921  if (Src) {
922  while (*Src) {
923  int sl = Utf8CharLen(Src);
924  n -= sl;
925  if (n > 0) {
926  while (sl--)
927  *d++ = *Src++;
928  }
929  else
930  break;
931  }
932  }
933  *d = 0;
934  }
935  return Dest;
936 }
937 
938 int Utf8ToArray(const char *s, uint *a, int Size)
939 {
940  int n = 0;
941  while (*s && --Size > 0) {
943  *a++ = (uchar)(*s++);
944  else {
945  int sl = Utf8CharLen(s);
946  *a++ = Utf8CharGet(s, sl);
947  s += sl;
948  }
949  n++;
950  }
951  if (Size > 0)
952  *a = 0;
953  return n;
954 }
955 
956 int Utf8FromArray(const uint *a, char *s, int Size, int Max)
957 {
958  int NumChars = 0;
959  int NumSyms = 0;
960  while (*a && NumChars < Size) {
961  if (Max >= 0 && NumSyms++ >= Max)
962  break;
964  *s++ = *a++;
965  NumChars++;
966  }
967  else {
968  int sl = Utf8CharSet(*a);
969  if (NumChars + sl <= Size) {
970  Utf8CharSet(*a, s);
971  a++;
972  s += sl;
973  NumChars += sl;
974  }
975  else
976  break;
977  }
978  }
979  if (NumChars < Size)
980  *s = 0;
981  return NumChars;
982 }
983 
984 // --- cCharSetConv ----------------------------------------------------------
985 
987 
988 cCharSetConv::cCharSetConv(const char *FromCode, const char *ToCode)
989 {
990  if (!FromCode)
991  FromCode = systemCharacterTable ? systemCharacterTable : "UTF-8";
992  if (!ToCode)
993  ToCode = "UTF-8";
994  cd = iconv_open(ToCode, FromCode);
995  result = NULL;
996  length = 0;
997 }
998 
1000 {
1001  free(result);
1002  if (cd != (iconv_t)-1)
1003  iconv_close(cd);
1004 }
1005 
1006 void cCharSetConv::SetSystemCharacterTable(const char *CharacterTable)
1007 {
1008  free(systemCharacterTable);
1009  systemCharacterTable = NULL;
1010  if (!strcasestr(CharacterTable, "UTF-8")) {
1011  // Set up a map for the character values 128...255:
1012  char buf[129];
1013  for (int i = 0; i < 128; i++)
1014  buf[i] = i + 128;
1015  buf[128] = 0;
1016  cCharSetConv csc(CharacterTable);
1017  const char *s = csc.Convert(buf);
1018  int i = 0;
1019  while (*s) {
1020  int sl = Utf8CharLen(s);
1021  SystemToUtf8[i] = Utf8CharGet(s, sl);
1022  s += sl;
1023  i++;
1024  }
1025  systemCharacterTable = strdup(CharacterTable);
1026  }
1027 }
1028 
1029 const char *cCharSetConv::Convert(const char *From, char *To, size_t ToLength)
1030 {
1031  if (cd != (iconv_t)-1 && From && *From) {
1032  char *FromPtr = (char *)From;
1033  size_t FromLength = strlen(From);
1034  char *ToPtr = To;
1035  if (!ToPtr) {
1036  int NewLength = max(length, FromLength * 2); // some reserve to avoid later reallocations
1037  if (char *NewBuffer = (char *)realloc(result, NewLength)) {
1038  length = NewLength;
1039  result = NewBuffer;
1040  }
1041  else {
1042  esyslog("ERROR: out of memory");
1043  return From;
1044  }
1045  ToPtr = result;
1046  ToLength = length;
1047  }
1048  else if (!ToLength)
1049  return From; // can't convert into a zero sized buffer
1050  ToLength--; // save space for terminating 0
1051  char *Converted = ToPtr;
1052  while (FromLength > 0) {
1053  if (iconv(cd, &FromPtr, &FromLength, &ToPtr, &ToLength) == size_t(-1)) {
1054  if (errno == E2BIG || errno == EILSEQ && ToLength < 1) {
1055  if (To)
1056  break; // caller provided a fixed size buffer, but it was too small
1057  // The result buffer is too small, so increase it:
1058  size_t d = ToPtr - result;
1059  size_t r = length / 2;
1060  int NewLength = length + r;
1061  if (char *NewBuffer = (char *)realloc(result, NewLength)) {
1062  length = NewLength;
1063  Converted = result = NewBuffer;
1064  }
1065  else {
1066  esyslog("ERROR: out of memory");
1067  return From;
1068  }
1069  ToLength += r;
1070  ToPtr = result + d;
1071  }
1072  if (errno == EILSEQ) {
1073  // A character can't be converted, so mark it with '?' and proceed:
1074  FromPtr++;
1075  FromLength--;
1076  *ToPtr++ = '?';
1077  ToLength--;
1078  }
1079  else if (errno != E2BIG)
1080  return From; // unknown error, return original string
1081  }
1082  }
1083  *ToPtr = 0;
1084  return Converted;
1085  }
1086  return From;
1087 }
1088 
1089 // --- cString ---------------------------------------------------------------
1090 
1091 cString::cString(const char *S, bool TakePointer)
1092 {
1093  s = TakePointer ? (char *)S : S ? strdup(S) : NULL;
1094 }
1095 
1096 cString::cString(const char *S, const char *To)
1097 {
1098  if (!S)
1099  s = NULL;
1100  else if (!To)
1101  s = strdup(S);
1102  else {
1103  int l = To - S;
1104  s = MALLOC(char, l + 1);
1105  strncpy(s, S, l);
1106  s[l] = 0;
1107  }
1108 }
1109 
1111 {
1112  s = String.s ? strdup(String.s) : NULL;
1113 }
1114 
1116 {
1117  free(s);
1118 }
1119 
1121 {
1122  if (this == &String)
1123  return *this;
1124  free(s);
1125  s = String.s ? strdup(String.s) : NULL;
1126  return *this;
1127 }
1128 
1130 {
1131  if (this != &String) {
1132  free(s);
1133  s = String.s;
1134  String.s = NULL;
1135  }
1136  return *this;
1137 }
1138 
1139 cString &cString::operator=(const char *String)
1140 {
1141  if (s == String)
1142  return *this;
1143  free(s);
1144  s = String ? strdup(String) : NULL;
1145  return *this;
1146 }
1147 
1148 cString &cString::Append(const char *String)
1149 {
1150  if (String) {
1151  int l1 = s ? strlen(s) : 0;
1152  int l2 = strlen(String);
1153  if (char *p = (char *)realloc(s, l1 + l2 + 1)) {
1154  s = p;
1155  strcpy(s + l1, String);
1156  }
1157  else
1158  esyslog("ERROR: out of memory");
1159  }
1160  return *this;
1161 }
1162 
1164 {
1165  if (c) {
1166  int l1 = s ? strlen(s) : 0;
1167  int l2 = 1;
1168  if (char *p = (char *)realloc(s, l1 + l2 + 1)) {
1169  s = p;
1170  *(s + l1) = c;
1171  *(s + l1 + 1) = 0;
1172  }
1173  else
1174  esyslog("ERROR: out of memory");
1175  }
1176  return *this;
1177 }
1178 
1180 {
1181  int l = strlen(s);
1182  if (Index < 0)
1183  Index = l + Index;
1184  if (Index >= 0 && Index < l)
1185  s[Index] = 0;
1186  return *this;
1187 }
1188 
1190 {
1191  compactchars(s, c);
1192  return *this;
1193 }
1194 
1195 cString cString::sprintf(const char *fmt, ...)
1196 {
1197  va_list ap;
1198  va_start(ap, fmt);
1199  char *buffer;
1200  if (!fmt || vasprintf(&buffer, fmt, ap) < 0) {
1201  esyslog("error in vasprintf('%s', ...)", fmt);
1202  buffer = strdup("???");
1203  }
1204  va_end(ap);
1205  return cString(buffer, true);
1206 }
1207 
1208 cString cString::vsprintf(const char *fmt, va_list &ap)
1209 {
1210  char *buffer;
1211  if (!fmt || vasprintf(&buffer, fmt, ap) < 0) {
1212  esyslog("error in vasprintf('%s', ...)", fmt);
1213  buffer = strdup("???");
1214  }
1215  return cString(buffer, true);
1216 }
1217 
1218 cString WeekDayName(int WeekDay)
1219 {
1220  char buffer[16];
1221  WeekDay = WeekDay == 0 ? 6 : WeekDay - 1; // we start with Monday==0!
1222  if (0 <= WeekDay && WeekDay <= 6) {
1223  // TRANSLATORS: abbreviated weekdays, beginning with monday (must all be 3 letters!)
1224  const char *day = tr("MonTueWedThuFriSatSun");
1225  day += Utf8SymChars(day, WeekDay * 3);
1226  strn0cpy(buffer, day, min(Utf8SymChars(day, 3) + 1, int(sizeof(buffer))));
1227  return buffer;
1228  }
1229  else
1230  return "???";
1231 }
1232 
1234 {
1235  struct tm tm_r;
1236  return WeekDayName(localtime_r(&t, &tm_r)->tm_wday);
1237 }
1238 
1240 {
1241  WeekDay = WeekDay == 0 ? 6 : WeekDay - 1; // we start with Monday==0!
1242  switch (WeekDay) {
1243  case 0: return tr("Monday");
1244  case 1: return tr("Tuesday");
1245  case 2: return tr("Wednesday");
1246  case 3: return tr("Thursday");
1247  case 4: return tr("Friday");
1248  case 5: return tr("Saturday");
1249  case 6: return tr("Sunday");
1250  default: return "???";
1251  }
1252 }
1253 
1255 {
1256  struct tm tm_r;
1257  return WeekDayNameFull(localtime_r(&t, &tm_r)->tm_wday);
1258 }
1259 
1261 {
1262  char buffer[32];
1263  if (t == 0)
1264  time(&t);
1265  struct tm tm_r;
1266  tm *tm = localtime_r(&t, &tm_r);
1267  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);
1268  return buffer;
1269 }
1270 
1272 {
1273  char buffer[32];
1274  if (ctime_r(&t, buffer)) {
1275  buffer[strlen(buffer) - 1] = 0; // strip trailing newline
1276  return buffer;
1277  }
1278  return "???";
1279 }
1280 
1282 {
1283  char buf[32];
1284  struct tm tm_r;
1285  tm *tm = localtime_r(&t, &tm_r);
1286  char *p = stpcpy(buf, WeekDayName(tm->tm_wday));
1287  *p++ = ' ';
1288  strftime(p, sizeof(buf) - (p - buf), "%d.%m.%Y", tm);
1289  return buf;
1290 }
1291 
1293 {
1294  char buf[32];
1295  struct tm tm_r;
1296  tm *tm = localtime_r(&t, &tm_r);
1297  strftime(buf, sizeof(buf), "%d.%m.%y", tm);
1298  return buf;
1299 }
1300 
1302 {
1303  char buf[25];
1304  struct tm tm_r;
1305  strftime(buf, sizeof(buf), "%R", localtime_r(&t, &tm_r));
1306  return buf;
1307 }
1308 
1309 // --- RgbToJpeg -------------------------------------------------------------
1310 
1311 #define JPEGCOMPRESSMEM 500000
1312 
1313 struct tJpegCompressData {
1314  int size;
1315  uchar *mem;
1316  };
1317 
1318 static void JpegCompressInitDestination(j_compress_ptr cinfo)
1319 {
1320  tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data;
1321  if (jcd) {
1322  cinfo->dest->free_in_buffer = jcd->size = JPEGCOMPRESSMEM;
1323  cinfo->dest->next_output_byte = jcd->mem = MALLOC(uchar, jcd->size);
1324  }
1325 }
1326 
1327 static boolean JpegCompressEmptyOutputBuffer(j_compress_ptr cinfo)
1328 {
1329  tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data;
1330  if (jcd) {
1331  int Used = jcd->size;
1332  int NewSize = jcd->size + JPEGCOMPRESSMEM;
1333  if (uchar *NewBuffer = (uchar *)realloc(jcd->mem, NewSize)) {
1334  jcd->size = NewSize;
1335  jcd->mem = NewBuffer;
1336  }
1337  else {
1338  esyslog("ERROR: out of memory");
1339  return FALSE;
1340  }
1341  if (jcd->mem) {
1342  cinfo->dest->next_output_byte = jcd->mem + Used;
1343  cinfo->dest->free_in_buffer = jcd->size - Used;
1344  return TRUE;
1345  }
1346  }
1347  return FALSE;
1348 }
1349 
1350 static void JpegCompressTermDestination(j_compress_ptr cinfo)
1351 {
1352  tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data;
1353  if (jcd) {
1354  int Used = cinfo->dest->next_output_byte - jcd->mem;
1355  if (Used < jcd->size) {
1356  if (uchar *NewBuffer = (uchar *)realloc(jcd->mem, Used)) {
1357  jcd->size = Used;
1358  jcd->mem = NewBuffer;
1359  }
1360  else
1361  esyslog("ERROR: out of memory");
1362  }
1363  }
1364 }
1365 
1366 uchar *RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality)
1367 {
1368  if (Quality < 0)
1369  Quality = 0;
1370  else if (Quality > 100)
1371  Quality = 100;
1372 
1373  jpeg_destination_mgr jdm;
1374 
1375  jdm.init_destination = JpegCompressInitDestination;
1376  jdm.empty_output_buffer = JpegCompressEmptyOutputBuffer;
1377  jdm.term_destination = JpegCompressTermDestination;
1378 
1379  struct jpeg_compress_struct cinfo;
1380  struct jpeg_error_mgr jerr;
1381  cinfo.err = jpeg_std_error(&jerr);
1382  jpeg_create_compress(&cinfo);
1383  cinfo.dest = &jdm;
1384  tJpegCompressData jcd;
1385  cinfo.client_data = &jcd;
1386  cinfo.image_width = Width;
1387  cinfo.image_height = Height;
1388  cinfo.input_components = 3;
1389  cinfo.in_color_space = JCS_RGB;
1390 
1391  jpeg_set_defaults(&cinfo);
1392  jpeg_set_quality(&cinfo, Quality, TRUE);
1393  jpeg_start_compress(&cinfo, TRUE);
1394 
1395  int rs = Width * 3;
1396  JSAMPROW rp[Height];
1397  for (int k = 0; k < Height; k++)
1398  rp[k] = &Mem[rs * k];
1399  jpeg_write_scanlines(&cinfo, rp, Height);
1400  jpeg_finish_compress(&cinfo);
1401  jpeg_destroy_compress(&cinfo);
1402 
1403  Size = jcd.size;
1404  return jcd.mem;
1405 }
1406 
1407 // --- GetHostName -----------------------------------------------------------
1408 
1409 const char *GetHostName(void)
1410 {
1411  static char buffer[HOST_NAME_MAX] = "";
1412  if (!*buffer) {
1413  if (gethostname(buffer, sizeof(buffer)) < 0) {
1414  LOG_ERROR;
1415  strcpy(buffer, "vdr");
1416  }
1417  }
1418  return buffer;
1419 }
1420 
1421 // --- cBase64Encoder --------------------------------------------------------
1422 
1423 const char *cBase64Encoder::b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1424 
1425 cBase64Encoder::cBase64Encoder(const uchar *Data, int Length, int MaxResult)
1426 {
1427  data = Data;
1428  length = Length;
1429  maxResult = MaxResult;
1430  i = 0;
1431  result = MALLOC(char, maxResult + 1);
1432 }
1433 
1435 {
1436  free(result);
1437 }
1438 
1439 const char *cBase64Encoder::NextLine(void)
1440 {
1441  int r = 0;
1442  while (i < length && r < maxResult - 3) {
1443  result[r++] = b64[(data[i] >> 2) & 0x3F];
1444  uchar c = (data[i] << 4) & 0x3F;
1445  if (++i < length)
1446  c |= (data[i] >> 4) & 0x0F;
1447  result[r++] = b64[c];
1448  if (i < length) {
1449  c = (data[i] << 2) & 0x3F;
1450  if (++i < length)
1451  c |= (data[i] >> 6) & 0x03;
1452  result[r++] = b64[c];
1453  }
1454  else {
1455  i++;
1456  result[r++] = '=';
1457  }
1458  if (i < length) {
1459  c = data[i] & 0x3F;
1460  result[r++] = b64[c];
1461  }
1462  else
1463  result[r++] = '=';
1464  i++;
1465  }
1466  if (r > 0) {
1467  result[r] = 0;
1468  return result;
1469  }
1470  return NULL;
1471 }
1472 
1473 // --- cBitStream ------------------------------------------------------------
1474 
1476 {
1477  if (index >= length)
1478  return 1;
1479  int r = (data[index >> 3] >> (7 - (index & 7))) & 1;
1480  ++index;
1481  return r;
1482 }
1483 
1484 uint32_t cBitStream::GetBits(int n)
1485 {
1486  uint32_t r = 0;
1487  while (n--)
1488  r |= GetBit() << n;
1489  return r;
1490 }
1491 
1493 {
1494  int n = index % 8;
1495  if (n > 0)
1496  SkipBits(8 - n);
1497 }
1498 
1500 {
1501  int n = index % 16;
1502  if (n > 0)
1503  SkipBits(16 - n);
1504 }
1505 
1506 bool cBitStream::SetLength(int Length)
1507 {
1508  if (Length > length)
1509  return false;
1510  length = Length;
1511  return true;
1512 }
1513 
1514 // --- cReadLine -------------------------------------------------------------
1515 
1517 {
1518  size = 0;
1519  buffer = NULL;
1520 }
1521 
1523 {
1524  free(buffer);
1525 }
1526 
1527 char *cReadLine::Read(FILE *f)
1528 {
1529  int n = getline(&buffer, &size, f);
1530  if (n > 0) {
1531  n--;
1532  if (buffer[n] == '\n') {
1533  buffer[n] = 0;
1534  if (n > 0) {
1535  n--;
1536  if (buffer[n] == '\r')
1537  buffer[n] = 0;
1538  }
1539  }
1540  return buffer;
1541  }
1542  return NULL;
1543 }
1544 
1545 // --- cPoller ---------------------------------------------------------------
1546 
1547 cPoller::cPoller(int FileHandle, bool Out)
1548 {
1549  numFileHandles = 0;
1550  Add(FileHandle, Out);
1551 }
1552 
1553 bool cPoller::Add(int FileHandle, bool Out)
1554 {
1555  if (FileHandle >= 0) {
1556  for (int i = 0; i < numFileHandles; i++) {
1557  if (pfd[i].fd == FileHandle && pfd[i].events == (Out ? POLLOUT : POLLIN))
1558  return true;
1559  }
1560  if (numFileHandles < MaxPollFiles) {
1561  pfd[numFileHandles].fd = FileHandle;
1562  pfd[numFileHandles].events = Out ? POLLOUT : POLLIN;
1563  pfd[numFileHandles].revents = 0;
1564  numFileHandles++;
1565  return true;
1566  }
1567  esyslog("ERROR: too many file handles in cPoller");
1568  }
1569  return false;
1570 }
1571 
1572 void cPoller::Del(int FileHandle, bool Out)
1573 {
1574  if (FileHandle >= 0) {
1575  for (int i = 0; i < numFileHandles; i++) {
1576  if (pfd[i].fd == FileHandle && pfd[i].events == (Out ? POLLOUT : POLLIN)) {
1577  if (i < numFileHandles - 1)
1578  memmove(&pfd[i], &pfd[i + 1], (numFileHandles - i - 1) * sizeof(pollfd));
1579  numFileHandles--;
1580  }
1581  }
1582  }
1583 }
1584 
1585 bool cPoller::Poll(int TimeoutMs)
1586 {
1587  if (numFileHandles) {
1588  if (TimeoutMs == 0)
1589  TimeoutMs = 1; // can't let it be 0, otherwise poll() returns immediately, even if no file descriptors are ready
1590  if (poll(pfd, numFileHandles, TimeoutMs) != 0)
1591  return true; // returns true even in case of an error, to let the caller
1592  // access the file and thus see the error code
1593  }
1594  return false;
1595 }
1596 
1597 // --- cReadDir --------------------------------------------------------------
1598 
1599 cReadDir::cReadDir(const char *Directory)
1600 {
1601  directory = opendir(Directory);
1602 }
1603 
1605 {
1606  if (directory)
1607  closedir(directory);
1608 }
1609 
1610 struct dirent *cReadDir::Next(void)
1611 {
1612  if (directory) {
1613 #if !__GLIBC_PREREQ(2, 24) // readdir_r() is deprecated as of GLIBC 2.24
1614  while (readdir_r(directory, &u.d, &result) == 0 && result) {
1615 #else
1616  while ((result = readdir(directory)) != NULL) {
1617 #endif
1618  if (strcmp(result->d_name, ".") && strcmp(result->d_name, ".."))
1619  return result;
1620  }
1621  }
1622  return NULL;
1623 }
1624 
1625 // --- cStringList -----------------------------------------------------------
1626 
1628 {
1629  Clear();
1630 }
1631 
1632 int cStringList::Find(const char *s) const
1633 {
1634  for (int i = 0; i < Size(); i++) {
1635  if (!strcmp(s, At(i)))
1636  return i;
1637  }
1638  return -1;
1639 }
1640 
1642 {
1643  for (int i = 0; i < Size(); i++)
1644  free(At(i));
1646 }
1647 
1648 // --- cFileNameList ---------------------------------------------------------
1649 
1650 // TODO better GetFileNames(const char *Directory, cStringList *List)?
1651 cFileNameList::cFileNameList(const char *Directory, bool DirsOnly)
1652 {
1653  Load(Directory, DirsOnly);
1654 }
1655 
1656 bool cFileNameList::Load(const char *Directory, bool DirsOnly)
1657 {
1658  Clear();
1659  if (Directory) {
1660  cReadDir d(Directory);
1661  struct dirent *e;
1662  if (d.Ok()) {
1663  while ((e = d.Next()) != NULL) {
1664  if (DirsOnly) {
1665  struct stat ds;
1666  if (stat(AddDirectory(Directory, e->d_name), &ds) == 0) {
1667  if (!S_ISDIR(ds.st_mode))
1668  continue;
1669  }
1670  }
1671  Append(strdup(e->d_name));
1672  }
1673  Sort();
1674  return true;
1675  }
1676  else
1677  LOG_ERROR_STR(Directory);
1678  }
1679  return false;
1680 }
1681 
1682 // --- cFile -----------------------------------------------------------------
1683 
1685 {
1686  f = -1;
1687 }
1688 
1690 {
1691  Close();
1692 }
1693 
1694 bool cFile::Open(const char *FileName, int Flags, mode_t Mode)
1695 {
1696  if (!IsOpen())
1697  return Open(open(FileName, Flags, Mode));
1698  esyslog("ERROR: attempt to re-open %s", FileName);
1699  return false;
1700 }
1701 
1702 bool cFile::Open(int FileDes)
1703 {
1704  if (FileDes >= 0) {
1705  if (!IsOpen())
1706  f = FileDes;
1707  else
1708  esyslog("ERROR: attempt to re-open file descriptor %d", FileDes);
1709  }
1710  return IsOpen();
1711 }
1712 
1713 void cFile::Close(void)
1714 {
1715  if (f >= 0) {
1716  close(f);
1717  f = -1;
1718  }
1719 }
1720 
1721 bool cFile::Ready(bool Wait)
1722 {
1723  return f >= 0 && FileReady(f, Wait ? 1000 : 0);
1724 }
1725 
1726 bool cFile::FileReady(int FileDes, int TimeoutMs)
1727 {
1728  fd_set set;
1729  struct timeval timeout;
1730  FD_ZERO(&set);
1731  FD_SET(FileDes, &set);
1732  if (TimeoutMs >= 0) {
1733  if (TimeoutMs < 100)
1734  TimeoutMs = 100;
1735  timeout.tv_sec = TimeoutMs / 1000;
1736  timeout.tv_usec = (TimeoutMs % 1000) * 1000;
1737  }
1738  return select(FD_SETSIZE, &set, NULL, NULL, (TimeoutMs >= 0) ? &timeout : NULL) > 0 && FD_ISSET(FileDes, &set);
1739 }
1740 
1741 // --- cSafeFile -------------------------------------------------------------
1742 
1743 cSafeFile::cSafeFile(const char *FileName)
1744 {
1745  f = NULL;
1746  fileName = ReadLink(FileName);
1747  tempName = fileName ? MALLOC(char, strlen(fileName) + 5) : NULL;
1748  if (tempName)
1749  strcat(strcpy(tempName, fileName), ".$$$");
1750 }
1751 
1753 {
1754  if (f)
1755  fclose(f);
1756  unlink(tempName);
1757  free(fileName);
1758  free(tempName);
1759 }
1760 
1762 {
1763  if (!f && fileName && tempName) {
1764  f = fopen(tempName, "w");
1765  if (!f)
1766  LOG_ERROR_STR(tempName);
1767  }
1768  return f != NULL;
1769 }
1770 
1772 {
1773  bool result = true;
1774  if (f) {
1775  if (ferror(f) != 0) {
1776  LOG_ERROR_STR(tempName);
1777  result = false;
1778  }
1779  fflush(f);
1780  fsync(fileno(f));
1781  if (fclose(f) < 0) {
1782  LOG_ERROR_STR(tempName);
1783  result = false;
1784  }
1785  f = NULL;
1786  if (result && rename(tempName, fileName) < 0) {
1787  LOG_ERROR_STR(fileName);
1788  result = false;
1789  }
1790  }
1791  else
1792  result = false;
1793  return result;
1794 }
1795 
1796 // --- cUnbufferedFile -------------------------------------------------------
1797 
1798 #ifndef USE_FADVISE_READ
1799 #define USE_FADVISE_READ 0
1800 #endif
1801 #ifndef USE_FADVISE_WRITE
1802 #define USE_FADVISE_WRITE 1
1803 #endif
1804 
1805 #define WRITE_BUFFER KILOBYTE(800)
1806 
1808 {
1809  fd = -1;
1810 }
1811 
1813 {
1814  Close();
1815 }
1816 
1817 int cUnbufferedFile::Open(const char *FileName, int Flags, mode_t Mode)
1818 {
1819  Close();
1820  fd = open(FileName, Flags, Mode);
1821  curpos = 0;
1822 #if USE_FADVISE_READ || USE_FADVISE_WRITE
1823  begin = lastpos = ahead = 0;
1824  cachedstart = 0;
1825  cachedend = 0;
1826  readahead = KILOBYTE(128);
1827  written = 0;
1828  totwritten = 0;
1829  if (fd >= 0)
1830  posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM); // we could use POSIX_FADV_SEQUENTIAL, but we do our own readahead, disabling the kernel one.
1831 #endif
1832  return fd;
1833 }
1834 
1836 {
1837  if (fd >= 0) {
1838 #if USE_FADVISE_READ || USE_FADVISE_WRITE
1839  if (totwritten) // if we wrote anything make sure the data has hit the disk before
1840  fdatasync(fd); // calling fadvise, as this is our last chance to un-cache it.
1841  posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
1842 #endif
1843  int OldFd = fd;
1844  fd = -1;
1845  return close(OldFd);
1846  }
1847  errno = EBADF;
1848  return -1;
1849 }
1850 
1851 // When replaying and going e.g. FF->PLAY the position jumps back 2..8M
1852 // hence we do not want to drop recently accessed data at once.
1853 // We try to handle the common cases such as PLAY->FF->PLAY, small
1854 // jumps, moving editing marks etc.
1855 
1856 #define FADVGRAN KILOBYTE(4) // AKA fadvise-chunk-size; PAGE_SIZE or getpagesize(2) would also work.
1857 #define READCHUNK MEGABYTE(8)
1858 
1860 {
1861  readahead = ra;
1862 }
1863 
1864 int cUnbufferedFile::FadviseDrop(off_t Offset, off_t Len)
1865 {
1866  // rounding up the window to make sure that not PAGE_SIZE-aligned data gets freed.
1867  return posix_fadvise(fd, Offset - (FADVGRAN - 1), Len + (FADVGRAN - 1) * 2, POSIX_FADV_DONTNEED);
1868 }
1869 
1870 off_t cUnbufferedFile::Seek(off_t Offset, int Whence)
1871 {
1872  if (Whence == SEEK_SET && Offset == curpos)
1873  return curpos;
1874  curpos = lseek(fd, Offset, Whence);
1875  return curpos;
1876 }
1877 
1878 ssize_t cUnbufferedFile::Read(void *Data, size_t Size)
1879 {
1880  if (fd >= 0) {
1881 #if USE_FADVISE_READ
1882  off_t jumped = curpos-lastpos; // nonzero means we're not at the last offset
1883  if ((cachedstart < cachedend) && (curpos < cachedstart || curpos > cachedend)) {
1884  // current position is outside the cached window -- invalidate it.
1885  FadviseDrop(cachedstart, cachedend-cachedstart);
1886  cachedstart = curpos;
1887  cachedend = curpos;
1888  }
1889  cachedstart = min(cachedstart, curpos);
1890 #endif
1891  ssize_t bytesRead = safe_read(fd, Data, Size);
1892  if (bytesRead > 0) {
1893  curpos += bytesRead;
1894 #if USE_FADVISE_READ
1895  cachedend = max(cachedend, curpos);
1896 
1897  // Read ahead:
1898  // no jump? (allow small forward jump still inside readahead window).
1899  if (jumped >= 0 && jumped <= (off_t)readahead) {
1900  // Trigger the readahead IO, but only if we've used at least
1901  // 1/2 of the previously requested area. This avoids calling
1902  // fadvise() after every read() call.
1903  if (ahead - curpos < (off_t)(readahead / 2)) {
1904  posix_fadvise(fd, curpos, readahead, POSIX_FADV_WILLNEED);
1905  ahead = curpos + readahead;
1906  cachedend = max(cachedend, ahead);
1907  }
1908  if (readahead < Size * 32) { // automagically tune readahead size.
1909  readahead = Size * 32;
1910  }
1911  }
1912  else
1913  ahead = curpos; // jumped -> we really don't want any readahead, otherwise e.g. fast-rewind gets in trouble.
1914 #endif
1915  }
1916 #if USE_FADVISE_READ
1917  if (cachedstart < cachedend) {
1918  if (curpos - cachedstart > READCHUNK * 2) {
1919  // current position has moved forward enough, shrink tail window.
1920  FadviseDrop(cachedstart, curpos - READCHUNK - cachedstart);
1921  cachedstart = curpos - READCHUNK;
1922  }
1923  else if (cachedend > ahead && cachedend - curpos > READCHUNK * 2) {
1924  // current position has moved back enough, shrink head window.
1925  FadviseDrop(curpos + READCHUNK, cachedend - (curpos + READCHUNK));
1926  cachedend = curpos + READCHUNK;
1927  }
1928  }
1929  lastpos = curpos;
1930 #endif
1931  return bytesRead;
1932  }
1933  return -1;
1934 }
1935 
1936 ssize_t cUnbufferedFile::Write(const void *Data, size_t Size)
1937 {
1938  if (fd >=0) {
1939  ssize_t bytesWritten = safe_write(fd, Data, Size);
1940 #if USE_FADVISE_WRITE
1941  if (bytesWritten > 0) {
1942  begin = min(begin, curpos);
1943  curpos += bytesWritten;
1944  written += bytesWritten;
1945  lastpos = max(lastpos, curpos);
1946  if (written > WRITE_BUFFER) {
1947  if (lastpos > begin) {
1948  // Now do three things:
1949  // 1) Start writeback of begin..lastpos range
1950  // 2) Drop the already written range (by the previous fadvise call)
1951  // 3) Handle nonpagealigned data.
1952  // This is why we double the WRITE_BUFFER; the first time around the
1953  // last (partial) page might be skipped, writeback will start only after
1954  // second call; the third call will still include this page and finally
1955  // drop it from cache.
1956  off_t headdrop = min(begin, off_t(WRITE_BUFFER * 2));
1957  posix_fadvise(fd, begin - headdrop, lastpos - begin + headdrop, POSIX_FADV_DONTNEED);
1958  }
1959  begin = lastpos = curpos;
1960  totwritten += written;
1961  written = 0;
1962  // The above fadvise() works when writing slowly (recording), but could
1963  // leave cached data around when writing at a high rate, e.g. when cutting,
1964  // because by the time we try to flush the cached pages (above) the data
1965  // can still be dirty - we are faster than the disk I/O.
1966  // So we do another round of flushing, just like above, but at larger
1967  // intervals -- this should catch any pages that couldn't be released
1968  // earlier.
1969  if (totwritten > MEGABYTE(32)) {
1970  // It seems in some setups, fadvise() does not trigger any I/O and
1971  // a fdatasync() call would be required do all the work (reiserfs with some
1972  // kind of write gathering enabled), but the syncs cause (io) load..
1973  // Uncomment the next line if you think you need them.
1974  //fdatasync(fd);
1975  off_t headdrop = min(off_t(curpos - totwritten), off_t(totwritten * 2));
1976  posix_fadvise(fd, curpos - totwritten - headdrop, totwritten + headdrop, POSIX_FADV_DONTNEED);
1977  totwritten = 0;
1978  }
1979  }
1980  }
1981 #endif
1982  return bytesWritten;
1983  }
1984  return -1;
1985 }
1986 
1987 cUnbufferedFile *cUnbufferedFile::Create(const char *FileName, int Flags, mode_t Mode)
1988 {
1989  cUnbufferedFile *File = new cUnbufferedFile;
1990  if (File->Open(FileName, Flags, Mode) < 0) {
1991  delete File;
1992  File = NULL;
1993  }
1994  return File;
1995 }
1996 
1997 // --- cLockFile -------------------------------------------------------------
1998 
1999 #define LOCKFILENAME ".lock-vdr"
2000 #define LOCKFILESTALETIME 600 // seconds before considering a lock file "stale"
2001 
2002 cLockFile::cLockFile(const char *Directory)
2003 {
2004  fileName = NULL;
2005  f = -1;
2006  if (DirectoryOk(Directory))
2007  fileName = strdup(AddDirectory(Directory, LOCKFILENAME));
2008 }
2009 
2011 {
2012  Unlock();
2013  free(fileName);
2014 }
2015 
2016 bool cLockFile::Lock(int WaitSeconds)
2017 {
2018  if (f < 0 && fileName) {
2019  time_t Timeout = time(NULL) + WaitSeconds;
2020  do {
2021  f = open(fileName, O_WRONLY | O_CREAT | O_EXCL, DEFFILEMODE);
2022  if (f < 0) {
2023  if (errno == EEXIST) {
2024  struct stat fs;
2025  if (stat(fileName, &fs) == 0) {
2026  if (abs(time(NULL) - fs.st_mtime) > LOCKFILESTALETIME) {
2027  esyslog("ERROR: removing stale lock file '%s'", fileName);
2028  if (remove(fileName) < 0) {
2029  LOG_ERROR_STR(fileName);
2030  break;
2031  }
2032  continue;
2033  }
2034  }
2035  else if (errno != ENOENT) {
2036  LOG_ERROR_STR(fileName);
2037  break;
2038  }
2039  }
2040  else {
2041  LOG_ERROR_STR(fileName);
2042  if (errno == ENOSPC) {
2043  esyslog("ERROR: can't create lock file '%s' - assuming lock anyway!", fileName);
2044  return true;
2045  }
2046  break;
2047  }
2048  if (WaitSeconds)
2049  cCondWait::SleepMs(1000);
2050  }
2051  } while (f < 0 && time(NULL) < Timeout);
2052  }
2053  return f >= 0;
2054 }
2055 
2057 {
2058  if (f >= 0) {
2059  close(f);
2060  remove(fileName);
2061  f = -1;
2062  }
2063 }
2064 
2065 // --- cListObject -----------------------------------------------------------
2066 
2068 {
2069  prev = next = NULL;
2070 }
2071 
2073 {
2074 }
2075 
2077 {
2078  next = Object;
2079  Object->prev = this;
2080 }
2081 
2083 {
2084  prev = Object;
2085  Object->next = this;
2086 }
2087 
2089 {
2090  if (next)
2091  next->prev = prev;
2092  if (prev)
2093  prev->next = next;
2094  next = prev = NULL;
2095 }
2096 
2097 int cListObject::Index(void) const
2098 {
2099  cListObject *p = prev;
2100  int i = 0;
2101 
2102  while (p) {
2103  i++;
2104  p = p->prev;
2105  }
2106  return i;
2107 }
2108 
2109 // --- cListGarbageCollector -------------------------------------------------
2110 
2111 #define LIST_GARBAGE_COLLECTOR_TIMEOUT 5 // seconds
2112 
2114 
2116 {
2117  objects = NULL;
2118  lastPut = 0;
2119 }
2120 
2122 {
2123  if (objects)
2124  esyslog("ERROR: ListGarbageCollector destroyed without prior Purge()!");
2125 }
2126 
2128 {
2129  mutex.Lock();
2130  Object->next = objects;
2131  objects = Object;
2132  lastPut = time(NULL);
2133  mutex.Unlock();
2134 }
2135 
2137 {
2138  mutex.Lock();
2139  if (objects && (time(NULL) - lastPut > LIST_GARBAGE_COLLECTOR_TIMEOUT || Force)) {
2140  // We make sure that any object stays in the garbage collector for at least
2141  // LIST_GARBAGE_COLLECTOR_TIMEOUT seconds, to give objects that have pointers
2142  // to them a chance to drop these references before the object is finally
2143  // deleted.
2144  while (cListObject *Object = objects) {
2145  objects = Object->next;
2146  delete Object;
2147  }
2148  }
2149  mutex.Unlock();
2150 }
2151 
2152 // --- cListBase -------------------------------------------------------------
2153 
2154 cListBase::cListBase(const char *NeedsLocking)
2155 :stateLock(NeedsLocking)
2156 {
2157  objects = lastObject = NULL;
2158  count = 0;
2159  needsLocking = NeedsLocking;
2161 }
2162 
2164 {
2165  Clear();
2166 }
2167 
2168 bool cListBase::Lock(cStateKey &StateKey, bool Write, int TimeoutMs) const
2169 {
2170  if (needsLocking)
2171  return stateLock.Lock(StateKey, Write, TimeoutMs);
2172  else
2173  esyslog("ERROR: cListBase::Lock() called for a list that doesn't require locking");
2174  return false;
2175 }
2176 
2178 {
2179  if (After && After != lastObject) {
2180  After->Next()->Insert(Object);
2181  After->Append(Object);
2182  }
2183  else {
2184  if (lastObject)
2185  lastObject->Append(Object);
2186  else
2187  objects = Object;
2188  lastObject = Object;
2189  }
2190  count++;
2191 }
2192 
2194 {
2195  if (Before && Before != objects) {
2196  Before->Prev()->Append(Object);
2197  Before->Insert(Object);
2198  }
2199  else {
2200  if (objects)
2201  objects->Insert(Object);
2202  else
2203  lastObject = Object;
2204  objects = Object;
2205  }
2206  count++;
2207 }
2208 
2209 void cListBase::Del(cListObject *Object, bool DeleteObject)
2210 {
2211  if (Object == objects)
2212  objects = Object->Next();
2213  if (Object == lastObject)
2214  lastObject = Object->Prev();
2215  Object->Unlink();
2216  if (DeleteObject) {
2217  if (useGarbageCollector)
2218  ListGarbageCollector.Put(Object);
2219  else
2220  delete Object;
2221  }
2222  count--;
2223 }
2224 
2225 void cListBase::Move(int From, int To)
2226 {
2227  Move(Get(From), Get(To));
2228 }
2229 
2231 {
2232  if (From && To && From != To) {
2233  if (From->Index() < To->Index())
2234  To = To->Next();
2235  if (From == objects)
2236  objects = From->Next();
2237  if (From == lastObject)
2238  lastObject = From->Prev();
2239  From->Unlink();
2240  if (To) {
2241  if (To->Prev())
2242  To->Prev()->Append(From);
2243  From->Append(To);
2244  }
2245  else {
2246  lastObject->Append(From);
2247  lastObject = From;
2248  }
2249  if (!From->Prev())
2250  objects = From;
2251  }
2252 }
2253 
2255 {
2256  while (objects) {
2257  cListObject *object = objects->Next();
2258  delete objects;
2259  objects = object;
2260  }
2261  objects = lastObject = NULL;
2262  count = 0;
2263 }
2264 
2265 bool cListBase::Contains(const cListObject *Object) const
2266 {
2267  for (const cListObject *o = objects; o; o = o->Next()) {
2268  if (o == Object)
2269  return true;
2270  }
2271  return false;
2272 }
2273 
2275 {
2277 }
2278 
2280 {
2282 }
2283 
2284 const cListObject *cListBase::Get(int Index) const
2285 {
2286  if (Index < 0)
2287  return NULL;
2288  const cListObject *object = objects;
2289  while (object && Index-- > 0)
2290  object = object->Next();
2291  return object;
2292 }
2293 
2294 static int CompareListObjects(const void *a, const void *b)
2295 {
2296  const cListObject *la = *(const cListObject **)a;
2297  const cListObject *lb = *(const cListObject **)b;
2298  return la->Compare(*lb);
2299 }
2300 
2302 {
2303  int n = Count();
2304  cListObject **a = MALLOC(cListObject *, n);
2305  if (a == NULL)
2306  return;
2307  cListObject *object = objects;
2308  int i = 0;
2309  while (object && i < n) {
2310  a[i++] = object;
2311  object = object->Next();
2312  }
2313  qsort(a, n, sizeof(cListObject *), CompareListObjects);
2314  objects = lastObject = NULL;
2315  for (i = 0; i < n; i++) {
2316  a[i]->Unlink();
2317  count--;
2318  Add(a[i]);
2319  }
2320  free(a);
2321 }
2322 
2323 // --- cDynamicBuffer --------------------------------------------------------
2324 
2326 {
2327  initialSize = InitialSize;
2328  buffer = NULL;
2329  size = used = 0;
2330 }
2331 
2333 {
2334  free(buffer);
2335 }
2336 
2337 bool cDynamicBuffer::Realloc(int NewSize)
2338 {
2339  if (size < NewSize) {
2340  NewSize = max(NewSize, size ? size * 3 / 2 : initialSize); // increase size by at least 50%
2341  if (uchar *NewBuffer = (uchar *)realloc(buffer, NewSize)) {
2342  buffer = NewBuffer;
2343  size = NewSize;
2344  }
2345  else {
2346  esyslog("ERROR: out of memory");
2347  return false;
2348  }
2349  }
2350  return true;
2351 }
2352 
2353 void cDynamicBuffer::Append(const uchar *Data, int Length)
2354 {
2355  if (Assert(used + Length)) {
2356  memcpy(buffer + used, Data, Length);
2357  used += Length;
2358  }
2359 }
2360 
2361 // --- cHashBase -------------------------------------------------------------
2362 
2363 cHashBase::cHashBase(int Size, bool OwnObjects)
2364 {
2365  size = Size;
2366  ownObjects = OwnObjects;
2367  hashTable = (cList<cHashObject>**)calloc(size, sizeof(cList<cHashObject>*));
2368 }
2369 
2371 {
2372  Clear();
2373  free(hashTable);
2374 }
2375 
2376 void cHashBase::Add(cListObject *Object, unsigned int Id)
2377 {
2378  unsigned int hash = hashfn(Id);
2379  if (!hashTable[hash])
2380  hashTable[hash] = new cList<cHashObject>;
2381  hashTable[hash]->Add(new cHashObject(Object, Id));
2382 }
2383 
2384 void cHashBase::Del(cListObject *Object, unsigned int Id)
2385 {
2386  cList<cHashObject> *list = hashTable[hashfn(Id)];
2387  if (list) {
2388  for (cHashObject *hob = list->First(); hob; hob = list->Next(hob)) {
2389  if (hob->object == Object) {
2390  list->Del(hob);
2391  break;
2392  }
2393  }
2394  }
2395 }
2396 
2398 {
2399  for (int i = 0; i < size; i++) {
2400  if (ownObjects) {
2401  cList<cHashObject> *list = hashTable[i];
2402  if (list) {
2403  for (cHashObject *hob = list->First(); hob; hob = list->Next(hob))
2404  delete hob->object;
2405  }
2406  }
2407  delete hashTable[i];
2408  hashTable[i] = NULL;
2409  }
2410 }
2411 
2412 cListObject *cHashBase::Get(unsigned int Id) const
2413 {
2414  cList<cHashObject> *list = hashTable[hashfn(Id)];
2415  if (list) {
2416  for (cHashObject *hob = list->First(); hob; hob = list->Next(hob)) {
2417  if (hob->id == Id)
2418  return hob->object;
2419  }
2420  }
2421  return NULL;
2422 }
2423 
2425 {
2426  return hashTable[hashfn(Id)];
2427 }
char * result
Definition: tools.h:364
cBase64Encoder(const uchar *Data, int Length, int MaxResult=64)
Sets up a new base 64 encoder for the given Data, with the given Length.
Definition: tools.c:1425
~cBase64Encoder()
Definition: tools.c:1434
int length
Definition: tools.h:361
const char * NextLine(void)
Returns the next line of encoded data (terminated by '\0'), or NULL if there is no more encoded data.
Definition: tools.c:1439
const uchar * data
Definition: tools.h:360
int maxResult
Definition: tools.h:362
static const char * b64
Definition: tools.h:365
void WordAlign(void)
Definition: tools.c:1499
bool SetLength(int Length)
Definition: tools.c:1506
int length
Definition: tools.h:385
const uint8_t * data
Definition: tools.h:384
int index
Definition: tools.h:386
int Length(void) const
Definition: tools.h:399
void SkipBits(int n)
Definition: tools.h:395
uint32_t GetBits(int n)
Definition: tools.c:1484
void ByteAlign(void)
Definition: tools.c:1492
int GetBit(void)
Definition: tools.c:1475
cCharSetConv(const char *FromCode=NULL, const char *ToCode=NULL)
Sets up a character set converter to convert from FromCode to ToCode.
Definition: tools.c:988
static void SetSystemCharacterTable(const char *CharacterTable)
Definition: tools.c:1006
char * result
Definition: tools.h:154
size_t length
Definition: tools.h:155
iconv_t cd
Definition: tools.h:153
static char * systemCharacterTable
Definition: tools.h:156
~cCharSetConv()
Definition: tools.c:999
static const char * SystemCharacterTable(void)
Definition: tools.h:174
const char * Convert(const char *From, char *To=NULL, size_t ToLength=0)
Converts the given Text from FromCode to ToCode (as set in the constructor).
Definition: tools.c:1029
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition: thread.c:73
cDynamicBuffer(int InitialSize=1024)
Definition: tools.c:2325
bool Realloc(int NewSize)
Definition: tools.c:2337
int Length(void)
Definition: tools.h:880
void Append(const uchar *Data, int Length)
Definition: tools.c:2353
uchar * Data(void)
Definition: tools.h:879
uchar * buffer
Definition: tools.h:865
~cDynamicBuffer()
Definition: tools.c:2332
bool Assert(int NewSize)
Definition: tools.h:870
int initialSize
Definition: tools.h:866
bool Load(const char *Directory, bool DirsOnly=false)
Definition: tools.c:1656
cFileNameList(const char *Directory=NULL, bool DirsOnly=false)
Definition: tools.c:1651
static bool FileReady(int FileDes, int TimeoutMs=1000)
Definition: tools.c:1726
bool Ready(bool Wait=true)
Definition: tools.c:1721
bool Open(const char *FileName, int Flags, mode_t Mode=DEFFILEMODE)
Definition: tools.c:1694
cFile(void)
Definition: tools.c:1684
~cFile()
Definition: tools.c:1689
void Close(void)
Definition: tools.c:1713
void Del(cListObject *Object, unsigned int Id)
Definition: tools.c:2384
cListObject * Get(unsigned int Id) const
Definition: tools.c:2412
cList< cHashObject > ** hashTable
Definition: tools.h:895
int size
Definition: tools.h:896
bool ownObjects
Definition: tools.h:897
virtual ~cHashBase()
Definition: tools.c:2370
cList< cHashObject > * GetList(unsigned int Id) const
Definition: tools.c:2424
cHashBase(int Size, bool OwnObjects)
Creates a new hash of the given Size.
Definition: tools.c:2363
void Clear(void)
Definition: tools.c:2397
void Add(cListObject *Object, unsigned int Id)
Definition: tools.c:2376
unsigned int hashfn(unsigned int Id) const
Definition: tools.h:898
virtual void Clear(void)
Definition: tools.c:2254
void Ins(cListObject *Object, cListObject *Before=NULL)
Definition: tools.c:2193
bool Contains(const cListObject *Object) const
If a pointer to an object contained in this list has been obtained while holding a lock,...
Definition: tools.c:2265
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2209
cListObject * lastObject
Definition: tools.h:566
virtual void Move(int From, int To)
Definition: tools.c:2225
cStateLock stateLock
Definition: tools.h:568
bool useGarbageCollector
Definition: tools.h:570
void SetExplicitModify(void)
If you have obtained a write lock on this list, and you don't want it to be automatically marked as m...
Definition: tools.c:2274
void SetModified(void)
Unconditionally marks this list as modified.
Definition: tools.c:2279
virtual ~cListBase()
Definition: tools.c:2163
bool Lock(cStateKey &StateKey, bool Write=false, int TimeoutMs=0) const
Tries to get a lock on this list and returns true if successful.
Definition: tools.c:2168
int count
Definition: tools.h:567
cListObject * objects
Definition: tools.h:566
const char * needsLocking
Definition: tools.h:569
cListBase(const char *NeedsLocking=NULL)
Definition: tools.c:2154
const cListObject * Get(int Index) const
Definition: tools.c:2284
int Count(void) const
Definition: tools.h:627
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2177
void Sort(void)
Definition: tools.c:2301
void Purge(bool Force=false)
Definition: tools.c:2136
cListGarbageCollector(void)
Definition: tools.c:2115
void Put(cListObject *Object)
Definition: tools.c:2127
void Unlink(void)
Definition: tools.c:2088
cListObject * next
Definition: tools.h:533
cListObject(void)
Definition: tools.c:2067
cListObject * Prev(void) const
Definition: tools.h:546
cListObject * prev
Definition: tools.h:533
int Index(void) const
Definition: tools.c:2097
virtual int Compare(const cListObject &ListObject) const
Must return 0 if this object is equal to ListObject, a positive value if it is "greater",...
Definition: tools.h:539
cListObject * Next(void) const
Definition: tools.h:547
void Insert(cListObject *Object)
Definition: tools.c:2082
virtual ~cListObject()
Definition: tools.c:2072
void Append(cListObject *Object)
Definition: tools.c:2076
const T * Next(const T *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
Definition: tools.h:650
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition: tools.h:643
bool Lock(int WaitSeconds=0)
Definition: tools.c:2016
void Unlock(void)
Definition: tools.c:2056
~cLockFile()
Definition: tools.c:2010
cLockFile(const char *Directory)
Definition: tools.c:2002
Definition: tools.h:434
cPoller(int FileHandle=-1, bool Out=false)
Definition: tools.c:1547
int numFileHandles
Definition: tools.h:438
bool Add(int FileHandle, bool Out)
Definition: tools.c:1553
@ MaxPollFiles
Definition: tools.h:436
bool Poll(int TimeoutMs=0)
Definition: tools.c:1585
void Del(int FileHandle, bool Out)
Definition: tools.c:1572
pollfd pfd[MaxPollFiles]
Definition: tools.h:437
struct dirent * result
Definition: tools.h:449
cReadDir(const char *Directory)
Definition: tools.c:1599
DIR * directory
Definition: tools.h:448
~cReadDir()
Definition: tools.c:1604
struct dirent * Next(void)
Definition: tools.c:1610
union cReadDir::@24 u
struct dirent d
Definition: tools.h:452
bool Ok(void)
Definition: tools.h:459
cReadLine(void)
Definition: tools.c:1516
char * buffer
Definition: tools.h:427
size_t size
Definition: tools.h:426
char * Read(FILE *f)
Definition: tools.c:1527
~cReadLine()
Definition: tools.c:1522
~cSafeFile()
Definition: tools.c:1752
cSafeFile(const char *FileName)
Definition: tools.c:1743
bool Open(void)
Definition: tools.c:1761
bool Close(void)
Definition: tools.c:1771
void SetExplicitModify(void)
If you have obtained a write lock on this lock, and you don't want its state to be automatically incr...
Definition: thread.c:820
void SetModified(void)
Sets this lock to have its state incremented when the current write lock state key is removed.
Definition: thread.c:835
bool Lock(cStateKey &StateKey, bool Write=false, int TimeoutMs=0)
Tries to get a lock and returns true if successful.
Definition: thread.c:725
virtual void Clear(void) override
Definition: tools.c:1641
int Find(const char *s) const
Definition: tools.c:1632
virtual ~cStringList() override
Definition: tools.c:1627
Definition: tools.h:178
cString & CompactChars(char c)
Compact any sequence of characters 'c' to a single character, and strip all of them from the beginnin...
Definition: tools.c:1189
static cString static cString vsprintf(const char *fmt, va_list &ap)
Definition: tools.c:1208
virtual ~cString()
Definition: tools.c:1115
cString(const char *S=NULL, bool TakePointer=false)
Definition: tools.c:1091
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1195
cString & operator=(const cString &String)
Definition: tools.c:1120
char * s
Definition: tools.h:180
cString & Append(const char *String)
Definition: tools.c:1148
cString & Truncate(int Index)
Truncate the string at the given Index (if Index is < 0 it is counted from the end of the string).
Definition: tools.c:1179
static tThreadId ThreadId(void)
Definition: thread.c:373
Definition: tools.h:404
uint64_t Elapsed(void) const
Definition: tools.c:818
void Set(int Ms=0)
Sets the timer.
Definition: tools.c:808
bool TimedOut(void) const
Definition: tools.c:813
cTimeMs(int Ms=0)
Creates a timer with ms resolution and an initial timeout of Ms.
Definition: tools.c:757
uint64_t begin
Definition: tools.h:406
static uint64_t Now(void)
Definition: tools.c:765
cUnbufferedFile is used for large files that are mainly written or read in a streaming manner,...
Definition: tools.h:494
static cUnbufferedFile * Create(const char *FileName, int Flags, mode_t Mode=DEFFILEMODE)
Definition: tools.c:1987
void SetReadAhead(size_t ra)
Definition: tools.c:1859
ssize_t Write(const void *Data, size_t Size)
Definition: tools.c:1936
int Close(void)
Definition: tools.c:1835
int Open(const char *FileName, int Flags, mode_t Mode=DEFFILEMODE)
Definition: tools.c:1817
ssize_t Read(void *Data, size_t Size)
Definition: tools.c:1878
int FadviseDrop(off_t Offset, off_t Len)
Definition: tools.c:1864
off_t Seek(off_t Offset, int Whence)
Definition: tools.c:1870
cUnbufferedFile(void)
Definition: tools.c:1807
~cUnbufferedFile()
Definition: tools.c:1812
virtual void Clear(void)
Definition: tools.h:805
#define tr(s)
Definition: i18n.h:85
#define WRITE_BUFFER
Definition: tools.c:1805
static boolean JpegCompressEmptyOutputBuffer(j_compress_ptr cinfo)
Definition: tools.c:1327
cString TimeString(time_t t)
Converts the given time to a string of the form "hh:mm".
Definition: tools.c:1301
#define LIST_GARBAGE_COLLECTOR_TIMEOUT
Definition: tools.c:2111
static void JpegCompressInitDestination(j_compress_ptr cinfo)
Definition: tools.c:1318
const char * strgetlast(const char *s, char c)
Definition: tools.c:221
cString WeekDayNameFull(int WeekDay)
Converts the given WeekDay (0=Sunday, 1=Monday, ...) to a full day name.
Definition: tools.c:1239
char * strcpyrealloc(char *dest, const char *src)
Definition: tools.c:114
int FreeDiskSpaceMB(const char *Directory, int *UsedMB)
Definition: tools.c:472
bool isempty(const char *s)
Definition: tools.c:357
int Utf8ToArray(const char *s, uint *a, int Size)
Converts the given character bytes (including the terminating 0) into an array of UTF-8 symbols of th...
Definition: tools.c:938
cString strescape(const char *s, const char *chars)
Definition: tools.c:280
#define LOCKFILENAME
Definition: tools.c:1999
#define MT(s, m, v)
#define READCHUNK
Definition: tools.c:1857
int Utf8CharSet(uint c, char *s)
Converts the given UTF-8 symbol to a sequence of character bytes and copies them to the given string.
Definition: tools.c:856
int strcountchr(const char *s, char c)
returns the number of occurrences of 'c' in 's'.
Definition: tools.c:199
cString TimeToString(time_t t)
Converts the given time to a string of the form "www mmm dd hh:mm:ss yyyy".
Definition: tools.c:1271
char * Utf8Strn0Cpy(char *Dest, const char *Src, int n)
Copies at most n character bytes from Src to Dest, making sure that the resulting copy ends with a co...
Definition: tools.c:915
bool SpinUpDisk(const char *FileName)
Definition: tools.c:693
bool MakeDirs(const char *FileName, bool IsDirectory)
Definition: tools.c:507
int Utf8StrLen(const char *s)
Returns the number of UTF-8 symbols formed by the given string of character bytes.
Definition: tools.c:903
#define LOCKFILESTALETIME
Definition: tools.c:2000
#define FADVGRAN
Definition: tools.c:1856
cString WeekDayName(int WeekDay)
Converts the given WeekDay (0=Sunday, 1=Monday, ...) to a three letter day name.
Definition: tools.c:1218
bool startswith(const char *s, const char *p)
Definition: tools.c:337
void syslog_with_tid(int priority, const char *format,...)
Definition: tools.c:35
uchar * RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality)
Converts the given Memory to a JPEG image and returns a pointer to the resulting image.
Definition: tools.c:1366
cString dtoa(double d, const char *Format)
Converts the given double value to a string, making sure it uses a '.
Definition: tools.c:440
char * compactchars(char *s, char c)
removes all occurrences of 'c' from the beginning an end of 's' and replaces sequences of multiple 'c...
Definition: tools.c:256
time_t LastModifiedTime(const char *FileName)
Definition: tools.c:739
double atod(const char *s)
Converts the given string, which is a floating point number using a '.
Definition: tools.c:419
cString ShortDateString(time_t t)
Converts the given time to a string of the form "dd.mm.yy".
Definition: tools.c:1292
char * strreplace(char *s, char c1, char c2)
Definition: tools.c:142
ssize_t safe_read(int filedes, void *buffer, size_t size)
Definition: tools.c:53
#define JPEGCOMPRESSMEM
Definition: tools.c:1311
static int CompareListObjects(const void *a, const void *b)
Definition: tools.c:2294
bool StrInArray(const char *a[], const char *s)
Returns true if the string s is equal to one of the strings pointed to by the (NULL terminated) array...
Definition: tools.c:398
const char * GetHostName(void)
Gets the host name of this machine.
Definition: tools.c:1409
cString strgetval(const char *s, const char *name, char d)
Returns the value part of a 'name=value' pair in s.
Definition: tools.c:303
char * stripspace(char *s)
Definition: tools.c:227
ssize_t safe_write(int filedes, const void *buffer, size_t size)
Definition: tools.c:65
int numdigits(int n)
Definition: tools.c:362
int Utf8SymChars(const char *s, int Symbols)
Returns the number of character bytes at the beginning of the given string that form at most the give...
Definition: tools.c:890
bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis, const char *IgnoreFiles[])
Removes all empty directories under the given directory DirName.
Definition: tools.c:593
#define DECIMAL_POINT_C
Definition: tools.c:417
static void JpegCompressTermDestination(j_compress_ptr cinfo)
Definition: tools.c:1350
char * ReadLink(const char *FileName)
returns a new string allocated on the heap, which the caller must delete (or NULL in case of an error...
Definition: tools.c:679
uint Utf8CharGet(const char *s, int Length)
Returns the UTF-8 symbol at the beginning of the given string.
Definition: tools.c:841
#define MAXSYSLOGBUF
Definition: tools.c:33
int DirSizeMB(const char *DirName)
returns the total size of the files in the given directory, or -1 in case of an error
Definition: tools.c:647
cString DateString(time_t t)
Converts the given time to a string of the form "www dd.mm.yyyy".
Definition: tools.c:1281
int SysLogLevel
Definition: tools.c:31
bool DirectoryOk(const char *DirName, bool LogErrors)
Definition: tools.c:489
int WriteAllOrNothing(int fd, const uchar *Data, int Length, int TimeoutMs, int RetryMs)
Writes either all Data to the given file descriptor, or nothing at all.
Definition: tools.c:90
char * strn0cpy(char *dest, const char *src, size_t n)
Definition: tools.c:131
int Utf8FromArray(const uint *a, char *s, int Size, int Max)
Converts the given array of UTF-8 symbols (including the terminating 0) into a sequence of character ...
Definition: tools.c:956
int Utf8CharLen(const char *s)
Returns the number of character bytes at the beginning of the given string that form a UTF-8 symbol.
Definition: tools.c:827
cString DayDateTime(time_t t)
Converts the given time to a string of the form "www dd.mm. hh:mm".
Definition: tools.c:1260
char * strshift(char *s, int n)
Shifts the given string to the left by the given number of bytes, thus removing the first n bytes fro...
Definition: tools.c:325
bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
Definition: tools.c:535
char * compactspace(char *s)
Definition: tools.c:239
off_t FileSize(const char *FileName)
returns the size of the given file, or -1 in case of an error (e.g. if the file doesn't exist)
Definition: tools.c:747
bool EntriesOnSameFileSystem(const char *File1, const char *File2)
Checks whether the given files are on the same file system.
Definition: tools.c:457
const char * strchrn(const char *s, char c, size_t n)
returns a pointer to the n'th occurrence (counting from 1) of c in s, or NULL if no such character wa...
Definition: tools.c:186
int BCD2INT(int x)
Definition: tools.c:45
static uint SystemToUtf8[128]
Definition: tools.c:825
bool endswith(const char *s, const char *p)
Definition: tools.c:346
cString itoa(int n)
Definition: tools.c:450
void TouchFile(const char *FileName, bool Create)
Definition: tools.c:725
bool isnumber(const char *s)
Definition: tools.c:372
cString AddDirectory(const char *DirName, const char *FileName)
Definition: tools.c:410
void writechar(int filedes, char c)
Definition: tools.c:85
cString strgetbefore(const char *s, char c, int n)
Definition: tools.c:211
cListGarbageCollector ListGarbageCollector
Definition: tools.c:2113
int64_t StrToNum(const char *s)
Converts the given string to a number.
Definition: tools.c:383
#define FATALERRNO
Definition: tools.h:52
#define MEGABYTE(n)
Definition: tools.h:45
char * skipspace(const char *s)
Definition: tools.h:244
#define BCDCHARTOINT(x)
Definition: tools.h:74
#define LOG_ERROR_STR(s)
Definition: tools.h:40
unsigned char uchar
Definition: tools.h:31
#define dsyslog(a...)
Definition: tools.h:37
#define MALLOC(type, size)
Definition: tools.h:47
T min(T a, T b)
Definition: tools.h:63
T max(T a, T b)
Definition: tools.h:64
#define esyslog(a...)
Definition: tools.h:35
#define LOG_ERROR
Definition: tools.h:39
#define isyslog(a...)
Definition: tools.h:36
#define KILOBYTE(n)
Definition: tools.h:44