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  */
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  */
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  */
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  */
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  */
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  */
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());
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 }
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
get_name
Returns the name of the page.
Definition: configPage.h:43
const std::string & get_string_value() const
Returns the value assigned to this variable.
static size_t extract_words(const std::string &str, vector_string &words)
Divides the string into a number of words according to whitespace.
The internal definition of a ConfigVariable.
void set_int64_word(size_t n, int64_t value)
Changes the nth word to the indicated value without affecting the other words.
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
bool make_true_case()
On a case-insensitive operating system (e.g.
Definition: filename.cxx:1053
void set_bool_word(size_t n, bool value)
Changes the nth word to the indicated value without affecting the other words.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_variable
Returns the variable that this declaration names.
Filename get_filename_value() const
Interprets the string value as a filename and returns it, with any variables expanded.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
static std::string expand_string(const std::string &str)
Reads the string, looking for environment variable names marked by a $.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_double_word(size_t n, double value)
Changes the nth word to the indicated value without affecting the other words.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A page of ConfigDeclarations that may be loaded or unloaded.
Definition: configPage.h:30
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...
void set_string_word(size_t n, const std::string &value)
Changes the nth word to the indicated value without affecting the other words.
A fake mutex implementation for single-threaded applications that don't need any synchronization cont...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_int_word(size_t n, int value)
Changes the nth word to the indicated value without affecting the other words.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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 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
static std::string downcase(const std::string &s)
Returns the input string with all uppercase letters converted to lowercase.