#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif int main() { return 0; } #ifdef DISABLE #include #include #include #include #ifdef TEST #include #endif #ifdef FALLIBLE #include #endif 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); } struct Pair { char *first; char *second; }; static int get_options(int argc, char *argv[], struct Pair *pair) { 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 %s\n", VERSION, DATE); exit(0); } } if (commandarg == NULL) { print_missing(stderr, "-c 'COMMAND'"); print_usage(stderr, argv[0]); return 2; } if (profilearg == NULL) { print_missing(stderr, "-p 'PROFILE'"); print_usage(stderr, argv[0]); return 2; } pair->first = malloc(strlen(commandarg) + 1); pair->second = malloc(strlen(profilearg) + 1); if (!pair->first || !pair->second) { return 1; } strcpy(pair->first, commandarg); strcpy(pair->second, profilearg); return 0; } 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); return expanded_profile; } #ifdef TEST char *set_test_home(const char *new_home) { size_t size = _PC_PATH_MAX == -1 ? 4096 : _PC_PATH_MAX; char *buf = malloc(size + 1); if (!buf) { return NULL; } char *cwd = getcwd(buf, size); if (!cwd) { free(buf); return NULL; } const char *suffix = "/tests/test-profiles/"; char *home = malloc(strlen(cwd) + strlen(suffix) + 1); if (!new_home) { free(buf); return NULL; } strcpy(new_home, buf); strcat(new_home, suffix); char *previous_home = getenv("HOME"); setenv("HOME", new_home, 1); return previous_home; } void expand_profile_name_test() { { testing("expanding profile name without setting $XDG_DATA_HOME"); // FIXME: this can fail const char *new_home = sharedc_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); char *n = expand_profile_name(new_home); assert(n); 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 unit_tests() { } #endif int main(int argc, char *argv[]) { #ifdef TEST unit_tests(); return 0; #endif int ret; struct Pair p; ret = get_options(argc, argv, &p); if (ret) { goto err; } err: return ret; } #endif