Hello,

as it was mentioned in the thread at 
http://lists.gnu.org/archive/html/bug-gnulib/2008-09/msg00198.html I propose 
new gnulib module filevercmp - in the attachment.

There are still opened some licensing issues as we are now waiting for reply 
from original author. I've fixed the ambiguity problem, so now it returns 
zero if (and only if) both strings are identical.

This is my first attempt to contribute to gnulib, so be patient, please :-) I 
read the 'Portability guidelines' section in README and was a bit confused. 
Could somebody provide me with a complete set of gcc parameters to catch all 
unportable code in this module? Thanks in advance.


Kamil
From 842bccfc33b46570b73956e39be8c0d9c064453b Mon Sep 17 00:00:00 2001
From: Kamil Dudka <[EMAIL PROTECTED]>
Date: Wed, 24 Sep 2008 16:01:57 +0200
Subject: [PATCH] add new module filevercmp

* lib/filevercmp.h: New function FILEVERCMP comparing version strings
(and file names with version).
* lib/filevercmp.c: Implementation of FILEVERCMP function.
* m4/filevercmp.m4: Link filevercmp module to library.
* modules/filevercmp: Module metadata.
* tests/test-filevercmp.c: Unit test for new module.
* modules/filevercmp-tests: Unit test metadata.
* MODULES.html.sh: Add FILEVERCMP module.
* NEWS: Mention the change.
---
 MODULES.html.sh          |    1 +
 NEWS                     |    8 +++
 lib/filevercmp.c         |  157 ++++++++++++++++++++++++++++++++++++++++++++++
 lib/filevercmp.h         |   29 +++++++++
 m4/filevercmp.m4         |    7 ++
 modules/filevercmp       |   24 +++++++
 modules/filevercmp-tests |   11 +++
 tests/test-filevercmp.c  |  101 +++++++++++++++++++++++++++++
 8 files changed, 338 insertions(+), 0 deletions(-)
 create mode 100644 lib/filevercmp.c
 create mode 100644 lib/filevercmp.h
 create mode 100644 m4/filevercmp.m4
 create mode 100644 modules/filevercmp
 create mode 100644 modules/filevercmp-tests
 create mode 100644 tests/test-filevercmp.c

diff --git a/MODULES.html.sh b/MODULES.html.sh
index afaf8ba..3ab5a90 100755
--- a/MODULES.html.sh
+++ b/MODULES.html.sh
@@ -1856,6 +1856,7 @@ func_all_modules ()
   func_module readtokens
   func_module readtokens0
   func_module strverscmp
+  func_module filevercmp
   func_end_table
 
   element="Support for systems lacking ISO C 99"
diff --git a/NEWS b/NEWS
index 16917cb..2b400ed 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,14 @@ User visible incompatible changes
 
 Date        Modules         Changes
 
+2008-09-24  filevercmp      New module containing FILEVERCMP function comparing
+                            version strings (and file names with version). This
+                            function is supposed to be replacement for
+                            STRVERSCMP function. It has same parameters and
+                            return value as standard STRCMP function. It is
+                            based on VERREVCMP function from dpkg and improved
+                            to deal better with file suffixes.
+
 2008-09-23  sys_socket      Under Windows (MinGW), the module now adds
                             wrappers around Winsock functions, so that
                             socket descriptors are now compatible with
diff --git a/lib/filevercmp.c b/lib/filevercmp.c
new file mode 100644
index 0000000..b5528b5
--- /dev/null
+++ b/lib/filevercmp.c
@@ -0,0 +1,157 @@
+/* 
+TODO: copyright?
+
+   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 3 of the License, 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 this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <ctype.h>
+
+#include "filevercmp.h"
+
+#define xisalnum(c) isalnum ((unsigned char) c)
+#define xisdigit(c) isdigit ((unsigned char) c)
+#define xisalpha(c) isalpha ((unsigned char) c)
+
+static int verrevcmp (const char *s1, size_t s1_len, const char *s2, size_t
+    s2_len);
+static const char * match_suffix (const char **str);
+
+/* function comparing version strings (and file names with version)
+
+   This function is supposed to be replacement for STRVERSCMP function. Same
+   parameters and return value as standard STRCMP function.
+
+   It is based on verrevcmp function from dpkg and improved to deal better
+   with file suffixes. */
+int
+filevercmp (const char *s1, const char *s2)
+{
+  /* easy comparison to see if versions are identical */
+  if (!strcmp (s1, s2))
+    return 0;
+
+  /* "cut" file suffixes */
+  const char *s1_pos = s1;
+  const char *s2_pos = s2;
+  const char *s1_suffix = match_suffix (&s1_pos);
+  const char *s2_suffix = match_suffix (&s2_pos);
+  size_t s1_len = (s1_suffix ? s1_suffix : s1_pos) - s1;
+  size_t s2_len = (s2_suffix ? s2_suffix : s2_pos) - s2;
+
+  /* restore file suffixes if strings are identical after "cut" */
+  if ((s1_suffix || s2_suffix) && (s1_len == s2_len) && 0 ==
+      strncmp (s1, s2, s1_len))
+  {
+    s1_len = s1_pos - s1;
+    s2_len = s2_pos - s2;
+  }
+  
+  int rc = verrevcmp (s1, s1_len, s2, s2_len);
+  return (rc == 0)
+    /* return 0 if (and only if) strings S1 and S2 are identical */
+    ? strcmp(s1, s2) : rc;
+}
+
+/*
+   match file suffix defined as RE (\.[A-Za-z][A-Za-z0-9]*)*$
+  
+   Scan string pointed by *str and return pointer to suffix begin or NULL if
+   not found. Pointer *str points to ending zero of scanned string after
+   return. */
+static const char *
+match_suffix (const char **str)
+{
+  const char *match = NULL;
+  bool read_alpha = false;
+  while (**str)
+    {
+      if (read_alpha)
+        {
+          read_alpha = false;
+          if (!xisalpha (**str))
+            match = NULL;
+        }
+      else if ('.'==**str)
+        {
+          read_alpha = true;
+          if (!match)
+            match = *str;
+        }
+      else if (!xisalnum (**str))
+        match = NULL;
+      (*str)++;
+    }
+  return match;
+}
+
+/* verrevcmp helper function */
+inline int
+order (unsigned char c)
+{
+  if (c == '~')
+    return -1;
+  else if (xisdigit (c))
+    return 0;
+  else if (xisalpha (c))
+    return c;
+  else
+    return c + 256;
+}
+
+/* slightly modified ververcmp function from dpkg
+   S1, S2 - compared string
+   S1_LEN, S2_LEN - length of strings to be scanned */
+static int
+verrevcmp (const char *s1, size_t s1_len, const char *s2, size_t s2_len)
+{
+  int s1_pos = 0;
+  int s2_pos = 0;
+  while (s1_pos < s1_len || s2_pos < s2_len)
+    {
+      int first_diff = 0;
+      while ((s1_pos < s1_len && !xisdigit (s1[s1_pos])) || (s2_pos < s2_len
+	    && !xisdigit (s2[s2_pos])))
+	{
+	  int s1_c = (s1_pos == s1_len)
+	    ? 0 : order (s1[s1_pos]);
+	  int s2_c = (s2_pos == s2_len)
+	    ? 0 : order (s2[s2_pos]);
+	  if (s1_c != s2_c)
+	    return s1_c - s2_c;
+	  s1_pos++;
+	  s2_pos++;
+	}
+      while (s1[s1_pos] == '0')
+	s1_pos++;
+      while (s2[s2_pos] == '0')
+	s2_pos++;
+      while (xisdigit (s1[s1_pos]) && xisdigit (s2[s2_pos]))
+	{
+	  if (!first_diff)
+	    first_diff = s1[s1_pos] - s2[s2_pos];
+	  s1_pos++;
+	  s2_pos++;
+	}
+      if (xisdigit (s1[s1_pos]))
+	return 1;
+      if (xisdigit (s2[s2_pos]))
+	return -1;
+      if (first_diff)
+	return first_diff;
+    }
+  return 0;
+}
+
diff --git a/lib/filevercmp.h b/lib/filevercmp.h
new file mode 100644
index 0000000..5076b4f
--- /dev/null
+++ b/lib/filevercmp.h
@@ -0,0 +1,29 @@
+/* 
+TODO: copyright?
+
+   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 3 of the License, 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 this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef FILEVERCMP_H
+#define FILEVERCMP_H
+
+/* function comparing version strings (and file names with version)
+
+   This function is supposed to be replacement for STRVERSCMP function. Same
+   parameters and return value as standard STRCMP function.
+
+   It is based on verrevcmp function from dpkg and improved to deal better
+   with file suffixes. */
+int filevercmp (const char *s1, const char *s2);
+
+#endif // FILEVERCMP_H
diff --git a/m4/filevercmp.m4 b/m4/filevercmp.m4
new file mode 100644
index 0000000..5c78ea0
--- /dev/null
+++ b/m4/filevercmp.m4
@@ -0,0 +1,7 @@
+# filevercmp.m4
+# TODO: license?
+
+AC_DEFUN([gl_FILEVERCMP],
+[
+  AC_LIBOBJ([filevercmp])
+])
diff --git a/modules/filevercmp b/modules/filevercmp
new file mode 100644
index 0000000..bdbd6df
--- /dev/null
+++ b/modules/filevercmp
@@ -0,0 +1,24 @@
+Description:
+function comparing version strings (and file names with version)
+
+Files:
+lib/filevercmp.h
+lib/filevercmp.c
+m4/filevercmp.m4
+
+Depends-on:
+string
+
+configure.ac:
+gl_FILEVERCMP
+
+Makefile.am:
+
+Include:
+"filevercmp.h"
+
+License:
+GPL
+
+Maintainer:
+all
diff --git a/modules/filevercmp-tests b/modules/filevercmp-tests
new file mode 100644
index 0000000..02554fe
--- /dev/null
+++ b/modules/filevercmp-tests
@@ -0,0 +1,11 @@
+Files:
+tests/test-filevercmp.c
+
+Depends-on:
+filevercmp
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-filevercmp
+check_PROGRAMS += test-filevercmp
diff --git a/tests/test-filevercmp.c b/tests/test-filevercmp.c
new file mode 100644
index 0000000..696d538
--- /dev/null
+++ b/tests/test-filevercmp.c
@@ -0,0 +1,101 @@
+/* Test of filevercmp() function.
+   Copyright (C) 2008 Free Software Foundation, Inc.
+
+   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 3, 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 this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#include <config.h>
+
+#include "filevercmp.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define ASSERT(expr) \
+  do									     \
+    {									     \
+      if (!(expr))							     \
+        {								     \
+          fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
+          fflush (stderr);						     \
+          abort ();							     \
+        }								     \
+    }									     \
+  while (0)
+
+/* set of well sorted examples */
+static const char *examples[] =
+{
+  "gcc-c++-10.fc9.tar.gz",
+  "gcc-c++-10.8.12-0.7rc2.fc9.tar.bz2",
+  "glibc-2-0.1.beta1.fc10.rpm",
+  "glibc-common-5-0.2.beta2.fc9.ebuild",
+  "glibc-common-5-0.2b.deb",
+  "glibc-common-11b.ebuild",
+  "glibc-common-11-0.6rc2.ebuild",
+  "libstdc++-0.5.8.11-0.7rc2.fc10.tar.gz",
+  "libstdc++-4a.fc8.tar.gz",
+  "libstdc++-4.10.4.20040204svn.rpm",
+  "libstdc++-devel-3.fc8.ebuild",
+  "libstdc++-devel-3a.fc9.tar.gz",
+  "libstdc++-devel-8.fc8.deb",
+  "libstdc++-devel-8.6.2-0.4b.fc8",
+  "nss_ldap-1-0.2b.fc9.tar.bz2",
+  "nss_ldap-1-0.6rc2.fc8.tar.gz",
+  "nss_ldap-1.0-0.1a.tar.gz",
+  "nss_ldap-10beta1.fc8.tar.gz",
+  "nss_ldap-10.11.8.6.20040204cvs.fc10.ebuild",
+  0
+};
+
+int
+main (int argc, char **argv)
+{
+  /* Following tests taken from test-strverscmp.c */
+  ASSERT (filevercmp ("", "") == 0);
+  ASSERT (filevercmp ("a", "a") == 0);
+  ASSERT (filevercmp ("a", "b") < 0);
+  ASSERT (filevercmp ("b", "a") > 0);
+  /* these results differ from strverscmp
+  ASSERT (filevercmp ("000", "00") < 0);
+  ASSERT (filevercmp ("00", "000") > 0); */
+  ASSERT (filevercmp ("a0", "a") > 0);
+  ASSERT (filevercmp ("00", "01") < 0);
+  ASSERT (filevercmp ("01", "010") < 0);
+  /* these results differ from strverscmp
+  ASSERT (filevercmp ("010", "09") < 0);
+  ASSERT (filevercmp ("09", "0") < 0); */
+  ASSERT (filevercmp ("9", "10") < 0);
+  ASSERT (filevercmp ("0a", "0") > 0);
+
+  /* compare each version string with each other - O(n^2) */
+  const char **i;
+  for (i = examples; *i; i++)
+    {
+      const char **j;
+      for (j = examples; *j; j++)
+	{
+	  int result = filevercmp (*i, *j);
+	  if (result < 0)
+	    ASSERT (i < j);
+	  else if (0 < result)
+	    ASSERT (j < i);
+	  else
+	    ASSERT (i == j);
+	}
+    }
+
+  return 0;
+}
+
-- 
1.5.4.1

Reply via email to