aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/bison/gnulib/src/strverscmp.c
blob: ec0066af27c2d549015c32202ac5224e7fbe47af (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
130
131
/* Compare strings while treating digits characters numerically. 
   Copyright (C) 1997, 2000, 2002, 2004, 2006, 2009-2013 Free Software 
   Foundation, Inc. 
   This file is part of the GNU C Library. 
   Contributed by Jean-François Bignolles <bignolle@ecoledoc.ibp.fr>, 1997. 
 
   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 3, 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, see <http://www.gnu.org/licenses/>.  */ 
 
#if !_LIBC 
# include <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