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