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