| File: | tools.c |
| Location: | line 225, column 23 |
| Description: | Null pointer passed as an argument to a 'nonnull' parameter |
| 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 | } |