OSDN Git Service

Interim work-around for MinGW-Bug #3520864.
[mingw/mingw-get.git] / src / vercmp.cpp
1 #include <stdio.h>
2 /*
3  * vercmp.cpp
4  *
5  * $Id$
6  *
7  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
8  * Copyright (C) 2009, 2010, 2011, MinGW Project
9  *
10  *
11  * Implementation of package version comparator module.
12  *
13  *
14  * This is free software.  Permission is granted to copy, modify and
15  * redistribute this software, under the provisions of the GNU General
16  * Public License, Version 3, (or, at your option, any later version),
17  * as published by the Free Software Foundation; see the file COPYING
18  * for licensing details.
19  *
20  * Note, in particular, that this software is provided "as is", in the
21  * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
22  * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
23  * PARTICULAR PURPOSE.  Under no circumstances will the author, or the
24  * MinGW Project, accept liability for any damages, however caused,
25  * arising from the use of this software.
26  *
27  */
28 #include "vercmp.h"
29 #include <string.h>
30
31 void pkgVersionInfo::Parse( const char* version, const char* build )
32 {
33   /* Delegated constructor/reconstructor implementation...
34    * Decompose given version number and build serial number strings,
35    * storing components within the specified class structure.
36    *
37    * Note that the strings to be parsed are invariant, (and it is
38    * necessary that they be so), but we need to create modifiable
39    * copies to facilitate decomposition...
40    */
41   char *wildcard = build_string = NULL;
42   char *p = version_string = strdup( version ? version : "" );
43
44   /* Walking over all version number constituent elements...
45    */
46   for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ )
47   {
48     /* ...initialise default element value to zero.
49      */
50     unsigned long value = 0L;
51
52     /* When appropriate...
53      */
54     if( (index == SNAPSHOT_DATE) && (*p == '\0') && (build != NULL) )
55       /*
56        * ...select second argument for parsing.
57        */
58       p = build_string = strdup( build );
59
60     /* When parsing an explicitly specified numeric argument...
61      */
62     while( (*p >= '0') && ((*p - '0') < 10) )
63     {
64       /* ...accumulate its ultimate value, and clear any prior
65        * "wildcard" matching request which may have been carried
66        * forward from the preceding field specification.
67        */
68       value = *p++ - '0' + 10 * value;
69       wildcard = NULL;
70     }
71
72     /* Store it, note the presence of any suffix, and establish
73      * the control state for a possible "wildcard" match.
74      */
75     version_elements[index].value = value;
76     version_elements[index].suffix = p;
77     if( (value == 0L) && (*p == '*') )
78       wildcard = p++;
79
80     /* Skip forward to next element field delimiter, clearing any
81      * active "wildcard" matching request, if the "suffix" doesn't
82      * represent a pure "wildcard" designator.
83      */
84     while( *p && (*p != '.') && (*p != '-') )
85       if( *p++ != '*' ) wildcard = NULL;
86
87     /* Evaluate the current field delimiter, to identify the type
88      * of the following field (if any)...
89      */
90     if( (*p == '-') || ((*p == '\0') && (build != NULL)) )
91       /*
92        * ...and, if we hit the end of the version number,
93        * before we filled out all of its possible elements,
94        * then zero the remainder, (while preserving "wildcard"
95        * matching state), before we progress to capture the
96        * build serial number.
97        */
98       while( index < VERSION_PATCHLEVEL )
99       {
100         version_elements[++index].value = 0L;
101         version_elements[index].suffix = wildcard ? wildcard : p;
102       }
103
104     /* If "wildcard" matching is in the active state, by the time
105      * we get to here, then it may have been activated for this field,
106      * or it may have been passed forward from a preceding field...
107      */
108     if( wildcard )
109       /*
110        * ...we don't know which applies, so we unconditionally adjust
111        * the "suffix" pointer, to ensure that the state is recorded.
112        */
113       version_elements[index].suffix = wildcard;
114
115     /* Step over any delimiter, which demarcates the current
116      * version number or build serial number element field, while
117      * ensuring that the decomposed field is properly terminated.
118      */
119     if( *p ) *p++ = '\0';
120   }
121 }
122
123 long pkgVersionInfo::Compare( const pkgVersionInfo& rhs, int index )
124 {
125   /* Compare a given element of a package version specification with the
126    * corresponding element of a reference (rhs) version specification; return
127    * <0L, 0L or >0L for less than, equal to or greater than rhs respectively.
128    */
129   long cmpval;
130   if( (cmpval = rhs.version_elements[index].value) == 0L )
131   {
132     /* In the special case where the reference value is zero...
133      */
134     const char *p = rhs.version_elements[index].suffix;
135     if( (p != NULL) && (p[0] == '*') && (p[1] == '\0') )
136     {
137       /* ...and where it has an explicit suffix which is identically
138        * equal to the string "*", then it represents a "wildcard" match,
139        * which unconditionally matches everything as "equal".
140        */
141       return 0L;
142     }
143   }
144
145   /* When we didn't match a "wildcard"...
146    * we fall through to here, and proceed with a normal comparison.
147    */
148   if( (cmpval = version_elements[index].value - cmpval) == 0L )
149   {
150     /* The specified element values are identically equal;
151      * discriminate on suffixes, if any...
152      */
153     const char *p = version_elements[index].suffix;
154     const char *r = rhs.version_elements[index].suffix;
155
156     /* Check that both version specifications include a suffix;
157      * ( p == r implies both are NULL, hence neither has a suffix );
158      * if only one has, then that is the greater...
159      */
160     if( p == r ) return 0L;
161     if( p == NULL ) return 1L;
162     if( r == NULL ) return -1L;
163
164     /* Both DO have suffixes...
165      */
166     while( *p && *r && (*p == *r) && (*p != '.') && (*p != '-') )
167     {
168       /* ...scan both, until we find a mismatched character,
169        * or the terminal delimiter for either.
170        */
171       ++p; ++r;
172     }
173
174     /* Compute return value based on difference between the
175      * mismatched characters, representing delimiters as NUL.
176      */
177     cmpval = (*p && (*p != '.') && (*p != '-')) ? (long)(*p) : 0L;
178     cmpval -= (*r && (*r != '.') && (*r != '-')) ? (long)(*r) : 0L;
179   }
180   return cmpval;
181 }
182
183 bool pkgVersionInfo::operator<( const pkgVersionInfo& rhs )
184 {
185   /* Comparison operator...
186    * Does the version number under consideration represent a less
187    * recent release than the specified reference version number.
188    */
189   long cmp;
190   for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ )
191     if( (cmp = Compare( rhs, index )) != 0L ) return (cmp < 0L);
192
193   /* If we get to here, lhs and rhs versions are identically equal,
194    * and hence fail the lhs < rhs comparison.
195    */
196   return false;
197 }
198
199 bool pkgVersionInfo::operator<=( const pkgVersionInfo& rhs )
200 {
201   /* Comparison operator...
202    * Does the version number under consideration represent the same, or
203    * a less recent release than the specified reference version number.
204    */
205   long cmp;
206   for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ )
207     if( (cmp = Compare( rhs, index )) != 0L ) return (cmp < 0L);
208
209   /* If we get to here, lhs and rhs versions are identically equal,
210    * and hence satisfy the lhs <= rhs comparison.
211    */
212   return true;
213 }
214
215 bool pkgVersionInfo::operator==( const pkgVersionInfo& rhs )
216 {
217   /* Comparison operator...
218    * Does the version number under consideration exactly match
219    * the specified reference version number.
220    */
221   for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ )
222     if( Compare( rhs, index ) != 0L ) return false;
223
224   /* If we get to here, lhs and rhs versions are identically equal,
225    * which is what we require to satisfy the lhs == rhs comparison.
226    */
227   return true;
228 }
229
230 bool pkgVersionInfo::operator!=( const pkgVersionInfo& rhs )
231 {
232   /* Comparison operator...
233    * Does the version number under consideration differ from
234    * the specified reference version number.
235    */
236   for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ )
237     if( Compare( rhs, index ) != 0L ) return true;
238
239   /* If we get to here, lhs and rhs versions are identically equal,
240    * which is the sole condition to fail the lhs != rhs comparison.
241    */
242   return false;
243 }
244
245 bool pkgVersionInfo::operator>=( const pkgVersionInfo& rhs )
246 {
247   /* Comparison operator...
248    * Does the version number under consideration represent the same, or
249    * a more recent release than the specified reference version number.
250    */
251   long cmp;
252   for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ )
253     if( (cmp = Compare( rhs, index )) != 0L ) return (cmp > 0L);
254
255   /* If we get to here, lhs and rhs versions are identically equal,
256    * and hence satisfy the lhs >= rhs comparison.
257    */
258   return true;
259 }
260
261 bool pkgVersionInfo::operator>( const pkgVersionInfo& rhs )
262 {
263   /* Comparison operator...
264    * Does the version number under consideration represent a more
265    * recent release than the specified reference version number.
266    */
267   long cmp;
268   for( int index = VERSION_MAJOR; index < VERSION_ELEMENT_COUNT; index++ )
269     if( (cmp = Compare( rhs, index )) != 0L ) return (cmp > 0L);
270
271   /* If we get to here, lhs and rhs versions are identically equal,
272    * and hence fail the lhs > rhs comparison.
273    */
274   return false;
275 }
276
277 /* $RCSfile$: end of file */