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