1 // -*- mode: cpp; mode: fold -*-
3 // $Id: configuration.cc,v 1.28 2004/04/30 04:00:15 mdz Exp $
4 /* ######################################################################
8 This class provides a configuration file and command line parser
9 for a tree-oriented configuration environment. All runtime configuration
12 This source is placed in the Public Domain, do with it what you will
13 It was originally written by Jason Gunthorpe <jgg@debian.org>.
15 ##################################################################### */
17 // Include files /*{{{*/
20 #include <apt-pkg/configuration.h>
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg/fileutl.h>
24 #include <apt-pkg/macros.h>
40 #include <unordered_map>
47 Configuration
*_config
= new Configuration
;
49 /* TODO: This config verification shouldn't be using a static variable
50 but a Cnf-member – but that would need ABI breaks and stuff and for now
51 that really is an apt-dev-only tool, so it isn't that bad that it is
52 unusable and allaround a bit strange */
53 enum class APT_HIDDEN ConfigType
{ UNDEFINED
, INT
, BOOL
, STRING
, STRING_OR_BOOL
, STRING_OR_LIST
, FILE, DIR, LIST
, PROGRAM_PATH
= FILE };
54 APT_HIDDEN
std::unordered_map
<std::string
, ConfigType
> apt_known_config
{};
55 static std::string
getConfigTypeString(ConfigType
const type
) /*{{{*/
59 case ConfigType::UNDEFINED
: return "UNDEFINED";
60 case ConfigType::INT
: return "INT";
61 case ConfigType::BOOL
: return "BOOL";
62 case ConfigType::STRING
: return "STRING";
63 case ConfigType::STRING_OR_BOOL
: return "STRING_OR_BOOL";
64 case ConfigType::FILE: return "FILE";
65 case ConfigType::DIR: return "DIR";
66 case ConfigType::LIST
: return "LIST";
67 case ConfigType::STRING_OR_LIST
: return "STRING_OR_LIST";
72 static ConfigType
getConfigType(std::string
const &type
) /*{{{*/
75 return ConfigType::INT
;
76 else if (type
== "<BOOL>")
77 return ConfigType::BOOL
;
78 else if (type
== "<STRING>")
79 return ConfigType::STRING
;
80 else if (type
== "<STRING_OR_BOOL>")
81 return ConfigType::STRING_OR_BOOL
;
82 else if (type
== "<FILE>")
83 return ConfigType::FILE;
84 else if (type
== "<DIR>")
85 return ConfigType::DIR;
86 else if (type
== "<LIST>")
87 return ConfigType::LIST
;
88 else if (type
== "<STRING_OR_LIST>")
89 return ConfigType::STRING_OR_LIST
;
90 else if (type
== "<PROGRAM_PATH>")
91 return ConfigType::PROGRAM_PATH
;
92 return ConfigType::UNDEFINED
;
95 static void checkFindConfigOptionType(std::string name
, ConfigType
const type
)/*{{{*/
97 if (apt_known_config
.empty())
99 std::transform(name
.begin(), name
.end(), name
.begin(), ::tolower
);
100 auto known
= apt_known_config
.find(name
);
101 if (known
== apt_known_config
.cend())
103 auto const rcolon
= name
.rfind(':');
104 if (rcolon
!= std::string::npos
)
106 known
= apt_known_config
.find(name
.substr(0, rcolon
) + ":*");
107 if (known
== apt_known_config
.cend())
109 auto const parts
= StringSplit(name
, "::");
110 size_t psize
= parts
.size();
113 for (size_t max
= psize
; max
!= 1; --max
)
115 std::ostringstream os
;
116 std::copy(parts
.begin(), parts
.begin() + max
, std::ostream_iterator
<std::string
>(os
, "::"));
118 known
= apt_known_config
.find(os
.str());
119 if (known
!= apt_known_config
.cend() && known
->second
== ConfigType::UNDEFINED
)
122 for (size_t max
= psize
- 1; max
!= 1; --max
)
124 std::ostringstream os
;
125 std::copy(parts
.begin(), parts
.begin() + max
- 1, std::ostream_iterator
<std::string
>(os
, "::"));
127 std::copy(parts
.begin() + max
+ 1, parts
.end() - 1, std::ostream_iterator
<std::string
>(os
, "::"));
128 os
<< *(parts
.end() - 1);
129 known
= apt_known_config
.find(os
.str());
130 if (known
!= apt_known_config
.cend())
137 if (known
== apt_known_config
.cend())
138 _error
->Warning("Using unknown config option »%s« of type %s",
139 name
.c_str(), getConfigTypeString(type
).c_str());
140 else if (known
->second
!= type
)
142 if (known
->second
== ConfigType::DIR && type
== ConfigType::FILE)
143 ; // implementation detail
144 else if (type
== ConfigType::STRING
&& (known
->second
== ConfigType::FILE || known
->second
== ConfigType::DIR))
145 ; // TODO: that might be an error or not, we will figure this out later
146 else if (known
->second
== ConfigType::STRING_OR_BOOL
&& (type
== ConfigType::BOOL
|| type
== ConfigType::STRING
))
148 else if (known
->second
== ConfigType::STRING_OR_LIST
&& (type
== ConfigType::LIST
|| type
== ConfigType::STRING
))
151 _error
->Warning("Using config option »%s« of type %s as a type %s",
152 name
.c_str(), getConfigTypeString(known
->second
).c_str(), getConfigTypeString(type
).c_str());
156 static bool LoadConfigurationIndex(std::string
const &filename
) /*{{{*/
158 apt_known_config
.clear();
159 if (filename
.empty())
162 if (ReadConfigFile(Idx
, filename
) == false)
165 Configuration::Item
const * Top
= Idx
.Tree(nullptr);
166 if (unlikely(Top
== nullptr))
170 if (Top
->Value
.empty() == false)
172 std::string fulltag
= Top
->FullTag();
173 std::transform(fulltag
.begin(), fulltag
.end(), fulltag
.begin(), ::tolower
);
174 apt_known_config
.emplace(std::move(fulltag
), getConfigType(Top
->Value
));
177 if (Top
->Child
!= nullptr)
183 while (Top
!= nullptr && Top
->Next
== nullptr)
187 } while (Top
!= nullptr);
193 // Configuration::Configuration - Constructor /*{{{*/
194 // ---------------------------------------------------------------------
196 Configuration::Configuration() : ToFree(true)
200 Configuration::Configuration(const Item
*Root
) : Root((Item
*)Root
), ToFree(false)
204 // Configuration::~Configuration - Destructor /*{{{*/
205 // ---------------------------------------------------------------------
207 Configuration::~Configuration()
221 while (Top
!= 0 && Top
->Next
== 0)
223 Item
*Parent
= Top
->Parent
;
229 Item
*Next
= Top
->Next
;
236 // Configuration::Lookup - Lookup a single item /*{{{*/
237 // ---------------------------------------------------------------------
238 /* This will lookup a single item by name below another item. It is a
239 helper function for the main lookup function */
240 Configuration::Item
*Configuration::Lookup(Item
*Head
,const char *S
,
241 unsigned long const &Len
,bool const &Create
)
244 Item
*I
= Head
->Child
;
245 Item
**Last
= &Head
->Child
;
247 // Empty strings match nothing. They are used for lists.
250 for (; I
!= 0; Last
= &I
->Next
, I
= I
->Next
)
251 if ((Res
= stringcasecmp(I
->Tag
,S
,S
+ Len
)) == 0)
255 for (; I
!= 0; Last
= &I
->Next
, I
= I
->Next
);
263 I
->Tag
.assign(S
,Len
);
270 // Configuration::Lookup - Lookup a fully scoped item /*{{{*/
271 // ---------------------------------------------------------------------
272 /* This performs a fully scoped lookup of a given name, possibly creating
274 Configuration::Item
*Configuration::Lookup(const char *Name
,bool const &Create
)
279 const char *Start
= Name
;
280 const char *End
= Start
+ strlen(Name
);
281 const char *TagEnd
= Name
;
283 for (; End
- TagEnd
>= 2; TagEnd
++)
285 if (TagEnd
[0] == ':' && TagEnd
[1] == ':')
287 Itm
= Lookup(Itm
,Start
,TagEnd
- Start
,Create
);
290 TagEnd
= Start
= TagEnd
+ 2;
294 // This must be a trailing ::, we create unique items in a list
295 if (End
- Start
== 0)
301 Itm
= Lookup(Itm
,Start
,End
- Start
,Create
);
305 // Configuration::Find - Find a value /*{{{*/
306 // ---------------------------------------------------------------------
308 string
Configuration::Find(const char *Name
,const char *Default
) const
310 checkFindConfigOptionType(Name
, ConfigType::STRING
);
311 const Item
*Itm
= Lookup(Name
);
312 if (Itm
== 0 || Itm
->Value
.empty() == true)
323 // Configuration::FindFile - Find a Filename /*{{{*/
324 // ---------------------------------------------------------------------
325 /* Directories are stored as the base dir in the Parent node and the
326 sub directory in sub nodes with the final node being the end filename
328 string
Configuration::FindFile(const char *Name
,const char *Default
) const
330 checkFindConfigOptionType(Name
, ConfigType::FILE);
331 const Item
*RootItem
= Lookup("RootDir");
332 std::string result
= (RootItem
== 0) ? "" : RootItem
->Value
;
333 if(result
.empty() == false && result
[result
.size() - 1] != '/')
334 result
.push_back('/');
336 const Item
*Itm
= Lookup(Name
);
337 if (Itm
== 0 || Itm
->Value
.empty() == true)
340 result
.append(Default
);
344 string val
= Itm
->Value
;
345 while (Itm
->Parent
!= 0)
347 if (Itm
->Parent
->Value
.empty() == true)
354 if (val
.length() >= 1 && val
[0] == '/')
356 if (val
.compare(0, 9, "/dev/null") == 0)
362 if (val
.length() >= 2 && (val
[0] == '~' || val
[0] == '.') && val
[1] == '/')
366 if (val
.length() >= 3 && val
[0] == '.' && val
[1] == '.' && val
[2] == '/')
369 if (Itm
->Parent
->Value
.end()[-1] != '/')
372 val
.insert(0, Itm
->Parent
->Value
);
377 return flNormalize(result
);
380 // Configuration::FindDir - Find a directory name /*{{{*/
381 // ---------------------------------------------------------------------
382 /* This is like findfile execept the result is terminated in a / */
383 string
Configuration::FindDir(const char *Name
,const char *Default
) const
385 checkFindConfigOptionType(Name
, ConfigType::DIR);
386 string Res
= FindFile(Name
,Default
);
387 if (Res
.end()[-1] != '/')
389 size_t const found
= Res
.rfind("/dev/null");
390 if (found
!= string::npos
&& found
== Res
.size() - 9)
391 return Res
; // /dev/null returning
397 // Configuration::FindVector - Find a vector of values /*{{{*/
398 // ---------------------------------------------------------------------
399 /* Returns a vector of config values under the given item */
400 vector
<string
> Configuration::FindVector(const char *Name
, std::string
const &Default
, bool const Keys
) const
402 checkFindConfigOptionType(Name
, ConfigType::LIST
);
404 const Item
*Top
= Lookup(Name
);
406 return VectorizeString(Default
, ',');
408 if (Top
->Value
.empty() == false)
409 return VectorizeString(Top
->Value
, ',');
411 Item
*I
= Top
->Child
;
414 Vec
.push_back(Keys
? I
->Tag
: I
->Value
);
417 if (Vec
.empty() == true)
418 return VectorizeString(Default
, ',');
423 // Configuration::FindI - Find an integer value /*{{{*/
424 // ---------------------------------------------------------------------
426 int Configuration::FindI(const char *Name
,int const &Default
) const
428 checkFindConfigOptionType(Name
, ConfigType::INT
);
429 const Item
*Itm
= Lookup(Name
);
430 if (Itm
== 0 || Itm
->Value
.empty() == true)
434 int Res
= strtol(Itm
->Value
.c_str(),&End
,0);
435 if (End
== Itm
->Value
.c_str())
441 // Configuration::FindB - Find a boolean type /*{{{*/
442 // ---------------------------------------------------------------------
444 bool Configuration::FindB(const char *Name
,bool const &Default
) const
446 checkFindConfigOptionType(Name
, ConfigType::BOOL
);
447 const Item
*Itm
= Lookup(Name
);
448 if (Itm
== 0 || Itm
->Value
.empty() == true)
451 return StringToBool(Itm
->Value
,Default
);
454 // Configuration::FindAny - Find an arbitrary type /*{{{*/
455 // ---------------------------------------------------------------------
456 /* a key suffix of /f, /d, /b or /i calls Find{File,Dir,B,I} */
457 string
Configuration::FindAny(const char *Name
,const char *Default
) const
462 if (key
.size() > 2 && key
.end()[-2] == '/')
464 type
= key
.end()[-1];
465 key
.resize(key
.size() - 2);
472 return FindFile(key
.c_str(), Default
);
476 return FindDir(key
.c_str(), Default
);
480 return FindB(key
, Default
) ? "true" : "false";
486 snprintf(buf
, sizeof(buf
)-1, "%d", FindI(key
, Default
? atoi(Default
) : 0 ));
492 return Find(Name
, Default
);
495 // Configuration::CndSet - Conditinal Set a value /*{{{*/
496 // ---------------------------------------------------------------------
497 /* This will not overwrite */
498 void Configuration::CndSet(const char *Name
,const string
&Value
)
500 Item
*Itm
= Lookup(Name
,true);
503 if (Itm
->Value
.empty() == true)
507 // Configuration::Set - Set an integer value /*{{{*/
508 // ---------------------------------------------------------------------
510 void Configuration::CndSet(const char *Name
,int const Value
)
512 Item
*Itm
= Lookup(Name
,true);
513 if (Itm
== 0 || Itm
->Value
.empty() == false)
516 snprintf(S
,sizeof(S
),"%i",Value
);
520 // Configuration::Set - Set a value /*{{{*/
521 // ---------------------------------------------------------------------
523 void Configuration::Set(const char *Name
,const string
&Value
)
525 Item
*Itm
= Lookup(Name
,true);
531 // Configuration::Set - Set an integer value /*{{{*/
532 // ---------------------------------------------------------------------
534 void Configuration::Set(const char *Name
,int const &Value
)
536 Item
*Itm
= Lookup(Name
,true);
540 snprintf(S
,sizeof(S
),"%i",Value
);
544 // Configuration::Clear - Clear an single value from a list /*{{{*/
545 // ---------------------------------------------------------------------
547 void Configuration::Clear(string
const &Name
, int const &Value
)
550 snprintf(S
,sizeof(S
),"%i",Value
);
554 // Configuration::Clear - Clear an single value from a list /*{{{*/
555 // ---------------------------------------------------------------------
557 void Configuration::Clear(string
const &Name
, string
const &Value
)
559 Item
*Top
= Lookup(Name
.c_str(),false);
560 if (Top
== 0 || Top
->Child
== 0)
563 Item
*Tmp
, *Prev
, *I
;
564 Prev
= I
= Top
->Child
;
568 if(I
->Value
== Value
)
571 // was first element, point parent to new first element
572 if(Top
->Child
== Tmp
)
573 Top
->Child
= I
->Next
;
585 // Configuration::Clear - Clear everything /*{{{*/
586 // ---------------------------------------------------------------------
587 void Configuration::Clear()
589 const Configuration::Item
*Top
= Tree(0);
592 Clear(Top
->FullTag());
597 // Configuration::Clear - Clear an entire tree /*{{{*/
598 // ---------------------------------------------------------------------
600 void Configuration::Clear(string
const &Name
)
602 Item
*Top
= Lookup(Name
.c_str(),false);
618 while (Top
!= 0 && Top
->Next
== 0)
635 void Configuration::MoveSubTree(char const * const OldRootName
, char const * const NewRootName
)/*{{{*/
637 // prevent NewRoot being a subtree of OldRoot
638 if (OldRootName
== nullptr)
640 if (NewRootName
!= nullptr)
642 if (strcmp(OldRootName
, NewRootName
) == 0)
644 std::string
const oldroot
= std::string(OldRootName
) + "::";
645 if (strcasestr(NewRootName
, oldroot
.c_str()) != NULL
)
650 Item
const * const OldRoot
= Top
= Lookup(OldRootName
, false);
654 if (NewRootName
!= nullptr)
655 NewRoot
.append(NewRootName
).append("::");
658 Item
* const Stop
= Top
;
669 while (Top
!= 0 && Top
->Next
== 0)
671 Set(NewRoot
+ Top
->FullTag(OldRoot
), Top
->Value
);
672 Item
const * const Tmp
= Top
;
680 Set(NewRoot
+ Top
->FullTag(OldRoot
), Top
->Value
);
681 Item
const * const Tmp
= Top
;
688 // Configuration::Exists - Returns true if the Name exists /*{{{*/
689 // ---------------------------------------------------------------------
691 bool Configuration::Exists(const char *Name
) const
693 const Item
*Itm
= Lookup(Name
);
699 // Configuration::ExistsAny - Returns true if the Name, possibly /*{{{*/
700 // ---------------------------------------------------------------------
701 /* qualified by /[fdbi] exists */
702 bool Configuration::ExistsAny(const char *Name
) const
706 if (key
.size() > 2 && key
.end()[-2] == '/')
708 if (key
.find_first_of("fdbi",key
.size()-1) < key
.size())
710 key
.resize(key
.size() - 2);
711 if (Exists(key
.c_str()))
716 _error
->Warning(_("Unrecognized type abbreviation: '%c'"), key
.end()[-3]);
722 // Configuration::Dump - Dump the config /*{{{*/
723 // ---------------------------------------------------------------------
724 /* Dump the entire configuration space */
725 void Configuration::Dump(ostream
& str
)
727 Dump(str
, NULL
, "%f \"%v\";\n", true);
729 void Configuration::Dump(ostream
& str
, char const * const root
,
730 char const * const formatstr
, bool const emptyValue
)
732 const Configuration::Item
* Top
= Tree(root
);
735 const Configuration::Item
* const Root
= (root
== NULL
) ? NULL
: Top
;
736 std::vector
<std::string
> const format
= VectorizeString(formatstr
, '%');
738 /* Write out all of the configuration directives by walking the
739 configuration tree */
741 if (emptyValue
== true || Top
->Value
.empty() == emptyValue
)
743 std::vector
<std::string
>::const_iterator f
= format
.begin();
745 for (++f
; f
!= format
.end(); ++f
)
747 if (f
->empty() == true)
753 char const type
= (*f
)[0];
755 str
<< Top
->FullTag();
756 else if (type
== 't')
758 else if (type
== 'v')
760 else if (type
== 'F')
761 str
<< QuoteString(Top
->FullTag(), "=\"\n");
762 else if (type
== 'T')
763 str
<< QuoteString(Top
->Tag
, "=\"\n");
764 else if (type
== 'V')
765 str
<< QuoteString(Top
->Value
, "=\"\n");
766 else if (type
== 'n')
768 else if (type
== 'N')
772 str
<< f
->c_str() + 1;
782 while (Top
!= 0 && Top
->Next
== 0)
789 const Configuration::Item
* I
= Top
;
804 // Configuration::Item::FullTag - Return the fully scoped tag /*{{{*/
805 // ---------------------------------------------------------------------
806 /* Stop sets an optional max recursion depth if this item is being viewed as
807 part of a sub tree. */
808 string
Configuration::Item::FullTag(const Item
*Stop
) const
810 if (Parent
== 0 || Parent
->Parent
== 0 || Parent
== Stop
)
812 return Parent
->FullTag(Stop
) + "::" + Tag
;
816 // ReadConfigFile - Read a configuration file /*{{{*/
817 // ---------------------------------------------------------------------
818 /* The configuration format is very much like the named.conf format
819 used in bind8, in fact this routine can parse most named.conf files.
820 Sectional config files are like bind's named.conf where there are
821 sections like 'zone "foo.org" { .. };' This causes each section to be
822 added in with a tag like "zone::foo.org" instead of being split
823 tag/value. AsSectional enables Sectional parsing.*/
824 static void leaveCurrentScope(std::stack
<std::string
> &Stack
, std::string
&ParentTag
)
830 ParentTag
= Stack
.top();
834 bool ReadConfigFile(Configuration
&Conf
,const string
&FName
,bool const &AsSectional
,
835 unsigned const &Depth
)
837 // Open the stream for reading
838 ifstream
F(FName
.c_str(),ios::in
);
839 if (F
.fail() == true)
840 return _error
->Errno("ifstream::ifstream",_("Opening configuration file %s"),FName
.c_str());
843 std::stack
<std::string
> Stack
;
849 bool InComment
= false;
850 while (F
.eof() == false)
852 // The raw input line.
854 // The input line with comments stripped.
855 std::string Fragment
;
857 // Grab the next line of F and place it in Input.
860 char *Buffer
= new char[1024];
863 F
.getline(Buffer
,sizeof(Buffer
) / 2);
868 while (F
.fail() && !F
.eof());
870 // Expand tabs in the input line and remove leading and trailing
873 const int BufferSize
= Input
.size() * 8 + 1;
874 char *Buffer
= new char[BufferSize
];
877 memcpy(Buffer
, Input
.c_str(), Input
.size() + 1);
879 _strtabexpand(Buffer
, BufferSize
);
892 // Now strip comments; if the whole line is contained in a
893 // comment, skip this line.
895 // The first meaningful character in the current fragment; will
896 // be adjusted below as we remove bytes from the front.
897 std::string::const_iterator Start
= Input
.begin();
898 // The last meaningful character in the current fragment.
899 std::string::const_iterator End
= Input
.end();
901 // Multi line comment
902 if (InComment
== true)
904 for (std::string::const_iterator I
= Start
;
907 if (*I
== '*' && I
+ 1 != End
&& I
[1] == '/')
914 if (InComment
== true)
918 // Discard single line comments
919 bool InQuote
= false;
920 for (std::string::const_iterator I
= Start
;
928 if ((*I
== '/' && I
+ 1 != End
&& I
[1] == '/') ||
929 (*I
== '#' && strcmp(string(I
,I
+6).c_str(),"#clear") != 0 &&
930 strcmp(string(I
,I
+8).c_str(),"#include") != 0 &&
931 strcmp(string(I
,I
+strlen("#x-apt-configure-index")).c_str(), "#x-apt-configure-index") != 0))
938 // Look for multi line comments and build up the
940 Fragment
.reserve(End
- Start
);
942 for (std::string::const_iterator I
= Start
;
948 Fragment
.push_back(*I
);
949 else if (*I
== '/' && I
+ 1 != End
&& I
[1] == '*')
952 for (std::string::const_iterator J
= I
;
955 if (*J
== '*' && J
+ 1 != End
&& J
[1] == '/')
957 // Pretend we just finished walking over the
958 // comment, and don't add anything to the output
966 if (InComment
== true)
970 Fragment
.push_back(*I
);
974 if (Fragment
.empty())
977 // The line has actual content; interpret what it means.
979 Start
= Fragment
.begin();
980 End
= Fragment
.end();
981 for (std::string::const_iterator I
= Start
;
987 if (InQuote
== false && (*I
== '{' || *I
== ';' || *I
== '}'))
989 // Put the last fragment into the buffer
990 std::string::const_iterator NonWhitespaceStart
= Start
;
991 std::string::const_iterator NonWhitespaceStop
= I
;
992 for (; NonWhitespaceStart
!= I
&& isspace(*NonWhitespaceStart
) != 0; ++NonWhitespaceStart
)
994 for (; NonWhitespaceStop
!= NonWhitespaceStart
&& isspace(NonWhitespaceStop
[-1]) != 0; --NonWhitespaceStop
)
996 if (LineBuffer
.empty() == false && NonWhitespaceStop
- NonWhitespaceStart
!= 0)
998 LineBuffer
+= string(NonWhitespaceStart
, NonWhitespaceStop
);
1000 // Drop this from the input string, saving the character
1001 // that terminated the construct we just closed. (i.e., a
1002 // brace or a semicolon)
1007 if (TermChar
== '{' && LineBuffer
.empty() == true)
1008 return _error
->Error(_("Syntax error %s:%u: Block starts with no name."),FName
.c_str(),CurLine
);
1010 // No string on this line
1011 if (LineBuffer
.empty() == true)
1013 if (TermChar
== '}')
1014 leaveCurrentScope(Stack
, ParentTag
);
1018 // Parse off the tag
1020 const char *Pos
= LineBuffer
.c_str();
1021 if (ParseQuoteWord(Pos
,Tag
) == false)
1022 return _error
->Error(_("Syntax error %s:%u: Malformed tag"),FName
.c_str(),CurLine
);
1024 // Parse off the word
1026 bool NoWord
= false;
1027 if (ParseCWord(Pos
,Word
) == false &&
1028 ParseQuoteWord(Pos
,Word
) == false)
1030 if (TermChar
!= '{')
1038 if (strlen(Pos
) != 0)
1039 return _error
->Error(_("Syntax error %s:%u: Extra junk after value"),FName
.c_str(),CurLine
);
1042 if (TermChar
== '{')
1044 Stack
.push(ParentTag
);
1046 /* Make sectional tags incorperate the section into the
1048 if (AsSectional
== true && Word
.empty() == false)
1050 Tag
.append("::").append(Word
);
1054 if (ParentTag
.empty() == true)
1057 ParentTag
.append("::").append(Tag
);
1061 // Generate the item name
1063 if (ParentTag
.empty() == true)
1067 if (TermChar
!= '{' || Tag
.empty() == false)
1068 Item
= ParentTag
+ "::" + Tag
;
1074 if (Tag
.length() >= 1 && Tag
[0] == '#')
1076 if (ParentTag
.empty() == false)
1077 return _error
->Error(_("Syntax error %s:%u: Directives can only be done at the top level"),FName
.c_str(),CurLine
);
1078 Tag
.erase(Tag
.begin());
1081 else if (Tag
== "include")
1084 return _error
->Error(_("Syntax error %s:%u: Too many nested includes"),FName
.c_str(),CurLine
);
1085 if (Word
.length() > 2 && Word
.end()[-1] == '/')
1087 if (ReadConfigDir(Conf
,Word
,AsSectional
,Depth
+1) == false)
1088 return _error
->Error(_("Syntax error %s:%u: Included from here"),FName
.c_str(),CurLine
);
1092 if (ReadConfigFile(Conf
,Word
,AsSectional
,Depth
+1) == false)
1093 return _error
->Error(_("Syntax error %s:%u: Included from here"),FName
.c_str(),CurLine
);
1096 else if (Tag
== "x-apt-configure-index")
1098 if (LoadConfigurationIndex(Word
) == false)
1099 return _error
->Warning("Loading the configure index %s in file %s:%u failed!", Word
.c_str(), FName
.c_str(), CurLine
);
1102 return _error
->Error(_("Syntax error %s:%u: Unsupported directive '%s'"),FName
.c_str(),CurLine
,Tag
.c_str());
1104 else if (Tag
.empty() == true && NoWord
== false && Word
== "#clear")
1105 return _error
->Error(_("Syntax error %s:%u: clear directive requires an option tree as argument"),FName
.c_str(),CurLine
);
1108 // Set the item in the configuration class
1109 if (NoWord
== false)
1110 Conf
.Set(Item
,Word
);
1116 // Move up a tag, but only if there is no bit to parse
1117 if (TermChar
== '}')
1118 leaveCurrentScope(Stack
, ParentTag
);
1122 // Store the remaining text, if any, in the current line buffer.
1124 // NB: could change this to use string-based operations; I'm
1125 // using strstrip now to ensure backwards compatibility.
1126 // -- dburrows 2008-04-01
1128 char *Buffer
= new char[End
- Start
+ 1];
1131 std::copy(Start
, End
, Buffer
);
1132 Buffer
[End
- Start
] = '\0';
1134 const char *Stripd
= _strstrip(Buffer
);
1135 if (*Stripd
!= 0 && LineBuffer
.empty() == false)
1137 LineBuffer
+= Stripd
;
1148 if (LineBuffer
.empty() == false)
1149 return _error
->Error(_("Syntax error %s:%u: Extra junk at end of file"),FName
.c_str(),CurLine
);
1153 // ReadConfigDir - Read a directory of config files /*{{{*/
1154 // ---------------------------------------------------------------------
1156 bool ReadConfigDir(Configuration
&Conf
,const string
&Dir
,
1157 bool const &AsSectional
, unsigned const &Depth
)
1159 vector
<string
> const List
= GetListOfFilesInDir(Dir
, "conf", true, true);
1162 for (vector
<string
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1163 if (ReadConfigFile(Conf
,*I
,AsSectional
,Depth
) == false)
1168 // MatchAgainstConfig Constructor /*{{{*/
1169 Configuration::MatchAgainstConfig::MatchAgainstConfig(char const * Config
)
1171 std::vector
<std::string
> const strings
= _config
->FindVector(Config
);
1172 for (std::vector
<std::string
>::const_iterator s
= strings
.begin();
1173 s
!= strings
.end(); ++s
)
1175 regex_t
*p
= new regex_t
;
1176 if (regcomp(p
, s
->c_str(), REG_EXTENDED
| REG_ICASE
| REG_NOSUB
) == 0)
1177 patterns
.push_back(p
);
1182 _error
->Warning("Invalid regular expression '%s' in configuration "
1183 "option '%s' will be ignored.",
1184 s
->c_str(), Config
);
1188 if (strings
.empty() == true)
1189 patterns
.push_back(NULL
);
1192 // MatchAgainstConfig Destructor /*{{{*/
1193 Configuration::MatchAgainstConfig::~MatchAgainstConfig()
1197 void Configuration::MatchAgainstConfig::clearPatterns()
1199 for(std::vector
<regex_t
*>::const_iterator p
= patterns
.begin();
1200 p
!= patterns
.end(); ++p
)
1202 if (*p
== NULL
) continue;
1209 // MatchAgainstConfig::Match - returns true if a pattern matches /*{{{*/
1210 bool Configuration::MatchAgainstConfig::Match(char const * str
) const
1212 for(std::vector
<regex_t
*>::const_iterator p
= patterns
.begin();
1213 p
!= patterns
.end(); ++p
)
1214 if (*p
!= NULL
&& regexec(*p
, str
, 0, 0, 0) == 0)