123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- /*
- Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
- Copyright (C) 2001 Anthony Towns <aj@azure.humbug.org.au>
- Copyright (C) 2008-2018 Free Software Foundation, Inc.
- 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 <https://www.gnu.org/licenses/>.
- */
- #include <config.h>
- #include <sys/types.h>
- #include <stdlib.h>
- #include <string.h>
- #include <limits.h>
- #include "lib/strutil.h"
- /*** global variables ****************************************************************************/
- /*** file scope macro definitions ****************************************************************/
- /*** file scope type declarations ****************************************************************/
- /*** file scope variables ************************************************************************/
- /* --------------------------------------------------------------------------------------------- */
- /*** file scope functions ************************************************************************/
- /* --------------------------------------------------------------------------------------------- */
- /* Match a file suffix defined by this regular expression: /(\.[A-Za-z~][A-Za-z0-9~]*)*$/
- *
- * @str pointer to string to scan.
- *
- * @return pointer to the matching suffix, or NULL if not found.
- * Upon return, @str points to terminating NUL.
- */
- static const char *
- match_suffix (const char **str)
- {
- const char *match = NULL;
- gboolean read_alpha = FALSE;
- while (**str != '\0')
- {
- if (read_alpha)
- {
- read_alpha = FALSE;
- if (!g_ascii_isalpha (**str) && **str != '~')
- match = NULL;
- }
- else if (**str == '.')
- {
- read_alpha = TRUE;
- if (match == NULL)
- match = *str;
- }
- else if (!g_ascii_isalnum (**str) && **str != '~')
- match = NULL;
- (*str)++;
- }
- return match;
- }
- /* --------------------------------------------------------------------------------------------- */
- /* verrevcmp helper function */
- static int
- order (unsigned char c)
- {
- if (g_ascii_isdigit (c))
- return 0;
- if (g_ascii_isalpha (c))
- return c;
- if (c == '~')
- return -1;
- return (int) c + UCHAR_MAX + 1;
- }
- /* --------------------------------------------------------------------------------------------- */
- /* Slightly modified verrevcmp function from dpkg
- *
- * This implements the algorithm for comparison of version strings
- * specified by Debian and now widely adopted. The detailed
- * specification can be found in the Debian Policy Manual in the
- * section on the 'Version' control field. This version of the code
- * implements that from s5.6.12 of Debian Policy v3.8.0.1
- * https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version
- *
- * @s1 first string to compare
- * @s1_len length of @s1
- * @s2 second string to compare
- * @s2_len length of @s2
- *
- * @return an integer less than, equal to, or greater than zero, if @s1 is <, == or > than @s2.
- */
- 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 && !g_ascii_isdigit (s1[s1_pos]))
- || (s2_pos < s2_len && !g_ascii_isdigit (s2[s2_pos])))
- {
- int s1_c = 0;
- int s2_c = 0;
- if (s1_pos != s1_len)
- s1_c = order (s1[s1_pos]);
- if (s2_pos != s2_len)
- s2_c = 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 (g_ascii_isdigit (s1[s1_pos]) && g_ascii_isdigit (s2[s2_pos]))
- {
- if (first_diff == 0)
- first_diff = s1[s1_pos] - s2[s2_pos];
- s1_pos++;
- s2_pos++;
- }
- if (g_ascii_isdigit (s1[s1_pos]))
- return 1;
- if (g_ascii_isdigit (s2[s2_pos]))
- return -1;
- if (first_diff != 0)
- return first_diff;
- }
- return 0;
- }
- /* --------------------------------------------------------------------------------------------- */
- /*** public functions ****************************************************************************/
- /* --------------------------------------------------------------------------------------------- */
- /* Compare version strings.
- *
- * @s1 first string to compare
- * @s2 second string to compare
- *
- * @return an integer less than, equal to, or greater than zero, if @s1 is <, == or > than @s2.
- */
- int
- filevercmp (const char *s1, const char *s2)
- {
- const char *s1_pos, *s2_pos;
- const char *s1_suffix, *s2_suffix;
- size_t s1_len, s2_len;
- int simple_cmp, result;
- /* easy comparison to see if strings are identical */
- simple_cmp = strcmp (s1, s2);
- if (simple_cmp == 0)
- return 0;
- /* special handle for "", "." and ".." */
- if (*s1 == '\0')
- return -1;
- if (*s2 == '\0')
- return 1;
- if (DIR_IS_DOT (s1))
- return -1;
- if (DIR_IS_DOT (s2))
- return 1;
- if (DIR_IS_DOTDOT (s1))
- return -1;
- if (DIR_IS_DOTDOT (s2))
- return 1;
- /* special handle for other hidden files */
- if (*s1 == '.' && *s2 != '.')
- return -1;
- if (*s1 != '.' && *s2 == '.')
- return 1;
- if (*s1 == '.' && *s2 == '.')
- {
- s1++;
- s2++;
- }
- /* "cut" file suffixes */
- s1_pos = s1;
- s2_pos = s2;
- s1_suffix = match_suffix (&s1_pos);
- s2_suffix = match_suffix (&s2_pos);
- s1_len = (s1_suffix != NULL ? s1_suffix : s1_pos) - s1;
- s2_len = (s2_suffix != NULL ? s2_suffix : s2_pos) - s2;
- /* restore file suffixes if strings are identical after "cut" */
- if ((s1_suffix != NULL || s2_suffix != NULL) && (s1_len == s2_len)
- && strncmp (s1, s2, s1_len) == 0)
- {
- s1_len = s1_pos - s1;
- s2_len = s2_pos - s2;
- }
- result = verrevcmp (s1, s1_len, s2, s2_len);
- return result == 0 ? simple_cmp : result;
- }
- /* --------------------------------------------------------------------------------------------- */
|