2002-10-20 12:28:55 +02:00
/*
* timers . c : Timer handling
*
* See the main source file ' vdr . c ' for copyright information and
* how to reach the author .
*
2022-11-20 10:57:31 +01:00
* $ Id : timers . c 5.18 2022 / 11 / 20 10 : 57 : 31 kls Exp $
2002-10-20 12:28:55 +02:00
*/
# include "timers.h"
# include <ctype.h>
2005-09-04 14:48:39 +02:00
# include "device.h"
2002-10-20 12:28:55 +02:00
# include "i18n.h"
2006-03-26 14:38:46 +02:00
# include "libsi/si.h"
2008-02-15 15:46:07 +01:00
# include "recording.h"
2004-07-17 12:48:14 +02:00
# include "remote.h"
2008-02-16 15:04:49 +01:00
# include "status.h"
2015-09-01 11:14:27 +02:00
# include "svdrp.h"
2002-10-20 12:28:55 +02:00
// 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!
2008-02-10 14:11:47 +01:00
// --- cTimer ----------------------------------------------------------------
2002-10-20 12:28:55 +02:00
2015-09-01 11:14:27 +02:00
cTimer : : cTimer ( bool Instant , bool Pause , const cChannel * Channel )
2002-10-20 12:28:55 +02:00
{
2015-09-06 09:14:53 +02:00
id = 0 ;
2002-10-20 12:28:55 +02:00
startTime = stopTime = 0 ;
2021-04-06 08:48:35 +02:00
scheduleStateSet = scheduleStateSpawn = scheduleStateAdjust = - 1 ;
2011-08-06 13:20:07 +02:00
deferred = 0 ;
2015-09-01 11:14:27 +02:00
pending = inVpsMargin = false ;
2004-02-29 14:21:22 +01:00
flags = tfNone ;
2020-12-26 15:49:01 +01:00
* pattern = 0 ;
2012-09-15 13:57:39 +02:00
* file = 0 ;
aux = NULL ;
2015-09-13 13:57:51 +02:00
remote = NULL ;
2012-09-15 13:57:39 +02:00
event = NULL ;
2004-02-29 14:21:22 +01:00
if ( Instant )
SetFlags ( tfActive | tfInstant ) ;
2015-09-01 11:14:27 +02:00
LOCK_CHANNELS_READ ;
channel = Channel ? Channel : Channels - > GetByNumber ( cDevice : : CurrentChannel ( ) ) ;
2002-10-20 12:28:55 +02:00
time_t t = time ( NULL ) ;
struct tm tm_r ;
struct tm * now = localtime_r ( & t , & tm_r ) ;
2005-03-19 15:38:43 +01:00
day = SetTime ( t , 0 ) ;
weekdays = 0 ;
2002-10-20 12:28:55 +02:00
start = now - > tm_hour * 100 + now - > tm_min ;
2012-09-15 13:57:39 +02:00
stop = 0 ;
2013-03-16 10:39:16 +01:00
if ( ! Setup . InstantRecordTime & & channel & & ( Instant | | Pause ) ) {
2015-09-01 11:14:27 +02:00
LOCK_SCHEDULES_READ ;
if ( const cSchedule * Schedule = Schedules - > GetSchedule ( channel ) ) {
if ( const cEvent * Event = Schedule - > GetPresentEvent ( ) ) {
time_t tstart = Event - > StartTime ( ) ;
time_t tstop = Event - > EndTime ( ) ;
if ( Event - > Vps ( ) & & Setup . UseVps ) {
SetFlags ( tfVps ) ;
tstart = Event - > Vps ( ) ;
2012-09-15 13:57:39 +02:00
}
2015-09-01 11:14:27 +02:00
else {
2021-04-04 13:38:13 +02:00
int MarginStart = 0 ;
int MarginStop = 0 ;
CalcMargins ( MarginStart , MarginStop , Event ) ;
tstart - = MarginStart ;
tstop + = MarginStop ;
2015-09-01 11:14:27 +02:00
}
day = SetTime ( tstart , 0 ) ;
struct tm * time = localtime_r ( & tstart , & tm_r ) ;
start = time - > tm_hour * 100 + time - > tm_min ;
time = localtime_r ( & tstop , & tm_r ) ;
stop = time - > tm_hour * 100 + time - > tm_min ;
SetEvent ( Event ) ;
2012-09-15 13:57:39 +02:00
}
}
}
if ( ! stop ) {
stop = now - > tm_hour * 60 + now - > tm_min + ( Setup . InstantRecordTime ? Setup . InstantRecordTime : DEFINSTRECTIME ) ;
stop = ( stop / 60 ) * 100 + ( stop % 60 ) ;
}
2002-10-20 12:28:55 +02:00
if ( stop > = 2400 )
stop - = 2400 ;
2003-05-11 14:10:00 +02:00
priority = Pause ? Setup . PausePriority : Setup . DefaultPriority ;
lifetime = Pause ? Setup . PauseLifetime : Setup . DefaultLifetime ;
2002-10-20 12:28:55 +02:00
if ( Instant & & channel )
snprintf ( file , sizeof ( file ) , " %s%s " , Setup . MarkInstantRecord ? " @ " : " " , * Setup . NameInstantRecord ? Setup . NameInstantRecord : channel - > Name ( ) ) ;
}
2020-12-26 15:49:01 +01:00
static bool MatchPattern ( const char * Pattern , const char * Title , cString * Before = NULL , cString * Match = NULL , cString * After = NULL )
{
if ( Title ) {
bool AvoidDuplicates = startswith ( Pattern , TIMERPATTERN_AVOID ) ;
if ( AvoidDuplicates )
Pattern + + ;
if ( strcmp ( Pattern , " * " ) = = 0 ) {
if ( Before )
* Before = " " ;
if ( Match )
* Match = Title ;
if ( After )
* After = " " ;
return true ;
}
bool AnchorBegin = startswith ( Pattern , TIMERPATTERN_BEGIN ) ;
if ( AnchorBegin )
Pattern + + ;
bool AnchorEnd = endswith ( Pattern , TIMERPATTERN_END ) ;
cNullTerminate nt ;
if ( AnchorEnd )
nt . Set ( const_cast < char * > ( Pattern + strlen ( Pattern ) - 1 ) ) ;
if ( AnchorBegin & & AnchorEnd ) {
if ( strcmp ( Title , Pattern ) = = 0 ) {
if ( Before )
* Before = " " ;
if ( Match )
* Match = Title ;
if ( After )
* After = " " ;
return true ;
}
}
else if ( AnchorBegin ) {
if ( strstr ( Title , Pattern ) = = Title ) {
if ( Before )
* Before = " " ;
if ( Match )
* Match = Pattern ;
if ( After )
* After = cString ( Title + strlen ( Pattern ) ) ;
return true ;
}
}
else if ( AnchorEnd ) {
if ( endswith ( Title , Pattern ) ) {
if ( Before )
* Before = cString ( Title , Title + strlen ( Title ) - strlen ( Pattern ) ) ;
if ( Match )
* Match = Pattern ;
if ( After )
* After = " " ;
return true ;
}
}
else if ( const char * p = strstr ( Title , Pattern ) ) {
if ( Before )
* Before = cString ( Title , p ) ;
if ( Match )
* Match = Pattern ;
if ( After )
* After = cString ( p + strlen ( Pattern ) ) ;
return true ;
}
}
return false ;
}
static cString MakePatternFileName ( const char * Pattern , const char * Title , const char * Episode , const char * File )
{
if ( ! Pattern | | ! Title | | ! File )
return NULL ;
cString Before = " " ;
cString Match = " " ;
cString After = " " ;
if ( MatchPattern ( Pattern , Title , & Before , & Match , & After ) ) {
char * Result = strdup ( File ) ;
Result = strreplace ( Result , TIMERMACRO_TITLE , Title ) ;
2021-04-06 09:50:30 +02:00
if ( ! isempty ( Episode ) ) // the event might not yet have a "short text", so we leave this to the actual recording
Result = strreplace ( Result , TIMERMACRO_EPISODE , Episode ) ;
2020-12-26 15:49:01 +01:00
Result = strreplace ( Result , TIMERMACRO_BEFORE , Before ) ;
Result = strreplace ( Result , TIMERMACRO_MATCH , Match ) ;
Result = strreplace ( Result , TIMERMACRO_AFTER , After ) ;
2021-05-03 14:06:12 +02:00
return cString ( Result , true ) ;
2020-12-26 15:49:01 +01:00
}
return NULL ;
}
cTimer : : cTimer ( const cEvent * Event , const char * FileName , const cTimer * PatternTimer )
2002-10-20 12:28:55 +02:00
{
2015-09-06 09:14:53 +02:00
id = 0 ;
2002-10-20 12:28:55 +02:00
startTime = stopTime = 0 ;
2021-04-06 08:48:35 +02:00
scheduleStateSet = scheduleStateSpawn = scheduleStateAdjust = - 1 ;
2011-08-06 13:20:07 +02:00
deferred = 0 ;
2015-09-01 11:14:27 +02:00
pending = inVpsMargin = false ;
2004-02-29 14:21:22 +01:00
flags = tfActive ;
2020-12-26 15:49:01 +01:00
* pattern = 0 ;
2012-09-15 13:57:39 +02:00
* file = 0 ;
aux = NULL ;
2015-09-13 13:57:51 +02:00
remote = NULL ;
2012-09-15 13:57:39 +02:00
event = NULL ;
2020-12-26 15:49:01 +01:00
if ( ! PatternTimer | | PatternTimer - > HasFlags ( tfVps ) ) {
2021-01-07 16:00:17 +01:00
if ( Event - > Vps ( ) & & ( PatternTimer | | Setup . UseVps ) )
2020-12-26 15:49:01 +01:00
SetFlags ( tfVps ) ;
}
2015-09-01 11:14:27 +02:00
LOCK_CHANNELS_READ ;
channel = Channels - > GetByChannelID ( Event - > ChannelID ( ) , true ) ;
2004-02-29 14:21:22 +01:00
time_t tstart = ( flags & tfVps ) ? Event - > Vps ( ) : Event - > StartTime ( ) ;
time_t tstop = tstart + Event - > Duration ( ) ;
if ( ! ( HasFlags ( tfVps ) ) ) {
2021-04-04 13:38:13 +02:00
int MarginStart = 0 ;
int MarginStop = 0 ;
CalcMargins ( MarginStart , MarginStop , Event ) ;
2021-01-19 13:21:51 +01:00
tstart - = MarginStart ;
tstop + = MarginStop ;
2004-02-29 14:21:22 +01:00
}
2002-10-20 12:28:55 +02:00
struct tm tm_r ;
struct tm * time = localtime_r ( & tstart , & tm_r ) ;
2005-03-19 15:38:43 +01:00
day = SetTime ( tstart , 0 ) ;
weekdays = 0 ;
2002-10-20 12:28:55 +02:00
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 ;
2020-12-26 15:49:01 +01:00
priority = PatternTimer ? PatternTimer - > Priority ( ) : Setup . DefaultPriority ;
lifetime = PatternTimer ? PatternTimer - > Lifetime ( ) : Setup . DefaultLifetime ;
if ( ! FileName )
FileName = Event - > Title ( ) ;
if ( ! isempty ( FileName ) )
Utf8Strn0Cpy ( file , FileName , sizeof ( file ) ) ;
2012-09-15 13:57:39 +02:00
SetEvent ( Event ) ;
2002-10-20 12:28:55 +02:00
}
2006-09-08 15:06:09 +02:00
cTimer : : cTimer ( const cTimer & Timer )
{
channel = NULL ;
aux = NULL ;
2015-09-01 11:14:27 +02:00
remote = NULL ;
2006-09-08 15:06:09 +02:00
event = NULL ;
2008-04-13 12:45:15 +02:00
flags = tfNone ;
2006-09-08 15:06:09 +02:00
* this = Timer ;
}
2002-10-20 12:28:55 +02:00
cTimer : : ~ cTimer ( )
{
2015-09-01 11:14:27 +02:00
if ( event )
event - > DecNumTimers ( ) ;
2006-02-25 12:09:22 +01:00
free ( aux ) ;
2015-09-01 11:14:27 +02:00
free ( remote ) ;
2002-10-20 12:28:55 +02:00
}
cTimer & cTimer : : operator = ( const cTimer & Timer )
{
2006-09-08 15:06:09 +02:00
if ( & Timer ! = this ) {
2015-09-06 09:14:53 +02:00
id = Timer . id ;
2006-09-08 15:06:09 +02:00
startTime = Timer . startTime ;
stopTime = Timer . stopTime ;
2021-04-06 08:48:35 +02:00
scheduleStateSet = scheduleStateSpawn = scheduleStateAdjust = - 1 ;
2015-09-01 11:14:27 +02:00
deferred = 0 ;
2006-09-08 15:06:09 +02:00
pending = Timer . pending ;
inVpsMargin = Timer . inVpsMargin ;
2015-09-13 13:57:51 +02:00
flags = Timer . flags ;
2006-09-08 15:06:09 +02:00
channel = Timer . channel ;
day = Timer . day ;
weekdays = Timer . weekdays ;
start = Timer . start ;
stop = Timer . stop ;
priority = Timer . priority ;
lifetime = Timer . lifetime ;
2020-12-26 15:49:01 +01:00
strncpy ( pattern , Timer . pattern , sizeof ( pattern ) ) ;
2006-09-08 15:06:09 +02:00
strncpy ( file , Timer . file , sizeof ( file ) ) ;
free ( aux ) ;
aux = Timer . aux ? strdup ( Timer . aux ) : NULL ;
2015-09-01 11:14:27 +02:00
free ( remote ) ;
remote = Timer . remote ? strdup ( Timer . remote ) : NULL ;
if ( event )
event - > DecNumTimers ( ) ;
event = Timer . event ;
if ( event )
event - > IncNumTimers ( ) ;
2006-09-08 15:06:09 +02:00
}
2002-10-20 12:28:55 +02:00
return * this ;
}
2021-04-04 13:38:13 +02:00
void cTimer : : CalcMargins ( int & MarginStart , int & MarginStop , const cEvent * Event )
{
MarginStart = Setup . MarginStart * 60 ;
MarginStop = Setup . MarginStop * 60 ;
// To make sure the timer gets assigned to the correct event, we must
// make sure that this is the only event that overlaps 100%:
if ( const cEvent * e = dynamic_cast < const cEvent * > ( Event - > Prev ( ) ) )
MarginStart = max ( 0 , min ( MarginStart , e - > Duration ( ) - 60 ) ) ;
if ( const cEvent * e = dynamic_cast < const cEvent * > ( Event - > Next ( ) ) )
MarginStop = max ( 0 , min ( MarginStop , e - > Duration ( ) - 60 ) ) ;
}
2004-11-01 10:40:38 +01:00
int cTimer : : Compare ( const cListObject & ListObject ) const
2002-10-20 12:28:55 +02:00
{
2012-06-02 12:10:36 +02:00
const cTimer * ti = ( const cTimer * ) & ListObject ;
2002-10-20 12:28:55 +02:00
time_t t1 = StartTime ( ) ;
time_t t2 = ti - > StartTime ( ) ;
2004-11-01 10:40:38 +01:00
int r = t1 - t2 ;
if ( r = = 0 )
r = ti - > priority - priority ;
2020-12-26 15:49:01 +01:00
if ( IsPatternTimer ( ) ^ ti - > IsPatternTimer ( ) ) {
if ( IsPatternTimer ( ) )
r = 1 ;
else
r = - 1 ;
}
else if ( IsPatternTimer ( ) & & ti - > IsPatternTimer ( ) )
r = strcoll ( Pattern ( ) , ti - > Pattern ( ) ) ;
2004-11-01 10:40:38 +01:00
return r ;
2002-10-20 12:28:55 +02:00
}
2020-12-26 15:49:01 +01:00
cString cTimer : : PatternAndFile ( void ) const
{
if ( IsPatternTimer ( ) )
return cString : : sprintf ( " {%s}%s " , pattern , file ) ;
return file ;
}
2008-02-16 15:04:49 +01:00
cString cTimer : : ToText ( bool UseChannelID ) const
2002-10-20 12:28:55 +02:00
{
2020-12-26 15:49:01 +01:00
strreplace ( pattern , ' : ' , ' | ' ) ;
2002-11-10 15:50:21 +01:00
strreplace ( file , ' : ' , ' | ' ) ;
2020-12-26 15:49:01 +01:00
cString buffer = cString : : sprintf ( " %u:%s:%s:%04d:%04d:%d:%d:%s:%s " , flags , UseChannelID ? * Channel ( ) - > GetChannelID ( ) . ToString ( ) : * itoa ( Channel ( ) - > Number ( ) ) , * PrintDay ( day , weekdays , true ) , start , stop , priority , lifetime , * PatternAndFile ( ) , aux ? aux : " " ) ;
strreplace ( pattern , ' | ' , ' : ' ) ;
2002-11-10 15:50:21 +01:00
strreplace ( file , ' | ' , ' : ' ) ;
2008-02-15 14:57:48 +01:00
return buffer ;
2002-10-20 12:28:55 +02:00
}
2005-03-20 15:15:42 +01:00
cString cTimer : : ToDescr ( void ) const
{
2020-12-26 15:49:01 +01:00
return cString : : sprintf ( " %d%s%s (%d %04d-%04d %s'%s') " , Id ( ) , remote ? " @ " : " " , remote ? remote : " " , Channel ( ) - > Number ( ) , start , stop , HasFlags ( tfVps ) ? " VPS " : " " , * PatternAndFile ( ) ) ;
2005-03-20 15:15:42 +01:00
}
2002-10-20 12:28:55 +02:00
int cTimer : : TimeToInt ( int t )
{
return ( t / 100 * 60 + t % 100 ) * 60 ;
}
2005-03-19 15:38:43 +01:00
bool cTimer : : ParseDay ( const char * s , time_t & Day , int & WeekDays )
{
// possible formats are:
// 19
// 2005-03-19
// MTWTFSS
// MTWTFSS@19
// MTWTFSS@2005-03-19
Day = 0 ;
WeekDays = 0 ;
s = skipspace ( s ) ;
if ( ! * s )
return false ;
const char * a = strchr ( s , ' @ ' ) ;
const char * d = a ? a + 1 : isdigit ( * s ) ? s : NULL ;
if ( d ) {
if ( strlen ( d ) = = 10 ) {
struct tm tm_r ;
if ( 3 = = sscanf ( d , " %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
Day = mktime ( & tm_r ) ;
2002-10-20 12:28:55 +02:00
}
2005-03-19 15:38:43 +01:00
else
return false ;
}
else {
// handle "day of month" for compatibility with older versions:
char * tail = NULL ;
2005-06-11 14:30:26 +02:00
int day = strtol ( d , & tail , 10 ) ;
2005-03-19 15:38:43 +01:00
if ( tail & & * tail | | day < 1 | | day > 31 )
return false ;
time_t t = time ( NULL ) ;
int DaysToCheck = 61 ; // 61 to handle months with 31/30/31
for ( int i = - 1 ; i < = DaysToCheck ; i + + ) {
time_t t0 = IncDay ( t , i ) ;
if ( GetMDay ( t0 ) = = day ) {
Day = SetTime ( t0 , 0 ) ;
break ;
}
}
2002-10-20 12:28:55 +02:00
}
}
2005-03-19 15:38:43 +01:00
if ( a | | ! isdigit ( * s ) ) {
if ( ( a & & a - s = = 7 ) | | strlen ( s ) = = 7 ) {
for ( const char * p = s + 6 ; p > = s ; p - - ) {
WeekDays < < = 1 ;
WeekDays | = ( * p ! = ' - ' ) ;
}
}
else
return false ;
}
return true ;
2002-10-20 12:28:55 +02:00
}
2007-06-10 13:02:43 +02:00
cString cTimer : : PrintDay ( time_t Day , int WeekDays , bool SingleByteChars )
2002-10-20 12:28:55 +02:00
{
2007-06-10 13:02:43 +02:00
# define DAYBUFFERSIZE 64
2004-12-26 12:45:22 +01:00
char buffer [ DAYBUFFERSIZE ] ;
2005-03-19 15:38:43 +01:00
char * b = buffer ;
if ( WeekDays ) {
2007-08-11 12:39:06 +02:00
// TRANSLATORS: the first character of each weekday, beginning with monday
const char * w = trNOOP ( " MTWTFSS " ) ;
2007-06-10 13:02:43 +02:00
if ( ! SingleByteChars )
w = tr ( w ) ;
2002-10-20 12:28:55 +02:00
while ( * w ) {
2007-06-10 13:02:43 +02:00
int sl = Utf8CharLen ( w ) ;
if ( WeekDays & 1 ) {
for ( int i = 0 ; i < sl ; i + + )
b [ i ] = w [ i ] ;
b + = sl ;
}
else
* b + + = ' - ' ;
2005-03-19 15:38:43 +01:00
WeekDays > > = 1 ;
2007-06-10 13:02:43 +02:00
w + = sl ;
2002-10-20 12:28:55 +02:00
}
2005-03-19 15:38:43 +01:00
if ( Day )
* b + + = ' @ ' ;
}
if ( Day ) {
struct tm tm_r ;
localtime_r ( & Day , & tm_r ) ;
b + = strftime ( b , DAYBUFFERSIZE - ( b - buffer ) , " %Y-%m-%d " , & tm_r ) ;
2002-10-20 12:28:55 +02:00
}
2005-03-19 15:38:43 +01:00
* b = 0 ;
2002-10-20 12:28:55 +02:00
return buffer ;
}
2005-03-20 15:15:42 +01:00
cString cTimer : : PrintFirstDay ( void ) const
2002-10-20 12:28:55 +02:00
{
2005-03-19 15:38:43 +01:00
if ( weekdays ) {
2007-06-10 13:02:43 +02:00
cString s = PrintDay ( day , weekdays , true ) ;
2002-10-20 12:28:55 +02:00
if ( strlen ( s ) = = 18 )
2004-12-26 12:45:22 +01:00
return * s + 8 ;
2002-10-20 12:28:55 +02:00
}
return " " ; // not NULL, so the caller can always use the result
}
bool cTimer : : Parse ( const char * s )
{
2002-11-10 15:50:21 +01:00
char * channelbuffer = NULL ;
char * daybuffer = NULL ;
char * filebuffer = NULL ;
2006-02-25 12:09:22 +01:00
free ( aux ) ;
aux = NULL ;
2013-12-28 11:37:42 +01:00
//XXX Apparently sscanf() doesn't work correctly if the last %m argument
2012-06-09 14:40:26 +02:00
//XXX results in an empty string (this first occurred when the EIT gathering
2002-10-20 12:28:55 +02:00
//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.
2020-09-16 13:48:33 +02:00
//XXX If anybody can shed some light on why sscanf() fails here, I'd love
2002-10-20 12:28:55 +02:00
//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 ;
}
2002-11-10 15:50:21 +01:00
bool result = false ;
2013-12-28 11:37:42 +01:00
if ( 8 < = sscanf ( s , " %u :%m[^:]:%m[^:]:%d :%d :%d :%d :%m[^: \n ]:%m[^ \n ] " , & flags , & channelbuffer , & daybuffer , & start , & stop , & priority , & lifetime , & filebuffer , & aux ) ) {
2006-02-25 12:09:22 +01:00
if ( aux & & ! * skipspace ( aux ) ) {
free ( aux ) ;
aux = NULL ;
2002-10-20 12:28:55 +02:00
}
//TODO add more plausibility checks
2005-03-19 15:38:43 +01:00
result = ParseDay ( daybuffer , day , weekdays ) ;
2020-12-26 15:49:01 +01:00
char * fb = filebuffer ;
if ( * fb = = ' { ' ) {
if ( char * p = strchr ( fb , ' } ' ) ) {
* p = 0 ;
Utf8Strn0Cpy ( pattern , fb + 1 , sizeof ( pattern ) ) ;
strreplace ( pattern , ' | ' , ' : ' ) ;
fb = p + 1 ;
}
}
else
* pattern = 0 ;
Utf8Strn0Cpy ( file , fb , sizeof ( file ) ) ;
2002-10-20 12:28:55 +02:00
strreplace ( file , ' | ' , ' : ' ) ;
2015-09-01 11:14:27 +02:00
LOCK_CHANNELS_READ ;
2004-01-04 12:30:00 +01:00
if ( isnumber ( channelbuffer ) )
2015-09-01 11:14:27 +02:00
channel = Channels - > GetByNumber ( atoi ( channelbuffer ) ) ;
2004-01-04 12:30:00 +01:00
else
2015-09-01 11:14:27 +02:00
channel = Channels - > GetByChannelID ( tChannelID : : FromString ( channelbuffer ) , true , true ) ;
2002-10-20 12:28:55 +02:00
if ( ! channel ) {
2002-11-10 15:50:21 +01:00
esyslog ( " ERROR: channel %s not defined " , channelbuffer ) ;
result = false ;
2002-10-20 12:28:55 +02:00
}
}
2002-11-10 15:50:21 +01:00
free ( channelbuffer ) ;
free ( daybuffer ) ;
free ( filebuffer ) ;
2002-10-20 12:28:55 +02:00
free ( s2 ) ;
2002-11-10 15:50:21 +01:00
return result ;
2002-10-20 12:28:55 +02:00
}
bool cTimer : : Save ( FILE * f )
{
2015-09-01 11:14:27 +02:00
if ( ! Remote ( ) )
2015-09-10 10:39:45 +02:00
return fprintf ( f , " %s \n " , * ToText ( true ) ) > 0 ;
2015-09-01 11:14:27 +02:00
return true ;
2002-10-20 12:28:55 +02:00
}
2004-11-01 10:40:38 +01:00
bool cTimer : : IsSingleEvent ( void ) const
2002-10-20 12:28:55 +02:00
{
2005-03-19 15:38:43 +01:00
return ! weekdays ;
2002-10-20 12:28:55 +02:00
}
2004-11-14 16:27:27 +01:00
int cTimer : : GetMDay ( time_t t )
2002-10-20 12:28:55 +02:00
{
struct tm tm_r ;
return localtime_r ( & t , & tm_r ) - > tm_mday ;
}
2004-11-14 16:27:27 +01:00
int cTimer : : GetWDay ( time_t t )
2002-10-20 12:28:55 +02:00
{
struct tm tm_r ;
int weekday = localtime_r ( & t , & tm_r ) - > tm_wday ;
2006-04-21 15:15:18 +02:00
return weekday = = 0 ? 6 : weekday - 1 ; // we start with Monday==0!
2002-10-20 12:28:55 +02:00
}
2004-11-01 10:40:38 +01:00
bool cTimer : : DayMatches ( time_t t ) const
2002-10-20 12:28:55 +02:00
{
2005-03-19 15:38:43 +01:00
return IsSingleEvent ( ) ? SetTime ( t , 0 ) = = day : ( weekdays & ( 1 < < GetWDay ( t ) ) ) ! = 0 ;
2002-10-20 12:28:55 +02:00
}
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 ) ;
}
2020-12-26 15:49:01 +01:00
void cTimer : : SetPattern ( const char * Pattern )
{
Utf8Strn0Cpy ( pattern , Pattern , sizeof ( pattern ) ) ;
}
2012-02-20 15:53:33 +01:00
void cTimer : : SetFile ( const char * File )
2002-10-20 12:28:55 +02:00
{
if ( ! isempty ( File ) )
2007-06-16 10:41:21 +02:00
Utf8Strn0Cpy ( file , File , sizeof ( file ) ) ;
2002-10-20 12:28:55 +02:00
}
2012-02-27 10:21:28 +01:00
# define EITPRESENTFOLLOWINGRATE 10 // max. seconds between two occurrences of the "EIT present/following table for the actual multiplex" (2s by the standard, using some more for safety)
2006-04-09 09:12:47 +02:00
bool cTimer : : Matches ( time_t t , bool Directly , int Margin ) const
2002-10-20 12:28:55 +02:00
{
startTime = stopTime = 0 ;
if ( t = = 0 )
t = time ( NULL ) ;
int begin = TimeToInt ( start ) ; // seconds from midnight
2017-11-12 13:13:06 +01:00
int end = TimeToInt ( stop ) ;
int length = end - begin ;
2002-10-20 12:28:55 +02:00
2005-03-19 15:38:43 +01:00
if ( IsSingleEvent ( ) ) {
2017-11-12 13:13:06 +01:00
time_t t0 = day ;
startTime = SetTime ( t0 , begin ) ;
if ( length < 0 )
t0 = IncDay ( day , 1 ) ;
stopTime = SetTime ( t0 , end ) ;
2005-03-19 15:38:43 +01:00
}
else {
2017-11-12 13:13:06 +01:00
time_t d = day ? max ( day , t ) : t ;
2005-03-19 15:38:43 +01:00
for ( int i = - 1 ; i < = 7 ; i + + ) {
2017-11-12 13:13:06 +01:00
time_t t0 = IncDay ( d , i ) ;
2005-03-19 15:38:43 +01:00
if ( DayMatches ( t0 ) ) {
time_t a = SetTime ( t0 , begin ) ;
2017-11-12 13:13:06 +01:00
if ( length < 0 )
t0 = IncDay ( d , i + 1 ) ;
time_t b = SetTime ( t0 , end ) ;
2006-08-05 12:06:11 +02:00
if ( ( ! day | | a > = day ) & & t < b ) {
2005-03-19 15:38:43 +01:00
startTime = a ;
stopTime = b ;
break ;
}
2002-10-20 12:28:55 +02:00
}
}
2005-03-19 15:38:43 +01:00
if ( ! startTime )
2006-05-25 12:44:07 +02:00
startTime = IncDay ( t , 7 ) ; // just to have something that's more than a week in the future
2005-03-19 15:38:43 +01:00
else if ( ! Directly & & ( t > startTime | | t > day + SECSINDAY + 3600 ) ) // +3600 in case of DST change
day = 0 ;
}
2004-02-29 14:21:22 +01:00
2020-12-26 15:49:01 +01:00
if ( IsPatternTimer ( ) )
return false ; // we only need to have start/stopTime initialized
2011-08-06 13:20:07 +02:00
if ( t < deferred )
return false ;
deferred = 0 ;
2004-02-29 14:21:22 +01:00
if ( HasFlags ( tfActive ) ) {
2021-04-16 16:26:47 +02:00
if ( event ) {
if ( HasFlags ( tfVps ) ) {
if ( event - > Vps ( ) ) {
if ( Margin | | ! Directly ) {
startTime = event - > StartTime ( ) ;
stopTime = event - > EndTime ( ) ;
if ( ! Margin ) { // this is an actual check
if ( event - > Schedule ( ) - > PresentSeenWithin ( EITPRESENTFOLLOWINGRATE ) ) // VPS control can only work with up-to-date events...
return event - > IsRunning ( true ) ;
// ...otherwise we fall back to normal timer handling below (note: Margin == 0!)
}
}
}
}
else if ( HasFlags ( tfSpawned ) ) {
if ( ! Margin & & ! Directly ) { // this is an actual check
// The spawned timer's start-/stopTimes are adjusted to the event's times in AdjustSpawnedTimer().
// However, in order to make sure the timer is set to the correct event, the margins at begin
// end end are limited by the durations of the events before and after this timer's event.
// The recording, though, shall always use the full start/stop margins, hence this calculation:
return event - > StartTime ( ) - Setup . MarginStart * 60 < = t & & t < event - > EndTime ( ) + Setup . MarginStop * 60 ;
2012-02-27 10:21:28 +01:00
}
2006-05-07 09:01:59 +02:00
}
2004-02-29 14:21:22 +01:00
}
2006-04-09 09:12:47 +02:00
return startTime < = t + Margin & & t < stopTime ; // must stop *before* stopTime to allow adjacent timers
2004-02-29 14:21:22 +01:00
}
return false ;
}
2005-03-13 11:42:59 +01:00
# define FULLMATCH 1000
2012-12-08 11:05:39 +01:00
eTimerMatch cTimer : : Matches ( const cEvent * Event , int * Overlap ) const
2004-02-29 14:21:22 +01:00
{
2005-03-20 13:13:02 +01:00
// Overlap is the percentage of the Event's duration that is covered by
// this timer (based on FULLMATCH for finer granularity than just 100).
// To make sure a VPS timer can be distinguished from a plain 100% overlap,
// it gets an additional 100 added, and a VPS event that is actually running
// gets 200 added to the FULLMATCH.
2017-06-25 10:03:19 +02:00
if ( channel - > GetChannelID ( ) = = Event - > ChannelID ( ) ) {
2004-02-29 14:21:22 +01:00
bool UseVps = HasFlags ( tfVps ) & & Event - > Vps ( ) ;
2020-12-26 15:49:01 +01:00
if ( IsPatternTimer ( ) ) {
if ( startswith ( Pattern ( ) , TIMERPATTERN_AVOID ) ) {
cString FileName = MakePatternFileName ( Pattern ( ) , Event - > Title ( ) , Event - > ShortText ( ) , File ( ) ) ;
if ( * FileName ) {
const char * p = strgetlast ( * FileName , FOLDERDELIMCHAR ) ;
if ( DoneRecordingsPattern . Contains ( p ) )
return tmNone ;
}
else
return tmNone ;
}
else if ( ! MatchPattern ( Pattern ( ) , Event - > Title ( ) ) )
return tmNone ;
UseVps = false ;
}
2005-03-13 11:42:59 +01:00
Matches ( UseVps ? Event - > Vps ( ) : Event - > StartTime ( ) , true ) ;
2005-06-18 13:32:03 +02:00
int overlap = 0 ;
2018-03-11 13:19:30 +01:00
if ( UseVps ) {
if ( startTime = = Event - > Vps ( ) ) {
overlap = FULLMATCH ;
if ( Event - > IsRunning ( ) )
overlap + = 200 ;
else if ( Event - > RunningStatus ( ) ! = SI : : RunningStatusNotRunning )
overlap + = 100 ;
}
}
else {
2005-06-18 13:32:03 +02:00
if ( startTime < = Event - > StartTime ( ) & & Event - > EndTime ( ) < = stopTime )
overlap = FULLMATCH ;
else if ( stopTime < = Event - > StartTime ( ) | | Event - > EndTime ( ) < = startTime )
overlap = 0 ;
2020-12-26 15:49:01 +01:00
else {
2005-06-18 13:32:03 +02:00
overlap = ( min ( stopTime , Event - > EndTime ( ) ) - max ( startTime , Event - > StartTime ( ) ) ) * FULLMATCH / max ( Event - > Duration ( ) , 1 ) ;
2020-12-26 15:49:01 +01:00
if ( IsPatternTimer ( ) & & overlap > 0 )
overlap = FULLMATCH ;
}
2005-06-18 13:32:03 +02:00
}
2004-02-29 14:21:22 +01:00
startTime = stopTime = 0 ;
2005-03-13 11:42:59 +01:00
if ( Overlap )
* Overlap = overlap ;
2006-04-01 13:27:14 +02:00
return overlap > = FULLMATCH ? tmFull : overlap > 0 ? tmPartial : tmNone ;
2004-02-29 14:21:22 +01:00
}
return tmNone ;
2002-10-20 12:28:55 +02:00
}
2005-03-20 11:19:36 +01:00
# define EXPIRELATENCY 60 // seconds (just in case there's a short glitch in the VPS signal)
2005-03-20 15:15:42 +01:00
bool cTimer : : Expired ( void ) const
2005-03-20 11:19:36 +01:00
{
2021-04-20 09:50:02 +02:00
if ( IsSingleEvent ( ) & & ! Recording ( ) ) {
time_t ExpireTime = StopTimeEvent ( ) ;
if ( HasFlags ( tfVps ) )
ExpireTime + = EXPIRELATENCY ;
return ExpireTime < = time ( NULL ) ;
}
return false ;
2005-03-20 11:19:36 +01:00
}
2004-11-01 10:40:38 +01:00
time_t cTimer : : StartTime ( void ) const
2002-10-20 12:28:55 +02:00
{
if ( ! startTime )
Matches ( ) ;
return startTime ;
}
2004-11-01 10:40:38 +01:00
time_t cTimer : : StopTime ( void ) const
2002-10-20 12:28:55 +02:00
{
if ( ! stopTime )
Matches ( ) ;
return stopTime ;
}
2021-04-18 14:56:40 +02:00
time_t cTimer : : StartTimeEvent ( void ) const
{
if ( event ) {
if ( HasFlags ( tfVps ) & & event - > Vps ( ) )
return event - > StartTime ( ) ;
else if ( HasFlags ( tfSpawned ) )
return event - > StartTime ( ) - Setup . MarginStart * 60 ;
}
return StartTime ( ) ;
}
time_t cTimer : : StopTimeEvent ( void ) const
{
if ( event ) {
if ( HasFlags ( tfVps ) & & event - > Vps ( ) )
return event - > EndTime ( ) ;
else if ( HasFlags ( tfSpawned ) )
return event - > EndTime ( ) + Setup . MarginStop * 60 ;
}
return StopTime ( ) ;
}
2006-02-28 12:44:00 +01:00
# define EPGLIMITBEFORE (1 * 3600) // Time in seconds before a timer's start time and
# define EPGLIMITAFTER (1 * 3600) // after its stop time within which EPG events will be taken into consideration.
2006-02-25 15:57:56 +01:00
2015-09-06 09:14:53 +02:00
void cTimer : : SetId ( int Id )
{
id = Id ;
}
2021-04-13 13:54:00 +02:00
cTimer * cTimer : : SpawnPatternTimer ( const cEvent * Event , cTimers * Timers )
2020-12-26 15:49:01 +01:00
{
cString FileName = MakePatternFileName ( Pattern ( ) , Event - > Title ( ) , Event - > ShortText ( ) , File ( ) ) ;
isyslog ( " spawning timer %s for event %s " , * ToDescr ( ) , * Event - > ToDescr ( ) ) ;
cTimer * t = new cTimer ( Event , FileName , this ) ;
t - > SetFlags ( tfSpawned ) ;
if ( startswith ( Pattern ( ) , TIMERPATTERN_AVOID ) )
t - > SetFlags ( tfAvoid ) ;
Timers - > Add ( t ) ;
HandleRemoteTimerModifications ( t ) ;
2021-04-13 13:54:00 +02:00
return t ;
2020-12-26 15:49:01 +01:00
}
bool cTimer : : SpawnPatternTimers ( const cSchedules * Schedules , cTimers * Timers )
{
bool TimersSpawned = false ;
const cSchedule * Schedule = Schedules - > GetSchedule ( Channel ( ) ) ;
if ( Schedule & & Schedule - > Events ( ) - > First ( ) ) {
2021-04-06 08:48:35 +02:00
if ( Schedule - > Modified ( scheduleStateSpawn ) ) {
2020-12-26 15:49:01 +01:00
time_t Now = time ( NULL ) ;
2021-04-13 13:54:00 +02:00
// Find the first event that matches this pattern timer and either already has a spawned
// timer, or has not yet ended:
2020-12-26 15:49:01 +01:00
for ( const cEvent * e = Schedule - > Events ( ) - > First ( ) ; e ; e = Schedule - > Events ( ) - > Next ( e ) ) {
if ( Matches ( e ) ! = tmNone ) {
2021-04-13 13:54:00 +02:00
const cTimer * Timer = Timers - > GetTimerForEvent ( e , tfSpawned ) ; // a matching event that already has a spawned timer
if ( ! Timer & & e - > EndTime ( ) > Now ) { // only look at events that have not yet ended
Timer = SpawnPatternTimer ( e , Timers ) ;
2020-12-26 15:49:01 +01:00
TimersSpawned = true ;
}
2021-04-13 13:54:00 +02:00
if ( Timer ) {
// Check all following matching events that would start while the first timer
// is still recording:
bool UseVps = Timer - > HasFlags ( tfVps ) ;
2021-04-20 09:50:02 +02:00
time_t Limit = Timer - > StopTimeEvent ( ) ;
if ( UseVps )
Limit + = EXPIRELATENCY ;
else
2021-04-13 13:54:00 +02:00
Limit + = Setup . MarginStart * 60 ;
for ( e = Schedule - > Events ( ) - > Next ( e ) ; e ; e = Schedule - > Events ( ) - > Next ( e ) ) {
if ( e - > StartTime ( ) < = Limit ) {
if ( ! Timers - > GetTimerForEvent ( e , tfSpawned ) & & Matches ( e ) ! = tmNone ) {
SpawnPatternTimer ( e , Timers ) ;
TimersSpawned = true ;
}
if ( UseVps )
break ; // with VPS we only need to check the event immediately following the first one
}
else
break ; // no need to check events that are too far in the future
}
2020-12-26 15:49:01 +01:00
break ;
2021-04-13 13:54:00 +02:00
}
2020-12-26 15:49:01 +01:00
}
}
}
}
return TimersSpawned ;
}
2021-04-06 08:48:35 +02:00
bool cTimer : : AdjustSpawnedTimer ( void )
{
if ( Event ( ) ) {
if ( const cSchedule * Schedule = Event ( ) - > Schedule ( ) ) { // events may be deleted from their schedule in cSchedule::DropOutdated()!
if ( Schedule - > Modified ( scheduleStateAdjust ) ) {
// Adjust the timer to shifted start/stop times of the event if necessary:
time_t tstart = Event ( ) - > StartTime ( ) ;
time_t tstop = Event ( ) - > EndTime ( ) ;
int MarginStart = 0 ;
int MarginStop = 0 ;
CalcMargins ( MarginStart , MarginStop , Event ( ) ) ;
tstart - = MarginStart ;
tstop + = MarginStop ;
// Event start/end times are given in "seconds since the epoch". Some broadcasters use values
// that result in full minutes (with zero seconds), while others use any values. VDR's timers
// use times given in full minutes, truncating any seconds. Thus we only react if the start/stop
// times of the timer are off by at least one minute:
if ( abs ( StartTime ( ) - tstart ) > = 60 | | abs ( StopTime ( ) - tstop ) > = 60 ) {
2021-04-10 11:32:50 +02:00
cString OldDescr = ToDescr ( ) ;
2021-04-06 08:48:35 +02:00
struct tm tm_r ;
struct tm * time = localtime_r ( & tstart , & tm_r ) ;
SetDay ( cTimer : : SetTime ( tstart , 0 ) ) ;
SetStart ( time - > tm_hour * 100 + time - > tm_min ) ;
time = localtime_r ( & tstop , & tm_r ) ;
SetStop ( time - > tm_hour * 100 + time - > tm_min ) ;
Matches ( ) ;
2021-04-10 11:32:50 +02:00
isyslog ( " timer %s times changed to %s-%s " , * OldDescr , * TimeString ( tstart ) , * TimeString ( tstop ) ) ;
2021-04-06 08:48:35 +02:00
return true ;
}
}
}
}
return false ;
}
2021-01-14 10:29:05 +01:00
void cTimer : : TriggerRespawn ( void )
{
2021-04-06 14:25:05 +02:00
if ( Local ( ) & & HasFlags ( tfSpawned ) | | IsPatternTimer ( ) ) {
2021-01-14 10:29:05 +01:00
if ( Channel ( ) ) {
LOCK_CHANNELS_READ ;
if ( const cSchedule * Schedule = Channel ( ) - > Schedule ( ) ) {
dsyslog ( " triggering respawn for timer %s " , * ToDescr ( ) ) ;
LOCK_SCHEDULES_WRITE ;
const_cast < cSchedule * > ( Schedule ) - > SetModified ( ) ;
}
}
}
}
2015-09-01 11:14:27 +02:00
bool cTimer : : SetEventFromSchedule ( const cSchedules * Schedules )
2006-02-25 15:57:56 +01:00
{
2020-12-26 15:49:01 +01:00
if ( IsPatternTimer ( ) )
return SetEvent ( NULL ) ;
2006-02-25 15:57:56 +01:00
const cSchedule * Schedule = Schedules - > GetSchedule ( Channel ( ) ) ;
2006-04-01 13:27:14 +02:00
if ( Schedule & & Schedule - > Events ( ) - > First ( ) ) {
2021-04-06 08:48:35 +02:00
if ( Schedule - > Modified ( scheduleStateSet ) ) {
2006-02-25 15:57:56 +01:00
const cEvent * Event = NULL ;
2006-04-01 13:27:14 +02:00
if ( HasFlags ( tfVps ) & & Schedule - > Events ( ) - > First ( ) - > Vps ( ) ) {
// VPS timers only match if their start time exactly matches the event's VPS time:
for ( const cEvent * e = Schedule - > Events ( ) - > First ( ) ; e ; e = Schedule - > Events ( ) - > Next ( e ) ) {
2018-03-11 13:19:30 +01:00
if ( e - > StartTime ( ) ) {
2006-04-09 09:12:47 +02:00
int overlap = 0 ;
2018-03-11 13:19:30 +01:00
if ( Matches ( e , & overlap ) = = tmFull ) {
2006-04-09 09:12:47 +02:00
Event = e ;
2018-03-11 13:19:30 +01:00
if ( overlap > FULLMATCH )
break ; // take the first matching event
2006-04-09 09:12:47 +02:00
}
2006-04-01 13:27:14 +02:00
}
}
}
else {
// Normal timers match the event they have the most overlap with:
int Overlap = 0 ;
// Set up the time frame within which to check events:
Matches ( 0 , true ) ;
time_t TimeFrameBegin = StartTime ( ) - EPGLIMITBEFORE ;
time_t TimeFrameEnd = StopTime ( ) + EPGLIMITAFTER ;
for ( const cEvent * e = Schedule - > Events ( ) - > First ( ) ; e ; e = Schedule - > Events ( ) - > Next ( e ) ) {
2006-03-25 11:39:57 +01:00
if ( e - > EndTime ( ) < TimeFrameBegin )
continue ; // skip events way before the timer starts
if ( e - > StartTime ( ) > TimeFrameEnd )
break ; // the rest is way after the timer ends
2006-04-01 13:27:14 +02:00
int overlap = 0 ;
Matches ( e , & overlap ) ;
if ( overlap & & overlap > = Overlap ) {
if ( Event & & overlap = = Overlap & & e - > Duration ( ) < = Event - > Duration ( ) )
2006-02-25 15:57:56 +01:00
continue ; // if overlap is the same, we take the longer event
2006-04-01 13:27:14 +02:00
Overlap = overlap ;
Event = e ;
2006-02-25 15:57:56 +01:00
}
}
2006-04-01 13:27:14 +02:00
}
2015-09-01 11:14:27 +02:00
return SetEvent ( Event ) ;
2006-02-25 15:57:56 +01:00
}
}
2015-09-01 11:14:27 +02:00
return false ;
2006-02-25 15:57:56 +01:00
}
2015-09-01 11:14:27 +02:00
bool cTimer : : SetEvent ( const cEvent * Event )
2004-02-29 14:21:22 +01:00
{
2015-09-01 11:14:27 +02:00
if ( event ! = Event ) {
if ( event )
event - > DecNumTimers ( ) ;
if ( Event ) {
2006-03-25 12:51:29 +01:00
isyslog ( " timer %s set to event %s " , * ToDescr ( ) , * Event - > ToDescr ( ) ) ;
2015-09-01 11:14:27 +02:00
Event - > IncNumTimers ( ) ;
2021-04-06 08:48:35 +02:00
Event - > Schedule ( ) - > Modified ( scheduleStateSet ) ; // to get the current state
2015-09-01 11:14:27 +02:00
}
else {
2005-03-20 15:15:42 +01:00
isyslog ( " timer %s set to no event " , * ToDescr ( ) ) ;
2021-04-06 08:48:35 +02:00
scheduleStateSet = scheduleStateSpawn = scheduleStateAdjust = - 1 ;
2015-09-01 11:14:27 +02:00
}
2004-02-29 14:21:22 +01:00
event = Event ;
2015-09-01 11:14:27 +02:00
return true ;
2004-02-29 14:21:22 +01:00
}
2015-09-01 11:14:27 +02:00
return false ;
2004-02-29 14:21:22 +01:00
}
2002-10-20 12:28:55 +02:00
void cTimer : : SetRecording ( bool Recording )
{
2015-09-01 11:14:27 +02:00
if ( Recording )
2005-05-07 11:10:56 +02:00
SetFlags ( tfRecording ) ;
else
ClrFlags ( tfRecording ) ;
2015-09-01 11:14:27 +02:00
isyslog ( " timer %s %s " , * ToDescr ( ) , Recording ? " start " : " stop " ) ;
2002-10-20 12:28:55 +02:00
}
void cTimer : : SetPending ( bool Pending )
{
pending = Pending ;
}
2004-02-29 14:21:22 +01:00
void cTimer : : SetInVpsMargin ( bool InVpsMargin )
{
if ( InVpsMargin & & ! inVpsMargin )
2005-03-20 15:15:42 +01:00
isyslog ( " timer %s entered VPS margin " , * ToDescr ( ) ) ;
2004-02-29 14:21:22 +01:00
inVpsMargin = InVpsMargin ;
}
2012-02-20 15:37:01 +01:00
void cTimer : : SetDay ( time_t Day )
{
day = Day ;
}
void cTimer : : SetWeekDays ( int WeekDays )
{
weekdays = WeekDays ;
}
void cTimer : : SetStart ( int Start )
{
start = Start ;
}
void cTimer : : SetStop ( int Stop )
{
stop = Stop ;
}
2006-01-03 11:46:57 +01:00
void cTimer : : SetPriority ( int Priority )
{
2006-01-08 11:44:37 +01:00
priority = Priority ;
2006-01-03 11:46:57 +01:00
}
2012-02-20 15:37:01 +01:00
void cTimer : : SetLifetime ( int Lifetime )
{
lifetime = Lifetime ;
}
void cTimer : : SetAux ( const char * Aux )
{
free ( aux ) ;
2015-09-01 11:14:27 +02:00
aux = Aux ? strdup ( Aux ) : NULL ;
}
void cTimer : : SetRemote ( const char * Remote )
{
free ( remote ) ;
remote = Remote ? strdup ( Remote ) : NULL ;
2012-02-20 15:37:01 +01:00
}
2011-08-06 13:20:07 +02:00
void cTimer : : SetDeferred ( int Seconds )
{
deferred = time ( NULL ) + Seconds ;
isyslog ( " timer %s deferred for %d seconds " , * ToDescr ( ) , Seconds ) ;
}
2006-01-06 14:31:57 +01:00
void cTimer : : SetFlags ( uint Flags )
2004-02-29 14:21:22 +01:00
{
flags | = Flags ;
}
2006-01-06 14:31:57 +01:00
void cTimer : : ClrFlags ( uint Flags )
2002-10-20 12:28:55 +02:00
{
2004-02-29 14:21:22 +01:00
flags & = ~ Flags ;
}
2006-01-06 14:31:57 +01:00
void cTimer : : InvFlags ( uint Flags )
2004-02-29 14:21:22 +01:00
{
flags ^ = Flags ;
}
2006-01-06 14:31:57 +01:00
bool cTimer : : HasFlags ( uint Flags ) const
2004-02-29 14:21:22 +01:00
{
return ( flags & Flags ) = = Flags ;
2002-10-20 12:28:55 +02:00
}
void cTimer : : Skip ( void )
{
2005-03-19 15:38:43 +01:00
day = IncDay ( SetTime ( StartTime ( ) , 0 ) , 1 ) ;
2006-05-25 14:38:04 +02:00
startTime = 0 ;
2006-02-25 15:57:56 +01:00
SetEvent ( NULL ) ;
2002-10-20 12:28:55 +02:00
}
void cTimer : : OnOff ( void )
{
2020-12-26 15:49:01 +01:00
if ( IsSingleEvent ( ) | | IsPatternTimer ( ) )
2004-02-29 14:21:22 +01:00
InvFlags ( tfActive ) ;
2005-03-19 15:38:43 +01:00
else if ( day ) {
day = 0 ;
2004-02-29 14:21:22 +01:00
ClrFlags ( tfActive ) ;
2002-10-20 12:28:55 +02:00
}
2004-02-29 14:21:22 +01:00
else if ( HasFlags ( tfActive ) )
2002-10-20 12:28:55 +02:00
Skip ( ) ;
else
2004-02-29 14:21:22 +01:00
SetFlags ( tfActive ) ;
2006-02-25 15:57:56 +01:00
SetEvent ( NULL ) ;
2020-12-26 15:49:01 +01:00
if ( HasFlags ( tfActive ) )
2021-01-14 10:29:05 +01:00
TriggerRespawn ( ) ; // have pattern timers spawn if necessary
2002-10-20 12:28:55 +02:00
Matches ( ) ; // refresh start and end time
}
2008-02-10 14:11:47 +01:00
// --- cTimers ---------------------------------------------------------------
2002-10-20 12:28:55 +02:00
2015-09-01 11:14:27 +02:00
cTimers cTimers : : timers ;
2015-09-06 09:14:53 +02:00
int cTimers : : lastTimerId = 0 ;
2002-10-20 12:28:55 +02:00
2004-10-24 15:01:50 +02:00
cTimers : : cTimers ( void )
2017-05-28 12:47:54 +02:00
: cConfig < cTimer > ( " 1 Timers " )
2004-10-24 15:01:50 +02:00
{
2006-03-26 14:38:46 +02:00
lastDeleteExpired = 0 ;
2004-10-24 15:01:50 +02:00
}
2015-09-01 11:14:27 +02:00
bool cTimers : : Load ( const char * FileName )
{
LOCK_TIMERS_WRITE ;
Timers - > SetExplicitModify ( ) ;
if ( timers . cConfig < cTimer > : : Load ( FileName ) ) {
for ( cTimer * ti = timers . First ( ) ; ti ; ti = timers . Next ( ti ) ) {
2015-09-10 10:39:45 +02:00
ti - > SetId ( NewTimerId ( ) ) ;
2015-09-01 11:14:27 +02:00
ti - > ClrFlags ( tfRecording ) ;
Timers - > SetModified ( ) ;
}
return true ;
}
return false ;
}
2015-09-10 10:39:45 +02:00
int cTimers : : NewTimerId ( void )
{
return + + lastTimerId ; // no need for locking, the caller must have a lock on the global Timers list
}
2017-04-20 10:08:04 +02:00
const cTimer * cTimers : : GetById ( int Id , const char * Remote ) const
2015-09-06 09:14:53 +02:00
{
for ( const cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
2017-04-20 10:08:04 +02:00
if ( ti - > Id ( ) = = Id ) {
if ( ! Remote & & ! ti - > Remote ( ) | | Remote & & ti - > Remote ( ) & & strcmp ( Remote , ti - > Remote ( ) ) = = 0 )
return ti ;
}
2015-09-06 09:14:53 +02:00
}
return NULL ;
}
2016-12-22 14:27:01 +01:00
const cTimer * cTimers : : GetTimer ( const cTimer * Timer ) const
2002-10-20 12:28:55 +02:00
{
2016-12-22 14:27:01 +01:00
for ( const cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
2015-09-01 11:14:27 +02:00
if ( ! ti - > Remote ( ) & &
ti - > Channel ( ) = = Timer - > Channel ( ) & &
2005-09-09 15:28:26 +02:00
( ti - > WeekDays ( ) & & ti - > WeekDays ( ) = = Timer - > WeekDays ( ) | | ! ti - > WeekDays ( ) & & ti - > Day ( ) = = Timer - > Day ( ) ) & &
ti - > Start ( ) = = Timer - > Start ( ) & &
ti - > Stop ( ) = = Timer - > Stop ( ) )
2002-10-20 12:28:55 +02:00
return ti ;
}
return NULL ;
}
2015-09-01 11:14:27 +02:00
const cTimer * cTimers : : GetMatch ( time_t t ) const
2002-10-20 12:28:55 +02:00
{
2006-02-03 15:25:51 +01:00
static int LastPending = - 1 ;
2015-09-01 11:14:27 +02:00
const cTimer * t0 = NULL ;
for ( const cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
if ( ! ti - > Remote ( ) & & ! ti - > Recording ( ) & & ti - > Matches ( t ) ) {
2006-02-03 15:25:51 +01:00
if ( ti - > Pending ( ) ) {
2013-03-29 15:37:16 +01:00
if ( ti - > Index ( ) > LastPending ) {
2006-02-03 15:25:51 +01:00
LastPending = ti - > Index ( ) ;
2013-03-29 15:37:16 +01:00
return ti ;
}
2006-02-03 15:25:51 +01:00
else
continue ;
}
2002-10-20 12:28:55 +02:00
if ( ! t0 | | ti - > Priority ( ) > t0 - > Priority ( ) )
t0 = ti ;
}
}
2006-02-03 15:25:51 +01:00
if ( ! t0 )
LastPending = - 1 ;
2002-10-20 12:28:55 +02:00
return t0 ;
}
2015-09-01 11:14:27 +02:00
const cTimer * cTimers : : GetMatch ( const cEvent * Event , eTimerMatch * Match ) const
2004-02-29 14:21:22 +01:00
{
2015-09-01 11:14:27 +02:00
const cTimer * t = NULL ;
2012-12-08 11:05:39 +01:00
eTimerMatch m = tmNone ;
2015-09-01 11:14:27 +02:00
for ( const cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
2012-12-08 11:05:39 +01:00
eTimerMatch tm = ti - > Matches ( Event ) ;
2021-01-15 13:52:40 +01:00
if ( tm > m | | tm = = tmFull & & t & & ( t - > Remote ( ) & & ti - > Local ( ) | | t - > IsPatternTimer ( ) & & ti - > HasFlags ( tfSpawned ) ) ) {
2004-02-29 14:21:22 +01:00
t = ti ;
m = tm ;
}
}
if ( Match )
* Match = m ;
return t ;
}
2022-11-20 10:57:31 +01:00
const cTimer * cTimers : : GetTimerForEvent ( const cEvent * Event , eTimerFlags Flags ) const
2021-04-10 10:09:50 +02:00
{
if ( Event & & Event - > HasTimer ( ) ) {
for ( const cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
if ( ti - > Event ( ) = = Event & & ti - > Local ( ) & & ti - > HasFlags ( Flags ) )
return ti ;
}
}
return NULL ;
}
2017-10-31 09:47:14 +01:00
int cTimers : : GetMaxPriority ( void ) const
{
2019-05-23 10:00:48 +02:00
int n = - 1 ;
2017-10-31 09:47:14 +01:00
for ( const cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
if ( ! ti - > Remote ( ) & & ti - > Recording ( ) )
n = max ( n , ti - > Priority ( ) ) ;
}
return n ;
}
2015-09-01 11:14:27 +02:00
const cTimer * cTimers : : GetNextActiveTimer ( void ) const
2002-10-20 12:28:55 +02:00
{
2015-09-01 11:14:27 +02:00
const cTimer * t0 = NULL ;
for ( const cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
2020-12-26 15:49:01 +01:00
if ( ! ti - > Remote ( ) & & ! ti - > IsPatternTimer ( ) ) {
2015-09-01 11:14:27 +02:00
ti - > Matches ( ) ;
if ( ( ti - > HasFlags ( tfActive ) ) & & ( ! t0 | | ti - > StopTime ( ) > time ( NULL ) & & ti - > Compare ( * t0 ) < 0 ) )
t0 = ti ;
}
2002-10-20 12:28:55 +02:00
}
return t0 ;
}
2004-02-29 14:21:22 +01:00
2015-09-01 11:14:27 +02:00
const cTimers * cTimers : : GetTimersRead ( cStateKey & StateKey , int TimeoutMs )
2004-10-31 10:22:32 +01:00
{
2015-09-01 11:14:27 +02:00
return timers . Lock ( StateKey , false , TimeoutMs ) ? & timers : NULL ;
}
cTimers * cTimers : : GetTimersWrite ( cStateKey & StateKey , int TimeoutMs )
{
return timers . Lock ( StateKey , true , TimeoutMs ) ? & timers : NULL ;
2004-10-31 10:22:32 +01:00
}
2008-02-16 15:04:49 +01:00
void cTimers : : Add ( cTimer * Timer , cTimer * After )
{
2015-09-06 09:14:53 +02:00
if ( ! Timer - > Remote ( ) )
2015-09-10 10:39:45 +02:00
Timer - > SetId ( NewTimerId ( ) ) ;
2008-02-16 15:04:49 +01:00
cConfig < cTimer > : : Add ( Timer , After ) ;
cStatus : : MsgTimerChange ( Timer , tcAdd ) ;
}
void cTimers : : Ins ( cTimer * Timer , cTimer * Before )
{
cConfig < cTimer > : : Ins ( Timer , Before ) ;
cStatus : : MsgTimerChange ( Timer , tcAdd ) ;
}
void cTimers : : Del ( cTimer * Timer , bool DeleteObject )
{
cStatus : : MsgTimerChange ( Timer , tcDel ) ;
cConfig < cTimer > : : Del ( Timer , DeleteObject ) ;
}
2015-09-01 11:14:27 +02:00
const cTimer * cTimers : : UsesChannel ( const cChannel * Channel ) const
2004-10-31 10:22:32 +01:00
{
2015-09-01 11:14:27 +02:00
for ( const cTimer * Timer = First ( ) ; Timer ; Timer = Next ( Timer ) ) {
if ( Timer - > Channel ( ) = = Channel )
return Timer ;
}
return NULL ;
2004-10-31 10:22:32 +01:00
}
2015-09-01 11:14:27 +02:00
bool cTimers : : SetEvents ( const cSchedules * Schedules )
2004-02-29 14:21:22 +01:00
{
2015-09-01 11:14:27 +02:00
bool TimersModified = false ;
2020-12-26 15:49:01 +01:00
for ( cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
if ( ! ti - > IsPatternTimer ( ) )
TimersModified | = ti - > SetEventFromSchedule ( Schedules ) ;
}
return TimersModified ;
}
bool cTimers : : SpawnPatternTimers ( const cSchedules * Schedules )
{
bool TimersModified = false ;
for ( cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
if ( ti - > IsPatternTimer ( ) & & ti - > Local ( ) ) {
if ( ti - > HasFlags ( tfActive ) )
TimersModified | = ti - > SpawnPatternTimers ( Schedules , this ) ;
}
2021-04-06 08:48:35 +02:00
}
return TimersModified ;
}
bool cTimers : : AdjustSpawnedTimers ( void )
{
bool TimersModified = false ;
for ( cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
if ( ti - > Local ( ) ) {
if ( ti - > HasFlags ( tfSpawned ) & & ! ti - > HasFlags ( tfVps ) )
TimersModified | = ti - > AdjustSpawnedTimer ( ) ;
}
2020-12-26 15:49:01 +01:00
}
2015-09-01 11:14:27 +02:00
return TimersModified ;
2004-02-29 14:21:22 +01:00
}
2005-03-20 11:19:36 +01:00
2021-04-20 13:22:37 +02:00
# define DELETE_EXPIRED_TIMEOUT 30 // seconds
bool cTimers : : DeleteExpired ( bool Force )
2005-03-20 11:19:36 +01:00
{
2021-04-20 13:22:37 +02:00
if ( ! Force & & time ( NULL ) - lastDeleteExpired < DELETE_EXPIRED_TIMEOUT )
2015-09-01 11:14:27 +02:00
return false ;
bool TimersModified = false ;
2005-03-20 11:19:36 +01:00
cTimer * ti = First ( ) ;
while ( ti ) {
cTimer * next = Next ( ti ) ;
2015-09-01 11:14:27 +02:00
if ( ! ti - > Remote ( ) & & ti - > Expired ( ) ) {
2020-12-26 15:49:01 +01:00
ti - > SetEvent ( NULL ) ; // Del() doesn't call ~cTimer() right away, so this is necessary here
2021-01-14 10:29:05 +01:00
ti - > TriggerRespawn ( ) ; // in case this is a spawned timer
2005-03-20 15:15:42 +01:00
isyslog ( " deleting timer %s " , * ti - > ToDescr ( ) ) ;
2005-03-20 11:19:36 +01:00
Del ( ti ) ;
2015-09-01 11:14:27 +02:00
TimersModified = true ;
2005-03-20 11:19:36 +01:00
}
ti = next ;
}
2006-03-26 14:38:46 +02:00
lastDeleteExpired = time ( NULL ) ;
2015-09-01 11:14:27 +02:00
return TimersModified ;
}
2018-02-25 13:26:17 +01:00
bool cTimers : : StoreRemoteTimers ( const char * ServerName , const cStringList * RemoteTimers )
2015-09-01 11:14:27 +02:00
{
bool Result = false ;
2018-02-28 10:06:47 +01:00
if ( ! ServerName | | ! RemoteTimers | | RemoteTimers - > Size ( ) = = 0 ) {
// Remove remote timers from this list:
cTimer * Timer = First ( ) ;
while ( Timer ) {
cTimer * t = Next ( Timer ) ;
if ( Timer - > Remote ( ) & & ( ! ServerName | | strcmp ( Timer - > Remote ( ) , ServerName ) = = 0 ) ) {
Del ( Timer ) ;
Result = true ;
}
Timer = t ;
2015-09-01 11:14:27 +02:00
}
2018-02-28 10:06:47 +01:00
return Result ;
}
// Collect all locally stored remote timers from ServerName:
cStringList tl ;
for ( cTimer * ti = First ( ) ; ti ; ti = Next ( ti ) ) {
if ( ti - > Remote ( ) & & strcmp ( ti - > Remote ( ) , ServerName ) = = 0 )
tl . Append ( strdup ( cString : : sprintf ( " %d %s " , ti - > Id ( ) , * ti - > ToText ( true ) ) ) ) ;
}
tl . SortNumerically ( ) ; // RemoteTimers is also sorted numerically!
// Compare the two lists and react accordingly:
int il = 0 ; // index into the local ("left") list of remote timers
int ir = 0 ; // index into the remote ("right") list of timers
int sl = tl . Size ( ) ;
int sr = RemoteTimers - > Size ( ) ;
for ( ; ; ) {
int AddTimer = 0 ;
int DelTimer = 0 ;
if ( il < sl ) { // still have left entries
int nl = atoi ( tl [ il ] ) ;
if ( ir < sr ) { // still have right entries
// Compare timers:
int nr = atoi ( ( * RemoteTimers ) [ ir ] ) ;
if ( nl = = nr ) // same timer id
AddTimer = DelTimer = nl ;
else if ( nl < nr ) // left entry not in right list
DelTimer = nl ;
else // right entry not in left list
AddTimer = nr ;
}
else // processed all right entries
DelTimer = nl ;
}
2018-03-17 10:07:19 +01:00
else if ( ir < sr ) { // still have right entries
2018-02-28 10:06:47 +01:00
AddTimer = atoi ( ( * RemoteTimers ) [ ir ] ) ;
2018-03-17 10:07:19 +01:00
if ( ! AddTimer ) {
esyslog ( " ERROR: %s: error in timer settings: %s " , ServerName , ( * RemoteTimers ) [ ir ] ) ;
ir + + ;
continue ; // let's see if we can process the rest
}
}
2018-02-28 10:06:47 +01:00
else // processed all left and right entries
break ;
if ( AddTimer & & DelTimer ) {
if ( strcmp ( tl [ il ] , ( * RemoteTimers ) [ ir ] ) ! = 0 ) {
// Overwrite timer:
char * v = ( * RemoteTimers ) [ ir ] ;
while ( * v & & * v ! = ' ' )
v + + ; // skip id
if ( cTimer * l = GetById ( DelTimer , ServerName ) ) {
cTimer r ;
if ( r . Parse ( v ) ) {
r . SetRemote ( ServerName ) ;
r . SetId ( AddTimer ) ;
* l = r ;
2018-02-25 13:26:17 +01:00
Result = true ;
}
2018-02-28 10:06:47 +01:00
else
2018-03-17 10:07:19 +01:00
esyslog ( " ERROR: %d@%s: error in timer settings: %s " , DelTimer , ServerName , v ) ;
2018-02-25 13:26:17 +01:00
}
}
2018-02-28 10:06:47 +01:00
else // identical timer, nothing to do
;
il + + ;
ir + + ;
2018-02-25 13:26:17 +01:00
}
2018-02-28 10:06:47 +01:00
else if ( AddTimer ) {
char * v = ( * RemoteTimers ) [ ir ] ;
while ( * v & & * v ! = ' ' )
v + + ; // skip id
cTimer * Timer = new cTimer ;
if ( Timer - > Parse ( v ) ) {
Timer - > SetRemote ( ServerName ) ;
Timer - > SetId ( AddTimer ) ;
Add ( Timer ) ;
Result = true ;
}
else {
esyslog ( " ERROR: %s: error in timer settings: %s " , ServerName , v ) ;
delete Timer ;
}
ir + + ;
}
else if ( DelTimer ) {
if ( cTimer * t = GetById ( DelTimer , ServerName ) ) {
Del ( t ) ;
Result = true ;
}
il + + ;
}
2018-03-17 10:07:19 +01:00
else {
esyslog ( " ERROR: oops while storing remote timers! " ) ;
break ; // let's not get stuck here!
}
2018-02-28 10:06:47 +01:00
}
2018-02-25 13:26:17 +01:00
return Result ;
2005-03-20 11:19:36 +01:00
}
2012-06-02 12:10:36 +02:00
2017-03-30 15:25:20 +02:00
static bool RemoteTimerError ( const cTimer * Timer , cString * Msg )
{
if ( Msg )
* Msg = cString : : sprintf ( " %s %d@%s! " , tr ( " Error while accessing remote timer " ) , Timer - > Id ( ) , Timer - > Remote ( ) ) ;
return false ; // convenience return code
}
bool HandleRemoteTimerModifications ( cTimer * NewTimer , cTimer * OldTimer , cString * Msg )
{
cStringList Response ;
if ( ! NewTimer ) {
if ( OldTimer ) { // timer shall be deleted from remote machine
if ( OldTimer - > Remote ( ) & & OldTimer - > Id ( ) ) {
if ( ! ExecSVDRPCommand ( OldTimer - > Remote ( ) , cString : : sprintf ( " DELT %d " , OldTimer - > Id ( ) ) , & Response ) | | SVDRPCode ( Response [ 0 ] ) ! = 250 )
return RemoteTimerError ( OldTimer , Msg ) ;
}
isyslog ( " deleted timer %s " , * OldTimer - > ToDescr ( ) ) ;
}
}
else if ( ! OldTimer | | OldTimer - > Local ( ) | | ! OldTimer - > Id ( ) ) {
if ( NewTimer - > Local ( ) ) { // timer stays local, nothing to do
if ( OldTimer & & OldTimer - > Id ( ) )
isyslog ( " modified timer %s " , * NewTimer - > ToDescr ( ) ) ;
else
isyslog ( " added timer %s " , * NewTimer - > ToDescr ( ) ) ;
}
else { // timer is new, or moved from local to remote
if ( ! ExecSVDRPCommand ( NewTimer - > Remote ( ) , cString : : sprintf ( " NEWT %s " , * NewTimer - > ToText ( true ) ) , & Response ) | | SVDRPCode ( Response [ 0 ] ) ! = 250 )
return RemoteTimerError ( NewTimer , Msg ) ;
int RemoteId = atoi ( SVDRPValue ( Response [ 0 ] ) ) ;
if ( RemoteId < = 0 )
return RemoteTimerError ( NewTimer , Msg ) ;
NewTimer - > SetId ( RemoteId ) ;
if ( OldTimer & & OldTimer - > Id ( ) ) {
isyslog ( " moved timer %d to %s " , OldTimer - > Id ( ) , * NewTimer - > ToDescr ( ) ) ;
}
else
isyslog ( " added timer %s " , * NewTimer - > ToDescr ( ) ) ;
}
}
else if ( NewTimer - > Local ( ) ) { // timer is moved from remote to local
if ( ! ExecSVDRPCommand ( OldTimer - > Remote ( ) , cString : : sprintf ( " DELT %d " , OldTimer - > Id ( ) ) , & Response ) | | SVDRPCode ( Response [ 0 ] ) ! = 250 )
return RemoteTimerError ( OldTimer , Msg ) ;
NewTimer - > SetId ( cTimers : : NewTimerId ( ) ) ;
NewTimer - > ClrFlags ( tfRecording ) ; // in case it was recording on the remote machine
isyslog ( " moved timer %d@%s to %s " , OldTimer - > Id ( ) , OldTimer - > Remote ( ) , * NewTimer - > ToDescr ( ) ) ;
}
else if ( strcmp ( OldTimer - > Remote ( ) , NewTimer - > Remote ( ) ) = = 0 ) { // timer stays remote on same machine
if ( ! ExecSVDRPCommand ( OldTimer - > Remote ( ) , cString : : sprintf ( " MODT %d %s " , OldTimer - > Id ( ) , * NewTimer - > ToText ( true ) ) , & Response ) | | SVDRPCode ( Response [ 0 ] ) ! = 250 )
return RemoteTimerError ( NewTimer , Msg ) ;
isyslog ( " modified timer %s " , * NewTimer - > ToDescr ( ) ) ;
}
else { // timer is moved from one remote machine to an other
if ( ! ExecSVDRPCommand ( NewTimer - > Remote ( ) , cString : : sprintf ( " NEWT %s " , * NewTimer - > ToText ( true ) ) , & Response ) | | SVDRPCode ( Response [ 0 ] ) ! = 250 )
return RemoteTimerError ( NewTimer , Msg ) ;
int RemoteId = atoi ( SVDRPValue ( Response [ 0 ] ) ) ;
if ( RemoteId < = 0 )
return RemoteTimerError ( NewTimer , Msg ) ;
NewTimer - > SetId ( RemoteId ) ;
if ( ! ExecSVDRPCommand ( OldTimer - > Remote ( ) , cString : : sprintf ( " DELT %d " , OldTimer - > Id ( ) ) , & Response ) | | SVDRPCode ( Response [ 0 ] ) ! = 250 )
return RemoteTimerError ( OldTimer , Msg ) ;
isyslog ( " moved timer %d@%s to %s " , OldTimer - > Id ( ) , OldTimer - > Remote ( ) , * NewTimer - > ToDescr ( ) ) ;
}
return true ;
}
2012-06-02 12:10:36 +02:00
// --- cSortedTimers ---------------------------------------------------------
static int CompareTimers ( const void * a , const void * b )
{
return ( * ( const cTimer * * ) a ) - > Compare ( * * ( const cTimer * * ) b ) ;
}
2015-09-01 11:14:27 +02:00
cSortedTimers : : cSortedTimers ( const cTimers * Timers )
: cVector < const cTimer * > ( Timers - > Count ( ) )
2012-06-02 12:10:36 +02:00
{
2015-09-01 11:14:27 +02:00
for ( const cTimer * Timer = Timers - > First ( ) ; Timer ; Timer = Timers - > Next ( Timer ) )
2012-06-02 12:10:36 +02:00
Append ( Timer ) ;
Sort ( CompareTimers ) ;
}