On Monday 29 September 2008 16:12:18 Ian Jackson wrote:
> And which files are we talking about ?
lib/vercmp.c
> NB that the dpkg comparison algorithm was recently extended to support
> a new character ~ which sorts before the empty string.  This work
> wasn't done by me - but I approve of it and it should be in the
> gnulib version too.
we use the extended version (handling ~ character), even slightly modified to 
work better with file names (with suffixes). The new (gnulib) filevercmp code 
is in attachment.


Kamil


/*
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 <http://www.gnu.org/licenses/>. */

#include <config.h>
#include "filevercmp.h"

#include <sys/types.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <c-ctype.h>
#include <limits.h>

#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;
}

Reply via email to