From b8baf407aacc09167c4e9fa0db55fe323b15854e Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Thu, 20 Nov 2008 23:41:56 +0000 Subject: [PATCH] dpkg: better and shorter code to compare versions (taken from "official" dpkg). By Eugene T. Bordenkircher (eugebo AT gmail.com) function old new delta order - 48 +48 test_version 273 276 +3 version_compare_part 396 187 -209 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/1 up/down: 51/-209) Total: -158 bytes --- archival/dpkg.c | 123 ++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 66 deletions(-) diff --git a/archival/dpkg.c b/archival/dpkg.c index 1bc8d27ce..577b77fec 100644 --- a/archival/dpkg.c +++ b/archival/dpkg.c @@ -6,6 +6,10 @@ * written by glenn mcgrath with the help of others * copyright (c) 2001 by glenn mcgrath * + * parts of the version comparison code is plucked from the real dpkg + * application which is licensed GPLv2 and + * copyright (c) 1995 Ian Jackson + * * started life as a busybox implementation of udpkg * * licensed under gplv2 or later, see file license in this tarball for details. @@ -135,7 +139,7 @@ static void make_hash(const char *key, unsigned *start, unsigned *decrement, con /* shifts the ascii based value and adds it to previous value * shift amount is mod 24 because long int is 32 bit and data * to be shifted is 8, don't want to shift data to where it has - * no effect*/ + * no effect */ hash_num += (key[i] + key[i-1]) << ((key[i] * i) % 24); } *start = (unsigned) hash_num % hash_prime; @@ -183,60 +187,52 @@ static unsigned search_status_hashtable(const char *key) return probe_address; } -/* Need to rethink version comparison, maybe the official dpkg has something i can use ? */ -static int version_compare_part(const char *version1, const char *version2) +static int order(char x) { - int upstream_len1 = 0; - int upstream_len2 = 0; - char *name1_char; - char *name2_char; - int len1 = 0; - int len2 = 0; - int tmp_int; - int ver_num1; - int ver_num2; + return (x == '~' ? -1 + : x == '\0' ? 0 + : isdigit(x) ? 0 + : isalpha(x) ? x + : (unsigned char)x + 256 + ); +} - if (version1 == NULL) { - version1 = xstrdup(""); - } - if (version2 == NULL) { - version2 = xstrdup(""); - } - upstream_len1 = strlen(version1); - upstream_len2 = strlen(version2); +/* This code is taken from dpkg and modified slightly to work with busybox */ +static int version_compare_part(const char *val, const char *ref) +{ + if (!val) val = ""; + if (!ref) ref = ""; - while ((len1 < upstream_len1) || (len2 < upstream_len2)) { - /* Compare non-digit section */ - tmp_int = strcspn(&version1[len1], "0123456789"); - name1_char = xstrndup(&version1[len1], tmp_int); - len1 += tmp_int; - tmp_int = strcspn(&version2[len2], "0123456789"); - name2_char = xstrndup(&version2[len2], tmp_int); - len2 += tmp_int; - tmp_int = strcmp(name1_char, name2_char); - free(name1_char); - free(name2_char); - if (tmp_int != 0) { - return tmp_int; + while (*val || *ref) { + int first_diff; + + while ((*val && !isdigit(*val)) || (*ref && !isdigit(*ref))) { + int vc = order(*val); + int rc = order(*ref); + if (vc != rc) + return vc - rc; + val++; + ref++; } - /* Compare digits */ - tmp_int = strspn(&version1[len1], "0123456789"); - name1_char = xstrndup(&version1[len1], tmp_int); - len1 += tmp_int; - tmp_int = strspn(&version2[len2], "0123456789"); - name2_char = xstrndup(&version2[len2], tmp_int); - len2 += tmp_int; - ver_num1 = atoi(name1_char); - ver_num2 = atoi(name2_char); - free(name1_char); - free(name2_char); - if (ver_num1 < ver_num2) { - return -1; + while (*val == '0') + val++; + while (*ref == '0') + ref++; + + first_diff = 0; + while (isdigit(*val) && isdigit(*ref)) { + if (first_diff == 0) + first_diff = *val - *ref; + val++; + ref++; } - if (ver_num1 > ver_num2) { + if (isdigit(*val)) return 1; - } + if (isdigit(*ref)) + return -1; + if (first_diff) + return first_diff; } return 0; } @@ -249,39 +245,34 @@ static int version_compare(const unsigned ver1, const unsigned ver2) { char *ch_ver1 = name_hashtable[ver1]; char *ch_ver2 = name_hashtable[ver2]; - - char epoch1, epoch2; + unsigned long epoch1 = 0, epoch2 = 0; + char *colon; char *deb_ver1, *deb_ver2; - char *ver1_ptr, *ver2_ptr; char *upstream_ver1; char *upstream_ver2; int result; /* Compare epoch */ - if (ch_ver1[1] == ':') { - epoch1 = ch_ver1[0]; - ver1_ptr = strchr(ch_ver1, ':') + 1; - } else { - epoch1 = '0'; - ver1_ptr = ch_ver1; + colon = strchr(ch_ver1, ':'); + if (colon) { + epoch1 = atoi(ch_ver1); + ch_ver1 = colon + 1; } - if (ch_ver2[1] == ':') { - epoch2 = ch_ver2[0]; - ver2_ptr = strchr(ch_ver2, ':') + 1; - } else { - epoch2 = '0'; - ver2_ptr = ch_ver2; + colon = strchr(ch_ver2, ':'); + if (colon) { + epoch2 = atoi(ch_ver2); + ch_ver2 = colon + 1; } if (epoch1 < epoch2) { return -1; } - else if (epoch1 > epoch2) { + if (epoch1 > epoch2) { return 1; } /* Compare upstream version */ - upstream_ver1 = xstrdup(ver1_ptr); - upstream_ver2 = xstrdup(ver2_ptr); + upstream_ver1 = xstrdup(ch_ver1); + upstream_ver2 = xstrdup(ch_ver2); /* Chop off debian version, and store for later use */ deb_ver1 = strrchr(upstream_ver1, '-');