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