aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2020-03-18 22:25:27 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2020-03-18 22:25:27 -0400
commitccaeb7092b5ad40b1b3833c987ba3ec4d47f0bb8 (patch)
treeae167772a9a2343aa77bf8944b56abe853f6a2ec /src
parent3731bb4679ee8716d4f579d5744c36a2d1b4a257 (diff)
downloadspmc-ccaeb7092b5ad40b1b3833c987ba3ec4d47f0bb8.tar.gz
Refactor project: build/install libspm[_static.a].so to make unit testing possible
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt30
-rw-r--r--src/archive.c110
-rw-r--r--src/checksum.c42
-rw-r--r--src/compat.c23
-rw-r--r--src/config.c227
-rw-r--r--src/config_global.c375
-rw-r--r--src/environment.c406
-rw-r--r--src/extern/url.c655
-rw-r--r--src/find.c151
-rw-r--r--src/fs.c504
-rw-r--r--src/install.c328
-rw-r--r--src/internal_cmd.c401
-rw-r--r--src/manifest.c668
-rw-r--r--src/metadata.c166
-rw-r--r--src/mime.c156
-rw-r--r--src/mirrors.c180
-rw-r--r--src/purge.c93
-rw-r--r--src/relocation.c440
-rw-r--r--src/resolve.c65
-rw-r--r--src/rpath.c303
-rw-r--r--src/shell.c116
-rw-r--r--src/shlib.c72
-rw-r--r--src/spm_build.c23
-rw-r--r--src/str.c699
-rw-r--r--src/strlist.c339
-rw-r--r--src/user_input.c75
-rw-r--r--src/version_spec.c445
27 files changed, 2 insertions, 7090 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bac2769..2bd023c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,37 +5,11 @@ include_directories(
add_executable(spm
spm.c
- config.c
- compat.c
- resolve.c
- fs.c
- rpath.c
- find.c
- shell.c
- archive.c
- str.c
- relocation.c
- install.c
- config_global.c
- manifest.c
- checksum.c
- extern/url.c
- version_spec.c
- spm_build.c
- mime.c
- internal_cmd.c
- environment.c
- mirrors.c
- strlist.c
- shlib.c
- user_input.c
- metadata.c
- purge.c
)
-target_link_libraries(spm crypto ssl curl)
+target_link_libraries(spm libspm crypto ssl curl)
if (LINUX)
- target_link_libraries(spm rt)
+ target_link_libraries(spm libspm rt)
endif()
if(MSVC)
diff --git a/src/archive.c b/src/archive.c
deleted file mode 100644
index d964469..0000000
--- a/src/archive.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * @file archive.c
- */
-#include "spm.h"
-
-/**
- * Extract a single file from a tar archive into a directory
- *
- * @param archive path to tar archive
- * @param filename known path inside the archive to extract
- * @param destination where to extract file to (must exist)
- * @return
- */
-int tar_extract_file(const char *_archive, const char* _filename, const char *_destination) {
- Process *proc = NULL;
- int status;
- char cmd[PATH_MAX];
- char *archive = strdup(_archive);
- if (!archive) {
- fprintf(SYSERROR);
- return -1;
- }
- char *filename = strdup(_filename);
- if (!filename) {
- fprintf(SYSERROR);
- return -1;
- }
- char *destination = strdup(_destination);
- if (!destination) {
- fprintf(SYSERROR);
- return -1;
- }
-
- strchrdel(archive, SHELL_INVALID);
- strchrdel(destination, SHELL_INVALID);
- strchrdel(filename, SHELL_INVALID);
-
- sprintf(cmd, "tar xf \"%s\" -C \"%s\" \"%s\" 2>&1", archive, destination, filename);
- if (exists(archive) != 0) {
- fprintf(stderr, "unable to find archive: %s\n", archive);
- fprintf(SYSERROR);
- return -1;
- }
-
- shell(&proc, SHELL_OUTPUT, cmd);
- if (!proc) {
- fprintf(SYSERROR);
- return -1;
- }
-
- status = proc->returncode;
- if (status != 0) {
- fprintf(stderr, "%s\n", proc->output);
- }
-
- shell_free(proc);
- free(archive);
- free(filename);
- free(destination);
- return status;
-}
-
-/**
- *
- * @param _archive
- * @param _destination
- * @return
- */
-int tar_extract_archive(const char *_archive, const char *_destination) {
- Process *proc = NULL;
- int status;
- char cmd[PATH_MAX];
-
- if (exists(_archive) != 0) {
- //fprintf(SYSERROR);
- return -1;
- }
-
- char *archive = strdup(_archive);
- if (!archive) {
- fprintf(SYSERROR);
- return -1;
- }
- char *destination = strdup(_destination);
- if (!destination) {
- fprintf(SYSERROR);
- return -1;
- }
-
- // sanitize archive
- strchrdel(archive, SHELL_INVALID);
- // sanitize destination
- strchrdel(destination, SHELL_INVALID);
-
- sprintf(cmd, "tar xf %s -C %s 2>&1", archive, destination);
- shell(&proc, SHELL_OUTPUT, cmd);
- if (!proc) {
- fprintf(SYSERROR);
- free(archive);
- free(destination);
- return -1;
- }
-
- status = proc->returncode;
- shell_free(proc);
- free(archive);
- free(destination);
- return status;
-}
-
diff --git a/src/checksum.c b/src/checksum.c
deleted file mode 100644
index 249d6cb..0000000
--- a/src/checksum.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * @file checksum.c
- */
-#include "spm.h"
-#include <openssl/sha.h>
-
-/**
- *
- * @param filename
- * @return
- */
-char *sha256sum(const char *filename) {
- size_t bytes = 0;
- unsigned char digest[SHA256_DIGEST_LENGTH];
- char buf[BUFSIZ];
- SHA256_CTX context;
- SHA256_Init(&context);
- FILE *fp = fopen(filename, "r");
- if (!fp) {
- perror(filename);
- return NULL;
- }
- char *result = calloc(SHA256_DIGEST_STRING_LENGTH, sizeof(char));
- if (!result) {
- fclose(fp);
- perror("SHA256 result");
- return NULL;
- }
-
- while ((bytes = fread(buf, sizeof(char), BUFSIZ, fp)) != 0) {
- SHA256_Update(&context, buf, bytes);
- }
- fclose(fp);
-
- SHA256_Final(digest, &context);
- char *rtmp = result;
- for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
- snprintf(&rtmp[i * 2], 3, "%02x", digest[i]);
- }
-
- return result;
-}
diff --git a/src/compat.c b/src/compat.c
deleted file mode 100644
index 5fd2cc4..0000000
--- a/src/compat.c
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "config.h"
-
-#ifndef HAVE_STRSEP
-#include <string.h>
-// credit: Dan Cross via https://unixpapa.com/incnote/string.html
-char *strsep(char **sp, char *sep)
-{
- char *p, *s;
- if (sp == NULL || *sp == NULL || **sp == '\0') return(NULL);
- s = *sp;
- p = s + strcspn(s, sep);
- if (*p != '\0') *p++ = '\0';
- *sp = p;
- return(s);
-}
-#endif
-
-#ifndef HAVE_REALLOCARRAY
-#include <stdlib.h>
-void *reallocarray (void *__ptr, size_t __nmemb, size_t __size) {
- return realloc(__ptr, __nmemb * __size);
-}
-#endif
diff --git a/src/config.c b/src/config.c
deleted file mode 100644
index 88c96fd..0000000
--- a/src/config.c
+++ /dev/null
@@ -1,227 +0,0 @@
-/**
- * @file config.c
- */
-#include "spm.h"
-
-/**
- * Parse a basic configuration file
- *
- * NOTE: All values are stored as strings. You need to convert non-string values yourself.
- *
- * Example:
- * ~~~{.c}
- * ConfigItem **items = config_read("example.cfg");
- * if (!items) {
- * // handle error
- * }
- * config_free(items);
- * ~~~
- *
- *
- * @param filename
- * @return success=`ConfigItem` array, failure=NULL
- */
-ConfigItem **config_read(const char *filename) {
- const char sep = '=';
- size_t record = 0;
- char *line = NULL;
- FILE *fp = NULL;
-
- if (SPM_GLOBAL.verbose) {
- printf("Reading configuration file: %s\n", filename);
- }
-
- fp = fopen(filename, "r");
- if (!fp) {
- // errno will be set, so die, and let the caller handle it
- return NULL;
- }
-
- ConfigItem **config = (ConfigItem **) calloc(record + 1, sizeof(ConfigItem *));
- if (!config) {
- perror("ConfigItem");
- fprintf(SYSERROR);
- fclose(fp);
- return NULL;
- }
-
- line = (char *)calloc(CONFIG_BUFFER_SIZE, sizeof(char));
- if (!line) {
- perror("config line buffer");
- fprintf(SYSERROR);
- return NULL;
- }
-
- while (fgets(line, CONFIG_BUFFER_SIZE - 1, fp) != NULL) {
- char *lptr = line;
- // Remove trailing space and newlines
- lptr = strip(lptr);
-
- // Remove leading space and newlines
- lptr = lstrip(lptr);
-
- // Skip empty lines
- if (isempty(lptr)) {
- continue;
- }
-
- // Skip comment-only lines
- if (*lptr == '#' || *lptr == ';') {
- continue;
- }
-
- // Get a pointer to the key pair separator
- char *sep_pos = strchr(lptr, sep);
- if (!sep_pos) {
- printf("invalid entry on line %zu: missing '%c': '%s'\n", record, sep, lptr);
- continue;
- }
-
- // These values are approximations. The real length(s) are recorded using strlen below.
- // At most we'll lose a few heap bytes to whitespace, but it's better than allocating PATH_MAX or BUFSIZ
- // for a measly ten byte string.
- size_t key_length = strcspn(lptr, &sep);
- size_t value_length = strlen(sep_pos);
-
- // Allocate a ConfigItem record
- config[record] = (ConfigItem *)calloc(1, sizeof(ConfigItem));
- config[record]->key = (char *)calloc(key_length + 1, sizeof(char));
- config[record]->value = (char *)calloc(value_length + 1, sizeof(char));
-
- if (!config[record] || !config[record]->key || !config[record]->value) {
- perror("ConfigItem record");
- fprintf(SYSERROR);
- return NULL;
- }
-
- // Shortcut our array at this point. Things get pretty ugly otherwise.
- char *key = config[record]->key;
- char *value = config[record]->value;
-
- // Copy the array pointers (used to populate config->key/value_length
- char *key_orig = key;
- char *value_orig = value;
-
- // Populate the key and remove any trailing space
- while (lptr != sep_pos) {
- *key++ = *lptr++;
- }
- key = strip(key_orig);
-
- // We're at the separator now, so skip over it
- lptr++;
- // and remove any leading space
- lptr = lstrip(lptr);
-
- // Determine whether the string is surrounded by quotes, if so, get rid of them
- if (isquoted(lptr)) {
- // Move pointer beyond quote
- lptr = strpbrk(lptr, "'\"") + 1;
- // Terminate on closing quote
- char *tmp = strpbrk(lptr, "'\"");
- *tmp = '\0';
- }
-
- // Populate the value, and ignore any inline comments
- while (*lptr) {
- if (*lptr == '#' || *lptr == ';') {
- // strip trailing whitespace where the comment is and stop processing
- value = strip(value);
- break;
- }
- *value++ = *lptr++;
- }
-
- // Populate length data
- config[record]->key_length = strlen(key_orig);
- config[record]->value_length = strlen(value_orig);
-
- // Destroy contents of line buffer
- memset(line, '\0', CONFIG_BUFFER_SIZE);
-
- if (SPM_GLOBAL.verbose) {
- printf("CONFIG RECORD=%zu, PTR='%p', KEY='%s', VALUE='%s'\n",
- record, config[record], config[record]->key, config[record]->value);
- }
-
- // increment record count
- record++;
-
- // Expand config by another record
- config = (ConfigItem **)reallocarray(config, record + 1, sizeof(ConfigItem *));
- if (!config) {
- perror("ConfigItem array");
- fprintf(SYSERROR);
- free(line);
- return NULL;
- }
-
- config[record] = NULL;
- }
- free(line);
- return config;
-}
-
-/**
- * Free memory allocated by `config_read`
- * @param item `ConfigItem` array
- */
-void config_free(ConfigItem **item) {
- for (size_t i = 0; item[i] != NULL; i++) {
- free(item[i]->key);
- free(item[i]->value);
- free(item[i]);
- }
- free(item);
-}
-
-/**
- * If the `ConfigItem` array contains `key`, return a pointer to that record
- *
- * Example:
- * ~~~{.c}
- * char *nptr = NULL;
- * ConfigItem *item = config_get(items, "a_number");
- * if (!item) {
- * // handle error
- * }
- * int the_number = strtol(item->value, &nptr, 10);
- * printf("%s = %d\n", item->key, the_number);
- * ~~~
- *
- * @param item pointer to array of config records
- * @param key search for key in config records
- * @return success=pointer to record, failure=NULL
- */
-ConfigItem *config_get(ConfigItem **item, const char *key) {
- if (!item) {
- return NULL;
- }
- for (size_t i = 0; item[i] != NULL; i++) {
- if (!strcmp(item[i]->key, key)) {
- return item[i];
- }
- }
- return NULL;
-}
-
-void config_test(void) {
- ConfigItem **config = config_read("program.conf");
- printf("Data Parsed:\n");
- for (int i = 0; config[i] != NULL; i++) {
- printf("key: '%s', value: '%s'\n", config[i]->key, config[i]->value);
- }
-
- printf("Testing config_get():\n");
- ConfigItem *cptr = NULL;
- if ((cptr = config_get(config, "integer_value"))) {
- printf("%s = %d\n", cptr->key, atoi(cptr->value));
- }
- if ((cptr = config_get(config, "float_value"))) {
- printf("%s = %.3f\n", cptr->key, atof(cptr->value));
- }
- if ((cptr = config_get(config, "string_value"))) {
- printf("%s = %s\n", cptr->key, cptr->value);
- }
- config_free(config);
-}
diff --git a/src/config_global.c b/src/config_global.c
deleted file mode 100644
index 16d97cf..0000000
--- a/src/config_global.c
+++ /dev/null
@@ -1,375 +0,0 @@
-/**
- * @file config_global.c
- */
-#include "spm.h"
-
-/**
- * Get path to user's local configuration directory
- * (The path will be created if it doesn't exist)
- * @return
- */
-char *get_user_conf_dir(void) {
- char *result = NULL;
- wordexp_t wexp;
- wordexp("~/.spm", &wexp, 0);
- if (wexp.we_wordc != 0) {
- result = (char *)calloc(strlen(wexp.we_wordv[0]) + 1, sizeof(char));
- if (!result) {
- wordfree(&wexp);
- return NULL;
- }
- strncpy(result, wexp.we_wordv[0], strlen(wexp.we_wordv[0]));
- if (access(result, F_OK) != 0) {
- mkdirs(result, 0755);
- }
- }
- wordfree(&wexp);
- return result;
-}
-
-/**
- * Get path to user's local configuration file
- * @return
- */
-char *get_user_config_file(void) {
- const char *filename = "spm.conf";
- char template[PATH_MAX];
- char *ucd = get_user_conf_dir();
- if (!ucd) {
- return NULL;
- }
- // Initialize temporary path
- template[0] = '\0';
-
- sprintf(template, "%s%c%s", ucd, DIRSEP, filename);
- if (access(template, F_OK) != 0) {
- // No configuration exists, so fail
- return NULL;
- }
- free(ucd);
- // Allocate and return path to configuration file
- return strdup(template);
-}
-
-/**
- * Determine location of temporary storage location
- * @return
- */
-char *get_user_tmp_dir(void) {
- char *template = NULL;
- char *ucd = get_user_conf_dir();
- template = join_ex(DIRSEPS, ucd, "tmp", NULL);
-
- if (access(template, F_OK) != 0) {
- if (mkdirs(template, 0755) != 0) {
- return NULL;
- }
- }
-
- free(ucd);
- return template;
-}
-
-/**
- * Determine location of package directory
- * @return
- */
-char *get_user_package_dir(void) {
- char *template = NULL;
- char *ucd = get_user_conf_dir();
-
- template = join_ex(DIRSEPS, ucd, "pkgs", SPM_GLOBAL.repo_target, NULL);
-
- if (access(template, F_OK) != 0) {
- if (mkdirs(template, 0755) != 0) {
- return NULL;
- }
- }
-
- free(ucd);
- return template;
-}
-
-/**
- * Determine location of the package manifest
- * @return
- */
-char *get_package_manifest(void) {
- Manifest *manifest = NULL;
- char *template = NULL;
- char *ucd = get_user_conf_dir();
-
- //free(ucd);
- //return strdup(template);
-
- template = join_ex(DIRSEPS, SPM_GLOBAL.package_dir, SPM_MANIFEST_FILENAME, NULL);
- if (access(template, F_OK) != 0) {
- fprintf(stderr, "Package manifest not found: %s\n", template);
- manifest = manifest_from(PKG_DIR);
- if (manifest == NULL) {
- perror("manifest generator");
- fprintf(SYSERROR);
- return NULL;
- }
- manifest_write(manifest, PKG_DIR);
- manifest_free(manifest);
- }
-
- free(ucd);
- return template;
-}
-
-
-/**
- * Check whether SPM has access to external programs it needs
- */
-void check_runtime_environment(void) {
- int bad_rt = 0;
- char *required[] = {
- "file",
- "patchelf",
- "objdump",
- "rsync",
- "tar",
- "bash",
- "reloc",
- NULL,
- };
-
- if (getenv("SHELL") == NULL) {
- fprintf(stderr, "Required environment variable 'SHELL' is not defined\n");
- bad_rt = 1;
- }
-
- for (int i = 0; required[i] != NULL; i++) {
- char *result = find_executable(required[i]);
- if (!result) {
- fprintf(stderr, "Required program '%s' is not installed\n", required[i]);
- bad_rt = 1;
- }
- free(result);
- }
- if (bad_rt) {
- exit(1);
- }
-}
-
-/**
- *
- * @param basepath
- * @return
- */
-SPM_Hierarchy *spm_hierarchy_init(char *basepath) {
- SPM_Hierarchy *fs = calloc(1, sizeof(SPM_Hierarchy));
- fs->rootdir = strdup(basepath);
- fs->bindir = join((char *[]) {fs->rootdir, "bin", NULL}, DIRSEPS);
- fs->includedir = join((char *[]) {fs->rootdir, "include", NULL}, DIRSEPS);
- fs->libdir = join((char *[]) {fs->rootdir, "lib", NULL}, DIRSEPS);
- fs->datadir = join((char *[]) {fs->rootdir, "share", NULL}, DIRSEPS);
- fs->localstatedir = join((char *[]) {fs->rootdir, "var", NULL}, DIRSEPS);
- fs->sysconfdir = join((char *[]) {fs->rootdir, "etc", NULL}, DIRSEPS);
- fs->mandir = join((char *[]) {fs->datadir, "man", NULL}, DIRSEPS);
- fs->tmpdir = join((char *[]) {fs->rootdir, "tmp", NULL}, DIRSEPS);
- fs->dbdir = join((char *[]) {fs->localstatedir, "db", NULL}, DIRSEPS);
- fs->dbrecdir = join((char *[]) {fs->dbdir, "records", NULL}, DIRSEPS);
-
- return fs;
-}
-
-/**
- *
- * @param fs
- */
-void spm_hierarchy_free(SPM_Hierarchy *fs) {
- free(fs->rootdir);
- free(fs->bindir);
- free(fs->includedir);
- free(fs->libdir);
- free(fs->datadir);
- free(fs->localstatedir);
- free(fs->sysconfdir);
- free(fs->mandir);
- free(fs->tmpdir);
- free(fs->dbdir);
- free(fs->dbrecdir);
- free(fs);
-}
-
-/**
- * Populate global configuration structure
- */
-void init_config_global(void) {
- SPM_GLOBAL.user_config_basedir = NULL;
- SPM_GLOBAL.user_config_file = NULL;
- SPM_GLOBAL.package_dir = NULL;
- SPM_GLOBAL.tmp_dir = NULL;
- SPM_GLOBAL.package_manifest = NULL;
- SPM_GLOBAL.config = NULL;
- SPM_GLOBAL.verbose = 0;
- SPM_GLOBAL.repo_target = NULL;
- SPM_GLOBAL.mirror_list = NULL;
- SPM_GLOBAL.prompt_user = 1;
-
- if (uname(&SPM_GLOBAL.sysinfo) != 0) {
- fprintf(SYSERROR);
- exit(errno);
- }
-
- // Initialize filesystem paths structure
- SPM_GLOBAL.fs.bindir = calloc(strlen(SPM_PROGRAM_BIN) + 1, sizeof(char));
- SPM_GLOBAL.fs.includedir = calloc(strlen(SPM_PROGRAM_INCLUDE) + 1, sizeof(char));
- SPM_GLOBAL.fs.libdir = calloc(strlen(SPM_PROGRAM_LIB) + 1, sizeof(char));
- SPM_GLOBAL.fs.datadir = calloc(strlen(SPM_PROGRAM_DATA) + 1, sizeof(char));
-
- if (!SPM_GLOBAL.fs.bindir || !SPM_GLOBAL.fs.includedir
- || !SPM_GLOBAL.fs.libdir) {
- perror("Unable to allocate memory for global filesystem paths");
- fprintf(SYSERROR);
- exit(errno);
- }
-
- strcpy(SPM_GLOBAL.fs.bindir, SPM_PROGRAM_BIN);
- strcpy(SPM_GLOBAL.fs.includedir, SPM_PROGRAM_INCLUDE);
- strcpy(SPM_GLOBAL.fs.libdir, SPM_PROGRAM_LIB);
- strcpy(SPM_GLOBAL.fs.datadir, SPM_PROGRAM_DATA);
- SPM_GLOBAL.fs.mandir = join((char *[]) {SPM_PROGRAM_DATA, "man", NULL}, DIRSEPS);
-
- SPM_GLOBAL.user_config_basedir = get_user_conf_dir();
- SPM_GLOBAL.user_config_file = get_user_config_file();
- if (SPM_GLOBAL.user_config_file) {
- SPM_GLOBAL.config = config_read(SPM_GLOBAL.user_config_file);
- }
-
- ConfigItem *item = NULL;
-
- // Initialize repository target (i.e. repository path suffix)
- SPM_GLOBAL.repo_target = join((char *[]) {SPM_GLOBAL.sysinfo.sysname, SPM_GLOBAL.sysinfo.machine, NULL}, DIRSEPS);
- item = config_get(SPM_GLOBAL.config, "repo_target");
- if (item) {
- free(SPM_GLOBAL.repo_target);
- SPM_GLOBAL.repo_target = normpath(item->value);
- }
-
- // Initialize mirror list filename
- SPM_GLOBAL.mirror_config = join((char *[]) {SPM_GLOBAL.user_config_basedir, SPM_MIRROR_FILENAME, NULL}, DIRSEPS);
- item = config_get(SPM_GLOBAL.config, "mirror_config");
- if (item) {
- free(SPM_GLOBAL.mirror_config);
- SPM_GLOBAL.mirror_config = normpath(item->value);
- }
-
- if (SPM_GLOBAL.mirror_config != NULL) {
- SPM_GLOBAL.mirror_list = mirror_list(SPM_GLOBAL.mirror_config);
- }
-
- // Initialize temp directory
- item = config_get(SPM_GLOBAL.config, "tmp_dir");
- if (item) {
- SPM_GLOBAL.tmp_dir = strdup(item->value);
- if (access(SPM_GLOBAL.tmp_dir, F_OK) != 0) {
- if (mkdirs(SPM_GLOBAL.tmp_dir, 0755) != 0) {
- fprintf(stderr, "Unable to create global temporary directory: %s\n", SPM_GLOBAL.tmp_dir);
- fprintf(SYSERROR);
- exit(1);
- }
- }
- }
- else {
- SPM_GLOBAL.tmp_dir = get_user_tmp_dir();
- }
-
- // Initialize package directory
- item = config_get(SPM_GLOBAL.config, "package_dir");
- if (item) {
- SPM_GLOBAL.package_dir = calloc(PATH_MAX, sizeof(char)); //strdup(item->value);
- strncpy(SPM_GLOBAL.package_dir, item->value, PATH_MAX - 1);
- strcat(SPM_GLOBAL.package_dir, DIRSEPS);
- strcat(SPM_GLOBAL.package_dir, SPM_GLOBAL.repo_target);
-
- if (access(SPM_GLOBAL.package_dir, F_OK) != 0) {
- if (mkdirs(SPM_GLOBAL.package_dir, 0755) != 0) {
- fprintf(stderr, "Unable to create global package directory: %s\n", SPM_GLOBAL.package_dir);
- fprintf(SYSERROR);
- exit(1);
- }
- }
- }
- else {
- SPM_GLOBAL.package_dir = get_user_package_dir();
- }
-
- // Initialize package manifest
- item = config_get(SPM_GLOBAL.config, "package_manifest");
- if (item) {
- SPM_GLOBAL.package_manifest = strdup(item->value);
- if (access(SPM_GLOBAL.package_manifest, F_OK) != 0) {
- fprintf(stderr, "Package manifest not found: %s\n", SPM_GLOBAL.package_manifest);
- Manifest *manifest = manifest_from(PKG_DIR);
- manifest_write(manifest, SPM_GLOBAL.package_manifest);
- manifest_free(manifest);
- }
- }
- else {
- SPM_GLOBAL.package_manifest = get_package_manifest();
- }
-}
-
-/**
- * Free memory allocated for global configuration
- */
-void free_global_config(void) {
- if (SPM_GLOBAL.package_dir) {
- free(SPM_GLOBAL.package_dir);
- }
- if (SPM_GLOBAL.tmp_dir) {
- free(SPM_GLOBAL.tmp_dir);
- }
- if (SPM_GLOBAL.package_manifest) {
- free(SPM_GLOBAL.package_manifest);
- }
- if (SPM_GLOBAL.user_config_basedir) {
- free(SPM_GLOBAL.user_config_basedir);
- }
- if (SPM_GLOBAL.user_config_file) {
- free(SPM_GLOBAL.user_config_file);
- }
- if (SPM_GLOBAL.repo_target) {
- free(SPM_GLOBAL.repo_target);
- }
- if (SPM_GLOBAL.mirror_config) {
- free(SPM_GLOBAL.mirror_config);
- }
- if (SPM_GLOBAL.mirror_list) {
- mirror_list_free(SPM_GLOBAL.mirror_list);
- }
-
- free(SPM_GLOBAL.fs.bindir);
- free(SPM_GLOBAL.fs.includedir);
- free(SPM_GLOBAL.fs.libdir);
- free(SPM_GLOBAL.fs.datadir);
- free(SPM_GLOBAL.fs.mandir);
- if (SPM_GLOBAL.config) {
- config_free(SPM_GLOBAL.config);
- }
-}
-
-/**
- * Display global configuration data
- */
-void show_global_config(void) {
- printf("#---------------------------\n");
- printf("#---- SPM CONFIGURATION ----\n");
- printf("#---------------------------\n");
- printf("# base dir: %s\n", SPM_GLOBAL.user_config_basedir ? SPM_GLOBAL.user_config_basedir : "none (check write permission on home directory)");
- printf("# config file: %s\n", SPM_GLOBAL.user_config_file ? SPM_GLOBAL.user_config_file : "none");
- if (SPM_GLOBAL.user_config_file) {
- printf("# config file contents:\n");
- for (int i = 0; SPM_GLOBAL.config[i] != NULL; i++) {
- printf("# -> %s: %s\n", SPM_GLOBAL.config[i]->key, SPM_GLOBAL.config[i]->value);
- }
- }
- printf("# package storage: %s\n", SPM_GLOBAL.package_dir);
- printf("# temp storage: %s\n", SPM_GLOBAL.tmp_dir);
- printf("# package manifest: %s\n", SPM_GLOBAL.package_manifest);
- printf("\n");
-}
diff --git a/src/environment.c b/src/environment.c
deleted file mode 100644
index d9bfe51..0000000
--- a/src/environment.c
+++ /dev/null
@@ -1,406 +0,0 @@
-/**
- * @file environment.c
- */
-#include "spm.h"
-
-/**
- * Print a shell-specific listing of environment variables to `stdout`
- *
- * 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(RuntimeEnv *env, char **keys) {
- char *borne[] = {
- "bash",
- "dash",
- "zsh",
- NULL,
- };
- char *unborne[] = {
- "csh"
- "tcsh",
- NULL,
- };
-
- char output[BUFSIZ];
- char export_command[7]; // export=6 and setenv=6... convenient
- char *_sh = getenv("SHELL");
- char *sh = basename(_sh);
- if (sh == NULL) {
- fprintf(stderr, "echo SHELL environment variable is not defined");
- exit(1);
- }
-
- for (size_t i = 0; borne[i] != NULL; i++) {
- if (strcmp(sh, borne[i]) == 0) {
- strcpy(export_command, "export");
- break;
- }
- }
- for (size_t i = 0; unborne[i] != NULL; i++) {
- if (strcmp(sh, unborne[i]) == 0) {
- strcpy(export_command, "setenv");
- break;
- }
- }
-
- for (size_t i = 0; i < strlist_count(env); i++) {
- char **pair = split(strlist_item(env, i), "=");
- char *key = pair[0];
- char *value = NULL;
-
- // We split a potentially large string by "=" so:
- // Recombine elements pair[1..N] into a single string by "="
- if (pair[1] != NULL) {
- value = join(&pair[1], "=");
- }
-
- if (keys != NULL) {
- for (size_t j = 0; keys[j] != NULL; j++) {
- if (strcmp(keys[j], key) == 0) {
- sprintf(output, "%s %s=\"%s\"", export_command, key, value ? value : "");
- puts(output);
- }
- }
- }
- else {
- sprintf(output, "%s %s=\"%s\"", export_command, key, value ? value : "");
- puts(output);
- }
- free(value);
- 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"})
- *
- * runtime_free(rt);
- * return 0;
- * }
- * ~~~
- *
- * @param env Array of strings in `var=value` format
- * @return `RuntimeEnv` structure
- */
-RuntimeEnv *runtime_copy(char **env) {
- RuntimeEnv *rt = NULL;
- size_t env_count;
- for (env_count = 0; env[env_count] != NULL; env_count++);
-
- rt = strlist_init();
- for (size_t i = 0; i < env_count; i++) {
- strlist_append(rt, env[i]);
- }
- 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) {
- ssize_t result = -1;
- for (size_t i = 0; i < strlist_count(env); i++) {
- char **pair = split(strlist_item(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(strlist_item(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 (expanded == NULL) {
- perror("could not allocate runtime_expand_var buffer");
- fprintf(SYSERROR);
- return NULL;
- }
-
- // 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
- size_t i;
- for (i = 0; i < strlen(input); i++) {
- char var[MAXNAMLEN]; // environment variable name
- memset(var, '\0', MAXNAMLEN); // zero out name
-
- // 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+1] == '{') {
- 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++) {
- // Ignore closing brace
- if (input[i] == '}') {
- 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
- if (input[i] == '}') {
- // Unless we ended on a closing brace
- continue;
- }
- strncat(expanded, &input[i], 1);
- }
-
- return expanded;
-}
-
-/**
- * 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 *now = join((char *[]) {key, value, NULL}, "=");
-
- if (key_offset < 0) {
- strlist_append(env, now);
- }
- else {
- strlist_set(env, key_offset, now);
- }
- free(now);
- 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 < strlist_count(env); i++) {
- char **pair = split(strlist_item(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;
- }
- strlist_free(env);
-}
diff --git a/src/extern/url.c b/src/extern/url.c
deleted file mode 100644
index fe54db2..0000000
--- a/src/extern/url.c
+++ /dev/null
@@ -1,655 +0,0 @@
-/*****************************************************************************
- *
- * This example source code introduces a c library buffered I/O interface to
- * URL reads it supports fopen(), fread(), fgets(), feof(), fclose(),
- * rewind(). Supported functions have identical prototypes to their normal c
- * lib namesakes and are preceaded by url_ .
- *
- * Using this code you can replace your program's fopen() with url_fopen()
- * and fread() with url_fread() and it become possible to read remote streams
- * instead of (only) local files. Local files (ie those that can be directly
- * fopened) will drop back to using the underlying clib implementations
- *
- * See the main() function at the bottom that shows an app that retrieves from
- * a specified url using fgets() and fread() and saves as two output files.
- *
- * Copyright (c) 2003 - 2019 Simtec Electronics
- *
- * Re-implemented by Vincent Sanders <vince@kyllikki.org> with extensive
- * reference to original curl example code
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * This example requires libcurl 7.9.7 or later.
- */
-/* <DESC>
- * implements an fopen() abstraction allowing reading from URLs
- * </DESC>
- */
-
-#include <url.h>
-#include "url.h"
-
-/* we use a global one for convenience */
-static CURLM *multi_handle;
-
-/* curl calls this routine to get more data */
-static size_t write_callback(char *buffer,
- size_t size,
- size_t nitems,
- void *userp) {
- char *newbuff;
- size_t rembuff;
-
- URL_FILE *url = (URL_FILE *) userp;
- size *= nitems;
-
- rembuff = url->buffer_len - url->buffer_pos; /* remaining space in buffer */
-
- if (size > rembuff) {
- /* not enough space in buffer */
- newbuff = realloc(url->buffer, url->buffer_len + (size - rembuff));
- if (newbuff == NULL) {
- fprintf(stderr, "callback buffer grow failed\n");
- size = rembuff;
- } else {
- /* realloc succeeded increase buffer size*/
- url->buffer_len += size - rembuff;
- url->buffer = newbuff;
- }
- }
-
- memcpy(&url->buffer[url->buffer_pos], buffer, size);
- url->buffer_pos += size;
-
- return size;
-}
-
-/* use to attempt to fill the read buffer up to requested number of bytes */
-static int fill_buffer(URL_FILE *file, size_t want) {
- fd_set fdread;
- fd_set fdwrite;
- fd_set fdexcep;
- struct timeval timeout;
- int rc;
- CURLMcode mc; /* curl_multi_fdset() return code */
-
- /* only attempt to fill buffer if transactions still running and buffer
- * doesn't exceed required size already
- */
- if ((!file->still_running) || (file->buffer_pos > want))
- return 0;
-
- /* attempt to fill buffer */
- do {
- int maxfd = -1;
- long curl_timeo = -1;
-
- FD_ZERO(&fdread);
- FD_ZERO(&fdwrite);
- FD_ZERO(&fdexcep);
-
- /* set a suitable timeout to fail on */
- timeout.tv_sec = 60; /* 1 minute */
- timeout.tv_usec = 0;
-
- curl_multi_timeout(multi_handle, &curl_timeo);
- if (curl_timeo >= 0) {
- timeout.tv_sec = curl_timeo / 1000;
- if (timeout.tv_sec > 1)
- timeout.tv_sec = 1;
- else
- timeout.tv_usec = (curl_timeo % 1000) * 1000;
- }
-
- /* get file descriptors from the transfers */
- mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
-
- if (mc != CURLM_OK) {
- fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc);
- break;
- }
-
- /* On success the value of maxfd is guaranteed to be >= -1. We call
- select(maxfd + 1, ...); specially in case of (maxfd == -1) there are
- no fds ready yet so we call select(0, ...) --or Sleep() on Windows--
- to sleep 100ms, which is the minimum suggested value in the
- curl_multi_fdset() doc. */
-
- if (maxfd == -1) {
-#ifdef _WIN32
- Sleep(100);
- rc = 0;
-#else
- /* Portable sleep for platforms other than Windows. */
- struct timeval wait = {0, 100 * 1000}; /* 100ms */
- rc = select(0, NULL, NULL, NULL, &wait);
-#endif
- } else {
- /* Note that on some platforms 'timeout' may be modified by select().
- If you need access to the original value save a copy beforehand. */
- rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
- }
-
- switch (rc) {
- case -1:
- /* select error */
- break;
-
- case 0:
- default:
- /* timeout or readable/writable sockets */
- curl_multi_perform(multi_handle, &file->still_running);
- file->http_status = get_http_response(multi_handle);
- break;
- }
- } while (file->still_running && (file->buffer_pos < want));
- return 1;
-}
-
-/* use to remove want bytes from the front of a files buffer */
-static int use_buffer(URL_FILE *file, size_t want) {
- /* sort out buffer */
- if (file->buffer_pos <= want) {
- /* ditch buffer - write will recreate */
- free(file->buffer);
- file->buffer = NULL;
- file->buffer_pos = 0;
- file->buffer_len = 0;
- } else {
- /* move rest down make it available for later */
- memmove(file->buffer,
- &file->buffer[want],
- (file->buffer_pos - want));
-
- file->buffer_pos -= want;
- }
- return 0;
-}
-
-/**
- *
- * @param handle
- * @return
- */
-long get_http_response(CURLM *handle) {
- long http_status = 0;
- CURLMsg *m = NULL;
-
- do {
- int msg_queue = 0;
- m = curl_multi_info_read(handle, &msg_queue);
- if (m != NULL) {
- curl_easy_getinfo(m->easy_handle, CURLINFO_RESPONSE_CODE, &http_status);
- }
- } while (m);
-
- return http_status;
-}
-
-URL_FILE *url_fopen(const char *url, const char *operation) {
- /* this code could check for URLs or types in the 'url' and
- basically use the real fopen() for standard files */
-
- URL_FILE *file;
- (void) operation;
-
- file = calloc(1, sizeof(URL_FILE));
- if (!file)
- return NULL;
-
- file->http_status = 0;
- file->handle.file = fopen(url, operation);
- if (file->handle.file)
- file->type = CFTYPE_FILE; /* marked as URL */
-
- else {
- file->type = CFTYPE_CURL; /* marked as URL */
- file->handle.curl = curl_easy_init();
-
- curl_easy_setopt(file->handle.curl, CURLOPT_URL, url);
- curl_easy_setopt(file->handle.curl, CURLOPT_WRITEDATA, file);
- curl_easy_setopt(file->handle.curl, CURLOPT_WRITEFUNCTION, write_callback);
- curl_easy_setopt(file->handle.curl, CURLOPT_VERBOSE, 0L);
- curl_easy_setopt(file->handle.curl, CURLOPT_FOLLOWLOCATION, 1L);
- curl_easy_setopt(file->handle.curl, CURLOPT_FAILONERROR, 1L);
-
- if (!multi_handle)
- multi_handle = curl_multi_init();
-
- curl_multi_add_handle(multi_handle, file->handle.curl);
-
- /* lets start the fetch */
- curl_multi_perform(multi_handle, &file->still_running);
-
- if ((file->buffer_pos == 0) && (!file->still_running)) {
- /* if still_running is 0 now, we should return NULL */
-
- /* make sure the easy handle is not in the multi handle anymore */
- curl_multi_remove_handle(multi_handle, file->handle.curl);
-
- /* cleanup */
- curl_easy_cleanup(file->handle.curl);
-
- free(file);
-
- file = NULL;
- }
- }
- return file;
-}
-
-int url_fclose(URL_FILE *file) {
- int ret = 0;/* default is good return */
-
- switch (file->type) {
- case CFTYPE_FILE:
- ret = fclose(file->handle.file); /* passthrough */
- break;
-
- case CFTYPE_CURL:
- /* make sure the easy handle is not in the multi handle anymore */
- curl_multi_remove_handle(multi_handle, file->handle.curl);
-
- /* cleanup */
- curl_easy_cleanup(file->handle.curl);
- break;
-
- default: /* unknown or supported type - oh dear */
- ret = EOF;
- errno = EBADF;
- break;
- }
-
- free(file->buffer);/* free any allocated buffer space */
- free(file);
-
- return ret;
-}
-
-int url_feof(URL_FILE *file) {
- int ret = 0;
-
- switch (file->type) {
- case CFTYPE_FILE:
- ret = feof(file->handle.file);
- break;
-
- case CFTYPE_CURL:
- if ((file->buffer_pos == 0) && (!file->still_running))
- ret = 1;
- break;
-
- default: /* unknown or supported type - oh dear */
- ret = -1;
- errno = EBADF;
- break;
- }
- return ret;
-}
-
-size_t url_fread(void *ptr, size_t size, size_t nmemb, URL_FILE *file) {
- size_t want;
-
- switch (file->type) {
- case CFTYPE_FILE:
- want = fread(ptr, size, nmemb, file->handle.file);
- break;
-
- case CFTYPE_CURL:
- want = nmemb * size;
-
- fill_buffer(file, want);
-
- /* check if there's data in the buffer - if not fill_buffer()
- * either errored or EOF */
- if (!file->buffer_pos)
- return 0;
-
- /* ensure only available data is considered */
- if (file->buffer_pos < want)
- want = file->buffer_pos;
-
- /* xfer data to caller */
- memcpy(ptr, file->buffer, want);
-
- use_buffer(file, want);
-
- want = want / size; /* number of items */
- break;
-
- default: /* unknown or supported type - oh dear */
- want = 0;
- errno = EBADF;
- break;
-
- }
- return want;
-}
-
-char *url_fgets(char *ptr, size_t size, URL_FILE *file) {
- size_t want = size - 1;/* always need to leave room for zero termination */
- size_t loop;
-
- switch (file->type) {
- case CFTYPE_FILE:
- ptr = fgets(ptr, (int) size, file->handle.file);
- break;
-
- case CFTYPE_CURL:
- fill_buffer(file, want);
-
- /* check if there's data in the buffer - if not fill either errored or
- * EOF */
- if (!file->buffer_pos)
- return NULL;
-
- /* ensure only available data is considered */
- if (file->buffer_pos < want)
- want = file->buffer_pos;
-
- /*buffer contains data */
- /* look for newline or eof */
- for (loop = 0; loop < want; loop++) {
- if (file->buffer[loop] == '\n') {
- want = loop + 1;/* include newline */
- break;
- }
- }
-
- /* xfer data to caller */
- memcpy(ptr, file->buffer, want);
- ptr[want] = 0;/* always null terminate */
-
- use_buffer(file, want);
-
- break;
-
- default: /* unknown or supported type - oh dear */
- ptr = NULL;
- errno = EBADF;
- break;
- }
-
- return ptr;/*success */
-}
-
-void url_rewind(URL_FILE *file) {
- switch (file->type) {
- case CFTYPE_FILE:
- rewind(file->handle.file); /* passthrough */
- break;
-
- case CFTYPE_CURL:
- /* halt transaction */
- curl_multi_remove_handle(multi_handle, file->handle.curl);
-
- /* restart */
- curl_multi_add_handle(multi_handle, file->handle.curl);
-
- /* ditch buffer - write will recreate - resets stream pos*/
- free(file->buffer);
- file->buffer = NULL;
- file->buffer_pos = 0;
- file->buffer_len = 0;
-
- break;
-
- default: /* unknown or supported type - oh dear */
- break;
- }
-}
-
-#define FGETSFILE "fgets.test"
-#define FREADFILE "fread.test"
-#define REWINDFILE "rewind.test"
-
-/* Small main program to retrieve from a url using fgets and fread saving the
- * output to two test files (note the fgets method will corrupt binary files if
- * they contain 0 chars */
-int _test_url_fopen(int argc, char *argv[]) {
- URL_FILE *handle;
- FILE *outf;
-
- size_t nread;
- char buffer[256];
- const char *url;
-
- if (argc < 2)
- url = "http://192.168.7.3/testfile";/* default to testurl */
- else
- url = argv[1];/* use passed url */
-
- /* copy from url line by line with fgets */
- outf = fopen(FGETSFILE, "wb+");
- if (!outf) {
- perror("couldn't open fgets output file\n");
- return 1;
- }
-
- handle = url_fopen(url, "r");
- if (!handle) {
- printf("couldn't url_fopen() %s\n", url);
- fclose(outf);
- return 2;
- }
-
- while (!url_feof(handle)) {
- url_fgets(buffer, sizeof(buffer), handle);
- fwrite(buffer, 1, strlen(buffer), outf);
- }
-
- url_fclose(handle);
-
- fclose(outf);
-
-
- /* Copy from url with fread */
- outf = fopen(FREADFILE, "wb+");
- if (!outf) {
- perror("couldn't open fread output file\n");
- return 1;
- }
-
- handle = url_fopen("testfile", "r");
- if (!handle) {
- printf("couldn't url_fopen() testfile\n");
- fclose(outf);
- return 2;
- }
-
- do {
- nread = url_fread(buffer, 1, sizeof(buffer), handle);
- fwrite(buffer, 1, nread, outf);
- } while (nread);
-
- url_fclose(handle);
-
- fclose(outf);
-
-
- /* Test rewind */
- outf = fopen(REWINDFILE, "wb+");
- if (!outf) {
- perror("couldn't open fread output file\n");
- return 1;
- }
-
- handle = url_fopen("testfile", "r");
- if (!handle) {
- printf("couldn't url_fopen() testfile\n");
- fclose(outf);
- return 2;
- }
-
- nread = url_fread(buffer, 1, sizeof(buffer), handle);
- fwrite(buffer, 1, nread, outf);
- url_rewind(handle);
-
- buffer[0] = '\n';
- fwrite(buffer, 1, 1, outf);
-
- nread = url_fread(buffer, 1, sizeof(buffer), handle);
- fwrite(buffer, 1, nread, outf);
-
- url_fclose(handle);
-
- fclose(outf);
-
- return 0;/* all done */
-}
-
-const char *http_response_str(long code) {
- switch (code) {
- case 100:
- return "Continue";
- case 101:
- return "Switching Protocol";
- case 102:
- return "Processing (WebDAV)";
- case 103:
- return "Early Hints";
- case 200:
- return "OK";
- case 201:
- return "Created";
- case 202:
- return "Accepted";
- case 203:
- return "Non-Authoritative Information";
- case 204:
- return "No Content";
- case 205:
- return "Reset Content";
- case 206:
- return "Partial Content";
- case 207:
- return "Multi-Status (WebDAV)";
- case 208:
- return "Already Reported";
- case 226:
- return "IM Used";
- case 300:
- return "Multiple Choices";
- case 301:
- return "Moved Permanently";
- case 302:
- return "Found";
- case 303:
- return "See Other";
- case 304:
- return "Not Modified";
- case 305:
- return "Use Proxy (DEPRECATED)";
- case 306:
- return "Unused";
- case 307:
- return "Temporary Redirect";
- case 308:
- return "Permanent Redirect";
- case 400:
- return "Bad Request";
- case 401:
- return "Unauthorized";
- case 402:
- return "Payment Required";
- case 403:
- return "Forbidden";
- case 404:
- return "Not Found";
- case 405:
- return "Method Not Allowed";
- case 406:
- return "Not Acceptable";
- case 407:
- return "Proxy Authentication Required";
- case 408:
- return "Request Timeout";
- case 409:
- return "Conflict";
- case 410:
- return "Gone";
- case 411:
- return "Length Required";
- case 412:
- return "Precondition Failed";
- case 413:
- return "Payload Too Large";
- case 414:
- return "URI Too Long";
- case 415:
- return "Unsupported Media Type";
- case 416:
- return "Range Not Satisfiable";
- case 417:
- return "Exception Failed";
- case 418:
- return "I'm a teapot";
- case 419:
- return "Expectation Failed";
- case 421:
- return "Misdirected Request";
- case 422:
- return "Unprocessable Entity (WebDAV)";
- case 423:
- return "Locked (WebDAV)";
- case 424:
- return "Failed Dependency (WebDAV)";
- case 425:
- return "Too Early";
- case 426:
- return "Upgrade Required";
- case 428:
- return "Precondition Required";
- case 429:
- return "Too Many Requests";
- case 431:
- return "Request Header Fields Too Large";
- case 451:
- return "Unavailable For Legal Reasons";
- case 500:
- return "Internal Server Error";
- case 501:
- return "Not Implemented";
- case 502:
- return "Bad Gateway";
- case 503:
- return "Service Unavailable";
- case 504:
- return "Gateway Timeout";
- case 505:
- return "HTTP Version Not Supported";
- case 506:
- return "Variant Also Negotiates";
- case 507:
- return "Insufficient Storage (WebDAV)";
- case 508:
- return "Loop Detected (WebDAV)";
- case 510:
- return "Not Extended";
- case 511:
- return "Network Authentication Required";
- default:
- return "Unknown";
- }
-}
diff --git a/src/find.c b/src/find.c
deleted file mode 100644
index 348a8f7..0000000
--- a/src/find.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/**
- * @file find.c
- */
-#include "spm.h"
-
-/**
- * glob callback function
- * @param epath path to file that generated the error condition
- * @param eerrno the error condition
- * @return the error condition
- */
-int errglob(const char *epath, int eerrno) {
- fprintf(stderr, "glob matching error: %s (%d)", epath, eerrno);
- return eerrno;
-}
-
-/**
- * Scan a directory for a file by name, or by wildcard
- *
- * @param root directory path to scan
- * @param filename file to find (wildcards accepted)
- * @return success=path to file, failure=NULL
- */
-char *find_file(const char *root, const char *filename) {
- glob_t results;
- int glob_flags = 0;
- int match = 0;
- char path[PATH_MAX];
- memset(path, '\0', PATH_MAX);
-
- // GUARD
- if (!root || !filename || strstr(filename, "..") || strstr(filename, "./")) {
- return NULL;
- }
-
- if (realpath(root, path) == NULL) {
- perror("Cannot determine realpath()");
- fprintf(SYSERROR);
- return NULL;
- }
-
- strcat(path, "/");
- strcat(path, filename);
-
- // Save a little time if the file exists
- if (access(path, F_OK) != -1) {
- return strdup(path);
- }
-
- // Inject wildcard
- strcat(path, "*");
- // Search for the file
- match = glob(path, glob_flags, errglob, &results);
-
- if (match != 0) {
- // report critical errors except GLOB_NOMATCH
- if (match == GLOB_NOSPACE || match == GLOB_ABORTED) {
- fprintf(SYSERROR);
- }
- globfree(&results);
- return NULL;
- }
-
- // Replace path string with wanted path string
- strcpy(path, results.gl_pathv[0]);
-
- globfree(&results);
- return strdup(path);
-}
-
-/**
- * Scan the package directory for a package by name
- * @param filename file to find
- * @return success=path to file, failure=NULL
- */
-char *find_package(const char *filename) {
- char *repo = join((char *[]) {SPM_GLOBAL.package_dir, SPM_GLOBAL.repo_target, NULL}, DIRSEPS);
- char *match = find_file(repo, filename);
- free(repo);
- return match;
-}
-
-/**
- * Determine whether `pattern` is present within a file
- * @param filename
- * @param pattern
- * @return 0=found, 1=not found, -1=OS error
- */
-int find_in_file(const char *filename, const char *pattern) {
- int result = 1; // default "not found"
-
- FILE *fp = fopen(filename, "rb");
- if (!fp) {
- return -1;
- }
-
- long int file_len = get_file_size(filename);
- if (file_len == -1) {
- fclose(fp);
- return -1;
- }
- char *buffer = (char *)calloc((size_t) file_len, sizeof(char));
- if (!buffer) {
- fclose(fp);
- return -1;
- }
- size_t pattern_len = strlen(pattern);
-
- fread(buffer, (size_t) file_len, sizeof(char), fp);
- fclose(fp);
-
- for (size_t i = 0; i < (size_t) file_len; i++) {
- if (memcmp(&buffer[i], pattern, pattern_len) == 0) {
- result = 0; // found
- break;
- }
- }
- free(buffer);
- return result;
-}
-
-/**
- * Get the full path of a shell command
- * @param program
- * @return success=absolute path to program, failure=NULL
- */
-char *find_executable(const char *program) {
- int found = 0;
- char *result = NULL;
- char *env_path = NULL;
- env_path = getenv("PATH");
- if (!env_path) {
- return NULL;
- }
- char **search_paths = split(env_path, ":");
-
- char buf[PATH_MAX];
- for (int i = 0; search_paths[i] != NULL; i++) {
- sprintf(buf, "%s%c%s", search_paths[i], DIRSEP, program);
- if (access(buf, F_OK | X_OK) == 0) {
- found = 1;
- break;
- }
- memset(buf, '\0', sizeof(buf));
- }
- if (found) {
- result = strdup(buf);
- }
- split_free(search_paths);
- return result;
-}
diff --git a/src/fs.c b/src/fs.c
deleted file mode 100644
index d920248..0000000
--- a/src/fs.c
+++ /dev/null
@@ -1,504 +0,0 @@
-/**
- * @file fs.c
- */
-#include "spm.h"
-
-/**
- *
- * @param _path
- * @return
- */
-FSTree *fstree(const char *_path, char **filter_by, unsigned int filter_mode) {
- FTS *parent = NULL;
- FTSENT *node = NULL;
- FSTree *fsdata = NULL;
- int no_filter = 0;
- char *path = NULL;
- char *abspath = realpath(_path, NULL);
-
- if (filter_mode & SPM_FSTREE_FLT_RELATIVE) {
- path = strdup(_path);
- } else {
- path = abspath;
- }
-
- if (path == NULL) {
- perror(_path);
- fprintf(SYSERROR);
- return NULL;
- }
- char *root[2] = { path, NULL };
-
- if (filter_by == NULL) {
- // Create an array with an empty string. This signifies we want don't want to filter any paths.
- no_filter = 1;
- filter_by = calloc(2, sizeof(char *));
- filter_by[0] = calloc(2, sizeof(char));
- strcpy(filter_by[0], "");
- }
-
- size_t dirs_size = 2;
- size_t dirs_records = 0;
- size_t files_size = 2;
- size_t files_records = 0;
-
- fsdata = (FSTree *)calloc(1, sizeof(FSTree));
- fsdata->root = (char *)calloc(PATH_MAX, sizeof(char));
- fsdata->dirs = (char **)calloc(dirs_size, sizeof(char *));
- fsdata->files = (char **)calloc(files_size, sizeof(char *));
-
- if (filter_mode & SPM_FSTREE_FLT_RELATIVE) {
- // Return an absolute path regardless
- strncpy(fsdata->root, abspath, PATH_MAX - 1);
- } else {
- strncpy(fsdata->root, path, PATH_MAX - 1);
- }
-
- parent = fts_open(root, FTS_PHYSICAL | FTS_NOCHDIR, &_fstree_compare);
-
- if (parent != NULL) {
- while ((node = fts_read(parent)) != NULL) {
- for (size_t i = 0; filter_by[i] != NULL; i++) {
- // Drop paths containing filter string(s) according to the requested mode
- if (filter_mode & SPM_FSTREE_FLT_CONTAINS && strstr(node->fts_path, filter_by[i]) == NULL) {
- continue;
- }
- else if (filter_mode & SPM_FSTREE_FLT_ENDSWITH && !endswith(node->fts_path, filter_by[i])) {
- continue;
- }
- else if (filter_mode & SPM_FSTREE_FLT_STARTSWITH && !startswith(node->fts_path, filter_by[i])) {
- continue;
- }
- switch (node->fts_info) {
- case FTS_D:
- if (strcmp(node->fts_path, "..") == 0 || strcmp(node->fts_path, ".") == 0) {
- continue;
- }
- fsdata->dirs = (char **) realloc(fsdata->dirs, sizeof(char *) * dirs_size);
- fsdata->dirs[dirs_size - 1] = NULL;
- fsdata->dirs[dirs_records] = (char *) calloc(strlen(node->fts_path) + 1, sizeof(char));
- strncpy(fsdata->dirs[dirs_records], node->fts_path, strlen(node->fts_path));
- dirs_size++;
- dirs_records++;
- break;
- case FTS_F:
- case FTS_SL:
- fsdata->files = (char **) realloc(fsdata->files, sizeof(char *) * files_size);
- fsdata->files[files_size - 1] = NULL;
- fsdata->files[files_records] = (char *) calloc(strlen(node->fts_path) + 1, sizeof(char));
- strncpy(fsdata->files[files_records], node->fts_path, strlen(node->fts_path));
- files_size++;
- files_records++;
- break;
- default:
- break;
- }
- }
- }
- fts_close(parent);
- }
- fsdata->dirs_length = dirs_records;
- fsdata->files_length = files_records;
- free(path);
- if (no_filter) {
- free(filter_by[0]);
- free(filter_by);
- }
- return fsdata;
-}
-
-/**
- *
- * @param one
- * @param two
- * @return
- */
-int _fstree_compare(const FTSENT **one, const FTSENT **two) {
- return (strcmp((*one)->fts_name, (*two)->fts_name));
-}
-
-/**
- *
- * @param _path
- * @return
- */
-int rmdirs(const char *_path) {
- if (access(_path, F_OK) != 0) {
- return -1;
- }
-
- FSTree *data = fstree(_path, NULL, SPM_FSTREE_FLT_NONE);
- if (data->files) {
- for (size_t i = 0; data->files[i] != NULL; i++) {
- remove(data->files[i]);
- }
- }
- if (data->dirs) {
- for (size_t i = data->dirs_length - 1; i != 0; i--) {
- remove(data->dirs[i]);
- }
- }
- remove(data->root);
-
- fstree_free(data);
- return 0;
-}
-
-/**
- * Free a `FSTree` structure
- * @param fsdata
- */
-void fstree_free(FSTree *fsdata) {
- if (fsdata != NULL) {
- if (fsdata->root != NULL) {
- free(fsdata->root);
- }
- if (fsdata->files != NULL) {
- for (int i = 0; fsdata->files[i] != NULL; i++) {
- free(fsdata->files[i]);
- }
- free(fsdata->files);
- }
- if (fsdata->dirs != NULL) {
- for (int i = 0; fsdata->dirs[i] != NULL; i++) {
- free(fsdata->dirs[i]);
- }
- free(fsdata->dirs);
- }
- free(fsdata);
- }
-}
-
-/**
- * Expand "~" to the user's home directory
- *
- * Example:
- * ~~~{.c}
- * char *home = expandpath("~"); // == /home/username
- * char *config = expandpath("~/.config"); // == /home/username/.config
- * char *nope = expandpath("/tmp/test"); // == /tmp/test
- * char *nada = expandpath("/~/broken"); // == /~/broken
- *
- * free(home);
- * free(config);
- * free(nope);
- * free(nada);
- * ~~~
- *
- * @param _path (Must start with a `~`)
- * @return success=expanded path or original path, failure=NULL
- */
-char *expandpath(const char *_path) {
- if (_path == NULL) {
- return NULL;
- }
- const char *homes[] = {
- "HOME",
- "USERPROFILE",
- };
- char home[PATH_MAX];
- char tmp[PATH_MAX];
- char *ptmp = tmp;
- char result[PATH_MAX];
- char *sep = NULL;
-
- memset(home, '\0', sizeof(home));
- memset(ptmp, '\0', sizeof(tmp));
- memset(result, '\0', sizeof(result));
-
- strncpy(ptmp, _path, PATH_MAX - 1);
-
- // Check whether there's a reason to continue processing the string
- if (*ptmp != '~') {
- return strdup(ptmp);
- }
-
- // Remove tilde from the string and shift its contents to the left
- strchrdel(ptmp, "~");
-
- // Figure out where the user's home directory resides
- for (size_t i = 0; i < sizeof(homes); i++) {
- char *tmphome;
- if ((tmphome = getenv(homes[i])) != NULL) {
- strncpy(home, tmphome, PATH_MAX - 1);
- break;
- }
- }
-
- // A broken runtime environment means we can't do anything else here
- if (isempty(home)) {
- return NULL;
- }
-
- // Scan the path for a directory separator
- if ((sep = strpbrk(ptmp, "/\\")) != NULL) {
- // Jump past it
- ptmp = sep + 1;
- }
-
- // Construct the new path
- strncat(result, home, PATH_MAX - 1);
- if (sep) {
- strncat(result, DIRSEPS, PATH_MAX - 1);
- strncat(result, ptmp, PATH_MAX - 1);
- }
-
- return strdup(result);
-}
-
-/**
- * Converts Win32 path to Unix path, and vice versa
- * - On UNIX, Win32 paths will be converted UNIX
- * - On Win32, UNIX paths will be converted to Win32
- *
- * This function is platform dependent. The string is modified in-place.
- *
- * @param path a system path
- * @return string
- */
-char *normpath(const char *path) {
- char *result = strdup(path);
- char *tmp = result;
-
- while (*tmp) {
- if (*tmp == NOT_DIRSEP) {
- *tmp = DIRSEP;
- tmp++;
- continue;
- }
- tmp++;
- }
- return result;
-}
-
-
-/**
- * Strip file name from directory
- * Note: Caller is responsible for freeing memory
- *
- * @param _path
- * @return success=path to directory, failure=NULL
- */
-char *dirname(const char *_path) {
- if (_path == NULL) {
- return NULL;
- }
- char *path = strdup(_path);
- char *last = strrchr(path, DIRSEP);
- if (!last) {
- return NULL;
- }
- // Step backward, stopping on the first non-separator
- // This ensures strings like "/usr//////" are converted to "/usr", but...
- // it will do nothing to fix up a path like "/usr//////bin/bash
- char *lookback = last;
- while (*(lookback - 1) == DIRSEP) {
- lookback--;
- }
-
- *lookback = '\0';
- return path;
-}
-
-/**
- * Strip directory from file name
- * Note: Caller is responsible for freeing memory
- *
- * @param _path
- * @return success=file name, failure=NULL
- */
-char *basename(char *path) {
- char *result = NULL;
- char *last = NULL;
-
- if ((last = strrchr(path, DIRSEP)) == NULL) {
- return result;
- }
- // Perform a lookahead ensuring the string is valid beyond the last separator
- if (last++ != NULL) {
- result = last;
- }
-
- return result;
-}
-
-/**
- * Basic rsync wrapper for copying files
- * @param _args arguments to pass to rsync (set to `NULL` for default options)
- * @param _source source file or directory
- * @param _destination destination file or directory
- * @return success=0, failure=-1
- */
-int rsync(const char *_args, const char *_source, const char *_destination) {
- int returncode;
- Process *proc = NULL;
- char *args = NULL;
- if (_args) {
- args = strdup(_args);
- }
- char *source = strdup(_source);
- char *destination = strdup(_destination);
- char cmd[PATH_MAX];
- char *args_combined = (char *)calloc(PATH_MAX, sizeof(char));
-
- memset(cmd, '\0', sizeof(cmd));
- strcpy(args_combined, "--archive --hard-links ");
- if (args) {
- strcat(args_combined, _args);
- }
-
- strchrdel(args_combined, SHELL_INVALID);
- strchrdel(source, SHELL_INVALID);
- strchrdel(destination, SHELL_INVALID);
-
- snprintf(cmd, PATH_MAX, "rsync %s \"%s\" \"%s\" 2>&1", args_combined, source, destination);
- shell(&proc, SHELL_OUTPUT, cmd);
- if (!proc) {
- if (args) {
- free(args);
- }
- free(source);
- free(destination);
- return -1;
- }
-
- returncode = proc->returncode;
- if (returncode != 0 && proc->output) {
- fprintf(stderr, "%s\n", proc->output);
- }
- shell_free(proc);
-
- if (args) {
- free(args);
- }
- free(args_combined);
- free(source);
- free(destination);
- return returncode;
-}
-
-/**
- * Return the size of a file
- * @param filename
- * @return
- */
-long int get_file_size(const char *filename) {
- long int result = 0;
- FILE *fp = fopen(filename, "rb");
- if (!fp) {
- return -1;
- }
- fseek(fp, 0, SEEK_END);
- result = ftell(fp);
- fclose(fp);
- return result;
-}
-
-/**
- * Attempt to create a directory (or directories)
- * @param _path A path to create
- * @param mode UNIX permissions (octal)
- * @return success=0, failure=-1 (+ errno will be set)
- */
-int mkdirs(const char *_path, mode_t mode) {
- int result = 0;
- char *path = normpath(_path);
- char tmp[PATH_MAX];
- tmp[0] = '\0';
-
- char sep[2];
- sprintf(sep, "%c", DIRSEP);
- char **parts = split(path, sep);
- for (int i = 0; parts[i] != NULL; i++) {
- strcat(tmp, parts[i]);
- strcat(tmp, sep);
- if (access(tmp, F_OK) != 0) {
- result = mkdir(tmp, mode);
- }
- }
- split_free(parts);
- return result;
-}
-
-/**
- * Short wrapper for `access`. Check if file exists.
- *
- * Example:
- * ~~~{.c}
- * if (exists("example.txt") != 0) {
- * // handle error
- * }
- * ~~~
- * @param filename
- * @return
- */
-int exists(const char *filename) {
- return access(filename, F_OK);
-}
-
-/**
- * Convert size in bytes to the closest human-readable unit
- *
- * NOTE: Caller is responsible for freeing memory
- *
- * Example:
- * ~~~{.c}
- * char *output;
- * output = human_readable_size(1); // "1B"
- * free(output);
- * output = human_readable_size(1024) // "1.0K"
- * free(output);
- * output = human_readable_size(1024000) // "1.0M"
- * free(output);
- * // and so on
- * ~~~
- *
- * @param n size to convert
- * @return string
- */
-char *human_readable_size(uint64_t n) {
- size_t i;
- double result = (double)n;
- char *unit[] = {"B", "K", "M", "G", "T", "P", "E"};
- char r[255];
- memset(r, '\0', sizeof(r));
-
- for (i = 0; i < sizeof(unit); i++) {
- if (fabs(result) < 1024) {
- break;
- }
- result /= 1024.0;
- }
-
- if (unit[i][0] == 'B') {
- sprintf(r, "%0.0lf%s", result, unit[i]);
- }
- else {
- sprintf(r, "%0.2lf%s", result, unit[i]);
- }
-
- return strdup(r);
-}
-
-/**
- * Create a named temporary directory
- * @param name
- * @return success=path, failure=NULL
- */
-char *spm_mkdtemp(const char *name, const char *extended_path) {
- const char *template_unique = "XXXXXX";
- char *tmpdir = NULL;
- char *template = calloc(PATH_MAX, sizeof(char));
-
- sprintf(template, "%s%s%s_%s", TMP_DIR, DIRSEPS, name, template_unique);
- tmpdir = mkdtemp(template);
- if (extended_path != NULL) {
- char extended[PATH_MAX] = {0,};
- strncpy(extended, tmpdir, PATH_MAX - 1);
- strcat(extended, DIRSEPS);
- strcat(extended, extended_path);
- mkdirs(extended, 0755);
- }
- return tmpdir;
-}
-
diff --git a/src/install.c b/src/install.c
deleted file mode 100644
index e0592db..0000000
--- a/src/install.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/**
- * @file install.c
- */
-#include <url.h>
-#include "spm.h"
-
-void spm_install_show_package(ManifestPackage *package) {
- if (package == NULL) {
- fprintf(stderr, "ERROR: package was NULL\n");
- return;
- }
- printf(" -> %-20s %-10s (origin: %s)\n", package->name, package->version, package->origin);
-}
-
-/**
- * Install a package and its dependencies into a destination root.
- * The destination is created if it does not exist.
- * @param _destroot directory to install package
- * @param _package name of archive to install (not a path)
- * @return success=0, exists=1, error=-1 (general), -2 (unable to create `destroot`)
- */
-int spm_install(SPM_Hierarchy *fs, const char *tmpdir, const char *_package) {
- char *package = strdup(_package);
-
- if (!package) {
- fprintf(SYSERROR);
- return -1;
- }
-
- if (exists(fs->rootdir) != 0) {
- if (SPM_GLOBAL.verbose) {
- printf("Creating destination root: %s\n", fs->rootdir);
- }
- if (mkdirs(fs->rootdir, 0755) != 0) {
- fprintf(SYSERROR);
- free(package);
- return -2;
- }
- }
-
- if (SPM_GLOBAL.verbose) {
- printf("Extracting archive: %s\n", package);
- }
-
- if (tar_extract_archive(package, tmpdir) != 0) {
- fprintf(stderr, "%s: %s\n", package, strerror(errno));
- free(package);
- return -1;
- }
-
- free(package);
- return 0;
-}
-
-int spm_install_package_record(SPM_Hierarchy *fs, char *tmpdir, char *package_name) {
- RuntimeEnv *rt = runtime_copy(__environ);
- char *records_topdir = strdup(fs->dbrecdir);
- char *records_pkgdir = join((char *[]) {records_topdir, package_name, NULL}, DIRSEPS);
- char *descriptor = join((char *[]) {tmpdir, SPM_META_DESCRIPTOR, NULL}, DIRSEPS);
- char *filelist = join((char *[]) {tmpdir, SPM_META_FILELIST, NULL}, DIRSEPS);
-
- if (exists(records_pkgdir) != 0) {
- if (mkdirs(records_pkgdir, 0755) != 0) {
- return -1;
- }
- }
-
- if (exists(descriptor) != 0) {
- fprintf(stderr, "Missing: %s\n", descriptor);
- return 1;
- }
-
- if (exists(filelist) != 0) {
- fprintf(stderr, "Missing: %s\n", filelist);
- return 2;
- }
-
- if (rsync(NULL, descriptor, records_pkgdir) != 0) {
- fprintf(stderr, "Failed to copy '%s' to '%s'\n", descriptor, records_pkgdir);
- return 3;
- }
-
- if (rsync(NULL, filelist, records_pkgdir) != 0) {
- fprintf(stderr, "Failed to copy '%s' to '%s'\n", filelist, records_pkgdir);
- return 4;
- }
-
- free(records_topdir);
- free(records_pkgdir);
- free(descriptor);
- free(filelist);
- runtime_free(rt);
- return 0;
-}
-
-int spm_check_installed(SPM_Hierarchy *fs, char *package_name) {
- char *records_topdir = join((char *[]) {fs->localstatedir, "db", "records", NULL}, DIRSEPS);
- char *records_pkgdir = join((char *[]) {records_topdir, package_name, NULL}, DIRSEPS);
-
- char *descriptor = join((char *[]) {records_pkgdir, SPM_META_DESCRIPTOR, NULL}, DIRSEPS);
- char *filelist = join((char *[]) {records_pkgdir, SPM_META_FILELIST, NULL}, DIRSEPS);
- char **data = NULL;
-
- if ((exists(records_pkgdir) || exists(descriptor) || exists(descriptor)) != 0) {
- free(records_topdir);
- free(records_pkgdir);
- free(descriptor);
- free(filelist);
- return 0; // does not exist
- }
-
- data = spm_metadata_read(filelist, SPM_METADATA_VERIFY);
- if (data == NULL) {
- free(records_topdir);
- free(records_pkgdir);
- free(descriptor);
- free(filelist);
- return -1;
- }
-
- for (size_t i = 0; data[i] != NULL; i++) {
- free(data[i]);
- }
- free(data);
-
- free(records_topdir);
- free(records_pkgdir);
- free(descriptor);
- free(filelist);
- return 1; // exists
-}
-
-/**
- *
- * @return
- */
-char *spm_install_fetch(const char *pkgdir, const char *_package) {
- char *package = strdup(_package);
- if (package == NULL) {
- perror("could not allocate memory for package name");
- fprintf(SYSERROR);
- return NULL;
- }
-
- long response = 0;
- char *url = strdup(package);
- char *payload = join_ex(DIRSEPS, pkgdir, basename(package), NULL);
- size_t tmp_package_len = strlen(payload);
-
- if (tmp_package_len > strlen(package)) {
- char *tmp = realloc(package, (tmp_package_len + 1) * sizeof(char));
- if (tmp == NULL) {
- perror("cannot realloc package path");
- return NULL;
- }
- package = tmp;
- }
- strcpy(package, payload);
-
- if (exists(payload) != 0) {
- if ((response = fetch(url, package)) >= 400) {
- fprintf(stderr, "HTTP(%ld): %s\n", response, http_response_str(response));
- return NULL;
- }
- }
- free(url);
- free(payload);
-
- return package;
-}
-
-/**
- * Perform a full package installation
- * @param mf
- * @param rootdir
- * @param packages
- * @return 0=success, -1=failed to create storage, -2=denied by user
- */
-int spm_do_install(SPM_Hierarchy *fs, ManifestList *mf, StrList *packages) {
- size_t num_requirements = 0;
- ManifestPackage **requirements = NULL;
- char source[PATH_MAX];
- char *tmpdir = spm_mkdtemp("spm_destroot", NULL);
-
- if (tmpdir == NULL) {
- perror("Could not create temporary destination root");
- fprintf(SYSERROR);
- return -1;
- }
-
- if (SPM_GLOBAL.verbose) {
- printf("Installation root: %s\n", fs->rootdir);
- }
-
- // Produce a dependency tree from requested package(s)
- for (size_t i = 0; i < strlist_count(packages); i++) {
- char *item = strlist_item(packages, i);
- requirements = resolve_dependencies(mf, item);
- if (requirements != NULL) {
- for (size_t c = num_requirements; requirements[c] != NULL; c++) {
- num_requirements++;
- }
- }
- }
-
- // Install packages
- printf("Requested package(s):\n");
- for (size_t i = 0; requirements !=NULL && requirements[i] != NULL; i++) {
- spm_install_show_package(requirements[i]);
- }
-
- if (SPM_GLOBAL.prompt_user) {
- if (spm_prompt_user("Proceed with installation?", 1) == 0) {
- exit(-2);
- }
- }
-
- int fetched = 0;
- char *package_dir = strdup(SPM_GLOBAL.package_dir);
- for (size_t i = 0; requirements != NULL && requirements[i] != NULL; i++) {
- char *package_origin = calloc(PATH_MAX, sizeof(char));
- strncpy(package_origin, requirements[i]->origin, PATH_MAX);
-
- if (strstr(package_origin, SPM_GLOBAL.repo_target) == NULL) {
- if (!endswith(package_origin, DIRSEPS)) {
- strcat(package_origin, DIRSEPS);
- }
- strcat(package_origin, SPM_GLOBAL.repo_target);
- }
-
- char *package_path = join((char *[]) {package_origin, requirements[i]->archive, NULL}, DIRSEPS);
- char *package_localpath = join_ex(DIRSEPS, package_dir, requirements[i]->archive, NULL);
- free(package_origin);
-
- // Download the archive if necessary
- if (strstr(package_path, "://") != NULL && exists(package_localpath) != 0) {
- printf("Fetching: %s\n", package_path);
- package_path = spm_install_fetch(package_dir, package_path);
- if (package_path == NULL) {
- free(package_path);
- free(package_localpath);
- exit(1);
- }
- fetched = 1;
- }
- // Or copy the archive if necessary
- else {
- // TODO: Possibly an issue down the road, but not at the moment
- // You have another local manifest in use. Copy any used packages from there into the local package directory.
- if (exists(package_localpath) != 0 && strncmp(package_dir, package_path, strlen(package_dir)) != 0) {
- printf("Copying: %s\n", package_path);
- if (rsync(NULL, package_path, package_dir) != 0) {
- fprintf(stderr, "Unable to copy: %s to %s\n", package_path, package_dir);
- return -1;
- }
- fetched = 1;
- } else if (exists(package_localpath) != 0) {
- // All attempts to retrieve the requested package have failed. Die.
- fprintf(stderr, "Package manifest in '%s' claims '%s' exists, however it does not.\n", requirements[i]->origin, package_path);
- return -1;
- }
- }
- free(package_path);
- free(package_localpath);
- }
-
- // Update the package manifest
- if (fetched) {
- printf("Updating package manifest...\n");
- Manifest *tmp_manifest = manifest_from(SPM_GLOBAL.package_dir);
- manifest_write(tmp_manifest, package_dir);
- manifest_free(tmp_manifest);
- }
-
- printf("Installing package(s):\n");
- size_t num_installed = 0;
- for (size_t i = 0; requirements != NULL && requirements[i] != NULL; i++) {
- char *package_path = join((char *[]) {package_dir, requirements[i]->archive, NULL}, DIRSEPS);
-
- if (spm_check_installed(fs, requirements[i]->name)) {
- printf(" -> %s is already installed\n", requirements[i]->name);
- free(package_path);
- continue;
- }
-
- spm_install_show_package(requirements[i]);
- spm_install(fs, tmpdir, package_path);
-
- // Relocate installation root
- relocate_root(fs->rootdir, tmpdir);
-
- spm_install_package_record(fs, tmpdir, requirements[i]->name);
- num_installed++;
- free(package_path);
- }
-
- // free requirements array
- for (size_t i = 0; requirements != NULL && requirements[i] != NULL; i++) {
- manifest_package_free(requirements[i]);
- }
- free(package_dir);
-
- if (num_installed != 0) {
- // Append a trailing slash to tmpdir to direct rsync to copy files, not the directory, into destroot
- sprintf(source, "%s%c", tmpdir, DIRSEP);
-
- // Remove metadata files before copying
- if (SPM_GLOBAL.verbose) {
- printf("Removing metadata\n");
- }
- spm_metadata_remove(source);
-
- // Copy temporary directory to destination
- if (SPM_GLOBAL.verbose) {
- printf("Installing tree: '%s' => '%s'\n", source, fs->rootdir);
- }
-
- if (rsync(NULL, source, fs->rootdir) != 0) {
- exit(1);
- }
- }
-
- if (SPM_GLOBAL.verbose) {
- printf("Removing temporary storage: '%s'\n", tmpdir);
- }
- rmdirs(tmpdir);
- return 0;
-}
diff --git a/src/internal_cmd.c b/src/internal_cmd.c
deleted file mode 100644
index a192ccf..0000000
--- a/src/internal_cmd.c
+++ /dev/null
@@ -1,401 +0,0 @@
-/**
- * @file internal_cmd.c
- */
-#include "spm.h"
-
-/**
- * List of valid internal commands
- */
-static char *internal_commands[] = {
- "mkprefixbin", "generate prefix manifest (binary)",
- "mkprefixtext", "generate prefix manifest (text)",
- "mkmanifest", "generate package repository manifest",
- "mkruntime", "emit runtime environment (stdout)",
- "mirror_clone", "mirror a mirror",
- "rpath_set", "modify binary RPATH",
- "rpath_autoset", "determine nearest lib directory and set RPATH",
- "get_package_ext", "show the default archive extension",
- "get_sys_target", "show this system's arch/platform",
- "check_rt_env", "check the integrity of the calling runtime environment",
- NULL, NULL,
-};
-
-/**
- *
- */
-void mkprefix_interface_usage(void) {
- printf("usage: mkprefix[bin|text] {output_file} {dir} {prefix ...}\n");
-}
-
-/**
- * Create prefix manifests from the CLI
- * @param argc
- * @param argv
- * @return return value of `prefixes_write`
- */
-int mkprefix_interface(int argc, char **argv) {
- char *command = argv[0];
- char *outfile = argv[1];
- char *tree = argv[2];
-
- size_t prefix_start = 3;
- size_t prefixes = 0;
- for (size_t i = prefix_start; i < (size_t) argc; i++) {
- prefixes = i;
- }
-
- // Check arguments
- if (!outfile) {
- fprintf(stderr, "error: missing output file name\n");
- mkprefix_interface_usage();
- return -1;
- }
- if (!tree) {
- fprintf(stderr, "error: missing directory path\n");
- mkprefix_interface_usage();
- return -1;
- }
- if (!prefixes) {
- fprintf(stderr, "error: missing prefix string(s)\n");
- mkprefix_interface_usage();
- return -1;
- }
-
- char **prefix = (char **) calloc(prefixes + 1, sizeof(char *));
- if (!prefix) {
- perror("prefix array");
- fprintf(SYSERROR);
- return -1;
- }
-
- // Populate array of prefixes; reusing pointers from argv
- for (size_t i = 0; (i + prefix_start) < (size_t) argc; i++) {
- prefix[i] = argv[(i + prefix_start)];
- }
-
- if (SPM_GLOBAL.verbose) {
- printf("Generating prefix manifest: %s\n", outfile);
- }
-
- int result = 0;
- if (strcmp(command, "mkprefixbin") == 0) {
- result = prefixes_write(outfile, PREFIX_WRITE_BIN, prefix, tree);
- } else if (strcmp(command, "mkprefixtext") == 0) {
- result = prefixes_write(outfile, PREFIX_WRITE_TEXT, prefix, tree);
- }
- return result;
-}
-
-/**
- *
- */
-void mkmanifest_interface_usage(void) {
- printf("usage: mkmanifest [package_dir] [output_dir]\n");
-}
-
-/**
- * Generate a named package manifest
- * @param argc
- * @param argv
- * @return value of `manifest_write`
- */
-int mkmanifest_interface(int argc, char **argv) {
- Manifest *manifest = NULL;
- int result = 0;
- char *pkgdir = NULL;
-
- if (argc < 2) {
- mkmanifest_interface_usage();
- return -1;
- }
-
- if ((pkgdir = expandpath(argv[1])) == NULL) {
- fprintf(stderr, "bad path\n");
- return -2;
- }
-
- if (exists(pkgdir) != 0) {
- fprintf(stderr, "'%s': does not exist\n", pkgdir);
- return -3;
- }
-
- manifest = manifest_from(pkgdir);
- if (manifest == NULL) {
- fprintf(stderr, "no packages\n");
- return -4;
- }
-
- result = manifest_write(manifest, pkgdir);
- if (result != 0) {
- fprintf(stderr, "an error occurred while writing manifest data\n");
- manifest_free(manifest);
- return -5;
- }
-
- free(pkgdir);
- manifest_free(manifest);
- return result;
-}
-
-/**
- *
- */
-void mkruntime_interface_usage(void) {
- printf("usage: mkruntime {root_dir}\n");
-}
-
-/**
- *
- * @param argc
- * @param argv
- * @return
- */
-int mkruntime_interface(int argc, char **argv) {
- if (argc < 2) {
- mkruntime_interface_usage();
- return -1;
- }
-
- RuntimeEnv *rt = runtime_copy(__environ);
- if (rt == NULL) {
- return -1;
- }
-
- char *root = argv[1];
- SPM_Hierarchy *fs = spm_hierarchy_init(root);
- char *spm_pkgconfigdir = join((char *[]) {fs->libdir, "pkgconfig", NULL}, DIRSEPS);
-
- runtime_set(rt, "SPM_BIN", fs->bindir);
- runtime_set(rt, "SPM_INCLUDE", fs->includedir);
- runtime_set(rt, "SPM_LIB", fs->libdir);
- runtime_set(rt, "SPM_LIB64", "${SPM_LIB}64");
- runtime_set(rt, "SPM_DATA", fs->datadir);
- runtime_set(rt, "SPM_MAN", fs->mandir);
- runtime_set(rt, "SPM_LOCALSTATE", fs->localstatedir);
- runtime_set(rt, "SPM_PKGCONFIG", spm_pkgconfigdir);
- runtime_set(rt, "SPM_PKGCONFIG", "${SPM_PKGCONFIG}:${SPM_LIB64}/pkgconfig:${SPM_DATA}/pkgconfig");
- runtime_set(rt, "SPM_META_DEPENDS", SPM_META_DEPENDS);
- runtime_set(rt, "SPM_META_PREFIX_BIN", SPM_META_PREFIX_BIN);
- runtime_set(rt, "SPM_META_PREFIX_TEXT", SPM_META_PREFIX_TEXT);
- runtime_set(rt, "SPM_META_DESCRIPTOR", SPM_META_DESCRIPTOR);
- runtime_set(rt, "SPM_META_FILELIST", SPM_META_FILELIST);
- runtime_set(rt, "SPM_META_PREFIX_PLACEHOLDER", SPM_META_PREFIX_PLACEHOLDER);
-
- runtime_set(rt, "PATH", "$SPM_BIN:$PATH");
- runtime_set(rt, "MANPATH", "$SPM_MAN:$MANPATH");
- runtime_set(rt, "PKG_CONFIG_PATH", "$SPM_PKGCONFIG:$PKG_CONFIG_PATH");
- runtime_set(rt, "ACLOCAL_PATH", "${SPM_DATA}/aclocal");
-
- char *spm_ccpath = join((char *[]) {fs->bindir, "gcc"}, DIRSEPS);
- if (exists(spm_ccpath) == 0) {
- runtime_set(rt, "CC", "$SPM_BIN/gcc");
- }
-
- runtime_set(rt, "CFLAGS", "-I$SPM_INCLUDE $CFLAGS");
- runtime_set(rt, "LDFLAGS", "-Wl,-rpath=$SPM_LIB:$SPM_LIB64 -L$SPM_LIB -L$SPM_LIB64 $LDFLAGS");
- runtime_export(rt, NULL);
- runtime_free(rt);
-
- free(spm_pkgconfigdir);
- free(spm_ccpath);
- spm_hierarchy_free(fs);
- return 0;
-}
-
-/**
- *
- */
-void mirror_clone_interface_usage(void) {
- printf("usage: mirror_clone {url} {output_dir}\n");
-}
-
-/**
- * Mirror packages referenced by a remote manifest
- * @param argc
- * @param argv
- * @return value of `manifest_write`
- */
-int mirror_clone_interface(int argc, char **argv) {
- if (argc < 3) {
- mirror_clone_interface_usage();
- return -1;
- }
- char *url = argv[1];
- char *path = argv[2];
-
- Manifest *manifest = manifest_read(url);
- if (manifest == NULL) {
- return -2;
- }
-
- mirror_clone(manifest, path);
- manifest_free(manifest);
- return 0;
-}
-/**
- *
- */
-void rpath_set_interface_usage(void) {
- printf("usage: rpath_set {file} {rpath}\n");
-}
-
-/**
- * Set a RPATH from the CLI
- * @param argc
- * @param argv
- * @return return value of `rpath_set`
- */
-int rpath_set_interface(int argc, char **argv) {
- if (argc < 3) {
- rpath_set_interface_usage();
- return -1;
- }
- char *filename = argv[1];
- char *rpath = argv[2];
- int result = rpath_set(filename, rpath);
- if (result < 0) {
- fprintf(SYSERROR);
- }
- return result;
-}
-
-/**
- *
- */
-void rpath_autoset_interface_usage(void) {
- printf("usage: rpath_autoset {file} {topdir}\n");
-}
-
-/**
- * Set a RPATH automatically from the CLI
- * @param argc
- * @param argv
- * @return return value of `rpath_autoset`
- */
-int rpath_autoset_interface(int argc, char **argv) {
- if (argc < 3) {
- rpath_autoset_interface_usage();
- return -1;
- }
- char *filename = argv[1];
- const char *topdir = argv[2];
-
- if (exists(filename) != 0) {
- perror(filename);
- return -1;
- }
-
- if (exists(topdir) != 0) {
- perror(topdir);
- return -1;
- }
-
- FSTree *libs = rpath_libraries_available(topdir);
- int result = rpath_autoset(filename, libs);
-
- if (result < 0) {
- fprintf(SYSERROR);
- }
-
- return result;
-}
-
-/**
- * Dump the default package extension for SPM archives to `stdout`
- * @return
- */
-int get_package_ext_interface(void) {
- puts(SPM_PACKAGE_EXTENSION);
- return 0;
-}
-
-/**
- * Dump the system arch/platform (i.e. Linux/x86_64)
- * @return
- */
-int get_sys_target_interface(void) {
- puts(SPM_GLOBAL.repo_target);
- return 0;
-}
-/**
- * Execute builtin runtime check.
- *
- * On failure this function will EXIT the program with a non-zero value
- *
- * @return
- */
-int check_runtime_environment_interface(void) {
- check_runtime_environment();
- return 0;
-}
-
-/**
- * Show a listing of valid internal commands
- */
-void internal_command_list(void) {
- printf("possible commands:\n");
- for (size_t i = 0; internal_commands[i] != NULL; i += 2) {
- printf(" %-20s - %-20s\n", internal_commands[i], internal_commands[i + 1]);
- }
-}
-
-/**
- * Execute an internal command
- * @param argc
- * @param argv
- * @return success=0, failure=1, error=-1
- */
-int internal_cmd(int argc, char **argv) {
- int command_valid = 0;
- char *command = argv[1];
- if (argc < 2) {
- internal_command_list();
- return 1;
- }
-
- for (int i = 0; internal_commands[i] != NULL; i++) {
- if (strcmp(internal_commands[i], command) == 0) {
- command_valid = 1;
- break;
- }
- }
-
- if (!command_valid) {
- fprintf(stderr, "error: '%s' is not a valid command\n", command);
- internal_command_list();
- return 1;
- }
-
- // Strip the first argument (this level) before passing it along to the interface
- int arg_count = argc - 1;
- char **arg_array = &argv[1];
-
- if (strcmp(command, "mkprefixbin") == 0 || strcmp(command, "mkprefixtext") == 0) {
- return mkprefix_interface(arg_count, arg_array);
- }
- else if (strcmp(command, "mkmanifest") == 0) {
- return mkmanifest_interface(arg_count, arg_array);
- }
- else if (strcmp(command, "mkruntime") == 0) {
- return mkruntime_interface(arg_count, arg_array);
- }
- else if (strcmp(command, "mirror_clone") == 0) {
- return mirror_clone_interface(arg_count, arg_array);
- }
- else if (strcmp(command, "rpath_set") == 0) {
- return rpath_set_interface(arg_count, arg_array);
- }
- else if (strcmp(command, "rpath_autoset") == 0) {
- return rpath_autoset_interface(arg_count, arg_array);
- }
- else if (strcmp(command, "get_package_ext") == 0) {
- return get_package_ext_interface();
- }
- else if (strcmp(command, "get_sys_target") == 0) {
- return get_sys_target_interface();
- }
- else if (strcmp(command, "check_rt_env") == 0) {
- return check_runtime_environment_interface();
- }
- return 0;
-}
diff --git a/src/manifest.c b/src/manifest.c
deleted file mode 100644
index 1b2b600..0000000
--- a/src/manifest.c
+++ /dev/null
@@ -1,668 +0,0 @@
-/**
- * @file manifest.c
- */
-#include "spm.h"
-#include <fnmatch.h>
-#include "url.h"
-
-/**
- * Compare `ManifestPackage` packages (lazily)
- * @param a
- * @param b
- * @return 0 = same, !0 = different
- */
-int manifest_package_cmp(ManifestPackage *a, ManifestPackage *b) {
- int result = 0;
- if (a == NULL || b == NULL) {
- return -1;
- }
- result += strcmp(a->origin, b->origin);
- result += strcmp(a->archive, b->archive);
- result += strcmp(a->checksum_sha256, b->checksum_sha256);
- return result;
-}
-
-void manifest_package_separator_swap(char **name) {
- // Replace unwanted separators in the package name with placeholder to prevent splitting on the wrong one
- int delim_count = num_chars((*name), SPM_PACKAGE_MEMBER_SEPARATOR) - SPM_PACKAGE_MIN_DELIM;
-
- if (delim_count < 0) {
- return;
- }
-
- for (size_t t = 0; t < strlen((*name)); t++) {
- if (delim_count == 0) break;
- if ((*name)[t] == SPM_PACKAGE_MEMBER_SEPARATOR) {
- (*name)[t] = SPM_PACKAGE_MEMBER_SEPARATOR_PLACEHOLD;
- delim_count--;
- }
- }
-}
-
-void manifest_package_separator_restore(char **name) {
- char separator[2];
- char placeholder[2];
- snprintf(separator, sizeof(separator), "%c", SPM_PACKAGE_MEMBER_SEPARATOR);
- snprintf(placeholder, sizeof(placeholder), "%c", SPM_PACKAGE_MEMBER_SEPARATOR_PLACEHOLD);
-
- replace_text((*name), placeholder, separator);
-}
-
-/**
- * Generate a `Manifest` of package data
- * @param package_dir a directory containing SPM packages
- * @return `Manifest`
- */
-Manifest *manifest_from(const char *package_dir) {
- char *package_filter[] = {SPM_PACKAGE_EXTENSION, NULL}; // We only want packages
- FSTree *fsdata = NULL;
- fsdata = fstree(package_dir, package_filter, SPM_FSTREE_FLT_ENDSWITH);
-
- Manifest *info = (Manifest *)calloc(1, sizeof(Manifest));
- info->records = fsdata->files_length;
- info->packages = (ManifestPackage **) calloc(info->records + 1, sizeof(ManifestPackage *));
- if (info->packages == NULL) {
- perror("Failed to allocate package array");
- fprintf(SYSERROR);
- free(info);
- fstree_free(fsdata);
- return NULL;
- }
-
- if (SPM_GLOBAL.verbose) {
- printf("Initializing package manifest:\n");
- }
- strncpy(info->origin, package_dir, SPM_PACKAGE_MEMBER_ORIGIN_SIZE);
-
-
- char *tmpdir = spm_mkdtemp("spm_manifest_from", NULL);
- if (!tmpdir) {
- perror("failed to create temporary directory");
- fprintf(SYSERROR);
- free(info);
- fstree_free(fsdata);
- return NULL;
- }
-
- for (size_t i = 0; i < fsdata->files_length; i++) {
- float percent = (((float)i + 1) / fsdata->files_length) * 100;
-
- if (SPM_GLOBAL.verbose) {
- printf("[%3.0f%%] %s\n", percent, basename(fsdata->files[i]));
- }
-
- // Initialize package record
- info->packages[i] = (ManifestPackage *) calloc(1, sizeof(ManifestPackage));
- if (info->packages[i] == NULL) {
- perror("Failed to allocate package record");
- fprintf(SYSERROR);
- fstree_free(fsdata);
- free(info);
- rmdirs(tmpdir);
- return NULL;
- }
-
- // Swap extra package separators with a bogus character
- manifest_package_separator_swap(&fsdata->files[i]);
-
- // Split the package name into parts
- char psep[2];
- snprintf(psep, sizeof(psep), "%c", SPM_PACKAGE_MEMBER_SEPARATOR);
- char **parts = split(fsdata->files[i], psep);
-
- // Restore package separator
- manifest_package_separator_restore(&parts[0]);
- manifest_package_separator_restore(&fsdata->files[i]);
-
- // Populate `ManifestPackage` record
- info->packages[i]->size = (size_t) get_file_size(fsdata->files[i]);
- strncpy(info->packages[i]->origin, info->origin, SPM_PACKAGE_MEMBER_ORIGIN_SIZE);
- strncpy(info->packages[i]->archive, basename(fsdata->files[i]), SPM_PACKAGE_MEMBER_SIZE);
- strncpy(info->packages[i]->name, basename(parts[0]), SPM_PACKAGE_MEMBER_SIZE);
- strncpy(info->packages[i]->version, parts[1], SPM_PACKAGE_MEMBER_SIZE);
- strncpy(info->packages[i]->revision, parts[2], SPM_PACKAGE_MEMBER_SIZE);
- strdelsuffix(info->packages[i]->revision, SPM_PACKAGE_EXTENSION);
-
- // Read package requirement specs
- char *archive = join((char *[]) {info->origin, info->packages[i]->archive, NULL}, DIRSEPS);
- if (tar_extract_file(archive, SPM_META_DEPENDS, tmpdir) != 0) {
- // TODO: at this point is the package is invalid? .SPM_DEPENDS should be there...
- fprintf(stderr, "extraction failure: %s\n", archive);
- rmdirs(tmpdir);
- exit(1);
- }
- char *depfile = join((char *[]) {tmpdir, SPM_META_DEPENDS, NULL}, DIRSEPS);
- info->packages[i]->requirements = file_readlines(depfile, 0, 0, NULL);
-
- // Record count of requirement specs
- if (info->packages[i]->requirements != NULL) {
- for (size_t rec = 0; info->packages[i]->requirements[rec] != NULL; rec++) {
- strip(info->packages[i]->requirements[rec]);
- info->packages[i]->requirements_records++;
- }
- }
-
- unlink(depfile);
- free(depfile);
- free(archive);
- split_free(parts);
- }
-
- fstree_free(fsdata);
- rmdirs(tmpdir);
- return info;
-}
-
-/**
- * Free a `Manifest` structure
- * @param info `Manifest`
- */
-void manifest_free(Manifest *info) {
- for (size_t i = 0; i < info->records; i++) {
- manifest_package_free(info->packages[i]);
- }
- free(info);
-}
-
-/**
- * Free a `ManifestPackage` structure
- * @param info `ManifestPackage`
- */
-void manifest_package_free(ManifestPackage *info) {
- for (size_t i = 0; i < info->requirements_records; i++) {
- free(info->requirements[i]);
- }
- free(info->requirements);
- free(info);
-}
-
-/**
- * Write a `Manifest` to the configuration directory
- * @param info
- * @param pkgdir
- * @return
- */
-int manifest_write(Manifest *info, const char *pkgdir) {
- char *reqs = NULL;
- char path[PATH_MAX];
- char path_manifest[PATH_MAX];
-
- memset(path, '\0', sizeof(path));
- memset(path_manifest, '\0', sizeof(path));
-
- strcpy(path, pkgdir);
-
- // Append the repo target if its missing
- if (strstr(path, SPM_GLOBAL.repo_target) == NULL) {
- strcat(path, DIRSEPS);
- strcat(path, SPM_GLOBAL.repo_target);
- }
- strcpy(path_manifest, path);
-
- // Append the manifest filename if its missing
- if (!endswith(path_manifest, SPM_MANIFEST_FILENAME)) {
- strcat(path_manifest, DIRSEPS);
- strcat(path_manifest, SPM_MANIFEST_FILENAME);
- }
-
- FILE *fp = fopen(path_manifest, "w+");
- if (fp == NULL) {
- perror(path_manifest);
- fprintf(SYSERROR);
- return -1;
- }
-#ifdef _DEBUG
- if (SPM_GLOBAL.verbose) {
- for (size_t i = 0; i < info->records; i++) {
- printf("%-20s: %s\n"
- "%-20s: %zu\n"
- "%-20s: %s\n"
- "%-20s: %s\n"
- "%-20s: %s\n"
- "%-20s: %zu\n",
- "archive", info->packages[i]->archive,
- "size", info->packages[i]->size,
- "name", info->packages[i]->name,
- "version", info->packages[i]->version,
- "revision", info->packages[i]->revision,
- "requirements_records", info->packages[i]->requirements_records
- );
- reqs = join(info->packages[i]->requirements, ", ");
- printf("%-20s: %s\n", "requirements", reqs ? reqs : "NONE");
- free(reqs);
- printf("\n");
- }
- }
-#endif
-
- if (SPM_GLOBAL.verbose) {
- printf("Generating manifest file: %s\n", path_manifest);
- }
- fprintf(fp, "%s\n", SPM_MANIFEST_HEADER);
- char data[BUFSIZ];
- for (size_t i = 0; i < info->records; i++) {
- // write CSV-like manifest
- memset(data, '\0', BUFSIZ);
- char *dptr = data;
- float percent = (((float)i + 1) / info->records) * 100;
- if (SPM_GLOBAL.verbose) {
- printf("[%3.0f%%] %s\n", percent, info->packages[i]->archive);
- }
- reqs = join(info->packages[i]->requirements, ",");
- char *archive = join((char *[]) {path, info->packages[i]->archive, NULL}, DIRSEPS);
- char *checksum_sha256 = sha256sum(archive);
-
- sprintf(dptr, "%s|" // archive
- "%zu|" // size
- "%s|" // name
- "%s|" // version
- "%s|" // revision
- "%zu|" // requirements_records
- "%s|" // requirements
- "%s" // checksum_md5
- , info->packages[i]->archive,
- info->packages[i]->size,
- info->packages[i]->name,
- info->packages[i]->version,
- info->packages[i]->revision,
- info->packages[i]->requirements_records,
- reqs ? reqs : SPM_MANIFEST_NODATA,
- checksum_sha256 ? checksum_sha256 : SPM_MANIFEST_NODATA);
- fprintf(fp, "%s\n", dptr);
- free(reqs);
- free(archive);
- if (checksum_sha256 != NULL)
- free(checksum_sha256);
- }
- fclose(fp);
- return 0;
-}
-
-/**
- *
- * @param url
- * @param dest
- * @return
- */
-int fetch(const char *url, const char *dest) {
- URL_FILE *handle = NULL;
- FILE *outf = NULL;
- size_t chunk_size = 0xffff;
- size_t nread = 0;
- char *buffer = calloc(chunk_size + 1, sizeof(char));
- if (!buffer) {
- perror("fetch buffer too big");
- return -1;
- }
-
- handle = url_fopen(url, "r");
- if(!handle) {
- fprintf(stderr, "couldn't url_fopen() %s\n", url);
- return 2;
- }
-
- outf = fopen(dest, "wb+");
- if(!outf) {
- perror("couldn't open fread output file\n");
- return 1;
- }
-
- do {
- nread = url_fread(buffer, 1, chunk_size, handle);
- if (handle->http_status >= 400) {
- free(buffer);
- fclose(outf);
- if (exists(dest) == 0) {
- unlink(dest);
- }
-
- long http_status = handle->http_status;
- url_fclose(handle);
- return http_status;
- }
- fwrite(buffer, 1, nread, outf);
- } while (nread);
-
- free(buffer);
- fclose(outf);
- url_fclose(handle);
- return 0;
-}
-
-int manifest_validate(void) {
- size_t line_count;
- int problems;
- char data[BUFSIZ];
- FILE *fp;
-
- if (exists(SPM_GLOBAL.package_manifest) != 0) {
- return -1;
- }
-
- if ((fp = fopen(SPM_GLOBAL.package_manifest, "r")) == NULL) {
- perror(SPM_GLOBAL.package_manifest);
- return -2;
- }
-
- line_count = 0;
- problems = 0;
- while (fgets(data, BUFSIZ, fp) != NULL) {
- int separators;
- if (line_count == 0) {
- if (strncmp(data, SPM_MANIFEST_HEADER, strlen(SPM_MANIFEST_HEADER)) != 0) {
- fprintf(stderr, "Invalid manifest header: %s (expecting '%s')\n", strip(data), SPM_MANIFEST_HEADER);
- problems++;
- line_count++;
- }
- }
- else if ((separators = num_chars(data, SPM_MANIFEST_SEPARATOR)) != SPM_MANIFEST_SEPARATOR_MAX) {
- fprintf(stderr, "Invalid manifest record on line %zu: %s (expecting %d separators, found %d)\n", line_count, strip(data), SPM_MANIFEST_SEPARATOR_MAX, separators);
- problems++;
- }
- line_count++;
- }
- return problems;
-}
-
-/**
- * Read the package manifest stored in the configuration directory
- * @return `Manifest` structure
- */
-Manifest *manifest_read(char *file_or_url) {
- FILE *fp = NULL;
- char *filename = SPM_MANIFEST_FILENAME;
- char *tmpdir = NULL;
- char path[PATH_MAX];
- char *pathptr = path;
- memset(path, '\0', PATH_MAX);
-
- // When file_or_url is NULL we want to use the global manifest
- if (file_or_url == NULL) {
- // TODO: move this out
- strcpy(path, SPM_GLOBAL.package_dir);
- }
- else {
- tmpdir = spm_mkdtemp("spm_manifest_read_XXXXXX", SPM_GLOBAL.repo_target);
- if (exists(tmpdir) != 0) {
- fprintf(stderr, "Failed to create temporary storage directory\n");
- fprintf(SYSERROR);
- return NULL;
- }
-
- snprintf(pathptr, PATH_MAX - 1, "%s%s%s%s%s", tmpdir, DIRSEPS, SPM_GLOBAL.repo_target, DIRSEPS, filename);
- }
-
- const char *target_is;
- if (strstr(file_or_url, SPM_GLOBAL.repo_target) != NULL) {
- target_is = "";
- }
- else {
- target_is = SPM_GLOBAL.repo_target;
- }
-
- char *remote_manifest = join_ex(DIRSEPS, file_or_url, target_is, filename, NULL);
-
- if (exists(pathptr) != 0) {
- // TODO: Move this out
- int fetch_status = fetch(remote_manifest, pathptr);
- if (fetch_status >= 400) {
- fprintf(stderr, "HTTP %d: %s: %s\n", fetch_status, http_response_str(fetch_status), remote_manifest);
- free(remote_manifest);
- return NULL;
- }
- else if (fetch_status == 1 || fetch_status < 0) {
- free(remote_manifest);
- return NULL;
- }
- }
-
- int valid = 0;
- size_t total_records = 0;
- char data[BUFSIZ];
- char *dptr = data;
- memset(dptr, '\0', BUFSIZ);
-
- fp = fopen(pathptr, "r+");
- if (!fp) {
- perror(filename);
- fprintf(SYSERROR);
- return NULL;
- }
-
- while (fgets(dptr, BUFSIZ, fp) != NULL) {
- total_records++;
- }
- total_records--; // header does not count
- rewind(fp);
-
- Manifest *info = (Manifest *)calloc(1, sizeof(Manifest));
- info->packages = (ManifestPackage **)calloc(total_records + 1, sizeof(ManifestPackage *));
-
- // Record manifest's origin
- memset(info->origin, '\0', SPM_PACKAGE_MEMBER_ORIGIN_SIZE);
- if (remote_manifest != NULL) {
- strcpy(info->origin, remote_manifest);
- }
- else {
- strcpy(info->origin, path);
- }
- free(remote_manifest);
-
- // Check validity of the manifest's formatting and field length
- if ((valid = manifest_validate()) != 0) {
- return NULL;
- }
-
- // Begin parsing the manifest
- char separator = SPM_MANIFEST_SEPARATOR;
- size_t i = 0;
-
- // Consume header
- if (fgets(dptr, BUFSIZ, fp) == NULL) {
- // file is probably empty
- return NULL;
- }
-
- info->records = total_records;
- while (fgets(dptr, BUFSIZ, fp) != NULL) {
- dptr = strip(dptr);
- char *garbage;
- char **parts = split(dptr, &separator);
- char *_origin = NULL;
- if (file_or_url != NULL) {
- _origin = strdup(file_or_url);
- }
- else {
- _origin = dirname(path);
- }
-
- info->packages[i] = (ManifestPackage *)calloc(1, sizeof(ManifestPackage));
-
- strncpy(info->packages[i]->origin, _origin, SPM_PACKAGE_MEMBER_ORIGIN_SIZE);
- free(_origin);
-
- strncpy(info->packages[i]->archive, parts[0], SPM_PACKAGE_MEMBER_SIZE);
- info->packages[i]->size = strtoul(parts[1], &garbage, 10);
- strncpy(info->packages[i]->name, parts[2], SPM_PACKAGE_MEMBER_SIZE);
- strncpy(info->packages[i]->version, parts[3], SPM_PACKAGE_MEMBER_SIZE);
- strncpy(info->packages[i]->revision, parts[4], SPM_PACKAGE_MEMBER_SIZE);
- info->packages[i]->requirements_records = (size_t) atoi(parts[5]);
-
- info->packages[i]->requirements = NULL;
- if (strncmp(parts[6], SPM_MANIFEST_NODATA, strlen(SPM_MANIFEST_NODATA)) != 0) {
- info->packages[i]->requirements = split(parts[6], ",");
-
- }
- if (strncmp(parts[7], SPM_MANIFEST_NODATA, strlen(SPM_MANIFEST_NODATA)) != 0) {
- memset(info->packages[i]->checksum_sha256, '\0', SHA256_DIGEST_STRING_LENGTH);
- strcpy(info->packages[i]->checksum_sha256, parts[7]);
-
- }
-
- split_free(parts);
- i++;
- }
-
- if (tmpdir != NULL) {
- rmdirs(tmpdir);
- free(tmpdir);
- }
- fclose(fp);
- return info;
-}
-
-/**
- * Find a package in a `Manifest`
- * @param info `Manifest`
- * @param _package package name
- * @return found=`ManifestPackage`, not found=NULL
- */
-ManifestPackage *manifest_search(Manifest *info, const char *_package) {
- ManifestPackage *match = NULL;
- char package[PATH_MAX];
-
- memset(package, '\0', PATH_MAX);
- strncpy(package, _package, PATH_MAX);
-
- if ((match = find_by_strspec(info, package)) != NULL) {
- return match;
- }
- return NULL;
-}
-
-/**
- * Duplicate a `ManifestPackage`
- * @param manifest
- * @return `ManifestPackage`
- */
-ManifestPackage *manifest_package_copy(ManifestPackage *manifest) {
- if (manifest == NULL) {
- return NULL;
- }
-
- ManifestPackage *result = calloc(1, sizeof(ManifestPackage));
- memcpy(result, manifest, sizeof(ManifestPackage));
-
- if (manifest->requirements_records > 0) {
- result->requirements = (char **)calloc(manifest->requirements_records, sizeof(char *));
- for (size_t i = 0; i < manifest->requirements_records; i++) {
- result->requirements[i] = strdup(manifest->requirements[i]);
- }
- }
-
- return result;
-}
-
-/**
- *
- * @param ManifestList `pManifestList`
- */
-void manifestlist_free(ManifestList *pManifestList) {
- for (size_t i = 0; i < pManifestList->num_inuse; i++) {
- manifest_free(pManifestList->data[i]);
- }
- free(pManifestList->data);
- free(pManifestList);
-}
-
-/**
- * Append a value to the list
- * @param ManifestList `pManifestList`
- * @param str
- */
-void manifestlist_append(ManifestList *pManifestList, char *path) {
- Manifest *manifest = manifest_read(path);
- if (manifest == NULL) {
- fprintf(stderr, "Failed to create manifest in memory\n");
- fprintf(SYSERROR);
- exit(1);
- }
-
- Manifest **tmp = realloc(pManifestList->data, (pManifestList->num_alloc + 1) * sizeof(Manifest *));
- if (tmp == NULL) {
- manifestlist_free(pManifestList);
- perror("failed to append to array");
- exit(1);
- }
- pManifestList->data = tmp;
- pManifestList->data[pManifestList->num_inuse] = manifest;
- pManifestList->num_inuse++;
- pManifestList->num_alloc++;
-}
-
-ManifestPackage *manifestlist_search(ManifestList *pManifestList, const char *_package) {
- ManifestPackage *found[255] = {0,};
- ManifestPackage *result = NULL;
- ssize_t offset = -1;
-
- for (size_t i = 0; i < manifestlist_count(pManifestList); i++) {
- Manifest *item = manifestlist_item(pManifestList, i);
- result = manifest_search(item, _package);
- if (result != NULL) {
- offset++;
- found[offset] = result;
- }
- }
-
- if (offset < 0) {
- return NULL;
- }
- return found[offset];
-}
-
-/**
- * Get the count of values stored in a `pManifestList`
- * @param ManifestList
- * @return
- */
-size_t manifestlist_count(ManifestList *pManifestList) {
- return pManifestList->num_inuse;
-}
-
-/**
- * Set value at index
- * @param ManifestList
- * @param value string
- * @return
- */
-void manifestlist_set(ManifestList *pManifestList, size_t index, Manifest *value) {
- Manifest *item = NULL;
- if (index > manifestlist_count(pManifestList)) {
- return;
- }
- if ((item = manifestlist_item(pManifestList, index)) == NULL) {
- return;
- }
- memcpy(pManifestList->data[index], value, sizeof(Manifest));
-}
-
-/**
- * Retrieve data from a `pManifestList`
- * @param ManifestList
- * @param index
- * @return string
- */
-Manifest *manifestlist_item(ManifestList *pManifestList, size_t index) {
- if (index > manifestlist_count(pManifestList)) {
- return NULL;
- }
- return pManifestList->data[index];
-}
-
-/**
- * Initialize an empty `pManifestList`
- * @return `pManifestList`
- */
-ManifestList *manifestlist_init() {
- ManifestList *pManifestList = calloc(1, sizeof(ManifestList));
- if (pManifestList == NULL) {
- perror("failed to allocate array");
- exit(errno);
- }
- pManifestList->num_inuse = 0;
- pManifestList->num_alloc = 1;
- pManifestList->data = calloc(pManifestList->num_alloc, sizeof(char *));
- return pManifestList;
-}
-
-
diff --git a/src/metadata.c b/src/metadata.c
deleted file mode 100644
index 6861740..0000000
--- a/src/metadata.c
+++ /dev/null
@@ -1,166 +0,0 @@
-#include "spm.h"
-
-extern const char *METADATA_FILES[];
-
-static int verify_filelist(size_t lineno, char **a) {
- if (lineno == 0) {
- if (strncmp((*a), SPM_PACKAGE_HEADER_FILELIST, strlen(SPM_PACKAGE_HEADER_FILELIST)) != 0) {
- fprintf(stderr, "invalid or missing header: line %zu: %s (expected: '%s')\n",
- lineno, (*a), SPM_PACKAGE_HEADER_FILELIST);
- return 1;
- }
- }
- return -1;
-}
-
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-static int verify_depends(size_t lineno, char **a) {
- return -1;
-}
-
-static int verify_descriptor(size_t lineno, char **a) {
- if (lineno == 0) {
- if (strncmp((*a), SPM_PACKAGE_HEADER_DESCRIPTOR, strlen(SPM_PACKAGE_HEADER_DESCRIPTOR)) != 0) {
- fprintf(stderr, "invalid or missing header: line %zu: %s (expected: '%s')\n",
- lineno, (*a), SPM_PACKAGE_HEADER_DESCRIPTOR);
- return 1;
- }
- }
- return -1;
-}
-
-static int verify_prefix(size_t lineno, char **a) {
- size_t parity = lineno % 2;
- if (parity == 0 && *(*a) == '#') {
- return 0;
- }
- else {
- return 1;
- }
-}
-
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-static int verify_no_op(size_t lineno, char **a) {
- return -1;
-}
-
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-static int reader_metadata(size_t lineno, char **line) {
- (*line) = strip((*line));
- if (isempty((*line))) {
- return 1; // indicate "continue"
- }
- return 0; // indicate "ok"
-}
-
-/**
- * Detect the type of metadata based on file name and execute the appropriate function against each line
- * in the file. Verification can be disabled by passing a non-zero value as the second argument.
- * @param filename
- * @param no_verify SPM_METADATA_VERIFY or SPM_METADATA_NO_VERIFY
- * @return array of strings (line endings removed)
- */
-char **spm_metadata_read(const char *_filename, int no_verify) {
- char *filename = strdup(_filename);
- char **data = NULL;
- char **result = NULL;
- size_t start = 0;
- ReaderFn *func_verify;
-
- // Guard
- if (file_is_metadata(filename) == 0) {
- free(filename);
- return NULL;
- }
-
- // Setup function pointer and data starting offsets (if any)
- if (strcmp(basename(filename), SPM_META_FILELIST) == 0) {
- func_verify = verify_filelist;
- start = 1;
- } else if (strcmp(basename(filename), SPM_META_DESCRIPTOR) == 0) {
- func_verify = verify_descriptor;
- start = 1;
- } else if (strcmp(basename(filename), SPM_META_DEPENDS) == 0) {
- func_verify = verify_depends;
- } else if (strcmp(basename(filename), SPM_META_PREFIX_BIN) == 0) {
- func_verify = verify_prefix;
- } else if (strcmp(basename(filename), SPM_META_PREFIX_TEXT) == 0) {
- func_verify = verify_prefix;
- } else {
- func_verify = verify_no_op;
- }
-
- // Get file contents
- data = file_readlines(filename, 0, 0, reader_metadata);
-
- // Strip newlines and whitespace
- for (size_t i = 0; data[i] != NULL; i++) {
- data[i] = strip(data[i]);
- }
-
- // Perform verification
- if (no_verify == 0) {
- for (size_t i = 0; data[i] != NULL; i++) {
- int status = func_verify(i, &data[i]);
- if (status > 0) {
- fprintf(stderr, "%s: file verification failed\n", filename);
- free(filename);
- return NULL;
- } else if (status < 0) {
- // NOT AN ERROR
- // a negative value indicates the verification function has processed enough information
- // so we can gracefully
- break;
- }
- }
- }
-
- // If there was a header, duplicate the array from the start of the data
- // Otherwise return the array
- if (start > 0) {
- result = strdup_array(&data[start]);
- for (size_t i = 0; data[i] != NULL; i++) {
- free(data[i]);
- }
- free(data);
- } else {
- result = data;
- }
-
- free(filename);
- return result;
-}
-
-/**
- * SPM packages contain metadata files that are not useful post-install and would amount to a lot of clutter.
- * This function removes these data files from a directory tree
- * @param _path
- * @return success=0, error=-1
- */
-int spm_metadata_remove(const char *_path) {
- if (exists(_path) != 0) {
- perror(_path);
- fprintf(SYSERROR);
- return -1;
- }
-
- for (int i = 0; METADATA_FILES[i] != NULL; i++) {
- char path[PATH_MAX];
- sprintf(path, "%s%c%s", _path, DIRSEP, METADATA_FILES[i]);
- if (exists(path) != 0) {
- continue;
- }
- if (unlink(path) < 0) {
- perror(path);
- fprintf(SYSERROR);
- return -1;
- }
- }
- return 0;
-}
-
-ConfigItem **spm_descriptor_read(const char *filename) {
- ConfigItem **result = config_read(filename);
- return result;
-}
-
diff --git a/src/mime.c b/src/mime.c
deleted file mode 100644
index 9e4bdce..0000000
--- a/src/mime.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/**
- * @file mime.c
- */
-#include "spm.h"
-#include <fnmatch.h>
-
-/**
- * Execute OS `file` command
- * @param _filename path to file
- * @return Process structure
- */
-Process *file_command(const char *_filename) {
- char *filename = strdup(_filename);
- Process *proc_info = NULL;
- char sh_cmd[PATH_MAX];
- sh_cmd[0] = '\0';
-#ifdef __APPLE__
- const char *fmt_cmd = "file -I \"%s\" 2>&1";
-#else // GNU
- const char *fmt_cmd = "file -i \"%s\" 2>&1";
-#endif
- const char *fail_pattern = ": cannot open";
-
- strchrdel(filename, SHELL_INVALID);
- sprintf(sh_cmd, fmt_cmd, filename);
- shell(&proc_info, SHELL_OUTPUT, sh_cmd);
-
- // POSIXly ridiculous. Return non-zero when a file can't be found, or isn't accessible
- if (strstr(proc_info->output, fail_pattern) != NULL) {
- proc_info->returncode = 1;
- }
- free(filename);
- return proc_info;
-}
-
-/**
- * Execute the `file` command, parse its output, and return the data in a `Mime` structure
- * @param filename path to file
- * @return Mime structure
- */
-Mime *file_mimetype(const char *filename) {
- char **output = NULL;
- char **parts = NULL;
- Mime *type = NULL;
- Process *proc = file_command(filename);
-
- if (proc->returncode != 0) {
- fprintf(stderr, "%s\n", proc->output);
- fprintf(stderr, "file command returned: %d\n", proc->returncode);
- fprintf(SYSERROR);
- shell_free(proc);
- return NULL;
- }
- output = split(proc->output, ":");
- if (!output || output[1] == NULL) {
- shell_free(proc);
- return NULL;
- }
- parts = split(output[1], ";");
- if (!parts || !parts[0] || !parts[1]) {
- shell_free(proc);
- return NULL;
- }
-
- char *what = strdup(parts[0]);
- what = lstrip(what);
-
- char *charset = strdup(strchr(parts[1], '=') + 1);
- charset = lstrip(charset);
- charset[strlen(charset) - 1] = '\0';
-
- char *origin = realpath(filename, NULL);
-
- type = (Mime *)calloc(1, sizeof(Mime));
- type->origin = origin;
- type->type = what;
- type->charset = charset;
-
- split_free(output);
- split_free(parts);
- shell_free(proc);
- return type;
-}
-
-/**
- * Free a `Mime` structure
- * @param m
- */
-void mime_free(Mime *m) {
- if (m != NULL) {
- free(m->origin);
- free(m->type);
- free(m->charset);
- free(m);
- }
-}
-
-/**
- * Determine if a file is a text file
- * @param filename
- * @return yes=1, no=0
- */
-int file_is_text(const char *filename) {
- int result = 0;
- char *path = normpath(filename);
- Mime *type = file_mimetype(path);
- if (type == NULL) {
- fprintf(stderr, "type detection failed: %s\n", filename);
- return -1;
- }
- if (startswith(type->type, "text/")) {
- result = 1;
- }
- free(path);
- mime_free(type);
- return result;
-}
-
-/**
- * Determine if a file is a binary data file
- * @param filename
- * @return yes=1, no=0
- */
-int file_is_binary(const char *filename) {
- int result = 0;
- char *path = normpath(filename);
- Mime *type = file_mimetype(path);
- if (type == NULL) {
- fprintf(stderr, "type detection failed: %s\n", filename);
- return -1;
- }
- if (startswith(type->type, "application/") && strcmp(type->charset, "binary") == 0) {
- result = 1;
- }
- free(path);
- mime_free(type);
- return result;
-}
-
-int file_is_binexec(const char *filename) {
- int result = 0;
- char *path = normpath(filename);
- Mime *type = file_mimetype(path);
- if (type == NULL) {
- fprintf(stderr, "type detection failed: %s\n", filename);
- return -1;
- }
- // file-5.38: changed mime name associated with executables
- // TODO: implement compatibility function to return the correct search pattern
- if (fnmatch("application/x-[pic|pie|ex|sh]*", type->type, FNM_PATHNAME) != FNM_NOMATCH && strcmp(type->charset, "binary") == 0) {
- result = 1;
- }
- free(path);
- mime_free(type);
- return result;
-}
diff --git a/src/mirrors.c b/src/mirrors.c
deleted file mode 100644
index cad3f6b..0000000
--- a/src/mirrors.c
+++ /dev/null
@@ -1,180 +0,0 @@
-#include "spm.h"
-#include "url.h"
-
-char **file_readlines(const char *filename, size_t start, size_t limit, ReaderFn *readerFn) {
- FILE *fp = NULL;
- char **result = NULL;
- char *buffer = NULL;
- size_t lines = 0;
-
- if ((fp = fopen(filename, "r")) == NULL) {
- perror(filename);
- fprintf(SYSERROR);
- return NULL;
- }
-
- // Allocate buffer
- if ((buffer = calloc(BUFSIZ + 1, sizeof(char))) == NULL) {
- perror("line buffer");
- fprintf(SYSERROR);
- fclose(fp);
- return NULL;
- }
-
- // count number the of lines in the file
- while ((fgets(buffer, BUFSIZ, fp)) != NULL) {
- lines++;
- }
-
- if (!lines) {
- free(buffer);
- fclose(fp);
- return NULL;
- }
-
- rewind(fp);
-
- // Handle invalid start offset
- if (start > lines) {
- start = 0;
- }
-
- // Adjust line count when start offset is non-zero
- if (start != 0 && start < lines) {
- lines -= start;
- }
-
-
- // Handle minimum and maximum limits
- if (limit == 0 || limit > lines) {
- limit = lines;
- }
-
- // Populate results array
- result = calloc(lines + 1, sizeof(char *));
- for (size_t i = start; i < limit; i++) {
- if (i < start) {
- continue;
- }
-
- if (fgets(buffer, BUFSIZ, fp) == NULL) {
- break;
- }
-
- if (readerFn != NULL) {
- int status = readerFn(i - start, &buffer);
- // A status greater than zero indicates we should ignore this line entirely and "continue"
- // A status less than zero indicates we should "break"
- // A zero status proceeds normally
- if (status > 0) {
- i--;
- continue;
- } else if (status < 0) {
- break;
- }
- }
- result[i - start] = strdup(buffer);
- }
-
- free(buffer);
- fclose(fp);
- return result;
-}
-
-/**
- *
- * @param filename
- * @return
- */
-char **mirror_list(const char *filename) {
- char **mirrors = NULL;
- char **result = NULL;
- size_t count;
-
- // The configuration file isn't critical so if it isn't available, no big deal
- if (exists(filename) != 0) {
- return NULL;
- }
-
- mirrors = file_readlines(filename, 0, 0, NULL);
- if (mirrors == NULL) {
- return NULL;
- }
-
- for (count = 0; mirrors[count] != NULL; count++);
- if (!count) {
- return NULL;
- }
-
- result = calloc(count + 1, sizeof(char **));
- for (size_t i = 0; mirrors[i] != NULL; i++) {
- if (startswith(mirrors[i], "#") || isempty(mirrors[i])) {
- continue;
- }
- result[i] = join((char *[]) {mirrors[i], SPM_GLOBAL.repo_target, NULL}, DIRSEPS);
- free(mirrors[i]);
- }
- free(mirrors);
- return result;
-}
-
-void mirror_list_free(char **m) {
- if (m == NULL) {
- return;
- }
- for (size_t i = 0; m[i] != NULL; i++) {
- free(m[i]);
- }
- free(m);
-}
-
-void mirror_clone(Manifest *info, char *_dest) {
- char *dest = NULL;
- if (endswith(_dest, SPM_GLOBAL.repo_target) != 0) {
- dest = strdup(_dest);
- }
- else {
- dest = join((char *[]) {_dest, SPM_GLOBAL.repo_target, NULL}, DIRSEPS);
- }
-
- if (exists(dest) != 0 && mkdirs(dest, 0755) != 0) {
- perror("Unable to create mirror directory");
- fprintf(SYSERROR);
- exit(1);
- }
-
- printf("Remote: %s\n", info->origin);
- printf("Local: %s\n", dest);
-
- for (size_t i = 0; i < info->records; i++) {
- long response = 0;
- char *archive = join((char *[]) {info->packages[i]->origin, SPM_GLOBAL.repo_target, info->packages[i]->archive, NULL}, DIRSEPS);
- char *path = join((char *[]) {dest, info->packages[i]->archive, NULL}, DIRSEPS);
- if (exists(path) == 0) {
- char *checksum = sha256sum(path);
- if (strcmp(checksum, info->packages[i]->checksum_sha256) == 0) {
- printf("Skipped: %s\n", archive);
- free(checksum);
- free(archive);
- free(path);
- continue;
- }
- }
- printf("Fetch: %s\n", archive);
- if ((response = fetch(archive, path)) >= 400) {
- fprintf(stderr, "WARNING: HTTP(%ld, %s): %s\n", response, http_response_str(response), archive);
- }
- free(archive);
- free(path);
- }
-
- // Now fetch a copy of the physical manifest
- char *datafile = join((char *[]) {dest, basename(info->origin), NULL}, DIRSEPS);
- long response = 0;
- if ((response = fetch(info->origin, datafile) >= 400)) {
- fprintf(stderr, "WARNING: HTTP(%ld, %s): %s\n", response, http_response_str(response), info->origin);
- }
- free(dest);
- free(datafile);
- printf("done!\n");
-} \ No newline at end of file
diff --git a/src/purge.c b/src/purge.c
deleted file mode 100644
index 997df51..0000000
--- a/src/purge.c
+++ /dev/null
@@ -1,93 +0,0 @@
-#include "spm.h"
-
-/**
- * Remove a package
- * @param fs `SPM_Hierarchy`
- * @param _package_name
- * @return
- */
-int spm_purge(SPM_Hierarchy *fs, const char *_package_name) {
- size_t files_count = 0;
- char **_files_orig = NULL;
- char *path = NULL;
- char *package_name = strdup(_package_name);
- char *package_topdir = join((char *[]) {fs->dbrecdir, package_name, NULL}, DIRSEPS);
- char *descriptor = join((char *[]) {package_topdir, SPM_META_DESCRIPTOR, NULL}, DIRSEPS);
- char *filelist = join((char *[]) {package_topdir, SPM_META_FILELIST, NULL}, DIRSEPS);
-
- if (spm_check_installed(fs, package_name) == 0) {
- // package is not installed in this root
- free(package_name);
- free(package_topdir);
- free(descriptor);
- free(filelist);
- return 1;
- }
-
- ConfigItem **desc = spm_descriptor_read(descriptor);
- char *name = config_get(desc, "name")->value;
- char *version = config_get(desc, "version")->value;
- char *revision = config_get(desc, "revision")->value;
-
- printf("Removing package: %s-%s-%s\n", name, version, revision);
- _files_orig = spm_metadata_read(filelist, SPM_METADATA_VERIFY);
- for (size_t i = 0; _files_orig[i] != NULL; i++) {
- files_count++;
- }
-
- for (size_t i = 0; _files_orig[i] != NULL; i++) {
- path = calloc(PATH_MAX, sizeof(char));
- path = join((char *[]) {fs->rootdir, _files_orig[i], NULL}, DIRSEPS);
- if (SPM_GLOBAL.verbose) {
- printf(" -> %s\n", path);
- }
- if (exists(path) != 0) {
- printf("%s does not exist\n", path);
- } else {
- remove(path);
- }
- free(path);
- }
- rmdirs(package_topdir);
-
- free(package_name);
- free(package_topdir);
- free(descriptor);
- free(filelist);
- config_free(desc);
- return 0;
-}
-
-/**
- * Remove packages
- * @param fs `SPM_Hierarchy`
- * @param packages `StrList`
- * @return
- */
-int spm_do_purge(SPM_Hierarchy *fs, StrList *packages) {
- int status_remove = 0;
-
- printf("Removing package(s):\n");
- for (size_t i = 0; i < strlist_count(packages); i++) {
- char *package = strlist_item(packages, i);
- if (spm_check_installed(fs, package) == 0) {
- printf("%s is not installed\n", package);
- return -1;
- }
- printf("-> %s\n", package);
- }
-
- if (SPM_GLOBAL.prompt_user) {
- if (spm_prompt_user("Proceed with removal?", 1) == 0) {
- return -2;
- }
- }
-
- for (size_t i = 0; i < strlist_count(packages); i++) {
- if ((status_remove = spm_purge(fs, strlist_item(packages, i))) != 0) {
- printf("Failed");
- break;
- }
- }
- return status_remove;
-} \ No newline at end of file
diff --git a/src/relocation.c b/src/relocation.c
deleted file mode 100644
index f22a25d..0000000
--- a/src/relocation.c
+++ /dev/null
@@ -1,440 +0,0 @@
-/**
- * @file relocation.c
- */
-#include "spm.h"
-
-const char *METADATA_FILES[] = {
- SPM_META_DEPENDS,
- SPM_META_PREFIX_BIN,
- SPM_META_PREFIX_TEXT,
- SPM_META_DESCRIPTOR,
- SPM_META_FILELIST,
- NULL,
-};
-
-/**
- * Replace all occurrences of `spattern` with `sreplacement` in `data`
- *
- * ~~~{.c}
- * char *str = (char *)calloc(100, sizeof(char));
- * strcpy(str, "This are a test.");
- * replace_text(str, "are", "is");
- * // str is: "This is a test."
- * free(str);
- * ~~~
- *
- * @param data string to modify
- * @param spattern string value to replace
- * @param sreplacement replacement string value
- * @return success=0, error=-1
- */
-int replace_text(char *data, const char *spattern, const char *sreplacement) {
- if (data == NULL || spattern == NULL || sreplacement == NULL) {
- return -1;
- }
-
- char *tmp = data;
- size_t data_len = strlen(data);
- size_t spattern_len = strlen(spattern);
- size_t sreplacement_len = strlen(sreplacement);
-
- if (sreplacement_len > spattern_len) {
- fprintf(stderr, "replacement string too long: %zu > %zu\n '%s'\n '%s'\n", sreplacement_len, spattern_len, sreplacement, spattern);
- return -1;
- }
-
- while (*tmp != '\0') {
- if (strncmp(tmp, spattern, spattern_len) == 0) {
- if (sreplacement_len == 1) {
- *tmp = *sreplacement;
- } else {
- memmove(tmp, sreplacement, sreplacement_len);
- memmove(tmp + sreplacement_len, tmp + spattern_len, data_len - spattern_len);
- memset(tmp + sreplacement_len + (data_len - spattern_len), '\0', 1);
- }
- }
- tmp++;
- }
- return 0;
-}
-
-/**
- * Replace all occurrences of `oldstr` in file `path` with `newstr`
- * @param filename file to modify
- * @param oldstr string to replace
- * @param newstr replacement string
- * @return success=0, failure=-1, or value of `ferror()`
- */
-int file_replace_text(char *filename, const char *spattern, const char *sreplacement) {
- char data[BUFSIZ];
- char tempfile[PATH_MAX];
- FILE *fp = NULL;
- if ((fp = fopen(filename, "r")) == NULL) {
- perror(filename);
- return -1;
- }
-
- sprintf(tempfile, "%s.spmfrt", filename);
- FILE *tfp = NULL;
- if ((tfp = fopen(tempfile, "w+")) == NULL) {
- fclose(fp);
- perror(tempfile);
- return -1;
- }
-
- // Zero the data buffer
- memset(data, '\0', BUFSIZ);
- while(fgets(data, BUFSIZ, fp) != NULL) {
- replace_text(data, spattern, sreplacement);
- fprintf(tfp, "%s", data);
- }
- fclose(fp);
- rewind(tfp);
-
- // Truncate the original file
- if ((fp = fopen(filename, "w+")) == NULL) {
- perror(filename);
- return -1;
- }
- // Zero the data buffer once more
- memset(data, '\0', BUFSIZ);
- // Dump the contents of the temporary file into the original file
- while(fgets(data, BUFSIZ, tfp) != NULL) {
- fprintf(fp, "%s", data);
- }
- fclose(fp);
- fclose(tfp);
-
- // Remove temporary file
- unlink(tempfile);
- return 0;
-}
-
-/**
- * Free memory allocated by `prefixes_read` function
- * @param entry array of RelocationEntry
- */
-void prefixes_free(RelocationEntry **entry) {
- if (!entry) {
- return;
- }
- for (int i = 0; entry[i] != NULL; i++) {
- if (entry[i]->prefix) free(entry[i]->prefix);
- if (entry[i]->path) free(entry[i]->path);
- if (entry[i]) free(entry[i]);
- }
- free(entry);
-}
-
-/**
- * Parse a prefix file
- *
- * The file format is as follows:
- *
- * ~~~
- * #prefix
- * path
- * #prefix
- * path
- * #...N
- * ...N
- * ~~~
- * @param filename path to prefix manifest
- * @return success=array of RelocationEntry, failure=NULL
- */
-RelocationEntry **prefixes_read(const char *filename) {
- size_t record_count = 0;
- size_t parity = 0;
- FILE *fp = fopen(filename, "r");
- if (!fp) {
- fprintf(SYSERROR);
- return NULL;
- }
- RelocationEntry **entry = NULL;
- char line[BUFSIZ];
- char *lptr = line;
- memset(lptr, '\0', BUFSIZ);
-
- while (fgets(lptr, BUFSIZ, fp) != NULL) {
- if (isempty(lptr)) {
- continue;
- }
- record_count++;
- }
- rewind(fp);
-
- // Initialize the relocation entry array
- if (record_count == 0) {
- return NULL;
- }
-
- parity = record_count % 2;
- if (parity != 0) {
- fprintf(stderr, "%s: records are not divisible by 2 (got: %zu %% 2 = %zu)\n", filename, record_count, parity);
- return NULL;
- }
- record_count /= 2;
-
- entry = (RelocationEntry **)calloc(record_count + 1, sizeof(RelocationEntry *));
- if (!entry) {
- return NULL;
- }
- for (size_t i = 0; i < record_count; i++) {
- entry[i] = (RelocationEntry *) calloc(1, sizeof(RelocationEntry));
- if (!entry[i]) {
- return NULL;
- }
- }
-
- int do_prefix = 0;
- int do_path = 0;
- size_t i = 0;
- while (fgets(lptr, BUFSIZ, fp) != NULL) {
- if (isempty(lptr)) {
- continue;
- }
- if (startswith(lptr, "#")) {
- do_prefix = 1;
- }
- else {
- do_path = 1;
- }
-
- // Allocate a relocation record
- if (!entry[i]) {
- fclose(fp);
- return NULL;
- }
-
-
- if (do_prefix) {
- // Populate prefix data (a prefix starts with a #)
- entry[i]->prefix = (char *) calloc(strlen(lptr) + 1, sizeof(char));
- if (!entry[i]->prefix) {
- fclose(fp);
- return NULL;
- }
- strncpy(entry[i]->prefix, lptr, strlen(lptr));
- // Remove prefix delimiter and whitespace
- strchrdel(entry[i]->prefix, "#");
- entry[i]->prefix = strip(entry[i]->prefix);
- do_prefix = 0;
- continue;
- }
-
- else if (do_path) {
- // Populate path data
- entry[i]->path = (char *) calloc(strlen(lptr) + 1, sizeof(char));
- if (!entry[i]->path) {
- fclose(fp);
- return NULL;
- }
- strncpy(entry[i]->path, lptr, strlen(lptr));
- entry[i]->path = strip(entry[i]->path);
- do_path = 0;
- }
- i++;
- }
- fclose(fp);
- return entry;
-}
-
-/**
- * Determine if `filename` is a SPM metadata file
- *
- * Example:
- *
- * ~~~{.c}
- * #include "spm.h"
- *
- * int main() {
- * if (file_is_metadata(".SPM_DEPENDS")) {
- * // file is metadata
- * } else {
- * // file is not metadata
- * }
- * }
- * ~~~
- *
- * @param filename
- * @return 0=no, 1=yes
- */
-int file_is_metadata(const char *filename) {
- for (size_t i = 0; METADATA_FILES[i] != NULL; i++) {
- if (strstr(filename, METADATA_FILES[i]) != NULL) {
- return 1;
- }
- }
- return 0;
-}
-
-/**
- * Scan `tree` for files containing `prefix`. Matches are recorded in `output_file` with the following format:
- *
- * ~~~
- * #prefix
- * path
- * #prefix
- * path
- * #...N
- * ...N
- * ~~~
- *
- * Example:
- * ~~~{.c}
- * char **prefixes = {"/usr", "/var", NULL};
- * prefixes_write("binary.manifest", PREFIX_WRITE_BIN, prefixes, "/usr/bin");
- * prefixes_write("text.manifest", PREFIX_WRITE_TEXT, prefixes, "/etc");
- * ~~~
- *
- * @param output_file file path to create
- * @param mode `PREFIX_WRITE_BIN`, `PREFIX_WRITE_TEXT`
- * @param prefix array of prefix strings
- * @param tree directory to scan
- * @return success=0, failure=1, error=-1
- */
-int prefixes_write(const char *output_file, int mode, char **prefix, const char *tree) {
- FILE *fp = fopen(output_file, "w+");
- if (!fp) {
- perror(output_file);
- fprintf(SYSERROR);
- return -1;
- }
-
- char *cwd = getcwd(NULL, PATH_MAX);
- chdir(tree);
- {
- FSTree *fsdata = fstree(".", NULL, SPM_FSTREE_FLT_RELATIVE);
- if (!fsdata) {
- fclose(fp);
- fprintf(SYSERROR);
- return -1;
- }
- for (size_t i = 0; i < fsdata->files_length; i++) {
- if (file_is_metadata(fsdata->files[i])) {
- continue;
- }
- for (int p = 0; prefix[p] != NULL; p++) {
- if (find_in_file(fsdata->files[i], prefix[p]) == 0) {
- int proceed = 0;
- if (mode == PREFIX_WRITE_BIN) {
- proceed = file_is_binary(fsdata->files[i]);
- } else if (mode == PREFIX_WRITE_TEXT) {
- proceed = file_is_text(fsdata->files[i]);
- }
-
- // file_is_* functions return NULL when they encounter anything but a regular file
- if (!proceed) {
- continue;
- }
- // Record in file
- fprintf(fp, "#%s\n%s\n", prefix[p], fsdata->files[i]);
- }
- }
- }
- } chdir(cwd);
- free(cwd);
- fclose(fp);
- return 0;
-}
-
-/**
- * Wrapper for `reloc` program. Replace text in binary data.
- * @param _filename
- * @param _oldstr
- * @param _newstr
- * @return
- */
-int relocate(const char *_filename, const char *_oldstr, const char *_newstr) {
- int returncode;
- Process *proc = NULL;
- char *oldstr = strdup(_oldstr);
- char *newstr = strdup(_newstr);
- char *filename = strdup(_filename);
- char cmd[PATH_MAX];
-
- // sanitize command
- strchrdel(oldstr, SHELL_INVALID);
- strchrdel(newstr, SHELL_INVALID);
- strchrdel(filename, SHELL_INVALID);
-
- memset(cmd, '\0', sizeof(cmd));
- sprintf(cmd, "reloc \"%s\" \"%s\" \"%s\" \"%s\" 2>&1", oldstr, newstr, filename, filename);
-
- if (SPM_GLOBAL.verbose > 1) {
- printf(" EXEC : %s\n", cmd);
- }
-
- shell(&proc, SHELL_OUTPUT, cmd);
- if (!proc) {
- free(oldstr);
- free(newstr);
- free(filename);
- return -1;
- }
-
- returncode = proc->returncode;
- if (returncode != 0 && proc->output) {
- fprintf(stderr, "%s\n", proc->output);
- }
-
- shell_free(proc);
- free(oldstr);
- free(newstr);
- free(filename);
- return returncode;
-}
-
-/**
- * Parse package metadata and set `baseroot` binaries/text to point to `destroot`.
- * `baseroot` should be a temporary directory because its contents are modified
- *
- * @param destroot
- * @param baseroot
- */
-void relocate_root(const char *destroot, const char *baseroot) {
- RelocationEntry **b_record = NULL;
- RelocationEntry **t_record = NULL;
- char cwd[PATH_MAX];
-
- getcwd(cwd, sizeof(cwd));
- chdir(baseroot);
- {
- FSTree *libs = rpath_libraries_available(".");
- // Rewrite binary prefixes
- b_record = prefixes_read(SPM_META_PREFIX_BIN);
- if (b_record) {
- for (int i = 0; b_record[i] != NULL; i++) {
- if (file_is_binexec(b_record[i]->path)) {
- if (SPM_GLOBAL.verbose) {
- printf("Relocate RPATH: %s\n", b_record[i]->path);
- }
- rpath_autoset(b_record[i]->path, libs);
- }
- if (SPM_GLOBAL.verbose) {
- printf("Relocate DATA : %s\n", b_record[i]->path);
- }
- relocate(b_record[i]->path, b_record[i]->prefix, destroot);
- }
- }
-
- // Rewrite text prefixes
- t_record = prefixes_read(SPM_META_PREFIX_TEXT);
- if (t_record) {
- for (int i = 0; t_record[i] != NULL; i++) {
- if (SPM_GLOBAL.verbose) {
- printf("Relocate TEXT : %s\n", t_record[i]->path);
- }
- if (SPM_GLOBAL.verbose > 1) {
- printf(" EDIT : '%s' -> '%s'\n", t_record[i]->prefix, destroot);
- }
- file_replace_text(t_record[i]->path, t_record[i]->prefix, destroot);
- }
- }
-
- prefixes_free(b_record);
- prefixes_free(t_record);
- }
- chdir(cwd);
-}
-
diff --git a/src/resolve.c b/src/resolve.c
deleted file mode 100644
index 1a4448f..0000000
--- a/src/resolve.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Dependency resolution functions
- * @file resolve.c
- */
-#include "spm.h"
-
-static ManifestPackage *requirements[SPM_REQUIREMENT_MAX] = {0, };
-
-void resolve_free() {
- for (size_t i = 0; i < SPM_REQUIREMENT_MAX; i++) {
- if (requirements[i] != NULL)
- manifest_package_free(requirements[i]);
- }
-}
-
-/**
- * Scan global `requirements` array for `archive`
- * @param archive
- * @return 0 = not found, 1 = found
- */
-int resolve_has_dependency(const char *archive) {
- for (size_t i = 0; requirements[i] != NULL && i < SPM_REQUIREMENT_MAX; i++) {
- if (strcmp(requirements[i]->archive, archive) == 0) {
- return 1;
- }
- }
- return 0;
-}
-
-/**
- * Recursively scan a package for its dependencies
- * @param manifests `ManifestList` struct
- * @param spec Package name (accepts version specifiers)
- * @return success = array of `ManifestPackage`, not found = NULL
- */
-ManifestPackage **resolve_dependencies(ManifestList *manifests, const char *spec) {
- static size_t req_i = 0;
- ManifestPackage *package = manifestlist_search(manifests, spec);
- ManifestPackage *requirement = NULL;
-
- if (package == NULL) {
- return requirements;
- }
-
- for (size_t i = 0; i < package->requirements_records && i < SPM_REQUIREMENT_MAX; i++) {
- requirement = manifestlist_search(manifests, package->requirements[i]);
- if (requirement == NULL) {
- fprintf(stderr, "ERROR: unable to resolve package via manifestlist_search(): '%s'\n", package->requirements[i]);
- exit(1);
- }
- if (resolve_has_dependency(requirement->archive)) {
- free(requirement);
- } else {
- resolve_dependencies(manifests, requirement->archive);
- requirements[req_i] = requirement;
- }
- }
-
- if (!resolve_has_dependency(package->archive)) {
- requirements[req_i] = package;
- req_i++;
- }
-
- return requirements;
-} \ No newline at end of file
diff --git a/src/rpath.c b/src/rpath.c
deleted file mode 100644
index 4d4d801..0000000
--- a/src/rpath.c
+++ /dev/null
@@ -1,303 +0,0 @@
-/**
- * @file rpath.c
- */
-#include "spm.h"
-
-/**
- * Wrapper function to execute `patchelf` with arguments
- * @param _filename Path of file to modify
- * @param _args Arguments to pass to `patchelf`
- * @return success=Process struct, failure=NULL
- */
-Process *patchelf(const char *_filename, const char *_args) {
- char *filename = strdup(_filename);
- char *args = strdup(_args);
- Process *proc_info = NULL;
- char sh_cmd[PATH_MAX];
- sh_cmd[0] = '\0';
-
- strchrdel(args, SHELL_INVALID);
- strchrdel(filename, SHELL_INVALID);
- sprintf(sh_cmd, "patchelf %s %s 2>&1", args, filename);
-
- if (SPM_GLOBAL.verbose > 1) {
- printf(" EXEC : %s\n", sh_cmd);
- }
-
- shell(&proc_info, SHELL_OUTPUT, sh_cmd);
-
- free(filename);
- free(args);
- return proc_info;
-}
-
-/**
- * Determine whether a RPATH or RUNPATH is present in file
- *
- * TODO: Replace with OS-native solution(s)
- *
- * @param _filename path to executable or library
- * @return -1=OS error, 0=has rpath, 1=not found
- */
-int has_rpath(const char *_filename) {
- int result = 1; // default: not found
-
- char *filename = strdup(_filename);
- if (!filename) {
- return -1;
- }
-
- // sanitize input path
- strchrdel(filename, SHELL_INVALID);
-
- Process *pe = patchelf(filename, "--print-rpath");
- strip(pe->output);
- if (!isempty(pe->output)) {
- result = 0;
- }
- else {
- // something went wrong with patchelf other than
- // what we're looking for
- result = -1;
- }
-
- free(filename);
- shell_free(pe);
- return result;
-}
-
-/**
- * Returns a RPATH or RUNPATH if one is defined in `_filename`
- *
- * TODO: Replace with OS-native solution(s)
- *
- * @param _filename path to executable or library
- * @return RPATH string, NULL=error (caller is responsible for freeing memory)
- */
-char *rpath_get(const char *_filename) {
- if ((has_rpath(_filename)) != 0) {
- return strdup("");
- }
- char *filename = strdup(_filename);
- if (!filename) {
- return NULL;
- }
- char *path = strdup(filename);
- if (!path) {
- free(filename);
- return NULL;
- }
-
- char *rpath = NULL;
-
- // sanitize input path
- strchrdel(path, SHELL_INVALID);
-
- Process *pe = patchelf(filename, "--print-rpath");
- if (pe->returncode != 0) {
- fprintf(stderr, "patchelf error: %s %s\n", path, strip(pe->output));
- return NULL;
- }
-
- rpath = (char *)calloc(strlen(pe->output) + 1, sizeof(char));
- if (!rpath) {
- free(filename);
- free(path);
- shell_free(pe);
- return NULL;
- }
-
- strncpy(rpath, pe->output, strlen(pe->output));
- strip(rpath);
-
- free(filename);
- free(path);
- shell_free(pe);
- return rpath;
-}
-
-/**
- * Generate a RPATH in the form of:
- *
- * `$ORIGIN/relative/path/to/lib/from/_filename/path`
- *
- * @param _filename
- * @return
- */
-char *rpath_generate(const char *_filename, FSTree *tree) {
- char *filename = realpath(_filename, NULL);
- if (!filename) {
- return NULL;
- }
-
- char *result = rpath_autodetect(filename, tree);
- if (!result) {
- free(filename);
- return NULL;
- }
-
- free(filename);
- return result;
-}
-
-/**
- * Set the RPATH of an executable
- * @param filename
- * @param rpath
- * @return
- */
-int rpath_set(const char *filename, const char *rpath) {
- int returncode = 0;
- char args[PATH_MAX];
-
- memset(args, '\0', PATH_MAX);
- sprintf(args, "--set-rpath '%s'", rpath); // note: rpath requires single-quotes
- Process *pe = patchelf(filename, args);
- if (pe != NULL) {
- returncode = pe->returncode;
- }
- shell_free(pe);
- return returncode;
-}
-
-/**
- * Automatically detect the nearest lib directory and set the RPATH of an executable
- * @param filename
- * @param _rpath
- * @return
- */
-int rpath_autoset(const char *filename, FSTree *tree) {
- int returncode = 0;
-
- char *rpath_new = rpath_generate(filename, tree);
- if (!rpath_new) {
- return -1;
- }
-
- returncode = rpath_set(filename, rpath_new);
- free(rpath_new);
-
- return returncode;
-}
-
-/**
- * Find shared libraries in a directory tree
- *
- * @param root directory
- * @return `FSTree`
- */
-FSTree *rpath_libraries_available(const char *root) {
- FSTree *tree = fstree(root, (char *[]) {SPM_SHLIB_EXTENSION, NULL}, SPM_FSTREE_FLT_CONTAINS | SPM_FSTREE_FLT_RELATIVE);
- if (tree == NULL) {
- perror(root);
- fprintf(SYSERROR);
- return NULL;
- }
- return tree;
-}
-
-/**
- * Compute a RPATH based on the location `filename` relative to the shared libraries it requires
- *
- * @param filename path to file (or a directory)
- * @return success=relative path from `filename` to nearest lib directory, failure=NULL
- */
-char *rpath_autodetect(const char *filename, FSTree *tree) {
- const char *origin = "$ORIGIN";
- char *rootdir = dirname(filename);
- char *start = realpath(rootdir, NULL);
- char *cwd = realpath(".", NULL);
- char *result = NULL;
-
- char *visit = NULL; // Current directory
- char _relative[PATH_MAX] = {0,}; // Generated relative path to lib directory
- char *relative = _relative; // Pointer to relative path
- size_t depth_to_root = 0;
-
- // BUG: Perl dumps its shared library in a strange place.
- // This function returns `$ORIGIN/../../../CORE` which is not what we want to see.
- // TODO: We WANT to see this: `$ORIGIN/../lib/perl5/xx.xx.xx/<arch>/CORE` not just the basename()
-
- // Change directory to the requested root
- chdir(start);
-
- // Count the relative path distance between the location of the binary, and the top of the root
- visit = strdup(start);
- for (depth_to_root = 0; strcmp(tree->root, visit) != 0; depth_to_root++) {
- // Copy the current visit pointer
- char *prev = visit;
- // Walk back another directory level
- visit = dirname(visit);
- // Free previous visit pointer
- if (prev) free(prev);
- }
- free(visit);
-
- // return to calling directory
- chdir(cwd);
-
- StrList *libs = strlist_init();
- if (libs == NULL) {
- fprintf(stderr, "failed to initialize library StrList\n");
- fprintf(SYSERROR);
- return NULL;
- }
-
- StrList *libs_wanted = shlib_deps(filename);
- if (libs_wanted == NULL) {
- fprintf(stderr, "failed to retrieve list of share libraries from: %s\n", filename);
- fprintf(SYSERROR);
- return NULL;
- }
-
- for (size_t i = 0; i < strlist_count(libs_wanted); i++) {
- // zero out relative path string
- memset(_relative, '\0', sizeof(_relative));
- // Get the shared library name we are going to look for in the tree
- char *shared_library = strlist_item(libs_wanted, i);
-
- // Is the the shared library in the tree?
- char *match = NULL;
- if ((match = dirname(strstr_array(tree->files, shared_library))) != NULL) {
- // Begin generating the relative path string
- strcat(relative, origin);
- strcat(relative, DIRSEPS);
-
- // Append the number of relative levels to the relative path string
- if (depth_to_root) {
- for (size_t d = 0; d < depth_to_root; d++) {
- strcat(relative, "..");
- strcat(relative, DIRSEPS);
- }
- } else {
- strcat(relative, "..");
- strcat(relative, DIRSEPS);
- }
- // Append the match to the relative path string
- strcat(relative, basename(match));
-
- // Append relative path to array of libraries (if it isn't already in there)
- if (strstr_array(libs->data, relative) == NULL) {
- strlist_append(libs, relative);
- }
- }
- }
-
- // Some programs do not require local libraries provided by SPM (i.e. libc)
- // Inject "likely" defaults here
- if (strlist_count(libs) == 0) {
- strlist_append(libs, "$ORIGIN/../lib");
- strlist_append(libs, "$ORIGIN/../lib64");
- }
-
- // Populate result string
- result = join(libs->data, ":");
-
- // Clean up
- strlist_free(libs);
- strlist_free(libs_wanted);
- free(rootdir);
- free(cwd);
- free(start);
- return result;
-}
diff --git a/src/shell.c b/src/shell.c
deleted file mode 100644
index 6b28e64..0000000
--- a/src/shell.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/**
- * @file shell.c
- */
-#include "spm.h"
-
-/**
- * A wrapper for `popen()` that executes non-interactive programs and reports their exit value.
- * To redirect stdout and stderr you must do so from within the `fmt` string
- *
- * ~~~{.c}
- * int fd = 1; // stdout
- * const char *log_file = "log.txt";
- * Process *proc_info;
- * int status;
- *
- * // Send stderr to stdout
- * shell(&proc_info, SHELL_OUTPUT, "foo 2>&1");
- * // Send stdout and stderr to /dev/null
- * shell(&proc_info, SHELL_OUTPUT,"bar &>/dev/null");
- * // Send stdout from baz to log.txt
- * shell(&proc_info, SHELL_OUTPUT, "baz %d>%s", fd, log_file);
- * // Do not record or redirect output from any streams
- * shell(&proc_info, SHELL_DEFAULT, "biff");
- * ~~~
- *
- * @param Process uninitialized `Process` struct will be populated with process data
- * @param options change behavior of the function
- * @param fmt shell command to execute (accepts `printf` style formatters)
- * @param ... variadic arguments (used by `fmt`)
- */
-void shell(Process **proc_info, u_int64_t option, const char *fmt, ...) {
- va_list args;
- va_start(args, fmt);
-
- clockid_t clkid = CLOCK_REALTIME;
- FILE *proc = NULL;
-
- (*proc_info) = (Process *)calloc(1, sizeof(Process));
- if (!(*proc_info)) {
- fprintf(SYSERROR);
- exit(errno);
- }
- (*proc_info)->returncode = -1;
-
- // outbuf needs to be an integer type because fgetc returns EOF (> char)
- int *outbuf = (int *)calloc(1, sizeof(int));
- if (!outbuf) {
- fprintf(SYSERROR);
- exit(errno);
- }
- char *cmd = (char *)calloc(PATH_MAX, sizeof(char));
- if (!cmd) {
- fprintf(SYSERROR);
- exit(errno);
- }
-
- vsnprintf(cmd, PATH_MAX, fmt, args);
-
- if (option & SHELL_BENCHMARK) {
- if (clock_gettime(clkid, &(*proc_info)->start_time) == -1) {
- perror("clock_gettime");
- exit(errno);
- }
- }
-
- proc = popen(cmd, "r");
- if (!proc) {
- free(outbuf);
- va_end(args);
- return;
- }
-
- if (option & SHELL_BENCHMARK) {
- if (clock_gettime(clkid, &(*proc_info)->stop_time) == -1) {
- perror("clock_gettime");
- exit(errno);
- }
- (*proc_info)->time_elapsed = ((*proc_info)->stop_time.tv_sec - (*proc_info)->start_time.tv_sec)
- + ((*proc_info)->stop_time.tv_nsec - (*proc_info)->start_time.tv_nsec) / 1E9;
- }
-
- if (option & SHELL_OUTPUT) {
- size_t bytes_read = 0;
- size_t i = 0;
- size_t new_buf_size;
- (*proc_info)->output = (char *)calloc(BUFSIZ, sizeof(char));
-
- while ((*outbuf = fgetc(proc)) != EOF) {
- if (i >= BUFSIZ) {
- new_buf_size = BUFSIZ + (i + bytes_read);
- (*proc_info)->output = (char *)realloc((*proc_info)->output, new_buf_size);
- i = 0;
- }
- if (*outbuf) {
- (*proc_info)->output[bytes_read] = (char)*outbuf;
- }
- bytes_read++;
- i++;
- }
- }
- (*proc_info)->returncode = pclose(proc);
- va_end(args);
- free(outbuf);
- free(cmd);
-}
-
-/**
- * Free process resources allocated by `shell()`
- * @param proc_info `Process` struct
- */
-void shell_free(Process *proc_info) {
- if (proc_info->output) {
- free(proc_info->output);
- }
- free(proc_info);
-}
diff --git a/src/shlib.c b/src/shlib.c
deleted file mode 100644
index a8222af..0000000
--- a/src/shlib.c
+++ /dev/null
@@ -1,72 +0,0 @@
-#include "spm.h"
-#include "shlib.h"
-
-char *shlib_deps_objdump(const char *_filename) {
- // do not expose this function
- char *filename = NULL;
- char *result = NULL;
- Process *proc = NULL;
- char cmd[PATH_MAX];
- memset(cmd, '\0', sizeof(cmd));
-
- if ((filename = strdup(_filename)) == NULL) {
- fprintf(SYSERROR);
- return NULL;
- }
-
- strchrdel(filename, SHELL_INVALID);
- snprintf(cmd, sizeof(cmd), "%s %s '%s'", SPM_SHLIB_EXEC, "-p", filename);
- shell(&proc, SHELL_OUTPUT, cmd);
-
- if (proc->returncode != 0) {
- free(filename);
- shell_free(proc);
- return NULL;
- }
- result = strdup(proc->output);
-
- free(filename);
- shell_free(proc);
- return result;
-}
-
-StrList *shlib_deps(const char *filename) {
- char **data = NULL;
- char *output = NULL;
- StrList *result = NULL;
-
- // Get output from objdump
- // TODO: use preprocessor or another function to select the correct shlib_deps_*() in the future
- if ((output = shlib_deps_objdump(filename)) == NULL) {
- return NULL;
- }
-
- // Initialize list array
- if ((result = strlist_init()) == NULL) {
- free(output);
- return NULL;
- }
-
- // Split output into individual lines
- if ((data = split(output, "\n")) == NULL) {
- free(output);
- strlist_free(result);
- return NULL;
- }
-
- // Parse output:
- // Collapse whitespace and extract the NEEDED libraries (second field)
- // AFAIK when "NEEDED" is present, a string containing the library name is guaranteed to be there
- for (size_t i = 0; data[i] != NULL; i++) {
- data[i] = normalize_space(data[i]);
- if (startswith(data[i], "NEEDED")) {
- char **field = split(data[i], " ");
- strlist_append(result, field[1]);
- split_free(field);
- }
- }
-
- free(output);
- split_free(data);
- return result;
-}
diff --git a/src/spm_build.c b/src/spm_build.c
deleted file mode 100644
index 726b540..0000000
--- a/src/spm_build.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * @file spm_build.c
- */
-#include "spm.h"
-
-/**
- *
- * @param argc
- * @param argv
- * @return
- */
-int build(int argc, char **argv) {
- printf("build:\n");
- printf("argc: %d\n", argc);
- printf("argv:\n");
- for (int i = 0; i < argc; i++) {
- printf("%d: %s\n", i, argv[i]);
- }
-
- return 0;
-}
-
-
diff --git a/src/str.c b/src/str.c
deleted file mode 100644
index 5db3adc..0000000
--- a/src/str.c
+++ /dev/null
@@ -1,699 +0,0 @@
-/**
- * @file strings.c
- */
-#include "spm.h"
-
-/**
- * Determine how many times the character `ch` appears in `sptr` string
- * @param sptr string to scan
- * @param ch character to find
- * @return count of characters found
- */
-int num_chars(const char *sptr, int ch) {
- int result = 0;
- for (int i = 0; sptr[i] != '\0'; i++) {
- if (sptr[i] == ch) {
- result++;
- }
- }
- return result;
-}
-
-/**
- * Scan for `pattern` string at the beginning of `sptr`
- *
- * @param sptr string to scan
- * @param pattern string to search for
- * @return 1 = found, 0 = not found, -1 = error
- */
-int startswith(const char *sptr, const char *pattern) {
- if (!sptr) {
- return -1;
- }
- for (size_t i = 0; i < strlen(pattern); i++) {
- if (sptr[i] != pattern[i]) {
- return 0;
- }
- }
- return 1;
-}
-
-/**
- * Scan for `pattern` string at the end of `sptr`
- *
- * @param sptr string to scan
- * @param pattern string to search for
- * @return 1 = found, 0 = not found, -1 = error
- */
-int endswith(const char *sptr, const char *pattern) {
- if (!sptr) {
- return -1;
- }
- ssize_t sptr_size = strlen(sptr);
- ssize_t pattern_size = strlen(pattern);
-
- if (sptr_size == pattern_size) {
- if (strcmp(sptr, pattern) == 0) {
- return 1; // yes
- }
- return 0; // no
- }
-
- ssize_t s = sptr_size - pattern_size;
- if (s < 0) {
- return 0;
- }
-
- for (size_t p = 0 ; s < sptr_size; s++, p++) {
- if (sptr[s] != pattern[p]) {
- // sptr does not end with pattern
- return 0;
- }
- }
- // sptr ends with pattern
- return 1;
-}
-
-/**
- * Deletes any characters matching `chars` from `sptr` string
- *
- * @param sptr string to be modified in-place
- * @param chars a string containing characters (e.g. " \n" would delete whitespace and line feeds)
- */
-void strchrdel(char *sptr, const char *chars) {
- while (*sptr != '\0') {
- for (int i = 0; chars[i] != '\0'; i++) {
- if (*sptr == chars[i]) {
- memmove(sptr, sptr + 1, strlen(sptr));
- }
- }
- sptr++;
- }
-}
-
-/**
- * Find the integer offset of the first occurrence of `ch` in `sptr`
- *
- * ~~~{.c}
- * char buffer[255];
- * char string[] = "abc=123";
- * long int separator_offset = strchroff(string, '=');
- * for (long int i = 0; i < separator_offset); i++) {
- * buffer[i] = string[i];
- * }
- * ~~~
- *
- * @param sptr string to scan
- * @param ch character to find
- * @return offset to character in string, or 0 on failure
- */
-long int strchroff(const char *sptr, int ch) {
- char *orig = strdup(sptr);
- char *tmp = orig;
- long int result = 0;
- while (*tmp != '\0') {
- if (*tmp == ch) {
- break;
- }
- tmp++;
- }
- result = tmp - orig;
- free(orig);
-
- return result;
-}
-
-/**
- * This function scans `sptr` from right to left removing any matches to `suffix`
- * from the string.
- *
- * @param sptr string to be modified
- * @param suffix string to be removed from `sptr`
- */
-void strdelsuffix(char *sptr, const char *suffix) {
- if (!sptr || !suffix) {
- return;
- }
- size_t sptr_len = strlen(sptr);
- size_t suffix_len = strlen(suffix);
- intptr_t target_offset = sptr_len - suffix_len;
-
- // Prevent access to memory below input string
- if (target_offset < 0) {
- return;
- }
-
- // Create a pointer to
- char *target = sptr + target_offset;
- if (!strcmp(target, suffix)) {
- // Purge the suffix
- memset(target, '\0', suffix_len);
- // Recursive call continues removing suffix until it is gone
- strip(sptr);
- }
-}
-
-/**
- * Split a string by every delimiter in `delim` string.
- *
- * Callee must free memory using `split_free()`
- *
- * @param sptr string to split
- * @param delim characters to split on
- * @return success=parts of string, failure=NULL
- */
-char** split(char *_sptr, const char* delim)
-{
- if (_sptr == NULL) {
- return NULL;
- }
- size_t split_alloc = 0;
- // Duplicate the input string and save a copy of the pointer to be freed later
- char *orig = strdup(_sptr);
- char *sptr = orig;
- if (!sptr) {
- return NULL;
- }
-
- // Determine how many delimiters are present
- for (size_t i = 0; i < strlen(delim); i++) {
- split_alloc += num_chars(sptr, delim[i]);
- }
- // Preallocate enough records based on the number of delimiters
- char **result = (char **)calloc(split_alloc + 2, sizeof(char *));
- if (!result) {
- free(sptr);
- return NULL;
- }
-
- // Separate the string into individual parts and store them in the result array
- int i = 0;
- char *token = NULL;
- while((token = strsep(&sptr, delim)) != NULL) {
- result[i] = (char *)calloc(strlen(token) + 1, sizeof(char));
- if (!result[i]) {
- free(sptr);
- return NULL;
- }
- memcpy(result[i], token, strlen(token) + 1); // copy the string contents into the record
- i++; // next record
- }
- free(orig);
- return result;
-}
-
-/**
- * Frees memory allocated by `split()`
- * @param ptr pointer to array
- */
-void split_free(char **ptr) {
- for (int i = 0; ptr[i] != NULL; i++) {
- free(ptr[i]);
- }
- free(ptr);
-}
-
-/**
- * Create new a string from an array of strings
- *
- * ~~~{.c}
- * char *array[] = {
- * "this",
- * "is",
- * "a",
- * "test",
- * NULL,
- * }
- *
- * char *test = join(array, " "); // "this is a test"
- * char *test2 = join(array, "_"); // "this_is_a_test"
- * char *test3 = join(array, ", "); // "this, is, a, test"
- *
- * free(test);
- * free(test2);
- * free(test3);
- * ~~~
- *
- * @param arr
- * @param separator characters to insert between elements in string
- * @return new joined string
- */
-char *join(char **arr, const char *separator) {
- char *result = NULL;
- int records = 0;
- size_t total_bytes = 0;
-
- if (!arr) {
- return NULL;
- }
-
- for (int i = 0; arr[i] != NULL; i++) {
- total_bytes += strlen(arr[i]);
- records++;
- }
- total_bytes += (records * strlen(separator)) + 1;
-
- result = (char *)calloc(total_bytes, sizeof(char));
- for (int i = 0; i < records; i++) {
- strcat(result, arr[i]);
- if (i < (records - 1)) {
- strcat(result, separator);
- }
- }
- return result;
-}
-
-/**
- * Join two or more strings by a `separator` string
- * @param separator
- * @param ...
- * @return string
- */
-char *join_ex(char *separator, ...) {
- va_list ap; // Variadic argument list
- size_t separator_len = 0; // Length of separator string
- size_t size = 0; // Length of output string
- size_t argc = 0; // Number of arguments ^ "..."
- char **argv = NULL; // Arguments
- char *current = NULL; // Current argument
- char *result = NULL; // Output string
-
- // Initialize array
- argv = calloc(argc + 1, sizeof(char *));
- if (argv == NULL) {
- perror("join_ex calloc failed");
- return NULL;
- }
-
- // Get length of the separator
- separator_len = strlen(separator);
-
- // Process variadic arguments:
- // 1. Iterate over argument list `ap`
- // 2. Assign `current` with the value of argument in `ap`
- // 3. Extend the `argv` array by the latest argument count `argc`
- // 4. Sum the length of the argument and the `separator` passed to the function
- // 5. Append `current` string to `argv` array
- // 6. Update argument counter `argc`
- va_start(ap, separator);
- for(argc = 0; (current = va_arg(ap, char *)) != NULL; argc++) {
- char **tmp = realloc(argv, (argc + 1) * sizeof(char *));
- if (tmp == NULL) {
- perror("join_ex realloc failed");
- return NULL;
- }
- argv = tmp;
- size += strlen(current) + separator_len;
- argv[argc] = strdup(current);
- }
- va_end(ap);
-
- // Generate output string
- result = calloc(size + 1, sizeof(char));
- for (size_t i = 0; i < argc; i++) {
- // Append argument to string
- strcat(result, argv[i]);
-
- // Do not append a trailing separator when we reach the last argument
- if (i < (argc - 1)) {
- strcat(result, separator);
- }
- free(argv[i]);
- }
- free(argv);
-
- return result;
-}
-
-/**
- * Extract the string encapsulated by characters listed in `delims`
- *
- * ~~~{.c}
- * char *str = "this is [some data] in a string";
- * char *data = substring_between(string, "[]");
- * // data = "some data";
- * ~~~
- *
- * @param sptr string to parse
- * @param delims two characters surrounding a string
- * @return success=text between delimiters, failure=NULL
- */
-char *substring_between(char *sptr, const char *delims) {
- // Ensure we have enough delimiters to continue
- size_t delim_count = strlen(delims);
- if (delim_count != 2) {
- return NULL;
- }
-
- // Create pointers to the delimiters
- char *start = strpbrk(sptr, &delims[0]);
- char *end = strpbrk(sptr, &delims[1]);
-
- // Ensure the string has both delimiters
- if (!start || !end) {
- return NULL;
- }
-
- start++; // ignore leading delimiter
-
- // Get length of the substring
- size_t length = end - start;
-
- char *result = (char *)calloc(length + 1, sizeof(char));
- if (!result) {
- return NULL;
- }
-
- // Copy the contents of the substring to the result
- char *tmp = result;
- while (start != end) {
- *tmp = *start;
- tmp++;
- start++;
- }
-
- return result;
-}
-
-/*
- * Helper function for `strsort`
- */
-static int _strsort_compare(const void *a, const void *b) {
- const char *aa = *(const char**)a;
- const char *bb = *(const char**)b;
- int result = strcmp(aa, bb);
- return result;
-}
-
-/**
- * Sort an array of strings alphabetically
- * @param arr
- */
-void strsort(char **arr) {
- size_t arr_size = 0;
-
- // Determine size of array
- for (size_t i = 0; arr[i] != NULL; i++) {
- arr_size = i;
- }
- qsort(arr, arr_size, sizeof(char *), _strsort_compare);
-}
-
-/*
- * Helper function for `strsortlen`
- */
-static int _strsortlen_asc_compare(const void *a, const void *b) {
- const char *aa = *(const char**)a;
- const char *bb = *(const char**)b;
- size_t len_a = strlen(aa);
- size_t len_b = strlen(bb);
- return len_a > len_b;
-}
-
-/*
- * Helper function for `strsortlen`
- */
-static int _strsortlen_dsc_compare(const void *a, const void *b) {
- const char *aa = *(const char**)a;
- const char *bb = *(const char**)b;
- size_t len_a = strlen(aa);
- size_t len_b = strlen(bb);
- return len_a < len_b;
-}
-/**
- * Sort an array of strings by length
- * @param arr
- */
-void strsortlen(char **arr, unsigned int sort_mode) {
- typedef int (*compar)(const void *, const void *);
-
- compar fn = _strsortlen_asc_compare;
- if (sort_mode != 0) {
- fn = _strsortlen_dsc_compare;
- }
-
- size_t arr_size = 0;
-
- // Determine size of array
- for (size_t i = 0; arr[i] != NULL; i++) {
- arr_size = i;
- }
- qsort(arr, arr_size, sizeof(char *), fn);
-}
-
-/**
- * Search for string in an array of strings
- * @param arr array of strings
- * @param str string to search for
- * @return yes=`pointer to string`, no=`NULL`, failure=`NULL`
- */
-char *strstr_array(char **arr, const char *str) {
- if (arr == NULL) {
- return NULL;
- }
-
- for (int i = 0; arr[i] != NULL; i++) {
- if (strstr(arr[i], str) != NULL) {
- return arr[i];
- }
- }
- return NULL;
-}
-
-/**
- * Remove duplicate strings from an array of strings
- * @param arr
- * @return success=array of unique strings, failure=NULL
- */
-char **strdeldup(char **arr) {
- if (!arr) {
- return NULL;
- }
-
- size_t records;
- // Determine the length of the array
- for (records = 0; arr[records] != NULL; records++);
-
- // Allocate enough memory to store the original array contents
- // (It might not have duplicate values, for example)
- char **result = (char **)calloc(records + 1, sizeof(char *));
- if (!result) {
- return NULL;
- }
-
- int rec = 0;
- size_t i = 0;
- while(i < records) {
- // Search for value in results
- if (strstr_array(result, arr[i]) == 0) {
- // value already exists in results so ignore it
- i++;
- continue;
- }
-
- // Store unique value
- result[rec] = (char *)calloc(strlen(arr[i]) + 1, sizeof(char));
- if (!result[rec]) {
- free(result);
- return NULL;
- }
- memcpy(result[rec], arr[i], strlen(arr[i]) + 1);
- i++;
- rec++;
- }
- return result;
-}
-
-/** Remove leading whitespace from a string
- * @param sptr pointer to string
- * @return pointer to first non-whitespace character in string
- */
-char *lstrip(char *sptr) {
- char *tmp = sptr;
- size_t bytes = 0;
- while (isblank(*tmp) || isspace(*tmp)) {
- bytes++;
- tmp++;
- }
- if (tmp != sptr) {
- memmove(sptr, sptr + bytes, strlen(sptr) - bytes);
- memset((sptr + strlen(sptr)) - bytes, '\0', bytes);
- }
- return sptr;
-}
-
-/**
- * Remove trailing whitespace from a string
- * @param sptr string
- * @return truncated string
- */
-char *strip(char *sptr) {
- size_t len = strlen(sptr);
- if (len == 0) {
- return sptr;
- }
- else if (len == 1 && (isblank(*sptr) || isspace(*sptr))) {
- *sptr = '\0';
- return sptr;
- }
- for (size_t i = len; i != 0; --i) {
- if (sptr[i] == '\0') {
- continue;
- }
- if (isspace(sptr[i]) || isblank(sptr[i])) {
- sptr[i] = '\0';
- }
- else {
- break;
- }
- }
- return sptr;
-}
-
-/**
- * Determine if a string is empty
- * @param sptr pointer to string
- * @return 0=not empty, 1=empty
- */
-int isempty(char *sptr) {
- char *tmp = sptr;
- while (*tmp) {
- if (!isblank(*tmp) || !isspace(*tmp)) {
- return 0;
- }
- tmp++;
- }
- return 1;
-}
-
-/**
- * Determine if a string is encapsulated by quotes
- * @param sptr pointer to string
- * @return 0=not quoted, 1=quoted
- */
-int isquoted(char *sptr) {
- const char *quotes = "'\"";
- char *quote_open = strpbrk(sptr, quotes);
- if (!quote_open) {
- return 0;
- }
- char *quote_close = strpbrk(quote_open + 1, quotes);
- if (!quote_close) {
- return 0;
- }
- return 1;
-}
-
-/**
- * Determine whether the input character is a relational operator
- * Note: `~` is non-standard
- * @param ch
- * @return 0=no, 1=yes
- */
-int isrelational(char ch) {
- char symbols[] = "~!=<>";
- char *symbol = symbols;
- while (*symbol != '\0') {
- if (ch == *symbol) {
- return 1;
- }
- symbol++;
- }
- return 0;
-}
-
-/**
- * Print characters in `s`, `len` times
- * @param s
- * @param len
- */
-void print_banner(const char *s, int len) {
- size_t s_len = strlen(s);
- if (!s_len) {
- return;
- }
- for (size_t i = 0; i < (len / s_len); i++) {
- for (size_t c = 0; c < s_len; c++) {
- putchar(s[c]);
- }
- }
- putchar('\n');
-}
-
-/**
- * Collapse whitespace in `s`. The string is modified in place.
- * @param s
- * @return pointer to `s`
- */
-char *normalize_space(char *s) {
- size_t len;
- size_t trim_pos;
- int add_whitespace = 0;
- char *result = s;
- char *tmp;
- if ((tmp = calloc(strlen(s) + 1, sizeof(char))) == NULL) {
- perror("could not allocate memory for temporary string");
- return NULL;
- }
- char *tmp_orig = tmp;
-
- // count whitespace, if any
- for (trim_pos = 0; isblank(s[trim_pos]); trim_pos++);
- // trim whitespace from the left, if any
- memmove(s, &s[trim_pos], strlen(&s[trim_pos]));
- // cull bytes not part of the string after moving
- len = strlen(s);
- s[len - trim_pos] = '\0';
-
- // Generate a new string with extra whitespace stripped out
- while (*s != '\0') {
- // Skip over any whitespace, but record that we encountered it
- if (isblank(*s)) {
- s++;
- add_whitespace = 1;
- continue;
- }
- // This gate avoids filling tmp with whitespace; we want to make our own
- if (add_whitespace) {
- *tmp = ' ';
- tmp++;
- add_whitespace = 0;
- }
- // Write character in s to tmp
- *tmp = *s;
- // Increment string pointers
- s++;
- tmp++;
- }
-
- // Rewrite the input string
- strcpy(result, tmp_orig);
- free(tmp_orig);
- return result;
-}
-
-/**
- * Duplicate an array of strings
- * @param array
- * @return
- */
-char **strdup_array(char **array) {
- char **result = NULL;
- size_t elems = 0;
-
- // Guard
- if (array == NULL) {
- return NULL;
- }
-
- // Count elements in `array`
- for (elems = 0; array[elems] != NULL; elems++);
-
- // Create new array
- result = calloc(elems + 1, sizeof(char *));
- for (size_t i = 0; i < elems; i++) {
- result[i] = strdup(array[i]);
- }
-
- return result;
-}
diff --git a/src/strlist.c b/src/strlist.c
deleted file mode 100644
index 1cab324..0000000
--- a/src/strlist.c
+++ /dev/null
@@ -1,339 +0,0 @@
-/**
- * String array convenience functions
- * @file strlist.c
- */
-#include "spm.h"
-#include "strlist.h"
-
-/**
- *
- * @param pStrList `StrList`
- */
-void strlist_free(StrList *pStrList) {
- for (size_t i = 0; i < pStrList->num_inuse; i++) {
- free(pStrList->data[i]);
- }
- free(pStrList->data);
- free(pStrList);
-}
-
-/**
- * Append a value to the list
- * @param pStrList `StrList`
- * @param str
- */
-void strlist_append(StrList *pStrList, char *str) {
- char **tmp = realloc(pStrList->data, (pStrList->num_alloc + 1) * sizeof(char *));
- if (tmp == NULL) {
- strlist_free(pStrList);
- perror("failed to append to array");
- exit(1);
- }
- pStrList->data = tmp;
- pStrList->data[pStrList->num_inuse] = strdup(str);
- pStrList->data[pStrList->num_alloc] = NULL;
- strcpy(pStrList->data[pStrList->num_inuse], str);
- pStrList->num_inuse++;
- pStrList->num_alloc++;
-}
-
-/**
- * Append the contents of a `StrList` to another `StrList`
- * @param pStrList1 `StrList`
- * @param pStrList2 `StrList`
- */
-void strlist_append_strlist(StrList *pStrList1, StrList *pStrList2) {
- size_t count = strlist_count(pStrList2);
- for (size_t i = 0; i < count; i++) {
- char *item = strlist_item(pStrList2, i);
- strlist_append(pStrList1, item);
- }
-}
-
-/**
- *
- * @param a
- * @param b
- * @return
- */
-static int _strlist_cmpfn(const void *a, const void *b) {
- const char *aa = *(const char**)a;
- const char *bb = *(const char**)b;
- int result = strcmp(aa, bb);
- return result;
-}
-
-/**
- *
- * @param a
- * @param b
- * @return
- */
-static int _strlist_asc_cmpfn(const void *a, const void *b) {
- const char *aa = *(const char**)a;
- const char *bb = *(const char**)b;
- size_t len_a = strlen(aa);
- size_t len_b = strlen(bb);
- return len_a > len_b - strcmp(aa, bb);
-}
-
-/**
- *
- * @param a
- * @param b
- * @return
- */
-static int _strlist_dsc_cmpfn(const void *a, const void *b) {
- const char *aa = *(const char**)a;
- const char *bb = *(const char**)b;
- size_t len_a = strlen(aa);
- size_t len_b = strlen(bb);
- return len_a < len_b - strcmp(aa, bb);
-}
-
-/**
- * Sort a `StrList` by `mode`
- * @param pStrList
- * @param mode Available modes: `STRLIST_DEFAULT` (alphabetic), `STRLIST_ASC` (ascending), `STRLIST_DSC` (descending)
- */
-void strlist_sort(StrList *pStrList, unsigned int mode) {
- void *fn = NULL;
- switch (mode) {
- case STRLIST_ASC:
- fn = _strlist_asc_cmpfn;
- break;
- case STRLIST_DSC:
- fn = _strlist_dsc_cmpfn;
- break;
- case STRLIST_DEFAULT:
- default:
- fn = _strlist_cmpfn;
- break;
- }
-
- qsort(pStrList->data, pStrList->num_inuse, sizeof(char *), fn);
-}
-
-/**
- * Reverse the order of a `StrList`
- * @param pStrList
- */
-void strlist_reverse(StrList *pStrList) {
- char *tmp = NULL;
- size_t i = 0;
- size_t j = pStrList->num_inuse - 1;
-
- for (i = 0; i < j; i++) {
- tmp = pStrList->data[i];
- pStrList->data[i] = pStrList->data[j];
- pStrList->data[j] = tmp;
- j--;
- }
-}
-
-/**
- * Get the count of values stored in a `StrList`
- * @param pStrList
- * @return
- */
-size_t strlist_count(StrList *pStrList) {
- return pStrList->num_inuse;
-}
-
-/**
- * Set value at index
- * @param pStrList
- * @param value string
- * @return
- */
-void strlist_set(StrList *pStrList, size_t index, char *value) {
- char *tmp = NULL;
- char *item = NULL;
- if (index > strlist_count(pStrList)) {
- return;
- }
- if ((item = strlist_item(pStrList, index)) == NULL) {
- return;
- }
- if (value == NULL) {
- pStrList->data[index] = NULL;
- } else {
- if ((tmp = realloc(pStrList->data[index], strlen(value) + 1)) == NULL) {
- perror("realloc strlist_set replacement value");
- return;
- }
-
- pStrList->data[index] = tmp;
- memset(pStrList->data[index], '\0', strlen(value) + 1);
- strncpy(pStrList->data[index], value, strlen(value));
- }
-}
-
-/**
- * Retrieve data from a `StrList`
- * @param pStrList
- * @param index
- * @return string
- */
-char *strlist_item(StrList *pStrList, size_t index) {
- if (index > strlist_count(pStrList)) {
- return NULL;
- }
- return pStrList->data[index];
-}
-
-/**
- * Alias of `strlist_item`
- * @param pStrList
- * @param index
- * @return string
- */
-char *strlist_item_as_str(StrList *pStrList, size_t index) {
- return strlist_item(pStrList, index);
-}
-
-/**
- * Convert value at index to `char`
- * @param pStrList
- * @param index
- * @return `char`
- */
-char strlist_item_as_char(StrList *pStrList, size_t index) {
- return (char) *(strlist_item(pStrList, index));
-}
-
-/**
- * Convert value at index to `unsigned char`
- * @param pStrList
- * @param index
- * @return `unsigned char`
- */
-unsigned char strlist_item_as_uchar(StrList *pStrList, size_t index) {
- return (unsigned char) *(strlist_item(pStrList, index));
-}
-
-/**
- * Convert value at index to `short`
- * @param pStrList
- * @param index
- * @return `short`
- */
-short strlist_item_as_short(StrList *pStrList, size_t index) {
- return (short)atoi(strlist_item(pStrList, index));
-}
-
-/**
- * Convert value at index to `unsigned short`
- * @param pStrList
- * @param index
- * @return `unsigned short`
- */
-unsigned short strlist_item_as_ushort(StrList *pStrList, size_t index) {
- return (unsigned short)atoi(strlist_item(pStrList, index));
-}
-
-/**
- * Convert value at index to `int`
- * @param pStrList
- * @param index
- * @return `int`
- */
-int strlist_item_as_int(StrList *pStrList, size_t index) {
- return atoi(strlist_item(pStrList, index));
-}
-
-/**
- * Convert value at index to `unsigned int`
- * @param pStrList
- * @param index
- * @return `unsigned int`
- */
-unsigned int strlist_item_as_uint(StrList *pStrList, size_t index) {
- return (unsigned int)atoi(strlist_item(pStrList, index));
-}
-
-/**
- * Convert value at index to `long`
- * @param pStrList
- * @param index
- * @return `long`
- */
-long strlist_item_as_long(StrList *pStrList, size_t index) {
- return atol(strlist_item(pStrList, index));
-}
-
-/**
- * Convert value at index to `unsigned long`
- * @param pStrList
- * @param index
- * @return `unsigned long`
- */
-unsigned long strlist_item_as_ulong(StrList *pStrList, size_t index) {
- return (unsigned long)atol(strlist_item(pStrList, index));
-}
-
-/**
- * Convert value at index to `long long`
- * @param pStrList
- * @param index
- * @return `long long`
- */
-long long strlist_item_as_long_long(StrList *pStrList, size_t index) {
- return (long long)atoll(strlist_item(pStrList, index));
-}
-
-/**
- * Convert value at index to `unsigned long long`
- * @param pStrList
- * @param index
- * @return `unsigned long long`
- */
-unsigned long long strlist_item_as_ulong_long(StrList *pStrList, size_t index) {
- return (unsigned long long)atoll(strlist_item(pStrList, index));
-}
-
-/**
- * Convert value at index to `float`
- * @param pStrList
- * @param index
- * @return `float`
- */
-float strlist_item_as_float(StrList *pStrList, size_t index) {
- return (float)atof(strlist_item(pStrList, index));
-}
-
-/**
- * Convert value at index to `double`
- * @param pStrList
- * @param index
- * @return `double`
- */
-double strlist_item_as_double(StrList *pStrList, size_t index) {
- return atof(strlist_item(pStrList, index));
-}
-
-/**
- * Convert value at index to `long double`
- * @param pStrList
- * @param index
- * @return `long double`
- */
-long double strlist_item_as_long_double(StrList *pStrList, size_t index) {
- return (long double)atof(strlist_item(pStrList, index));
-}
-
-/**
- * Initialize an empty `StrList`
- * @return `StrList`
- */
-StrList *strlist_init() {
- StrList *pStrList = calloc(1, sizeof(StrList));
- if (pStrList == NULL) {
- perror("failed to allocate array");
- exit(errno);
- }
- pStrList->num_inuse = 0;
- pStrList->num_alloc = 1;
- pStrList->data = calloc(pStrList->num_alloc, sizeof(char *));
- return pStrList;
-}
diff --git a/src/user_input.c b/src/user_input.c
deleted file mode 100644
index 3f358fa..0000000
--- a/src/user_input.c
+++ /dev/null
@@ -1,75 +0,0 @@
-#include "spm.h"
-
-/**
- * Basic case-insensitive interactive choice function
- * @param answer
- * @param answer_default
- * @return
- */
-int spm_user_yesno(int answer, int empty_input_is_yes) {
- int result = 0;
- answer = tolower(answer);
-
- if (answer == 'y') {
- result = 1;
- } else if (answer == 'n') {
- result = 0;
- } else {
- if (empty_input_is_yes) {
- result = 1;
- } else {
- result = -1;
- }
- }
-
- return result;
-}
-
-int spm_prompt_user(const char *msg, int empty_input_is_yes) {
- int user_choice = 0;
- int status_choice = 0;
- char ch_yes = 'y';
- char ch_no = 'n';
-
- if (empty_input_is_yes) {
- ch_yes = 'Y';
- } else {
- ch_no = 'N';
- }
-
- printf("\n%s [%c/%c] ", msg, ch_yes, ch_no);
- while ((user_choice = getchar())) {
- status_choice = spm_user_yesno(user_choice, 1);
- if (status_choice == 0) { // No
- break;
- } else if (status_choice == 1) { // Yes
- break;
- } else { // Only triggers when spm_user_yesno's second argument is zero
- puts("Please answer 'y' or 'n'...");
- }
- }
- puts("");
-
- return status_choice;
-}
-
-
-void spm_user_yesno_test() {
- int choice;
- int status;
- while ((choice = getchar())) {
- status = spm_user_yesno(choice, 1);
- if (status == -1) {
- puts("Please answer Y or N");
- continue;
- }
- else if (status == 0) {
- puts("You answered no");
- break;
- }
- else if (status == 1) {
- puts("You answered yes");
- break;
- }
- }
-}
diff --git a/src/version_spec.c b/src/version_spec.c
deleted file mode 100644
index 06fcd1b..0000000
--- a/src/version_spec.c
+++ /dev/null
@@ -1,445 +0,0 @@
-/**
- * @file version_spec.c
- */
-#include "spm.h"
-
-/**
- *
- * @param str
- * @return
- */
-char *version_suffix_get_alpha(char *str) {
- size_t i;
- size_t len = strlen(str);
- for (i = 0; i < len; i++) {
- // return pointer to the first alphabetic character we find
- if (isalpha(str[i])) {
- return &str[i];
- }
- }
- return NULL;
-}
-
-/**
- *
- * @param str
- * @return
- */
-char *version_suffix_get_modifier(char *str) {
- size_t i;
- char *modifiers[] = {
- "rc",
- "pre",
- "dev",
- "post",
- NULL,
- };
- for (i = 0; i < strlen(str); i++) {
- for (int m = 0; modifiers[m] != NULL; m++) {
- if (strncasecmp(&str[i], modifiers[m], strlen(modifiers[m])) == 0) {
- return &str[i];
- }
- }
- }
- return NULL;
-}
-
-/**
- *
- * @param str
- * @return
- */
-int64_t version_suffix_modifier_calc(char *str) {
- int64_t result = 0;
- char *tmp_s = str;
-
- if (strncasecmp(str, "rc", 2) == 0) {
- // do rc
- tmp_s += strlen("rc");
- if (isdigit(*tmp_s)) {
- result -= atoi(tmp_s);
- }
- else {
- result -= 1;
- }
- }
- else if (strncasecmp(str, "pre", 3) == 0) {
- // do pre
- tmp_s += strlen("pre");
- if (isdigit(*tmp_s)) {
- result -= atoi(tmp_s);
- }
- else {
- result -= 1;
- }
- }
- else if (strncasecmp(str, "dev", 3) == 0) {
- // do dev
- tmp_s += strlen("dev");
- if (isdigit(*tmp_s)) {
- result -= atoi(tmp_s);
- }
- else {
- result -= 1;
- }
- }
- else if (strncasecmp(str, "post", 4) == 0) {
- // do post
- tmp_s += strlen("post");
- if (isdigit(*tmp_s)) {
- result += atoi(tmp_s);
- }
- else {
- result += 1;
- }
- }
-
- return result;
-}
-
-/**
- *
- * @param str
- * @return
- */
-int version_suffix_alpha_calc(char *str) {
- int x = 0;
- char chs[255];
- char *ch = chs;
- memset(chs, '\0', sizeof(chs));
- strncpy(chs, str, strlen(str));
-
- // Handle cases where the two suffixes are not delimited by anything
- // Start scanning one character ahead of the alphabetic suffix and terminate the string
- // when/if we reach another alphabetic character (presumably a version modifer)
- for (int i = 1; chs[i] != '\0'; i++) {
- if (isalpha(chs[i])) {
- chs[i] = '\0';
- }
- }
-
- // Convert character to hex-ish
- x = (*ch - 'a') + 0xa;
-
- // Ensure the string ends with a digit
- if (strlen(str) == 1) {
- strcat(ch, "0");
- }
-
- // Convert trailing numerical value to an integer
- while (*ch != '\0') {
- if (!isdigit(*ch)) {
- ch++;
- continue;
- }
- x += atoi(ch);
- break;
- }
-
- return x;
-}
-
-/**
- *
- * @param version_str
- * @return
- */
-int64_t version_from(const char *version_str) {
- const char *delim = ".";
- int64_t result = 0;
- if (version_str == NULL) {
- return 0;
- }
-
- int seen_alpha = 0; // Does the tail contain a single character, but not a modifier?
- int seen_modifier = 0; // Does the tail contain "rc", "dev", "pre", and so forth?
- char head[255]; // digits of the string
- char tail[255]; // alphabetic characters of the string
- char *suffix_alpha = NULL; // pointer to location of the first character after the version
- char *suffix_modifier = NULL; // pointer to location of the modifier after the version
- char *x = NULL; // pointer to each string delimited by "."
- char *vstr = strdup(version_str);
- if (!vstr) {
- perror("Version string copy");
- return -1;
- }
-
- memset(head, '\0', sizeof(head));
- memset(tail, '\0', sizeof(tail));
-
- // Split the version into parts
- while ((x = strsep(&vstr, delim)) != NULL) {
- int64_t tmp = 0;
-
- // populate the head (numeric characters)
- strncpy(head, x, strlen(x));
- for (size_t i = 0; i < strlen(head); i++) {
- if (isalpha(head[i])) {
- // populate the tail (alphabetic characters)
- strncpy(tail, &head[i], strlen(&head[i]));
- head[i] = '\0';
- break;
- }
- }
-
- // Detect alphabetic suffix
- if (!seen_alpha) {
- if ((suffix_alpha = version_suffix_get_alpha(x)) != NULL) {
- seen_alpha = 1;
- }
- }
-
- // Detect modifier suffix
- if (!seen_modifier) {
- if ((suffix_modifier = version_suffix_get_modifier(x)) != NULL) {
- seen_modifier = 1;
- }
- }
-
- // Stop processing if the head starts with something other than numbers
- if (!isdigit(head[0])) {
- break;
- }
-
- // Convert the head to an integer
- tmp = atoi(head);
- // Update result. Each portion of the numeric version is its own byte
- // Version PARTS are limited to 255
- result = result << 8 | tmp;
- }
-
- if (suffix_alpha != NULL) {
- // Convert the alphabetic suffix to an integer
- int64_t sac = version_suffix_alpha_calc(suffix_alpha);
- result += sac;
- }
-
- if (suffix_modifier != NULL) {
- // Convert the modifier string to an integer
- int64_t smc = version_suffix_modifier_calc(suffix_modifier);
- if (smc < 0) {
- result -= ~smc + 1;
- }
- else {
- result += smc;
- }
- }
-
- free(vstr);
- return result;
-}
-
-/**
- *
- * @param op
- * @return
- */
-int version_spec_from(const char *op) {
- int flags = VERSION_NOOP;
- size_t len = strlen(op);
- for (size_t i = 0; i < len; i++) {
- if (op[i] == '>') {
- flags |= VERSION_GT;
- }
- else if (op[i] == '<') {
- flags |= VERSION_LT;
- }
- else if (op[i] == '=' || (len > 1 && strncmp(&op[i], "==", 2) == 0)) {
- flags |= VERSION_EQ;
- }
- else if (op[i] == '!') {
- flags |= VERSION_NE;
- }
- else if (op[i] == '~') {
- flags |= VERSION_COMPAT;
- }
- }
- return flags;
-}
-
-/**
- *
- * @param a
- * @param b
- * @return
- */
-static int _find_by_spec_compare(const void *a, const void *b) {
- const ManifestPackage *aa = *(const ManifestPackage**)a;
- const ManifestPackage *bb = *(const ManifestPackage**)b;
- int64_t version_a = version_from(aa->version);
- int64_t version_b = version_from(bb->version);
- return version_a > version_b;
-}
-
-/**
- *
- * @param manifest
- * @param name
- * @param op
- * @param version_str
- * @return
- */
-ManifestPackage **find_by_spec(Manifest *manifest, const char *name, const char *op, const char *version_str) {
- size_t record = 0;
- ManifestPackage **list = (ManifestPackage **) calloc(manifest->records + 1, sizeof(ManifestPackage *));
- if (!list) {
- perror("ManifestPackage array");
- fprintf(SYSERROR);
- return NULL;
- }
-
- for (size_t i = 0; i < manifest->records; i++) {
- if (strcmp(manifest->packages[i]->name, name) == 0) {
- int64_t version_a = version_from(manifest->packages[i]->version);
- int64_t version_b = version_from(version_str);
- int spec = version_spec_from(op);
-
- int res = 0;
- if (spec & VERSION_GT && spec & VERSION_EQ) {
- res = version_a >= version_b;
- }
- else if (spec & VERSION_LT && spec & VERSION_EQ) {
- res = version_a <= version_b;
- }
- else if (spec & VERSION_NE && spec & VERSION_EQ) {
- res = version_a != version_b;
- }
- else if (spec & VERSION_GT) {
- res = version_a > version_b;
- }
- else if (spec & VERSION_LT) {
- res = version_a < version_b;
- }
- else if (spec & VERSION_COMPAT) {
- // TODO
- }
- else if (spec & VERSION_EQ) {
- res = version_a == version_b;
- }
-
- if (res != 0) {
- list[record] = manifest_package_copy(manifest->packages[i]);
- if (!list[record]) {
- perror("Unable to allocate memory for manifest record");
- fprintf(SYSERROR);
- return NULL;
- }
- record++;
- }
- }
- }
- qsort(list, record, sizeof(ManifestPackage *), _find_by_spec_compare);
-
- return list;
-}
-
-static void get_name(char **buf, const char *_str) {
- char *str = strdup(_str);
- int has_relational = 0;
- int is_archive = endswith(str, SPM_PACKAGE_EXTENSION);
- for (size_t i = 0; str[i] != '\0'; i++) {
- if (isrelational(str[i]))
- has_relational = 1;
- }
-
- if (is_archive == 0 && !has_relational) {
- strcpy((*buf), str);
- }
- else if (has_relational) {
- size_t stop = 0;
- for (stop = 0; !isrelational(str[stop]); stop++);
- strncpy((*buf), str, stop);
- (*buf)[stop] = '\0';
- } else {
- StrList *tmp = strlist_init();
- char sep[2];
- sep[0] = SPM_PACKAGE_MEMBER_SEPARATOR;
- sep[1] = '\0';
-
- char **parts = split(str, sep);
- if (parts != NULL) {
- for (size_t i = 0; parts[i] != NULL; i++) {
- strlist_append(tmp, parts[i]);
- }
- }
- split_free(parts);
-
- if (strlist_count(tmp) > SPM_PACKAGE_MIN_DELIM) {
- strlist_set(tmp, strlist_count(tmp) - SPM_PACKAGE_MIN_DELIM, NULL);
- }
- char *result = join(tmp->data, sep);
- strcpy((*buf), result);
- free(result);
- strlist_free(tmp);
- }
- free(str);
-}
-
-static char *get_operators(char **op, const char *_strspec) {
- const char *operators = VERSION_OPERATORS; // note: whitespace is synonymous with ">=" if no operators are present
- char *pos = NULL;
- pos = strpbrk(_strspec, operators);
- if (pos != NULL) {
- for (size_t i = 0; !isalnum(*pos) || *pos == '.'; i++) {
- (*op)[i] = *pos++;
- }
- }
- return pos;
-}
-
-ManifestPackage *find_by_strspec(Manifest *manifest, const char *_strspec) {
- char *pos = NULL;
- char s_op[NAME_MAX];
- char s_name[NAME_MAX];
- char s_version[NAME_MAX];
- char *op = s_op;
- char *name = s_name;
- char *version = s_version;
- char *strspec = strdup(_strspec);
-
- memset(op, '\0', NAME_MAX);
- memset(name, '\0', NAME_MAX);
- memset(version, '\0', NAME_MAX);
-
- // Parse name
- //for (size_t i = 0; isalnum(_strspec[i]) || _strspec[i] == '_' || _strspec[i] == '-'; i++) {
- // name[i] = _strspec[i];
- //}
- get_name(&name, strspec);
- pos = get_operators(&op, strspec);
-
-
- ManifestPackage **m = NULL;
- // No operators found
- if (pos == NULL) {
- m = find_by_spec(manifest, name, ">=", NULL);
- }
-
- // When `m` is still NULL after applying the default operator
- if (m == NULL) {
- for (size_t i = 0; *(pos + i) != '\0'; i++) {
- version[i] = *(pos + i);
- }
- m = find_by_spec(manifest, name, op, version);
- }
-
- // When `m` has been populated by either test above, return a COPY of the manifest
- if (m != NULL) {
- ManifestPackage *selected = NULL;
- for (size_t i = 0; m[i] != NULL; i++) {
- selected = m[i];
- }
-
- ManifestPackage *result = manifest_package_copy(selected);
- for (size_t i = 0; m[i] != NULL; i++) {
- manifest_package_free(m[i]);
- }
- free(m);
- free(strspec);
- return result;
- }
-
- // Obviously it didn't work out
- free(strspec);
- return NULL;
-}