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