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