#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include // #include /* #include "vendor/vector.h" #include #include #include #include */ /* 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 %s\n", VERSION, DATE); 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"; */