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(©rightList); 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