| /** @file | |
| Return the initial module search path. | |
| Search in specified locations for the associated Python libraries. | |
| Py_GetPath returns module_search_path. | |
| Py_GetPrefix returns PREFIX | |
| Py_GetExec_Prefix returns PREFIX | |
| Py_GetProgramFullPath returns the full path to the python executable. | |
| These are built dynamically so that the proper volume name can be prefixed | |
| to the paths. | |
| For the EDK II, UEFI, implementation of Python, PREFIX and EXEC_PREFIX | |
| are set as follows: | |
| PREFIX = /Efi/StdLib | |
| EXEC_PREFIX = PREFIX | |
| The following final paths are assumed: | |
| /Efi/Tools/Python.efi The Python executable. | |
| /Efi/StdLib/lib/python.VERSION The platform independent Python modules. | |
| /Efi/StdLib/lib/python.VERSION/dynalib Dynamically loadable Python extension modules. | |
| Copyright (c) 2011 - 2012, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials are licensed and made available under | |
| the terms and conditions of the BSD License that accompanies this distribution. | |
| The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license. | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include <Python.h> | |
| #include <osdefs.h> | |
| #include <ctype.h> | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| /* VERSION must be at least two characters long. */ | |
| #ifndef VERSION | |
| #define VERSION "27" | |
| #endif | |
| #ifndef VPATH | |
| #define VPATH "." | |
| #endif | |
| /* Search path entry delimiter */ | |
| #ifdef DELIM | |
| #define sDELIM ";" | |
| #endif | |
| #ifndef PREFIX | |
| #define PREFIX "/Efi/StdLib" | |
| #endif | |
| #ifndef EXEC_PREFIX | |
| #define EXEC_PREFIX PREFIX | |
| #endif | |
| #ifndef LIBPYTHON | |
| #define LIBPYTHON "lib/python." VERSION | |
| #endif | |
| #ifndef PYTHONPATH | |
| #ifdef HAVE_ENVIRONMENT_OPS | |
| #define PYTHONPATH PREFIX LIBPYTHON sDELIM \ | |
| EXEC_PREFIX LIBPYTHON "/lib-dynload" | |
| #else | |
| #define PYTHONPATH LIBPYTHON | |
| #endif | |
| #endif | |
| #ifndef LANDMARK | |
| #define LANDMARK "os.py" | |
| #endif | |
| static char prefix[MAXPATHLEN+1]; | |
| static char exec_prefix[MAXPATHLEN+1]; | |
| static char progpath[MAXPATHLEN+1]; | |
| static char *module_search_path = NULL; | |
| static char lib_python[] = LIBPYTHON; | |
| static char volume_name[32] = { 0 }; | |
| /** Determine if "ch" is a separator character. | |
| @param[in] ch The character to test. | |
| @retval TRUE ch is a separator character. | |
| @retval FALSE ch is NOT a separator character. | |
| **/ | |
| static int | |
| is_sep(char ch) | |
| { | |
| #ifdef ALTSEP | |
| return ch == SEP || ch == ALTSEP; | |
| #else | |
| return ch == SEP; | |
| #endif | |
| } | |
| /** Reduce a path by its last element. | |
| The last element (everything to the right of the last separator character) | |
| in the path, dir, is removed from the path. Parameter dir is modified in place. | |
| @param[in,out] dir Pointer to the path to modify. | |
| **/ | |
| static void | |
| reduce(char *dir) | |
| { | |
| size_t i = strlen(dir); | |
| while (i > 0 && !is_sep(dir[i])) | |
| --i; | |
| dir[i] = '\0'; | |
| } | |
| #ifndef UEFI_C_SOURCE | |
| /** Does filename point to a file and not directory? | |
| @param[in] filename The fully qualified path to the object to test. | |
| @retval 0 Filename was not found, or is a directory. | |
| @retval 1 Filename refers to a regular file. | |
| **/ | |
| static int | |
| isfile(char *filename) | |
| { | |
| struct stat buf; | |
| if (stat(filename, &buf) != 0) { | |
| return 0; | |
| } | |
| //if (!S_ISREG(buf.st_mode)) | |
| if (S_ISDIR(buf.st_mode)) { | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| /** Determine if filename refers to a Python module. | |
| A Python module is indicated if the file exists, or if the file with | |
| 'o' or 'c' appended exists. | |
| @param[in] filename The fully qualified path to the object to test. | |
| @retval 0 | |
| **/ | |
| static int | |
| ismodule(char *filename) | |
| { | |
| if (isfile(filename)) { | |
| //if (Py_VerboseFlag) PySys_WriteStderr("%s[%d]: file = \"%s\"\n", __func__, __LINE__, filename); | |
| return 1; | |
| } | |
| /* Check for the compiled version of prefix. */ | |
| if (strlen(filename) < MAXPATHLEN) { | |
| strcat(filename, Py_OptimizeFlag ? "o" : "c"); | |
| if (isfile(filename)) { | |
| return 1; | |
| } | |
| } | |
| return 0; | |
| } | |
| /** Does filename point to a directory? | |
| @param[in] filename The fully qualified path to the object to test. | |
| @retval 0 Filename was not found, or is not a regular file. | |
| @retval 1 Filename refers to a directory. | |
| **/ | |
| static int | |
| isdir(char *filename) | |
| { | |
| struct stat buf; | |
| if (stat(filename, &buf) != 0) | |
| return 0; | |
| if (!S_ISDIR(buf.st_mode)) | |
| return 0; | |
| return 1; | |
| } | |
| #endif /* UEFI_C_SOURCE */ | |
| /** Determine if a path is absolute, or not. | |
| An absolute path consists of a volume name, "VOL:", followed by a rooted path, | |
| "/path/elements". If both of these components are present, the path is absolute. | |
| Let P be a pointer to the path to test. | |
| Let A be a pointer to the first ':' in P. | |
| Let B be a pointer to the first '/' or '\\' in P. | |
| If A and B are not NULL | |
| If (A-P+1) == (B-P) then the path is absolute. | |
| Otherwise, the path is NOT absolute. | |
| @param[in] path The path to test. | |
| @retval -1 Path is absolute but lacking volume name. | |
| @retval 0 Path is NOT absolute. | |
| @retval 1 Path is absolute. | |
| */ | |
| static int | |
| is_absolute(char *path) | |
| { | |
| char *A; | |
| char *B; | |
| A = strchr(path, ':'); | |
| B = strpbrk(path, "/\\"); | |
| if(B != NULL) { | |
| if(A == NULL) { | |
| if(B == path) { | |
| return -1; | |
| } | |
| } | |
| else { | |
| if(((A - path) + 1) == (B - path)) { | |
| return 1; | |
| } | |
| } | |
| } | |
| return 0; | |
| } | |
| /** Add a path component, by appending stuff to buffer. | |
| buffer must have at least MAXPATHLEN + 1 bytes allocated, and contain a | |
| NUL-terminated string with no more than MAXPATHLEN characters (not counting | |
| the trailing NUL). It's a fatal error if it contains a string longer than | |
| that (callers must be careful!). If these requirements are met, it's | |
| guaranteed that buffer will still be a NUL-terminated string with no more | |
| than MAXPATHLEN characters at exit. If stuff is too long, only as much of | |
| stuff as fits will be appended. | |
| @param[in,out] buffer The path to be extended. | |
| @param[in] stuff The stuff to join onto the path. | |
| */ | |
| static void | |
| joinpath(char *buffer, char *stuff) | |
| { | |
| size_t n, k; | |
| k = 0; | |
| if (is_absolute(stuff) == 1) { | |
| n = 0; | |
| } | |
| else { | |
| n = strlen(buffer); | |
| if(n == 0) { | |
| strncpy(buffer, volume_name, MAXPATHLEN); | |
| n = strlen(buffer); | |
| } | |
| /* We must not use an else clause here because we want to test n again. | |
| volume_name may have been empty. | |
| */ | |
| if (n > 0 && n < MAXPATHLEN) { | |
| if(!is_sep(buffer[n-1])) { | |
| buffer[n++] = SEP; | |
| } | |
| if(is_sep(stuff[0])) ++stuff; | |
| } | |
| } | |
| if (n > MAXPATHLEN) | |
| Py_FatalError("buffer overflow in getpath.c's joinpath()"); | |
| k = strlen(stuff); | |
| if (n + k > MAXPATHLEN) | |
| k = MAXPATHLEN - n; | |
| strncpy(buffer+n, stuff, k); | |
| buffer[n+k] = '\0'; | |
| } | |
| /** Is filename an executable file? | |
| An executable file: | |
| 1) exists | |
| 2) is a file, not a directory | |
| 3) has a name ending with ".efi" | |
| 4) Only has a single '.' in the name. | |
| If basename(filename) does not contain a '.', append ".efi" to filename | |
| If filename ends in ".efi", it is executable, else it isn't. | |
| This routine is used to when searching for the file named by argv[0]. | |
| As such, there is no need to search for extensions other than ".efi". | |
| @param[in] filename The name of the file to test. It may, or may not, have an extension. | |
| @retval 0 filename already has a path other than ".efi", or it doesn't exist, or is a directory. | |
| @retval 1 filename refers to an executable file. | |
| **/ | |
| static int | |
| isxfile(char *filename) | |
| { | |
| struct stat buf; | |
| char *bn; | |
| char *newbn; | |
| int bnlen; | |
| bn = basename(filename); // Separate off the file name component | |
| reduce(filename); // and isolate the path component | |
| bnlen = strlen(bn); | |
| newbn = strrchr(bn, '.'); // Does basename contain a period? | |
| if(newbn == NULL) { // Does NOT contain a period. | |
| newbn = &bn[bnlen]; | |
| strncpyX(newbn, ".efi", MAXPATHLEN - bnlen); // append ".efi" to basename | |
| bnlen += 4; | |
| } | |
| else if(strcmp(newbn, ".efi") != 0) { | |
| return 0; // File can not be executable. | |
| } | |
| joinpath(filename, bn); // Stitch path and file name back together | |
| if (stat(filename, &buf) != 0) { // Now, verify that file exists | |
| return 0; | |
| } | |
| if(S_ISDIR(buf.st_mode)) { // And it is not a directory. | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| /** Copy p into path, ensuring that the result is an absolute path. | |
| copy_absolute requires that path be allocated at least | |
| MAXPATHLEN + 1 bytes and that p be no more than MAXPATHLEN bytes. | |
| @param[out] path Destination to receive the absolute path. | |
| @param[in] p Path to be tested and possibly converted. | |
| **/ | |
| static void | |
| copy_absolute(char *path, char *p) | |
| { | |
| if (is_absolute(p) == 1) | |
| strcpy(path, p); | |
| else { | |
| if (!getcwd(path, MAXPATHLEN)) { | |
| /* unable to get the current directory */ | |
| if(volume_name[0] != 0) { | |
| strcpy(path, volume_name); | |
| joinpath(path, p); | |
| } | |
| else | |
| strcpy(path, p); | |
| return; | |
| } | |
| if (p[0] == '.' && is_sep(p[1])) | |
| p += 2; | |
| joinpath(path, p); | |
| } | |
| } | |
| /** Modify path so that the result is an absolute path. | |
| absolutize() requires that path be allocated at least MAXPATHLEN+1 bytes. | |
| @param[in,out] path The path to be made absolute. | |
| */ | |
| static void | |
| absolutize(char *path) | |
| { | |
| char buffer[MAXPATHLEN + 1]; | |
| if (is_absolute(path) == 1) | |
| return; | |
| copy_absolute(buffer, path); | |
| strcpy(path, buffer); | |
| } | |
| /** Extract the volume name from a path. | |
| @param[out] Dest Pointer to location in which to store the extracted volume name. | |
| @param[in] path Pointer to the path to extract the volume name from. | |
| **/ | |
| static void | |
| set_volume(char *Dest, char *path) | |
| { | |
| size_t VolLen; | |
| if(is_absolute(path)) { | |
| VolLen = strcspn(path, "/\\:"); | |
| if((VolLen != 0) && (path[VolLen] == ':')) { | |
| (void) strncpyX(Dest, path, VolLen + 1); | |
| } | |
| } | |
| } | |
| /** Determine paths. | |
| Two directories must be found, the platform independent directory | |
| (prefix), containing the common .py and .pyc files, and the platform | |
| dependent directory (exec_prefix), containing the shared library | |
| modules. Note that prefix and exec_prefix are the same directory | |
| for UEFI installations. | |
| Separate searches are carried out for prefix and exec_prefix. | |
| Each search tries a number of different locations until a ``landmark'' | |
| file or directory is found. If no prefix or exec_prefix is found, a | |
| warning message is issued and the preprocessor defined PREFIX and | |
| EXEC_PREFIX are used (even though they may not work); python carries on | |
| as best as is possible, but some imports may fail. | |
| Before any searches are done, the location of the executable is | |
| determined. If argv[0] has one or more slashes in it, it is used | |
| unchanged. Otherwise, it must have been invoked from the shell's path, | |
| so we search %PATH% for the named executable and use that. If the | |
| executable was not found on %PATH% (or there was no %PATH% environment | |
| variable), the original argv[0] string is used. | |
| Finally, argv0_path is set to the directory containing the executable | |
| (i.e. the last component is stripped). | |
| With argv0_path in hand, we perform a number of steps. The same steps | |
| are performed for prefix and for exec_prefix, but with a different | |
| landmark. | |
| The prefix landmark will always be lib/python.VERSION/os.py and the | |
| exec_prefix will always be lib/python.VERSION/dynaload, where VERSION | |
| is Python's version number as defined at the beginning of this file. | |
| First. See if the %PYTHONHOME% environment variable points to the | |
| installed location of the Python libraries. If %PYTHONHOME% is set, then | |
| it points to prefix and exec_prefix. %PYTHONHOME% can be a single | |
| directory, which is used for both, or the prefix and exec_prefix | |
| directories separated by the DELIM character. | |
| Next. Search the directories pointed to by the preprocessor variables | |
| PREFIX and EXEC_PREFIX. These paths are prefixed with the volume name | |
| extracted from argv0_path. The volume names correspond to the UEFI | |
| shell "map" names. | |
| That's it! | |
| Well, almost. Once we have determined prefix and exec_prefix, the | |
| preprocessor variable PYTHONPATH is used to construct a path. Each | |
| relative path on PYTHONPATH is prefixed with prefix. Then the directory | |
| containing the shared library modules is appended. The environment | |
| variable $PYTHONPATH is inserted in front of it all. Finally, the | |
| prefix and exec_prefix globals are tweaked so they reflect the values | |
| expected by other code, by stripping the "lib/python$VERSION/..." stuff | |
| off. This seems to make more sense given that currently the only | |
| known use of sys.prefix and sys.exec_prefix is for the ILU installation | |
| process to find the installed Python tree. | |
| The final, fully resolved, paths should look something like: | |
| fs0:/Efi/Tools/python.efi | |
| fs0:/Efi/StdLib/lib/python27 | |
| fs0:/Efi/StdLib/lib/python27/dynaload | |
| **/ | |
| static void | |
| calculate_path(void) | |
| { | |
| extern char *Py_GetProgramName(void); | |
| static char delimiter[2] = {DELIM, '\0'}; | |
| static char separator[2] = {SEP, '\0'}; | |
| char *pythonpath = PYTHONPATH; | |
| char *rtpypath = Py_GETENV("PYTHONPATH"); | |
| //char *home = Py_GetPythonHome(); | |
| char *path = getenv("PATH"); | |
| char *prog = Py_GetProgramName(); | |
| char argv0_path[MAXPATHLEN+1]; | |
| char zip_path[MAXPATHLEN+1]; | |
| char *buf; | |
| size_t bufsz; | |
| size_t prefixsz; | |
| char *defpath; | |
| /* ########################################################################### | |
| Determine path to the Python.efi binary. | |
| Produces progpath, argv0_path, and volume_name. | |
| ########################################################################### */ | |
| /* If there is no slash in the argv0 path, then we have to | |
| * assume python is on the user's $PATH, since there's no | |
| * other way to find a directory to start the search from. If | |
| * $PATH isn't exported, you lose. | |
| */ | |
| if (strchr(prog, SEP)) | |
| strncpy(progpath, prog, MAXPATHLEN); | |
| else if (path) { | |
| while (1) { | |
| char *delim = strchr(path, DELIM); | |
| if (delim) { | |
| size_t len = delim - path; | |
| if (len > MAXPATHLEN) | |
| len = MAXPATHLEN; | |
| strncpy(progpath, path, len); | |
| *(progpath + len) = '\0'; | |
| } | |
| else | |
| strncpy(progpath, path, MAXPATHLEN); | |
| joinpath(progpath, prog); | |
| if (isxfile(progpath)) | |
| break; | |
| if (!delim) { | |
| progpath[0] = '\0'; | |
| break; | |
| } | |
| path = delim + 1; | |
| } | |
| } | |
| else | |
| progpath[0] = '\0'; | |
| if ( (!is_absolute(progpath)) && (progpath[0] != '\0') ) | |
| absolutize(progpath); | |
| strncpy(argv0_path, progpath, MAXPATHLEN); | |
| argv0_path[MAXPATHLEN] = '\0'; | |
| set_volume(volume_name, argv0_path); | |
| reduce(argv0_path); | |
| /* At this point, argv0_path is guaranteed to be less than | |
| MAXPATHLEN bytes long. | |
| */ | |
| /* ########################################################################### | |
| Build the FULL prefix string, including volume name. | |
| This is the full path to the platform independent libraries. | |
| ########################################################################### */ | |
| strncpy(prefix, volume_name, MAXPATHLEN); | |
| joinpath(prefix, PREFIX); | |
| joinpath(prefix, lib_python); | |
| /* ########################################################################### | |
| Build the FULL path to the zipped-up Python library. | |
| ########################################################################### */ | |
| strncpy(zip_path, prefix, MAXPATHLEN); | |
| zip_path[MAXPATHLEN] = '\0'; | |
| reduce(zip_path); | |
| joinpath(zip_path, "python00.zip"); | |
| bufsz = strlen(zip_path); /* Replace "00" with version */ | |
| zip_path[bufsz - 6] = VERSION[0]; | |
| zip_path[bufsz - 5] = VERSION[1]; | |
| /* ########################################################################### | |
| Build the FULL path to dynamically loadable libraries. | |
| ########################################################################### */ | |
| strncpy(exec_prefix, volume_name, MAXPATHLEN); | |
| joinpath(exec_prefix, EXEC_PREFIX); | |
| joinpath(exec_prefix, lib_python); | |
| joinpath(exec_prefix, "lib-dynload"); | |
| /* ########################################################################### | |
| Build the module search path. | |
| ########################################################################### */ | |
| /* Reduce prefix and exec_prefix to their essence, | |
| * e.g. /usr/local/lib/python1.5 is reduced to /usr/local. | |
| * If we're loading relative to the build directory, | |
| * return the compiled-in defaults instead. | |
| */ | |
| reduce(prefix); | |
| reduce(prefix); | |
| /* The prefix is the root directory, but reduce() chopped | |
| * off the "/". */ | |
| if (!prefix[0]) { | |
| strcpy(prefix, volume_name); | |
| } | |
| bufsz = strlen(prefix); | |
| if(prefix[bufsz-1] == ':') { | |
| prefix[bufsz] = SEP; | |
| prefix[bufsz+1] = 0; | |
| } | |
| /* Calculate size of return buffer. | |
| */ | |
| defpath = pythonpath; | |
| bufsz = 0; | |
| if (rtpypath) | |
| bufsz += strlen(rtpypath) + 1; | |
| prefixsz = strlen(prefix) + 1; | |
| while (1) { | |
| char *delim = strchr(defpath, DELIM); | |
| if (is_absolute(defpath) == 0) | |
| /* Paths are relative to prefix */ | |
| bufsz += prefixsz; | |
| if (delim) | |
| bufsz += delim - defpath + 1; | |
| else { | |
| bufsz += strlen(defpath) + 1; | |
| break; | |
| } | |
| defpath = delim + 1; | |
| } | |
| bufsz += strlen(zip_path) + 1; | |
| bufsz += strlen(exec_prefix) + 1; | |
| /* This is the only malloc call in this file */ | |
| buf = (char *)PyMem_Malloc(bufsz); | |
| if (buf == NULL) { | |
| /* We can't exit, so print a warning and limp along */ | |
| fprintf(stderr, "Not enough memory for dynamic PYTHONPATH.\n"); | |
| fprintf(stderr, "Using default static PYTHONPATH.\n"); | |
| module_search_path = PYTHONPATH; | |
| } | |
| else { | |
| /* Run-time value of $PYTHONPATH goes first */ | |
| if (rtpypath) { | |
| strcpy(buf, rtpypath); | |
| strcat(buf, delimiter); | |
| } | |
| else | |
| buf[0] = '\0'; | |
| /* Next is the default zip path */ | |
| strcat(buf, zip_path); | |
| strcat(buf, delimiter); | |
| /* Next goes merge of compile-time $PYTHONPATH with | |
| * dynamically located prefix. | |
| */ | |
| defpath = pythonpath; | |
| while (1) { | |
| char *delim = strchr(defpath, DELIM); | |
| if (is_absolute(defpath) != 1) { | |
| strcat(buf, prefix); | |
| strcat(buf, separator); | |
| } | |
| if (delim) { | |
| size_t len = delim - defpath + 1; | |
| size_t end = strlen(buf) + len; | |
| strncat(buf, defpath, len); | |
| *(buf + end) = '\0'; | |
| } | |
| else { | |
| strcat(buf, defpath); | |
| break; | |
| } | |
| defpath = delim + 1; | |
| } | |
| strcat(buf, delimiter); | |
| /* Finally, on goes the directory for dynamic-load modules */ | |
| strcat(buf, exec_prefix); | |
| /* And publish the results */ | |
| module_search_path = buf; | |
| } | |
| /* At this point, exec_prefix is set to VOL:/Efi/StdLib/lib/python.27/dynalib. | |
| We want to get back to the root value, so we have to remove the final three | |
| segments to get VOL:/Efi/StdLib. Because we don't know what VOL is, and | |
| EXEC_PREFIX is also indeterminate, we just remove the three final segments. | |
| */ | |
| reduce(exec_prefix); | |
| reduce(exec_prefix); | |
| reduce(exec_prefix); | |
| if (!exec_prefix[0]) { | |
| strcpy(exec_prefix, volume_name); | |
| } | |
| bufsz = strlen(exec_prefix); | |
| if(exec_prefix[bufsz-1] == ':') { | |
| exec_prefix[bufsz] = SEP; | |
| exec_prefix[bufsz+1] = 0; | |
| } | |
| if (Py_VerboseFlag) PySys_WriteStderr("%s[%d]: module_search_path = \"%s\"\n", __func__, __LINE__, module_search_path); | |
| if (Py_VerboseFlag) PySys_WriteStderr("%s[%d]: prefix = \"%s\"\n", __func__, __LINE__, prefix); | |
| if (Py_VerboseFlag) PySys_WriteStderr("%s[%d]: exec_prefix = \"%s\"\n", __func__, __LINE__, exec_prefix); | |
| if (Py_VerboseFlag) PySys_WriteStderr("%s[%d]: progpath = \"%s\"\n", __func__, __LINE__, progpath); | |
| } | |
| /* External interface */ | |
| char * | |
| Py_GetPath(void) | |
| { | |
| if (!module_search_path) | |
| calculate_path(); | |
| return module_search_path; | |
| } | |
| char * | |
| Py_GetPrefix(void) | |
| { | |
| if (!module_search_path) | |
| calculate_path(); | |
| return prefix; | |
| } | |
| char * | |
| Py_GetExecPrefix(void) | |
| { | |
| if (!module_search_path) | |
| calculate_path(); | |
| return exec_prefix; | |
| } | |
| char * | |
| Py_GetProgramFullPath(void) | |
| { | |
| if (!module_search_path) | |
| calculate_path(); | |
| return progpath; | |
| } | |
| #ifdef __cplusplus | |
| } | |
| #endif | |