Panda3D

configDeclaration.cxx
Go to the documentation of this file.
1 /**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file configDeclaration.cxx
10 * @author drose
11 * @date 2004-10-15
12 */
13
14 #include "configDeclaration.h"
15 #include "configVariableCore.h"
16 #include "config_prc.h"
17 #include "pstrtod.h"
18 #include "string_utils.h"
19 #include "executionEnvironment.h"
20 #include "mutexImpl.h"
21
22 using std::string;
23
24 /**
25 * Use the ConfigPage::make_declaration() interface to create a new
26 * declaration.
27 */
28 ConfigDeclaration::
29 ConfigDeclaration(ConfigPage *page, ConfigVariableCore *variable,
30 const string &string_value, int decl_seq) :
31 _page(page),
32 _variable(variable),
33 _string_value(string_value),
34 _decl_seq(decl_seq),
35 _got_words(false)
36 {
37 if (!_page->is_special()) {
38 _variable->add_declaration(this);
39 }
40 }
41
42 /**
43 * Use the ConfigPage::delete_declaration() interface to delete a declaration.
44 */
45 ConfigDeclaration::
46 ~ConfigDeclaration() {
47 if (!_page->is_special()) {
48 _variable->remove_declaration(this);
49 }
50 }
51
52 /**
53 * Changes the nth word to the indicated value without affecting the other
54 * words.
55 */
56 void ConfigDeclaration::
57 set_string_word(size_t n, const string &value) {
58 if (!_got_words) {
59 get_words();
60 }
61
62 while (n >= _words.size()) {
63 Word w;
64 w._flags = 0;
65 _words.push_back(w);
66 }
67
68 _words[n]._str = value;
69 _words[n]._flags = 0;
70
71 // Now recompose the overall string value.
72 Words::const_iterator wi = _words.begin();
73 _string_value = (*wi)._str;
74 ++wi;
75
76 while (wi != _words.end()) {
77 _string_value += " ";
78 _string_value += (*wi)._str;
79 ++wi;
80 }
81 invalidate_cache();
82 }
83
84 /**
85 * Changes the nth word to the indicated value without affecting the other
86 * words.
87 */
88 void ConfigDeclaration::
89 set_bool_word(size_t n, bool value) {
90 set_string_word(n, value ? "1" : "0");
91
92 _words[n]._flags |= (F_checked_bool | F_valid_bool);
93 _words[n]._bool = value;
94 invalidate_cache();
95 }
96
97 /**
98 * Changes the nth word to the indicated value without affecting the other
99 * words.
100 */
101 void ConfigDeclaration::
102 set_int_word(size_t n, int value) {
103 set_string_word(n, format_string(value));
104
105 _words[n]._flags |= (F_checked_int | F_valid_int);
106 _words[n]._int = value;
107 invalidate_cache();
108 }
109
110 /**
111 * Changes the nth word to the indicated value without affecting the other
112 * words.
113 */
114 void ConfigDeclaration::
115 set_int64_word(size_t n, int64_t value) {
116 set_string_word(n, format_string(value));
117
118 _words[n]._flags |= (F_checked_int64 | F_valid_int64);
119 _words[n]._int_64 = value;
120 invalidate_cache();
121 }
122
123 /**
124 * Changes the nth word to the indicated value without affecting the other
125 * words.
126 */
127 void ConfigDeclaration::
128 set_double_word(size_t n, double value) {
129 set_string_word(n, format_string(value));
130
131 _words[n]._flags |= (F_checked_double | F_valid_double);
132 _words[n]._double = value;
133 invalidate_cache();
134 }
135
136 /**
137 * Interprets the string value as a filename and returns it, with any
138 * variables expanded.
139 */
140 Filename ConfigDeclaration::
141 get_filename_value() const {
142 // Since we are about to set THIS_PRC_DIR globally, we need to ensure that
143 // no two threads call this method at the same time.
144 // NB. MSVC doesn't guarantee that this mutex is initialized in a
145 // thread-safe manner. But chances are that the first time this is called
146 // is at static init time, when there is no risk of data races.
147 static MutexImpl lock;
148
149 string str = _string_value;
150
151 // Are there any variables to be expanded?
152 if (str.find('$') != string::npos) {
153 Filename page_filename(_page->get_name());
154 Filename page_dirname = page_filename.get_dirname();
155
156 lock.lock();
157 ExecutionEnvironment::shadow_environment_variable("THIS_PRC_DIR", page_dirname.to_os_specific());
158 str = ExecutionEnvironment::expand_string(str);
159 ExecutionEnvironment::clear_shadow("THIS_PRC_DIR");
160 lock.unlock();
161 }
162
163 Filename fn;
164 if (!str.empty()) {
165 fn = Filename::from_os_specific(str);
166 fn.make_true_case();
167 }
168 return fn;
169 }
170
171 /**
172 *
173 */
174 void ConfigDeclaration::
175 output(std::ostream &out) const {
176 out << get_variable()->get_name() << " " << get_string_value();
177 }
178
179 /**
180 *
181 */
182 void ConfigDeclaration::
183 write(std::ostream &out) const {
184 out << get_variable()->get_name() << " " << get_string_value();
185 // if (!get_variable()->is_used()) { out << " (not used)"; }
186 out << "\n";
187 }
188
189 /**
190 * Separates the string value into words.
191 */
192 void ConfigDeclaration::
193 get_words() {
194 if (!_got_words) {
195 _words.clear();
196 vector_string words;
197 extract_words(_string_value, words);
198 _words.reserve(words.size());
199
200 for (vector_string::const_iterator wi = words.begin();
201 wi != words.end();
202 ++wi) {
203 Word w;
204 w._str = (*wi);
205 w._flags = 0;
206 _words.push_back(w);
207 }
208
209 _got_words = true;
210 }
211 }
212
213 /**
214 * Checks whether the nth word can be interpreted as a boolean value.
215 */
216 void ConfigDeclaration::
217 check_bool_word(size_t n) {
218 if (!_got_words) {
219 get_words();
220 }
221
222 if (n < _words.size()) {
223 Word &word = _words[n];
224 if ((word._flags & F_checked_bool) == 0) {
225 word._flags |= F_checked_bool;
226
227 string s = downcase(word._str);
228 if (s.empty()) {
229 word._bool = false;
230
231 } else if (s == "#t" || s == "1" || s[0] == 't') {
232 word._bool = true;
233
234 } else if (s == "#f" || s == "0" || s[0] == 'f') {
235 word._bool = false;
236
237 } else {
238 // Not a recognized bool value.
239 check_double_word(n);
240 if ((word._flags & F_checked_double) != 0) {
241 word._bool = (word._double != 0.0);
242 } else {
243 word._bool = false;
244 }
245
246 prc_cat->warning()
247 << "Invalid bool value for ConfigVariable "
248 << get_variable()->get_name() << ": " << word._str << "\n";
249 return;
250 }
251
252 word._flags |= F_valid_bool;
253 }
254 }
255 }
256
257 /**
258 * Checks whether the nth word can be interpreted as an integer value.
259 */
260 void ConfigDeclaration::
261 check_int_word(size_t n) {
262 if (!_got_words) {
263 get_words();
264 }
265
266 if (n < _words.size()) {
267 Word &word = _words[n];
268 if ((word._flags & F_checked_int) == 0) {
269 word._flags |= F_checked_int;
270
271 // We scan the word by hand, rather than relying on strtol(), so we can
272 // check for overflow of the 32-bit value.
273 word._int = 0;
274 bool overflow = false;
275
276 string::const_iterator pi = word._str.begin();
277 if (pi != word._str.end() && (*pi) == '-') {
278 ++pi;
279 // Negative number.
280 while (pi != word._str.end() && isdigit(*pi)) {
281 int next = word._int * 10 - (int)((*pi) - '0');
282 if ((int)(next / 10) != word._int) {
283 // Overflow.
284 overflow = true;
285 }
286 word._int = next;
287 ++pi;
288 }
289
290 } else {
291 // Positive number.
292 while (pi != word._str.end() && isdigit(*pi)) {
293 int next = word._int * 10 + (int)((*pi) - '0');
294 if ((int)(next / 10) != word._int) {
295 // Overflow.
296 overflow = true;
297 }
298 word._int = next;
299 ++pi;
300 }
301 }
302
303 if (pi == word._str.end() && !overflow) {
304 word._flags |= F_valid_int;
305 } else {
306 prc_cat->warning()
307 << "Invalid integer value for ConfigVariable "
308 << get_variable()->get_name() << ": " << word._str << "\n";
309 }
310 }
311 }
312 }
313
314 /**
315 * Checks whether the nth word can be interpreted as an integer value.
316 */
317 void ConfigDeclaration::
318 check_int64_word(size_t n) {
319 if (!_got_words) {
320 get_words();
321 }
322
323 if (n < _words.size()) {
324 Word &word = _words[n];
325 if ((word._flags & F_checked_int64) == 0) {
326 word._flags |= F_checked_int64;
327
328 word._int_64 = 0;
329 bool overflow = false;
330
331 string::const_iterator pi = word._str.begin();
332 if (pi != word._str.end() && (*pi) == '-') {
333 ++pi;
334 // Negative number.
335 while (pi != word._str.end() && isdigit(*pi)) {
336 int64_t next = word._int_64 * 10 - (int)((*pi) - '0');
337 if ((int64_t)(next / 10) != word._int_64) {
338 // Overflow.
339 overflow = true;
340 }
341 word._int_64 = next;
342 ++pi;
343 }
344
345 } else {
346 // Positive number.
347 while (pi != word._str.end() && isdigit(*pi)) {
348 int64_t next = word._int_64 * 10 + (int)((*pi) - '0');
349 if ((int64_t)(next / 10) != word._int_64) {
350 // Overflow.
351 overflow = true;
352 }
353 word._int_64 = next;
354 ++pi;
355 }
356 }
357
358 if (pi == word._str.end() && !overflow) {
359 word._flags |= F_valid_int64;
360 } else {
361 prc_cat->warning()
362 << "Invalid int64 value for ConfigVariable "
363 << get_variable()->get_name() << ": " << word._str << "\n";
364 }
365 }
366 }
367 }
368
369 /**
370 * Checks whether the nth word can be interpreted as a floating-point value.
371 */
372 void ConfigDeclaration::
373 check_double_word(size_t n) {
374 if (!_got_words) {
375 get_words();
376 }
377
378 if (n < _words.size()) {
379 Word &word = _words[n];
380 if ((word._flags & F_checked_double) == 0) {
381 word._flags |= F_checked_double;
382
383 const char *nptr = word._str.c_str();
384 char *endptr;
385 word._double = pstrtod(nptr, &endptr);
386
387 if (*endptr == '\0') {
388 word._flags |= F_valid_double;
389 } else {
390 prc_cat->warning()
391 << "Invalid floating-point value for ConfigVariable "
392 << get_variable()->get_name() << ": " << word._str << "\n";
393 }
394 }
395 }
396 }
397
398 /**
399 * Divides the string into a number of words according to whitespace. The
400 * words vector should be cleared by the user before calling; otherwise, the
401 * list of words in the string will be appended to the end of whatever was
402 * there before.
403 *
404 * The return value is the number of words extracted.
405 */
406 size_t ConfigDeclaration::
407 extract_words(const string &str, vector_string &words) {
408 size_t num_words = 0;
409
410 size_t pos = 0;
411 while (pos < str.length() && isspace((unsigned int)str[pos])) {
412 pos++;
413 }
414 while (pos < str.length()) {
415 size_t word_start = pos;
416 while (pos < str.length() && !isspace((unsigned int)str[pos])) {
417 pos++;
418 }
419 words.push_back(str.substr(word_start, pos - word_start));
420 num_words++;
421
422 while (pos < str.length() && isspace((unsigned int)str[pos])) {
423 pos++;
424 }
425 }
426
427 return num_words;
428 }
429
430 /**
431 * Returns the input string with all uppercase letters converted to lowercase.
432 */
433 string ConfigDeclaration::
434 downcase(const string &s) {
435 string result;
436 result.reserve(s.size());
437 string::const_iterator p;
438 for (p = s.begin(); p != s.end(); ++p) {
439 result += (char)tolower(*p);
440 }
441 return result;
442 }
ConfigDeclaration::set_double_word
void set_double_word(size_t n, double value)
Changes the nth word to the indicated value without affecting the other words.
Definition: configDeclaration.cxx:128
ConfigDeclaration::set_int_word
void set_int_word(size_t n, int value)
Changes the nth word to the indicated value without affecting the other words.
Definition: configDeclaration.cxx:102
ConfigDeclaration::set_int64_word
void set_int64_word(size_t n, int64_t value)
Changes the nth word to the indicated value without affecting the other words.
Definition: configDeclaration.cxx:115
ConfigDeclaration::get_string_value
const std::string & get_string_value() const
Returns the value assigned to this variable.
Definition: configDeclaration.I:53
ConfigDeclaration::set_string_word
void set_string_word(size_t n, const std::string &value)
Changes the nth word to the indicated value without affecting the other words.
Definition: configDeclaration.cxx:57
ConfigDeclaration::get_filename_value
Filename get_filename_value() const
Interprets the string value as a filename and returns it, with any variables expanded.
Definition: configDeclaration.cxx:141
ConfigDeclaration::downcase
static std::string downcase(const std::string &s)
Returns the input string with all uppercase letters converted to lowercase.
Definition: configDeclaration.cxx:434
ConfigDeclaration::set_bool_word
void set_bool_word(size_t n, bool value)
Changes the nth word to the indicated value without affecting the other words.
Definition: configDeclaration.cxx:89
ConfigDeclaration::extract_words
static size_t extract_words(const std::string &str, vector_string &words)
Divides the string into a number of words according to whitespace.
Definition: configDeclaration.cxx:407
ConfigDeclaration::get_variable
get_variable
Returns the variable that this declaration names.
Definition: configDeclaration.h:47
ConfigPage
A page of ConfigDeclarations that may be loaded or unloaded.
Definition: configPage.h:30
ConfigPage::is_special
is_special
Returns true if this is the special "default" or "local" page, or false if it is an ordinary page,...
Definition: configPage.h:47
ConfigPage::get_name
get_name
Returns the name of the page.
Definition: configPage.h:43
ConfigVariableCore
The internal definition of a ConfigVariable.
Definition: configVariableCore.h:34
ExecutionEnvironment::clear_shadow
static void clear_shadow(const std::string &var)
Removes a value set by a previous call to shadow_environment_variable(), and lets the actual value of...
Definition: executionEnvironment.I:55
ExecutionEnvironment::expand_string
static std::string expand_string(const std::string &str)
Reads the string, looking for environment variable names marked by a $.
Definition: executionEnvironment.cxx:112
ExecutionEnvironment::shadow_environment_variable
static void shadow_environment_variable(const std::string &var, const std::string &value)
Changes the apparent definition of the indicated environment variable by masking it within this class...
Definition: executionEnvironment.I:46
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
Filename::to_os_specific
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1123
Filename::make_true_case
bool make_true_case()
On a case-insensitive operating system (e.g.
Definition: filename.cxx:1053
Filename::from_os_specific
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition: filename.cxx:328
Filename::get_dirname
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
MutexDummyImpl
A fake mutex implementation for single-threaded applications that don't need any synchronization cont...
Definition: mutexDummyImpl.h:24
configDeclaration.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
configVariableCore.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_prc.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
executionEnvironment.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
mutexImpl.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pstrtod
double pstrtod(const char *nptr, char **endptr)
This function re-implements strtod, to avoid the problems that occur when the LC_NUMERIC locale gets ...
Definition: pstrtod.cxx:31
pstrtod.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
string_utils.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.