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  */
163 Filename ExecutionEnvironment::
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 #ifdef PREREAD_ENVIRONMENT
221  return _variables.count(var) != 0;
222 #else
223  return getenv(var.c_str()) != nullptr;
224 #endif
225 }
226 
227 /**
228  * Returns the definition of the indicated environment variable, or the empty
229  * string if the variable is undefined. The nonstatic implementation.
230  */
231 string ExecutionEnvironment::
232 ns_get_environment_variable(const string &var) const {
233  EnvironmentVariables::const_iterator evi;
234  evi = _variables.find(var);
235  if (evi != _variables.end()) {
236  return (*evi).second;
237  }
238 
239  // Some special case variables. We virtually stuff these values into the
240  // Panda environment, shadowing whatever values they have in the true
241  // environment, so they can be used in config files.
242  if (var == "HOME") {
244  } else if (var == "TEMP") {
246  } else if (var == "USER_APPDATA") {
248  } else if (var == "COMMON_APPDATA") {
250  } else if (var == "MAIN_DIR") {
251  // Return the binary name's parent directory. If we're running inside the
252  // Python interpreter, this will be overridden by a setting from
253  // panda3d/core.py.
254  if (!_binary_name.empty()) {
255  Filename main_dir (_binary_name);
256  main_dir.make_absolute();
257  return Filename(main_dir.get_dirname()).to_os_specific();
258  }
259  }
260 
261 #ifndef PREREAD_ENVIRONMENT
262  const char *def = getenv(var.c_str());
263  if (def != nullptr) {
264  return def;
265  }
266 #endif
267 
268 #ifdef _WIN32
269  // On Windows only, we also simulate several standard folder names as
270  // "environment" variables. I know we're supposed to be using
271  // KnownFolderID's these days, but those calls aren't compatible with XP, so
272  // we'll continue to use SHGetSpecialFolderPath() until we're forced out of
273  // it.
274 
275  static struct { int id; const char *name; } csidl_table[] = {
276  { CSIDL_ADMINTOOLS, "ADMINTOOLS" },
277  { CSIDL_ALTSTARTUP, "ALTSTARTUP" },
278  { CSIDL_APPDATA, "APPDATA" },
279  { CSIDL_BITBUCKET, "BITBUCKET" },
280  { CSIDL_CDBURN_AREA, "CDBURN_AREA" },
281  { CSIDL_COMMON_ADMINTOOLS, "COMMON_ADMINTOOLS" },
282  { CSIDL_COMMON_ALTSTARTUP, "COMMON_ALTSTARTUP" },
283  { CSIDL_COMMON_APPDATA, "COMMON_APPDATA" },
284  { CSIDL_COMMON_DESKTOPDIRECTORY, "COMMON_DESKTOPDIRECTORY" },
285  { CSIDL_COMMON_DOCUMENTS, "COMMON_DOCUMENTS" },
286  { CSIDL_COMMON_FAVORITES, "COMMON_FAVORITES" },
287  { CSIDL_COMMON_MUSIC, "COMMON_MUSIC" },
288  { CSIDL_COMMON_OEM_LINKS, "COMMON_OEM_LINKS" },
289  { CSIDL_COMMON_PICTURES, "COMMON_PICTURES" },
290  { CSIDL_COMMON_PROGRAMS, "COMMON_PROGRAMS" },
291  { CSIDL_COMMON_STARTMENU, "COMMON_STARTMENU" },
292  { CSIDL_COMMON_STARTUP, "COMMON_STARTUP" },
293  { CSIDL_COMMON_TEMPLATES, "COMMON_TEMPLATES" },
294  { CSIDL_COMMON_VIDEO, "COMMON_VIDEO" },
295  { CSIDL_COMPUTERSNEARME, "COMPUTERSNEARME" },
296  { CSIDL_CONNECTIONS, "CONNECTIONS" },
297  { CSIDL_CONTROLS, "CONTROLS" },
298  { CSIDL_COOKIES, "COOKIES" },
299  { CSIDL_DESKTOP, "DESKTOP" },
300  { CSIDL_DESKTOPDIRECTORY, "DESKTOPDIRECTORY" },
301  { CSIDL_DRIVES, "DRIVES" },
302  { CSIDL_FAVORITES, "FAVORITES" },
303  { CSIDL_FONTS, "FONTS" },
304  { CSIDL_HISTORY, "HISTORY" },
305  { CSIDL_INTERNET, "INTERNET" },
306  { CSIDL_INTERNET_CACHE, "INTERNET_CACHE" },
307  { CSIDL_LOCAL_APPDATA, "LOCAL_APPDATA" },
308  { CSIDL_MYDOCUMENTS, "MYDOCUMENTS" },
309  { CSIDL_MYMUSIC, "MYMUSIC" },
310  { CSIDL_MYPICTURES, "MYPICTURES" },
311  { CSIDL_MYVIDEO, "MYVIDEO" },
312  { CSIDL_NETHOOD, "NETHOOD" },
313  { CSIDL_NETWORK, "NETWORK" },
314  { CSIDL_PERSONAL, "PERSONAL" },
315  { CSIDL_PRINTERS, "PRINTERS" },
316  { CSIDL_PRINTHOOD, "PRINTHOOD" },
317  { CSIDL_PROFILE, "PROFILE" },
318  { CSIDL_PROGRAM_FILES, "PROGRAM_FILES" },
319  { CSIDL_PROGRAM_FILESX86, "PROGRAM_FILESX86" },
320  { CSIDL_PROGRAM_FILES_COMMON, "PROGRAM_FILES_COMMON" },
321  { CSIDL_PROGRAM_FILES_COMMONX86, "PROGRAM_FILES_COMMONX86" },
322  { CSIDL_PROGRAMS, "PROGRAMS" },
323  { CSIDL_RECENT, "RECENT" },
324  { CSIDL_RESOURCES, "RESOURCES" },
325  { CSIDL_RESOURCES_LOCALIZED, "RESOURCES_LOCALIZED" },
326  { CSIDL_SENDTO, "SENDTO" },
327  { CSIDL_STARTMENU, "STARTMENU" },
328  { CSIDL_STARTUP, "STARTUP" },
329  { CSIDL_SYSTEM, "SYSTEM" },
330  { CSIDL_SYSTEMX86, "SYSTEMX86" },
331  { CSIDL_TEMPLATES, "TEMPLATES" },
332  { CSIDL_WINDOWS, "WINDOWS" },
333  { 0, nullptr },
334  };
335 
336  for (int i = 0; csidl_table[i].name != nullptr; ++i) {
337  if (strcmp(var.c_str(), csidl_table[i].name) == 0) {
338  wchar_t buffer[MAX_PATH];
339  if (SHGetSpecialFolderPathW(nullptr, buffer, csidl_table[i].id, true)) {
340  Filename pathname = Filename::from_os_specific_w(buffer);
341  return pathname.to_os_specific();
342  }
343  break;
344  }
345  }
346 
347 #elif !defined(__APPLE__)
348  // Similarly, we define fallbacks on POSIX systems for the variables defined
349  // in the XDG Base Directory specification, so that they can be safely used
350  // in Config.prc files.
351  if (var == "XDG_CONFIG_HOME") {
353  return home_dir.get_fullpath() + "/.config";
354 
355  } else if (var == "XDG_CACHE_HOME") {
357  return home_dir.get_fullpath() + "/.cache";
358 
359  } else if (var == "XDG_DATA_HOME") {
361  return home_dir.get_fullpath() + "/.local/share";
362  }
363 #endif // _WIN32
364 
365  return string();
366 }
367 
368 /**
369  * Changes the definition of the indicated environment variable. The
370  * nonstatic implementation.
371  */
372 void ExecutionEnvironment::
373 ns_set_environment_variable(const string &var, const string &value) {
374  _variables[var] = value;
375  string putstr = var + "=" + value;
376 
377  // putenv() requires us to malloc a new C-style string.
378  char *put = (char *)malloc(putstr.length() + 1);
379  strcpy(put, putstr.c_str());
380  putenv(put);
381 }
382 
383 /**
384  *
385  */
386 void ExecutionEnvironment::
387 ns_shadow_environment_variable(const string &var, const string &value) {
388  _variables[var] = value;
389  string putstr = var + "=" + value;
390 }
391 
392 /**
393  *
394  */
395 void ExecutionEnvironment::
396 ns_clear_shadow(const string &var) {
397  EnvironmentVariables::iterator vi = _variables.find(var);
398  if (vi == _variables.end()) {
399  return;
400  }
401 
402 #ifdef PREREAD_ENVIRONMENT
403  // Now we have to replace the value in the table.
404  const char *def = getenv(var.c_str());
405  if (def != nullptr) {
406  (*vi).second = def;
407  } else {
408  _variables.erase(vi);
409  }
410 #endif // PREREAD_ENVIRONMENT
411 }
412 
413 /**
414  * Returns the number of command-line arguments available, not counting arg 0,
415  * the binary name. The nonstatic implementation.
416  */
417 size_t ExecutionEnvironment::
418 ns_get_num_args() const {
419  return _args.size();
420 }
421 
422 /**
423  * Returns the nth command-line argument. The index n must be in the range [0
424  * .. get_num_args()). The first parameter, n == 0, is the first actual
425  * parameter, not the binary name. The nonstatic implementation.
426  */
427 string ExecutionEnvironment::
428 ns_get_arg(size_t n) const {
429  assert(n < ns_get_num_args());
430  return _args[n];
431 }
432 
433 /**
434  * Returns the name of the binary executable that started this program, if it
435  * can be determined. The nonstatic implementation.
436  */
437 string ExecutionEnvironment::
438 ns_get_binary_name() const {
439  if (_binary_name.empty()) {
440  return "unknown";
441  }
442  return _binary_name;
443 }
444 
445 /**
446  * Returns the name of the libp3dtool DLL that is used in this program, if it
447  * can be determined. The nonstatic implementation.
448  */
449 string ExecutionEnvironment::
450 ns_get_dtool_name() const {
451  if (_dtool_name.empty()) {
452  return "unknown";
453  }
454  return _dtool_name;
455 }
456 
457 /**
458  * Returns a static pointer that may be used to access the global
459  * ExecutionEnvironment object.
460  */
461 ExecutionEnvironment *ExecutionEnvironment::
462 get_ptr() {
463  if (_global_ptr == nullptr) {
464  _global_ptr = new ExecutionEnvironment;
465  }
466  return _global_ptr;
467 }
468 
469 
470 /**
471  * Fills up the internal table of existing environment variables, if we are in
472  * PREREAD_ENVIRONMENT mode. Otherwise, does nothing.
473  */
474 void ExecutionEnvironment::
475 read_environment_variables() {
476 #ifdef PREREAD_ENVIRONMENT
477 #if defined(IS_OSX) || defined(IS_FREEBSD) || defined(IS_LINUX)
478  // In the case of Mac, we'll try reading _NSGetEnviron(). In the case of
479  // FreeBSD and Linux, use the "environ" variable.
480 
481  char **envp;
482  for (envp = environ; envp && *envp; envp++) {
483  string variable;
484  string value;
485 
486  char *envc;
487  for (envc = *envp; envc && *envc && strncmp(envc, "=", 1) != 0; envc++) {
488  variable += (char) *envc;
489  }
490 
491  if (strncmp(envc, "=", 1) == 0) {
492  for (envc++; envc && *envc; envc++) {
493  value += (char) *envc;
494  }
495  }
496 
497  if (!variable.empty()) {
498  _variables[variable] = value;
499  }
500  }
501 #elif defined(HAVE_PROC_SELF_ENVIRON)
502  // In some cases, we may have a file called procselfenviron that may be read
503  // to determine all of our environment variables.
504 
505  pifstream proc("/proc/self/environ");
506  if (proc.fail()) {
507  cerr << "Cannot read /proc/self/environ; environment variables unavailable.\n";
508  return;
509  }
510 
511  int ch = proc.get();
512  while (!proc.eof() && !proc.fail()) {
513  string variable;
514  string value;
515 
516  while (!proc.eof() && !proc.fail() && ch != '=' && ch != '\0') {
517  variable += (char)ch;
518  ch = proc.get();
519  }
520 
521  if (ch == '=') {
522  ch = proc.get();
523  while (!proc.eof() && !proc.fail() && ch != '\0') {
524  value += (char)ch;
525  ch = proc.get();
526  }
527  }
528 
529  if (!variable.empty()) {
530  _variables[variable] = value;
531  }
532  ch = proc.get();
533  }
534 #else
535  cerr << "Warning: environment variables unavailable to dconfig.\n";
536 #endif
537 #endif // PREREAD_ENVIRONMENT
538 }
539 
540 /**
541  * Reads all the command-line arguments and the name of the binary file, if
542  * possible.
543  */
544 void ExecutionEnvironment::
545 read_args() {
546  // First, we need to fill in _dtool_name. This contains the full path to
547  // the p3dtool library.
548 
549 #ifdef WIN32_VC
550 #ifdef _DEBUG
551  HMODULE dllhandle = GetModuleHandle("libp3dtool_d.dll");
552 #else
553  HMODULE dllhandle = GetModuleHandle("libp3dtool.dll");
554 #endif
555  if (dllhandle != 0) {
556  static const DWORD buffer_size = 1024;
557  wchar_t buffer[buffer_size];
558  DWORD size = GetModuleFileNameW(dllhandle, buffer, buffer_size);
559  if (size != 0) {
560  Filename tmp = Filename::from_os_specific_w(std::wstring(buffer, size));
561  tmp.make_true_case();
562  _dtool_name = tmp;
563  }
564  }
565 #endif
566 
567 #if defined(__APPLE__)
568  // And on OSX we don't have procselfmaps, but some _dyld_* functions.
569 
570  if (_dtool_name.empty()) {
571  uint32_t ic = _dyld_image_count();
572  for (uint32_t i = 0; i < ic; ++i) {
573  const char *buffer = _dyld_get_image_name(i);
574  const char *tail = strrchr(buffer, '/');
575  if (tail && (strcmp(tail, "/libp3dtool." PANDA_ABI_VERSION_STR ".dylib") == 0
576  || strcmp(tail, "/libp3dtool.dylib") == 0)) {
577  _dtool_name = buffer;
578  }
579  }
580  }
581 #endif
582 
583 #if defined(RTLD_DI_ORIGIN)
584  // When building with glibc/uClibc, we typically have access to RTLD_DI_ORIGIN in Unix-like operating systems.
585 
586  char origin[PATH_MAX + 1];
587 
588  if (_dtool_name.empty()) {
589  void *dtool_handle = dlopen("libp3dtool.so." PANDA_ABI_VERSION_STR, RTLD_NOW | RTLD_NOLOAD);
590  if (dtool_handle != nullptr && dlinfo(dtool_handle, RTLD_DI_ORIGIN, origin) != -1) {
591  _dtool_name = origin;
592  _dtool_name += "/libp3dtool.so." PANDA_ABI_VERSION_STR;
593  } else {
594  // Try the version of libp3dtool.so without ABI suffix.
595  dtool_handle = dlopen("libp3dtool.so", RTLD_NOW | RTLD_NOLOAD);
596  if (dtool_handle != nullptr && dlinfo(dtool_handle, RTLD_DI_ORIGIN, origin) != -1) {
597  _dtool_name = origin;
598  _dtool_name += "/libp3dtool.so";
599  }
600  }
601  }
602 #endif
603 
604 #if !defined(RTLD_DI_ORIGIN) && defined(RTLD_DI_LINKMAP)
605  // On platforms without RTLD_DI_ORIGIN, we can use dlinfo with RTLD_DI_LINKMAP to get the origin of a loaded library.
606  if (_dtool_name.empty()) {
607  struct link_map *map;
608 #ifdef RTLD_SELF
609  void *self = RTLD_SELF;
610 #else
611  void *self = dlopen(NULL, RTLD_NOW | RTLD_NOLOAD);
612 #endif
613  dlinfo(self, RTLD_DI_LINKMAP, &map);
614 
615  while (map != nullptr) {
616  const char *tail = strrchr(map->l_name, '/');
617  const char *head = strchr(map->l_name, '/');
618  if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
619  || strcmp(tail, "/libp3dtool.so") == 0)) {
620  _dtool_name = head;
621  }
622  map = map->l_next;
623  }
624  }
625 #endif
626 
627 #if defined(HAVE_PROC_SELF_MAPS) || defined(HAVE_PROC_CURPROC_MAP)
628  // Some operating systems provide a file in the proc filesystem.
629 
630  if (_dtool_name.empty()) {
631 #ifdef HAVE_PROC_CURPROC_MAP
632  pifstream maps("/proc/curproc/map");
633 #else
634  pifstream maps("/proc/self/maps");
635 #endif
636  while (!maps.fail() && !maps.eof()) {
637  char buffer[PATH_MAX];
638  buffer[0] = 0;
639  maps.getline(buffer, PATH_MAX);
640  const char *tail = strrchr(buffer, '/');
641  const char *head = strchr(buffer, '/');
642  if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
643  || strcmp(tail, "/libp3dtool.so") == 0)) {
644  _dtool_name = head;
645  }
646  }
647  maps.close();
648  }
649 #endif
650 
651  // Now, we need to fill in _binary_name. This contains the full path to the
652  // currently running executable.
653 
654 #ifdef WIN32_VC
655  if (_binary_name.empty()) {
656  static const DWORD buffer_size = 1024;
657  wchar_t buffer[buffer_size];
658  DWORD size = GetModuleFileNameW(nullptr, buffer, buffer_size);
659  if (size != 0) {
660  Filename tmp = Filename::from_os_specific_w(std::wstring(buffer, size));
661  tmp.make_true_case();
662  _binary_name = tmp;
663  }
664  }
665 #endif
666 
667 #if defined(__APPLE__)
668  // And on Mac, we have _NSGetExecutablePath.
669  if (_binary_name.empty()) {
670  char *pathbuf = new char[PATH_MAX];
671  uint32_t bufsize = PATH_MAX;
672  if (_NSGetExecutablePath(pathbuf, &bufsize) == 0) {
673  _binary_name = pathbuf;
674  }
675  delete[] pathbuf;
676  }
677 #endif
678 
679 #if defined(IS_FREEBSD)
680  // In FreeBSD, we can use sysctl to determine the pathname.
681 
682  if (_binary_name.empty()) {
683  size_t bufsize = 4096;
684  char buffer[4096];
685  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
686  mib[3] = getpid();
687  if (sysctl(mib, 4, (void*) buffer, &bufsize, nullptr, 0) == -1) {
688  perror("sysctl");
689  } else {
690  _binary_name = buffer;
691  }
692  }
693 #endif
694 
695 #if defined(HAVE_PROC_SELF_EXE) || defined(HAVE_PROC_CURPROC_FILE)
696  // Some operating systems provide a symbolic link to the executable in the
697  // proc filesystem. Use readlink to resolve that link.
698 
699  if (_binary_name.empty()) {
700  char readlinkbuf [PATH_MAX];
701 #ifdef HAVE_PROC_CURPROC_FILE
702  ssize_t pathlen = readlink("/proc/curproc/file", readlinkbuf, PATH_MAX - 1);
703 #else
704  ssize_t pathlen = readlink("/proc/self/exe", readlinkbuf, PATH_MAX - 1);
705 #endif
706  if (pathlen > 0) {
707  readlinkbuf[pathlen] = 0;
708  _binary_name = readlinkbuf;
709  }
710  }
711 #endif
712 
713  // Next we need to fill in _args, which is a vector containing the command-
714  // line arguments that the executable was invoked with.
715 
716 #if defined(WIN32_VC)
717 
718  // We cannot rely on __argv when Python is linked in Unicode mode. Instead,
719  // let's use GetCommandLine.
720 
721  LPWSTR cmdline = GetCommandLineW();
722  int argc = 0;
723  LPWSTR *wargv = CommandLineToArgvW(cmdline, &argc);
724 
725  if (wargv == nullptr) {
726  cerr << "CommandLineToArgvW failed; command-line arguments unavailable to config.\n";
727 
728  } else {
729  TextEncoder encoder;
731 
732  for (int i = 0; i < argc; ++i) {
733  std::wstring wtext(wargv[i]);
734  encoder.set_wtext(wtext);
735 
736  if (i == 0) {
737  if (_binary_name.empty()) {
738  _binary_name = encoder.get_text();
739  }
740  } else {
741  _args.push_back(encoder.get_text());
742  }
743  }
744 
745  LocalFree(wargv);
746  }
747 
748 #elif defined(IS_FREEBSD)
749  // In FreeBSD, we can use sysctl to determine the command-line arguments.
750 
751  size_t bufsize = 4096;
752  char buffer[4096];
753  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, 0};
754  mib[3] = getpid();
755  if (sysctl(mib, 4, (void*) buffer, &bufsize, nullptr, 0) == -1) {
756  perror("sysctl");
757  } else {
758  if (_binary_name.empty()) {
759  _binary_name = buffer;
760  }
761  size_t idx = strlen(buffer) + 1;
762  while (idx < bufsize) {
763  _args.push_back((char*)(buffer + idx));
764  size_t newidx = strlen(buffer + idx);
765  idx += newidx + 1;
766  }
767  }
768 
769 #elif defined(HAVE_GLOBAL_ARGV)
770  int argc = GLOBAL_ARGC;
771 
772  // On Windows, __argv can be NULL when the main entry point is compiled in
773  // Unicode mode (as is the case with Python 3)
774  if (GLOBAL_ARGV != nullptr) {
775  if (_binary_name.empty() && argc > 0) {
776  _binary_name = GLOBAL_ARGV[0];
777  // This really needs to be resolved against PATH.
778  }
779 
780  for (int i = 1; i < argc; i++) {
781  _args.push_back(GLOBAL_ARGV[i]);
782  }
783  }
784 
785 #elif defined(HAVE_PROC_SELF_CMDLINE) || defined(HAVE_PROC_CURPROC_CMDLINE)
786  // In Linux, and possibly in other systems as well, we might not be able to
787  // use the global ARGCARGV variables at static init time. However, we may
788  // be lucky and have a file called procselfcmdline that may be read to
789  // determine all of our command-line arguments.
790 
791 #ifdef HAVE_PROC_CURPROC_CMDLINE
792  pifstream proc("/proc/curproc/cmdline");
793  if (proc.fail()) {
794  cerr << "Cannot read /proc/curproc/cmdline; command-line arguments unavailable to config.\n";
795 #else
796  pifstream proc("/proc/self/cmdline");
797  if (proc.fail()) {
798  cerr << "Cannot read /proc/self/cmdline; command-line arguments unavailable to config.\n";
799 #endif
800  } else {
801  int ch = proc.get();
802  int index = 0;
803  while (!proc.eof() && !proc.fail()) {
804  string arg;
805 
806  while (!proc.eof() && !proc.fail() && ch != '\0') {
807  arg += (char)ch;
808  ch = proc.get();
809  }
810 
811  if (index == 0) {
812  if (_binary_name.empty())
813  _binary_name = arg;
814  } else {
815  _args.push_back(arg);
816  }
817  index++;
818 
819  ch = proc.get();
820  }
821  }
822 #endif
823 
824 #ifndef _WIN32
825  // Try to use realpath to get cleaner paths.
826 
827  if (!_binary_name.empty()) {
828  char newpath [PATH_MAX + 1];
829  if (realpath(_binary_name.c_str(), newpath) != nullptr) {
830  _binary_name = newpath;
831  }
832  }
833 
834  if (!_dtool_name.empty()) {
835  char newpath [PATH_MAX + 1];
836  if (realpath(_dtool_name.c_str(), newpath) != nullptr) {
837  _dtool_name = newpath;
838  }
839  }
840 #endif // _WIN32
841 
842  if (_dtool_name.empty()) {
843  _dtool_name = _binary_name;
844  }
845 }
This class can be used to convert text between multiple representations, e.g.
Definition: textEncoder.h:33
bool make_true_case()
On a case-insensitive operating system (e.g.
Definition: filename.cxx:1053
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 TextEncoder::Encoding get_filesystem_encoding()
Specifies the default encoding to be used for all subsequent Filenames objects.
Definition: filename.I:639
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
static std::string expand_string(const std::string &str)
Reads the string, looking for environment variable names marked by a $.
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:338
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
static const Filename & get_temp_directory()
Returns a path to a system-defined temporary directory.
Definition: filename.cxx:539
get_text
Returns the current text, as encoded via the current encoding system.
Definition: textEncoder.h:124
void set_encoding(Encoding encoding)
Specifies how the string set via set_text() is to be interpreted.
Definition: textEncoder.I:48
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
Encapsulates access to the environment variables and command-line arguments at the time of execution.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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 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
void set_wtext(const std::wstring &wtext)
Changes the text that is stored in the encoder.
Definition: textEncoder.I:443
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
get_environment_variable
Returns the definition of the indicated environment variable, or the empty string if the variable is ...