/*
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;
}