summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlaus Schmidinger <vdr@tvdr.de>2002-10-20 12:28:55 +0200
committerKlaus Schmidinger <vdr@tvdr.de>2002-10-20 12:28:55 +0200
commitac9622bb8ad8d660fca4e77460c970f72c344afa (patch)
tree9738265846abf2c1a491cfec77a4e55f434297be
parentab4ceb29a033f8a3cc051d5ea9a6f20ca6e75f8a (diff)
downloadvdr-ac9622bb8ad8d660fca4e77460c970f72c344afa.tar.gz
vdr-ac9622bb8ad8d660fca4e77460c970f72c344afa.tar.bz2
Timers now internally have a pointer to their channel
-rw-r--r--HISTORY4
-rw-r--r--Makefile4
-rw-r--r--channels.c8
-rw-r--r--channels.h3
-rw-r--r--config.c377
-rw-r--r--config.h61
-rw-r--r--menu.c172
-rw-r--r--recording.c20
-rw-r--r--recording.h3
-rw-r--r--svdrp.c11
-rw-r--r--timers.c408
-rw-r--r--timers.h91
-rw-r--r--vdr.c7
13 files changed, 596 insertions, 573 deletions
diff --git a/HISTORY b/HISTORY
index df92d6b4..83f9e917 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1611,7 +1611,7 @@ Video Disk Recorder Revision History
shall be executed from the "Recordings" menu; see MANUAL and 'man vdr(5)' for
details (suggested by Gerhard Steiner).
-2002-10-19: Version 1.1.14
+2002-10-20: Version 1.1.14
- Fixed some faulty default parameter initializations (thanks to Robert Schiele).
- Added further satellites to 'sources.conf' (thanks to Reinhard Walter Buchner).
@@ -1624,3 +1624,5 @@ Video Disk Recorder Revision History
used to create 'gaps' in the channel numbering (see 'man 5 vdr'). BE CAREFUL
TO UPDATE YOUR 'timers.conf' ACCORDINGLY IF INSERTING THIS NEW FEATURE INTO YOUR
'channels.conf' FILE!
+- Timers now internally have a pointer to their channel (this is necessary to
+ handle gaps in channel numbers, and in preparation for unique channel ids).
diff --git a/Makefile b/Makefile
index 7e45d5c6..7e44971c 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: Makefile 1.48 2002/10/04 14:29:14 kls Exp $
+# $Id: Makefile 1.49 2002/10/19 15:46:08 kls Exp $
.DELETE_ON_ERROR:
@@ -36,7 +36,7 @@ OBJS = audio.o channels.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbosd
dvbplayer.o dvbspu.o eit.o eitscan.o font.o i18n.o interface.o keys.o\
lirc.o menu.o menuitems.o osdbase.o osd.o player.o plugin.o rcu.o\
receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sources.o\
- spu.o status.o svdrp.o thread.o tools.o transfer.o vdr.o videodir.o
+ spu.o status.o svdrp.o thread.o timers.o tools.o transfer.o vdr.o videodir.o
OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
diff --git a/channels.c b/channels.c
index 5dd4b742..e142a28f 100644
--- a/channels.c
+++ b/channels.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: channels.c 1.4 2002/10/19 14:46:05 kls Exp $
+ * $Id: channels.c 1.5 2002/10/20 11:50:47 kls Exp $
*/
#include "channels.h"
@@ -450,9 +450,3 @@ bool cChannels::SwitchTo(int Number)
cChannel *channel = GetByNumber(Number);
return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true);
}
-
-const char *cChannels::GetChannelNameByNumber(int Number)
-{
- cChannel *channel = GetByNumber(Number);
- return channel ? channel->Name() : NULL;
-}
diff --git a/channels.h b/channels.h
index d014cf3a..7eb818c3 100644
--- a/channels.h
+++ b/channels.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: channels.h 1.2 2002/10/19 11:48:02 kls Exp $
+ * $Id: channels.h 1.3 2002/10/20 11:50:36 kls Exp $
*/
#ifndef __CHANNELS_H
@@ -114,7 +114,6 @@ public:
void ReNumber(void); // Recalculate 'number' based on channel type
cChannel *GetByNumber(int Number, int SkipGap = 0);
cChannel *GetByServiceID(unsigned short ServiceId);
- const char *GetChannelNameByNumber(int Number);
bool SwitchTo(int Number);
int MaxNumber(void) { return maxNumber; }
};
diff --git a/config.c b/config.c
index f4ef7b86..7cea98ed 100644
--- a/config.c
+++ b/config.c
@@ -4,13 +4,12 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.c 1.110 2002/10/19 11:34:01 kls Exp $
+ * $Id: config.c 1.111 2002/10/19 15:49:51 kls Exp $
*/
#include "config.h"
#include <ctype.h>
#include <stdlib.h>
-#include "channels.h" //XXX timers!
#include "i18n.h"
#include "interface.h"
#include "plugin.h"
@@ -20,339 +19,6 @@
// 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;
- cChannel *ch = Channels.GetByNumber(cDevice::CurrentChannel());
- channel = ch ? ch->Number() : 0;
- 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;
-//TODO VPS???
- priority = Setup.DefaultPriority;
- lifetime = Setup.DefaultLifetime;
- *file = 0;
- firstday = 0;
- summary = NULL;
- if (Instant && ch)
- snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : ch->Name());
-}
-
-cTimer::cTimer(const cEventInfo *EventInfo)
-{
- startTime = stopTime = 0;
- recording = pending = false;
- active = true;
- cChannel *ch = Channels.GetByServiceID(EventInfo->GetServiceID());
- channel = ch ? ch->Number() : 0;
- 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, 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;
- }
- if (8 <= sscanf(s, "%d :%d :%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &active, &channel, &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);
- 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::Skip(void)
-{
- firstday = IncDay(SetTime(StartTime(), 0), 1);
-}
-
// --- cCommand -------------------------------------------------------------
char *cCommand::result = NULL;
@@ -476,47 +142,6 @@ bool cCaDefinition::Parse(const char *s)
cCommands Commands;
cCommands RecordingCommands;
-// -- cTimers ----------------------------------------------------------------
-
-cTimers Timers;
-
-cTimer *cTimers::GetTimer(cTimer *Timer)
-{
- cTimer *ti = (cTimer *)First();
- while (ti) {
- if (ti->channel == Timer->channel && ti->day == Timer->day && ti->start == Timer->start && ti->stop == Timer->stop)
- return ti;
- ti = (cTimer *)ti->Next();
- }
- return NULL;
-}
-
-cTimer *cTimers::GetMatch(time_t t)
-{
- cTimer *t0 = NULL;
- cTimer *ti = First();
- while (ti) {
- if (!ti->recording && ti->Matches(t)) {
- if (!t0 || ti->priority > t0->priority)
- t0 = ti;
- }
- ti = (cTimer *)ti->Next();
- }
- return t0;
-}
-
-cTimer *cTimers::GetNextActiveTimer(void)
-{
- cTimer *t0 = NULL;
- cTimer *ti = First();
- while (ti) {
- if (ti->active && (!t0 || *ti < *t0))
- t0 = ti;
- ti = (cTimer *)ti->Next();
- }
- return t0;
-}
-
// -- cSVDRPhosts ------------------------------------------------------------
cSVDRPhosts SVDRPhosts;
diff --git a/config.h b/config.h
index a2223d6b..0cac7b9d 100644
--- a/config.h
+++ b/config.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.h 1.136 2002/10/19 11:29:46 kls Exp $
+ * $Id: config.h 1.137 2002/10/19 15:43:31 kls Exp $
*/
#ifndef __CONFIG_H
@@ -32,57 +32,6 @@
#define MaxFileName 256
-enum eTimerActive { taInactive = 0,
- taActive = 1,
- taInstant = 2,
- taActInst = (taActive | taInstant)
- };
-
-class cTimer : public cListObject {
-private:
- time_t startTime, stopTime;
- static char *buffer;
- static const char *ToText(cTimer *Timer);
-public:
- bool recording, pending;
- int active;
- int channel;
- int day;
- int start;
- int stop;
-//TODO VPS???
- int priority;
- int lifetime;
- char file[MaxFileName];
- time_t firstday;
- char *summary;
- cTimer(bool Instant = false);
- cTimer(const cEventInfo *EventInfo);
- virtual ~cTimer();
- cTimer& operator= (const cTimer &Timer);
- virtual bool operator< (const cListObject &ListObject);
- const char *ToText(void);
- bool Parse(const char *s);
- bool Save(FILE *f);
- bool IsSingleEvent(void);
- int GetMDay(time_t t);
- int GetWDay(time_t t);
- bool DayMatches(time_t t);
- static time_t IncDay(time_t t, int Days);
- static time_t SetTime(time_t t, int SecondsFromMidnight);
- char *SetFile(const char *File);
- bool Matches(time_t t = 0);
- time_t StartTime(void);
- time_t StopTime(void);
- void SetRecording(bool Recording);
- void SetPending(bool Pending);
- void Skip(void);
- const char *PrintFirstDay(void);
- static int TimeToInt(int t);
- static int ParseDay(const char *s, time_t *FirstDay = NULL);
- static const char *PrintDay(int d, time_t FirstDay = 0);
- };
-
class cCommand : public cListObject {
private:
char *title;
@@ -203,13 +152,6 @@ public:
}
};
-class cTimers : public cConfig<cTimer> {
-public:
- cTimer *GetTimer(cTimer *Timer);
- cTimer *GetMatch(time_t t);
- cTimer *GetNextActiveTimer(void);
- };
-
class cCommands : public cConfig<cCommand> {};
class cSVDRPhosts : public cConfig<cSVDRPhost> {
@@ -222,7 +164,6 @@ public:
const cCaDefinition *Get(int Number);
};
-extern cTimers Timers;
extern cCommands Commands;
extern cCommands RecordingCommands;
extern cSVDRPhosts SVDRPhosts;
diff --git a/menu.c b/menu.c
index 286820ef..18cf0500 100644
--- a/menu.c
+++ b/menu.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.c 1.218 2002/10/19 15:33:37 kls Exp $
+ * $Id: menu.c 1.219 2002/10/20 12:03:13 kls Exp $
*/
#include "menu.h"
@@ -24,6 +24,7 @@
#include "remote.h"
#include "sources.h"
#include "status.h"
+#include "timers.h"
#include "videodir.h"
#define MENUTIMEOUT 120 // seconds
@@ -617,17 +618,14 @@ eOSState cMenuEditChannel::ProcessKey(eKeys Key)
class cMenuChannelItem : public cOsdItem {
private:
- int index;
cChannel *channel;
public:
- cMenuChannelItem(int Index, cChannel *Channel);
+ cMenuChannelItem(cChannel *Channel);
virtual void Set(void);
- void SetIndex(int Index);
};
-cMenuChannelItem::cMenuChannelItem(int Index, cChannel *Channel)
+cMenuChannelItem::cMenuChannelItem(cChannel *Channel)
{
- index = Index;
channel = Channel;
if (channel->GroupSep())
SetColor(clrCyan, clrBackground);
@@ -644,15 +642,11 @@ void cMenuChannelItem::Set(void)
SetText(buffer, false);
}
-void cMenuChannelItem::SetIndex(int Index)
-{
- index = Index;
- Set();
-}
-
// --- cMenuChannels ---------------------------------------------------------
class cMenuChannels : public cOsdMenu {
+private:
+ void Propagate(void);
protected:
eOSState Switch(void);
eOSState Edit(void);
@@ -673,12 +667,22 @@ cMenuChannels::cMenuChannels(void)
int curr = ((channel = Channels.GetByNumber(cDevice::CurrentChannel())) != NULL) ? channel->Index() : -1;
while ((channel = Channels.Get(i)) != NULL) {
- Add(new cMenuChannelItem(i, channel), i == curr);
+ Add(new cMenuChannelItem(channel), i == curr);
i++;
}
SetHelp(tr("Edit"), tr("New"), tr("Delete"), tr("Mark"));
}
+void cMenuChannels::Propagate(void)
+{
+ Channels.ReNumber();
+ Channels.Save();
+ for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
+ ci->Set();
+ Timers.Save(); // channel numbering has changed!
+ Display();
+}
+
eOSState cMenuChannels::Switch(void)
{
cChannel *ch = Channels.Get(Current());
@@ -702,7 +706,7 @@ eOSState cMenuChannels::New(void)
cChannel *channel = new cChannel(Channels.Get(Current()));
Channels.Add(channel);
Channels.ReNumber();
- Add(new cMenuChannelItem(channel->Index()/*XXX*/, channel), true);
+ Add(new cMenuChannelItem(channel), true);
Channels.Save();
isyslog("channel %d added", channel->Number());
return AddSubMenu(new cMenuEditChannel(Current()));
@@ -715,36 +719,17 @@ eOSState cMenuChannels::Del(void)
cChannel *channel = Channels.Get(Index);
int DeletedChannel = channel->Number();
// Check if there is a timer using this channel:
- for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) {
- if (ti->channel == DeletedChannel) {
+ for (cTimer *ti = Timers.First(); ti; ti = Timers.Next(ti)) {
+ if (ti->Channel() == channel) {
Interface->Error(tr("Channel is being used by a timer!"));
return osContinue;
}
}
if (Interface->Confirm(tr("Delete channel?"))) {
- // Move and renumber the channels:
Channels.Del(channel);
- Channels.ReNumber();
cOsdMenu::Del(Index);
- int i = 0;
- for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
- ci->SetIndex(i++);
- Channels.Save();
+ Propagate();
isyslog("channel %d deleted", DeletedChannel);
- // Fix the timers:
- bool TimersModified = false;
- for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) {
- int OldChannel = ti->channel;
- if (ti->channel > DeletedChannel)
- ti->channel--;
- if (ti->channel != OldChannel) {
- TimersModified = true;
- isyslog("timer %d: channel changed from %d to %d", ti->Index() + 1, OldChannel, ti->channel);
- }
- }
- if (TimersModified)
- Timers.Save();
- Display();
}
}
return osContinue;
@@ -754,35 +739,10 @@ void cMenuChannels::Move(int From, int To)
{
int FromNumber = Channels.Get(From)->Number();
int ToNumber = Channels.Get(To)->Number();
- // Move and renumber the channels:
Channels.Move(From, To);
- Channels.ReNumber();
cOsdMenu::Move(From, To);
- int i = 0;
- for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
- ci->SetIndex(i++);
- Channels.Save();
+ Propagate();
isyslog("channel %d moved to %d", FromNumber, ToNumber);
- // Fix the timers:
- bool TimersModified = false;
- From++; // user visible channel numbers start with '1'
- To++;
- for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) {
- int OldChannel = ti->channel;
- if (ti->channel == FromNumber)
- ti->channel = ToNumber;
- else if (ti->channel > FromNumber && ti->channel <= ToNumber)
- ti->channel--;
- else if (ti->channel < FromNumber && ti->channel >= ToNumber)
- ti->channel++;
- if (ti->channel != OldChannel) {
- TimersModified = true;
- isyslog("timer %d: channel changed from %d to %d", ti->Index() + 1, OldChannel, ti->channel);
- }
- }
- if (TimersModified)
- Timers.Save();
- Display();
}
eOSState cMenuChannels::ProcessKey(eKeys Key)
@@ -835,6 +795,7 @@ class cMenuEditTimer : public cOsdMenu {
private:
cTimer *timer;
cTimer data;
+ int channel;
cMenuEditDateItem *firstday;
void SetFirstDayItem(void);
public:
@@ -851,12 +812,12 @@ cMenuEditTimer::cMenuEditTimer(int Index, bool New)
data = *timer;
if (New)
data.active = 1;
+ channel = data.Channel()->Number();
Add(new cMenuEditBoolItem(tr("Active"), &data.active));
- Add(new cMenuEditChanItem(tr("Channel"), &data.channel));
+ Add(new cMenuEditChanItem(tr("Channel"), &channel));
Add(new cMenuEditDayItem( tr("Day"), &data.day));
Add(new cMenuEditTimeItem(tr("Start"), &data.start));
Add(new cMenuEditTimeItem(tr("Stop"), &data.stop));
-//TODO VPS???
Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
Add(new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file), tr(FileNameChars)));
@@ -884,15 +845,24 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
if (state == osUnknown) {
switch (Key) {
- case kOk: if (!*data.file)
- strcpy(data.file, Channels.GetChannelNameByNumber(data.channel));
- if (timer && memcmp(timer, &data, sizeof(data)) != 0) {
- *timer = data;
- if (timer->active)
- timer->active = 1; // allows external programs to mark active timers with values > 1 and recognize if the user has modified them
- Timers.Save();
- isyslog("timer %d modified (%s)", timer->Index() + 1, timer->active ? "active" : "inactive");
- }
+ case kOk: {
+ cChannel *ch = Channels.GetByNumber(channel);
+ if (ch)
+ data.channel = ch;
+ else {
+ Interface->Error(tr("*** Invalid Channel ***"));
+ break;
+ }
+ if (!*data.file)
+ strcpy(data.file, data.Channel()->Name());
+ if (timer && memcmp(timer, &data, sizeof(data)) != 0) {
+ *timer = data;
+ if (timer->active)
+ timer->active = 1; // allows external programs to mark active timers with values > 1 and recognize if the user has modified them
+ Timers.Save();
+ isyslog("timer %d modified (%s)", timer->Index() + 1, timer->active ? "active" : "inactive");
+ }
+ }
return osBack;
case kRed:
case kGreen:
@@ -933,14 +903,14 @@ void cMenuTimerItem::Set(void)
{
char *buffer = NULL;
asprintf(&buffer, "%c\t%d\t%s\t%02d:%02d\t%02d:%02d\t%s",
- !timer->active ? ' ' : timer->firstday ? '!' : timer->recording ? '#' : '>',
- timer->channel,
- timer->PrintDay(timer->day),
- timer->start / 100,
- timer->start % 100,
- timer->stop / 100,
- timer->stop % 100,
- timer->file);
+ !timer->Active() ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
+ timer->Channel()->Number(),
+ timer->PrintDay(timer->Day()),
+ timer->Start() / 100,
+ timer->Start() % 100,
+ timer->Stop() / 100,
+ timer->Stop() % 100,
+ timer->File());
SetText(buffer, false);
}
@@ -985,23 +955,13 @@ eOSState cMenuTimers::OnOff(void)
{
cTimer *timer = CurrentTimer();
if (timer) {
- if (timer->IsSingleEvent())
- timer->active = !timer->active;
- else if (timer->firstday) {
- timer->firstday = 0;
- timer->active = false;
- }
- else if (timer->active)
- timer->Skip();
- else
- timer->active = true;
- timer->Matches(); // refresh start and end time
+ timer->OnOff();
RefreshCurrent();
DisplayCurrent(true);
- if (timer->firstday)
+ if (timer->FirstDay())
isyslog("timer %d first day set to %s", timer->Index() + 1, timer->PrintFirstDay());
else
- isyslog("timer %d %sactivated", timer->Index() + 1, timer->active ? "" : "de");
+ isyslog("timer %d %sactivated", timer->Index() + 1, timer->Active() ? "" : "de");
Timers.Save();
}
return osContinue;
@@ -1032,7 +992,7 @@ eOSState cMenuTimers::Del(void)
// Check if this timer is active:
cTimer *ti = CurrentTimer();
if (ti) {
- if (!ti->recording) {
+ if (!ti->Recording()) {
if (Interface->Confirm(tr("Delete timer?"))) {
int Index = ti->Index();
Timers.Del(ti);
@@ -1062,8 +1022,8 @@ eOSState cMenuTimers::Summary(void)
if (HasSubMenu() || Count() == 0)
return osContinue;
cTimer *ti = CurrentTimer();
- if (ti && ti->summary && *ti->summary)
- return AddSubMenu(new cMenuText(tr("Summary"), ti->summary));
+ if (ti && !isempty(ti->Summary()))
+ return AddSubMenu(new cMenuText(tr("Summary"), ti->Summary()));
return Edit(); // convenience for people not using the Summary feature ;-)
}
@@ -2675,7 +2635,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer)
timer = new cTimer(true);
Timers.Add(timer);
Timers.Save();
- asprintf(&instantId, cDevice::NumDevices() > 1 ? "%s - %d" : "%s", Channels.GetChannelNameByNumber(timer->channel), device->CardIndex() + 1);
+ asprintf(&instantId, cDevice::NumDevices() > 1 ? "%s - %d" : "%s", timer->Channel()->Name(), device->CardIndex() + 1);
}
timer->SetPending(true);
timer->SetRecording(true);
@@ -2692,8 +2652,8 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer)
cRecording Recording(timer, Title, Subtitle, Summary);
fileName = strdup(Recording.FileName());
cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName);
- cChannel *ch = Channels.GetByNumber(timer->channel);
- recorder = new cRecorder(fileName, ch->Ca(), timer->priority, ch->Vpid(), ch->Apid1(), ch->Apid2(), ch->Dpid1(), ch->Dpid2());
+ const cChannel *ch = timer->Channel();
+ recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apid1(), ch->Apid2(), ch->Dpid1(), ch->Dpid2());
if (device->AttachReceiver(recorder)) {
Recording.WriteSummary();
cStatus::MsgRecording(device, Recording.Name());
@@ -2713,8 +2673,8 @@ cRecordControl::~cRecordControl()
bool cRecordControl::GetEventInfo(void)
{
- cChannel *channel = Channels.GetByNumber(timer->channel);
- time_t Time = timer->active == taActInst ? timer->StartTime() + INSTANT_REC_EPG_LOOKAHEAD : timer->StartTime() + (timer->StopTime() - timer->StartTime()) / 2;
+ const cChannel *channel = timer->Channel();
+ time_t Time = timer->Active() == taActInst ? timer->StartTime() + INSTANT_REC_EPG_LOOKAHEAD : timer->StartTime() + (timer->StopTime() - timer->StartTime()) / 2;
for (int seconds = 0; seconds <= MAXWAIT4EPGINFO; seconds++) {
{
cMutexLock MutexLock;
@@ -2759,7 +2719,7 @@ bool cRecordControl::Process(time_t t)
{
if (!recorder || !timer || !timer->Matches(t))
return false;
- AssertFreeDiskSpace(timer->priority);
+ AssertFreeDiskSpace(timer->Priority());
return true;
}
@@ -2769,12 +2729,12 @@ cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL };
bool cRecordControls::Start(cTimer *Timer)
{
- int ch = Timer ? Timer->channel : cDevice::CurrentChannel();
+ int ch = Timer ? Timer->Channel()->Number() : cDevice::CurrentChannel();
cChannel *channel = Channels.GetByNumber(ch);
if (channel) {
bool NeedsDetachReceivers = false;
- cDevice *device = cDevice::GetDevice(channel, Timer ? Timer->priority : Setup.DefaultPriority, &NeedsDetachReceivers);
+ cDevice *device = cDevice::GetDevice(channel, Timer ? Timer->Priority() : Setup.DefaultPriority, &NeedsDetachReceivers);
if (device) {
if (NeedsDetachReceivers)
Stop(device);
@@ -2789,7 +2749,7 @@ bool cRecordControls::Start(cTimer *Timer)
}
}
}
- else if (!Timer || (Timer->priority >= Setup.PrimaryLimit && !Timer->pending))
+ else if (!Timer || (Timer->Priority() >= Setup.PrimaryLimit && !Timer->Pending()))
isyslog("no free DVB device to record channel %d!", ch);
}
else
diff --git a/recording.c b/recording.c
index 1be6623b..ba85ded1 100644
--- a/recording.c
+++ b/recording.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recording.c 1.69 2002/10/13 09:08:45 kls Exp $
+ * $Id: recording.c 1.70 2002/10/20 11:54:29 kls Exp $
*/
#include "recording.h"
@@ -307,13 +307,13 @@ cRecording::cRecording(cTimer *Timer, const char *Title, const char *Subtitle, c
name = NULL;
// set up the actual name:
if (isempty(Title))
- Title = Channels.GetChannelNameByNumber(Timer->channel);
+ Title = Timer->Channel()->Name();
if (isempty(Subtitle))
Subtitle = " ";
- char *macroTITLE = strstr(Timer->file, TIMERMACRO_TITLE);
- char *macroEPISODE = strstr(Timer->file, TIMERMACRO_EPISODE);
+ char *macroTITLE = strstr(Timer->File(), TIMERMACRO_TITLE);
+ char *macroEPISODE = strstr(Timer->File(), TIMERMACRO_EPISODE);
if (macroTITLE || macroEPISODE) {
- name = strdup(Timer->file);
+ name = strdup(Timer->File());
name = strreplace(name, TIMERMACRO_TITLE, Title);
name = strreplace(name, TIMERMACRO_EPISODE, Subtitle);
if (Timer->IsSingleEvent()) {
@@ -322,16 +322,16 @@ cRecording::cRecording(cTimer *Timer, const char *Title, const char *Subtitle, c
}
}
else if (Timer->IsSingleEvent() || !Setup.UseSubtitle)
- name = strdup(Timer->file);
+ name = strdup(Timer->File());
else
- asprintf(&name, "%s~%s", Timer->file, Subtitle);
+ asprintf(&name, "%s~%s", Timer->File(), Subtitle);
// substitute characters that would cause problems in file names:
strreplace(name, '\n', ' ');
start = Timer->StartTime();
- priority = Timer->priority;
- lifetime = Timer->lifetime;
+ priority = Timer->Priority();
+ lifetime = Timer->Lifetime();
// handle summary:
- summary = !isempty(Timer->summary) ? strdup(Timer->summary) : NULL;
+ summary = !isempty(Timer->Summary()) ? strdup(Timer->Summary()) : NULL;
if (!summary) {
if (isempty(Subtitle))
Subtitle = "";
diff --git a/recording.h b/recording.h
index 2fae49ee..82f2fcca 100644
--- a/recording.h
+++ b/recording.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recording.h 1.24 2002/06/22 10:09:27 kls Exp $
+ * $Id: recording.h 1.25 2002/10/19 15:48:52 kls Exp $
*/
#ifndef __RECORDING_H
@@ -12,6 +12,7 @@
#include <time.h>
#include "config.h"
+#include "timers.h"
#include "tools.h"
void RemoveDeletedRecordings(void);
diff --git a/svdrp.c b/svdrp.c
index 114be09a..a029da2f 100644
--- a/svdrp.c
+++ b/svdrp.c
@@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection.
*
- * $Id: svdrp.c 1.46 2002/10/19 11:48:02 kls Exp $
+ * $Id: svdrp.c 1.47 2002/10/20 10:24:20 kls Exp $
*/
#include "svdrp.h"
@@ -31,6 +31,7 @@
#include "device.h"
#include "keys.h"
#include "remote.h"
+#include "timers.h"
#include "tools.h"
// --- cSocket ---------------------------------------------------------------
@@ -487,7 +488,7 @@ void cSVDRP::CmdDELT(const char *Option)
if (isnumber(Option)) {
cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1);
if (timer) {
- if (!timer->recording) {
+ if (!timer->Recording()) {
Timers.Del(timer);
Timers.Save();
isyslog("timer %s deleted", Option);
@@ -806,16 +807,16 @@ void cSVDRP::CmdMODT(const char *Option)
if (timer) {
cTimer t = *timer;
if (strcasecmp(tail, "ON") == 0)
- t.active = 1;
+ t.SetActive(taActive);
else if (strcasecmp(tail, "OFF") == 0)
- t.active = 0;
+ t.SetActive(taInactive);
else if (!t.Parse(tail)) {
Reply(501, "Error in timer settings");
return;
}
*timer = t;
Timers.Save();
- isyslog("timer %d modified (%s)", timer->Index() + 1, timer->active ? "active" : "inactive");
+ isyslog("timer %d modified (%s)", timer->Index() + 1, timer->Active() ? "active" : "inactive");
Reply(250, "%d %s", timer->Index() + 1, timer->ToText());
}
else
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;
+}
diff --git a/timers.h b/timers.h
new file mode 100644
index 00000000..8dc762e3
--- /dev/null
+++ b/timers.h
@@ -0,0 +1,91 @@
+/*
+ * timers.h: Timer handling
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: timers.h 1.1 2002/10/20 11:52:23 kls Exp $
+ */
+
+#ifndef __TIMERS_H
+#define __TIMERS_H
+
+#include "channels.h"
+#include "config.h"
+#include "tools.h"
+
+enum eTimerActive { taInactive = 0,
+ taActive = 1,
+ taInstant = 2,
+ taActInst = (taActive | taInstant)
+ };
+
+class cTimer : public cListObject {
+ friend class cMenuEditTimer;
+private:
+ time_t startTime, stopTime;
+ static char *buffer;
+ bool recording, pending;
+ int active;
+ cChannel *channel;
+ int day;
+ int start;
+ int stop;
+ int priority;
+ int lifetime;
+ char file[MaxFileName];
+ time_t firstday;
+ char *summary;
+ static const char *ToText(cTimer *Timer);
+public:
+ cTimer(bool Instant = false);
+ cTimer(const cEventInfo *EventInfo);
+ virtual ~cTimer();
+ cTimer& operator= (const cTimer &Timer);
+ virtual bool operator< (const cListObject &ListObject);
+ bool Recording(void) { return recording; }
+ bool Pending(void) { return pending; }
+ int Active(void) { return active; }
+ const cChannel *Channel(void) { return channel; }
+ int Day(void) { return day; }
+ int Start(void) { return start; }
+ int Stop(void) { return stop; }
+ int Priority(void) { return priority; }
+ int Lifetime(void) { return lifetime; }
+ const char *File(void) { return file; }
+ time_t FirstDay(void) { return firstday; }
+ const char *Summary(void) { return summary; }
+ const char *ToText(void);
+ bool Parse(const char *s);
+ bool Save(FILE *f);
+ bool IsSingleEvent(void);
+ int GetMDay(time_t t);
+ int GetWDay(time_t t);
+ bool DayMatches(time_t t);
+ static time_t IncDay(time_t t, int Days);
+ static time_t SetTime(time_t t, int SecondsFromMidnight);
+ char *SetFile(const char *File);
+ bool Matches(time_t t = 0);
+ time_t StartTime(void);
+ time_t StopTime(void);
+ void SetRecording(bool Recording);
+ void SetPending(bool Pending);
+ void SetActive(int Active);
+ void Skip(void);
+ void OnOff(void);
+ const char *PrintFirstDay(void);
+ static int TimeToInt(int t);
+ static int ParseDay(const char *s, time_t *FirstDay = NULL);
+ static const char *PrintDay(int d, time_t FirstDay = 0);
+ };
+
+class cTimers : public cConfig<cTimer> {
+public:
+ cTimer *GetTimer(cTimer *Timer);
+ cTimer *GetMatch(time_t t);
+ cTimer *GetNextActiveTimer(void);
+ };
+
+extern cTimers Timers;
+
+#endif //__TIMERS_H
diff --git a/vdr.c b/vdr.c
index beeee889..b44d74fb 100644
--- a/vdr.c
+++ b/vdr.c
@@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
- * $Id: vdr.c 1.127 2002/10/13 12:13:19 kls Exp $
+ * $Id: vdr.c 1.128 2002/10/20 12:09:45 kls Exp $
*/
#include <getopt.h>
@@ -47,6 +47,7 @@
#include "rcu.h"
#include "recording.h"
#include "sources.h"
+#include "timers.h"
#include "tools.h"
#include "videodir.h"
@@ -603,8 +604,8 @@ int main(int argc, char *argv[])
if (WatchdogTimeout > 0)
signal(SIGALRM, SIG_IGN);
if (Interface->Confirm(tr("Press any key to cancel shutdown"), UserShutdown ? 5 : SHUTDOWNWAIT, true)) {
- int Channel = timer ? timer->channel : 0;
- const char *File = timer ? timer->file : "";
+ int Channel = timer ? timer->Channel()->Number() : 0;
+ const char *File = timer ? timer->File() : "";
char *cmd;
asprintf(&cmd, "%s %ld %ld %d \"%s\" %d", Shutdown, Next, Delta, Channel, strescape(File, "\"$"), UserShutdown);
isyslog("executing '%s'", cmd);