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