libalpm
Arch Linux Package Manager Library
|
00001 /* 00002 * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00016 */ 00017 00018 #include <string.h> 00019 #include <ctype.h> 00020 00021 /* libalpm */ 00022 #include "util.h" 00023 00024 /** 00025 * Some functions in this file have been adopted from the rpm source, notably 00026 * 'rpmvercmp' located at lib/rpmvercmp.c and 'parseEVR' located at 00027 * lib/rpmds.c. It was most recently updated against rpm version 4.8.1. Small 00028 * modifications have been made to make it more consistent with the libalpm 00029 * coding style. 00030 */ 00031 00032 /** 00033 * Split EVR into epoch, version, and release components. 00034 * @param evr [epoch:]version[-release] string 00035 * @retval *ep pointer to epoch 00036 * @retval *vp pointer to version 00037 * @retval *rp pointer to release 00038 */ 00039 static void parseEVR(char *evr, const char **ep, const char **vp, 00040 const char **rp) 00041 { 00042 const char *epoch; 00043 const char *version; 00044 const char *release; 00045 char *s, *se; 00046 00047 s = evr; 00048 /* s points to epoch terminator */ 00049 while (*s && isdigit(*s)) s++; 00050 /* se points to version terminator */ 00051 se = strrchr(s, '-'); 00052 00053 if(*s == ':') { 00054 epoch = evr; 00055 *s++ = '\0'; 00056 version = s; 00057 if(*epoch == '\0') { 00058 epoch = "0"; 00059 } 00060 } else { 00061 /* different from RPM- always assume 0 epoch */ 00062 epoch = "0"; 00063 version = evr; 00064 } 00065 if(se) { 00066 *se++ = '\0'; 00067 release = se; 00068 } else { 00069 release = NULL; 00070 } 00071 00072 if(ep) *ep = epoch; 00073 if(vp) *vp = version; 00074 if(rp) *rp = release; 00075 } 00076 00077 /** 00078 * Compare alpha and numeric segments of two versions. 00079 * return 1: a is newer than b 00080 * 0: a and b are the same version 00081 * -1: b is newer than a 00082 */ 00083 static int rpmvercmp(const char *a, const char *b) 00084 { 00085 char oldch1, oldch2; 00086 char *str1, *str2; 00087 char *ptr1, *ptr2; 00088 char *one, *two; 00089 int rc; 00090 int isnum; 00091 int ret = 0; 00092 00093 /* easy comparison to see if versions are identical */ 00094 if(strcmp(a, b) == 0) return 0; 00095 00096 str1 = strdup(a); 00097 str2 = strdup(b); 00098 00099 one = ptr1 = str1; 00100 two = ptr2 = str2; 00101 00102 /* loop through each version segment of str1 and str2 and compare them */ 00103 while (*one && *two) { 00104 while (*one && !isalnum((int)*one)) one++; 00105 while (*two && !isalnum((int)*two)) two++; 00106 00107 /* If we ran to the end of either, we are finished with the loop */ 00108 if (!(*one && *two)) break; 00109 00110 /* If the separator lengths were different, we are also finished */ 00111 if ((one - ptr1) != (two - ptr2)) { 00112 return (one - ptr1) < (two - ptr2) ? -1 : 1; 00113 } 00114 00115 ptr1 = one; 00116 ptr2 = two; 00117 00118 /* grab first completely alpha or completely numeric segment */ 00119 /* leave one and two pointing to the start of the alpha or numeric */ 00120 /* segment and walk ptr1 and ptr2 to end of segment */ 00121 if (isdigit((int)*ptr1)) { 00122 while (*ptr1 && isdigit((int)*ptr1)) ptr1++; 00123 while (*ptr2 && isdigit((int)*ptr2)) ptr2++; 00124 isnum = 1; 00125 } else { 00126 while (*ptr1 && isalpha((int)*ptr1)) ptr1++; 00127 while (*ptr2 && isalpha((int)*ptr2)) ptr2++; 00128 isnum = 0; 00129 } 00130 00131 /* save character at the end of the alpha or numeric segment */ 00132 /* so that they can be restored after the comparison */ 00133 oldch1 = *ptr1; 00134 *ptr1 = '\0'; 00135 oldch2 = *ptr2; 00136 *ptr2 = '\0'; 00137 00138 /* this cannot happen, as we previously tested to make sure that */ 00139 /* the first string has a non-null segment */ 00140 if (one == ptr1) { 00141 ret = -1; /* arbitrary */ 00142 goto cleanup; 00143 } 00144 00145 /* take care of the case where the two version segments are */ 00146 /* different types: one numeric, the other alpha (i.e. empty) */ 00147 /* numeric segments are always newer than alpha segments */ 00148 /* XXX See patch #60884 (and details) from bugzilla #50977. */ 00149 if (two == ptr2) { 00150 ret = isnum ? 1 : -1; 00151 goto cleanup; 00152 } 00153 00154 if (isnum) { 00155 /* this used to be done by converting the digit segments */ 00156 /* to ints using atoi() - it's changed because long */ 00157 /* digit segments can overflow an int - this should fix that. */ 00158 00159 /* throw away any leading zeros - it's a number, right? */ 00160 while (*one == '0') one++; 00161 while (*two == '0') two++; 00162 00163 /* whichever number has more digits wins */ 00164 if (strlen(one) > strlen(two)) { 00165 ret = 1; 00166 goto cleanup; 00167 } 00168 if (strlen(two) > strlen(one)) { 00169 ret = -1; 00170 goto cleanup; 00171 } 00172 } 00173 00174 /* strcmp will return which one is greater - even if the two */ 00175 /* segments are alpha or if they are numeric. don't return */ 00176 /* if they are equal because there might be more segments to */ 00177 /* compare */ 00178 rc = strcmp(one, two); 00179 if (rc) { 00180 ret = rc < 1 ? -1 : 1; 00181 goto cleanup; 00182 } 00183 00184 /* restore character that was replaced by null above */ 00185 *ptr1 = oldch1; 00186 one = ptr1; 00187 *ptr2 = oldch2; 00188 two = ptr2; 00189 } 00190 00191 /* this catches the case where all numeric and alpha segments have */ 00192 /* compared identically but the segment separating characters were */ 00193 /* different */ 00194 if ((!*one) && (!*two)) { 00195 ret = 0; 00196 goto cleanup; 00197 } 00198 00199 /* the final showdown. we never want a remaining alpha string to 00200 * beat an empty string. the logic is a bit weird, but: 00201 * - if one is empty and two is not an alpha, two is newer. 00202 * - if one is an alpha, two is newer. 00203 * - otherwise one is newer. 00204 * */ 00205 if ( (!*one && !isalpha((int)*two)) 00206 || isalpha((int)*one) ) { 00207 ret = -1; 00208 } else { 00209 ret = 1; 00210 } 00211 00212 cleanup: 00213 free(str1); 00214 free(str2); 00215 return ret; 00216 } 00217 00218 /** Compare two version strings and determine which one is 'newer'. 00219 * Returns a value comparable to the way strcmp works. Returns 1 00220 * if a is newer than b, 0 if a and b are the same version, or -1 00221 * if b is newer than a. 00222 * 00223 * Different epoch values for version strings will override any further 00224 * comparison. If no epoch is provided, 0 is assumed. 00225 * 00226 * Keep in mind that the pkgrel is only compared if it is available 00227 * on both versions handed to this function. For example, comparing 00228 * 1.5-1 and 1.5 will yield 0; comparing 1.5-1 and 1.5-2 will yield 00229 * -1 as expected. This is mainly for supporting versioned dependencies 00230 * that do not include the pkgrel. 00231 */ 00232 int SYMEXPORT alpm_pkg_vercmp(const char *a, const char *b) 00233 { 00234 char *full1, *full2; 00235 const char *epoch1, *ver1, *rel1; 00236 const char *epoch2, *ver2, *rel2; 00237 int ret; 00238 00239 /* ensure our strings are not null */ 00240 if(!a && !b) { 00241 return 0; 00242 } else if(!a) { 00243 return -1; 00244 } else if(!b) { 00245 return 1; 00246 } 00247 /* another quick shortcut- if full version specs are equal */ 00248 if(strcmp(a, b) == 0) { 00249 return 0; 00250 } 00251 00252 /* Parse both versions into [epoch:]version[-release] triplets. We probably 00253 * don't need epoch and release to support all the same magic, but it is 00254 * easier to just run it all through the same code. */ 00255 full1 = strdup(a); 00256 full2 = strdup(b); 00257 00258 /* parseEVR modifies passed in version, so have to dupe it first */ 00259 parseEVR(full1, &epoch1, &ver1, &rel1); 00260 parseEVR(full2, &epoch2, &ver2, &rel2); 00261 00262 ret = rpmvercmp(epoch1, epoch2); 00263 if(ret == 0) { 00264 ret = rpmvercmp(ver1, ver2); 00265 if(ret == 0 && rel1 && rel2) { 00266 ret = rpmvercmp(rel1, rel2); 00267 } 00268 } 00269 00270 free(full1); 00271 free(full2); 00272 return ret; 00273 } 00274 00275 /* vim: set ts=2 sw=2 noet: */