From ed3814abca56225d5dfe36acfd54e5e5323b8cbd Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Mon, 24 Jun 2013 19:11:46 +0200 Subject: [PATCH] Add strverscmp(3). --- libc/Makefile | 1 + libc/include/string.h | 7 ++- libc/string/strverscmp.cpp | 115 +++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 libc/string/strverscmp.cpp diff --git a/libc/Makefile b/libc/Makefile index cd2b4fe2..f8f5228a 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -122,6 +122,7 @@ string/strspn.o \ string/strstr.o \ string/strtok.o \ string/strtok_r.o \ +string/strverscmp.o \ string/strxfrm.o \ strtod.o \ strtof.o \ diff --git a/libc/include/string.h b/libc/include/string.h index 70004735..eaba8809 100644 --- a/libc/include/string.h +++ b/libc/include/string.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013. This file is part of the Sortix C Library. @@ -22,8 +22,8 @@ *******************************************************************************/ -#ifndef _STRING_H -#define _STRING_H 1 +#ifndef INCLUDE_STRING_H +#define INCLUDE_STRING_H #include #include @@ -61,6 +61,7 @@ size_t strspn(const char*, const char*); char* strstr(const char*, const char*); char* strtok(char* __restrict, const char* __restrict); char* strtok_r(char* __restrict, const char* __restrict, char** __restrict); +int strverscmp(const char*, const char*); size_t strxfrm(char* __restrict, const char* __restrict, size_t); /* TODO: These are not implemented in sortix libc yet. */ diff --git a/libc/string/strverscmp.cpp b/libc/string/strverscmp.cpp new file mode 100644 index 00000000..bd0b0f59 --- /dev/null +++ b/libc/string/strverscmp.cpp @@ -0,0 +1,115 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix 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 Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + string/strverscmp.cpp + Compares two version strings. + +*******************************************************************************/ + +#include + +static bool is_number(char c) +{ + return '0' <= c && c <= '9'; +} + +static int to_number(char c) +{ + return c - '0'; +} + +extern "C" int strverscmp(const char* a, const char* b) +{ + for ( size_t i = 0; true; i++ ) + { + // Be a regular strcmp if the strings are equal. + if ( a[i] == '\0' && b[i] == '\0' ) + return 0; + if ( a[i] == b[i] ) + continue; + + // Be a regular strcmp if no digits are involed when they differ. + bool version_string = is_number(a[i]) && is_number(b[i]); + if ( !version_string && a[i] < b[i] ) + return -1; + if ( !version_string && a[i] > b[i] ) + return 1; + + // Because the number of leading zeroes matter, we have to find the + // entire numeric block we are currently within. We know the strings are + // equal until i, so we can simply find the number of shared digits by + // looking in the first string. + size_t digits_start = i; + while ( digits_start && is_number(a[digits_start-1]) ) + digits_start--; + size_t shared_digits = i - digits_start; + + // Find the number of shared leading zeroes. + size_t shared_zeroes = 0; + while ( shared_zeroes < shared_digits && + to_number(a[digits_start + shared_zeroes]) == 0 ) + shared_zeroes++; + + // Try to expand the leading zeroes amount into a. + size_t a_zeroes = shared_zeroes; + while ( is_number(a[digits_start + a_zeroes]) == 0 ) + a_zeroes++; + + // Try to expand the leading zeroes amount into b. + size_t b_zeroes = shared_zeroes; + while ( is_number(b[digits_start + b_zeroes]) == 0 ) + b_zeroes++; + + // We treat strings with leading zeroes as if they have a decimal point + // in front of them, so strings with more zeroes sort lower. + if ( a_zeroes > b_zeroes ) + return -1; + if ( b_zeroes > a_zeroes ) + return 1; + + // Find the number of consecutive digits in a where the strings differ. + size_t a_digits = a_zeroes; + while ( is_number(a[digits_start + a_digits]) ) + a_digits++; + + // Find the number of consecutive digits in b where the strings differ. + size_t b_digits = b_zeroes; + while ( is_number(b[digits_start + b_digits]) ) + b_digits++; + + // We know the strings have the same amount of leading zeroes, so we + // so if a a block is longer than the other, then the value must be + // longer as well. + if ( a_digits < b_digits ) + return -1; + if ( b_digits < a_digits ) + return 1; + + // Finally run through the strings from where they differ and sort them + // numerically. We know this terminates because the strings differ. The + // strings have the same amount of digits, so comparing them is easy. + for ( size_t n = shared_zeroes; true; n++ ) + { + if ( a[digits_start + n] < b[digits_start + n] ) + return -1; + if ( b[digits_start + n] < a[digits_start + n] ) + return 1; + } + } +}