diff options
-rw-r--r-- | src/remembering.c | 1419 |
1 files changed, 735 insertions, 684 deletions
diff --git a/src/remembering.c b/src/remembering.c index ad69c30..17993ee 100644 --- a/src/remembering.c +++ b/src/remembering.c @@ -20,242 +20,292 @@ #include <assert.h> static void testing(const char *message) { - fprintf(stderr, "testing: %s...", message); + fprintf(stderr, "testing: %s...", message); } -static void test_ok() { fprintf(stderr, " OK.\n"); } +static void test_ok() { + fprintf(stderr, " OK.\n"); +} -/* static */ FILE *tmpfile() { - char filename[] = "remembering-test.XXXXXX"; +static FILE *testfile() { + char filename[] = "remembering-test.XXXXXX"; - errno = 0; - int fd = mkstemp(filename); - if (fd == -1) { - perror("tmpfile mkstemp"); - return NULL; - } + 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; - } + errno = 0; + FILE *f = fdopen(fd, "w"); + if (!f) { + perror("tmpfile fdopen"); + close(fd); + return NULL; + } - return f; + return f; } -// static -char *tmpdir() { - char dirname[] = "remembering-test.XXXXXX"; +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 *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; + } - errno = 0; - char *dir = mkdtemp(dirname); - if (!dir) { - perror("mkdtemp"); - free(dircpy); - return NULL; - } + strcpy(dircpy, dir); + + return dircpy; +} - strcpy(dircpy, dir); +static void assert_file_contents(const char *const filename, const char *const contents) { - return dircpy; } #endif -static void print_usage(FILE *stream, char *name) { - fprintf(stream, "Usage: %s -p PROFILE -c 'COMMAND'\n", - name ? name : "remembering"); +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); + 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); + fprintf(stream, "Missing option: %s\n", text); } static void print_version(FILE *stream) { - fprintf(stream, "remembering-%s %s\n", VERSION, DATE); + 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; + 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); + 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__) +#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; + 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++; - } + { + 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); - } + 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(); - } - // { "" }, + 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; - } + 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); + 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); + 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__) @@ -263,7 +313,7 @@ static int fallible_mkdir_p(char *path, mode_t mode, const char *const filename, #ifdef TEST static void mkdir_p_test() { - /* + /* { testing("mkdir_p a single directory"); @@ -281,8 +331,8 @@ exit(EXIT_FAILURE); free(dir); test_ok(); } - */ - /* + */ + /* { testing("mkdir_p nested directories"); @@ -292,93 +342,93 @@ if (!dir_prefix) { exit(EXIT_FAILURE); } - char *dir = malloc(strlen(dir_prefix) + strlen(dir_suffix) + 1); - if (!dir) { - free(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); + strcpy(dir, dir_prefix); + strcat(dir, dir_suffix); int ret = mkdir_p(dir, 0755); if (ret) { - free(dir); - free(dir_prefix); + free(dir); + free(dir_prefix); exit(EXIT_FAILURE); } - free(dir); - free(dir_prefix); + 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; + 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); + 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__) +#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(); - } + { + testing("expand_profile_name"); + if (0) { + expand_profile_name("oij"); + } + test_ok(); + } } #endif @@ -386,507 +436,508 @@ 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; + 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; + 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(); + 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); + 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; + 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); + 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__) +#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(); - } + { + 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; + 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); + 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__) +#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(); - } + { + 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; + 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; + 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; + 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; + 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() { - get_options_test(); - mkdir_p_test(); - expand_profile_name_test(); - rankings_test(); - parse_ranked_line_test(); + 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; + unit_tests(); + return EXIT_SUCCESS; #endif - int ret; + 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; - } + char *command, *profile; + ret = get_options(stdout, stderr, argc, argv, &command, &profile); + if (ret == -1) { + return EXIT_FAILURE; + } else if (ret) { + return EXIT_SUCCESS; + } } |