16da0ce6fSIngo Weinhold/*
26da0ce6fSIngo Weinhold * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
3fa2dd9c4SAndrew Lindesay * Copyright 2016, Andrew Lindesay <apl@lindesay.co.nz>
46da0ce6fSIngo Weinhold * Distributed under the terms of the MIT License.
56da0ce6fSIngo Weinhold */
66da0ce6fSIngo Weinhold
76da0ce6fSIngo Weinhold
86da0ce6fSIngo Weinhold#include "PackageInfoParser.h"
96da0ce6fSIngo Weinhold
106da0ce6fSIngo Weinhold#include <ctype.h>
116da0ce6fSIngo Weinhold#include <stdint.h>
126a2d12cbSJonathan Schleifer#include <stdlib.h>
136da0ce6fSIngo Weinhold
146da0ce6fSIngo Weinhold#include <algorithm>
150e9ec703SIngo Weinhold#include <string>
166da0ce6fSIngo Weinhold
17fa2dd9c4SAndrew Lindesay#include <Url.h>
186da0ce6fSIngo Weinhold
196da0ce6fSIngo Weinholdnamespace BPackageKit {
206da0ce6fSIngo Weinhold
216da0ce6fSIngo Weinhold
226da0ce6fSIngo WeinholdBPackageInfo::ParseErrorListener::~ParseErrorListener()
236da0ce6fSIngo Weinhold{
246da0ce6fSIngo Weinhold}
256da0ce6fSIngo Weinhold
266da0ce6fSIngo Weinhold
276da0ce6fSIngo WeinholdBPackageInfo::Parser::Parser(ParseErrorListener* listener)
286da0ce6fSIngo Weinhold	:
296da0ce6fSIngo Weinhold	fListener(listener),
306da0ce6fSIngo Weinhold	fPos(NULL)
316da0ce6fSIngo Weinhold{
326da0ce6fSIngo Weinhold}
336da0ce6fSIngo Weinhold
346da0ce6fSIngo Weinhold
356da0ce6fSIngo Weinholdstatus_t
366da0ce6fSIngo WeinholdBPackageInfo::Parser::Parse(const BString& packageInfoString,
376da0ce6fSIngo Weinhold	BPackageInfo* packageInfo)
386da0ce6fSIngo Weinhold{
396da0ce6fSIngo Weinhold	if (packageInfo == NULL)
406da0ce6fSIngo Weinhold		return B_BAD_VALUE;
416da0ce6fSIngo Weinhold
426da0ce6fSIngo Weinhold	fPos = packageInfoString.String();
436da0ce6fSIngo Weinhold
446da0ce6fSIngo Weinhold	try {
456da0ce6fSIngo Weinhold		_Parse(packageInfo);
466da0ce6fSIngo Weinhold	} catch (const ParseError& error) {
476da0ce6fSIngo Weinhold		if (fListener != NULL) {
486da0ce6fSIngo Weinhold			// map error position to line and column
496da0ce6fSIngo Weinhold			int line = 1;
5038a0419aSIngo Weinhold			int inLineOffset;
516da0ce6fSIngo Weinhold			int32 offset = error.pos - packageInfoString.String();
526da0ce6fSIngo Weinhold			int32 newlinePos = packageInfoString.FindLast('\n', offset - 1);
536da0ce6fSIngo Weinhold			if (newlinePos < 0)
5438a0419aSIngo Weinhold				inLineOffset = offset;
556da0ce6fSIngo Weinhold			else {
5638a0419aSIngo Weinhold				inLineOffset = offset - newlinePos - 1;
576da0ce6fSIngo Weinhold				do {
586da0ce6fSIngo Weinhold					line++;
596da0ce6fSIngo Weinhold					newlinePos = packageInfoString.FindLast('\n',
606da0ce6fSIngo Weinhold						newlinePos - 1);
616da0ce6fSIngo Weinhold				} while (newlinePos >= 0);
626da0ce6fSIngo Weinhold			}
6338a0419aSIngo Weinhold
6438a0419aSIngo Weinhold			int column = 0;
6538a0419aSIngo Weinhold			for (int i = 0; i < inLineOffset; i++) {
6638a0419aSIngo Weinhold				column++;
6738a0419aSIngo Weinhold				if (error.pos[i - inLineOffset] == '\t')
6838a0419aSIngo Weinhold					column = (column + 3) / 4 * 4;
6938a0419aSIngo Weinhold			}
7038a0419aSIngo Weinhold
7138a0419aSIngo Weinhold			fListener->OnError(error.message, line, column + 1);
726da0ce6fSIngo Weinhold		}
736da0ce6fSIngo Weinhold		return B_BAD_DATA;
746da0ce6fSIngo Weinhold	} catch (const std::bad_alloc& e) {
756da0ce6fSIngo Weinhold		if (fListener != NULL)
766da0ce6fSIngo Weinhold			fListener->OnError("out of memory", 0, 0);
776da0ce6fSIngo Weinhold		return B_NO_MEMORY;
786da0ce6fSIngo Weinhold	}
796da0ce6fSIngo Weinhold
806da0ce6fSIngo Weinhold	return B_OK;
816da0ce6fSIngo Weinhold}
826da0ce6fSIngo Weinhold
836da0ce6fSIngo Weinhold
846da0ce6fSIngo Weinholdstatus_t
856da0ce6fSIngo WeinholdBPackageInfo::Parser::ParseVersion(const BString& versionString,
866da0ce6fSIngo Weinhold	bool revisionIsOptional, BPackageVersion& _version)
876da0ce6fSIngo Weinhold{
886da0ce6fSIngo Weinhold	fPos = versionString.String();
896da0ce6fSIngo Weinhold
906da0ce6fSIngo Weinhold	try {
910e9ec703SIngo Weinhold		Token token(TOKEN_STRING, fPos, versionString.Length());
926da0ce6fSIngo Weinhold		_ParseVersionValue(token, &_version, revisionIsOptional);
936da0ce6fSIngo Weinhold	} catch (const ParseError& error) {
946da0ce6fSIngo Weinhold		if (fListener != NULL) {
956da0ce6fSIngo Weinhold			int32 offset = error.pos - versionString.String();
966da0ce6fSIngo Weinhold			fListener->OnError(error.message, 1, offset);
976da0ce6fSIngo Weinhold		}
986da0ce6fSIngo Weinhold		return B_BAD_DATA;
996da0ce6fSIngo Weinhold	} catch (const std::bad_alloc& e) {
1006da0ce6fSIngo Weinhold		if (fListener != NULL)
1016da0ce6fSIngo Weinhold			fListener->OnError("out of memory", 0, 0);
1026da0ce6fSIngo Weinhold		return B_NO_MEMORY;
1036da0ce6fSIngo Weinhold	}
1046da0ce6fSIngo Weinhold
1056da0ce6fSIngo Weinhold	return B_OK;
1066da0ce6fSIngo Weinhold}
1076da0ce6fSIngo Weinhold
1086da0ce6fSIngo Weinhold
10988706cfaSIngo Weinholdstatus_t
11088706cfaSIngo WeinholdBPackageInfo::Parser::ParseResolvableExpression(const BString& expressionString,
11188706cfaSIngo Weinhold	BPackageResolvableExpression& _expression)
11288706cfaSIngo Weinhold{
11388706cfaSIngo Weinhold	fPos = expressionString.String();
11488706cfaSIngo Weinhold
11588706cfaSIngo Weinhold	try {
11688706cfaSIngo Weinhold		Token token(TOKEN_STRING, fPos, expressionString.Length());
11788706cfaSIngo Weinhold		_ParseResolvableExpression(_NextToken(), _expression, NULL);
11888706cfaSIngo Weinhold	} catch (const ParseError& error) {
11988706cfaSIngo Weinhold		if (fListener != NULL) {
12088706cfaSIngo Weinhold			int32 offset = error.pos - expressionString.String();
12188706cfaSIngo Weinhold			fListener->OnError(error.message, 1, offset);
12288706cfaSIngo Weinhold		}
12388706cfaSIngo Weinhold		return B_BAD_DATA;
12488706cfaSIngo Weinhold	} catch (const std::bad_alloc& e) {
12588706cfaSIngo Weinhold		if (fListener != NULL)
12688706cfaSIngo Weinhold			fListener->OnError("out of memory", 0, 0);
12788706cfaSIngo Weinhold		return B_NO_MEMORY;
12888706cfaSIngo Weinhold	}
12988706cfaSIngo Weinhold
13088706cfaSIngo Weinhold	return B_OK;
13188706cfaSIngo Weinhold}
13288706cfaSIngo Weinhold
13388706cfaSIngo Weinhold
1346da0ce6fSIngo WeinholdBPackageInfo::Parser::Token
1356da0ce6fSIngo WeinholdBPackageInfo::Parser::_NextToken()
1366da0ce6fSIngo Weinhold{
137a98dd49aSIngo Weinhold	// Eat any whitespace, comments, or escaped new lines. Also eat ';' -- they
138a98dd49aSIngo Weinhold	// have the same function as newlines. We remember the last encountered ';'
139a98dd49aSIngo Weinhold	// or '\n' and return it as a token afterwards.
1406da0ce6fSIngo Weinhold	const char* itemSeparatorPos = NULL;
1416da0ce6fSIngo Weinhold	bool inComment = false;
1426da0ce6fSIngo Weinhold	while ((inComment && *fPos != '\0') || isspace(*fPos) || *fPos == ';'
143a98dd49aSIngo Weinhold		|| *fPos == '#' || *fPos == '\\') {
1446da0ce6fSIngo Weinhold		if (*fPos == '#') {
1456da0ce6fSIngo Weinhold			inComment = true;
146a98dd49aSIngo Weinhold		} else if (!inComment && *fPos == '\\') {
147a98dd49aSIngo Weinhold			if (fPos[1] != '\n')
148a98dd49aSIngo Weinhold				break;
149a98dd49aSIngo Weinhold			// ignore escaped line breaks
150a98dd49aSIngo Weinhold			fPos++;
1516da0ce6fSIngo Weinhold		} else if (*fPos == '\n') {
1526da0ce6fSIngo Weinhold			itemSeparatorPos = fPos;
1536da0ce6fSIngo Weinhold			inComment = false;
1546da0ce6fSIngo Weinhold		} else if (!inComment && *fPos == ';')
1556da0ce6fSIngo Weinhold			itemSeparatorPos = fPos;
1566da0ce6fSIngo Weinhold		fPos++;
1576da0ce6fSIngo Weinhold	}
1586da0ce6fSIngo Weinhold
1596da0ce6fSIngo Weinhold	if (itemSeparatorPos != NULL) {
1606da0ce6fSIngo Weinhold		return Token(TOKEN_ITEM_SEPARATOR, itemSeparatorPos);
1616da0ce6fSIngo Weinhold	}
1626da0ce6fSIngo Weinhold
1636da0ce6fSIngo Weinhold	const char* tokenPos = fPos;
1646da0ce6fSIngo Weinhold	switch (*fPos) {
1656da0ce6fSIngo Weinhold		case '\0':
1666da0ce6fSIngo Weinhold			return Token(TOKEN_EOF, fPos);
1676da0ce6fSIngo Weinhold
1686da0ce6fSIngo Weinhold		case '{':
1696da0ce6fSIngo Weinhold			fPos++;
1706da0ce6fSIngo Weinhold			return Token(TOKEN_OPEN_BRACE, tokenPos);
1716da0ce6fSIngo Weinhold
1726da0ce6fSIngo Weinhold		case '}':
1736da0ce6fSIngo Weinhold			fPos++;
1746da0ce6fSIngo Weinhold			return Token(TOKEN_CLOSE_BRACE, tokenPos);
1756da0ce6fSIngo Weinhold
1766da0ce6fSIngo Weinhold		case '<':
1776da0ce6fSIngo Weinhold			fPos++;
1786da0ce6fSIngo Weinhold			if (*fPos == '=') {
1796da0ce6fSIngo Weinhold				fPos++;
1806da0ce6fSIngo Weinhold				return Token(TOKEN_OPERATOR_LESS_EQUAL, tokenPos, 2);
1816da0ce6fSIngo Weinhold			}
1826da0ce6fSIngo Weinhold			return Token(TOKEN_OPERATOR_LESS, tokenPos, 1);
1836da0ce6fSIngo Weinhold
1846da0ce6fSIngo Weinhold		case '=':
1856da0ce6fSIngo Weinhold			fPos++;
1866da0ce6fSIngo Weinhold			if (*fPos == '=') {
1876da0ce6fSIngo Weinhold				fPos++;
1886da0ce6fSIngo Weinhold				return Token(TOKEN_OPERATOR_EQUAL, tokenPos, 2);
1896da0ce6fSIngo Weinhold			}
1906da0ce6fSIngo Weinhold			return Token(TOKEN_OPERATOR_ASSIGN, tokenPos, 1);
1916da0ce6fSIngo Weinhold
1926da0ce6fSIngo Weinhold		case '!':
1936da0ce6fSIngo Weinhold			if (fPos[1] == '=') {
1946da0ce6fSIngo Weinhold				fPos += 2;
1956da0ce6fSIngo Weinhold				return Token(TOKEN_OPERATOR_NOT_EQUAL, tokenPos, 2);
1966da0ce6fSIngo Weinhold			}
1976da0ce6fSIngo Weinhold			break;
1986da0ce6fSIngo Weinhold
1996da0ce6fSIngo Weinhold		case '>':
2006da0ce6fSIngo Weinhold			fPos++;
2016da0ce6fSIngo Weinhold			if (*fPos == '=') {
2026da0ce6fSIngo Weinhold				fPos++;
2036da0ce6fSIngo Weinhold				return Token(TOKEN_OPERATOR_GREATER_EQUAL, tokenPos, 2);
2046da0ce6fSIngo Weinhold			}
2056da0ce6fSIngo Weinhold			return Token(TOKEN_OPERATOR_GREATER, tokenPos, 1);
2066da0ce6fSIngo Weinhold
2076da0ce6fSIngo Weinhold		default:
2086da0ce6fSIngo Weinhold		{
2090e9ec703SIngo Weinhold			std::string string;
2100e9ec703SIngo Weinhold			char quoteChar = '\0';
2110e9ec703SIngo Weinhold
2120e9ec703SIngo Weinhold			for (; *fPos != '\0'; fPos++) {
2130e9ec703SIngo Weinhold				char c = *fPos;
2140e9ec703SIngo Weinhold				if (quoteChar != '\0') {
2150e9ec703SIngo Weinhold					// within a quoted string segment
2160e9ec703SIngo Weinhold					if (c == quoteChar) {
2170e9ec703SIngo Weinhold						quoteChar = '\0';
2180e9ec703SIngo Weinhold						continue;
2190e9ec703SIngo Weinhold					}
2200e9ec703SIngo Weinhold
2210e9ec703SIngo Weinhold					if (c == '\\') {
2220e9ec703SIngo Weinhold						// next char is escaped
2230e9ec703SIngo Weinhold						c = *++fPos;
2240e9ec703SIngo Weinhold						if (c == '\0') {
2250e9ec703SIngo Weinhold							throw ParseError("unterminated quoted-string",
2260e9ec703SIngo Weinhold								tokenPos);
2270e9ec703SIngo Weinhold						}
2280e9ec703SIngo Weinhold
2290e9ec703SIngo Weinhold						if (c == 'n')
2300e9ec703SIngo Weinhold							c = '\n';
2310e9ec703SIngo Weinhold						else if (c == 't')
2320e9ec703SIngo Weinhold							c = '\t';
2330e9ec703SIngo Weinhold					}
2340e9ec703SIngo Weinhold
2350e9ec703SIngo Weinhold					string += c;
2360e9ec703SIngo Weinhold				} else {
2370e9ec703SIngo Weinhold					// unquoted string segment
2380e9ec703SIngo Weinhold					switch (c) {
2390e9ec703SIngo Weinhold						case '"':
2400e9ec703SIngo Weinhold						case '\'':
2410e9ec703SIngo Weinhold							// quoted string start
2420e9ec703SIngo Weinhold							quoteChar = c;
2430e9ec703SIngo Weinhold							continue;
2440e9ec703SIngo Weinhold
2450e9ec703SIngo Weinhold						case '{':
2460e9ec703SIngo Weinhold						case '}':
2470e9ec703SIngo Weinhold						case '<':
2480e9ec703SIngo Weinhold						case '=':
2490e9ec703SIngo Weinhold						case '!':
2500e9ec703SIngo Weinhold						case '>':
2510e9ec703SIngo Weinhold							// a separator character -- this ends the string
2520e9ec703SIngo Weinhold							break;
2530e9ec703SIngo Weinhold
2540e9ec703SIngo Weinhold						case '\\':
2550e9ec703SIngo Weinhold							// next char is escaped
2560e9ec703SIngo Weinhold							c = *++fPos;
2570e9ec703SIngo Weinhold							if (c == '\0') {
2580e9ec703SIngo Weinhold								throw ParseError("'\\' at end of string",
2590e9ec703SIngo Weinhold									tokenPos);
2600e9ec703SIngo Weinhold							}
2610e9ec703SIngo Weinhold							string += c;
2620e9ec703SIngo Weinhold							continue;
2630e9ec703SIngo Weinhold
2640e9ec703SIngo Weinhold						default:
2650e9ec703SIngo Weinhold							if (isspace(c))
2660e9ec703SIngo Weinhold								break;
2670e9ec703SIngo Weinhold							string += c;
2680e9ec703SIngo Weinhold							continue;
2690e9ec703SIngo Weinhold					}
2700e9ec703SIngo Weinhold
2710e9ec703SIngo Weinhold					break;
2720e9ec703SIngo Weinhold				}
2736da0ce6fSIngo Weinhold			}
2740e9ec703SIngo Weinhold
2750e9ec703SIngo Weinhold			return Token(TOKEN_STRING, tokenPos, fPos - tokenPos,
2760e9ec703SIngo Weinhold				string.c_str());
2776da0ce6fSIngo Weinhold		}
2786da0ce6fSIngo Weinhold	}
2796da0ce6fSIngo Weinhold
2806da0ce6fSIngo Weinhold	BString error = BString("unknown token '") << *fPos << "' encountered";
2816da0ce6fSIngo Weinhold	throw ParseError(error.String(), fPos);
2826da0ce6fSIngo Weinhold}
2836da0ce6fSIngo Weinhold
2846da0ce6fSIngo Weinhold
2856da0ce6fSIngo Weinholdvoid
2866da0ce6fSIngo WeinholdBPackageInfo::Parser::_RewindTo(const Token& token)
2876da0ce6fSIngo Weinhold{
2886da0ce6fSIngo Weinhold	fPos = token.pos;
2896da0ce6fSIngo Weinhold}
2906da0ce6fSIngo Weinhold
2916da0ce6fSIngo Weinhold
2926da0ce6fSIngo Weinholdvoid
2936da0ce6fSIngo WeinholdBPackageInfo::Parser::_ParseStringValue(BString* value, const char** _tokenPos)
2946da0ce6fSIngo Weinhold{
2956da0ce6fSIngo Weinhold	Token string = _NextToken();
2960e9ec703SIngo Weinhold	if (string.type != TOKEN_STRING)
2970e9ec703SIngo Weinhold		throw ParseError("expected string", string.pos);
2986da0ce6fSIngo Weinhold
2996da0ce6fSIngo Weinhold	*value = string.text;
3006da0ce6fSIngo Weinhold	if (_tokenPos != NULL)
3016da0ce6fSIngo Weinhold		*_tokenPos = string.pos;
3026da0ce6fSIngo Weinhold}
3036da0ce6fSIngo Weinhold
3046da0ce6fSIngo Weinhold
3056da0ce6fSIngo Weinholdvoid
3066da0ce6fSIngo WeinholdBPackageInfo::Parser::_ParseArchitectureValue(BPackageArchitecture* value)
3076da0ce6fSIngo Weinhold{
3086da0ce6fSIngo Weinhold	Token arch = _NextToken();
3090e9ec703SIngo Weinhold	if (arch.type == TOKEN_STRING) {
3106da0ce6fSIngo Weinhold		for (int i = 0; i < B_PACKAGE_ARCHITECTURE_ENUM_COUNT; ++i) {
3116da0ce6fSIngo Weinhold			if (arch.text.ICompare(BPackageInfo::kArchitectureNames[i]) == 0) {
3126da0ce6fSIngo Weinhold				*value = (BPackageArchitecture)i;
3136da0ce6fSIngo Weinhold				return;
3146da0ce6fSIngo Weinhold			}
3156da0ce6fSIngo Weinhold		}
3166da0ce6fSIngo Weinhold	}
3176da0ce6fSIngo Weinhold
3186da0ce6fSIngo Weinhold	BString error("architecture must be one of: [");
3196da0ce6fSIngo Weinhold	for (int i = 0; i < B_PACKAGE_ARCHITECTURE_ENUM_COUNT; ++i) {
3206da0ce6fSIngo Weinhold		if (i > 0)
3216da0ce6fSIngo Weinhold			error << ",";
3226da0ce6fSIngo Weinhold		error << BPackageInfo::kArchitectureNames[i];
3236da0ce6fSIngo Weinhold	}
3246da0ce6fSIngo Weinhold	error << "]";
3256da0ce6fSIngo Weinhold	throw ParseError(error, arch.pos);
3266da0ce6fSIngo Weinhold}
3276da0ce6fSIngo Weinhold
3286da0ce6fSIngo Weinhold
3296da0ce6fSIngo Weinholdvoid
3306da0ce6fSIngo WeinholdBPackageInfo::Parser::_ParseVersionValue(BPackageVersion* value,
3316da0ce6fSIngo Weinhold	bool revisionIsOptional)
3326da0ce6fSIngo Weinhold{
3336da0ce6fSIngo Weinhold	Token word = _NextToken();
3346da0ce6fSIngo Weinhold	_ParseVersionValue(word, value, revisionIsOptional);
3356da0ce6fSIngo Weinhold}
3366da0ce6fSIngo Weinhold
3376da0ce6fSIngo Weinhold
3386da0ce6fSIngo Weinhold/*static*/ void
3396da0ce6fSIngo WeinholdBPackageInfo::Parser::_ParseVersionValue(Token& word, BPackageVersion* value,
3406da0ce6fSIngo Weinhold	bool revisionIsOptional)
3416da0ce6fSIngo Weinhold{
3420e9ec703SIngo Weinhold	if (word.type != TOKEN_STRING)
3430e9ec703SIngo Weinhold		throw ParseError("expected string (a version)", word.pos);
3446da0ce6fSIngo Weinhold
3456da0ce6fSIngo Weinhold	// get the revision number
3466da0ce6fSIngo Weinhold	uint32 revision = 0;
3476da0ce6fSIngo Weinhold	int32 dashPos = word.text.FindLast('-');
3486da0ce6fSIngo Weinhold	if (dashPos >= 0) {
3496da0ce6fSIngo Weinhold		char* end;
3506da0ce6fSIngo Weinhold		long long number = strtoll(word.text.String() + dashPos + 1, &end,
3516da0ce6fSIngo Weinhold			0);
3526da0ce6fSIngo Weinhold		if (*end != '\0' || number < 0 || number > UINT_MAX) {
3536da0ce6fSIngo Weinhold			throw ParseError("revision must be a number > 0 and < UINT_MAX",
3546da0ce6fSIngo Weinhold				word.pos + dashPos + 1);
3556da0ce6fSIngo Weinhold		}
3566da0ce6fSIngo Weinhold
3576da0ce6fSIngo Weinhold		revision = (uint32)number;
3586da0ce6fSIngo Weinhold		word.text.Truncate(dashPos);
3596da0ce6fSIngo Weinhold	}
3606da0ce6fSIngo Weinhold
3616da0ce6fSIngo Weinhold	if (revision == 0 && !revisionIsOptional) {
3626da0ce6fSIngo Weinhold		throw ParseError("expected revision number (-<number> suffix)",
3636da0ce6fSIngo Weinhold			word.pos + word.text.Length());
3646da0ce6fSIngo Weinhold	}
3656da0ce6fSIngo Weinhold
3666da0ce6fSIngo Weinhold	// get the pre-release string
3676da0ce6fSIngo Weinhold	BString preRelease;
3686da0ce6fSIngo Weinhold	int32 tildePos = word.text.FindLast('~');
3696da0ce6fSIngo Weinhold	if (tildePos >= 0) {
3706da0ce6fSIngo Weinhold		word.text.CopyInto(preRelease, tildePos + 1,
3716da0ce6fSIngo Weinhold			word.text.Length() - tildePos - 1);
3726da0ce6fSIngo Weinhold		word.text.Truncate(tildePos);
3736da0ce6fSIngo Weinhold
3746da0ce6fSIngo Weinhold		if (preRelease.IsEmpty()) {
3756da0ce6fSIngo Weinhold			throw ParseError("invalid empty pre-release string",
3766da0ce6fSIngo Weinhold				word.pos + tildePos + 1);
3776da0ce6fSIngo Weinhold		}
3786da0ce6fSIngo Weinhold
3796da0ce6fSIngo Weinhold		int32 errorPos;
3806da0ce6fSIngo Weinhold		if (!_IsAlphaNumUnderscore(preRelease, ".", &errorPos)) {
3816da0ce6fSIngo Weinhold			throw ParseError("invalid character in pre-release string",
3826da0ce6fSIngo Weinhold				word.pos + tildePos + 1 + errorPos);
3836da0ce6fSIngo Weinhold		}
3846da0ce6fSIngo Weinhold	}
3856da0ce6fSIngo Weinhold
3866da0ce6fSIngo Weinhold	// get major, minor, and micro strings
3876da0ce6fSIngo Weinhold	BString major;
3886da0ce6fSIngo Weinhold	BString minor;
3896da0ce6fSIngo Weinhold	BString micro;
3906da0ce6fSIngo Weinhold	int32 firstDotPos = word.text.FindFirst('.');
3916da0ce6fSIngo Weinhold	if (firstDotPos < 0)
3926da0ce6fSIngo Weinhold		major = word.text;
3936da0ce6fSIngo Weinhold	else {
3946da0ce6fSIngo Weinhold		word.text.CopyInto(major, 0, firstDotPos);
3956da0ce6fSIngo Weinhold		int32 secondDotPos = word.text.FindFirst('.', firstDotPos + 1);
3966da0ce6fSIngo Weinhold		if (secondDotPos == firstDotPos + 1)
3976da0ce6fSIngo Weinhold			throw ParseError("expected minor version", word.pos + secondDotPos);
3986da0ce6fSIngo Weinhold
3996da0ce6fSIngo Weinhold		if (secondDotPos < 0) {
4006da0ce6fSIngo Weinhold			word.text.CopyInto(minor, firstDotPos + 1, word.text.Length());
4016da0ce6fSIngo Weinhold		} else {
4026da0ce6fSIngo Weinhold			word.text.CopyInto(minor, firstDotPos + 1,
4036da0ce6fSIngo Weinhold				secondDotPos - (firstDotPos + 1));
4046da0ce6fSIngo Weinhold			word.text.CopyInto(micro, secondDotPos + 1, word.text.Length());
4056da0ce6fSIngo Weinhold
4066da0ce6fSIngo Weinhold			int32 errorPos;
4076da0ce6fSIngo Weinhold			if (!_IsAlphaNumUnderscore(micro, ".", &errorPos)) {
4086da0ce6fSIngo Weinhold				throw ParseError("invalid character in micro version string",
4096da0ce6fSIngo Weinhold					word.pos + secondDotPos + 1 + errorPos);
4106da0ce6fSIngo Weinhold			}
4116da0ce6fSIngo Weinhold		}
4126da0ce6fSIngo Weinhold
4136da0ce6fSIngo Weinhold		int32 errorPos;
4146da0ce6fSIngo Weinhold		if (!_IsAlphaNumUnderscore(minor, "", &errorPos)) {
4156da0ce6fSIngo Weinhold			throw ParseError("invalid character in minor version string",
4166da0ce6fSIngo Weinhold				word.pos + firstDotPos + 1 + errorPos);
4176da0ce6fSIngo Weinhold		}
4186da0ce6fSIngo Weinhold	}
4196da0ce6fSIngo Weinhold
4206da0ce6fSIngo Weinhold	int32 errorPos;
4216da0ce6fSIngo Weinhold	if (!_IsAlphaNumUnderscore(major, "", &errorPos)) {
4226da0ce6fSIngo Weinhold		throw ParseError("invalid character in major version string",
4236da0ce6fSIngo Weinhold			word.pos + errorPos);
4246da0ce6fSIngo Weinhold	}
4256da0ce6fSIngo Weinhold
4266da0ce6fSIngo Weinhold	value->SetTo(major, minor, micro, preRelease, revision);
4276da0ce6fSIngo Weinhold}
4286da0ce6fSIngo Weinhold
4296da0ce6fSIngo Weinhold
43088706cfaSIngo Weinholdvoid
43188706cfaSIngo WeinholdBPackageInfo::Parser::_ParseResolvableExpression(const Token& token,
43288706cfaSIngo Weinhold	BPackageResolvableExpression& _value, BString* _basePackage)
43388706cfaSIngo Weinhold{
43488706cfaSIngo Weinhold	if (token.type != TOKEN_STRING) {
43588706cfaSIngo Weinhold		throw ParseError("expected word (a resolvable name)",
43688706cfaSIngo Weinhold			token.pos);
43788706cfaSIngo Weinhold	}
43888706cfaSIngo Weinhold
43988706cfaSIngo Weinhold	int32 errorPos;
44088706cfaSIngo Weinhold	if (!_IsValidResolvableName(token.text, &errorPos)) {
44188706cfaSIngo Weinhold		throw ParseError("invalid character in resolvable name",
44288706cfaSIngo Weinhold			token.pos + errorPos);
44388706cfaSIngo Weinhold	}
44488706cfaSIngo Weinhold
44588706cfaSIngo Weinhold	BPackageVersion version;
44688706cfaSIngo Weinhold	Token op = _NextToken();
44788706cfaSIngo Weinhold	BPackageResolvableOperator resolvableOperator;
44888706cfaSIngo Weinhold	if (op.type == TOKEN_OPERATOR_LESS
44988706cfaSIngo Weinhold		|| op.type == TOKEN_OPERATOR_LESS_EQUAL
45088706cfaSIngo Weinhold		|| op.type == TOKEN_OPERATOR_EQUAL
45188706cfaSIngo Weinhold		|| op.type == TOKEN_OPERATOR_NOT_EQUAL
45288706cfaSIngo Weinhold		|| op.type == TOKEN_OPERATOR_GREATER_EQUAL
45388706cfaSIngo Weinhold		|| op.type == TOKEN_OPERATOR_GREATER) {
45488706cfaSIngo Weinhold		_ParseVersionValue(&version, true);
45588706cfaSIngo Weinhold
45688706cfaSIngo Weinhold		if (_basePackage != NULL) {
45788706cfaSIngo Weinhold			Token base = _NextToken();
45888706cfaSIngo Weinhold			if (base.type == TOKEN_STRING && base.text == "base") {
45988706cfaSIngo Weinhold				if (!_basePackage->IsEmpty()) {
46088706cfaSIngo Weinhold					throw ParseError("multiple packages marked as base package",
46188706cfaSIngo Weinhold						token.pos);
46288706cfaSIngo Weinhold				}
46388706cfaSIngo Weinhold
46488706cfaSIngo Weinhold				*_basePackage = token.text;
46588706cfaSIngo Weinhold			} else
46688706cfaSIngo Weinhold				_RewindTo(base);
46788706cfaSIngo Weinhold		}
46888706cfaSIngo Weinhold
46988706cfaSIngo Weinhold		resolvableOperator = (BPackageResolvableOperator)
47088706cfaSIngo Weinhold			(op.type - TOKEN_OPERATOR_LESS);
47188706cfaSIngo Weinhold	} else if (op.type == TOKEN_ITEM_SEPARATOR
47288706cfaSIngo Weinhold		|| op.type == TOKEN_CLOSE_BRACE || op.type == TOKEN_EOF) {
47388706cfaSIngo Weinhold		_RewindTo(op);
47488706cfaSIngo Weinhold		resolvableOperator = B_PACKAGE_RESOLVABLE_OP_ENUM_COUNT;
47588706cfaSIngo Weinhold	} else {
47688706cfaSIngo Weinhold		throw ParseError(
47788706cfaSIngo Weinhold			"expected '<', '<=', '==', '!=', '>=', '>', comma or '}'",
47888706cfaSIngo Weinhold			op.pos);
47988706cfaSIngo Weinhold	}
48088706cfaSIngo Weinhold
48188706cfaSIngo Weinhold	_value.SetTo(token.text, resolvableOperator, version);
48288706cfaSIngo Weinhold}
48388706cfaSIngo Weinhold
48488706cfaSIngo Weinhold
4856da0ce6fSIngo Weinholdvoid
4866da0ce6fSIngo WeinholdBPackageInfo::Parser::_ParseList(ListElementParser& elementParser,
4876da0ce6fSIngo Weinhold	bool allowSingleNonListElement)
4886da0ce6fSIngo Weinhold{
4896da0ce6fSIngo Weinhold	Token openBracket = _NextToken();
4906da0ce6fSIngo Weinhold	if (openBracket.type != TOKEN_OPEN_BRACE) {
4916da0ce6fSIngo Weinhold		if (!allowSingleNonListElement)
4926da0ce6fSIngo Weinhold			throw ParseError("expected start of list ('{')", openBracket.pos);
4936da0ce6fSIngo Weinhold
4946da0ce6fSIngo Weinhold		elementParser(openBracket);
4956da0ce6fSIngo Weinhold		return;
4966da0ce6fSIngo Weinhold	}
4976da0ce6fSIngo Weinhold
4986da0ce6fSIngo Weinhold	while (true) {
4996da0ce6fSIngo Weinhold		Token token = _NextToken();
5006da0ce6fSIngo Weinhold		if (token.type == TOKEN_CLOSE_BRACE)
5016da0ce6fSIngo Weinhold			return;
5026da0ce6fSIngo Weinhold
5036da0ce6fSIngo Weinhold		if (token.type == TOKEN_ITEM_SEPARATOR)
5046da0ce6fSIngo Weinhold			continue;
5056da0ce6fSIngo Weinhold
5066da0ce6fSIngo Weinhold		elementParser(token);
5076da0ce6fSIngo Weinhold	}
5086da0ce6fSIngo Weinhold}
5096da0ce6fSIngo Weinhold
5106da0ce6fSIngo Weinhold
5116da0ce6fSIngo Weinholdvoid
5126da0ce6fSIngo WeinholdBPackageInfo::Parser::_ParseStringList(BStringList* value,
513fa2dd9c4SAndrew Lindesay	bool requireResolvableName, bool convertToLowerCase,
514fa2dd9c4SAndrew Lindesay	StringValidator* stringValidator)
5156da0ce6fSIngo Weinhold{
5166da0ce6fSIngo Weinhold	struct StringParser : public ListElementParser {
5176da0ce6fSIngo Weinhold		BStringList* value;
5180e9ec703SIngo Weinhold		bool requireResolvableName;
5196da0ce6fSIngo Weinhold		bool convertToLowerCase;
520fa2dd9c4SAndrew Lindesay		StringValidator* stringValidator;
5216da0ce6fSIngo Weinhold
5220e9ec703SIngo Weinhold		StringParser(BStringList* value, bool requireResolvableName,
523fa2dd9c4SAndrew Lindesay			bool convertToLowerCase, StringValidator* stringValidator)
5246da0ce6fSIngo Weinhold			:
5256da0ce6fSIngo Weinhold			value(value),
5260e9ec703SIngo Weinhold			requireResolvableName(requireResolvableName),
527fa2dd9c4SAndrew Lindesay			convertToLowerCase(convertToLowerCase),
528fa2dd9c4SAndrew Lindesay			stringValidator(stringValidator)
5296da0ce6fSIngo Weinhold		{
5306da0ce6fSIngo Weinhold		}
5316da0ce6fSIngo Weinhold
5326da0ce6fSIngo Weinhold		virtual void operator()(const Token& token)
5336da0ce6fSIngo Weinhold		{
5340e9ec703SIngo Weinhold			if (token.type != TOKEN_STRING)
5350e9ec703SIngo Weinhold				throw ParseError("expected string", token.pos);
5360e9ec703SIngo Weinhold
5370e9ec703SIngo Weinhold			if (requireResolvableName) {
5380e9ec703SIngo Weinhold				int32 errorPos;
5390e9ec703SIngo Weinhold				if (!_IsValidResolvableName(token.text, &errorPos)) {
5400e9ec703SIngo Weinhold					throw ParseError("invalid character in resolvable name",
5410e9ec703SIngo Weinhold						token.pos + errorPos);
5426da0ce6fSIngo Weinhold				}
5436da0ce6fSIngo Weinhold			}
5446da0ce6fSIngo Weinhold
5456da0ce6fSIngo Weinhold			BString element(token.text);
5466da0ce6fSIngo Weinhold			if (convertToLowerCase)
5476da0ce6fSIngo Weinhold				element.ToLower();
5486da0ce6fSIngo Weinhold
549fa2dd9c4SAndrew Lindesay			if (stringValidator != NULL)
550fa2dd9c4SAndrew Lindesay				stringValidator->Validate(element, token.pos);
551fa2dd9c4SAndrew Lindesay
5526da0ce6fSIngo Weinhold			value->Add(element);
5536da0ce6fSIngo Weinhold		}
554fa2dd9c4SAndrew Lindesay	} stringParser(value, requireResolvableName, convertToLowerCase,
555fa2dd9c4SAndrew Lindesay		stringValidator);
5566da0ce6fSIngo Weinhold
5576da0ce6fSIngo Weinhold	_ParseList(stringParser, true);
5586da0ce6fSIngo Weinhold}
5596da0ce6fSIngo Weinhold
5606da0ce6fSIngo Weinhold
5616da0ce6fSIngo Weinholduint32
5626da0ce6fSIngo WeinholdBPackageInfo::Parser::_ParseFlags()
5636da0ce6fSIngo Weinhold{
5646da0ce6fSIngo Weinhold	struct FlagParser : public ListElementParser {
5656da0ce6fSIngo Weinhold		uint32 flags;
5666da0ce6fSIngo Weinhold
5676da0ce6fSIngo Weinhold		FlagParser()
5686da0ce6fSIngo Weinhold			:
5696da0ce6fSIngo Weinhold			flags(0)
5706da0ce6fSIngo Weinhold		{
5716da0ce6fSIngo Weinhold		}
5726da0ce6fSIngo Weinhold
5736da0ce6fSIngo Weinhold		virtual void operator()(const Token& token)
5746da0ce6fSIngo Weinhold		{
5750e9ec703SIngo Weinhold			if (token.type != TOKEN_STRING)
576276c321bSIngo Weinhold				throw ParseError("expected word (a flag)", token.pos);
577276c321bSIngo Weinhold
578276c321bSIngo Weinhold			if (token.text.ICompare("approve_license") == 0)
579276c321bSIngo Weinhold				flags |= B_PACKAGE_FLAG_APPROVE_LICENSE;
580276c321bSIngo Weinhold			else if (token.text.ICompare("system_package") == 0)
581276c321bSIngo Weinhold				flags |= B_PACKAGE_FLAG_SYSTEM_PACKAGE;
582276c321bSIngo Weinhold			else {
5836da0ce6fSIngo Weinhold				throw ParseError(
5846da0ce6fSIngo Weinhold					"expected 'approve_license' or 'system_package'",
585276c321bSIngo Weinhold					token.pos);
586276c321bSIngo Weinhold			}
5876da0ce6fSIngo Weinhold		}
5886da0ce6fSIngo Weinhold	} flagParser;
5896da0ce6fSIngo Weinhold
5906da0ce6fSIngo Weinhold	_ParseList(flagParser, true);
5916da0ce6fSIngo Weinhold
5926da0ce6fSIngo Weinhold	return flagParser.flags;
5936da0ce6fSIngo Weinhold}
5946da0ce6fSIngo Weinhold
5956da0ce6fSIngo Weinhold
5966da0ce6fSIngo Weinholdvoid
5976da0ce6fSIngo WeinholdBPackageInfo::Parser::_ParseResolvableList(
5986da0ce6fSIngo Weinhold	BObjectList<BPackageResolvable>* value)
5996da0ce6fSIngo Weinhold{
6006da0ce6fSIngo Weinhold	struct ResolvableParser : public ListElementParser {
6016da0ce6fSIngo Weinhold		Parser& parser;
6026da0ce6fSIngo Weinhold		BObjectList<BPackageResolvable>* value;
6036da0ce6fSIngo Weinhold
6046da0ce6fSIngo Weinhold		ResolvableParser(Parser& parser_,
6056da0ce6fSIngo Weinhold			BObjectList<BPackageResolvable>* value_)
6066da0ce6fSIngo Weinhold			:
6076da0ce6fSIngo Weinhold			parser(parser_),
6086da0ce6fSIngo Weinhold			value(value_)
6096da0ce6fSIngo Weinhold		{
6106da0ce6fSIngo Weinhold		}
6116da0ce6fSIngo Weinhold
6126da0ce6fSIngo Weinhold		virtual void operator()(const Token& token)
6136da0ce6fSIngo Weinhold		{
6140e9ec703SIngo Weinhold			if (token.type != TOKEN_STRING) {
6156da0ce6fSIngo Weinhold				throw ParseError("expected word (a resolvable name)",
6166da0ce6fSIngo Weinhold					token.pos);
6176da0ce6fSIngo Weinhold			}
6186da0ce6fSIngo Weinhold
6196da0ce6fSIngo Weinhold			int32 errorPos;
6206da0ce6fSIngo Weinhold			if (!_IsValidResolvableName(token.text, &errorPos)) {
6216da0ce6fSIngo Weinhold				throw ParseError("invalid character in resolvable name",
6226da0ce6fSIngo Weinhold					token.pos + errorPos);
6236da0ce6fSIngo Weinhold			}
6246da0ce6fSIngo Weinhold
6256da0ce6fSIngo Weinhold			// parse version
6266da0ce6fSIngo Weinhold			BPackageVersion version;
6276da0ce6fSIngo Weinhold			Token op = parser._NextToken();
6286da0ce6fSIngo Weinhold			if (op.type == TOKEN_OPERATOR_ASSIGN) {
6296da0ce6fSIngo Weinhold				parser._ParseVersionValue(&version, true);
6306da0ce6fSIngo Weinhold			} else if (op.type == TOKEN_ITEM_SEPARATOR
6316da0ce6fSIngo Weinhold				|| op.type == TOKEN_CLOSE_BRACE) {
6326da0ce6fSIngo Weinhold				parser._RewindTo(op);
6336da0ce6fSIngo Weinhold			} else
6346da0ce6fSIngo Weinhold				throw ParseError("expected '=', comma or '}'", op.pos);
6356da0ce6fSIngo Weinhold
6366da0ce6fSIngo Weinhold			// parse compatible version
6376da0ce6fSIngo Weinhold			BPackageVersion compatibleVersion;
6386da0ce6fSIngo Weinhold			Token compatible = parser._NextToken();
6390e9ec703SIngo Weinhold			if (compatible.type == TOKEN_STRING
6406da0ce6fSIngo Weinhold				&& (compatible.text == "compat"
6416da0ce6fSIngo Weinhold					|| compatible.text == "compatible")) {
6426da0ce6fSIngo Weinhold				op = parser._NextToken();
6436da0ce6fSIngo Weinhold				if (op.type == TOKEN_OPERATOR_GREATER_EQUAL) {
6446da0ce6fSIngo Weinhold					parser._ParseVersionValue(&compatibleVersion, true);
6456da0ce6fSIngo Weinhold				} else
6466da0ce6fSIngo Weinhold					parser._RewindTo(compatible);
6476da0ce6fSIngo Weinhold			} else
6486da0ce6fSIngo Weinhold				parser._RewindTo(compatible);
6496da0ce6fSIngo Weinhold
6506da0ce6fSIngo Weinhold			value->AddItem(new BPackageResolvable(token.text, version,
6516da0ce6fSIngo Weinhold				compatibleVersion));
6526da0ce6fSIngo Weinhold		}
6536da0ce6fSIngo Weinhold	} resolvableParser(*this, value);
6546da0ce6fSIngo Weinhold
6556da0ce6fSIngo Weinhold	_ParseList(resolvableParser, false);
6566da0ce6fSIngo Weinhold}
6576da0ce6fSIngo Weinhold
6586da0ce6fSIngo Weinhold
6596da0ce6fSIngo Weinholdvoid
6606da0ce6fSIngo WeinholdBPackageInfo::Parser::_ParseResolvableExprList(
6616da0ce6fSIngo Weinhold	BObjectList<BPackageResolvableExpression>* value, BString* _basePackage)
6626da0ce6fSIngo Weinhold{
6636da0ce6fSIngo Weinhold	struct ResolvableExpressionParser : public ListElementParser {
6646da0ce6fSIngo Weinhold		Parser& parser;
6656da0ce6fSIngo Weinhold		BObjectList<BPackageResolvableExpression>* value;
6666da0ce6fSIngo Weinhold		BString* basePackage;
6676da0ce6fSIngo Weinhold
6686da0ce6fSIngo Weinhold		ResolvableExpressionParser(Parser& parser,
6696da0ce6fSIngo Weinhold			BObjectList<BPackageResolvableExpression>* value,
6706da0ce6fSIngo Weinhold			BString* basePackage)
6716da0ce6fSIngo Weinhold			:
6726da0ce6fSIngo Weinhold			parser(parser),
6736da0ce6fSIngo Weinhold			value(value),
6746da0ce6fSIngo Weinhold			basePackage(basePackage)
6756da0ce6fSIngo Weinhold		{
6766da0ce6fSIngo Weinhold		}
6776da0ce6fSIngo Weinhold
6786da0ce6fSIngo Weinhold		virtual void operator()(const Token& token)
6796da0ce6fSIngo Weinhold		{
68088706cfaSIngo Weinhold			BPackageResolvableExpression expression;
68188706cfaSIngo Weinhold			parser._ParseResolvableExpression(token, expression, basePackage);
68288706cfaSIngo Weinhold			value->AddItem(new BPackageResolvableExpression(expression));
6836da0ce6fSIngo Weinhold		}
6846da0ce6fSIngo Weinhold	} resolvableExpressionParser(*this, value, _basePackage);
6856da0ce6fSIngo Weinhold
6866da0ce6fSIngo Weinhold	_ParseList(resolvableExpressionParser, false);
6876da0ce6fSIngo Weinhold}
6886da0ce6fSIngo Weinhold
6896da0ce6fSIngo Weinhold
6906da0ce6fSIngo Weinholdvoid
6914489c88bSIngo WeinholdBPackageInfo::Parser::_ParseGlobalWritableFileInfos(
6924489c88bSIngo Weinhold	GlobalWritableFileInfoList* infos)
6936da0ce6fSIngo Weinhold{
6944489c88bSIngo Weinhold	struct GlobalWritableFileInfoParser : public ListElementParser {
6956da0ce6fSIngo Weinhold		Parser& parser;
6964489c88bSIngo Weinhold		GlobalWritableFileInfoList* infos;
6976da0ce6fSIngo Weinhold
6984489c88bSIngo Weinhold		GlobalWritableFileInfoParser(Parser& parser,
6994489c88bSIngo Weinhold			GlobalWritableFileInfoList* infos)
7006da0ce6fSIngo Weinhold			:
7016da0ce6fSIngo Weinhold			parser(parser),
7026da0ce6fSIngo Weinhold			infos(infos)
7036da0ce6fSIngo Weinhold		{
7046da0ce6fSIngo Weinhold		}
7056da0ce6fSIngo Weinhold
7066da0ce6fSIngo Weinhold		virtual void operator()(const Token& token)
7076da0ce6fSIngo Weinhold		{
7080e9ec703SIngo Weinhold			if (token.type != TOKEN_STRING) {
7090e9ec703SIngo Weinhold				throw ParseError("expected string (a file path)",
7106da0ce6fSIngo Weinhold					token.pos);
7116da0ce6fSIngo Weinhold			}
7126da0ce6fSIngo Weinhold
7134489c88bSIngo Weinhold			BWritableFileUpdateType updateType
7144489c88bSIngo Weinhold				= B_WRITABLE_FILE_UPDATE_TYPE_ENUM_COUNT;
7154489c88bSIngo Weinhold			bool isDirectory = false;
7166da0ce6fSIngo Weinhold
7176da0ce6fSIngo Weinhold			Token nextToken = parser._NextToken();
7180e9ec703SIngo Weinhold			if (nextToken.type == TOKEN_STRING
7190e9ec703SIngo Weinhold				&& nextToken.text == "directory") {
7204489c88bSIngo Weinhold				isDirectory = true;
7214489c88bSIngo Weinhold				nextToken = parser._NextToken();
7224489c88bSIngo Weinhold			}
7234489c88bSIngo Weinhold
7240e9ec703SIngo Weinhold			if (nextToken.type == TOKEN_STRING) {
7254489c88bSIngo Weinhold				const char* const* end = kWritableFileUpdateTypes
7264489c88bSIngo Weinhold					+ B_WRITABLE_FILE_UPDATE_TYPE_ENUM_COUNT;
7274489c88bSIngo Weinhold				const char* const* found = std::find(kWritableFileUpdateTypes,
7286da0ce6fSIngo Weinhold					end, nextToken.text);
7296da0ce6fSIngo Weinhold				if (found == end) {
7306da0ce6fSIngo Weinhold					throw ParseError(BString("expected an update type"),
7316da0ce6fSIngo Weinhold						nextToken.pos);
7326da0ce6fSIngo Weinhold				}
7334489c88bSIngo Weinhold				updateType = (BWritableFileUpdateType)(
7344489c88bSIngo Weinhold					found - kWritableFileUpdateTypes);
7356da0ce6fSIngo Weinhold			} else if (nextToken.type == TOKEN_ITEM_SEPARATOR
7366da0ce6fSIngo Weinhold				|| nextToken.type == TOKEN_CLOSE_BRACE) {
7376da0ce6fSIngo Weinhold				parser._RewindTo(nextToken);
7386da0ce6fSIngo Weinhold			} else {
7396da0ce6fSIngo Weinhold				throw ParseError(
7406da0ce6fSIngo Weinhold					"expected 'included', semicolon, new line or '}'",
7416da0ce6fSIngo Weinhold					nextToken.pos);
7426da0ce6fSIngo Weinhold			}
7436da0ce6fSIngo Weinhold
7444489c88bSIngo Weinhold			if (!infos->AddItem(new BGlobalWritableFileInfo(token.text,
7454489c88bSIngo Weinhold					updateType, isDirectory))) {
7466da0ce6fSIngo Weinhold				throw std::bad_alloc();
7476da0ce6fSIngo Weinhold			}
7486da0ce6fSIngo Weinhold		}
7496da0ce6fSIngo Weinhold	} resolvableExpressionParser(*this, infos);
7506da0ce6fSIngo Weinhold
7516da0ce6fSIngo Weinhold	_ParseList(resolvableExpressionParser, false);
7526da0ce6fSIngo Weinhold}
7536da0ce6fSIngo Weinhold
7546da0ce6fSIngo Weinhold
7556da0ce6fSIngo Weinholdvoid
7566da0ce6fSIngo WeinholdBPackageInfo::Parser::_ParseUserSettingsFileInfos(
7576da0ce6fSIngo Weinhold	UserSettingsFileInfoList* infos)
7586da0ce6fSIngo Weinhold{
7596da0ce6fSIngo Weinhold	struct UserSettingsFileInfoParser : public ListElementParser {
7606da0ce6fSIngo Weinhold		Parser& parser;
7616da0ce6fSIngo Weinhold		UserSettingsFileInfoList* infos;
7626da0ce6fSIngo Weinhold
7636da0ce6fSIngo Weinhold		UserSettingsFileInfoParser(Parser& parser,
7646da0ce6fSIngo Weinhold			UserSettingsFileInfoList* infos)
7656da0ce6fSIngo Weinhold			:
7666da0ce6fSIngo Weinhold			parser(parser),
7676da0ce6fSIngo Weinhold			infos(infos)
7686da0ce6fSIngo Weinhold		{
7696da0ce6fSIngo Weinhold		}
7706da0ce6fSIngo Weinhold
7716da0ce6fSIngo Weinhold		virtual void operator()(const Token& token)
7726da0ce6fSIngo Weinhold		{
7730e9ec703SIngo Weinhold			if (token.type != TOKEN_STRING) {
7746da0ce6fSIngo Weinhold				throw ParseError("expected string (a settings file path)",
7756da0ce6fSIngo Weinhold					token.pos);
7766da0ce6fSIngo Weinhold			}
7776da0ce6fSIngo Weinhold
7786da0ce6fSIngo Weinhold			BString templatePath;
7794489c88bSIngo Weinhold			bool isDirectory = false;
7806da0ce6fSIngo Weinhold
7816da0ce6fSIngo Weinhold			Token nextToken = parser._NextToken();
7820e9ec703SIngo Weinhold			if (nextToken.type == TOKEN_STRING
7830e9ec703SIngo Weinhold				&& nextToken.text == "directory") {
7844489c88bSIngo Weinhold				isDirectory = true;
7850e9ec703SIngo Weinhold			} else if (nextToken.type == TOKEN_STRING
7864489c88bSIngo Weinhold				&& nextToken.text == "template") {
7876da0ce6fSIngo Weinhold				nextToken = parser._NextToken();
7880e9ec703SIngo Weinhold				if (nextToken.type != TOKEN_STRING) {
7896da0ce6fSIngo Weinhold					throw ParseError(
7906da0ce6fSIngo Weinhold						"expected string (a settings template file path)",
7916da0ce6fSIngo Weinhold						nextToken.pos);
7926da0ce6fSIngo Weinhold				}
7936da0ce6fSIngo Weinhold				templatePath = nextToken.text;
7946da0ce6fSIngo Weinhold			} else if (nextToken.type == TOKEN_ITEM_SEPARATOR
7956da0ce6fSIngo Weinhold				|| nextToken.type == TOKEN_CLOSE_BRACE) {
7966da0ce6fSIngo Weinhold				parser._RewindTo(nextToken);
7976da0ce6fSIngo Weinhold			} else {
7986da0ce6fSIngo Weinhold				throw ParseError(
7996da0ce6fSIngo Weinhold					"expected 'template', semicolon, new line or '}'",
8006da0ce6fSIngo Weinhold					nextToken.pos);
8016da0ce6fSIngo Weinhold			}
8026da0ce6fSIngo Weinhold
8034489c88bSIngo Weinhold			if (isDirectory
8044489c88bSIngo Weinhold				? !infos->AddItem(new BUserSettingsFileInfo(token.text, true))
8054489c88bSIngo Weinhold				: !infos->AddItem(new BUserSettingsFileInfo(token.text,
8064489c88bSIngo Weinhold						templatePath))) {
8076da0ce6fSIngo Weinhold				throw std::bad_alloc();
8086da0ce6fSIngo Weinhold			}
8096da0ce6fSIngo Weinhold		}
8106da0ce6fSIngo Weinhold	} resolvableExpressionParser(*this, infos);
8116da0ce6fSIngo Weinhold
8126da0ce6fSIngo Weinhold	_ParseList(resolvableExpressionParser, false);
8136da0ce6fSIngo Weinhold}
8146da0ce6fSIngo Weinhold
8156da0ce6fSIngo Weinhold
8160f4e11e7SIngo Weinholdvoid
8170f4e11e7SIngo WeinholdBPackageInfo::Parser::_ParseUsers(UserList* users)
8180f4e11e7SIngo Weinhold{
8190f4e11e7SIngo Weinhold	struct UserParser : public ListElementParser {
8200f4e11e7SIngo Weinhold		Parser& parser;
8210f4e11e7SIngo Weinhold		UserList* users;
8220f4e11e7SIngo Weinhold
8230f4e11e7SIngo Weinhold		UserParser(Parser& parser, UserList* users)
8240f4e11e7SIngo Weinhold			:
8250f4e11e7SIngo Weinhold			parser(parser),
8260f4e11e7SIngo Weinhold			users(users)
8270f4e11e7SIngo Weinhold		{
8280f4e11e7SIngo Weinhold		}
8290f4e11e7SIngo Weinhold
8300f4e11e7SIngo Weinhold		virtual void operator()(const Token& token)
8310f4e11e7SIngo Weinhold		{
8320e9ec703SIngo Weinhold			if (token.type != TOKEN_STRING
8330e9ec703SIngo Weinhold				|| !BUser::IsValidUserName(token.text)) {
8340e9ec703SIngo Weinhold				throw ParseError("expected a user name", token.pos);
8350f4e11e7SIngo Weinhold			}
8360f4e11e7SIngo Weinhold
8370f4e11e7SIngo Weinhold			BString realName;
8380f4e11e7SIngo Weinhold			BString home;
8390f4e11e7SIngo Weinhold			BString shell;
8400f4e11e7SIngo Weinhold			BStringList groups;
8410f4e11e7SIngo Weinhold
8420f4e11e7SIngo Weinhold			for (;;) {
8430f4e11e7SIngo Weinhold				Token nextToken = parser._NextToken();
8440e9ec703SIngo Weinhold				if (nextToken.type != TOKEN_STRING) {
8450f4e11e7SIngo Weinhold					parser._RewindTo(nextToken);
8460f4e11e7SIngo Weinhold					break;
8470f4e11e7SIngo Weinhold				}
8480f4e11e7SIngo Weinhold
8490f4e11e7SIngo Weinhold				if (nextToken.text == "real-name") {
8500f4e11e7SIngo Weinhold					nextToken = parser._NextToken();
8510e9ec703SIngo Weinhold					if (nextToken.type != TOKEN_STRING) {
8520f4e11e7SIngo Weinhold						throw ParseError("expected string (a user real name)",
8530f4e11e7SIngo Weinhold							nextToken.pos);
8540f4e11e7SIngo Weinhold					}
8550f4e11e7SIngo Weinhold					realName = nextToken.text;
8560f4e11e7SIngo Weinhold				} else if (nextToken.text == "home") {
8570f4e11e7SIngo Weinhold					nextToken = parser._NextToken();
8580e9ec703SIngo Weinhold					if (nextToken.type != TOKEN_STRING) {
8590f4e11e7SIngo Weinhold						throw ParseError("expected string (a home path)",
8600f4e11e7SIngo Weinhold							nextToken.pos);
8610f4e11e7SIngo Weinhold					}
8620f4e11e7SIngo Weinhold					home = nextToken.text;
8630f4e11e7SIngo Weinhold				} else if (nextToken.text == "shell") {
8640f4e11e7SIngo Weinhold					nextToken = parser._NextToken();
8650e9ec703SIngo Weinhold					if (nextToken.type != TOKEN_STRING) {
8660f4e11e7SIngo Weinhold						throw ParseError("expected string (a shell path)",
8670f4e11e7SIngo Weinhold							nextToken.pos);
8680f4e11e7SIngo Weinhold					}
8690f4e11e7SIngo Weinhold					shell = nextToken.text;
8700f4e11e7SIngo Weinhold				} else if (nextToken.text == "groups") {
8710f4e11e7SIngo Weinhold					for (;;) {
8720f4e11e7SIngo Weinhold						nextToken = parser._NextToken();
8730e9ec703SIngo Weinhold						if (nextToken.type == TOKEN_STRING
8740e9ec703SIngo Weinhold							&& BUser::IsValidUserName(nextToken.text)) {
8750f4e11e7SIngo Weinhold							if (!groups.Add(nextToken.text))
8760f4e11e7SIngo Weinhold								throw std::bad_alloc();
8770f4e11e7SIngo Weinhold						} else if (nextToken.type == TOKEN_ITEM_SEPARATOR
8780f4e11e7SIngo Weinhold							|| nextToken.type == TOKEN_CLOSE_BRACE) {
8790f4e11e7SIngo Weinhold							parser._RewindTo(nextToken);
8800f4e11e7SIngo Weinhold							break;
8810f4e11e7SIngo Weinhold						} else {
8820f4e11e7SIngo Weinhold							throw ParseError("expected a group name",
8830f4e11e7SIngo Weinhold								nextToken.pos);
8840f4e11e7SIngo Weinhold						}
8850f4e11e7SIngo Weinhold					}
8860f4e11e7SIngo Weinhold					break;
8870f4e11e7SIngo Weinhold				} else {
8880f4e11e7SIngo Weinhold					throw ParseError(
8890f4e11e7SIngo Weinhold						"expected 'real-name', 'home', 'shell', or 'groups'",
8900f4e11e7SIngo Weinhold						nextToken.pos);
8910f4e11e7SIngo Weinhold				}
8920f4e11e7SIngo Weinhold			}
8930f4e11e7SIngo Weinhold
8940f4e11e7SIngo Weinhold			BString templatePath;
8950f4e11e7SIngo Weinhold
8960f4e11e7SIngo Weinhold			Token nextToken = parser._NextToken();
8970e9ec703SIngo Weinhold			if (nextToken.type == TOKEN_STRING
8980e9ec703SIngo Weinhold				&& nextToken.text == "template") {
8990f4e11e7SIngo Weinhold				nextToken = parser._NextToken();
9000e9ec703SIngo Weinhold				if (nextToken.type != TOKEN_STRING) {
9010f4e11e7SIngo Weinhold					throw ParseError(
9020f4e11e7SIngo Weinhold						"expected string (a settings template file path)",
9030f4e11e7SIngo Weinhold						nextToken.pos);
9040f4e11e7SIngo Weinhold				}
9050f4e11e7SIngo Weinhold				templatePath = nextToken.text;
9060f4e11e7SIngo Weinhold			} else if (nextToken.type == TOKEN_ITEM_SEPARATOR
9070f4e11e7SIngo Weinhold				|| nextToken.type == TOKEN_CLOSE_BRACE) {
9080f4e11e7SIngo Weinhold				parser._RewindTo(nextToken);
9090f4e11e7SIngo Weinhold			} else {
9100f4e11e7SIngo Weinhold				throw ParseError(
9110f4e11e7SIngo Weinhold					"expected 'template', semicolon, new line or '}'",
9120f4e11e7SIngo Weinhold					nextToken.pos);
9130f4e11e7SIngo Weinhold			}
9140f4e11e7SIngo Weinhold
9150f4e11e7SIngo Weinhold			if (!users->AddItem(new BUser(token.text, realName, home, shell,
9160f4e11e7SIngo Weinhold					groups))) {
9170f4e11e7SIngo Weinhold				throw std::bad_alloc();
9180f4e11e7SIngo Weinhold			}
9190f4e11e7SIngo Weinhold		}
9200f4e11e7SIngo Weinhold	} resolvableExpressionParser(*this, users);
9210f4e11e7SIngo Weinhold
9220f4e11e7SIngo Weinhold	_ParseList(resolvableExpressionParser, false);
9230f4e11e7SIngo Weinhold}
9240f4e11e7SIngo Weinhold
9250f4e11e7SIngo Weinhold
9266da0ce6fSIngo Weinholdvoid
9276da0ce6fSIngo WeinholdBPackageInfo::Parser::_Parse(BPackageInfo* packageInfo)
9286da0ce6fSIngo Weinhold{
9296da0ce6fSIngo Weinhold	bool seen[B_PACKAGE_INFO_ENUM_COUNT];
9306da0ce6fSIngo Weinhold	for (int i = 0; i < B_PACKAGE_INFO_ENUM_COUNT; ++i)
9316da0ce6fSIngo Weinhold		seen[i] = false;
9326da0ce6fSIngo Weinhold
9336da0ce6fSIngo Weinhold	const char* const* names = BPackageInfo::kElementNames;
9346da0ce6fSIngo Weinhold
9356da0ce6fSIngo Weinhold	while (Token t = _NextToken()) {
9366da0ce6fSIngo Weinhold		if (t.type == TOKEN_ITEM_SEPARATOR)
9376da0ce6fSIngo Weinhold			continue;
9386da0ce6fSIngo Weinhold
9390e9ec703SIngo Weinhold		if (t.type != TOKEN_STRING)
9400e9ec703SIngo Weinhold			throw ParseError("expected string (a variable name)", t.pos);
9416da0ce6fSIngo Weinhold
9426da0ce6fSIngo Weinhold		BPackageInfoAttributeID attribute = B_PACKAGE_INFO_ENUM_COUNT;
9436da0ce6fSIngo Weinhold		for (int i = 0; i < B_PACKAGE_INFO_ENUM_COUNT; i++) {
9446da0ce6fSIngo Weinhold			if (names[i] != NULL && t.text.ICompare(names[i]) == 0) {
9456da0ce6fSIngo Weinhold				attribute = (BPackageInfoAttributeID)i;
9466da0ce6fSIngo Weinhold				break;
9476da0ce6fSIngo Weinhold			}
9486da0ce6fSIngo Weinhold		}
9496da0ce6fSIngo Weinhold
9506da0ce6fSIngo Weinhold		if (attribute == B_PACKAGE_INFO_ENUM_COUNT) {
9516da0ce6fSIngo Weinhold			BString error = BString("unknown attribute \"") << t.text << '"';
9526da0ce6fSIngo Weinhold			throw ParseError(error, t.pos);
9536da0ce6fSIngo Weinhold		}
9546da0ce6fSIngo Weinhold
9556da0ce6fSIngo Weinhold		if (seen[attribute]) {
9566da0ce6fSIngo Weinhold			BString error = BString(names[attribute]) << " already seen!";
9576da0ce6fSIngo Weinhold			throw ParseError(error, t.pos);
9586da0ce6fSIngo Weinhold		}
9596da0ce6fSIngo Weinhold
9606da0ce6fSIngo Weinhold		switch (attribute) {
9616da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_NAME:
9626da0ce6fSIngo Weinhold			{
9636da0ce6fSIngo Weinhold				BString name;
9646da0ce6fSIngo Weinhold				const char* namePos;
9656da0ce6fSIngo Weinhold				_ParseStringValue(&name, &namePos);
9666da0ce6fSIngo Weinhold
9676da0ce6fSIngo Weinhold				int32 errorPos;
9686da0ce6fSIngo Weinhold				if (!_IsValidResolvableName(name, &errorPos)) {
9696da0ce6fSIngo Weinhold					throw ParseError("invalid character in package name",
9706da0ce6fSIngo Weinhold						namePos + errorPos);
9716da0ce6fSIngo Weinhold				}
9726da0ce6fSIngo Weinhold
9736da0ce6fSIngo Weinhold				packageInfo->SetName(name);
9746da0ce6fSIngo Weinhold				break;
9756da0ce6fSIngo Weinhold			}
9766da0ce6fSIngo Weinhold
9776da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_SUMMARY:
9786da0ce6fSIngo Weinhold			{
9796da0ce6fSIngo Weinhold				BString summary;
9806da0ce6fSIngo Weinhold				_ParseStringValue(&summary);
9816da0ce6fSIngo Weinhold				if (summary.FindFirst('\n') >= 0)
9826da0ce6fSIngo Weinhold					throw ParseError("the summary contains linebreaks", t.pos);
9836da0ce6fSIngo Weinhold				packageInfo->SetSummary(summary);
9846da0ce6fSIngo Weinhold				break;
9856da0ce6fSIngo Weinhold			}
9866da0ce6fSIngo Weinhold
9876da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_DESCRIPTION:
9886da0ce6fSIngo Weinhold				_ParseStringValue(&packageInfo->fDescription);
9896da0ce6fSIngo Weinhold				break;
9906da0ce6fSIngo Weinhold
9916da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_VENDOR:
9926da0ce6fSIngo Weinhold				_ParseStringValue(&packageInfo->fVendor);
9936da0ce6fSIngo Weinhold				break;
9946da0ce6fSIngo Weinhold
9956da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_PACKAGER:
9966da0ce6fSIngo Weinhold				_ParseStringValue(&packageInfo->fPackager);
9976da0ce6fSIngo Weinhold				break;
9986da0ce6fSIngo Weinhold
9996da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_BASE_PACKAGE:
10006da0ce6fSIngo Weinhold				_ParseStringValue(&packageInfo->fBasePackage);
10016da0ce6fSIngo Weinhold				break;
10026da0ce6fSIngo Weinhold
10036da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_ARCHITECTURE:
10046da0ce6fSIngo Weinhold				_ParseArchitectureValue(&packageInfo->fArchitecture);
10056da0ce6fSIngo Weinhold				break;
10066da0ce6fSIngo Weinhold
10076da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_VERSION:
10086da0ce6fSIngo Weinhold				_ParseVersionValue(&packageInfo->fVersion, false);
10096da0ce6fSIngo Weinhold				break;
10106da0ce6fSIngo Weinhold
10116da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_COPYRIGHTS:
10126da0ce6fSIngo Weinhold				_ParseStringList(&packageInfo->fCopyrightList);
10136da0ce6fSIngo Weinhold				break;
10146da0ce6fSIngo Weinhold
10156da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_LICENSES:
10166da0ce6fSIngo Weinhold				_ParseStringList(&packageInfo->fLicenseList);
10176da0ce6fSIngo Weinhold				break;
10186da0ce6fSIngo Weinhold
10196da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_URLS:
1020fa2dd9c4SAndrew Lindesay			{
1021fa2dd9c4SAndrew Lindesay				UrlStringValidator stringValidator;
1022fa2dd9c4SAndrew Lindesay				_ParseStringList(&packageInfo->fURLList,
1023fa2dd9c4SAndrew Lindesay					false, false, &stringValidator);
1024fa2dd9c4SAndrew Lindesay			}
10256da0ce6fSIngo Weinhold				break;
10266da0ce6fSIngo Weinhold
10276da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_SOURCE_URLS:
1028fa2dd9c4SAndrew Lindesay			{
1029fa2dd9c4SAndrew Lindesay				UrlStringValidator stringValidator;
1030fa2dd9c4SAndrew Lindesay				_ParseStringList(&packageInfo->fSourceURLList,
1031fa2dd9c4SAndrew Lindesay					false, false, &stringValidator);
1032fa2dd9c4SAndrew Lindesay			}
10336da0ce6fSIngo Weinhold				break;
10346da0ce6fSIngo Weinhold
10354489c88bSIngo Weinhold			case B_PACKAGE_INFO_GLOBAL_WRITABLE_FILES:
10364489c88bSIngo Weinhold				_ParseGlobalWritableFileInfos(
10374489c88bSIngo Weinhold					&packageInfo->fGlobalWritableFileInfos);
10386da0ce6fSIngo Weinhold				break;
10396da0ce6fSIngo Weinhold
10406da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_USER_SETTINGS_FILES:
10416da0ce6fSIngo Weinhold				_ParseUserSettingsFileInfos(
10426da0ce6fSIngo Weinhold					&packageInfo->fUserSettingsFileInfos);
10436da0ce6fSIngo Weinhold				break;
10446da0ce6fSIngo Weinhold
10450f4e11e7SIngo Weinhold			case B_PACKAGE_INFO_USERS:
10460f4e11e7SIngo Weinhold				_ParseUsers(&packageInfo->fUsers);
10470f4e11e7SIngo Weinhold				break;
10480f4e11e7SIngo Weinhold
10490f4e11e7SIngo Weinhold			case B_PACKAGE_INFO_GROUPS:
10500f4e11e7SIngo Weinhold				_ParseStringList(&packageInfo->fGroups);
10510f4e11e7SIngo Weinhold				break;
10520f4e11e7SIngo Weinhold
1053c0ab1409SIngo Weinhold			case B_PACKAGE_INFO_POST_INSTALL_SCRIPTS:
1054c0ab1409SIngo Weinhold				_ParseStringList(&packageInfo->fPostInstallScripts);
1055c0ab1409SIngo Weinhold				break;
1056c0ab1409SIngo Weinhold
10576da0ce6fSIngo Weinhold			case B_PACKAGE_INFO_PROVIDES: