1/* 2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include "Version.h" 8 9#include <stdio.h> 10#include <stdlib.h> 11#include <string.h> 12 13#include <algorithm> 14#include <new> 15 16#include <NaturalCompare.h> 17 18#include "DebugSupport.h" 19 20 21static const char* const kVersionPartPlaceholder = "_"; 22 23 24static int 25compare_version_part(const char* a, const char* b) 26{ 27 if (a == NULL) 28 return b != NULL ? -1 : 0; 29 if (b == NULL) 30 return 1; 31 32 return BPrivate::NaturalCompare(a, b); 33} 34 35 36Version::Version() 37 : 38 fMajor(NULL), 39 fMinor(NULL), 40 fMicro(NULL), 41 fPreRelease(NULL), 42 fRelease(0) 43{ 44} 45 46 47Version::~Version() 48{ 49 free(fMajor); 50 free(fMinor); 51 free(fMicro); 52 free(fPreRelease); 53} 54 55 56status_t 57Version::Init(const char* major, const char* minor, const char* micro, 58 const char* preRelease, uint8 release) 59{ 60 if (major != NULL) { 61 fMajor = strdup(major); 62 if (fMajor == NULL) 63 return B_NO_MEMORY; 64 } 65 66 if (minor != NULL) { 67 fMinor = strdup(minor); 68 if (fMinor == NULL) 69 return B_NO_MEMORY; 70 } 71 72 if (micro != NULL) { 73 fMicro = strdup(micro); 74 if (fMicro == NULL) 75 return B_NO_MEMORY; 76 } 77 78 if (preRelease != NULL) { 79 fPreRelease = strdup(preRelease); 80 if (fPreRelease == NULL) 81 return B_NO_MEMORY; 82 } 83 84 fRelease = release; 85 86 return B_OK; 87} 88 89 90/*static*/ status_t 91Version::Create(const char* major, const char* minor, const char* micro, 92 const char* preRelease, uint8 release, Version*& _version) 93{ 94 Version* version = new(std::nothrow) Version; 95 if (version == NULL) 96 return B_NO_MEMORY; 97 98 status_t error = version->Init(major, minor, micro, preRelease, release); 99 if (error != B_OK) { 100 delete version; 101 return error; 102 } 103 104 _version = version; 105 return B_OK; 106} 107 108 109int 110Version::Compare(const Version& other) const 111{ 112 int cmp = compare_version_part(fMajor, other.fMajor); 113 if (cmp != 0) 114 return cmp; 115 116 cmp = compare_version_part(fMinor, other.fMinor); 117 if (cmp != 0) 118 return cmp; 119 120 cmp = compare_version_part(fMicro, other.fMicro); 121 if (cmp != 0) 122 return cmp; 123 124 // The pre-version works differently: The empty string is greater than any 125 // non-empty string (e.g. "R1" is newer than "R1-rc2"). So we catch the 126 // empty string cases first. 127 if (fPreRelease == NULL) { 128 if (other.fPreRelease != NULL) 129 return 1; 130 } else if (other.fPreRelease == NULL) { 131 return -1; 132 } else { 133 // both are non-null -- compare normally 134 cmp = BPrivate::NaturalCompare(fPreRelease, other.fPreRelease); 135 if (cmp != 0) 136 return cmp; 137 } 138 139 return (int)fRelease - other.fRelease; 140} 141 142 143bool 144Version::Compare(BPackageResolvableOperator op, 145 const Version& other) const 146{ 147 int cmp = Compare(other); 148 149 switch (op) { 150 case B_PACKAGE_RESOLVABLE_OP_LESS: 151 return cmp < 0; 152 case B_PACKAGE_RESOLVABLE_OP_LESS_EQUAL: 153 return cmp <= 0; 154 case B_PACKAGE_RESOLVABLE_OP_EQUAL: 155 return cmp == 0; 156 case B_PACKAGE_RESOLVABLE_OP_NOT_EQUAL: 157 return cmp != 0; 158 case B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL: 159 return cmp >= 0; 160 case B_PACKAGE_RESOLVABLE_OP_GREATER: 161 return cmp > 0; 162 default: 163 ERROR("packagefs: Version::Compare(): Invalid operator %d\n", op); 164 return false; 165 } 166} 167 168 169size_t 170Version::ToString(char* buffer, size_t bufferSize) const 171{ 172 // We need to normalize the version string somewhat. If a subpart is given, 173 // make sure that also the superparts are defined, using a placeholder. This 174 // avoids clashes, e.g. if one version defines major and minor and one only 175 // major and micro. 176 const char* major = fMajor; 177 const char* minor = fMinor; 178 const char* micro = fMicro; 179 180 if (micro != NULL && minor == NULL) 181 minor = kVersionPartPlaceholder; 182 if (minor != NULL && major == NULL) 183 major = kVersionPartPlaceholder; 184 185 size_t size = strlcpy(buffer, major, bufferSize); 186 187 if (minor != NULL) { 188 size_t offset = std::min(bufferSize, size); 189 size += snprintf(buffer + offset, bufferSize - offset, ".%s", minor); 190 } 191 192 if (micro != NULL) { 193 size_t offset = std::min(bufferSize, size); 194 size += snprintf(buffer + offset, bufferSize - offset, ".%s", micro); 195 } 196 197 if (fPreRelease != NULL) { 198 size_t offset = std::min(bufferSize, size); 199 size += snprintf(buffer + offset, bufferSize - offset, "-%s", 200 fPreRelease); 201 } 202 203 if (fRelease != 0) { 204 size_t offset = std::min(bufferSize, size); 205 size += snprintf(buffer + offset, bufferSize - offset, "-%u", fRelease); 206 } 207 208 return size; 209} 210