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