]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/configuration.cc
[ Martin Pitt ]
[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 /*}}}*/
6c139d6e
AL
321// Configuration::Set - Set a value /*{{{*/
322// ---------------------------------------------------------------------
323/* */
171c75f1 324void Configuration::Set(const char *Name,const string &Value)
6c139d6e
AL
325{
326 Item *Itm = Lookup(Name,true);
327 if (Itm == 0)
328 return;
329 Itm->Value = Value;
330}
331 /*}}}*/
332// Configuration::Set - Set an integer value /*{{{*/
333// ---------------------------------------------------------------------
334/* */
ce857f32 335void Configuration::Set(const char *Name,int const &Value)
6c139d6e
AL
336{
337 Item *Itm = Lookup(Name,true);
338 if (Itm == 0)
339 return;
340 char S[300];
341 snprintf(S,sizeof(S),"%i",Value);
342 Itm->Value = S;
75ef8f14
MV
343}
344 /*}}}*/
345// Configuration::Clear - Clear an single value from a list /*{{{*/
346// ---------------------------------------------------------------------
347/* */
ce857f32 348void Configuration::Clear(string const &Name, int const &Value)
75ef8f14
MV
349{
350 char S[300];
351 snprintf(S,sizeof(S),"%i",Value);
352 Clear(Name, S);
353}
354 /*}}}*/
355// Configuration::Clear - Clear an single value from a list /*{{{*/
356// ---------------------------------------------------------------------
357/* */
ce857f32 358void Configuration::Clear(string const &Name, string const &Value)
75ef8f14
MV
359{
360 Item *Top = Lookup(Name.c_str(),false);
361 if (Top == 0 || Top->Child == 0)
362 return;
363
364 Item *Tmp, *Prev, *I;
365 Prev = I = Top->Child;
366
367 while(I != NULL)
368 {
369 if(I->Value == Value)
370 {
371 Tmp = I;
372 // was first element, point parent to new first element
373 if(Top->Child == Tmp)
374 Top->Child = I->Next;
375 I = I->Next;
376 Prev->Next = I;
377 delete Tmp;
378 } else {
379 Prev = I;
380 I = I->Next;
381 }
382 }
383
6c139d6e
AL
384}
385 /*}}}*/
b2e465d6
AL
386// Configuration::Clear - Clear an entire tree /*{{{*/
387// ---------------------------------------------------------------------
388/* */
ce857f32 389void Configuration::Clear(string const &Name)
b2e465d6
AL
390{
391 Item *Top = Lookup(Name.c_str(),false);
75ef8f14 392 if (Top == 0)
b2e465d6 393 return;
75ef8f14 394
171c75f1 395 Top->Value.clear();
b2e465d6
AL
396 Item *Stop = Top;
397 Top = Top->Child;
398 Stop->Child = 0;
399 for (; Top != 0;)
400 {
401 if (Top->Child != 0)
402 {
403 Top = Top->Child;
404 continue;
405 }
406
407 while (Top != 0 && Top->Next == 0)
408 {
409 Item *Tmp = Top;
410 Top = Top->Parent;
411 delete Tmp;
412
413 if (Top == Stop)
414 return;
415 }
416
417 Item *Tmp = Top;
418 if (Top != 0)
419 Top = Top->Next;
420 delete Tmp;
421 }
422}
423 /*}}}*/
08e8f724
AL
424// Configuration::Exists - Returns true if the Name exists /*{{{*/
425// ---------------------------------------------------------------------
426/* */
b2e465d6 427bool Configuration::Exists(const char *Name) const
08e8f724 428{
b2e465d6 429 const Item *Itm = Lookup(Name);
08e8f724
AL
430 if (Itm == 0)
431 return false;
432 return true;
433}
434 /*}}}*/
b2e465d6
AL
435// Configuration::ExistsAny - Returns true if the Name, possibly /*{{{*/
436// ---------------------------------------------------------------------
437/* qualified by /[fdbi] exists */
438bool Configuration::ExistsAny(const char *Name) const
439{
440 string key = Name;
441
8d998026 442 if (key.size() > 2 && key.end()[-2] == '/')
ff6bf1be 443 {
8d998026
AL
444 if (key.find_first_of("fdbi",key.size()-1) < key.size())
445 {
446 key.resize(key.size() - 2);
447 if (Exists(key.c_str()))
448 return true;
449 }
450 else
451 {
69a63027 452 _error->Warning(_("Unrecognized type abbreviation: '%c'"), key.end()[-3]);
8d998026 453 }
ff6bf1be 454 }
b2e465d6
AL
455 return Exists(Name);
456}
457 /*}}}*/
93bf083d
AL
458// Configuration::Dump - Dump the config /*{{{*/
459// ---------------------------------------------------------------------
460/* Dump the entire configuration space */
ff2a211a 461void Configuration::Dump(ostream& str)
93bf083d
AL
462{
463 /* Write out all of the configuration directives by walking the
464 configuration tree */
b2e465d6 465 const Configuration::Item *Top = Tree(0);
93bf083d
AL
466 for (; Top != 0;)
467 {
ff2a211a 468 str << Top->FullTag() << " \"" << Top->Value << "\";" << endl;
93bf083d
AL
469
470 if (Top->Child != 0)
471 {
472 Top = Top->Child;
473 continue;
474 }
475
476 while (Top != 0 && Top->Next == 0)
477 Top = Top->Parent;
478 if (Top != 0)
479 Top = Top->Next;
b2e465d6 480 }
93bf083d
AL
481}
482 /*}}}*/
08e8f724 483
0a8a80e5
AL
484// Configuration::Item::FullTag - Return the fully scoped tag /*{{{*/
485// ---------------------------------------------------------------------
b2e465d6
AL
486/* Stop sets an optional max recursion depth if this item is being viewed as
487 part of a sub tree. */
488string Configuration::Item::FullTag(const Item *Stop) const
0a8a80e5 489{
b2e465d6 490 if (Parent == 0 || Parent->Parent == 0 || Parent == Stop)
0a8a80e5 491 return Tag;
b2e465d6 492 return Parent->FullTag(Stop) + "::" + Tag;
0a8a80e5
AL
493}
494 /*}}}*/
495
08e8f724
AL
496// ReadConfigFile - Read a configuration file /*{{{*/
497// ---------------------------------------------------------------------
498/* The configuration format is very much like the named.conf format
b2e465d6
AL
499 used in bind8, in fact this routine can parse most named.conf files.
500 Sectional config files are like bind's named.conf where there are
501 sections like 'zone "foo.org" { .. };' This causes each section to be
502 added in with a tag like "zone::foo.org" instead of being split
6df63aa6 503 tag/value. AsSectional enables Sectional parsing.*/
ce857f32
DK
504bool ReadConfigFile(Configuration &Conf,const string &FName,bool const &AsSectional,
505 unsigned const &Depth)
b2e465d6 506{
08e8f724 507 // Open the stream for reading
851a45a8 508 ifstream F(FName.c_str(),ios::in);
08e8f724 509 if (!F != 0)
b2e465d6 510 return _error->Errno("ifstream::ifstream",_("Opening configuration file %s"),FName.c_str());
3f807f6c 511
08e8f724 512 string LineBuffer;
a4e87467
AL
513 string Stack[100];
514 unsigned int StackPos = 0;
08e8f724
AL
515
516 // Parser state
517 string ParentTag;
518
519 int CurLine = 0;
520 bool InComment = false;
521 while (F.eof() == false)
522 {
3f807f6c
DB
523 // The raw input line.
524 std::string Input;
525 // The input line with comments stripped.
526 std::string Fragment;
8a27f5da
DB
527
528 // Grab the next line of F and place it in Input.
3f807f6c
DB
529 do
530 {
531 char *Buffer = new char[1024];
532
533 F.clear();
534 F.getline(Buffer,sizeof(Buffer) / 2);
535
536 Input += Buffer;
8aea8c3f 537 delete[] Buffer;
3f807f6c
DB
538 }
539 while (F.fail() && !F.eof());
540
8a27f5da
DB
541 // Expand tabs in the input line and remove leading and trailing
542 // whitespace.
3f807f6c 543 {
3f807f6c
DB
544 const int BufferSize = Input.size() * 8 + 1;
545 char *Buffer = new char[BufferSize];
546 try
547 {
548 memcpy(Buffer, Input.c_str(), Input.size() + 1);
549
550 _strtabexpand(Buffer, BufferSize);
551 _strstrip(Buffer);
552 Input = Buffer;
553 }
554 catch(...)
555 {
556 delete[] Buffer;
557 throw;
558 }
559 delete[] Buffer;
560 }
08e8f724 561 CurLine++;
5e14c452 562
8a27f5da
DB
563 // Now strip comments; if the whole line is contained in a
564 // comment, skip this line.
565
3f807f6c
DB
566 // The first meaningful character in the current fragment; will
567 // be adjusted below as we remove bytes from the front.
568 std::string::const_iterator Start = Input.begin();
569 // The last meaningful character in the current fragment.
570 std::string::const_iterator End = Input.end();
08e8f724
AL
571
572 // Multi line comment
573 if (InComment == true)
574 {
3f807f6c
DB
575 for (std::string::const_iterator I = Start;
576 I != End; ++I)
08e8f724 577 {
3f807f6c 578 if (*I == '*' && I + 1 != End && I[1] == '/')
08e8f724 579 {
3f807f6c 580 Start = I + 2;
08e8f724
AL
581 InComment = false;
582 break;
583 }
584 }
585 if (InComment == true)
586 continue;
587 }
588
589 // Discard single line comments
bfd22fc0 590 bool InQuote = false;
3f807f6c
DB
591 for (std::string::const_iterator I = Start;
592 I != End; ++I)
08e8f724 593 {
bfd22fc0
AL
594 if (*I == '"')
595 InQuote = !InQuote;
596 if (InQuote == true)
597 continue;
c914647f
DK
598
599 if ((*I == '/' && I + 1 != End && I[1] == '/') ||
600 (*I == '#' && strcmp(string(I,I+6).c_str(),"#clear") != 0 &&
601 strcmp(string(I,I+8).c_str(),"#include") != 0))
602 {
3f807f6c 603 End = I;
08e8f724
AL
604 break;
605 }
606 }
7834cb57 607
3f807f6c
DB
608 // Look for multi line comments and build up the
609 // fragment.
610 Fragment.reserve(End - Start);
7834cb57 611 InQuote = false;
3f807f6c
DB
612 for (std::string::const_iterator I = Start;
613 I != End; ++I)
08e8f724 614 {
bfd22fc0
AL
615 if (*I == '"')
616 InQuote = !InQuote;
617 if (InQuote == true)
3f807f6c
DB
618 Fragment.push_back(*I);
619 else if (*I == '/' && I + 1 != End && I[1] == '*')
08e8f724
AL
620 {
621 InComment = true;
3f807f6c
DB
622 for (std::string::const_iterator J = I;
623 J != End; ++J)
08e8f724 624 {
3f807f6c 625 if (*J == '*' && J + 1 != End && J[1] == '/')
08e8f724 626 {
3f807f6c
DB
627 // Pretend we just finished walking over the
628 // comment, and don't add anything to the output
629 // fragment.
630 I = J + 1;
08e8f724
AL
631 InComment = false;
632 break;
633 }
634 }
635
636 if (InComment == true)
3f807f6c 637 break;
08e8f724 638 }
3f807f6c
DB
639 else
640 Fragment.push_back(*I);
08e8f724 641 }
3f807f6c
DB
642
643 // Skip blank lines.
644 if (Fragment.empty())
08e8f724
AL
645 continue;
646
8a27f5da 647 // The line has actual content; interpret what it means.
7834cb57 648 InQuote = false;
3f807f6c
DB
649 Start = Fragment.begin();
650 End = Fragment.end();
651 for (std::string::const_iterator I = Start;
652 I != End; ++I)
08e8f724 653 {
7834cb57
AL
654 if (*I == '"')
655 InQuote = !InQuote;
656
657 if (InQuote == false && (*I == '{' || *I == ';' || *I == '}'))
08e8f724 658 {
b2e465d6 659 // Put the last fragment into the buffer
a4dcbf1c
DB
660 std::string::const_iterator NonWhitespaceStart = Start;
661 std::string::const_iterator NonWhitespaceStop = I;
662 for (; NonWhitespaceStart != I && isspace(*NonWhitespaceStart) != 0; NonWhitespaceStart++)
3f807f6c 663 ;
a4dcbf1c 664 for (; NonWhitespaceStop != NonWhitespaceStart && isspace(NonWhitespaceStop[-1]) != 0; NonWhitespaceStop--)
3f807f6c 665 ;
a4dcbf1c 666 if (LineBuffer.empty() == false && NonWhitespaceStop - NonWhitespaceStart != 0)
08e8f724 667 LineBuffer += ' ';
a4dcbf1c 668 LineBuffer += string(NonWhitespaceStart, NonWhitespaceStop);
3f807f6c
DB
669
670 // Drop this from the input string, saving the character
671 // that terminated the construct we just closed. (i.e., a
672 // brace or a semicolon)
08e8f724 673 char TermChar = *I;
3f807f6c 674 Start = I + 1;
08e8f724
AL
675
676 // Syntax Error
677 if (TermChar == '{' && LineBuffer.empty() == true)
b2e465d6 678 return _error->Error(_("Syntax error %s:%u: Block starts with no name."),FName.c_str(),CurLine);
08e8f724 679
b2e465d6 680 // No string on this line
08e8f724 681 if (LineBuffer.empty() == true)
b2e465d6
AL
682 {
683 if (TermChar == '}')
684 {
685 if (StackPos == 0)
686 ParentTag = string();
687 else
688 ParentTag = Stack[--StackPos];
689 }
08e8f724 690 continue;
b2e465d6
AL
691 }
692
08e8f724 693 // Parse off the tag
7f25bdff
AL
694 string Tag;
695 const char *Pos = LineBuffer.c_str();
696 if (ParseQuoteWord(Pos,Tag) == false)
db0db9fe 697 return _error->Error(_("Syntax error %s:%u: Malformed tag"),FName.c_str(),CurLine);
b2e465d6
AL
698
699 // Parse off the word
700 string Word;
bcae6dd4 701 bool NoWord = false;
b2e465d6
AL
702 if (ParseCWord(Pos,Word) == false &&
703 ParseQuoteWord(Pos,Word) == false)
704 {
705 if (TermChar != '{')
706 {
707 Word = Tag;
708 Tag = "";
bcae6dd4 709 }
4ae405e9
AL
710 else
711 NoWord = true;
b2e465d6
AL
712 }
713 if (strlen(Pos) != 0)
714 return _error->Error(_("Syntax error %s:%u: Extra junk after value"),FName.c_str(),CurLine);
715
08e8f724
AL
716 // Go down a level
717 if (TermChar == '{')
718 {
a4e87467
AL
719 if (StackPos <= 100)
720 Stack[StackPos++] = ParentTag;
b2e465d6
AL
721
722 /* Make sectional tags incorperate the section into the
723 tag string */
724 if (AsSectional == true && Word.empty() == false)
725 {
726 Tag += "::" ;
727 Tag += Word;
728 Word = "";
729 }
730
08e8f724
AL
731 if (ParentTag.empty() == true)
732 ParentTag = Tag;
733 else
734 ParentTag += string("::") + Tag;
735 Tag = string();
736 }
bfd22fc0 737
08e8f724
AL
738 // Generate the item name
739 string Item;
740 if (ParentTag.empty() == true)
741 Item = Tag;
742 else
743 {
7f25bdff 744 if (TermChar != '{' || Tag.empty() == false)
08e8f724 745 Item = ParentTag + "::" + Tag;
7f25bdff
AL
746 else
747 Item = ParentTag;
08e8f724
AL
748 }
749
b2e465d6
AL
750 // Specials
751 if (Tag.length() >= 1 && Tag[0] == '#')
752 {
753 if (ParentTag.empty() == false)
754 return _error->Error(_("Syntax error %s:%u: Directives can only be done at the top level"),FName.c_str(),CurLine);
755 Tag.erase(Tag.begin());
756 if (Tag == "clear")
757 Conf.Clear(Word);
758 else if (Tag == "include")
759 {
760 if (Depth > 10)
761 return _error->Error(_("Syntax error %s:%u: Too many nested includes"),FName.c_str(),CurLine);
762 if (Word.length() > 2 && Word.end()[-1] == '/')
763 {
764 if (ReadConfigDir(Conf,Word,AsSectional,Depth+1) == false)
765 return _error->Error(_("Syntax error %s:%u: Included from here"),FName.c_str(),CurLine);
766 }
767 else
768 {
769 if (ReadConfigFile(Conf,Word,AsSectional,Depth+1) == false)
770 return _error->Error(_("Syntax error %s:%u: Included from here"),FName.c_str(),CurLine);
771 }
772 }
773 else
774 return _error->Error(_("Syntax error %s:%u: Unsupported directive '%s'"),FName.c_str(),CurLine,Tag.c_str());
775 }
c3a3a1b1
DK
776 else if (Tag.empty() == true && NoWord == false && Word == "#clear")
777 return _error->Error(_("Syntax error %s:%u: clear directive requires an option tree as argument"),FName.c_str(),CurLine);
b2e465d6
AL
778 else
779 {
780 // Set the item in the configuration class
bcae6dd4
AL
781 if (NoWord == false)
782 Conf.Set(Item,Word);
b2e465d6
AL
783 }
784
08e8f724 785 // Empty the buffer
171c75f1 786 LineBuffer.clear();
b2e465d6
AL
787
788 // Move up a tag, but only if there is no bit to parse
789 if (TermChar == '}')
790 {
791 if (StackPos == 0)
171c75f1 792 ParentTag.clear();
b2e465d6
AL
793 else
794 ParentTag = Stack[--StackPos];
795 }
796
08e8f724 797 }
08e8f724
AL
798 }
799
3f807f6c
DB
800 // Store the remaining text, if any, in the current line buffer.
801
802 // NB: could change this to use string-based operations; I'm
803 // using strstrip now to ensure backwards compatibility.
804 // -- dburrows 2008-04-01
805 {
806 char *Buffer = new char[End - Start + 1];
807 try
808 {
809 std::copy(Start, End, Buffer);
810 Buffer[End - Start] = '\0';
811
812 const char *Stripd = _strstrip(Buffer);
813 if (*Stripd != 0 && LineBuffer.empty() == false)
814 LineBuffer += " ";
815 LineBuffer += Stripd;
816 }
817 catch(...)
818 {
819 delete[] Buffer;
820 throw;
821 }
822 delete[] Buffer;
823 }
08e8f724 824 }
b2e465d6
AL
825
826 if (LineBuffer.empty() == false)
827 return _error->Error(_("Syntax error %s:%u: Extra junk at end of file"),FName.c_str(),CurLine);
828 return true;
829}
830 /*}}}*/
831// ReadConfigDir - Read a directory of config files /*{{{*/
832// ---------------------------------------------------------------------
833/* */
a74cd17a 834bool ReadConfigDir(Configuration &Conf,const string &Dir,
be4eec61 835 bool const &AsSectional, unsigned const &Depth)
52643bec 836{
e29a6bb1 837 vector<string> const List = GetListOfFilesInDir(Dir, "conf", true, true);
b2e465d6
AL
838
839 // Read the files
840 for (vector<string>::const_iterator I = List.begin(); I != List.end(); I++)
841 if (ReadConfigFile(Conf,*I,AsSectional,Depth) == false)
842 return false;
08e8f724
AL
843 return true;
844}
845 /*}}}*/
1f2933a8
DK
846// MatchAgainstConfig Constructor /*{{{*/
847Configuration::MatchAgainstConfig::MatchAgainstConfig(char const * Config)
848{
849 std::vector<std::string> const strings = _config->FindVector(Config);
850 for (std::vector<std::string>::const_iterator s = strings.begin();
851 s != strings.end(); ++s)
852 {
853 regex_t *p = new regex_t;
854 if (regcomp(p, s->c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB) == 0)
855 patterns.push_back(p);
856 else
857 {
858 regfree(p);
859 delete p;
860 _error->Warning("Regex compilation error for '%s' in configuration option '%s'",
861 s->c_str(), Config);
862 }
863 }
864
865}
866 /*}}}*/
867// MatchAgainstConfig Destructor /*{{{*/
868Configuration::MatchAgainstConfig::~MatchAgainstConfig()
869{
870 for(std::vector<regex_t *>::const_iterator p = patterns.begin();
871 p != patterns.end(); ++p)
872 {
873 regfree(*p);
874 delete *p;
875 }
876}
877 /*}}}*/
878// MatchAgainstConfig::Match - returns true if a pattern matches /*{{{*/
879bool Configuration::MatchAgainstConfig::Match(char const * str) const
880{
881 for(std::vector<regex_t *>::const_iterator p = patterns.begin();
882 p != patterns.end(); ++p)
883 if (regexec(*p, str, 0, 0, 0) == 0)
884 return true;
885
886 return false;
887}
888 /*}}}*/