vdr-1.4.7/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 1.121 2006/12/02 11:12:59 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 <stdarg.h>
00022 #include <stdlib.h>
00023 #include <sys/time.h>
00024 #include <sys/vfs.h>
00025 #include <time.h>
00026 #include <unistd.h>
00027 #include <utime.h>
00028 #include "i18n.h"
00029 #include "thread.h"
00030 
00031 int SysLogLevel = 3;
00032 
00033 #define MAXSYSLOGBUF 256
00034 
00035 void syslog_with_tid(int priority, const char *format, ...)
00036 {
00037   va_list ap;
00038   char fmt[MAXSYSLOGBUF];
00039   snprintf(fmt, sizeof(fmt), "[%d] %s", cThread::ThreadId(), format);
00040   va_start(ap, format);
00041   vsyslog(priority, fmt, ap);
00042   va_end(ap);
00043 }
00044 
00045 int BCD2INT(int x)
00046 {
00047   return ((1000000 * BCDCHARTOINT((x >> 24) & 0xFF)) +
00048             (10000 * BCDCHARTOINT((x >> 16) & 0xFF)) +
00049               (100 * BCDCHARTOINT((x >>  8) & 0xFF)) +
00050                      BCDCHARTOINT( x        & 0xFF));
00051 }
00052 
00053 ssize_t safe_read(int filedes, void *buffer, size_t size)
00054 {
00055   for (;;) {
00056       ssize_t p = read(filedes, buffer, size);
00057       if (p < 0 && errno == EINTR) {
00058          dsyslog("EINTR while reading from file handle %d - retrying", filedes);
00059          continue;
00060          }
00061       return p;
00062       }
00063 }
00064 
00065 ssize_t safe_write(int filedes, const void *buffer, size_t size)
00066 {
00067   ssize_t p = 0;
00068   ssize_t written = size;
00069   const unsigned char *ptr = (const unsigned char *)buffer;
00070   while (size > 0) {
00071         p = write(filedes, ptr, size);
00072         if (p < 0) {
00073            if (errno == EINTR) {
00074               dsyslog("EINTR while writing to file handle %d - retrying", filedes);
00075               continue;
00076               }
00077            break;
00078            }
00079         ptr  += p;
00080         size -= p;
00081         }
00082   return p < 0 ? p : written;
00083 }
00084 
00085 void writechar(int filedes, char c)
00086 {
00087   safe_write(filedes, &c, sizeof(c));
00088 }
00089 
00090 int WriteAllOrNothing(int fd, const uchar *Data, int Length, int TimeoutMs, int RetryMs)
00091 {
00092   int written = 0;
00093   while (Length > 0) {
00094         int w = write(fd, Data + written, Length);
00095         if (w > 0) {
00096            Length -= w;
00097            written += w;
00098            }
00099         else if (written > 0 && !FATALERRNO) {
00100            // we've started writing, so we must finish it!
00101            cTimeMs t;
00102            cPoller Poller(fd, true);
00103            Poller.Poll(RetryMs);
00104            if (TimeoutMs > 0 && (TimeoutMs -= t.Elapsed()) <= 0)
00105               break;
00106            }
00107         else
00108            // nothing written yet (or fatal error), so we can just return the error code:
00109            return w;
00110         }
00111   return written;
00112 }
00113 
00114 char *strcpyrealloc(char *dest, const char *src)
00115 {
00116   if (src) {
00117      int l = max(dest ? strlen(dest) : 0, strlen(src)) + 1; // don't let the block get smaller!
00118      dest = (char *)realloc(dest, l);
00119      if (dest)
00120         strcpy(dest, src);
00121      else
00122         esyslog("ERROR: out of memory");
00123      }
00124   else {
00125      free(dest);
00126      dest = NULL;
00127      }
00128   return dest;
00129 }
00130 
00131 char *strn0cpy(char *dest, const char *src, size_t n)
00132 {
00133   char *s = dest;
00134   for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
00135   *dest = 0;
00136   return s;
00137 }
00138 
00139 char *strreplace(char *s, char c1, char c2)
00140 {
00141   if (s) {
00142      char *p = s;
00143      while (*p) {
00144            if (*p == c1)
00145               *p = c2;
00146            p++;
00147            }
00148      }
00149   return s;
00150 }
00151 
00152 char *strreplace(char *s, const char *s1, const char *s2)
00153 {
00154   char *p = strstr(s, s1);
00155   if (p) {
00156      int of = p - s;
00157      int l  = strlen(s);
00158      int l1 = strlen(s1);
00159      int l2 = strlen(s2);
00160      if (l2 > l1)
00161         s = (char *)realloc(s, strlen(s) + l2 - l1 + 1);
00162      if (l2 != l1)
00163         memmove(s + of + l2, s + of + l1, l - of - l1 + 1);
00164      strncpy(s + of, s2, l2);
00165      }
00166   return s;
00167 }
00168 
00169 char *skipspace(const char *s)
00170 {
00171   while (*s && isspace(*s))
00172         s++;
00173   return (char *)s;
00174 }
00175 
00176 char *stripspace(char *s)
00177 {
00178   if (s && *s) {
00179      for (char *p = s + strlen(s) - 1; p >= s; p--) {
00180          if (!isspace(*p))
00181             break;
00182          *p = 0;
00183          }
00184      }
00185   return s;
00186 }
00187 
00188 char *compactspace(char *s)
00189 {
00190   if (s && *s) {
00191      char *t = stripspace(skipspace(s));
00192      char *p = t;
00193      while (p && *p) {
00194            char *q = skipspace(p);
00195            if (q - p > 1)
00196               memmove(p + 1, q, strlen(q) + 1);
00197            p++;
00198            }
00199      if (t != s)
00200         memmove(s, t, strlen(t) + 1);
00201      }
00202   return s;
00203 }
00204 
00205 cString strescape(const char *s, const char *chars)
00206 {
00207   char *buffer;
00208   const char *p = s;
00209   char *t = NULL;
00210   while (*p) {
00211         if (strchr(chars, *p)) {
00212            if (!t) {
00213               buffer = MALLOC(char, 2 * strlen(s) + 1);
00214               t = buffer + (p - s);
00215               s = strcpy(buffer, s);
00216               }
00217            *t++ = '\\';
00218            }
00219         if (t)
00220            *t++ = *p;
00221         p++;
00222         }
00223   if (t)
00224      *t = 0;
00225   return cString(s, t != NULL);
00226 }
00227 
00228 bool startswith(const char *s, const char *p)
00229 {
00230   while (*p) {
00231         if (*p++ != *s++)
00232            return false;
00233         }
00234   return true;
00235 }
00236 
00237 bool endswith(const char *s, const char *p)
00238 {
00239   const char *se = s + strlen(s) - 1;
00240   const char *pe = p + strlen(p) - 1;
00241   while (pe >= p) {
00242         if (*pe-- != *se-- || (se < s && pe >= p))
00243            return false;
00244         }
00245   return true;
00246 }
00247 
00248 bool isempty(const char *s)
00249 {
00250   return !(s && *skipspace(s));
00251 }
00252 
00253 int numdigits(int n)
00254 {
00255   char buf[16];
00256   snprintf(buf, sizeof(buf), "%d", n);
00257   return strlen(buf);
00258 }
00259 
00260 bool isnumber(const char *s)
00261 {
00262   if (!*s)
00263      return false;
00264   while (*s) {
00265         if (!isdigit(*s))
00266            return false;
00267         s++;
00268         }
00269   return true;
00270 }
00271 
00272 cString AddDirectory(const char *DirName, const char *FileName)
00273 {
00274   char *buf;
00275   asprintf(&buf, "%s/%s", DirName && *DirName ? DirName : ".", FileName);
00276   return cString(buf, true);
00277 }
00278 
00279 cString itoa(int n)
00280 {
00281   char buf[16];
00282   snprintf(buf, sizeof(buf), "%d", n);
00283   return buf;
00284 }
00285 
00286 int FreeDiskSpaceMB(const char *Directory, int *UsedMB)
00287 {
00288   if (UsedMB)
00289      *UsedMB = 0;
00290   int Free = 0;
00291   struct statfs statFs;
00292   if (statfs(Directory, &statFs) == 0) {
00293      double blocksPerMeg = 1024.0 * 1024.0 / statFs.f_bsize;
00294      if (UsedMB)
00295         *UsedMB = int((statFs.f_blocks - statFs.f_bfree) / blocksPerMeg);
00296      Free = int(statFs.f_bavail / blocksPerMeg);
00297      }
00298   else
00299      LOG_ERROR_STR(Directory);
00300   return Free;
00301 }
00302 
00303 bool DirectoryOk(const char *DirName, bool LogErrors)
00304 {
00305   struct stat ds;
00306   if (stat(DirName, &ds) == 0) {
00307      if (S_ISDIR(ds.st_mode)) {
00308         if (access(DirName, R_OK | W_OK | X_OK) == 0)
00309            return true;
00310         else if (LogErrors)
00311            esyslog("ERROR: can't access %s", DirName);
00312         }
00313      else if (LogErrors)
00314         esyslog("ERROR: %s is not a directory", DirName);
00315      }
00316   else if (LogErrors)
00317      LOG_ERROR_STR(DirName);
00318   return false;
00319 }
00320 
00321 bool MakeDirs(const char *FileName, bool IsDirectory)
00322 {
00323   bool result = true;
00324   char *s = strdup(FileName);
00325   char *p = s;
00326   if (*p == '/')
00327      p++;
00328   while ((p = strchr(p, '/')) != NULL || IsDirectory) {
00329         if (p)
00330            *p = 0;
00331         struct stat fs;
00332         if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
00333            dsyslog("creating directory %s", s);
00334            if (mkdir(s, ACCESSPERMS) == -1) {
00335               LOG_ERROR_STR(s);
00336               result = false;
00337               break;
00338               }
00339            }
00340         if (p)
00341            *p++ = '/';
00342         else
00343            break;
00344         }
00345   free(s);
00346   return result;
00347 }
00348 
00349 bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
00350 {
00351   struct stat st;
00352   if (stat(FileName, &st) == 0) {
00353      if (S_ISDIR(st.st_mode)) {
00354         cReadDir d(FileName);
00355         if (d.Ok()) {
00356            struct dirent *e;
00357            while ((e = d.Next()) != NULL) {
00358                  if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
00359                     char *buffer;
00360                     asprintf(&buffer, "%s/%s", FileName, e->d_name);
00361                     if (FollowSymlinks) {
00362                        int size = strlen(buffer) * 2; // should be large enough
00363                        char *l = MALLOC(char, size);
00364                        int n = readlink(buffer, l, size);
00365                        if (n < 0) {
00366                           if (errno != EINVAL)
00367                              LOG_ERROR_STR(buffer);
00368                           }
00369                        else if (n < size) {
00370                           l[n] = 0;
00371                           dsyslog("removing %s", l);
00372                           if (remove(l) < 0)
00373                              LOG_ERROR_STR(l);
00374                           }
00375                        else
00376                           esyslog("ERROR: symlink name length (%d) exceeded anticipated buffer size (%d)", n, size);
00377                        free(l);
00378                        }
00379                     dsyslog("removing %s", buffer);
00380                     if (remove(buffer) < 0)
00381                        LOG_ERROR_STR(buffer);
00382                     free(buffer);
00383                     }
00384                  }
00385            }
00386         else {
00387            LOG_ERROR_STR(FileName);
00388            return false;
00389            }
00390         }
00391      dsyslog("removing %s", FileName);
00392      if (remove(FileName) < 0) {
00393         LOG_ERROR_STR(FileName);
00394         return false;
00395         }
00396      }
00397   else if (errno != ENOENT) {
00398      LOG_ERROR_STR(FileName);
00399      return false;
00400      }
00401   return true;
00402 }
00403 
00404 bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis)
00405 {
00406   cReadDir d(DirName);
00407   if (d.Ok()) {
00408      bool empty = true;
00409      struct dirent *e;
00410      while ((e = d.Next()) != NULL) {
00411            if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..") && strcmp(e->d_name, "lost+found")) {
00412               char *buffer;
00413               asprintf(&buffer, "%s/%s", DirName, e->d_name);
00414               struct stat st;
00415               if (stat(buffer, &st) == 0) {
00416                  if (S_ISDIR(st.st_mode)) {
00417                     if (!RemoveEmptyDirectories(buffer, true))
00418                        empty = false;
00419                     }
00420                  else
00421                     empty = false;
00422                  }
00423               else {
00424                  LOG_ERROR_STR(buffer);
00425                  empty = false;
00426                  }
00427               free(buffer);
00428               }
00429            }
00430      if (RemoveThis && empty) {
00431         dsyslog("removing %s", DirName);
00432         if (remove(DirName) < 0) {
00433            LOG_ERROR_STR(DirName);
00434            return false;
00435            }
00436         }
00437      return empty;
00438      }
00439   else
00440      LOG_ERROR_STR(DirName);
00441   return false;
00442 }
00443 
00444 int DirSizeMB(const char *DirName)
00445 {
00446   cReadDir d(DirName);
00447   if (d.Ok()) {
00448      int size = 0;
00449      struct dirent *e;
00450      while (size >= 0 && (e = d.Next()) != NULL) {
00451            if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
00452               char *buffer;
00453               asprintf(&buffer, "%s/%s", DirName, e->d_name);
00454               struct stat st;
00455               if (stat(buffer, &st) == 0) {
00456                  if (S_ISDIR(st.st_mode)) {
00457                     int n = DirSizeMB(buffer);
00458                     if (n >= 0)
00459                        size += n;
00460                     else
00461                        size = -1;
00462                     }
00463                  else
00464                     size += st.st_size / MEGABYTE(1);
00465                  }
00466               else {
00467                  LOG_ERROR_STR(buffer);
00468                  size = -1;
00469                  }
00470               free(buffer);
00471               }
00472            }
00473      return size;
00474      }
00475   else
00476      LOG_ERROR_STR(DirName);
00477   return -1;
00478 }
00479 
00480 char *ReadLink(const char *FileName)
00481 {
00482   if (!FileName)
00483      return NULL;
00484   char *TargetName = canonicalize_file_name(FileName);
00485   if (!TargetName) {
00486      if (errno == ENOENT) // file doesn't exist
00487         TargetName = strdup(FileName);
00488      else // some other error occurred
00489         LOG_ERROR_STR(FileName);
00490      }
00491   return TargetName;
00492 }
00493 
00494 bool SpinUpDisk(const char *FileName)
00495 {
00496   char *buf = NULL;
00497   for (int n = 0; n < 10; n++) {
00498       free(buf);
00499       if (DirectoryOk(FileName))
00500          asprintf(&buf, "%s/vdr-%06d", *FileName ? FileName : ".", n);
00501       else
00502          asprintf(&buf, "%s.vdr-%06d", FileName, n);
00503       if (access(buf, F_OK) != 0) { // the file does not exist
00504          timeval tp1, tp2;
00505          gettimeofday(&tp1, NULL);
00506          int f = open(buf, O_WRONLY | O_CREAT, DEFFILEMODE);
00507          // O_SYNC doesn't work on all file systems
00508          if (f >= 0) {
00509             if (fdatasync(f) < 0)
00510                LOG_ERROR_STR(buf);
00511             close(f);
00512             remove(buf);
00513             gettimeofday(&tp2, NULL);
00514             double seconds = (((long long)tp2.tv_sec * 1000000 + tp2.tv_usec) - ((long long)tp1.tv_sec * 1000000 + tp1.tv_usec)) / 1000000.0;
00515             if (seconds > 0.5)
00516                dsyslog("SpinUpDisk took %.2f seconds", seconds);
00517             free(buf);
00518             return true;
00519             }
00520          else
00521             LOG_ERROR_STR(buf);
00522          }
00523       }
00524   free(buf);
00525   esyslog("ERROR: SpinUpDisk failed");
00526   return false;
00527 }
00528 
00529 void TouchFile(const char *FileName)
00530 {
00531   if (utime(FileName, NULL) == -1 && errno != ENOENT)
00532      LOG_ERROR_STR(FileName);
00533 }
00534 
00535 time_t LastModifiedTime(const char *FileName)
00536 {
00537   struct stat fs;
00538   if (stat(FileName, &fs) == 0)
00539      return fs.st_mtime;
00540   return 0;
00541 }
00542 
00543 // --- cTimeMs ---------------------------------------------------------------
00544 
00545 cTimeMs::cTimeMs(void)
00546 {
00547   Set();
00548 }
00549 
00550 uint64_t cTimeMs::Now(void)
00551 {
00552   struct timeval t;
00553   if (gettimeofday(&t, NULL) == 0)
00554      return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000;
00555   return 0;
00556 }
00557 
00558 void cTimeMs::Set(int Ms)
00559 {
00560   begin = Now() + Ms;
00561 }
00562 
00563 bool cTimeMs::TimedOut(void)
00564 {
00565   return Now() >= begin;
00566 }
00567 
00568 uint64_t cTimeMs::Elapsed(void)
00569 {
00570   return Now() - begin;
00571 }
00572 
00573 // --- cString ---------------------------------------------------------------
00574 
00575 cString::cString(const char *S, bool TakePointer)
00576 {
00577   s = TakePointer ? (char *)S : S ? strdup(S) : NULL;
00578 }
00579 
00580 cString::cString(const cString &String)
00581 {
00582   s = String.s ? strdup(String.s) : NULL;
00583 }
00584 
00585 cString::~cString()
00586 {
00587   free(s);
00588 }
00589 
00590 cString &cString::operator=(const cString &String)
00591 {
00592   if (this == &String)
00593      return *this;
00594   free(s);
00595   s = String.s ? strdup(String.s) : NULL;
00596   return *this;
00597 }
00598 
00599 cString cString::sprintf(const char *fmt, ...)
00600 {
00601   va_list ap;
00602   va_start(ap, fmt);
00603   char *buffer;
00604   vasprintf(&buffer, fmt, ap);
00605   return cString(buffer, true);
00606 }
00607 
00608 cString WeekDayName(int WeekDay)
00609 {
00610   char buffer[4];
00611   WeekDay = WeekDay == 0 ? 6 : WeekDay - 1; // we start with Monday==0!
00612   if (0 <= WeekDay && WeekDay <= 6) {
00613      const char *day = tr("MonTueWedThuFriSatSun");
00614      day += WeekDay * 3;
00615      strn0cpy(buffer, day, sizeof(buffer));
00616      return buffer;
00617      }
00618   else
00619      return "???";
00620 }
00621 
00622 cString WeekDayName(time_t t)
00623 {
00624   struct tm tm_r;
00625   return WeekDayName(localtime_r(&t, &tm_r)->tm_wday);
00626 }
00627 
00628 cString DayDateTime(time_t t)
00629 {
00630   char buffer[32];
00631   if (t == 0)
00632      time(&t);
00633   struct tm tm_r;
00634   tm *tm = localtime_r(&t, &tm_r);
00635   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);
00636   return buffer;
00637 }
00638 
00639 cString TimeToString(time_t t)
00640 {
00641   char buffer[32];
00642   if (ctime_r(&t, buffer)) {
00643      buffer[strlen(buffer) - 1] = 0; // strip trailing newline
00644      return buffer;
00645      }
00646   return "???";
00647 }
00648 
00649 cString DateString(time_t t)
00650 {
00651   char buf[32];
00652   struct tm tm_r;
00653   tm *tm = localtime_r(&t, &tm_r);
00654   char *p = stpcpy(buf, WeekDayName(tm->tm_wday));
00655   *p++ = ' ';
00656   strftime(p, sizeof(buf) - (p - buf), "%d.%m.%Y", tm);
00657   return buf;
00658 }
00659 
00660 cString TimeString(time_t t)
00661 {
00662   char buf[25];
00663   struct tm tm_r;
00664   strftime(buf, sizeof(buf), "%R", localtime_r(&t, &tm_r));
00665   return buf;
00666 }
00667 
00668 // --- RgbToJpeg -------------------------------------------------------------
00669 
00670 #define JPEGCOMPRESSMEM 500000
00671 
00672 struct tJpegCompressData {
00673   int size;
00674   uchar *mem;
00675   };
00676 
00677 static void JpegCompressInitDestination(j_compress_ptr cinfo)
00678 {
00679   tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data;
00680   if (jcd) {
00681      cinfo->dest->free_in_buffer = jcd->size = JPEGCOMPRESSMEM;
00682      cinfo->dest->next_output_byte = jcd->mem = MALLOC(uchar, jcd->size);
00683      }
00684 }
00685 
00686 static boolean JpegCompressEmptyOutputBuffer(j_compress_ptr cinfo)
00687 {
00688   tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data;
00689   if (jcd) {
00690      int Used = jcd->size;
00691      jcd->size += JPEGCOMPRESSMEM;
00692      jcd->mem = (uchar *)realloc(jcd->mem, jcd->size);
00693      if (jcd->mem) {
00694         cinfo->dest->next_output_byte = jcd->mem + Used;
00695         cinfo->dest->free_in_buffer = jcd->size - Used;
00696         return TRUE;
00697         }
00698      }
00699   return FALSE;
00700 }
00701 
00702 static void JpegCompressTermDestination(j_compress_ptr cinfo)
00703 {
00704   tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data;
00705   if (jcd) {
00706      int Used = cinfo->dest->next_output_byte - jcd->mem;
00707      if (Used < jcd->size) {
00708         jcd->size = Used;
00709         jcd->mem = (uchar *)realloc(jcd->mem, jcd->size);
00710         }
00711      }
00712 }
00713 
00714 uchar *RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality)
00715 {
00716   if (Quality < 0)
00717      Quality = 0;
00718   else if (Quality > 100)
00719      Quality = 100;
00720 
00721   jpeg_destination_mgr jdm;
00722 
00723   jdm.init_destination = JpegCompressInitDestination;
00724   jdm.empty_output_buffer = JpegCompressEmptyOutputBuffer;
00725   jdm.term_destination = JpegCompressTermDestination;
00726 
00727   struct jpeg_compress_struct cinfo;
00728   struct jpeg_error_mgr jerr;
00729   cinfo.err = jpeg_std_error(&jerr);
00730   jpeg_create_compress(&cinfo);
00731   cinfo.dest = &jdm;
00732   tJpegCompressData jcd;
00733   cinfo.client_data = &jcd;
00734   cinfo.image_width = Width;
00735   cinfo.image_height = Height;
00736   cinfo.input_components = 3;
00737   cinfo.in_color_space = JCS_RGB;
00738 
00739   jpeg_set_defaults(&cinfo);
00740   jpeg_set_quality(&cinfo, Quality, true);
00741   jpeg_start_compress(&cinfo, true);
00742 
00743   int rs = Width * 3;
00744   JSAMPROW rp[Height];
00745   for (int k = 0; k < Height; k++)
00746       rp[k] = &Mem[rs * k];
00747   jpeg_write_scanlines(&cinfo, rp, Height);
00748   jpeg_finish_compress(&cinfo);
00749   jpeg_destroy_compress(&cinfo);
00750 
00751   Size = jcd.size;
00752   return jcd.mem;
00753 }
00754 
00755 // --- cBase64Encoder --------------------------------------------------------
00756 
00757 const char *cBase64Encoder::b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00758 
00759 cBase64Encoder::cBase64Encoder(const uchar *Data, int Length, int MaxResult)
00760 {
00761   data = Data;
00762   length = Length;
00763   maxResult = MaxResult;
00764   i = 0;
00765   result = MALLOC(char, maxResult + 1);
00766 }
00767 
00768 cBase64Encoder::~cBase64Encoder()
00769 {
00770   free(result);
00771 }
00772 
00773 const char *cBase64Encoder::NextLine(void)
00774 {
00775   int r = 0;
00776   while (i < length && r < maxResult - 3) {
00777         result[r++] = b64[(data[i] >> 2) & 0x3F];
00778         char c = (data[i] << 4) & 0x3F;
00779         if (++i < length)
00780            c |= (data[i] >> 4) & 0x0F;
00781         result[r++] = b64[c];
00782         if (i < length) {
00783            c = (data[i] << 2) & 0x3F;
00784            if (++i < length)
00785               c |= (data[i] >> 6) & 0x03;
00786            result[r++] = b64[c];
00787            }
00788         else {
00789            i++;
00790            result[r++] = '=';
00791            }
00792         if (i < length) {
00793            c = data[i] & 0x3F;
00794            result[r++] = b64[c];
00795            }
00796         else
00797            result[r++] = '=';
00798         i++;
00799         }
00800   if (r > 0) {
00801      result[r] = 0;
00802      return result;
00803      }
00804   return NULL;
00805 }
00806 
00807 // --- cReadLine -------------------------------------------------------------
00808 
00809 cReadLine::cReadLine(void)
00810 {
00811   size = 0;
00812   buffer = NULL;
00813 }
00814 
00815 cReadLine::~cReadLine()
00816 {
00817   free(buffer);
00818 }
00819 
00820 char *cReadLine::Read(FILE *f)
00821 {
00822   int n = getline(&buffer, &size, f);
00823   if (n > 0) {
00824      n--;
00825      if (buffer[n] == '\n') {
00826         buffer[n] = 0;
00827         if (n > 0) {
00828            n--;
00829            if (buffer[n] == '\r')
00830               buffer[n] = 0;
00831            }
00832         }
00833      return buffer;
00834      }
00835   return NULL;
00836 }
00837 
00838 // --- cPoller ---------------------------------------------------------------
00839 
00840 cPoller::cPoller(int FileHandle, bool Out)
00841 {
00842   numFileHandles = 0;
00843   Add(FileHandle, Out);
00844 }
00845 
00846 bool cPoller::Add(int FileHandle, bool Out)
00847 {
00848   if (FileHandle >= 0) {
00849      for (int i = 0; i < numFileHandles; i++) {
00850          if (pfd[i].fd == FileHandle && pfd[i].events == (Out ? POLLOUT : POLLIN))
00851             return true;
00852          }
00853      if (numFileHandles < MaxPollFiles) {
00854         pfd[numFileHandles].fd = FileHandle;
00855         pfd[numFileHandles].events = Out ? POLLOUT : POLLIN;
00856         pfd[numFileHandles].revents = 0;
00857         numFileHandles++;
00858         return true;
00859         }
00860      esyslog("ERROR: too many file handles in cPoller");
00861      }
00862   return false;
00863 }
00864 
00865 bool cPoller::Poll(int TimeoutMs)
00866 {
00867   if (numFileHandles) {
00868      if (poll(pfd, numFileHandles, TimeoutMs) != 0)
00869         return true; // returns true even in case of an error, to let the caller
00870                      // access the file and thus see the error code
00871      }
00872   return false;
00873 }
00874 
00875 // --- cReadDir --------------------------------------------------------------
00876 
00877 cReadDir::cReadDir(const char *Directory)
00878 {
00879   directory = opendir(Directory);
00880 }
00881 
00882 cReadDir::~cReadDir()
00883 {
00884   if (directory)
00885      closedir(directory);
00886 }
00887 
00888 struct dirent *cReadDir::Next(void)
00889 {
00890   return directory && readdir_r(directory, &u.d, &result) == 0 ? result : NULL;
00891 }
00892 
00893 // --- cFile -----------------------------------------------------------------
00894 
00895 bool cFile::files[FD_SETSIZE] = { false };
00896 int cFile::maxFiles = 0;
00897 
00898 cFile::cFile(void)
00899 {
00900   f = -1;
00901 }
00902 
00903 cFile::~cFile()
00904 {
00905   Close();
00906 }
00907 
00908 bool cFile::Open(const char *FileName, int Flags, mode_t Mode)
00909 {
00910   if (!IsOpen())
00911      return Open(open(FileName, Flags, Mode));
00912   esyslog("ERROR: attempt to re-open %s", FileName);
00913   return false;
00914 }
00915 
00916 bool cFile::Open(int FileDes)
00917 {
00918   if (FileDes >= 0) {
00919      if (!IsOpen()) {
00920         f = FileDes;
00921         if (f >= 0) {
00922            if (f < FD_SETSIZE) {
00923               if (f >= maxFiles)
00924                  maxFiles = f + 1;
00925               if (!files[f])
00926                  files[f] = true;
00927               else
00928                  esyslog("ERROR: file descriptor %d already in files[]", f);
00929               return true;
00930               }
00931            else
00932               esyslog("ERROR: file descriptor %d is larger than FD_SETSIZE (%d)", f, FD_SETSIZE);
00933            }
00934         }
00935      else
00936         esyslog("ERROR: attempt to re-open file descriptor %d", FileDes);
00937      }
00938   return false;
00939 }
00940 
00941 void cFile::Close(void)
00942 {
00943   if (f >= 0) {
00944      close(f);
00945      files[f] = false;
00946      f = -1;
00947      }
00948 }
00949 
00950 bool cFile::Ready(bool Wait)
00951 {
00952   return f >= 0 && AnyFileReady(f, Wait ? 1000 : 0);
00953 }
00954 
00955 bool cFile::AnyFileReady(int FileDes, int TimeoutMs)
00956 {
00957   fd_set set;
00958   FD_ZERO(&set);
00959   for (int i = 0; i < maxFiles; i++) {
00960       if (files[i])
00961          FD_SET(i, &set);
00962       }
00963   if (0 <= FileDes && FileDes < FD_SETSIZE && !files[FileDes])
00964      FD_SET(FileDes, &set); // in case we come in with an arbitrary descriptor
00965   if (TimeoutMs == 0)
00966      TimeoutMs = 10; // load gets too heavy with 0
00967   struct timeval timeout;
00968   timeout.tv_sec  = TimeoutMs / 1000;
00969   timeout.tv_usec = (TimeoutMs % 1000) * 1000;
00970   return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && (FileDes < 0 || FD_ISSET(FileDes, &set));
00971 }
00972 
00973 bool cFile::FileReady(int FileDes, int TimeoutMs)
00974 {
00975   fd_set set;
00976   struct timeval timeout;
00977   FD_ZERO(&set);
00978   FD_SET(FileDes, &set);
00979   if (TimeoutMs >= 0) {
00980      if (TimeoutMs < 100)
00981         TimeoutMs = 100;
00982      timeout.tv_sec  = TimeoutMs / 1000;
00983      timeout.tv_usec = (TimeoutMs % 1000) * 1000;
00984      }
00985   return select(FD_SETSIZE, &set, NULL, NULL, (TimeoutMs >= 0) ? &timeout : NULL) > 0 && FD_ISSET(FileDes, &set);
00986 }
00987 
00988 bool cFile::FileReadyForWriting(int FileDes, int TimeoutMs)
00989 {
00990   fd_set set;
00991   struct timeval timeout;
00992   FD_ZERO(&set);
00993   FD_SET(FileDes, &set);
00994   if (TimeoutMs < 100)
00995      TimeoutMs = 100;
00996   timeout.tv_sec  = 0;
00997   timeout.tv_usec = TimeoutMs * 1000;
00998   return select(FD_SETSIZE, NULL, &set, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
00999 }
01000 
01001 // --- cSafeFile -------------------------------------------------------------
01002 
01003 cSafeFile::cSafeFile(const char *FileName)
01004 {
01005   f = NULL;
01006   fileName = ReadLink(FileName);
01007   tempName = fileName ? MALLOC(char, strlen(fileName) + 5) : NULL;
01008   if (tempName)
01009      strcat(strcpy(tempName, fileName), ".$$$");
01010 }
01011 
01012 cSafeFile::~cSafeFile()
01013 {
01014   if (f)
01015      fclose(f);
01016   unlink(tempName);
01017   free(fileName);
01018   free(tempName);
01019 }
01020 
01021 bool cSafeFile::Open(void)
01022 {
01023   if (!f && fileName && tempName) {
01024      f = fopen(tempName, "w");
01025      if (!f)
01026         LOG_ERROR_STR(tempName);
01027      }
01028   return f != NULL;
01029 }
01030 
01031 bool cSafeFile::Close(void)
01032 {
01033   bool result = true;
01034   if (f) {
01035      if (ferror(f) != 0) {
01036         LOG_ERROR_STR(tempName);
01037         result = false;
01038         }
01039      if (fclose(f) < 0) {
01040         LOG_ERROR_STR(tempName);
01041         result = false;
01042         }
01043      f = NULL;
01044      if (result && rename(tempName, fileName) < 0) {
01045         LOG_ERROR_STR(fileName);
01046         result = false;
01047         }
01048      }
01049   else
01050      result = false;
01051   return result;
01052 }
01053 
01054 // --- cUnbufferedFile -------------------------------------------------------
01055 
01056 #define USE_FADVISE
01057 
01058 #define WRITE_BUFFER KILOBYTE(800)
01059 
01060 cUnbufferedFile::cUnbufferedFile(void)
01061 {
01062   fd = -1;
01063 }
01064 
01065 cUnbufferedFile::~cUnbufferedFile()
01066 {
01067   Close();
01068 }
01069 
01070 int cUnbufferedFile::Open(const char *FileName, int Flags, mode_t Mode)
01071 {
01072   Close();
01073   fd = open(FileName, Flags, Mode);
01074   curpos = 0;
01075 #ifdef USE_FADVISE
01076   begin = lastpos = ahead = 0;
01077   cachedstart = 0;
01078   cachedend = 0;
01079   readahead = KILOBYTE(128);
01080   written = 0;
01081   totwritten = 0;
01082   if (fd >= 0)
01083      posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM); // we could use POSIX_FADV_SEQUENTIAL, but we do our own readahead, disabling the kernel one.
01084 #endif
01085   return fd;
01086 }
01087 
01088 int cUnbufferedFile::Close(void)
01089 {
01090 #ifdef USE_FADVISE
01091   if (fd >= 0) {
01092      if (totwritten)    // if we wrote anything make sure the data has hit the disk before
01093         fdatasync(fd);  // calling fadvise, as this is our last chance to un-cache it.
01094      posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
01095      }
01096 #endif
01097   int OldFd = fd;
01098   fd = -1;
01099   return close(OldFd);
01100 }
01101 
01102 // When replaying and going e.g. FF->PLAY the position jumps back 2..8M
01103 // hence we do not want to drop recently accessed data at once.
01104 // We try to handle the common cases such as PLAY->FF->PLAY, small
01105 // jumps, moving editing marks etc.
01106 
01107 #define FADVGRAN   KILOBYTE(4) // AKA fadvise-chunk-size; PAGE_SIZE or getpagesize(2) would also work.
01108 #define READCHUNK  MEGABYTE(8)
01109 
01110 void cUnbufferedFile::SetReadAhead(size_t ra)
01111 {
01112   readahead = ra;
01113 }
01114 
01115 int cUnbufferedFile::FadviseDrop(off_t Offset, off_t Len)
01116 {
01117   // rounding up the window to make sure that not PAGE_SIZE-aligned data gets freed.
01118   return posix_fadvise(fd, Offset - (FADVGRAN - 1), Len + (FADVGRAN - 1) * 2, POSIX_FADV_DONTNEED);
01119 }
01120 
01121 off_t cUnbufferedFile::Seek(off_t Offset, int Whence)
01122 {
01123   if (Whence == SEEK_SET && Offset == curpos)
01124      return curpos;
01125   curpos = lseek(fd, Offset, Whence);
01126   return curpos;
01127 }
01128 
01129 ssize_t cUnbufferedFile::Read(void *Data, size_t Size)
01130 {
01131   if (fd >= 0) {
01132 #ifdef USE_FADVISE
01133      off_t jumped = curpos-lastpos; // nonzero means we're not at the last offset
01134      if ((cachedstart < cachedend) && (curpos < cachedstart || curpos > cachedend)) {
01135         // current position is outside the cached window -- invalidate it.
01136         FadviseDrop(cachedstart, cachedend-cachedstart);
01137         cachedstart = curpos;
01138         cachedend = curpos;
01139         }
01140      cachedstart = min(cachedstart, curpos);
01141 #endif
01142      ssize_t bytesRead = safe_read(fd, Data, Size);
01143 #ifdef USE_FADVISE
01144      if (bytesRead > 0) {
01145         curpos += bytesRead;
01146         cachedend = max(cachedend, curpos);
01147 
01148         // Read ahead:
01149         // no jump? (allow small forward jump still inside readahead window).
01150         if (jumped >= 0 && jumped <= (off_t)readahead) {
01151            // Trigger the readahead IO, but only if we've used at least
01152            // 1/2 of the previously requested area. This avoids calling
01153            // fadvise() after every read() call.
01154            if (ahead - curpos < (off_t)(readahead / 2)) {
01155               posix_fadvise(fd, curpos, readahead, POSIX_FADV_WILLNEED);
01156               ahead = curpos + readahead;
01157               cachedend = max(cachedend, ahead);
01158               }
01159            if (readahead < Size * 32) { // automagically tune readahead size.
01160               readahead = Size * 32;
01161               }
01162            }
01163         else
01164            ahead = curpos; // jumped -> we really don't want any readahead, otherwise e.g. fast-rewind gets in trouble.
01165         }
01166 
01167      if (cachedstart < cachedend) {
01168         if (curpos - cachedstart > READCHUNK * 2) {
01169            // current position has moved forward enough, shrink tail window.
01170            FadviseDrop(cachedstart, curpos - READCHUNK - cachedstart);
01171            cachedstart = curpos - READCHUNK;
01172            }
01173         else if (cachedend > ahead && cachedend - curpos > READCHUNK * 2) {
01174            // current position has moved back enough, shrink head window.
01175            FadviseDrop(curpos + READCHUNK, cachedend - (curpos + READCHUNK));
01176            cachedend = curpos + READCHUNK;
01177            }
01178         }
01179      lastpos = curpos;
01180 #endif
01181      return bytesRead;
01182      }
01183   return -1;
01184 }
01185 
01186 ssize_t cUnbufferedFile::Write(const void *Data, size_t Size)
01187 {
01188   if (fd >=0) {
01189      ssize_t bytesWritten = safe_write(fd, Data, Size);
01190 #ifdef USE_FADVISE
01191      if (bytesWritten > 0) {
01192         begin = min(begin, curpos);
01193         curpos += bytesWritten;
01194         written += bytesWritten;
01195         lastpos = max(lastpos, curpos);
01196         if (written > WRITE_BUFFER) {
01197            if (lastpos > begin) {
01198               // Now do three things:
01199               // 1) Start writeback of begin..lastpos range
01200               // 2) Drop the already written range (by the previous fadvise call)
01201               // 3) Handle nonpagealigned data.
01202               //    This is why we double the WRITE_BUFFER; the first time around the
01203               //    last (partial) page might be skipped, writeback will start only after
01204               //    second call; the third call will still include this page and finally
01205               //    drop it from cache.
01206               off_t headdrop = min(begin, WRITE_BUFFER * 2L);
01207               posix_fadvise(fd, begin - headdrop, lastpos - begin + headdrop, POSIX_FADV_DONTNEED);
01208               }
01209            begin = lastpos = curpos;
01210            totwritten += written;
01211            written = 0;
01212            // The above fadvise() works when writing slowly (recording), but could
01213            // leave cached data around when writing at a high rate, e.g. when cutting,
01214            // because by the time we try to flush the cached pages (above) the data
01215            // can still be dirty - we are faster than the disk I/O.
01216            // So we do another round of flushing, just like above, but at larger
01217            // intervals -- this should catch any pages that couldn't be released
01218            // earlier.
01219            if (totwritten > MEGABYTE(32)) {
01220               // It seems in some setups, fadvise() does not trigger any I/O and
01221               // a fdatasync() call would be required do all the work (reiserfs with some
01222               // kind of write gathering enabled), but the syncs cause (io) load..
01223               // Uncomment the next line if you think you need them.
01224               //fdatasync(fd);
01225               off_t headdrop = min(curpos - totwritten, totwritten * 2L);
01226               posix_fadvise(fd, curpos - totwritten - headdrop, totwritten + headdrop, POSIX_FADV_DONTNEED);
01227               totwritten = 0;
01228               }
01229            }
01230         }
01231 #endif
01232      return bytesWritten;
01233      }
01234   return -1;
01235 }
01236 
01237 cUnbufferedFile *cUnbufferedFile::Create(const char *FileName, int Flags, mode_t Mode)
01238 {
01239   cUnbufferedFile *File = new cUnbufferedFile;
01240   if (File->Open(FileName, Flags, Mode) < 0) {
01241      delete File;
01242      File = NULL;
01243      }
01244   return File;
01245 }
01246 
01247 // --- cLockFile -------------------------------------------------------------
01248 
01249 #define LOCKFILENAME      ".lock-vdr"
01250 #define LOCKFILESTALETIME 600 // seconds before considering a lock file "stale"
01251 
01252 cLockFile::cLockFile(const char *Directory)
01253 {
01254   fileName = NULL;
01255   f = -1;
01256   if (DirectoryOk(Directory))
01257      asprintf(&fileName, "%s/%s", Directory, LOCKFILENAME);
01258 }
01259 
01260 cLockFile::~cLockFile()
01261 {
01262   Unlock();
01263   free(fileName);
01264 }
01265 
01266 bool cLockFile::Lock(int WaitSeconds)
01267 {
01268   if (f < 0 && fileName) {
01269      time_t Timeout = time(NULL) + WaitSeconds;
01270      do {
01271         f = open(fileName, O_WRONLY | O_CREAT | O_EXCL, DEFFILEMODE);
01272         if (f < 0) {
01273            if (errno == EEXIST) {
01274               struct stat fs;
01275               if (stat(fileName, &fs) == 0) {
01276                  if (abs(time(NULL) - fs.st_mtime) > LOCKFILESTALETIME) {
01277                     esyslog("ERROR: removing stale lock file '%s'", fileName);
01278                     if (remove(fileName) < 0) {
01279                        LOG_ERROR_STR(fileName);
01280                        break;
01281                        }
01282                     continue;
01283                     }
01284                  }
01285               else if (errno != ENOENT) {
01286                  LOG_ERROR_STR(fileName);
01287                  break;
01288                  }
01289               }
01290            else {
01291               LOG_ERROR_STR(fileName);
01292               break;
01293               }
01294            if (WaitSeconds)
01295               sleep(1);
01296            }
01297         } while (f < 0 && time(NULL) < Timeout);
01298      }
01299   return f >= 0;
01300 }
01301 
01302 void cLockFile::Unlock(void)
01303 {
01304   if (f >= 0) {
01305      close(f);
01306      remove(fileName);
01307      f = -1;
01308      }
01309 }
01310 
01311 // --- cListObject -----------------------------------------------------------
01312 
01313 cListObject::cListObject(void)
01314 {
01315   prev = next = NULL;
01316 }
01317 
01318 cListObject::~cListObject()
01319 {
01320 }
01321 
01322 void cListObject::Append(cListObject *Object)
01323 {
01324   next = Object;
01325   Object->prev = this;
01326 }
01327 
01328 void cListObject::Insert(cListObject *Object)
01329 {
01330   prev = Object;
01331   Object->next = this;
01332 }
01333 
01334 void cListObject::Unlink(void)
01335 {
01336   if (next)
01337      next->prev = prev;
01338   if (prev)
01339      prev->next = next;
01340   next = prev = NULL;
01341 }
01342 
01343 int cListObject::Index(void) const
01344 {
01345   cListObject *p = prev;
01346   int i = 0;
01347 
01348   while (p) {
01349         i++;
01350         p = p->prev;
01351         }
01352   return i;
01353 }
01354 
01355 // --- cListBase -------------------------------------------------------------
01356 
01357 cListBase::cListBase(void)
01358 {
01359   objects = lastObject = NULL;
01360   count = 0;
01361 }
01362 
01363 cListBase::~cListBase()
01364 {
01365   Clear();
01366 }
01367 
01368 void cListBase::Add(cListObject *Object, cListObject *After)
01369 {
01370   if (After && After != lastObject) {
01371      After->Next()->Insert(Object);
01372      After->Append(Object);
01373      }
01374   else {
01375      if (lastObject)
01376         lastObject->Append(Object);
01377      else
01378         objects = Object;
01379      lastObject = Object;
01380      }
01381   count++;
01382 }
01383 
01384 void cListBase::Ins(cListObject *Object, cListObject *Before)
01385 {
01386   if (Before && Before != objects) {
01387      Before->Prev()->Append(Object);
01388      Before->Insert(Object);
01389      }
01390   else {
01391      if (objects)
01392         objects->Insert(Object);
01393      else
01394         lastObject = Object;
01395      objects = Object;
01396      }
01397   count++;
01398 }
01399 
01400 void cListBase::Del(cListObject *Object, bool DeleteObject)
01401 {
01402   if (Object == objects)
01403      objects = Object->Next();
01404   if (Object == lastObject)
01405      lastObject = Object->Prev();
01406   Object->Unlink();
01407   if (DeleteObject)
01408      delete Object;
01409   count--;
01410 }
01411 
01412 void cListBase::Move(int From, int To)
01413 {
01414   Move(Get(From), Get(To));
01415 }
01416 
01417 void cListBase::Move(cListObject *From, cListObject *To)
01418 {
01419   if (From && To) {
01420      if (From->Index() < To->Index())
01421         To = To->Next();
01422      if (From == objects)
01423         objects = From->Next();
01424      if (From == lastObject)
01425         lastObject = From->Prev();
01426      From->Unlink();
01427      if (To) {
01428         if (To->Prev())
01429            To->Prev()->Append(From);
01430         From->Append(To);
01431         }
01432      else {
01433         lastObject->Append(From);
01434         lastObject = From;
01435         }
01436      if (!From->Prev())
01437         objects = From;
01438      }
01439 }
01440 
01441 void cListBase::Clear(void)
01442 {
01443   while (objects) {
01444         cListObject *object = objects->Next();
01445         delete objects;
01446         objects = object;
01447         }
01448   objects = lastObject = NULL;
01449   count = 0;
01450 }
01451 
01452 cListObject *cListBase::Get(int Index) const
01453 {
01454   if (Index < 0)
01455      return NULL;
01456   cListObject *object = objects;
01457   while (object && Index-- > 0)
01458         object = object->Next();
01459   return object;
01460 }
01461 
01462 static int CompareListObjects(const void *a, const void *b)
01463 {
01464   const cListObject *la = *(const cListObject **)a;
01465   const cListObject *lb = *(const cListObject **)b;
01466   return la->Compare(*lb);
01467 }
01468 
01469 void cListBase::Sort(void)
01470 {
01471   int n = Count();
01472   cListObject *a[n];
01473   cListObject *object = objects;
01474   int i = 0;
01475   while (object && i < n) {
01476         a[i++] = object;
01477         object = object->Next();
01478         }
01479   qsort(a, n, sizeof(cListObject *), CompareListObjects);
01480   objects = lastObject = NULL;
01481   for (i = 0; i < n; i++) {
01482       a[i]->Unlink();
01483       count--;
01484       Add(a[i]);
01485       }
01486 }
01487 
01488 // --- cHashBase -------------------------------------------------------------
01489 
01490 cHashBase::cHashBase(int Size)
01491 {
01492   size = Size;
01493   hashTable = (cList<cHashObject>**)calloc(size, sizeof(cList<cHashObject>*));
01494 }
01495 
01496 cHashBase::~cHashBase(void)
01497 {
01498   Clear();
01499   free(hashTable);
01500 }
01501 
01502 void cHashBase::Add(cListObject *Object, unsigned int Id)
01503 {
01504   unsigned int hash = hashfn(Id);
01505   if (!hashTable[hash])
01506      hashTable[hash] = new cList<cHashObject>;
01507   hashTable[hash]->Add(new cHashObject(Object, Id));
01508 }
01509 
01510 void cHashBase::Del(cListObject *Object, unsigned int Id)
01511 {
01512   cList<cHashObject> *list = hashTable[hashfn(Id)];
01513   if (list) {
01514      for (cHashObject *hob = list->First(); hob; hob = list->Next(hob)) {
01515          if (hob->object == Object) {
01516             list->Del(hob);
01517             break;
01518             }
01519          }
01520      }
01521 }
01522 
01523 void cHashBase::Clear(void)
01524 {
01525   for (int i = 0; i < size; i++) {
01526       delete hashTable[i];
01527       hashTable[i] = NULL;
01528       }
01529 }
01530 
01531 cListObject *cHashBase::Get(unsigned int Id) const
01532 {
01533   cList<cHashObject> *list = hashTable[hashfn(Id)];
01534   if (list) {
01535      for (cHashObject *hob = list->First(); hob; hob = list->Next(hob)) {
01536          if (hob->id == Id)
01537             return hob->object;
01538          }
01539      }
01540   return NULL;
01541 }
01542 
01543 cList<cHashObject> *cHashBase::GetList(unsigned int Id) const
01544 {
01545   return hashTable[hashfn(Id)];
01546 }

Generated on Tue Nov 6 19:57:56 2007 for VDR by  doxygen 1.5.3-20071008