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