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