From c3e214455825c0b6bd07688cf4f807ba38c872f7 Mon Sep 17 00:00:00 2001 From: EuAndreh Date: Thu, 18 Feb 2021 00:09:59 -0300 Subject: Add commented src/remembering.c code --- src/remembering.c | 487 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 487 insertions(+) create mode 100644 src/remembering.c (limited to 'src') diff --git a/src/remembering.c b/src/remembering.c new file mode 100644 index 0000000..22784d9 --- /dev/null +++ b/src/remembering.c @@ -0,0 +1,487 @@ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif + +/* +#include "vendor/tuple.h" +#include "vendor/vector.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static void print_usage(FILE *stream, char *given_name) { + char *name = "remembering"; + if (given_name && given_name[0]) { + name = given_name; + } + fprintf(stream, "Usage: %s -p PROFILE -c 'COMMAND'\n", name); +} + +static void print_help(FILE *stream) { + const char *help_text = + "\n" + "Options:\n" + " -p PROFILE profile to be used for gathering and storing data\n" + " -c 'COMMAND' command to be run, reading from STDIN, writing to " + "STDOUT\n" + " -h show this help\n" + " -V print program version\n" + "See manpages for more information.\n"; + fprintf(stream, "%s", help_text); +} + +static void print_missing(FILE *stream, char *text) { + fprintf(stream, "Missing option: %s\n", text); +} + +static struct Tuple *get_options(int argc, char *argv[]) { + for (int i = 0; i < argc; i++) { + if (!strcmp(argv[i], "--help")) { + print_usage(stdout, argv[0]); + print_help(stdout); + exit(0); + } + } + + char *profilearg = NULL; + char *commandarg = NULL; + int option; + while ((option = getopt(argc, argv, "c:p:hV")) != -1) { + switch (option) { + case 'c': + commandarg = optarg; + break; + case 'p': + profilearg = optarg; + break; + case 'h': + print_usage(stdout, argv[0]); + print_help(stdout); + exit(0); + case 'V': + fprintf(stdout, "remembering-%s\n", VERSION); + exit(0); + } + } + + if (commandarg == NULL) { + print_missing(stderr, "-c 'COMMAND'"); + print_usage(stderr, argv[0]); + exit(2); + } + + if (profilearg == NULL) { + print_missing(stderr, "-p 'PROFILE'"); + print_usage(stderr, argv[0]); + exit(2); + } + + struct Tuple *options = tuple_new(); + if (!options) { + return NULL; + } + + options->first = malloc(strlen(commandarg) + 1); + options->second = malloc(strlen(profilearg) + 1); + if (!options->first || !options->second) { + tuple_free(options); + return NULL; + } + strcpy(options->first, commandarg); + strcpy(options->second, profilearg); + + return options; +} + +static char *expand_profile_name(char *profile_name) { + char *prefix = NULL; + char *env_prefix = getenv("XDG_DATA_HOME"); + if (env_prefix) { + prefix = strdup(env_prefix); + if (!prefix) { + return NULL; + } + } else { + char *home = getenv("HOME"); + char *path = "/.local/share/remembering"; + prefix = malloc(strlen(home) + strlen(path) + 1); + if (!prefix) { + return NULL; + } + strcpy(prefix, home); + strcat(prefix, path); + } + char *separator = "/"; + char *expanded_profile = + malloc(strlen(prefix) + strlen(separator) + strlen(profile_name) + 1); + if (!expanded_profile) { + free(prefix); + return NULL; + } + strcpy(expanded_profile, prefix); + strcat(expanded_profile, separator); + strcat(expanded_profile, profile_name); + + free(prefix); + + return expanded_profile; +} + +#ifdef TEST +static char *rand_str(size_t size) { + static const char CHARSET[] = "0123456789"; + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + static const int CEILING = sizeof CHARSET - 1; + static const int DEFAULT_LENGTH = 24; + + size = size == 0 ? DEFAULT_LENGTH : size; + + char *str = malloc(size + 1); + assert(str); + + size_t cur = size; + while (cur--) { + str[cur] = CHARSET[rand() % CEILING]; + } + str[size] = '\0'; + return str; +} + +const char *set_test_home(const char *new_home) { + size_t size = _PC_PATH_MAX == -1 ? 4096 : _PC_PATH_MAX; + char *buf = malloc(size + 1); + assert(buf); + char *cwd = getcwd(buf, size); + assert(buf); + const char *suffix = "/tests/test-profiles/"; + char *home = malloc(strlen(cwd) + strlen(suffix) + 1); + assert(home); + strcpy(home, cwd); + strcat(home, suffix); + // free(buf); + // free(cwd); +} + +static void test_expand_profile_name() { + { + testing("expanding profile name without setting $XDG_DATA_HOME"); + char *r1 = rand_str(0); + printf("\n\nrand_str() is: %s----\n", r1); + char *r2 = rand_str(10); + printf("\n\nrand_str() is: %s----\n", r2); + free(r1); + free(r2); + // exit(1); + // const char *new_home = random_string("test-expand-profile-name-"); + // const char *previous_home = set_test_home(new_home); + + // int ret = setenv("HOME", previous_home, 1); + // assert(ret == 0); + test_ok(); + } +} +#endif + +static void mkdir_p(char *dir, mode_t mode) { mkdir(dir, mode); } + +#ifdef TEST +static void test_mkdir_p() { + { + testing("xablau"); + test_ok(); + } +} +#endif + +static void ensure_profile(char *profile) { + char *prefix = dirname(profile); + struct stat sb; + if (!(stat(prefix, &sb) == 0 && S_ISDIR(sb.st_mode))) { + mkdir_p(prefix, 0755); + } + // FILE *f = fopen(profile, "w+"); + // fclose(f); +} + +#ifdef TEST +static void test_ensure_profile() { + { + testing("xablau"); + test_ok(); + } +} +#endif + +static const char RANKING_DELIMITER = ':'; + +struct Rankings { + struct Vector *values; + struct Vector *ranks; +}; + +static struct Rankings *rankings_new() { + struct Rankings *r = malloc(sizeof(struct Rankings)); + if (!r) { + return NULL; + } + r->values = vector_new(); + if (!r->values) { + free(r); + return NULL; + } + r->ranks = vector_new(); + if (!r->ranks) { + vector_free(r->values); + free(r); + return NULL; + } + return r; +} + +static void rankings_free(struct Rankings *r) { + vector_free(r->values); + vector_free(r->ranks); + free(r); +} + +static bool rankings_insert(struct Rankings *r, char *value, int *rank) { + return vector_push_back(r->values, value) && vector_push_back(r->ranks, rank); +} + +#ifdef TEST +static void test_rankings() { + { + testing("struct Rankings grows values and ranks at the same rate"); + struct Rankings *r = rankings_new(); + int some_limit = 123; + for (int i = 0; i < some_limit; i++) { + char *s = strdup("some text"); + + int *p = malloc(sizeof(int)); + assert(p); + *p = i; + + rankings_insert(r, s, p); + } + + assert(r->values->count == r->ranks->count); + assert(r->values->size == r->ranks->size); + + rankings_free(r); + test_ok(); + } + + { + testing("an empty Rankings doesn't leak"); + struct Rankings *r = rankings_new(); + rankings_free(r); + test_ok(); + } +} +#endif + +/* + +struct GetlineParams { + char *line; + size_t len; + ssize_t read; + FILE *stream; +}; + +int parse_ranked_line(char *entry, struct Tuple *t) { + char *value_substr = strchr(entry, RANKING_DELIMITER); + if (value_substr == NULL) { + fprintf(stderr, "Missing delimiter ('%c') in line:\n%s\n", + RANKING_DELIMITER, entry); + return 1; + } + + int ranking_chars_count = value_substr - entry; + char *ranking_chars = malloc(ranking_chars_count + 1); + strncpy(ranking_chars, entry, ranking_chars_count); + int *ranking = malloc(sizeof(int)); // how to handle fail to parse? + *ranking = atoi(ranking_chars); + free(ranking_chars); + + value_substr++; // drop included delimiter + char *value_copy = malloc(strlen(value_substr) + 1); + strcpy(value_copy, entry); + + t->first = value_copy; + t->second = ranking; + + return 0; +} + +int get_stdin(struct GetlineParams *s_params, struct GetlineParams *p_params, + struct Rankings *p_rankings) { + s_params->read = getline(&s_params->line, &s_params->len, s_params->stream); + if (s_params->read == -1) { + while ((p_params->read = getline(&p_params->line, &p_params->len, + p_params->stream)) != -1) { + struct Tuple t; + parse_ranked_line(p_params->line, &t); + rankings_insert(p_rankings, t.first, t.second); + } + return 1; + } + return 0; +} + +int get_profile(struct GetlineParams *s_params, struct GetlineParams *p_params, + struct Rankings *s_rankings, struct Rankings *p_rankings) { + p_params->read = getline(&p_params->line, &p_params->len, p_params->stream); + if (p_params->read == -1) { + while ((s_params->read = getline(&s_params->line, &s_params->len, + s_params->stream)) != -1) { + char *value_copy = malloc(strlen(s_params->line) + 1); + strcpy(value_copy, s_params->line); + + int *rank = malloc(sizeof(int)); + *rank = 0; + rankings_insert(s_rankings, value_copy, rank); + rankings_insert(p_rankings, value_copy, rank); + } + return 1; + } + return 0; +} + +int merge_stdin_with_profile(char *profile_name, struct Rankings *s_rankings, + struct Rankings *p_rankings) { + FILE *profile = fopen(profile_name, "r"); + struct GetlineParams s_params = { + .line = NULL, .len = 0, .read = 0, .stream = stdin}; + struct GetlineParams p_params = { + .line = NULL, .len = 0, .read = 0, .stream = profile}; + + int stop = get_stdin(&s_params, &p_params, p_rankings) || + get_profile(&s_params, &p_params, s_rankings, p_rankings); + while (!stop) { + struct Tuple t; + parse_ranked_line(p_params.line, &t); + int cmp = strcmp(s_params.line, t.first); + + if (cmp == 0) { + rankings_insert(s_rankings, t.first, t.second); + rankings_insert(p_rankings, t.first, t.second); + stop = get_stdin(&s_params, &p_params, p_rankings) || + get_profile(&s_params, &p_params, s_rankings, p_rankings); + tuple_free(&t); + continue; + } + + if (cmp < 0) { + char *value_copy = malloc(strlen(s_params.line) + 1); + strcpy(value_copy, s_params.line); + int *rank = malloc(sizeof(int)); + *rank = 0; + rankings_insert(s_rankings, value_copy, rank); + rankings_insert(p_rankings, value_copy, rank); + stop = get_stdin(&s_params, &p_params, p_rankings); + tuple_free(&t); + continue; + } + + if (cmp > 0) { + rankings_insert(p_rankings, t.first, t.second); + stop = get_profile(&s_params, &p_params, s_rankings, p_rankings); + tuple_free(&t); + continue; + } + } + + free(s_params.line); + free(p_params.line); + fclose(profile); + return 0; +} +*/ + +/* +#ifdef TEST +void test_libeuandreh() { + time_t t; + srand((unsigned)time(&t)); +} +void unit_tests() { + test_libeuandreh(); + + test_vector(); + test_tuple(); + test_expand_profile_name(); + test_mkdir_p(); + test_ensure_profile(); + test_rankings(); +} +#endif +*/ + +int main() { return 0; } +/* +int main(int argc, char *argv[]) { +#ifdef TEST +unit_tests(); +return 0; +#endif + +struct Tuple *options = get_options(argc, argv); +if (!options) { + goto oom; +} + +char *profile = expand_profile_name(options->second); +if (!profile) { + tuple_free(options); + goto oom; +} +free(options->second); +options->second = profile; +ensure_profile(options->second); + +tuple_free(options); +return 0; +oom: +fprintf(stderr, "Unable to allocate memory, exitting.\n"); +return 1; +// exit(1); + + struct Tuple options; + struct Rankings s_rankings; + struct Rankings p_rankings; + rankings_init(&s_rankings); + rankings_init(&p_rankings); + + int ret = 0; + + get_options(&options, argc, argv); + if (process_options(&options)) { + ret = 1; + goto cleanup; + } + // char *command = options.first; + char *profile = options.second; + merge_stdin_with_profile(profile, &s_rankings, &p_rankings); + +cleanup: + options_free(&options); + rankings_free(&s_rankings); + rankings_free(&p_rankings); + return ret; +} +*/ + +/* + char *err_msg = "malloc failed: unable to allocate memory while processing " + "command-line options.\n"; + + + +*/ -- cgit v1.2.3