f3aeef258c4fa8db1241f704dcf3451646f5bf64
[public/netxms.git] / src / libnetxms / config.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** NetXMS Foundation Library
4 ** Copyright (C) 2003-2009 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: config.cpp
21 **
22 **/
23
24 #include "libnetxms.h"
25 #include <nxconfig.h>
26
27
28 //
29 // Constructor for config entry
30 //
31
32 ConfigEntry::ConfigEntry(const TCHAR *name, ConfigEntry *parent, const TCHAR *file, int line)
33 {
34 m_name = _tcsdup(CHECK_NULL(name));
35 m_childs = NULL;
36 m_next = NULL;
37 if (parent != NULL)
38 parent->addEntry(this);
39 m_valueCount = 0;
40 m_values = NULL;
41 m_file = _tcsdup(CHECK_NULL(file));
42 m_line = line;
43 }
44
45
46 //
47 // Destructor for config entry
48 //
49
50 ConfigEntry::~ConfigEntry()
51 {
52 ConfigEntry *entry, *next;
53
54 for(entry = m_childs; entry != NULL; entry = next)
55 {
56 next = entry->getNext();
57 delete entry;
58 }
59 safe_free(m_name);
60
61 for(int i = 0; i < m_valueCount; i++)
62 safe_free(m_values[i]);
63 safe_free(m_values);
64 }
65
66
67 //
68 // Find entry by name
69 //
70
71 ConfigEntry *ConfigEntry::findEntry(const TCHAR *name)
72 {
73 ConfigEntry *e;
74
75 for(e = m_childs; e != NULL; e = e->getNext())
76 if (!_tcsicmp(e->getName(), name))
77 return e;
78 return NULL;
79 }
80
81
82 //
83 // Get value
84 //
85
86 const TCHAR *ConfigEntry::getValue(int index)
87 {
88 if ((index < 0) || (index >= m_valueCount))
89 return NULL;
90 return m_values[index];
91 }
92
93
94 //
95 // Set value
96 //
97
98 void ConfigEntry::setValue(const TCHAR *value)
99 {
100 for(int i = 0; i < m_valueCount; i++)
101 safe_free(m_values[i]);
102 m_valueCount = 1;
103 m_values = (TCHAR **)realloc(m_values, sizeof(TCHAR *));
104 m_values[0] = _tcsdup(value);
105 }
106
107
108 //
109 // Add value
110 //
111
112 void ConfigEntry::addValue(const TCHAR *value)
113 {
114 m_values = (TCHAR **)realloc(m_values, sizeof(TCHAR *) * (m_valueCount + 1));
115 m_values[m_valueCount] = _tcsdup(value);
116 m_valueCount++;
117 }
118
119
120 //
121 // Get summary length of all values as if they was concatenated with separator character
122 //
123
124 int ConfigEntry::getConcatenatedValuesLength()
125 {
126 int i, len;
127
128 if (m_valueCount < 1)
129 return 0;
130
131 for(i = 0, len = 0; i < m_valueCount; i++)
132 len += _tcslen(m_values[i]);
133 return len + (m_valueCount - 1);
134 }
135
136
137 //
138 // Constructor for config
139 //
140
141 Config::Config()
142 {
143 m_root = new ConfigEntry(_T("[root]"), NULL, NULL, 0);
144 m_errorCount = 0;
145 }
146
147
148 //
149 // Destructor
150 //
151
152 Config::~Config()
153 {
154 delete m_root;
155 }
156
157
158 //
159 // Default error handler
160 //
161
162 void Config::onError(const TCHAR *errorMessage)
163 {
164 _ftprintf(stderr, _T("%s\n"), errorMessage);
165 }
166
167
168 //
169 // Report error
170 //
171
172 void Config::error(const TCHAR *format, ...)
173 {
174 va_list args;
175 TCHAR buffer[4096];
176
177 m_errorCount++;
178 va_start(args, format);
179 _vsntprintf(buffer, 4096, format, args);
180 va_end(args);
181 onError(buffer);
182 }
183
184
185 //
186 // Bind parameters to variables: simulation of old NxLoadConfig() API
187 //
188
189 bool Config::bindParameters(const TCHAR *section, NX_CFG_TEMPLATE *cfgTemplate)
190 {
191 TCHAR name[MAX_PATH], *curr, *eptr;
192 int i, j, pos;
193 ConfigEntry *entry;
194
195 name[0] = _T('/');
196 nx_strncpy(&name[1], section, MAX_PATH - 2);
197 _tcscat(name, _T("/"));
198 pos = _tcslen(name);
199
200 for(i = 0; cfgTemplate[i].iType != CT_END_OF_LIST; i++)
201 {
202 nx_strncpy(&name[pos], cfgTemplate[i].szToken, MAX_PATH - pos);
203 entry = getEntry(name);
204 if (entry != NULL)
205 {
206 const TCHAR *value = CHECK_NULL(entry->getValue());
207 switch(cfgTemplate[i].iType)
208 {
209 case CT_LONG:
210 *((LONG *)cfgTemplate[i].pBuffer) = _tcstol(value, &eptr, 0);
211 if (*eptr != 0)
212 {
213 error(_T("Invalid number '%s' in configuration file %s at line %d\n"), value, entry->getFile(), entry->getLine());
214 }
215 break;
216 case CT_WORD:
217 *((WORD *)cfgTemplate[i].pBuffer) = (WORD)_tcstoul(value, &eptr, 0);
218 if (*eptr != 0)
219 {
220 error(_T("Invalid number '%s' in configuration file %s at line %d\n"), value, entry->getFile(), entry->getLine());
221 }
222 break;
223 case CT_BOOLEAN:
224 if (!_tcsicmp(value, _T("yes")) || !_tcsicmp(value, _T("true")) ||
225 !_tcsicmp(value, _T("on")) || !_tcsicmp(value, _T("1")))
226 {
227 *((DWORD *)cfgTemplate[i].pBuffer) |= cfgTemplate[i].dwBufferSize;
228 }
229 else
230 {
231 *((DWORD *)cfgTemplate[i].pBuffer) &= ~(cfgTemplate[i].dwBufferSize);
232 }
233 break;
234 case CT_STRING:
235 nx_strncpy((TCHAR *)cfgTemplate[i].pBuffer, value, cfgTemplate[i].dwBufferSize);
236 break;
237 case CT_STRING_LIST:
238 *((TCHAR **)cfgTemplate[i].pBuffer) = (TCHAR *)malloc(sizeof(TCHAR) * (entry->getConcatenatedValuesLength() + 1));
239 for(j = 0, curr = *((TCHAR **)cfgTemplate[i].pBuffer); j < entry->getValueCount(); j++)
240 {
241 _tcscpy(curr, entry->getValue(j));
242 curr += _tcslen(curr);
243 *curr = cfgTemplate[i].cSeparator;
244 curr++;
245 }
246 if (j > 0)
247 curr--;
248 *curr = 0;
249 break;
250 case CT_IGNORE:
251 break;
252 default:
253 break;
254 }
255 }
256 }
257
258 return m_errorCount == 0;
259 }
260
261
262 //
263 // Load INI-style config
264 //
265
266 bool Config::loadIniConfig(const TCHAR *file, const TCHAR *defaultSectionName)
267 {
268 FILE *cfg;
269 TCHAR buffer[4096], *ptr;
270 ConfigEntry *currentSection;
271 int sourceLine = 0;
272
273 cfg = _tfopen(file, _T("r"));
274 if (cfg == NULL)
275 {
276 error(_T("Cannot open file %s"), file);
277 return false;
278 }
279
280 currentSection = m_root->findEntry(defaultSectionName);
281 if (currentSection == NULL)
282 {
283 currentSection = new ConfigEntry(defaultSectionName, m_root, file, 0);
284 }
285
286 while(!feof(cfg))
287 {
288 // Read line from file
289 buffer[0] = 0;
290 _fgetts(buffer, 4095, cfg);
291 sourceLine++;
292 ptr = _tcschr(buffer, _T('\n'));
293 if (ptr != NULL)
294 *ptr = 0;
295 ptr = _tcschr(buffer, _T('#'));
296 if (ptr != NULL)
297 *ptr = 0;
298
299 StrStrip(buffer);
300 if (buffer[0] == 0)
301 continue;
302
303 // Check if it's a section name
304 if ((buffer[0] == _T('*')) || (buffer[0] == _T('[')))
305 {
306 if (buffer[0] == _T('['))
307 {
308 TCHAR *end = _tcschr(buffer, _T(']'));
309 if (end != NULL)
310 *end = 0;
311 }
312 currentSection = m_root->findEntry(&buffer[1]);
313 if (currentSection == NULL)
314 {
315 currentSection = new ConfigEntry(&buffer[1], m_root, file, sourceLine);
316 }
317 }
318 else
319 {
320 // Divide on two parts at = sign
321 ptr = _tcschr(buffer, _T('='));
322 if (ptr == NULL)
323 {
324 error(_T("Syntax error in configuration file %s at line %d"), file, sourceLine);
325 continue;
326 }
327 *ptr = 0;
328 ptr++;
329 StrStrip(buffer);
330 StrStrip(ptr);
331
332 ConfigEntry *entry = currentSection->findEntry(buffer);
333 if (entry == NULL)
334 entry = new ConfigEntry(buffer, currentSection, file, sourceLine);
335 entry->addValue(ptr);
336 }
337 }
338 fclose(cfg);
339 return true;
340 }
341
342
343 //
344 // Get value
345 //
346
347 const TCHAR *Config::getValue(const TCHAR *path)
348 {
349 ConfigEntry *entry = getEntry(path);
350 return (entry != NULL) ? entry->getValue() : NULL;
351 }
352
353
354 //
355 // Get entry
356 //
357
358 ConfigEntry *Config::getEntry(const TCHAR *path)
359 {
360 const TCHAR *curr, *end;
361 TCHAR name[256];
362 ConfigEntry *entry = m_root;
363
364 if ((path == NULL) || (*path != _T('/')))
365 return NULL;
366
367 curr = path + 1;
368 while(entry != NULL)
369 {
370 end = _tcschr(curr, _T('/'));
371 if (end != NULL)
372 {
373 int len = min((int)(end - curr), 255);
374 _tcsncpy(name, curr, len);
375 name[len] = 0;
376 entry = entry->findEntry(name);
377 curr = end + 1;
378 }
379 else
380 {
381 return entry->findEntry(curr);
382 }
383 }
384 return NULL;
385 }