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