root/lib/strutil/filevercmp.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. match_suffix
  2. order
  3. verrevcmp
  4. filevercmp

   1 /*
   2    Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
   3    Copyright (C) 2001 Anthony Towns <aj@azure.humbug.org.au>
   4    Copyright (C) 2008-2018 Free Software Foundation, Inc.
   5 
   6    This program is free software: you can redistribute it and/or modify
   7    it under the terms of the GNU General Public License as published by
   8    the Free Software Foundation, either version 3 of the License, or
   9    (at your option) any later version.
  10 
  11    This program is distributed in the hope that it will be useful,
  12    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14    GNU General Public License for more details.
  15 
  16    You should have received a copy of the GNU General Public License
  17    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  18  */
  19 
  20 #include <config.h>
  21 
  22 #include <sys/types.h>
  23 #include <stdlib.h>
  24 #include <string.h>
  25 #include <limits.h>
  26 
  27 #include "lib/strutil.h"
  28 
  29 /*** global variables ****************************************************************************/
  30 
  31 /*** file scope macro definitions ****************************************************************/
  32 
  33 /*** file scope type declarations ****************************************************************/
  34 
  35 /*** file scope variables ************************************************************************/
  36 
  37 /* --------------------------------------------------------------------------------------------- */
  38 /*** file scope functions ************************************************************************/
  39 /* --------------------------------------------------------------------------------------------- */
  40 
  41 /* Match a file suffix defined by this regular expression: /(\.[A-Za-z~][A-Za-z0-9~]*)*$/
  42  *
  43  * @str pointer to string to scan.
  44  *
  45  * @return pointer to the matching suffix, or NULL if not found.
  46  *         Upon return, @str points to terminating NUL.
  47  */
  48 static const char *
  49 match_suffix (const char **str)
     /* [previous][next][first][last][top][bottom][index][help]  */
  50 {
  51     const char *match = NULL;
  52     gboolean read_alpha = FALSE;
  53 
  54     while (**str != '\0')
  55     {
  56         if (read_alpha)
  57         {
  58             read_alpha = FALSE;
  59             if (!g_ascii_isalpha (**str) && **str != '~')
  60                 match = NULL;
  61         }
  62         else if (**str == '.')
  63         {
  64             read_alpha = TRUE;
  65             if (match == NULL)
  66                 match = *str;
  67         }
  68         else if (!g_ascii_isalnum (**str) && **str != '~')
  69             match = NULL;
  70         (*str)++;
  71     }
  72 
  73     return match;
  74 }
  75 
  76 /* --------------------------------------------------------------------------------------------- */
  77 
  78 /* verrevcmp helper function */
  79 static int
  80 order (unsigned char c)
     /* [previous][next][first][last][top][bottom][index][help]  */
  81 {
  82     if (g_ascii_isdigit (c))
  83         return 0;
  84     if (g_ascii_isalpha (c))
  85         return c;
  86     if (c == '~')
  87         return -1;
  88     return (int) c + UCHAR_MAX + 1;
  89 }
  90 
  91 /* --------------------------------------------------------------------------------------------- */
  92 
  93 /* Slightly modified verrevcmp function from dpkg
  94  *
  95  * This implements the algorithm for comparison of version strings
  96  * specified by Debian and now widely adopted.  The detailed
  97  * specification can be found in the Debian Policy Manual in the
  98  * section on the 'Version' control field.  This version of the code
  99  * implements that from s5.6.12 of Debian Policy v3.8.0.1
 100  * https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version
 101  *
 102  * @s1 first string to compare
 103  * @s1_len length of @s1
 104  * @s2 second string to compare
 105  * @s2_len length of @s2
 106  *
 107  * @return an integer less than, equal to, or greater than zero, if @s1 is <, == or > than @s2.
 108  */
 109 static int
 110 verrevcmp (const char *s1, size_t s1_len, const char *s2, size_t s2_len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 111 {
 112     size_t s1_pos = 0;
 113     size_t s2_pos = 0;
 114 
 115     while (s1_pos < s1_len || s2_pos < s2_len)
 116     {
 117         int first_diff = 0;
 118 
 119         while ((s1_pos < s1_len && !g_ascii_isdigit (s1[s1_pos]))
 120                || (s2_pos < s2_len && !g_ascii_isdigit (s2[s2_pos])))
 121         {
 122             int s1_c = 0;
 123             int s2_c = 0;
 124 
 125             if (s1_pos != s1_len)
 126                 s1_c = order (s1[s1_pos]);
 127             if (s2_pos != s2_len)
 128                 s2_c = order (s2[s2_pos]);
 129 
 130             if (s1_c != s2_c)
 131                 return (s1_c - s2_c);
 132 
 133             s1_pos++;
 134             s2_pos++;
 135         }
 136 
 137         while (s1[s1_pos] == '0')
 138             s1_pos++;
 139         while (s2[s2_pos] == '0')
 140             s2_pos++;
 141 
 142         while (g_ascii_isdigit (s1[s1_pos]) && g_ascii_isdigit (s2[s2_pos]))
 143         {
 144             if (first_diff == 0)
 145                 first_diff = s1[s1_pos] - s2[s2_pos];
 146 
 147             s1_pos++;
 148             s2_pos++;
 149         }
 150 
 151         if (g_ascii_isdigit (s1[s1_pos]))
 152             return 1;
 153         if (g_ascii_isdigit (s2[s2_pos]))
 154             return -1;
 155         if (first_diff != 0)
 156             return first_diff;
 157     }
 158 
 159     return 0;
 160 }
 161 
 162 /* --------------------------------------------------------------------------------------------- */
 163 /*** public functions ****************************************************************************/
 164 /* --------------------------------------------------------------------------------------------- */
 165 
 166 /* Compare version strings.
 167  *
 168  * @s1 first string to compare
 169  * @s2 second string to compare
 170  *
 171  * @return an integer less than, equal to, or greater than zero, if @s1 is <, == or > than @s2.
 172  */
 173 int
 174 filevercmp (const char *s1, const char *s2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 175 {
 176     const char *s1_pos, *s2_pos;
 177     const char *s1_suffix, *s2_suffix;
 178     size_t s1_len, s2_len;
 179     int simple_cmp, result;
 180 
 181     /* easy comparison to see if strings are identical */
 182     simple_cmp = strcmp (s1, s2);
 183     if (simple_cmp == 0)
 184         return 0;
 185 
 186     /* special handle for "", "." and ".." */
 187     if (*s1 == '\0')
 188         return -1;
 189     if (*s2 == '\0')
 190         return 1;
 191     if (DIR_IS_DOT (s1))
 192         return -1;
 193     if (DIR_IS_DOT (s2))
 194         return 1;
 195     if (DIR_IS_DOTDOT (s1))
 196         return -1;
 197     if (DIR_IS_DOTDOT (s2))
 198         return 1;
 199 
 200     /* special handle for other hidden files */
 201     if (*s1 == '.' && *s2 != '.')
 202         return -1;
 203     if (*s1 != '.' && *s2 == '.')
 204         return 1;
 205     if (*s1 == '.' && *s2 == '.')
 206     {
 207         s1++;
 208         s2++;
 209     }
 210 
 211     /* "cut" file suffixes */
 212     s1_pos = s1;
 213     s2_pos = s2;
 214     s1_suffix = match_suffix (&s1_pos);
 215     s2_suffix = match_suffix (&s2_pos);
 216     s1_len = (s1_suffix != NULL ? s1_suffix : s1_pos) - s1;
 217     s2_len = (s2_suffix != NULL ? s2_suffix : s2_pos) - s2;
 218 
 219     /* restore file suffixes if strings are identical after "cut" */
 220     if ((s1_suffix != NULL || s2_suffix != NULL) && (s1_len == s2_len)
 221         && strncmp (s1, s2, s1_len) == 0)
 222     {
 223         s1_len = s1_pos - s1;
 224         s2_len = s2_pos - s2;
 225     }
 226 
 227     result = verrevcmp (s1, s1_len, s2, s2_len);
 228 
 229     return result == 0 ? simple_cmp : result;
 230 }
 231 
 232 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */