1 //
2 // Configuration.cc
3 //
4 // Configuration: This class provides an object lookup table. Each object
5 // in the Configuration is indexed with a string. The objects
6 // can be returned by mentioning their string index. Values may
7 // include files with `/path/to/file` or other configuration
8 // variables with ${variable}
9 //
10 // Part of the ht://Dig package <http://www.htdig.org/>
11 // Copyright (c) 1999-2004 The ht://Dig Group
12 // For copyright details, see the file COPYING in your distribution
13 // or the GNU Library General Public License (LGPL) version 2 or later
14 // <http://www.gnu.org/copyleft/lgpl.html>
15 //
16 // $Id: Configuration.cc,v 1.20 2004/05/28 13:15:20 lha Exp $
17 //
18
19 #ifdef HAVE_CONFIG_H
20 #include "htconfig.h"
21 #endif /* HAVE_CONFIG_H */
22
23 #include <stdio.h>
24 #include "Configuration.h"
25 #include "htString.h"
26 #include "ParsedString.h"
27
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <locale.h>
31
32
33 //*********************************************************************
34 // Configuration::Configuration()
35 //
Configuration()36 Configuration::Configuration() : separators("=:"), allow_multiple(0)
37 {
38 }
39
40
41 //*********************************************************************
42 // void Configuration::NameValueSeparators(char *s)
43 //
NameValueSeparators(const String & s)44 void Configuration::NameValueSeparators(const String& s)
45 {
46 separators = s;
47 }
48
49
50 //*********************************************************************
51 // Add an entry to the configuration table.
52 //
Add(const String & str_arg)53 void Configuration::Add(const String& str_arg)
54 {
55 const char* str = str_arg;
56 String name, value;
57
58 while (str && *str)
59 {
60 while (isspace(*str))
61 str++;
62 name = 0;
63 if (!isalpha(*str))
64 break;
65 // Some isalnum() implementations don't allow all the letters that
66 // isalpha() does, e.g. accented ones. They're not POSIX.2 compliant
67 // but we won't punish them with an infinite loop...
68 if (!isalnum(*str))
69 break;
70 while (isalnum(*str) || *str == '-' || *str == '_')
71 name << *str++;
72
73 name.lowercase();
74
75 //
76 // We have the name. Let's see if we will get a value
77 //
78 while (isspace(*str))
79 str++;
80 if (!*str)
81 {
82 //
83 // End of string. We need to store the name as a boolean TRUE
84 //
85 Add(name, "true");
86 return;
87 }
88
89 if (!strchr((char*)separators, *str))
90 {
91 //
92 // We are now at a new name. The previous one needs to be set
93 // to boolean TRUE
94 //
95 Add(name, "true");
96 continue;
97 }
98
99 //
100 // We now need to deal with the value
101 //
102 str++; // Skip the separator
103 while (isspace(*str))
104 str++;
105 if (!*str)
106 {
107 //
108 // End of string reached. The value must be blank
109 //
110 Add(name, "");
111 break;
112 }
113 value = 0;
114 if (*str == '"')
115 {
116 //
117 // Ah! A quoted value. This should be easy to deal with...
118 // (Just kidding!)
119 //
120 str++;
121 while (*str && *str != '"')
122 {
123 value << *str++;
124 }
125 Add(name, value);
126 if (*str == '"')
127 str++;
128 continue;
129 }
130 else if (*str == '\'')
131 {
132 // A single quoted value.
133 str++;
134 while (*str && *str != '\'')
135 {
136 value << *str++;
137 }
138 Add(name, value);
139 if (*str == '\'')
140 str++;
141 continue;
142 }
143 else
144 {
145 //
146 // A non-quoted string. This string will terminate at the
147 // next blank
148 //
149 while (*str && !isspace(*str))
150 {
151 value << *str++;
152 }
153 Add(name, value);
154 continue;
155 }
156 }
157 }
158
159
160 //*********************************************************************
161 // Add an entry to the configuration table, without allowing variable
162 // or file expansion of the value.
163 //
Add(const String & name,const String & value)164 void Configuration::Add(const String& name, const String& value)
165 {
166 String escaped;
167 const char *s = value.get();
168 while (*s)
169 {
170 if (strchr("$`\\", *s))
171 escaped << '\\';
172 escaped << *s++;
173 }
174 ParsedString *ps = new ParsedString(escaped);
175 dcGlobalVars.Add(name, ps);
176 }
177
178
179 //*********************************************************************
180 // Add an entry to the configuration table, allowing parsing for variable
181 // or file expansion of the value.
182 //
AddParsed(const String & name,const String & value)183 void Configuration::AddParsed(const String& name, const String& value)
184 {
185 ParsedString *ps = new ParsedString(value);
186 if (mystrcasecmp(name, "locale") == 0)
187 {
188 String str(setlocale(LC_ALL, ps->get(dcGlobalVars)));
189 ps->set(str);
190
191 //
192 // Set time format to standard to avoid sending If-Modified-Since
193 // http headers in native format which http servers can't
194 // understand
195 //
196 setlocale(LC_TIME, "C");
197 }
198 dcGlobalVars.Add(name, ps);
199 }
200
201
202 //*********************************************************************
203 // Remove an entry from both the hash table and from the list of keys.
204 //
Remove(const String & name)205 int Configuration::Remove(const String& name)
206 {
207 return dcGlobalVars.Remove(name);
208 }
209
210
211 //*********************************************************************
212 // char *Configuration::Find(const char *name) const
213 // Retrieve a variable from the configuration database. This variable
214 // will be parsed and a new String object will be returned.
215 //
Find(const String & name) const216 const String Configuration::Find(const String& name) const
217 {
218 ParsedString *ps = (ParsedString *) dcGlobalVars[name];
219 if (ps)
220 {
221 return ps->get(dcGlobalVars);
222 }
223 else
224 {
225 #ifdef DEBUG
226 fprintf (stderr, "Could not find configuration option %s\n", (const char*)name);
227 #endif
228 return 0;
229 }
230 }
231
232 //-
233 // Return 1 if the value of configuration attribute <b>name</b> has
234 // been set, 0 otherwise
Exists(const String & name) const235 int Configuration::Exists(const String& name) const
236 {
237 return dcGlobalVars.Exists(name);
238 }
239
240 //*********************************************************************
Get_Object(char * name)241 Object *Configuration::Get_Object(char *name) {
242 return dcGlobalVars[name];
243 }
244
245
246 //*********************************************************************
247 //
Value(const String & name,int default_value) const248 int Configuration::Value(const String& name, int default_value) const
249 {
250 return Find(name).as_integer(default_value);
251 }
252
253
254 //*********************************************************************
255 //
Double(const String & name,double default_value) const256 double Configuration::Double(const String& name, double default_value) const
257 {
258 return Find(name).as_double(default_value);
259 }
260
261
262 //*********************************************************************
263 // int Configuration::Boolean(char *name, int default_value)
264 //
Boolean(const String & name,int default_value) const265 int Configuration::Boolean(const String& name, int default_value) const
266 {
267 int value = default_value;
268 const String s = Find(name);
269 if (s[0])
270 {
271 if (s.nocase_compare("true") == 0 ||
272 s.nocase_compare("yes") == 0 ||
273 s.nocase_compare("1") == 0)
274 value = 1;
275 else if (s.nocase_compare("false") == 0 ||
276 s.nocase_compare("no") == 0 ||
277 s.nocase_compare("0") == 0)
278 value = 0;
279 }
280
281 return value;
282 }
283
284
285 //*********************************************************************
286 //
operator [](const String & name) const287 const String Configuration::operator[](const String& name) const
288 {
289 return Find(name);
290 }
291
292
293 //*********************************************************************
294 //
Read(const String & filename)295 int Configuration::Read(const String& filename)
296 {
297 FILE* in = fopen((const char*)filename, "r");
298
299 if(!in) {
300 fprintf(stderr, "Configuration::Read: cannot open %s for reading : ", (const char*)filename);
301 perror("");
302 return NOTOK;
303 }
304
305 #define CONFIG_BUFFER_SIZE (50*1024)
306 //
307 // Make the line buffer large so that we can read long lists of start
308 // URLs.
309 //
310 char buffer[CONFIG_BUFFER_SIZE + 1];
311 char *current;
312 String line;
313 String name;
314 char *value;
315 int len;
316 while (fgets(buffer, CONFIG_BUFFER_SIZE, in))
317 {
318 line << buffer;
319 line.chop("\r\n");
320 if (line.last() == '\\')
321 {
322 line.chop(1);
323 continue; // Append the next line to this one
324 }
325
326 current = line.get();
327 if (*current == '#' || *current == '\0')
328 {
329 line = 0;
330 continue; // Comments and blank lines are skipped
331 }
332
333 name = strtok(current, ": =\t");
334 value = strtok(0, "\r\n");
335 if (!value)
336 value = ""; // Blank value
337
338 //
339 // Skip any whitespace before the actual text
340 //
341 while (*value == ' ' || *value == '\t')
342 value++;
343 len = strlen(value) - 1;
344 //
345 // Skip any whitespace after the actual text
346 //
347 while (len >= 0 && (value[len] == ' ' || value[len] == '\t'))
348 {
349 value[len] = '\0';
350 len--;
351 }
352
353 if (mystrcasecmp((char*)name, "include") == 0)
354 {
355 ParsedString ps(value);
356 String str(ps.get(dcGlobalVars));
357 if (str[0] != '/') // Given file name not fully qualified
358 {
359 str = filename; // so strip dir. name from current one
360 len = str.lastIndexOf('/') + 1;
361 if (len > 0)
362 str.chop(str.length() - len);
363 else
364 str = ""; // No slash in current filename
365 str << ps.get(dcGlobalVars);
366 }
367 Read(str);
368 line = 0;
369 continue;
370 }
371
372 AddParsed(name, value);
373 line = 0;
374 }
375 fclose(in);
376 return OK;
377 }
378
379
380 //*********************************************************************
381 // void Configuration::Defaults(ConfigDefaults *array)
382 //
Defaults(const ConfigDefaults * array)383 void Configuration::Defaults(const ConfigDefaults *array)
384 {
385 for (int i = 0; array[i].name; i++)
386 {
387 AddParsed(array[i].name, array[i].value);
388 }
389 }
390
391