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