Panda3D
Loading...
Searching...
No Matches
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"
20#include "mutexImpl.h"
21
22using std::string;
23
24/**
25 * Use the ConfigPage::make_declaration() interface to create a new
26 * declaration.
27 */
28ConfigDeclaration::
29ConfigDeclaration(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 */
45ConfigDeclaration::
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 */
57set_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 */
89set_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 */
102set_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 */
115set_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 */
128set_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 */
141get_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();
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 */
174void ConfigDeclaration::
175output(std::ostream &out) const {
176 out << get_variable()->get_name() << " " << get_string_value();
177}
178
179/**
180 *
181 */
182void ConfigDeclaration::
183write(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 */
192void ConfigDeclaration::
193get_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 */
216void ConfigDeclaration::
217check_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 */
260void ConfigDeclaration::
261check_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 */
317void ConfigDeclaration::
318check_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 */
372void ConfigDeclaration::
373check_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 */
407extract_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 */
434downcase(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}
void set_double_word(size_t n, double value)
Changes the nth word to the indicated value without affecting the other words.
void set_int_word(size_t n, int value)
Changes the nth word to the indicated value without affecting the other words.
void set_int64_word(size_t n, int64_t value)
Changes the nth word to the indicated value without affecting the other words.
const std::string & get_string_value() const
Returns the value assigned to this variable.
void set_string_word(size_t n, const std::string &value)
Changes the nth word to the indicated value without affecting the other words.
Filename get_filename_value() const
Interprets the string value as a filename and returns it, with any variables expanded.
static std::string downcase(const std::string &s)
Returns the input string with all uppercase letters converted to lowercase.
void set_bool_word(size_t n, bool value)
Changes the nth word to the indicated value without affecting the other words.
static size_t extract_words(const std::string &str, vector_string &words)
Divides the string into a number of words according to whitespace.
get_variable
Returns the variable that this declaration names.
A page of ConfigDeclarations that may be loaded or unloaded.
Definition configPage.h:30
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
get_name
Returns the name of the page.
Definition configPage.h:43
The internal definition of a ConfigVariable.
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...
static std::string expand_string(const std::string &str)
Reads the string, looking for environment variable names marked by a $.
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...
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
bool make_true_case()
On a case-insensitive operating system (e.g.
std::string get_dirname() const
Returns the directory part of the filename.
Definition filename.I:358
A fake mutex implementation for single-threaded applications that don't need any synchronization cont...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.