BPackage{Version,Info}: Switch to new pre-release rule
Also add several checks in the package-info parser to enforce the <alphanum_underscore> requirement of package/resolvable names and version components.
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
#include <package/PackageInfo.h>
|
#include <package/PackageInfo.h>
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -84,7 +85,8 @@ private:
|
|||||||
Token _NextToken();
|
Token _NextToken();
|
||||||
void _RewindTo(const Token& token);
|
void _RewindTo(const Token& token);
|
||||||
|
|
||||||
void _ParseStringValue(BString* value);
|
void _ParseStringValue(BString* value,
|
||||||
|
const char** _tokenPos = NULL);
|
||||||
uint32 _ParseFlags();
|
uint32 _ParseFlags();
|
||||||
void _ParseArchitectureValue(
|
void _ParseArchitectureValue(
|
||||||
BPackageArchitecture* value);
|
BPackageArchitecture* value);
|
||||||
@@ -106,6 +108,13 @@ private:
|
|||||||
|
|
||||||
void _Parse(BPackageInfo* packageInfo);
|
void _Parse(BPackageInfo* packageInfo);
|
||||||
|
|
||||||
|
static bool _IsAlphaNumUnderscore(const BString& string,
|
||||||
|
int32* _errorPos = NULL);
|
||||||
|
static bool _IsAlphaNumUnderscore(const char* string,
|
||||||
|
int32* _errorPos = NULL);
|
||||||
|
static bool _IsAlphaNumUnderscore(const char* start,
|
||||||
|
const char* end, int32* _errorPos = NULL);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ParseErrorListener* fListener;
|
ParseErrorListener* fListener;
|
||||||
const char* fPos;
|
const char* fPos;
|
||||||
@@ -335,7 +344,8 @@ BPackageInfo::Parser::_NextToken()
|
|||||||
{
|
{
|
||||||
const char* start = fPos;
|
const char* start = fPos;
|
||||||
while (isalnum(*fPos) || *fPos == '.' || *fPos == '-'
|
while (isalnum(*fPos) || *fPos == '.' || *fPos == '-'
|
||||||
|| *fPos == '_' || *fPos == ':' || *fPos == '+') {
|
|| *fPos == '_' || *fPos == ':' || *fPos == '+' || *fPos == '['
|
||||||
|
|| *fPos == ']') {
|
||||||
fPos++;
|
fPos++;
|
||||||
}
|
}
|
||||||
if (fPos == start)
|
if (fPos == start)
|
||||||
@@ -357,13 +367,15 @@ BPackageInfo::Parser::_RewindTo(const Token& token)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BPackageInfo::Parser::_ParseStringValue(BString* value)
|
BPackageInfo::Parser::_ParseStringValue(BString* value, const char** _tokenPos)
|
||||||
{
|
{
|
||||||
Token string = _NextToken();
|
Token string = _NextToken();
|
||||||
if (string.type != TOKEN_QUOTED_STRING && string.type != TOKEN_WORD)
|
if (string.type != TOKEN_QUOTED_STRING && string.type != TOKEN_WORD)
|
||||||
throw ParseError("expected quoted-string or word", string.pos);
|
throw ParseError("expected quoted-string or word", string.pos);
|
||||||
|
|
||||||
*value = string.text;
|
*value = string.text;
|
||||||
|
if (_tokenPos != NULL)
|
||||||
|
*_tokenPos = string.pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -409,21 +421,18 @@ BPackageInfo::Parser::_ParseVersionValue(Token& word, BPackageVersion* value,
|
|||||||
|
|
||||||
// get the revision number
|
// get the revision number
|
||||||
uint32 revision = 0;
|
uint32 revision = 0;
|
||||||
int32 lastDashPos = word.text.FindLast('-');
|
int32 dashPos = word.text.FindLast('-');
|
||||||
if (lastDashPos >= 0) {
|
if (dashPos >= 0) {
|
||||||
// Might be either the revision number or, if that is optional, a
|
char* end;
|
||||||
// pre-release. The former always is a number, the latter starts with a
|
long long number = strtoll(word.text.String() + dashPos + 1, &end,
|
||||||
// non-digit.
|
0);
|
||||||
if (isdigit(word.text[lastDashPos + 1])) {
|
if (*end != '\0' || number < 0 || number > UINT_MAX) {
|
||||||
int number = atoi(word.text.String() + lastDashPos + 1);
|
throw ParseError("revision must be a number > 0 and < UINT_MAX",
|
||||||
if (number <= 0) {
|
word.pos + dashPos + 1);
|
||||||
throw ParseError("revision number must be > 0",
|
|
||||||
word.pos + word.text.Length());
|
|
||||||
}
|
|
||||||
revision = number;
|
|
||||||
word.text.Truncate(lastDashPos);
|
|
||||||
lastDashPos = word.text.FindLast('-');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
revision = (uint32)number;
|
||||||
|
word.text.Truncate(dashPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (revision == 0 && !revisionIsOptional) {
|
if (revision == 0 && !revisionIsOptional) {
|
||||||
@@ -433,14 +442,27 @@ BPackageInfo::Parser::_ParseVersionValue(Token& word, BPackageVersion* value,
|
|||||||
|
|
||||||
// get the pre-release string
|
// get the pre-release string
|
||||||
BString preRelease;
|
BString preRelease;
|
||||||
if (lastDashPos >= 0) {
|
if (word.text.Length() > 0 && word.text[word.text.Length() - 1] == ']') {
|
||||||
if (isdigit(word.text[lastDashPos + 1])) {
|
int32 openingBracket = word.text.FindLast('[');
|
||||||
throw ParseError("pre-release number must not start with a digit",
|
if (openingBracket < 0) {
|
||||||
word.pos + word.text.Length());
|
throw ParseError("unmatched ']' in version string",
|
||||||
|
word.pos + word.text.Length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
word.text.CopyInto(preRelease, lastDashPos + 1, word.text.Length());
|
word.text.CopyInto(preRelease, openingBracket + 1,
|
||||||
word.text.Truncate(lastDashPos);
|
word.text.Length() - openingBracket - 2);
|
||||||
|
word.text.Truncate(openingBracket);
|
||||||
|
|
||||||
|
if (preRelease.IsEmpty()) {
|
||||||
|
throw ParseError("invalid empty pre-release string",
|
||||||
|
word.pos + openingBracket + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 errorPos;
|
||||||
|
if (!_IsAlphaNumUnderscore(preRelease, &errorPos)) {
|
||||||
|
throw ParseError("invalid character in pre-release string",
|
||||||
|
word.pos + openingBracket + 1 + errorPos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get major, minor, and micro strings
|
// get major, minor, and micro strings
|
||||||
@@ -456,13 +478,31 @@ BPackageInfo::Parser::_ParseVersionValue(Token& word, BPackageVersion* value,
|
|||||||
if (secondDotPos == firstDotPos + 1)
|
if (secondDotPos == firstDotPos + 1)
|
||||||
throw ParseError("expected minor version", word.pos + secondDotPos);
|
throw ParseError("expected minor version", word.pos + secondDotPos);
|
||||||
|
|
||||||
if (secondDotPos < 0)
|
if (secondDotPos < 0) {
|
||||||
word.text.CopyInto(minor, firstDotPos + 1, word.text.Length());
|
word.text.CopyInto(minor, firstDotPos + 1, word.text.Length());
|
||||||
else {
|
} else {
|
||||||
word.text.CopyInto(minor, firstDotPos + 1,
|
word.text.CopyInto(minor, firstDotPos + 1,
|
||||||
secondDotPos - (firstDotPos + 1));
|
secondDotPos - (firstDotPos + 1));
|
||||||
word.text.CopyInto(micro, secondDotPos + 1, word.text.Length());
|
word.text.CopyInto(micro, secondDotPos + 1, word.text.Length());
|
||||||
|
|
||||||
|
int32 errorPos;
|
||||||
|
if (!_IsAlphaNumUnderscore(micro, &errorPos)) {
|
||||||
|
throw ParseError("invalid character in micro version string",
|
||||||
|
word.pos + secondDotPos + 1 + errorPos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32 errorPos;
|
||||||
|
if (!_IsAlphaNumUnderscore(minor, &errorPos)) {
|
||||||
|
throw ParseError("invalid character in minor version string",
|
||||||
|
word.pos + firstDotPos + 1 + errorPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 errorPos;
|
||||||
|
if (!_IsAlphaNumUnderscore(major, &errorPos)) {
|
||||||
|
throw ParseError("invalid character in major version string",
|
||||||
|
word.pos + errorPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
value->SetTo(major, minor, micro, preRelease, revision);
|
value->SetTo(major, minor, micro, preRelease, revision);
|
||||||
@@ -476,7 +516,7 @@ BPackageInfo::Parser::_ParseList(ListElementParser& elementParser,
|
|||||||
Token openBracket = _NextToken();
|
Token openBracket = _NextToken();
|
||||||
if (openBracket.type != TOKEN_OPEN_BRACE) {
|
if (openBracket.type != TOKEN_OPEN_BRACE) {
|
||||||
if (!allowSingleNonListElement)
|
if (!allowSingleNonListElement)
|
||||||
throw ParseError("expected start of list ('[')", openBracket.pos);
|
throw ParseError("expected start of list ('{')", openBracket.pos);
|
||||||
|
|
||||||
elementParser(openBracket);
|
elementParser(openBracket);
|
||||||
return;
|
return;
|
||||||
@@ -620,6 +660,26 @@ BPackageInfo::Parser::_ParseResolvableList(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (colonPos >= 0) {
|
||||||
|
int32 errorPos;
|
||||||
|
if (!_IsAlphaNumUnderscore(token.text.String(),
|
||||||
|
token.text.String() + colonPos, &errorPos)) {
|
||||||
|
throw ParseError("invalid character in resolvable name",
|
||||||
|
token.pos + errorPos);
|
||||||
|
}
|
||||||
|
if (!_IsAlphaNumUnderscore(token.text.String() + colonPos + 1,
|
||||||
|
&errorPos)) {
|
||||||
|
throw ParseError("invalid character in resolvable name",
|
||||||
|
token.pos + colonPos + 1 + errorPos);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int32 errorPos;
|
||||||
|
if (!_IsAlphaNumUnderscore(token.text, &errorPos)) {
|
||||||
|
throw ParseError("invalid character in resolvable name",
|
||||||
|
token.pos + errorPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// parse version
|
// parse version
|
||||||
BPackageVersion version;
|
BPackageVersion version;
|
||||||
Token op = parser._NextToken();
|
Token op = parser._NextToken();
|
||||||
@@ -629,7 +689,7 @@ BPackageInfo::Parser::_ParseResolvableList(
|
|||||||
|| op.type == TOKEN_CLOSE_BRACE) {
|
|| op.type == TOKEN_CLOSE_BRACE) {
|
||||||
parser._RewindTo(op);
|
parser._RewindTo(op);
|
||||||
} else
|
} else
|
||||||
throw ParseError("expected '=', comma or ']'", op.pos);
|
throw ParseError("expected '=', comma or '}'", op.pos);
|
||||||
|
|
||||||
// parse compatible version
|
// parse compatible version
|
||||||
BPackageVersion compatibleVersion;
|
BPackageVersion compatibleVersion;
|
||||||
@@ -677,26 +737,47 @@ BPackageInfo::Parser::_ParseResolvableExprList(
|
|||||||
token.pos);
|
token.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
BPackageVersion version;
|
int32 colonPos = token.text.FindFirst(':');
|
||||||
Token op = parser._NextToken();
|
if (colonPos >= 0) {
|
||||||
if (op.type == TOKEN_OPERATOR_LESS
|
int32 errorPos;
|
||||||
|| op.type == TOKEN_OPERATOR_LESS_EQUAL
|
if (!_IsAlphaNumUnderscore(token.text.String(),
|
||||||
|| op.type == TOKEN_OPERATOR_EQUAL
|
token.text.String() + colonPos, &errorPos)) {
|
||||||
|| op.type == TOKEN_OPERATOR_NOT_EQUAL
|
throw ParseError("invalid character in resolvable name",
|
||||||
|| op.type == TOKEN_OPERATOR_GREATER_EQUAL
|
token.pos + errorPos);
|
||||||
|| op.type == TOKEN_OPERATOR_GREATER) {
|
}
|
||||||
parser._ParseVersionValue(&version, true);
|
if (!_IsAlphaNumUnderscore(token.text.String() + colonPos + 1,
|
||||||
} else if (op.type == TOKEN_ITEM_SEPARATOR
|
&errorPos)) {
|
||||||
|| op.type == TOKEN_CLOSE_BRACE) {
|
throw ParseError("invalid character in resolvable name",
|
||||||
parser._RewindTo(op);
|
token.pos + colonPos + 1 + errorPos);
|
||||||
} else {
|
}
|
||||||
throw ParseError(
|
} else {
|
||||||
"expected '<', '<=', '==', '!=', '>=', '>', comma or ']'",
|
int32 errorPos;
|
||||||
op.pos);
|
if (!_IsAlphaNumUnderscore(token.text, &errorPos)) {
|
||||||
}
|
throw ParseError("invalid character in resolvable name",
|
||||||
|
token.pos + errorPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BPackageResolvableOperator resolvableOperator
|
BPackageVersion version;
|
||||||
= (BPackageResolvableOperator)(op.type - TOKEN_OPERATOR_LESS);
|
Token op = parser._NextToken();
|
||||||
|
if (op.type == TOKEN_OPERATOR_LESS
|
||||||
|
|| op.type == TOKEN_OPERATOR_LESS_EQUAL
|
||||||
|
|| op.type == TOKEN_OPERATOR_EQUAL
|
||||||
|
|| op.type == TOKEN_OPERATOR_NOT_EQUAL
|
||||||
|
|| op.type == TOKEN_OPERATOR_GREATER_EQUAL
|
||||||
|
|| op.type == TOKEN_OPERATOR_GREATER) {
|
||||||
|
parser._ParseVersionValue(&version, true);
|
||||||
|
} else if (op.type == TOKEN_ITEM_SEPARATOR
|
||||||
|
|| op.type == TOKEN_CLOSE_BRACE) {
|
||||||
|
parser._RewindTo(op);
|
||||||
|
} else {
|
||||||
|
throw ParseError(
|
||||||
|
"expected '<', '<=', '=', '==', '!=', '>=', '>', comma or "
|
||||||
|
"'}'", op.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
BPackageResolvableOperator resolvableOperator
|
||||||
|
= (BPackageResolvableOperator)(op.type - TOKEN_OPERATOR_LESS);
|
||||||
|
|
||||||
value->AddItem(new BPackageResolvableExpression(token.text,
|
value->AddItem(new BPackageResolvableExpression(token.text,
|
||||||
resolvableOperator, version));
|
resolvableOperator, version));
|
||||||
@@ -745,7 +826,15 @@ BPackageInfo::Parser::_Parse(BPackageInfo* packageInfo)
|
|||||||
case B_PACKAGE_INFO_NAME:
|
case B_PACKAGE_INFO_NAME:
|
||||||
{
|
{
|
||||||
BString name;
|
BString name;
|
||||||
_ParseStringValue(&name);
|
const char* namePos;
|
||||||
|
_ParseStringValue(&name, &namePos);
|
||||||
|
|
||||||
|
int32 errorPos;
|
||||||
|
if (!_IsAlphaNumUnderscore(name, &errorPos)) {
|
||||||
|
throw ParseError("invalid character in package name",
|
||||||
|
namePos + errorPos);
|
||||||
|
}
|
||||||
|
|
||||||
packageInfo->SetName(name);
|
packageInfo->SetName(name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -842,6 +931,39 @@ BPackageInfo::Parser::_Parse(BPackageInfo* packageInfo)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ inline bool
|
||||||
|
BPackageInfo::Parser::_IsAlphaNumUnderscore(const BString& string,
|
||||||
|
int32* _errorPos)
|
||||||
|
{
|
||||||
|
return _IsAlphaNumUnderscore(string.String(),
|
||||||
|
string.String() + string.Length(), _errorPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ inline bool
|
||||||
|
BPackageInfo::Parser::_IsAlphaNumUnderscore(const char* string,
|
||||||
|
int32* _errorPos)
|
||||||
|
{
|
||||||
|
return _IsAlphaNumUnderscore(string, string + strlen(string), _errorPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ bool
|
||||||
|
BPackageInfo::Parser::_IsAlphaNumUnderscore(const char* start, const char* end,
|
||||||
|
int32* _errorPos)
|
||||||
|
{
|
||||||
|
for (const char* c = start; c < end; c++) {
|
||||||
|
if (!isalnum(*c) && *c != '_') {
|
||||||
|
if (_errorPos != NULL)
|
||||||
|
*_errorPos = c - start;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const char* BPackageInfo::kElementNames[B_PACKAGE_INFO_ENUM_COUNT] = {
|
const char* BPackageInfo::kElementNames[B_PACKAGE_INFO_ENUM_COUNT] = {
|
||||||
"name",
|
"name",
|
||||||
"summary",
|
"summary",
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ BPackageVersion::ToString() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!fPreRelease.IsEmpty())
|
if (!fPreRelease.IsEmpty())
|
||||||
string << '-' << fPreRelease;
|
string << '[' << fPreRelease << ']';
|
||||||
|
|
||||||
if (fRevision > 0)
|
if (fRevision > 0)
|
||||||
string << '-' << fRevision;
|
string << '-' << fRevision;
|
||||||
|
|||||||
Reference in New Issue
Block a user