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