blob: 9ebc20a8a19c31bacbc846af68f91d647c88b0e4 [file] [log] [blame]
Constantin Kaplinskyde179d42006-04-16 06:53:44 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Peter Åstrande2ab84e2005-02-05 16:33:44 +00002 * Copyright (C) 2004-2005 Cendio AB. All rights reserved.
Constantin Kaplinskyde179d42006-04-16 06:53:44 +00003 *
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00004 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20// -=- Configuration.cxx
21
22#include <stdlib.h>
23#include <ctype.h>
24#include <string.h>
25#include <assert.h>
26#ifdef WIN32
27#define strcasecmp _stricmp
28#define strncasecmp _strnicmp
29#endif
30
31#include <rfb/util.h>
32#include <rfb/Configuration.h>
33#include <rfb/LogWriter.h>
34#include <rfb/Exception.h>
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000035#include <rfb/Threading.h>
36
Constantin Kaplinskyde179d42006-04-16 06:53:44 +000037#ifdef __RFB_THREADING_IMPL
38// On platforms that support Threading, we use Locks to make getData safe
39#define LOCK_CONFIG Lock l(*configLock())
40rfb::Mutex* configLock_ = 0;
41static rfb::Mutex* configLock() {
42 if (!configLock_)
43 configLock_ = new rfb::Mutex;
44 return configLock_;
45}
46#else
47#define LOCK_CONFIG
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000048#endif
49
50#include <rdr/HexOutStream.h>
51#include <rdr/HexInStream.h>
52
53using namespace rfb;
54
55static LogWriter vlog("Config");
56
57
Constantin Kaplinskyde179d42006-04-16 06:53:44 +000058// -=- The Global Configuration object
59Configuration* Configuration::global_ = 0;
60Configuration* Configuration::global() {
61 if (!global_)
62 global_ = new Configuration("Global");
63 return global_;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000064}
65
Constantin Kaplinskyde179d42006-04-16 06:53:44 +000066
67// -=- Configuration implementation
68
69Configuration::Configuration(const char* name_, Configuration* attachToGroup)
70: name(strDup(name_)), head(0), _next(0) {
71 if (attachToGroup) {
72 _next = attachToGroup->_next;
73 attachToGroup->_next = this;
74 }
75}
76
77Configuration& Configuration::operator=(const Configuration& src) {
78 VoidParameter* current = head;
79 while (current) {
80 VoidParameter* srcParam = ((Configuration&)src).get(current->getName());
81 if (srcParam) {
82 current->immutable = false;
83 CharArray value(srcParam->getValueStr());
84 vlog.debug("operator=(%s, %s)", current->getName(), value.buf);
85 current->setParam(value.buf);
86 }
87 current = current->_next;
88 }
89 if (_next)
90 *_next=src;
91 return *this;
92}
93
94bool Configuration::set(const char* n, const char* v, bool immutable) {
95 return set(n, strlen(n), v, immutable);
96}
97
98bool Configuration::set(const char* name, int len,
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000099 const char* val, bool immutable)
100{
101 VoidParameter* current = head;
102 while (current) {
103 if ((int)strlen(current->getName()) == len &&
104 strncasecmp(current->getName(), name, len) == 0)
105 {
106 bool b = current->setParam(val);
Peter Åstrand55855d52005-01-03 12:01:45 +0000107 current->setHasBeenSet();
Peter Åstrand07350592005-01-03 14:47:42 +0000108 if (b && immutable)
109 current->setImmutable();
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000110 return b;
111 }
112 current = current->_next;
113 }
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000114 return _next ? _next->set(name, len, val, immutable) : false;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000115}
116
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000117bool Configuration::set(const char* config, bool immutable) {
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000118 bool hyphen = false;
119 if (config[0] == '-') {
120 hyphen = true;
121 config++;
122 if (config[0] == '-') config++; // allow gnu-style --<option>
123 }
124 const char* equal = strchr(config, '=');
125 if (equal) {
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000126 return set(config, equal-config, equal+1, immutable);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000127 } else if (hyphen) {
128 VoidParameter* current = head;
129 while (current) {
130 if (strcasecmp(current->getName(), config) == 0) {
131 bool b = current->setParam();
Peter Åstrand55855d52005-01-03 12:01:45 +0000132 current->setHasBeenSet();
Peter Åstrand07350592005-01-03 14:47:42 +0000133 if (b && immutable)
134 current->setImmutable();
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000135 return b;
136 }
137 current = current->_next;
138 }
139 }
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000140 return _next ? _next->set(config, immutable) : false;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000141}
142
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000143VoidParameter* Configuration::get(const char* param)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000144{
145 VoidParameter* current = head;
146 while (current) {
147 if (strcasecmp(current->getName(), param) == 0)
148 return current;
149 current = current->_next;
150 }
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000151 return _next ? _next->get(param) : 0;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000152}
153
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000154void Configuration::list(int width, int nameWidth) {
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000155 VoidParameter* current = head;
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000156
157 fprintf(stderr, "%s Parameters:\n", name.buf);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000158 while (current) {
159 char* def_str = current->getDefaultStr();
160 const char* desc = current->getDescription();
161 fprintf(stderr," %-*s -", nameWidth, current->getName());
162 int column = strlen(current->getName());
163 if (column < nameWidth) column = nameWidth;
164 column += 4;
165 while (true) {
166 const char* s = strchr(desc, ' ');
167 int wordLen;
168 if (s) wordLen = s-desc;
169 else wordLen = strlen(desc);
170
171 if (column + wordLen + 1 > width) {
172 fprintf(stderr,"\n%*s",nameWidth+4,"");
173 column = nameWidth+4;
174 }
175 fprintf(stderr," %.*s",wordLen,desc);
176 column += wordLen + 1;
177 desc += wordLen + 1;
178 if (!s) break;
179 }
180
181 if (def_str) {
182 if (column + (int)strlen(def_str) + 11 > width)
183 fprintf(stderr,"\n%*s",nameWidth+4,"");
184 fprintf(stderr," (default=%s)\n",def_str);
185 strFree(def_str);
186 } else {
187 fprintf(stderr,"\n");
188 }
189 current = current->_next;
190 }
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000191
192 if (_next)
193 _next->list(width, nameWidth);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000194}
195
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000196
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000197// -=- VoidParameter
198
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000199VoidParameter::VoidParameter(const char* name_, const char* desc_, Configuration* conf)
Peter Åstrand55855d52005-01-03 12:01:45 +0000200 : immutable(false), _hasBeenSet(false), name(name_), description(desc_) {
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000201 if (!conf)
202 conf = Configuration::global();
203 _next = conf->head;
204 conf->head = this;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000205}
206
207VoidParameter::~VoidParameter() {
208}
209
210const char*
211VoidParameter::getName() const {
212 return name;
213}
214
215const char*
216VoidParameter::getDescription() const {
217 return description;
218}
219
220bool VoidParameter::setParam() {
221 return false;
222}
223
224bool VoidParameter::isBool() const {
225 return false;
226}
227
228void
229VoidParameter::setImmutable() {
230 vlog.debug("set immutable %s", getName());
231 immutable = true;
232}
233
Peter Åstrand55855d52005-01-03 12:01:45 +0000234void
235VoidParameter::setHasBeenSet() {
236 _hasBeenSet = true;
237}
238
239bool
240VoidParameter::hasBeenSet() {
241 return _hasBeenSet;
242}
243
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000244// -=- AliasParameter
245
246AliasParameter::AliasParameter(const char* name_, const char* desc_,
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000247 VoidParameter* param_, Configuration* conf)
248 : VoidParameter(name_, desc_, conf), param(param_) {
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000249}
250
251bool
252AliasParameter::setParam(const char* v) {
253 return param->setParam(v);
254}
255
256bool AliasParameter::setParam() {
257 return param->setParam();
258}
259
260char*
261AliasParameter::getDefaultStr() const {
262 return 0;
263}
264
265char* AliasParameter::getValueStr() const {
266 return param->getValueStr();
267}
268
269bool AliasParameter::isBool() const {
270 return param->isBool();
271}
272
Peter Åstrand07350592005-01-03 14:47:42 +0000273void
274AliasParameter::setImmutable() {
275 vlog.debug("set immutable %s (Alias)", getName());
276 param->setImmutable();
277}
278
279
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000280// -=- BoolParameter
281
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000282BoolParameter::BoolParameter(const char* name_, const char* desc_, bool v, Configuration* conf)
283: VoidParameter(name_, desc_, conf), value(v), def_value(v) {
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000284}
285
286bool
287BoolParameter::setParam(const char* v) {
288 if (immutable) return true;
289
290 if (*v == 0 || strcasecmp(v, "1") == 0 || strcasecmp(v, "on") == 0
291 || strcasecmp(v, "true") == 0 || strcasecmp(v, "yes") == 0)
292 value = 1;
293 else if (strcasecmp(v, "0") == 0 || strcasecmp(v, "off") == 0
294 || strcasecmp(v, "false") == 0 || strcasecmp(v, "no") == 0)
295 value = 0;
296 else {
297 vlog.error("Bool parameter %s: invalid value '%s'", getName(), v);
298 return false;
299 }
300
301 vlog.debug("set %s(Bool) to %s(%d)", getName(), v, value);
302 return true;
303}
304
305bool BoolParameter::setParam() {
306 setParam(true);
307 return true;
308}
309
310void BoolParameter::setParam(bool b) {
311 if (immutable) return;
312 value = b;
313 vlog.debug("set %s(Bool) to %d", getName(), value);
314}
315
316char*
317BoolParameter::getDefaultStr() const {
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000318 return strDup(def_value ? "1" : "0");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000319}
320
321char* BoolParameter::getValueStr() const {
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000322 return strDup(value ? "1" : "0");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000323}
324
325bool BoolParameter::isBool() const {
326 return true;
327}
328
329BoolParameter::operator bool() const {
330 return value;
331}
332
333// -=- IntParameter
334
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000335IntParameter::IntParameter(const char* name_, const char* desc_, int v,
336 int minValue_, int maxValue_, Configuration* conf)
337 : VoidParameter(name_, desc_, conf), value(v), def_value(v),
338 minValue(minValue_), maxValue(maxValue_)
339{
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000340}
341
342bool
343IntParameter::setParam(const char* v) {
344 if (immutable) return true;
345 vlog.debug("set %s(Int) to %s", getName(), v);
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000346 int i = atoi(v);
347 if (i < minValue || i > maxValue)
348 return false;
349 value = i;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000350 return true;
351}
352
353bool
354IntParameter::setParam(int v) {
355 if (immutable) return true;
356 vlog.debug("set %s(Int) to %d", getName(), v);
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000357 if (v < minValue || v > maxValue)
358 return false;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000359 value = v;
360 return true;
361}
362
363char*
364IntParameter::getDefaultStr() const {
365 char* result = new char[16];
366 sprintf(result, "%d", def_value);
367 return result;
368}
369
370char* IntParameter::getValueStr() const {
371 char* result = new char[16];
372 sprintf(result, "%d", value);
373 return result;
374}
375
376IntParameter::operator int() const {
377 return value;
378}
379
380// -=- StringParameter
381
382StringParameter::StringParameter(const char* name_, const char* desc_,
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000383 const char* v, Configuration* conf)
384 : VoidParameter(name_, desc_, conf), value(strDup(v)), def_value(v)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000385{
386 if (!v) {
387 fprintf(stderr,"Default value <null> for %s not allowed\n",name_);
388 throw rfb::Exception("Default value <null> not allowed");
389 }
390}
391
392StringParameter::~StringParameter() {
393 strFree(value);
394}
395
396bool StringParameter::setParam(const char* v) {
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000397 LOCK_CONFIG;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000398 if (immutable) return true;
399 if (!v)
400 throw rfb::Exception("setParam(<null>) not allowed");
401 vlog.debug("set %s(String) to %s", getName(), v);
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000402 CharArray oldValue(value);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000403 value = strDup(v);
404 return value != 0;
405}
406
407char* StringParameter::getDefaultStr() const {
408 return strDup(def_value);
409}
410
411char* StringParameter::getValueStr() const {
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000412 LOCK_CONFIG;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000413 return strDup(value);
414}
415
416// -=- BinaryParameter
417
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000418BinaryParameter::BinaryParameter(const char* name_, const char* desc_, const void* v, int l, Configuration* conf)
419: VoidParameter(name_, desc_, conf), value(0), length(0), def_value((char*)v), def_length(l) {
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000420 if (l) {
421 value = new char[l];
422 length = l;
423 memcpy(value, v, l);
424 }
425}
426BinaryParameter::~BinaryParameter() {
427 if (value)
428 delete [] value;
429}
430
431bool BinaryParameter::setParam(const char* v) {
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000432 LOCK_CONFIG;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000433 if (immutable) return true;
434 vlog.debug("set %s(Binary) to %s", getName(), v);
435 return rdr::HexInStream::hexStrToBin(v, &value, &length);
436}
437
438void BinaryParameter::setParam(const void* v, int len) {
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000439 LOCK_CONFIG;
Peter Åstrand07350592005-01-03 14:47:42 +0000440 if (immutable) return;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000441 vlog.debug("set %s(Binary)", getName());
442 delete [] value; value = 0;
443 if (len) {
444 value = new char[len];
445 length = len;
446 memcpy(value, v, len);
447 }
448}
449
450char* BinaryParameter::getDefaultStr() const {
451 return rdr::HexOutStream::binToHexStr(def_value, def_length);
452}
453
454char* BinaryParameter::getValueStr() const {
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000455 LOCK_CONFIG;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000456 return rdr::HexOutStream::binToHexStr(value, length);
457}
458
459void BinaryParameter::getData(void** data_, int* length_) const {
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000460 LOCK_CONFIG;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000461 if (length_) *length_ = length;
462 if (data_) {
463 *data_ = new char[length];
464 memcpy(*data_, value, length);
465 }
466}