matchver.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /*
  2. * Compare project/version strings of the form:
  3. *
  4. * PROJECT v<num>.<num>...[ <flags>]
  5. *
  6. * Pattern is of the form:
  7. *
  8. * PROJECT[ v[<num>.<num>...][-[<num>.<num>...]] ][+-]flags...
  9. *
  10. * Chunks must be separated by exactly one space. Flags are A-Za-z0-9.
  11. * Any control character is treated as end of string.
  12. */
  13. #include <stdlib.h>
  14. #include <stdbool.h>
  15. #include <inttypes.h>
  16. static int flag_val(char c)
  17. {
  18. if (c < '0')
  19. return -1;
  20. else if (c <= '9')
  21. return c-'0';
  22. else if (c < 'A')
  23. return -1;
  24. else if (c <= 'Z')
  25. return c-'A'+10;
  26. else if (c < 'a')
  27. return -1;
  28. else if (c <= 'z')
  29. return c-'a'+36;
  30. else
  31. return -1;
  32. }
  33. static uint64_t flag_mask(char c)
  34. {
  35. int v = flag_val(c);
  36. return (v < 0) ? 0 : UINT64_C(1) << v;
  37. }
  38. static inline bool is_digit(char c)
  39. {
  40. return (unsigned int)(c - '0') < 10;
  41. }
  42. /*
  43. * Compare numeric strings with components separated by dots, return the end
  44. * of each string.
  45. */
  46. static int compare_numbers(const char **ap, const char **bp,
  47. unsigned long bmissing)
  48. {
  49. bool adig, bdig;
  50. unsigned long an, bn;
  51. int result = 0;
  52. const char *a = *ap, *b = *bp;
  53. for (;;) {
  54. adig = is_digit(*a);
  55. bdig = is_digit(*b);
  56. if (!adig && !bdig)
  57. break;
  58. if (adig) {
  59. an = strtoul(a, (char **)&a, 10);
  60. if (*a == '.')
  61. a++;
  62. } else {
  63. an = 0;
  64. }
  65. if (bdig) {
  66. bn = strtoul(b, (char **)&b, 10);
  67. if (*b == '.')
  68. b++;
  69. } else {
  70. bn = bmissing;
  71. }
  72. /* If result is set, the answer is already known, just find the end */
  73. if (!result) {
  74. result = (an < bn) ? -1 : (an > bn) ? 1 : 0;
  75. }
  76. }
  77. *ap = a; *bp = b;
  78. return result;
  79. }
  80. static const char *parse_flags(const char *str, uint64_t *flags, uint64_t *mask)
  81. {
  82. uint64_t polarity = -1;
  83. uint64_t f = 0, m = 0;
  84. while (1) {
  85. char c = *str++;
  86. uint64_t bit;
  87. if (c == '+') {
  88. polarity = -1;
  89. } else if (c == '-') {
  90. polarity = 0;
  91. } else {
  92. bit = flag_mask(c);
  93. if (!bit)
  94. break;
  95. m |= bit;
  96. f = (f & ~bit) | (polarity & bit);
  97. }
  98. }
  99. *flags = f; *mask = m;
  100. return str;
  101. }
  102. bool match_version(const char *version, const char *pattern)
  103. {
  104. char v, p;
  105. const char *vstart, *pstart;
  106. uint64_t vflags, pflags, pmask;
  107. while (1) {
  108. v = *version++;
  109. p = *pattern++;
  110. if (v <= ' ' && p <= ' ')
  111. break;
  112. if (v != p)
  113. return false;
  114. }
  115. if (p < ' ')
  116. return true; /* Project-only pattern */
  117. if (v != ' ' || *version++ != 'v')
  118. return false; /* Invalid/unparsable version string */
  119. if (*pattern++ != 'v')
  120. return false; /* Invalid pattern */
  121. vstart = version;
  122. pstart = pattern;
  123. if (compare_numbers(&version, &pattern, 0UL) < 0)
  124. return false;
  125. if (*pattern == '-')
  126. pattern++;
  127. else
  128. pattern = pstart;
  129. if (compare_numbers(&vstart, &pattern, -1UL) > 0)
  130. return false;
  131. v = *version++;
  132. vflags = 0;
  133. if (v == ' ') {
  134. uint64_t dummy;
  135. parse_flags(version, &vflags, &dummy);
  136. } else if (v > ' ') {
  137. return false;
  138. }
  139. p = *pattern++;
  140. pflags = pmask = 0;
  141. if (p == ' ') {
  142. parse_flags(pattern, &pflags, &pmask);
  143. } else if (p > ' ') {
  144. return false;
  145. }
  146. return (vflags & pmask) == pflags;
  147. }
  148. #ifdef COMMAND
  149. #include <stdio.h>
  150. int main(int argc, char *argv[])
  151. {
  152. if (argc != 3) {
  153. fprintf(stderr, "Usage: %s version pattern\n", argv[0]);
  154. return 127;
  155. }
  156. return !match_version(argv[1], argv[2]);
  157. }
  158. #endif