diff options
Diffstat (limited to 'src/remembering.c')
-rw-r--r-- | src/remembering.c | 312 |
1 files changed, 250 insertions, 62 deletions
diff --git a/src/remembering.c b/src/remembering.c index 215aa52..a2185b5 100644 --- a/src/remembering.c +++ b/src/remembering.c @@ -10,26 +10,46 @@ #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); } void test_ok() { fprintf(stderr, " OK.\n"); } -#endif -#ifdef FALLIBLE -#include <fallible/alloc.h> -#include <fallible/string.h> -#endif +FILE *tmpfile() { + char filename[] = "remembering-test.XXXXXX"; + + errno = 0; + int fd = mkstemp(filename); + if (fd == -1) { + perror("tmpfile mkstemp"); + return NULL; + } -static void print_usage(FILE *stream, char *given_name) { - char *name = "remembering"; - if (given_name && given_name[0]) { - name = given_name; + errno = 0; + FILE *f = fdopen(fd, "w"); + if (!f) { + perror("tmpfile fdopen"); + close(fd); + return NULL; } - fprintf(stream, "Usage: %s -p PROFILE -c 'COMMAND'\n", name); + + return f; +} +#endif + +static void print_usage(FILE *stream, char *name) { + fprintf(stream, "Usage: %s -p PROFILE -c 'COMMAND'\n", + name ? name : "remembering"); } static void print_help(FILE *stream) { @@ -49,12 +69,20 @@ static void print_missing(FILE *stream, char *text) { fprintf(stream, "Missing option: %s\n", text); } -static int get_options(int argc, char *argv[], char **command, char **profile) { +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")) { - print_usage(stdout, argv[0]); - print_help(stdout); - exit(EXIT_SUCCESS); + 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; } } @@ -70,42 +98,119 @@ static int get_options(int argc, char *argv[], char **command, char **profile) { profilearg = optarg; break; case 'h': - print_usage(stdout, argv[0]); - print_help(stdout); - exit(EXIT_SUCCESS); + print_usage(out, argv[0]); + print_help(out); + return 1; case 'V': - fprintf(stdout, "remembering-%s %s\n", VERSION, DATE); - exit(EXIT_SUCCESS); + print_version(out); + return 1; } } - if (commandarg == NULL) { - print_missing(stderr, "-c 'COMMAND'"); - print_usage(stderr, argv[0]); - return 2; + if (!commandarg) { + print_missing(err, "-c 'COMMAND'"); + print_usage(err, argv[0]); + return -1; } - if (profilearg == NULL) { - print_missing(stderr, "-p 'PROFILE'"); - print_usage(stderr, argv[0]); - return 2; + if (!profilearg) { + print_missing(err, "-p 'PROFILE'"); + print_usage(err, argv[0]); + return 1; } - *command = malloc(strlen(commandarg) + 1); + *command = strdup(commandarg); if (!*command) { - return 1; + perror("strdup"); + return -1; } - *profile = malloc(strlen(profilearg) + 1); + *profile = strdup(profilearg); if (!*profile) { + perror("strdup"); free(*command); - return 1; + return -1; } - strcpy(*command, commandarg); - strcpy(*profile, profilearg); 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)) { @@ -120,7 +225,27 @@ static int mkdir_p(char *path, mode_t mode) { return mkdir(path, mode); } -// add fallible variation? where are the tests to stress it? +#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"); + test_ok(); + } +} +#endif + static char *expand_profile_name(const char *const profile_name) { char *prefix = NULL; char *env_prefix = getenv("XDG_DATA_HOME"); @@ -155,11 +280,38 @@ static char *expand_profile_name(const char *const profile_name) { 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; @@ -334,20 +486,16 @@ int fallible_parse_ranked_line(FILE *stream, const char *entry, char **value, #endif #ifdef TEST -void parse_ranked_line_test() { +static void parse_ranked_line_test() { { testing("parse_ranked_line with an empty string"); - char *value; - int rank; - - char filename[] = "remembering-test.XXXXXX"; - int fd; - if ((fd = mkstemp(filename)) == -1) { - fprintf(stderr, "\nERR: Can't create test tempfile.\n"); + FILE *f = tmpfile(); + if (!f) { exit(EXIT_FAILURE); } - FILE *f = fdopen(fd, "w"); + char *value; + int rank; int ret = parse_ranked_line(f, "", &value, &rank); assert(ret == -1); fclose(f); @@ -355,17 +503,13 @@ void parse_ranked_line_test() { } { testing("parse_ranked_line when RANKING DELIMITER is missing"); - char *value; - int rank; - - char filename[] = "remembering-test.XXXXXX"; - int fd; - if ((fd = mkstemp(filename)) == -1) { - fprintf(stderr, "\nERR: Can't create test tempfile.\n"); + FILE *f = tmpfile(); + if (!f) { exit(EXIT_FAILURE); } - FILE *f = fdopen(fd, "w"); + char *value; + int rank; int ret = parse_ranked_line(f, "0 command", &value, &rank); assert(ret == -1); fclose(f); @@ -378,30 +522,45 @@ void parse_ranked_line_test() { 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); @@ -409,6 +568,9 @@ void parse_ranked_line_test() { 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); @@ -416,6 +578,9 @@ void parse_ranked_line_test() { 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); @@ -429,6 +594,9 @@ void parse_ranked_line_test() { 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); @@ -442,36 +610,54 @@ void parse_ranked_line_test() { 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); @@ -603,7 +789,10 @@ int merge_stdin_with_profile(char *profile_name, struct Rankings *s_rankings, */ #ifdef TEST -void unit_tests() { +static void unit_tests() { + get_options_test(); + mkdir_p_test(); + expand_profile_name_test(); rankings_test(); parse_ranked_line_test(); } @@ -612,17 +801,16 @@ void unit_tests() { int main(int argc, char *argv[]) { #ifdef TEST unit_tests(); - return 0; + return EXIT_SUCCESS; #endif int ret; - char *command; - char *profile; - if ((ret = get_options(argc, argv, &command, &profile))) { - goto err; - expand_profile_name("oijoI"); - } -err: - return 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; + } } |