From 9c3ce0048a4572e602dc9f23c36940993db4959b Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Wed, 28 Feb 2018 10:06:47 +0100 Subject: [PATCH] When remote timers are fetched from a peer VDR, we no longer blindly delete and re-add them, but rather compare them and make only the minimum necessary changes --- HISTORY | 4 +- svdrp.c | 21 ++++++++-- timers.c | 121 ++++++++++++++++++++++++++++++++++++++++--------------- timers.h | 6 ++- tools.c | 14 ++++++- tools.h | 18 ++++++++- 6 files changed, 144 insertions(+), 40 deletions(-) diff --git a/HISTORY b/HISTORY index 468e58ea..cdb632c6 100644 --- a/HISTORY +++ b/HISTORY @@ -9162,7 +9162,7 @@ Video Disk Recorder Revision History a subdirectory. - SVDRP peering can now be limited to the default SVDRP host (see MANUAL for details). -2018-02-27: Version 2.3.9 +2018-02-28: Version 2.3.9 - Updated the Italian OSD texts (thanks to Diego Pierotto). - Updated the Finnish OSD texts (thanks to Rolf Ahrenberg). @@ -9286,3 +9286,5 @@ Video Disk Recorder Revision History - Moved handling remote timers into cSVDRPClientHandler::ProcessConnections(). - Combined Start/StopSVDRPServer/ClientHandler() into Start/StopSVDRPHandler(). - Updated the Polish OSD texts (thanks to Tomasz Maciej Nowak). +- When remote timers are fetched from a peer VDR, we no longer blindly delete and re-add + them, but rather compare them and make only the minimum necessary changes. diff --git a/svdrp.c b/svdrp.c index 6e3a58a2..93ead5eb 100644 --- a/svdrp.c +++ b/svdrp.c @@ -10,7 +10,7 @@ * and interact with the Video Disk Recorder - or write a full featured * graphical interface that sits on top of an SVDRP connection. * - * $Id: svdrp.c 4.30 2018/02/26 15:42:15 kls Exp $ + * $Id: svdrp.c 4.31 2018/02/28 10:04:00 kls Exp $ */ #include "svdrp.h" @@ -483,8 +483,23 @@ bool cSVDRPClient::HasFetchFlag(eSvdrpFetchFlags Flag) bool cSVDRPClient::GetRemoteTimers(cStringList &Response) { - if (HasFetchFlag(sffTimers)) - return Execute("LSTT ID", &Response); + if (HasFetchFlag(sffTimers)) { + if (Execute("LSTT ID", &Response)) { + for (int i = 0; i < Response.Size(); i++) { + char *s = Response[i]; + int Code = SVDRPCode(s); + if (Code == 250) + strshift(s, 4); + else { + if (Code != 550) + esyslog("ERROR: %s: %s", ServerName(), s); + return false; + } + } + Response.SortNumerically(); + return true; + } + } return false; } diff --git a/timers.c b/timers.c index 9e4c4856..bc4dbbfa 100644 --- a/timers.c +++ b/timers.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.c 4.15 2018/02/25 13:05:03 kls Exp $ + * $Id: timers.c 4.16 2018/02/28 10:05:52 kls Exp $ */ #include "timers.h" @@ -900,45 +900,102 @@ bool cTimers::DeleteExpired(void) bool cTimers::StoreRemoteTimers(const char *ServerName, const cStringList *RemoteTimers) { - //TODO handle only new/deleted/modified timers? bool Result = false; - // Delete old remote timers: - cTimer *Timer = First(); - while (Timer) { - cTimer *t = Next(Timer); - if (Timer->Remote() && (!ServerName || strcmp(Timer->Remote(), ServerName) == 0)) { - Del(Timer); - Result = true; + 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; } - Timer = t; - } - // Add new remote timers: - if (ServerName && RemoteTimers) { - for (int i = 0; i < RemoteTimers->Size(); i++) { - const char *s = (*RemoteTimers)[i]; - int Code = SVDRPCode(s); - if (Code == 250) { - if (const char *v = SVDRPValue(s)) { - int Id = atoi(v); - while (*v && *v != ' ') - v++; // skip id - cTimer *Timer = new cTimer; - if (Timer->Parse(v)) { - Timer->SetRemote(ServerName); - Timer->SetId(Id); - Add(Timer); + 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; + } + else if (ir < sr) // still have right entries + AddTimer = atoi((*RemoteTimers)[ir]); + 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; Result = true; } - else { + else esyslog("ERROR: %s: error in timer settings: %s", ServerName, v); - delete Timer; - } } } - else if (Code != 550) - esyslog("ERROR: %s: %s", ServerName, s); + else // identical timer, nothing to do + ; + il++; + ir++; } - } + 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++; + } + } return Result; } diff --git a/timers.h b/timers.h index 3146fce5..93d5a447 100644 --- a/timers.h +++ b/timers.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.h 4.10 2018/02/25 12:54:55 kls Exp $ + * $Id: timers.h 4.11 2018/02/27 13:57:26 kls Exp $ */ #ifndef __TIMERS_H @@ -190,7 +190,9 @@ public: ///< this list. If no ServerName is given, all remote timers from all peer machines ///< will be removed from this list. If no RemoteTimers are given, only the remote ///< timers from ServerName will be removed from this list. - ///< Returns true if any remote timers have been added or deleted + ///< The given list of RemoteTimers must be sorted numerically (by a call to its + ///< SortNumerically() function). + ///< Returns true if any remote timers have been added, deleted or modified. }; bool HandleRemoteTimerModifications(cTimer *NewTimer, cTimer *OldTimer = NULL, cString *Msg = NULL); diff --git a/tools.c b/tools.c index ac2f9eb2..28e03cdc 100644 --- a/tools.c +++ b/tools.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.c 4.8 2017/06/25 11:45:39 kls Exp $ + * $Id: tools.c 4.9 2018/02/27 10:09:21 kls Exp $ */ #include "tools.h" @@ -296,6 +296,18 @@ cString strgetval(const char *s, const char *name, char d) return NULL; } +char *strshift(char *s, int n) +{ + if (s && n > 0) { + int l = strlen(s); + if (n < l) + memmove(s, s + n, l - n + 1); // we also copy the terminating 0! + else + *s = 0; + } + return s; +} + bool startswith(const char *s, const char *p) { while (*p) { diff --git a/tools.h b/tools.h index aaba602f..ffbd6825 100644 --- a/tools.h +++ b/tools.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.h 4.13 2017/06/25 11:45:38 kls Exp $ + * $Id: tools.h 4.14 2018/02/28 10:06:47 kls Exp $ */ #ifndef __TOOLS_H @@ -226,6 +226,12 @@ cString strgetval(const char *s, const char *name, char d = '='); ///< If an other delimiter shall be used (like, e.g., ':'), it can be given ///< as the third parameter. ///< If name occurs more than once in s, only the first occurrence is taken. +char *strshift(char *s, int n); + ///< Shifts the given string to the left by the given number of bytes, thus + ///< removing the first n bytes from s. + ///< If n is greater than the length of s, the resulting string will be empty. + ///< If n is <= 0 s will be unchanged. + ///< Returns s. bool startswith(const char *s, const char *p); bool endswith(const char *s, const char *p); bool isempty(const char *s); @@ -781,6 +787,12 @@ inline int CompareStringsIgnoreCase(const void *a, const void *b) return strcasecmp(*(const char **)a, *(const char **)b); } +inline int CompareStringsNumerically(const void *a, const void *b) +{ + int d = atoi(*(const char **)a) - atoi(*(const char **)b); + return d ? d : CompareStrings(a, b); +} + class cStringList : public cVector { public: cStringList(int Allocated = 10): cVector(Allocated) {} @@ -793,6 +805,10 @@ public: else cVector::Sort(CompareStrings); } + void SortNumerically(void) + { + cVector::Sort(CompareStringsNumerically); + } virtual void Clear(void); };