vdr  2.4.0
timers.c
Go to the documentation of this file.
1 /*
2  * timers.c: Timer handling
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: timers.c 4.18 2018/03/17 10:07:19 kls Exp $
8  */
9 
10 #include "timers.h"
11 #include <ctype.h>
12 #include "device.h"
13 #include "i18n.h"
14 #include "libsi/si.h"
15 #include "recording.h"
16 #include "remote.h"
17 #include "status.h"
18 #include "svdrp.h"
19 
20 // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
21 // format characters in order to allow any number of blanks after a numeric
22 // value!
23 
24 // --- cTimer ----------------------------------------------------------------
25 
26 cTimer::cTimer(bool Instant, bool Pause, const cChannel *Channel)
27 {
28  id = 0;
29  startTime = stopTime = 0;
30  scheduleState = -1;
31  deferred = 0;
32  pending = inVpsMargin = false;
33  flags = tfNone;
34  *file = 0;
35  aux = NULL;
36  remote = NULL;
37  event = NULL;
38  if (Instant)
41  channel = Channel ? Channel : Channels->GetByNumber(cDevice::CurrentChannel());
42  time_t t = time(NULL);
43  struct tm tm_r;
44  struct tm *now = localtime_r(&t, &tm_r);
45  day = SetTime(t, 0);
46  weekdays = 0;
47  start = now->tm_hour * 100 + now->tm_min;
48  stop = 0;
49  if (!Setup.InstantRecordTime && channel && (Instant || Pause)) {
51  if (const cSchedule *Schedule = Schedules->GetSchedule(channel)) {
52  if (const cEvent *Event = Schedule->GetPresentEvent()) {
53  time_t tstart = Event->StartTime();
54  time_t tstop = Event->EndTime();
55  if (Event->Vps() && Setup.UseVps) {
56  SetFlags(tfVps);
57  tstart = Event->Vps();
58  }
59  else {
60  tstop += Setup.MarginStop * 60;
61  tstart -= Setup.MarginStart * 60;
62  }
63  day = SetTime(tstart, 0);
64  struct tm *time = localtime_r(&tstart, &tm_r);
65  start = time->tm_hour * 100 + time->tm_min;
66  time = localtime_r(&tstop, &tm_r);
67  stop = time->tm_hour * 100 + time->tm_min;
68  SetEvent(Event);
69  }
70  }
71  }
72  if (!stop) {
73  stop = now->tm_hour * 60 + now->tm_min + (Setup.InstantRecordTime ? Setup.InstantRecordTime : DEFINSTRECTIME);
74  stop = (stop / 60) * 100 + (stop % 60);
75  }
76  if (stop >= 2400)
77  stop -= 2400;
80  if (Instant && channel)
81  snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", *Setup.NameInstantRecord ? Setup.NameInstantRecord : channel->Name());
82 }
83 
84 cTimer::cTimer(const cEvent *Event)
85 {
86  id = 0;
87  startTime = stopTime = 0;
88  scheduleState = -1;
89  deferred = 0;
90  pending = inVpsMargin = false;
91  flags = tfActive;
92  *file = 0;
93  aux = NULL;
94  remote = NULL;
95  event = NULL;
96  if (Event->Vps() && Setup.UseVps)
97  SetFlags(tfVps);
99  channel = Channels->GetByChannelID(Event->ChannelID(), true);
100  time_t tstart = (flags & tfVps) ? Event->Vps() : Event->StartTime();
101  time_t tstop = tstart + Event->Duration();
102  if (!(HasFlags(tfVps))) {
103  tstop += Setup.MarginStop * 60;
104  tstart -= Setup.MarginStart * 60;
105  }
106  struct tm tm_r;
107  struct tm *time = localtime_r(&tstart, &tm_r);
108  day = SetTime(tstart, 0);
109  weekdays = 0;
110  start = time->tm_hour * 100 + time->tm_min;
111  time = localtime_r(&tstop, &tm_r);
112  stop = time->tm_hour * 100 + time->tm_min;
113  if (stop >= 2400)
114  stop -= 2400;
117  const char *Title = Event->Title();
118  if (!isempty(Title))
119  Utf8Strn0Cpy(file, Event->Title(), sizeof(file));
120  SetEvent(Event);
121 }
122 
123 cTimer::cTimer(const cTimer &Timer)
124 {
125  channel = NULL;
126  aux = NULL;
127  remote = NULL;
128  event = NULL;
129  flags = tfNone;
130  *this = Timer;
131 }
132 
134 {
135  if (event)
136  event->DecNumTimers();
137  free(aux);
138  free(remote);
139 }
140 
142 {
143  if (&Timer != this) {
144  id = Timer.id;
145  startTime = Timer.startTime;
146  stopTime = Timer.stopTime;
147  scheduleState = -1;
148  deferred = 0;
149  pending = Timer.pending;
150  inVpsMargin = Timer.inVpsMargin;
151  flags = Timer.flags;
152  channel = Timer.channel;
153  day = Timer.day;
154  weekdays = Timer.weekdays;
155  start = Timer.start;
156  stop = Timer.stop;
157  priority = Timer.priority;
158  lifetime = Timer.lifetime;
159  strncpy(file, Timer.file, sizeof(file));
160  free(aux);
161  aux = Timer.aux ? strdup(Timer.aux) : NULL;
162  free(remote);
163  remote = Timer.remote ? strdup(Timer.remote) : NULL;
164  if (event)
165  event->DecNumTimers();
166  event = Timer.event;
167  if (event)
168  event->IncNumTimers();
169  }
170  return *this;
171 }
172 
173 int cTimer::Compare(const cListObject &ListObject) const
174 {
175  const cTimer *ti = (const cTimer *)&ListObject;
176  time_t t1 = StartTime();
177  time_t t2 = ti->StartTime();
178  int r = t1 - t2;
179  if (r == 0)
180  r = ti->priority - priority;
181  return r;
182 }
183 
184 cString cTimer::ToText(bool UseChannelID) const
185 {
186  strreplace(file, ':', '|');
187  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, file, aux ? aux : "");
188  strreplace(file, '|', ':');
189  return buffer;
190 }
191 
193 {
194  return cString::sprintf("%d%s%s (%d %04d-%04d %s'%s')", Id(), remote ? "@" : "", remote ? remote : "", Channel()->Number(), start, stop, HasFlags(tfVps) ? "VPS " : "", file);
195 }
196 
198 {
199  return (t / 100 * 60 + t % 100) * 60;
200 }
201 
202 bool cTimer::ParseDay(const char *s, time_t &Day, int &WeekDays)
203 {
204  // possible formats are:
205  // 19
206  // 2005-03-19
207  // MTWTFSS
208  // MTWTFSS@19
209  // MTWTFSS@2005-03-19
210 
211  Day = 0;
212  WeekDays = 0;
213  s = skipspace(s);
214  if (!*s)
215  return false;
216  const char *a = strchr(s, '@');
217  const char *d = a ? a + 1 : isdigit(*s) ? s : NULL;
218  if (d) {
219  if (strlen(d) == 10) {
220  struct tm tm_r;
221  if (3 == sscanf(d, "%d-%d-%d", &tm_r.tm_year, &tm_r.tm_mon, &tm_r.tm_mday)) {
222  tm_r.tm_year -= 1900;
223  tm_r.tm_mon--;
224  tm_r.tm_hour = tm_r.tm_min = tm_r.tm_sec = 0;
225  tm_r.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
226  Day = mktime(&tm_r);
227  }
228  else
229  return false;
230  }
231  else {
232  // handle "day of month" for compatibility with older versions:
233  char *tail = NULL;
234  int day = strtol(d, &tail, 10);
235  if (tail && *tail || day < 1 || day > 31)
236  return false;
237  time_t t = time(NULL);
238  int DaysToCheck = 61; // 61 to handle months with 31/30/31
239  for (int i = -1; i <= DaysToCheck; i++) {
240  time_t t0 = IncDay(t, i);
241  if (GetMDay(t0) == day) {
242  Day = SetTime(t0, 0);
243  break;
244  }
245  }
246  }
247  }
248  if (a || !isdigit(*s)) {
249  if ((a && a - s == 7) || strlen(s) == 7) {
250  for (const char *p = s + 6; p >= s; p--) {
251  WeekDays <<= 1;
252  WeekDays |= (*p != '-');
253  }
254  }
255  else
256  return false;
257  }
258  return true;
259 }
260 
261 cString cTimer::PrintDay(time_t Day, int WeekDays, bool SingleByteChars)
262 {
263 #define DAYBUFFERSIZE 64
264  char buffer[DAYBUFFERSIZE];
265  char *b = buffer;
266  if (WeekDays) {
267  // TRANSLATORS: the first character of each weekday, beginning with monday
268  const char *w = trNOOP("MTWTFSS");
269  if (!SingleByteChars)
270  w = tr(w);
271  while (*w) {
272  int sl = Utf8CharLen(w);
273  if (WeekDays & 1) {
274  for (int i = 0; i < sl; i++)
275  b[i] = w[i];
276  b += sl;
277  }
278  else
279  *b++ = '-';
280  WeekDays >>= 1;
281  w += sl;
282  }
283  if (Day)
284  *b++ = '@';
285  }
286  if (Day) {
287  struct tm tm_r;
288  localtime_r(&Day, &tm_r);
289  b += strftime(b, DAYBUFFERSIZE - (b - buffer), "%Y-%m-%d", &tm_r);
290  }
291  *b = 0;
292  return buffer;
293 }
294 
296 {
297  if (weekdays) {
298  cString s = PrintDay(day, weekdays, true);
299  if (strlen(s) == 18)
300  return *s + 8;
301  }
302  return ""; // not NULL, so the caller can always use the result
303 }
304 
305 bool cTimer::Parse(const char *s)
306 {
307  char *channelbuffer = NULL;
308  char *daybuffer = NULL;
309  char *filebuffer = NULL;
310  free(aux);
311  aux = NULL;
312  //XXX Apparently sscanf() doesn't work correctly if the last %m argument
313  //XXX results in an empty string (this first occurred when the EIT gathering
314  //XXX was put into a separate thread - don't know why this happens...
315  //XXX As a cure we copy the original string and add a blank.
316  //XXX If anybody can shed some light on why sscanf() failes here, I'd love
317  //XXX to hear about that!
318  char *s2 = NULL;
319  int l2 = strlen(s);
320  while (l2 > 0 && isspace(s[l2 - 1]))
321  l2--;
322  if (s[l2 - 1] == ':') {
323  s2 = MALLOC(char, l2 + 3);
324  strcat(strn0cpy(s2, s, l2 + 1), " \n");
325  s = s2;
326  }
327  bool result = false;
328  if (8 <= sscanf(s, "%u :%m[^:]:%m[^:]:%d :%d :%d :%d :%m[^:\n]:%m[^\n]", &flags, &channelbuffer, &daybuffer, &start, &stop, &priority, &lifetime, &filebuffer, &aux)) {
329  if (aux && !*skipspace(aux)) {
330  free(aux);
331  aux = NULL;
332  }
333  //TODO add more plausibility checks
334  result = ParseDay(daybuffer, day, weekdays);
335  Utf8Strn0Cpy(file, filebuffer, sizeof(file));
336  strreplace(file, '|', ':');
338  if (isnumber(channelbuffer))
339  channel = Channels->GetByNumber(atoi(channelbuffer));
340  else
341  channel = Channels->GetByChannelID(tChannelID::FromString(channelbuffer), true, true);
342  if (!channel) {
343  esyslog("ERROR: channel %s not defined", channelbuffer);
344  result = false;
345  }
346  }
347  free(channelbuffer);
348  free(daybuffer);
349  free(filebuffer);
350  free(s2);
351  return result;
352 }
353 
354 bool cTimer::Save(FILE *f)
355 {
356  if (!Remote())
357  return fprintf(f, "%s\n", *ToText(true)) > 0;
358  return true;
359 }
360 
361 bool cTimer::IsSingleEvent(void) const
362 {
363  return !weekdays;
364 }
365 
366 int cTimer::GetMDay(time_t t)
367 {
368  struct tm tm_r;
369  return localtime_r(&t, &tm_r)->tm_mday;
370 }
371 
372 int cTimer::GetWDay(time_t t)
373 {
374  struct tm tm_r;
375  int weekday = localtime_r(&t, &tm_r)->tm_wday;
376  return weekday == 0 ? 6 : weekday - 1; // we start with Monday==0!
377 }
378 
379 bool cTimer::DayMatches(time_t t) const
380 {
381  return IsSingleEvent() ? SetTime(t, 0) == day : (weekdays & (1 << GetWDay(t))) != 0;
382 }
383 
384 time_t cTimer::IncDay(time_t t, int Days)
385 {
386  struct tm tm_r;
387  tm tm = *localtime_r(&t, &tm_r);
388  tm.tm_mday += Days; // now tm_mday may be out of its valid range
389  int h = tm.tm_hour; // save original hour to compensate for DST change
390  tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
391  t = mktime(&tm); // normalize all values
392  tm.tm_hour = h; // compensate for DST change
393  return mktime(&tm); // calculate final result
394 }
395 
396 time_t cTimer::SetTime(time_t t, int SecondsFromMidnight)
397 {
398  struct tm tm_r;
399  tm tm = *localtime_r(&t, &tm_r);
400  tm.tm_hour = SecondsFromMidnight / 3600;
401  tm.tm_min = (SecondsFromMidnight % 3600) / 60;
402  tm.tm_sec = SecondsFromMidnight % 60;
403  tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
404  return mktime(&tm);
405 }
406 
407 void cTimer::SetFile(const char *File)
408 {
409  if (!isempty(File))
410  Utf8Strn0Cpy(file, File, sizeof(file));
411 }
412 
413 #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)
414 
415 bool cTimer::Matches(time_t t, bool Directly, int Margin) const
416 {
417  startTime = stopTime = 0;
418  if (t == 0)
419  t = time(NULL);
420 
421  int begin = TimeToInt(start); // seconds from midnight
422  int end = TimeToInt(stop);
423  int length = end - begin;
424 
425  if (IsSingleEvent()) {
426  time_t t0 = day;
427  startTime = SetTime(t0, begin);
428  if (length < 0)
429  t0 = IncDay(day, 1);
430  stopTime = SetTime(t0, end);
431  }
432  else {
433  time_t d = day ? max(day, t) : t;
434  for (int i = -1; i <= 7; i++) {
435  time_t t0 = IncDay(d, i);
436  if (DayMatches(t0)) {
437  time_t a = SetTime(t0, begin);
438  if (length < 0)
439  t0 = IncDay(d, i + 1);
440  time_t b = SetTime(t0, end);
441  if ((!day || a >= day) && t < b) {
442  startTime = a;
443  stopTime = b;
444  break;
445  }
446  }
447  }
448  if (!startTime)
449  startTime = IncDay(t, 7); // just to have something that's more than a week in the future
450  else if (!Directly && (t > startTime || t > day + SECSINDAY + 3600)) // +3600 in case of DST change
451  day = 0;
452  }
453 
454  if (t < deferred)
455  return false;
456  deferred = 0;
457 
458  if (HasFlags(tfActive)) {
459  if (HasFlags(tfVps) && event && event->Vps()) {
460  if (Margin || !Directly) {
461  startTime = event->StartTime();
462  stopTime = event->EndTime();
463  if (!Margin) { // this is an actual check
464  if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) // VPS control can only work with up-to-date events...
465  return event->IsRunning(true);
466  return startTime <= t && t < stopTime; // ...otherwise we fall back to normal timer handling
467  }
468  }
469  }
470  return startTime <= t + Margin && t < stopTime; // must stop *before* stopTime to allow adjacent timers
471  }
472  return false;
473 }
474 
475 #define FULLMATCH 1000
476 
477 eTimerMatch cTimer::Matches(const cEvent *Event, int *Overlap) const
478 {
479  // Overlap is the percentage of the Event's duration that is covered by
480  // this timer (based on FULLMATCH for finer granularity than just 100).
481  // To make sure a VPS timer can be distinguished from a plain 100% overlap,
482  // it gets an additional 100 added, and a VPS event that is actually running
483  // gets 200 added to the FULLMATCH.
484  if (channel->GetChannelID() == Event->ChannelID()) {
485  bool UseVps = HasFlags(tfVps) && Event->Vps();
486  Matches(UseVps ? Event->Vps() : Event->StartTime(), true);
487  int overlap = 0;
488  if (UseVps) {
489  if (startTime == Event->Vps()) {
490  overlap = FULLMATCH;
491  if (Event->IsRunning())
492  overlap += 200;
494  overlap += 100;
495  }
496  }
497  else {
498  if (startTime <= Event->StartTime() && Event->EndTime() <= stopTime)
499  overlap = FULLMATCH;
500  else if (stopTime <= Event->StartTime() || Event->EndTime() <= startTime)
501  overlap = 0;
502  else
503  overlap = (min(stopTime, Event->EndTime()) - max(startTime, Event->StartTime())) * FULLMATCH / max(Event->Duration(), 1);
504  }
505  startTime = stopTime = 0;
506  if (Overlap)
507  *Overlap = overlap;
508  return overlap >= FULLMATCH ? tmFull : overlap > 0 ? tmPartial : tmNone;
509  }
510  return tmNone;
511 }
512 
513 #define EXPIRELATENCY 60 // seconds (just in case there's a short glitch in the VPS signal)
514 
515 bool cTimer::Expired(void) const
516 {
517  return IsSingleEvent()
518  && !Recording()
519  && StopTime() + EXPIRELATENCY <= time(NULL)
520  && (!HasFlags(tfVps) || !event || !event->Vps() || event->EndTime() + EXPIRELATENCY <= time(NULL));
521 }
522 
523 time_t cTimer::StartTime(void) const
524 {
525  if (!startTime)
526  Matches();
527  return startTime;
528 }
529 
530 time_t cTimer::StopTime(void) const
531 {
532  if (!stopTime)
533  Matches();
534  return stopTime;
535 }
536 
537 #define EPGLIMITBEFORE (1 * 3600) // Time in seconds before a timer's start time and
538 #define EPGLIMITAFTER (1 * 3600) // after its stop time within which EPG events will be taken into consideration.
539 
540 void cTimer::SetId(int Id)
541 {
542  id = Id;
543 }
544 
546 {
547  const cSchedule *Schedule = Schedules->GetSchedule(Channel());
548  if (Schedule && Schedule->Events()->First()) {
549  if (Schedule->Modified(scheduleState)) {
550  const cEvent *Event = NULL;
551  if (HasFlags(tfVps) && Schedule->Events()->First()->Vps()) {
552  // VPS timers only match if their start time exactly matches the event's VPS time:
553  for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) {
554  if (e->StartTime()) {
555  int overlap = 0;
556  if (Matches(e, &overlap) == tmFull) {
557  Event = e;
558  if (overlap > FULLMATCH)
559  break; // take the first matching event
560  }
561  }
562  }
563  }
564  else {
565  // Normal timers match the event they have the most overlap with:
566  int Overlap = 0;
567  // Set up the time frame within which to check events:
568  Matches(0, true);
569  time_t TimeFrameBegin = StartTime() - EPGLIMITBEFORE;
570  time_t TimeFrameEnd = StopTime() + EPGLIMITAFTER;
571  for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) {
572  if (e->EndTime() < TimeFrameBegin)
573  continue; // skip events way before the timer starts
574  if (e->StartTime() > TimeFrameEnd)
575  break; // the rest is way after the timer ends
576  int overlap = 0;
577  Matches(e, &overlap);
578  if (overlap && overlap >= Overlap) {
579  if (Event && overlap == Overlap && e->Duration() <= Event->Duration())
580  continue; // if overlap is the same, we take the longer event
581  Overlap = overlap;
582  Event = e;
583  }
584  }
585  }
586  return SetEvent(Event);
587  }
588  }
589  return false;
590 }
591 
592 bool cTimer::SetEvent(const cEvent *Event)
593 {
594  if (event != Event) {
595  if (event)
596  event->DecNumTimers();
597  if (Event) {
598  isyslog("timer %s set to event %s", *ToDescr(), *Event->ToDescr());
599  Event->IncNumTimers();
600  Event->Schedule()->Modified(scheduleState); // to get the current state
601  }
602  else {
603  isyslog("timer %s set to no event", *ToDescr());
604  scheduleState = -1;
605  }
606  event = Event;
607  return true;
608  }
609  return false;
610 }
611 
612 void cTimer::SetRecording(bool Recording)
613 {
614  if (Recording)
616  else
618  isyslog("timer %s %s", *ToDescr(), Recording ? "start" : "stop");
619 }
620 
621 void cTimer::SetPending(bool Pending)
622 {
623  pending = Pending;
624 }
625 
626 void cTimer::SetInVpsMargin(bool InVpsMargin)
627 {
628  if (InVpsMargin && !inVpsMargin)
629  isyslog("timer %s entered VPS margin", *ToDescr());
631 }
632 
633 void cTimer::SetDay(time_t Day)
634 {
635  day = Day;
636 }
637 
638 void cTimer::SetWeekDays(int WeekDays)
639 {
640  weekdays = WeekDays;
641 }
642 
643 void cTimer::SetStart(int Start)
644 {
645  start = Start;
646 }
647 
648 void cTimer::SetStop(int Stop)
649 {
650  stop = Stop;
651 }
652 
653 void cTimer::SetPriority(int Priority)
654 {
655  priority = Priority;
656 }
657 
658 void cTimer::SetLifetime(int Lifetime)
659 {
660  lifetime = Lifetime;
661 }
662 
663 void cTimer::SetAux(const char *Aux)
664 {
665  free(aux);
666  aux = Aux ? strdup(Aux) : NULL;
667 }
668 
669 void cTimer::SetRemote(const char *Remote)
670 {
671  free(remote);
672  remote = Remote ? strdup(Remote) : NULL;
673 }
674 
675 void cTimer::SetDeferred(int Seconds)
676 {
677  deferred = time(NULL) + Seconds;
678  isyslog("timer %s deferred for %d seconds", *ToDescr(), Seconds);
679 }
680 
681 void cTimer::SetFlags(uint Flags)
682 {
683  flags |= Flags;
684 }
685 
686 void cTimer::ClrFlags(uint Flags)
687 {
688  flags &= ~Flags;
689 }
690 
691 void cTimer::InvFlags(uint Flags)
692 {
693  flags ^= Flags;
694 }
695 
696 bool cTimer::HasFlags(uint Flags) const
697 {
698  return (flags & Flags) == Flags;
699 }
700 
701 void cTimer::Skip(void)
702 {
703  day = IncDay(SetTime(StartTime(), 0), 1);
704  startTime = 0;
705  SetEvent(NULL);
706 }
707 
708 void cTimer::OnOff(void)
709 {
710  if (IsSingleEvent())
712  else if (day) {
713  day = 0;
715  }
716  else if (HasFlags(tfActive))
717  Skip();
718  else
720  SetEvent(NULL);
721  Matches(); // refresh start and end time
722 }
723 
724 // --- cTimers ---------------------------------------------------------------
725 
727 int cTimers::lastTimerId = 0;
728 
730 :cConfig<cTimer>("1 Timers")
731 {
732  lastDeleteExpired = 0;
733 }
734 
735 bool cTimers::Load(const char *FileName)
736 {
738  Timers->SetExplicitModify();
739  if (timers.cConfig<cTimer>::Load(FileName)) {
740  for (cTimer *ti = timers.First(); ti; ti = timers.Next(ti)) {
741  ti->SetId(NewTimerId());
742  ti->ClrFlags(tfRecording);
743  Timers->SetModified();
744  }
745  return true;
746  }
747  return false;
748 }
749 
751 {
752  return ++lastTimerId; // no need for locking, the caller must have a lock on the global Timers list
753 }
754 
755 const cTimer *cTimers::GetById(int Id, const char *Remote) const
756 {
757  for (const cTimer *ti = First(); ti; ti = Next(ti)) {
758  if (ti->Id() == Id) {
759  if (!Remote && !ti->Remote() || Remote && ti->Remote() && strcmp(Remote, ti->Remote()) == 0)
760  return ti;
761  }
762  }
763  return NULL;
764 }
765 
766 const cTimer *cTimers::GetTimer(const cTimer *Timer) const
767 {
768  for (const cTimer *ti = First(); ti; ti = Next(ti)) {
769  if (!ti->Remote() &&
770  ti->Channel() == Timer->Channel() &&
771  (ti->WeekDays() && ti->WeekDays() == Timer->WeekDays() || !ti->WeekDays() && ti->Day() == Timer->Day()) &&
772  ti->Start() == Timer->Start() &&
773  ti->Stop() == Timer->Stop())
774  return ti;
775  }
776  return NULL;
777 }
778 
779 const cTimer *cTimers::GetMatch(time_t t) const
780 {
781  static int LastPending = -1;
782  const cTimer *t0 = NULL;
783  for (const cTimer *ti = First(); ti; ti = Next(ti)) {
784  if (!ti->Remote() && !ti->Recording() && ti->Matches(t)) {
785  if (ti->Pending()) {
786  if (ti->Index() > LastPending) {
787  LastPending = ti->Index();
788  return ti;
789  }
790  else
791  continue;
792  }
793  if (!t0 || ti->Priority() > t0->Priority())
794  t0 = ti;
795  }
796  }
797  if (!t0)
798  LastPending = -1;
799  return t0;
800 }
801 
802 const cTimer *cTimers::GetMatch(const cEvent *Event, eTimerMatch *Match) const
803 {
804  const cTimer *t = NULL;
805  eTimerMatch m = tmNone;
806  for (const cTimer *ti = First(); ti; ti = Next(ti)) {
807  eTimerMatch tm = ti->Matches(Event);
808  if (tm > m || tm == tmFull && ti->Local()) {
809  t = ti;
810  m = tm;
811  if (m == tmFull && ti->Local())
812  break;
813  }
814  }
815  if (Match)
816  *Match = m;
817  return t;
818 }
819 
820 int cTimers::GetMaxPriority(void) const
821 {
822  int n = 0;
823  for (const cTimer *ti = First(); ti; ti = Next(ti)) {
824  if (!ti->Remote() && ti->Recording())
825  n = max(n, ti->Priority());
826  }
827  return n;
828 }
829 
831 {
832  const cTimer *t0 = NULL;
833  for (const cTimer *ti = First(); ti; ti = Next(ti)) {
834  if (!ti->Remote()) {
835  ti->Matches();
836  if ((ti->HasFlags(tfActive)) && (!t0 || ti->StopTime() > time(NULL) && ti->Compare(*t0) < 0))
837  t0 = ti;
838  }
839  }
840  return t0;
841 }
842 
843 const cTimers *cTimers::GetTimersRead(cStateKey &StateKey, int TimeoutMs)
844 {
845  return timers.Lock(StateKey, false, TimeoutMs) ? &timers : NULL;
846 }
847 
848 cTimers *cTimers::GetTimersWrite(cStateKey &StateKey, int TimeoutMs)
849 {
850  return timers.Lock(StateKey, true, TimeoutMs) ? &timers : NULL;
851 }
852 
853 void cTimers::Add(cTimer *Timer, cTimer *After)
854 {
855  if (!Timer->Remote())
856  Timer->SetId(NewTimerId());
857  cConfig<cTimer>::Add(Timer, After);
859 }
860 
861 void cTimers::Ins(cTimer *Timer, cTimer *Before)
862 {
863  cConfig<cTimer>::Ins(Timer, Before);
865 }
866 
867 void cTimers::Del(cTimer *Timer, bool DeleteObject)
868 {
870  cConfig<cTimer>::Del(Timer, DeleteObject);
871 }
872 
873 const cTimer *cTimers::UsesChannel(const cChannel *Channel) const
874 {
875  for (const cTimer *Timer = First(); Timer; Timer = Next(Timer)) {
876  if (Timer->Channel() == Channel)
877  return Timer;
878  }
879  return NULL;
880 }
881 
882 bool cTimers::SetEvents(const cSchedules *Schedules)
883 {
884  bool TimersModified = false;
885  for (cTimer *ti = First(); ti; ti = Next(ti))
886  TimersModified |= ti->SetEventFromSchedule(Schedules);
887  return TimersModified;
888 }
889 
891 {
892  if (time(NULL) - lastDeleteExpired < 30)
893  return false;
894  bool TimersModified = false;
895  cTimer *ti = First();
896  while (ti) {
897  cTimer *next = Next(ti);
898  if (!ti->Remote() && ti->Expired()) {
899  isyslog("deleting timer %s", *ti->ToDescr());
900  Del(ti);
901  TimersModified = true;
902  }
903  ti = next;
904  }
905  lastDeleteExpired = time(NULL);
906  return TimersModified;
907 }
908 
909 bool cTimers::StoreRemoteTimers(const char *ServerName, const cStringList *RemoteTimers)
910 {
911  bool Result = false;
912  if (!ServerName || !RemoteTimers || RemoteTimers->Size() == 0) {
913  // Remove remote timers from this list:
914  cTimer *Timer = First();
915  while (Timer) {
916  cTimer *t = Next(Timer);
917  if (Timer->Remote() && (!ServerName || strcmp(Timer->Remote(), ServerName) == 0)) {
918  Del(Timer);
919  Result = true;
920  }
921  Timer = t;
922  }
923  return Result;
924  }
925  // Collect all locally stored remote timers from ServerName:
926  cStringList tl;
927  for (cTimer *ti = First(); ti; ti = Next(ti)) {
928  if (ti->Remote() && strcmp(ti->Remote(), ServerName) == 0)
929  tl.Append(strdup(cString::sprintf("%d %s", ti->Id(), *ti->ToText(true))));
930  }
931  tl.SortNumerically(); // RemoteTimers is also sorted numerically!
932  // Compare the two lists and react accordingly:
933  int il = 0; // index into the local ("left") list of remote timers
934  int ir = 0; // index into the remote ("right") list of timers
935  int sl = tl.Size();
936  int sr = RemoteTimers->Size();
937  for (;;) {
938  int AddTimer = 0;
939  int DelTimer = 0;
940  if (il < sl) { // still have left entries
941  int nl = atoi(tl[il]);
942  if (ir < sr) { // still have right entries
943  // Compare timers:
944  int nr = atoi((*RemoteTimers)[ir]);
945  if (nl == nr) // same timer id
946  AddTimer = DelTimer = nl;
947  else if (nl < nr) // left entry not in right list
948  DelTimer = nl;
949  else // right entry not in left list
950  AddTimer = nr;
951  }
952  else // processed all right entries
953  DelTimer = nl;
954  }
955  else if (ir < sr) { // still have right entries
956  AddTimer = atoi((*RemoteTimers)[ir]);
957  if (!AddTimer) {
958  esyslog("ERROR: %s: error in timer settings: %s", ServerName, (*RemoteTimers)[ir]);
959  ir++;
960  continue; // let's see if we can process the rest
961  }
962  }
963  else // processed all left and right entries
964  break;
965  if (AddTimer && DelTimer) {
966  if (strcmp(tl[il], (*RemoteTimers)[ir]) != 0) {
967  // Overwrite timer:
968  char *v = (*RemoteTimers)[ir];
969  while (*v && *v != ' ')
970  v++; // skip id
971  if (cTimer *l = GetById(DelTimer, ServerName)) {
972  cTimer r;
973  if (r.Parse(v)) {
974  r.SetRemote(ServerName);
975  r.SetId(AddTimer);
976  *l = r;
977  Result = true;
978  }
979  else
980  esyslog("ERROR: %d@%s: error in timer settings: %s", DelTimer, ServerName, v);
981  }
982  }
983  else // identical timer, nothing to do
984  ;
985  il++;
986  ir++;
987  }
988  else if (AddTimer) {
989  char *v = (*RemoteTimers)[ir];
990  while (*v && *v != ' ')
991  v++; // skip id
992  cTimer *Timer = new cTimer;
993  if (Timer->Parse(v)) {
994  Timer->SetRemote(ServerName);
995  Timer->SetId(AddTimer);
996  Add(Timer);
997  Result = true;
998  }
999  else {
1000  esyslog("ERROR: %s: error in timer settings: %s", ServerName, v);
1001  delete Timer;
1002  }
1003  ir++;
1004  }
1005  else if (DelTimer) {
1006  if (cTimer *t = GetById(DelTimer, ServerName)) {
1007  Del(t);
1008  Result = true;
1009  }
1010  il++;
1011  }
1012  else {
1013  esyslog("ERROR: oops while storing remote timers!");
1014  break; // let's not get stuck here!
1015  }
1016  }
1017  return Result;
1018 }
1019 
1020 static bool RemoteTimerError(const cTimer *Timer, cString *Msg)
1021 {
1022  if (Msg)
1023  *Msg = cString::sprintf("%s %d@%s!", tr("Error while accessing remote timer"), Timer->Id(), Timer->Remote());
1024  return false; // convenience return code
1025 }
1026 
1027 bool HandleRemoteTimerModifications(cTimer *NewTimer, cTimer *OldTimer, cString *Msg)
1028 {
1029  cStringList Response;
1030  if (!NewTimer) {
1031  if (OldTimer) { // timer shall be deleted from remote machine
1032  if (OldTimer->Remote() && OldTimer->Id()) {
1033  if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("DELT %d", OldTimer->Id()), &Response) || SVDRPCode(Response[0]) != 250)
1034  return RemoteTimerError(OldTimer, Msg);
1035  }
1036  isyslog("deleted timer %s", *OldTimer->ToDescr());
1037  }
1038  }
1039  else if (!OldTimer || OldTimer->Local() || !OldTimer->Id()) {
1040  if (NewTimer->Local()) { // timer stays local, nothing to do
1041  if (OldTimer && OldTimer->Id())
1042  isyslog("modified timer %s", *NewTimer->ToDescr());
1043  else
1044  isyslog("added timer %s", *NewTimer->ToDescr());
1045  }
1046  else { // timer is new, or moved from local to remote
1047  if (!ExecSVDRPCommand(NewTimer->Remote(), cString::sprintf("NEWT %s", *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250)
1048  return RemoteTimerError(NewTimer, Msg);
1049  int RemoteId = atoi(SVDRPValue(Response[0]));
1050  if (RemoteId <= 0)
1051  return RemoteTimerError(NewTimer, Msg);
1052  NewTimer->SetId(RemoteId);
1053  if (OldTimer && OldTimer->Id()) {
1054  isyslog("moved timer %d to %s", OldTimer->Id(), *NewTimer->ToDescr());
1055  }
1056  else
1057  isyslog("added timer %s", *NewTimer->ToDescr());
1058  }
1059  }
1060  else if (NewTimer->Local()) { // timer is moved from remote to local
1061  if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("DELT %d", OldTimer->Id()), &Response) || SVDRPCode(Response[0]) != 250)
1062  return RemoteTimerError(OldTimer, Msg);
1063  NewTimer->SetId(cTimers::NewTimerId());
1064  NewTimer->ClrFlags(tfRecording); // in case it was recording on the remote machine
1065  isyslog("moved timer %d@%s to %s", OldTimer->Id(), OldTimer->Remote(), *NewTimer->ToDescr());
1066  }
1067  else if (strcmp(OldTimer->Remote(), NewTimer->Remote()) == 0) { // timer stays remote on same machine
1068  if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("MODT %d %s", OldTimer->Id(), *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250)
1069  return RemoteTimerError(NewTimer, Msg);
1070  isyslog("modified timer %s", *NewTimer->ToDescr());
1071  }
1072  else { // timer is moved from one remote machine to an other
1073  if (!ExecSVDRPCommand(NewTimer->Remote(), cString::sprintf("NEWT %s", *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250)
1074  return RemoteTimerError(NewTimer, Msg);
1075  int RemoteId = atoi(SVDRPValue(Response[0]));
1076  if (RemoteId <= 0)
1077  return RemoteTimerError(NewTimer, Msg);
1078  NewTimer->SetId(RemoteId);
1079  if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("DELT %d", OldTimer->Id()), &Response) || SVDRPCode(Response[0]) != 250)
1080  return RemoteTimerError(OldTimer, Msg);
1081  isyslog("moved timer %d@%s to %s", OldTimer->Id(), OldTimer->Remote(), *NewTimer->ToDescr());
1082  }
1083  return true;
1084 }
1085 
1086 // --- cSortedTimers ---------------------------------------------------------
1087 
1088 static int CompareTimers(const void *a, const void *b)
1089 {
1090  return (*(const cTimer **)a)->Compare(**(const cTimer **)b);
1091 }
1092 
1094 :cVector<const cTimer *>(Timers->Count())
1095 {
1096  for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer))
1097  Append(Timer);
1099 }
const cTimer * GetMatch(time_t t) const
Definition: timers.c:779
cString itoa(int n)
Definition: tools.c:424
time_t stopTime
Definition: timers.h:31
#define EPGLIMITAFTER
Definition: timers.c:538
Definition: epg.h:71
void SetWeekDays(int WeekDays)
Definition: timers.c:638
int Id(void) const
Definition: timers.h:54
const char * Aux(void) const
Definition: timers.h:68
bool PresentSeenWithin(int Seconds) const
Definition: epg.h:166
bool isempty(const char *s)
Definition: tools.c:331
static tChannelID FromString(const char *s)
Definition: channels.c:24
bool isnumber(const char *s)
Definition: tools.c:346
void OnOff(void)
Definition: timers.c:708
time_t StartTime(void) const
Definition: epg.h:109
bool DayMatches(time_t t) const
Definition: timers.c:379
void SetRecording(bool Recording)
Definition: timers.c:612
char file[NAME_MAX *2+1]
Definition: timers.h:43
const cTimer * GetTimer(const cTimer *Timer) const
Definition: timers.c:766
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2152
cString ToText(bool UseChannelID=false) const
Definition: timers.c:184
cSortedTimers(const cTimers *Timers)
Definition: timers.c:1093
int UseVps
Definition: config.h:307
int stop
Definition: timers.h:40
char * aux
Definition: timers.h:44
static int TimeToInt(int t)
Definition: timers.c:197
int id
Definition: timers.h:30
Definition: status.h:31
bool HasFlags(uint Flags) const
Definition: timers.c:696
#define EXPIRELATENCY
Definition: timers.c:513
bool Lock(cStateKey &StateKey, bool Write=false, int TimeoutMs=0) const
Tries to get a lock on this list and returns true if successful.
Definition: tools.c:2143
int DefaultPriority
Definition: config.h:302
bool Matches(time_t t=0, bool Directly=false, int Margin=0) const
Definition: timers.c:415
Definition: timers.h:18
cString ToDescr(void) const
Definition: epg.c:248
int start
Definition: timers.h:39
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1127
int PausePriority
Definition: config.h:305
virtual void Append(T Data)
Definition: tools.h:737
time_t Vps(void) const
Definition: epg.h:112
#define EPGLIMITBEFORE
Definition: timers.c:537
cString ToDescr(void) const
Definition: timers.c:192
#define esyslog(a...)
Definition: tools.h:35
void SetStop(int Stop)
Definition: timers.c:648
void Skip(void)
Definition: timers.c:701
char * strn0cpy(char *dest, const char *src, size_t n)
Definition: tools.c:131
bool Parse(const char *s)
Definition: timers.c:305
void SetInVpsMargin(bool InVpsMargin)
Definition: timers.c:626
int Priority(void) const
Definition: timers.h:64
int Index(void) const
Definition: tools.c:2072
#define EITPRESENTFOLLOWINGRATE
Definition: timers.c:413
T max(T a, T b)
Definition: tools.h:60
int PauseLifetime
Definition: config.h:305
int MarkInstantRecord
Definition: config.h:267
bool SetEventFromSchedule(const cSchedules *Schedules)
Definition: timers.c:545
void SetPending(bool Pending)
Definition: timers.c:621
int weekdays
bitmask, lowest bits: SSFTWTM (the 'M' is the LSB)
Definition: timers.h:38
Definition: timers.h:25
bool InVpsMargin(void) const
Definition: timers.h:57
bool inVpsMargin
Definition: timers.h:34
void SetStart(int Start)
Definition: timers.c:643
T min(T a, T b)
Definition: tools.h:59
void Add(cTimer *Timer, cTimer *After=NULL)
Definition: timers.c:853
virtual ~cTimer()
Definition: timers.c:133
const cEvent * event
Definition: timers.h:46
const char * FileName(void)
Definition: config.h:119
char * remote
Definition: timers.h:45
int Start(void) const
Definition: timers.h:62
static bool Load(const char *FileName)
Definition: timers.c:735
static cTimers * GetTimersWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of timers for write access.
Definition: timers.c:848
#define MALLOC(type, size)
Definition: tools.h:47
cString PrintFirstDay(void) const
Definition: timers.c:295
cTimer(bool Instant=false, bool Pause=false, const cChannel *Channel=NULL)
Definition: timers.c:26
Definition: timers.h:27
eTimerMatch
Definition: timers.h:25
bool DeleteExpired(void)
Definition: timers.c:890
static time_t SetTime(time_t t, int SecondsFromMidnight)
Definition: timers.c:396
Definition: tools.h:664
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
Definition: device.h:350
bool IsRunning(bool OrAboutToStart=false) const
Definition: epg.c:274
cTimers(void)
Definition: timers.c:729
#define trNOOP(s)
Definition: i18n.h:88
void SetRemote(const char *Remote)
Definition: timers.c:669
time_t EndTime(void) const
Definition: epg.h:110
void Ins(cTimer *Timer, cTimer *Before=NULL)
Definition: timers.c:861
void SetId(int Id)
Definition: timers.c:540
bool ExecSVDRPCommand(const char *ServerName, const char *Command, cStringList *Response)
Sends the given SVDRP Command string to the remote VDR identified by ServerName and collects all of t...
Definition: svdrp.c:2849
int Size(void) const
Definition: tools.h:717
#define DEFINSTRECTIME
Definition: config.h:45
#define LOCK_CHANNELS_READ
Definition: channels.h:267
static cTimers timers
Definition: timers.h:118
static int GetMDay(time_t t)
Definition: timers.c:366
int lifetime
Definition: timers.h:42
const char * SVDRPValue(const char *s)
Returns the actual value of the given SVDRP response string, skipping the three digit reply code and ...
Definition: svdrp.h:50
const cTimer * UsesChannel(const cChannel *Channel) const
Definition: timers.c:873
int WeekDays(void) const
Definition: timers.h:61
void Ins(cListObject *Object, cListObject *Before=NULL)
Definition: tools.c:2168
void SetLifetime(int Lifetime)
Definition: timers.c:658
#define LOCK_TIMERS_WRITE
Definition: timers.h:223
const cTimer * GetNextActiveTimer(void) const
Definition: timers.c:830
const cTimer * GetById(int Id, const char *Remote=NULL) const
Definition: timers.c:755
cSetup Setup
Definition: config.c:372
uint Flags(void) const
Definition: timers.h:58
static int Utf8CharLen(const char *s)
Definition: si.c:417
int Duration(void) const
Definition: epg.h:111
const cSchedule * GetSchedule(tChannelID ChannelID) const
Definition: epg.c:1331
static int lastTimerId
Definition: timers.h:119
void Sort(__compar_fn_t Compare)
Definition: tools.h:774
int Lifetime(void) const
Definition: timers.h:65
const cChannel * Channel(void) const
Definition: timers.h:59
void SetAux(const char *Aux)
Definition: timers.c:663
cTimer & operator=(const cTimer &Timer)
Definition: timers.c:141
time_t day
midnight of the day this timer shall hit, or of the first day it shall hit in case of a repeating tim...
Definition: timers.h:37
static time_t IncDay(time_t t, int Days)
Definition: timers.c:384
bool Modified(int &State) const
Definition: epg.h:164
static int GetWDay(time_t t)
Definition: timers.c:372
int InstantRecordTime
Definition: config.h:269
const T * First(void) const
Returns the first element in this list, or NULL if the list is empty.
Definition: tools.h:606
tChannelID ChannelID(void) const
Definition: epg.c:151
void SetPriority(int Priority)
Definition: timers.c:653
const char * Name(void) const
Definition: channels.c:108
time_t deferred
Matches(time_t, ...) will return false if the current time is before this value.
Definition: timers.h:33
uint flags
Definition: timers.h:35
int RunningStatus(void) const
Definition: epg.h:102
const char * Title(void) const
Definition: epg.h:103
static bool RemoteTimerError(const cTimer *Timer, cString *Msg)
Definition: timers.c:1020
Definition: timers.h:25
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2184
time_t StopTime(void) const
Definition: timers.c:530
int MarginStop
Definition: config.h:284
static int NewTimerId(void)
Definition: timers.c:750
void InvFlags(uint Flags)
Definition: timers.c:691
const cEvent * Event(void) const
Definition: timers.h:74
const cSchedule * Schedule(void) const
Definition: epg.h:98
time_t lastDeleteExpired
Definition: timers.h:120
tChannelID GetChannelID(void) const
Definition: channels.h:190
void SetFlags(uint Flags)
Definition: timers.c:681
Definition: epg.h:150
void SetDeferred(int Seconds)
Definition: timers.c:675
bool pending
Definition: timers.h:34
int GetMaxPriority(void) const
Returns the maximum priority of all local timers that are currently recording.
Definition: timers.c:820
Definition: timers.h:21
bool Recording(void) const
Definition: timers.h:55
const char * Remote(void) const
Definition: timers.h:69
bool Local(void) const
Definition: timers.h:70
#define tr(s)
Definition: i18n.h:85
static void MsgTimerChange(const cTimer *Timer, eTimerChange Change)
Definition: status.c:32
void IncNumTimers(void) const
Definition: epg.c:256
time_t startTime
Definition: timers.h:31
bool SetEvent(const cEvent *Event)
Definition: timers.c:592
char * skipspace(const char *s)
Definition: tools.h:209
#define SECSINDAY
Definition: tools.h:42
bool Pending(void) const
Definition: timers.h:56
#define isyslog(a...)
Definition: tools.h:36
bool StoreRemoteTimers(const char *ServerName=NULL, const cStringList *RemoteTimers=NULL)
Stores the given list of RemoteTimers, which come from the VDR ServerName, in this list.
Definition: timers.c:909
bool IsSingleEvent(void) const
Definition: timers.c:361
static bool ParseDay(const char *s, time_t &Day, int &WeekDays)
Definition: timers.c:202
bool Save(FILE *f)
Definition: timers.c:354
void ClrFlags(uint Flags)
Definition: timers.c:686
int priority
Definition: timers.h:41
time_t Day(void) const
Definition: timers.h:60
bool HandleRemoteTimerModifications(cTimer *NewTimer, cTimer *OldTimer, cString *Msg)
Performs any operations necessary to synchronize changes to a timer between peer VDR machines.
Definition: timers.c:1027
const cChannel * channel
Definition: timers.h:36
char NameInstantRecord[NAME_MAX+1]
Definition: config.h:268
bool Expired(void) const
Definition: timers.c:515
bool SetEvents(const cSchedules *Schedules)
Definition: timers.c:882
virtual int Compare(const cListObject &ListObject) const
Must return 0 if this object is equal to ListObject, a positive value if it is "greater",...
Definition: timers.c:173
static int CompareTimers(const void *a, const void *b)
Definition: timers.c:1088
char * Utf8Strn0Cpy(char *Dest, const char *Src, int n)
Copies at most n character bytes from Src to Dest, making sure that the resulting copy ends with a co...
Definition: tools.c:881
int scheduleState
Definition: timers.h:32
const cList< cEvent > * Events(void) const
Definition: epg.h:183
#define LOCK_SCHEDULES_READ
Definition: epg.h:224
int Stop(void) const
Definition: timers.h:63
int MarginStart
Definition: config.h:284
static const cTimers * GetTimersRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of timers for read access.
Definition: timers.c:843
void Del(cTimer *Timer, bool DeleteObject=true)
Definition: timers.c:867
#define DAYBUFFERSIZE
void SetFile(const char *File)
Definition: timers.c:407
char * strreplace(char *s, char c1, char c2)
Definition: tools.c:139
void SetDay(time_t Day)
Definition: timers.c:633
void SortNumerically(void)
Definition: tools.h:813
#define FULLMATCH
Definition: timers.c:475
Definition: status.h:31
Definition: tools.h:176
time_t StartTime(void) const
Definition: timers.c:523
int DefaultLifetime
Definition: config.h:302
static cString PrintDay(time_t Day, int WeekDays, bool SingleByteChars)
Definition: timers.c:261
const char * File(void) const
Definition: timers.h:66
int SVDRPCode(const char *s)
Returns the value of the three digit reply code of the given SVDRP response string.
Definition: svdrp.h:47
const T * Next(const T *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
Definition: tools.h:613