From 55f5de477be1b4c4e46bfe3a3ba0acb07a4393da Mon Sep 17 00:00:00 2001 From: Arfrever Frehtes Taifersar Arahesis Date: Tue, 4 Aug 2009 19:41:36 +0000 Subject: Add python.c. Patch by: Jonathan Callen --- python.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 python.c diff --git a/python.c b/python.c new file mode 100644 index 0000000..dd1bc8b --- /dev/null +++ b/python.c @@ -0,0 +1,203 @@ +/* Copyright 1999-2009 Gentoo Foundation + * Distributed under the terms of the GNU General Public License v2 + */ +#include +#include +#include +#include +#include +#include +#include + +#define ENVD_CONFIG "/etc/env.d/python/config" + +/* 127 is the standard return code for "command not found" */ +#define EXIT_ERROR 127 + +char* dir_cat(const char* dir, const char* file) +{ + size_t dir_len = strlen(dir); + char* abs = malloc(dir_len + strlen(file) + 2); + strcpy(abs, dir); + abs[dir_len] = '/'; + abs[dir_len + 1] = 0; + return strcat(abs, file); +} + +const char* find_path(const char* exe) +{ + const char* last_slash = strrchr(exe, '/'); + if (last_slash) + { + return strndup(exe, last_slash - exe); + } + char* path = strdup(getenv("PATH")); + char* state; + const char* token = strtok_r(path, ":", &state); + while (token) + { + /* If an element of PATH is empty ("::"), then it is "." */ + if (! *token) + { + token = "."; + } + /* If it starts with "~/", use "${HOME}/" instead */ + if (strncmp(token, "~/", 2) == 0) + { + char* home = getenv("HOME"); + char* new_token = malloc(strlen(token) + strlen(home)); + strcpy(new_token, home); + strcat(new_token, token + 1); + token = new_token; + } + struct stat sbuf; + char* str = dir_cat(token, exe); + if (stat(str, &sbuf) == 0 && (S_ISREG(sbuf.st_mode) || S_ISLNK(sbuf.st_mode))) + { + return token; + } + token = strtok_r(NULL, ":", &state); + } + return NULL; +} + +/* True if a valid file name, and not "python" */ +int valid_interpreter(const char* name) +{ + if (! name || ! *name || (strcmp(name, "python") == 0)) + { + return 0; + } + return 1; +} + +int get_version(const char* name) +{ + /* Only find files beginning with "python" - this is a fallback, + * so we only want CPython + */ + if (! valid_interpreter(name) || strncmp(name, "python", 6) != 0) + return -1; + int pos = 6; + int major = 0; + int minor = 0; + if (name[pos] < '0' || name[pos] > '9') + return -1; + do + { + major = major * 10 + name[pos] - '0'; + if (! name[++pos]) + return -1; + } + while (name[pos] >= '0' && name[pos] <= '9'); + if (name[pos++] != '.') + return -1; + if (name[pos] < '0' || name[pos] > '9') + return -1; + do + { + minor = minor * 10 + name[pos] - '0'; + if (! name[++pos]) + return (major << 8) | minor; + } + while (name[pos] >= '0' && name[pos] <= '9'); + return -1; +} + +int filter_python(const struct dirent* file) +{ + return get_version(file->d_name) != -1; +} + +/* This implements a version sort, such that the following order applies: + * (should never be seen) + * python2.6 + * python2.9 + * python2.10 + * python3.0 + * python3.1 + * python3.2 + * python9.1 + * python9.9 + * python9.10 + * python10.1 + * python10.9 + * python10.10 + */ +int sort_python(const struct dirent**f1, const struct dirent** f2) +{ + int ver1 = get_version((*f1)->d_name); + int ver2 = get_version((*f2)->d_name); + return ver1 - ver2; +} + +const char* find_latest(const char* exe) +{ + int major = -1; + int minor = -1; + const char* path = find_path(exe); + if (! path || ! *path) + { + path = "/usr/bin"; + } + struct dirent** namelist; + int n = scandir(path, &namelist, filter_python, sort_python); + const char* ret = NULL; + if (n < 0) + { + return NULL; + } + /* walk backwards through the list */ + while (n--) + { + if (! ret) + ret = strdup(namelist[n]->d_name); + free(namelist[n]); + } + free(namelist); + return ret; +} + +int main(int argc, char** argv) +{ + const char* EPYTHON = getenv("EPYTHON"); + if (! valid_interpreter(EPYTHON)) + { + FILE* f = fopen(ENVD_CONFIG, "r"); + if (f) + { + struct stat st; + fstat(fileno(f), &st); + size_t size = st.st_size; + char* cont = malloc(size + 1); + fgets(cont, size + 1, f); + fclose(f); + size_t len = strlen(cont); + if (len && cont[len - 1] == '\n') + cont[len - 1] = 0; + EPYTHON = cont; + } + } + + if (! valid_interpreter(EPYTHON)) + EPYTHON = find_latest(argv[0]); + + if (! EPYTHON) + return EXIT_ERROR; + + if (strchr(EPYTHON, '/')) + { + execv(EPYTHON, argv); + return EXIT_ERROR; + } + + const char* path = find_path(argv[0]); + if (*path) + { + execv(dir_cat(path, EPYTHON), argv); + return EXIT_ERROR; + } + + execvp(EPYTHON, argv); + return EXIT_ERROR; +} -- cgit v1.2.3-65-gdbad