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