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  */
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();
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  */
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  */
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 }
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:39
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
bool make_true_case()
On a case-insensitive operating system (e.g.
Definition: filename.cxx:1053
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
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.