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