summaryrefslogtreecommitdiff
path: root/timers.c
diff options
context:
space:
mode:
Diffstat (limited to 'timers.c')
-rw-r--r--timers.c408
1 files changed, 408 insertions, 0 deletions
diff --git a/timers.c b/timers.c
new file mode 100644
index 00000000..76f86a5e
--- /dev/null
+++ b/timers.c
@@ -0,0 +1,408 @@
+/*
+ * timers.c: Timer handling
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: timers.c 1.1 2002/10/20 12:28:55 kls Exp $
+ */
+
+#include "timers.h"
+#include <ctype.h>
+#include "channels.h"
+#include "i18n.h"
+
+// IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
+// format characters in order to allow any number of blanks after a numeric
+// value!
+
+// -- cTimer -----------------------------------------------------------------
+
+char *cTimer::buffer = NULL;
+
+cTimer::cTimer(bool Instant)
+{
+ startTime = stopTime = 0;
+ recording = pending = false;
+ active = Instant ? taActInst : taInactive;
+ channel = Channels.GetByNumber(cDevice::CurrentChannel());
+ time_t t = time(NULL);
+ struct tm tm_r;
+ struct tm *now = localtime_r(&t, &tm_r);
+ day = now->tm_mday;
+ start = now->tm_hour * 100 + now->tm_min;
+ stop = now->tm_hour * 60 + now->tm_min + Setup.InstantRecordTime;
+ stop = (stop / 60) * 100 + (stop % 60);
+ if (stop >= 2400)
+ stop -= 2400;
+ priority = Setup.DefaultPriority;
+ lifetime = Setup.DefaultLifetime;
+ *file = 0;
+ firstday = 0;
+ summary = NULL;
+ if (Instant && channel)
+ snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
+}
+
+cTimer::cTimer(const cEventInfo *EventInfo)
+{
+ startTime = stopTime = 0;
+ recording = pending = false;
+ active = true;
+ channel = Channels.GetByServiceID(EventInfo->GetServiceID());
+ time_t tstart = EventInfo->GetTime();
+ time_t tstop = tstart + EventInfo->GetDuration() + Setup.MarginStop * 60;
+ tstart -= Setup.MarginStart * 60;
+ struct tm tm_r;
+ struct tm *time = localtime_r(&tstart, &tm_r);
+ day = time->tm_mday;
+ start = time->tm_hour * 100 + time->tm_min;
+ time = localtime_r(&tstop, &tm_r);
+ stop = time->tm_hour * 100 + time->tm_min;
+ if (stop >= 2400)
+ stop -= 2400;
+ priority = Setup.DefaultPriority;
+ lifetime = Setup.DefaultLifetime;
+ *file = 0;
+ const char *Title = EventInfo->GetTitle();
+ if (!isempty(Title))
+ strn0cpy(file, EventInfo->GetTitle(), sizeof(file));
+ firstday = 0;
+ summary = NULL;
+}
+
+cTimer::~cTimer()
+{
+ free(summary);
+}
+
+cTimer& cTimer::operator= (const cTimer &Timer)
+{
+ memcpy(this, &Timer, sizeof(*this));
+ if (summary)
+ summary = strdup(summary);
+ return *this;
+}
+
+bool cTimer::operator< (const cListObject &ListObject)
+{
+ cTimer *ti = (cTimer *)&ListObject;
+ time_t t1 = StartTime();
+ time_t t2 = ti->StartTime();
+ return t1 < t2 || (t1 == t2 && priority > ti->priority);
+}
+
+const char *cTimer::ToText(cTimer *Timer)
+{
+ free(buffer);
+ strreplace(Timer->file, ':', '|');
+ strreplace(Timer->summary, '\n', '|');
+ asprintf(&buffer, "%d:%d:%s:%04d:%04d:%d:%d:%s:%s\n", Timer->active, Timer->Channel()->Number(), PrintDay(Timer->day, Timer->firstday), Timer->start, Timer->stop, Timer->priority, Timer->lifetime, Timer->file, Timer->summary ? Timer->summary : "");
+ strreplace(Timer->summary, '|', '\n');
+ strreplace(Timer->file, '|', ':');
+ return buffer;
+}
+
+const char *cTimer::ToText(void)
+{
+ return ToText(this);
+}
+
+int cTimer::TimeToInt(int t)
+{
+ return (t / 100 * 60 + t % 100) * 60;
+}
+
+int cTimer::ParseDay(const char *s, time_t *FirstDay)
+{
+ char *tail;
+ int d = strtol(s, &tail, 10);
+ if (FirstDay)
+ *FirstDay = 0;
+ if (tail && *tail) {
+ d = 0;
+ if (tail == s) {
+ const char *first = strchr(s, '@');
+ int l = first ? first - s : strlen(s);
+ if (l == 7) {
+ for (const char *p = s + 6; p >= s; p--) {
+ d <<= 1;
+ d |= (*p != '-');
+ }
+ d |= 0x80000000;
+ }
+ if (FirstDay && first) {
+ ++first;
+ if (strlen(first) == 10) {
+ struct tm tm_r;
+ if (3 == sscanf(first, "%d-%d-%d", &tm_r.tm_year, &tm_r.tm_mon, &tm_r.tm_mday)) {
+ tm_r.tm_year -= 1900;
+ tm_r.tm_mon--;
+ tm_r.tm_hour = tm_r.tm_min = tm_r.tm_sec = 0;
+ tm_r.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
+ *FirstDay = mktime(&tm_r);
+ }
+ }
+ else
+ d = 0;
+ }
+ }
+ }
+ else if (d < 1 || d > 31)
+ d = 0;
+ return d;
+}
+
+const char *cTimer::PrintDay(int d, time_t FirstDay)
+{
+#define DAYBUFFERSIZE 32
+ static char buffer[DAYBUFFERSIZE];
+ if ((d & 0x80000000) != 0) {
+ char *b = buffer;
+ const char *w = tr("MTWTFSS");
+ while (*w) {
+ *b++ = (d & 1) ? *w : '-';
+ d >>= 1;
+ w++;
+ }
+ if (FirstDay) {
+ struct tm tm_r;
+ localtime_r(&FirstDay, &tm_r);
+ b += strftime(b, DAYBUFFERSIZE - (b - buffer), "@%Y-%m-%d", &tm_r);
+ }
+ *b = 0;
+ }
+ else
+ sprintf(buffer, "%d", d);
+ return buffer;
+}
+
+const char *cTimer::PrintFirstDay(void)
+{
+ if (firstday) {
+ const char *s = PrintDay(day, firstday);
+ if (strlen(s) == 18)
+ return s + 8;
+ }
+ return ""; // not NULL, so the caller can always use the result
+}
+
+bool cTimer::Parse(const char *s)
+{
+ char *buffer1 = NULL;
+ char *buffer2 = NULL;
+ free(summary);
+ summary = NULL;
+ //XXX Apparently sscanf() doesn't work correctly if the last %a argument
+ //XXX results in an empty string (this first occured when the EIT gathering
+ //XXX was put into a separate thread - don't know why this happens...
+ //XXX As a cure we copy the original string and add a blank.
+ //XXX If anybody can shed some light on why sscanf() failes here, I'd love
+ //XXX to hear about that!
+ char *s2 = NULL;
+ int l2 = strlen(s);
+ while (l2 > 0 && isspace(s[l2 - 1]))
+ l2--;
+ if (s[l2 - 1] == ':') {
+ s2 = MALLOC(char, l2 + 3);
+ strcat(strn0cpy(s2, s, l2 + 1), " \n");
+ s = s2;
+ }
+ int ch = 0;
+ if (8 <= sscanf(s, "%d :%d :%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &active, &ch, &buffer1, &start, &stop, &priority, &lifetime, &buffer2, &summary)) {
+ if (summary && !*skipspace(summary)) {
+ free(summary);
+ summary = NULL;
+ }
+ //TODO add more plausibility checks
+ day = ParseDay(buffer1, &firstday);
+ strn0cpy(file, buffer2, MaxFileName);
+ strreplace(file, '|', ':');
+ strreplace(summary, '|', '\n');
+ free(buffer1);
+ free(buffer2);
+ free(s2);
+ channel = Channels.GetByNumber(ch);
+ if (!channel) {
+ esyslog("ERROR: channel %d not defined", ch);
+ return false;
+ }
+ return day != 0;
+ }
+ free(s2);
+ return false;
+}
+
+bool cTimer::Save(FILE *f)
+{
+ return fprintf(f, ToText()) > 0;
+}
+
+bool cTimer::IsSingleEvent(void)
+{
+ return (day & 0x80000000) == 0;
+}
+
+int cTimer::GetMDay(time_t t)
+{
+ struct tm tm_r;
+ return localtime_r(&t, &tm_r)->tm_mday;
+}
+
+int cTimer::GetWDay(time_t t)
+{
+ struct tm tm_r;
+ int weekday = localtime_r(&t, &tm_r)->tm_wday;
+ return weekday == 0 ? 6 : weekday - 1; // we start with monday==0!
+}
+
+bool cTimer::DayMatches(time_t t)
+{
+ return IsSingleEvent() ? GetMDay(t) == day : (day & (1 << GetWDay(t))) != 0;
+}
+
+time_t cTimer::IncDay(time_t t, int Days)
+{
+ struct tm tm_r;
+ tm tm = *localtime_r(&t, &tm_r);
+ tm.tm_mday += Days; // now tm_mday may be out of its valid range
+ int h = tm.tm_hour; // save original hour to compensate for DST change
+ tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
+ t = mktime(&tm); // normalize all values
+ tm.tm_hour = h; // compensate for DST change
+ return mktime(&tm); // calculate final result
+}
+
+time_t cTimer::SetTime(time_t t, int SecondsFromMidnight)
+{
+ struct tm tm_r;
+ tm tm = *localtime_r(&t, &tm_r);
+ tm.tm_hour = SecondsFromMidnight / 3600;
+ tm.tm_min = (SecondsFromMidnight % 3600) / 60;
+ tm.tm_sec = SecondsFromMidnight % 60;
+ tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
+ return mktime(&tm);
+}
+
+char *cTimer::SetFile(const char *File)
+{
+ if (!isempty(File))
+ strn0cpy(file, File, sizeof(file));
+ return file;
+}
+
+bool cTimer::Matches(time_t t)
+{
+ startTime = stopTime = 0;
+ if (t == 0)
+ t = time(NULL);
+
+ int begin = TimeToInt(start); // seconds from midnight
+ int length = TimeToInt(stop) - begin;
+ if (length < 0)
+ length += SECSINDAY;
+
+ int DaysToCheck = IsSingleEvent() ? 61 : 7; // 61 to handle months with 31/30/31
+ for (int i = -1; i <= DaysToCheck; i++) {
+ time_t t0 = IncDay(t, i);
+ if (DayMatches(t0)) {
+ time_t a = SetTime(t0, begin);
+ time_t b = a + length;
+ if ((!firstday || a >= firstday) && t <= b) {
+ startTime = a;
+ stopTime = b;
+ break;
+ }
+ }
+ }
+ if (!startTime)
+ startTime = firstday; // just to have something that's more than a week in the future
+ else if (t > startTime || t > firstday + SECSINDAY + 3600) // +3600 in case of DST change
+ firstday = 0;
+ return active && startTime <= t && t < stopTime; // must stop *before* stopTime to allow adjacent timers
+}
+
+time_t cTimer::StartTime(void)
+{
+ if (!startTime)
+ Matches();
+ return startTime;
+}
+
+time_t cTimer::StopTime(void)
+{
+ if (!stopTime)
+ Matches();
+ return stopTime;
+}
+
+void cTimer::SetRecording(bool Recording)
+{
+ recording = Recording;
+ isyslog("timer %d %s", Index() + 1, recording ? "start" : "stop");
+}
+
+void cTimer::SetPending(bool Pending)
+{
+ pending = Pending;
+}
+
+void cTimer::SetActive(int Active)
+{
+ active = Active;
+}
+
+void cTimer::Skip(void)
+{
+ firstday = IncDay(SetTime(StartTime(), 0), 1);
+}
+
+void cTimer::OnOff(void)
+{
+ if (IsSingleEvent())
+ active = !active;
+ else if (firstday) {
+ firstday = 0;
+ active = false;
+ }
+ else if (active)
+ Skip();
+ else
+ active = true;
+ Matches(); // refresh start and end time
+}
+
+// -- cTimers ----------------------------------------------------------------
+
+cTimers Timers;
+
+cTimer *cTimers::GetTimer(cTimer *Timer)
+{
+ for (cTimer *ti = First(); ti; ti = Next(ti)) {
+ if (ti->Channel() == Timer->Channel() && ti->Day() == Timer->Day() && ti->Start() == Timer->Start() && ti->Stop() == Timer->Stop())
+ return ti;
+ }
+ return NULL;
+}
+
+cTimer *cTimers::GetMatch(time_t t)
+{
+ cTimer *t0 = NULL;
+ for (cTimer *ti = First(); ti; ti = Next(ti)) {
+ if (!ti->Recording() && ti->Matches(t)) {
+ if (!t0 || ti->Priority() > t0->Priority())
+ t0 = ti;
+ }
+ }
+ return t0;
+}
+
+cTimer *cTimers::GetNextActiveTimer(void)
+{
+ cTimer *t0 = NULL;
+ for (cTimer *ti = First(); ti; ti = Next(ti)) {
+ if (ti->Active() && (!t0 || *ti < *t0))
+ t0 = ti;
+ }
+ return t0;
+}