aboutsummaryrefslogtreecommitdiff
path: root/booki.c
diff options
context:
space:
mode:
Diffstat (limited to 'booki.c')
-rw-r--r--booki.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/booki.c b/booki.c
new file mode 100644
index 0000000..b7bb92f
--- /dev/null
+++ b/booki.c
@@ -0,0 +1,312 @@
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "toml.h"
+
+#define BOOKI_FILE_REL ".local/share/booki/books.toml"
+
+#define MAX_SEARCH_OPTS 5
+
+char* BOOKI_FILE;
+
+const char* get_last_word(const char* str) {
+ const char* last_space = strrchr(str, ' ');
+ if ((last_space - str) > 0)
+ return last_space + 1;
+ else
+ return 0;
+}
+
+bool regex_match(const char* pattern, const char* text) {
+
+ // empty pattern matches everything
+ if (!*pattern) return true;
+
+ // get lengths
+ int pattern_length = strlen(pattern);
+ int text_length = strlen(text);
+
+ if (pattern_length > text_length) return false;
+
+ // we only need to compare while remaining text is
+ // as long or longer than pattern (without special)
+ for (int i = 0; i <= (text_length - pattern_length); i++) {
+ if (strncasecmp(pattern, text + i, pattern_length) == 0) return true;
+ }
+
+ return false;
+}
+
+toml_table_t* get_toml_data(const char* filename) {
+ FILE* fp;
+ char errbuf[200];
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ printf("can't open file :(");
+ return 0;
+ }
+
+ toml_table_t* conf = toml_parse_file(fp, errbuf, sizeof(errbuf));
+ fclose(fp);
+
+ return conf;
+}
+
+static struct option search_options[] = {
+ {"show", no_argument, 0, 's'},
+ {0, 0, 0, 0} // marks the end of the array
+};
+
+struct search_opt {
+ int show;
+ int count;
+ char* opts[MAX_SEARCH_OPTS];
+ char* args[MAX_SEARCH_OPTS];
+};
+
+struct search_opt parse_search_options(int argc, char* argv[]) {
+
+ // return struct
+ struct search_opt opt_out;
+ int count = 0;
+ int show = false;
+
+ // opt options
+ int opt;
+ int opt_idx = 0;
+ opterr = 0; // turn off getopt error messages
+
+ // look at each option
+ while ((opt = getopt_long_only(argc, argv, "", search_options, &opt_idx)) != -1) {
+ switch(opt) {
+ case 's':
+ show = true;
+ break;
+ case '?':
+ // optind points at the argument (one past the option)
+ opt_out.opts[count] = argv[optind-1] + 2; // '--example' -> 'example'
+ opt_out.args[count] = argv[optind];
+ count++;
+ break;
+ default:
+ printf("something went wrong with parsing!\n");
+ break;
+ }
+ }
+
+ // set the count/show values
+ opt_out.count = count;
+ opt_out.show = show;
+
+ return opt_out;
+}
+
+char** search(int argc, char* argv[]) {
+ toml_table_t* data = get_toml_data(BOOKI_FILE);
+
+ if (!data) {
+ return NULL;
+ }
+
+ struct search_opt search_opts = parse_search_options(argc, argv);
+ bool print = false;
+
+ // get the books array
+ toml_array_t* books = toml_array_in(data, "books");
+ if (!books) {
+ printf("no such array: 'books'");
+ return NULL;
+ }
+
+ // book loop
+ for (int i = 0; ; i++) {
+ toml_table_t* book = toml_table_at(books, i);
+ if (!book) break;
+
+ if (search_opts.count == 0) {
+ print = true;
+ } else {
+ toml_datum_t datapoint;
+ char* field;
+ bool inner_print = true;
+
+ // loop through all options
+ int i;
+ for (i = 0; i < search_opts.count; i++) {
+ field = search_opts.opts[i];
+
+ // if the key doesn't exist, we won't print
+ if (!toml_key_exists(book, field)) {
+ break;
+ }
+
+ // try and get a string
+ datapoint = toml_string_in(book, field);
+ if (datapoint.ok) {
+ if (!regex_match(search_opts.args[i], datapoint.u.s)) {
+ free(datapoint.u.s);
+ break;
+ }
+ free(datapoint.u.s);
+ } else {
+ // try and get an integer
+ datapoint = toml_int_in(book, field);
+ int ret;
+ if (datapoint.ok) {
+ ret = atoi(search_opts.args[i]);
+ if (ret == 0) {
+ printf("not an int: %s\n", search_opts.args[i]);
+ break;
+ } else if (ret != datapoint.u.i) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ // if we made it all the way through the loop,
+ // we should print
+ print = i == search_opts.count;
+ }
+
+ // get title and author for printing
+ toml_datum_t book_title = toml_string_in(book, "title");
+ if (!book_title.ok) {
+ continue;
+ }
+
+ // author can be a string or a list
+ toml_datum_t book_author = toml_string_in(book, "author");
+ char author_str[100];
+ char* tmp;
+ if (!book_author.ok) {
+ toml_array_t* book_author_array = toml_array_in(book, "author");
+ if (!book_author_array) {
+ continue;
+ }
+ int len = toml_array_nelem(book_author_array);
+ tmp = toml_string_at(book_author_array, 0).u.s;
+ strcpy(author_str, tmp);
+ free(tmp);
+ for (int i = 1; i < len; i++) {
+ book_author = toml_string_at(book_author_array, i);
+ tmp = book_author.u.s;
+ if (i == len-1) {
+ strcat(author_str, " and ");
+ } else {
+ strcat(author_str, ", ");
+ }
+ strcat(author_str, book_author.u.s);
+ free(tmp);
+ }
+ } else {
+ strcpy(author_str, book_author.u.s);
+ free(book_author.u.s);
+ }
+
+ // print!
+ if (print) {
+ printf("%s by %s\n", book_title.u.s, author_str);
+ if (search_opts.show) {
+ //toml_datum_t d;
+ for (int i = 0; ; i++) {
+ const char* key = toml_key_in(book, i);
+ if (!key) break;
+
+ /*
+ if (strcmp("pages", key) == 0) {
+ d = toml_int_in(book, key);
+ if (d.ok) {
+ printf(" - %s: %d\n",
+ }
+ }
+ */
+
+
+ // currently we support strings and ints
+ toml_datum_t s = toml_string_in(book, key);
+ if (s.ok) {
+ printf(" - %s: %s\n", key, s.u.s);
+ free(s.u.s);
+ }
+ toml_datum_t i = toml_int_in(book, key);
+ if (i.ok) {
+ printf(" - %s: %d\n", key, i.u.i);
+ }
+
+ }
+ }
+ }
+
+ free(book_title.u.s);
+ //free(book_author.u.s);
+ }
+
+ toml_free(data);
+
+ return argv + optind;
+}
+
+void open() {
+
+ char* home = getenv("HOME");
+ if (!home) {
+ printf("no home?\n");
+ home = "/"; // no way
+ }
+ char* editor = getenv("EDITOR");
+ if (!editor)
+ editor = "nano";
+
+ pid_t pid;
+ int status;
+ switch ((pid = fork())) {
+ case -1:
+ printf("fork has failed!\n");
+ break;
+ case 0:
+ execlp(editor, editor, BOOKI_FILE, NULL);
+ printf("child failed :(\n");
+ break;
+ default:
+ // wait for the child to finish
+ pid = wait(&status);
+ break;
+ }
+}
+
+void help(bool err) {
+ printf("booki! it's a thing\n");
+ if (err) {
+ printf("you did something wrong\n");
+ }
+}
+
+int main(int argc, char* argv[]) {
+
+ char* home = getenv("HOME");
+ char booki_file[strlen(home) + strlen(BOOKI_FILE_REL) + 1];
+ sprintf(booki_file, "%s/%s", home, BOOKI_FILE_REL);
+ BOOKI_FILE = booki_file;
+ char** remaining = NULL;
+
+ if (argc == 1) {
+ help(false);
+ return 0;
+ } else if (strcmp(argv[1], "open") == 0) {
+ open();
+ } else if (strcmp(argv[1], "search") == 0) {
+ remaining = search(argc - 1, argv + 1);
+ }
+
+ return 0;
+}