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