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