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
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#endif // _WIN32
368
369 return string();
370}
371
372/**
373 * Changes the definition of the indicated environment variable. The
374 * nonstatic implementation.
375 */
376void ExecutionEnvironment::
377ns_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 */
390void ExecutionEnvironment::
391ns_shadow_environment_variable(const string &var, const string &value) {
392 _variables[var] = value;
393 string putstr = var + "=" + value;
394}
395
396/**
397 *
398 */
399void ExecutionEnvironment::
400ns_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 */
421size_t ExecutionEnvironment::
422ns_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 */
431string ExecutionEnvironment::
432ns_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 */
441string ExecutionEnvironment::
442ns_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 */
453string ExecutionEnvironment::
454ns_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 */
465ExecutionEnvironment *ExecutionEnvironment::
466get_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 */
478void ExecutionEnvironment::
479read_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 */
548void ExecutionEnvironment::
549read_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.