1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Timers now internally have a pointer to their channel

This commit is contained in:
Klaus Schmidinger 2002-10-20 12:28:55 +02:00
parent ab4ceb29a0
commit ac9622bb8a
13 changed files with 596 additions and 573 deletions

View File

@ -1611,7 +1611,7 @@ Video Disk Recorder Revision History
shall be executed from the "Recordings" menu; see MANUAL and 'man vdr(5)' for shall be executed from the "Recordings" menu; see MANUAL and 'man vdr(5)' for
details (suggested by Gerhard Steiner). 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). - Fixed some faulty default parameter initializations (thanks to Robert Schiele).
- Added further satellites to 'sources.conf' (thanks to Reinhard Walter Buchner). - 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 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 TO UPDATE YOUR 'timers.conf' ACCORDINGLY IF INSERTING THIS NEW FEATURE INTO YOUR
'channels.conf' FILE! '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).

View File

@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and # See the main source file 'vdr.c' for copyright information and
# how to reach the author. # 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: .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\ 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\ 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\ 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 OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1 FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "channels.h"
@ -450,9 +450,3 @@ bool cChannels::SwitchTo(int Number)
cChannel *channel = GetByNumber(Number); cChannel *channel = GetByNumber(Number);
return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true); return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true);
} }
const char *cChannels::GetChannelNameByNumber(int Number)
{
cChannel *channel = GetByNumber(Number);
return channel ? channel->Name() : NULL;
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __CHANNELS_H
@ -114,7 +114,6 @@ public:
void ReNumber(void); // Recalculate 'number' based on channel type void ReNumber(void); // Recalculate 'number' based on channel type
cChannel *GetByNumber(int Number, int SkipGap = 0); cChannel *GetByNumber(int Number, int SkipGap = 0);
cChannel *GetByServiceID(unsigned short ServiceId); cChannel *GetByServiceID(unsigned short ServiceId);
const char *GetChannelNameByNumber(int Number);
bool SwitchTo(int Number); bool SwitchTo(int Number);
int MaxNumber(void) { return maxNumber; } int MaxNumber(void) { return maxNumber; }
}; };

377
config.c
View File

@ -4,13 +4,12 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 "config.h"
#include <ctype.h> #include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
#include "channels.h" //XXX timers!
#include "i18n.h" #include "i18n.h"
#include "interface.h" #include "interface.h"
#include "plugin.h" #include "plugin.h"
@ -20,339 +19,6 @@
// format characters in order to allow any number of blanks after a numeric // format characters in order to allow any number of blanks after a numeric
// value! // 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 ------------------------------------------------------------- // --- cCommand -------------------------------------------------------------
char *cCommand::result = NULL; char *cCommand::result = NULL;
@ -476,47 +142,6 @@ bool cCaDefinition::Parse(const char *s)
cCommands Commands; cCommands Commands;
cCommands RecordingCommands; 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 ------------------------------------------------------------
cSVDRPhosts SVDRPhosts; cSVDRPhosts SVDRPhosts;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __CONFIG_H
@ -32,57 +32,6 @@
#define MaxFileName 256 #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 { class cCommand : public cListObject {
private: private:
char *title; 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 cCommands : public cConfig<cCommand> {};
class cSVDRPhosts : public cConfig<cSVDRPhost> { class cSVDRPhosts : public cConfig<cSVDRPhost> {
@ -222,7 +164,6 @@ public:
const cCaDefinition *Get(int Number); const cCaDefinition *Get(int Number);
}; };
extern cTimers Timers;
extern cCommands Commands; extern cCommands Commands;
extern cCommands RecordingCommands; extern cCommands RecordingCommands;
extern cSVDRPhosts SVDRPhosts; extern cSVDRPhosts SVDRPhosts;

158
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "menu.h"
@ -24,6 +24,7 @@
#include "remote.h" #include "remote.h"
#include "sources.h" #include "sources.h"
#include "status.h" #include "status.h"
#include "timers.h"
#include "videodir.h" #include "videodir.h"
#define MENUTIMEOUT 120 // seconds #define MENUTIMEOUT 120 // seconds
@ -617,17 +618,14 @@ eOSState cMenuEditChannel::ProcessKey(eKeys Key)
class cMenuChannelItem : public cOsdItem { class cMenuChannelItem : public cOsdItem {
private: private:
int index;
cChannel *channel; cChannel *channel;
public: public:
cMenuChannelItem(int Index, cChannel *Channel); cMenuChannelItem(cChannel *Channel);
virtual void Set(void); virtual void Set(void);
void SetIndex(int Index);
}; };
cMenuChannelItem::cMenuChannelItem(int Index, cChannel *Channel) cMenuChannelItem::cMenuChannelItem(cChannel *Channel)
{ {
index = Index;
channel = Channel; channel = Channel;
if (channel->GroupSep()) if (channel->GroupSep())
SetColor(clrCyan, clrBackground); SetColor(clrCyan, clrBackground);
@ -644,15 +642,11 @@ void cMenuChannelItem::Set(void)
SetText(buffer, false); SetText(buffer, false);
} }
void cMenuChannelItem::SetIndex(int Index)
{
index = Index;
Set();
}
// --- cMenuChannels --------------------------------------------------------- // --- cMenuChannels ---------------------------------------------------------
class cMenuChannels : public cOsdMenu { class cMenuChannels : public cOsdMenu {
private:
void Propagate(void);
protected: protected:
eOSState Switch(void); eOSState Switch(void);
eOSState Edit(void); eOSState Edit(void);
@ -673,12 +667,22 @@ cMenuChannels::cMenuChannels(void)
int curr = ((channel = Channels.GetByNumber(cDevice::CurrentChannel())) != NULL) ? channel->Index() : -1; int curr = ((channel = Channels.GetByNumber(cDevice::CurrentChannel())) != NULL) ? channel->Index() : -1;
while ((channel = Channels.Get(i)) != NULL) { while ((channel = Channels.Get(i)) != NULL) {
Add(new cMenuChannelItem(i, channel), i == curr); Add(new cMenuChannelItem(channel), i == curr);
i++; i++;
} }
SetHelp(tr("Edit"), tr("New"), tr("Delete"), tr("Mark")); 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) eOSState cMenuChannels::Switch(void)
{ {
cChannel *ch = Channels.Get(Current()); cChannel *ch = Channels.Get(Current());
@ -702,7 +706,7 @@ eOSState cMenuChannels::New(void)
cChannel *channel = new cChannel(Channels.Get(Current())); cChannel *channel = new cChannel(Channels.Get(Current()));
Channels.Add(channel); Channels.Add(channel);
Channels.ReNumber(); Channels.ReNumber();
Add(new cMenuChannelItem(channel->Index()/*XXX*/, channel), true); Add(new cMenuChannelItem(channel), true);
Channels.Save(); Channels.Save();
isyslog("channel %d added", channel->Number()); isyslog("channel %d added", channel->Number());
return AddSubMenu(new cMenuEditChannel(Current())); return AddSubMenu(new cMenuEditChannel(Current()));
@ -715,36 +719,17 @@ eOSState cMenuChannels::Del(void)
cChannel *channel = Channels.Get(Index); cChannel *channel = Channels.Get(Index);
int DeletedChannel = channel->Number(); int DeletedChannel = channel->Number();
// Check if there is a timer using this channel: // Check if there is a timer using this channel:
for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) { for (cTimer *ti = Timers.First(); ti; ti = Timers.Next(ti)) {
if (ti->channel == DeletedChannel) { if (ti->Channel() == channel) {
Interface->Error(tr("Channel is being used by a timer!")); Interface->Error(tr("Channel is being used by a timer!"));
return osContinue; return osContinue;
} }
} }
if (Interface->Confirm(tr("Delete channel?"))) { if (Interface->Confirm(tr("Delete channel?"))) {
// Move and renumber the channels:
Channels.Del(channel); Channels.Del(channel);
Channels.ReNumber();
cOsdMenu::Del(Index); cOsdMenu::Del(Index);
int i = 0; Propagate();
for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
ci->SetIndex(i++);
Channels.Save();
isyslog("channel %d deleted", DeletedChannel); 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; return osContinue;
@ -754,35 +739,10 @@ void cMenuChannels::Move(int From, int To)
{ {
int FromNumber = Channels.Get(From)->Number(); int FromNumber = Channels.Get(From)->Number();
int ToNumber = Channels.Get(To)->Number(); int ToNumber = Channels.Get(To)->Number();
// Move and renumber the channels:
Channels.Move(From, To); Channels.Move(From, To);
Channels.ReNumber();
cOsdMenu::Move(From, To); cOsdMenu::Move(From, To);
int i = 0; Propagate();
for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
ci->SetIndex(i++);
Channels.Save();
isyslog("channel %d moved to %d", FromNumber, ToNumber); 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) eOSState cMenuChannels::ProcessKey(eKeys Key)
@ -835,6 +795,7 @@ class cMenuEditTimer : public cOsdMenu {
private: private:
cTimer *timer; cTimer *timer;
cTimer data; cTimer data;
int channel;
cMenuEditDateItem *firstday; cMenuEditDateItem *firstday;
void SetFirstDayItem(void); void SetFirstDayItem(void);
public: public:
@ -851,12 +812,12 @@ cMenuEditTimer::cMenuEditTimer(int Index, bool New)
data = *timer; data = *timer;
if (New) if (New)
data.active = 1; data.active = 1;
channel = data.Channel()->Number();
Add(new cMenuEditBoolItem(tr("Active"), &data.active)); 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 cMenuEditDayItem( tr("Day"), &data.day));
Add(new cMenuEditTimeItem(tr("Start"), &data.start)); Add(new cMenuEditTimeItem(tr("Start"), &data.start));
Add(new cMenuEditTimeItem(tr("Stop"), &data.stop)); Add(new cMenuEditTimeItem(tr("Stop"), &data.stop));
//TODO VPS???
Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY)); Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME)); Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
Add(new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file), tr(FileNameChars))); Add(new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file), tr(FileNameChars)));
@ -884,8 +845,16 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
if (state == osUnknown) { if (state == osUnknown) {
switch (Key) { switch (Key) {
case kOk: if (!*data.file) case kOk: {
strcpy(data.file, Channels.GetChannelNameByNumber(data.channel)); 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) { if (timer && memcmp(timer, &data, sizeof(data)) != 0) {
*timer = data; *timer = data;
if (timer->active) if (timer->active)
@ -893,6 +862,7 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key)
Timers.Save(); 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");
} }
}
return osBack; return osBack;
case kRed: case kRed:
case kGreen: case kGreen:
@ -933,14 +903,14 @@ void cMenuTimerItem::Set(void)
{ {
char *buffer = NULL; char *buffer = NULL;
asprintf(&buffer, "%c\t%d\t%s\t%02d:%02d\t%02d:%02d\t%s", asprintf(&buffer, "%c\t%d\t%s\t%02d:%02d\t%02d:%02d\t%s",
!timer->active ? ' ' : timer->firstday ? '!' : timer->recording ? '#' : '>', !timer->Active() ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
timer->channel, timer->Channel()->Number(),
timer->PrintDay(timer->day), timer->PrintDay(timer->Day()),
timer->start / 100, timer->Start() / 100,
timer->start % 100, timer->Start() % 100,
timer->stop / 100, timer->Stop() / 100,
timer->stop % 100, timer->Stop() % 100,
timer->file); timer->File());
SetText(buffer, false); SetText(buffer, false);
} }
@ -985,23 +955,13 @@ eOSState cMenuTimers::OnOff(void)
{ {
cTimer *timer = CurrentTimer(); cTimer *timer = CurrentTimer();
if (timer) { if (timer) {
if (timer->IsSingleEvent()) timer->OnOff();
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
RefreshCurrent(); RefreshCurrent();
DisplayCurrent(true); DisplayCurrent(true);
if (timer->firstday) if (timer->FirstDay())
isyslog("timer %d first day set to %s", timer->Index() + 1, timer->PrintFirstDay()); isyslog("timer %d first day set to %s", timer->Index() + 1, timer->PrintFirstDay());
else else
isyslog("timer %d %sactivated", timer->Index() + 1, timer->active ? "" : "de"); isyslog("timer %d %sactivated", timer->Index() + 1, timer->Active() ? "" : "de");
Timers.Save(); Timers.Save();
} }
return osContinue; return osContinue;
@ -1032,7 +992,7 @@ eOSState cMenuTimers::Del(void)
// Check if this timer is active: // Check if this timer is active:
cTimer *ti = CurrentTimer(); cTimer *ti = CurrentTimer();
if (ti) { if (ti) {
if (!ti->recording) { if (!ti->Recording()) {
if (Interface->Confirm(tr("Delete timer?"))) { if (Interface->Confirm(tr("Delete timer?"))) {
int Index = ti->Index(); int Index = ti->Index();
Timers.Del(ti); Timers.Del(ti);
@ -1062,8 +1022,8 @@ eOSState cMenuTimers::Summary(void)
if (HasSubMenu() || Count() == 0) if (HasSubMenu() || Count() == 0)
return osContinue; return osContinue;
cTimer *ti = CurrentTimer(); cTimer *ti = CurrentTimer();
if (ti && ti->summary && *ti->summary) if (ti && !isempty(ti->Summary()))
return AddSubMenu(new cMenuText(tr("Summary"), ti->summary)); return AddSubMenu(new cMenuText(tr("Summary"), ti->Summary()));
return Edit(); // convenience for people not using the Summary feature ;-) return Edit(); // convenience for people not using the Summary feature ;-)
} }
@ -2675,7 +2635,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer)
timer = new cTimer(true); timer = new cTimer(true);
Timers.Add(timer); Timers.Add(timer);
Timers.Save(); 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->SetPending(true);
timer->SetRecording(true); timer->SetRecording(true);
@ -2692,8 +2652,8 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer)
cRecording Recording(timer, Title, Subtitle, Summary); cRecording Recording(timer, Title, Subtitle, Summary);
fileName = strdup(Recording.FileName()); fileName = strdup(Recording.FileName());
cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName); cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName);
cChannel *ch = Channels.GetByNumber(timer->channel); const cChannel *ch = timer->Channel();
recorder = new cRecorder(fileName, ch->Ca(), timer->priority, ch->Vpid(), ch->Apid1(), ch->Apid2(), ch->Dpid1(), ch->Dpid2()); recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apid1(), ch->Apid2(), ch->Dpid1(), ch->Dpid2());
if (device->AttachReceiver(recorder)) { if (device->AttachReceiver(recorder)) {
Recording.WriteSummary(); Recording.WriteSummary();
cStatus::MsgRecording(device, Recording.Name()); cStatus::MsgRecording(device, Recording.Name());
@ -2713,8 +2673,8 @@ cRecordControl::~cRecordControl()
bool cRecordControl::GetEventInfo(void) bool cRecordControl::GetEventInfo(void)
{ {
cChannel *channel = Channels.GetByNumber(timer->channel); const cChannel *channel = timer->Channel();
time_t Time = timer->active == taActInst ? timer->StartTime() + INSTANT_REC_EPG_LOOKAHEAD : timer->StartTime() + (timer->StopTime() - timer->StartTime()) / 2; 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++) { for (int seconds = 0; seconds <= MAXWAIT4EPGINFO; seconds++) {
{ {
cMutexLock MutexLock; cMutexLock MutexLock;
@ -2759,7 +2719,7 @@ bool cRecordControl::Process(time_t t)
{ {
if (!recorder || !timer || !timer->Matches(t)) if (!recorder || !timer || !timer->Matches(t))
return false; return false;
AssertFreeDiskSpace(timer->priority); AssertFreeDiskSpace(timer->Priority());
return true; return true;
} }
@ -2769,12 +2729,12 @@ cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL };
bool cRecordControls::Start(cTimer *Timer) 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); cChannel *channel = Channels.GetByNumber(ch);
if (channel) { if (channel) {
bool NeedsDetachReceivers = false; 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 (device) {
if (NeedsDetachReceivers) if (NeedsDetachReceivers)
Stop(device); 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); isyslog("no free DVB device to record channel %d!", ch);
} }
else else

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "recording.h"
@ -307,13 +307,13 @@ cRecording::cRecording(cTimer *Timer, const char *Title, const char *Subtitle, c
name = NULL; name = NULL;
// set up the actual name: // set up the actual name:
if (isempty(Title)) if (isempty(Title))
Title = Channels.GetChannelNameByNumber(Timer->channel); Title = Timer->Channel()->Name();
if (isempty(Subtitle)) if (isempty(Subtitle))
Subtitle = " "; Subtitle = " ";
char *macroTITLE = strstr(Timer->file, TIMERMACRO_TITLE); char *macroTITLE = strstr(Timer->File(), TIMERMACRO_TITLE);
char *macroEPISODE = strstr(Timer->file, TIMERMACRO_EPISODE); char *macroEPISODE = strstr(Timer->File(), TIMERMACRO_EPISODE);
if (macroTITLE || macroEPISODE) { if (macroTITLE || macroEPISODE) {
name = strdup(Timer->file); name = strdup(Timer->File());
name = strreplace(name, TIMERMACRO_TITLE, Title); name = strreplace(name, TIMERMACRO_TITLE, Title);
name = strreplace(name, TIMERMACRO_EPISODE, Subtitle); name = strreplace(name, TIMERMACRO_EPISODE, Subtitle);
if (Timer->IsSingleEvent()) { if (Timer->IsSingleEvent()) {
@ -322,16 +322,16 @@ cRecording::cRecording(cTimer *Timer, const char *Title, const char *Subtitle, c
} }
} }
else if (Timer->IsSingleEvent() || !Setup.UseSubtitle) else if (Timer->IsSingleEvent() || !Setup.UseSubtitle)
name = strdup(Timer->file); name = strdup(Timer->File());
else else
asprintf(&name, "%s~%s", Timer->file, Subtitle); asprintf(&name, "%s~%s", Timer->File(), Subtitle);
// substitute characters that would cause problems in file names: // substitute characters that would cause problems in file names:
strreplace(name, '\n', ' '); strreplace(name, '\n', ' ');
start = Timer->StartTime(); start = Timer->StartTime();
priority = Timer->priority; priority = Timer->Priority();
lifetime = Timer->lifetime; lifetime = Timer->Lifetime();
// handle summary: // handle summary:
summary = !isempty(Timer->summary) ? strdup(Timer->summary) : NULL; summary = !isempty(Timer->Summary()) ? strdup(Timer->Summary()) : NULL;
if (!summary) { if (!summary) {
if (isempty(Subtitle)) if (isempty(Subtitle))
Subtitle = ""; Subtitle = "";

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __RECORDING_H
@ -12,6 +12,7 @@
#include <time.h> #include <time.h>
#include "config.h" #include "config.h"
#include "timers.h"
#include "tools.h" #include "tools.h"
void RemoveDeletedRecordings(void); void RemoveDeletedRecordings(void);

11
svdrp.c
View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured * and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection. * 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" #include "svdrp.h"
@ -31,6 +31,7 @@
#include "device.h" #include "device.h"
#include "keys.h" #include "keys.h"
#include "remote.h" #include "remote.h"
#include "timers.h"
#include "tools.h" #include "tools.h"
// --- cSocket --------------------------------------------------------------- // --- cSocket ---------------------------------------------------------------
@ -487,7 +488,7 @@ void cSVDRP::CmdDELT(const char *Option)
if (isnumber(Option)) { if (isnumber(Option)) {
cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1); cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1);
if (timer) { if (timer) {
if (!timer->recording) { if (!timer->Recording()) {
Timers.Del(timer); Timers.Del(timer);
Timers.Save(); Timers.Save();
isyslog("timer %s deleted", Option); isyslog("timer %s deleted", Option);
@ -806,16 +807,16 @@ void cSVDRP::CmdMODT(const char *Option)
if (timer) { if (timer) {
cTimer t = *timer; cTimer t = *timer;
if (strcasecmp(tail, "ON") == 0) if (strcasecmp(tail, "ON") == 0)
t.active = 1; t.SetActive(taActive);
else if (strcasecmp(tail, "OFF") == 0) else if (strcasecmp(tail, "OFF") == 0)
t.active = 0; t.SetActive(taInactive);
else if (!t.Parse(tail)) { else if (!t.Parse(tail)) {
Reply(501, "Error in timer settings"); Reply(501, "Error in timer settings");
return; return;
} }
*timer = t; *timer = t;
Timers.Save(); 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()); Reply(250, "%d %s", timer->Index() + 1, timer->ToText());
} }
else else

408
timers.c Normal file
View File

@ -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;
}

91
timers.h Normal file
View File

@ -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

7
vdr.c
View File

@ -22,7 +22,7 @@
* *
* The project's page is at http://www.cadsoft.de/people/kls/vdr * 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> #include <getopt.h>
@ -47,6 +47,7 @@
#include "rcu.h" #include "rcu.h"
#include "recording.h" #include "recording.h"
#include "sources.h" #include "sources.h"
#include "timers.h"
#include "tools.h" #include "tools.h"
#include "videodir.h" #include "videodir.h"
@ -603,8 +604,8 @@ int main(int argc, char *argv[])
if (WatchdogTimeout > 0) if (WatchdogTimeout > 0)
signal(SIGALRM, SIG_IGN); signal(SIGALRM, SIG_IGN);
if (Interface->Confirm(tr("Press any key to cancel shutdown"), UserShutdown ? 5 : SHUTDOWNWAIT, true)) { if (Interface->Confirm(tr("Press any key to cancel shutdown"), UserShutdown ? 5 : SHUTDOWNWAIT, true)) {
int Channel = timer ? timer->channel : 0; int Channel = timer ? timer->Channel()->Number() : 0;
const char *File = timer ? timer->file : ""; const char *File = timer ? timer->File() : "";
char *cmd; char *cmd;
asprintf(&cmd, "%s %ld %ld %d \"%s\" %d", Shutdown, Next, Delta, Channel, strescape(File, "\"$"), UserShutdown); asprintf(&cmd, "%s %ld %ld %d \"%s\" %d", Shutdown, Next, Delta, Channel, strescape(File, "\"$"), UserShutdown);
isyslog("executing '%s'", cmd); isyslog("executing '%s'", cmd);