From bfb904ac14db9e67cdd4603a006cc7d18b69c12e Mon Sep 17 00:00:00 2001
From: Martin Ellis <malard@gmail.com>
Date: Sun, 12 Jun 2011 21:56:28 +0100
Subject: changed: version checking code so that debian style versioning can be
 used -
 http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version

Code ported from Josh Kelly -- http://codingcastles.blogspot.com/2009/05/comparing-version-numbers.html who lists this availble under the public domain
---
 project/VS2010Express/XBMC.vcxproj         |   2 +
 project/VS2010Express/XBMC.vcxproj.filters |   6 ++
 xbmc/addons/Addon.cpp                      |  42 ----------
 xbmc/addons/Addon.h                        |  17 +---
 xbmc/addons/AddonVersion.cpp               | 129 +++++++++++++++++++++++++++++
 xbmc/addons/AddonVersion.h                 |  78 +++++++++++++++++
 6 files changed, 216 insertions(+), 58 deletions(-)
 create mode 100644 xbmc/addons/AddonVersion.cpp
 create mode 100644 xbmc/addons/AddonVersion.h

diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj
index 53dc657b64..86f65657cd 100644
--- a/project/VS2010Express/XBMC.vcxproj
+++ b/project/VS2010Express/XBMC.vcxproj
@@ -283,6 +283,7 @@
     <ClCompile Include="..\..\lib\tinyXML\tinyxmlparser.cpp" />
     <ClCompile Include="..\..\xbmc\addons\AddonDatabase.cpp" />
     <ClCompile Include="..\..\xbmc\addons\AddonInstaller.cpp" />
+    <ClCompile Include="..\..\xbmc\addons\AddonVersion.cpp" />
     <ClCompile Include="..\..\xbmc\addons\GUIDialogAddonInfo.cpp" />
     <ClCompile Include="..\..\xbmc\addons\GUIDialogAddonSettings.cpp" />
     <ClCompile Include="..\..\xbmc\addons\GUIViewStateAddonBrowser.cpp" />
@@ -1165,6 +1166,7 @@
     <ClInclude Include="..\..\lib\tinyXML\tinyxml.h" />
     <ClInclude Include="..\..\xbmc\addons\AddonDatabase.h" />
     <ClInclude Include="..\..\xbmc\addons\AddonInstaller.h" />
+    <ClInclude Include="..\..\xbmc\addons\AddonVersion.h" />
     <ClInclude Include="..\..\xbmc\addons\DllLibCPluff.h" />
     <ClInclude Include="..\..\xbmc\addons\GUIDialogAddonInfo.h" />
     <ClInclude Include="..\..\xbmc\addons\GUIDialogAddonSettings.h" />
diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters
index 25e4e0b1ab..25650685c7 100644
--- a/project/VS2010Express/XBMC.vcxproj.filters
+++ b/project/VS2010Express/XBMC.vcxproj.filters
@@ -2460,6 +2460,9 @@
     <ClCompile Include="..\..\xbmc\settings\AppParamParser.cpp">
       <Filter>settings</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\xbmc\addons\AddonVersion.cpp">
+      <Filter>addons</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\xbmc\win32\pch.h">
@@ -4925,6 +4928,9 @@
     <ClInclude Include="..\..\xbmc\settings\AppParamParser.h">
       <Filter>settings</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\xbmc\addons\AddonVersion.h">
+      <Filter>addons</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="..\..\xbmc\win32\XBMC.ico">
diff --git a/xbmc/addons/Addon.cpp b/xbmc/addons/Addon.cpp
index 0c5225bc6b..7805faca2d 100644
--- a/xbmc/addons/Addon.cpp
+++ b/xbmc/addons/Addon.cpp
@@ -120,48 +120,6 @@ const CStdString GetIcon(const ADDON::TYPE& type)
   return "";
 }
 
-/**
- * AddonVersion
- *
- */
-
-bool AddonVersion::operator==(const AddonVersion &rhs) const
-{
-  return str.Equals(rhs.str);
-}
-
-bool AddonVersion::operator!=(const AddonVersion &rhs) const
-{
-  return !(*this == rhs);
-}
-
-bool AddonVersion::operator>(const AddonVersion &rhs) const
-{
-  return (strverscmp(str.c_str(), rhs.str.c_str()) > 0);
-}
-
-bool AddonVersion::operator>=(const AddonVersion &rhs) const
-{
-  return (*this == rhs) || (*this > rhs);
-}
-
-bool AddonVersion::operator<(const AddonVersion &rhs) const
-{
-  return (strverscmp(str.c_str(), rhs.str.c_str()) < 0);
-}
-
-bool AddonVersion::operator<=(const AddonVersion &rhs) const
-{
-  return (*this == rhs) || !(*this > rhs);
-}
-
-CStdString AddonVersion::Print() const
-{
-  CStdString out;
-  out.Format("%s %s", g_localizeStrings.Get(24051), str); // "Version <str>"
-  return CStdString(out);
-}
-
 #define EMPTY_IF(x,y) \
   { \
     CStdString fan=CAddonMgr::Get().GetExtValue(metadata->configuration, x); \
diff --git a/xbmc/addons/Addon.h b/xbmc/addons/Addon.h
index 53dff2694b..b79f963ffb 100644
--- a/xbmc/addons/Addon.h
+++ b/xbmc/addons/Addon.h
@@ -21,6 +21,7 @@
  */
 
 #include "IAddon.h"
+#include "addons/AddonVersion.h"
 #include "tinyXML/tinyxml.h"
 #include "Util.h"
 #include "URL.h"
@@ -44,22 +45,6 @@ const CStdString    GetIcon(const TYPE &type);
 const CStdString    UpdateVideoScraper(const CStdString &scraper);
 const CStdString    UpdateMusicScraper(const CStdString &scraper);
 
-class AddonVersion
-{
-public:
-  AddonVersion(const CStdString &str) : str(str) {}
-  bool operator==(const AddonVersion &rhs) const;
-  bool operator!=(const AddonVersion &rhs) const;
-  bool operator>(const AddonVersion &rhs) const;
-  bool operator>=(const AddonVersion &rhs) const;
-  bool operator<(const AddonVersion &rhs) const;
-  bool operator<=(const AddonVersion &rhs) const;
-  CStdString Print() const;
-  const char *c_str() const { return str.c_str(); };
-private:
-  CStdString str;
-};
-
 class AddonProps
 {
 public:
diff --git a/xbmc/addons/AddonVersion.cpp b/xbmc/addons/AddonVersion.cpp
new file mode 100644
index 0000000000..06980aa93d
--- /dev/null
+++ b/xbmc/addons/AddonVersion.cpp
@@ -0,0 +1,129 @@
+/*
+ *      Copyright (C) 2005-2011 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "AddonVersion.h"
+#include "guilib/LocalizeStrings.h"
+
+namespace ADDON
+{
+  AddonVersion::AddonVersion(CStdString version)
+  {
+    m_originalVersion = version;
+    const char *epoch_end = strchr(version, ':');
+    if (epoch_end != NULL) {
+      mEpoch = atoi(version);
+    } else {
+      mEpoch = 0;
+    }
+
+    const char *upstream_start;
+    if (epoch_end) {
+      upstream_start = epoch_end + 1;
+    } else {
+      upstream_start = version;
+    }
+
+    const char *upstream_end = strrchr(upstream_start, '-');
+    size_t upstream_size;
+    if (upstream_end == NULL) {
+      upstream_size = strlen(upstream_start);
+    } else {
+      upstream_size = upstream_end - upstream_start;
+    }
+
+    mUpstream = (char*) malloc(upstream_size + 1);
+    strncpy(mUpstream, upstream_start, upstream_size);
+    mUpstream[upstream_size] = '\0';
+
+    if (upstream_end == NULL) {
+      mRevision = strdup("0");
+    } else {
+      mRevision = strdup(upstream_end + 1);
+    }
+  }
+
+  /**Compare two components of a Debian-style version.  Return -1, 0, or 1
+   * if a is less than, equal to, or greater than b, respectively.
+   */
+  int AddonVersion::CompareComponent(const char *a, const char *b)
+  {
+    while (*a && *b) {
+
+      while (*a && *b && !isdigit(*a) && !isdigit(*b)) {
+        if (*a != *b) {
+          if (*a == '~') return -1;
+          if (*b == '~') return 1;
+          return *a < *b ? -1 : 1;
+        }
+        a++;
+        b++;
+      }
+      if (*a && *b && (!isdigit(*a) || !isdigit(*b))) {
+        if (*a == '~') return -1;
+        if (*b == '~') return 1;
+        return isdigit(*a) ? -1 : 1;
+      }
+
+      char *next_a, *next_b;
+      long int num_a = strtol(a, &next_a, 10);
+      long int num_b = strtol(b, &next_b, 10);
+      if (num_a != num_b) {
+        return num_a < num_b ? -1 : 1;
+      }
+      a = next_a;
+      b = next_b;
+
+    }
+    if (!*a && !*b) {
+      return 0;
+    } else if (*a) {
+      return *a == '~' ? -1 : 1;
+    } else {
+      return *b == '~' ? 1 : -1;
+    }
+  }
+
+  bool AddonVersion::operator<(const AddonVersion& other) const
+  {
+    if (Epoch() != other.Epoch()) {
+      return Epoch() < other.Epoch();
+    }
+
+    int result = CompareComponent(Upstream(), other.Upstream());
+    if (result) {
+      return -1 == result;
+    }
+
+    return -1 == CompareComponent(Revision(), other.Revision());
+  }
+
+  CStdString AddonVersion::Print() const
+  {
+    CStdString out;
+    out.Format("%s %s", g_localizeStrings.Get(24051), m_originalVersion); // "Version <str>"
+    return CStdString(out);
+  }
+}
\ No newline at end of file
diff --git a/xbmc/addons/AddonVersion.h b/xbmc/addons/AddonVersion.h
new file mode 100644
index 0000000000..b4ed415e0e
--- /dev/null
+++ b/xbmc/addons/AddonVersion.h
@@ -0,0 +1,78 @@
+/*
+ *      Copyright (C) 2005-2011 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <iostream>
+#include <boost/operators.hpp>
+
+namespace ADDON
+{
+  class AddonVersion : public boost::totally_ordered<AddonVersion> {
+  public:
+    AddonVersion() : mEpoch(0), mUpstream(NULL), mRevision(NULL) {}
+    AddonVersion(const AddonVersion& other) { *this = other; }
+    explicit AddonVersion(CStdString version);
+    ~AddonVersion();
+
+    int Epoch() const { return mEpoch; }
+    const char *Upstream() const { return mUpstream; }
+    const char *Revision() const { return mRevision; }
+
+    void Epoch(int new_epoch) { mEpoch = new_epoch; }
+    void Upstream(const char *new_upstream) { free(mUpstream); mUpstream = strdup(new_upstream); }
+    void Revision(const char *new_revision) { free(mRevision); mRevision = strdup(new_revision); }
+    void ClearRevision() { Revision("0"); }
+
+    AddonVersion& operator=(const AddonVersion& other);
+    bool operator<(const AddonVersion& other) const;
+    bool operator==(const AddonVersion& other) const;
+    CStdString Print() const;
+    const char *c_str() const { return m_originalVersion.c_str(); };
+  protected:
+    CStdString m_originalVersion;
+    int mEpoch;
+    char *mUpstream;
+    char *mRevision;
+
+    static int CompareComponent(const char *a, const char *b);
+  };
+
+  inline AddonVersion::~AddonVersion()
+  {
+    free(mUpstream);
+    free(mRevision);
+  }
+
+  inline bool AddonVersion::operator==(const AddonVersion& other) const
+  {
+    return Epoch() == other.Epoch()
+      && strcmp(Upstream(), other.Upstream()) == 0
+      && strcmp(Revision(), other.Revision()) == 0;
+  }
+
+  inline AddonVersion& AddonVersion::operator=(const AddonVersion& other)
+  {
+    mEpoch = other.Epoch();
+    mUpstream = strdup(other.Upstream());
+    mRevision = strdup(other.Revision());
+    return *this;
+  }
+}
\ No newline at end of file
-- 
cgit v1.2.3