Panda3D
 All Classes Functions Variables Enumerations
executionEnvironment.cxx
1 // Filename: executionEnvironment.cxx
2 // Created by: drose (15May00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "executionEnvironment.h"
16 #include "pandaVersion.h"
17 
18 #include <assert.h>
19 #include <errno.h>
20 #include <stdio.h> // for perror
21 
22 #ifdef __APPLE__
23 #include <sys/param.h> // for realpath
24 #endif // __APPLE__
25 
26 #ifdef WIN32_VC
27 // Windows requires this for getcwd().
28 #include <direct.h>
29 #define getcwd _getcwd
30 
31 // And this is for GetModuleFileName().
32 #include <windows.h>
33 
34 // And this is for CommandLineToArgvW.
35 #include <shellapi.h>
36 
37 // SHGetSpecialFolderPath()
38 #include <shlobj.h>
39 #endif
40 
41 #ifdef __APPLE__
42 // This is for _NSGetExecutablePath() and _NSGetEnviron().
43 #include <mach-o/dyld.h>
44 #ifndef BUILD_IPHONE
45 #include <crt_externs.h> // For some reason, not in the IPhone SDK.
46 #endif
47 #define environ (*_NSGetEnviron())
48 #endif
49 
50 #ifdef IS_LINUX
51 // extern char **environ is defined here:
52 #include <unistd.h>
53 #endif
54 
55 #ifdef IS_FREEBSD
56 extern char **environ;
57 
58 // This is for sysctl.
59 #include <sys/types.h>
60 #include <sys/sysctl.h>
61 #endif
62 
63 #if defined(IS_LINUX) || defined(IS_FREEBSD)
64 // For link_map and dlinfo.
65 #include <link.h>
66 #include <dlfcn.h>
67 #endif
68 
69 #ifdef HAVE_PYTHON
70 #include "Python.h"
71 #endif
72 
73 // We define the symbol PREREAD_ENVIRONMENT if we cannot rely on
74 // getenv() to read environment variables at static init time. In
75 // this case, we must read all of the environment variables directly
76 // and cache them locally.
77 
78 #ifndef STATIC_INIT_GETENV
79 #define PREREAD_ENVIRONMENT
80 #endif
81 
82 // We define the symbol HAVE_GLOBAL_ARGV if we have global variables
83 // named GLOBAL_ARGC/GLOBAL_ARGV that we can read at static init time
84 // to determine our command-line arguments.
85 
86 #if !defined(WIN32_VC) && defined(HAVE_GLOBAL_ARGV) && defined(PROTOTYPE_GLOBAL_ARGV)
87 extern char **GLOBAL_ARGV;
88 extern int GLOBAL_ARGC;
89 #endif
90 
91 // Linux with GNU libc does have global argv/argc variables, but we
92 // can't safely access them at stat init time--at least, not in libc5.
93 // (It does seem to work with glibc2, however.)
94 
95 ExecutionEnvironment *ExecutionEnvironment::_global_ptr = NULL;
96 
97 ////////////////////////////////////////////////////////////////////
98 // Function: ExecutionEnvironment::Constructor
99 // Access: Private
100 // Description: You shouldn't need to construct one of these; there's
101 // only one and it constructs itself.
102 ////////////////////////////////////////////////////////////////////
103 ExecutionEnvironment::
104 ExecutionEnvironment() {
105  read_environment_variables();
106  read_args();
107 }
108 
109 ////////////////////////////////////////////////////////////////////
110 // Function: ExecutionEnviroment::expand_string
111 // Access: Public, Static
112 // Description: Reads the string, looking for environment variable
113 // names marked by a $. Expands all such variable
114 // names. A repeated dollar sign ($$) is mapped to a
115 // single dollar sign.
116 //
117 // Returns the expanded string.
118 ////////////////////////////////////////////////////////////////////
120 expand_string(const string &str) {
121  string result;
122 
123  size_t last = 0;
124  size_t dollar = str.find('$');
125  while (dollar != string::npos && dollar + 1 < str.length()) {
126  size_t start = dollar + 1;
127 
128  if (str[start] == '$') {
129  // A double dollar sign maps to a single dollar sign.
130  result += str.substr(last, start - last);
131  last = start + 1;
132 
133  } else {
134  string varname;
135  size_t end = start;
136 
137  if (str[start] == '{') {
138  // Curly braces delimit the variable name explicitly.
139  end = str.find('}', start + 1);
140  if (end != string::npos) {
141  varname = str.substr(start + 1, end - (start + 1));
142  end++;
143  }
144  }
145 
146  if (end == start) {
147  // Scan for the end of the variable name.
148  while (end < str.length() && (isalnum(str[end]) || str[end] == '_')) {
149  end++;
150  }
151  varname = str.substr(start, end - start);
152  }
153 
154  string subst =
155  result += str.substr(last, dollar - last);
156  result += get_environment_variable(varname);
157  last = end;
158  }
159 
160  dollar = str.find('$', last);
161  }
162 
163  result += str.substr(last);
164 
165  return result;
166 }
167 
168 ////////////////////////////////////////////////////////////////////
169 // Function: ExecutionEnviroment::get_cwd
170 // Access: Public, Static
171 // Description: Returns the name of the current working directory.
172 ////////////////////////////////////////////////////////////////////
175 #ifdef WIN32_VC
176  // getcwd() requires us to allocate a dynamic buffer and grow it on
177  // demand.
178  static size_t bufsize = 1024;
179  static wchar_t *buffer = NULL;
180 
181  if (buffer == (wchar_t *)NULL) {
182  buffer = new wchar_t[bufsize];
183  }
184 
185  while (_wgetcwd(buffer, bufsize) == (wchar_t *)NULL) {
186  if (errno != ERANGE) {
187  perror("getcwd");
188  return string();
189  }
190  delete[] buffer;
191  bufsize = bufsize * 2;
192  buffer = new wchar_t[bufsize];
193  assert(buffer != (wchar_t *)NULL);
194  }
195 
197  cwd.make_true_case();
198  return cwd;
199 #else // WIN32_VC
200  // getcwd() requires us to allocate a dynamic buffer and grow it on
201  // demand.
202  static size_t bufsize = 1024;
203  static char *buffer = NULL;
204 
205  if (buffer == (char *)NULL) {
206  buffer = new char[bufsize];
207  }
208 
209  while (getcwd(buffer, bufsize) == (char *)NULL) {
210  if (errno != ERANGE) {
211  perror("getcwd");
212  return string();
213  }
214  delete[] buffer;
215  bufsize = bufsize * 2;
216  buffer = new char[bufsize];
217  assert(buffer != (char *)NULL);
218  }
219 
220  Filename cwd = Filename::from_os_specific(buffer);
221  cwd.make_true_case();
222  return cwd;
223 #endif // WIN32_VC
224 }
225 
226 ////////////////////////////////////////////////////////////////////
227 // Function: ExecutionEnvironment::ns_has_environment_variable
228 // Access: Private
229 // Description: Returns true if the indicated environment variable
230 // is defined. The nonstatic implementation.
231 ////////////////////////////////////////////////////////////////////
232 bool ExecutionEnvironment::
233 ns_has_environment_variable(const string &var) const {
234 #ifdef PREREAD_ENVIRONMENT
235  return _variables.count(var) != 0;
236 #else
237  return getenv(var.c_str()) != (char *)NULL;
238 #endif
239 }
240 
241 ////////////////////////////////////////////////////////////////////
242 // Function: ExecutionEnvironment::ns_get_environment_variable
243 // Access: Private
244 // Description: Returns the definition of the indicated environment
245 // variable, or the empty string if the variable is
246 // undefined. The nonstatic implementation.
247 ////////////////////////////////////////////////////////////////////
248 string ExecutionEnvironment::
249 ns_get_environment_variable(const string &var) const {
250  EnvironmentVariables::const_iterator evi;
251  evi = _variables.find(var);
252  if (evi != _variables.end()) {
253  return (*evi).second;
254  }
255 
256  // Some special case variables. We virtually stuff these values
257  // into the Panda environment, shadowing whatever values they have
258  // in the true environment, so they can be used in config files.
259  if (var == "HOME") {
261  } else if (var == "TEMP") {
263  } else if (var == "USER_APPDATA") {
265  } else if (var == "COMMON_APPDATA") {
267  } else if (var == "MAIN_DIR") {
268 #ifdef HAVE_PYTHON
269  // If we're running from Python code, read out sys.argv.
270  if (!ns_has_environment_variable("PANDA_INCOMPATIBLE_PYTHON") && Py_IsInitialized()) {
271  // Since we might have gotten to this point from a function call
272  // marked BLOCKING, which releases the Python thread state, we
273  // have to temporarily re-establish our thread state in the
274  // Python interpreter.
275 #ifdef WITH_THREAD
276  PyGILState_STATE state = PyGILState_Ensure();
277 #endif
278 
279  Filename main_dir;
280  PyObject *obj = PySys_GetObject((char*) "argv"); // borrowed reference
281  if (obj != NULL && PyList_Check(obj)) {
282  PyObject *item = PyList_GetItem(obj, 0); // borrowed reference
283  if (item != NULL) {
284  if (PyUnicode_Check(item)) {
285  Py_ssize_t size = PyUnicode_GetSize(item);
286  wchar_t *data = new wchar_t[size + 1];
287 #if PY_MAJOR_VERSION >= 3
288  if (PyUnicode_AsWideChar(item, data, size) != -1) {
289 #else
290  if (PyUnicode_AsWideChar((PyUnicodeObject*) item, data, size) != -1) {
291 #endif
292  wstring wstr (data, size);
293  main_dir = Filename::from_os_specific_w(wstr);
294  }
295  delete data;
296  }
297 #if PY_MAJOR_VERSION < 3
298  else if (PyString_Check(item)) {
299  char *str = PyString_AsString(item);
300  if (str != (char *)NULL) {
301  main_dir = Filename::from_os_specific(str);
302  }
303  }
304 #endif
305  }
306  }
307 
308 #ifdef WITH_THREAD
309  PyGILState_Release(state);
310 #endif
311 
312  if (main_dir.empty()) {
313  // We must be running in the Python interpreter directly, so return the CWD.
314  return get_cwd().to_os_specific();
315  }
316  main_dir.make_absolute();
317  return Filename(main_dir.get_dirname()).to_os_specific();
318  }
319 #endif
320 
321  // Otherwise, Return the binary name's parent directory.
322  if (!_binary_name.empty()) {
323  Filename main_dir (_binary_name);
324  main_dir.make_absolute();
325  return Filename(main_dir.get_dirname()).to_os_specific();
326  }
327  }
328 
329 #ifndef PREREAD_ENVIRONMENT
330  const char *def = getenv(var.c_str());
331  if (def != (char *)NULL) {
332  return def;
333  }
334 #endif
335 
336 #ifdef _WIN32
337  // On Windows only, we also simulate several standard folder names
338  // as "environment" variables. I know we're supposed to be using
339  // KnownFolderID's these days, but those calls aren't compatible
340  // with XP, so we'll continue to use SHGetSpecialFolderPath() until
341  // we're forced out of it.
342 
343  static struct { int id; const char *name; } csidl_table[] = {
344  { CSIDL_ADMINTOOLS, "ADMINTOOLS" },
345  { CSIDL_ALTSTARTUP, "ALTSTARTUP" },
346  { CSIDL_APPDATA, "APPDATA" },
347  { CSIDL_BITBUCKET, "BITBUCKET" },
348  { CSIDL_CDBURN_AREA, "CDBURN_AREA" },
349  { CSIDL_COMMON_ADMINTOOLS, "COMMON_ADMINTOOLS" },
350  { CSIDL_COMMON_ALTSTARTUP, "COMMON_ALTSTARTUP" },
351  { CSIDL_COMMON_APPDATA, "COMMON_APPDATA" },
352  { CSIDL_COMMON_DESKTOPDIRECTORY, "COMMON_DESKTOPDIRECTORY" },
353  { CSIDL_COMMON_DOCUMENTS, "COMMON_DOCUMENTS" },
354  { CSIDL_COMMON_FAVORITES, "COMMON_FAVORITES" },
355  { CSIDL_COMMON_MUSIC, "COMMON_MUSIC" },
356  { CSIDL_COMMON_OEM_LINKS, "COMMON_OEM_LINKS" },
357  { CSIDL_COMMON_PICTURES, "COMMON_PICTURES" },
358  { CSIDL_COMMON_PROGRAMS, "COMMON_PROGRAMS" },
359  { CSIDL_COMMON_STARTMENU, "COMMON_STARTMENU" },
360  { CSIDL_COMMON_STARTUP, "COMMON_STARTUP" },
361  { CSIDL_COMMON_TEMPLATES, "COMMON_TEMPLATES" },
362  { CSIDL_COMMON_VIDEO, "COMMON_VIDEO" },
363  { CSIDL_COMPUTERSNEARME, "COMPUTERSNEARME" },
364  { CSIDL_CONNECTIONS, "CONNECTIONS" },
365  { CSIDL_CONTROLS, "CONTROLS" },
366  { CSIDL_COOKIES, "COOKIES" },
367  { CSIDL_DESKTOP, "DESKTOP" },
368  { CSIDL_DESKTOPDIRECTORY, "DESKTOPDIRECTORY" },
369  { CSIDL_DRIVES, "DRIVES" },
370  { CSIDL_FAVORITES, "FAVORITES" },
371  { CSIDL_FONTS, "FONTS" },
372  { CSIDL_HISTORY, "HISTORY" },
373  { CSIDL_INTERNET, "INTERNET" },
374  { CSIDL_INTERNET_CACHE, "INTERNET_CACHE" },
375  { CSIDL_LOCAL_APPDATA, "LOCAL_APPDATA" },
376  { CSIDL_MYDOCUMENTS, "MYDOCUMENTS" },
377  { CSIDL_MYMUSIC, "MYMUSIC" },
378  { CSIDL_MYPICTURES, "MYPICTURES" },
379  { CSIDL_MYVIDEO, "MYVIDEO" },
380  { CSIDL_NETHOOD, "NETHOOD" },
381  { CSIDL_NETWORK, "NETWORK" },
382  { CSIDL_PERSONAL, "PERSONAL" },
383  { CSIDL_PRINTERS, "PRINTERS" },
384  { CSIDL_PRINTHOOD, "PRINTHOOD" },
385  { CSIDL_PROFILE, "PROFILE" },
386  { CSIDL_PROGRAM_FILES, "PROGRAM_FILES" },
387  { CSIDL_PROGRAM_FILESX86, "PROGRAM_FILESX86" },
388  { CSIDL_PROGRAM_FILES_COMMON, "PROGRAM_FILES_COMMON" },
389  { CSIDL_PROGRAM_FILES_COMMONX86, "PROGRAM_FILES_COMMONX86" },
390  { CSIDL_PROGRAMS, "PROGRAMS" },
391  { CSIDL_RECENT, "RECENT" },
392  { CSIDL_RESOURCES, "RESOURCES" },
393  { CSIDL_RESOURCES_LOCALIZED, "RESOURCES_LOCALIZED" },
394  { CSIDL_SENDTO, "SENDTO" },
395  { CSIDL_STARTMENU, "STARTMENU" },
396  { CSIDL_STARTUP, "STARTUP" },
397  { CSIDL_SYSTEM, "SYSTEM" },
398  { CSIDL_SYSTEMX86, "SYSTEMX86" },
399  { CSIDL_TEMPLATES, "TEMPLATES" },
400  { CSIDL_WINDOWS, "WINDOWS" },
401  { 0, NULL },
402  };
403 
404  for (int i = 0; csidl_table[i].name != NULL; ++i) {
405  if (strcmp(var.c_str(), csidl_table[i].name) == 0) {
406  wchar_t buffer[MAX_PATH];
407  if (SHGetSpecialFolderPathW(NULL, buffer, csidl_table[i].id, true)) {
408  Filename pathname = Filename::from_os_specific_w(buffer);
409  return pathname.to_os_specific();
410  }
411  break;
412  }
413  }
414 
415 #endif // _WIN32
416 
417  return string();
418 }
419 
420 ////////////////////////////////////////////////////////////////////
421 // Function: ExecutionEnvironment::ns_set_environment_variable
422 // Access: Private
423 // Description: Changes the definition of the indicated environment
424 // variable. The nonstatic implementation.
425 ////////////////////////////////////////////////////////////////////
426 void ExecutionEnvironment::
427 ns_set_environment_variable(const string &var, const string &value) {
428  _variables[var] = value;
429  string putstr = var + "=" + value;
430 
431  // putenv() requires us to malloc a new C-style string.
432  char *put = (char *)malloc(putstr.length() + 1);
433  strcpy(put, putstr.c_str());
434  putenv(put);
435 }
436 
437 ////////////////////////////////////////////////////////////////////
438 // Function: ExecutionEnvironment::ns_shadow_environment_variable
439 // Access: Private
440 // Description:
441 ////////////////////////////////////////////////////////////////////
442 void ExecutionEnvironment::
443 ns_shadow_environment_variable(const string &var, const string &value) {
444  _variables[var] = value;
445  string putstr = var + "=" + value;
446 }
447 
448 ////////////////////////////////////////////////////////////////////
449 // Function: ExecutionEnvironment::ns_clear_shadow
450 // Access: Private
451 // Description:
452 ////////////////////////////////////////////////////////////////////
453 void ExecutionEnvironment::
454 ns_clear_shadow(const string &var) {
455  EnvironmentVariables::iterator vi = _variables.find(var);
456  if (vi == _variables.end()) {
457  return;
458  }
459 
460 #ifdef PREREAD_ENVIRONMENT
461  // Now we have to replace the value in the table.
462  const char *def = getenv(var.c_str());
463  if (def != (char *)NULL) {
464  (*vi).second = def;
465  } else {
466  _variables.erase(vi);
467  }
468 #endif // PREREAD_ENVIRONMENT
469 }
470 
471 ////////////////////////////////////////////////////////////////////
472 // Function: ExecutionEnvironment::ns_get_num_args
473 // Access: Private
474 // Description: Returns the number of command-line arguments
475 // available, not counting arg 0, the binary name. The
476 // nonstatic implementation.
477 ////////////////////////////////////////////////////////////////////
478 int ExecutionEnvironment::
479 ns_get_num_args() const {
480  return _args.size();
481 }
482 
483 ////////////////////////////////////////////////////////////////////
484 // Function: ExecutionEnvironment::ns_get_arg
485 // Access: Private
486 // Description: Returns the nth command-line argument. The index n
487 // must be in the range [0 .. get_num_args()). The
488 // first parameter, n == 0, is the first actual
489 // parameter, not the binary name. The nonstatic
490 // implementation.
491 ////////////////////////////////////////////////////////////////////
492 string ExecutionEnvironment::
493 ns_get_arg(int n) const {
494  assert(n >= 0 && n < ns_get_num_args());
495  return _args[n];
496 }
497 
498 ////////////////////////////////////////////////////////////////////
499 // Function: ExecutionEnvironment::ns_get_binary_name
500 // Access: Private
501 // Description: Returns the name of the binary executable that
502 // started this program, if it can be determined. The
503 // nonstatic implementation.
504 ////////////////////////////////////////////////////////////////////
505 string ExecutionEnvironment::
506 ns_get_binary_name() const {
507  if (_binary_name.empty()) {
508  return "unknown";
509  }
510  return _binary_name;
511 }
512 
513 ////////////////////////////////////////////////////////////////////
514 // Function: ExecutionEnvironment::ns_get_dtool_name
515 // Access: Private
516 // Description: Returns the name of the libp3dtool DLL that
517 // is used in this program, if it can be determined. The
518 // nonstatic implementation.
519 ////////////////////////////////////////////////////////////////////
520 string ExecutionEnvironment::
521 ns_get_dtool_name() const {
522  if (_dtool_name.empty()) {
523  return "unknown";
524  }
525  return _dtool_name;
526 }
527 
528 ////////////////////////////////////////////////////////////////////
529 // Function: ExecutionEnvironment::get_ptr
530 // Access: Private, Static
531 // Description: Returns a static pointer that may be used to access
532 // the global ExecutionEnvironment object.
533 ////////////////////////////////////////////////////////////////////
534 ExecutionEnvironment *ExecutionEnvironment::
535 get_ptr() {
536  if (_global_ptr == (ExecutionEnvironment *)NULL) {
537  _global_ptr = new ExecutionEnvironment;
538  }
539  return _global_ptr;
540 }
541 
542 
543 ////////////////////////////////////////////////////////////////////
544 // Function: ExecutionEnvironment::read_environment_variables
545 // Access: Private
546 // Description: Fills up the internal table of existing environment
547 // variables, if we are in PREREAD_ENVIRONMENT mode.
548 // Otherwise, does nothing.
549 ////////////////////////////////////////////////////////////////////
550 void ExecutionEnvironment::
551 read_environment_variables() {
552 #ifdef PREREAD_ENVIRONMENT
553 #if defined(IS_OSX) || defined(IS_FREEBSD) || defined(IS_LINUX)
554  // In the case of Mac, we'll try reading _NSGetEnviron().
555  // In the case of FreeBSD and Linux, use the "environ" variable.
556 
557  char **envp;
558  for (envp = environ; envp && *envp; envp++) {
559  string variable;
560  string value;
561 
562  char *envc;
563  for (envc = *envp; envc && *envc && strncmp(envc, "=", 1) != 0; envc++) {
564  variable += (char) *envc;
565  }
566 
567  if (strncmp(envc, "=", 1) == 0) {
568  for (envc++; envc && *envc; envc++) {
569  value += (char) *envc;
570  }
571  }
572 
573  if (!variable.empty()) {
574  _variables[variable] = value;
575  }
576  }
577 #elif defined(HAVE_PROC_SELF_ENVIRON)
578  // In some cases, we may have a file called /proc/self/environ
579  // that may be read to determine all of our environment variables.
580 
581  pifstream proc("/proc/self/environ");
582  if (proc.fail()) {
583  cerr << "Cannot read /proc/self/environ; environment variables unavailable.\n";
584  return;
585  }
586 
587  int ch = proc.get();
588  while (!proc.eof() && !proc.fail()) {
589  string variable;
590  string value;
591 
592  while (!proc.eof() && !proc.fail() && ch != '=' && ch != '\0') {
593  variable += (char)ch;
594  ch = proc.get();
595  }
596 
597  if (ch == '=') {
598  ch = proc.get();
599  while (!proc.eof() && !proc.fail() && ch != '\0') {
600  value += (char)ch;
601  ch = proc.get();
602  }
603  }
604 
605  if (!variable.empty()) {
606  _variables[variable] = value;
607  }
608  ch = proc.get();
609  }
610 #else
611  cerr << "Warning: environment variables unavailable to dconfig.\n";
612 #endif
613 #endif // PREREAD_ENVIRONMENT
614 }
615 
616 ////////////////////////////////////////////////////////////////////
617 // Function: ExecutionEnvironment::read_args
618 // Access: Private
619 // Description: Reads all the command-line arguments and the name of
620 // the binary file, if possible.
621 ////////////////////////////////////////////////////////////////////
622 void ExecutionEnvironment::
623 read_args() {
624 #ifndef ANDROID
625  // First, we need to fill in _dtool_name. This contains
626  // the full path to the p3dtool library.
627 
628 #ifdef WIN32_VC
629 #ifdef _DEBUG
630  HMODULE dllhandle = GetModuleHandle("libp3dtool_d.dll");
631 #else
632  HMODULE dllhandle = GetModuleHandle("libp3dtool.dll");
633 #endif
634  if (dllhandle != 0) {
635  static const DWORD buffer_size = 1024;
636  wchar_t buffer[buffer_size];
637  DWORD size = GetModuleFileNameW(dllhandle, buffer, buffer_size);
638  if (size != 0) {
639  Filename tmp = Filename::from_os_specific_w(wstring(buffer, size));
640  tmp.make_true_case();
641  _dtool_name = tmp;
642  }
643  }
644 #endif
645 
646 #if defined(__APPLE__)
647  // And on OSX we don't have /proc/self/maps, but some _dyld_* functions.
648 
649  if (_dtool_name.empty()) {
650  uint32_t ic = _dyld_image_count();
651  for (uint32_t i = 0; i < ic; ++i) {
652  const char *buffer = _dyld_get_image_name(i);
653  const char *tail = strrchr(buffer, '/');
654  if (tail && (strcmp(tail, "/libp3dtool." PANDA_ABI_VERSION_STR ".dylib") == 0
655  || strcmp(tail, "/libp3dtool.dylib") == 0)) {
656  _dtool_name = buffer;
657  }
658  }
659  }
660 #endif
661 
662 #if defined(IS_FREEBSD) || defined(IS_LINUX)
663  // FreeBSD and Linux have a function to get the origin of a loaded library.
664 
665  char origin[PATH_MAX + 1];
666 
667  if (_dtool_name.empty()) {
668  void *dtool_handle = dlopen("libp3dtool.so." PANDA_ABI_VERSION_STR, RTLD_NOW | RTLD_NOLOAD);
669  if (dtool_handle != NULL && dlinfo(dtool_handle, RTLD_DI_ORIGIN, origin) != -1) {
670  _dtool_name = origin;
671  _dtool_name += "/libp3dtool.so." PANDA_ABI_VERSION_STR;
672  } else {
673  // Try the version of libp3dtool.so without ABI suffix.
674  dtool_handle = dlopen("libp3dtool.so", RTLD_NOW | RTLD_NOLOAD);
675  if (dtool_handle != NULL && dlinfo(dtool_handle, RTLD_DI_ORIGIN, origin) != -1) {
676  _dtool_name = origin;
677  _dtool_name += "/libp3dtool.so";
678  }
679  }
680  }
681 #endif
682 
683 #if defined(IS_FREEBSD)
684  // On FreeBSD, we can use dlinfo to get the linked libraries.
685 
686  if (_dtool_name.empty()) {
687  Link_map *map;
688  dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map);
689 
690  while (map != NULL) {
691  char *tail = strrchr(map->l_name, '/');
692  char *head = strchr(map->l_name, '/');
693  if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
694  || strcmp(tail, "/libp3dtool.so") == 0)) {
695  _dtool_name = head;
696  }
697  map = map->l_next;
698  }
699  }
700 #endif
701 
702 #if defined(HAVE_PROC_SELF_MAPS) || defined(HAVE_PROC_CURPROC_MAP)
703  // Some operating systems provide a file in the /proc filesystem.
704 
705  if (_dtool_name.empty()) {
706 #ifdef HAVE_PROC_CURPROC_MAP
707  pifstream maps("/proc/curproc/map");
708 #else
709  pifstream maps("/proc/self/maps");
710 #endif
711  while (!maps.fail() && !maps.eof()) {
712  char buffer[PATH_MAX];
713  buffer[0] = 0;
714  maps.getline(buffer, PATH_MAX);
715  char *tail = strrchr(buffer, '/');
716  char *head = strchr(buffer, '/');
717  if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
718  || strcmp(tail, "/libp3dtool.so") == 0)) {
719  _dtool_name = head;
720  }
721  }
722  maps.close();
723  }
724 #endif
725 
726  // Now, we need to fill in _binary_name. This contains
727  // the full path to the currently running executable.
728 
729 #ifdef WIN32_VC
730  if (_binary_name.empty()) {
731  static const DWORD buffer_size = 1024;
732  wchar_t buffer[buffer_size];
733  DWORD size = GetModuleFileNameW(NULL, buffer, buffer_size);
734  if (size != 0) {
735  Filename tmp = Filename::from_os_specific_w(wstring(buffer, size));
736  tmp.make_true_case();
737  _binary_name = tmp;
738  }
739  }
740 #endif
741 
742 #if defined(__APPLE__)
743  // And on Mac, we have _NSGetExecutablePath.
744  if (_binary_name.empty()) {
745  char *pathbuf = new char[PATH_MAX];
746  uint32_t bufsize = PATH_MAX;
747  if (_NSGetExecutablePath(pathbuf, &bufsize) == 0) {
748  _binary_name = pathbuf;
749  }
750  delete[] pathbuf;
751  }
752 #endif
753 
754 #if defined(IS_FREEBSD)
755  // In FreeBSD, we can use sysctl to determine the pathname.
756 
757  if (_binary_name.empty()) {
758  size_t bufsize = 4096;
759  char buffer[4096];
760  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
761  mib[3] = getpid();
762  if (sysctl(mib, 4, (void*) buffer, &bufsize, NULL, 0) == -1) {
763  perror("sysctl");
764  } else {
765  _binary_name = buffer;
766  }
767  }
768 #endif
769 
770 #if defined(HAVE_PROC_SELF_EXE) || defined(HAVE_PROC_CURPROC_FILE)
771  // Some operating systems provide a symbolic link to the executable
772  // in the /proc filesystem. Use readlink to resolve that link.
773 
774  if (_binary_name.empty()) {
775  char readlinkbuf [PATH_MAX];
776 #ifdef HAVE_PROC_CURPROC_FILE
777  int pathlen = readlink("/proc/curproc/file", readlinkbuf, PATH_MAX - 1);
778 #else
779  int pathlen = readlink("/proc/self/exe", readlinkbuf, PATH_MAX - 1);
780 #endif
781  if (pathlen > 0) {
782  readlinkbuf[pathlen] = 0;
783  _binary_name = readlinkbuf;
784  }
785  }
786 #endif
787 
788  // Next we need to fill in _args, which is a vector containing
789  // the command-line arguments that the executable was invoked with.
790 
791 #if defined(WIN32_VC)
792 
793  // We cannot rely on __argv when Python is linked in Unicode mode.
794  // Instead, let's use GetCommandLine.
795 
796  LPWSTR cmdline = GetCommandLineW();
797  int argc = 0;
798  LPWSTR *wargv = CommandLineToArgvW(cmdline, &argc);
799 
800  if (wargv == NULL) {
801  cerr << "CommandLineToArgvW failed; command-line arguments unavailable to config.\n";
802 
803  } else {
804  TextEncoder encoder;
806 
807  for (int i = 0; i < argc; ++i) {
808  wstring wtext(wargv[i]);
809  encoder.set_wtext(wtext);
810 
811  if (i == 0) {
812  if (_binary_name.empty()) {
813  _binary_name = encoder.get_text();
814  }
815  } else {
816  _args.push_back(encoder.get_text());
817  }
818  }
819 
820  LocalFree(wargv);
821  }
822 
823 #elif defined(IS_FREEBSD)
824  // In FreeBSD, we can use sysctl to determine the command-line arguments.
825 
826  size_t bufsize = 4096;
827  char buffer[4096];
828  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, 0};
829  mib[3] = getpid();
830  if (sysctl(mib, 4, (void*) buffer, &bufsize, NULL, 0) == -1) {
831  perror("sysctl");
832  } else {
833  if (_binary_name.empty()) {
834  _binary_name = buffer;
835  }
836  int idx = strlen(buffer) + 1;
837  while (idx < bufsize) {
838  _args.push_back((char*)(buffer + idx));
839  int newidx = strlen(buffer + idx);
840  idx += newidx + 1;
841  }
842  }
843 
844 #elif defined(HAVE_GLOBAL_ARGV)
845  int argc = GLOBAL_ARGC;
846 
847  // On Windows, __argv can be NULL when the main entry point is
848  // compiled in Unicode mode (as is the case with Python 3)
849  if (GLOBAL_ARGV != NULL) {
850  if (_binary_name.empty() && argc > 0) {
851  _binary_name = GLOBAL_ARGV[0];
852  // This really needs to be resolved against PATH.
853  }
854 
855  for (int i = 1; i < argc; i++) {
856  _args.push_back(GLOBAL_ARGV[i]);
857  }
858  }
859 
860 #elif defined(HAVE_PROC_SELF_CMDLINE) || defined(HAVE_PROC_CURPROC_CMDLINE)
861  // In Linux, and possibly in other systems as well, we might not be
862  // able to use the global ARGC/ARGV variables at static init time.
863  // However, we may be lucky and have a file called
864  // /proc/self/cmdline that may be read to determine all of our
865  // command-line arguments.
866 
867 #ifdef HAVE_PROC_CURPROC_CMDLINE
868  pifstream proc("/proc/curproc/cmdline");
869  if (proc.fail()) {
870  cerr << "Cannot read /proc/curproc/cmdline; command-line arguments unavailable to config.\n";
871 #else
872  pifstream proc("/proc/self/cmdline");
873  if (proc.fail()) {
874  cerr << "Cannot read /proc/self/cmdline; command-line arguments unavailable to config.\n";
875 #endif
876  } else {
877  int ch = proc.get();
878  int index = 0;
879  while (!proc.eof() && !proc.fail()) {
880  string arg;
881 
882  while (!proc.eof() && !proc.fail() && ch != '\0') {
883  arg += (char)ch;
884  ch = proc.get();
885  }
886 
887  if (index == 0) {
888  if (_binary_name.empty())
889  _binary_name = arg;
890  } else {
891  _args.push_back(arg);
892  }
893  index++;
894 
895  ch = proc.get();
896  }
897  }
898 #endif
899 
900 #ifndef _WIN32
901  // Try to use realpath to get cleaner paths.
902 
903  if (!_binary_name.empty()) {
904  char newpath [PATH_MAX + 1];
905  if (realpath(_binary_name.c_str(), newpath) != NULL) {
906  _binary_name = newpath;
907  }
908  }
909 
910  if (!_dtool_name.empty()) {
911  char newpath [PATH_MAX + 1];
912  if (realpath(_dtool_name.c_str(), newpath) != NULL) {
913  _dtool_name = newpath;
914  }
915  }
916 #endif // _WIN32
917 
918 #endif // ANDROID
919 
920  if (_dtool_name.empty()) {
921  _dtool_name = _binary_name;
922  }
923 }
This class can be used to convert text between multiple representations, e.g.
Definition: textEncoder.h:37
string get_text() const
Returns the current text, as encoded via the current encoding system.
Definition: textEncoder.I:167
bool make_true_case()
On a case-insensitive operating system (e.g.
Definition: filename.cxx:1120
string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:424
static const Filename & get_home_directory()
Returns a path to the user&#39;s home directory, if such a thing makes sense in the current OS...
Definition: filename.cxx:488
static TextEncoder::Encoding get_filesystem_encoding()
Specifies the default encoding to be used for all subsequent Filenames objects.
Definition: filename.I:778
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
static const Filename & get_user_appdata_directory()
Returns a path to a system-defined directory appropriate for creating a subdirectory for storing appl...
Definition: filename.cxx:609
static const Filename & get_temp_directory()
Returns a path to a system-defined temporary directory.
Definition: filename.cxx:556
string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1196
static string get_environment_variable(const string &var)
Returns the definition of the indicated environment variable, or the empty string if the variable is ...
void make_absolute()
Converts the filename to a fully-qualified pathname from the root (if it is a relative pathname)...
Definition: filename.cxx:1019
void set_encoding(Encoding encoding)
Specifies how the string set via set_text() is to be interpreted.
Definition: textEncoder.I:59
static const Filename & get_common_appdata_directory()
Returns a path to a system-defined directory appropriate for creating a subdirectory for storing appl...
Definition: filename.cxx:662
Encapsulates access to the environment variables and command-line arguments at the time of execution...
static Filename from_os_specific_w(const wstring &os_specific, Type type=T_general)
The wide-string variant of from_os_specific().
Definition: filename.cxx:401
static Filename get_cwd()
Returns the name of the current working directory.
static string expand_string(const string &str)
Reads the string, looking for environment variable names marked by a $.
void set_wtext(const wstring &wtext)
Changes the text that is stored in the encoder.
Definition: textEncoder.I:516
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).
Definition: filename.cxx:332