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