Branch data Line data Source code
1 : : /* 2 : : * libdpkg - Debian packaging suite library routines 3 : : * version.c - version handling functions 4 : : * 5 : : * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> 6 : : * Copyright © 2008-2014 Guillem Jover <guillem@debian.org> 7 : : * 8 : : * This is free software; you can redistribute it and/or modify 9 : : * it under the terms of the GNU General Public License as published by 10 : : * the Free Software Foundation; either version 2 of the License, or 11 : : * (at your option) any later version. 12 : : * 13 : : * This is distributed in the hope that it will be useful, 14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 : : * GNU General Public License for more details. 17 : : * 18 : : * You should have received a copy of the GNU General Public License 19 : : * along with this program. If not, see <https://www.gnu.org/licenses/>. 20 : : */ 21 : : 22 : : #include <config.h> 23 : : #include <compat.h> 24 : : 25 : : #include <dpkg/c-ctype.h> 26 : : #include <dpkg/ehandle.h> 27 : : #include <dpkg/string.h> 28 : : #include <dpkg/version.h> 29 : : 30 : : /** 31 : : * Turn the passed version into an empty version. 32 : : * 33 : : * This can be used to ensure the version is properly initialized. 34 : : * 35 : : * @param version The version to clear. 36 : : */ 37 : : void 38 : 325 : dpkg_version_blank(struct dpkg_version *version) 39 : : { 40 : 325 : version->epoch = 0; 41 : 325 : version->version = NULL; 42 : 325 : version->revision = NULL; 43 : 325 : } 44 : : 45 : : /** 46 : : * Test if a version is not empty. 47 : : * 48 : : * @param version The version to test. 49 : : * 50 : : * @retval true If the version is informative (i.e. not an empty version). 51 : : * @retval false If the version is empty. 52 : : */ 53 : : bool 54 : 10 : dpkg_version_is_informative(const struct dpkg_version *version) 55 : : { 56 [ + + ]: 9 : return (version->epoch || 57 [ + + + + ]: 19 : str_is_set(version->version) || 58 : 5 : str_is_set(version->revision)); 59 : : } 60 : : 61 : : /** 62 : : * Give a weight to the character to order in the version comparison. 63 : : * 64 : : * @param c An ASCII character. 65 : : */ 66 : : static int 67 : 64 : order(int c) 68 : : { 69 [ - + ]: 64 : if (c_isdigit(c)) 70 : 0 : return 0; 71 [ + + ]: 64 : else if (c_isalpha(c)) 72 : 24 : return c; 73 [ + + ]: 40 : else if (c == '~') 74 : 4 : return -1; 75 [ + - ]: 36 : else if (c) 76 : 36 : return c + 256; 77 : : else 78 : 0 : return 0; 79 : : } 80 : : 81 : : static int 82 : 71 : verrevcmp(const char *a, const char *b) 83 : : { 84 [ + + ]: 71 : if (a == NULL) 85 : 2 : a = ""; 86 [ + + ]: 71 : if (b == NULL) 87 : 2 : b = ""; 88 : : 89 [ + + - + ]: 134 : while (*a || *b) { 90 : 79 : int first_diff = 0; 91 : : 92 [ + + + + : 107 : while ((*a && !c_isdigit(*a)) || (*b && !c_isdigit(*b))) { + + - + ] 93 : 32 : int ac = order(*a); 94 : 32 : int bc = order(*b); 95 : : 96 [ + + ]: 32 : if (ac != bc) 97 : 4 : return ac - bc; 98 : : 99 : 28 : a++; 100 : 28 : b++; 101 : : } 102 [ + + ]: 120 : while (*a == '0') 103 : 45 : a++; 104 [ + + ]: 120 : while (*b == '0') 105 : 45 : b++; 106 [ + + + - ]: 106 : while (c_isdigit(*a) && c_isdigit(*b)) { 107 [ + - ]: 31 : if (!first_diff) 108 : 31 : first_diff = *a - *b; 109 : 31 : a++; 110 : 31 : b++; 111 : : } 112 : : 113 [ - + ]: 75 : if (c_isdigit(*a)) 114 : 0 : return 1; 115 [ - + ]: 75 : if (c_isdigit(*b)) 116 : 0 : return -1; 117 [ + + ]: 75 : if (first_diff) 118 : 12 : return first_diff; 119 : : } 120 : : 121 : 55 : return 0; 122 : : } 123 : : 124 : : /** 125 : : * Compares two Debian versions. 126 : : * 127 : : * This function follows the convention of the comparator functions used by 128 : : * qsort(). 129 : : * 130 : : * @see deb-version(5) 131 : : * 132 : : * @param a The first version. 133 : : * @param b The second version. 134 : : * 135 : : * @retval 0 If a and b are equal. 136 : : * @retval <0 If a is smaller than b. 137 : : * @retval >0 If a is greater than b. 138 : : */ 139 : : int 140 : 45 : dpkg_version_compare(const struct dpkg_version *a, 141 : : const struct dpkg_version *b) 142 : : { 143 : : int rc; 144 : : 145 [ + + ]: 45 : if (a->epoch > b->epoch) 146 : 1 : return 1; 147 [ + + ]: 44 : if (a->epoch < b->epoch) 148 : 2 : return -1; 149 : : 150 : 42 : rc = verrevcmp(a->version, b->version); 151 [ + + ]: 42 : if (rc) 152 : 13 : return rc; 153 : : 154 : 29 : return verrevcmp(a->revision, b->revision); 155 : : } 156 : : 157 : : /** 158 : : * Check if two versions have a certain relation. 159 : : * 160 : : * @param a The first version. 161 : : * @param rel The relation. 162 : : * @param b The second version. 163 : : * 164 : : * @retval true If rel is #DPKG_RELATION_NONE or the expression “a rel b” is 165 : : * true. 166 : : * @retval false Otherwise. 167 : : * 168 : : * @warning If rel is not a valid relation, this function will terminate 169 : : * the program. 170 : : */ 171 : : bool 172 : 16 : dpkg_version_relate(const struct dpkg_version *a, 173 : : enum dpkg_relation rel, 174 : : const struct dpkg_version *b) 175 : : { 176 : : int rc; 177 : : 178 [ + + ]: 16 : if (rel == DPKG_RELATION_NONE) 179 : 1 : return true; 180 : : 181 : 15 : rc = dpkg_version_compare(a, b); 182 : : 183 [ + + + + : 15 : switch (rel) { + - ] 184 : 3 : case DPKG_RELATION_EQ: 185 : 3 : return rc == 0; 186 : 3 : case DPKG_RELATION_LT: 187 : 3 : return rc < 0; 188 : 3 : case DPKG_RELATION_LE: 189 : 3 : return rc <= 0; 190 : 3 : case DPKG_RELATION_GT: 191 : 3 : return rc > 0; 192 : 3 : case DPKG_RELATION_GE: 193 : 3 : return rc >= 0; 194 : 0 : default: 195 : 0 : internerr("unknown dpkg_relation %d", rel); 196 : : } 197 : : return false; 198 : : }