aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/spm.h45
-rw-r--r--src/environment.c361
-rw-r--r--src/spm.c45
3 files changed, 427 insertions, 24 deletions
diff --git a/include/spm.h b/include/spm.h
index a8a4ab7..ad30b9d 100644
--- a/include/spm.h
+++ b/include/spm.h
@@ -29,13 +29,33 @@
// spm.c
#define SYSERROR stderr, "%s:%s:%d: %s\n", basename(__FILE__), __FUNCTION__, __LINE__, strerror(errno)
#define DIRSEP_WIN32 '\\'
+#define DIRSEPS_WIN32 "\\"
+#define PATHSEP_WIN32 ';'
+#define PATHSEPS_WIN32 ";"
#define DIRSEP_UNIX '/'
+#define DIRSEPS_UNIX "/"
+#define PATHSEP_UNIX ';'
+#define PATHSEPS_UNIX ";"
#if defined(_WIN32)
#define DIRSEP DIRSEP_WIN32
+#define DIRSEPS DIRSEPS_WIN32
#define NOT_DIRSEP DIRSEP_UNIX
+#define NOT_DIRSEPS DIRSEPS_UNIX
+
+#define PATHSEP PATHSEP_WIN32
+#define PATHSEPS PATHSEPS_WIN32
+#define NOT_PATHSEP PATHSEP_UNIX
+#define NOT_PATHSEPS PATHSEPS_UNIX
#else
#define DIRSEP DIRSEP_UNIX
+#define DIRSEPS DIRSEPS_UNIX
#define NOT_DIRSEP DIRSEP_WIN32
+#define NOT_DIRSEPS DIRSEPS_WIN32
+
+#define PATHSEP PATHSEP_UNIX
+#define PATHSEPS PATHSEPS_UNIX
+#define NOT_PATHSEP PATHSEP_WIN32
+#define NOT_PATHSEPS PATHSEPS_WIN32
#endif
#define SPM_META_DEPENDS ".SPM_DEPENDS"
@@ -128,6 +148,20 @@ typedef struct {
char *charset;
} Mime;
+typedef struct {
+ size_t num_alloc;
+ size_t num_inuse;
+ char **env;
+} RuntimeEnv;
+
+typedef struct {
+ char *binpath;
+ char *includepath;
+ char *libpath;
+ char *datapath;
+ char *manpath;
+} SPM_Hierarchy;
+
// GLOBALS
spm_vars SPM_GLOBAL;
@@ -262,8 +296,13 @@ int file_is_binexec(const char *filename);
int internal_cmd(int argc, char **argv);
// environment.c
-char **runtime_copy(char **env);
-void runtime_export(char **env);
-void runtime_free(char **env);
+ssize_t runtime_contains(RuntimeEnv *env, const char *key);
+RuntimeEnv *runtime_copy(char **env);
+char *runtime_get(RuntimeEnv *env, const char *key);
+void runtime_set(RuntimeEnv *env, const char *_key, const char *_value);
+char *runtime_expand_var(RuntimeEnv *env, const char *input);
+void runtime_export(RuntimeEnv *env, char **keys);
+void runtime_apply(RuntimeEnv *env);
+void runtime_free(RuntimeEnv *env);
#endif //SPM_SPM_H
diff --git a/src/environment.c b/src/environment.c
index d338e4a..4c8fb14 100644
--- a/src/environment.c
+++ b/src/environment.c
@@ -4,10 +4,56 @@
#include "spm.h"
/**
+ * Print a shell-specific listing of environment variables to `stdout`
*
- * @param env
+ * Example:
+ * ~~~{.c}
+ * int main(int argc, char *argv[], char *arge[]) {
+ * RuntimeEnv *rt = runtime_copy(arge);
+ * runtime_export(rt, NULL);
+ * runtime_free(rt);
+ * return 0;
+ * }
+ * ~~~
+ *
+ * Usage:
+ * ~~~{.sh}
+ * $ gcc program.c
+ * $ ./a.out
+ * PATH="/thing/stuff/bin:/example/please/bin"
+ * SHELL="/your/shell"
+ * CC="/your/compiler"
+ * ...=...
+ *
+ * # You can also use this to modify the shell environment
+ * # (use `runtime_set` to manipulate the output)
+ * $ source $(./a.out)
+ * ~~~
+ *
+ * Example of exporting specific keys from the environment:
+ *
+ * ~~~{.c}
+ * int main(int argc, char *argv[], char *arge[]) {
+ * RuntimeEnv *rt = runtime_copy(arge);
+ *
+ * // inline declaration
+ * runtime_export(rt, (char *[]) {"PATH", "LS_COLORS", NULL});
+ *
+ * // standard declaration
+ * char *keys_to_export[] = {
+ * "PATH", "LS_COLORS", NULL
+ * }
+ * runtime_export(rt, keys_to_export);
+ *
+ * runtime_free(rt);
+ * return 0;
+ * }
+ * ~~~
+ *
+ * @param env `RuntimeEnv` structure
+ * @param keys Array of keys to export. A value of `NULL` exports all environment keys
*/
-void runtime_export(char **env) {
+void runtime_export(RuntimeEnv *env, char **keys) {
char *borne[] = {
"bash",
"dash",
@@ -42,36 +88,319 @@ void runtime_export(char **env) {
}
}
- for (size_t i = 0; env[i] != NULL; i++) {
- char **pair = split(env[i], "=");
- sprintf(output, "%s %s=\"%s\"", export_command, pair[0], pair[1] ? pair[1] : "");
- puts(output);
+ for (size_t i = 0; i < env->num_inuse; i++) {
+ char **pair = split(env->env[i], "=");
+ if (keys != NULL) {
+ for (size_t j = 0; keys[j] != NULL; j++) {
+ if (strcmp(keys[j], pair[0]) == 0) {
+ sprintf(output, "%s %s=\"%s\"", export_command, pair[0], pair[1] ? pair[1] : "");
+ puts(output);
+ }
+ }
+ }
+ else {
+ sprintf(output, "%s %s=\"%s\"", export_command, pair[0], pair[1] ? pair[1] : "");
+ puts(output);
+ }
split_free(pair);
}
}
/**
+ * Populate a `RuntimeEnv` structure
+ *
+ * Example:
+ *
+ * ~~~{.c}
+ * int main(int argc, char *argv[], char *arge[]) {
+ * RuntimeEnv *rt = NULL;
+ * // Example 1: Copy the shell environment
+ * rt = runtime_copy(arge);
+ * // Example 2: Create your own environment
+ * rt = runtime_copy((char *[]) {"SHELL=/bin/bash", "PATH=/opt/secure:/bin:/usr/bin"})
*
- * @param env
- * @return
+ * runtime_free(rt);
+ * return 0;
+ * }
+ * ~~~
+ *
+ * @param env Array of strings in `var=value` format
+ * @return `RuntimeEnv` structure
*/
-char **runtime_copy(char **env) {
+RuntimeEnv *runtime_copy(char **env) {
+ RuntimeEnv *rt = NULL;
char **envp = NULL;
size_t env_count;
for (env_count = 0; env[env_count] != NULL; env_count++);
- envp = (char **)calloc(env_count + 1, sizeof(char *));
- for (size_t i = 0; i < env_count; i++) {
+ rt = (RuntimeEnv *)calloc(1, sizeof(RuntimeEnv));
+ rt->num_alloc = env_count + 1;
+ rt->num_inuse = env_count;
+ if (rt->num_inuse > 1) {
+ rt->num_inuse--;
+ }
+
+ envp = (char **)calloc(rt->num_alloc, sizeof(char *));
+ for (size_t i = 0; i < rt->num_inuse; i++) {
size_t len = strlen(env[i]);
- envp[i] = (char *)calloc(len + 1, sizeof(char));
+ envp[i] = (char *) calloc(len + 1, sizeof(char));
memcpy(envp[i], env[i], len);
}
- return envp;
+ rt->env = envp;
+ return rt;
+}
+
+/**
+ * Determine whether or not a key exists in the runtime environment
+ *
+ * Example:
+ *
+ * ~~~{.c}
+ * int main(int argc, char *argv[], char *arge[]) {
+ * RuntimeEnv *rt = runtime_copy(arge);
+ * if (runtime_contains(rt, "PATH") {
+ * // $PATH is present
+ * }
+ * else {
+ * // $PATH is NOT present
+ * }
+ *
+ * runtime_free(rt);
+ * return 0;
+ * }
+ * ~~~
+ *
+ * @param env `RuntimeEnv` structure
+ * @param key Environment variable string
+ * @return -1=no, positive_value=yes
+ */
+ssize_t runtime_contains(RuntimeEnv *env, const char *key) {
+ int result = -1;
+ for (size_t i = 0; i < env->num_inuse; i++) {
+ char **pair = split(env->env[i], "=");
+ if (pair == NULL) {
+ break;
+ }
+ if (strcmp(pair[0], key) == 0) {
+ result = i;
+ split_free(pair);
+ break;
+ }
+ split_free(pair);
+ }
+ return result;
+}
+
+/**
+ * Retrieve the value of a runtime environment variable
+ *
+ * Example:
+ *
+ * ~~~{.c}
+ * int main(int argc, char *argv[], char *arge[]) {
+ * RuntimeEnv *rt = runtime_copy(arge);
+ * char *path = runtime_get("PATH");
+ * if (path == NULL) {
+ * // handle error
+ * }
+ *
+ * runtime_free(rt);
+ * return 0;
+ * }
+ * ~~~
+ *
+ * @param env `RuntimeEnv` structure
+ * @param key Environment variable string
+ * @return success=string, failure=`NULL`
+ */
+char *runtime_get(RuntimeEnv *env, const char *key) {
+ char *result = NULL;
+ ssize_t key_offset = runtime_contains(env, key);
+ if (key_offset != -1) {
+ char **pair = split(env->env[key_offset], "=");
+ result = join(&pair[1], "=");
+ split_free(pair);
+ }
+ return result;
+}
+
+/**
+ * Parse an input string and expand any environment variable(s) found
+ *
+ * Example:
+ *
+ * ~~~{.c}
+ * int main(int argc, char *argv[], char *arge[]) {
+ * RuntimeEnv *rt = runtime_copy(arge);
+ * char *secure_path = runtime_expand_var(rt, "/opt/secure:$PATH:/aux/bin");
+ * if (secure_path == NULL) {
+ * // handle error
+ * }
+ * // secure_path = "/opt/secure:/your/original/path/here:/aux/bin");
+ *
+ * runtime_free(rt);
+ * return 0;
+ * }
+ * ~~~
+ *
+ * @param env `RuntimeEnv` structure
+ * @param input String to parse
+ * @return success=expanded string, failure=`NULL`
+ */
+char *runtime_expand_var(RuntimeEnv *env, const char *input) {
+ const char delim = '$';
+ const char *delim_literal = "$$";
+ const char *escape = "\\";
+ char *expanded = calloc(BUFSIZ, sizeof(char));
+
+ // If there's no environment variables to process return a copy of the input string
+ if (strchr(input, delim) == NULL) {
+ return strdup(input);
+ }
+
+ // Parse the input string
+ for (size_t i = 0; i < strlen(input); i++) {
+ char var[MAXNAMLEN]; // environment variable name
+ memset(var, '\0', MAXNAMLEN); // zero out name
+
+ // Ignore closing brace
+ if (input[i] == '}') {
+ i++;
+ }
+
+ // Handle literal statement "$$var"
+ // Value becomes "\$var"
+ if (strncmp(&input[i], delim_literal, strlen(delim_literal)) == 0) {
+ strncat(expanded, escape, strlen(escape));
+ strncat(expanded, &delim, 1);
+ i += strlen(delim_literal);
+ // Ignore opening brace
+ if (input[i] == '{') i++;
+ }
+
+ // Handle variable when encountering a single $
+ // Value expands from "$var" to "environment value of var"
+ if (input[i] == delim) {
+ // Ignore opening brace
+ if (input[i] == '{') i++;
+ char *tmp = NULL;
+ i++;
+
+ // Construct environment variable name from input
+ // "$ var" == no
+ // "$-*)!@ == no
+ // "$var" == yes
+ for (size_t c = 0; isalnum(input[i]) || input[i] == '_'; c++, i++) {
+ var[c] = input[i];
+ }
+
+ tmp = runtime_get(env, var);
+ if (tmp == NULL) {
+ // This mimics shell behavior in general.
+ // Prevent appending whitespace when an environment variable does not exist
+ if (i > 0) {
+ i--;
+ }
+ continue;
+ }
+ // Append expanded environment variable to output
+ strncat(expanded, tmp, strlen(tmp));
+ free(tmp);
+ }
+ // Nothing to do so append input to output
+ strncat(expanded, &input[i], 1);
+ }
+
+ return expanded;
}
-void runtime_free(char **env) {
- for (size_t i = 0; env[i] != NULL; i++) {
- free(env[i]);
+/**
+ * Set a runtime environment variable.
+ *
+ *
+ * Note: `_value` is passed through `runtime_expand_var` to provide shell expansion
+ *
+ *
+ * Example:
+ *
+ * ~~~{.c}
+ * int main(int argc, char *argv[], char *arge[]) {
+ * RuntimeEnv *rt = runtime_copy(arge);
+ *
+ * runtime_set(rt, "new_var", "1");
+ * char *new_var = runtime_get("new_var");
+ * // new_var = 1;
+ *
+ * char *path = runtime_get("PATH");
+ * // path = /your/path:/here
+ *
+ * runtime_set(rt, "PATH", "/opt/secure:$PATH");
+ * char *secure_path = runtime_get("PATH");
+ * // secure_path = /opt/secure:/your/path:/here
+ * // NOTE: path and secure_path are COPIES, unlike `getenv()` and `setenv()` that reuse their pointers in `environ`
+ *
+ * runtime_free(rt);
+ * return 0;
+ * }
+ * ~~~
+ *
+ *
+ * @param env `RuntimeEnv` structure
+ * @param _key Environment variable to set
+ * @param _value New environment variable value
+ */
+void runtime_set(RuntimeEnv *env, const char *_key, const char *_value) {
+ if (_key == NULL) {
+ return;
+ }
+ char *key = strdup(_key);
+ ssize_t key_offset = runtime_contains(env, key);
+ char *value = runtime_expand_var(env, _value);
+ char *arr[] = {
+ key, value, NULL
+ };
+ char *now = join(arr, "=");
+
+ if (key_offset != -1) {
+ free(env->env[key_offset]);
+ env->env[key_offset] = now;
+ }
+ else {
+ env->num_alloc++;
+ env->env = reallocarray(env->env, sizeof(char *), env->num_alloc);
+ env->env[env->num_inuse] = (char *)calloc(strlen(now) + 1, sizeof(char));
+ env->env[env->num_inuse] = now;
+ env->num_inuse++;
+ }
+ free(key);
+ free(value);
+}
+
+/**
+ * Update the global `environ` array with data from `RuntimeEnv`
+ * @param env `RuntimeEnv` structure
+ */
+void runtime_apply(RuntimeEnv *env) {
+ for (size_t i = 0; i < env->num_inuse; i++) {
+ char **pair = split(env->env[i], "=");
+ setenv(pair[0], pair[1], 1);
+ split_free(pair);
+ }
+}
+
+/**
+ * Free `RuntimeEnv` allocated by `runtime_copy`
+ * @param env `RuntimeEnv` structure
+ */
+void runtime_free(RuntimeEnv *env) {
+ if (env == NULL) {
+ return;
+ }
+
+ env->num_alloc = 0;
+ env->num_inuse = 0;
+ for (size_t i = 0; i < env->num_inuse; i++) {
+ free(env->env[i]);
}
free(env);
} \ No newline at end of file
diff --git a/src/spm.c b/src/spm.c
index 09538eb..b732f83 100644
--- a/src/spm.c
+++ b/src/spm.c
@@ -143,6 +143,38 @@ int main(int argc, char *argv[], char *arge[]) {
exit(1);
}
+ if (isempty(root)) {
+ sprintf(root, "%s%c%s", getenv("HOME"), DIRSEP, "spm_root");
+ }
+
+ // Construct installation runtime environment
+ RuntimeEnv *rt = runtime_copy(arge);
+ SPM_Hierarchy *root_hierarchy = NULL;
+ // TODO: Move environment allocation out of (above) this loop if possible
+ // TODO: replace variables below with SPM_Hierarchy, and write some control functions
+
+ char *spm_binpath = join((char *[]) {root, "bin"}, DIRSEPS);
+ char *spm_includepath = join((char *[]) {root, "include"}, DIRSEPS);
+ char *spm_libpath = join((char *[]) {root, "lib"}, DIRSEPS);
+ char *spm_datapath = join((char *[]) {root, "share"}, DIRSEPS);
+ char *spm_manpath = join((char *[]) {spm_datapath, "man"}, DIRSEPS);
+
+ runtime_set(rt, "SPM_BIN", spm_binpath);
+ runtime_set(rt, "SPM_INCLUDE", spm_includepath);
+ runtime_set(rt, "SPM_LIB", spm_libpath);
+ runtime_set(rt, "SPM_DATA", spm_datapath);
+ runtime_set(rt, "SPM_MAN", spm_manpath);
+ runtime_set(rt, "PATH", "$SPM_BIN:$PATH");
+ runtime_set(rt, "MANPATH", "$SPM_MAN:$MANPATH");
+
+ if (exists(join((char *[]) {spm_binpath, "gcc"}, DIRSEPS)) == 0) {
+ runtime_set(rt, "CC", "$SPM_BIN/gcc");
+ }
+
+ runtime_set(rt, "CFLAGS", "-I$SPM_INCLUDE $CFLAGS");
+ runtime_set(rt, "LDFLAGS", "-Wl,-rpath $SPM_LIB:$${ORIGIN}/lib -L$SPM_LIB $LDFLAGS");
+ runtime_apply(rt);
+
if (RUNTIME_INSTALL) {
Dependencies *deps = NULL;
dep_init(&deps);
@@ -151,15 +183,11 @@ int main(int argc, char *argv[], char *arge[]) {
Manifest *manifest = manifest_read();
if (!manifest) {
fprintf(stderr, "Package manifest is missing or corrupt\n");
+ runtime_free(rt);
exit(1);
}
printf("done\n");
- if (isempty(root)) {
- printf("Using default installation root\n");
- sprintf(root, "%s%c%s", getenv("HOME"), DIRSEP, "spm_root");
- }
-
printf("Installation root: %s\n", root);
printf("Requested packages:\n");
@@ -194,6 +222,7 @@ int main(int argc, char *argv[], char *arge[]) {
if (dep_all(&deps, package->archive) < 0) {
dep_free(&deps);
free_global_config();
+ runtime_free(rt);
exit(1);
}
}
@@ -209,6 +238,7 @@ int main(int argc, char *argv[], char *arge[]) {
printf(" -> %s\n", deps->list[i]);
if (install(root, deps->list[i]) < 0) {
fprintf(SYSERROR);
+ runtime_free(rt);
exit(errno);
}
}
@@ -225,11 +255,13 @@ int main(int argc, char *argv[], char *arge[]) {
if ((match = find_package(packages[i])) == NULL) {
fprintf(SYSERROR);
+ runtime_free(rt);
exit(1);
}
if ((package = basename(match)) == NULL) {
fprintf(stderr, "Unable to derive package name from package path:\n\t-> %s\n", match);
+ runtime_free(rt);
exit(1);
}
@@ -241,6 +273,7 @@ int main(int argc, char *argv[], char *arge[]) {
printf(" -> %s\n", package);
if (install(root, packages[i]) < 0) {
fprintf(SYSERROR);
+ runtime_free(rt);
exit(errno);
}
free(match);
@@ -313,8 +346,10 @@ int main(int argc, char *argv[], char *arge[]) {
}
}
+ runtime_free(rt);
exit(0);
}
+
free_global_config();
return 0;
}