summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2016-02-21 13:25:24 -0800
committerPaul Eggert <eggert@cs.ucla.edu>2016-02-21 13:27:40 -0800
commit1f7feecaee0ed3fb79758fe60020aefb30d9ff01 (patch)
treeb0476096aabea32658be041242caa74e7a5232cd /lib
parent3e67708d7239cde24b0988d4d1288bc75585cfea (diff)
downloademacs-1f7feecaee0ed3fb79758fe60020aefb30d9ff01.tar.gz
Use Gnulib filevercmp for version comparison
* admin/merge-gnulib (GNULIB_MODULES): Add filevercmp. * doc/lispref/strings.texi (Text Comparison): * etc/NEWS, src/fns.c: * test/src/fns-tests.el (fns-tests-string-version-lessp): Rename newly-introduced function to string-version-lessp, by analogy with strverscmp. * lib/filevercmp.c, lib/filevercmp.h: New files, copied from gnulib. * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. * src/fns.c: Include <filevercmp.h>. (gather_number_from_string): Remove. (Fstring_version_lessp): Reimplement via filevercmp.
Diffstat (limited to 'lib')
-rw-r--r--lib/filevercmp.c181
-rw-r--r--lib/filevercmp.h42
-rw-r--r--lib/gnulib.mk10
3 files changed, 232 insertions, 1 deletions
diff --git a/lib/filevercmp.c b/lib/filevercmp.c
new file mode 100644
index 00000000000..a75c9468e31
--- /dev/null
+++ b/lib/filevercmp.c
@@ -0,0 +1,181 @@
+/*
+ Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
+ Copyright (C) 2001 Anthony Towns <aj@azure.humbug.org.au>
+ Copyright (C) 2008-2016 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 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 <config.h>
+#include "filevercmp.h"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <c-ctype.h>
+#include <limits.h>
+
+/* Match a file suffix defined by this regular expression:
+ /(\.[A-Za-z~][A-Za-z0-9~]*)*$/
+ Scan the string *STR and return a pointer to the matching suffix, or
+ NULL if not found. Upon return, *STR points to terminating NUL. */
+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 (!c_isalpha (**str) && '~' != **str)
+ match = NULL;
+ }
+ else if ('.' == **str)
+ {
+ read_alpha = true;
+ if (!match)
+ match = *str;
+ }
+ else if (!c_isalnum (**str) && '~' != **str)
+ match = NULL;
+ (*str)++;
+ }
+ return match;
+}
+
+/* verrevcmp helper function */
+static int
+order (unsigned char c)
+{
+ if (c_isdigit (c))
+ return 0;
+ else if (c_isalpha (c))
+ return c;
+ else if (c == '~')
+ return -1;
+ else
+ return (int) c + UCHAR_MAX + 1;
+}
+
+/* slightly modified verrevcmp function from dpkg
+ S1, S2 - compared string
+ S1_LEN, S2_LEN - length of strings to be scanned
+
+ This implements the algorithm for comparison of version strings
+ specified by Debian and now widely adopted. The detailed
+ specification can be found in the Debian Policy Manual in the
+ section on the 'Version' control field. This version of the code
+ implements that from s5.6.12 of Debian Policy v3.8.0.1
+ http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version */
+static int _GL_ATTRIBUTE_PURE
+verrevcmp (const char *s1, size_t s1_len, const char *s2, size_t s2_len)
+{
+ size_t s1_pos = 0;
+ size_t s2_pos = 0;
+ while (s1_pos < s1_len || s2_pos < s2_len)
+ {
+ int first_diff = 0;
+ while ((s1_pos < s1_len && !c_isdigit (s1[s1_pos]))
+ || (s2_pos < s2_len && !c_isdigit (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 (c_isdigit (s1[s1_pos]) && c_isdigit (s2[s2_pos]))
+ {
+ if (!first_diff)
+ first_diff = s1[s1_pos] - s2[s2_pos];
+ s1_pos++;
+ s2_pos++;
+ }
+ if (c_isdigit (s1[s1_pos]))
+ return 1;
+ if (c_isdigit (s2[s2_pos]))
+ return -1;
+ if (first_diff)
+ return first_diff;
+ }
+ return 0;
+}
+
+/* Compare version strings S1 and S2.
+ See filevercmp.h for function description. */
+int
+filevercmp (const char *s1, const char *s2)
+{
+ const char *s1_pos;
+ const char *s2_pos;
+ const char *s1_suffix, *s2_suffix;
+ size_t s1_len, s2_len;
+ int result;
+
+ /* easy comparison to see if strings are identical */
+ int simple_cmp = strcmp (s1, s2);
+ if (simple_cmp == 0)
+ return 0;
+
+ /* special handle for "", "." and ".." */
+ if (!*s1)
+ return -1;
+ if (!*s2)
+ return 1;
+ if (0 == strcmp (".", s1))
+ return -1;
+ if (0 == strcmp (".", s2))
+ return 1;
+ if (0 == strcmp ("..", s1))
+ return -1;
+ if (0 == strcmp ("..", s2))
+ return 1;
+
+ /* special handle for other hidden files */
+ if (*s1 == '.' && *s2 != '.')
+ return -1;
+ if (*s1 != '.' && *s2 == '.')
+ return 1;
+ if (*s1 == '.' && *s2 == '.')
+ {
+ s1++;
+ s2++;
+ }
+
+ /* "cut" file suffixes */
+ s1_pos = s1;
+ s2_pos = s2;
+ s1_suffix = match_suffix (&s1_pos);
+ s2_suffix = match_suffix (&s2_pos);
+ s1_len = (s1_suffix ? s1_suffix : s1_pos) - s1;
+ 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;
+ }
+
+ result = verrevcmp (s1, s1_len, s2, s2_len);
+ return result == 0 ? simple_cmp : result;
+}
diff --git a/lib/filevercmp.h b/lib/filevercmp.h
new file mode 100644
index 00000000000..220b71b5790
--- /dev/null
+++ b/lib/filevercmp.h
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
+ Copyright (C) 2001 Anthony Towns <aj@azure.humbug.org.au>
+ Copyright (C) 2008-2016 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 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
+
+/* Compare version strings:
+
+ This function compares strings S1 and S2:
+ 1) By PREFIX in the same way as strcmp.
+ 2) Then by VERSION (most similarly to version compare of Debian's dpkg).
+ Leading zeros in version numbers are ignored.
+ 3) If both (PREFIX and VERSION) are equal, strcmp function is used for
+ comparison. So this function can return 0 if (and only if) strings S1
+ and S2 are identical.
+
+ It returns number >0 for S1 > S2, 0 for S1 == S2 and number <0 for S1 < S2.
+
+ This function compares strings, in a way that if VER1 and VER2 are version
+ numbers and PREFIX and SUFFIX (SUFFIX defined as (\.[A-Za-z~][A-Za-z0-9~]*)*)
+ are strings then VER1 < VER2 implies filevercmp (PREFIX VER1 SUFFIX,
+ PREFIX VER2 SUFFIX) < 0.
+
+ This function is intended to be a replacement for strverscmp. */
+int filevercmp (const char *s1, const char *s2) _GL_ATTRIBUTE_PURE;
+
+#endif /* FILEVERCMP_H */
diff --git a/lib/gnulib.mk b/lib/gnulib.mk
index b1edd86f92c..cc8429658e1 100644
--- a/lib/gnulib.mk
+++ b/lib/gnulib.mk
@@ -21,7 +21,7 @@
# the same distribution terms as the rest of that program.
#
# Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=close --avoid=dup --avoid=fchdir --avoid=flexmember --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=setenv --avoid=sigprocmask --avoid=stdarg --avoid=stdbool --avoid=threadlib --avoid=unsetenv --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt binary-io byteswap c-ctype c-strcase careadlinkat close-stream count-one-bits count-trailing-zeros crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog ignore-value intprops largefile lstat manywarnings memrchr mkostemp mktime pipe2 pselect pthread_sigmask putenv qcopy-acl readlink readlinkat sig2str socklen stat-time std-gnu11 stdalign stddef stdio stpcpy strftime strtoimax strtoumax symlink sys_stat sys_time time time_r time_rz timegm timer-time timespec-add timespec-sub unsetenv update-copyright utimens vla warnings
+# Reproduce by: gnulib-tool --import --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=close --avoid=dup --avoid=fchdir --avoid=flexmember --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=setenv --avoid=sigprocmask --avoid=stdarg --avoid=stdbool --avoid=threadlib --avoid=unsetenv --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt binary-io byteswap c-ctype c-strcase careadlinkat close-stream count-one-bits count-trailing-zeros crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl fcntl-h fdatasync fdopendir filemode filevercmp fstatat fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog ignore-value intprops largefile lstat manywarnings memrchr mkostemp mktime pipe2 pselect pthread_sigmask putenv qcopy-acl readlink readlinkat sig2str socklen stat-time std-gnu11 stdalign stddef stdio stpcpy strftime strtoimax strtoumax symlink sys_stat sys_time time time_r time_rz timegm timer-time timespec-add timespec-sub unsetenv update-copyright utimens vla warnings
MOSTLYCLEANFILES += core *.stackdump
@@ -441,6 +441,14 @@ EXTRA_DIST += filemode.h
## end gnulib module filemode
+## begin gnulib module filevercmp
+
+libgnu_a_SOURCES += filevercmp.c
+
+EXTRA_DIST += filevercmp.h
+
+## end gnulib module filevercmp
+
## begin gnulib module fpending