]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/contrib/configuration.cc
add default and override handling for Cnf::FindVector
[apt.git] / apt-pkg / contrib / configuration.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: configuration.cc,v 1.28 2004/04/30 04:00:15 mdz Exp $
4/* ######################################################################
5
6 Configuration Class
7
8 This class provides a configuration file and command line parser
9 for a tree-oriented configuration environment. All runtime configuration
10 is stored in here.
11
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>.
14
15 ##################################################################### */
16 /*}}}*/
17// Include files /*{{{*/
18#include <config.h>
19
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/init.h>
25
26#include <vector>
27#include <fstream>
28#include <iostream>
29
30#include <apti18n.h>
31
32using namespace std;
33 /*}}}*/
34
35Configuration *_config = new Configuration;
36
37// Configuration::Configuration - Constructor /*{{{*/
38// ---------------------------------------------------------------------
39/* */
40Configuration::Configuration() : ToFree(true)
41{
42 Root = new Item;
43}
44Configuration::Configuration(const Item *Root) : Root((Item *)Root), ToFree(false)
45{
46};
47
48 /*}}}*/
49// Configuration::~Configuration - Destructor /*{{{*/
50// ---------------------------------------------------------------------
51/* */
52Configuration::~Configuration()
53{
54 if (ToFree == false)
55 return;
56
57 Item *Top = Root;
58 for (; Top != 0;)
59 {
60 if (Top->Child != 0)
61 {
62 Top = Top->Child;
63 continue;
64 }
65
66 while (Top != 0 && Top->Next == 0)
67 {
68 Item *Parent = Top->Parent;
69 delete Top;
70 Top = Parent;
71 }
72 if (Top != 0)
73 {
74 Item *Next = Top->Next;
75 delete Top;
76 Top = Next;
77 }
78 }
79}
80 /*}}}*/
81// Configuration::Lookup - Lookup a single item /*{{{*/
82// ---------------------------------------------------------------------
83/* This will lookup a single item by name below another item. It is a
84 helper function for the main lookup function */
85Configuration::Item *Configuration::Lookup(Item *Head,const char *S,
86 unsigned long const &Len,bool const &Create)
87{
88 int Res = 1;
89 Item *I = Head->Child;
90 Item **Last = &Head->Child;
91
92 // Empty strings match nothing. They are used for lists.
93 if (Len != 0)
94 {
95 for (; I != 0; Last = &I->Next, I = I->Next)
96 if ((Res = stringcasecmp(I->Tag,S,S + Len)) == 0)
97 break;
98 }
99 else
100 for (; I != 0; Last = &I->Next, I = I->Next);
101
102 if (Res == 0)
103 return I;
104 if (Create == false)
105 return 0;
106
107 I = new Item;
108 I->Tag.assign(S,Len);
109 I->Next = *Last;
110 I->Parent = Head;
111 *Last = I;
112 return I;
113}
114 /*}}}*/
115// Configuration::Lookup - Lookup a fully scoped item /*{{{*/
116// ---------------------------------------------------------------------
117/* This performs a fully scoped lookup of a given name, possibly creating
118 new items */
119Configuration::Item *Configuration::Lookup(const char *Name,bool const &Create)
120{
121 if (Name == 0)
122 return Root->Child;
123
124 const char *Start = Name;
125 const char *End = Start + strlen(Name);
126 const char *TagEnd = Name;
127 Item *Itm = Root;
128 for (; End - TagEnd >= 2; TagEnd++)
129 {
130 if (TagEnd[0] == ':' && TagEnd[1] == ':')
131 {
132 Itm = Lookup(Itm,Start,TagEnd - Start,Create);
133 if (Itm == 0)
134 return 0;
135 TagEnd = Start = TagEnd + 2;
136 }
137 }
138
139 // This must be a trailing ::, we create unique items in a list
140 if (End - Start == 0)
141 {
142 if (Create == false)
143 return 0;
144 }
145
146 Itm = Lookup(Itm,Start,End - Start,Create);
147 return Itm;
148}
149 /*}}}*/
150// Configuration::Find - Find a value /*{{{*/
151// ---------------------------------------------------------------------
152/* */
153string Configuration::Find(const char *Name,const char *Default) const
154{
155 const Item *Itm = Lookup(Name);
156 if (Itm == 0 || Itm->Value.empty() == true)
157 {
158 if (Default == 0)
159 return "";
160 else
161 return Default;
162 }
163
164 return Itm->Value;
165}
166 /*}}}*/
167// Configuration::FindFile - Find a Filename /*{{{*/
168// ---------------------------------------------------------------------
169/* Directories are stored as the base dir in the Parent node and the
170 sub directory in sub nodes with the final node being the end filename
171 */
172string Configuration::FindFile(const char *Name,const char *Default) const
173{
174 const Item *RootItem = Lookup("RootDir");
175 std::string result = (RootItem == 0) ? "" : RootItem->Value;
176 if(result.empty() == false && result[result.size() - 1] != '/')
177 result.push_back('/');
178
179 const Item *Itm = Lookup(Name);
180 if (Itm == 0 || Itm->Value.empty() == true)
181 {
182 if (Default != 0)
183 result.append(Default);
184 }
185 else
186 {
187 string val = Itm->Value;
188 while (Itm->Parent != 0)
189 {
190 if (Itm->Parent->Value.empty() == true)
191 {
192 Itm = Itm->Parent;
193 continue;
194 }
195
196 // Absolute
197 if (val.length() >= 1 && val[0] == '/')
198 {
199 if (val.compare(0, 9, "/dev/null") == 0)
200 val.erase(9);
201 break;
202 }
203
204 // ~/foo or ./foo
205 if (val.length() >= 2 && (val[0] == '~' || val[0] == '.') && val[1] == '/')
206 break;
207
208 // ../foo
209 if (val.length() >= 3 && val[0] == '.' && val[1] == '.' && val[2] == '/')
210 break;
211
212 if (Itm->Parent->Value.end()[-1] != '/')
213 val.insert(0, "/");
214
215 val.insert(0, Itm->Parent->Value);
216 Itm = Itm->Parent;
217 }
218 result.append(val);
219 }
220
221 // do some normalisation by removing // and /./ from the path
222 size_t found = string::npos;
223 while ((found = result.find("/./")) != string::npos)
224 result.replace(found, 3, "/");
225 while ((found = result.find("//")) != string::npos)
226 result.replace(found, 2, "/");
227
228 return result;
229}
230 /*}}}*/
231// Configuration::FindDir - Find a directory name /*{{{*/
232// ---------------------------------------------------------------------
233/* This is like findfile execept the result is terminated in a / */
234string Configuration::FindDir(const char *Name,const char *Default) const
235{
236 string Res = FindFile(Name,Default);
237 if (Res.end()[-1] != '/')
238 {
239 size_t const found = Res.rfind("/dev/null");
240 if (found != string::npos && found == Res.size() - 9)
241 return Res; // /dev/null returning
242 return Res + '/';
243 }
244 return Res;
245}
246 /*}}}*/
247// Configuration::FindVector - Find a vector of values /*{{{*/
248// ---------------------------------------------------------------------
249/* Returns a vector of config values under the given item */
250#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR < 13)
251vector<string> Configuration::FindVector(const char *Name) const { return FindVector(Name, ""); }
252#endif
253vector<string> Configuration::FindVector(const char *Name, std::string const &Default) const
254{
255 vector<string> Vec;
256 const Item *Top = Lookup(Name);
257 if (Top == NULL)
258 return VectorizeString(Default, ',');
259
260 if (Top->Value.empty() == false)
261 return VectorizeString(Top->Value, ',');
262
263 Item *I = Top->Child;
264 while(I != NULL)
265 {
266 Vec.push_back(I->Value);
267 I = I->Next;
268 }
269 if (Vec.empty() == true)
270 return VectorizeString(Default, ',');
271
272 return Vec;
273}
274 /*}}}*/
275// Configuration::FindI - Find an integer value /*{{{*/
276// ---------------------------------------------------------------------
277/* */
278int Configuration::FindI(const char *Name,int const &Default) const
279{
280 const Item *Itm = Lookup(Name);
281 if (Itm == 0 || Itm->Value.empty() == true)
282 return Default;
283
284 char *End;
285 int Res = strtol(Itm->Value.c_str(),&End,0);
286 if (End == Itm->Value.c_str())
287 return Default;
288
289 return Res;
290}
291 /*}}}*/
292// Configuration::FindB - Find a boolean type /*{{{*/
293// ---------------------------------------------------------------------
294/* */
295bool Configuration::FindB(const char *Name,bool const &Default) const
296{
297 const Item *Itm = Lookup(Name);
298 if (Itm == 0 || Itm->Value.empty() == true)
299 return Default;
300
301 return StringToBool(Itm->Value,Default);
302}
303 /*}}}*/
304// Configuration::FindAny - Find an arbitrary type /*{{{*/
305// ---------------------------------------------------------------------
306/* a key suffix of /f, /d, /b or /i calls Find{File,Dir,B,I} */
307string Configuration::FindAny(const char *Name,const char *Default) const
308{
309 string key = Name;
310 char type = 0;
311
312 if (key.size() > 2 && key.end()[-2] == '/')
313 {
314 type = key.end()[-1];
315 key.resize(key.size() - 2);
316 }
317
318 switch (type)
319 {
320 // file
321 case 'f':
322 return FindFile(key.c_str(), Default);
323
324 // directory
325 case 'd':
326 return FindDir(key.c_str(), Default);
327
328 // bool
329 case 'b':
330 return FindB(key, Default) ? "true" : "false";
331
332 // int
333 case 'i':
334 {
335 char buf[16];
336 snprintf(buf, sizeof(buf)-1, "%d", FindI(key, Default ? atoi(Default) : 0 ));
337 return buf;
338 }
339 }
340
341 // fallback
342 return Find(Name, Default);
343}
344 /*}}}*/
345// Configuration::CndSet - Conditinal Set a value /*{{{*/
346// ---------------------------------------------------------------------
347/* This will not overwrite */
348void Configuration::CndSet(const char *Name,const string &Value)
349{
350 Item *Itm = Lookup(Name,true);
351 if (Itm == 0)
352 return;
353 if (Itm->Value.empty() == true)
354 Itm->Value = Value;
355}
356 /*}}}*/
357// Configuration::Set - Set an integer value /*{{{*/
358// ---------------------------------------------------------------------
359/* */
360void Configuration::CndSet(const char *Name,int const Value)
361{
362 Item *Itm = Lookup(Name,true);
363 if (Itm == 0 || Itm->Value.empty() == false)
364 return;
365 char S[300];
366 snprintf(S,sizeof(S),"%i",Value);
367 Itm->Value = S;
368}
369 /*}}}*/
370// Configuration::Set - Set a value /*{{{*/
371// ---------------------------------------------------------------------
372/* */
373void Configuration::Set(const char *Name,const string &Value)
374{
375 Item *Itm = Lookup(Name,true);
376 if (Itm == 0)
377 return;
378 Itm->Value = Value;
379}
380 /*}}}*/
381// Configuration::Set - Set an integer value /*{{{*/
382// ---------------------------------------------------------------------
383/* */
384void Configuration::Set(const char *Name,int const &Value)
385{
386 Item *Itm = Lookup(Name,true);
387 if (Itm == 0)
388 return;
389 char S[300];
390 snprintf(S,sizeof(S),"%i",Value);
391 Itm->Value = S;
392}
393 /*}}}*/
394// Configuration::Clear - Clear an single value from a list /*{{{*/
395// ---------------------------------------------------------------------
396/* */
397void Configuration::Clear(string const &Name, int const &Value)
398{
399 char S[300];
400 snprintf(S,sizeof(S),"%i",Value);
401 Clear(Name, S);
402}
403 /*}}}*/
404// Configuration::Clear - Clear an single value from a list /*{{{*/
405// ---------------------------------------------------------------------
406/* */
407void Configuration::Clear(string const &Name, string const &Value)
408{
409 Item *Top = Lookup(Name.c_str(),false);
410 if (Top == 0 || Top->Child == 0)
411 return;
412
413 Item *Tmp, *Prev, *I;
414 Prev = I = Top->Child;
415
416 while(I != NULL)
417 {
418 if(I->Value == Value)
419 {
420 Tmp = I;
421 // was first element, point parent to new first element
422 if(Top->Child == Tmp)
423 Top->Child = I->Next;
424 I = I->Next;
425 Prev->Next = I;
426 delete Tmp;
427 } else {
428 Prev = I;
429 I = I->Next;
430 }
431 }
432
433}
434 /*}}}*/
435// Configuration::Clear - Clear everything /*{{{*/
436// ---------------------------------------------------------------------
437void Configuration::Clear()
438{
439 const Configuration::Item *Top = Tree(0);
440 while( Top != 0 )
441 {
442 Clear(Top->FullTag());
443 Top = Top->Next;
444 }
445}
446 /*}}}*/
447// Configuration::Clear - Clear an entire tree /*{{{*/
448// ---------------------------------------------------------------------
449/* */
450void Configuration::Clear(string const &Name)
451{
452 Item *Top = Lookup(Name.c_str(),false);
453 if (Top == 0)
454 return;
455
456 Top->Value.clear();
457 Item *Stop = Top;
458 Top = Top->Child;
459 Stop->Child = 0;
460 for (; Top != 0;)
461 {
462 if (Top->Child != 0)
463 {
464 Top = Top->Child;
465 continue;
466 }
467
468 while (Top != 0 && Top->Next == 0)
469 {
470 Item *Tmp = Top;
471 Top = Top->Parent;
472 delete Tmp;
473
474 if (Top == Stop)
475 return;
476 }
477
478 Item *Tmp = Top;
479 if (Top != 0)
480 Top = Top->Next;
481 delete Tmp;
482 }
483}
484 /*}}}*/
485// Configuration::Exists - Returns true if the Name exists /*{{{*/
486// ---------------------------------------------------------------------
487/* */
488bool Configuration::Exists(const char *Name) const
489{
490 const Item *Itm = Lookup(Name);
491 if (Itm == 0)
492 return false;
493 return true;
494}
495 /*}}}*/
496// Configuration::ExistsAny - Returns true if the Name, possibly /*{{{*/
497// ---------------------------------------------------------------------
498/* qualified by /[fdbi] exists */
499bool Configuration::ExistsAny(const char *Name) const
500{
501 string key = Name;
502
503 if (key.size() > 2 && key.end()[-2] == '/')
504 {
505 if (key.find_first_of("fdbi",key.size()-1) < key.size())
506 {
507 key.resize(key.size() - 2);
508 if (Exists(key.c_str()))
509 return true;
510 }
511 else
512 {
513 _error->Warning(_("Unrecognized type abbreviation: '%c'"), key.end()[-3]);
514 }
515 }
516 return Exists(Name);
517}
518 /*}}}*/
519// Configuration::Dump - Dump the config /*{{{*/
520// ---------------------------------------------------------------------
521/* Dump the entire configuration space */
522void Configuration::Dump(ostream& str)
523{
524 Dump(str, NULL, "%f \"%v\";\n", true);
525}
526void Configuration::Dump(ostream& str, char const * const root,
527 char const * const formatstr, bool const emptyValue)
528{
529 const Configuration::Item* Top = Tree(root);
530 if (Top == 0)
531 return;
532 const Configuration::Item* const Root = (root == NULL) ? NULL : Top;
533 std::vector<std::string> const format = VectorizeString(formatstr, '%');
534
535 /* Write out all of the configuration directives by walking the
536 configuration tree */
537 do {
538 if (emptyValue == true || Top->Value.empty() == emptyValue)
539 {
540 std::vector<std::string>::const_iterator f = format.begin();
541 str << *f;
542 for (++f; f != format.end(); ++f)
543 {
544 if (f->empty() == true)
545 {
546 ++f;
547 str << '%' << *f;
548 continue;
549 }
550 char const type = (*f)[0];
551 if (type == 'f')
552 str << Top->FullTag();
553 else if (type == 't')
554 str << Top->Tag;
555 else if (type == 'v')
556 str << Top->Value;
557 else if (type == 'F')
558 str << QuoteString(Top->FullTag(), "=\"\n");
559 else if (type == 'T')
560 str << QuoteString(Top->Tag, "=\"\n");
561 else if (type == 'V')
562 str << QuoteString(Top->Value, "=\"\n");
563 else if (type == 'n')
564 str << "\n";
565 else if (type == 'N')
566 str << "\t";
567 else
568 str << '%' << type;
569 str << f->c_str() + 1;
570 }
571 }
572
573 if (Top->Child != 0)
574 {
575 Top = Top->Child;
576 continue;
577 }
578
579 while (Top != 0 && Top->Next == 0)
580 Top = Top->Parent;
581 if (Top != 0)
582 Top = Top->Next;
583
584 if (Root != NULL)
585 {
586 const Configuration::Item* I = Top;
587 while(I != 0)
588 {
589 if (I == Root)
590 break;
591 else
592 I = I->Parent;
593 }
594 if (I == 0)
595 break;
596 }
597 } while (Top != 0);
598}
599 /*}}}*/
600
601// Configuration::Item::FullTag - Return the fully scoped tag /*{{{*/
602// ---------------------------------------------------------------------
603/* Stop sets an optional max recursion depth if this item is being viewed as
604 part of a sub tree. */
605string Configuration::Item::FullTag(const Item *Stop) const
606{
607 if (Parent == 0 || Parent->Parent == 0 || Parent == Stop)
608 return Tag;
609 return Parent->FullTag(Stop) + "::" + Tag;
610}
611 /*}}}*/
612
613// ReadConfigFile - Read a configuration file /*{{{*/
614// ---------------------------------------------------------------------
615/* The configuration format is very much like the named.conf format
616 used in bind8, in fact this routine can parse most named.conf files.
617 Sectional config files are like bind's named.conf where there are
618 sections like 'zone "foo.org" { .. };' This causes each section to be
619 added in with a tag like "zone::foo.org" instead of being split
620 tag/value. AsSectional enables Sectional parsing.*/
621bool ReadConfigFile(Configuration &Conf,const string &FName,bool const &AsSectional,
622 unsigned const &Depth)
623{
624 // Open the stream for reading
625 ifstream F(FName.c_str(),ios::in);
626 if (!F != 0)
627 return _error->Errno("ifstream::ifstream",_("Opening configuration file %s"),FName.c_str());
628
629 string LineBuffer;
630 string Stack[100];
631 unsigned int StackPos = 0;
632
633 // Parser state
634 string ParentTag;
635
636 int CurLine = 0;
637 bool InComment = false;
638 while (F.eof() == false)
639 {
640 // The raw input line.
641 std::string Input;
642 // The input line with comments stripped.
643 std::string Fragment;
644
645 // Grab the next line of F and place it in Input.
646 do
647 {
648 char *Buffer = new char[1024];
649
650 F.clear();
651 F.getline(Buffer,sizeof(Buffer) / 2);
652
653 Input += Buffer;
654 delete[] Buffer;
655 }
656 while (F.fail() && !F.eof());
657
658 // Expand tabs in the input line and remove leading and trailing
659 // whitespace.
660 {
661 const int BufferSize = Input.size() * 8 + 1;
662 char *Buffer = new char[BufferSize];
663 try
664 {
665 memcpy(Buffer, Input.c_str(), Input.size() + 1);
666
667 _strtabexpand(Buffer, BufferSize);
668 _strstrip(Buffer);
669 Input = Buffer;
670 }
671 catch(...)
672 {
673 delete[] Buffer;
674 throw;
675 }
676 delete[] Buffer;
677 }
678 CurLine++;
679
680 // Now strip comments; if the whole line is contained in a
681 // comment, skip this line.
682
683 // The first meaningful character in the current fragment; will
684 // be adjusted below as we remove bytes from the front.
685 std::string::const_iterator Start = Input.begin();
686 // The last meaningful character in the current fragment.
687 std::string::const_iterator End = Input.end();
688
689 // Multi line comment
690 if (InComment == true)
691 {
692 for (std::string::const_iterator I = Start;
693 I != End; ++I)
694 {
695 if (*I == '*' && I + 1 != End && I[1] == '/')
696 {
697 Start = I + 2;
698 InComment = false;
699 break;
700 }
701 }
702 if (InComment == true)
703 continue;
704 }
705
706 // Discard single line comments
707 bool InQuote = false;
708 for (std::string::const_iterator I = Start;
709 I != End; ++I)
710 {
711 if (*I == '"')
712 InQuote = !InQuote;
713 if (InQuote == true)
714 continue;
715
716 if ((*I == '/' && I + 1 != End && I[1] == '/') ||
717 (*I == '#' && strcmp(string(I,I+6).c_str(),"#clear") != 0 &&
718 strcmp(string(I,I+8).c_str(),"#include") != 0))
719 {
720 End = I;
721 break;
722 }
723 }
724
725 // Look for multi line comments and build up the
726 // fragment.
727 Fragment.reserve(End - Start);
728 InQuote = false;
729 for (std::string::const_iterator I = Start;
730 I != End; ++I)
731 {
732 if (*I == '"')
733 InQuote = !InQuote;
734 if (InQuote == true)
735 Fragment.push_back(*I);
736 else if (*I == '/' && I + 1 != End && I[1] == '*')
737 {
738 InComment = true;
739 for (std::string::const_iterator J = I;
740 J != End; ++J)
741 {
742 if (*J == '*' && J + 1 != End && J[1] == '/')
743 {
744 // Pretend we just finished walking over the
745 // comment, and don't add anything to the output
746 // fragment.
747 I = J + 1;
748 InComment = false;
749 break;
750 }
751 }
752
753 if (InComment == true)
754 break;
755 }
756 else
757 Fragment.push_back(*I);
758 }
759
760 // Skip blank lines.
761 if (Fragment.empty())
762 continue;
763
764 // The line has actual content; interpret what it means.
765 InQuote = false;
766 Start = Fragment.begin();
767 End = Fragment.end();
768 for (std::string::const_iterator I = Start;
769 I != End; ++I)
770 {
771 if (*I == '"')
772 InQuote = !InQuote;
773
774 if (InQuote == false && (*I == '{' || *I == ';' || *I == '}'))
775 {
776 // Put the last fragment into the buffer
777 std::string::const_iterator NonWhitespaceStart = Start;
778 std::string::const_iterator NonWhitespaceStop = I;
779 for (; NonWhitespaceStart != I && isspace(*NonWhitespaceStart) != 0; ++NonWhitespaceStart)
780 ;
781 for (; NonWhitespaceStop != NonWhitespaceStart && isspace(NonWhitespaceStop[-1]) != 0; --NonWhitespaceStop)
782 ;
783 if (LineBuffer.empty() == false && NonWhitespaceStop - NonWhitespaceStart != 0)
784 LineBuffer += ' ';
785 LineBuffer += string(NonWhitespaceStart, NonWhitespaceStop);
786
787 // Drop this from the input string, saving the character
788 // that terminated the construct we just closed. (i.e., a
789 // brace or a semicolon)
790 char TermChar = *I;
791 Start = I + 1;
792
793 // Syntax Error
794 if (TermChar == '{' && LineBuffer.empty() == true)
795 return _error->Error(_("Syntax error %s:%u: Block starts with no name."),FName.c_str(),CurLine);
796
797 // No string on this line
798 if (LineBuffer.empty() == true)
799 {
800 if (TermChar == '}')
801 {
802 if (StackPos == 0)
803 ParentTag = string();
804 else
805 ParentTag = Stack[--StackPos];
806 }
807 continue;
808 }
809
810 // Parse off the tag
811 string Tag;
812 const char *Pos = LineBuffer.c_str();
813 if (ParseQuoteWord(Pos,Tag) == false)
814 return _error->Error(_("Syntax error %s:%u: Malformed tag"),FName.c_str(),CurLine);
815
816 // Parse off the word
817 string Word;
818 bool NoWord = false;
819 if (ParseCWord(Pos,Word) == false &&
820 ParseQuoteWord(Pos,Word) == false)
821 {
822 if (TermChar != '{')
823 {
824 Word = Tag;
825 Tag = "";
826 }
827 else
828 NoWord = true;
829 }
830 if (strlen(Pos) != 0)
831 return _error->Error(_("Syntax error %s:%u: Extra junk after value"),FName.c_str(),CurLine);
832
833 // Go down a level
834 if (TermChar == '{')
835 {
836 if (StackPos < sizeof(Stack)/sizeof(std::string))
837 Stack[StackPos++] = ParentTag;
838
839 /* Make sectional tags incorperate the section into the
840 tag string */
841 if (AsSectional == true && Word.empty() == false)
842 {
843 Tag += "::" ;
844 Tag += Word;
845 Word = "";
846 }
847
848 if (ParentTag.empty() == true)
849 ParentTag = Tag;
850 else
851 ParentTag += string("::") + Tag;
852 Tag = string();
853 }
854
855 // Generate the item name
856 string Item;
857 if (ParentTag.empty() == true)
858 Item = Tag;
859 else
860 {
861 if (TermChar != '{' || Tag.empty() == false)
862 Item = ParentTag + "::" + Tag;
863 else
864 Item = ParentTag;
865 }
866
867 // Specials
868 if (Tag.length() >= 1 && Tag[0] == '#')
869 {
870 if (ParentTag.empty() == false)
871 return _error->Error(_("Syntax error %s:%u: Directives can only be done at the top level"),FName.c_str(),CurLine);
872 Tag.erase(Tag.begin());
873 if (Tag == "clear")
874 Conf.Clear(Word);
875 else if (Tag == "include")
876 {
877 if (Depth > 10)
878 return _error->Error(_("Syntax error %s:%u: Too many nested includes"),FName.c_str(),CurLine);
879 if (Word.length() > 2 && Word.end()[-1] == '/')
880 {
881 if (ReadConfigDir(Conf,Word,AsSectional,Depth+1) == false)
882 return _error->Error(_("Syntax error %s:%u: Included from here"),FName.c_str(),CurLine);
883 }
884 else
885 {
886 if (ReadConfigFile(Conf,Word,AsSectional,Depth+1) == false)
887 return _error->Error(_("Syntax error %s:%u: Included from here"),FName.c_str(),CurLine);
888 }
889 }
890 else
891 return _error->Error(_("Syntax error %s:%u: Unsupported directive '%s'"),FName.c_str(),CurLine,Tag.c_str());
892 }
893 else if (Tag.empty() == true && NoWord == false && Word == "#clear")
894 return _error->Error(_("Syntax error %s:%u: clear directive requires an option tree as argument"),FName.c_str(),CurLine);
895 else
896 {
897 // Set the item in the configuration class
898 if (NoWord == false)
899 Conf.Set(Item,Word);
900 }
901
902 // Empty the buffer
903 LineBuffer.clear();
904
905 // Move up a tag, but only if there is no bit to parse
906 if (TermChar == '}')
907 {
908 if (StackPos == 0)
909 ParentTag.clear();
910 else
911 ParentTag = Stack[--StackPos];
912 }
913
914 }
915 }
916
917 // Store the remaining text, if any, in the current line buffer.
918
919 // NB: could change this to use string-based operations; I'm
920 // using strstrip now to ensure backwards compatibility.
921 // -- dburrows 2008-04-01
922 {
923 char *Buffer = new char[End - Start + 1];
924 try
925 {
926 std::copy(Start, End, Buffer);
927 Buffer[End - Start] = '\0';
928
929 const char *Stripd = _strstrip(Buffer);
930 if (*Stripd != 0 && LineBuffer.empty() == false)
931 LineBuffer += " ";
932 LineBuffer += Stripd;
933 }
934 catch(...)
935 {
936 delete[] Buffer;
937 throw;
938 }
939 delete[] Buffer;
940 }
941 }
942
943 if (LineBuffer.empty() == false)
944 return _error->Error(_("Syntax error %s:%u: Extra junk at end of file"),FName.c_str(),CurLine);
945 return true;
946}
947 /*}}}*/
948// ReadConfigDir - Read a directory of config files /*{{{*/
949// ---------------------------------------------------------------------
950/* */
951bool ReadConfigDir(Configuration &Conf,const string &Dir,
952 bool const &AsSectional, unsigned const &Depth)
953{
954 vector<string> const List = GetListOfFilesInDir(Dir, "conf", true, true);
955
956 // Read the files
957 for (vector<string>::const_iterator I = List.begin(); I != List.end(); ++I)
958 if (ReadConfigFile(Conf,*I,AsSectional,Depth) == false)
959 return false;
960 return true;
961}
962 /*}}}*/
963// MatchAgainstConfig Constructor /*{{{*/
964Configuration::MatchAgainstConfig::MatchAgainstConfig(char const * Config)
965{
966 std::vector<std::string> const strings = _config->FindVector(Config);
967 for (std::vector<std::string>::const_iterator s = strings.begin();
968 s != strings.end(); ++s)
969 {
970 regex_t *p = new regex_t;
971 if (regcomp(p, s->c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB) == 0)
972 patterns.push_back(p);
973 else
974 {
975 regfree(p);
976 delete p;
977 _error->Warning("Invalid regular expression '%s' in configuration "
978 "option '%s' will be ignored.",
979 s->c_str(), Config);
980 continue;
981 }
982 }
983 if (strings.empty() == true)
984 patterns.push_back(NULL);
985}
986 /*}}}*/
987// MatchAgainstConfig Destructor /*{{{*/
988Configuration::MatchAgainstConfig::~MatchAgainstConfig()
989{
990 clearPatterns();
991}
992void Configuration::MatchAgainstConfig::clearPatterns()
993{
994 for(std::vector<regex_t *>::const_iterator p = patterns.begin();
995 p != patterns.end(); ++p)
996 {
997 if (*p == NULL) continue;
998 regfree(*p);
999 delete *p;
1000 }
1001 patterns.clear();
1002}
1003 /*}}}*/
1004// MatchAgainstConfig::Match - returns true if a pattern matches /*{{{*/
1005bool Configuration::MatchAgainstConfig::Match(char const * str) const
1006{
1007 for(std::vector<regex_t *>::const_iterator p = patterns.begin();
1008 p != patterns.end(); ++p)
1009 if (*p != NULL && regexec(*p, str, 0, 0, 0) == 0)
1010 return true;
1011
1012 return false;
1013}
1014 /*}}}*/