Mercurial > gnulib
view lib/strverscmp.c @ 2563:456c0b36a3c5 karl
.
author | Jim Meyering <jim@meyering.net> |
---|---|
date | Sat, 03 Jun 2000 08:36:49 +0000 |
parents | 9b7ce618e1fb |
children |
line wrap: on
line source
/* Compare strings while treating digits characters numerically. Copyright (C) 1997, 2000 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #if HAVE_CONFIG_H # include <config.h> #endif /* ISDIGIT differs from isdigit, as follows: - Its arg may be any int or unsigned int; it need not be an unsigned char. - It's guaranteed to evaluate its argument exactly once. - It's typically faster. Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that only '0' through '9' are digits. Prefer ISDIGIT to isdigit unless it's important to use the locale's definition of `digit' even when the host does not conform to Posix. */ #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) /* Compare S1 and S2 as strings holding indices/version numbers, returning less than, equal to or greater than zero if S1 is less than, equal to or greater than S2 (for more info, see the texinfo doc). */ int strverscmp (const char *s1, const char *s2) { const unsigned char *p1 = (const unsigned char *) s1; const unsigned char *p2 = (const unsigned char *) s2; int diff; while (*p1 != '\0' && *p1 == *p2) { ++p1; ++p2; } diff = *p1 - *p2; if (ISDIGIT (*p1) || ISDIGIT (*p2)) { while (ISDIGIT (*p1++)) if (!ISDIGIT (*p2++)) return 1; if (ISDIGIT (*p2)) return -1; } return diff; } #ifdef TESTING_STRVERSCMP # include <stdio.h> /* BEGIN-DATA 1.010 1.01 1 # 10 > 1; yes, these are non-intuitive, but see below. 1.010.tgz 1.01.tgz 1 # 10 > 1 1.007 1.01a 1 # 7 > 1 1.51a 1.507 -1 # 51 < 507 1.001 1.2 -1 # 1 < 2 foo-1.tar foo-1.2.tar -1 END-DATA Justification from Karl Heuer: Yes, it's possible that 1.007 was meant to sort earlier than 1.01a, but anyone who's using that style is also going to want 1.507 to sort earlier than 1.51a, and I think that making opposite assumptions for these two examples is just asking for trouble. People who want their version numbers to be sorted "floating-point style" should simply make the digits strings have fixed width; 1.007 < 1.010a unambiguously.) Run this test like this: gcc -DTESTING_STRVERSCMP -Wall strverscmp.c sed -n '/^BEGIN-DATA/,/END-DATA/p' strverscmp.c|grep -v -e -DATA | ./a.out */ # define MAX_BUFF_LEN 1024 # define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) int main () { char buff[MAX_BUFF_LEN + 1]; int fail = 0; buff[MAX_BUFF_LEN] = 0; while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0]) { char v1[MAX_BUFF_LEN], v2[MAX_BUFF_LEN], result[MAX_BUFF_LEN]; int r_expected, r_actual; sscanf (buff, "%s %s %s", v1, v2, result); r_expected = atoi (result); if (!(STREQ (result, "1") || STREQ (result, "-1") || STREQ (result, "0"))) abort (); r_actual = strverscmp (v1, v2); if (r_expected != r_actual) { printf ("FAIL: %s %s %s (actual: %d)\n", v1, v2, result, r_actual); fail = 1; } } exit (fail); } #endif