xref: /haiku/src/kits/package/PackageInfo.cpp (revision f2b4344867e97c3f4e742a1b4a15e6879644601a)
1 /*
2  * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <package/PackageInfo.h>
8 
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include <new>
14 
15 #include <File.h>
16 #include <Entry.h>
17 
18 
19 namespace BPackageKit {
20 
21 
22 namespace {
23 
24 
25 enum TokenType {
26 	TOKEN_WORD,
27 	TOKEN_QUOTED_STRING,
28 	TOKEN_OPERATOR_ASSIGN,
29 	TOKEN_OPERATOR_LESS,
30 	TOKEN_OPERATOR_LESS_EQUAL,
31 	TOKEN_OPERATOR_EQUAL,
32 	TOKEN_OPERATOR_NOT_EQUAL,
33 	TOKEN_OPERATOR_GREATER_EQUAL,
34 	TOKEN_OPERATOR_GREATER,
35 	TOKEN_OPEN_BRACE,
36 	TOKEN_CLOSE_BRACE,
37 	TOKEN_ITEM_SEPARATOR,
38 	//
39 	TOKEN_EOF,
40 };
41 
42 
43 struct ParseError {
44 	BString 	message;
45 	const char*	pos;
46 
47 	ParseError(const BString& _message, const char* _pos)
48 		: message(_message), pos(_pos)
49 	{
50 	}
51 };
52 
53 
54 }	// anonymous namespace
55 
56 
57 /*
58  * Parses a ".PackageInfo" file and fills a BPackageInfo object with the
59  * package info elements found.
60  */
61 class BPackageInfo::Parser {
62 public:
63 								Parser(ParseErrorListener* listener = NULL);
64 
65 			status_t			Parse(const BString& packageInfoString,
66 									BPackageInfo* packageInfo);
67 
68 private:
69 			struct Token;
70 			struct ListElementParser;
71 	friend	struct ListElementParser;
72 
73 			Token				_NextToken();
74 			void				_RewindTo(const Token& token);
75 
76 			void				_ParseStringValue(BString* value);
77 			uint32				_ParseFlags();
78 			void				_ParseArchitectureValue(
79 									BPackageArchitecture* value);
80 			void				_ParseVersionValue(BPackageVersion* value,
81 									bool releaseIsOptional);
82 			void				_ParseList(ListElementParser& elementParser,
83 									bool allowSingleNonListElement);
84 			void				_ParseStringList(BStringList* value,
85 									bool allowQuotedStrings = true,
86 									bool convertToLowerCase = false);
87 			void				_ParseResolvableList(
88 									BObjectList<BPackageResolvable>* value);
89 			void				_ParseResolvableExprList(
90 									BObjectList<BPackageResolvableExpression>*
91 										value);
92 
93 			void				_Parse(BPackageInfo* packageInfo);
94 
95 private:
96 			ParseErrorListener*	fListener;
97 			const char*			fPos;
98 };
99 
100 
101 struct BPackageInfo::Parser::Token {
102 	TokenType	type;
103 	BString		text;
104 	const char*	pos;
105 
106 	Token(TokenType _type, const char* _pos, int length = 0)
107 		: type(_type), pos(_pos)
108 	{
109 		if (length != 0) {
110 			text.SetTo(pos, length);
111 
112 			if (type == TOKEN_QUOTED_STRING) {
113 				// unescape value of quoted string
114 				char* value = text.LockBuffer(length);
115 				if (value == NULL)
116 					return;
117 				int index = 0;
118 				int newIndex = 0;
119 				bool lastWasEscape = false;
120 				while (char c = value[index++]) {
121 					if (lastWasEscape) {
122 						lastWasEscape = false;
123 						// map \n to newline and \t to tab
124 						if (c == 'n')
125 							c = '\n';
126 						else if (c == 't')
127 							c = '\t';
128 					} else if (c == '\\') {
129 						lastWasEscape = true;
130 						continue;
131 					}
132 					value[newIndex++] = c;
133 				}
134 				value[newIndex] = '\0';
135 				text.UnlockBuffer(newIndex);
136 			}
137 		}
138 	}
139 
140 	operator bool() const
141 	{
142 		return type != TOKEN_EOF;
143 	}
144 };
145 
146 
147 struct BPackageInfo::Parser::ListElementParser {
148 	virtual void operator()(const Token& token) = 0;
149 };
150 
151 
152 BPackageInfo::ParseErrorListener::~ParseErrorListener()
153 {
154 }
155 
156 
157 BPackageInfo::Parser::Parser(ParseErrorListener* listener)
158 	:
159 	fListener(listener),
160 	fPos(NULL)
161 {
162 }
163 
164 
165 status_t
166 BPackageInfo::Parser::Parse(const BString& packageInfoString,
167 	BPackageInfo* packageInfo)
168 {
169 	if (packageInfo == NULL)
170 		return B_BAD_VALUE;
171 
172 	fPos = packageInfoString.String();
173 
174 	try {
175 		_Parse(packageInfo);
176 	} catch (const ParseError& error) {
177 		if (fListener != NULL) {
178 			// map error position to line and column
179 			int line = 1;
180 			int column;
181 			int32 offset = error.pos - packageInfoString.String();
182 			int32 newlinePos = packageInfoString.FindLast('\n', offset - 1);
183 			if (newlinePos < 0)
184 				column = offset;
185 			else {
186 				column = offset - newlinePos;
187 				do {
188 					line++;
189 					newlinePos = packageInfoString.FindLast('\n',
190 						newlinePos - 1);
191 				} while (newlinePos >= 0);
192 			}
193 			fListener->OnError(error.message, line, column);
194 		}
195 		return B_BAD_DATA;
196 	} catch (const std::bad_alloc& e) {
197 		if (fListener != NULL)
198 			fListener->OnError("out of memory", 0, 0);
199 		return B_NO_MEMORY;
200 	}
201 
202 	return B_OK;
203 }
204 
205 
206 BPackageInfo::Parser::Token
207 BPackageInfo::Parser::_NextToken()
208 {
209 	// Eat any whitespace or comments. Also eat ';' -- they have the same
210 	// function as newlines. We remember the last encountered ';' or '\n' and
211 	// return it as a token afterwards.
212 	const char* itemSeparatorPos = NULL;
213 	bool inComment = false;
214 	while ((inComment && *fPos != '\0') || isspace(*fPos) || *fPos == ';'
215 		|| *fPos == '#') {
216 		if (*fPos == '#') {
217 			inComment = true;
218 		} else if (*fPos == '\n') {
219 			itemSeparatorPos = fPos;
220 			inComment = false;
221 		} else if (!inComment && *fPos == ';')
222 			itemSeparatorPos = fPos;
223 		fPos++;
224 	}
225 
226 	if (itemSeparatorPos != NULL) {
227 		return Token(TOKEN_ITEM_SEPARATOR, itemSeparatorPos);
228 	}
229 
230 	const char* tokenPos = fPos;
231 	switch (*fPos) {
232 		case '\0':
233 			return Token(TOKEN_EOF, fPos);
234 
235 		case '{':
236 			fPos++;
237 			return Token(TOKEN_OPEN_BRACE, tokenPos);
238 
239 		case '}':
240 			fPos++;
241 			return Token(TOKEN_CLOSE_BRACE, tokenPos);
242 
243 		case '<':
244 			fPos++;
245 			if (*fPos == '=') {
246 				fPos++;
247 				return Token(TOKEN_OPERATOR_LESS_EQUAL, tokenPos, 2);
248 			}
249 			return Token(TOKEN_OPERATOR_LESS, tokenPos, 1);
250 
251 		case '=':
252 			fPos++;
253 			if (*fPos == '=') {
254 				fPos++;
255 				return Token(TOKEN_OPERATOR_EQUAL, tokenPos, 2);
256 			}
257 			return Token(TOKEN_OPERATOR_ASSIGN, tokenPos, 1);
258 
259 		case '!':
260 			if (fPos[1] == '=') {
261 				fPos += 2;
262 				return Token(TOKEN_OPERATOR_NOT_EQUAL, tokenPos, 2);
263 			}
264 			break;
265 
266 		case '>':
267 			fPos++;
268 			if (*fPos == '=') {
269 				fPos++;
270 				return Token(TOKEN_OPERATOR_GREATER_EQUAL, tokenPos, 2);
271 			}
272 			return Token(TOKEN_OPERATOR_GREATER, tokenPos, 1);
273 
274 		case '"':
275 		case '\'':
276 		{
277 			char quoteChar = *fPos;
278 			fPos++;
279 			const char* start = fPos;
280 			// anything until the next quote is part of the value
281 			bool lastWasEscape = false;
282 			while ((*fPos != quoteChar || lastWasEscape) && *fPos != '\0') {
283 				if (lastWasEscape)
284 					lastWasEscape = false;
285 				else if (*fPos == '\\')
286 					lastWasEscape = true;
287 				fPos++;
288 			}
289 			if (*fPos != quoteChar)
290 				throw ParseError("unterminated quoted-string", tokenPos);
291 			const char* end = fPos++;
292 			return Token(TOKEN_QUOTED_STRING, start, end - start);
293 		}
294 
295 		default:
296 		{
297 			const char* start = fPos;
298 			while (isalnum(*fPos) || *fPos == '.' || *fPos == '-'
299 				|| *fPos == '_' || *fPos == ':' || *fPos == '+') {
300 				fPos++;
301 			}
302 			if (fPos == start)
303 				break;
304 			return Token(TOKEN_WORD, start, fPos - start);
305 		}
306 	}
307 
308 	BString error = BString("unknown token '") << *fPos << "' encountered";
309 	throw ParseError(error.String(), fPos);
310 }
311 
312 
313 void
314 BPackageInfo::Parser::_RewindTo(const Token& token)
315 {
316 	fPos = token.pos;
317 }
318 
319 
320 void
321 BPackageInfo::Parser::_ParseStringValue(BString* value)
322 {
323 	Token string = _NextToken();
324 	if (string.type != TOKEN_QUOTED_STRING && string.type != TOKEN_WORD)
325 		throw ParseError("expected quoted-string or word", string.pos);
326 
327 	*value = string.text;
328 }
329 
330 
331 void
332 BPackageInfo::Parser::_ParseArchitectureValue(BPackageArchitecture* value)
333 {
334 	Token arch = _NextToken();
335 	if (arch.type == TOKEN_WORD) {
336 		for (int i = 0; i < B_PACKAGE_ARCHITECTURE_ENUM_COUNT; ++i) {
337 			if (arch.text.ICompare(BPackageInfo::kArchitectureNames[i]) == 0) {
338 				*value = (BPackageArchitecture)i;
339 				return;
340 			}
341 		}
342 	}
343 
344 	BString error("architecture must be one of: [");
345 	for (int i = 0; i < B_PACKAGE_ARCHITECTURE_ENUM_COUNT; ++i) {
346 		if (i > 0)
347 			error << ",";
348 		error << BPackageInfo::kArchitectureNames[i];
349 	}
350 	error << "]";
351 	throw ParseError(error, arch.pos);
352 }
353 
354 
355 void
356 BPackageInfo::Parser::_ParseVersionValue(BPackageVersion* value,
357 	bool releaseIsOptional)
358 {
359 	Token word = _NextToken();
360 	if (word.type != TOKEN_WORD)
361 		throw ParseError("expected word (a version)", word.pos);
362 
363 	// get the release number
364 	uint8 release = 0;
365 	int32 lastDashPos = word.text.FindLast('-');
366 	if (lastDashPos >= 0) {
367 		// Might be either the release number or, if that is optional, a
368 		// pre-release. The former always is a number, the latter starts with a
369 		// non-digit.
370 		if (isdigit(word.text[lastDashPos + 1])) {
371 			int number = atoi(word.text.String() + lastDashPos + 1);
372 			if (number <= 0 || number > 99) {
373 				throw ParseError("release number must be from 1-99",
374 					word.pos + word.text.Length());
375 			}
376 			release = number;
377 			word.text.Truncate(lastDashPos);
378 			lastDashPos = word.text.FindLast('-');
379 		}
380 	}
381 
382 	if (release == 0 && !releaseIsOptional) {
383 		throw ParseError("expected release number (-<number> suffix)",
384 			word.pos + word.text.Length());
385 	}
386 
387 	// get the pre-release string
388 	BString preRelease;
389 	if (lastDashPos >= 0) {
390 		if (isdigit(word.text[lastDashPos + 1])) {
391 			throw ParseError("pre-release number must not start with a digit",
392 				word.pos + word.text.Length());
393 		}
394 
395 		word.text.CopyInto(preRelease, lastDashPos + 1, word.text.Length());
396 		word.text.Truncate(lastDashPos);
397 	}
398 
399 	// get major, minor, and micro strings
400 	BString major;
401 	BString minor;
402 	BString micro;
403 	int32 firstDotPos = word.text.FindFirst('.');
404 	if (firstDotPos < 0)
405 		major = word.text;
406 	else {
407 		word.text.CopyInto(major, 0, firstDotPos);
408 		int32 secondDotPos = word.text.FindFirst('.', firstDotPos + 1);
409 		if (secondDotPos == firstDotPos + 1)
410 			throw ParseError("expected minor version", word.pos + secondDotPos);
411 
412 		if (secondDotPos < 0)
413 			word.text.CopyInto(minor, firstDotPos + 1, word.text.Length());
414 		else {
415 			word.text.CopyInto(minor, firstDotPos + 1,
416 				secondDotPos - (firstDotPos + 1));
417 			word.text.CopyInto(micro, secondDotPos + 1, word.text.Length());
418 		}
419 	}
420 
421 	value->SetTo(major, minor, micro, preRelease, release);
422 }
423 
424 
425 void
426 BPackageInfo::Parser::_ParseList(ListElementParser& elementParser,
427 	bool allowSingleNonListElement)
428 {
429 	Token openBracket = _NextToken();
430 	if (openBracket.type != TOKEN_OPEN_BRACE) {
431 		if (!allowSingleNonListElement)
432 			throw ParseError("expected start of list ('[')", openBracket.pos);
433 
434 		elementParser(openBracket);
435 		return;
436 	}
437 
438 	while (true) {
439 		Token token = _NextToken();
440 		if (token.type == TOKEN_CLOSE_BRACE)
441 			return;
442 
443 		if (token.type == TOKEN_ITEM_SEPARATOR)
444 			continue;
445 
446 		elementParser(token);
447 	}
448 }
449 
450 
451 void
452 BPackageInfo::Parser::_ParseStringList(BStringList* value,
453 	bool allowQuotedStrings, bool convertToLowerCase)
454 {
455 	struct StringParser : public ListElementParser {
456 		BStringList* value;
457 		bool allowQuotedStrings;
458 		bool convertToLowerCase;
459 
460 		StringParser(BStringList* value, bool allowQuotedStrings,
461 			bool convertToLowerCase)
462 			:
463 			value(value),
464 			allowQuotedStrings(allowQuotedStrings),
465 			convertToLowerCase(convertToLowerCase)
466 		{
467 		}
468 
469 		virtual void operator()(const Token& token)
470 		{
471 			if (allowQuotedStrings) {
472 				if (token.type != TOKEN_QUOTED_STRING
473 					&& token.type != TOKEN_WORD) {
474 					throw ParseError("expected quoted-string or word",
475 						token.pos);
476 				}
477 			} else {
478 				if (token.type != TOKEN_WORD)
479 					throw ParseError("expected word", token.pos);
480 			}
481 
482 			BString element(token.text);
483 			if (convertToLowerCase)
484 				element.ToLower();
485 
486 			value->Add(element);
487 		}
488 	} stringParser(value, allowQuotedStrings, convertToLowerCase);
489 
490 	_ParseList(stringParser, true);
491 }
492 
493 
494 uint32
495 BPackageInfo::Parser::_ParseFlags()
496 {
497 	struct FlagParser : public ListElementParser {
498 		uint32 flags;
499 
500 		FlagParser()
501 			:
502 			flags(0)
503 		{
504 		}
505 
506 		virtual void operator()(const Token& token)
507 		{
508 		if (token.type != TOKEN_WORD)
509 			throw ParseError("expected word (a flag)", token.pos);
510 
511 		if (token.text.ICompare("approve_license") == 0)
512 			flags |= B_PACKAGE_FLAG_APPROVE_LICENSE;
513 		else if (token.text.ICompare("system_package") == 0)
514 			flags |= B_PACKAGE_FLAG_SYSTEM_PACKAGE;
515 		else {
516 				throw ParseError(
517 					"expected 'approve_license' or 'system_package'",
518 				token.pos);
519 		}
520 	}
521 	} flagParser;
522 
523 	_ParseList(flagParser, true);
524 
525 	return flagParser.flags;
526 }
527 
528 
529 void
530 BPackageInfo::Parser::_ParseResolvableList(
531 	BObjectList<BPackageResolvable>* value)
532 {
533 	struct ResolvableParser : public ListElementParser {
534 		Parser& parser;
535 		BObjectList<BPackageResolvable>* value;
536 
537 		ResolvableParser(Parser& parser_,
538 			BObjectList<BPackageResolvable>* value_)
539 			:
540 			parser(parser_),
541 			value(value_)
542 		{
543 		}
544 
545 		virtual void operator()(const Token& token)
546 		{
547 			if (token.type != TOKEN_WORD) {
548 				throw ParseError("expected word (a resolvable name)",
549 					token.pos);
550 			}
551 
552 			BPackageResolvableType type = B_PACKAGE_RESOLVABLE_TYPE_DEFAULT;
553 			int32 colonPos = token.text.FindFirst(':');
554 			if (colonPos >= 0) {
555 				BString typeName(token.text, colonPos);
556 				for (int i = 0; i < B_PACKAGE_RESOLVABLE_TYPE_ENUM_COUNT; ++i) {
557 					if (typeName.ICompare(BPackageResolvable::kTypeNames[i])
558 							== 0) {
559 						type = (BPackageResolvableType)i;
560 						break;
561 					}
562 				}
563 				if (type == B_PACKAGE_RESOLVABLE_TYPE_DEFAULT) {
564 					BString error("resolvable type (<type>:) must be one of [");
565 					for (int i = 1; i < B_PACKAGE_RESOLVABLE_TYPE_ENUM_COUNT;
566 							++i) {
567 						if (i > 1)
568 							error << ",";
569 						error << BPackageResolvable::kTypeNames[i];
570 					}
571 					error << "]";
572 					throw ParseError(error, token.pos);
573 				}
574 			}
575 
576 			// parse version
577 			BPackageVersion version;
578 			Token op = parser._NextToken();
579 			if (op.type == TOKEN_OPERATOR_ASSIGN) {
580 				parser._ParseVersionValue(&version, true);
581 			} else if (op.type == TOKEN_ITEM_SEPARATOR
582 				|| op.type == TOKEN_CLOSE_BRACE) {
583 				parser._RewindTo(op);
584 			} else
585 				throw ParseError("expected '=', comma or ']'", op.pos);
586 
587 			// parse compatible version
588 			BPackageVersion compatibleVersion;
589 			Token compatible = parser._NextToken();
590 			if (compatible.type == TOKEN_WORD
591 				&& (compatible.text == "compat"
592 					|| compatible.text == "compatible")) {
593 				op = parser._NextToken();
594 				if (op.type == TOKEN_OPERATOR_GREATER_EQUAL) {
595 					parser._ParseVersionValue(&compatibleVersion, true);
596 				} else
597 					parser._RewindTo(compatible);
598 			} else
599 				parser._RewindTo(compatible);
600 
601 			value->AddItem(new BPackageResolvable(token.text, type, version,
602 				compatibleVersion));
603 		}
604 	} resolvableParser(*this, value);
605 
606 	_ParseList(resolvableParser, false);
607 }
608 
609 
610 void
611 BPackageInfo::Parser::_ParseResolvableExprList(
612 	BObjectList<BPackageResolvableExpression>* value)
613 {
614 	struct ResolvableExpressionParser : public ListElementParser {
615 		Parser& parser;
616 		BObjectList<BPackageResolvableExpression>* value;
617 
618 		ResolvableExpressionParser(Parser& parser_,
619 			BObjectList<BPackageResolvableExpression>* value_)
620 			:
621 			parser(parser_),
622 			value(value_)
623 		{
624 		}
625 
626 		virtual void operator()(const Token& token)
627 		{
628 			if (token.type != TOKEN_WORD) {
629 				throw ParseError("expected word (a resolvable name)",
630 					token.pos);
631 			}
632 
633 		BPackageVersion version;
634 			Token op = parser._NextToken();
635 		if (op.type == TOKEN_OPERATOR_LESS
636 			|| op.type == TOKEN_OPERATOR_LESS_EQUAL
637 			|| op.type == TOKEN_OPERATOR_EQUAL
638 			|| op.type == TOKEN_OPERATOR_NOT_EQUAL
639 			|| op.type == TOKEN_OPERATOR_GREATER_EQUAL
640 			|| op.type == TOKEN_OPERATOR_GREATER) {
641 				parser._ParseVersionValue(&version, true);
642 		} else if (op.type == TOKEN_ITEM_SEPARATOR
643 			|| op.type == TOKEN_CLOSE_BRACE) {
644 				parser._RewindTo(op);
645 		} else {
646 			throw ParseError(
647 				"expected '<', '<=', '==', '!=', '>=', '>', comma or ']'",
648 				op.pos);
649 		}
650 
651 		BPackageResolvableOperator resolvableOperator
652 			= (BPackageResolvableOperator)(op.type - TOKEN_OPERATOR_LESS);
653 
654 			value->AddItem(new BPackageResolvableExpression(token.text,
655 			resolvableOperator, version));
656 	}
657 	} resolvableExpressionParser(*this, value);
658 
659 	_ParseList(resolvableExpressionParser, false);
660 }
661 
662 
663 void
664 BPackageInfo::Parser::_Parse(BPackageInfo* packageInfo)
665 {
666 	bool seen[B_PACKAGE_INFO_ENUM_COUNT];
667 	for (int i = 0; i < B_PACKAGE_INFO_ENUM_COUNT; ++i)
668 		seen[i] = false;
669 
670 	const char* const* names = BPackageInfo::kElementNames;
671 
672 	while (Token t = _NextToken()) {
673 		if (t.type == TOKEN_ITEM_SEPARATOR)
674 			continue;
675 
676 		if (t.type != TOKEN_WORD)
677 			throw ParseError("expected word (a variable name)", t.pos);
678 
679 		BPackageInfoAttributeID attribute = B_PACKAGE_INFO_ENUM_COUNT;
680 		for (int i = 0; i < B_PACKAGE_INFO_ENUM_COUNT; i++) {
681 			if (names[i] != NULL && t.text.ICompare(names[i]) == 0) {
682 				attribute = (BPackageInfoAttributeID)i;
683 				break;
684 			}
685 		}
686 
687 		if (attribute == B_PACKAGE_INFO_ENUM_COUNT) {
688 			BString error = BString("unknown attribute \"") << t.text << '"';
689 			throw ParseError(error, t.pos);
690 		}
691 
692 		if (seen[attribute]) {
693 			BString error = BString(names[attribute]) << " already seen!";
694 			throw ParseError(error, t.pos);
695 		}
696 
697 		switch (attribute) {
698 			case B_PACKAGE_INFO_NAME:
699 			{
700 				BString name;
701 				_ParseStringValue(&name);
702 				packageInfo->SetName(name);
703 				break;
704 			}
705 
706 			case B_PACKAGE_INFO_SUMMARY:
707 			{
708 				BString summary;
709 				_ParseStringValue(&summary);
710 				if (summary.FindFirst('\n') >= 0)
711 					throw ParseError("the summary contains linebreaks", t.pos);
712 				packageInfo->SetSummary(summary);
713 				break;
714 			}
715 
716 			case B_PACKAGE_INFO_DESCRIPTION:
717 				_ParseStringValue(&packageInfo->fDescription);
718 				break;
719 
720 			case B_PACKAGE_INFO_VENDOR:
721 				_ParseStringValue(&packageInfo->fVendor);
722 				break;
723 
724 			case B_PACKAGE_INFO_PACKAGER:
725 				_ParseStringValue(&packageInfo->fPackager);
726 				break;
727 
728 			case B_PACKAGE_INFO_ARCHITECTURE:
729 				_ParseArchitectureValue(&packageInfo->fArchitecture);
730 				break;
731 
732 			case B_PACKAGE_INFO_VERSION:
733 				_ParseVersionValue(&packageInfo->fVersion, false);
734 				break;
735 
736 			case B_PACKAGE_INFO_COPYRIGHTS:
737 				_ParseStringList(&packageInfo->fCopyrightList);
738 				break;
739 
740 			case B_PACKAGE_INFO_LICENSES:
741 				_ParseStringList(&packageInfo->fLicenseList);
742 				break;
743 
744 			case B_PACKAGE_INFO_URLS:
745 				_ParseStringList(&packageInfo->fURLList);
746 				break;
747 
748 			case B_PACKAGE_INFO_SOURCE_URLS:
749 				_ParseStringList(&packageInfo->fSourceURLList);
750 				break;
751 
752 			case B_PACKAGE_INFO_PROVIDES:
753 				_ParseResolvableList(&packageInfo->fProvidesList);
754 				break;
755 
756 			case B_PACKAGE_INFO_REQUIRES:
757 				_ParseResolvableExprList(&packageInfo->fRequiresList);
758 				break;
759 
760 			case B_PACKAGE_INFO_SUPPLEMENTS:
761 				_ParseResolvableExprList(&packageInfo->fSupplementsList);
762 				break;
763 
764 			case B_PACKAGE_INFO_CONFLICTS:
765 				_ParseResolvableExprList(&packageInfo->fConflictsList);
766 				break;
767 
768 			case B_PACKAGE_INFO_FRESHENS:
769 				_ParseResolvableExprList(&packageInfo->fFreshensList);
770 				break;
771 
772 			case B_PACKAGE_INFO_REPLACES:
773 				_ParseStringList(&packageInfo->fReplacesList, false, true);
774 				break;
775 
776 			case B_PACKAGE_INFO_FLAGS:
777 				packageInfo->SetFlags(_ParseFlags());
778 				break;
779 
780 			default:
781 				// can never get here
782 				break;
783 		}
784 
785 		seen[attribute] = true;
786 	}
787 
788 	// everything up to and including 'provides' is mandatory
789 	for (int i = 0; i <= B_PACKAGE_INFO_PROVIDES; ++i) {
790 		if (!seen[i]) {
791 			BString error = BString(names[i]) << " is not being set anywhere!";
792 			throw ParseError(error, fPos);
793 		}
794 	}
795 }
796 
797 
798 const char* BPackageInfo::kElementNames[B_PACKAGE_INFO_ENUM_COUNT] = {
799 	"name",
800 	"summary",
801 	"description",
802 	"vendor",
803 	"packager",
804 	"architecture",
805 	"version",
806 	"copyrights",
807 	"licenses",
808 	"provides",
809 	"requires",
810 	"supplements",
811 	"conflicts",
812 	"freshens",
813 	"replaces",
814 	"flags",
815 	"urls",
816 	"source-urls",
817 	"checksum",		// not being parsed, computed externally
818 	NULL,			// install-path -- not settable via .PackageInfo
819 };
820 
821 
822 const char*
823 BPackageInfo::kArchitectureNames[B_PACKAGE_ARCHITECTURE_ENUM_COUNT] = {
824 	"any",
825 	"x86",
826 	"x86_gcc2",
827 };
828 
829 
830 BPackageInfo::BPackageInfo()
831 	:
832 	fFlags(0),
833 	fArchitecture(B_PACKAGE_ARCHITECTURE_ENUM_COUNT),
834 	fCopyrightList(5),
835 	fLicenseList(5),
836 	fURLList(5),
837 	fSourceURLList(5),
838 	fProvidesList(20, true),
839 	fRequiresList(20, true),
840 	fSupplementsList(20, true),
841 	fConflictsList(5, true),
842 	fFreshensList(5, true),
843 	fReplacesList(5)
844 {
845 }
846 
847 
848 BPackageInfo::~BPackageInfo()
849 {
850 }
851 
852 
853 status_t
854 BPackageInfo::ReadFromConfigFile(const BEntry& packageInfoEntry,
855 	ParseErrorListener* listener)
856 {
857 	status_t result = packageInfoEntry.InitCheck();
858 	if (result != B_OK)
859 		return result;
860 
861 	BFile file(&packageInfoEntry, B_READ_ONLY);
862 	if ((result = file.InitCheck()) != B_OK)
863 		return result;
864 
865 	return ReadFromConfigFile(file, listener);
866 }
867 
868 
869 status_t
870 BPackageInfo::ReadFromConfigFile(BFile& packageInfoFile,
871 	ParseErrorListener* listener)
872 {
873 	off_t size;
874 	status_t result = packageInfoFile.GetSize(&size);
875 	if (result != B_OK)
876 		return result;
877 
878 	BString packageInfoString;
879 	char* buffer = packageInfoString.LockBuffer(size);
880 	if (buffer == NULL)
881 		return B_NO_MEMORY;
882 
883 	if ((result = packageInfoFile.Read(buffer, size)) < size) {
884 		packageInfoString.UnlockBuffer(0);
885 		return result >= 0 ? B_IO_ERROR : result;
886 	}
887 
888 	buffer[size] = '\0';
889 	packageInfoString.UnlockBuffer(size);
890 
891 	return ReadFromConfigString(packageInfoString, listener);
892 }
893 
894 
895 status_t
896 BPackageInfo::ReadFromConfigString(const BString& packageInfoString,
897 	ParseErrorListener* listener)
898 {
899 	Clear();
900 
901 	Parser parser(listener);
902 	return parser.Parse(packageInfoString, this);
903 }
904 
905 
906 status_t
907 BPackageInfo::InitCheck() const
908 {
909 	if (fName.Length() == 0 || fSummary.Length() == 0
910 		|| fDescription.Length() == 0 || fVendor.Length() == 0
911 		|| fPackager.Length() == 0
912 		|| fArchitecture == B_PACKAGE_ARCHITECTURE_ENUM_COUNT
913 		|| fVersion.InitCheck() != B_OK
914 		|| fCopyrightList.IsEmpty() || fLicenseList.IsEmpty()
915 		|| fProvidesList.IsEmpty())
916 		return B_NO_INIT;
917 
918 	return B_OK;
919 }
920 
921 
922 const BString&
923 BPackageInfo::Name() const
924 {
925 	return fName;
926 }
927 
928 
929 const BString&
930 BPackageInfo::Summary() const
931 {
932 	return fSummary;
933 }
934 
935 
936 const BString&
937 BPackageInfo::Description() const
938 {
939 	return fDescription;
940 }
941 
942 
943 const BString&
944 BPackageInfo::Vendor() const
945 {
946 	return fVendor;
947 }
948 
949 
950 const BString&
951 BPackageInfo::Packager() const
952 {
953 	return fPackager;
954 }
955 
956 
957 const BString&
958 BPackageInfo::Checksum() const
959 {
960 	return fChecksum;
961 }
962 
963 
964 const BString&
965 BPackageInfo::InstallPath() const
966 {
967 	return fInstallPath;
968 }
969 
970 
971 uint32
972 BPackageInfo::Flags() const
973 {
974 	return fFlags;
975 }
976 
977 
978 BPackageArchitecture
979 BPackageInfo::Architecture() const
980 {
981 	return fArchitecture;
982 }
983 
984 
985 const BPackageVersion&
986 BPackageInfo::Version() const
987 {
988 	return fVersion;
989 }
990 
991 
992 const BStringList&
993 BPackageInfo::CopyrightList() const
994 {
995 	return fCopyrightList;
996 }
997 
998 
999 const BStringList&
1000 BPackageInfo::LicenseList() const
1001 {
1002 	return fLicenseList;
1003 }
1004 
1005 
1006 const BStringList&
1007 BPackageInfo::URLList() const
1008 {
1009 	return fURLList;
1010 }
1011 
1012 
1013 const BStringList&
1014 BPackageInfo::SourceURLList() const
1015 {
1016 	return fSourceURLList;
1017 }
1018 
1019 
1020 const BObjectList<BPackageResolvable>&
1021 BPackageInfo::ProvidesList() const
1022 {
1023 	return fProvidesList;
1024 }
1025 
1026 
1027 const BObjectList<BPackageResolvableExpression>&
1028 BPackageInfo::RequiresList() const
1029 {
1030 	return fRequiresList;
1031 }
1032 
1033 
1034 const BObjectList<BPackageResolvableExpression>&
1035 BPackageInfo::SupplementsList() const
1036 {
1037 	return fSupplementsList;
1038 }
1039 
1040 
1041 const BObjectList<BPackageResolvableExpression>&
1042 BPackageInfo::ConflictsList() const
1043 {
1044 	return fConflictsList;
1045 }
1046 
1047 
1048 const BObjectList<BPackageResolvableExpression>&
1049 BPackageInfo::FreshensList() const
1050 {
1051 	return fFreshensList;
1052 }
1053 
1054 
1055 const BStringList&
1056 BPackageInfo::ReplacesList() const
1057 {
1058 	return fReplacesList;
1059 }
1060 
1061 
1062 void
1063 BPackageInfo::SetName(const BString& name)
1064 {
1065 	fName = name;
1066 	fName.ToLower();
1067 }
1068 
1069 
1070 void
1071 BPackageInfo::SetSummary(const BString& summary)
1072 {
1073 	fSummary = summary;
1074 }
1075 
1076 
1077 void
1078 BPackageInfo::SetDescription(const BString& description)
1079 {
1080 	fDescription = description;
1081 }
1082 
1083 
1084 void
1085 BPackageInfo::SetVendor(const BString& vendor)
1086 {
1087 	fVendor = vendor;
1088 }
1089 
1090 
1091 void
1092 BPackageInfo::SetPackager(const BString& packager)
1093 {
1094 	fPackager = packager;
1095 }
1096 
1097 
1098 void
1099 BPackageInfo::SetChecksum(const BString& checksum)
1100 {
1101 	fChecksum = checksum;
1102 }
1103 
1104 
1105 void
1106 BPackageInfo::SetInstallPath(const BString& installPath)
1107 {
1108 	fInstallPath = installPath;
1109 }
1110 
1111 
1112 void
1113 BPackageInfo::SetVersion(const BPackageVersion& version)
1114 {
1115 	fVersion = version;
1116 }
1117 
1118 
1119 void
1120 BPackageInfo::SetFlags(uint32 flags)
1121 {
1122 	fFlags = flags;
1123 }
1124 
1125 
1126 void
1127 BPackageInfo::SetArchitecture(BPackageArchitecture architecture)
1128 {
1129 	fArchitecture = architecture;
1130 }
1131 
1132 
1133 void
1134 BPackageInfo::ClearCopyrightList()
1135 {
1136 	fCopyrightList.MakeEmpty();
1137 }
1138 
1139 
1140 status_t
1141 BPackageInfo::AddCopyright(const BString& copyright)
1142 {
1143 	return fCopyrightList.Add(copyright) ? B_OK : B_ERROR;
1144 }
1145 
1146 
1147 void
1148 BPackageInfo::ClearLicenseList()
1149 {
1150 	fLicenseList.MakeEmpty();
1151 }
1152 
1153 
1154 status_t
1155 BPackageInfo::AddLicense(const BString& license)
1156 {
1157 	return fLicenseList.Add(license) ? B_OK : B_ERROR;
1158 }
1159 
1160 
1161 void
1162 BPackageInfo::ClearURLList()
1163 {
1164 	fURLList.MakeEmpty();
1165 }
1166 
1167 
1168 status_t
1169 BPackageInfo::AddURL(const BString& url)
1170 {
1171 	return fURLList.Add(url) ? B_OK : B_NO_MEMORY;
1172 }
1173 
1174 
1175 void
1176 BPackageInfo::ClearSourceURLList()
1177 {
1178 	fSourceURLList.MakeEmpty();
1179 }
1180 
1181 
1182 status_t
1183 BPackageInfo::AddSourceURL(const BString& url)
1184 {
1185 	return fSourceURLList.Add(url) ? B_OK : B_NO_MEMORY;
1186 }
1187 
1188 
1189 void
1190 BPackageInfo::ClearProvidesList()
1191 {
1192 	fProvidesList.MakeEmpty();
1193 }
1194 
1195 
1196 status_t
1197 BPackageInfo::AddProvides(const BPackageResolvable& provides)
1198 {
1199 	BPackageResolvable* newProvides
1200 		= new (std::nothrow) BPackageResolvable(provides);
1201 	if (newProvides == NULL)
1202 		return B_NO_MEMORY;
1203 
1204 	return fProvidesList.AddItem(newProvides) ? B_OK : B_ERROR;
1205 }
1206 
1207 
1208 void
1209 BPackageInfo::ClearRequiresList()
1210 {
1211 	fRequiresList.MakeEmpty();
1212 }
1213 
1214 
1215 status_t
1216 BPackageInfo::AddRequires(const BPackageResolvableExpression& requires)
1217 {
1218 	BPackageResolvableExpression* newRequires
1219 		= new (std::nothrow) BPackageResolvableExpression(requires);
1220 	if (newRequires == NULL)
1221 		return B_NO_MEMORY;
1222 
1223 	return fRequiresList.AddItem(newRequires) ? B_OK : B_ERROR;
1224 }
1225 
1226 
1227 void
1228 BPackageInfo::ClearSupplementsList()
1229 {
1230 	fSupplementsList.MakeEmpty();
1231 }
1232 
1233 
1234 status_t
1235 BPackageInfo::AddSupplements(const BPackageResolvableExpression& supplements)
1236 {
1237 	BPackageResolvableExpression* newSupplements
1238 		= new (std::nothrow) BPackageResolvableExpression(supplements);
1239 	if (newSupplements == NULL)
1240 		return B_NO_MEMORY;
1241 
1242 	return fSupplementsList.AddItem(newSupplements) ? B_OK : B_ERROR;
1243 }
1244 
1245 
1246 void
1247 BPackageInfo::ClearConflictsList()
1248 {
1249 	fConflictsList.MakeEmpty();
1250 }
1251 
1252 
1253 status_t
1254 BPackageInfo::AddConflicts(const BPackageResolvableExpression& conflicts)
1255 {
1256 	BPackageResolvableExpression* newConflicts
1257 		= new (std::nothrow) BPackageResolvableExpression(conflicts);
1258 	if (newConflicts == NULL)
1259 		return B_NO_MEMORY;
1260 
1261 	return fConflictsList.AddItem(newConflicts) ? B_OK : B_ERROR;
1262 }
1263 
1264 
1265 void
1266 BPackageInfo::ClearFreshensList()
1267 {
1268 	fFreshensList.MakeEmpty();
1269 }
1270 
1271 
1272 status_t
1273 BPackageInfo::AddFreshens(const BPackageResolvableExpression& freshens)
1274 {
1275 	BPackageResolvableExpression* newFreshens
1276 		= new (std::nothrow) BPackageResolvableExpression(freshens);
1277 	if (newFreshens == NULL)
1278 		return B_NO_MEMORY;
1279 
1280 	return fFreshensList.AddItem(newFreshens) ? B_OK : B_ERROR;
1281 }
1282 
1283 
1284 void
1285 BPackageInfo::ClearReplacesList()
1286 {
1287 	fReplacesList.MakeEmpty();
1288 }
1289 
1290 
1291 status_t
1292 BPackageInfo::AddReplaces(const BString& replaces)
1293 {
1294 	return fReplacesList.Add(BString(replaces).ToLower()) ? B_OK : B_ERROR;
1295 }
1296 
1297 
1298 void
1299 BPackageInfo::Clear()
1300 {
1301 	fName.Truncate(0);
1302 	fSummary.Truncate(0);
1303 	fDescription.Truncate(0);
1304 	fVendor.Truncate(0);
1305 	fPackager.Truncate(0);
1306 	fChecksum.Truncate(0);
1307 	fInstallPath.Truncate(0);
1308 	fFlags = 0;
1309 	fArchitecture = B_PACKAGE_ARCHITECTURE_ENUM_COUNT;
1310 	fVersion.Clear();
1311 	fCopyrightList.MakeEmpty();
1312 	fLicenseList.MakeEmpty();
1313 	fURLList.MakeEmpty();
1314 	fSourceURLList.MakeEmpty();
1315 	fRequiresList.MakeEmpty();
1316 	fProvidesList.MakeEmpty();
1317 	fSupplementsList.MakeEmpty();
1318 	fConflictsList.MakeEmpty();
1319 	fFreshensList.MakeEmpty();
1320 	fReplacesList.MakeEmpty();
1321 }
1322 
1323 
1324 /*static*/ status_t
1325 BPackageInfo::GetArchitectureByName(const BString& name,
1326 	BPackageArchitecture& _architecture)
1327 {
1328 	for (int i = 0; i < B_PACKAGE_ARCHITECTURE_ENUM_COUNT; ++i) {
1329 		if (name.ICompare(kArchitectureNames[i]) == 0) {
1330 			_architecture = (BPackageArchitecture)i;
1331 			return B_OK;
1332 		}
1333 	}
1334 	return B_NAME_NOT_FOUND;
1335 }
1336 
1337 }	// namespace BPackageKit
1338