# version-cmp.c

Go to the documentation of this file.

    00001
    00011 /* $Progeny$
    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/discover.h>
    00051
    00052 #include <discover/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