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