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