Panda3D
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 = (wchar_t *)alloca(sizeof(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  }
296 #if PY_MAJOR_VERSION < 3
297  else if (PyString_Check(item)) {
298  char *str = PyString_AsString(item);
299  if (str != (char *)NULL) {
300  main_dir = Filename::from_os_specific(str);
301  }
302  }
303 #endif
304  }
305  }
306 
307 #ifdef WITH_THREAD
308  PyGILState_Release(state);
309 #endif
310 
311  if (main_dir.empty()) {
312  // We must be running in the Python interpreter directly, so return the CWD.
313  return get_cwd().to_os_specific();
314  }
315  main_dir.make_absolute();
316  return Filename(main_dir.get_dirname()).to_os_specific();
317  }
318 #endif
319 
320  // Otherwise, Return the binary name's parent directory.
321  if (!_binary_name.empty()) {
322  Filename main_dir (_binary_name);
323  main_dir.make_absolute();
324  return Filename(main_dir.get_dirname()).to_os_specific();
325  }
326  }
327 
328 #ifndef PREREAD_ENVIRONMENT
329  const char *def = getenv(var.c_str());
330  if (def != (char *)NULL) {
331  return def;
332  }
333 #endif
334 
335 #ifdef _WIN32
336  // On Windows only, we also simulate several standard folder names
337  // as "environment" variables. I know we're supposed to be using
338  // KnownFolderID's these days, but those calls aren't compatible
339  // with XP, so we'll continue to use SHGetSpecialFolderPath() until
340  // we're forced out of it.
341 
342  static struct { int id; const char *name; } csidl_table[] = {
343  { CSIDL_ADMINTOOLS, "ADMINTOOLS" },
344  { CSIDL_ALTSTARTUP, "ALTSTARTUP" },
345  { CSIDL_APPDATA, "APPDATA" },
346  { CSIDL_BITBUCKET, "BITBUCKET" },
347  { CSIDL_CDBURN_AREA, "CDBURN_AREA" },
348  { CSIDL_COMMON_ADMINTOOLS, "COMMON_ADMINTOOLS" },
349  { CSIDL_COMMON_ALTSTARTUP, "COMMON_ALTSTARTUP" },
350  { CSIDL_COMMON_APPDATA, "COMMON_APPDATA" },
351  { CSIDL_COMMON_DESKTOPDIRECTORY, "COMMON_DESKTOPDIRECTORY" },
352  { CSIDL_COMMON_DOCUMENTS, "COMMON_DOCUMENTS" },
353  { CSIDL_COMMON_FAVORITES, "COMMON_FAVORITES" },
354  { CSIDL_COMMON_MUSIC, "COMMON_MUSIC" },
355  { CSIDL_COMMON_OEM_LINKS, "COMMON_OEM_LINKS" },
356  { CSIDL_COMMON_PICTURES, "COMMON_PICTURES" },
357  { CSIDL_COMMON_PROGRAMS, "COMMON_PROGRAMS" },
358  { CSIDL_COMMON_STARTMENU, "COMMON_STARTMENU" },
359  { CSIDL_COMMON_STARTUP, "COMMON_STARTUP" },
360  { CSIDL_COMMON_TEMPLATES, "COMMON_TEMPLATES" },
361  { CSIDL_COMMON_VIDEO, "COMMON_VIDEO" },
362  { CSIDL_COMPUTERSNEARME, "COMPUTERSNEARME" },
363  { CSIDL_CONNECTIONS, "CONNECTIONS" },
364  { CSIDL_CONTROLS, "CONTROLS" },
365  { CSIDL_COOKIES, "COOKIES" },
366  { CSIDL_DESKTOP, "DESKTOP" },
367  { CSIDL_DESKTOPDIRECTORY, "DESKTOPDIRECTORY" },
368  { CSIDL_DRIVES, "DRIVES" },
369  { CSIDL_FAVORITES, "FAVORITES" },
370  { CSIDL_FONTS, "FONTS" },
371  { CSIDL_HISTORY, "HISTORY" },
372  { CSIDL_INTERNET, "INTERNET" },
373  { CSIDL_INTERNET_CACHE, "INTERNET_CACHE" },
374  { CSIDL_LOCAL_APPDATA, "LOCAL_APPDATA" },
375  { CSIDL_MYDOCUMENTS, "MYDOCUMENTS" },
376  { CSIDL_MYMUSIC, "MYMUSIC" },
377  { CSIDL_MYPICTURES, "MYPICTURES" },
378  { CSIDL_MYVIDEO, "MYVIDEO" },
379  { CSIDL_NETHOOD, "NETHOOD" },
380  { CSIDL_NETWORK, "NETWORK" },
381  { CSIDL_PERSONAL, "PERSONAL" },
382  { CSIDL_PRINTERS, "PRINTERS" },
383  { CSIDL_PRINTHOOD, "PRINTHOOD" },
384  { CSIDL_PROFILE, "PROFILE" },
385  { CSIDL_PROGRAM_FILES, "PROGRAM_FILES" },
386  { CSIDL_PROGRAM_FILESX86, "PROGRAM_FILESX86" },
387  { CSIDL_PROGRAM_FILES_COMMON, "PROGRAM_FILES_COMMON" },
388  { CSIDL_PROGRAM_FILES_COMMONX86, "PROGRAM_FILES_COMMONX86" },
389  { CSIDL_PROGRAMS, "PROGRAMS" },
390  { CSIDL_RECENT, "RECENT" },
391  { CSIDL_RESOURCES, "RESOURCES" },
392  { CSIDL_RESOURCES_LOCALIZED, "RESOURCES_LOCALIZED" },
393  { CSIDL_SENDTO, "SENDTO" },
394  { CSIDL_STARTMENU, "STARTMENU" },
395  { CSIDL_STARTUP, "STARTUP" },
396  { CSIDL_SYSTEM, "SYSTEM" },
397  { CSIDL_SYSTEMX86, "SYSTEMX86" },
398  { CSIDL_TEMPLATES, "TEMPLATES" },
399  { CSIDL_WINDOWS, "WINDOWS" },
400  { 0, NULL },
401  };
402 
403  for (int i = 0; csidl_table[i].name != NULL; ++i) {
404  if (strcmp(var.c_str(), csidl_table[i].name) == 0) {
405  wchar_t buffer[MAX_PATH];
406  if (SHGetSpecialFolderPathW(NULL, buffer, csidl_table[i].id, true)) {
407  Filename pathname = Filename::from_os_specific_w(buffer);
408  return pathname.to_os_specific();
409  }
410  break;
411  }
412  }
413 
414 #endif // _WIN32
415 
416  return string();
417 }
418 
419 ////////////////////////////////////////////////////////////////////
420 // Function: ExecutionEnvironment::ns_set_environment_variable
421 // Access: Private
422 // Description: Changes the definition of the indicated environment
423 // variable. The nonstatic implementation.
424 ////////////////////////////////////////////////////////////////////
425 void ExecutionEnvironment::
426 ns_set_environment_variable(const string &var, const string &value) {
427  _variables[var] = value;
428  string putstr = var + "=" + value;
429 
430  // putenv() requires us to malloc a new C-style string.
431  char *put = (char *)malloc(putstr.length() + 1);
432  strcpy(put, putstr.c_str());
433  putenv(put);
434 }
435 
436 ////////////////////////////////////////////////////////////////////
437 // Function: ExecutionEnvironment::ns_shadow_environment_variable
438 // Access: Private
439 // Description:
440 ////////////////////////////////////////////////////////////////////
441 void ExecutionEnvironment::
442 ns_shadow_environment_variable(const string &var, const string &value) {
443  _variables[var] = value;
444  string putstr = var + "=" + value;
445 }
446 
447 ////////////////////////////////////////////////////////////////////
448 // Function: ExecutionEnvironment::ns_clear_shadow
449 // Access: Private
450 // Description:
451 ////////////////////////////////////////////////////////////////////
452 void ExecutionEnvironment::
453 ns_clear_shadow(const string &var) {
454  EnvironmentVariables::iterator vi = _variables.find(var);
455  if (vi == _variables.end()) {
456  return;
457  }
458 
459 #ifdef PREREAD_ENVIRONMENT
460  // Now we have to replace the value in the table.
461  const char *def = getenv(var.c_str());
462  if (def != (char *)NULL) {
463  (*vi).second = def;
464  } else {
465  _variables.erase(vi);
466  }
467 #endif // PREREAD_ENVIRONMENT
468 }
469 
470 ////////////////////////////////////////////////////////////////////
471 // Function: ExecutionEnvironment::ns_get_num_args
472 // Access: Private
473 // Description: Returns the number of command-line arguments
474 // available, not counting arg 0, the binary name. The
475 // nonstatic implementation.
476 ////////////////////////////////////////////////////////////////////
477 int ExecutionEnvironment::
478 ns_get_num_args() const {
479  return _args.size();
480 }
481 
482 ////////////////////////////////////////////////////////////////////
483 // Function: ExecutionEnvironment::ns_get_arg
484 // Access: Private
485 // Description: Returns the nth command-line argument. The index n
486 // must be in the range [0 .. get_num_args()). The
487 // first parameter, n == 0, is the first actual
488 // parameter, not the binary name. The nonstatic
489 // implementation.
490 ////////////////////////////////////////////////////////////////////
491 string ExecutionEnvironment::
492 ns_get_arg(int n) const {
493  assert(n >= 0 && n < ns_get_num_args());
494  return _args[n];
495 }
496 
497 ////////////////////////////////////////////////////////////////////
498 // Function: ExecutionEnvironment::ns_get_binary_name
499 // Access: Private
500 // Description: Returns the name of the binary executable that
501 // started this program, if it can be determined. The
502 // nonstatic implementation.
503 ////////////////////////////////////////////////////////////////////
504 string ExecutionEnvironment::
505 ns_get_binary_name() const {
506  if (_binary_name.empty()) {
507  return "unknown";
508  }
509  return _binary_name;
510 }
511 
512 ////////////////////////////////////////////////////////////////////
513 // Function: ExecutionEnvironment::ns_get_dtool_name
514 // Access: Private
515 // Description: Returns the name of the libp3dtool DLL that
516 // is used in this program, if it can be determined. The
517 // nonstatic implementation.
518 ////////////////////////////////////////////////////////////////////
519 string ExecutionEnvironment::
520 ns_get_dtool_name() const {
521  if (_dtool_name.empty()) {
522  return "unknown";
523  }
524  return _dtool_name;
525 }
526 
527 ////////////////////////////////////////////////////////////////////
528 // Function: ExecutionEnvironment::get_ptr
529 // Access: Private, Static
530 // Description: Returns a static pointer that may be used to access
531 // the global ExecutionEnvironment object.
532 ////////////////////////////////////////////////////////////////////
533 ExecutionEnvironment *ExecutionEnvironment::
534 get_ptr() {
535  if (_global_ptr == (ExecutionEnvironment *)NULL) {
536  _global_ptr = new ExecutionEnvironment;
537  }
538  return _global_ptr;
539 }
540 
541 
542 ////////////////////////////////////////////////////////////////////
543 // Function: ExecutionEnvironment::read_environment_variables
544 // Access: Private
545 // Description: Fills up the internal table of existing environment
546 // variables, if we are in PREREAD_ENVIRONMENT mode.
547 // Otherwise, does nothing.
548 ////////////////////////////////////////////////////////////////////
549 void ExecutionEnvironment::
550 read_environment_variables() {
551 #ifdef PREREAD_ENVIRONMENT
552 #if defined(IS_OSX) || defined(IS_FREEBSD) || defined(IS_LINUX)
553  // In the case of Mac, we'll try reading _NSGetEnviron().
554  // In the case of FreeBSD and Linux, use the "environ" variable.
555 
556  char **envp;
557  for (envp = environ; envp && *envp; envp++) {
558  string variable;
559  string value;
560 
561  char *envc;
562  for (envc = *envp; envc && *envc && strncmp(envc, "=", 1) != 0; envc++) {
563  variable += (char) *envc;
564  }
565 
566  if (strncmp(envc, "=", 1) == 0) {
567  for (envc++; envc && *envc; envc++) {
568  value += (char) *envc;
569  }
570  }
571 
572  if (!variable.empty()) {
573  _variables[variable] = value;
574  }
575  }
576 #elif defined(HAVE_PROC_SELF_ENVIRON)
577  // In some cases, we may have a file called /proc/self/environ
578  // that may be read to determine all of our environment variables.
579 
580  pifstream proc("/proc/self/environ");
581  if (proc.fail()) {
582  cerr << "Cannot read /proc/self/environ; environment variables unavailable.\n";
583  return;
584  }
585 
586  int ch = proc.get();
587  while (!proc.eof() && !proc.fail()) {
588  string variable;
589  string value;
590 
591  while (!proc.eof() && !proc.fail() && ch != '=' && ch != '\0') {
592  variable += (char)ch;
593  ch = proc.get();
594  }
595 
596  if (ch == '=') {
597  ch = proc.get();
598  while (!proc.eof() && !proc.fail() && ch != '\0') {
599  value += (char)ch;
600  ch = proc.get();
601  }
602  }
603 
604  if (!variable.empty()) {
605  _variables[variable] = value;
606  }
607  ch = proc.get();
608  }
609 #else
610  cerr << "Warning: environment variables unavailable to dconfig.\n";
611 #endif
612 #endif // PREREAD_ENVIRONMENT
613 }
614 
615 ////////////////////////////////////////////////////////////////////
616 // Function: ExecutionEnvironment::read_args
617 // Access: Private
618 // Description: Reads all the command-line arguments and the name of
619 // the binary file, if possible.
620 ////////////////////////////////////////////////////////////////////
621 void ExecutionEnvironment::
622 read_args() {
623 #ifndef ANDROID
624  // First, we need to fill in _dtool_name. This contains
625  // the full path to the p3dtool library.
626 
627 #ifdef WIN32_VC
628 #ifdef _DEBUG
629  HMODULE dllhandle = GetModuleHandle("libp3dtool_d.dll");
630 #else
631  HMODULE dllhandle = GetModuleHandle("libp3dtool.dll");
632 #endif
633  if (dllhandle != 0) {
634  static const DWORD buffer_size = 1024;
635  wchar_t buffer[buffer_size];
636  DWORD size = GetModuleFileNameW(dllhandle, buffer, buffer_size);
637  if (size != 0) {
638  Filename tmp = Filename::from_os_specific_w(wstring(buffer, size));
639  tmp.make_true_case();
640  _dtool_name = tmp;
641  }
642  }
643 #endif
644 
645 #if defined(__APPLE__)
646  // And on OSX we don't have /proc/self/maps, but some _dyld_* functions.
647 
648  if (_dtool_name.empty()) {
649  uint32_t ic = _dyld_image_count();
650  for (uint32_t i = 0; i < ic; ++i) {
651  const char *buffer = _dyld_get_image_name(i);
652  const char *tail = strrchr(buffer, '/');
653  if (tail && (strcmp(tail, "/libp3dtool." PANDA_ABI_VERSION_STR ".dylib") == 0
654  || strcmp(tail, "/libp3dtool.dylib") == 0)) {
655  _dtool_name = buffer;
656  }
657  }
658  }
659 #endif
660 
661 #if defined(IS_FREEBSD) || defined(IS_LINUX)
662  // FreeBSD and Linux have a function to get the origin of a loaded library.
663 
664  char origin[PATH_MAX + 1];
665 
666  if (_dtool_name.empty()) {
667  void *dtool_handle = dlopen("libp3dtool.so." PANDA_ABI_VERSION_STR, RTLD_NOW | RTLD_NOLOAD);
668  if (dtool_handle != NULL && dlinfo(dtool_handle, RTLD_DI_ORIGIN, origin) != -1) {
669  _dtool_name = origin;
670  _dtool_name += "/libp3dtool.so." PANDA_ABI_VERSION_STR;
671  } else {
672  // Try the version of libp3dtool.so without ABI suffix.
673  dtool_handle = dlopen("libp3dtool.so", RTLD_NOW | RTLD_NOLOAD);
674  if (dtool_handle != NULL && dlinfo(dtool_handle, RTLD_DI_ORIGIN, origin) != -1) {
675  _dtool_name = origin;
676  _dtool_name += "/libp3dtool.so";
677  }
678  }
679  }
680 #endif
681 
682 #if defined(IS_FREEBSD)
683  // On FreeBSD, we can use dlinfo to get the linked libraries.
684 
685  if (_dtool_name.empty()) {
686  Link_map *map;
687  dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map);
688 
689  while (map != NULL) {
690  const char *tail = strrchr(map->l_name, '/');
691  const char *head = strchr(map->l_name, '/');
692  if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
693  || strcmp(tail, "/libp3dtool.so") == 0)) {
694  _dtool_name = head;
695  }
696  map = map->l_next;
697  }
698  }
699 #endif
700 
701 #if defined(HAVE_PROC_SELF_MAPS) || defined(HAVE_PROC_CURPROC_MAP)
702  // Some operating systems provide a file in the /proc filesystem.
703 
704  if (_dtool_name.empty()) {
705 #ifdef HAVE_PROC_CURPROC_MAP
706  pifstream maps("/proc/curproc/map");
707 #else
708  pifstream maps("/proc/self/maps");
709 #endif
710  while (!maps.fail() && !maps.eof()) {
711  char buffer[PATH_MAX];
712  buffer[0] = 0;
713  maps.getline(buffer, PATH_MAX);
714  const char *tail = strrchr(buffer, '/');
715  const char *head = strchr(buffer, '/');
716  if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
717  || strcmp(tail, "/libp3dtool.so") == 0)) {
718  _dtool_name = head;
719  }
720  }
721  maps.close();
722  }
723 #endif
724 
725  // Now, we need to fill in _binary_name. This contains
726  // the full path to the currently running executable.
727 
728 #ifdef WIN32_VC
729  if (_binary_name.empty()) {
730  static const DWORD buffer_size = 1024;
731  wchar_t buffer[buffer_size];
732  DWORD size = GetModuleFileNameW(NULL, buffer, buffer_size);
733  if (size != 0) {
734  Filename tmp = Filename::from_os_specific_w(wstring(buffer, size));
735  tmp.make_true_case();
736  _binary_name = tmp;
737  }
738  }
739 #endif
740 
741 #if defined(__APPLE__)
742  // And on Mac, we have _NSGetExecutablePath.
743  if (_binary_name.empty()) {
744  char *pathbuf = new char[PATH_MAX];
745  uint32_t bufsize = PATH_MAX;
746  if (_NSGetExecutablePath(pathbuf, &bufsize) == 0) {
747  _binary_name = pathbuf;
748  }
749  delete[] pathbuf;
750  }
751 #endif
752 
753 #if defined(IS_FREEBSD)
754  // In FreeBSD, we can use sysctl to determine the pathname.
755 
756  if (_binary_name.empty()) {
757  size_t bufsize = 4096;
758  char buffer[4096];
759  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
760  mib[3] = getpid();
761  if (sysctl(mib, 4, (void*) buffer, &bufsize, NULL, 0) == -1) {
762  perror("sysctl");
763  } else {
764  _binary_name = buffer;
765  }
766  }
767 #endif
768 
769 #if defined(HAVE_PROC_SELF_EXE) || defined(HAVE_PROC_CURPROC_FILE)
770  // Some operating systems provide a symbolic link to the executable
771  // in the /proc filesystem. Use readlink to resolve that link.
772 
773  if (_binary_name.empty()) {
774  char readlinkbuf [PATH_MAX];
775 #ifdef HAVE_PROC_CURPROC_FILE
776  int pathlen = readlink("/proc/curproc/file", readlinkbuf, PATH_MAX - 1);
777 #else
778  int pathlen = readlink("/proc/self/exe", readlinkbuf, PATH_MAX - 1);
779 #endif
780  if (pathlen > 0) {
781  readlinkbuf[pathlen] = 0;
782  _binary_name = readlinkbuf;
783  }
784  }
785 #endif
786 
787  // Next we need to fill in _args, which is a vector containing
788  // the command-line arguments that the executable was invoked with.
789 
790 #if defined(WIN32_VC)
791 
792  // We cannot rely on __argv when Python is linked in Unicode mode.
793  // Instead, let's use GetCommandLine.
794 
795  LPWSTR cmdline = GetCommandLineW();
796  int argc = 0;
797  LPWSTR *wargv = CommandLineToArgvW(cmdline, &argc);
798 
799  if (wargv == NULL) {
800  cerr << "CommandLineToArgvW failed; command-line arguments unavailable to config.\n";
801 
802  } else {
803  TextEncoder encoder;
805 
806  for (int i = 0; i < argc; ++i) {
807  wstring wtext(wargv[i]);
808  encoder.set_wtext(wtext);
809 
810  if (i == 0) {
811  if (_binary_name.empty()) {
812  _binary_name = encoder.get_text();
813  }
814  } else {
815  _args.push_back(encoder.get_text());
816  }
817  }
818 
819  LocalFree(wargv);
820  }
821 
822 #elif defined(IS_FREEBSD)
823  // In FreeBSD, we can use sysctl to determine the command-line arguments.
824 
825  size_t bufsize = 4096;
826  char buffer[4096];
827  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, 0};
828  mib[3] = getpid();
829  if (sysctl(mib, 4, (void*) buffer, &bufsize, NULL, 0) == -1) {
830  perror("sysctl");
831  } else {
832  if (_binary_name.empty()) {
833  _binary_name = buffer;
834  }
835  int idx = strlen(buffer) + 1;
836  while (idx < bufsize) {
837  _args.push_back((char*)(buffer + idx));
838  int newidx = strlen(buffer + idx);
839  idx += newidx + 1;
840  }
841  }
842 
843 #elif defined(HAVE_GLOBAL_ARGV)
844  int argc = GLOBAL_ARGC;
845 
846  // On Windows, __argv can be NULL when the main entry point is
847  // compiled in Unicode mode (as is the case with Python 3)
848  if (GLOBAL_ARGV != NULL) {
849  if (_binary_name.empty() && argc > 0) {
850  _binary_name = GLOBAL_ARGV[0];
851  // This really needs to be resolved against PATH.
852  }
853 
854  for (int i = 1; i < argc; i++) {
855  _args.push_back(GLOBAL_ARGV[i]);
856  }
857  }
858 
859 #elif defined(HAVE_PROC_SELF_CMDLINE) || defined(HAVE_PROC_CURPROC_CMDLINE)
860  // In Linux, and possibly in other systems as well, we might not be
861  // able to use the global ARGC/ARGV variables at static init time.
862  // However, we may be lucky and have a file called
863  // /proc/self/cmdline that may be read to determine all of our
864  // command-line arguments.
865 
866 #ifdef HAVE_PROC_CURPROC_CMDLINE
867  pifstream proc("/proc/curproc/cmdline");
868  if (proc.fail()) {
869  cerr << "Cannot read /proc/curproc/cmdline; command-line arguments unavailable to config.\n";
870 #else
871  pifstream proc("/proc/self/cmdline");
872  if (proc.fail()) {
873  cerr << "Cannot read /proc/self/cmdline; command-line arguments unavailable to config.\n";
874 #endif
875  } else {
876  int ch = proc.get();
877  int index = 0;
878  while (!proc.eof() && !proc.fail()) {
879  string arg;
880 
881  while (!proc.eof() && !proc.fail() && ch != '\0') {
882  arg += (char)ch;
883  ch = proc.get();
884  }
885 
886  if (index == 0) {
887  if (_binary_name.empty())
888  _binary_name = arg;
889  } else {
890  _args.push_back(arg);
891  }
892  index++;
893 
894  ch = proc.get();
895  }
896  }
897 #endif
898 
899 #ifndef _WIN32
900  // Try to use realpath to get cleaner paths.
901 
902  if (!_binary_name.empty()) {
903  char newpath [PATH_MAX + 1];
904  if (realpath(_binary_name.c_str(), newpath) != NULL) {
905  _binary_name = newpath;
906  }
907  }
908 
909  if (!_dtool_name.empty()) {
910  char newpath [PATH_MAX + 1];
911  if (realpath(_dtool_name.c_str(), newpath) != NULL) {
912  _dtool_name = newpath;
913  }
914  }
915 #endif // _WIN32
916 
917 #endif // ANDROID
918 
919  if (_dtool_name.empty()) {
920  _dtool_name = _binary_name;
921  }
922 }
string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:424
This class can be used to convert text between multiple representations, e.g.
Definition: textEncoder.h:37
bool make_true_case()
On a case-insensitive operating system (e.g.
Definition: filename.cxx:1120
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
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
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
string get_text() const
Returns the current text, as encoded via the current encoding system.
Definition: textEncoder.I:167
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