/* TODO: copyright? This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "filevercmp.h" #include #include #include #include #include #include #define ISALNUM(c) c_isalnum ((unsigned char) (c)) #define ISALPHA(c) c_isalpha ((unsigned char) (c)) #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9) /* match file suffix defined as RE (\.[A-Za-z][A-Za-z0-9]*)*$ Scan string pointed by *str and return pointer to suffix begin or NULL if not found. Pointer *str points to ending zero of scanned string after return. */ static const char * match_suffix (const char **str) { const char *match = NULL; bool read_alpha = false; while (**str) { if (read_alpha) { read_alpha = false; if (!ISALPHA (**str)) match = NULL; } else if ('.' == **str) { read_alpha = true; if (!match) match = *str; } else if (!ISALNUM (**str)) match = NULL; (*str)++; } return match; } /* verrevcmp helper function */ static inline int order (unsigned char c) { if (ISDIGIT (c)) return 0; else if (ISALPHA (c)) return c; else if (c == '~') return -1; else return (int) c + UCHAR_MAX + 1; } /* slightly modified ververcmp function from dpkg S1, S2 - compared string S1_LEN, S2_LEN - length of strings to be scanned */ static int verrevcmp (const char *s1, size_t s1_len, const char *s2, size_t s2_len) { size_t s1_pos = 0; size_t s2_pos = 0; while (s1_pos < s1_len || s2_pos < s2_len) { int first_diff = 0; while ((s1_pos < s1_len && !ISDIGIT (s1[s1_pos])) || (s2_pos < s2_len && !ISDIGIT (s2[s2_pos]))) { int s1_c = (s1_pos == s1_len) ? 0 : order (s1[s1_pos]); int s2_c = (s2_pos == s2_len) ? 0 : order (s2[s2_pos]); if (s1_c != s2_c) return s1_c - s2_c; s1_pos++; s2_pos++; } while (s1[s1_pos] == '0') s1_pos++; while (s2[s2_pos] == '0') s2_pos++; while (ISDIGIT (s1[s1_pos]) && ISDIGIT (s2[s2_pos])) { if (!first_diff) first_diff = s1[s1_pos] - s2[s2_pos]; s1_pos++; s2_pos++; } if (ISDIGIT (s1[s1_pos])) return 1; if (ISDIGIT (s2[s2_pos])) return -1; if (first_diff) return first_diff; } return 0; } /* Compare version strings S1 and S2. See filevercmp.h for function description. */ int filevercmp (const char *s1, const char *s2) { const char *s1_pos = s1; const char *s2_pos = s2; const char *s1_suffix, *s2_suffix; size_t s1_len, s2_len; int result; /* easy comparison to see if strings are identical */ int simple_cmp = strcmp (s1, s2); if (simple_cmp == 0) return 0; /* "cut" file suffixes */ s1_suffix = match_suffix (&s1_pos); s2_suffix = match_suffix (&s2_pos); s1_len = (s1_suffix ? s1_suffix : s1_pos) - s1; s2_len = (s2_suffix ? s2_suffix : s2_pos) - s2; /* restore file suffixes if strings are identical after "cut" */ if ((s1_suffix || s2_suffix) && (s1_len == s2_len) && 0 == strncmp (s1, s2, s1_len)) { s1_len = s1_pos - s1; s2_len = s2_pos - s2; } result = verrevcmp (s1, s1_len, s2, s2_len); return result == 0 ? simple_cmp : result; }