File: | tools.c |
Location: | line 1602, column 10 |
Description: | The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage |
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> | |||||
14 | extern "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 | ||||||
31 | int SysLogLevel = 3; | |||||
32 | ||||||
33 | #define MAXSYSLOGBUF256 256 | |||||
34 | ||||||
35 | void 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 | ||||||
45 | int 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 | ||||||
53 | ssize_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 | ||||||
65 | ssize_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 | ||||||
85 | void writechar(int filedes, char c) | |||||
86 | { | |||||
87 | safe_write(filedes, &c, sizeof(c)); | |||||
88 | } | |||||
89 | ||||||
90 | int 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 | ||||||
114 | char *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 | ||||||
131 | char *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 | ||||||
139 | char *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 | ||||||
152 | char *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 | ||||||
176 | const 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 | ||||||
189 | int 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 | ||||||
201 | char *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 | ||||||
213 | char *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 | ||||||
230 | char *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 | ||||||
254 | cString 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 | ||||||
277 | bool startswith(const char *s, const char *p) | |||||
278 | { | |||||
279 | while (*p) { | |||||
280 | if (*p++ != *s++) | |||||
281 | return false; | |||||
282 | } | |||||
283 | return true; | |||||
284 | } | |||||
285 | ||||||
286 | bool 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 | ||||||
297 | bool isempty(const char *s) | |||||
298 | { | |||||
299 | return !(s && *skipspace(s)); | |||||
300 | } | |||||
301 | ||||||
302 | int numdigits(int n) | |||||
303 | { | |||||
304 | int res = 1; | |||||
305 | while (n >= 10) { | |||||
306 | n /= 10; | |||||
307 | res++; | |||||
308 | } | |||||
309 | return res; | |||||
310 | } | |||||
311 | ||||||
312 | bool 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 | ||||||
323 | int64_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 | ||||||
338 | bool 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 | ||||||
350 | cString 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 | ||||||
357 | double 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 | ||||||
378 | cString 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 | ||||||
388 | cString itoa(int n) | |||||
389 | { | |||||
390 | char buf[16]; | |||||
391 | snprintf(buf, sizeof(buf), "%d", n); | |||||
392 | return buf; | |||||
393 | } | |||||
394 | ||||||
395 | bool 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 | ||||||
410 | int 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 | ||||||
427 | bool 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 | ||||||
445 | bool 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 | ||||||
473 | bool 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 | ||||||
531 | bool 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 | ||||||
585 | int 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 | ||||||
617 | char *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 | ||||||
631 | bool 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 | ||||||
663 | void 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 | ||||||
669 | time_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 | ||||||
677 | off_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 | ||||||
687 | cTimeMs::cTimeMs(int Ms) | |||||
688 | { | |||||
689 | if (Ms >= 0) | |||||
690 | Set(Ms); | |||||
691 | else | |||||
692 | begin = 0; | |||||
693 | } | |||||
694 | ||||||
695 | uint64_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 | ||||||
738 | void cTimeMs::Set(int Ms) | |||||
739 | { | |||||
740 | begin = Now() + Ms; | |||||
741 | } | |||||
742 | ||||||
743 | bool cTimeMs::TimedOut(void) const | |||||
744 | { | |||||
745 | return Now() >= begin; | |||||
746 | } | |||||
747 | ||||||
748 | uint64_t cTimeMs::Elapsed(void) const | |||||
749 | { | |||||
750 | return Now() - begin; | |||||
751 | } | |||||
752 | ||||||
753 | // --- UTF-8 support --------------------------------------------------------- | |||||
754 | ||||||
755 | static uint SystemToUtf8[128] = { 0 }; | |||||
756 | ||||||
757 | int 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 | ||||||
771 | uint 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 | ||||||
786 | int 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 | ||||||
820 | int 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 | ||||||
833 | int 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 | ||||||
845 | char *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 | ||||||
864 | int 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 | ||||||
882 | int 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 | ||||||
912 | char *cCharSetConv::systemCharacterTable = NULL__null; | |||||
913 | ||||||
914 | cCharSetConv::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 | ||||||
925 | cCharSetConv::~cCharSetConv() | |||||
926 | { | |||||
927 | free(result); | |||||
928 | if (cd != (iconv_t)-1) | |||||
929 | iconv_close(cd); | |||||
930 | } | |||||
931 | ||||||
932 | void 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 | ||||||
955 | const 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 | ||||||
1017 | cString::cString(const char *S, bool TakePointer) | |||||
1018 | { | |||||
1019 | s = TakePointer ? (char *)S : S ? strdup(S) : NULL__null; | |||||
1020 | } | |||||
1021 | ||||||
1022 | cString::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 | ||||||
1036 | cString::cString(const cString &String) | |||||
1037 | { | |||||
1038 | s = String.s ? strdup(String.s) : NULL__null; | |||||
1039 | } | |||||
1040 | ||||||
1041 | cString::~cString() | |||||
1042 | { | |||||
1043 | free(s); | |||||
1044 | } | |||||
1045 | ||||||
1046 | cString &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 | ||||||
1055 | cString &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 | ||||||
1064 | cString &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 | ||||||
1074 | cString &cString::CompactChars(char c) | |||||
1075 | { | |||||
1076 | compactchars(s, c); | |||||
1077 | return *this; | |||||
1078 | } | |||||
1079 | ||||||
1080 | cString 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) { | |||||
1086 | esyslog("error in vasprintf('%s', ...)", fmt)void( (SysLogLevel > 0) ? syslog_with_tid(3, "error in vasprintf('%s', ...)" , fmt) : void() ); | |||||
1087 | buffer = strdup("???"); | |||||
1088 | } | |||||
1089 | va_end(ap)__builtin_va_end(ap); | |||||
1090 | return cString(buffer, true); | |||||
1091 | } | |||||
1092 | ||||||
1093 | cString 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 | ||||||
1103 | cString 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 | ||||||
1118 | cString WeekDayName(time_t t) | |||||
1119 | { | |||||
1120 | struct tm tm_r; | |||||
1121 | return WeekDayName(localtime_r(&t, &tm_r)->tm_wday); | |||||
1122 | } | |||||
1123 | ||||||
1124 | cString 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 | ||||||
1139 | cString WeekDayNameFull(time_t t) | |||||
1140 | { | |||||
1141 | struct tm tm_r; | |||||
1142 | return WeekDayNameFull(localtime_r(&t, &tm_r)->tm_wday); | |||||
1143 | } | |||||
1144 | ||||||
1145 | cString 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 | ||||||
1156 | cString 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 | ||||||
1166 | cString 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 | ||||||
1177 | cString 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 | ||||||
1186 | cString 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 | ||||||
1198 | struct tJpegCompressData { | |||||
1199 | int size; | |||||
1200 | uchar *mem; | |||||
1201 | }; | |||||
1202 | ||||||
1203 | static 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 | ||||||
1212 | static 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 | ||||||
1235 | static 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 | ||||||
1251 | uchar *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 | ||||||
1294 | const char *cBase64Encoder::b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |||||
1295 | ||||||
1296 | cBase64Encoder::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 | ||||||
1305 | cBase64Encoder::~cBase64Encoder() | |||||
1306 | { | |||||
1307 | free(result); | |||||
1308 | } | |||||
1309 | ||||||
1310 | const 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 | ||||||
1346 | int 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 | ||||||
1355 | uint32_t cBitStream::GetBits(int n) | |||||
1356 | { | |||||
1357 | uint32_t r = 0; | |||||
1358 | while (n--) | |||||
1359 | r |= GetBit() << n; | |||||
1360 | return r; | |||||
1361 | } | |||||
1362 | ||||||
1363 | void cBitStream::ByteAlign(void) | |||||
1364 | { | |||||
1365 | int n = index % 8; | |||||
1366 | if (n > 0) | |||||
1367 | SkipBits(8 - n); | |||||
1368 | } | |||||
1369 | ||||||
1370 | void cBitStream::WordAlign(void) | |||||
1371 | { | |||||
1372 | int n = index % 16; | |||||
1373 | if (n > 0) | |||||
1374 | SkipBits(16 - n); | |||||
1375 | } | |||||
1376 | ||||||
1377 | bool cBitStream::SetLength(int Length) | |||||
1378 | { | |||||
1379 | if (Length > length) | |||||
1380 | return false; | |||||
1381 | length = Length; | |||||
1382 | return true; | |||||
1383 | } | |||||
1384 | ||||||
1385 | // --- cReadLine ------------------------------------------------------------- | |||||
1386 | ||||||
1387 | cReadLine::cReadLine(void) | |||||
1388 | { | |||||
1389 | size = 0; | |||||
1390 | buffer = NULL__null; | |||||
1391 | } | |||||
1392 | ||||||
1393 | cReadLine::~cReadLine() | |||||
1394 | { | |||||
1395 | free(buffer); | |||||
1396 | } | |||||
1397 | ||||||
1398 | char *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 | ||||||
1418 | cPoller::cPoller(int FileHandle, bool Out) | |||||
1419 | { | |||||
1420 | numFileHandles = 0; | |||||
1421 | Add(FileHandle, Out); | |||||
1422 | } | |||||
1423 | ||||||
1424 | bool 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 | ||||||
1443 | bool 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 | ||||||
1455 | cReadDir::cReadDir(const char *Directory) | |||||
1456 | { | |||||
1457 | directory = opendir(Directory); | |||||
1458 | } | |||||
1459 | ||||||
1460 | cReadDir::~cReadDir() | |||||
1461 | { | |||||
1462 | if (directory) | |||||
1463 | closedir(directory); | |||||
1464 | } | |||||
1465 | ||||||
1466 | struct 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 | ||||||
1479 | cStringList::~cStringList() | |||||
1480 | { | |||||
1481 | Clear(); | |||||
1482 | } | |||||
1483 | ||||||
1484 | int 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 | ||||||
1493 | void 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)? | |||||
1503 | cFileNameList::cFileNameList(const char *Directory, bool DirsOnly) | |||||
1504 | { | |||||
1505 | Load(Directory, DirsOnly); | |||||
1506 | } | |||||
1507 | ||||||
1508 | bool 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 | ||||||
1536 | bool cFile::files[FD_SETSIZE1024] = { false }; | |||||
1537 | int cFile::maxFiles = 0; | |||||
1538 | ||||||
1539 | cFile::cFile(void) | |||||
1540 | { | |||||
1541 | f = -1; | |||||
1542 | } | |||||
1543 | ||||||
1544 | cFile::~cFile() | |||||
1545 | { | |||||
1546 | Close(); | |||||
1547 | } | |||||
1548 | ||||||
1549 | bool 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 | ||||||
1557 | bool 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 | ||||||
1582 | void cFile::Close(void) | |||||
1583 | { | |||||
1584 | if (f >= 0) { | |||||
1585 | close(f); | |||||
1586 | files[f] = false; | |||||
1587 | f = -1; | |||||
1588 | } | |||||
1589 | } | |||||
1590 | ||||||
1591 | bool cFile::Ready(bool Wait) | |||||
1592 | { | |||||
1593 | return f >= 0 && AnyFileReady(f, Wait ? 1000 : 0); | |||||
1594 | } | |||||
1595 | ||||||
1596 | bool 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 | ||||||
1614 | bool 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 | ||||||
1629 | bool 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 | ||||||
1644 | cSafeFile::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 | ||||||
1653 | cSafeFile::~cSafeFile() | |||||
1654 | { | |||||
1655 | if (f) | |||||
1656 | fclose(f); | |||||
1657 | unlink(tempName); | |||||
1658 | free(fileName); | |||||
1659 | free(tempName); | |||||
1660 | } | |||||
1661 | ||||||
1662 | bool 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 | ||||||
1672 | bool 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 | ||||||
1703 | cUnbufferedFile::cUnbufferedFile(void) | |||||
1704 | { | |||||
1705 | fd = -1; | |||||
1706 | } | |||||
1707 | ||||||
1708 | cUnbufferedFile::~cUnbufferedFile() | |||||
1709 | { | |||||
1710 | Close(); | |||||
1711 | } | |||||
1712 | ||||||
1713 | int 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 | ||||||
1731 | int 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 | ||||||
1755 | void cUnbufferedFile::SetReadAhead(size_t ra) | |||||
1756 | { | |||||
1757 | readahead = ra; | |||||
1758 | } | |||||
1759 | ||||||
1760 | int 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 | ||||||
1766 | off_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 | ||||||
1774 | ssize_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 | ||||||
1832 | ssize_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 | ||||||
1883 | cUnbufferedFile *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 | ||||||
1898 | cLockFile::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 | ||||||
1906 | cLockFile::~cLockFile() | |||||
1907 | { | |||||
1908 | Unlock(); | |||||
1909 | free(fileName); | |||||
1910 | } | |||||
1911 | ||||||
1912 | bool 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 | ||||||
1948 | void cLockFile::Unlock(void) | |||||
1949 | { | |||||
1950 | if (f >= 0) { | |||||
1951 | close(f); | |||||
1952 | remove(fileName); | |||||
1953 | f = -1; | |||||
1954 | } | |||||
1955 | } | |||||
1956 | ||||||
1957 | // --- cListObject ----------------------------------------------------------- | |||||
1958 | ||||||
1959 | cListObject::cListObject(void) | |||||
1960 | { | |||||
1961 | prev = next = NULL__null; | |||||
1962 | } | |||||
1963 | ||||||
1964 | cListObject::~cListObject() | |||||
1965 | { | |||||
1966 | } | |||||
1967 | ||||||
1968 | void cListObject::Append(cListObject *Object) | |||||
1969 | { | |||||
1970 | next = Object; | |||||
1971 | Object->prev = this; | |||||
1972 | } | |||||
1973 | ||||||
1974 | void cListObject::Insert(cListObject *Object) | |||||
1975 | { | |||||
1976 | prev = Object; | |||||
1977 | Object->next = this; | |||||
1978 | } | |||||
1979 | ||||||
1980 | void 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 | ||||||
1989 | int 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 | ||||||
2003 | cListBase::cListBase(void) | |||||
2004 | { | |||||
2005 | objects = lastObject = NULL__null; | |||||
2006 | count = 0; | |||||
2007 | } | |||||
2008 | ||||||
2009 | cListBase::~cListBase() | |||||
2010 | { | |||||
2011 | Clear(); | |||||
2012 | } | |||||
2013 | ||||||
2014 | void 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 | ||||||
2030 | void 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 | ||||||
2046 | void 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 | ||||||
2058 | void cListBase::Move(int From, int To) | |||||
2059 | { | |||||
2060 | Move(Get(From), Get(To)); | |||||
2061 | } | |||||
2062 | ||||||
2063 | void 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 | ||||||
2087 | void 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 | ||||||
2098 | cListObject *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 | ||||||
2108 | static 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 | ||||||
2115 | void 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 | ||||||
2136 | cHashBase::cHashBase(int Size) | |||||
2137 | { | |||||
2138 | size = Size; | |||||
2139 | hashTable = (cList<cHashObject>**)calloc(size, sizeof(cList<cHashObject>*)); | |||||
2140 | } | |||||
2141 | ||||||
2142 | cHashBase::~cHashBase(void) | |||||
2143 | { | |||||
2144 | Clear(); | |||||
2145 | free(hashTable); | |||||
2146 | } | |||||
2147 | ||||||
2148 | void 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 | ||||||
2156 | void 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 | ||||||
2169 | void cHashBase::Clear(void) | |||||
2170 | { | |||||
2171 | for (int i = 0; i < size; i++) { | |||||
2172 | delete hashTable[i]; | |||||
2173 | hashTable[i] = NULL__null; | |||||
2174 | } | |||||
2175 | } | |||||
2176 | ||||||
2177 | cListObject *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 | ||||||
2189 | cList<cHashObject> *cHashBase::GetList(unsigned int Id) const | |||||
2190 | { | |||||
2191 | return hashTable[hashfn(Id)]; | |||||
2192 | } |