2 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
3 * Copyright 2016, Andrew Lindesay <apl@lindesay.co.nz>
4 * Distributed under the terms of the MIT License.
8 #include "PackageInfoParser.h"
19 namespace BPackageKit
{
22 BPackageInfo::ParseErrorListener::~ParseErrorListener()
27 BPackageInfo::Parser::Parser(ParseErrorListener
* listener
)
36 BPackageInfo::Parser::Parse(const BString
& packageInfoString
,
37 BPackageInfo
* packageInfo
)
39 if (packageInfo
== NULL
)
42 fPos
= packageInfoString
.String();
46 } catch (const ParseError
& error
) {
47 if (fListener
!= NULL
) {
48 // map error position to line and column
51 int32 offset
= error
.pos
- packageInfoString
.String();
52 int32 newlinePos
= packageInfoString
.FindLast('\n', offset
- 1);
54 inLineOffset
= offset
;
56 inLineOffset
= offset
- newlinePos
- 1;
59 newlinePos
= packageInfoString
.FindLast('\n',
61 } while (newlinePos
>= 0);
65 for (int i
= 0; i
< inLineOffset
; i
++) {
67 if (error
.pos
[i
- inLineOffset
] == '\t')
68 column
= (column
+ 3) / 4 * 4;
71 fListener
->OnError(error
.message
, line
, column
+ 1);
74 } catch (const std::bad_alloc
& e
) {
75 if (fListener
!= NULL
)
76 fListener
->OnError("out of memory", 0, 0);
85 BPackageInfo::Parser::ParseVersion(const BString
& versionString
,
86 bool revisionIsOptional
, BPackageVersion
& _version
)
88 fPos
= versionString
.String();
91 Token
token(TOKEN_STRING
, fPos
, versionString
.Length());
92 _ParseVersionValue(token
, &_version
, revisionIsOptional
);
93 } catch (const ParseError
& error
) {
94 if (fListener
!= NULL
) {
95 int32 offset
= error
.pos
- versionString
.String();
96 fListener
->OnError(error
.message
, 1, offset
);
99 } catch (const std::bad_alloc
& e
) {
100 if (fListener
!= NULL
)
101 fListener
->OnError("out of memory", 0, 0);
110 BPackageInfo::Parser::ParseResolvableExpression(const BString
& expressionString
,
111 BPackageResolvableExpression
& _expression
)
113 fPos
= expressionString
.String();
116 Token
token(TOKEN_STRING
, fPos
, expressionString
.Length());
117 _ParseResolvableExpression(_NextToken(), _expression
, NULL
);
118 } catch (const ParseError
& error
) {
119 if (fListener
!= NULL
) {
120 int32 offset
= error
.pos
- expressionString
.String();
121 fListener
->OnError(error
.message
, 1, offset
);
124 } catch (const std::bad_alloc
& e
) {
125 if (fListener
!= NULL
)
126 fListener
->OnError("out of memory", 0, 0);
134 BPackageInfo::Parser::Token
135 BPackageInfo::Parser::_NextToken()
137 // Eat any whitespace, comments, or escaped new lines. Also eat ';' -- they
138 // have the same function as newlines. We remember the last encountered ';'
139 // or '\n' and return it as a token afterwards.
140 const char* itemSeparatorPos
= NULL
;
141 bool inComment
= false;
142 while ((inComment
&& *fPos
!= '\0') || isspace(*fPos
) || *fPos
== ';'
143 || *fPos
== '#' || *fPos
== '\\') {
146 } else if (!inComment
&& *fPos
== '\\') {
149 // ignore escaped line breaks
151 } else if (*fPos
== '\n') {
152 itemSeparatorPos
= fPos
;
154 } else if (!inComment
&& *fPos
== ';')
155 itemSeparatorPos
= fPos
;
159 if (itemSeparatorPos
!= NULL
) {
160 return Token(TOKEN_ITEM_SEPARATOR
, itemSeparatorPos
);
163 const char* tokenPos
= fPos
;
166 return Token(TOKEN_EOF
, fPos
);
170 return Token(TOKEN_OPEN_BRACE
, tokenPos
);
174 return Token(TOKEN_CLOSE_BRACE
, tokenPos
);
180 return Token(TOKEN_OPERATOR_LESS_EQUAL
, tokenPos
, 2);
182 return Token(TOKEN_OPERATOR_LESS
, tokenPos
, 1);
188 return Token(TOKEN_OPERATOR_EQUAL
, tokenPos
, 2);
190 return Token(TOKEN_OPERATOR_ASSIGN
, tokenPos
, 1);
193 if (fPos
[1] == '=') {
195 return Token(TOKEN_OPERATOR_NOT_EQUAL
, tokenPos
, 2);
203 return Token(TOKEN_OPERATOR_GREATER_EQUAL
, tokenPos
, 2);
205 return Token(TOKEN_OPERATOR_GREATER
, tokenPos
, 1);
210 char quoteChar
= '\0';
212 for (; *fPos
!= '\0'; fPos
++) {
214 if (quoteChar
!= '\0') {
215 // within a quoted string segment
216 if (c
== quoteChar
) {
222 // next char is escaped
225 throw ParseError("unterminated quoted-string",
237 // unquoted string segment
241 // quoted string start
251 // a separator character -- this ends the string
255 // next char is escaped
258 throw ParseError("'\\' at end of string",
275 return Token(TOKEN_STRING
, tokenPos
, fPos
- tokenPos
,
280 BString error
= BString("unknown token '") << *fPos
<< "' encountered";
281 throw ParseError(error
.String(), fPos
);
286 BPackageInfo::Parser::_RewindTo(const Token
& token
)
293 BPackageInfo::Parser::_ParseStringValue(BString
* value
, const char** _tokenPos
)
295 Token string
= _NextToken();
296 if (string
.type
!= TOKEN_STRING
)
297 throw ParseError("expected string", string
.pos
);
299 *value
= string
.text
;
300 if (_tokenPos
!= NULL
)
301 *_tokenPos
= string
.pos
;
306 BPackageInfo::Parser::_ParseArchitectureValue(BPackageArchitecture
* value
)
308 Token arch
= _NextToken();
309 if (arch
.type
== TOKEN_STRING
) {
310 for (int i
= 0; i
< B_PACKAGE_ARCHITECTURE_ENUM_COUNT
; ++i
) {
311 if (arch
.text
.ICompare(BPackageInfo::kArchitectureNames
[i
]) == 0) {
312 *value
= (BPackageArchitecture
)i
;
318 BString
error("architecture must be one of: [");
319 for (int i
= 0; i
< B_PACKAGE_ARCHITECTURE_ENUM_COUNT
; ++i
) {
322 error
<< BPackageInfo::kArchitectureNames
[i
];
325 throw ParseError(error
, arch
.pos
);
330 BPackageInfo::Parser::_ParseVersionValue(BPackageVersion
* value
,
331 bool revisionIsOptional
)
333 Token word
= _NextToken();
334 _ParseVersionValue(word
, value
, revisionIsOptional
);
339 BPackageInfo::Parser::_ParseVersionValue(Token
& word
, BPackageVersion
* value
,
340 bool revisionIsOptional
)
342 if (word
.type
!= TOKEN_STRING
)
343 throw ParseError("expected string (a version)", word
.pos
);
345 // get the revision number
347 int32 dashPos
= word
.text
.FindLast('-');
350 long long number
= strtoll(word
.text
.String() + dashPos
+ 1, &end
,
352 if (*end
!= '\0' || number
< 0 || number
> UINT_MAX
) {
353 throw ParseError("revision must be a number > 0 and < UINT_MAX",
354 word
.pos
+ dashPos
+ 1);
357 revision
= (uint32
)number
;
358 word
.text
.Truncate(dashPos
);
361 if (revision
== 0 && !revisionIsOptional
) {
362 throw ParseError("expected revision number (-<number> suffix)",
363 word
.pos
+ word
.text
.Length());
366 // get the pre-release string
368 int32 tildePos
= word
.text
.FindLast('~');
370 word
.text
.CopyInto(preRelease
, tildePos
+ 1,
371 word
.text
.Length() - tildePos
- 1);
372 word
.text
.Truncate(tildePos
);
374 if (preRelease
.IsEmpty()) {
375 throw ParseError("invalid empty pre-release string",
376 word
.pos
+ tildePos
+ 1);
380 if (!_IsAlphaNumUnderscore(preRelease
, ".", &errorPos
)) {
381 throw ParseError("invalid character in pre-release string",
382 word
.pos
+ tildePos
+ 1 + errorPos
);
386 // get major, minor, and micro strings
390 int32 firstDotPos
= word
.text
.FindFirst('.');
394 word
.text
.CopyInto(major
, 0, firstDotPos
);
395 int32 secondDotPos
= word
.text
.FindFirst('.', firstDotPos
+ 1);
396 if (secondDotPos
== firstDotPos
+ 1)
397 throw ParseError("expected minor version", word
.pos
+ secondDotPos
);
399 if (secondDotPos
< 0) {
400 word
.text
.CopyInto(minor
, firstDotPos
+ 1, word
.text
.Length());
402 word
.text
.CopyInto(minor
, firstDotPos
+ 1,
403 secondDotPos
- (firstDotPos
+ 1));
404 word
.text
.CopyInto(micro
, secondDotPos
+ 1, word
.text
.Length());
407 if (!_IsAlphaNumUnderscore(micro
, ".", &errorPos
)) {
408 throw ParseError("invalid character in micro version string",
409 word
.pos
+ secondDotPos
+ 1 + errorPos
);
414 if (!_IsAlphaNumUnderscore(minor
, "", &errorPos
)) {
415 throw ParseError("invalid character in minor version string",
416 word
.pos
+ firstDotPos
+ 1 + errorPos
);
421 if (!_IsAlphaNumUnderscore(major
, "", &errorPos
)) {
422 throw ParseError("invalid character in major version string",
423 word
.pos
+ errorPos
);
426 value
->SetTo(major
, minor
, micro
, preRelease
, revision
);
431 BPackageInfo::Parser::_ParseResolvableExpression(const Token
& token
,
432 BPackageResolvableExpression
& _value
, BString
* _basePackage
)
434 if (token
.type
!= TOKEN_STRING
) {
435 throw ParseError("expected word (a resolvable name)",
440 if (!_IsValidResolvableName(token
.text
, &errorPos
)) {
441 throw ParseError("invalid character in resolvable name",
442 token
.pos
+ errorPos
);
445 BPackageVersion version
;
446 Token op
= _NextToken();
447 BPackageResolvableOperator resolvableOperator
;
448 if (op
.type
== TOKEN_OPERATOR_LESS
449 || op
.type
== TOKEN_OPERATOR_LESS_EQUAL
450 || op
.type
== TOKEN_OPERATOR_EQUAL
451 || op
.type
== TOKEN_OPERATOR_NOT_EQUAL
452 || op
.type
== TOKEN_OPERATOR_GREATER_EQUAL
453 || op
.type
== TOKEN_OPERATOR_GREATER
) {
454 _ParseVersionValue(&version
, true);
456 if (_basePackage
!= NULL
) {
457 Token base
= _NextToken();
458 if (base
.type
== TOKEN_STRING
&& base
.text
== "base") {
459 if (!_basePackage
->IsEmpty()) {
460 throw ParseError("multiple packages marked as base package",
464 *_basePackage
= token
.text
;
469 resolvableOperator
= (BPackageResolvableOperator
)
470 (op
.type
- TOKEN_OPERATOR_LESS
);
471 } else if (op
.type
== TOKEN_ITEM_SEPARATOR
472 || op
.type
== TOKEN_CLOSE_BRACE
|| op
.type
== TOKEN_EOF
) {
474 resolvableOperator
= B_PACKAGE_RESOLVABLE_OP_ENUM_COUNT
;
477 "expected '<', '<=', '==', '!=', '>=', '>', comma or '}'",
481 _value
.SetTo(token
.text
, resolvableOperator
, version
);
486 BPackageInfo::Parser::_ParseList(ListElementParser
& elementParser
,
487 bool allowSingleNonListElement
)
489 Token openBracket
= _NextToken();
490 if (openBracket
.type
!= TOKEN_OPEN_BRACE
) {
491 if (!allowSingleNonListElement
)
492 throw ParseError("expected start of list ('{')", openBracket
.pos
);
494 elementParser(openBracket
);
499 Token token
= _NextToken();
500 if (token
.type
== TOKEN_CLOSE_BRACE
)
503 if (token
.type
== TOKEN_ITEM_SEPARATOR
)
506 elementParser(token
);
512 BPackageInfo::Parser::_ParseStringList(BStringList
* value
,
513 bool requireResolvableName
, bool convertToLowerCase
,
514 StringValidator
* stringValidator
)
516 struct StringParser
: public ListElementParser
{
518 bool requireResolvableName
;
519 bool convertToLowerCase
;
520 StringValidator
* stringValidator
;
522 StringParser(BStringList
* value
, bool requireResolvableName
,
523 bool convertToLowerCase
, StringValidator
* stringValidator
)
526 requireResolvableName(requireResolvableName
),
527 convertToLowerCase(convertToLowerCase
),
528 stringValidator(stringValidator
)
532 virtual void operator()(const Token
& token
)
534 if (token
.type
!= TOKEN_STRING
)
535 throw ParseError("expected string", token
.pos
);
537 if (requireResolvableName
) {
539 if (!_IsValidResolvableName(token
.text
, &errorPos
)) {
540 throw ParseError("invalid character in resolvable name",
541 token
.pos
+ errorPos
);
545 BString
element(token
.text
);
546 if (convertToLowerCase
)
549 if (stringValidator
!= NULL
)
550 stringValidator
->Validate(element
, token
.pos
);
554 } stringParser(value
, requireResolvableName
, convertToLowerCase
,
557 _ParseList(stringParser
, true);
562 BPackageInfo::Parser::_ParseFlags()
564 struct FlagParser
: public ListElementParser
{
573 virtual void operator()(const Token
& token
)
575 if (token
.type
!= TOKEN_STRING
)
576 throw ParseError("expected word (a flag)", token
.pos
);
578 if (token
.text
.ICompare("approve_license") == 0)
579 flags
|= B_PACKAGE_FLAG_APPROVE_LICENSE
;
580 else if (token
.text
.ICompare("system_package") == 0)
581 flags
|= B_PACKAGE_FLAG_SYSTEM_PACKAGE
;
584 "expected 'approve_license' or 'system_package'",
590 _ParseList(flagParser
, true);
592 return flagParser
.flags
;
597 BPackageInfo::Parser::_ParseResolvableList(
598 BObjectList
<BPackageResolvable
>* value
)
600 struct ResolvableParser
: public ListElementParser
{
602 BObjectList
<BPackageResolvable
>* value
;
604 ResolvableParser(Parser
& parser_
,
605 BObjectList
<BPackageResolvable
>* value_
)
612 virtual void operator()(const Token
& token
)
614 if (token
.type
!= TOKEN_STRING
) {
615 throw ParseError("expected word (a resolvable name)",
620 if (!_IsValidResolvableName(token
.text
, &errorPos
)) {
621 throw ParseError("invalid character in resolvable name",
622 token
.pos
+ errorPos
);
626 BPackageVersion version
;
627 Token op
= parser
._NextToken();
628 if (op
.type
== TOKEN_OPERATOR_ASSIGN
) {
629 parser
._ParseVersionValue(&version
, true);
630 } else if (op
.type
== TOKEN_ITEM_SEPARATOR
631 || op
.type
== TOKEN_CLOSE_BRACE
) {
632 parser
._RewindTo(op
);
634 throw ParseError("expected '=', comma or '}'", op
.pos
);
636 // parse compatible version
637 BPackageVersion compatibleVersion
;
638 Token compatible
= parser
._NextToken();
639 if (compatible
.type
== TOKEN_STRING
640 && (compatible
.text
== "compat"
641 || compatible
.text
== "compatible")) {
642 op
= parser
._NextToken();
643 if (op
.type
== TOKEN_OPERATOR_GREATER_EQUAL
) {
644 parser
._ParseVersionValue(&compatibleVersion
, true);
646 parser
._RewindTo(compatible
);
648 parser
._RewindTo(compatible
);
650 value
->AddItem(new BPackageResolvable(token
.text
, version
,
653 } resolvableParser(*this, value
);
655 _ParseList(resolvableParser
, false);
660 BPackageInfo::Parser::_ParseResolvableExprList(
661 BObjectList
<BPackageResolvableExpression
>* value
, BString
* _basePackage
)
663 struct ResolvableExpressionParser
: public ListElementParser
{
665 BObjectList
<BPackageResolvableExpression
>* value
;
666 BString
* basePackage
;
668 ResolvableExpressionParser(Parser
& parser
,
669 BObjectList
<BPackageResolvableExpression
>* value
,
670 BString
* basePackage
)
674 basePackage(basePackage
)
678 virtual void operator()(const Token
& token
)
680 BPackageResolvableExpression expression
;
681 parser
._ParseResolvableExpression(token
, expression
, basePackage
);
682 value
->AddItem(new BPackageResolvableExpression(expression
));
684 } resolvableExpressionParser(*this, value
, _basePackage
);
686 _ParseList(resolvableExpressionParser
, false);
691 BPackageInfo::Parser::_ParseGlobalWritableFileInfos(
692 GlobalWritableFileInfoList
* infos
)
694 struct GlobalWritableFileInfoParser
: public ListElementParser
{
696 GlobalWritableFileInfoList
* infos
;
698 GlobalWritableFileInfoParser(Parser
& parser
,
699 GlobalWritableFileInfoList
* infos
)
706 virtual void operator()(const Token
& token
)
708 if (token
.type
!= TOKEN_STRING
) {
709 throw ParseError("expected string (a file path)",
713 BWritableFileUpdateType updateType
714 = B_WRITABLE_FILE_UPDATE_TYPE_ENUM_COUNT
;
715 bool isDirectory
= false;
717 Token nextToken
= parser
._NextToken();
718 if (nextToken
.type
== TOKEN_STRING
719 && nextToken
.text
== "directory") {
721 nextToken
= parser
._NextToken();
724 if (nextToken
.type
== TOKEN_STRING
) {
725 const char* const* end
= kWritableFileUpdateTypes
726 + B_WRITABLE_FILE_UPDATE_TYPE_ENUM_COUNT
;
727 const char* const* found
= std::find(kWritableFileUpdateTypes
,
728 end
, nextToken
.text
);
730 throw ParseError(BString("expected an update type"),
733 updateType
= (BWritableFileUpdateType
)(
734 found
- kWritableFileUpdateTypes
);
735 } else if (nextToken
.type
== TOKEN_ITEM_SEPARATOR
736 || nextToken
.type
== TOKEN_CLOSE_BRACE
) {
737 parser
._RewindTo(nextToken
);
740 "expected 'included', semicolon, new line or '}'",
744 if (!infos
->AddItem(new BGlobalWritableFileInfo(token
.text
,
745 updateType
, isDirectory
))) {
746 throw std::bad_alloc();
749 } resolvableExpressionParser(*this, infos
);
751 _ParseList(resolvableExpressionParser
, false);
756 BPackageInfo::Parser::_ParseUserSettingsFileInfos(
757 UserSettingsFileInfoList
* infos
)
759 struct UserSettingsFileInfoParser
: public ListElementParser
{
761 UserSettingsFileInfoList
* infos
;
763 UserSettingsFileInfoParser(Parser
& parser
,
764 UserSettingsFileInfoList
* infos
)
771 virtual void operator()(const Token
& token
)
773 if (token
.type
!= TOKEN_STRING
) {
774 throw ParseError("expected string (a settings file path)",
778 BString templatePath
;
779 bool isDirectory
= false;
781 Token nextToken
= parser
._NextToken();
782 if (nextToken
.type
== TOKEN_STRING
783 && nextToken
.text
== "directory") {
785 } else if (nextToken
.type
== TOKEN_STRING
786 && nextToken
.text
== "template") {
787 nextToken
= parser
._NextToken();
788 if (nextToken
.type
!= TOKEN_STRING
) {
790 "expected string (a settings template file path)",
793 templatePath
= nextToken
.text
;
794 } else if (nextToken
.type
== TOKEN_ITEM_SEPARATOR
795 || nextToken
.type
== TOKEN_CLOSE_BRACE
) {
796 parser
._RewindTo(nextToken
);
799 "expected 'template', semicolon, new line or '}'",
804 ? !infos
->AddItem(new BUserSettingsFileInfo(token
.text
, true))
805 : !infos
->AddItem(new BUserSettingsFileInfo(token
.text
,
807 throw std::bad_alloc();
810 } resolvableExpressionParser(*this, infos
);
812 _ParseList(resolvableExpressionParser
, false);
817 BPackageInfo::Parser::_ParseUsers(UserList
* users
)
819 struct UserParser
: public ListElementParser
{
823 UserParser(Parser
& parser
, UserList
* users
)
830 virtual void operator()(const Token
& token
)
832 if (token
.type
!= TOKEN_STRING
833 || !BUser::IsValidUserName(token
.text
)) {
834 throw ParseError("expected a user name", token
.pos
);
843 Token nextToken
= parser
._NextToken();
844 if (nextToken
.type
!= TOKEN_STRING
) {
845 parser
._RewindTo(nextToken
);
849 if (nextToken
.text
== "real-name") {
850 nextToken
= parser
._NextToken();
851 if (nextToken
.type
!= TOKEN_STRING
) {
852 throw ParseError("expected string (a user real name)",
855 realName
= nextToken
.text
;
856 } else if (nextToken
.text
== "home") {
857 nextToken
= parser
._NextToken();
858 if (nextToken
.type
!= TOKEN_STRING
) {
859 throw ParseError("expected string (a home path)",
862 home
= nextToken
.text
;
863 } else if (nextToken
.text
== "shell") {
864 nextToken
= parser
._NextToken();
865 if (nextToken
.type
!= TOKEN_STRING
) {
866 throw ParseError("expected string (a shell path)",
869 shell
= nextToken
.text
;
870 } else if (nextToken
.text
== "groups") {
872 nextToken
= parser
._NextToken();
873 if (nextToken
.type
== TOKEN_STRING
874 && BUser::IsValidUserName(nextToken
.text
)) {
875 if (!groups
.Add(nextToken
.text
))
876 throw std::bad_alloc();
877 } else if (nextToken
.type
== TOKEN_ITEM_SEPARATOR
878 || nextToken
.type
== TOKEN_CLOSE_BRACE
) {
879 parser
._RewindTo(nextToken
);
882 throw ParseError("expected a group name",
889 "expected 'real-name', 'home', 'shell', or 'groups'",
894 BString templatePath
;
896 Token nextToken
= parser
._NextToken();
897 if (nextToken
.type
== TOKEN_STRING
898 && nextToken
.text
== "template") {
899 nextToken
= parser
._NextToken();
900 if (nextToken
.type
!= TOKEN_STRING
) {
902 "expected string (a settings template file path)",
905 templatePath
= nextToken
.text
;
906 } else if (nextToken
.type
== TOKEN_ITEM_SEPARATOR
907 || nextToken
.type
== TOKEN_CLOSE_BRACE
) {
908 parser
._RewindTo(nextToken
);
911 "expected 'template', semicolon, new line or '}'",
915 if (!users
->AddItem(new BUser(token
.text
, realName
, home
, shell
,
917 throw std::bad_alloc();
920 } resolvableExpressionParser(*this, users
);
922 _ParseList(resolvableExpressionParser
, false);
927 BPackageInfo::Parser::_Parse(BPackageInfo
* packageInfo
)
929 bool seen
[B_PACKAGE_INFO_ENUM_COUNT
];
930 for (int i
= 0; i
< B_PACKAGE_INFO_ENUM_COUNT
; ++i
)
933 const char* const* names
= BPackageInfo::kElementNames
;
935 while (Token t
= _NextToken()) {
936 if (t
.type
== TOKEN_ITEM_SEPARATOR
)
939 if (t
.type
!= TOKEN_STRING
)
940 throw ParseError("expected string (a variable name)", t
.pos
);
942 BPackageInfoAttributeID attribute
= B_PACKAGE_INFO_ENUM_COUNT
;
943 for (int i
= 0; i
< B_PACKAGE_INFO_ENUM_COUNT
; i
++) {
944 if (names
[i
] != NULL
&& t
.text
.ICompare(names
[i
]) == 0) {
945 attribute
= (BPackageInfoAttributeID
)i
;
950 if (attribute
== B_PACKAGE_INFO_ENUM_COUNT
) {
951 BString error
= BString("unknown attribute \"") << t
.text
<< '"';
952 throw ParseError(error
, t
.pos
);
955 if (seen
[attribute
]) {
956 BString error
= BString(names
[attribute
]) << " already seen!";
957 throw ParseError(error
, t
.pos
);
961 case B_PACKAGE_INFO_NAME
:
965 _ParseStringValue(&name
, &namePos
);
968 if (!_IsValidResolvableName(name
, &errorPos
)) {
969 throw ParseError("invalid character in package name",
973 packageInfo
->SetName(name
);
977 case B_PACKAGE_INFO_SUMMARY
:
980 _ParseStringValue(&summary
);
981 if (summary
.FindFirst('\n') >= 0)
982 throw ParseError("the summary contains linebreaks", t
.pos
);
983 packageInfo
->SetSummary(summary
);
987 case B_PACKAGE_INFO_DESCRIPTION
:
988 _ParseStringValue(&packageInfo
->fDescription
);
991 case B_PACKAGE_INFO_VENDOR
:
992 _ParseStringValue(&packageInfo
->fVendor
);
995 case B_PACKAGE_INFO_PACKAGER
:
996 _ParseStringValue(&packageInfo
->fPackager
);
999 case B_PACKAGE_INFO_BASE_PACKAGE
:
1000 _ParseStringValue(&packageInfo
->fBasePackage
);
1003 case B_PACKAGE_INFO_ARCHITECTURE
:
1004 _ParseArchitectureValue(&packageInfo
->fArchitecture
);
1007 case B_PACKAGE_INFO_VERSION
:
1008 _ParseVersionValue(&packageInfo
->fVersion
, false);
1011 case B_PACKAGE_INFO_COPYRIGHTS
:
1012 _ParseStringList(&packageInfo
->fCopyrightList
);
1015 case B_PACKAGE_INFO_LICENSES
:
1016 _ParseStringList(&packageInfo
->fLicenseList
);
1019 case B_PACKAGE_INFO_URLS
:
1021 UrlStringValidator stringValidator
;
1022 _ParseStringList(&packageInfo
->fURLList
,
1023 false, false, &stringValidator
);
1027 case B_PACKAGE_INFO_SOURCE_URLS
:
1029 UrlStringValidator stringValidator
;
1030 _ParseStringList(&packageInfo
->fSourceURLList
,
1031 false, false, &stringValidator
);
1035 case B_PACKAGE_INFO_GLOBAL_WRITABLE_FILES
:
1036 _ParseGlobalWritableFileInfos(
1037 &packageInfo
->fGlobalWritableFileInfos
);
1040 case B_PACKAGE_INFO_USER_SETTINGS_FILES
:
1041 _ParseUserSettingsFileInfos(
1042 &packageInfo
->fUserSettingsFileInfos
);
1045 case B_PACKAGE_INFO_USERS
:
1046 _ParseUsers(&packageInfo
->fUsers
);
1049 case B_PACKAGE_INFO_GROUPS
:
1050 _ParseStringList(&packageInfo
->fGroups
);
1053 case B_PACKAGE_INFO_POST_INSTALL_SCRIPTS
:
1054 _ParseStringList(&packageInfo
->fPostInstallScripts
);
1057 case B_PACKAGE_INFO_PROVIDES
:
1058 _ParseResolvableList(&packageInfo
->fProvidesList
);
1061 case B_PACKAGE_INFO_REQUIRES
:
1062 packageInfo
->fBasePackage
.Truncate(0);
1063 _ParseResolvableExprList(&packageInfo
->fRequiresList
,
1064 &packageInfo
->fBasePackage
);
1067 case B_PACKAGE_INFO_SUPPLEMENTS
:
1068 _ParseResolvableExprList(&packageInfo
->fSupplementsList
);
1071 case B_PACKAGE_INFO_CONFLICTS
:
1072 _ParseResolvableExprList(&packageInfo
->fConflictsList
);
1075 case B_PACKAGE_INFO_FRESHENS
:
1076 _ParseResolvableExprList(&packageInfo
->fFreshensList
);
1079 case B_PACKAGE_INFO_REPLACES
:
1080 _ParseStringList(&packageInfo
->fReplacesList
, true);
1083 case B_PACKAGE_INFO_FLAGS
:
1084 packageInfo
->SetFlags(_ParseFlags());
1088 // can never get here
1092 seen
[attribute
] = true;
1095 // everything up to and including 'provides' is mandatory
1096 for (int i
= 0; i
<= B_PACKAGE_INFO_PROVIDES
; ++i
) {
1098 BString error
= BString(names
[i
]) << " is not being set anywhere!";
1099 throw ParseError(error
, fPos
);
1105 /*static*/ inline bool
1106 BPackageInfo::Parser::_IsAlphaNumUnderscore(const BString
& string
,
1107 const char* additionalChars
, int32
* _errorPos
)
1109 return _IsAlphaNumUnderscore(string
.String(),
1110 string
.String() + string
.Length(), additionalChars
, _errorPos
);
1114 /*static*/ inline bool
1115 BPackageInfo::Parser::_IsAlphaNumUnderscore(const char* string
,
1116 const char* additionalChars
, int32
* _errorPos
)
1118 return _IsAlphaNumUnderscore(string
, string
+ strlen(string
),
1119 additionalChars
, _errorPos
);
1124 BPackageInfo::Parser::_IsAlphaNumUnderscore(const char* start
, const char* end
,
1125 const char* additionalChars
, int32
* _errorPos
)
1127 for (const char* c
= start
; c
< end
; c
++) {
1128 if (!isalnum(*c
) && *c
!= '_' && strchr(additionalChars
, *c
) == NULL
) {
1129 if (_errorPos
!= NULL
)
1130 *_errorPos
= c
- start
;
1140 BPackageInfo::Parser::_IsValidResolvableName(const char* string
,
1143 for (const char* c
= string
; *c
!= '\0'; c
++) {
1158 if (_errorPos
!= NULL
)
1159 *_errorPos
= c
- string
;
1166 BPackageInfo::Parser::UrlStringValidator::Validate(const BString
& urlString
,
1169 BUrl
url(urlString
);
1172 throw ParseError("invalid url", pos
);
1176 } // namespace BPackageKit