aboutsummaryrefslogtreecommitdiff
path: root/src/remembering-c.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/remembering-c.c')
-rw-r--r--src/remembering-c.c943
1 files changed, 943 insertions, 0 deletions
diff --git a/src/remembering-c.c b/src/remembering-c.c
new file mode 100644
index 0000000..2dda122
--- /dev/null
+++ b/src/remembering-c.c
@@ -0,0 +1,943 @@
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#endif
+
+/*
+#include <errno.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef FALLIBLE
+#include <fallible.h>
+#include <fallible/alloc.h>
+#include <fallible/string.h>
+#endif
+
+#ifdef TEST
+#include <assert.h>
+
+static void testing(const char *message) {
+ fprintf(stderr, "testing: %s...", message);
+}
+
+static void test_ok() {
+ fprintf(stderr, " OK.\n");
+}
+
+static FILE *testfile() {
+ char filename[] = "remembering-test.XXXXXX";
+
+ errno = 0;
+ int fd = mkstemp(filename);
+ if (fd == -1) {
+ perror("tmpfile mkstemp");
+ return NULL;
+ }
+
+ errno = 0;
+ FILE *f = fdopen(fd, "w");
+ if (!f) {
+ perror("tmpfile fdopen");
+ close(fd);
+ return NULL;
+ }
+
+ return f;
+}
+
+static char *testdir() {
+ char dirname[] = "remembering-test.XXXXXX";
+
+ errno = 0;
+ char *dircpy = malloc(strlen(dirname) + 1);
+ if (!dircpy) {
+ perror("malloc");
+ return NULL;
+ }
+
+ errno = 0;
+ char *dir = mkdtemp(dirname);
+ if (!dir) {
+ perror("mkdtemp");
+ free(dircpy);
+ return NULL;
+ }
+
+ strcpy(dircpy, dir);
+
+ return dircpy;
+}
+
+static void assert_file_contents(const char *const filename, const char *const contents) {
+
+}
+#endif
+
+static int print_usage(FILE *stream, char *name) {
+ errno = 0;
+ fprintf(stream, "Usage: %s -p PROFILE -c 'COMMAND'\n",
+ name ? name : "remembering");
+ if (errno) {
+ perror("fprintf");
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef TEST
+static void print_usage_test() {
+ {
+ testing("print_usage a-name");
+ FILE *f = testfile();
+ if (!f) {
+ exit(EXIT_FAILURE);
+ }
+
+ int ret = print_usage(f, "a-name");
+ fclose(f);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+
+ assert_file_contents(f, "Usage: a-name -p PROFILE -c 'COMMAND'\n");
+
+ test_ok();
+ }
+ {
+ testing("print_usage NULL");
+ FILE *f = testfile();
+ if (!f) {
+ exit(EXIT_FAILURE);
+ }
+
+ int ret = print_usage(f, NULL);
+ fclose(f);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+
+ assert_file_contents(f, "Usage: remembering -p PROFILE -c 'COMMAND'\n");
+
+ test_ok();
+ }
+}
+#endif
+
+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 remembering(1) 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 void print_version(FILE *stream) {
+ fprintf(stream, "remembering-%s %s\n", VERSION, DATE);
+}
+
+static int get_options(FILE *out, FILE *err, int argc, char *argv[],
+ char **command, char **profile) {
+ for (int i = 0; i < argc; i++) {
+ if (strcmp(argv[i], "--help") == 0) {
+ print_usage(out, argv[0]);
+ print_help(out);
+ return 1;
+ } else if (strcmp(argv[i], "--version")) {
+ print_version(out);
+ return 1;
+ }
+ }
+
+ 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(out, argv[0]);
+ print_help(out);
+ return 1;
+ case 'V':
+ print_version(out);
+ return 1;
+ }
+ }
+
+ if (!commandarg) {
+ print_missing(err, "-c 'COMMAND'");
+ print_usage(err, argv[0]);
+ return -1;
+ }
+
+ if (!profilearg) {
+ print_missing(err, "-p 'PROFILE'");
+ print_usage(err, argv[0]);
+ return 1;
+ }
+
+ *command = strdup(commandarg);
+ if (!*command) {
+ perror("strdup");
+ return -1;
+ }
+ *profile = strdup(profilearg);
+ if (!*profile) {
+ perror("strdup");
+ free(*command);
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef FALLIBLE
+static int fallible_get_options(FILE *out, FILE *err, int argc, char *argv[],
+ char **command, char **profile,
+ const char *const filename, int lineno) {
+ if (fallible_should_fail(filename, lineno)) {
+ return -1;
+ }
+ return get_options(out, err, argc, argv, command, profile);
+}
+
+#define get_options(out, err, argc, argv, command, profile) \
+ fallible_get_options(out, err, argc, argv, command, profile, __FILE__, \
+ __LINE__)
+#endif
+
+#ifdef TEST
+int arrlen(char **argv) {
+ int count = 0;
+ while (argv[count] != NULL) {
+ count++;
+ }
+ return count;
+}
+
+void get_options_test() {
+ {
+ testing("get_options when given -h, -V and --help");
+ FILE *f = tmpfile();
+ if (!f) {
+ exit(EXIT_FAILURE);
+ }
+
+ char *argvs[13][6] = {
+ {"--help", NULL},
+ {"other", "arguments before", "--help", NULL},
+ {"some", "--help", "arguments after", NULL},
+ {"--help", "all", "arguments after", NULL},
+ {"-h", NULL},
+ {"-V", NULL},
+ {"-hc", "1", NULL},
+ {"-hp", "2", NULL},
+ {"-hV", NULL},
+ {"-Vh", NULL},
+ {"-p", "p", "-h", NULL},
+ {"-p", "p", "-c", "c", "-h", NULL},
+ {NULL},
+ };
+ int rows = 0;
+ while (argvs[rows][0] != NULL) {
+ rows++;
+ }
+
+#ifdef DISABLE
+ int ret;
+ char *command = NULL;
+ char *profile = NULL;
+ for (int i = 0; i < rows; i++) {
+ char **argv = argvs[i];
+ ret = get_options(f, f, arrlen(argvs[i]), argv, &command, &profile);
+ if (ret == -1) {
+ fclose(f);
+ exit(EXIT_FAILURE);
+ }
+
+ assert(command == NULL);
+ assert(profile == NULL);
+ assert(ret == 1);
+ }
+#endif
+
+ fclose(f);
+ test_ok();
+ }
+ // { "" },
+}
+#endif
+
+static int mkdir_p(char *path, mode_t mode) {
+ struct stat s;
+ if (stat(path, &s) == 0 && S_ISDIR(s.st_mode)) {
+ return 0;
+ }
+
+ char *parent = dirname(path);
+ int ret = mkdir_p(parent, mode);
+ if (ret) {
+ return ret;
+ }
+ return mkdir(path, mode);
+}
+
+#ifdef FALLIBLE
+static int fallible_mkdir_p(char *path, mode_t mode, const char *const filename,
+ int lineno) {
+ if (fallible_should_fail(filename, lineno)) {
+ return -1;
+ }
+ return mkdir_p(path, mode);
+}
+
+#define mkdir_p(path, mode) fallible_mkdir_p(path, mode, __FILE__, __LINE__)
+#endif
+
+#ifdef TEST
+static void mkdir_p_test() {
+{
+testing("mkdir_p a single directory");
+
+char *dir = tmpdir();
+if (!dir) {
+exit(EXIT_FAILURE);
+}
+
+int ret = mkdir_p(dir, 0755);
+if (ret) {
+free(dir);
+exit(EXIT_FAILURE);
+}
+
+free(dir);
+test_ok();
+}
+{
+testing("mkdir_p nested directories");
+
+const char *const dir_suffix = "/a/b/c/d/e";
+char *dir_prefix = tmpdir();
+if (!dir_prefix) {
+exit(EXIT_FAILURE);
+}
+
+ char *dir = malloc(strlen(dir_prefix) + strlen(dir_suffix) + 1);
+ if (!dir) {
+ free(dir_prefix);
+ exit(EXIT_FAILURE);
+ }
+
+ strcpy(dir, dir_prefix);
+ strcat(dir, dir_suffix);
+
+int ret = mkdir_p(dir, 0755);
+if (ret) {
+ free(dir);
+ free(dir_prefix);
+exit(EXIT_FAILURE);
+}
+
+ free(dir);
+ free(dir_prefix);
+test_ok();
+}
+}
+#endif
+
+static char *expand_profile_name(const char *const 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);
+
+ errno = 0;
+ int ret = mkdir_p(dirname(expanded_profile), 0755);
+ if (ret) {
+ perror("mkdir_p");
+ return NULL;
+ }
+ return expanded_profile;
+}
+
+#ifdef FALLIBLE
+static char *fallible_expand_profile_name(const char *const profile_name,
+ const char *const filename,
+ int lineno) {
+ if (fallible_should_fail(filename, lineno)) {
+ return NULL;
+ }
+ return expand_profile_name(profile_name);
+}
+
+#define expand_profile_name(profile_name) \
+ fallible_expand_profile_name(profile_name, __FILE__, __LINE__)
+#endif
+
+#ifdef TEST
+static void expand_profile_name_test() {
+ {
+ testing("expand_profile_name");
+ if (0) {
+ expand_profile_name("oij");
+ }
+ test_ok();
+ }
+}
+#endif
+
+static const size_t RANKINGS_INITIAL_SIZE = 100;
+static const int RANKINGS_GROWTH_MULTIPLIER = 2;
+
+struct Rankings {
+ size_t count;
+ size_t size;
+ char **values;
+ int *ranks;
+};
+
+static struct Rankings *rankings_new() {
+ struct Rankings *r = malloc(sizeof(struct Rankings));
+ if (!r) {
+ return NULL;
+ }
+ r->count = 0;
+ r->size = RANKINGS_INITIAL_SIZE;
+ r->values = malloc(r->size * sizeof(char *));
+ if (!r->values) {
+ free(r);
+ return NULL;
+ }
+ r->ranks = malloc(r->size * sizeof(int));
+ if (!r->ranks) {
+ free(r->values);
+ free(r);
+ return NULL;
+ }
+ return r;
+}
+
+#ifdef FALLIBLE
+static struct Rankings *fallible_rankings_new(const char *const filename,
+ int lineno) {
+ if (fallible_should_fail(filename, lineno)) {
+ return NULL;
+ }
+ return rankings_new();
+}
+
+#define rankings_new() fallible_rankings_new(__FILE__, __LINE__)
+#endif
+
+static void rankings_free(struct Rankings *r) {
+ if (!r) {
+ return;
+ }
+ for (size_t i = 0; i < r->count; i++) {
+ free(r->values[i]);
+ }
+ free(r->values);
+ free(r->ranks);
+ free(r);
+}
+
+static int rankings_insert(struct Rankings *r, char *value, int rank) {
+ if (r->count == r->size) {
+ r->size *= RANKINGS_GROWTH_MULTIPLIER;
+ char **new_values = realloc(r->values, r->size * sizeof(char *));
+ if (!new_values) {
+ return -1;
+ }
+ r->values = new_values;
+ int *new_ranks = realloc(r->ranks, r->size * sizeof(int));
+ if (!new_ranks) {
+ return -1;
+ }
+ r->ranks = new_ranks;
+ }
+ r->values[r->count] = value;
+ r->ranks[r->count] = rank;
+ r->count++;
+ return 0;
+}
+
+#ifdef FALLIBLE
+static int fallible_rankings_insert(struct Rankings *r, char *value, int rank,
+ const char *const filename, int lineno) {
+ if (fallible_should_fail(filename, lineno)) {
+ return -1;
+ }
+ return rankings_insert(r, value, rank);
+}
+
+#define rankings_insert(r, value, rank) \
+ fallible_rankings_insert(r, value, rank, __FILE__, __LINE__)
+#endif
+
+#ifdef TEST
+static void rankings_test() {
+ {
+ testing("struct Rankings expands its size when needed");
+ struct Rankings *r = rankings_new();
+ if (!r) {
+ exit(EXIT_FAILURE);
+ }
+ size_t some_limit = RANKINGS_INITIAL_SIZE + 1;
+ int ret;
+ for (size_t i = 0; i < some_limit; i++) {
+ char *s = strdup("some string");
+ ret = rankings_insert(r, s, i);
+ if (ret) {
+ free(s);
+ rankings_free(r);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ assert(r->size == RANKINGS_INITIAL_SIZE * RANKINGS_GROWTH_MULTIPLIER);
+ assert(r->count == some_limit);
+
+ rankings_free(r);
+ test_ok();
+ }
+
+ {
+ testing("an empty Rankings doesn't leak");
+ struct Rankings *r = rankings_new();
+ if (!r) {
+ exit(EXIT_FAILURE);
+ }
+
+ assert(r->size == RANKINGS_INITIAL_SIZE);
+ assert(r->count == 0);
+
+ rankings_free(r);
+ test_ok();
+ }
+}
+#endif
+
+static const char RANKING_DELIMITER = ':';
+
+int parse_ranked_line(FILE *stream, const char *entry, char **value,
+ int *rank) {
+ char *value_substr = strchr(entry, RANKING_DELIMITER);
+ if (value_substr == NULL) {
+ fprintf(stream, "WARN: Missing delimiter ('%c') in line: %s\n",
+ RANKING_DELIMITER, entry);
+ return -1;
+ }
+
+ int rank_strlen = value_substr - entry;
+ char *rank_str = strndup(entry, rank_strlen);
+ if (!rank_str) {
+ return -1;
+ }
+ *rank = atoi(rank_str);
+ free(rank_str);
+
+ *value = malloc(strlen(value_substr) + 1);
+ if (!*value) {
+ return -1;
+ }
+ strcpy(*value, entry + (rank_strlen + 1 / * RANKING_DELIMITER * /));
+
+ return 0;
+}
+
+#ifdef FALLIBLE
+int fallible_parse_ranked_line(FILE *stream, const char *entry, char **value,
+ int *rank, const char *const filename,
+ int lineno) {
+ if (fallible_should_fail(filename, lineno)) {
+ return -1;
+ }
+ return parse_ranked_line(stream, entry, value, rank);
+}
+
+#define parse_ranked_line(stream, entry, value, rank) \
+ fallible_parse_ranked_line(stream, entry, value, rank, __FILE__, __LINE__)
+#endif
+
+#ifdef TEST
+static void parse_ranked_line_test() {
+ {
+ testing("parse_ranked_line with an empty string");
+ FILE *f = tmpfile();
+ if (!f) {
+ exit(EXIT_FAILURE);
+ }
+
+ char *value;
+ int rank;
+ int ret = parse_ranked_line(f, "", &value, &rank);
+ assert(ret == -1);
+ fclose(f);
+ test_ok();
+ }
+ {
+ testing("parse_ranked_line when RANKING DELIMITER is missing");
+ FILE *f = tmpfile();
+ if (!f) {
+ exit(EXIT_FAILURE);
+ }
+
+ char *value;
+ int rank, ret;
+
+ ret = parse_ranked_line(f, "0 command", &value, &rank);
+ assert(ret == -1);
+
+ ret = parse_ranked_line(f, "1", &value, &rank);
+ assert(ret == -1);
+
+ fclose(f);
+ test_ok();
+ }
+ {
+ testing("parse_ranked_line with a happy path examples");
+ char *value;
+ int rank;
+ int ret;
+
+ ret = parse_ranked_line(stderr, "0:command", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "command") == 0);
+ assert(rank == 0);
+ free(value);
+
+ ret = parse_ranked_line(stderr, "10:another command", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "another command") == 0);
+ assert(rank == 10);
+ free(value);
+
+ ret = parse_ranked_line(stderr, "123:123", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "123") == 0);
+ assert(rank == 123);
+ free(value);
+
+ ret = parse_ranked_line(stderr, "-123:command", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "command") == 0);
+ assert(rank == -123);
+ free(value);
+
+ ret = parse_ranked_line(stderr, "0:0", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "0") == 0);
+ assert(rank == 0);
+ free(value);
+
+ ret = parse_ranked_line(stderr, "0:command with : in the middle", &value,
+ &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "command with : in the middle") == 0);
+ assert(rank == 0);
+ free(value);
+
+ ret = parse_ranked_line(
+ stderr, "0:::command:with:multiple:::in:the:middle:", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "::command:with:multiple:::in:the:middle:") == 0);
+ assert(rank == 0);
+ free(value);
+
+ test_ok();
+ }
+ {
+ testing("parse_ranked_line with an empty command");
+ char *value;
+ int rank;
+
+ int ret = parse_ranked_line(stderr, "0:", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "") == 0);
+ assert(rank == 0);
+ free(value);
+
+ test_ok();
+ }
+ {
+ testing("parse_ranked_line with a bad rank numbers");
+ char *value;
+ int rank;
+ int ret;
+
+ ret = parse_ranked_line(stderr, ":command", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "command") == 0);
+ assert(rank == 0);
+ free(value);
+
+ ret = parse_ranked_line(stderr, "1 2 3:command", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "command") == 0);
+ assert(rank == 1);
+ free(value);
+
+ ret = parse_ranked_line(stderr, ".1:command", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "command") == 0);
+ assert(rank == 0);
+ free(value);
+
+ ret = parse_ranked_line(stderr, "3.14:command", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "command") == 0);
+ assert(rank == 3);
+ free(value);
+
+ ret = parse_ranked_line(stderr, ":5:command", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "5:command") == 0);
+ assert(rank == 0);
+ free(value);
+
+ ret = parse_ranked_line(stderr, "command:command", &value, &rank);
+ if (ret) {
+ exit(EXIT_FAILURE);
+ }
+ assert(ret == 0);
+ assert(strcmp(value, "command") == 0);
+ assert(rank == 0);
+ free(value);
+
+ test_ok();
+ }
+}
+#endif
+
+struct GetlineParams {
+ char *line;
+ size_t len;
+ ssize_t read;
+ FILE *stream;
+};
+
+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) {
+ char *value;
+ int rank;
+ int ret;
+ if ((ret = parse_ranked_line(stderr, p_params->line, &value, &rank)) ==
+ -1) {
+ return ret;
+ }
+ if ((ret = rankings_insert(p_rankings, value, rank)) == -1) {
+ return ret;
+ }
+ }
+ 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 *s_value_copy = strdup(s_params->line);
+ if (!s_value_copy) {
+ return -1;
+ }
+ char *p_value_copy = strdup(s_params->line);
+ if (!p_value_copy) {
+ free(s_value_copy);
+ return -1;
+ }
+
+ int ret;
+ if ((ret = rankings_insert(s_rankings, s_value_copy, 0)) == -1) {
+ free(s_value_copy);
+ free(p_value_copy);
+ return -1;
+ }
+
+ // profile grows with stdin entries
+ if ((ret = rankings_insert(p_rankings, p_value_copy, 0)) == -1) {
+ free(s_value_copy);
+ free(p_value_copy);
+ return -1;
+ }
+ }
+ 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) {
+ char *value;
+ int rank;
+ parse_ranked_line(stderr, p_params.line, &value, &rank);
+ int cmp = strcmp(s_params.line, value);
+
+ if (cmp == 0) {
+ rankings_insert(s_rankings, value, rank);
+ rankings_insert(p_rankings, value, rank); // double free
+ stop = get_stdin(&s_params, &p_params, p_rankings) ||
+ get_profile(&s_params, &p_params, s_rankings, p_rankings);
+ 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);
+
+ if (stop == -1) {
+ return stop;
+ }
+ return 0;
+}
+
+#ifdef TEST
+static void unit_tests() {
+ print_usage_test();
+ get_options_test();
+ mkdir_p_test();
+ expand_profile_name_test();
+ rankings_test();
+ parse_ranked_line_test();
+}
+#endif
+
+int main(int argc, char *argv[]) {
+#ifdef TEST
+ unit_tests();
+ return EXIT_SUCCESS;
+#endif
+
+ int ret;
+
+ char *command, *profile;
+ ret = get_options(stdout, stderr, argc, argv, &command, &profile);
+ if (ret == -1) {
+ return EXIT_FAILURE;
+ } else if (ret) {
+ return EXIT_SUCCESS;
+ }
+}
+*/
+
+int main() {
+ return 0;
+}