1/* Path configuration like module_search_path (sys.path) */ 2 3#include "Python.h" 4#include "marshal.h" // PyMarshal_ReadObjectFromString 5#include "osdefs.h" // DELIM 6#include "pycore_initconfig.h" 7#include "pycore_fileutils.h" 8#include "pycore_pathconfig.h" 9#include "pycore_pymem.h" // _PyMem_SetDefaultAllocator() 10#include <wchar.h> 11#ifdef MS_WINDOWS 12# include <windows.h> // GetFullPathNameW(), MAX_PATH 13# include <pathcch.h> 14# include <shlwapi.h> 15#endif 16 17#ifdef __cplusplus 18extern "C" { 19#endif 20 21 22/* External interface */ 23 24/* Stored values set by C API functions */ 25typedef struct _PyPathConfig { 26 /* Full path to the Python program */ 27 wchar_t *program_full_path; 28 wchar_t *prefix; 29 wchar_t *exec_prefix; 30 wchar_t *stdlib_dir; 31 /* Set by Py_SetPath */ 32 wchar_t *module_search_path; 33 /* Set by _PyPathConfig_UpdateGlobal */ 34 wchar_t *calculated_module_search_path; 35 /* Python program name */ 36 wchar_t *program_name; 37 /* Set by Py_SetPythonHome() or PYTHONHOME environment variable */ 38 wchar_t *home; 39 int _is_python_build; 40} _PyPathConfig; 41 42# define _PyPathConfig_INIT \ 43 {.module_search_path = NULL, ._is_python_build = 0} 44 45 46_PyPathConfig _Py_path_config = _PyPathConfig_INIT; 47 48 49const wchar_t * 50_PyPathConfig_GetGlobalModuleSearchPath(void) 51{ 52 return _Py_path_config.module_search_path; 53} 54 55 56void 57_PyPathConfig_ClearGlobal(void) 58{ 59 PyMemAllocatorEx old_alloc; 60 _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 61 62#define CLEAR(ATTR) \ 63 do { \ 64 PyMem_RawFree(_Py_path_config.ATTR); \ 65 _Py_path_config.ATTR = NULL; \ 66 } while (0) 67 68 CLEAR(program_full_path); 69 CLEAR(prefix); 70 CLEAR(exec_prefix); 71 CLEAR(stdlib_dir); 72 CLEAR(module_search_path); 73 CLEAR(calculated_module_search_path); 74 CLEAR(program_name); 75 CLEAR(home); 76 _Py_path_config._is_python_build = 0; 77 78#undef CLEAR 79 80 PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 81} 82 83PyStatus 84_PyPathConfig_ReadGlobal(PyConfig *config) 85{ 86 PyStatus status = _PyStatus_OK(); 87 88#define COPY(ATTR) \ 89 do { \ 90 if (_Py_path_config.ATTR && !config->ATTR) { \ 91 status = PyConfig_SetString(config, &config->ATTR, _Py_path_config.ATTR); \ 92 if (_PyStatus_EXCEPTION(status)) goto done; \ 93 } \ 94 } while (0) 95 96#define COPY2(ATTR, SRCATTR) \ 97 do { \ 98 if (_Py_path_config.SRCATTR && !config->ATTR) { \ 99 status = PyConfig_SetString(config, &config->ATTR, _Py_path_config.SRCATTR); \ 100 if (_PyStatus_EXCEPTION(status)) goto done; \ 101 } \ 102 } while (0) 103 104#define COPY_INT(ATTR) \ 105 do { \ 106 assert(_Py_path_config.ATTR >= 0); \ 107 if ((_Py_path_config.ATTR >= 0) && (config->ATTR <= 0)) { \ 108 config->ATTR = _Py_path_config.ATTR; \ 109 } \ 110 } while (0) 111 112 COPY(prefix); 113 COPY(exec_prefix); 114 COPY(stdlib_dir); 115 COPY(program_name); 116 COPY(home); 117 COPY2(executable, program_full_path); 118 COPY_INT(_is_python_build); 119 // module_search_path must be initialised - not read 120#undef COPY 121#undef COPY2 122#undef COPY_INT 123 124done: 125 return status; 126} 127 128PyStatus 129_PyPathConfig_UpdateGlobal(const PyConfig *config) 130{ 131 PyMemAllocatorEx old_alloc; 132 _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 133 134#define COPY(ATTR) \ 135 do { \ 136 if (config->ATTR) { \ 137 PyMem_RawFree(_Py_path_config.ATTR); \ 138 _Py_path_config.ATTR = _PyMem_RawWcsdup(config->ATTR); \ 139 if (!_Py_path_config.ATTR) goto error; \ 140 } \ 141 } while (0) 142 143#define COPY2(ATTR, SRCATTR) \ 144 do { \ 145 if (config->SRCATTR) { \ 146 PyMem_RawFree(_Py_path_config.ATTR); \ 147 _Py_path_config.ATTR = _PyMem_RawWcsdup(config->SRCATTR); \ 148 if (!_Py_path_config.ATTR) goto error; \ 149 } \ 150 } while (0) 151 152#define COPY_INT(ATTR) \ 153 do { \ 154 if (config->ATTR > 0) { \ 155 _Py_path_config.ATTR = config->ATTR; \ 156 } \ 157 } while (0) 158 159 COPY(prefix); 160 COPY(exec_prefix); 161 COPY(stdlib_dir); 162 COPY(program_name); 163 COPY(home); 164 COPY2(program_full_path, executable); 165 COPY_INT(_is_python_build); 166#undef COPY 167#undef COPY2 168#undef COPY_INT 169 170 PyMem_RawFree(_Py_path_config.module_search_path); 171 _Py_path_config.module_search_path = NULL; 172 PyMem_RawFree(_Py_path_config.calculated_module_search_path); 173 _Py_path_config.calculated_module_search_path = NULL; 174 175 do { 176 size_t cch = 1; 177 for (Py_ssize_t i = 0; i < config->module_search_paths.length; ++i) { 178 cch += 1 + wcslen(config->module_search_paths.items[i]); 179 } 180 181 wchar_t *path = (wchar_t*)PyMem_RawMalloc(sizeof(wchar_t) * cch); 182 if (!path) { 183 goto error; 184 } 185 wchar_t *p = path; 186 for (Py_ssize_t i = 0; i < config->module_search_paths.length; ++i) { 187 wcscpy(p, config->module_search_paths.items[i]); 188 p = wcschr(p, L'\0'); 189 *p++ = DELIM; 190 *p = L'\0'; 191 } 192 193 do { 194 *p = L'\0'; 195 } while (p != path && *--p == DELIM); 196 _Py_path_config.calculated_module_search_path = path; 197 } while (0); 198 199 PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 200 return _PyStatus_OK(); 201 202error: 203 PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 204 return _PyStatus_NO_MEMORY(); 205} 206 207 208static void _Py_NO_RETURN 209path_out_of_memory(const char *func) 210{ 211 _Py_FatalErrorFunc(func, "out of memory"); 212} 213 214void 215Py_SetPath(const wchar_t *path) 216{ 217 if (path == NULL) { 218 _PyPathConfig_ClearGlobal(); 219 return; 220 } 221 222 PyMemAllocatorEx old_alloc; 223 _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 224 225 PyMem_RawFree(_Py_path_config.prefix); 226 PyMem_RawFree(_Py_path_config.exec_prefix); 227 PyMem_RawFree(_Py_path_config.stdlib_dir); 228 PyMem_RawFree(_Py_path_config.module_search_path); 229 PyMem_RawFree(_Py_path_config.calculated_module_search_path); 230 231 _Py_path_config.prefix = _PyMem_RawWcsdup(L""); 232 _Py_path_config.exec_prefix = _PyMem_RawWcsdup(L""); 233 // XXX Copy this from the new module_search_path? 234 if (_Py_path_config.home != NULL) { 235 _Py_path_config.stdlib_dir = _PyMem_RawWcsdup(_Py_path_config.home); 236 } 237 else { 238 _Py_path_config.stdlib_dir = _PyMem_RawWcsdup(L""); 239 } 240 _Py_path_config.module_search_path = _PyMem_RawWcsdup(path); 241 _Py_path_config.calculated_module_search_path = NULL; 242 243 PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 244 245 if (_Py_path_config.prefix == NULL 246 || _Py_path_config.exec_prefix == NULL 247 || _Py_path_config.stdlib_dir == NULL 248 || _Py_path_config.module_search_path == NULL) 249 { 250 path_out_of_memory(__func__); 251 } 252} 253 254 255void 256Py_SetPythonHome(const wchar_t *home) 257{ 258 int has_value = home && home[0]; 259 260 PyMemAllocatorEx old_alloc; 261 _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 262 263 PyMem_RawFree(_Py_path_config.home); 264 _Py_path_config.home = NULL; 265 266 if (has_value) { 267 _Py_path_config.home = _PyMem_RawWcsdup(home); 268 } 269 270 PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 271 272 if (has_value && _Py_path_config.home == NULL) { 273 path_out_of_memory(__func__); 274 } 275} 276 277 278void 279Py_SetProgramName(const wchar_t *program_name) 280{ 281 int has_value = program_name && program_name[0]; 282 283 PyMemAllocatorEx old_alloc; 284 _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 285 286 PyMem_RawFree(_Py_path_config.program_name); 287 _Py_path_config.program_name = NULL; 288 289 if (has_value) { 290 _Py_path_config.program_name = _PyMem_RawWcsdup(program_name); 291 } 292 293 PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 294 295 if (has_value && _Py_path_config.program_name == NULL) { 296 path_out_of_memory(__func__); 297 } 298} 299 300void 301_Py_SetProgramFullPath(const wchar_t *program_full_path) 302{ 303 int has_value = program_full_path && program_full_path[0]; 304 305 PyMemAllocatorEx old_alloc; 306 _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 307 308 PyMem_RawFree(_Py_path_config.program_full_path); 309 _Py_path_config.program_full_path = NULL; 310 311 if (has_value) { 312 _Py_path_config.program_full_path = _PyMem_RawWcsdup(program_full_path); 313 } 314 315 PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); 316 317 if (has_value && _Py_path_config.program_full_path == NULL) { 318 path_out_of_memory(__func__); 319 } 320} 321 322 323wchar_t * 324Py_GetPath(void) 325{ 326 /* If the user has provided a path, return that */ 327 if (_Py_path_config.module_search_path) { 328 return _Py_path_config.module_search_path; 329 } 330 /* If we have already done calculations, return the calculated path */ 331 return _Py_path_config.calculated_module_search_path; 332} 333 334 335wchar_t * 336_Py_GetStdlibDir(void) 337{ 338 wchar_t *stdlib_dir = _Py_path_config.stdlib_dir; 339 if (stdlib_dir != NULL && stdlib_dir[0] != L'\0') { 340 return stdlib_dir; 341 } 342 return NULL; 343} 344 345 346wchar_t * 347Py_GetPrefix(void) 348{ 349 return _Py_path_config.prefix; 350} 351 352 353wchar_t * 354Py_GetExecPrefix(void) 355{ 356 return _Py_path_config.exec_prefix; 357} 358 359 360wchar_t * 361Py_GetProgramFullPath(void) 362{ 363 return _Py_path_config.program_full_path; 364} 365 366 367wchar_t* 368Py_GetPythonHome(void) 369{ 370 return _Py_path_config.home; 371} 372 373 374wchar_t * 375Py_GetProgramName(void) 376{ 377 return _Py_path_config.program_name; 378} 379 380 381 382/* Compute module search path from argv[0] or the current working 383 directory ("-m module" case) which will be prepended to sys.argv: 384 sys.path[0]. 385 386 Return 1 if the path is correctly resolved and written into *path0_p. 387 388 Return 0 if it fails to resolve the full path. For example, return 0 if the 389 current working directory has been removed (bpo-36236) or if argv is empty. 390 391 Raise an exception and return -1 on error. 392 */ 393int 394_PyPathConfig_ComputeSysPath0(const PyWideStringList *argv, PyObject **path0_p) 395{ 396 assert(_PyWideStringList_CheckConsistency(argv)); 397 398 if (argv->length == 0) { 399 /* Leave sys.path unchanged if sys.argv is empty */ 400 return 0; 401 } 402 403 wchar_t *argv0 = argv->items[0]; 404 int have_module_arg = (wcscmp(argv0, L"-m") == 0); 405 int have_script_arg = (!have_module_arg && (wcscmp(argv0, L"-c") != 0)); 406 407 wchar_t *path0 = argv0; 408 Py_ssize_t n = 0; 409 410#ifdef HAVE_REALPATH 411 wchar_t fullpath[MAXPATHLEN]; 412#elif defined(MS_WINDOWS) 413 wchar_t fullpath[MAX_PATH]; 414#endif 415 416 if (have_module_arg) { 417#if defined(HAVE_REALPATH) || defined(MS_WINDOWS) 418 if (!_Py_wgetcwd(fullpath, Py_ARRAY_LENGTH(fullpath))) { 419 return 0; 420 } 421 path0 = fullpath; 422#else 423 path0 = L"."; 424#endif 425 n = wcslen(path0); 426 } 427 428#ifdef HAVE_READLINK 429 wchar_t link[MAXPATHLEN + 1]; 430 int nr = 0; 431 wchar_t path0copy[2 * MAXPATHLEN + 1]; 432 433 if (have_script_arg) { 434 nr = _Py_wreadlink(path0, link, Py_ARRAY_LENGTH(link)); 435 } 436 if (nr > 0) { 437 /* It's a symlink */ 438 link[nr] = '\0'; 439 if (link[0] == SEP) { 440 path0 = link; /* Link to absolute path */ 441 } 442 else if (wcschr(link, SEP) == NULL) { 443 /* Link without path */ 444 } 445 else { 446 /* Must join(dirname(path0), link) */ 447 wchar_t *q = wcsrchr(path0, SEP); 448 if (q == NULL) { 449 /* path0 without path */ 450 path0 = link; 451 } 452 else { 453 /* Must make a copy, path0copy has room for 2 * MAXPATHLEN */ 454 wcsncpy(path0copy, path0, MAXPATHLEN); 455 q = wcsrchr(path0copy, SEP); 456 wcsncpy(q+1, link, MAXPATHLEN); 457 q[MAXPATHLEN + 1] = L'\0'; 458 path0 = path0copy; 459 } 460 } 461 } 462#endif /* HAVE_READLINK */ 463 464 wchar_t *p = NULL; 465 466#if SEP == '\\' 467 /* Special case for Microsoft filename syntax */ 468 if (have_script_arg) { 469 wchar_t *q; 470#if defined(MS_WINDOWS) 471 /* Replace the first element in argv with the full path. */ 472 wchar_t *ptemp; 473 if (GetFullPathNameW(path0, 474 Py_ARRAY_LENGTH(fullpath), 475 fullpath, 476 &ptemp)) { 477 path0 = fullpath; 478 } 479#endif 480 p = wcsrchr(path0, SEP); 481 /* Test for alternate separator */ 482 q = wcsrchr(p ? p : path0, '/'); 483 if (q != NULL) 484 p = q; 485 if (p != NULL) { 486 n = p + 1 - path0; 487 if (n > 1 && p[-1] != ':') 488 n--; /* Drop trailing separator */ 489 } 490 } 491#else 492 /* All other filename syntaxes */ 493 if (have_script_arg) { 494#if defined(HAVE_REALPATH) 495 if (_Py_wrealpath(path0, fullpath, Py_ARRAY_LENGTH(fullpath))) { 496 path0 = fullpath; 497 } 498#endif 499 p = wcsrchr(path0, SEP); 500 } 501 if (p != NULL) { 502 n = p + 1 - path0; 503#if SEP == '/' /* Special case for Unix filename syntax */ 504 if (n > 1) { 505 /* Drop trailing separator */ 506 n--; 507 } 508#endif /* Unix */ 509 } 510#endif /* All others */ 511 512 PyObject *path0_obj = PyUnicode_FromWideChar(path0, n); 513 if (path0_obj == NULL) { 514 return -1; 515 } 516 517 *path0_p = path0_obj; 518 return 1; 519} 520 521 522#ifdef __cplusplus 523} 524#endif 525