Merge branch 'jc/diff-test-updates' into maint
[git] / versioncmp.c
1 #include "cache.h"
2
3 /*
4  * versioncmp(): copied from string/strverscmp.c in glibc commit
5  * ee9247c38a8def24a59eb5cfb7196a98bef8cfdc, reformatted to Git coding
6  * style. The implementation is under LGPL-2.1 and Git relicenses it
7  * to GPLv2.
8  */
9
10 /*
11  * states: S_N: normal, S_I: comparing integral part, S_F: comparing
12  * fractionnal parts, S_Z: idem but with leading Zeroes only
13  */
14 #define  S_N    0x0
15 #define  S_I    0x3
16 #define  S_F    0x6
17 #define  S_Z    0x9
18
19 /* result_type: CMP: return diff; LEN: compare using len_diff/diff */
20 #define  CMP    2
21 #define  LEN    3
22
23
24 /*
25  * Compare S1 and S2 as strings holding indices/version numbers,
26  * returning less than, equal to or greater than zero if S1 is less
27  * than, equal to or greater than S2 (for more info, see the texinfo
28  * doc).
29  */
30
31 int versioncmp(const char *s1, const char *s2)
32 {
33         const unsigned char *p1 = (const unsigned char *) s1;
34         const unsigned char *p2 = (const unsigned char *) s2;
35         unsigned char c1, c2;
36         int state, diff;
37
38         /*
39          * Symbol(s)    0       [1-9]   others
40          * Transition   (10) 0  (01) d  (00) x
41          */
42         static const uint8_t next_state[] = {
43                 /* state    x    d    0  */
44                 /* S_N */  S_N, S_I, S_Z,
45                 /* S_I */  S_N, S_I, S_I,
46                 /* S_F */  S_N, S_F, S_F,
47                 /* S_Z */  S_N, S_F, S_Z
48         };
49
50         static const int8_t result_type[] = {
51                 /* state   x/x  x/d  x/0  d/x  d/d  d/0  0/x  0/d  0/0  */
52
53                 /* S_N */  CMP, CMP, CMP, CMP, LEN, CMP, CMP, CMP, CMP,
54                 /* S_I */  CMP, -1,  -1,  +1,  LEN, LEN, +1,  LEN, LEN,
55                 /* S_F */  CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
56                 /* S_Z */  CMP, +1,  +1,  -1,  CMP, CMP, -1,  CMP, CMP
57         };
58
59         if (p1 == p2)
60                 return 0;
61
62         c1 = *p1++;
63         c2 = *p2++;
64         /* Hint: '0' is a digit too.  */
65         state = S_N + ((c1 == '0') + (isdigit (c1) != 0));
66
67         while ((diff = c1 - c2) == 0) {
68                 if (c1 == '\0')
69                         return diff;
70
71                 state = next_state[state];
72                 c1 = *p1++;
73                 c2 = *p2++;
74                 state += (c1 == '0') + (isdigit (c1) != 0);
75         }
76
77         state = result_type[state * 3 + (((c2 == '0') + (isdigit (c2) != 0)))];
78
79         switch (state) {
80         case CMP:
81                 return diff;
82
83         case LEN:
84                 while (isdigit (*p1++))
85                         if (!isdigit (*p2++))
86                                 return 1;
87
88                 return isdigit (*p2) ? -1 : diff;
89
90         default:
91                 return state;
92         }
93 }