]> git.saurik.com Git - apt.git/blob - apt-pkg/contrib/configuration.cc
rename CleaAll() -> Clear()
[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::Clear - Clear everything /*{{{*/
409 // ---------------------------------------------------------------------
410 void Configuration::Clear()
411 {
412 const Configuration::Item *Top = Tree(0);
413 while( Top != 0 )
414 {
415 Clear(Top->FullTag());
416 Top = Top->Next;
417 }
418 }
419 /*}}}*/
420 // Configuration::Clear - Clear an entire tree /*{{{*/
421 // ---------------------------------------------------------------------
422 /* */
423 void Configuration::Clear(string const &Name)
424 {
425 Item *Top = Lookup(Name.c_str(),false);
426 if (Top == 0)
427 return;
428
429 Top->Value.clear();
430 Item *Stop = Top;
431 Top = Top->Child;
432 Stop->Child = 0;
433 for (; Top != 0;)
434 {
435 if (Top->Child != 0)
436 {
437 Top = Top->Child;
438 continue;
439 }
440
441 while (Top != 0 && Top->Next == 0)
442 {
443 Item *Tmp = Top;
444 Top = Top->Parent;
445 delete Tmp;
446
447 if (Top == Stop)
448 return;
449 }
450
451 Item *Tmp = Top;
452 if (Top != 0)
453 Top = Top->Next;
454 delete Tmp;
455 }
456 }
457 /*}}}*/
458 // Configuration::Exists - Returns true if the Name exists /*{{{*/
459 // ---------------------------------------------------------------------
460 /* */
461 bool Configuration::Exists(const char *Name) const
462 {
463 const Item *Itm = Lookup(Name);
464 if (Itm == 0)
465 return false;
466 return true;
467 }
468 /*}}}*/
469 // Configuration::ExistsAny - Returns true if the Name, possibly /*{{{*/
470 // ---------------------------------------------------------------------
471 /* qualified by /[fdbi] exists */
472 bool Configuration::ExistsAny(const char *Name) const
473 {
474 string key = Name;
475
476 if (key.size() > 2 && key.end()[-2] == '/')
477 {
478 if (key.find_first_of("fdbi",key.size()-1) < key.size())
479 {
480 key.resize(key.size() - 2);
481 if (Exists(key.c_str()))
482 return true;
483 }
484 else
485 {
486 _error->Warning(_("Unrecognized type abbreviation: '%c'"), key.end()[-3]);
487 }
488 }
489 return Exists(Name);
490 }
491 /*}}}*/
492 // Configuration::Dump - Dump the config /*{{{*/
493 // ---------------------------------------------------------------------
494 /* Dump the entire configuration space */
495 void Configuration::Dump(ostream& str)
496 {
497 /* Write out all of the configuration directives by walking the
498 configuration tree */
499 const Configuration::Item *Top = Tree(0);
500 for (; Top != 0;)
501 {
502 str << Top->FullTag() << " \"" << Top->Value << "\";" << endl;
503
504 if (Top->Child != 0)
505 {
506 Top = Top->Child;
507 continue;
508 }
509
510 while (Top != 0 && Top->Next == 0)
511 Top = Top->Parent;
512 if (Top != 0)
513 Top = Top->Next;
514 }
515 }
516 /*}}}*/
517
518 // Configuration::Item::FullTag - Return the fully scoped tag /*{{{*/
519 // ---------------------------------------------------------------------
520 /* Stop sets an optional max recursion depth if this item is being viewed as
521 part of a sub tree. */
522 string Configuration::Item::FullTag(const Item *Stop) const
523 {
524 if (Parent == 0 || Parent->Parent == 0 || Parent == Stop)
525 return Tag;
526 return Parent->FullTag(Stop) + "::" + Tag;
527 }
528 /*}}}*/
529
530 // ReadConfigFile - Read a configuration file /*{{{*/
531 // ---------------------------------------------------------------------
532 /* The configuration format is very much like the named.conf format
533 used in bind8, in fact this routine can parse most named.conf files.
534 Sectional config files are like bind's named.conf where there are
535 sections like 'zone "foo.org" { .. };' This causes each section to be
536 added in with a tag like "zone::foo.org" instead of being split
537 tag/value. AsSectional enables Sectional parsing.*/
538 bool ReadConfigFile(Configuration &Conf,const string &FName,bool const &AsSectional,
539 unsigned const &Depth)
540 {
541 // Open the stream for reading
542 ifstream F(FName.c_str(),ios::in);
543 if (!F != 0)
544 return _error->Errno("ifstream::ifstream",_("Opening configuration file %s"),FName.c_str());
545
546 string LineBuffer;
547 string Stack[100];
548 unsigned int StackPos = 0;
549
550 // Parser state
551 string ParentTag;
552
553 int CurLine = 0;
554 bool InComment = false;
555 while (F.eof() == false)
556 {
557 // The raw input line.
558 std::string Input;
559 // The input line with comments stripped.
560 std::string Fragment;
561
562 // Grab the next line of F and place it in Input.
563 do
564 {
565 char *Buffer = new char[1024];
566
567 F.clear();
568 F.getline(Buffer,sizeof(Buffer) / 2);
569
570 Input += Buffer;
571 delete[] Buffer;
572 }
573 while (F.fail() && !F.eof());
574
575 // Expand tabs in the input line and remove leading and trailing
576 // whitespace.
577 {
578 const int BufferSize = Input.size() * 8 + 1;
579 char *Buffer = new char[BufferSize];
580 try
581 {
582 memcpy(Buffer, Input.c_str(), Input.size() + 1);
583
584 _strtabexpand(Buffer, BufferSize);
585 _strstrip(Buffer);
586 Input = Buffer;
587 }
588 catch(...)
589 {
590 delete[] Buffer;
591 throw;
592 }
593 delete[] Buffer;
594 }
595 CurLine++;
596
597 // Now strip comments; if the whole line is contained in a
598 // comment, skip this line.
599
600 // The first meaningful character in the current fragment; will
601 // be adjusted below as we remove bytes from the front.
602 std::string::const_iterator Start = Input.begin();
603 // The last meaningful character in the current fragment.
604 std::string::const_iterator End = Input.end();
605
606 // Multi line comment
607 if (InComment == true)
608 {
609 for (std::string::const_iterator I = Start;
610 I != End; ++I)
611 {
612 if (*I == '*' && I + 1 != End && I[1] == '/')
613 {
614 Start = I + 2;
615 InComment = false;
616 break;
617 }
618 }
619 if (InComment == true)
620 continue;
621 }
622
623 // Discard single line comments
624 bool InQuote = false;
625 for (std::string::const_iterator I = Start;
626 I != End; ++I)
627 {
628 if (*I == '"')
629 InQuote = !InQuote;
630 if (InQuote == true)
631 continue;
632
633 if ((*I == '/' && I + 1 != End && I[1] == '/') ||
634 (*I == '#' && strcmp(string(I,I+6).c_str(),"#clear") != 0 &&
635 strcmp(string(I,I+8).c_str(),"#include") != 0))
636 {
637 End = I;
638 break;
639 }
640 }
641
642 // Look for multi line comments and build up the
643 // fragment.
644 Fragment.reserve(End - Start);
645 InQuote = false;
646 for (std::string::const_iterator I = Start;
647 I != End; ++I)
648 {
649 if (*I == '"')
650 InQuote = !InQuote;
651 if (InQuote == true)
652 Fragment.push_back(*I);
653 else if (*I == '/' && I + 1 != End && I[1] == '*')
654 {
655 InComment = true;
656 for (std::string::const_iterator J = I;
657 J != End; ++J)
658 {
659 if (*J == '*' && J + 1 != End && J[1] == '/')
660 {
661 // Pretend we just finished walking over the
662 // comment, and don't add anything to the output
663 // fragment.
664 I = J + 1;
665 InComment = false;
666 break;
667 }
668 }
669
670 if (InComment == true)
671 break;
672 }
673 else
674 Fragment.push_back(*I);
675 }
676
677 // Skip blank lines.
678 if (Fragment.empty())
679 continue;
680
681 // The line has actual content; interpret what it means.
682 InQuote = false;
683 Start = Fragment.begin();
684 End = Fragment.end();
685 for (std::string::const_iterator I = Start;
686 I != End; ++I)
687 {
688 if (*I == '"')
689 InQuote = !InQuote;
690
691 if (InQuote == false && (*I == '{' || *I == ';' || *I == '}'))
692 {
693 // Put the last fragment into the buffer
694 std::string::const_iterator NonWhitespaceStart = Start;
695 std::string::const_iterator NonWhitespaceStop = I;
696 for (; NonWhitespaceStart != I && isspace(*NonWhitespaceStart) != 0; ++NonWhitespaceStart)
697 ;
698 for (; NonWhitespaceStop != NonWhitespaceStart && isspace(NonWhitespaceStop[-1]) != 0; --NonWhitespaceStop)
699 ;
700 if (LineBuffer.empty() == false && NonWhitespaceStop - NonWhitespaceStart != 0)
701 LineBuffer += ' ';
702 LineBuffer += string(NonWhitespaceStart, NonWhitespaceStop);
703
704 // Drop this from the input string, saving the character
705 // that terminated the construct we just closed. (i.e., a
706 // brace or a semicolon)
707 char TermChar = *I;
708 Start = I + 1;
709
710 // Syntax Error
711 if (TermChar == '{' && LineBuffer.empty() == true)
712 return _error->Error(_("Syntax error %s:%u: Block starts with no name."),FName.c_str(),CurLine);
713
714 // No string on this line
715 if (LineBuffer.empty() == true)
716 {
717 if (TermChar == '}')
718 {
719 if (StackPos == 0)
720 ParentTag = string();
721 else
722 ParentTag = Stack[--StackPos];
723 }
724 continue;
725 }
726
727 // Parse off the tag
728 string Tag;
729 const char *Pos = LineBuffer.c_str();
730 if (ParseQuoteWord(Pos,Tag) == false)
731 return _error->Error(_("Syntax error %s:%u: Malformed tag"),FName.c_str(),CurLine);
732
733 // Parse off the word
734 string Word;
735 bool NoWord = false;
736 if (ParseCWord(Pos,Word) == false &&
737 ParseQuoteWord(Pos,Word) == false)
738 {
739 if (TermChar != '{')
740 {
741 Word = Tag;
742 Tag = "";
743 }
744 else
745 NoWord = true;
746 }
747 if (strlen(Pos) != 0)
748 return _error->Error(_("Syntax error %s:%u: Extra junk after value"),FName.c_str(),CurLine);
749
750 // Go down a level
751 if (TermChar == '{')
752 {
753 if (StackPos <= 100)
754 Stack[StackPos++] = ParentTag;
755
756 /* Make sectional tags incorperate the section into the
757 tag string */
758 if (AsSectional == true && Word.empty() == false)
759 {
760 Tag += "::" ;
761 Tag += Word;
762 Word = "";
763 }
764
765 if (ParentTag.empty() == true)
766 ParentTag = Tag;
767 else
768 ParentTag += string("::") + Tag;
769 Tag = string();
770 }
771
772 // Generate the item name
773 string Item;
774 if (ParentTag.empty() == true)
775 Item = Tag;
776 else
777 {
778 if (TermChar != '{' || Tag.empty() == false)
779 Item = ParentTag + "::" + Tag;
780 else
781 Item = ParentTag;
782 }
783
784 // Specials
785 if (Tag.length() >= 1 && Tag[0] == '#')
786 {
787 if (ParentTag.empty() == false)
788 return _error->Error(_("Syntax error %s:%u: Directives can only be done at the top level"),FName.c_str(),CurLine);
789 Tag.erase(Tag.begin());
790 if (Tag == "clear")
791 Conf.Clear(Word);
792 else if (Tag == "include")
793 {
794 if (Depth > 10)
795 return _error->Error(_("Syntax error %s:%u: Too many nested includes"),FName.c_str(),CurLine);
796 if (Word.length() > 2 && Word.end()[-1] == '/')
797 {
798 if (ReadConfigDir(Conf,Word,AsSectional,Depth+1) == false)
799 return _error->Error(_("Syntax error %s:%u: Included from here"),FName.c_str(),CurLine);
800 }
801 else
802 {
803 if (ReadConfigFile(Conf,Word,AsSectional,Depth+1) == false)
804 return _error->Error(_("Syntax error %s:%u: Included from here"),FName.c_str(),CurLine);
805 }
806 }
807 else
808 return _error->Error(_("Syntax error %s:%u: Unsupported directive '%s'"),FName.c_str(),CurLine,Tag.c_str());
809 }
810 else if (Tag.empty() == true && NoWord == false && Word == "#clear")
811 return _error->Error(_("Syntax error %s:%u: clear directive requires an option tree as argument"),FName.c_str(),CurLine);
812 else
813 {
814 // Set the item in the configuration class
815 if (NoWord == false)
816 Conf.Set(Item,Word);
817 }
818
819 // Empty the buffer
820 LineBuffer.clear();
821
822 // Move up a tag, but only if there is no bit to parse
823 if (TermChar == '}')
824 {
825 if (StackPos == 0)
826 ParentTag.clear();
827 else
828 ParentTag = Stack[--StackPos];
829 }
830
831 }
832 }
833
834 // Store the remaining text, if any, in the current line buffer.
835
836 // NB: could change this to use string-based operations; I'm
837 // using strstrip now to ensure backwards compatibility.
838 // -- dburrows 2008-04-01
839 {
840 char *Buffer = new char[End - Start + 1];
841 try
842 {
843 std::copy(Start, End, Buffer);
844 Buffer[End - Start] = '\0';
845
846 const char *Stripd = _strstrip(Buffer);
847 if (*Stripd != 0 && LineBuffer.empty() == false)
848 LineBuffer += " ";
849 LineBuffer += Stripd;
850 }
851 catch(...)
852 {
853 delete[] Buffer;
854 throw;
855 }
856 delete[] Buffer;
857 }
858 }
859
860 if (LineBuffer.empty() == false)
861 return _error->Error(_("Syntax error %s:%u: Extra junk at end of file"),FName.c_str(),CurLine);
862 return true;
863 }
864 /*}}}*/
865 // ReadConfigDir - Read a directory of config files /*{{{*/
866 // ---------------------------------------------------------------------
867 /* */
868 bool ReadConfigDir(Configuration &Conf,const string &Dir,
869 bool const &AsSectional, unsigned const &Depth)
870 {
871 vector<string> const List = GetListOfFilesInDir(Dir, "conf", true, true);
872
873 // Read the files
874 for (vector<string>::const_iterator I = List.begin(); I != List.end(); ++I)
875 if (ReadConfigFile(Conf,*I,AsSectional,Depth) == false)
876 return false;
877 return true;
878 }
879 /*}}}*/
880 // MatchAgainstConfig Constructor /*{{{*/
881 Configuration::MatchAgainstConfig::MatchAgainstConfig(char const * Config)
882 {
883 std::vector<std::string> const strings = _config->FindVector(Config);
884 for (std::vector<std::string>::const_iterator s = strings.begin();
885 s != strings.end(); ++s)
886 {
887 regex_t *p = new regex_t;
888 if (regcomp(p, s->c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB) == 0)
889 patterns.push_back(p);
890 else
891 {
892 regfree(p);
893 delete p;
894 _error->Warning("Invalid regular expression '%s' in configuration "
895 "option '%s' will be ignored.",
896 s->c_str(), Config);
897 continue;
898 }
899 }
900 if (strings.size() == 0)
901 patterns.push_back(NULL);
902 }
903 /*}}}*/
904 // MatchAgainstConfig Destructor /*{{{*/
905 Configuration::MatchAgainstConfig::~MatchAgainstConfig()
906 {
907 clearPatterns();
908 }
909 void Configuration::MatchAgainstConfig::clearPatterns()
910 {
911 for(std::vector<regex_t *>::const_iterator p = patterns.begin();
912 p != patterns.end(); ++p)
913 {
914 if (*p == NULL) continue;
915 regfree(*p);
916 delete *p;
917 }
918 patterns.clear();
919 }
920 /*}}}*/
921 // MatchAgainstConfig::Match - returns true if a pattern matches /*{{{*/
922 bool Configuration::MatchAgainstConfig::Match(char const * str) const
923 {
924 for(std::vector<regex_t *>::const_iterator p = patterns.begin();
925 p != patterns.end(); ++p)
926 if (*p != NULL && regexec(*p, str, 0, 0, 0) == 0)
927 return true;
928
929 return false;
930 }
931 /*}}}*/