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