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