Git fork
at reftables-rust 202 lines 5.5 kB view raw
1#define USE_THE_REPOSITORY_VARIABLE 2 3#include "git-compat-util.h" 4#include "config.h" 5#include "strbuf.h" 6#include "string-list.h" 7#include "versioncmp.h" 8 9/* 10 * versioncmp(): copied from string/strverscmp.c in glibc commit 11 * ee9247c38a8def24a59eb5cfb7196a98bef8cfdc, reformatted to Git coding 12 * style. The implementation is under LGPL-2.1 and Git relicenses it 13 * to GPLv2. 14 */ 15 16/* 17 * states: S_N: normal, S_I: comparing integral part, S_F: comparing 18 * fractionnal parts, S_Z: idem but with leading Zeroes only 19 */ 20#define S_N 0x0 21#define S_I 0x3 22#define S_F 0x6 23#define S_Z 0x9 24 25/* result_type: CMP: return diff; LEN: compare using len_diff/diff */ 26#define CMP 2 27#define LEN 3 28 29static const struct string_list *prereleases; 30static int initialized; 31 32struct suffix_match { 33 int conf_pos; 34 int start; 35 int len; 36}; 37 38static void find_better_matching_suffix(const char *tagname, const char *suffix, 39 int suffix_len, int start, int conf_pos, 40 struct suffix_match *match) 41{ 42 /* 43 * A better match either starts earlier or starts at the same offset 44 * but is longer. 45 */ 46 int end = match->len < suffix_len ? match->start : match->start-1; 47 int i; 48 for (i = start; i <= end; i++) 49 if (starts_with(tagname + i, suffix)) { 50 match->conf_pos = conf_pos; 51 match->start = i; 52 match->len = suffix_len; 53 break; 54 } 55} 56 57/* 58 * off is the offset of the first different character in the two strings 59 * s1 and s2. If either s1 or s2 contains a prerelease suffix containing 60 * that offset or a suffix ends right before that offset, then that 61 * string will be forced to be on top. 62 * 63 * If both s1 and s2 contain a (different) suffix around that position, 64 * their order is determined by the order of those two suffixes in the 65 * configuration. 66 * If any of the strings contains more than one different suffixes around 67 * that position, then that string is sorted according to the contained 68 * suffix which starts at the earliest offset in that string. 69 * If more than one different contained suffixes start at that earliest 70 * offset, then that string is sorted according to the longest of those 71 * suffixes. 72 * 73 * Return non-zero if *diff contains the return value for versioncmp() 74 */ 75static int swap_prereleases(const char *s1, 76 const char *s2, 77 int off, 78 int *diff) 79{ 80 struct suffix_match match1 = { -1, off, -1 }; 81 struct suffix_match match2 = { -1, off, -1 }; 82 83 for (size_t i = 0; i < prereleases->nr; i++) { 84 const char *suffix = prereleases->items[i].string; 85 int start, suffix_len = strlen(suffix); 86 if (suffix_len < off) 87 start = off - suffix_len; 88 else 89 start = 0; 90 find_better_matching_suffix(s1, suffix, suffix_len, start, 91 i, &match1); 92 find_better_matching_suffix(s2, suffix, suffix_len, start, 93 i, &match2); 94 } 95 if (match1.conf_pos == -1 && match2.conf_pos == -1) 96 return 0; 97 if (match1.conf_pos == match2.conf_pos) 98 /* Found the same suffix in both, e.g. "-rc" in "v1.0-rcX" 99 * and "v1.0-rcY": the caller should decide based on "X" 100 * and "Y". */ 101 return 0; 102 103 if (match1.conf_pos >= 0 && match2.conf_pos >= 0) 104 *diff = match1.conf_pos - match2.conf_pos; 105 else if (match1.conf_pos >= 0) 106 *diff = -1; 107 else /* if (match2.conf_pos >= 0) */ 108 *diff = 1; 109 return 1; 110} 111 112/* 113 * Compare S1 and S2 as strings holding indices/version numbers, 114 * returning less than, equal to or greater than zero if S1 is less 115 * than, equal to or greater than S2 (for more info, see the texinfo 116 * doc). 117 */ 118 119int versioncmp(const char *s1, const char *s2) 120{ 121 const unsigned char *p1 = (const unsigned char *) s1; 122 const unsigned char *p2 = (const unsigned char *) s2; 123 unsigned char c1, c2; 124 int state, diff; 125 126 /* 127 * Symbol(s) 0 [1-9] others 128 * Transition (10) 0 (01) d (00) x 129 */ 130 static const uint8_t next_state[] = { 131 /* state x d 0 */ 132 /* S_N */ S_N, S_I, S_Z, 133 /* S_I */ S_N, S_I, S_I, 134 /* S_F */ S_N, S_F, S_F, 135 /* S_Z */ S_N, S_F, S_Z 136 }; 137 138 static const int8_t result_type[] = { 139 /* state x/x x/d x/0 d/x d/d d/0 0/x 0/d 0/0 */ 140 141 /* S_N */ CMP, CMP, CMP, CMP, LEN, CMP, CMP, CMP, CMP, 142 /* S_I */ CMP, -1, -1, +1, LEN, LEN, +1, LEN, LEN, 143 /* S_F */ CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, 144 /* S_Z */ CMP, +1, +1, -1, CMP, CMP, -1, CMP, CMP 145 }; 146 147 if (p1 == p2) 148 return 0; 149 150 c1 = *p1++; 151 c2 = *p2++; 152 /* Hint: '0' is a digit too. */ 153 state = S_N + ((c1 == '0') + (isdigit (c1) != 0)); 154 155 while ((diff = c1 - c2) == 0) { 156 if (c1 == '\0') 157 return diff; 158 159 state = next_state[state]; 160 c1 = *p1++; 161 c2 = *p2++; 162 state += (c1 == '0') + (isdigit (c1) != 0); 163 } 164 165 if (!initialized) { 166 const char *const newk = "versionsort.suffix"; 167 const char *const oldk = "versionsort.prereleasesuffix"; 168 const struct string_list *newl; 169 const struct string_list *oldl; 170 int new = repo_config_get_string_multi(the_repository, newk, &newl); 171 int old = repo_config_get_string_multi(the_repository, oldk, &oldl); 172 173 if (!new && !old) 174 warning("ignoring %s because %s is set", oldk, newk); 175 if (!new) 176 prereleases = newl; 177 else if (!old) 178 prereleases = oldl; 179 180 initialized = 1; 181 } 182 if (prereleases && swap_prereleases(s1, s2, (const char *) p1 - s1 - 1, 183 &diff)) 184 return diff; 185 186 state = result_type[state * 3 + (((c2 == '0') + (isdigit (c2) != 0)))]; 187 188 switch (state) { 189 case CMP: 190 return diff; 191 192 case LEN: 193 while (isdigit (*p1++)) 194 if (!isdigit (*p2++)) 195 return 1; 196 197 return isdigit (*p2) ? -1 : diff; 198 199 default: 200 return state; 201 } 202}