--- net-tools-1.60/lib/nstrcmp.c~ 1999-01-09 18:55:20 +0300 +++ net-tools-1.60/lib/nstrcmp.c 2002-07-03 01:35:04 +0400 @@ -1,34 +1,157 @@ -/* Copyright 1998 by Andi Kleen. Subject to the GPL. */ -/* $Id: nstrcmp.c,v 1.2 1998/11/15 20:11:38 freitag Exp $ */ +/* + compare alpha and numeric segments of two strings. + + Copyright (c) 1998 by Red Hat Software, Inc. + Copyright (c) 2002 by Dmitry V. Levin + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include #include #include -#include "util.h" -/* like strcmp(), but knows about numbers */ -int nstrcmp(const char *astr, const char *b) +/* + * compares alpha and numeric segments of two strings. + * returns an integer less than, equal to, or greater + * than zero if a is found, respectively, to be less + * than, to match, or be greater than b. +*/ +int +nstrcmp (const char *a, const char *b) { - const char *a = astr; + char *str1, *str2; + char *one, *two; - while (*a == *b) { - if (*a == '\0') - return 0; - a++; - b++; - } - if (isdigit(*a)) { - if (!isdigit(*b)) - return -1; - while (a > astr) { - a--; - if (!isdigit(*a)) { - a++; - break; - } - if (!isdigit(*b)) - return -1; - b--; + /* easy comparison to see if versions are identical */ + if (!strcmp (a, b)) + return 0; + + str1 = alloca (strlen (a) + 1); + str2 = alloca (strlen (b) + 1); + + strcpy (str1, a); + strcpy (str2, b); + + one = str1; + two = str2; + + /* loop through each version segment of str1 and str2 and compare them */ + while (*one && *two) + { + int isalnum1 = isalnum (*one); + int isalnum2 = isalnum (*two); + int isnum, rc; + char oldch1, oldch2; + + if (*one == *two) + { + if (!isalnum1) + { + ++one; + ++two; + continue; + } + } else + { + if (!(isalnum1 && isalnum2)) + return *one - *two; + } + + str1 = one; + str2 = two; + + /* grab first completely alpha or completely numeric segment */ + /* leave one and two pointing to the start of the alpha or numeric */ + /* segment and walk str1 and str2 to end of segment */ + /* Also take care of the case where the two version segments are */ + /* different types: one numeric and one alpha */ + if (isdigit (*str1)) + { + if (isalpha (*str2)) + return -1; + while (*str1 && isdigit (*str1)) + str1++; + while (*str2 && isdigit (*str2)) + str2++; + isnum = 1; + } else + { + while (*str1 && isalpha (*str1)) + str1++; + while (*str2 && isalpha (*str2)) + str2++; + isnum = 0; + } + + /* Again, take care of the case where the two version segments are */ + /* different types: one numeric and one alpha */ + if (one == str1) + return -1; + if (two == str2) + return 1; + + /* save character at the end of the alpha or numeric segment */ + /* so that they can be restored after the comparison */ + oldch1 = *str1; + *str1 = '\0'; + oldch2 = *str2; + *str2 = '\0'; + + if (isnum) + { + /* this used to be done by converting the digit segments */ + /* to ints using atoi() - it's changed because long */ + /* digit segments can overflow an int - this should fix that. */ + + /* throw away any leading zeros - it's a number, right? */ + while (*one == '0') + one++; + while (*two == '0') + two++; + + /* whichever number has more digits wins */ + if (strlen (one) > strlen (two)) + return 1; + if (strlen (two) > strlen (one)) + return -1; + } + + /* strcmp will return which one is greater - even if the two */ + /* segments are alpha or if they are numeric. don't return */ + /* if they are equal because there might be more segments to */ + /* compare */ + rc = strcmp (one, two); + if (rc) + return rc; + + /* restore character that was replaced by null above */ + *str1 = oldch1; + one = str1; + *str2 = oldch2; + two = str2; } - return atoi(a) > atoi(b) ? 1 : -1; - } - return *a - *b; + + /* this catches the case where all numeric and alpha segments have */ + /* compared identically but the segment sepparating characters were */ + /* different */ + if (!*one && !*two) + return 0; + + /* whichever version still has characters left over wins */ + if (!*one) + return -1; + else + return 1; }