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