File: | tools.c |
Location: | line 274, column 10 |
Description: | Potential leak of memory pointed to by 's' |
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 | } |