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