diff options
-rw-r--r-- | src/food.c | 90 | ||||
-rw-r--r-- | src/foodopts.c | 0 | ||||
-rw-r--r-- | src/foodopts.h | 16 | ||||
-rw-r--r-- | src/lib.c | 6 | ||||
-rw-r--r-- | src/parser.c | 12 | ||||
-rw-r--r-- | src/parser.h | 9 | ||||
-rw-r--r-- | src/types.c | 9 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/foodtest.in | 7 | ||||
-rw-r--r-- | tests/parser.c | 122 |
10 files changed, 225 insertions, 49 deletions
@@ -13,10 +13,13 @@ static struct opts { int html; int rcp; char query[2048]; + char add_val[100][2048]; char includes[100][2048]; int includes_n; int title; int eval; + int add; + int add_n; int list; int hash; int search; @@ -27,9 +30,12 @@ static struct opts { .html = 0, .rcp = 0, .query = "", + .add_val = {""}, .includes = {""}, .includes_n = 0, .eval = 1, + .add = 0, + .add_n = 0, .title = 0, .list = 0, .hash = 0, @@ -71,6 +77,8 @@ main(int argc, char * argv[]) {"to-json", no_argument, 0, 'j'}, {"to-html", no_argument, 0, 'w'}, {"to-rcp", no_argument, 0, 'r'}, + {"add-item", required_argument, 0, 'a'}, + //{"add-step", required_argument, 0, 'o'}, {"format", required_argument, 0, 'f'}, {"include", required_argument, 0, 'I'}, {"search", required_argument, 0, 's'}, @@ -84,7 +92,7 @@ main(int argc, char * argv[]) int option_index = 0; - c = getopt_long (argc, argv, "jnlhrwf:s:S:t:H:I:", + c = getopt_long (argc, argv, "jnlhrwf:s:S:t:H:I:a:", long_options, &option_index); if (c == -1) @@ -147,6 +155,10 @@ main(int argc, char * argv[]) case 'n': opt.eval = 0; break; + case 'a': + opt.add = 1; + strcpy(opt.add_val[opt.add_n++], optarg); + break; case '?': return -1; break; @@ -164,49 +176,59 @@ main(int argc, char * argv[]) int n = collect_library(&lib, argv, argc, optind, opt.includes, opt.includes_n); - + for (int i = 0; i < n; i++) { recipe * r = parse(lib[i], NULL); - if (r) { - if (opt.eval) { - recipe * r_eval = eval(r); + if (!r) { + fprintf(stderr, "Recipe %s not found\n", lib[i]); + continue; + } + + if (opt.eval) { + recipe * r_eval = eval(r); + free_recipe(r); + r = r_eval; + } + if (opt.hash) { + if (!check_hash(r, opt.query)) { free_recipe(r); - r = r_eval; + continue; } - if (opt.hash) { - if (!check_hash(r, opt.query)) { - free_recipe(r); - continue; - } - } - if (opt.title) { - if (strcmp(r->title, opt.query)) { - free_recipe(r); - continue; - } + } + if (opt.title) { + if (strcmp(r->title, opt.query)) { + free_recipe(r); + continue; } - if (opt.search) { - int c; - if (!(c = query_for_items_pbn(r, opt.query, opt.search_strict))) { - free_recipe(r); - continue; - } - if (c < 0) - exit(1); + } + if (opt.search) { + int c; + if (!(c = query_for_items_pbn(r, opt.query, opt.search_strict))) { + free_recipe(r); + continue; } - if (opt.list) { - pprint_items(r); + if (c < 0) + exit(1); + } + if (opt.add) { + for (int j = 0; j < opt.add_n; j++) { + char err[1000]; + if (parse_item(opt.add_val[j], r, (pt *)NULL, err)) + fprintf(stderr, "Error: %s\n", err); } + } + if (opt.list) { + pprint_items(r); + } + else { + if (opt.json) tojson(r); + else if (opt.html) tohtml(r); + else if (opt.rcp) torcp(r); else { - if (opt.json) tojson(r); - else if (opt.html) tohtml(r); - else if (opt.rcp) torcp(r); - else { - listing(r); - } + listing(r); } - free_recipe(r); } + free_recipe(r); } free_library(lib, n); diff --git a/src/foodopts.c b/src/foodopts.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/foodopts.c diff --git a/src/foodopts.h b/src/foodopts.h new file mode 100644 index 0000000..544dd3f --- /dev/null +++ b/src/foodopts.h @@ -0,0 +1,16 @@ +#ifndef __FOODOPTS_H +#define __FOODOPTS_H + +#include <getopt.h> + +/** + * This is a wrapper on getopt.h, specifically + * + * It provides a unified interface for defining + * cli options, printing the help, and looping though + * the parsing results. + * + */ + + +#endif /* __FOODOPTS_H */ @@ -1,6 +1,8 @@ #include "lib.h" #include "util.h" +#define LINE_SIZE 4096 + const char * default_food_lib = "/usr/local/share/food"; const char * @@ -17,10 +19,10 @@ static int rcp_find(char *** lib, int lib_n, const char * path) { int n = lib_n; - char line[1035]; + char line[LINE_SIZE]; FILE *fp; - char cmd[2048] = "/bin/find "; + char cmd[LINE_SIZE] = "/bin/find "; strcat(cmd, path); strcat(cmd, " -name '*.rcp'"); diff --git a/src/parser.c b/src/parser.c index 5009ca1..e4bffb4 100644 --- a/src/parser.c +++ b/src/parser.c @@ -3,12 +3,6 @@ #include "parser.h" #include "util.h" -typedef enum parser_type { - TITLE = 0, - ITEMS, - STEPS, -} pt; - static const int parse_title(const char * s, recipe * r, pt * type) { @@ -24,16 +18,16 @@ parse_title(const char * s, recipe * r, pt * type) r->title = strdup(r->filename); rc = 1; } - *type = ITEMS; + if (type) *type = ITEMS; return rc; } -static const int +int parse_item(const char * s, recipe * r, pt * type, char * error) { fdebug("^ item\n"); if (!strcmp(s, "---")) { - *type = STEPS; + if (type) *type = STEPS; return 0; } diff --git a/src/parser.h b/src/parser.h index d47bad5..70b3120 100644 --- a/src/parser.h +++ b/src/parser.h @@ -5,6 +5,12 @@ #define LINE_SIZE 4096 +typedef enum parser_type { + TITLE = 0, + ITEMS, + STEPS, +} pt; + /** * Return a recipe struct after parsing file in path. * @@ -14,4 +20,7 @@ recipe * parse(char * path, const char * prev); +int +parse_item(const char * s, recipe * r, pt * type, char * error); + #endif /* __PARSER_H */ diff --git a/src/types.c b/src/types.c index 8918d98..9fbb59b 100644 --- a/src/types.c +++ b/src/types.c @@ -279,9 +279,12 @@ void copy_metadata(recipe * dst, recipe * src) { if (!dst || !src) return; - dst->filename = strdup(src->filename); - dst->path = strdup(src->path); - dst->title = strdup(src->title); + if (src->filename) + dst->filename = strdup(src->filename); + if (src->path) + dst->path = strdup(src->path); + if (src->title) + dst->title = strdup(src->title); } void diff --git a/tests/Makefile.am b/tests/Makefile.am index d92a287..18d6314 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -7,7 +7,8 @@ TESTS = foodtest EXTRA_DIST = maketests.sh \ foodtest.in \ types.c \ - util.c + util.c \ + parser.c check_PROGRAMS = foodtest diff --git a/tests/foodtest.in b/tests/foodtest.in index 484cfeb..8d45f26 100644 --- a/tests/foodtest.in +++ b/tests/foodtest.in @@ -4,8 +4,14 @@ /* * Source files check by this suite: */ +#include "../src/parser.c" #include "../src/types.c" #include "../src/util.c" + +#define is1(arg) ck_assert_int_eq(arg, 1) +#define is0(arg) ck_assert_int_eq(arg, 0) + + /* * Test suite based on check * @@ -19,6 +25,7 @@ */ #include "types.c" #include "util.c" +#include "parser.c" Suite * foodtest_suite(void) { diff --git a/tests/parser.c b/tests/parser.c new file mode 100644 index 0000000..694653b --- /dev/null +++ b/tests/parser.c @@ -0,0 +1,122 @@ +/* -*- eval: (outline-minor-mode); outline-regexp: "START_TEST("; -*- */ + +static FILE * +file_from_str(char * s) { + if (!s) return NULL; + return fmemopen(s, strlen(s), "r"); +} + +START_TEST(check_next_escaped_line_full_file) +{ + char * file = + " \n" + "call #2\n" + " \n" + "ca\\\n" + "ll #4\n" + "\n" + "call \\ #6\n" + "\n" + "c\\\n" + "a\\\n" + "l\\\n" + "l \\\n" + "\\\n" + "#\\\n" + "8\\\n" + "\n" + " call \\\n" + " #9\n" + "last call"; + + + FILE * f = file_from_str(file); + if (!f) ck_abort_msg("Couldn't fmemopen"); + + char line[LINE_SIZE] = ""; + int actual_line = 0; + char error[LINE_SIZE] = ""; + + is0(next_escaped_line(f, line, &actual_line, error)); + ck_assert_int_eq(actual_line, 1); + ck_assert_str_eq(line, ""); + + is0(next_escaped_line(f, line, &actual_line, error)); + ck_assert_int_eq(actual_line, 2); + ck_assert_str_eq(line, "call #2"); + + is0(next_escaped_line(f, line, &actual_line, error)); + ck_assert_int_eq(actual_line, 3); + ck_assert_str_eq(line, ""); + + is0(next_escaped_line(f, line, &actual_line, error)); + ck_assert_int_eq(actual_line, 5); + ck_assert_str_eq(line, "call #4"); + + is0(next_escaped_line(f, line, &actual_line, error)); + ck_assert_int_eq(actual_line, 6); + ck_assert_str_eq(line, ""); + + is0(next_escaped_line(f, line, &actual_line, error)); + ck_assert_int_eq(actual_line, 7); + ck_assert_str_eq(line, "call \\ #6"); + + is0(next_escaped_line(f, line, &actual_line, error)); + ck_assert_int_eq(actual_line, 8); + ck_assert_str_eq(line, ""); + + is0(next_escaped_line(f, line, &actual_line, error)); + ck_assert_int_eq(actual_line, 16); + ck_assert_str_eq(line, "call #8"); + + is0(next_escaped_line(f, line, &actual_line, error)); + ck_assert_int_eq(actual_line, 18); + ck_assert_str_eq(line, "call #9"); + + is0(next_escaped_line(f, line, &actual_line, error)); + ck_assert_int_eq(actual_line, 19); + ck_assert_str_eq(line, "last call"); + + is1(next_escaped_line(f, line, &actual_line, error)); +} +END_TEST + +START_TEST(check_next_escaped_line_chunks) +{ + char line[LINE_SIZE] = ""; + int actual_line = 0; + char error[LINE_SIZE] = ""; + + next_escaped_line(file_from_str("check \\\nthis \\\nout!"), + line, &actual_line, error); + ck_assert_int_eq(actual_line, 3); + ck_assert_str_eq(line, "check this out!"); + + actual_line = 0; + next_escaped_line(file_from_str(" "), + line, &actual_line, error); + ck_assert_int_eq(actual_line, 1); + ck_assert_str_eq(line, ""); +} +END_TEST + +START_TEST(check_parse_item) +{ + recipe * r = new_recipe(); + char error[1000] = ""; + + is0(parse_item("test = val", r, (pt *)NULL, error)); + ck_assert_str_eq(r->i[r->in - 1]->name, "test"); + ck_assert_str_eq(r->i[r->in - 1]->qty, "val"); + + is1(parse_item("wrong!", r, (pt *)NULL, error)); + ck_assert_str_eq(r->i[r->in - 1]->name, "test"); + ck_assert_str_eq(r->i[r->in - 1]->qty, "val"); + + is0(parse_item("---", r, (pt *)NULL, error)); + ck_assert_str_eq(r->i[r->in - 1]->name, "test"); + ck_assert_str_eq(r->i[r->in - 1]->qty, "val"); + + free_recipe(r); +} +END_TEST |