diff options
author | Joseph Hunkeler <jhunkeler@gmail.com> | 2020-03-18 22:25:27 -0400 |
---|---|---|
committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2020-03-18 22:25:27 -0400 |
commit | ccaeb7092b5ad40b1b3833c987ba3ec4d47f0bb8 (patch) | |
tree | ae167772a9a2343aa77bf8944b56abe853f6a2ec /src | |
parent | 3731bb4679ee8716d4f579d5744c36a2d1b4a257 (diff) | |
download | spmc-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.txt | 30 | ||||
-rw-r--r-- | src/archive.c | 110 | ||||
-rw-r--r-- | src/checksum.c | 42 | ||||
-rw-r--r-- | src/compat.c | 23 | ||||
-rw-r--r-- | src/config.c | 227 | ||||
-rw-r--r-- | src/config_global.c | 375 | ||||
-rw-r--r-- | src/environment.c | 406 | ||||
-rw-r--r-- | src/extern/url.c | 655 | ||||
-rw-r--r-- | src/find.c | 151 | ||||
-rw-r--r-- | src/fs.c | 504 | ||||
-rw-r--r-- | src/install.c | 328 | ||||
-rw-r--r-- | src/internal_cmd.c | 401 | ||||
-rw-r--r-- | src/manifest.c | 668 | ||||
-rw-r--r-- | src/metadata.c | 166 | ||||
-rw-r--r-- | src/mime.c | 156 | ||||
-rw-r--r-- | src/mirrors.c | 180 | ||||
-rw-r--r-- | src/purge.c | 93 | ||||
-rw-r--r-- | src/relocation.c | 440 | ||||
-rw-r--r-- | src/resolve.c | 65 | ||||
-rw-r--r-- | src/rpath.c | 303 | ||||
-rw-r--r-- | src/shell.c | 116 | ||||
-rw-r--r-- | src/shlib.c | 72 | ||||
-rw-r--r-- | src/spm_build.c | 23 | ||||
-rw-r--r-- | src/str.c | 699 | ||||
-rw-r--r-- | src/strlist.c | 339 | ||||
-rw-r--r-- | src/user_input.c | 75 | ||||
-rw-r--r-- | src/version_spec.c | 445 |
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; -} |