Panda3D
Loading...
Searching...
No Matches
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
15#include "pandaVersion.h"
16
17#include <assert.h>
18#include <errno.h>
19#include <stdio.h> // for perror
20
21using std::cerr;
22using 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
58extern 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)
84extern char **GLOBAL_ARGV;
85extern 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
92ExecutionEnvironment *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 */
98ExecutionEnvironment::
99ExecutionEnvironment() {
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 */
112expand_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 */
164get_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
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 */
218bool ExecutionEnvironment::
219ns_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 */
235string ExecutionEnvironment::
236ns_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 } else if (var == "XDG_STATE_HOME") {
369 return home_dir.get_fullpath() + "/.local/state";
370 }
371#endif // _WIN32
372
373 return string();
374}
375
376/**
377 * Changes the definition of the indicated environment variable. The
378 * nonstatic implementation.
379 */
380void ExecutionEnvironment::
381ns_set_environment_variable(const string &var, const string &value) {
382 _variables[var] = value;
383 string putstr = var + "=" + value;
384
385 // putenv() requires us to malloc a new C-style string.
386 char *put = (char *)malloc(putstr.length() + 1);
387 strcpy(put, putstr.c_str());
388 putenv(put);
389}
390
391/**
392 *
393 */
394void ExecutionEnvironment::
395ns_shadow_environment_variable(const string &var, const string &value) {
396 _variables[var] = value;
397 string putstr = var + "=" + value;
398}
399
400/**
401 *
402 */
403void ExecutionEnvironment::
404ns_clear_shadow(const string &var) {
405 EnvironmentVariables::iterator vi = _variables.find(var);
406 if (vi == _variables.end()) {
407 return;
408 }
409
410#ifdef PREREAD_ENVIRONMENT
411 // Now we have to replace the value in the table.
412 const char *def = getenv(var.c_str());
413 if (def != nullptr) {
414 (*vi).second = def;
415 } else {
416 _variables.erase(vi);
417 }
418#endif // PREREAD_ENVIRONMENT
419}
420
421/**
422 * Returns the number of command-line arguments available, not counting arg 0,
423 * the binary name. The nonstatic implementation.
424 */
425size_t ExecutionEnvironment::
426ns_get_num_args() const {
427 return _args.size();
428}
429
430/**
431 * Returns the nth command-line argument. The index n must be in the range [0
432 * .. get_num_args()). The first parameter, n == 0, is the first actual
433 * parameter, not the binary name. The nonstatic implementation.
434 */
435string ExecutionEnvironment::
436ns_get_arg(size_t n) const {
437 assert(n < ns_get_num_args());
438 return _args[n];
439}
440
441/**
442 * Returns the name of the binary executable that started this program, if it
443 * can be determined. The nonstatic implementation.
444 */
445string ExecutionEnvironment::
446ns_get_binary_name() const {
447 if (_binary_name.empty()) {
448 return "unknown";
449 }
450 return _binary_name;
451}
452
453/**
454 * Returns the name of the libp3dtool DLL that is used in this program, if it
455 * can be determined. The nonstatic implementation.
456 */
457string ExecutionEnvironment::
458ns_get_dtool_name() const {
459 if (_dtool_name.empty()) {
460 return "unknown";
461 }
462 return _dtool_name;
463}
464
465/**
466 * Returns a static pointer that may be used to access the global
467 * ExecutionEnvironment object.
468 */
469ExecutionEnvironment *ExecutionEnvironment::
470get_ptr() {
471 if (_global_ptr == nullptr) {
472 _global_ptr = new ExecutionEnvironment;
473 }
474 return _global_ptr;
475}
476
477
478/**
479 * Fills up the internal table of existing environment variables, if we are in
480 * PREREAD_ENVIRONMENT mode. Otherwise, does nothing.
481 */
482void ExecutionEnvironment::
483read_environment_variables() {
484#ifdef PREREAD_ENVIRONMENT
485#if defined(IS_OSX) || defined(IS_FREEBSD) || defined(IS_LINUX)
486 // In the case of Mac, we'll try reading _NSGetEnviron(). In the case of
487 // FreeBSD and Linux, use the "environ" variable.
488
489 char **envp;
490 for (envp = environ; envp && *envp; envp++) {
491 string variable;
492 string value;
493
494 char *envc;
495 for (envc = *envp; envc && *envc && strncmp(envc, "=", 1) != 0; envc++) {
496 variable += (char) *envc;
497 }
498
499 if (strncmp(envc, "=", 1) == 0) {
500 for (envc++; envc && *envc; envc++) {
501 value += (char) *envc;
502 }
503 }
504
505 if (!variable.empty()) {
506 _variables[variable] = value;
507 }
508 }
509#elif defined(HAVE_PROC_SELF_ENVIRON)
510 // In some cases, we may have a file called procselfenviron that may be read
511 // to determine all of our environment variables.
512
513 pifstream proc("/proc/self/environ");
514 if (proc.fail()) {
515 cerr << "Cannot read /proc/self/environ; environment variables unavailable.\n";
516 return;
517 }
518
519 int ch = proc.get();
520 while (!proc.eof() && !proc.fail()) {
521 string variable;
522 string value;
523
524 while (!proc.eof() && !proc.fail() && ch != '=' && ch != '\0') {
525 variable += (char)ch;
526 ch = proc.get();
527 }
528
529 if (ch == '=') {
530 ch = proc.get();
531 while (!proc.eof() && !proc.fail() && ch != '\0') {
532 value += (char)ch;
533 ch = proc.get();
534 }
535 }
536
537 if (!variable.empty()) {
538 _variables[variable] = value;
539 }
540 ch = proc.get();
541 }
542#else
543 cerr << "Warning: environment variables unavailable to dconfig.\n";
544#endif
545#endif // PREREAD_ENVIRONMENT
546}
547
548/**
549 * Reads all the command-line arguments and the name of the binary file, if
550 * possible.
551 */
552void ExecutionEnvironment::
553read_args() {
554 // First, we need to fill in _dtool_name. This contains the full path to
555 // the p3dtool library.
556
557#ifdef WIN32_VC
558#ifdef _DEBUG
559 HMODULE dllhandle = GetModuleHandle("libp3dtool_d.dll");
560#else
561 HMODULE dllhandle = GetModuleHandle("libp3dtool.dll");
562#endif
563 if (dllhandle != 0) {
564 static const DWORD buffer_size = 1024;
565 wchar_t buffer[buffer_size];
566 DWORD size = GetModuleFileNameW(dllhandle, buffer, buffer_size);
567 if (size != 0) {
568 Filename tmp = Filename::from_os_specific_w(std::wstring(buffer, size));
569 tmp.make_true_case();
570 _dtool_name = tmp;
571 }
572 }
573#endif
574
575#if defined(__APPLE__)
576 // And on OSX we don't have procselfmaps, but some _dyld_* functions.
577
578 if (_dtool_name.empty()) {
579 uint32_t ic = _dyld_image_count();
580 for (uint32_t i = 0; i < ic; ++i) {
581 const char *buffer = _dyld_get_image_name(i);
582 const char *tail = strrchr(buffer, '/');
583 if (tail && (strcmp(tail, "/libp3dtool." PANDA_ABI_VERSION_STR ".dylib") == 0
584 || strcmp(tail, "/libp3dtool.dylib") == 0)) {
585 _dtool_name = buffer;
586 }
587 }
588 }
589#endif
590
591#if defined(RTLD_DI_ORIGIN)
592 // When building with glibc/uClibc, we typically have access to RTLD_DI_ORIGIN in Unix-like operating systems.
593
594 char origin[PATH_MAX + 1];
595
596 if (_dtool_name.empty()) {
597 void *dtool_handle = dlopen("libp3dtool.so." PANDA_ABI_VERSION_STR, RTLD_NOW | RTLD_NOLOAD);
598 if (dtool_handle != nullptr && dlinfo(dtool_handle, RTLD_DI_ORIGIN, origin) != -1) {
599 _dtool_name = origin;
600 _dtool_name += "/libp3dtool.so." PANDA_ABI_VERSION_STR;
601 } else {
602 // Try the version of libp3dtool.so without ABI suffix.
603 dtool_handle = dlopen("libp3dtool.so", RTLD_NOW | RTLD_NOLOAD);
604 if (dtool_handle != nullptr && dlinfo(dtool_handle, RTLD_DI_ORIGIN, origin) != -1) {
605 _dtool_name = origin;
606 _dtool_name += "/libp3dtool.so";
607 }
608 }
609 }
610#endif
611
612#if !defined(RTLD_DI_ORIGIN) && defined(RTLD_DI_LINKMAP)
613 // On platforms without RTLD_DI_ORIGIN, we can use dlinfo with RTLD_DI_LINKMAP to get the origin of a loaded library.
614 if (_dtool_name.empty()) {
615 struct link_map *map;
616#ifdef RTLD_SELF
617 void *self = RTLD_SELF;
618#else
619 void *self = dlopen(NULL, RTLD_NOW | RTLD_NOLOAD);
620#endif
621 if (dlinfo(self, RTLD_DI_LINKMAP, &map)) {
622 while (map != nullptr) {
623 const char *tail = strrchr(map->l_name, '/');
624 const char *head = strchr(map->l_name, '/');
625 if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
626 || strcmp(tail, "/libp3dtool.so") == 0)) {
627 _dtool_name = head;
628 }
629 map = map->l_next;
630 }
631 }
632 }
633#endif
634
635#if defined(HAVE_PROC_SELF_MAPS) || defined(HAVE_PROC_CURPROC_MAP)
636 // Some operating systems provide a file in the proc filesystem.
637
638 if (_dtool_name.empty()) {
639#ifdef HAVE_PROC_CURPROC_MAP
640 pifstream maps("/proc/curproc/map");
641#else
642 pifstream maps("/proc/self/maps");
643#endif
644 while (!maps.fail() && !maps.eof()) {
645 char buffer[PATH_MAX];
646 buffer[0] = 0;
647 maps.getline(buffer, PATH_MAX);
648 const char *tail = strrchr(buffer, '/');
649 const char *head = strchr(buffer, '/');
650 if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
651 || strcmp(tail, "/libp3dtool.so") == 0)) {
652 _dtool_name = head;
653 }
654 }
655 maps.close();
656 }
657#endif
658
659 // Now, we need to fill in _binary_name. This contains the full path to the
660 // currently running executable.
661
662#ifdef WIN32_VC
663 if (_binary_name.empty()) {
664 static const DWORD buffer_size = 1024;
665 wchar_t buffer[buffer_size];
666 DWORD size = GetModuleFileNameW(nullptr, buffer, buffer_size);
667 if (size != 0) {
668 Filename tmp = Filename::from_os_specific_w(std::wstring(buffer, size));
669 tmp.make_true_case();
670 _binary_name = tmp;
671 }
672 }
673#endif
674
675#if defined(__APPLE__)
676 // And on Mac, we have _NSGetExecutablePath.
677 if (_binary_name.empty()) {
678 char *pathbuf = new char[PATH_MAX];
679 uint32_t bufsize = PATH_MAX;
680 if (_NSGetExecutablePath(pathbuf, &bufsize) == 0) {
681 _binary_name = pathbuf;
682 }
683 delete[] pathbuf;
684 }
685#endif
686
687#if defined(IS_FREEBSD)
688 // In FreeBSD, we can use sysctl to determine the pathname.
689
690 if (_binary_name.empty()) {
691 size_t bufsize = 4096;
692 char buffer[4096];
693 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
694 mib[3] = getpid();
695 if (sysctl(mib, 4, (void*) buffer, &bufsize, nullptr, 0) == -1) {
696 perror("sysctl");
697 } else {
698 _binary_name = buffer;
699 }
700 }
701#endif
702
703#if defined(HAVE_PROC_SELF_EXE) || defined(HAVE_PROC_CURPROC_FILE)
704 // Some operating systems provide a symbolic link to the executable in the
705 // proc filesystem. Use readlink to resolve that link.
706
707 if (_binary_name.empty()) {
708 char readlinkbuf [PATH_MAX];
709#ifdef HAVE_PROC_CURPROC_FILE
710 ssize_t pathlen = readlink("/proc/curproc/file", readlinkbuf, PATH_MAX - 1);
711#else
712 ssize_t pathlen = readlink("/proc/self/exe", readlinkbuf, PATH_MAX - 1);
713#endif
714 if (pathlen > 0) {
715 readlinkbuf[pathlen] = 0;
716 _binary_name = readlinkbuf;
717 }
718 }
719#endif
720
721 // Next we need to fill in _args, which is a vector containing the command-
722 // line arguments that the executable was invoked with.
723
724#if defined(WIN32_VC)
725
726 // We cannot rely on __argv when Python is linked in Unicode mode. Instead,
727 // let's use GetCommandLine.
728
729 LPWSTR cmdline = GetCommandLineW();
730 int argc = 0;
731 LPWSTR *wargv = CommandLineToArgvW(cmdline, &argc);
732
733 if (wargv == nullptr) {
734 cerr << "CommandLineToArgvW failed; command-line arguments unavailable to config.\n";
735
736 } else {
737 TextEncoder encoder;
738 encoder.set_encoding(Filename::get_filesystem_encoding());
739
740 for (int i = 0; i < argc; ++i) {
741 std::wstring wtext(wargv[i]);
742 encoder.set_wtext(wtext);
743
744 if (i == 0) {
745 if (_binary_name.empty()) {
746 _binary_name = encoder.get_text();
747 }
748 } else {
749 _args.push_back(encoder.get_text());
750 }
751 }
752
753 LocalFree(wargv);
754 }
755
756#elif defined(IS_FREEBSD)
757 // In FreeBSD, we can use sysctl to determine the command-line arguments.
758
759 size_t bufsize = 0;
760 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, 0};
761 mib[3] = getpid();
762 if (sysctl(mib, 4, nullptr, &bufsize, nullptr, 0) == -1) {
763 perror("sysctl");
764 } else {
765 char *buffer = (char *)alloca(bufsize);
766 if (sysctl(mib, 4, buffer, &bufsize, nullptr, 0) == -1) {
767 perror("sysctl");
768 } else {
769 if (_binary_name.empty()) {
770 _binary_name = buffer;
771 }
772 size_t idx = strlen(buffer) + 1;
773 while (idx < bufsize) {
774 _args.push_back((char*)(buffer + idx));
775 size_t newidx = strlen(buffer + idx);
776 idx += newidx + 1;
777 }
778 }
779 }
780
781#elif defined(HAVE_GLOBAL_ARGV)
782 int argc = GLOBAL_ARGC;
783
784 // On Windows, __argv can be NULL when the main entry point is compiled in
785 // Unicode mode (as is the case with Python 3)
786 if (GLOBAL_ARGV != nullptr) {
787 if (_binary_name.empty() && argc > 0) {
788 _binary_name = GLOBAL_ARGV[0];
789 // This really needs to be resolved against PATH.
790 }
791
792 for (int i = 1; i < argc; i++) {
793 _args.push_back(GLOBAL_ARGV[i]);
794 }
795 }
796
797#elif defined(HAVE_PROC_SELF_CMDLINE) || defined(HAVE_PROC_CURPROC_CMDLINE)
798 // In Linux, and possibly in other systems as well, we might not be able to
799 // use the global ARGCARGV variables at static init time. However, we may
800 // be lucky and have a file called procselfcmdline that may be read to
801 // determine all of our command-line arguments.
802
803#ifdef HAVE_PROC_CURPROC_CMDLINE
804 pifstream proc("/proc/curproc/cmdline");
805 if (proc.fail()) {
806 cerr << "Cannot read /proc/curproc/cmdline; command-line arguments unavailable to config.\n";
807#else
808 pifstream proc("/proc/self/cmdline");
809 if (proc.fail()) {
810 cerr << "Cannot read /proc/self/cmdline; command-line arguments unavailable to config.\n";
811#endif
812 } else {
813 int ch = proc.get();
814 int index = 0;
815 while (!proc.eof() && !proc.fail()) {
816 string arg;
817
818 while (!proc.eof() && !proc.fail() && ch != '\0') {
819 arg += (char)ch;
820 ch = proc.get();
821 }
822
823 if (index == 0) {
824 if (_binary_name.empty())
825 _binary_name = arg;
826 } else {
827 _args.push_back(arg);
828 }
829 index++;
830
831 ch = proc.get();
832 }
833 }
834#endif
835
836#ifndef _WIN32
837 // Try to use realpath to get cleaner paths.
838
839 if (!_binary_name.empty()) {
840 char newpath [PATH_MAX + 1];
841 if (realpath(_binary_name.c_str(), newpath) != nullptr) {
842 _binary_name = newpath;
843 }
844 }
845
846 if (!_dtool_name.empty()) {
847 char newpath [PATH_MAX + 1];
848 if (realpath(_dtool_name.c_str(), newpath) != nullptr) {
849 _dtool_name = newpath;
850 }
851 }
852#endif // _WIN32
853
854 if (_dtool_name.empty()) {
855 _dtool_name = _binary_name;
856 }
857}
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:44
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
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 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.
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.
void set_wtext(const std::wstring &wtext)
Changes the text that is stored in the encoder.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.