diff options
Diffstat (limited to 'timers.c')
-rw-r--r-- | timers.c | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/timers.c b/timers.c new file mode 100644 index 0000000..76f86a5 --- /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; +} |