Jilles Tjoelker wrote:
On Wed, Feb 15, 2006 at 10:05:24AM -0600, Dan Nelson wrote:
> Your function is simpler than the C implementation on that site, but
> falls over when a run of numbers exceeds 2^31 (raise it to 2^64 if you
> use strtoull, but that's as high as you can yet).

That problem can be avoided fairly easily. First skip the leading zeroes
on both sides, then strspn for digits; the longest run of digits is
greater, otherwise strncmp it.

Yes, the trouble begins, when you really want to be compatible with the GNU version.

Attached is an new, ugly version of strverscmp(3) that behaves exactly as the glibc version (at least for a limit amount of testing).

Ulrich Spoerlein
--
 PGP Key ID: 20FEE9DD                           Encrypted mail welcome!
Fingerprint: AEC9 AF5E 01AC 4EE1 8F70  6CBD E76E 2227 20FE E9DD
Which is worse: ignorance or apathy?
Don't know. Don't care.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#ifdef __FreeBSD__
int
strverscmp(const char *s1, const char *s2);

int
strverscmp(const char *s1, const char *s2)
{
  static const char *digits = "0123456789";
  int ret, lz1, lz2;
  size_t p1, p2;

  p1 = strcspn(s1, digits);
  p2 = strcspn(s2, digits);
  while (p1 == p2 && s1[p1] != '\0' && s2[p2] != '\0') {
    /* Different prefix */
    if ((ret = strncmp(s1, s2, p1)) != 0)
      return ret;

    s1 += p1;
    s2 += p2;

    lz1 = lz2 = 0;
    if (*s1 == '0')
      lz1 = 1;
    if (*s2 == '0')
      lz2 = 1;
    
    if (lz1 > lz2)
      return -1;
    else if (lz1 < lz2)
      return 1;
    else if (lz1 == 1) {
      /*
       * If the common prefix for s1 and s2 consists only of zeros, then the
       * "longer" number has to compare less. Otherwise the comparison needs
       * to be numerical (just fallthrough). See
       * http://refspecs.freestandards.org/LSB_2.0.1/LSB-generic/
       *                                 LSB-generic/baselib-strverscmp.html
       */
      while (*s1 == '0' && *s2 == '0') {
        ++s1;
        ++s2;
      }

      p1 = strspn(s1, digits);
      p2 = strspn(s2, digits);

      /* Catch empty strings */
      if (p1 == 0 && p2 > 0)
        return 1;
      else if (p2 == 0 && p1 > 0)
        return -1;

      /* Prefixes are not same */
      if (*s1 != *s2 && *s1 != '0' && *s2 != '0') {
        if (p1 < p2)
          return 1;
        else if (p1 > p2)
          return -1;
      } else {
        if (p1 < p2)
          ret = strncmp(s1, s2, p1);
        else if (p1 > p2)
          ret = strncmp(s1, s2, p2);
        if (ret != 0)
          return ret;
      }
    }
    
    p1 = strspn(s1, digits);
    p2 = strspn(s2, digits);
    
    if (p1 < p2)
      return -1;
    else if (p1 > p2)
      return 1;
    else if ((ret = strncmp(s1, s2, p1)) != 0)
      return ret;

    /* Numbers are equal or not present, try with next ones. */
    s1 += p1;
    s2 += p2;
    p1 = strcspn(s1, digits);
    p2 = strcspn(s2, digits);
  }
  
  return strcmp(s1, s2);
}
#endif

int
main(int argc, char **argv)
{
  char **array, *temp;
  int i, j, n = 18;

  array = (char **) calloc(n, sizeof(char *));
  array[0] = strdup("10.jpg");
  array[1] = strdup("2.jpg");
  array[2] = strdup("1.jpg");
  array[3] = strdup("09.jpg");
  array[4] = strdup("1.012");
  array[5] = strdup("0010.jpg");
  array[6] = strdup("1.002");
  array[7] = strdup("1.01");
  array[8] = strdup("1.10");
  array[9] = strdup("1.01000");
  array[10] = strdup("00000009999999999999999999999999999999999999999999");
  array[11] = strdup("00000010000000000000000000000000000000000000000000");
  array[12] = strdup("9999999999999999999999999999999999999999999");
  array[13] = strdup("10000000000000000000000000000000000000000000");
  array[14] = strdup("1.010000");
  array[15] = strdup("1.09");
  array[17] = strdup("1.01020");
  array[16] = strdup("1.0102");

  /* Bubble sort */
#if 1
  for (i=0; i<n; ++i) {
    for (j=i; j<n; ++j) {
      if (strverscmp(array[i], array[j]) > 0) {
        temp = array[j];
        array[j] = array[i];
        array[i] = temp;
      }
    }
    printf("%s\n", array[i]);
  }
  printf("%2d = 0\n", strverscmp("a", "a"));
  printf("%2d > 0\n", strverscmp("alpha1", "alpha001"));
  printf("%2d > 0\n", strverscmp("part1_f012", "part1_f01"));
  printf("%2d < 0\n", strverscmp("item#99", "item#100"));
  printf("%2d < 0\n", strverscmp("foo.009", "foo.0"));
  printf("%2d < 0\n", strverscmp("0009", "09"));
#endif
  printf("%2d < 0\n", strverscmp("1.002", "1.01000"));
  printf("%2d < 0\n", strverscmp("1.01000", "1.0102"));
  printf("%2d < 0\n", strverscmp("1.002", "1.01"));
  printf("%2d < 0\n", strverscmp("1.012", "1.09"));
  printf("%2d < 0\n", strverscmp("1.01", "1.012"));

  return 0;
}

Attachment: pgpwA8JaW0pSQ.pgp
Description: PGP signature

Reply via email to