Bug Summary

File:tools.c
Location:line 1090, column 10
Description:Potential leak of memory pointed to by 'buffer'

Annotated Source Code

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