diff --git a/headers/os/package/PackageInfoAttributes.h b/headers/os/package/PackageInfoAttributes.h index a6cc093953..19bb8ff3f1 100644 --- a/headers/os/package/PackageInfoAttributes.h +++ b/headers/os/package/PackageInfoAttributes.h @@ -16,7 +16,7 @@ enum BPackageInfoAttributeID { B_PACKAGE_INFO_VENDOR, // e.g. "Haiku Project" B_PACKAGE_INFO_PACKAGER, // e-mail address preferred B_PACKAGE_INFO_ARCHITECTURE, - B_PACKAGE_INFO_VERSION, // [.[.]] + B_PACKAGE_INFO_VERSION, // [.[.]][-
]-
 	B_PACKAGE_INFO_COPYRIGHTS,	// list
 	B_PACKAGE_INFO_LICENSES,	// list
 	B_PACKAGE_INFO_PROVIDES,	// list of resolvables this package provides,
diff --git a/headers/os/package/PackageVersion.h b/headers/os/package/PackageVersion.h
index 376d315197..e571b64757 100644
--- a/headers/os/package/PackageVersion.h
+++ b/headers/os/package/PackageVersion.h
@@ -25,20 +25,22 @@ public:
 									const BPackageVersionData& data);
 								BPackageVersion(const BString& major,
 									const BString& minor, const BString& micro,
-									uint8 release);
+									const BString& preRelease, uint8 release);
 
 			status_t			InitCheck() const;
 
 			const BString&		Major() const;
 			const BString&		Minor() const;
 			const BString&		Micro() const;
+			const BString&		PreRelease() const;
+									// "alpha3", "beta2", "rc1" or "" if final
 			uint8				Release() const;
 
 			BString				ToString() const;
 
 			void				SetTo(const BString& major,
 									const BString& minor, const BString& micro,
-									uint8 release);
+									const BString& preRelease, uint8 release);
 			void				Clear();
 
 			int					Compare(const BPackageVersion& other) const;
@@ -49,6 +51,7 @@ private:
 			BString				fMajor;
 			BString				fMinor;
 			BString				fMicro;
+			BString				fPreRelease;
 			uint8				fRelease;
 };
 
diff --git a/headers/os/package/hpkg/HPKGDefs.h b/headers/os/package/hpkg/HPKGDefs.h
index c77a1a8ccc..aee8d9406f 100644
--- a/headers/os/package/hpkg/HPKGDefs.h
+++ b/headers/os/package/hpkg/HPKGDefs.h
@@ -110,6 +110,7 @@ enum BHPKGAttributeID {
 	B_HPKG_ATTRIBUTE_ID_PACKAGE_REPLACES			= 37,
 	B_HPKG_ATTRIBUTE_ID_PACKAGE_RESOLVABLE_OPERATOR	= 38,
 	B_HPKG_ATTRIBUTE_ID_PACKAGE_CHECKSUM			= 39,
+	B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE	= 40,
 	//
 	B_HPKG_ATTRIBUTE_ID_ENUM_COUNT,
 };
diff --git a/headers/os/package/hpkg/PackageInfoAttributeValue.h b/headers/os/package/hpkg/PackageInfoAttributeValue.h
index e2edc95e36..ab38858522 100644
--- a/headers/os/package/hpkg/PackageInfoAttributeValue.h
+++ b/headers/os/package/hpkg/PackageInfoAttributeValue.h
@@ -23,6 +23,7 @@ struct BPackageVersionData {
 			const char*			major;
 			const char*			minor;
 			const char*			micro;
+			const char*			preRelease;
 			uint8				release;
 };
 
diff --git a/src/add-ons/kernel/file_systems/packagefs/Version.cpp b/src/add-ons/kernel/file_systems/packagefs/Version.cpp
index 30ab3aba46..a9b212959c 100644
--- a/src/add-ons/kernel/file_systems/packagefs/Version.cpp
+++ b/src/add-ons/kernel/file_systems/packagefs/Version.cpp
@@ -10,6 +10,7 @@
 #include 
 #include 
 
+#include 
 #include 
 
 #include 
@@ -37,6 +38,7 @@ Version::Version()
 	fMajor(NULL),
 	fMinor(NULL),
 	fMicro(NULL),
+	fPreRelease(NULL),
 	fRelease(0)
 {
 }
@@ -47,12 +49,13 @@ Version::~Version()
 	free(fMajor);
 	free(fMinor);
 	free(fMicro);
+	free(fPreRelease);
 }
 
 
 status_t
 Version::Init(const char* major, const char* minor, const char* micro,
-	uint8 release)
+	const char* preRelease, uint8 release)
 {
 	if (major != NULL) {
 		fMajor = strdup(major);
@@ -72,6 +75,12 @@ Version::Init(const char* major, const char* minor, const char* micro,
 			return B_NO_MEMORY;
 	}
 
+	if (preRelease != NULL) {
+		fPreRelease = strdup(preRelease);
+		if (fPreRelease == NULL)
+			return B_NO_MEMORY;
+	}
+
 	fRelease = release;
 
 	return B_OK;
@@ -80,13 +89,13 @@ Version::Init(const char* major, const char* minor, const char* micro,
 
 /*static*/ status_t
 Version::Create(const char* major, const char* minor, const char* micro,
-	uint8 release, Version*& _version)
+	const char* preRelease, uint8 release, Version*& _version)
 {
 	Version* version = new(std::nothrow) Version;
 	if (version == NULL)
 		return B_NO_MEMORY;
 
-	status_t error = version->Init(major, minor, micro, release);
+	status_t error = version->Init(major, minor, micro, preRelease, release);
 	if (error != B_OK) {
 		delete version;
 		return error;
@@ -112,6 +121,21 @@ Version::Compare(const Version& other) const
 	if (cmp != 0)
 		return cmp;
 
+	// The pre-version works differently: The empty string is greater than any
+	// non-empty string (e.g. "R1" is newer than "R1-rc2"). So we catch the
+	// empty string cases first.
+	if (fPreRelease == NULL) {
+		if (other.fPreRelease != NULL)
+			return 1;
+	} else if (other.fPreRelease == NULL) {
+		return -1;
+	} else {
+		// both are non-null -- compare normally
+		cmp = BPrivate::NaturalCompare(fPreRelease, other.fPreRelease);
+		if (cmp != 0)
+			return cmp;
+	}
+
 	return (int)fRelease - other.fRelease;
 }
 
@@ -147,8 +171,8 @@ Version::ToString(char* buffer, size_t bufferSize) const
 {
 	// We need to normalize the version string somewhat. If a subpart is given,
 	// make sure that also the superparts are defined, using a placeholder. This
-	// avoids clashes, e.g. if one version defines only major and one only
-	// micro.
+	// avoids clashes, e.g. if one version defines major and minor and one only
+	// major and micro.
 	const char* major = fMajor;
 	const char* minor = fMinor;
 	const char* micro = fMicro;
@@ -158,16 +182,28 @@ Version::ToString(char* buffer, size_t bufferSize) const
 	if (minor != NULL && major == NULL)
 		major = kVersionPartPlaceholder;
 
-	if (micro != NULL) {
-		return snprintf(buffer, bufferSize, "%s.%s.%s-%u", major, minor, micro,
-			fRelease);
+	size_t size = strlcpy(buffer, major, bufferSize);
+
+	if (minor != NULL) {
+		size_t offset = std::min(bufferSize, size);
+		size += snprintf(buffer + offset, bufferSize - offset, ".%s", minor);
 	}
 
-	if (minor != NULL)
-		return snprintf(buffer, bufferSize, "%s.%s-%u", major, minor, fRelease);
+	if (micro != NULL) {
+		size_t offset = std::min(bufferSize, size);
+		size += snprintf(buffer + offset, bufferSize - offset, ".%s", micro);
+	}
 
-	if (major != NULL)
-		return snprintf(buffer, bufferSize, "%s-%u", major, fRelease);
+	if (fPreRelease != NULL) {
+		size_t offset = std::min(bufferSize, size);
+		size += snprintf(buffer + offset, bufferSize - offset, "-%s",
+			fPreRelease);
+	}
 
-	return snprintf(buffer, bufferSize, "%u", fRelease);
+	if (fRelease != 0) {
+		size_t offset = std::min(bufferSize, size);
+		size += snprintf(buffer + offset, bufferSize - offset, "-%u", fRelease);
+	}
+
+	return size;
 }
diff --git a/src/add-ons/kernel/file_systems/packagefs/Version.h b/src/add-ons/kernel/file_systems/packagefs/Version.h
index 1c72731215..a78beb0a03 100644
--- a/src/add-ons/kernel/file_systems/packagefs/Version.h
+++ b/src/add-ons/kernel/file_systems/packagefs/Version.h
@@ -19,11 +19,12 @@ public:
 								~Version();
 
 			status_t			Init(const char* major, const char* minor,
-									const char* micro, uint8 release);
+									const char* micro, const char* preRelease,
+									uint8 release);
 
 	static	status_t			Create(const char* major, const char* minor,
-									const char* micro, uint8 release,
-									Version*& _version);
+									const char* micro, const char* preRelease,
+									uint8 release, Version*& _version);
 
 			int					Compare(const Version& other) const;
 			bool				Compare(BPackageResolvableOperator op,
@@ -37,6 +38,7 @@ private:
 			char*				fMajor;
 			char*				fMinor;
 			char*				fMicro;
+			char*				fPreRelease;
 			uint8				fRelease;
 };
 
diff --git a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp
index 317676c722..1733b9a27c 100644
--- a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp
+++ b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp
@@ -294,7 +294,7 @@ struct Volume::PackageLoaderContentHandler : BPackageContentHandler {
 				Version* version;
 				status_t error = Version::Create(value.version.major,
 					value.version.minor, value.version.micro,
-					value.version.release, version);
+					value.version.preRelease, value.version.release, version);
 				if (error != B_OK)
 					RETURN_ERROR(error);
 
@@ -312,7 +312,7 @@ struct Volume::PackageLoaderContentHandler : BPackageContentHandler {
 						= value.resolvable.version;
 					status_t error = Version::Create(versionInfo.major,
 						versionInfo.minor, versionInfo.micro,
-						versionInfo.release, version);
+						versionInfo.preRelease, versionInfo.release, version);
 					if (error != B_OK)
 						RETURN_ERROR(error);
 				}
@@ -354,7 +354,7 @@ struct Volume::PackageLoaderContentHandler : BPackageContentHandler {
 						= value.resolvableExpression.version;
 					status_t error = Version::Create(versionInfo.major,
 						versionInfo.minor, versionInfo.micro,
-						versionInfo.release, version);
+						versionInfo.preRelease, versionInfo.release, version);
 					if (error != B_OK)
 						RETURN_ERROR(error);
 
diff --git a/src/bin/package/command_list.cpp b/src/bin/package/command_list.cpp
index 1d4187f033..64f57e456a 100644
--- a/src/bin/package/command_list.cpp
+++ b/src/bin/package/command_list.cpp
@@ -253,6 +253,8 @@ private:
 			printf(".%s", version.minor);
 		if (version.micro != NULL && version.micro[0] != '\0')
 			printf(".%s", version.micro);
+		if (version.preRelease != NULL && version.preRelease[0] != '\0')
+			printf("-%s", version.preRelease);
 		if (version.release > 0)
 			printf("-%d", version.release);
 	}
diff --git a/src/bin/package_repo/command_list.cpp b/src/bin/package_repo/command_list.cpp
index 1d91d4b303..ad290d2065 100644
--- a/src/bin/package_repo/command_list.cpp
+++ b/src/bin/package_repo/command_list.cpp
@@ -226,6 +226,8 @@ private:
 			printf(".%s", version.minor);
 		if (version.micro != NULL && version.micro[0] != '\0')
 			printf(".%s", version.micro);
+		if (version.preRelease != NULL && version.preRelease[0] != '\0')
+			printf("-%s", version.preRelease);
 		if (version.release > 0)
 			printf("-%d", version.release);
 	}
diff --git a/src/kits/package/PackageInfo.cpp b/src/kits/package/PackageInfo.cpp
index 4c42ad6f8c..9fd699fc67 100644
--- a/src/kits/package/PackageInfo.cpp
+++ b/src/kits/package/PackageInfo.cpp
@@ -349,23 +349,43 @@ BPackageInfo::Parser::_ParseVersionValue(BPackageVersion* value,
 	if (word.type != TOKEN_WORD)
 		throw ParseError("expected word (a version)", word.pos);
 
+	// get the release number
 	uint8 release = 0;
 	int32 lastDashPos = word.text.FindLast('-');
-	if (lastDashPos < 0) {
-		if (!releaseIsOptional) {
-			throw ParseError("expected release number (- suffix)",
+	if (lastDashPos >= 0) {
+		// Might be either the release number or, if that is optional, a
+		// pre-release. The former always is a number, the latter starts with a
+		// non-digit.
+		if (isdigit(word.text[lastDashPos + 1])) {
+			int number = atoi(word.text.String() + lastDashPos + 1);
+			if (number <= 0 || number > 99) {
+				throw ParseError("release number must be from 1-99",
+					word.pos + word.text.Length());
+			}
+			release = number;
+			word.text.Truncate(lastDashPos);
+			lastDashPos = word.text.FindLast('-');
+		}
+	}
+
+	if (release == 0 && !releaseIsOptional) {
+		throw ParseError("expected release number (- suffix)",
+			word.pos + word.text.Length());
+	}
+
+	// get the pre-release string
+	BString preRelease;
+	if (lastDashPos >= 0) {
+		if (isdigit(word.text[lastDashPos + 1])) {
+			throw ParseError("pre-release number must not start with a digit",
 				word.pos + word.text.Length());
 		}
-	} else {
-		int number = atoi(word.text.String() + lastDashPos + 1);
-		if (number <= 0 || number > 99) {
-			throw ParseError("release number must be from 1-99",
-				word.pos + word.text.Length());
-		}
-		release = number;
+
+		word.text.CopyInto(preRelease, lastDashPos + 1, word.text.Length());
 		word.text.Truncate(lastDashPos);
 	}
 
+	// get major, minor, and micro strings
 	BString major;
 	BString minor;
 	BString micro;
@@ -387,7 +407,7 @@ BPackageInfo::Parser::_ParseVersionValue(BPackageVersion* value,
 		}
 	}
 
-	value->SetTo(major, minor, micro, release);
+	value->SetTo(major, minor, micro, preRelease, release);
 }
 
 
diff --git a/src/kits/package/PackageVersion.cpp b/src/kits/package/PackageVersion.cpp
index 73e3c03ce3..2c1e53c2d6 100644
--- a/src/kits/package/PackageVersion.cpp
+++ b/src/kits/package/PackageVersion.cpp
@@ -29,17 +29,19 @@ BPackageVersion::BPackageVersion(const BPackageVersionData& data)
 	fMajor(data.major),
 	fMinor(data.minor),
 	fMicro(data.micro),
+	fPreRelease(data.preRelease),
 	fRelease(data.release)
 {
 }
 
 
 BPackageVersion::BPackageVersion(const BString& major, const BString& minor,
-	const BString& micro, uint8 release)
+	const BString& micro, const BString& preRelease, uint8 release)
 	:
 	fMajor(major),
 	fMinor(minor),
 	fMicro(micro),
+	fPreRelease(preRelease),
 	fRelease(release)
 {
 }
@@ -73,6 +75,13 @@ BPackageVersion::Micro() const
 }
 
 
+const BString&
+BPackageVersion::PreRelease() const
+{
+	return fPreRelease;
+}
+
+
 uint8
 BPackageVersion::Release() const
 {
@@ -83,17 +92,32 @@ BPackageVersion::Release() const
 int
 BPackageVersion::Compare(const BPackageVersion& other) const
 {
-	int majorDiff = NaturalCompare(fMajor.String(), other.fMajor.String());
-	if (majorDiff != 0)
-		return majorDiff;
+	int diff = NaturalCompare(fMajor.String(), other.fMajor.String());
+	if (diff != 0)
+		return diff;
 
-	int minorDiff = NaturalCompare(fMinor.String(), other.fMinor.String());
-	if (minorDiff != 0)
-		return minorDiff;
+	diff = NaturalCompare(fMinor.String(), other.fMinor.String());
+	if (diff != 0)
+		return diff;
 
-	int microDiff = NaturalCompare(fMicro.String(), other.fMicro.String());
-	if (microDiff != 0)
-		return microDiff;
+	diff = NaturalCompare(fMicro.String(), other.fMicro.String());
+	if (diff != 0)
+		return diff;
+
+	// The pre-version works differently: The empty string is greater than any
+	// non-empty string (e.g. "R1" is newer than "R1-rc2"). So we catch the
+	// empty string cases first.
+	if (fPreRelease.IsEmpty()) {
+		if (!other.fPreRelease.IsEmpty())
+			return 1;
+	} else if (other.fPreRelease.IsEmpty()) {
+		return -1;
+	} else {
+		// both are non-null -- compare normally
+		diff = NaturalCompare(fPreRelease.String(), other.fPreRelease.String());
+		if (diff != 0)
+			return diff;
+	}
 
 	return (int)fRelease - (int)other.fRelease;
 }
@@ -110,6 +134,9 @@ BPackageVersion::ToString() const
 			string << '.' << fMicro;
 	}
 
+	if (!fPreRelease.IsEmpty())
+		string << '-' << fPreRelease;
+
 	if (fRelease > 0)
 		string << '-' << fRelease;
 
@@ -119,11 +146,12 @@ BPackageVersion::ToString() const
 
 void
 BPackageVersion::SetTo(const BString& major, const BString& minor,
-	const BString& micro, uint8 release)
+	const BString& micro, const BString& preRelease, uint8 release)
 {
 	fMajor = major;
 	fMinor = minor;
 	fMicro = micro;
+	fPreRelease = preRelease;
 	fRelease = release;
 }
 
@@ -134,6 +162,7 @@ BPackageVersion::Clear()
 	fMajor.Truncate(0);
 	fMinor.Truncate(0);
 	fMicro.Truncate(0);
+	fPreRelease.Truncate(0);
 	fRelease = 0;
 }
 
diff --git a/src/kits/package/hpkg/PackageContentHandler.cpp b/src/kits/package/hpkg/PackageContentHandler.cpp
index c981f59d4c..8dc47c2a92 100644
--- a/src/kits/package/hpkg/PackageContentHandler.cpp
+++ b/src/kits/package/hpkg/PackageContentHandler.cpp
@@ -56,6 +56,7 @@ static const char* kAttributeNames[B_HPKG_ATTRIBUTE_ID_ENUM_COUNT + 1] = {
 	"package:replaces",
 	"package:resolvable.operator",
 	"package:checksum",
+	"package:version.prerelease",
 	NULL
 };
 
diff --git a/src/kits/package/hpkg/ReaderImplBase.cpp b/src/kits/package/hpkg/ReaderImplBase.cpp
index c10b5e0fda..e0fbe71389 100644
--- a/src/kits/package/hpkg/ReaderImplBase.cpp
+++ b/src/kits/package/hpkg/ReaderImplBase.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
+ * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
  * Copyright 2011, Oliver Tappe 
  * Distributed under the terms of the MIT License.
  */
@@ -127,6 +127,10 @@ ReaderImplBase::PackageVersionAttributeHandler::HandleAttribute(
 			fPackageVersionData.micro = value.string;
 			break;
 
+		case B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE:
+			fPackageVersionData.preRelease = value.string;
+			break;
+
 		case B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_RELEASE:
 			fPackageVersionData.release = value.unsignedInt;
 			break;
diff --git a/src/kits/package/hpkg/WriterImplBase.cpp b/src/kits/package/hpkg/WriterImplBase.cpp
index 27e47bc608..c3ee95aa15 100644
--- a/src/kits/package/hpkg/WriterImplBase.cpp
+++ b/src/kits/package/hpkg/WriterImplBase.cpp
@@ -523,6 +523,16 @@ WriterImplBase::RegisterPackageVersion(PackageAttributeList& attributeList,
 		}
 	}
 
+	if (!version.PreRelease().IsEmpty()) {
+		PackageAttribute* preRelease = new PackageAttribute(
+			B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_PRE_RELEASE,
+			B_HPKG_ATTRIBUTE_TYPE_STRING,
+			B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE);
+		preRelease->string
+			= fPackageStringCache.Get(version.PreRelease().String());
+		versionMajor->children.Add(preRelease);
+	}
+
 	if (version.Release() != 0) {
 		PackageAttribute* versionRelease = new PackageAttribute(
 			B_HPKG_ATTRIBUTE_ID_PACKAGE_VERSION_RELEASE,