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