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