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