aboutsummaryrefslogtreecommitdiff
path: root/src/remembering.c
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2021-02-18 00:09:59 -0300
committerEuAndreh <eu@euandre.org>2021-02-18 00:09:59 -0300
commitc3e214455825c0b6bd07688cf4f807ba38c872f7 (patch)
tree37dea090661a50efaca78d0e345fc2e43f84b735 /src/remembering.c
parentREADME.md: Add more links (diff)
downloadremembering-c3e214455825c0b6bd07688cf4f807ba38c872f7.tar.gz
remembering-c3e214455825c0b6bd07688cf4f807ba38c872f7.tar.xz
Add commented src/remembering.c code
Diffstat (limited to 'src/remembering.c')
-rw-r--r--src/remembering.c487
1 files changed, 487 insertions, 0 deletions
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 <assert.h>
+#include <libgen.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+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";
+
+
+
+*/