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