xref: /netbsd-src/external/gpl3/gcc.old/dist/libiberty/strverscmp.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
11debfc3dSmrg /* Compare strings while treating digits characters numerically.
2*8feb0f0bSmrg    Copyright (C) 1997-2020 Free Software Foundation, Inc.
31debfc3dSmrg    This file is part of the libiberty library.
41debfc3dSmrg    Contributed by Jean-Fran�ois Bignolles <bignolle@ecoledoc.ibp.fr>, 1997.
51debfc3dSmrg 
61debfc3dSmrg    Libiberty is free software; you can redistribute it and/or
71debfc3dSmrg    modify it under the terms of the GNU Lesser General Public
81debfc3dSmrg    License as published by the Free Software Foundation; either
91debfc3dSmrg    version 2.1 of the License, or (at your option) any later version.
101debfc3dSmrg 
111debfc3dSmrg    Libiberty is distributed in the hope that it will be useful,
121debfc3dSmrg    but WITHOUT ANY WARRANTY; without even the implied warranty of
131debfc3dSmrg    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
141debfc3dSmrg    Lesser General Public License for more details.
151debfc3dSmrg 
161debfc3dSmrg    You should have received a copy of the GNU Lesser General Public
171debfc3dSmrg    License along with the GNU C Library; if not, write to the Free
181debfc3dSmrg    Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
191debfc3dSmrg    02110-1301 USA.  */
201debfc3dSmrg 
211debfc3dSmrg #include "libiberty.h"
221debfc3dSmrg #include "safe-ctype.h"
231debfc3dSmrg 
241debfc3dSmrg /*
251debfc3dSmrg @deftypefun int strverscmp (const char *@var{s1}, const char *@var{s2})
261debfc3dSmrg The @code{strverscmp} function compares the string @var{s1} against
271debfc3dSmrg @var{s2}, considering them as holding indices/version numbers.  Return
281debfc3dSmrg value follows the same conventions as found in the @code{strverscmp}
291debfc3dSmrg function.  In fact, if @var{s1} and @var{s2} contain no digits,
301debfc3dSmrg @code{strverscmp} behaves like @code{strcmp}.
311debfc3dSmrg 
321debfc3dSmrg Basically, we compare strings normally (character by character), until
331debfc3dSmrg we find a digit in each string - then we enter a special comparison
341debfc3dSmrg mode, where each sequence of digits is taken as a whole.  If we reach the
351debfc3dSmrg end of these two parts without noticing a difference, we return to the
361debfc3dSmrg standard comparison mode.  There are two types of numeric parts:
371debfc3dSmrg "integral" and "fractional" (those  begin with a '0'). The types
381debfc3dSmrg of the numeric parts affect the way we sort them:
391debfc3dSmrg 
401debfc3dSmrg @itemize @bullet
411debfc3dSmrg @item
421debfc3dSmrg integral/integral: we compare values as you would expect.
431debfc3dSmrg 
441debfc3dSmrg @item
451debfc3dSmrg fractional/integral: the fractional part is less than the integral one.
461debfc3dSmrg Again, no surprise.
471debfc3dSmrg 
481debfc3dSmrg @item
491debfc3dSmrg fractional/fractional: the things become a bit more complex.
501debfc3dSmrg If the common prefix contains only leading zeroes, the longest part is less
511debfc3dSmrg than the other one; else the comparison behaves normally.
521debfc3dSmrg @end itemize
531debfc3dSmrg 
541debfc3dSmrg @smallexample
551debfc3dSmrg strverscmp ("no digit", "no digit")
561debfc3dSmrg     @result{} 0    // @r{same behavior as strcmp.}
571debfc3dSmrg strverscmp ("item#99", "item#100")
581debfc3dSmrg     @result{} <0   // @r{same prefix, but 99 < 100.}
591debfc3dSmrg strverscmp ("alpha1", "alpha001")
601debfc3dSmrg     @result{} >0   // @r{fractional part inferior to integral one.}
611debfc3dSmrg strverscmp ("part1_f012", "part1_f01")
621debfc3dSmrg     @result{} >0   // @r{two fractional parts.}
631debfc3dSmrg strverscmp ("foo.009", "foo.0")
641debfc3dSmrg     @result{} <0   // @r{idem, but with leading zeroes only.}
651debfc3dSmrg @end smallexample
661debfc3dSmrg 
671debfc3dSmrg This function is especially useful when dealing with filename sorting,
681debfc3dSmrg because filenames frequently hold indices/version numbers.
691debfc3dSmrg @end deftypefun
701debfc3dSmrg 
711debfc3dSmrg */
721debfc3dSmrg 
731debfc3dSmrg /* states: S_N: normal, S_I: comparing integral part, S_F: comparing
741debfc3dSmrg            fractional parts, S_Z: idem but with leading Zeroes only */
751debfc3dSmrg #define  S_N    0x0
761debfc3dSmrg #define  S_I    0x4
771debfc3dSmrg #define  S_F    0x8
781debfc3dSmrg #define  S_Z    0xC
791debfc3dSmrg 
801debfc3dSmrg /* result_type: CMP: return diff; LEN: compare using len_diff/diff */
811debfc3dSmrg #define  CMP    2
821debfc3dSmrg #define  LEN    3
831debfc3dSmrg 
841debfc3dSmrg 
851debfc3dSmrg /* Compare S1 and S2 as strings holding indices/version numbers,
861debfc3dSmrg    returning less than, equal to or greater than zero if S1 is less than,
871debfc3dSmrg    equal to or greater than S2 (for more info, see the Glibc texinfo doc).  */
881debfc3dSmrg 
891debfc3dSmrg int
strverscmp(const char * s1,const char * s2)901debfc3dSmrg strverscmp (const char *s1, const char *s2)
911debfc3dSmrg {
921debfc3dSmrg   const unsigned char *p1 = (const unsigned char *) s1;
931debfc3dSmrg   const unsigned char *p2 = (const unsigned char *) s2;
941debfc3dSmrg   unsigned char c1, c2;
951debfc3dSmrg   int state;
961debfc3dSmrg   int diff;
971debfc3dSmrg 
981debfc3dSmrg   /* Symbol(s)    0       [1-9]   others  (padding)
991debfc3dSmrg      Transition   (10) 0  (01) d  (00) x  (11) -   */
1001debfc3dSmrg   static const unsigned int next_state[] =
1011debfc3dSmrg     {
1021debfc3dSmrg       /* state    x    d    0    - */
1031debfc3dSmrg       /* S_N */  S_N, S_I, S_Z, S_N,
1041debfc3dSmrg       /* S_I */  S_N, S_I, S_I, S_I,
1051debfc3dSmrg       /* S_F */  S_N, S_F, S_F, S_F,
1061debfc3dSmrg       /* S_Z */  S_N, S_F, S_Z, S_Z
1071debfc3dSmrg     };
1081debfc3dSmrg 
1091debfc3dSmrg   static const int result_type[] =
1101debfc3dSmrg     {
1111debfc3dSmrg       /* state   x/x  x/d  x/0  x/-  d/x  d/d  d/0  d/-
1121debfc3dSmrg                  0/x  0/d  0/0  0/-  -/x  -/d  -/0  -/- */
1131debfc3dSmrg 
1141debfc3dSmrg       /* S_N */  CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
1151debfc3dSmrg                  CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
1161debfc3dSmrg       /* S_I */  CMP, -1,  -1,  CMP, +1,  LEN, LEN, CMP,
1171debfc3dSmrg                  +1,  LEN, LEN, CMP, CMP, CMP, CMP, CMP,
1181debfc3dSmrg       /* S_F */  CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
1191debfc3dSmrg                  CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
1201debfc3dSmrg       /* S_Z */  CMP, +1,  +1,  CMP, -1,  CMP, CMP, CMP,
1211debfc3dSmrg                  -1,  CMP, CMP, CMP
1221debfc3dSmrg     };
1231debfc3dSmrg 
1241debfc3dSmrg   if (p1 == p2)
1251debfc3dSmrg     return 0;
1261debfc3dSmrg 
1271debfc3dSmrg   c1 = *p1++;
1281debfc3dSmrg   c2 = *p2++;
1291debfc3dSmrg   /* Hint: '0' is a digit too.  */
1301debfc3dSmrg   state = S_N | ((c1 == '0') + (ISDIGIT (c1) != 0));
1311debfc3dSmrg 
1321debfc3dSmrg   while ((diff = c1 - c2) == 0 && c1 != '\0')
1331debfc3dSmrg     {
1341debfc3dSmrg       state = next_state[state];
1351debfc3dSmrg       c1 = *p1++;
1361debfc3dSmrg       c2 = *p2++;
1371debfc3dSmrg       state |= (c1 == '0') + (ISDIGIT (c1) != 0);
1381debfc3dSmrg     }
1391debfc3dSmrg 
1401debfc3dSmrg   state = result_type[state << 2 | (((c2 == '0') + (ISDIGIT (c2) != 0)))];
1411debfc3dSmrg 
1421debfc3dSmrg   switch (state)
1431debfc3dSmrg     {
1441debfc3dSmrg     case CMP:
1451debfc3dSmrg       return diff;
1461debfc3dSmrg 
1471debfc3dSmrg     case LEN:
1481debfc3dSmrg       while (ISDIGIT (*p1++))
1491debfc3dSmrg 	if (!ISDIGIT (*p2++))
1501debfc3dSmrg 	  return 1;
1511debfc3dSmrg 
1521debfc3dSmrg       return ISDIGIT (*p2) ? -1 : diff;
1531debfc3dSmrg 
1541debfc3dSmrg     default:
1551debfc3dSmrg       return state;
1561debfc3dSmrg     }
1571debfc3dSmrg }
158