Main Page | Modules | Data Structures | File List | Data Fields | Globals

version-cmp.c

Go to the documentation of this file.
00001 
00011 /* $Progeny: version-cmp.c 3839 2003-11-17 04:25:01Z dsp $
00012  *
00013  * AUTHOR: John R. Daily <jdaily@progeny.com>
00014  *
00015  * Copyright 2002 Progeny Linux Systems, Inc.
00016  * Copyright 2002 Hewlett-Packard Company
00017  *
00018  * Permission is hereby granted, free of charge, to any person obtaining a
00019  * copy of this software and associated documentation files (the "Software"),
00020  * to deal in the Software without restriction, including without limitation
00021  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00022  * and/or sell copies of the Software, and to permit persons to whom the
00023  * Software is furnished to do so, subject to the following conditions:
00024  *
00025  * The above copyright notice and this permission notice shall be included in
00026  * all copies or substantial portions of the Software.
00027  *
00028  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00029  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00030  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00031  * THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00032  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00033  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00034  * DEALINGS IN THE SOFTWARE.
00035  */
00036 
00037 /*
00038  * This package should include a state machine diagram that
00039  * is important to understanding this code.
00040  */
00041 
00042 #include <config.h>
00043 
00044 #include <assert.h>
00045 #include <ctype.h>
00046 #include <stdio.h>
00047 #include <stdlib.h>
00048 #include <string.h>
00049 
00050 #include <discover.h>
00051 
00052 #include "utils.h"
00053 
00055 #define MAXBUFLEN 256
00056 
00058 typedef enum range_states {
00059     START,
00060     OPEN,
00061     VERSION1,
00062     VERSEP1,
00063     PAUSE1,
00064     SEPARATOR,
00065     I, N, F,
00066     VERSION2,
00067     VERSEP2,
00068     PAUSE2,
00069     CLOSE,
00070     ERROR
00071 }
00073 range_state;
00074 
00075 /*
00076  * For now, we only understand '.' as a separator, but that could
00077  * change.
00078  */
00079 static int
00080 is_versep_char(char c)
00081 {
00082     return c == '.';
00083 }
00084 
00085 /*
00086  * Wrapper for isdigit(), in case we want to add more legitimate
00087  * version characters later.
00088  */
00089 static int
00090 is_version_char(char c)
00091 {
00092     return isdigit(c);
00093 }
00094 
00095 /*
00096  * We only handle dot-separated integers. Anything else becomes
00097  * subject to interpretation.
00098  *
00099  * Return values:
00100  *   1 - v1 is higher
00101  *   0 - v2 is higher
00102  *  -1 - v1, v2 are equivalent
00103  *
00104  * The string "inf" represents an undefined upper bound; the equivalent on
00105  * the lower bound should be 0.
00106  */
00107 static int
00108 version_compare(const char *const v1, const char *const v2)
00109 {
00110     int int1, int2;
00111     char *word1, *word2;
00112     char *version1, *version2;
00113     char *buffer1;
00114     char *buffer2;
00115 
00116     if (strcmp(v1, v2) == 0) {
00117         return -1;
00118     }
00119 
00120     if (strcmp(v1, "inf") == 0) {
00121         return 1;
00122     }
00123 
00124     if (strcmp(v2, "inf") == 0) {
00125         return 0;
00126     }
00127 
00128     buffer1 = _discover_xmalloc(MAXBUFLEN);
00129     buffer2 = _discover_xmalloc(MAXBUFLEN);
00130 
00131     version1 = _discover_xstrdup(v1);
00132     version2 = _discover_xstrdup(v2);
00133 
00134     word1 = strtok_r(version1, ".", &buffer1);
00135     word2 = strtok_r(version2, ".", &buffer2);
00136 
00137     do {
00138         sscanf(word1, "%d", &int1);
00139         sscanf(word2, "%d", &int2);
00140 
00141         if (int1 != int2) {
00142             return int1 > int2;
00143         }
00144 
00145         word1 = strtok_r(NULL, ".", &buffer1);
00146         word2 = strtok_r(NULL, ".", &buffer2);
00147 
00148     } while(word1 != NULL && word2 != NULL);
00149 
00150     free(version1);
00151     free(version2);
00152 
00153     if (word2 == NULL && word1 == NULL) {
00154         return -1;
00155     }
00156     if (word2 == NULL) {
00157         return 1;
00158     }
00159     return -0;
00160 }
00161 
00162 
00163 static int
00164 determine_range(char * range_text, char **upper,
00165                 int *upper_inclusive, char **lower, int *lower_inclusive,
00166                 char **next)
00167 {
00168     char *current = range_text;
00169     char c;
00170     char last_char = '\0';
00171 
00172     char buffer1[MAXBUFLEN];
00173     char *bufptr1 = buffer1;
00174 
00175     char buffer2[MAXBUFLEN];
00176     char *bufptr2 = buffer2;
00177 
00178     range_state state = START;
00179     range_state last_good_state = START;
00180     int first_inclusive = -1, second_inclusive = -1;
00181 
00182     while(1) {
00183         c = *current;
00184 
00185         switch(state) {
00186         case START:
00187             if (isspace(c)) {
00188                 state = START;
00189             } else if (c == '(' || c == '[') {
00190                 state = OPEN;
00191             } else {
00192                 last_good_state = state;
00193                 state = ERROR;
00194             }
00195             break;
00196 
00197         case OPEN:
00198             if (last_char == '[') {
00199                 first_inclusive = 1;
00200             } else {
00201                 first_inclusive = 0;
00202             }
00203 
00204             if (isspace(c)) {
00205                 state = OPEN;
00206             } else if (is_version_char(c)) {
00207                 state = VERSION1;
00208             } else {
00209                 last_good_state = state;
00210                 state = ERROR;
00211             }
00212             break;
00213 
00214         case VERSION1:
00215             if (bufptr1 - buffer1 > 254) {
00216                 last_good_state = state;
00217                 state = ERROR;
00218                 break;
00219             }
00220 
00221             *bufptr1 = last_char;
00222             bufptr1++;
00223 
00224             if (isspace(c) || c == ',') {
00225                 *bufptr1 = '\0';
00226 
00227                 if (c == ',') {
00228                     state = SEPARATOR;
00229                 } else {
00230                     state = PAUSE1;
00231                 }
00232             } else if (is_version_char(c)) {
00233                 state = VERSION1;
00234             } else if (is_versep_char(c)) {
00235                 state = VERSEP1;
00236             } else {
00237                 last_good_state = VERSION1;
00238                 state = ERROR;
00239             }
00240             break;
00241 
00242         case VERSEP1:
00243             if (bufptr1 - buffer1 > 254) {
00244                 last_good_state = state;
00245                 state = ERROR;
00246                 break;
00247             }
00248 
00249             *bufptr1 = last_char;
00250             bufptr1++;
00251 
00252             if (is_version_char(c)) {
00253                 state = VERSION1;
00254             } else {
00255                 last_good_state = VERSION1;
00256                 state = ERROR;
00257             }
00258             break;
00259 
00260         case PAUSE1:
00261             if (isspace(c)) {
00262                 state = PAUSE1;
00263             } else if (c == ',') {
00264                 state = SEPARATOR;
00265             } else {
00266                 last_good_state = PAUSE1;
00267                 state = ERROR;
00268             }
00269             break;
00270 
00271         case SEPARATOR:
00272             if (isspace(c)) {
00273                 state = SEPARATOR;
00274             } else if (is_version_char(c)) {
00275                 state = VERSION2;
00276             } else if (c == 'i') {
00277                 state = I;
00278             } else {
00279                 last_good_state = SEPARATOR;
00280                 state = ERROR;
00281             }
00282             break;
00283 
00284         case I:
00285             if (c == 'n') {
00286                 state = N;
00287             } else {
00288                 last_good_state = state;
00289                 state = ERROR;
00290             }
00291             break;
00292 
00293         case N:
00294             if (c == 'f') {
00295                 state = F;
00296             } else {
00297                 last_good_state = state;
00298                 state = ERROR;
00299             }
00300             break;
00301 
00302         case F:
00303             strcpy(bufptr2, "inf");
00304             if (isspace(c)) {
00305                 state = PAUSE2;
00306             } else if (c == ')' || c == ']') {
00307                 state = CLOSE;
00308             } else {
00309                 last_good_state = state;
00310                 state = ERROR;
00311             }
00312             break;
00313 
00314         case VERSION2:
00315             if (bufptr2 - buffer2 > 254) {
00316                 last_good_state = state;
00317                 state = ERROR;
00318                 break;
00319             }
00320 
00321             *bufptr2 = last_char;
00322             bufptr2++;
00323 
00324             if (isspace(c) || c == ')' || c == ']') {
00325                 *bufptr2 = '\0';
00326 
00327                 if (c == ')' || c == ']') {
00328                     state = CLOSE;
00329                 } else {
00330                     state = PAUSE2;
00331                 }
00332             } else if (is_version_char(c)) {
00333                 state = VERSION2;
00334             } else if (is_versep_char(c)) {
00335                 state = VERSEP2;
00336             } else {
00337                 last_good_state = VERSION2;
00338                 state = ERROR;
00339             }
00340             break;
00341 
00342         case VERSEP2:
00343             if (bufptr2 - buffer2 > 254) {
00344                 last_good_state = state;
00345                 state = ERROR;
00346                 break;
00347             }
00348 
00349             *bufptr2 = last_char;
00350             bufptr2++;
00351 
00352             if (is_version_char(c)) {
00353                 state = VERSION2;
00354             } else {
00355                 last_good_state = VERSION2;
00356                 state = ERROR;
00357             }
00358             break;
00359 
00360         case PAUSE2:
00361             if (isspace(c)) {
00362                 state = PAUSE2;
00363             } else if (c == ')' || c == ']') {
00364                 state = CLOSE;
00365             } else {
00366                 last_good_state = PAUSE2;
00367                 state = ERROR;
00368             }
00369             break;
00370 
00371         case CLOSE:
00372             *next = current;
00373 
00374             if (last_char == ')') {
00375                 second_inclusive = 0;
00376             } else if (last_char == ']') {
00377                 second_inclusive = 1;
00378             }
00379 
00380             if (version_compare(buffer1, buffer2) == 1) {
00381                 *upper = _discover_xmalloc(bufptr1 - buffer1 + 1);
00382                 strcpy(*upper, buffer1);
00383                 *upper_inclusive = first_inclusive;
00384 
00385                 *lower = _discover_xmalloc(bufptr2 - buffer2 + 1);
00386                 strcpy(*lower, buffer2);
00387                 *lower_inclusive = second_inclusive;
00388             } else {
00389                 *lower = _discover_xmalloc(bufptr1 - buffer1 + 1);
00390                 strcpy(*lower, buffer1);
00391                 *lower_inclusive = first_inclusive;
00392 
00393                 *upper = _discover_xmalloc(bufptr2 - buffer2 + 1);
00394                 strcpy(*upper, buffer2);
00395                 *upper_inclusive = second_inclusive;
00396             }
00397 
00398             /* EXIT POINT */
00399             return 1;
00400 
00401         default:
00402             state = ERROR;
00403             last_good_state = -1;
00404             break;
00405         }
00406 
00407         if (state == ERROR) {
00408             /* Do something. */
00409             return 0;
00410         }
00411 
00412         /*
00413          * We don't care about recording spaces; let's simplify by not
00414          * doing so, in case we'd be missing something valuable.
00415          */
00416         if (!isspace(*current)) {
00417             last_char = *current;
00418         }
00419 
00420         current++;
00421     }
00422 }
00423 
00437 int
00438 discover_xml_version_cmp(char *range, char *version, discover_error_t *status)
00439 {
00440     char *upper, *lower;
00441     int upper_inclusive, lower_inclusive;
00442     char *next_range;
00443     int range_count = 0;
00444 
00445     assert(range != NULL);
00446     assert(version != NULL);
00447     assert(status != NULL);
00448 
00449     next_range = range;
00450 
00451     while(*next_range != '\0') {
00452         range_count++;
00453         if (determine_range(next_range, &upper, &upper_inclusive,
00454                             &lower, &lower_inclusive, &next_range)) {
00455             /*
00456              * First test: Does this version match the upper end of the
00457              * range exactly? If so, is the upper end of the range
00458              * inclusive?
00459              *
00460              * Second test: Does this version match the lower end of the
00461              * range? If so, is the lower end inclusive?
00462              *
00463              * Finally, is the version higher than the lower end, and lower
00464              * then the upper end?
00465              */
00466             if (version_compare(version, upper) == -1
00467                 && upper_inclusive) {
00468                 return range_count;
00469             } else if (version_compare(version, lower) == -1
00470                        && lower_inclusive) {
00471                 return range_count;
00472             } else if (version_compare(version, lower) > 0 &&
00473                        version_compare(upper, version) > 0) {
00474                 return range_count;
00475             }
00476 
00477         } else {
00478             status->code = DISCOVER_EBADVERSION;
00479             return -1;
00480         }
00481     }
00482 
00483     return 0;
00484 }
00485 

Generated on Sat Jan 31 14:39:17 2004 for discover by doxygen 1.3.4