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