vdr  1.7.27
tools.c
Go to the documentation of this file.
00001 /*
00002  * tools.c: Various tools
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: tools.c 2.22 2012/02/18 15:30:35 kls Exp $
00008  */
00009 
00010 #include "tools.h"
00011 #include <ctype.h>
00012 #include <dirent.h>
00013 #include <errno.h>
00014 extern "C" {
00015 #ifdef boolean
00016 #define HAVE_BOOLEAN
00017 #endif
00018 #include <jpeglib.h>
00019 #undef boolean
00020 }
00021 #include <stdlib.h>
00022 #include <sys/time.h>
00023 #include <sys/vfs.h>
00024 #include <time.h>
00025 #include <unistd.h>
00026 #include <utime.h>
00027 #include "i18n.h"
00028 #include "thread.h"
00029 
00030 int SysLogLevel = 3;
00031 
00032 #define MAXSYSLOGBUF 256
00033 
00034 void syslog_with_tid(int priority, const char *format, ...)
00035 {
00036   va_list ap;
00037   char fmt[MAXSYSLOGBUF];
00038   snprintf(fmt, sizeof(fmt), "[%d] %s", cThread::ThreadId(), format);
00039   va_start(ap, format);
00040   vsyslog(priority, fmt, ap);
00041   va_end(ap);
00042 }
00043 
00044 int BCD2INT(int x)
00045 {
00046   return ((1000000 * BCDCHARTOINT((x >> 24) & 0xFF)) +
00047             (10000 * BCDCHARTOINT((x >> 16) & 0xFF)) +
00048               (100 * BCDCHARTOINT((x >>  8) & 0xFF)) +
00049                      BCDCHARTOINT( x        & 0xFF));
00050 }
00051 
00052 ssize_t safe_read(int filedes, void *buffer, size_t size)
00053 {
00054   for (;;) {
00055       ssize_t p = read(filedes, buffer, size);
00056       if (p < 0 && errno == EINTR) {
00057          dsyslog("EINTR while reading from file handle %d - retrying", filedes);
00058          continue;
00059          }
00060       return p;
00061       }
00062 }
00063 
00064 ssize_t safe_write(int filedes, const void *buffer, size_t size)
00065 {
00066   ssize_t p = 0;
00067   ssize_t written = size;
00068   const unsigned char *ptr = (const unsigned char *)buffer;
00069   while (size > 0) {
00070         p = write(filedes, ptr, size);
00071         if (p < 0) {
00072            if (errno == EINTR) {
00073               dsyslog("EINTR while writing to file handle %d - retrying", filedes);
00074               continue;
00075               }
00076            break;
00077            }
00078         ptr  += p;
00079         size -= p;
00080         }
00081   return p < 0 ? p : written;
00082 }
00083 
00084 void writechar(int filedes, char c)
00085 {
00086   safe_write(filedes, &c, sizeof(c));
00087 }
00088 
00089 int WriteAllOrNothing(int fd, const uchar *Data, int Length, int TimeoutMs, int RetryMs)
00090 {
00091   int written = 0;
00092   while (Length > 0) {
00093         int w = write(fd, Data + written, Length);
00094         if (w > 0) {
00095            Length -= w;
00096            written += w;
00097            }
00098         else if (written > 0 && !FATALERRNO) {
00099            // we've started writing, so we must finish it!
00100            cTimeMs t;
00101            cPoller Poller(fd, true);
00102            Poller.Poll(RetryMs);
00103            if (TimeoutMs > 0 && (TimeoutMs -= t.Elapsed()) <= 0)
00104               break;
00105            }
00106         else
00107            // nothing written yet (or fatal error), so we can just return the error code:
00108            return w;
00109         }
00110   return written;
00111 }
00112 
00113 char *strcpyrealloc(char *dest, const char *src)
00114 {
00115   if (src) {
00116      int l = max(dest ? strlen(dest) : 0, strlen(src)) + 1; // don't let the block get smaller!
00117      dest = (char *)realloc(dest, l);
00118      if (dest)
00119         strcpy(dest, src);
00120      else
00121         esyslog("ERROR: out of memory");
00122      }
00123   else {
00124      free(dest);
00125      dest = NULL;
00126      }
00127   return dest;
00128 }
00129 
00130 char *strn0cpy(char *dest, const char *src, size_t n)
00131 {
00132   char *s = dest;
00133   for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
00134   *dest = 0;
00135   return s;
00136 }
00137 
00138 char *strreplace(char *s, char c1, char c2)
00139 {
00140   if (s) {
00141      char *p = s;
00142      while (*p) {
00143            if (*p == c1)
00144               *p = c2;
00145            p++;
00146            }
00147      }
00148   return s;
00149 }
00150 
00151 char *strreplace(char *s, const char *s1, const char *s2)
00152 {
00153   char *p = strstr(s, s1);
00154   if (p) {
00155      int of = p - s;
00156      int l  = strlen(s);
00157      int l1 = strlen(s1);
00158      int l2 = strlen(s2);
00159      if (l2 > l1) {
00160         if (char *NewBuffer = (char *)realloc(s, l + l2 - l1 + 1))
00161            s = NewBuffer;
00162         else {
00163            esyslog("ERROR: out of memory");
00164            return s;
00165            }
00166         }
00167      char *sof = s + of;
00168      if (l2 != l1)
00169         memmove(sof + l2, sof + l1, l - of - l1 + 1);
00170      strncpy(sof, s2, l2);
00171      }
00172   return s;
00173 }
00174 
00175 char *stripspace(char *s)
00176 {
00177   if (s && *s) {
00178      for (char *p = s + strlen(s) - 1; p >= s; p--) {
00179          if (!isspace(*p))
00180             break;
00181          *p = 0;
00182          }
00183      }
00184   return s;
00185 }
00186 
00187 char *compactspace(char *s)
00188 {
00189   if (s && *s) {
00190      char *t = stripspace(skipspace(s));
00191      char *p = t;
00192      while (p && *p) {
00193            char *q = skipspace(p);
00194            if (q - p > 1)
00195               memmove(p + 1, q, strlen(q) + 1);
00196            p++;
00197            }
00198      if (t != s)
00199         memmove(s, t, strlen(t) + 1);
00200      }
00201   return s;
00202 }
00203 
00204 cString strescape(const char *s, const char *chars)
00205 {
00206   char *buffer;
00207   const char *p = s;
00208   char *t = NULL;
00209   while (*p) {
00210         if (strchr(chars, *p)) {
00211            if (!t) {
00212               buffer = MALLOC(char, 2 * strlen(s) + 1);
00213               t = buffer + (p - s);
00214               s = strcpy(buffer, s);
00215               }
00216            *t++ = '\\';
00217            }
00218         if (t)
00219            *t++ = *p;
00220         p++;
00221         }
00222   if (t)
00223      *t = 0;
00224   return cString(s, t != NULL);
00225 }
00226 
00227 bool startswith(const char *s, const char *p)
00228 {
00229   while (*p) {
00230         if (*p++ != *s++)
00231            return false;
00232         }
00233   return true;
00234 }
00235 
00236 bool endswith(const char *s, const char *p)
00237 {
00238   const char *se = s + strlen(s) - 1;
00239   const char *pe = p + strlen(p) - 1;
00240   while (pe >= p) {
00241         if (*pe-- != *se-- || (se < s && pe >= p))
00242            return false;
00243         }
00244   return true;
00245 }
00246 
00247 bool isempty(const char *s)
00248 {
00249   return !(s && *skipspace(s));
00250 }
00251 
00252 int numdigits(int n)
00253 {
00254   int res = 1;
00255   while (n >= 10) {
00256         n /= 10;
00257         res++;
00258         }
00259   return res;
00260 }
00261 
00262 bool isnumber(const char *s)
00263 {
00264   if (!s || !*s)
00265      return false;
00266   do {
00267      if (!isdigit(*s))
00268         return false;
00269      } while (*++s);
00270   return true;
00271 }
00272 
00273 int64_t StrToNum(const char *s)
00274 {
00275   char *t = NULL;
00276   int64_t n = strtoll(s, &t, 10);
00277   if (t) {
00278      switch (*t) {
00279        case 'T': n *= 1024;
00280        case 'G': n *= 1024;
00281        case 'M': n *= 1024;
00282        case 'K': n *= 1024;
00283        }
00284      }
00285   return n;
00286 }
00287 
00288 cString AddDirectory(const char *DirName, const char *FileName)
00289 {
00290   return cString::sprintf("%s/%s", DirName && *DirName ? DirName : ".", FileName);
00291 }
00292 
00293 cString itoa(int n)
00294 {
00295   char buf[16];
00296   snprintf(buf, sizeof(buf), "%d", n);
00297   return buf;
00298 }
00299 
00300 bool EntriesOnSameFileSystem(const char *File1, const char *File2)
00301 {
00302   struct stat st;
00303   if (stat(File1, &st) == 0) {
00304      dev_t dev1 = st.st_dev;
00305      if (stat(File2, &st) == 0)
00306         return st.st_dev == dev1;
00307      else
00308         LOG_ERROR_STR(File2);
00309      }
00310   else
00311      LOG_ERROR_STR(File1);
00312   return false;
00313 }
00314 
00315 int FreeDiskSpaceMB(const char *Directory, int *UsedMB)
00316 {
00317   if (UsedMB)
00318      *UsedMB = 0;
00319   int Free = 0;
00320   struct statfs statFs;
00321   if (statfs(Directory, &statFs) == 0) {
00322      double blocksPerMeg = 1024.0 * 1024.0 / statFs.f_bsize;
00323      if (UsedMB)
00324         *UsedMB = int((statFs.f_blocks - statFs.f_bfree) / blocksPerMeg);
00325      Free = int(statFs.f_bavail / blocksPerMeg);
00326      }
00327   else
00328      LOG_ERROR_STR(Directory);
00329   return Free;
00330 }
00331 
00332 bool DirectoryOk(const char *DirName, bool LogErrors)
00333 {
00334   struct stat ds;
00335   if (stat(DirName, &ds) == 0) {
00336      if (S_ISDIR(ds.st_mode)) {
00337         if (access(DirName, R_OK | W_OK | X_OK) == 0)
00338            return true;
00339         else if (LogErrors)
00340            esyslog("ERROR: can't access %s", DirName);
00341         }
00342      else if (LogErrors)
00343         esyslog("ERROR: %s is not a directory", DirName);
00344      }
00345   else if (LogErrors)
00346      LOG_ERROR_STR(DirName);
00347   return false;
00348 }
00349 
00350 bool MakeDirs(const char *FileName, bool IsDirectory)
00351 {
00352   bool result = true;
00353   char *s = strdup(FileName);
00354   char *p = s;
00355   if (*p == '/')
00356      p++;
00357   while ((p = strchr(p, '/')) != NULL || IsDirectory) {
00358         if (p)
00359            *p = 0;
00360         struct stat fs;
00361         if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
00362            dsyslog("creating directory %s", s);
00363            if (mkdir(s, ACCESSPERMS) == -1) {
00364               LOG_ERROR_STR(s);
00365               result = false;
00366               break;
00367               }
00368            }
00369         if (p)
00370            *p++ = '/';
00371         else
00372            break;
00373         }
00374   free(s);
00375   return result;
00376 }
00377 
00378 bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
00379 {
00380   struct stat st;
00381   if (stat(FileName, &st) == 0) {
00382      if (S_ISDIR(st.st_mode)) {
00383         cReadDir d(FileName);
00384         if (d.Ok()) {
00385            struct dirent *e;
00386            while ((e = d.Next()) != NULL) {
00387                  cString buffer = AddDirectory(FileName, e->d_name);
00388                  if (FollowSymlinks) {
00389                     struct stat st2;
00390                     if (lstat(buffer, &st2) == 0) {
00391                        if (S_ISLNK(st2.st_mode)) {
00392                           int size = st2.st_size + 1;
00393                           char *l = MALLOC(char, size);
00394                           int n = readlink(buffer, l, size - 1);
00395                           if (n < 0) {
00396                              if (errno != EINVAL)
00397                                 LOG_ERROR_STR(*buffer);
00398                              }
00399                           else {
00400                              l[n] = 0;
00401                              dsyslog("removing %s", l);
00402                              if (remove(l) < 0)
00403                                 LOG_ERROR_STR(l);
00404                              }
00405                           free(l);
00406                           }
00407                        }
00408                     else if (errno != ENOENT) {
00409                        LOG_ERROR_STR(FileName);
00410                        return false;
00411                        }
00412                     }
00413                  dsyslog("removing %s", *buffer);
00414                  if (remove(buffer) < 0)
00415                     LOG_ERROR_STR(*buffer);
00416                  }
00417            }
00418         else {
00419            LOG_ERROR_STR(FileName);
00420            return false;
00421            }
00422         }
00423      dsyslog("removing %s", FileName);
00424      if (remove(FileName) < 0) {
00425         LOG_ERROR_STR(FileName);
00426         return false;
00427         }
00428      }
00429   else if (errno != ENOENT) {
00430      LOG_ERROR_STR(FileName);
00431      return false;
00432      }
00433   return true;
00434 }
00435 
00436 bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis)
00437 {
00438   cReadDir d(DirName);
00439   if (d.Ok()) {
00440      bool empty = true;
00441      struct dirent *e;
00442      while ((e = d.Next()) != NULL) {
00443            if (strcmp(e->d_name, "lost+found")) {
00444               cString buffer = AddDirectory(DirName, e->d_name);
00445               struct stat st;
00446               if (stat(buffer, &st) == 0) {
00447                  if (S_ISDIR(st.st_mode)) {
00448                     if (!RemoveEmptyDirectories(buffer, true))
00449                        empty = false;
00450                     }
00451                  else
00452                     empty = false;
00453                  }
00454               else {
00455                  LOG_ERROR_STR(*buffer);
00456                  empty = false;
00457                  }
00458               }
00459            }
00460      if (RemoveThis && empty) {
00461         dsyslog("removing %s", DirName);
00462         if (remove(DirName) < 0) {
00463            LOG_ERROR_STR(DirName);
00464            return false;
00465            }
00466         }
00467      return empty;
00468      }
00469   else
00470      LOG_ERROR_STR(DirName);
00471   return false;
00472 }
00473 
00474 int DirSizeMB(const char *DirName)
00475 {
00476   cReadDir d(DirName);
00477   if (d.Ok()) {
00478      int size = 0;
00479      struct dirent *e;
00480      while (size >= 0 && (e = d.Next()) != NULL) {
00481            cString buffer = AddDirectory(DirName, e->d_name);
00482            struct stat st;
00483            if (stat(buffer, &st) == 0) {
00484               if (S_ISDIR(st.st_mode)) {
00485                  int n = DirSizeMB(buffer);
00486                  if (n >= 0)
00487                     size += n;
00488                  else
00489                     size = -1;
00490                  }
00491               else
00492                  size += st.st_size / MEGABYTE(1);
00493               }
00494            else {
00495               LOG_ERROR_STR(*buffer);
00496               size = -1;
00497               }
00498            }
00499      return size;
00500      }
00501   else
00502      LOG_ERROR_STR(DirName);
00503   return -1;
00504 }
00505 
00506 char *ReadLink(const char *FileName)
00507 {
00508   if (!FileName)
00509      return NULL;
00510   char *TargetName = canonicalize_file_name(FileName);
00511   if (!TargetName) {
00512      if (errno == ENOENT) // file doesn't exist
00513         TargetName = strdup(FileName);
00514      else // some other error occurred
00515         LOG_ERROR_STR(FileName);
00516      }
00517   return TargetName;
00518 }
00519 
00520 bool SpinUpDisk(const char *FileName)
00521 {
00522   for (int n = 0; n < 10; n++) {
00523       cString buf;
00524       if (DirectoryOk(FileName))
00525          buf = cString::sprintf("%s/vdr-%06d", *FileName ? FileName : ".", n);
00526       else
00527          buf = cString::sprintf("%s.vdr-%06d", FileName, n);
00528       if (access(buf, F_OK) != 0) { // the file does not exist
00529          timeval tp1, tp2;
00530          gettimeofday(&tp1, NULL);
00531          int f = open(buf, O_WRONLY | O_CREAT, DEFFILEMODE);
00532          // O_SYNC doesn't work on all file systems
00533          if (f >= 0) {
00534             if (fdatasync(f) < 0)
00535                LOG_ERROR_STR(*buf);
00536             close(f);
00537             remove(buf);
00538             gettimeofday(&tp2, NULL);
00539             double seconds = (((long long)tp2.tv_sec * 1000000 + tp2.tv_usec) - ((long long)tp1.tv_sec * 1000000 + tp1.tv_usec)) / 1000000.0;
00540             if (seconds > 0.5)
00541                dsyslog("SpinUpDisk took %.2f seconds", seconds);
00542             return true;
00543             }
00544          else
00545             LOG_ERROR_STR(*buf);
00546          }
00547       }
00548   esyslog("ERROR: SpinUpDisk failed");
00549   return false;
00550 }
00551 
00552 void TouchFile(const char *FileName)
00553 {
00554   if (utime(FileName, NULL) == -1 && errno != ENOENT)
00555      LOG_ERROR_STR(FileName);
00556 }
00557 
00558 time_t LastModifiedTime(const char *FileName)
00559 {
00560   struct stat fs;
00561   if (stat(FileName, &fs) == 0)
00562      return fs.st_mtime;
00563   return 0;
00564 }
00565 
00566 off_t FileSize(const char *FileName)
00567 {
00568   struct stat fs;
00569   if (stat(FileName, &fs) == 0)
00570      return fs.st_size;
00571   return -1;
00572 }
00573 
00574 // --- cTimeMs ---------------------------------------------------------------
00575 
00576 cTimeMs::cTimeMs(int Ms)
00577 {
00578   if (Ms >= 0)
00579      Set(Ms);
00580   else
00581      begin = 0;
00582 }
00583 
00584 uint64_t cTimeMs::Now(void)
00585 {
00586 #if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK)
00587 #define MIN_RESOLUTION 5 // ms
00588   static bool initialized = false;
00589   static bool monotonic = false;
00590   struct timespec tp;
00591   if (!initialized) {
00592      // check if monotonic timer is available and provides enough accurate resolution:
00593      if (clock_getres(CLOCK_MONOTONIC, &tp) == 0) {
00594         long Resolution = tp.tv_nsec;
00595         // require a minimum resolution:
00596         if (tp.tv_sec == 0 && tp.tv_nsec <= MIN_RESOLUTION * 1000000) {
00597            if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
00598               dsyslog("cTimeMs: using monotonic clock (resolution is %ld ns)", Resolution);
00599               monotonic = true;
00600               }
00601            else
00602               esyslog("cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
00603            }
00604         else
00605            dsyslog("cTimeMs: not using monotonic clock - resolution is too bad (%ld s %ld ns)", tp.tv_sec, tp.tv_nsec);
00606         }
00607      else
00608         esyslog("cTimeMs: clock_getres(CLOCK_MONOTONIC) failed");
00609      initialized = true;
00610      }
00611   if (monotonic) {
00612      if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
00613         return (uint64_t(tp.tv_sec)) * 1000 + tp.tv_nsec / 1000000;
00614      esyslog("cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
00615      monotonic = false;
00616      // fall back to gettimeofday()
00617      }
00618 #else
00619 #  warning Posix monotonic clock not available
00620 #endif
00621   struct timeval t;
00622   if (gettimeofday(&t, NULL) == 0)
00623      return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000;
00624   return 0;
00625 }
00626 
00627 void cTimeMs::Set(int Ms)
00628 {
00629   begin = Now() + Ms;
00630 }
00631 
00632 bool cTimeMs::TimedOut(void)
00633 {
00634   return Now() >= begin;
00635 }
00636 
00637 uint64_t cTimeMs::Elapsed(void)
00638 {
00639   return Now() - begin;
00640 }
00641 
00642 // --- UTF-8 support ---------------------------------------------------------
00643 
00644 static uint SystemToUtf8[128] = { 0 };
00645 
00646 int Utf8CharLen(const char *s)
00647 {
00648   if (cCharSetConv::SystemCharacterTable())
00649      return 1;
00650 #define MT(s, m, v) ((*(s) & (m)) == (v)) // Mask Test
00651   if (MT(s, 0xE0, 0xC0) && MT(s + 1, 0xC0, 0x80))
00652      return 2;
00653   if (MT(s, 0xF0, 0xE0) && MT(s + 1, 0xC0, 0x80) && MT(s + 2, 0xC0, 0x80))
00654      return 3;
00655   if (MT(s, 0xF8, 0xF0) && MT(s + 1, 0xC0, 0x80) && MT(s + 2, 0xC0, 0x80) && MT(s + 3, 0xC0, 0x80))
00656      return 4;
00657   return 1;
00658 }
00659 
00660 uint Utf8CharGet(const char *s, int Length)
00661 {
00662   if (cCharSetConv::SystemCharacterTable())
00663      return (uchar)*s < 128 ? *s : SystemToUtf8[(uchar)*s - 128];
00664   if (!Length)
00665      Length = Utf8CharLen(s);
00666   switch (Length) {
00667     case 2: return ((*s & 0x1F) <<  6) |  (*(s + 1) & 0x3F);
00668     case 3: return ((*s & 0x0F) << 12) | ((*(s + 1) & 0x3F) <<  6) |  (*(s + 2) & 0x3F);
00669     case 4: return ((*s & 0x07) << 18) | ((*(s + 1) & 0x3F) << 12) | ((*(s + 2) & 0x3F) << 6) | (*(s + 3) & 0x3F);
00670     default: ;
00671     }
00672   return *s;
00673 }
00674 
00675 int Utf8CharSet(uint c, char *s)
00676 {
00677   if (c < 0x80 || cCharSetConv::SystemCharacterTable()) {
00678      if (s)
00679         *s = c;
00680      return 1;
00681      }
00682   if (c < 0x800) {
00683      if (s) {
00684         *s++ = ((c >> 6) & 0x1F) | 0xC0;
00685         *s   = (c & 0x3F) | 0x80;
00686         }
00687      return 2;
00688      }
00689   if (c < 0x10000) {
00690      if (s) {
00691         *s++ = ((c >> 12) & 0x0F) | 0xE0;
00692         *s++ = ((c >>  6) & 0x3F) | 0x80;
00693         *s   = (c & 0x3F) | 0x80;
00694         }
00695      return 3;
00696      }
00697   if (c < 0x110000) {
00698      if (s) {
00699         *s++ = ((c >> 18) & 0x07) | 0xF0;
00700         *s++ = ((c >> 12) & 0x3F) | 0x80;
00701         *s++ = ((c >>  6) & 0x3F) | 0x80;
00702         *s   = (c & 0x3F) | 0x80;
00703         }
00704      return 4;
00705      }
00706   return 0; // can't convert to UTF-8
00707 }
00708 
00709 int Utf8SymChars(const char *s, int Symbols)
00710 {
00711   if (cCharSetConv::SystemCharacterTable())
00712      return Symbols;
00713   int n = 0;
00714   while (*s && Symbols--) {
00715         int sl = Utf8CharLen(s);
00716         s += sl;
00717         n += sl;
00718         }
00719   return n;
00720 }
00721 
00722 int Utf8StrLen(const char *s)
00723 {
00724   if (cCharSetConv::SystemCharacterTable())
00725      return strlen(s);
00726   int n = 0;
00727   while (*s) {
00728         s += Utf8CharLen(s);
00729         n++;
00730         }
00731   return n;
00732 }
00733 
00734 char *Utf8Strn0Cpy(char *Dest, const char *Src, int n)
00735 {
00736   if (cCharSetConv::SystemCharacterTable())
00737      return strn0cpy(Dest, Src, n);
00738   char *d = Dest;
00739   while (*Src) {
00740         int sl = Utf8CharLen(Src);
00741         n -= sl;
00742         if (n > 0) {
00743            while (sl--)
00744                  *d++ = *Src++;
00745            }
00746         else
00747            break;
00748         }
00749   *d = 0;
00750   return Dest;
00751 }
00752 
00753 int Utf8ToArray(const char *s, uint *a, int Size)
00754 {
00755   int n = 0;
00756   while (*s && --Size > 0) {
00757         if (cCharSetConv::SystemCharacterTable())
00758            *a++ = (uchar)(*s++);
00759         else {
00760            int sl = Utf8CharLen(s);
00761            *a++ = Utf8CharGet(s, sl);
00762            s += sl;
00763            }
00764         n++;
00765         }
00766   if (Size > 0)
00767      *a = 0;
00768   return n;
00769 }
00770 
00771 int Utf8FromArray(const uint *a, char *s, int Size, int Max)
00772 {
00773   int NumChars = 0;
00774   int NumSyms = 0;
00775   while (*a && NumChars < Size) {
00776         if (Max >= 0 && NumSyms++ >= Max)
00777            break;
00778         if (cCharSetConv::SystemCharacterTable()) {
00779            *s++ = *a++;
00780            NumChars++;
00781            }
00782         else {
00783            int sl = Utf8CharSet(*a);
00784            if (NumChars + sl <= Size) {
00785               Utf8CharSet(*a, s);
00786               a++;
00787               s += sl;
00788               NumChars += sl;
00789               }
00790            else
00791               break;
00792            }
00793         }
00794   if (NumChars < Size)
00795      *s = 0;
00796   return NumChars;
00797 }
00798 
00799 // --- cCharSetConv ----------------------------------------------------------
00800 
00801 char *cCharSetConv::systemCharacterTable = NULL;
00802 
00803 cCharSetConv::cCharSetConv(const char *FromCode, const char *ToCode)
00804 {
00805   if (!FromCode)
00806      FromCode = systemCharacterTable ? systemCharacterTable : "UTF-8";
00807   if (!ToCode)
00808      ToCode = "UTF-8";
00809   cd = iconv_open(ToCode, FromCode);
00810   result = NULL;
00811   length = 0;
00812 }
00813 
00814 cCharSetConv::~cCharSetConv()
00815 {
00816   free(result);
00817   iconv_close(cd);
00818 }
00819 
00820 void cCharSetConv::SetSystemCharacterTable(const char *CharacterTable)
00821 {
00822   free(systemCharacterTable);
00823   systemCharacterTable = NULL;
00824   if (!strcasestr(CharacterTable, "UTF-8")) {
00825      // Set up a map for the character values 128...255:
00826      char buf[129];
00827      for (int i = 0; i < 128; i++)
00828          buf[i] = i + 128;
00829      buf[128] = 0;
00830      cCharSetConv csc(CharacterTable);
00831      const char *s = csc.Convert(buf);
00832      int i = 0;
00833      while (*s) {
00834            int sl = Utf8CharLen(s);
00835            SystemToUtf8[i] = Utf8CharGet(s, sl);
00836            s += sl;
00837            i++;
00838            }
00839      systemCharacterTable = strdup(CharacterTable);
00840      }
00841 }
00842 
00843 const char *cCharSetConv::Convert(const char *From, char *To, size_t ToLength)
00844 {
00845   if (cd != (iconv_t)-1 && From && *From) {
00846      char *FromPtr = (char *)From;
00847      size_t FromLength = strlen(From);
00848      char *ToPtr = To;
00849      if (!ToPtr) {
00850         int NewLength = max(length, FromLength * 2); // some reserve to avoid later reallocations
00851         if (char *NewBuffer = (char *)realloc(result, NewLength)) {
00852            length = NewLength;
00853            result = NewBuffer;
00854            }
00855         else {
00856            esyslog("ERROR: out of memory");
00857            return From;
00858            }
00859         ToPtr = result;
00860         ToLength = length;
00861         }
00862      else if (!ToLength)
00863         return From; // can't convert into a zero sized buffer
00864      ToLength--; // save space for terminating 0
00865      char *Converted = ToPtr;
00866      while (FromLength > 0) {
00867            if (iconv(cd, &FromPtr, &FromLength, &ToPtr, &ToLength) == size_t(-1)) {
00868               if (errno == E2BIG || errno == EILSEQ && ToLength < 1) {
00869                  if (To)
00870                     break; // caller provided a fixed size buffer, but it was too small
00871                  // The result buffer is too small, so increase it:
00872                  size_t d = ToPtr - result;
00873                  size_t r = length / 2;
00874                  int NewLength = length + r;
00875                  if (char *NewBuffer = (char *)realloc(result, NewLength)) {
00876                     length = NewLength;
00877                     Converted = result = NewBuffer;
00878                     }
00879                  else {
00880                     esyslog("ERROR: out of memory");
00881                     return From;
00882                     }
00883                  ToLength += r;
00884                  ToPtr = result + d;
00885                  }
00886               if (errno == EILSEQ) {
00887                  // A character can't be converted, so mark it with '?' and proceed:
00888                  FromPtr++;
00889                  FromLength--;
00890                  *ToPtr++ = '?';
00891                  ToLength--;
00892                  }
00893               else if (errno != E2BIG)
00894                  return From; // unknown error, return original string
00895               }
00896            }
00897      *ToPtr = 0;
00898      return Converted;
00899      }
00900   return From;
00901 }
00902 
00903 // --- cString ---------------------------------------------------------------
00904 
00905 cString::cString(const char *S, bool TakePointer)
00906 {
00907   s = TakePointer ? (char *)S : S ? strdup(S) : NULL;
00908 }
00909 
00910 cString::cString(const cString &String)
00911 {
00912   s = String.s ? strdup(String.s) : NULL;
00913 }
00914 
00915 cString::~cString()
00916 {
00917   free(s);
00918 }
00919 
00920 cString &cString::operator=(const cString &String)
00921 {
00922   if (this == &String)
00923      return *this;
00924   free(s);
00925   s = String.s ? strdup(String.s) : NULL;
00926   return *this;
00927 }
00928 
00929 cString &cString::operator=(const char *String)
00930 {
00931   if (s == String)
00932     return *this;
00933   free(s);
00934   s = String ? strdup(String) : NULL;
00935   return *this;
00936 }
00937 
00938 cString &cString::Truncate(int Index)
00939 {
00940   int l = strlen(s);
00941   if (Index < 0)
00942      Index = l + Index;
00943   if (Index >= 0 && Index < l)
00944      s[Index] = 0;
00945   return *this;
00946 }
00947 
00948 cString cString::sprintf(const char *fmt, ...)
00949 {
00950   va_list ap;
00951   va_start(ap, fmt);
00952   char *buffer;
00953   if (!fmt || vasprintf(&buffer, fmt, ap) < 0) {
00954      esyslog("error in vasprintf('%s', ...)", fmt);
00955      buffer = strdup("???");
00956      }
00957   va_end(ap);
00958   return cString(buffer, true);
00959 }
00960 
00961 cString cString::sprintf(const char *fmt, va_list &ap)
00962 {
00963   char *buffer;
00964   if (!fmt || vasprintf(&buffer, fmt, ap) < 0) {
00965      esyslog("error in vasprintf('%s', ...)", fmt);
00966      buffer = strdup("???");
00967      }
00968   return cString(buffer, true);
00969 }
00970 
00971 cString WeekDayName(int WeekDay)
00972 {
00973   char buffer[16];
00974   WeekDay = WeekDay == 0 ? 6 : WeekDay - 1; // we start with Monday==0!
00975   if (0 <= WeekDay && WeekDay <= 6) {
00976      // TRANSLATORS: abbreviated weekdays, beginning with monday (must all be 3 letters!)
00977      const char *day = tr("MonTueWedThuFriSatSun");
00978      day += Utf8SymChars(day, WeekDay * 3);
00979      strn0cpy(buffer, day, min(Utf8SymChars(day, 3) + 1, int(sizeof(buffer))));
00980      return buffer;
00981      }
00982   else
00983      return "???";
00984 }
00985 
00986 cString WeekDayName(time_t t)
00987 {
00988   struct tm tm_r;
00989   return WeekDayName(localtime_r(&t, &tm_r)->tm_wday);
00990 }
00991 
00992 cString WeekDayNameFull(int WeekDay)
00993 {
00994   WeekDay = WeekDay == 0 ? 6 : WeekDay - 1; // we start with Monday==0!
00995   switch (WeekDay) {
00996     case 0: return tr("Monday");
00997     case 1: return tr("Tuesday");
00998     case 2: return tr("Wednesday");
00999     case 3: return tr("Thursday");
01000     case 4: return tr("Friday");
01001     case 5: return tr("Saturday");
01002     case 6: return tr("Sunday");
01003     default: return "???";
01004     }
01005 }
01006 
01007 cString WeekDayNameFull(time_t t)
01008 {
01009   struct tm tm_r;
01010   return WeekDayNameFull(localtime_r(&t, &tm_r)->tm_wday);
01011 }
01012 
01013 cString DayDateTime(time_t t)
01014 {
01015   char buffer[32];
01016   if (t == 0)
01017      time(&t);
01018   struct tm tm_r;
01019   tm *tm = localtime_r(&t, &tm_r);
01020   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);
01021   return buffer;
01022 }
01023 
01024 cString TimeToString(time_t t)
01025 {
01026   char buffer[32];
01027   if (ctime_r(&t, buffer)) {
01028      buffer[strlen(buffer) - 1] = 0; // strip trailing newline
01029      return buffer;
01030      }
01031   return "???";
01032 }
01033 
01034 cString DateString(time_t t)
01035 {
01036   char buf[32];
01037   struct tm tm_r;
01038   tm *tm = localtime_r(&t, &tm_r);
01039   char *p = stpcpy(buf, WeekDayName(tm->tm_wday));
01040   *p++ = ' ';
01041   strftime(p, sizeof(buf) - (p - buf), "%d.%m.%Y", tm);
01042   return buf;
01043 }
01044 
01045 cString TimeString(time_t t)
01046 {
01047   char buf[25];
01048   struct tm tm_r;
01049   strftime(buf, sizeof(buf), "%R", localtime_r(&t, &tm_r));
01050   return buf;
01051 }
01052 
01053 // --- RgbToJpeg -------------------------------------------------------------
01054 
01055 #define JPEGCOMPRESSMEM 500000
01056 
01057 struct tJpegCompressData {
01058   int size;
01059   uchar *mem;
01060   };
01061 
01062 static void JpegCompressInitDestination(j_compress_ptr cinfo)
01063 {
01064   tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data;
01065   if (jcd) {
01066      cinfo->dest->free_in_buffer = jcd->size = JPEGCOMPRESSMEM;
01067      cinfo->dest->next_output_byte = jcd->mem = MALLOC(uchar, jcd->size);
01068      }
01069 }
01070 
01071 static boolean JpegCompressEmptyOutputBuffer(j_compress_ptr cinfo)
01072 {
01073   tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data;
01074   if (jcd) {
01075      int Used = jcd->size;
01076      int NewSize = jcd->size + JPEGCOMPRESSMEM;
01077      if (uchar *NewBuffer = (uchar *)realloc(jcd->mem, NewSize)) {
01078         jcd->size = NewSize;
01079         jcd->mem = NewBuffer;
01080         }
01081      else {
01082         esyslog("ERROR: out of memory");
01083         return false;
01084         }
01085      if (jcd->mem) {
01086         cinfo->dest->next_output_byte = jcd->mem + Used;
01087         cinfo->dest->free_in_buffer = jcd->size - Used;
01088         return true;
01089         }
01090      }
01091   return false;
01092 }
01093 
01094 static void JpegCompressTermDestination(j_compress_ptr cinfo)
01095 {
01096   tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data;
01097   if (jcd) {
01098      int Used = cinfo->dest->next_output_byte - jcd->mem;
01099      if (Used < jcd->size) {
01100         if (uchar *NewBuffer = (uchar *)realloc(jcd->mem, Used)) {
01101            jcd->size = Used;
01102            jcd->mem = NewBuffer;
01103            }
01104         else
01105            esyslog("ERROR: out of memory");
01106         }
01107      }
01108 }
01109 
01110 uchar *RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality)
01111 {
01112   if (Quality < 0)
01113      Quality = 0;
01114   else if (Quality > 100)
01115      Quality = 100;
01116 
01117   jpeg_destination_mgr jdm;
01118 
01119   jdm.init_destination = JpegCompressInitDestination;
01120   jdm.empty_output_buffer = JpegCompressEmptyOutputBuffer;
01121   jdm.term_destination = JpegCompressTermDestination;
01122 
01123   struct jpeg_compress_struct cinfo;
01124   struct jpeg_error_mgr jerr;
01125   cinfo.err = jpeg_std_error(&jerr);
01126   jpeg_create_compress(&cinfo);
01127   cinfo.dest = &jdm;
01128   tJpegCompressData jcd;
01129   cinfo.client_data = &jcd;
01130   cinfo.image_width = Width;
01131   cinfo.image_height = Height;
01132   cinfo.input_components = 3;
01133   cinfo.in_color_space = JCS_RGB;
01134 
01135   jpeg_set_defaults(&cinfo);
01136   jpeg_set_quality(&cinfo, Quality, true);
01137   jpeg_start_compress(&cinfo, true);
01138 
01139   int rs = Width * 3;
01140   JSAMPROW rp[Height];
01141   for (int k = 0; k < Height; k++)
01142       rp[k] = &Mem[rs * k];
01143   jpeg_write_scanlines(&cinfo, rp, Height);
01144   jpeg_finish_compress(&cinfo);
01145   jpeg_destroy_compress(&cinfo);
01146 
01147   Size = jcd.size;
01148   return jcd.mem;
01149 }
01150 
01151 // --- cBase64Encoder --------------------------------------------------------
01152 
01153 const char *cBase64Encoder::b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
01154 
01155 cBase64Encoder::cBase64Encoder(const uchar *Data, int Length, int MaxResult)
01156 {
01157   data = Data;
01158   length = Length;
01159   maxResult = MaxResult;
01160   i = 0;
01161   result = MALLOC(char, maxResult + 1);
01162 }
01163 
01164 cBase64Encoder::~cBase64Encoder()
01165 {
01166   free(result);
01167 }
01168 
01169 const char *cBase64Encoder::NextLine(void)
01170 {
01171   int r = 0;
01172   while (i < length && r < maxResult - 3) {
01173         result[r++] = b64[(data[i] >> 2) & 0x3F];
01174         uchar c = (data[i] << 4) & 0x3F;
01175         if (++i < length)
01176            c |= (data[i] >> 4) & 0x0F;
01177         result[r++] = b64[c];
01178         if (i < length) {
01179            c = (data[i] << 2) & 0x3F;
01180            if (++i < length)
01181               c |= (data[i] >> 6) & 0x03;
01182            result[r++] = b64[c];
01183            }
01184         else {
01185            i++;
01186            result[r++] = '=';
01187            }
01188         if (i < length) {
01189            c = data[i] & 0x3F;
01190            result[r++] = b64[c];
01191            }
01192         else
01193            result[r++] = '=';
01194         i++;
01195         }
01196   if (r > 0) {
01197      result[r] = 0;
01198      return result;
01199      }
01200   return NULL;
01201 }
01202 
01203 // --- cBitStream ------------------------------------------------------------
01204 
01205 int cBitStream::GetBit(void)
01206 {
01207   if (index >= length)
01208      return 1;
01209   int r = (data[index >> 3] >> (7 - (index & 7))) & 1;
01210   ++index;
01211   return r;
01212 }
01213 
01214 uint32_t cBitStream::GetBits(int n)
01215 {
01216   uint32_t r = 0;
01217   while (n--)
01218         r |= GetBit() << n;
01219   return r;
01220 }
01221 
01222 void cBitStream::ByteAlign(void)
01223 {
01224   int n = index % 8;
01225   if (n > 0)
01226      SkipBits(8 - n);
01227 }
01228 
01229 void cBitStream::WordAlign(void)
01230 {
01231   int n = index % 16;
01232   if (n > 0)
01233      SkipBits(16 - n);
01234 }
01235 
01236 bool cBitStream::SetLength(int Length)
01237 {
01238   if (Length > length)
01239      return false;
01240   length = Length;
01241   return true;
01242 }
01243 
01244 // --- cReadLine -------------------------------------------------------------
01245 
01246 cReadLine::cReadLine(void)
01247 {
01248   size = 0;
01249   buffer = NULL;
01250 }
01251 
01252 cReadLine::~cReadLine()
01253 {
01254   free(buffer);
01255 }
01256 
01257 char *cReadLine::Read(FILE *f)
01258 {
01259   int n = getline(&buffer, &size, f);
01260   if (n > 0) {
01261      n--;
01262      if (buffer[n] == '\n') {
01263         buffer[n] = 0;
01264         if (n > 0) {
01265            n--;
01266            if (buffer[n] == '\r')
01267               buffer[n] = 0;
01268            }
01269         }
01270      return buffer;
01271      }
01272   return NULL;
01273 }
01274 
01275 // --- cPoller ---------------------------------------------------------------
01276 
01277 cPoller::cPoller(int FileHandle, bool Out)
01278 {
01279   numFileHandles = 0;
01280   Add(FileHandle, Out);
01281 }
01282 
01283 bool cPoller::Add(int FileHandle, bool Out)
01284 {
01285   if (FileHandle >= 0) {
01286      for (int i = 0; i < numFileHandles; i++) {
01287          if (pfd[i].fd == FileHandle && pfd[i].events == (Out ? POLLOUT : POLLIN))
01288             return true;
01289          }
01290      if (numFileHandles < MaxPollFiles) {
01291         pfd[numFileHandles].fd = FileHandle;
01292         pfd[numFileHandles].events = Out ? POLLOUT : POLLIN;
01293         pfd[numFileHandles].revents = 0;
01294         numFileHandles++;
01295         return true;
01296         }
01297      esyslog("ERROR: too many file handles in cPoller");
01298      }
01299   return false;
01300 }
01301 
01302 bool cPoller::Poll(int TimeoutMs)
01303 {
01304   if (numFileHandles) {
01305      if (poll(pfd, numFileHandles, TimeoutMs) != 0)
01306         return true; // returns true even in case of an error, to let the caller
01307                      // access the file and thus see the error code
01308      }
01309   return false;
01310 }
01311 
01312 // --- cReadDir --------------------------------------------------------------
01313 
01314 cReadDir::cReadDir(const char *Directory)
01315 {
01316   directory = opendir(Directory);
01317 }
01318 
01319 cReadDir::~cReadDir()
01320 {
01321   if (directory)
01322      closedir(directory);
01323 }
01324 
01325 struct dirent *cReadDir::Next(void)
01326 {
01327   if (directory) {
01328      while (readdir_r(directory, &u.d, &result) == 0 && result) {
01329            if (strcmp(result->d_name, ".") && strcmp(result->d_name, ".."))
01330               return result;
01331            }
01332      }
01333   return NULL;
01334 }
01335 
01336 // --- cStringList -----------------------------------------------------------
01337 
01338 cStringList::~cStringList()
01339 {
01340   Clear();
01341 }
01342 
01343 int cStringList::Find(const char *s) const
01344 {
01345   for (int i = 0; i < Size(); i++) {
01346       if (!strcmp(s, At(i)))
01347          return i;
01348       }
01349   return -1;
01350 }
01351 
01352 void cStringList::Clear(void)
01353 {
01354   for (int i = 0; i < Size(); i++)
01355       free(At(i));
01356   cVector<char *>::Clear();
01357 }
01358 
01359 // --- cFileNameList ---------------------------------------------------------
01360 
01361 // TODO better GetFileNames(const char *Directory, cStringList *List)?
01362 cFileNameList::cFileNameList(const char *Directory, bool DirsOnly)
01363 {
01364   Load(Directory, DirsOnly);
01365 }
01366 
01367 bool cFileNameList::Load(const char *Directory, bool DirsOnly)
01368 {
01369   Clear();
01370   if (Directory) {
01371      cReadDir d(Directory);
01372      struct dirent *e;
01373      if (d.Ok()) {
01374         while ((e = d.Next()) != NULL) {
01375               if (DirsOnly) {
01376                  struct stat ds;
01377                  if (stat(AddDirectory(Directory, e->d_name), &ds) == 0) {
01378                     if (!S_ISDIR(ds.st_mode))
01379                        continue;
01380                     }
01381                  }
01382               Append(strdup(e->d_name));
01383               }
01384         Sort();
01385         return true;
01386         }
01387      else
01388         LOG_ERROR_STR(Directory);
01389      }
01390   return false;
01391 }
01392 
01393 // --- cFile -----------------------------------------------------------------
01394 
01395 bool cFile::files[FD_SETSIZE] = { false };
01396 int cFile::maxFiles = 0;
01397 
01398 cFile::cFile(void)
01399 {
01400   f = -1;
01401 }
01402 
01403 cFile::~cFile()
01404 {
01405   Close();
01406 }
01407 
01408 bool cFile::Open(const char *FileName, int Flags, mode_t Mode)
01409 {
01410   if (!IsOpen())
01411      return Open(open(FileName, Flags, Mode));
01412   esyslog("ERROR: attempt to re-open %s", FileName);
01413   return false;
01414 }
01415 
01416 bool cFile::Open(int FileDes)
01417 {
01418   if (FileDes >= 0) {
01419      if (!IsOpen()) {
01420         f = FileDes;
01421         if (f >= 0) {
01422            if (f < FD_SETSIZE) {
01423               if (f >= maxFiles)
01424                  maxFiles = f + 1;
01425               if (!files[f])
01426                  files[f] = true;
01427               else
01428                  esyslog("ERROR: file descriptor %d already in files[]", f);
01429               return true;
01430               }
01431            else
01432               esyslog("ERROR: file descriptor %d is larger than FD_SETSIZE (%d)", f, FD_SETSIZE);
01433            }
01434         }
01435      else
01436         esyslog("ERROR: attempt to re-open file descriptor %d", FileDes);
01437      }
01438   return false;
01439 }
01440 
01441 void cFile::Close(void)
01442 {
01443   if (f >= 0) {
01444      close(f);
01445      files[f] = false;
01446      f = -1;
01447      }
01448 }
01449 
01450 bool cFile::Ready(bool Wait)
01451 {
01452   return f >= 0 && AnyFileReady(f, Wait ? 1000 : 0);
01453 }
01454 
01455 bool cFile::AnyFileReady(int FileDes, int TimeoutMs)
01456 {
01457   fd_set set;
01458   FD_ZERO(&set);
01459   for (int i = 0; i < maxFiles; i++) {
01460       if (files[i])
01461          FD_SET(i, &set);
01462       }
01463   if (0 <= FileDes && FileDes < FD_SETSIZE && !files[FileDes])
01464      FD_SET(FileDes, &set); // in case we come in with an arbitrary descriptor
01465   if (TimeoutMs == 0)
01466      TimeoutMs = 10; // load gets too heavy with 0
01467   struct timeval timeout;
01468   timeout.tv_sec  = TimeoutMs / 1000;
01469   timeout.tv_usec = (TimeoutMs % 1000) * 1000;
01470   return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && (FileDes < 0 || FD_ISSET(FileDes, &set));
01471 }
01472 
01473 bool cFile::FileReady(int FileDes, int TimeoutMs)
01474 {
01475   fd_set set;
01476   struct timeval timeout;
01477   FD_ZERO(&set);
01478   FD_SET(FileDes, &set);
01479   if (TimeoutMs >= 0) {
01480      if (TimeoutMs < 100)
01481         TimeoutMs = 100;
01482      timeout.tv_sec  = TimeoutMs / 1000;
01483      timeout.tv_usec = (TimeoutMs % 1000) * 1000;
01484      }
01485   return select(FD_SETSIZE, &set, NULL, NULL, (TimeoutMs >= 0) ? &timeout : NULL) > 0 && FD_ISSET(FileDes, &set);
01486 }
01487 
01488 bool cFile::FileReadyForWriting(int FileDes, int TimeoutMs)
01489 {
01490   fd_set set;
01491   struct timeval timeout;
01492   FD_ZERO(&set);
01493   FD_SET(FileDes, &set);
01494   if (TimeoutMs < 100)
01495      TimeoutMs = 100;
01496   timeout.tv_sec  = 0;
01497   timeout.tv_usec = TimeoutMs * 1000;
01498   return select(FD_SETSIZE, NULL, &set, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
01499 }
01500 
01501 // --- cSafeFile -------------------------------------------------------------
01502 
01503 cSafeFile::cSafeFile(const char *FileName)
01504 {
01505   f = NULL;
01506   fileName = ReadLink(FileName);
01507   tempName = fileName ? MALLOC(char, strlen(fileName) + 5) : NULL;
01508   if (tempName)
01509      strcat(strcpy(tempName, fileName), ".$$$");
01510 }
01511 
01512 cSafeFile::~cSafeFile()
01513 {
01514   if (f)
01515      fclose(f);
01516   unlink(tempName);
01517   free(fileName);
01518   free(tempName);
01519 }
01520 
01521 bool cSafeFile::Open(void)
01522 {
01523   if (!f && fileName && tempName) {
01524      f = fopen(tempName, "w");
01525      if (!f)
01526         LOG_ERROR_STR(tempName);
01527      }
01528   return f != NULL;
01529 }
01530 
01531 bool cSafeFile::Close(void)
01532 {
01533   bool result = true;
01534   if (f) {
01535      if (ferror(f) != 0) {
01536         LOG_ERROR_STR(tempName);
01537         result = false;
01538         }
01539      fflush(f);
01540      fsync(fileno(f));
01541      if (fclose(f) < 0) {
01542         LOG_ERROR_STR(tempName);
01543         result = false;
01544         }
01545      f = NULL;
01546      if (result && rename(tempName, fileName) < 0) {
01547         LOG_ERROR_STR(fileName);
01548         result = false;
01549         }
01550      }
01551   else
01552      result = false;
01553   return result;
01554 }
01555 
01556 // --- cUnbufferedFile -------------------------------------------------------
01557 
01558 #define USE_FADVISE
01559 
01560 #define WRITE_BUFFER KILOBYTE(800)
01561 
01562 cUnbufferedFile::cUnbufferedFile(void)
01563 {
01564   fd = -1;
01565 }
01566 
01567 cUnbufferedFile::~cUnbufferedFile()
01568 {
01569   Close();
01570 }
01571 
01572 int cUnbufferedFile::Open(const char *FileName, int Flags, mode_t Mode)
01573 {
01574   Close();
01575   fd = open(FileName, Flags, Mode);
01576   curpos = 0;
01577 #ifdef USE_FADVISE
01578   begin = lastpos = ahead = 0;
01579   cachedstart = 0;
01580   cachedend = 0;
01581   readahead = KILOBYTE(128);
01582   written = 0;
01583   totwritten = 0;
01584   if (fd >= 0)
01585      posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM); // we could use POSIX_FADV_SEQUENTIAL, but we do our own readahead, disabling the kernel one.
01586 #endif
01587   return fd;
01588 }
01589 
01590 int cUnbufferedFile::Close(void)
01591 {
01592   if (fd >= 0) {
01593 #ifdef USE_FADVISE
01594      if (totwritten)    // if we wrote anything make sure the data has hit the disk before
01595         fdatasync(fd);  // calling fadvise, as this is our last chance to un-cache it.
01596      posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
01597 #endif
01598      int OldFd = fd;
01599      fd = -1;
01600      return close(OldFd);
01601      }
01602   errno = EBADF;
01603   return -1;
01604 }
01605 
01606 // When replaying and going e.g. FF->PLAY the position jumps back 2..8M
01607 // hence we do not want to drop recently accessed data at once.
01608 // We try to handle the common cases such as PLAY->FF->PLAY, small
01609 // jumps, moving editing marks etc.
01610 
01611 #define FADVGRAN   KILOBYTE(4) // AKA fadvise-chunk-size; PAGE_SIZE or getpagesize(2) would also work.
01612 #define READCHUNK  MEGABYTE(8)
01613 
01614 void cUnbufferedFile::SetReadAhead(size_t ra)
01615 {
01616   readahead = ra;
01617 }
01618 
01619 int cUnbufferedFile::FadviseDrop(off_t Offset, off_t Len)
01620 {
01621   // rounding up the window to make sure that not PAGE_SIZE-aligned data gets freed.
01622   return posix_fadvise(fd, Offset - (FADVGRAN - 1), Len + (FADVGRAN - 1) * 2, POSIX_FADV_DONTNEED);
01623 }
01624 
01625 off_t cUnbufferedFile::Seek(off_t Offset, int Whence)
01626 {
01627   if (Whence == SEEK_SET && Offset == curpos)
01628      return curpos;
01629   curpos = lseek(fd, Offset, Whence);
01630   return curpos;
01631 }
01632 
01633 ssize_t cUnbufferedFile::Read(void *Data, size_t Size)
01634 {
01635   if (fd >= 0) {
01636 #ifdef USE_FADVISE
01637      off_t jumped = curpos-lastpos; // nonzero means we're not at the last offset
01638      if ((cachedstart < cachedend) && (curpos < cachedstart || curpos > cachedend)) {
01639         // current position is outside the cached window -- invalidate it.
01640         FadviseDrop(cachedstart, cachedend-cachedstart);
01641         cachedstart = curpos;
01642         cachedend = curpos;
01643         }
01644      cachedstart = min(cachedstart, curpos);
01645 #endif
01646      ssize_t bytesRead = safe_read(fd, Data, Size);
01647      if (bytesRead > 0) {
01648         curpos += bytesRead;
01649 #ifdef USE_FADVISE
01650         cachedend = max(cachedend, curpos);
01651 
01652         // Read ahead:
01653         // no jump? (allow small forward jump still inside readahead window).
01654         if (jumped >= 0 && jumped <= (off_t)readahead) {
01655            // Trigger the readahead IO, but only if we've used at least
01656            // 1/2 of the previously requested area. This avoids calling
01657            // fadvise() after every read() call.
01658            if (ahead - curpos < (off_t)(readahead / 2)) {
01659               posix_fadvise(fd, curpos, readahead, POSIX_FADV_WILLNEED);
01660               ahead = curpos + readahead;
01661               cachedend = max(cachedend, ahead);
01662               }
01663            if (readahead < Size * 32) { // automagically tune readahead size.
01664               readahead = Size * 32;
01665               }
01666            }
01667         else
01668            ahead = curpos; // jumped -> we really don't want any readahead, otherwise e.g. fast-rewind gets in trouble.
01669 #endif
01670         }
01671 #ifdef USE_FADVISE
01672      if (cachedstart < cachedend) {
01673         if (curpos - cachedstart > READCHUNK * 2) {
01674            // current position has moved forward enough, shrink tail window.
01675            FadviseDrop(cachedstart, curpos - READCHUNK - cachedstart);
01676            cachedstart = curpos - READCHUNK;
01677            }
01678         else if (cachedend > ahead && cachedend - curpos > READCHUNK * 2) {
01679            // current position has moved back enough, shrink head window.
01680            FadviseDrop(curpos + READCHUNK, cachedend - (curpos + READCHUNK));
01681            cachedend = curpos + READCHUNK;
01682            }
01683         }
01684      lastpos = curpos;
01685 #endif
01686      return bytesRead;
01687      }
01688   return -1;
01689 }
01690 
01691 ssize_t cUnbufferedFile::Write(const void *Data, size_t Size)
01692 {
01693   if (fd >=0) {
01694      ssize_t bytesWritten = safe_write(fd, Data, Size);
01695 #ifdef USE_FADVISE
01696      if (bytesWritten > 0) {
01697         begin = min(begin, curpos);
01698         curpos += bytesWritten;
01699         written += bytesWritten;
01700         lastpos = max(lastpos, curpos);
01701         if (written > WRITE_BUFFER) {
01702            if (lastpos > begin) {
01703               // Now do three things:
01704               // 1) Start writeback of begin..lastpos range
01705               // 2) Drop the already written range (by the previous fadvise call)
01706               // 3) Handle nonpagealigned data.
01707               //    This is why we double the WRITE_BUFFER; the first time around the
01708               //    last (partial) page might be skipped, writeback will start only after
01709               //    second call; the third call will still include this page and finally
01710               //    drop it from cache.
01711               off_t headdrop = min(begin, off_t(WRITE_BUFFER * 2));
01712               posix_fadvise(fd, begin - headdrop, lastpos - begin + headdrop, POSIX_FADV_DONTNEED);
01713               }
01714            begin = lastpos = curpos;
01715            totwritten += written;
01716            written = 0;
01717            // The above fadvise() works when writing slowly (recording), but could
01718            // leave cached data around when writing at a high rate, e.g. when cutting,
01719            // because by the time we try to flush the cached pages (above) the data
01720            // can still be dirty - we are faster than the disk I/O.
01721            // So we do another round of flushing, just like above, but at larger
01722            // intervals -- this should catch any pages that couldn't be released
01723            // earlier.
01724            if (totwritten > MEGABYTE(32)) {
01725               // It seems in some setups, fadvise() does not trigger any I/O and
01726               // a fdatasync() call would be required do all the work (reiserfs with some
01727               // kind of write gathering enabled), but the syncs cause (io) load..
01728               // Uncomment the next line if you think you need them.
01729               //fdatasync(fd);
01730               off_t headdrop = min(off_t(curpos - totwritten), off_t(totwritten * 2));
01731               posix_fadvise(fd, curpos - totwritten - headdrop, totwritten + headdrop, POSIX_FADV_DONTNEED);
01732               totwritten = 0;
01733               }
01734            }
01735         }
01736 #endif
01737      return bytesWritten;
01738      }
01739   return -1;
01740 }
01741 
01742 cUnbufferedFile *cUnbufferedFile::Create(const char *FileName, int Flags, mode_t Mode)
01743 {
01744   cUnbufferedFile *File = new cUnbufferedFile;
01745   if (File->Open(FileName, Flags, Mode) < 0) {
01746      delete File;
01747      File = NULL;
01748      }
01749   return File;
01750 }
01751 
01752 // --- cLockFile -------------------------------------------------------------
01753 
01754 #define LOCKFILENAME      ".lock-vdr"
01755 #define LOCKFILESTALETIME 600 // seconds before considering a lock file "stale"
01756 
01757 cLockFile::cLockFile(const char *Directory)
01758 {
01759   fileName = NULL;
01760   f = -1;
01761   if (DirectoryOk(Directory))
01762      fileName = strdup(AddDirectory(Directory, LOCKFILENAME));
01763 }
01764 
01765 cLockFile::~cLockFile()
01766 {
01767   Unlock();
01768   free(fileName);
01769 }
01770 
01771 bool cLockFile::Lock(int WaitSeconds)
01772 {
01773   if (f < 0 && fileName) {
01774      time_t Timeout = time(NULL) + WaitSeconds;
01775      do {
01776         f = open(fileName, O_WRONLY | O_CREAT | O_EXCL, DEFFILEMODE);
01777         if (f < 0) {
01778            if (errno == EEXIST) {
01779               struct stat fs;
01780               if (stat(fileName, &fs) == 0) {
01781                  if (abs(time(NULL) - fs.st_mtime) > LOCKFILESTALETIME) {
01782                     esyslog("ERROR: removing stale lock file '%s'", fileName);
01783                     if (remove(fileName) < 0) {
01784                        LOG_ERROR_STR(fileName);
01785                        break;
01786                        }
01787                     continue;
01788                     }
01789                  }
01790               else if (errno != ENOENT) {
01791                  LOG_ERROR_STR(fileName);
01792                  break;
01793                  }
01794               }
01795            else {
01796               LOG_ERROR_STR(fileName);
01797               break;
01798               }
01799            if (WaitSeconds)
01800               cCondWait::SleepMs(1000);
01801            }
01802         } while (f < 0 && time(NULL) < Timeout);
01803      }
01804   return f >= 0;
01805 }
01806 
01807 void cLockFile::Unlock(void)
01808 {
01809   if (f >= 0) {
01810      close(f);
01811      remove(fileName);
01812      f = -1;
01813      }
01814 }
01815 
01816 // --- cListObject -----------------------------------------------------------
01817 
01818 cListObject::cListObject(void)
01819 {
01820   prev = next = NULL;
01821 }
01822 
01823 cListObject::~cListObject()
01824 {
01825 }
01826 
01827 void cListObject::Append(cListObject *Object)
01828 {
01829   next = Object;
01830   Object->prev = this;
01831 }
01832 
01833 void cListObject::Insert(cListObject *Object)
01834 {
01835   prev = Object;
01836   Object->next = this;
01837 }
01838 
01839 void cListObject::Unlink(void)
01840 {
01841   if (next)
01842      next->prev = prev;
01843   if (prev)
01844      prev->next = next;
01845   next = prev = NULL;
01846 }
01847 
01848 int cListObject::Index(void) const
01849 {
01850   cListObject *p = prev;
01851   int i = 0;
01852 
01853   while (p) {
01854         i++;
01855         p = p->prev;
01856         }
01857   return i;
01858 }
01859 
01860 // --- cListBase -------------------------------------------------------------
01861 
01862 cListBase::cListBase(void)
01863 {
01864   objects = lastObject = NULL;
01865   count = 0;
01866 }
01867 
01868 cListBase::~cListBase()
01869 {
01870   Clear();
01871 }
01872 
01873 void cListBase::Add(cListObject *Object, cListObject *After)
01874 {
01875   if (After && After != lastObject) {
01876      After->Next()->Insert(Object);
01877      After->Append(Object);
01878      }
01879   else {
01880      if (lastObject)
01881         lastObject->Append(Object);
01882      else
01883         objects = Object;
01884      lastObject = Object;
01885      }
01886   count++;
01887 }
01888 
01889 void cListBase::Ins(cListObject *Object, cListObject *Before)
01890 {
01891   if (Before && Before != objects) {
01892      Before->Prev()->Append(Object);
01893      Before->Insert(Object);
01894      }
01895   else {
01896      if (objects)
01897         objects->Insert(Object);
01898      else
01899         lastObject = Object;
01900      objects = Object;
01901      }
01902   count++;
01903 }
01904 
01905 void cListBase::Del(cListObject *Object, bool DeleteObject)
01906 {
01907   if (Object == objects)
01908      objects = Object->Next();
01909   if (Object == lastObject)
01910      lastObject = Object->Prev();
01911   Object->Unlink();
01912   if (DeleteObject)
01913      delete Object;
01914   count--;
01915 }
01916 
01917 void cListBase::Move(int From, int To)
01918 {
01919   Move(Get(From), Get(To));
01920 }
01921 
01922 void cListBase::Move(cListObject *From, cListObject *To)
01923 {
01924   if (From && To && From != To) {
01925      if (From->Index() < To->Index())
01926         To = To->Next();
01927      if (From == objects)
01928         objects = From->Next();
01929      if (From == lastObject)
01930         lastObject = From->Prev();
01931      From->Unlink();
01932      if (To) {
01933         if (To->Prev())
01934            To->Prev()->Append(From);
01935         From->Append(To);
01936         }
01937      else {
01938         lastObject->Append(From);
01939         lastObject = From;
01940         }
01941      if (!From->Prev())
01942         objects = From;
01943      }
01944 }
01945 
01946 void cListBase::Clear(void)
01947 {
01948   while (objects) {
01949         cListObject *object = objects->Next();
01950         delete objects;
01951         objects = object;
01952         }
01953   objects = lastObject = NULL;
01954   count = 0;
01955 }
01956 
01957 cListObject *cListBase::Get(int Index) const
01958 {
01959   if (Index < 0)
01960      return NULL;
01961   cListObject *object = objects;
01962   while (object && Index-- > 0)
01963         object = object->Next();
01964   return object;
01965 }
01966 
01967 static int CompareListObjects(const void *a, const void *b)
01968 {
01969   const cListObject *la = *(const cListObject **)a;
01970   const cListObject *lb = *(const cListObject **)b;
01971   return la->Compare(*lb);
01972 }
01973 
01974 void cListBase::Sort(void)
01975 {
01976   int n = Count();
01977   cListObject *a[n];
01978   cListObject *object = objects;
01979   int i = 0;
01980   while (object && i < n) {
01981         a[i++] = object;
01982         object = object->Next();
01983         }
01984   qsort(a, n, sizeof(cListObject *), CompareListObjects);
01985   objects = lastObject = NULL;
01986   for (i = 0; i < n; i++) {
01987       a[i]->Unlink();
01988       count--;
01989       Add(a[i]);
01990       }
01991 }
01992 
01993 // --- cHashBase -------------------------------------------------------------
01994 
01995 cHashBase::cHashBase(int Size)
01996 {
01997   size = Size;
01998   hashTable = (cList<cHashObject>**)calloc(size, sizeof(cList<cHashObject>*));
01999 }
02000 
02001 cHashBase::~cHashBase(void)
02002 {
02003   Clear();
02004   free(hashTable);
02005 }
02006 
02007 void cHashBase::Add(cListObject *Object, unsigned int Id)
02008 {
02009   unsigned int hash = hashfn(Id);
02010   if (!hashTable[hash])
02011      hashTable[hash] = new cList<cHashObject>;
02012   hashTable[hash]->Add(new cHashObject(Object, Id));
02013 }
02014 
02015 void cHashBase::Del(cListObject *Object, unsigned int Id)
02016 {
02017   cList<cHashObject> *list = hashTable[hashfn(Id)];
02018   if (list) {
02019      for (cHashObject *hob = list->First(); hob; hob = list->Next(hob)) {
02020          if (hob->object == Object) {
02021             list->Del(hob);
02022             break;
02023             }
02024          }
02025      }
02026 }
02027 
02028 void cHashBase::Clear(void)
02029 {
02030   for (int i = 0; i < size; i++) {
02031       delete hashTable[i];
02032       hashTable[i] = NULL;
02033       }
02034 }
02035 
02036 cListObject *cHashBase::Get(unsigned int Id) const
02037 {
02038   cList<cHashObject> *list = hashTable[hashfn(Id)];
02039   if (list) {
02040      for (cHashObject *hob = list->First(); hob; hob = list->Next(hob)) {
02041          if (hob->id == Id)
02042             return hob->object;
02043          }
02044      }
02045   return NULL;
02046 }
02047 
02048 cList<cHashObject> *cHashBase::GetList(unsigned int Id) const
02049 {
02050   return hashTable[hashfn(Id)];
02051 }