]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/configuration.cc
Start on acquire stuff
[apt.git] / apt-pkg / contrib / configuration.cc
CommitLineData
6c139d6e
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3b5421b4 3// $Id: configuration.cc,v 1.7 1998/10/20 02:39:26 jgg 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.
11
12 ##################################################################### */
13 /*}}}*/
14// Include files /*{{{*/
15#ifdef __GNUG__
094a497d 16#pragma implementation "apt-pkg/configuration.h"
6c139d6e 17#endif
094a497d 18#include <apt-pkg/configuration.h>
08e8f724 19#include <apt-pkg/error.h>
6c139d6e
AL
20#include <strutl.h>
21
22#include <stdio.h>
08e8f724 23#include <fstream.h>
6c139d6e 24 /*}}}*/
9c14e3d6
AL
25
26Configuration *_config = new Configuration;
6c139d6e
AL
27
28// Configuration::Configuration - Constructor /*{{{*/
29// ---------------------------------------------------------------------
30/* */
31Configuration::Configuration()
32{
33 Root = new Item;
34}
35 /*}}}*/
36// Configuration::Lookup - Lookup a single item /*{{{*/
37// ---------------------------------------------------------------------
38/* This will lookup a single item by name below another item. It is a
39 helper function for the main lookup function */
40Configuration::Item *Configuration::Lookup(Item *Head,const char *S,
41 unsigned long Len,bool Create)
42{
43 int Res = 1;
44 Item *I = Head->Child;
45 Item **Last = &Head->Child;
46 for (; I != 0; Last = &I->Next, I = I->Next)
9c14e3d6 47 if ((Res = stringcasecmp(I->Tag.begin(),I->Tag.end(),S,S + Len)) == 0)
6c139d6e 48 break;
9c14e3d6 49
6c139d6e
AL
50 if (Res == 0)
51 return I;
52 if (Create == false)
53 return 0;
54
55 I = new Item;
9c14e3d6 56 I->Tag = string(S,Len);
6c139d6e 57 I->Next = *Last;
9c14e3d6 58 I->Parent = Head;
6c139d6e
AL
59 *Last = I;
60 return I;
61}
62 /*}}}*/
63// Configuration::Lookup - Lookup a fully scoped item /*{{{*/
64// ---------------------------------------------------------------------
65/* This performs a fully scoped lookup of a given name, possibly creating
66 new items */
67Configuration::Item *Configuration::Lookup(const char *Name,bool Create)
68{
69 const char *Start = Name;
70 const char *End = Start + strlen(Name);
71 const char *TagEnd = Name;
72 Item *Itm = Root;
73 for (; End - TagEnd > 2; TagEnd++)
74 {
75 if (TagEnd[0] == ':' && TagEnd[1] == ':')
76 {
77 Itm = Lookup(Itm,Start,TagEnd - Start,Create);
78 if (Itm == 0)
79 return 0;
80 TagEnd = Start = TagEnd + 2;
81 }
82 }
83
84 Itm = Lookup(Itm,Start,End - Start,Create);
6c139d6e
AL
85 return Itm;
86}
87 /*}}}*/
88// Configuration::Find - Find a value /*{{{*/
89// ---------------------------------------------------------------------
90/* */
91string Configuration::Find(const char *Name,const char *Default)
92{
93 Item *Itm = Lookup(Name,false);
94 if (Itm == 0 || Itm->Value.empty() == true)
9c14e3d6
AL
95 {
96 if (Default == 0)
97 return string();
98 else
99 return Default;
100 }
101
6c139d6e
AL
102 return Itm->Value;
103}
104 /*}}}*/
3b5421b4 105// Configuration::FindFile - Find a Filename /*{{{*/
9c14e3d6
AL
106// ---------------------------------------------------------------------
107/* Directories are stored as the base dir in the Parent node and the
3b5421b4 108 sub directory in sub nodes with the final node being the end filename
9c14e3d6 109 */
3b5421b4 110string Configuration::FindFile(const char *Name,const char *Default)
9c14e3d6
AL
111{
112 Item *Itm = Lookup(Name,false);
113 if (Itm == 0 || Itm->Value.empty() == true)
114 {
115 if (Default == 0)
116 return string();
117 else
118 return Default;
119 }
120
0a8e3465 121 // Absolute path
9c14e3d6
AL
122 if (Itm->Value[0] == '/' || Itm->Parent == 0)
123 return Itm->Value;
0a8e3465
AL
124
125 // ./ is also considered absolute as is anything with ~ in it
126 if (Itm->Value[0] != 0 &&
127 ((Itm->Value[0] == '.' && Itm->Value[1] == '/') ||
128 (Itm->Value[0] == '~' && Itm->Value[1] == '/')))
129 return Itm->Value;
130
9c14e3d6
AL
131 if (Itm->Parent->Value.end()[-1] == '/')
132 return Itm->Parent->Value + Itm->Value;
133 else
134 return Itm->Parent->Value + '/' + Itm->Value;
135}
136 /*}}}*/
3b5421b4
AL
137// Configuration::FindDir - Find a directory name /*{{{*/
138// ---------------------------------------------------------------------
139/* This is like findfile execept the result is terminated in a / */
140string Configuration::FindDir(const char *Name,const char *Default)
141{
142 string Res = FindFile(Name,Default);
143 if (Res.end()[-1] != '/')
144 return Res + '/';
145 return Res;
146}
147 /*}}}*/
6c139d6e
AL
148// Configuration::FindI - Find an integer value /*{{{*/
149// ---------------------------------------------------------------------
150/* */
151int Configuration::FindI(const char *Name,int Default)
152{
153 Item *Itm = Lookup(Name,false);
154 if (Itm == 0 || Itm->Value.empty() == true)
155 return Default;
156
157 char *End;
158 int Res = strtol(Itm->Value.c_str(),&End,0);
159 if (End == Itm->Value.c_str())
160 return Default;
161
08e8f724
AL
162 return Res;
163}
164 /*}}}*/
165// Configuration::FindB - Find a boolean type /*{{{*/
166// ---------------------------------------------------------------------
167/* */
168bool Configuration::FindB(const char *Name,bool Default)
169{
170 Item *Itm = Lookup(Name,false);
171 if (Itm == 0 || Itm->Value.empty() == true)
172 return Default;
173
3b5421b4 174 return StringToBool(Itm->Value,Default);
6c139d6e
AL
175}
176 /*}}}*/
177// Configuration::Set - Set a value /*{{{*/
178// ---------------------------------------------------------------------
179/* */
180void Configuration::Set(const char *Name,string Value)
181{
182 Item *Itm = Lookup(Name,true);
183 if (Itm == 0)
184 return;
185 Itm->Value = Value;
186}
187 /*}}}*/
188// Configuration::Set - Set an integer value /*{{{*/
189// ---------------------------------------------------------------------
190/* */
191void Configuration::Set(const char *Name,int Value)
192{
193 Item *Itm = Lookup(Name,true);
194 if (Itm == 0)
195 return;
196 char S[300];
197 snprintf(S,sizeof(S),"%i",Value);
198 Itm->Value = S;
199}
200 /*}}}*/
08e8f724
AL
201// Configuration::Exists - Returns true if the Name exists /*{{{*/
202// ---------------------------------------------------------------------
203/* */
204bool Configuration::Exists(const char *Name)
205{
206 Item *Itm = Lookup(Name,false);
207 if (Itm == 0)
208 return false;
209 return true;
210}
211 /*}}}*/
212
213// ReadConfigFile - Read a configuration file /*{{{*/
214// ---------------------------------------------------------------------
215/* The configuration format is very much like the named.conf format
216 used in bind8, in fact this routine can parse most named.conf files. */
217bool ReadConfigFile(Configuration &Conf,string FName)
218{
219 // Open the stream for reading
220 ifstream F(FName.c_str(),ios::in | ios::nocreate);
221 if (!F != 0)
222 return _error->Errno("ifstream::ifstream","Opening configuration file %s",FName.c_str());
223
224 char Buffer[300];
225 string LineBuffer;
226
227 // Parser state
228 string ParentTag;
229
230 int CurLine = 0;
231 bool InComment = false;
232 while (F.eof() == false)
233 {
234 F.getline(Buffer,sizeof(Buffer));
235 CurLine++;
236 _strtabexpand(Buffer,sizeof(Buffer));
237 _strstrip(Buffer);
238
239 // Multi line comment
240 if (InComment == true)
241 {
242 for (const char *I = Buffer; *I != 0; I++)
243 {
244 if (*I == '*' && I[1] == '/')
245 {
246 memmove(Buffer,I+2,strlen(I+2) + 1);
247 InComment = false;
248 break;
249 }
250 }
251 if (InComment == true)
252 continue;
253 }
254
255 // Discard single line comments
256 for (char *I = Buffer; *I != 0; I++)
257 {
258 if (*I == '/' && I[1] == '/')
259 {
260 *I = 0;
261 break;
262 }
263 }
264
265 // Look for multi line comments
266 for (char *I = Buffer; *I != 0; I++)
267 {
268 if (*I == '/' && I[1] == '*')
269 {
270 InComment = true;
271 for (char *J = Buffer; *J != 0; J++)
272 {
273 if (*J == '*' && J[1] == '/')
274 {
275 memmove(I,J+2,strlen(J+2) + 1);
276 InComment = false;
277 break;
278 }
279 }
280
281 if (InComment == true)
282 {
283 *I = 0;
284 break;
285 }
286 }
287 }
288
289 // Blank
290 if (Buffer[0] == 0)
291 continue;
292
293 // We now have a valid line fragment
294 for (char *I = Buffer; *I != 0;)
295 {
296 if (*I == '{' || *I == ';' || *I == '}')
297 {
298 // Put the last fragement into the buffer
299 char *Start = Buffer;
300 char *Stop = I;
301 for (; Start != I && isspace(*Start) != 0; Start++);
302 for (; Stop != Start && isspace(Stop[-1]) != 0; Stop--);
303 if (LineBuffer.empty() == false && Stop - Start != 0)
304 LineBuffer += ' ';
305 LineBuffer += string(Start,Stop - Start);
306
307 // Remove the fragment
308 char TermChar = *I;
309 memmove(Buffer,I + 1,strlen(I + 1) + 1);
310 I = Buffer;
311
312 // Move up a tag
313 if (TermChar == '}')
314 {
315 string::size_type Pos = ParentTag.rfind("::");
316 if (Pos == string::npos)
317 ParentTag = string();
318 else
319 ParentTag = string(ParentTag,0,Pos);
320 }
321
322 // Syntax Error
323 if (TermChar == '{' && LineBuffer.empty() == true)
324 return _error->Error("Syntax error %s:%u: Block starts with no name.",FName.c_str(),CurLine);
325
326 if (LineBuffer.empty() == true)
327 continue;
328
329 // Parse off the tag
330 string::size_type Pos = LineBuffer.find(' ');
331 if (Pos == string::npos)
332 {
333 if (TermChar == '{')
334 Pos = LineBuffer.length();
335 else
336 return _error->Error("Syntax error %s:%u: Tag with no value",FName.c_str(),CurLine);
337 }
338
339 string Tag = string(LineBuffer,0,Pos);
340
341 // Go down a level
342 if (TermChar == '{')
343 {
344 if (ParentTag.empty() == true)
345 ParentTag = Tag;
346 else
347 ParentTag += string("::") + Tag;
348 Tag = string();
349 }
350
351 // We dont have a value to set
352 if (Pos == LineBuffer.length())
353 {
354 LineBuffer = string();
355 continue;
356 }
357
358 // Parse off the word
359 string Word;
360 if (ParseCWord(LineBuffer.c_str()+Pos,Word) == false)
361 return _error->Error("Syntax error %s:%u: Malformed value",FName.c_str(),CurLine);
362
363 // Generate the item name
364 string Item;
365 if (ParentTag.empty() == true)
366 Item = Tag;
367 else
368 {
369 if (Tag.empty() == true)
370 Item = ParentTag;
371 else
372 Item = ParentTag + "::" + Tag;
373 }
374
375 // Set the item in the configuration class
376 Conf.Set(Item,Word);
377
378 // Empty the buffer
379 LineBuffer = string();
380 }
381 else
382 I++;
383 }
384
385 // Store the fragment
386 const char *Stripd = _strstrip(Buffer);
387 if (*Stripd != 0 && LineBuffer.empty() == false)
388 LineBuffer += " ";
389 LineBuffer += Stripd;
390 }
391
392 return true;
393}
394 /*}}}*/