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