Magellan Linux

Diff of /branches/mage-next/src/vercomp.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2806 by niro, Mon Feb 3 14:02:05 2014 UTC revision 2807 by niro, Wed Sep 3 11:53:06 2014 UTC
# Line 20  Line 20 
20  #include <string.h>  #include <string.h>
21  #include <ctype.h>  #include <ctype.h>
22    
23  /**  #include "package.h"
  * Some functions in this file have been adopted from the rpm source, notably  
  * 'rpmvercmp' located at lib/rpmvercmp.c and 'parseEVR' located at  
  * lib/rpmds.c. It was most recently updated against rpm version 4.8.1. Small  
  * modifications have been made to make it more consistent with the libalpm  
  * coding style.  
  */  
   
 /**  
  * Split EVR into epoch, version, and release components.  
  * @param evr [epoch:]version[-release] string  
  * @retval *ep pointer to epoch  
  * @retval *vp pointer to version  
  * @retval *rp pointer to release  
  */  
 static void parseEVR(char *evr, const char **ep, const char **vp,  
  const char **rp)  
 {  
  const char *epoch;  
  const char *version;  
  const char *release;  
  char *s, *se;  
   
  s = evr;  
  /* s points to epoch terminator */  
  while (*s && isdigit(*s)) s++;  
  /* se points to version terminator */  
  se = strrchr(s, '-');  
   
  if(*s == ':') {  
  epoch = evr;  
  *s++ = '\0';  
  version = s;  
  if(*epoch == '\0') {  
  epoch = "0";  
  }  
  } else {  
  /* different from RPM- always assume 0 epoch */  
  epoch = "0";  
  version = evr;  
  }  
  if(se) {  
  *se++ = '\0';  
  release = se;  
  } else {  
  release = NULL;  
  }  
   
  if(ep) *ep = epoch;  
  if(vp) *vp = version;  
  if(rp) *rp = release;  
 }  
   
 /**  
  * Compare alpha and numeric segments of two versions.  
  * return 1: a is newer than b  
  *        0: a and b are the same version  
  *       -1: b is newer than a  
  */  
 static int rpmvercmp(const char *a, const char *b)  
 {  
  char oldch1, oldch2;  
  char *str1, *str2;  
  char *ptr1, *ptr2;  
  char *one, *two;  
  int rc;  
  int isnum;  
  int ret = 0;  
   
  /* easy comparison to see if versions are identical */  
  if(strcmp(a, b) == 0) return 0;  
   
  str1 = strdup(a);  
  str2 = strdup(b);  
   
  one = ptr1 = str1;  
  two = ptr2 = str2;  
   
  /* loop through each version segment of str1 and str2 and compare them */  
  while (*one && *two) {  
  while (*one && !isalnum((int)*one)) one++;  
  while (*two && !isalnum((int)*two)) two++;  
   
  /* If we ran to the end of either, we are finished with the loop */  
  if (!(*one && *two)) break;  
   
  /* If the separator lengths were different, we are also finished */  
  if ((one - ptr1) != (two - ptr2)) {  
  return (one - ptr1) < (two - ptr2) ? -1 : 1;  
  }  
   
  ptr1 = one;  
  ptr2 = two;  
   
  /* grab first completely alpha or completely numeric segment */  
  /* leave one and two pointing to the start of the alpha or numeric */  
  /* segment and walk ptr1 and ptr2 to end of segment */  
  if (isdigit((int)*ptr1)) {  
  while (*ptr1 && isdigit((int)*ptr1)) ptr1++;  
  while (*ptr2 && isdigit((int)*ptr2)) ptr2++;  
  isnum = 1;  
  } else {  
  while (*ptr1 && isalpha((int)*ptr1)) ptr1++;  
  while (*ptr2 && isalpha((int)*ptr2)) ptr2++;  
  isnum = 0;  
  }  
   
  /* save character at the end of the alpha or numeric segment */  
  /* so that they can be restored after the comparison */  
  oldch1 = *ptr1;  
  *ptr1 = '\0';  
  oldch2 = *ptr2;  
  *ptr2 = '\0';  
   
  /* this cannot happen, as we previously tested to make sure that */  
  /* the first string has a non-null segment */  
  if (one == ptr1) {  
  ret = -1; /* arbitrary */  
  goto cleanup;  
  }  
   
  /* take care of the case where the two version segments are */  
  /* different types: one numeric, the other alpha (i.e. empty) */  
  /* numeric segments are always newer than alpha segments */  
  /* XXX See patch #60884 (and details) from bugzilla #50977. */  
  if (two == ptr2) {  
  ret = isnum ? 1 : -1;  
  goto cleanup;  
  }  
   
  if (isnum) {  
  /* this used to be done by converting the digit segments */  
  /* to ints using atoi() - it's changed because long  */  
  /* digit segments can overflow an int - this should fix that. */  
   
  /* throw away any leading zeros - it's a number, right? */  
  while (*one == '0') one++;  
  while (*two == '0') two++;  
   
  /* whichever number has more digits wins */  
  if (strlen(one) > strlen(two)) {  
  ret = 1;  
  goto cleanup;  
  }  
  if (strlen(two) > strlen(one)) {  
  ret = -1;  
  goto cleanup;  
  }  
  }  
   
  /* strcmp will return which one is greater - even if the two */  
  /* segments are alpha or if they are numeric.  don't return  */  
  /* if they are equal because there might be more segments to */  
  /* compare */  
  rc = strcmp(one, two);  
  if (rc) {  
  ret = rc < 1 ? -1 : 1;  
  goto cleanup;  
  }  
   
  /* restore character that was replaced by null above */  
  *ptr1 = oldch1;  
  one = ptr1;  
  *ptr2 = oldch2;  
  two = ptr2;  
  }  
   
  /* this catches the case where all numeric and alpha segments have */  
  /* compared identically but the segment separating characters were */  
  /* different */  
  if ((!*one) && (!*two)) {  
  ret = 0;  
  goto cleanup;  
  }  
   
  /* the final showdown. we never want a remaining alpha string to  
  * beat an empty string. the logic is a bit weird, but:  
  * - if one is empty and two is not an alpha, two is newer.  
  * - if one is an alpha, two is newer.  
  * - otherwise one is newer.  
  * */  
  if ( (!*one && !isalpha((int)*two))  
  || isalpha((int)*one) ) {  
  ret = -1;  
  } else {  
  ret = 1;  
  }  
   
 cleanup:  
  free(str1);  
  free(str2);  
  return ret;  
 }  
   
 /** Compare two version strings and determine which one is 'newer'.  
  * Returns a value comparable to the way strcmp works. Returns 1  
  * if a is newer than b, 0 if a and b are the same version, or -1  
  * if b is newer than a.  
  *  
  * Different epoch values for version strings will override any further  
  * comparison. If no epoch is provided, 0 is assumed.  
  *  
  * Keep in mind that the pkgrel is only compared if it is available  
  * on both versions handed to this function. For example, comparing  
  * 1.5-1 and 1.5 will yield 0; comparing 1.5-1 and 1.5-2 will yield  
  * -1 as expected. This is mainly for supporting versioned dependencies  
  * that do not include the pkgrel.  
  */  
 int alpm_pkg_vercmp(const char *a, const char *b)  
 {  
  char *full1, *full2;  
  const char *epoch1, *ver1, *rel1;  
  const char *epoch2, *ver2, *rel2;  
  int ret;  
   
  /* ensure our strings are not null */  
  if(!a && !b) {  
  return 0;  
  } else if(!a) {  
  return -1;  
  } else if(!b) {  
  return 1;  
  }  
  /* another quick shortcut- if full version specs are equal */  
  if(strcmp(a, b) == 0) {  
  return 0;  
  }  
   
  /* Parse both versions into [epoch:]version[-release] triplets. We probably  
  * don't need epoch and release to support all the same magic, but it is  
  * easier to just run it all through the same code. */  
  full1 = strdup(a);  
  full2 = strdup(b);  
   
  /* parseEVR modifies passed in version, so have to dupe it first */  
  parseEVR(full1, &epoch1, &ver1, &rel1);  
  parseEVR(full2, &epoch2, &ver2, &rel2);  
   
  ret = rpmvercmp(epoch1, epoch2);  
  if(ret == 0) {  
  ret = rpmvercmp(ver1, ver2);  
  if(ret == 0 && rel1 && rel2) {  
  ret = rpmvercmp(rel1, rel2);  
  }  
  }  
   
  free(full1);  
  free(full2);  
  return ret;  
 }  
24    
25  #define BASENAME "vercmp"  #define BASENAME "vercmp"
26    

Legend:
Removed from v.2806  
changed lines
  Added in v.2807