/* * launcher.c * Copyright (C) 2017 Kovid Goyal * * Distributed under terms of the GPL3 license. */ #include #include #include #include #include #include #include #ifdef __APPLE__ #include #include #include #else #include #endif #include #include #include #ifndef KITTY_LIB_PATH #define KITTY_LIB_PATH "../.." #endif #ifndef KITTY_LIB_DIR_NAME #define KITTY_LIB_DIR_NAME "lib" #endif #ifndef __FreeBSD__ static inline bool safe_realpath(const char* src, char *buf, size_t buf_sz) { char* ans = realpath(src, NULL); if (ans == NULL) return false; snprintf(buf, buf_sz, "%s", ans); free(ans); return true; } #endif static inline bool set_xoptions(const char *exe_dir_c, const char *lc_ctype, bool from_source) { wchar_t *exe_dir = Py_DecodeLocale(exe_dir_c, NULL); if (exe_dir == NULL) { fprintf(stderr, "Fatal error: cannot decode exe_dir: %s\n", exe_dir_c); return false; } wchar_t buf[PATH_MAX+1] = {0}; swprintf(buf, PATH_MAX, L"bundle_exe_dir=%ls", exe_dir); PySys_AddXOption(buf); PyMem_RawFree(exe_dir); if (from_source) PySys_AddXOption(L"kitty_from_source=1"); if (lc_ctype) { swprintf(buf, PATH_MAX, L"lc_ctype_before_python=%s", lc_ctype); PySys_AddXOption(buf); } return true; } typedef struct { const char *exe, *exe_dir, *lc_ctype, *lib_dir; char **argv; int argc; } RunData; #ifdef FOR_BUNDLE #include static int run_embedded(const RunData run_data) { bypy_pre_initialize_interpreter(false); wchar_t extensions_dir[PATH_MAX+1] = {0}, python_home[PATH_MAX+1] = {0}; #ifdef __APPLE__ const char *python_relpath = "../Resources/Python/lib"; #else const char *python_relpath = "../" KITTY_LIB_DIR_NAME; #endif int num = swprintf(extensions_dir, PATH_MAX, L"%s/%s/kitty-extensions", run_data.exe_dir, python_relpath); if (num < 0 || num >= PATH_MAX) { fprintf(stderr, "Failed to create path to extensions_dir: %s/%s\n", run_data.exe_dir, python_relpath); return 1; } num = swprintf(python_home, PATH_MAX, L"%s/%s/python%s", run_data.exe_dir, python_relpath, PYVER); if (num < 0 || num >= PATH_MAX) { fprintf(stderr, "Failed to create path to python home: %s/%s\n", run_data.exe_dir, python_relpath); return 1; } bypy_initialize_interpreter(L"kitty", python_home, L"kitty_main", extensions_dir, run_data.argc, run_data.argv); if (!set_xoptions(run_data.exe_dir, run_data.lc_ctype, false)) return 1; set_sys_bool("frozen", true); set_sys_string("kitty_extensions_dir", extensions_dir); return bypy_run_interpreter(); } #else static int free_argv(wchar_t **argv) { wchar_t **p = argv; while (*p) { PyMem_RawFree(*p); p++; } free(argv); return 1; } static int run_embedded(const RunData run_data) { bool from_source = false; #ifdef FROM_SOURCE from_source = true; #endif if (!set_xoptions(run_data.exe_dir, run_data.lc_ctype, from_source)) return 1; int argc = run_data.argc + 1; wchar_t **argv = calloc(argc, sizeof(wchar_t*)); if (!argv) { fprintf(stderr, "Out of memory creating argv\n"); return 1; } argv[0] = Py_DecodeLocale(run_data.exe, NULL); if (!argv[0]) { fprintf(stderr, "Failed to decode path to exe\n"); return free_argv(argv); } argv[1] = Py_DecodeLocale(run_data.lib_dir, NULL); if (!argv[1]) { fprintf(stderr, "Failed to decode path to lib_dir\n"); return free_argv(argv); } for (int i=1; i < run_data.argc; i++) { argv[i+1] = Py_DecodeLocale(run_data.argv[i], NULL); if (!argv[i+1]) { fprintf(stderr, "Failed to decode the command line argument: %s\n", run_data.argv[i]); return free_argv(argv); } } int ret = Py_Main(argc, argv); // we cannot free argv properly as Py_Main odifies it free(argv); return ret; } #endif // read_exe_path() {{{ #ifdef __APPLE__ static inline bool read_exe_path(char *exe, size_t buf_sz) { (void)buf_sz; uint32_t size = PATH_MAX; char apple[PATH_MAX+1] = {0}; if (_NSGetExecutablePath(apple, &size) != 0) { fprintf(stderr, "Failed to get path to executable\n"); return false; } if (!safe_realpath(apple, exe, buf_sz)) { fprintf(stderr, "realpath() failed on the executable's path\n"); return false; } return true; } #elif defined(__FreeBSD__) #include #include static inline bool read_exe_path(char *exe, size_t buf_sz) { int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; size_t length = buf_sz; int error = sysctl(name, 4, exe, &length, NULL, 0); if (error < 0 || length <= 1) { fprintf(stderr, "failed to get path to executable, sysctl() failed\n"); return false; } return true; } #elif defined(__NetBSD__) static inline bool read_exe_path(char *exe, size_t buf_sz) { if (!safe_realpath("/proc/curproc/exe", exe, buf_sz)) { fprintf(stderr, "Failed to read /proc/curproc/exe\n"); return false; } return true; } #elif defined(__OpenBSD__) static inline bool read_exe_path(char *exe, size_t buf_sz) { const char *path = getenv("PATH"); if (!path) { fprintf(stderr, "No PATH environment variable set, aborting\n"); return false; } char buf[PATH_MAX + 1] = {0}; strncpy(buf, path, PATH_MAX); char *token = strtok(buf, ":"); while (token != NULL) { char q[PATH_MAX + 1] = {0}; snprintf(q, PATH_MAX, "%s/kitty", token); if (safe_realpath(q, exe, buf_sz)) return true; token = strtok(NULL, ":"); } fprintf(stderr, "kitty not found in PATH aborting\n"); return false; } #else static inline bool read_exe_path(char *exe, size_t buf_sz) { if (!safe_realpath("/proc/self/exe", exe, buf_sz)) { fprintf(stderr, "Failed to read /proc/self/exe\n"); return false; } return true; } #endif // }}} int main(int argc, char *argv[]) { char exe[PATH_MAX+1] = {0}; char exe_dir_buf[PATH_MAX+1] = {0}; const char *lc_ctype = NULL; #ifdef __APPLE__ lc_ctype = getenv("LC_CTYPE"); #endif if (!read_exe_path(exe, sizeof(exe))) return 1; strncpy(exe_dir_buf, exe, sizeof(exe_dir_buf)); char *exe_dir = dirname(exe_dir_buf); int num, ret=0; char lib[PATH_MAX+1] = {0}; num = snprintf(lib, PATH_MAX, "%s/%s", exe_dir, KITTY_LIB_PATH); if (num < 0 || num >= PATH_MAX) { fprintf(stderr, "Failed to create path to kitty lib\n"); return 1; } #if PY_VERSION_HEX >= 0x03070000 // Always use UTF-8 mode, see https://github.com/kovidgoyal/kitty/issues/924 Py_UTF8Mode = 1; #endif if (lc_ctype) lc_ctype = strdup(lc_ctype); RunData run_data = {.exe = exe, .exe_dir = exe_dir, .lib_dir = lib, .argc = argc, .argv = argv, .lc_ctype = lc_ctype}; ret = run_embedded(run_data); if (lc_ctype) free((void*)lc_ctype); return ret; }