Home | History | Annotate | Line # | Download | only in libiberty
      1      1.1     skrll /* Compare strings while treating digits characters numerically.
      2  1.1.1.7  christos    Copyright (C) 1997-2026 Free Software Foundation, Inc.
      3      1.1     skrll    This file is part of the libiberty library.
      4  1.1.1.4  christos    Contributed by Jean-Franois Bignolles <bignolle (at) ecoledoc.ibp.fr>, 1997.
      5      1.1     skrll 
      6      1.1     skrll    Libiberty is free software; you can redistribute it and/or
      7      1.1     skrll    modify it under the terms of the GNU Lesser General Public
      8      1.1     skrll    License as published by the Free Software Foundation; either
      9      1.1     skrll    version 2.1 of the License, or (at your option) any later version.
     10      1.1     skrll 
     11      1.1     skrll    Libiberty is distributed in the hope that it will be useful,
     12      1.1     skrll    but WITHOUT ANY WARRANTY; without even the implied warranty of
     13      1.1     skrll    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      1.1     skrll    Lesser General Public License for more details.
     15      1.1     skrll 
     16      1.1     skrll    You should have received a copy of the GNU Lesser General Public
     17      1.1     skrll    License along with the GNU C Library; if not, write to the Free
     18      1.1     skrll    Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
     19      1.1     skrll    02110-1301 USA.  */
     20      1.1     skrll 
     21      1.1     skrll #include "libiberty.h"
     22      1.1     skrll #include "safe-ctype.h"
     23      1.1     skrll 
     24      1.1     skrll /*
     25      1.1     skrll @deftypefun int strverscmp (const char *@var{s1}, const char *@var{s2})
     26      1.1     skrll The @code{strverscmp} function compares the string @var{s1} against
     27      1.1     skrll @var{s2}, considering them as holding indices/version numbers.  Return
     28      1.1     skrll value follows the same conventions as found in the @code{strverscmp}
     29      1.1     skrll function.  In fact, if @var{s1} and @var{s2} contain no digits,
     30      1.1     skrll @code{strverscmp} behaves like @code{strcmp}.
     31      1.1     skrll 
     32      1.1     skrll Basically, we compare strings normally (character by character), until
     33      1.1     skrll we find a digit in each string - then we enter a special comparison
     34      1.1     skrll mode, where each sequence of digits is taken as a whole.  If we reach the
     35      1.1     skrll end of these two parts without noticing a difference, we return to the
     36      1.1     skrll standard comparison mode.  There are two types of numeric parts:
     37      1.1     skrll "integral" and "fractional" (those  begin with a '0'). The types
     38      1.1     skrll of the numeric parts affect the way we sort them:
     39      1.1     skrll 
     40      1.1     skrll @itemize @bullet
     41      1.1     skrll @item
     42      1.1     skrll integral/integral: we compare values as you would expect.
     43      1.1     skrll 
     44      1.1     skrll @item
     45      1.1     skrll fractional/integral: the fractional part is less than the integral one.
     46      1.1     skrll Again, no surprise.
     47      1.1     skrll 
     48      1.1     skrll @item
     49      1.1     skrll fractional/fractional: the things become a bit more complex.
     50      1.1     skrll If the common prefix contains only leading zeroes, the longest part is less
     51      1.1     skrll than the other one; else the comparison behaves normally.
     52      1.1     skrll @end itemize
     53      1.1     skrll 
     54      1.1     skrll @smallexample
     55      1.1     skrll strverscmp ("no digit", "no digit")
     56      1.1     skrll     @result{} 0    // @r{same behavior as strcmp.}
     57      1.1     skrll strverscmp ("item#99", "item#100")
     58      1.1     skrll     @result{} <0   // @r{same prefix, but 99 < 100.}
     59      1.1     skrll strverscmp ("alpha1", "alpha001")
     60      1.1     skrll     @result{} >0   // @r{fractional part inferior to integral one.}
     61      1.1     skrll strverscmp ("part1_f012", "part1_f01")
     62      1.1     skrll     @result{} >0   // @r{two fractional parts.}
     63      1.1     skrll strverscmp ("foo.009", "foo.0")
     64      1.1     skrll     @result{} <0   // @r{idem, but with leading zeroes only.}
     65      1.1     skrll @end smallexample
     66      1.1     skrll 
     67      1.1     skrll This function is especially useful when dealing with filename sorting,
     68      1.1     skrll because filenames frequently hold indices/version numbers.
     69      1.1     skrll @end deftypefun
     70      1.1     skrll 
     71      1.1     skrll */
     72      1.1     skrll 
     73      1.1     skrll /* states: S_N: normal, S_I: comparing integral part, S_F: comparing
     74      1.1     skrll            fractional parts, S_Z: idem but with leading Zeroes only */
     75      1.1     skrll #define  S_N    0x0
     76      1.1     skrll #define  S_I    0x4
     77      1.1     skrll #define  S_F    0x8
     78      1.1     skrll #define  S_Z    0xC
     79      1.1     skrll 
     80      1.1     skrll /* result_type: CMP: return diff; LEN: compare using len_diff/diff */
     81      1.1     skrll #define  CMP    2
     82      1.1     skrll #define  LEN    3
     83      1.1     skrll 
     84      1.1     skrll 
     85      1.1     skrll /* Compare S1 and S2 as strings holding indices/version numbers,
     86      1.1     skrll    returning less than, equal to or greater than zero if S1 is less than,
     87      1.1     skrll    equal to or greater than S2 (for more info, see the Glibc texinfo doc).  */
     88      1.1     skrll 
     89      1.1     skrll int
     90      1.1     skrll strverscmp (const char *s1, const char *s2)
     91      1.1     skrll {
     92      1.1     skrll   const unsigned char *p1 = (const unsigned char *) s1;
     93      1.1     skrll   const unsigned char *p2 = (const unsigned char *) s2;
     94      1.1     skrll   unsigned char c1, c2;
     95      1.1     skrll   int state;
     96      1.1     skrll   int diff;
     97      1.1     skrll 
     98      1.1     skrll   /* Symbol(s)    0       [1-9]   others  (padding)
     99      1.1     skrll      Transition   (10) 0  (01) d  (00) x  (11) -   */
    100      1.1     skrll   static const unsigned int next_state[] =
    101      1.1     skrll     {
    102      1.1     skrll       /* state    x    d    0    - */
    103      1.1     skrll       /* S_N */  S_N, S_I, S_Z, S_N,
    104      1.1     skrll       /* S_I */  S_N, S_I, S_I, S_I,
    105      1.1     skrll       /* S_F */  S_N, S_F, S_F, S_F,
    106      1.1     skrll       /* S_Z */  S_N, S_F, S_Z, S_Z
    107      1.1     skrll     };
    108      1.1     skrll 
    109      1.1     skrll   static const int result_type[] =
    110      1.1     skrll     {
    111      1.1     skrll       /* state   x/x  x/d  x/0  x/-  d/x  d/d  d/0  d/-
    112      1.1     skrll                  0/x  0/d  0/0  0/-  -/x  -/d  -/0  -/- */
    113      1.1     skrll 
    114      1.1     skrll       /* S_N */  CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
    115      1.1     skrll                  CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
    116      1.1     skrll       /* S_I */  CMP, -1,  -1,  CMP, +1,  LEN, LEN, CMP,
    117      1.1     skrll                  +1,  LEN, LEN, CMP, CMP, CMP, CMP, CMP,
    118      1.1     skrll       /* S_F */  CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
    119      1.1     skrll                  CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
    120      1.1     skrll       /* S_Z */  CMP, +1,  +1,  CMP, -1,  CMP, CMP, CMP,
    121      1.1     skrll                  -1,  CMP, CMP, CMP
    122      1.1     skrll     };
    123      1.1     skrll 
    124      1.1     skrll   if (p1 == p2)
    125      1.1     skrll     return 0;
    126      1.1     skrll 
    127      1.1     skrll   c1 = *p1++;
    128      1.1     skrll   c2 = *p2++;
    129      1.1     skrll   /* Hint: '0' is a digit too.  */
    130      1.1     skrll   state = S_N | ((c1 == '0') + (ISDIGIT (c1) != 0));
    131      1.1     skrll 
    132      1.1     skrll   while ((diff = c1 - c2) == 0 && c1 != '\0')
    133      1.1     skrll     {
    134      1.1     skrll       state = next_state[state];
    135      1.1     skrll       c1 = *p1++;
    136      1.1     skrll       c2 = *p2++;
    137      1.1     skrll       state |= (c1 == '0') + (ISDIGIT (c1) != 0);
    138      1.1     skrll     }
    139      1.1     skrll 
    140      1.1     skrll   state = result_type[state << 2 | (((c2 == '0') + (ISDIGIT (c2) != 0)))];
    141      1.1     skrll 
    142      1.1     skrll   switch (state)
    143      1.1     skrll     {
    144      1.1     skrll     case CMP:
    145      1.1     skrll       return diff;
    146      1.1     skrll 
    147      1.1     skrll     case LEN:
    148      1.1     skrll       while (ISDIGIT (*p1++))
    149      1.1     skrll 	if (!ISDIGIT (*p2++))
    150      1.1     skrll 	  return 1;
    151      1.1     skrll 
    152      1.1     skrll       return ISDIGIT (*p2) ? -1 : diff;
    153      1.1     skrll 
    154      1.1     skrll     default:
    155      1.1     skrll       return state;
    156      1.1     skrll     }
    157      1.1     skrll }
    158