Home

Dokumentation

Impressum

Dokumentation VDR
 

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

timers.c

Go to the documentation of this file.
00001 /*
00002  * timers.c: Timer handling
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: timers.c 1.3 2002/11/24 14:29:21 kls Exp $
00008  */
00009 
00010 #include "timers.h"
00011 #include <ctype.h>
00012 #include "channels.h"
00013 #include "i18n.h"
00014 
00015 // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
00016 // format characters in order to allow any number of blanks after a numeric
00017 // value!
00018 
00019 // -- cTimer -----------------------------------------------------------------
00020 
00021 char *cTimer::buffer = NULL;
00022 
00023 cTimer::cTimer(bool Instant)
00024 {
00025   startTime = stopTime = 0;
00026   recording = pending = false;
00027   active = Instant ? taActInst : taInactive;
00028   channel = Channels.GetByNumber(cDevice::CurrentChannel());
00029   time_t t = time(NULL);
00030   struct tm tm_r;
00031   struct tm *now = localtime_r(&t, &tm_r);
00032   day = now->tm_mday;
00033   start = now->tm_hour * 100 + now->tm_min;
00034   stop = now->tm_hour * 60 + now->tm_min + Setup.InstantRecordTime;
00035   stop = (stop / 60) * 100 + (stop % 60);
00036   if (stop >= 2400)
00037      stop -= 2400;
00038   priority = Setup.DefaultPriority;
00039   lifetime = Setup.DefaultLifetime;
00040   *file = 0;
00041   firstday = 0;
00042   summary = NULL;
00043   if (Instant && channel)
00044      snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
00045 }
00046 
00047 cTimer::cTimer(const cEventInfo *EventInfo)
00048 {
00049   startTime = stopTime = 0;
00050   recording = pending = false;
00051   active = true;
00052   channel = Channels.GetByChannelID(EventInfo->GetChannelID(), true);
00053   time_t tstart = EventInfo->GetTime();
00054   time_t tstop = tstart + EventInfo->GetDuration() + Setup.MarginStop * 60;
00055   tstart -= Setup.MarginStart * 60;
00056   struct tm tm_r;
00057   struct tm *time = localtime_r(&tstart, &tm_r);
00058   day = time->tm_mday;
00059   start = time->tm_hour * 100 + time->tm_min;
00060   time = localtime_r(&tstop, &tm_r);
00061   stop = time->tm_hour * 100 + time->tm_min;
00062   if (stop >= 2400)
00063      stop -= 2400;
00064   priority = Setup.DefaultPriority;
00065   lifetime = Setup.DefaultLifetime;
00066   *file = 0;
00067   const char *Title = EventInfo->GetTitle();
00068   if (!isempty(Title))
00069      strn0cpy(file, EventInfo->GetTitle(), sizeof(file));
00070   firstday = 0;
00071   summary = NULL;
00072 }
00073 
00074 cTimer::~cTimer()
00075 {
00076   free(summary);
00077 }
00078 
00079 cTimer& cTimer::operator= (const cTimer &Timer)
00080 {
00081   memcpy(this, &Timer, sizeof(*this));
00082   if (summary)
00083      summary = strdup(summary);
00084   return *this;
00085 }
00086 
00087 bool cTimer::operator< (const cListObject &ListObject)
00088 {
00089   cTimer *ti = (cTimer *)&ListObject;
00090   time_t t1 = StartTime();
00091   time_t t2 = ti->StartTime();
00092   return t1 < t2 || (t1 == t2 && priority > ti->priority);
00093 }
00094 
00095 const char *cTimer::ToText(bool UseChannelID)
00096 {
00097   free(buffer);
00098   strreplace(file, ':', '|');
00099   strreplace(summary, '\n', '|');
00100   asprintf(&buffer, "%d:%s:%s:%04d:%04d:%d:%d:%s:%s\n", active, UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number()), PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
00101   strreplace(summary, '|', '\n');
00102   strreplace(file, '|', ':');
00103   return buffer;
00104 }
00105 
00106 int cTimer::TimeToInt(int t)
00107 {
00108   return (t / 100 * 60 + t % 100) * 60;
00109 }
00110 
00111 int cTimer::ParseDay(const char *s, time_t *FirstDay)
00112 {
00113   char *tail;
00114   int d = strtol(s, &tail, 10);
00115   if (FirstDay)
00116      *FirstDay = 0;
00117   if (tail && *tail) {
00118      d = 0;
00119      if (tail == s) {
00120         const char *first = strchr(s, '@');
00121         int l = first ? first - s : strlen(s);
00122         if (l == 7) {
00123            for (const char *p = s + 6; p >= s; p--) {
00124                d <<= 1;
00125                d |= (*p != '-');
00126                }
00127            d |= 0x80000000;
00128            }
00129         if (FirstDay && first) {
00130            ++first;
00131            if (strlen(first) == 10) {
00132               struct tm tm_r;
00133               if (3 == sscanf(first, "%d-%d-%d", &tm_r.tm_year, &tm_r.tm_mon, &tm_r.tm_mday)) {
00134                  tm_r.tm_year -= 1900;
00135                  tm_r.tm_mon--;
00136                  tm_r.tm_hour = tm_r.tm_min = tm_r.tm_sec = 0;
00137                  tm_r.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
00138                  *FirstDay = mktime(&tm_r);
00139                  }
00140               }
00141            else
00142               d = 0;
00143            }
00144         }
00145      }
00146   else if (d < 1 || d > 31)
00147      d = 0;
00148   return d;
00149 }
00150 
00151 const char *cTimer::PrintDay(int d, time_t FirstDay)
00152 {
00153 #define DAYBUFFERSIZE 32
00154   static char buffer[DAYBUFFERSIZE];
00155   if ((d & 0x80000000) != 0) {
00156      char *b = buffer;
00157      const char *w = tr("MTWTFSS");
00158      while (*w) {
00159            *b++ = (d & 1) ? *w : '-';
00160            d >>= 1;
00161            w++;
00162            }
00163      if (FirstDay) {
00164         struct tm tm_r;
00165         localtime_r(&FirstDay, &tm_r);
00166         b += strftime(b, DAYBUFFERSIZE - (b - buffer), "@%Y-%m-%d", &tm_r);
00167         }
00168      *b = 0;
00169      }
00170   else
00171      sprintf(buffer, "%d", d);
00172   return buffer;
00173 }
00174 
00175 const char *cTimer::PrintFirstDay(void)
00176 {
00177   if (firstday) {
00178      const char *s = PrintDay(day, firstday);
00179      if (strlen(s) == 18)
00180         return s + 8;
00181      }
00182   return ""; // not NULL, so the caller can always use the result
00183 }
00184 
00185 bool cTimer::Parse(const char *s)
00186 {
00187   char *channelbuffer = NULL;
00188   char *daybuffer = NULL;
00189   char *filebuffer = NULL;
00190   free(summary);
00191   summary = NULL;
00192   //XXX Apparently sscanf() doesn't work correctly if the last %a argument
00193   //XXX results in an empty string (this first occured when the EIT gathering
00194   //XXX was put into a separate thread - don't know why this happens...
00195   //XXX As a cure we copy the original string and add a blank.
00196   //XXX If anybody can shed some light on why sscanf() failes here, I'd love
00197   //XXX to hear about that!
00198   char *s2 = NULL;
00199   int l2 = strlen(s);
00200   while (l2 > 0 && isspace(s[l2 - 1]))
00201         l2--;
00202   if (s[l2 - 1] == ':') {
00203      s2 = MALLOC(char, l2 + 3);
00204      strcat(strn0cpy(s2, s, l2 + 1), " \n");
00205      s = s2;
00206      }
00207   bool result = false;
00208   if (8 <= sscanf(s, "%d :%a[^:]:%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &active, &channelbuffer, &daybuffer, &start, &stop, &priority, &lifetime, &filebuffer, &summary)) {
00209      if (summary && !*skipspace(summary)) {
00210         free(summary);
00211         summary = NULL;
00212         }
00213      //TODO add more plausibility checks
00214      day = ParseDay(daybuffer, &firstday);
00215      result = day != 0;
00216      strn0cpy(file, filebuffer, MaxFileName);
00217      strreplace(file, '|', ':');
00218      strreplace(summary, '|', '\n');
00219      tChannelID cid = tChannelID::FromString(channelbuffer);
00220      channel = cid.Valid() ? Channels.GetByChannelID(cid) : Channels.GetByNumber(atoi(channelbuffer));
00221      if (!channel) {
00222         esyslog("ERROR: channel %s not defined", channelbuffer);
00223         result = false;
00224         }
00225      }
00226   free(channelbuffer);
00227   free(daybuffer);
00228   free(filebuffer);
00229   free(s2);
00230   return result;
00231 }
00232 
00233 bool cTimer::Save(FILE *f)
00234 {
00235   return fprintf(f, ToText(true)) > 0;
00236 }
00237 
00238 bool cTimer::IsSingleEvent(void)
00239 {
00240   return (day & 0x80000000) == 0;
00241 }
00242 
00243 int cTimer::GetMDay(time_t t)
00244 {
00245   struct tm tm_r;
00246   return localtime_r(&t, &tm_r)->tm_mday;
00247 }
00248 
00249 int cTimer::GetWDay(time_t t)
00250 {
00251   struct tm tm_r;
00252   int weekday = localtime_r(&t, &tm_r)->tm_wday;
00253   return weekday == 0 ? 6 : weekday - 1; // we start with monday==0!
00254 }
00255 
00256 bool cTimer::DayMatches(time_t t)
00257 {
00258   return IsSingleEvent() ? GetMDay(t) == day : (day & (1 << GetWDay(t))) != 0;
00259 }
00260 
00261 time_t cTimer::IncDay(time_t t, int Days)
00262 {
00263   struct tm tm_r;
00264   tm tm = *localtime_r(&t, &tm_r);
00265   tm.tm_mday += Days; // now tm_mday may be out of its valid range
00266   int h = tm.tm_hour; // save original hour to compensate for DST change
00267   tm.tm_isdst = -1;   // makes sure mktime() will determine the correct DST setting
00268   t = mktime(&tm);    // normalize all values
00269   tm.tm_hour = h;     // compensate for DST change
00270   return mktime(&tm); // calculate final result
00271 }
00272 
00273 time_t cTimer::SetTime(time_t t, int SecondsFromMidnight)
00274 {
00275   struct tm tm_r;
00276   tm tm = *localtime_r(&t, &tm_r);
00277   tm.tm_hour = SecondsFromMidnight / 3600;
00278   tm.tm_min = (SecondsFromMidnight % 3600) / 60;
00279   tm.tm_sec =  SecondsFromMidnight % 60;
00280   tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
00281   return mktime(&tm);
00282 }
00283 
00284 char *cTimer::SetFile(const char *File)
00285 {
00286   if (!isempty(File))
00287      strn0cpy(file, File, sizeof(file));
00288   return file;
00289 }
00290 
00291 bool cTimer::Matches(time_t t)
00292 {
00293   startTime = stopTime = 0;
00294   if (t == 0)
00295      t = time(NULL);
00296 
00297   int begin  = TimeToInt(start); // seconds from midnight
00298   int length = TimeToInt(stop) - begin;
00299   if (length < 0)
00300      length += SECSINDAY;
00301 
00302   int DaysToCheck = IsSingleEvent() ? 61 : 7; // 61 to handle months with 31/30/31
00303   for (int i = -1; i <= DaysToCheck; i++) {
00304       time_t t0 = IncDay(t, i);
00305       if (DayMatches(t0)) {
00306          time_t a = SetTime(t0, begin);
00307          time_t b = a + length;
00308          if ((!firstday || a >= firstday) && t <= b) {
00309             startTime = a;
00310             stopTime = b;
00311             break;
00312             }
00313          }
00314       }
00315   if (!startTime)
00316      startTime = firstday; // just to have something that's more than a week in the future
00317   else if (t > startTime || t > firstday + SECSINDAY + 3600) // +3600 in case of DST change
00318      firstday = 0;
00319   return active && startTime <= t && t < stopTime; // must stop *before* stopTime to allow adjacent timers
00320 }
00321 
00322 time_t cTimer::StartTime(void)
00323 {
00324   if (!startTime)
00325      Matches();
00326   return startTime;
00327 }
00328 
00329 time_t cTimer::StopTime(void)
00330 {
00331   if (!stopTime)
00332      Matches();
00333   return stopTime;
00334 }
00335 
00336 void cTimer::SetRecording(bool Recording)
00337 {
00338   recording = Recording;
00339   isyslog("timer %d %s", Index() + 1, recording ? "start" : "stop");
00340 }
00341 
00342 void cTimer::SetPending(bool Pending)
00343 {
00344   pending = Pending;
00345 }
00346 
00347 void cTimer::SetActive(int Active)
00348 {
00349   active = Active;
00350 }
00351 
00352 void cTimer::Skip(void)
00353 {
00354   firstday = IncDay(SetTime(StartTime(), 0), 1);
00355 }
00356 
00357 void cTimer::OnOff(void)
00358 {
00359   if (IsSingleEvent())
00360      active = !active;
00361   else if (firstday) {
00362      firstday = 0;
00363      active = false;
00364      }
00365   else if (active)
00366      Skip();
00367   else
00368      active = true;
00372   Matches();
00373 }
00374 
00379 cTimers Timers;
00380 
00381 cTimer *cTimers::GetTimer(cTimer *Timer)
00382 {
00383   for (cTimer *ti = First(); ti; ti = Next(ti)) {
00384       if (ti->Channel() == Timer->Channel() && ti->Day() == Timer->Day() && ti->Start() == Timer->Start() && ti->Stop() == Timer->Stop())
00385          return ti;
00386       }
00387   return NULL;
00388 }
00389 
00390 cTimer *cTimers::GetMatch(time_t t)
00391 {
00392   cTimer *t0 = NULL;
00393   for (cTimer *ti = First(); ti; ti = Next(ti)) {
00394       if (!ti->Recording() && ti->Matches(t)) {
00395          if (!t0 || ti->Priority() > t0->Priority())
00396             t0 = ti;
00397          }
00398       }
00399   return t0;
00400 }
00401 
00402 cTimer *cTimers::GetNextActiveTimer(void)
00403 {
00404   cTimer *t0 = NULL;
00405   for (cTimer *ti = First(); ti; ti = Next(ti)) {
00406       if (ti->Active() && (!t0 || *ti < *t0))
00407          t0 = ti;
00408       }
00409   return t0;
00410 }

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