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