summaryrefslogtreecommitdiffstats
path: root/contrib/libs/libidn/strverscmp.c
blob: 78e6750c6fb17ecd21ad94dcec7150604074fdf6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/* Compare strings while treating digits characters numerically. 
   Copyright (C) 1997, 2000, 2002, 2004, 2006 Free Software Foundation, Inc. 
   This file is part of the GNU C Library. 
   Contributed by Jean-François Bignolles <[email protected]>, 1997. 
 
   This program 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 2.1, 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 Lesser General Public License for more details. 
 
   You should have received a copy of the GNU Lesser General Public License along 
   with this program; if not, write to the Free Software Foundation, 
   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */ 
 
#if !_LIBC 
# include "idn_config.h"
#endif 
 
#include <string.h> 
#include <ctype.h> 
 
/* states: S_N: normal, S_I: comparing integral part, S_F: comparing 
           fractional parts, S_Z: idem but with leading Zeroes only */ 
#define S_N    0x0 
#define S_I    0x4 
#define S_F    0x8 
#define S_Z    0xC 
 
/* result_type: CMP: return diff; LEN: compare using len_diff/diff */ 
#define CMP    2 
#define LEN    3 
 
 
/* ISDIGIT differs from isdigit, as follows: 
   - Its arg may be any int or unsigned int; it need not be an unsigned char 
     or EOF. 
   - It's typically faster. 
   POSIX 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 int) (c) - '0' <= 9) 
 
#undef __strverscmp 
#undef strverscmp 
 
#ifndef weak_alias 
# define __strverscmp strverscmp 
#endif 
 
/* 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; 
  unsigned char c1, c2; 
  int state; 
  int diff; 
 
  /* Symbol(s)    0       [1-9]   others  (padding) 
     Transition   (10) 0  (01) d  (00) x  (11) -   */ 
  static const unsigned int next_state[] = 
  { 
      /* state    x    d    0    - */ 
      /* S_N */  S_N, S_I, S_Z, S_N, 
      /* S_I */  S_N, S_I, S_I, S_I, 
      /* S_F */  S_N, S_F, S_F, S_F, 
      /* S_Z */  S_N, S_F, S_Z, S_Z 
  }; 
 
  static const int result_type[] = 
  { 
      /* state   x/x  x/d  x/0  x/-  d/x  d/d  d/0  d/- 
                 0/x  0/d  0/0  0/-  -/x  -/d  -/0  -/- */ 
 
      /* S_N */  CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, 
                 CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, 
      /* S_I */  CMP, -1,  -1,  CMP,  1,  LEN, LEN, CMP, 
                  1,  LEN, LEN, CMP, CMP, CMP, CMP, CMP, 
      /* S_F */  CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP, 
                 CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, 
      /* S_Z */  CMP,  1,   1,  CMP, -1,  CMP, CMP, CMP, 
                 -1,  CMP, CMP, CMP 
  }; 
 
  if (p1 == p2) 
    return 0; 
 
  c1 = *p1++; 
  c2 = *p2++; 
  /* Hint: '0' is a digit too.  */ 
  state = S_N | ((c1 == '0') + (ISDIGIT (c1) != 0)); 
 
  while ((diff = c1 - c2) == 0 && c1 != '\0') 
    { 
      state = next_state[state]; 
      c1 = *p1++; 
      c2 = *p2++; 
      state |= (c1 == '0') + (ISDIGIT (c1) != 0); 
    } 
 
  state = result_type[state << 2 | ((c2 == '0') + (ISDIGIT (c2) != 0))]; 
 
  switch (state) 
    { 
    case CMP: 
      return diff; 
 
    case LEN: 
      while (ISDIGIT (*p1++)) 
	if (!ISDIGIT (*p2++)) 
	  return 1; 
 
      return ISDIGIT (*p2) ? -1 : diff; 
 
    default: 
      return state; 
    } 
} 
#ifdef weak_alias 
weak_alias (__strverscmp, strverscmp) 
#endif