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