summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/food.c90
-rw-r--r--src/foodopts.c0
-rw-r--r--src/foodopts.h16
-rw-r--r--src/lib.c6
-rw-r--r--src/parser.c12
-rw-r--r--src/parser.h9
-rw-r--r--src/types.c9
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/foodtest.in7
-rw-r--r--tests/parser.c122
10 files changed, 225 insertions, 49 deletions
diff --git a/src/food.c b/src/food.c
index 7ee222e..9506713 100644
--- a/src/food.c
+++ b/src/food.c
@@ -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 */
diff --git a/src/lib.c b/src/lib.c
index 3da93ed..a1f1f29 100644
--- a/src/lib.c
+++ b/src/lib.c
@@ -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