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