THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
5.2KB

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