From 9bd603b639d7dd08fbe0af7e8669358bd066b7cd Mon Sep 17 00:00:00 2001 From: Joseph Hunkeler Date: Tue, 11 Feb 2020 11:43:25 -0500 Subject: Refactor project structure: * Move prototypes and definitions to respective header files * Renamed strings.h to str.h to avoid collision with standard library header --- include/archive.h | 7 + include/checksum.h | 7 + include/conf.h | 54 +++++ include/deps.h | 20 ++ include/environment.h | 15 ++ include/find.h | 9 + include/fs.h | 29 +++ include/install.h | 7 + include/internal_cmd.h | 7 + include/manifest.h | 48 ++++ include/mime.h | 18 ++ include/mirrors.h | 12 + include/relocation.h | 21 ++ include/rpath.h | 12 + include/shell.h | 18 ++ include/spm.h | 300 ++----------------------- include/str.h | 27 +++ include/version_spec.h | 21 ++ src/CMakeLists.txt | 2 +- src/str.c | 592 +++++++++++++++++++++++++++++++++++++++++++++++++ src/strings.c | 591 ------------------------------------------------ 21 files changed, 943 insertions(+), 874 deletions(-) create mode 100644 include/archive.h create mode 100644 include/checksum.h create mode 100644 include/conf.h create mode 100644 include/deps.h create mode 100644 include/environment.h create mode 100644 include/find.h create mode 100644 include/fs.h create mode 100644 include/install.h create mode 100644 include/internal_cmd.h create mode 100644 include/manifest.h create mode 100644 include/mime.h create mode 100644 include/mirrors.h create mode 100644 include/relocation.h create mode 100644 include/rpath.h create mode 100644 include/shell.h create mode 100644 include/str.h create mode 100644 include/version_spec.h create mode 100644 src/str.c delete mode 100644 src/strings.c diff --git a/include/archive.h b/include/archive.h new file mode 100644 index 0000000..264a0d9 --- /dev/null +++ b/include/archive.h @@ -0,0 +1,7 @@ +#ifndef SPM_ARCHIVE_H +#define SPM_ARCHIVE_H + +int tar_extract_archive(const char *_archive, const char *_destination); +int tar_extract_file(const char *archive, const char* filename, const char *destination); + +#endif //SPM_ARCHIVE_H diff --git a/include/checksum.h b/include/checksum.h new file mode 100644 index 0000000..001192f --- /dev/null +++ b/include/checksum.h @@ -0,0 +1,7 @@ +#ifndef SPM_CHECKSUM_H +#define SPM_CHECKSUM_H + +char *md5sum(const char *filename); +char *sha256sum(const char *filename); + +#endif //SPM_CHECKSUM_H diff --git a/include/conf.h b/include/conf.h new file mode 100644 index 0000000..e1ab93a --- /dev/null +++ b/include/conf.h @@ -0,0 +1,54 @@ +#ifndef SPM_CONF_H +#define SPM_CONF_H + +#define CONFIG_BUFFER_SIZE 1024 +#define PKG_DIR SPM_GLOBAL.package_dir +#define TMP_DIR SPM_GLOBAL.tmp_dir + +typedef struct { + char *key; + char *value; + size_t key_length; + size_t value_length; +} ConfigItem; + +typedef struct { + char *binpath; + char *includepath; + char *libpath; + char *datapath; + char *manpath; +} SPM_Hierarchy; + +typedef struct { + char *package_dir; + char *tmp_dir; + char *package_manifest; + char *mirror_config; + char **mirror_list; + char *repo_target; + char *user_config_basedir; + char *user_config_file; + int verbose; + ConfigItem **config; + struct utsname sysinfo; + SPM_Hierarchy fs; +} spm_vars; + +ConfigItem **config_read(const char *filename); +ConfigItem *config_get(ConfigItem **item, const char *key); +void config_free(ConfigItem **item); +void config_test(void); + +char *get_user_conf_dir(void); +char *get_user_config_file(void); +char *get_user_tmp_dir(void); +char *get_user_package_dir(void); +char *get_package_manifest(void); + +void init_config_global(void); +void free_global_config(void); +void show_global_config(void); +void check_runtime_environment(void); + +#endif //SPM_CONF_H diff --git a/include/deps.h b/include/deps.h new file mode 100644 index 0000000..b9a07fd --- /dev/null +++ b/include/deps.h @@ -0,0 +1,20 @@ +#ifndef SPM_DEPS_H +#define SPM_DEPS_H + +typedef struct { + size_t __size; // Count of allocated records + size_t records; // Count of usable records + char **list; // Array of dependencies +} Dependencies; + +// deps.c +int exists(const char *filename); +int dep_seen(Dependencies **deps, const char *name); +int dep_init(Dependencies **deps); +void dep_free(Dependencies **deps); +int dep_append(Dependencies **deps, char *name); +int dep_solve(Dependencies **deps, const char *filename); +int dep_all(Dependencies **deps, const char *_package); +void dep_show(Dependencies **deps); + +#endif //SPM_DEPS_H diff --git a/include/environment.h b/include/environment.h new file mode 100644 index 0000000..79e5013 --- /dev/null +++ b/include/environment.h @@ -0,0 +1,15 @@ +#ifndef SPM_ENVIRONMENT_H +#define SPM_ENVIRONMENT_H + +typedef StrList RuntimeEnv; + +ssize_t runtime_contains(RuntimeEnv *env, const char *key); +RuntimeEnv *runtime_copy(char **env); +char *runtime_get(RuntimeEnv *env, const char *key); +void runtime_set(RuntimeEnv *env, const char *_key, const char *_value); +char *runtime_expand_var(RuntimeEnv *env, const char *input); +void runtime_export(RuntimeEnv *env, char **keys); +void runtime_apply(RuntimeEnv *env); +void runtime_free(RuntimeEnv *env); + +#endif //SPM_ENVIRONMENT_H diff --git a/include/find.h b/include/find.h new file mode 100644 index 0000000..6a3b2f5 --- /dev/null +++ b/include/find.h @@ -0,0 +1,9 @@ +#ifndef SPM_FIND_H +#define SPM_FIND_H + +char *find_executable(const char *program); +char *find_file(const char *root, const char *filename); +char *find_package(const char *filename); +int errglob(const char *epath, int eerrno); + +#endif //SPM_FIND_H diff --git a/include/fs.h b/include/fs.h new file mode 100644 index 0000000..7987e33 --- /dev/null +++ b/include/fs.h @@ -0,0 +1,29 @@ +#ifndef SPM_FSTREE_H +#define SPM_FSTREE_H + +#define SPM_FSTREE_FLT_NONE 1 << 0 +#define SPM_FSTREE_FLT_CONTAINS 1 << 1 +#define SPM_FSTREE_FLT_ENDSWITH 1 << 2 +#define SPM_FSTREE_FLT_STARTSWITH 1 << 3 + +typedef struct { + char *root; + char **dirs; + size_t dirs_length; + char **files; + size_t files_length; +} FSTree; + +int _fstree_compare(const FTSENT **a, const FTSENT **b); +void fstree_free(FSTree *fsdata); +FSTree *fstree(const char *_path, char **filter_by, unsigned int filter_mode); +int rmdirs(const char *_path); +long int get_file_size(const char *filename); +int mkdirs(const char *_path, mode_t mode); +char *dirname(const char *_path); +char *basename(char *path); +int rsync(const char *_args, const char *_source, const char *_destination); +char *human_readable_size(uint64_t n); + + +#endif //SPM_FSTREE_H diff --git a/include/install.h b/include/install.h new file mode 100644 index 0000000..3ecba04 --- /dev/null +++ b/include/install.h @@ -0,0 +1,7 @@ +#ifndef SPM_INSTALL_H +#define SPM_INSTALL_H + +int metadata_remove(const char *_path); +int install(const char *destroot, const char *_package); + +#endif //SPM_INSTALL_H diff --git a/include/internal_cmd.h b/include/internal_cmd.h new file mode 100644 index 0000000..d01a63c --- /dev/null +++ b/include/internal_cmd.h @@ -0,0 +1,7 @@ +#ifndef SPM_INTERNAL_CMD_H +#define SPM_INTERNAL_CMD_H + +// internal_cmd.c +int internal_cmd(int argc, char **argv); + +#endif //SPM_INTERNAL_CMD_H diff --git a/include/manifest.h b/include/manifest.h new file mode 100644 index 0000000..c32ed20 --- /dev/null +++ b/include/manifest.h @@ -0,0 +1,48 @@ +#ifndef SPM_MANIFEST_H +#define SPM_MANIFEST_H + +#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2) + 1 + +#define PACKAGE_MEMBER_SIZE 0xff +#define PACKAGE_MEMBER_ORIGIN_SIZE PATH_MAX +#define PACKAGE_MEMBER_SEPARATOR '-' +#define PACKAGE_MEMBER_SEPARATOR_PLACEHOLD '*' + +#define SPM_MANIFEST_SEPARATOR '|' +#define SPM_MANIFEST_SEPARATOR_MAX 7 +#define SPM_MANIFEST_NODATA "*" +#define SPM_MANIFEST_HEADER "# SPM PACKAGE MANIFEST" +#define SPM_MANIFEST_FILENAME "manifest.dat" +#define SPM_PACKAGE_EXTENSION ".tar.gz" + +typedef struct { + char **requirements; + size_t requirements_records; + size_t size; + char archive[PACKAGE_MEMBER_SIZE]; + char name[PACKAGE_MEMBER_SIZE]; + char version[PACKAGE_MEMBER_SIZE]; + char revision[PACKAGE_MEMBER_SIZE]; + char checksum_sha256[SHA256_DIGEST_STRING_LENGTH]; + char origin[PACKAGE_MEMBER_ORIGIN_SIZE]; +} ManifestPackage; + +typedef struct { + size_t records; + ManifestPackage **packages; + char origin[PACKAGE_MEMBER_ORIGIN_SIZE]; +} Manifest; + +int fetch(const char *url, const char *dest); +void manifest_package_separator_swap(char **name); +void manifest_package_separator_restore(char **name); +Manifest *manifest_from(const char *package_dir); +Manifest *manifest_read(char *file_or_url); +int manifest_write(Manifest *info, const char *dest); +void manifest_free(Manifest *info); +void manifest_package_free(ManifestPackage *info); +ManifestPackage *manifest_search(Manifest *info, const char *package); +ManifestPackage *find_by_strspec(Manifest *manifest, const char *_strspec); +ManifestPackage *manifest_package_copy(ManifestPackage *manifest); + +#endif //SPM_MANIFEST_H diff --git a/include/mime.h b/include/mime.h new file mode 100644 index 0000000..d05be77 --- /dev/null +++ b/include/mime.h @@ -0,0 +1,18 @@ +#ifndef SPM_MIME_H +#define SPM_MIME_H + +typedef struct { + char *origin; + char *type; + char *charset; +} Mime; + +Process *file_command(const char *_filename); +Mime *file_mimetype(const char *filename); +void mime_free(Mime *m); +int build(int bargc, char **bargv); +int file_is_binary(const char *filename); +int file_is_text(const char *filename); +int file_is_binexec(const char *filename); + +#endif //SPM_MIME_H diff --git a/include/mirrors.h b/include/mirrors.h new file mode 100644 index 0000000..1be39f6 --- /dev/null +++ b/include/mirrors.h @@ -0,0 +1,12 @@ +#ifndef SPM_MIRRORS_H +#define SPM_MIRRORS_H + +#define SPM_MIRROR_MAX 0xff +#define SPM_MIRROR_FILENAME "mirrorlist" + +char **file_readlines(const char *filename); +char **mirror_list(const char *filename); +void mirror_list_free(char **m); +void mirror_clone(Manifest *info, char *dest); + +#endif //SPM_MIRRORS_H diff --git a/include/relocation.h b/include/relocation.h new file mode 100644 index 0000000..128b2bd --- /dev/null +++ b/include/relocation.h @@ -0,0 +1,21 @@ +#ifndef SPM_RELOCATION_H +#define SPM_RELOCATION_H + +#define PREFIX_WRITE_BIN 0 +#define PREFIX_WRITE_TEXT 1 + +typedef struct { + char *prefix; + char *path; +} RelocationEntry; + +int relocate(const char *filename, const char *_oldstr, const char *_newstr); +void relocate_root(const char *destroot, const char *baseroot); +int replace_text(char *data, const char *_spattern, const char *_sreplacement); +int file_replace_text(char *filename, const char *spattern, const char *sreplacement); +RelocationEntry **prefixes_read(const char *filename); +void prefixes_free(RelocationEntry **entry); +int prefixes_write(const char *output_file, int mode, char **prefix, const char *tree); + + +#endif //SPM_RELOCATION_H diff --git a/include/rpath.h b/include/rpath.h new file mode 100644 index 0000000..742d969 --- /dev/null +++ b/include/rpath.h @@ -0,0 +1,12 @@ +#ifndef SPM_RPATH_H +#define SPM_RPATH_H + +Process *patchelf(const char *_filename, const char *_args); +char *rpath_autodetect(const char *filename); +int has_rpath(const char *_filename); +char *rpath_get(const char *_filename); +char *rpath_generate(const char *_filename); +int rpath_autoset(const char *filename); +int rpath_set(const char *filename, const char *rpath); + +#endif //SPM_RPATH_H diff --git a/include/shell.h b/include/shell.h new file mode 100644 index 0000000..2d115f9 --- /dev/null +++ b/include/shell.h @@ -0,0 +1,18 @@ +#ifndef SPM_SHELL_H +#define SPM_SHELL_H + +#define SHELL_DEFAULT 1 << 0 +#define SHELL_OUTPUT 1 << 1 +#define SHELL_BENCHMARK 1 << 2 + +typedef struct { + struct timespec start_time, stop_time; + double time_elapsed; + int returncode; + char *output; +} Process; + +void shell(Process **proc_info, u_int64_t option, const char *fmt, ...); +void shell_free(Process *proc_info); + +#endif //SPM_SHELL_H diff --git a/include/spm.h b/include/spm.h index d62cb42..b743c7a 100644 --- a/include/spm.h +++ b/include/spm.h @@ -27,12 +27,29 @@ #include #endif +#include "str.h" #include "strlist.h" #include "shlib.h" #include "config.h" +#include "internal_cmd.h" +#include "environment.h" +#include "manifest.h" +#include "fs.h" +#include "version_spec.h" +#include "checksum.h" +#include "deps.h" +#include "shell.h" +#include "relocation.h" +#include "conf.h" +#include "archive.h" +#include "find.h" +#include "rpath.h" +#include "mime.h" +#include "mirrors.h" +#include "install.h" -// spm.c #define SYSERROR stderr, "%s:%s:%d: %s\n", basename(__FILE__), __FUNCTION__, __LINE__, strerror(errno) + #define DIRSEP_WIN32 '\\' #define DIRSEPS_WIN32 "\\" #define PATHSEP_WIN32 ';' @@ -68,288 +85,7 @@ #define SPM_META_PREFIX_TEXT ".SPM_PREFIX_TEXT" #define SPM_META_MANIFEST ".SPM_MANIFEST" // TODO: Implement -#define SPM_MANIFEST_SEPARATOR '|' -#define SPM_MANIFEST_SEPARATOR_MAX 7 -#define SPM_MANIFEST_NODATA "*" -#define SPM_MANIFEST_HEADER "# SPM PACKAGE MANIFEST" -#define SPM_MANIFEST_FILENAME "manifest.dat" - -#define SPM_FSTREE_FLT_NONE 1 << 0 -#define SPM_FSTREE_FLT_CONTAINS 1 << 1 -#define SPM_FSTREE_FLT_ENDSWITH 1 << 2 -#define SPM_FSTREE_FLT_STARTSWITH 1 << 3 - -#define PREFIX_WRITE_BIN 0 -#define PREFIX_WRITE_TEXT 1 - -#define SPM_PACKAGE_EXTENSION ".tar.gz" -#define PKG_DIR SPM_GLOBAL.package_dir -#define TMP_DIR SPM_GLOBAL.tmp_dir - -#define SHELL_DEFAULT 1 << 0 -#define SHELL_OUTPUT 1 << 1 -#define SHELL_BENCHMARK 1 << 2 - -#define PACKAGE_MEMBER_SIZE 0xff -#define PACKAGE_MEMBER_ORIGIN_SIZE PATH_MAX -#define PACKAGE_MEMBER_SEPARATOR '-' -#define PACKAGE_MEMBER_SEPARATOR_PLACEHOLD '*' - -#define VERSION_OPERATORS " ~!=<>" -#define VERSION_NOOP 1 << 0 -#define VERSION_EQ 1 << 1 -#define VERSION_NE 1 << 2 -#define VERSION_GT 1 << 3 -#define VERSION_LT 1 << 4 -#define VERSION_COMPAT 1 << 5 - -#define SPM_MIRROR_MAX 0xff -#define SPM_MIRROR_FILENAME "mirrorlist" - -#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2) + 1 - -typedef struct { - char **requirements; - size_t requirements_records; - size_t size; - char archive[PACKAGE_MEMBER_SIZE]; - char name[PACKAGE_MEMBER_SIZE]; - char version[PACKAGE_MEMBER_SIZE]; - char revision[PACKAGE_MEMBER_SIZE]; - char checksum_sha256[SHA256_DIGEST_STRING_LENGTH]; - char origin[PACKAGE_MEMBER_ORIGIN_SIZE]; -} ManifestPackage; - -typedef struct { - size_t records; - ManifestPackage **packages; - char origin[PACKAGE_MEMBER_ORIGIN_SIZE]; -} Manifest; - -typedef struct { - char *root; - char **dirs; - size_t dirs_length; - char **files; - size_t files_length; -} FSTree; - -typedef struct { - size_t __size; // Count of allocated records - size_t records; // Count of usable records - char **list; // Array of dependencies -} Dependencies; - -typedef struct { - char *key; - char *value; - size_t key_length; - size_t value_length; -} ConfigItem; - - -typedef struct { - char *binpath; - char *includepath; - char *libpath; - char *datapath; - char *manpath; -} SPM_Hierarchy; - -typedef struct { - char *package_dir; - char *tmp_dir; - char *package_manifest; - char *mirror_config; - char **mirror_list; - char *repo_target; - char *user_config_basedir; - char *user_config_file; - int verbose; - ConfigItem **config; - struct utsname sysinfo; - SPM_Hierarchy fs; -} spm_vars; - -typedef struct { - struct timespec start_time, stop_time; - double time_elapsed; - int returncode; - char *output; -} Process; - -typedef struct { - char *prefix; - char *path; -} RelocationEntry; - -typedef struct { - char *origin; - char *type; - char *charset; -} Mime; - -typedef StrList RuntimeEnv; -//typedef StrList Dependencies; - // GLOBALS spm_vars SPM_GLOBAL; -// shell.c -void shell(Process **proc_info, u_int64_t option, const char *fmt, ...); -void shell_free(Process *proc_info); - -// archive.c -int tar_extract_archive(const char *_archive, const char *_destination); -int tar_extract_file(const char *archive, const char* filename, const char *destination); - -// relocation.c -int relocate(const char *filename, const char *_oldstr, const char *_newstr); -void relocate_root(const char *destroot, const char *baseroot); -int replace_text(char *data, const char *_spattern, const char *_sreplacement); -int file_replace_text(char *filename, const char *spattern, const char *sreplacement); -RelocationEntry **prefixes_read(const char *filename); -void prefixes_free(RelocationEntry **entry); -int prefixes_write(const char *output_file, int mode, char **prefix, const char *tree); - -// strings.c -int num_chars(const char *sptr, int ch); -int startswith(const char *sptr, const char *pattern); -int endswith(const char *sptr, const char *pattern); -char *normpath(const char *path); -void strchrdel(char *sptr, const char *chars); -long int strchroff(const char *sptr, int ch); -void strdelsuffix(char *sptr, const char *suffix); -char** split(char *sptr, const char* delim); -void split_free(char **ptr); -char *join(char **arr, const char *separator); -char *substring_between(char *sptr, const char *delims); -void strsort(char **arr); -int find_in_file(const char *filename, const char *pattern); -int isrelational(char ch); -void print_banner(const char *s, int len); -char *strstr_array(char **arr, const char *str); -char **strdeldup(char **arr); -char *lstrip(char *sptr); -char *strip(char *sptr); -int isempty(char *sptr); -int isquoted(char *sptr); -char *normalize_space(char *s); - -// find.c -char *find_executable(const char *program); -char *find_file(const char *root, const char *filename); -char *find_package(const char *filename); -int errglob(const char *epath, int eerrno); - -// rpath.c -Process *patchelf(const char *_filename, const char *_args); -char *rpath_autodetect(const char *filename); -int has_rpath(const char *_filename); -char *rpath_get(const char *_filename); -char *rpath_generate(const char *_filename); -int rpath_autoset(const char *filename); -int rpath_set(const char *filename, const char *rpath); - -// fs.c -int _fstree_compare(const FTSENT **a, const FTSENT **b); -void fstree_free(FSTree *fsdata); -FSTree *fstree(const char *_path, char **filter_by, unsigned int filter_mode); -int rmdirs(const char *_path); -long int get_file_size(const char *filename); -int mkdirs(const char *_path, mode_t mode); -char *dirname(const char *_path); -char *basename(char *path); -int rsync(const char *_args, const char *_source, const char *_destination); -char *human_readable_size(uint64_t n); - -// config_global.c -char *get_user_conf_dir(void); -char *get_user_config_file(void); -char *get_user_tmp_dir(void); -char *get_user_package_dir(void); -char *get_package_manifest(void); - -void init_config_global(void); -void free_global_config(void); -void show_global_config(void); -void check_runtime_environment(void); - -// install.c -int metadata_remove(const char *_path); -int install(const char *destroot, const char *_package); - -// config.c -#define CONFIG_BUFFER_SIZE 1024 - -ConfigItem **config_read(const char *filename); -ConfigItem *config_get(ConfigItem **item, const char *key); -void config_free(ConfigItem **item); -void config_test(void); - -// deps.c -int exists(const char *filename); -int dep_seen(Dependencies **deps, const char *name); -int dep_init(Dependencies **deps); -void dep_free(Dependencies **deps); -int dep_append(Dependencies **deps, char *name); -int dep_solve(Dependencies **deps, const char *filename); -int dep_all(Dependencies **deps, const char *_package); -void dep_show(Dependencies **deps); - -// manifest.c -int fetch(const char *url, const char *dest); -void manifest_package_separator_swap(char **name); -void manifest_package_separator_restore(char **name); -Manifest *manifest_from(const char *package_dir); -Manifest *manifest_read(char *file_or_url); -int manifest_write(Manifest *info, const char *dest); -void manifest_free(Manifest *info); -void manifest_package_free(ManifestPackage *info); -ManifestPackage *manifest_search(Manifest *info, const char *package); -ManifestPackage *find_by_strspec(Manifest *manifest, const char *_strspec); -ManifestPackage *manifest_package_copy(ManifestPackage *manifest); - - -// checksum.c -char *md5sum(const char *filename); -char *sha256sum(const char *filename); - -// version_spec.c -char *version_suffix_get_alpha(char *str); -char *version_suffix_get_modifier(char *str); -int64_t version_suffix_modifier_calc(char *str); -int version_suffix_alpha_calc(char *str); -int64_t version_from(const char *version_str); -int version_spec_from(const char *op); -ManifestPackage **find_by_spec(Manifest *manifest, const char *name, const char *op, const char *version_str); - -// build.c -Process *file_command(const char *_filename); -Mime *file_mimetype(const char *filename); -void mime_free(Mime *m); -int build(int bargc, char **bargv); -int file_is_binary(const char *filename); -int file_is_text(const char *filename); -int file_is_binexec(const char *filename); - -// internal_cmd.c -int internal_cmd(int argc, char **argv); - -// environment.c -ssize_t runtime_contains(RuntimeEnv *env, const char *key); -RuntimeEnv *runtime_copy(char **env); -char *runtime_get(RuntimeEnv *env, const char *key); -void runtime_set(RuntimeEnv *env, const char *_key, const char *_value); -char *runtime_expand_var(RuntimeEnv *env, const char *input); -void runtime_export(RuntimeEnv *env, char **keys); -void runtime_apply(RuntimeEnv *env); -void runtime_free(RuntimeEnv *env); - -// mirrors.c -char **file_readlines(const char *filename); -char **mirror_list(const char *filename); -void mirror_list_free(char **m); -void mirror_clone(Manifest *info, char *dest); - #endif //SPM_SPM_H diff --git a/include/str.h b/include/str.h new file mode 100644 index 0000000..2674fb9 --- /dev/null +++ b/include/str.h @@ -0,0 +1,27 @@ +#ifndef SPM_STR_H +#define SPM_STR_H + +int num_chars(const char *sptr, int ch); +int startswith(const char *sptr, const char *pattern); +int endswith(const char *sptr, const char *pattern); +char *normpath(const char *path); +void strchrdel(char *sptr, const char *chars); +long int strchroff(const char *sptr, int ch); +void strdelsuffix(char *sptr, const char *suffix); +char** split(char *sptr, const char* delim); +void split_free(char **ptr); +char *join(char **arr, const char *separator); +char *substring_between(char *sptr, const char *delims); +void strsort(char **arr); +int find_in_file(const char *filename, const char *pattern); +int isrelational(char ch); +void print_banner(const char *s, int len); +char *strstr_array(char **arr, const char *str); +char **strdeldup(char **arr); +char *lstrip(char *sptr); +char *strip(char *sptr); +int isempty(char *sptr); +int isquoted(char *sptr); +char *normalize_space(char *s); + +#endif //SPM_STR_H diff --git a/include/version_spec.h b/include/version_spec.h new file mode 100644 index 0000000..995d24a --- /dev/null +++ b/include/version_spec.h @@ -0,0 +1,21 @@ +#ifndef SPM_VERSION_SPEC_H +#define SPM_VERSION_SPEC_H + +#define VERSION_OPERATORS " ~!=<>" +#define VERSION_NOOP 1 << 0 +#define VERSION_EQ 1 << 1 +#define VERSION_NE 1 << 2 +#define VERSION_GT 1 << 3 +#define VERSION_LT 1 << 4 +#define VERSION_COMPAT 1 << 5 + +// version_spec.c +char *version_suffix_get_alpha(char *str); +char *version_suffix_get_modifier(char *str); +int64_t version_suffix_modifier_calc(char *str); +int version_suffix_alpha_calc(char *str); +int64_t version_from(const char *version_str); +int version_spec_from(const char *op); +ManifestPackage **find_by_spec(Manifest *manifest, const char *name, const char *op, const char *version_str); + +#endif //SPM_VERSION_SPEC_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index de2a01f..8a0a69a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,7 +3,7 @@ include_directories( ${CMAKE_BINARY_DIR}/include ) -add_executable(spm spm.c config.c compat.c deps.c fs.c rpath.c find.c shell.c archive.c strings.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) +add_executable(spm spm.c config.c compat.c deps.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) target_link_libraries(spm rt crypto ssl curl) if(MSVC) diff --git a/src/str.c b/src/str.c new file mode 100644 index 0000000..7519cb1 --- /dev/null +++ b/src/str.c @@ -0,0 +1,592 @@ +/** + * @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 0 = found, 1 = 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 1; + } + } + return 0; +} + +/** + * Scan for `pattern` string at the end of `sptr` + * + * @param sptr string to scan + * @param pattern string to search for + * @return 0 = found, 1 = not found, -1 = error + */ +int endswith(const char *sptr, const char *pattern) { + if (!sptr) { + return -1; + } + size_t sptr_size = strlen(sptr); + size_t pattern_size = strlen(pattern); + for (size_t s = sptr_size - pattern_size, p = 0 ; s < sptr_size; s++, p++) { + if (sptr[s] != pattern[p]) { + return 1; + } + } + return 0; +} + +/** + * 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; +} + +/** + * 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)) { + 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) - 1; + if (len < 1) { + *sptr = '\0'; + return sptr; + } + for (size_t i = len; ; i--) { + 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)) { + 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; +} + + diff --git a/src/strings.c b/src/strings.c deleted file mode 100644 index afd5be3..0000000 --- a/src/strings.c +++ /dev/null @@ -1,591 +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 0 = found, 1 = 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 1; - } - } - return 0; -} - -/** - * Scan for `pattern` string at the end of `sptr` - * - * @param sptr string to scan - * @param pattern string to search for - * @return 0 = found, 1 = not found, -1 = error - */ -int endswith(const char *sptr, const char *pattern) { - if (!sptr) { - return -1; - } - size_t sptr_size = strlen(sptr); - size_t pattern_size = strlen(pattern); - for (size_t s = sptr_size - pattern_size, p = 0 ; s < sptr_size; s++, p++) { - if (sptr[s] != pattern[p]) { - return 1; - } - } - return 0; -} - -/** - * 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; -} - -/** - * 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)) { - 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) - 1; - if (len < 1) { - *sptr = '\0'; - return sptr; - } - for (size_t i = len; ; i--) { - 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)) { - 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; -} - -- cgit