From 67f3cb056f76df8ae8afc2db99589273f4c468be Mon Sep 17 00:00:00 2001 From: Geoffrey Keating Date: Tue, 21 Jun 2005 00:28:25 +0000 Subject: [PATCH] Index: include/ChangeLog 2005-06-20 Geoffrey Keating * libiberty.h (strverscmp): Prototype. Index: libiberty/ChangeLog 2005-06-20 Geoffrey Keating * strverscmp.c: New. * Makefile.in (CFILES): Add strverscmp.c. (CONFIGURED_OFILES): Add strverscmp.o. (strverscmp.o): New rule. (stamp-functions): Add $(srcdir) to files in source directory. * configure.ac (funcs): Add strverscmp. (AC_CHECK_FUNCS): Add strverscmp. * configure: Regenerate. * functions.texi: Regenerate. --- include/ChangeLog | 4 + include/libiberty.h | 5 ++ libiberty/ChangeLog | 12 +++ libiberty/Makefile.in | 15 +++- libiberty/configure | 78 ++++++++++++++++++- libiberty/configure.ac | 4 +- libiberty/functions.texi | 57 ++++++++++++-- libiberty/strverscmp.c | 157 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 320 insertions(+), 12 deletions(-) create mode 100644 libiberty/strverscmp.c diff --git a/include/ChangeLog b/include/ChangeLog index fab3d9ca20b..107913db6e2 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,7 @@ +2005-06-20 Geoffrey Keating + + * libiberty.h (strverscmp): Prototype. + 2005-06-17 Jakub Jelinek * elf/external.h (GRP_ENTRY_SIZE): Define. diff --git a/include/libiberty.h b/include/libiberty.h index 4b0d532db78..6fb5e19ccd5 100644 --- a/include/libiberty.h +++ b/include/libiberty.h @@ -540,6 +540,11 @@ extern int snprintf (char *, size_t, const char *, ...) ATTRIBUTE_PRINTF_3; extern int vsnprintf (char *, size_t, const char *, va_list) ATTRIBUTE_PRINTF(3,0); #endif +#if defined(HAVE_DECL_STRVERSCMP) && !HAVE_DECL_STRVERSCMP +/* Compare version strings. */ +extern int strverscmp (const char *, const char *); +#endif + #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) /* Drastically simplified alloca configurator. If we're using GCC, diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog index 9cf2b1dafe6..04ab848a675 100644 --- a/libiberty/ChangeLog +++ b/libiberty/ChangeLog @@ -1,3 +1,15 @@ +2005-06-20 Geoffrey Keating + + * strverscmp.c: New. + * Makefile.in (CFILES): Add strverscmp.c. + (CONFIGURED_OFILES): Add strverscmp.o. + (strverscmp.o): New rule. + (stamp-functions): Add $(srcdir) to files in source directory. + * configure.ac (funcs): Add strverscmp. + (AC_CHECK_FUNCS): Add strverscmp. + * configure: Regenerate. + * functions.texi: Regenerate. + 2005-05-28 Eli Zaretskii * configure.ac: Add snprintf and vsnprintf to AC_CHEK_DECLS. diff --git a/libiberty/Makefile.in b/libiberty/Makefile.in index 4624998025c..67d61972f4a 100644 --- a/libiberty/Makefile.in +++ b/libiberty/Makefile.in @@ -1,6 +1,6 @@ # # Makefile -# Copyright (C) 1990, 91-99, 2000, 2001, 2002, 2003, 2004 +# Copyright (C) 1990, 91-99, 2000, 2001, 2002, 2003, 2004, 2005 # Free Software Foundation # # This file is part of the libiberty library. @@ -151,7 +151,7 @@ CFILES = alloca.c argv.c asprintf.c atexit.c \ safe-ctype.c setenv.c sigsetmask.c snprintf.c sort.c spaces.c \ splay-tree.c stpcpy.c stpncpy.c strcasecmp.c strchr.c strdup.c \ strerror.c strncasecmp.c strncmp.c strrchr.c strsignal.c \ - strstr.c strtod.c strtol.c strtoul.c strndup.c \ + strstr.c strtod.c strtol.c strtoul.c strndup.c strverscmp.c \ ternary.c tmpnam.c \ unlink-if-ordinary.c \ vasprintf.c vfork.c vfprintf.c vprintf.c vsnprintf.c vsprintf.c \ @@ -200,7 +200,7 @@ CONFIGURED_OFILES = ./asprintf.o ./atexit.o \ ./setenv.o ./sigsetmask.o ./snprintf.o ./stpcpy.o ./stpncpy.o \ ./strcasecmp.o ./strchr.o ./strdup.o ./strncasecmp.o \ ./strncmp.o ./strndup.o ./strrchr.o ./strstr.o \ - ./strtod.o ./strtol.o ./strtoul.o \ + ./strtod.o ./strtol.o ./strtoul.o ./strverscmp.o \ ./tmpnam.o \ ./vasprintf.o ./vfork.o ./vfprintf.o ./vprintf.o ./vsnprintf.o \ ./vsprintf.o \ @@ -270,7 +270,7 @@ libiberty.html : $(srcdir)/libiberty.texi $(TEXISRC) @MAINT@$(srcdir)/functions.texi : stamp-functions @MAINT@ @true -@MAINT@stamp-functions : $(CFILES) $(TEXIFILES) $(srcdir)/gather-docs Makefile +@MAINT@stamp-functions : $(CFILES:%=$(srcdir)/%) $(TEXIFILES:%=$(srcdir)/%) $(srcdir)/gather-docs Makefile @MAINT@@HAVE_PERL@ $(PERL) $(srcdir)/gather-docs $(srcdir) $(srcdir)/functions.texi $(CFILES) $(TEXIFILES) @MAINT@ echo stamp > stamp-functions @@ -996,6 +996,13 @@ $(CONFIGURED_OFILES): stamp-picdir else true; fi $(COMPILE.c) $(srcdir)/strtoul.c $(OUTPUT_OPTION) +./strverscmp.o: $(srcdir)/strverscmp.c $(INCDIR)/safe-ctype.h \ + $(INCDIR)/libiberty.h + if [ x"$(PICFLAG)" != x ]; then \ + $(COMPILE.c) $(PICFLAG) $(srcdir)/strverscmp.c -o pic/$@; \ + else true; fi + $(COMPILE.c) $(srcdir)/strverscmp.c $(OUTPUT_OPTION) + ./ternary.o: $(srcdir)/ternary.c config.h $(INCDIR)/ansidecl.h \ $(INCDIR)/libiberty.h $(INCDIR)/ternary.h if [ x"$(PICFLAG)" != x ]; then \ diff --git a/libiberty/configure b/libiberty/configure index 48407c9a8b2..9ccdacdea3f 100755 --- a/libiberty/configure +++ b/libiberty/configure @@ -4825,6 +4825,7 @@ funcs="$funcs strstr" funcs="$funcs strtod" funcs="$funcs strtol" funcs="$funcs strtoul" +funcs="$funcs strverscmp" funcs="$funcs tmpnam" funcs="$funcs vasprintf" funcs="$funcs vfprintf" @@ -4910,13 +4911,14 @@ if test "x" = "y"; then + for ac_func in asprintf atexit basename bcmp bcopy bsearch bzero calloc clock \ getcwd getpagesize gettimeofday index insque mkstemps memchr memcmp memcpy \ memmove mempcpy memset putenv random rename rindex sigsetmask \ strcasecmp setenv stpcpy stpncpy strchr strdup strncasecmp strndup strrchr strstr \ - strtod strtol strtoul tmpnam vasprintf vfprintf vprintf \ + strtod strtol strtoul strverscmp tmpnam vasprintf vfprintf vprintf \ vsprintf waitpid getrusage on_exit psignal strerror strsignal \ sysconf times sbrk gettimeofday ffs snprintf vsnprintf \ pstat_getstatic pstat_getdynamic sysmp getsysinfo table sysctl wait3 wait4 \ @@ -7846,6 +7848,78 @@ else _ACEOF +fi + + + echo "$as_me:$LINENO: checking whether strverscmp is declared" >&5 +echo $ECHO_N "checking whether strverscmp is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl_strverscmp+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef strverscmp + char *p = (char *) strverscmp; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl_strverscmp=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl_strverscmp=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl_strverscmp" >&5 +echo "${ECHO_T}$ac_cv_have_decl_strverscmp" >&6 +if test $ac_cv_have_decl_strverscmp = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_STRVERSCMP 1 +_ACEOF + + +else + cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_STRVERSCMP 0 +_ACEOF + + fi @@ -7942,7 +8016,7 @@ if test x$gcc_no_link = xyes; then ac_cv_func_mmap_fixed_mapped=no fi fi -if test "x${ac_cv_func_mmap_fixed_mapped+set}" != xset; then +if test "x${ac_cv_func_mmap_fixed_mapped}" != xno; then for ac_header in stdlib.h unistd.h diff --git a/libiberty/configure.ac b/libiberty/configure.ac index 97abc327038..c88a9141876 100644 --- a/libiberty/configure.ac +++ b/libiberty/configure.ac @@ -254,6 +254,7 @@ funcs="$funcs strstr" funcs="$funcs strtod" funcs="$funcs strtol" funcs="$funcs strtoul" +funcs="$funcs strverscmp" funcs="$funcs tmpnam" funcs="$funcs vasprintf" funcs="$funcs vfprintf" @@ -277,7 +278,7 @@ if test "x" = "y"; then getcwd getpagesize gettimeofday index insque mkstemps memchr memcmp memcpy \ memmove mempcpy memset putenv random rename rindex sigsetmask \ strcasecmp setenv stpcpy stpncpy strchr strdup strncasecmp strndup strrchr strstr \ - strtod strtol strtoul tmpnam vasprintf vfprintf vprintf \ + strtod strtol strtoul strverscmp tmpnam vasprintf vfprintf vprintf \ vsprintf waitpid getrusage on_exit psignal strerror strsignal \ sysconf times sbrk gettimeofday ffs snprintf vsnprintf \ pstat_getstatic pstat_getdynamic sysmp getsysinfo table sysctl wait3 wait4 \ @@ -520,6 +521,7 @@ if test -z "${setobjs}"; then AC_CHECK_FUNCS($checkfuncs) AC_CHECK_DECLS([basename, ffs, asprintf, vasprintf, snprintf, vsnprintf]) AC_CHECK_DECLS([calloc, getenv, malloc, realloc, sbrk]) + AC_CHECK_DECLS([strverscmp]) libiberty_NEED_DECLARATION(canonicalize_file_name) fi diff --git a/libiberty/functions.texi b/libiberty/functions.texi index d8637797e4c..8b4a50ef45e 100644 --- a/libiberty/functions.texi +++ b/libiberty/functions.texi @@ -631,17 +631,17 @@ Sets the first @var{count} bytes of @var{s} to the constant byte @end deftypefn @c mkstemps.c:54 -@deftypefn Replacement int mkstemps (char *@var{template}, int @var{suffix_len}) +@deftypefn Replacement int mkstemps (char *@var{pattern}, int @var{suffix_len}) -Generate a unique temporary file name from @var{template}. -@var{template} has the form: +Generate a unique temporary file name from @var{pattern}. +@var{pattern} has the form: @example @var{path}/ccXXXXXX@var{suffix} @end example @var{suffix_len} tells us how long @var{suffix} is (it can be zero -length). The last six characters of @var{template} before @var{suffix} +length). The last six characters of @var{pattern} before @var{suffix} must be @samp{XXXXXX}; they are replaced with a string that makes the filename unique. Returns a file descriptor open on the file for reading and writing. @@ -891,7 +891,7 @@ control over the state of the random number generator. @end deftypefn -@c concat.c:167 +@c concat.c:173 @deftypefn Extension char* reconcat (char *@var{optr}, const char *@var{s1}, @dots{}, @code{NULL}) Same as @code{concat}, except that if @var{optr} is not @code{NULL} it @@ -1194,6 +1194,53 @@ translation is found, returns 0. @end deftypefn +@c strverscmp.c:24 +@deftypefun int strverscmp (const char *@var{s1}, const char *@var{s2}) +The @code{strverscmp} function compares the string @var{s1} against +@var{s2}, considering them as holding indices/version numbers. Return +value follows the same conventions as found in the @code{strverscmp} +function. In fact, if @var{s1} and @var{s2} contain no digits, +@code{strverscmp} behaves like @code{strcmp}. + +Basically, we compare strings normally (character by character), until +we find a digit in each string - then we enter a special comparison +mode, where each sequence of digits is taken as a whole. If we reach the +end of these two parts without noticing a difference, we return to the +standard comparison mode. There are two types of numeric parts: +"integral" and "fractional" (those begin with a '0'). The types +of the numeric parts affect the way we sort them: + +@itemize @bullet +@item +integral/integral: we compare values as you would expect. + +@item +fractional/integral: the fractional part is less than the integral one. +Again, no surprise. + +@item +fractional/fractional: the things become a bit more complex. +If the common prefix contains only leading zeroes, the longest part is less +than the other one; else the comparison behaves normally. +@end itemize + +@smallexample +strverscmp ("no digit", "no digit") + @result{} 0 // @r{same behavior as strcmp.} +strverscmp ("item#99", "item#100") + @result{} <0 // @r{same prefix, but 99 < 100.} +strverscmp ("alpha1", "alpha001") + @result{} >0 // @r{fractional part inferior to integral one.} +strverscmp ("part1_f012", "part1_f01") + @result{} >0 // @r{two fractional parts.} +strverscmp ("foo.009", "foo.0") + @result{} <0 // @r{idem, but with leading zeroes only.} +@end smallexample + +This function is especially useful when dealing with filename sorting, +because filenames frequently hold indices/version numbers. +@end deftypefun + @c tmpnam.c:3 @deftypefn Supplemental char* tmpnam (char *@var{s}) diff --git a/libiberty/strverscmp.c b/libiberty/strverscmp.c new file mode 100644 index 00000000000..2c6fe8a5910 --- /dev/null +++ b/libiberty/strverscmp.c @@ -0,0 +1,157 @@ +/* Compare strings while treating digits characters numerically. + Copyright (C) 1997, 2002, 2005 Free Software Foundation, Inc. + This file is part of the libiberty library. + Contributed by Jean-François Bignolles , 1997. + + Libiberty is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + Libiberty 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include "libiberty.h" +#include "safe-ctype.h" + +/* +@deftypefun int strverscmp (const char *@var{s1}, const char *@var{s2}) +The @code{strverscmp} function compares the string @var{s1} against +@var{s2}, considering them as holding indices/version numbers. Return +value follows the same conventions as found in the @code{strverscmp} +function. In fact, if @var{s1} and @var{s2} contain no digits, +@code{strverscmp} behaves like @code{strcmp}. + +Basically, we compare strings normally (character by character), until +we find a digit in each string - then we enter a special comparison +mode, where each sequence of digits is taken as a whole. If we reach the +end of these two parts without noticing a difference, we return to the +standard comparison mode. There are two types of numeric parts: +"integral" and "fractional" (those begin with a '0'). The types +of the numeric parts affect the way we sort them: + +@itemize @bullet +@item +integral/integral: we compare values as you would expect. + +@item +fractional/integral: the fractional part is less than the integral one. +Again, no surprise. + +@item +fractional/fractional: the things become a bit more complex. +If the common prefix contains only leading zeroes, the longest part is less +than the other one; else the comparison behaves normally. +@end itemize + +@smallexample +strverscmp ("no digit", "no digit") + @result{} 0 // @r{same behavior as strcmp.} +strverscmp ("item#99", "item#100") + @result{} <0 // @r{same prefix, but 99 < 100.} +strverscmp ("alpha1", "alpha001") + @result{} >0 // @r{fractional part inferior to integral one.} +strverscmp ("part1_f012", "part1_f01") + @result{} >0 // @r{two fractional parts.} +strverscmp ("foo.009", "foo.0") + @result{} <0 // @r{idem, but with leading zeroes only.} +@end smallexample + +This function is especially useful when dealing with filename sorting, +because filenames frequently hold indices/version numbers. +@end deftypefun + +*/ + +/* states: S_N: normal, S_I: comparing integral part, S_F: comparing + fractional parts, S_Z: idem but with leading Zeroes only */ +#define S_N 0x0 +#define S_I 0x4 +#define S_F 0x8 +#define S_Z 0xC + +/* result_type: CMP: return diff; LEN: compare using len_diff/diff */ +#define CMP 2 +#define LEN 3 + + +/* Compare S1 and S2 as strings holding indices/version numbers, + returning less than, equal to or greater than zero if S1 is less than, + equal to or greater than S2 (for more info, see the Glibc texinfo doc). */ + +int +strverscmp (const char *s1, const char *s2) +{ + const unsigned char *p1 = (const unsigned char *) s1; + const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + int state; + int diff; + + /* Symbol(s) 0 [1-9] others (padding) + Transition (10) 0 (01) d (00) x (11) - */ + static const unsigned int next_state[] = + { + /* state x d 0 - */ + /* S_N */ S_N, S_I, S_Z, S_N, + /* S_I */ S_N, S_I, S_I, S_I, + /* S_F */ S_N, S_F, S_F, S_F, + /* S_Z */ S_N, S_F, S_Z, S_Z + }; + + static const int result_type[] = + { + /* state x/x x/d x/0 x/- d/x d/d d/0 d/- + 0/x 0/d 0/0 0/- -/x -/d -/0 -/- */ + + /* S_N */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, + CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_I */ CMP, -1, -1, CMP, +1, LEN, LEN, CMP, + +1, LEN, LEN, CMP, CMP, CMP, CMP, CMP, + /* S_F */ CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, + CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, + /* S_Z */ CMP, +1, +1, CMP, -1, CMP, CMP, CMP, + -1, CMP, CMP, CMP + }; + + if (p1 == p2) + return 0; + + c1 = *p1++; + c2 = *p2++; + /* Hint: '0' is a digit too. */ + state = S_N | ((c1 == '0') + (ISDIGIT (c1) != 0)); + + while ((diff = c1 - c2) == 0 && c1 != '\0') + { + state = next_state[state]; + c1 = *p1++; + c2 = *p2++; + state |= (c1 == '0') + (ISDIGIT (c1) != 0); + } + + state = result_type[state << 2 | (((c2 == '0') + (ISDIGIT (c2) != 0)))]; + + switch (state) + { + case CMP: + return diff; + + case LEN: + while (ISDIGIT (*p1++)) + if (!ISDIGIT (*p2++)) + return 1; + + return ISDIGIT (*p2) ? -1 : diff; + + default: + return state; + } +} -- 2.30.2