Home

Dokumentation

Impressum

Dokumentation VDR
 

Main Page   Class Hierarchy   Alphabetical List   Data Structures   File List   Data Fields   Globals  

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.76 2002/11/24 20:17:31 kls Exp $
00008  */
00009 
00010 #include "tools.h"
00011 #include <ctype.h>
00012 #include <dirent.h>
00013 #include <errno.h>
00014 #include <stdlib.h>
00015 #include <sys/time.h>
00016 #include <sys/vfs.h>
00017 #include <time.h>
00018 #include <unistd.h>
00019 #include "i18n.h"
00020 
00021 int SysLogLevel = 3;
00022 
00023 ssize_t safe_read(int filedes, void *buffer, size_t size)
00024 {
00025   for (;;) {
00026       ssize_t p = read(filedes, buffer, size);
00027       if (p < 0 && errno == EINTR) {
00028          dsyslog("EINTR while reading from file handle %d - retrying", filedes);
00029          continue;
00030          }
00031       return p;
00032       }
00033 }
00034 
00035 ssize_t safe_write(int filedes, const void *buffer, size_t size)
00036 {
00037   ssize_t p = 0;
00038   ssize_t written = size;
00039   const unsigned char *ptr = (const unsigned char *)buffer;
00040   while (size > 0) {
00041         p = write(filedes, ptr, size);
00042         if (p < 0) {
00043            if (errno == EINTR) {
00044               dsyslog("EINTR while writing to file handle %d - retrying", filedes);
00045               continue;
00046               }
00047            break;
00048            }
00049         ptr  += p;
00050         size -= p;
00051         }
00052   return p < 0 ? p : written;
00053 }
00054 
00055 void writechar(int filedes, char c)
00056 {
00057   safe_write(filedes, &c, sizeof(c));
00058 }
00059 
00060 char *readline(FILE *f)
00061 {
00062   static char buffer[MAXPARSEBUFFER];
00063   if (fgets(buffer, sizeof(buffer), f) > 0) {
00064      int l = strlen(buffer) - 1;
00065      if (l >= 0 && buffer[l] == '\n')
00066         buffer[l] = 0;
00067      return buffer;
00068      }
00069   return NULL;
00070 }
00071 
00072 char *strcpyrealloc(char *dest, const char *src)
00073 {
00074   if (src) {
00075      int l = max(dest ? strlen(dest) : 0, strlen(src)) + 1; // don't let the block get smaller!
00076      dest = (char *)realloc(dest, l);
00077      if (dest)
00078         strcpy(dest, src);
00079      else
00080         esyslog("ERROR: out of memory");
00081      }
00082   else {
00083      free(dest);
00084      dest = NULL;
00085      }
00086   return dest;
00087 }
00088 
00089 char *strn0cpy(char *dest, const char *src, size_t n)
00090 {
00091   char *s = dest;
00092   for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
00093   *dest = 0;
00094   return s;
00095 }
00096 
00097 char *strreplace(char *s, char c1, char c2)
00098 {
00099   char *p = s;
00100 
00101   while (p && *p) {
00102         if (*p == c1)
00103            *p = c2;
00104         p++;
00105         }
00106   return s;
00107 }
00108 
00109 char *strreplace(char *s, const char *s1, const char *s2)
00110 {
00111   char *p = strstr(s, s1);
00112   if (p) {
00113      int of = p - s;
00114      int l  = strlen(s);
00115      int l1 = strlen(s1);
00116      int l2 = strlen(s2);
00117      if (l2 > l1)
00118         s = (char *)realloc(s, strlen(s) + l2 - l1 + 1);
00119      if (l2 != l1)
00120         memmove(s + of + l2, s + of + l1, l - of - l1 + 1);
00121      strncpy(s + of, s2, l2);
00122      }
00123   return s;
00124 }
00125 
00126 char *skipspace(const char *s)
00127 {
00128   while (*s && isspace(*s))
00129         s++;
00130   return (char *)s;
00131 }
00132 
00133 char *stripspace(char *s)
00134 {
00135   if (s && *s) {
00136      for (char *p = s + strlen(s) - 1; p >= s; p--) {
00137          if (!isspace(*p))
00138             break;
00139          *p = 0;
00140          }
00141      }
00142   return s;
00143 }
00144 
00145 char *compactspace(char *s)
00146 {
00147   if (s && *s) {
00148      char *t = stripspace(skipspace(s));
00149      char *p = t;
00150      while (p && *p) {
00151            char *q = skipspace(p);
00152            if (q - p > 1)
00153               memmove(p + 1, q, strlen(q) + 1);
00154            p++;
00155            }
00156      if (t != s)
00157         memmove(s, t, strlen(t) + 1);
00158      }
00159   return s;
00160 }
00161 
00162 const char *strescape(const char *s, const char *chars)
00163 {
00164   static char *buffer = NULL;
00165   const char *p = s;
00166   char *t = NULL;
00167   while (*p) {
00168         if (strchr(chars, *p)) {
00169            if (!t) {
00170               buffer = (char *)realloc(buffer, 2 * strlen(s) + 1);
00171               t = buffer + (p - s);
00172               s = strcpy(buffer, s);
00173               }
00174            *t++ = '\\';
00175            }
00176         if (t)
00177            *t++ = *p;
00178         p++;
00179         }
00180   if (t)
00181      *t = 0;
00182   return s;
00183 }
00184 
00185 bool startswith(const char *s, const char *p)
00186 {
00187   while (*p) {
00188         if (*p++ != *s++)
00189            return false;
00190         }
00191   return true;
00192 }
00193 
00194 bool endswith(const char *s, const char *p)
00195 {
00196   const char *se = s + strlen(s) - 1;
00197   const char *pe = p + strlen(p) - 1;
00198   while (pe >= p) {
00199         if (*pe-- != *se-- || (se < s && pe >= p))
00200            return false;
00201         }
00202   return true;
00203 }
00204 
00205 bool isempty(const char *s)
00206 {
00207   return !(s && *skipspace(s));
00208 }
00209 
00210 int numdigits(int n)
00211 {
00212   char buf[16];
00213   snprintf(buf, sizeof(buf), "%d", n);
00214   return strlen(buf);
00215 }
00216 
00217 int time_ms(void)
00218 {
00219   static time_t t0 = 0;
00220   struct timeval t;
00221   if (gettimeofday(&t, NULL) == 0) {
00222      if (t0 == 0)
00223         t0 = t.tv_sec; // this avoids an overflow (we only work with deltas)
00224      return (t.tv_sec - t0) * 1000 + t.tv_usec / 1000;
00225      }
00226   return 0;
00227 }
00228 
00229 void delay_ms(int ms)
00230 {
00231   int t0 = time_ms();
00232   while (time_ms() - t0 < ms)
00233         ;
00234 }
00235 
00236 bool isnumber(const char *s)
00237 {
00238   if (!*s)
00239      return false;
00240   while (*s) {
00241         if (!isdigit(*s))
00242            return false;
00243         s++;
00244         }
00245   return true;
00246 }
00247 
00248 const char *itoa(int n)
00249 {
00250   static char buf[16];
00251   snprintf(buf, sizeof(buf), "%d", n);
00252   return buf;
00253 }
00254 
00255 const char *AddDirectory(const char *DirName, const char *FileName)
00256 {
00257   static char *buf = NULL;
00258   free(buf);
00259   asprintf(&buf, "%s/%s", DirName && *DirName ? DirName : ".", FileName);
00260   return buf;
00261 }
00262 
00263 int FreeDiskSpaceMB(const char *Directory, int *UsedMB)
00264 {
00265   if (UsedMB)
00266      *UsedMB = 0;
00267   int Free = 0;
00268   struct statfs statFs;
00269   if (statfs(Directory, &statFs) == 0) {
00270      double blocksPerMeg = 1024.0 * 1024.0 / statFs.f_bsize;
00271      if (UsedMB)
00272         *UsedMB = int((statFs.f_blocks - statFs.f_bfree) / blocksPerMeg);
00273      Free = int(statFs.f_bavail / blocksPerMeg);
00274      }
00275   else
00276      LOG_ERROR_STR(Directory);
00277   return Free;
00278 }
00279 
00280 bool DirectoryOk(const char *DirName, bool LogErrors)
00281 {
00282   struct stat ds;
00283   if (stat(DirName, &ds) == 0) {
00284      if (S_ISDIR(ds.st_mode)) {
00285         if (access(DirName, R_OK | W_OK | X_OK) == 0)
00286            return true;
00287         else if (LogErrors)
00288            esyslog("ERROR: can't access %s", DirName);
00289         }
00290      else if (LogErrors)
00291         esyslog("ERROR: %s is not a directory", DirName);
00292      }
00293   else if (LogErrors)
00294      LOG_ERROR_STR(DirName);
00295   return false;
00296 }
00297 
00298 bool MakeDirs(const char *FileName, bool IsDirectory)
00299 {
00300   bool result = true;
00301   char *s = strdup(FileName);
00302   char *p = s;
00303   if (*p == '/')
00304      p++;
00305   while ((p = strchr(p, '/')) != NULL || IsDirectory) {
00306         if (p)
00307            *p = 0;
00308         struct stat fs;
00309         if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
00310            dsyslog("creating directory %s", s);
00311            if (mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) {
00312               LOG_ERROR_STR(s);
00313               result = false;
00314               break;
00315               }
00316            }
00317         if (p)
00318            *p++ = '/';
00319         else
00320            break;
00321         }
00322   free(s);
00323   return result;
00324 }
00325 
00326 bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
00327 {
00328   struct stat st;
00329   if (stat(FileName, &st) == 0) {
00330      if (S_ISDIR(st.st_mode)) {
00331         DIR *d = opendir(FileName);
00332         if (d) {
00333            struct dirent *e;
00334            while ((e = readdir(d)) != NULL) {
00335                  if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
00336                     char *buffer;
00337                     asprintf(&buffer, "%s/%s", FileName, e->d_name);
00338                     if (FollowSymlinks) {
00339                        int size = strlen(buffer) * 2; // should be large enough
00340                        char *l = MALLOC(char, size);
00341                        int n = readlink(buffer, l, size);
00342                        if (n < 0) {
00343                           if (errno != EINVAL)
00344                              LOG_ERROR_STR(buffer);
00345                           }
00346                        else if (n < size) {
00347                           l[n] = 0;
00348                           dsyslog("removing %s", l);
00349                           if (remove(l) < 0)
00350                              LOG_ERROR_STR(l);
00351                           }
00352                        else
00353                           esyslog("ERROR: symlink name length (%d) exceeded anticipated buffer size (%d)", n, size);
00354                        free(l);
00355                        }
00356                     dsyslog("removing %s", buffer);
00357                     if (remove(buffer) < 0)
00358                        LOG_ERROR_STR(buffer);
00359                     free(buffer);
00360                     }
00361                  }
00362            closedir(d);
00363            }
00364         else {
00365            LOG_ERROR_STR(FileName);
00366            return false;
00367            }
00368         }
00369      dsyslog("removing %s", FileName);
00370      if (remove(FileName) < 0) {
00371         LOG_ERROR_STR(FileName);
00372         return false;
00373         }
00374      }
00375   else if (errno != ENOENT) {
00376      LOG_ERROR_STR(FileName);
00377      return false;
00378      }
00379   return true;
00380 }
00381 
00382 bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis)
00383 {
00384   DIR *d = opendir(DirName);
00385   if (d) {
00386      bool empty = true;
00387      struct dirent *e;
00388      while ((e = readdir(d)) != NULL) {
00389            if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..") && strcmp(e->d_name, "lost+found")) {
00390               char *buffer;
00391               asprintf(&buffer, "%s/%s", DirName, e->d_name);
00392               struct stat st;
00393               if (stat(buffer, &st) == 0) {
00394                  if (S_ISDIR(st.st_mode)) {
00395                     if (!RemoveEmptyDirectories(buffer, true))
00396                        empty = false;
00397                     }
00398                  else
00399                     empty = false;
00400                  }
00401               else {
00402                  LOG_ERROR_STR(buffer);
00403                  free(buffer);
00404                  return false;
00405                  }
00406               free(buffer);
00407               }
00408            }
00409      closedir(d);
00410      if (RemoveThis && empty) {
00411         dsyslog("removing %s", DirName);
00412         if (remove(DirName) < 0) {
00413            LOG_ERROR_STR(DirName);
00414            return false;
00415            }
00416         }
00417      return empty;
00418      }
00419   else
00420      LOG_ERROR_STR(DirName);
00421   return false;
00422 }
00423 
00424 char *ReadLink(const char *FileName)
00425 {
00426   char RealName[PATH_MAX];
00427   const char *TargetName = NULL;
00428   int n = readlink(FileName, RealName, sizeof(RealName) - 1);
00429   if (n < 0) {
00430      if (errno == ENOENT || errno == EINVAL) // file doesn't exist or is not a symlink
00431         TargetName = FileName;
00432      else // some other error occurred
00433         LOG_ERROR_STR(FileName);
00434      }
00435   else if (n < int(sizeof(RealName))) { // got it!
00436      RealName[n] = 0;
00437      TargetName = RealName;
00438      }
00439   else
00440      esyslog("ERROR: symlink's target name too long: %s", FileName);
00441   return TargetName ? strdup(TargetName) : NULL;
00442 }
00443 
00444 bool SpinUpDisk(const char *FileName)
00445 {
00446   static char *buf = NULL;
00447   for (int n = 0; n < 10; n++) {
00448       free(buf);
00449       if (DirectoryOk(FileName))
00450          asprintf(&buf, "%s/vdr-%06d", *FileName ? FileName : ".", n);
00451       else
00452          asprintf(&buf, "%s.vdr-%06d", FileName, n);
00453       if (access(buf, F_OK) != 0) { // the file does not exist
00454          timeval tp1, tp2;
00455          gettimeofday(&tp1, NULL);
00456          int f = open(buf, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
00457          // O_SYNC doesn't work on all file systems
00458          if (f >= 0) {
00459             close(f);
00460             system("sync");
00461             remove(buf);
00462             gettimeofday(&tp2, NULL);
00463             double seconds = (((long long)tp2.tv_sec * 1000000 + tp2.tv_usec) - ((long long)tp1.tv_sec * 1000000 + tp1.tv_usec)) / 1000000.0;
00464             if (seconds > 0.5)
00465                dsyslog("SpinUpDisk took %.2f seconds\n", seconds);
00466             return true;
00467             }
00468          else
00469             LOG_ERROR_STR(buf);
00470          }
00471       }
00472   esyslog("ERROR: SpinUpDisk failed");
00473   return false;
00474 }
00475 
00476 const char *WeekDayName(int WeekDay)
00477 {
00478   static char buffer[4];
00479   WeekDay = WeekDay == 0 ? 6 : WeekDay - 1; // we start with monday==0!
00480   if (0 <= WeekDay && WeekDay <= 6) {
00481      const char *day = tr("MonTueWedThuFriSatSun");
00482      day += WeekDay * 3;
00483      strncpy(buffer, day, 3);
00484      return buffer;
00485      }
00486   else
00487      return "???";
00488 }
00489 
00490 const char *DayDateTime(time_t t)
00491 {
00492   static char buffer[32];
00493   if (t == 0)
00494      time(&t);
00495   struct tm tm_r;
00496   tm *tm = localtime_r(&t, &tm_r);
00497   snprintf(buffer, sizeof(buffer), "%s %2d.%02d %02d:%02d", WeekDayName(tm->tm_wday), tm->tm_mday, tm->tm_mon + 1, tm->tm_hour, tm->tm_min);
00498   return buffer;
00499 }
00500 
00501 // --- cPoller ---------------------------------------------------------------
00502 
00503 cPoller::cPoller(int FileHandle, bool Out)
00504 {
00505   numFileHandles = 0;
00506   Add(FileHandle, Out);
00507 }
00508 
00509 bool cPoller::Add(int FileHandle, bool Out)
00510 {
00511   if (FileHandle >= 0) {
00512      for (int i = 0; i < numFileHandles; i++) {
00513          if (pfd[i].fd == FileHandle)
00514             return true;
00515          }
00516      if (numFileHandles < MaxPollFiles) {
00517         pfd[numFileHandles].fd = FileHandle;
00518         pfd[numFileHandles].events = Out ? POLLOUT : POLLIN;
00519         numFileHandles++;
00520         return true;
00521         }
00522      esyslog("ERROR: too many file handles in cPoller");
00523      }
00524   return false;
00525 }
00526 
00527 bool cPoller::Poll(int TimeoutMs)
00528 {
00529   if (numFileHandles) {
00530      if (poll(pfd, numFileHandles, TimeoutMs) != 0)
00531         return true; // returns true even in case of an error, to let the caller
00532                      // access the file and thus see the error code
00533      }
00534   return false;
00535 }
00536 
00537 // --- cFile -----------------------------------------------------------------
00538 
00539 bool cFile::files[FD_SETSIZE] = { false };
00540 int cFile::maxFiles = 0;
00541 
00542 cFile::cFile(void)
00543 {
00544   f = -1;
00545 }
00546 
00547 cFile::~cFile()
00548 {
00549   Close();
00550 }
00551 
00552 bool cFile::Open(const char *FileName, int Flags, mode_t Mode)
00553 {
00554   if (!IsOpen())
00555      return Open(open(FileName, Flags, Mode));
00556   esyslog("ERROR: attempt to re-open %s", FileName);
00557   return false;
00558 }
00559 
00560 bool cFile::Open(int FileDes)
00561 {
00562   if (FileDes >= 0) {
00563      if (!IsOpen()) {
00564         f = FileDes;
00565         if (f >= 0) {
00566            if (f < FD_SETSIZE) {
00567               if (f >= maxFiles)
00568                  maxFiles = f + 1;
00569               if (!files[f])
00570                  files[f] = true;
00571               else
00572                  esyslog("ERROR: file descriptor %d already in files[]", f);
00573               return true;
00574               }
00575            else
00576               esyslog("ERROR: file descriptor %d is larger than FD_SETSIZE (%d)", f, FD_SETSIZE);
00577            }
00578         }
00579      else
00580         esyslog("ERROR: attempt to re-open file descriptor %d", FileDes);
00581      }
00582   return false;
00583 }
00584 
00585 void cFile::Close(void)
00586 {
00587   if (f >= 0) {
00588      close(f);
00589      files[f] = false;
00590      f = -1;
00591      }
00592 }
00593 
00594 bool cFile::Ready(bool Wait)
00595 {
00596   return f >= 0 && AnyFileReady(f, Wait ? 1000 : 0);
00597 }
00598 
00599 bool cFile::AnyFileReady(int FileDes, int TimeoutMs)
00600 {
00601   fd_set set;
00602   FD_ZERO(&set);
00603   for (int i = 0; i < maxFiles; i++) {
00604       if (files[i])
00605          FD_SET(i, &set);
00606       }
00607   if (0 <= FileDes && FileDes < FD_SETSIZE && !files[FileDes])
00608      FD_SET(FileDes, &set); // in case we come in with an arbitrary descriptor
00609   if (TimeoutMs == 0)
00610      TimeoutMs = 10; // load gets too heavy with 0
00611   struct timeval timeout;
00612   timeout.tv_sec  = TimeoutMs / 1000;
00613   timeout.tv_usec = (TimeoutMs % 1000) * 1000;
00614   return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && (FileDes < 0 || FD_ISSET(FileDes, &set));
00615 }
00616 
00617 bool cFile::FileReady(int FileDes, int TimeoutMs)
00618 {
00619   fd_set set;
00620   struct timeval timeout;
00621   FD_ZERO(&set);
00622   FD_SET(FileDes, &set);
00623   if (TimeoutMs < 100)
00624      TimeoutMs = 100;
00625   timeout.tv_sec  = TimeoutMs / 1000;
00626   timeout.tv_usec = (TimeoutMs % 1000) * 1000;
00627   return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
00628 }
00629 
00630 bool cFile::FileReadyForWriting(int FileDes, int TimeoutMs)
00631 {
00632   fd_set set;
00633   struct timeval timeout;
00634   FD_ZERO(&set);
00635   FD_SET(FileDes, &set);
00636   if (TimeoutMs < 100)
00637      TimeoutMs = 100;
00638   timeout.tv_sec  = 0;
00639   timeout.tv_usec = TimeoutMs * 1000;
00640   return select(FD_SETSIZE, NULL, &set, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
00641 }
00642 
00643 // --- cSafeFile -------------------------------------------------------------
00644 
00645 cSafeFile::cSafeFile(const char *FileName)
00646 {
00647   f = NULL;
00648   fileName = ReadLink(FileName);
00649   tempName = fileName ? MALLOC(char, strlen(fileName) + 5) : NULL;
00650   if (tempName)
00651      strcat(strcpy(tempName, fileName), ".$$$");
00652 }
00653 
00654 cSafeFile::~cSafeFile()
00655 {
00656   if (f)
00657      fclose(f);
00658   unlink(tempName);
00659   free(fileName);
00660   free(tempName);
00661 }
00662 
00663 bool cSafeFile::Open(void)
00664 {
00665   if (!f && fileName && tempName) {
00666      f = fopen(tempName, "w");
00667      if (!f)
00668         LOG_ERROR_STR(tempName);
00669      }
00670   return f != NULL;
00671 }
00672 
00673 bool cSafeFile::Close(void)
00674 {
00675   bool result = true;
00676   if (f) {
00677      if (ferror(f) != 0) {
00678         LOG_ERROR_STR(tempName);
00679         result = false;
00680         }
00681      if (fclose(f) < 0) {
00682         LOG_ERROR_STR(tempName);
00683         result = false;
00684         }
00685      f = NULL;
00686      if (result && rename(tempName, fileName) < 0) {
00687         LOG_ERROR_STR(fileName);
00688         result = false;
00689         }
00690      }
00691   else
00692      result = false;
00693   return result;
00694 }
00695 
00696 // --- cLockFile -------------------------------------------------------------
00697 
00698 #define LOCKFILENAME      ".lock-vdr"
00699 #define LOCKFILESTALETIME 600 // seconds before considering a lock file "stale"
00700 
00701 cLockFile::cLockFile(const char *Directory)
00702 {
00703   fileName = NULL;
00704   f = -1;
00705   if (DirectoryOk(Directory))
00706      asprintf(&fileName, "%s/%s", Directory, LOCKFILENAME);
00707 }
00708 
00709 cLockFile::~cLockFile()
00710 {
00711   Unlock();
00712   free(fileName);
00713 }
00714 
00715 bool cLockFile::Lock(int WaitSeconds)
00716 {
00717   if (f < 0 && fileName) {
00718      time_t Timeout = time(NULL) + WaitSeconds;
00719      do {
00720         f = open(fileName, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
00721         if (f < 0) {
00722            if (errno == EEXIST) {
00723               struct stat fs;
00724               if (stat(fileName, &fs) == 0) {
00725                  if (abs(time(NULL) - fs.st_mtime) > LOCKFILESTALETIME) {
00726                     esyslog("ERROR: removing stale lock file '%s'", fileName);
00727                     if (remove(fileName) < 0) {
00728                        LOG_ERROR_STR(fileName);
00729                        break;
00730                        }
00731                     continue;
00732                     }
00733                  }
00734               else if (errno != ENOENT) {
00735                  LOG_ERROR_STR(fileName);
00736                  break;
00737                  }
00738               }
00739            else {
00740               LOG_ERROR_STR(fileName);
00741               break;
00742               }
00743            if (WaitSeconds)
00744               sleep(1);
00745            }
00746         } while (f < 0 && time(NULL) < Timeout);
00747      }
00748   return f >= 0;
00749 }
00750 
00751 void cLockFile::Unlock(void)
00752 {
00753   if (f >= 0) {
00754      close(f);
00755      remove(fileName);
00756      f = -1;
00757      }
00758 }
00759 
00760 // --- cListObject -----------------------------------------------------------
00761 
00762 cListObject::cListObject(void)
00763 {
00764   prev = next = NULL;
00765 }
00766 
00767 cListObject::~cListObject()
00768 {
00769 }
00770 
00771 void cListObject::Append(cListObject *Object)
00772 {
00773   next = Object;
00774   Object->prev = this;
00775 }
00776 
00777 void cListObject::Insert(cListObject *Object)
00778 {
00779   prev = Object;
00780   Object->next = this;
00781 }
00782 
00783 void cListObject::Unlink(void)
00784 {
00785   if (next)
00786      next->prev = prev;
00787   if (prev)
00788      prev->next = next;
00789   next = prev = NULL;
00790 }
00791 
00792 int cListObject::Index(void)
00793 {
00794   cListObject *p = prev;
00795   int i = 0;
00796 
00797   while (p) {
00798         i++;
00799         p = p->prev;
00800         }
00801   return i;
00802 }
00803 
00804 // --- cListBase -------------------------------------------------------------
00805 
00806 cListBase::cListBase(void)
00807 { 
00808   objects = lastObject = NULL;
00809 }
00810 
00811 cListBase::~cListBase()
00812 {
00813   Clear();
00814 }
00815 
00816 void cListBase::Add(cListObject *Object, cListObject *After)
00817 { 
00818   if (After && After != lastObject) {
00819      After->Next()->Insert(Object);
00820      After->Append(Object);
00821      }
00822   else {
00823      if (lastObject)
00824         lastObject->Append(Object);
00825      else
00826         objects = Object;
00827      lastObject = Object;
00828      }
00829 }
00830 
00831 void cListBase::Ins(cListObject *Object, cListObject *Before)
00832 { 
00833   if (Before && Before != objects) {
00834      Before->Prev()->Append(Object);
00835      Before->Insert(Object);
00836      }
00837   else {
00838      if (objects)
00839         objects->Insert(Object);
00840      else
00841         lastObject = Object;
00842      objects = Object;
00843      }
00844 }
00845 
00846 void cListBase::Del(cListObject *Object, bool DeleteObject)
00847 {
00848   if (Object == objects)
00849      objects = Object->Next();
00850   if (Object == lastObject)
00851      lastObject = Object->Prev();
00852   Object->Unlink();
00853   if (DeleteObject)
00854      delete Object;
00855 }
00856 
00857 void cListBase::Move(int From, int To)
00858 {
00859   Move(Get(From), Get(To));
00860 }
00861 
00862 void cListBase::Move(cListObject *From, cListObject *To)
00863 {
00864   if (From && To) {
00865      if (From->Index() < To->Index())
00866         To = To->Next();
00867      if (From == objects)
00868         objects = From->Next();
00869      if (From == lastObject)
00870         lastObject = From->Prev();
00871      From->Unlink();
00872      if (To) {
00873         if (To->Prev())
00874            To->Prev()->Append(From);
00875         From->Append(To);
00876         }
00877      else {
00878         lastObject->Append(From);
00879         lastObject = From;
00880         }
00881      if (!From->Prev())
00882         objects = From;
00883      }
00884 }
00885 
00886 void cListBase::Clear(void)
00887 {
00888   while (objects) {
00889         cListObject *object = objects->Next();
00890         delete objects;
00891         objects = object;
00892         }
00893   objects = lastObject = NULL;
00894 }
00895 
00896 cListObject *cListBase::Get(int Index) const
00897 {
00898   if (Index < 0)
00899      return NULL;
00900   cListObject *object = objects;
00901   while (object && Index-- > 0)
00902         object = object->Next();
00903   return object;
00904 }
00905 
00906 int cListBase::Count(void) const
00907 {
00908   int n = 0;
00909   cListObject *object = objects;
00910 
00911   while (object) {
00912         n++;
00913         object = object->Next();
00914         }
00915   return n;
00916 }
00917 
00918 void cListBase::Sort(void)
00919 {
00920   bool swapped;
00921   do {
00922      swapped = false;
00923      cListObject *object = objects;
00924      while (object) {
00925            if (object->Next() && *object->Next() < *object) {
00926               Move(object->Next(), object);
00927               swapped = true;
00928               }
00929            object = object->Next();
00930            }
00931      } while (swapped);
00932 }
00933 

Generated on Wed Feb 5 23:30:12 2003 for VDR by doxygen1.3-rc2