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