5 kx
5 kx /**********************************************************************
5 kx
5 kx Copyright 2019 Andrey V.Kosteltsev
5 kx
9 kx Licensed under the Radix cross Linux License, Version 1.0 .
5 kx you may not use this file except in compliance with the License.
5 kx You may obtain a copy of the License at
5 kx
9 kx https://radix-linux.su/licenses/LICENSE-1.0-en_US.txt
5 kx
5 kx Unless required by applicable law or agreed to in writing, software
5 kx distributed under the License is distributed on an "AS IS" BASIS,
5 kx WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
5 kx implied.
5 kx
5 kx **********************************************************************/
5 kx
5 kx #include <config.h>
5 kx
5 kx #include <stdint.h>
5 kx #include <string.h>
5 kx #include <ctype.h>
5 kx
5 kx /* states: S_N: normal, S_I: comparing integral part, S_F: comparing
5 kx fractionnal parts, S_Z: idem but with leading Zeroes only */
5 kx #define S_N 0x0
5 kx #define S_I 0x3
5 kx #define S_F 0x6
5 kx #define S_Z 0x9
5 kx
5 kx /* result_type: CMP: return diff; LEN: compare using len_diff/diff */
5 kx #define CMP 2
5 kx #define LEN 3
5 kx
5 kx
5 kx /* Compare S1 and S2 as strings holding indices/version numbers,
5 kx returning less than, equal to or greater than zero if S1 is less than,
5 kx equal to or greater than S2 (for more info, see the texinfo doc).
5 kx */
5 kx
5 kx int cmp_version( const char *s1, const char *s2 )
5 kx {
5 kx const unsigned char *p1 = (const unsigned char *)s1;
5 kx const unsigned char *p2 = (const unsigned char *)s2;
5 kx
5 kx /* Symbol(s) 0 [1-9] others
5 kx Transition (10) 0 (01) d (00) x */
5 kx static const uint8_t next_state[] =
5 kx {
5 kx /* state x d 0 */
5 kx /* S_N */ S_N, S_I, S_Z,
5 kx /* S_I */ S_N, S_I, S_I,
5 kx /* S_F */ S_N, S_F, S_F,
5 kx /* S_Z */ S_N, S_F, S_Z
5 kx };
5 kx
5 kx static const int8_t result_type[] =
5 kx {
5 kx /* state x/x x/d x/0 d/x d/d d/0 0/x 0/d 0/0 */
5 kx
5 kx /* S_N */ CMP, CMP, CMP, CMP, LEN, CMP, CMP, CMP, CMP,
5 kx /* S_I */ CMP, -1, -1, +1, LEN, LEN, +1, LEN, LEN,
5 kx /* S_F */ CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
5 kx /* S_Z */ CMP, +1, +1, -1, CMP, CMP, -1, CMP, CMP
5 kx };
5 kx
5 kx if( p1 == p2 ) return 0;
5 kx
5 kx unsigned char c1 = *p1++;
5 kx unsigned char c2 = *p2++;
5 kx /* Hint: '0' is a digit too. */
5 kx int state = S_N + ((c1 == '0') + (isdigit (c1) != 0));
5 kx
5 kx int diff;
5 kx while( (diff = c1 - c2) == 0 )
5 kx {
5 kx if( c1 == '\0' ) return diff;
5 kx
5 kx state = next_state[state];
5 kx c1 = *p1++;
5 kx c2 = *p2++;
5 kx state += (c1 == '0') + (isdigit (c1) != 0);
5 kx }
5 kx
5 kx state = result_type[state * 3 + (((c2 == '0') + (isdigit (c2) != 0)))];
5 kx
5 kx switch (state)
5 kx {
5 kx case CMP:
5 kx return diff;
5 kx
5 kx case LEN:
5 kx while( isdigit (*p1++) )
5 kx if( !isdigit (*p2++) )
5 kx return 1;
5 kx
5 kx return isdigit (*p2) ? -1 : diff;
5 kx
5 kx default:
5 kx return state;
5 kx }
5 kx }
5 kx
5 kx
5 kx const char *max_version( const char *s1, const char *s2 )
5 kx {
5 kx if( cmp_version( s1, s2 ) < 0 ) return s2;
5 kx else return s1;
5 kx }