summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile11
-rw-r--r--README.org10
-rw-r--r--cookbook.c162
-rw-r--r--emacs/recipe-mode.el67
-rw-r--r--food.c64
-rw-r--r--lib/minced-meat.rcp32
-rw-r--r--lib/pasta-red-sauce.rcp5
-rw-r--r--lib/pasta-with-minced-meat.rcp4
-rw-r--r--lib/sandwich-base.rcp2
-rw-r--r--lib/sandwich-sour.rcp13
-rw-r--r--src/eval.c23
-rw-r--r--src/lib.c48
-rw-r--r--src/lib.h7
-rw-r--r--src/parser.h6
l---------src/pbg.c1
l---------src/pbg.h1
-rw-r--r--src/search.c118
-rw-r--r--src/search.h2
-rw-r--r--src/search_stuff.c126
l---------src/teeny-sha1.c1
-rw-r--r--src/types.c64
-rw-r--r--src/types.h7
23 files changed, 745 insertions, 32 deletions
diff --git a/.gitignore b/.gitignore
index 3b0f025..85db1bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
hs/*
-food \ No newline at end of file
+food
+cookbook \ No newline at end of file
diff --git a/Makefile b/Makefile
index 3b011df..28066bd 100644
--- a/Makefile
+++ b/Makefile
@@ -1,13 +1,13 @@
CC=gcc
-CFLAGS=-D_GNU_SOURCE -std=c99 -pedantic -O3
-DBG_FLAGS=-Wall -g3 -D_FOOD_DEBUG -fsanitize=address -Og
+CFLAGS=-D_GNU_SOURCE -std=c99 -pedantic
+DBG_FLAGS=-Wall -g -D_FOOD_DEBUG -fsanitize=address -O0
SRC=src/*.c
.PHONY: all debug clean
-all: food #eval
+all: food cookbook #eval
food: $(SRC) food.c
$(CC) $(CFLAGS) -o food $^ $(LIBS)
@@ -15,12 +15,15 @@ food: $(SRC) food.c
eval: $(SRC) eval.c
$(CC) $(CFLAGS) -o eval $^ $(LIBS)
+cookbook: $(SRC) cookbook.c
+ $(CC) $(CFLAGS) -o cookbook $^ $(LIBS)
+
debug: CFLAGS += $(DBG_FLAGS)
debug: all
clean:
rm -f *.o
- rm -f food eval
+ rm -f food eval cookbook
# tests:
# $(MAKE) -C test
diff --git a/README.org b/README.org
index 5e0f020..8d4f973 100644
--- a/README.org
+++ b/README.org
@@ -81,3 +81,13 @@
}
#+end_src
+
+* Food compiler
+
+ recipes are text files defined above
+
+ ingredients and results from steps are variables linking back to the quantity or the step that produced them
+
+ the food compiler parses these files into internal C structures, the errors reported are syntax ones
+
+ then they can be eval'd which merges any subrecipe ingredients and steps into one plain recipe
diff --git a/cookbook.c b/cookbook.c
new file mode 100644
index 0000000..ca435ec
--- /dev/null
+++ b/cookbook.c
@@ -0,0 +1,162 @@
+#include <getopt.h>
+
+#include "src/util.h"
+#include "src/parser.h"
+#include "src/search.h"
+#include "src/eval.h"
+#include "src/lib.h"
+
+recipe ** cookbook;
+
+static struct opts {
+ int json;
+ int html;
+ int rcp;
+ char query[2048];
+ int eval;
+ int list;
+ int hash;
+ int search;
+ int search_strict;
+ int help;
+} opt = {
+ .json = 0,
+ .html = 0,
+ .rcp = 0,
+ .query = "",
+ .eval = 1,
+ .list = 0,
+ .hash = 0,
+ .search = 0,
+ .search_strict = 0,
+ .help = 0,
+};
+
+void
+print_help(char * argv0) {
+ printf("%s [OPTION] FILE ...\n", argv0);
+ printf("OPTIONS:\n");
+ printf("--format json,html,rcp\n");
+ printf("--to-{json,html,rcp}\n");
+ printf("-j json\n");
+ printf("-w html\n");
+ printf("-r rcp\n");
+ printf("-l, --list-ingredients\n");
+ printf("-s TERMS, --search TERMS\n");
+ printf("-S TERMS, --strict TERMS\n");
+ printf("-n, --no-eval\n");
+}
+
+int
+main(int argc, char * argv[])
+{
+ fdebug("--- Debug mode is on ---\n");
+
+ int c;
+
+ while (1) {
+ static struct option long_options[] =
+ {
+ /* <span class="roman">These options set a flag.</span> */
+ // {"verbose", no_argument, &verbose_flag, 1},
+ // {"brief", no_argument, &verbose_flag, 0},
+ {"help", no_argument, 0, 'h'},
+ {"no-eval", no_argument, 0, 'n'},
+ {"to-json", no_argument, 0, 'j'},
+ {"to-html", no_argument, 0, 'w'},
+ {"to-rcp", no_argument, 0, 'r'},
+ {"format", required_argument, 0, 'f'},
+ {"search", required_argument, 0, 's'},
+ {"strict", required_argument, 0, 'S'},
+ {"hash", required_argument, 0, 'H'},
+ {"list-ingredients", no_argument, 0, 'l'},
+ // {"to-rcp", required_argument, 0, 'r'},
+ {0, 0, 0, 0}
+ };
+
+ int option_index = 0;
+
+ c = getopt_long (argc, argv, "jnlhrwf:s:S:H:",
+ long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 0:
+ if (long_options[option_index].flag != 0)
+ break;
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+ case 's':
+ opt.search = 1;
+ strcpy(opt.query, optarg);
+ break;
+ case 'S':
+ opt.search = 1;
+ opt.search_strict = 1;
+ strcpy(opt.query, optarg);
+ break;
+ case 'H':
+ opt.hash = 1;
+ strcpy(opt.query, optarg);
+ break;
+ case 'j':
+ opt.json = 1;
+ break;
+ case 'w':
+ opt.html = 1;
+ break;
+ case 'r':
+ opt.rcp = 1;
+ break;
+ case 'l':
+ opt.list = 1;
+ break;
+ case 'h':
+ opt.help = 1;
+ break;
+ case 'n':
+ opt.eval = 0;
+ break;
+ case '?':
+ return -1;
+ break;
+ default:
+ abort ();
+ }
+ }
+
+ if (opt.help) {
+ print_help(argv[0]);
+ return 0;
+ }
+
+ char ** lib = NULL;
+ int n = collect_library(&lib, argv, argc, optind);
+
+ cookbook = (recipe **)malloc(sizeof(recipe *) * n);
+ for (int i = 0; i < n; i++) {
+ cookbook[i] = parse(lib[i], NULL);
+ if (opt.eval) {
+ recipe * _r = eval(cookbook[i]);
+ free_recipe(cookbook[i]);
+ cookbook[i] = _r;
+ }
+ }
+ free_library(lib, n);
+
+ for (int i = 0; i < n; i++) {
+ listing(cookbook[i]);
+ }
+
+ for (int i = 0; i < n; i++) {
+ free_recipe(cookbook[i]);
+ }
+ free(cookbook);
+
+ return 0;
+}
diff --git a/emacs/recipe-mode.el b/emacs/recipe-mode.el
new file mode 100644
index 0000000..c38008a
--- /dev/null
+++ b/emacs/recipe-mode.el
@@ -0,0 +1,67 @@
+;;; recipe-mode.el --- major mode for foodtool's recipe files -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021
+
+;; Author: <gramanas@EYE>
+;; Keywords: files, data
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+
+(eval-when-compile
+ (require 'rx))
+
+(setq recipe-highlights
+ '(
+ ;; comment
+ ("^ *#+.*$" . 'font-lock-comment-delimiter-face)
+ ;; title
+ ("^ *@\\(.*\\)$" . (1 'font-lock-preprocessor-face))
+ ;; duration
+ ("\\[\\(.+?\\)\\]" . (1 'font-lock-builtin-face))
+ ;; quantity measurements
+ ("= *[[:digit:]/]+\\([ [:graph:]]+?\\)$" . (1 'font-lock-builtin-face))
+ ;; variable in assignment
+ ("^\\(.+?\\)=.+?$" . (1 'font-lock-variable-name-face))
+ ;; variable in ${}
+ ("\\${\\(.+?\\)}" . (1 'font-lock-variable-name-face))
+ ;; single word variable
+ ("\\$\\([^ {\n]+\\)" . (1 'font-lock-variable-name-face))
+ ;; step result
+ ("=>\\(.*\\)" . (1 'font-lock-variable-name-face))
+ ;; include
+ ("^[ [:digit:]]*!" . 'font-lock-constant-face)
+ ;; symbols
+ ("@\\|-\\|>\\|+\\|=>" . 'font-lock-constant-face)
+ ))
+
+(define-derived-mode recipe-mode prog-mode "rcp"
+ "Mode for rcp files
+\\{recipe-mode-map}"
+
+ ;;(setq-local font-lock-defaults '(conf-font-lock-keywords nil t nil nil))
+ (setq-local font-lock-defaults '(recipe-highlights))
+ (setq-local comment-use-syntax nil)
+ (setq-local comment-start "#")
+ (setq-local parse-sexp-ignore-comments t))
+
+(add-to-list 'auto-mode-alist '("\\.rcp\\'" . recipe-mode))
+
+(provide 'recipe-mode)
+;;; recipe-mode.el ends here
diff --git a/food.c b/food.c
index d0466a3..f7ddbc1 100644
--- a/food.c
+++ b/food.c
@@ -4,6 +4,7 @@
#include "src/parser.h"
#include "src/search.h"
#include "src/eval.h"
+#include "src/lib.h"
recipe ** cookbook;
@@ -14,6 +15,7 @@ static struct opts {
char query[2048];
int eval;
int list;
+ int hash;
int search;
int search_strict;
int help;
@@ -24,6 +26,7 @@ static struct opts {
.query = "",
.eval = 1,
.list = 0,
+ .hash = 0,
.search = 0,
.search_strict = 0,
.help = 0,
@@ -65,6 +68,7 @@ main(int argc, char * argv[])
{"format", required_argument, 0, 'f'},
{"search", required_argument, 0, 's'},
{"strict", required_argument, 0, 'S'},
+ {"hash", required_argument, 0, 'H'},
{"list-ingredients", no_argument, 0, 'l'},
// {"to-rcp", required_argument, 0, 'r'},
{0, 0, 0, 0}
@@ -72,7 +76,7 @@ main(int argc, char * argv[])
int option_index = 0;
- c = getopt_long (argc, argv, "jnlhrwf:s:S:",
+ c = getopt_long (argc, argv, "jnlhrwf:s:S:H:",
long_options, &option_index);
if (c == -1)
@@ -106,6 +110,10 @@ main(int argc, char * argv[])
opt.search_strict = 1;
strcpy(opt.query, optarg);
break;
+ case 'H':
+ opt.hash = 1;
+ strcpy(opt.query, optarg);
+ break;
case 'j':
opt.json = 1;
break;
@@ -137,33 +145,49 @@ main(int argc, char * argv[])
return 0;
}
- if (optind < argc) {
- while (optind < argc) {
- recipe * r = parse(argv[optind++], NULL);
- if (r) {
- if (opt.search) {
- if (!query_for_items(r, opt.query, opt.search_strict))
- continue;
- }
- if (opt.eval) {
- recipe * r_eval = eval(r);
+ char ** lib = NULL;
+ int n = collect_library(&lib, argv, argc, optind);
+
+ for (int i = 0; i < n; i++) {
+ recipe * r = parse(lib[i], NULL);
+ if (r) {
+ 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.list) {
- pprint_items(r);
+ }
+ 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.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);
- if (opt.html) tohtml(r);
- if (opt.rcp) torcp(r);
+ listing(r);
}
- free_recipe(r);
}
+ free_recipe(r);
}
- } else {
- fprintf(stderr, "Specify filenames\n");
}
+ free_library(lib, n);
+
return 0;
}
diff --git a/lib/minced-meat.rcp b/lib/minced-meat.rcp
new file mode 100644
index 0000000..1b25998
--- /dev/null
+++ b/lib/minced-meat.rcp
@@ -0,0 +1,32 @@
+@minced meat
+
+minced veal = 400g
+minced pork = 100g
+
+salt, pepper = *
+kayen pepper = 1/2 tbsp
+cinamon = 1/2 tbsp
+cumin = 1/2 tbsp
+bay leaf = 2
+
+sun dried tomato = 6 pieces
+
+white onion = 1
+garlic = 2 cloves
+carrot = 1 small
+
+white wine = 1/2 glass
+tomato sauce = 2 cans
+olive oil = 3 tbsp
+
+---
+
+- chop the ${white onion} and $garlic, slice the $carrot
+> heat ${olive oil} in a pan and add ${white onion} and $garlic and $salt and [brown for 3 minutes]
+> add the $carrot and [stir until well browned]
+> add the ${minced pork} and ${minced veal}
+> add the $pepper ${kayen pepper} $cinamon $cumin ${bay leaf}
+> stir [until the meat is brown and starts to stick on the pan]
+> deglaze with ${white wine} and stir [until most wine has evaporated]
+> add the ${tomato sauce} and stir, if too thick add some water
+> [cook for 30 minutesd]
diff --git a/lib/pasta-red-sauce.rcp b/lib/pasta-red-sauce.rcp
index 2b56894..a57f30c 100644
--- a/lib/pasta-red-sauce.rcp
+++ b/lib/pasta-red-sauce.rcp
@@ -1,4 +1,4 @@
-@pasta with red sauce 3
+@ pasta with red sauce 3
3!pasta.rcp
salt = *
@@ -14,8 +14,7 @@ olive oil = *
- prepare $pasta
> heat some ${olive oil} in the pan
> fry $garlic [until light brown]
-> add $onion and some $salt, fry [until \
-onion is translucent]
+> add $onion and some $salt, fry [until onion is translucent]
> deglaze with the $wine and cook [until it reduces in half]
> add the ${tomato sauce}, and some warm water to rinse the can
> add $pasta to the sauce, turn the heat off, and cook [for 1-2 minutes]
diff --git a/lib/pasta-with-minced-meat.rcp b/lib/pasta-with-minced-meat.rcp
new file mode 100644
index 0000000..3a4a6bc
--- /dev/null
+++ b/lib/pasta-with-minced-meat.rcp
@@ -0,0 +1,4 @@
+@ pasta with minced meat
+
+!pasta.rcp
+!minced-meat.rcp
diff --git a/lib/sandwich-base.rcp b/lib/sandwich-base.rcp
index 98568a7..7a485b2 100644
--- a/lib/sandwich-base.rcp
+++ b/lib/sandwich-base.rcp
@@ -5,5 +5,5 @@ butter or mayo = *
---
-- toast the $bread => toasted bread
+- toast the $bread [for 3 minutes] => toasted bread
- spead the ${butter or mayo} on the ${toasted bread}
diff --git a/lib/sandwich-sour.rcp b/lib/sandwich-sour.rcp
new file mode 100644
index 0000000..de06b3d
--- /dev/null
+++ b/lib/sandwich-sour.rcp
@@ -0,0 +1,13 @@
+@ sandwich with sun dried tomatoes
+
+!sandwich-base.rcp
+
+sun dried tomates = 3
+turkey = 2pcs
+cheddar = 2pcs
+
+---
+
+> chop the ${sun dried tomatoes} into litle pieces
+> slice the $turkey and ${cheddar} in half
++ assembly order: bread mayo tomatoes cheddar turkey bread
diff --git a/src/eval.c b/src/eval.c
index 8bc886a..57bfbb0 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1,5 +1,25 @@
#include "eval.h"
#include "util.h"
+#include <stdint.h>
+
+extern int
+sha1digest(uint8_t *digest, char *hexdigest, const uint8_t *data, size_t databytes);
+
+void
+create_hash(recipe * r)
+{
+ char data[65536] = "\0";
+
+ for (int i = 0; i < r->in; i++) {
+ strcat(data, r->i[i]->name);
+ strcat(data, r->i[i]->qty);
+ }
+ if (r->sn) {
+ for (int i = 0; i < r->sn; i++)
+ strcat(data, r->s[i]->inst);
+ }
+ sha1digest(NULL, r->sha1, (uint8_t *)data, strlen(data));
+}
recipe *
eval(recipe * r)
@@ -10,7 +30,10 @@ eval(recipe * r)
merge_items(_r, r);
merge_steps(_r, r);
copy_metadata(_r, r);
+ copy_subrecipes(_r, r);
+ create_hash(_r);
+
return _r;
}
diff --git a/src/lib.c b/src/lib.c
new file mode 100644
index 0000000..f43bdc2
--- /dev/null
+++ b/src/lib.c
@@ -0,0 +1,48 @@
+#include "lib.h"
+#include "util.h"
+
+int
+collect_library(char *** dst, char * argv[], int argc, int optind)
+{
+ int n = 0;
+ FILE *fp;
+ char path[1035];
+
+ char ** lib = NULL;
+
+ for (int i = optind; i < argc; i++) {
+ lib = realloc(lib, sizeof(char **) * (n + 1));
+ lib[n] = strdup(argv[i]);
+ fdebug("%d: %s\n", n, lib[n]);
+ n = n + 1;
+ }
+
+ fp = popen("/bin/find /home/gramanas/code/foodtools/lib/ -name '*.rcp'", "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Couldn't run /bin/find\n");
+ exit(1);
+ }
+
+ /* Read the output a line at a time */
+ while (fgets(path, sizeof(path), fp) != NULL) {
+ lib = realloc(lib, sizeof(char **) * (n + 1));
+ trim(path);
+ lib[n] = strdup(path);
+ fdebug("%d: %s\n", n, lib[n]);
+ n = n + 1;
+ }
+
+ /* close */
+ pclose(fp);
+
+ *dst = lib;
+ return n; // no of items
+}
+
+void
+free_library(char ** lib, int n) {
+ for (int i = 0; i < n; i++) {
+ free(lib[i]);
+ }
+ free(lib);
+}
diff --git a/src/lib.h b/src/lib.h
new file mode 100644
index 0000000..005a330
--- /dev/null
+++ b/src/lib.h
@@ -0,0 +1,7 @@
+#ifndef __LIB_H
+#define __LIB_H
+
+int collect_library(char *** lib, char * argv[], int argc, int optind);
+void free_library(char ** lib, int n);
+
+#endif /* __LIB_H */
diff --git a/src/parser.h b/src/parser.h
index 2680da5..d47bad5 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -5,6 +5,12 @@
#define LINE_SIZE 4096
+/**
+* Return a recipe struct after parsing file in path.
+*
+* Path can be relative or full, ~ will be resolved as $HOME, and - will accept
+* input from stdin
+*/
recipe *
parse(char * path, const char * prev);
diff --git a/src/pbg.c b/src/pbg.c
new file mode 120000
index 0000000..c707a6b
--- /dev/null
+++ b/src/pbg.c
@@ -0,0 +1 @@
+../pbg/pbg.c \ No newline at end of file
diff --git a/src/pbg.h b/src/pbg.h
new file mode 120000
index 0000000..36a1da6
--- /dev/null
+++ b/src/pbg.h
@@ -0,0 +1 @@
+../pbg/pbg.h \ No newline at end of file
diff --git a/src/search.c b/src/search.c
index cf2eab9..a038cfb 100644
--- a/src/search.c
+++ b/src/search.c
@@ -1,5 +1,115 @@
#include "util.h"
#include "search.h"
+#include "pbg.h"
+
+static char *
+form_string(const recipe * r, const char * s, int strict)
+{
+ char _s[4048] = "";
+ int l = 0;
+ char buf[256] = "";
+
+ for (int i = 0; i < strlen(s); i++) {
+ if (s[i] == '(' ||
+ s[i] == ')' ||
+ s[i] == ' ') {
+ if (strlen(buf)) {
+ if (!strcmp(buf, "not") ||
+ !strcmp(buf, "NOT") ||
+ !strcmp(buf, "!")) {
+ _s[l++] = '!';
+ }
+ else if (!strcmp(buf, "and") ||
+ !strcmp(buf, "AND") ||
+ !strcmp(buf, "&&") ||
+ !strcmp(buf, "&")) {
+ _s[l++] = '&';
+ }
+ else if (!strcmp(buf, "or") ||
+ !strcmp(buf, "OR") ||
+ !strcmp(buf, "||") ||
+ !strcmp(buf, "|")) {
+ _s[l++] = '|';
+ }
+ else {
+ fdebug("querying %s for %s\n", r->title, buf);
+ if (query_for_items(r, buf, strict)) {
+ strcat(_s, "TRUE");
+ l+=4;
+ }
+ else {
+ strcat(_s, "FALSE");
+ l+=5;
+ }
+ }
+ strcpy(buf, "");
+ }
+ _s[l++] = s[i];
+ }
+ else {
+ int k = strlen(buf);
+ buf[k] = s[i];
+ buf[k+1] = '\0';
+ }
+ }
+ _s[l] = '\0';
+
+ return strdup(_s);
+}
+
+int
+query_for_items_pbn(const recipe * r, const char * s, int strict)
+{
+ if (!strlen(s)) {
+ return 0;
+ }
+
+ char * query = NULL;
+
+ if (s[0] != '(') {
+ if (strstr(s, "and ") ||
+ strstr(s, "or ") ||
+ strstr(s, "not ")) {
+ query = (char *)malloc(sizeof(char) * strlen(s) + 3);
+ strcpy(query, "(");
+ strcat(query, s);
+ strcat(query, ")");
+ } else {
+ return query_for_items(r, s, strict);
+ }
+ } else {
+ query = strdup(s);
+ }
+
+ pbg_error err;
+ pbg_expr e;
+ int result;
+
+ fdebug("PBG Query string: %s\n", query);
+ char* str = form_string(r, query, strict);
+ fdebug("PBG Query eval'd string: %s\n", str);
+
+ /* Parse the expression string and check if
+ * there were any compilation errors. */
+ pbg_parse(&e, &err, str);
+ if(pbg_iserror(&err)) {
+ fprintf(stderr, "Parsing error: %s => %s\n", s, str);
+ free(query); free(str); free(err._data);
+ return -1;
+ }
+
+ /* Evaluate the expression string and check if
+ * there were any runtime errors. */
+ result = pbg_evaluate(&e, &err, NULL);
+ if(pbg_iserror(&err)) {
+ fprintf(stderr, "Eval error: %s => %s\n", s, str);
+ free(query); free(str); pbg_free(&e); free(err._data);
+ return -1;
+ }
+
+ free(query); free(str); pbg_free(&e);
+ return (result == PBG_TRUE) ? 1 : 0;
+}
/**
* Query recipe `r` for input `s` and return 1 if found 0 otherwise
@@ -17,3 +127,11 @@ query_for_items(const recipe * r, const char * s, int strict)
}
return 0;
}
+
+int
+check_hash(const recipe * r, const char * s)
+{
+ char _sha1[9];
+ sprintf(_sha1, "%.*s", 8, r->sha1);
+ return (!strcmp(s, _sha1) || !strcmp(s, r->sha1)) ? 1 : 0;
+}
diff --git a/src/search.h b/src/search.h
index 1295cd4..1c7c61f 100644
--- a/src/search.h
+++ b/src/search.h
@@ -3,6 +3,8 @@
#include "types.h"
+int check_hash(const recipe * r, const char * s);
int query_for_items(const recipe * r, const char * s, int strict);
+int query_for_items_pbn(const recipe * r, const char * s, int strict);
#endif
diff --git a/src/search_stuff.c b/src/search_stuff.c
new file mode 100644
index 0000000..5d4cb6c
--- /dev/null
+++ b/src/search_stuff.c
@@ -0,0 +1,126 @@
+#include "util.h"
+#include "search.h"
+
+char stk[4048];
+int p = 0;
+
+/*
+ * expr : (expr) | expr op expr | not expr | term
+ * op : and | or
+ * term : <string>
+ *
+ */
+
+void
+reverse_str(char ** s)
+{
+ int l = strlen(*s);
+ char * rev_s = strdup(*s);
+ int j = 0;
+ for (int i = l - 1; i >= 0; i--, j++) {
+ rev_s[j] = (*s)[i];
+ }
+ rev_s[j] = '\0';
+ free(*s);
+ *s = rev_s;
+}
+
+
+/*
+ * Implementing https://www.javatpoint.com/convert-infix-to-prefix-notation
+ *
+ * ascii values:
+ * & -> 38
+ * | -> 124
+ * ( -> 40
+ * ) -> 41
+ * a-z -> 97-122
+ * A-Z -> 64-90
+ *
+ * precidence:
+ * & > |
+ * (http://www.cs.ucc.ie/~gavin/cs1001/Notes/chap40/ch40_16.html)
+ */
+int
+precedence(char c)
+{
+ switch (c) {
+ case '&':
+ return 100;
+ case '|':
+ return 50;
+ default:
+ return 0;
+ }
+}
+
+void
+infix_to_prefix(char ** s)
+{
+ char * prefixed = malloc(sizeof(char) * strlen(*s) * 2);
+ prefixed[0] = '\0';
+ char * rev_s = strdup(*s);
+ reverse_str(&rev_s);
+
+ for (int i = 0; i < strlen(rev_s); i++) {
+ if (rev_s[i] == '&' ||
+ rev_s[i] == '|') {
+ if (p == 0) {
+ stk[p++] = rev_s[i];
+ } else {
+ if (precedence(rev_s[i] > precedence(stk[p - 1]))) {
+ stk[p++] = rev_s[i];
+ } else {
+ int k = strlen(prefixed);
+ for (int j = p; j > 0 || precedence(rev_s[i]) < precedence(stk[p - 1]); j--) {
+ prefixed[k++] = stk[j - 1];
+ p--;
+ }
+ prefixed[k] = '\0';
+ stk[p++] = rev_s[i];
+ }
+ }
+ }
+ else if (rev_s[i] == '(') {
+ stk[p++] = rev_s[i];
+ }
+ else if (rev_s[i] == ')') {
+ int k = strlen(prefixed);
+ prefixed[k++] = rev_s[i];
+ for (int j = p; j > 0 && stk[j] != ')'; j--) {
+ prefixed[k++] = stk[j - 1];
+ p--;
+ }
+ prefixed[k] = '\0';
+ }
+ else {
+ int k = strlen(prefixed);
+ prefixed[k] = rev_s[i];
+ prefixed[k+1] = '\0';
+ }
+ }
+
+ int k = strlen(prefixed);
+ prefixed[k++] = ' '; // add space after the first operator
+ for (int j = p; j > 0; j--) {
+ prefixed[k++] = stk[j - 1];
+ p--;
+ }
+ prefixed[k] = '\0';
+
+ printf("rev %s\n", prefixed);
+ for (int i = 0; i < strlen(prefixed); i++) {
+ if (prefixed[i] == '(' &&
+ i < strlen(prefixed) &&
+ (prefixed[i+1] == '&' || prefixed[i+1] == '|')) {
+ prefixed[i] = prefixed[i+1];
+ prefixed[i+1] = '(';
+ }
+ }
+
+ printf("rev fixed %s\n", prefixed);
+ reverse_str(&prefixed);
+ free(rev_s);
+ free(*s);
+ *s = prefixed;
+}
diff --git a/src/teeny-sha1.c b/src/teeny-sha1.c
new file mode 120000
index 0000000..4208eb3
--- /dev/null
+++ b/src/teeny-sha1.c
@@ -0,0 +1 @@
+../teeny-sha1/teeny-sha1.c \ No newline at end of file
diff --git a/src/types.c b/src/types.c
index dbc7458..3233323 100644
--- a/src/types.c
+++ b/src/types.c
@@ -21,6 +21,7 @@ new_recipe()
r->filename = NULL;
r->path = NULL;
r->title = NULL;
+ r->sha1[0] = '\0';
return r;
}
@@ -137,6 +138,12 @@ pprint_items(recipe * r)
}
void
+listing(recipe * r)
+{
+ printf("%.*s %d:%d\t%d\t%s\n", 8, (strcmp(r->sha1, "") ? r->sha1 : "neval"), r->in, r->sn, r->rn, r->title);
+}
+
+void
show(recipe * r)
{
printf("Filename\t%s\n", r->filename);
@@ -158,7 +165,9 @@ tojson(recipe * r)
printf("{\"filename\":\"%s\",", r->filename);
printf("\"dirname\":\"%s\",", r->path);
printf("\"title\":\"%s\",", r->title);
- printf("\"n\":\"%d\"", r->n);
+ printf("\"n\":\"%d\",", r->n);
+ printf("\"sha1_short\":\"%.*s\",", 8, r->sha1);
+ printf("\"sha1\":\"%s\"", r->sha1);
if (r->rn) {
printf(",\"subrecipes\":[");
int i = 0;
@@ -189,16 +198,51 @@ tojson(recipe * r)
void
tohtml(recipe * r)
{
- printf("todo\n");
+ printf("<div class=\"recipe\">\n");
+ printf("<!-- %s/%s -->\n", r->path, r->filename);
+ printf("<h1>%s</h1>\n\n", r->title);
+ if (r->rn) {
+ printf("<ul class=\"list items subrecipes\">\n");
+ for (int i = 0; i < r->rn; i++) {
+ printf("<li>!%s/%s</li>\n", r->r[i]->path, r->r[i]->filename);
+ }
+ printf("</ul>\n");
+ }
+ if (r->in) {
+ printf("<ul class=\"list items\">\n");
+ for (int i = 0; i < r->in; i++) {
+ printf("<li>%s = %s</li>\n", r->i[i]->name, r->i[i]->qty);
+ }
+ printf("</ul>\n");
+ }
+ if (r->sn) {
+ printf("<br><p>---</p><br>\n");
+ printf("<ul class=\"list steps\">\n");
+ for (int i = 0; i < r->sn; i++) {
+ char c;
+ if (r->s[i]->type == PREP)
+ c = '-';
+ else if (r->s[i]->type == COOK)
+ c = '>';
+ else
+ c = '+';
+ printf("<li>%c %s</li>\n", c, r->s[i]->inst);
+ }
+ printf("</ul>\n");
+ }
+ printf("<hr>\n</div>\n");
}
void
torcp(recipe * r)
{
printf("# %s/%s\n\n", r->path, r->filename);
+ if (r->sha1[0] != '\0') {
+ printf("# %s\n\n", r->sha1);
+ }
printf("@%s\n\n", r->title);
for (int i = 0; i < r->rn; i++) {
- printf("!%s/%s\n", r->r[i]->path, r->r[i]->filename);
+ printf("%s!%s/%s\n", (r->sha1[0] != '\0') ? "# ": "", r->r[i]->path, r->r[i]->filename);
}
for (int i = 0; i < r->in; i++)
printf("%s = %s\n", r->i[i]->name, r->i[i]->qty);
@@ -218,6 +262,20 @@ torcp(recipe * r)
}
void
+copy_subrecipes(recipe * dst, recipe * src)
+{
+ if (!dst || !src) return;
+ for (int i = 0; i < src->rn; i++) {
+ recipe * r = new_recipe();
+ r->title = strdup(src->r[i]->title);
+ r->path = strdup(src->r[i]->path);
+ r->filename = strdup(src->r[i]->filename);
+ r->n = src->r[i]->n;
+ new_subrecipe(dst, r);
+ }
+}
+
+void
copy_metadata(recipe * dst, recipe * src)
{
if (!dst || !src) return;
diff --git a/src/types.h b/src/types.h
index 2c58537..867368b 100644
--- a/src/types.h
+++ b/src/types.h
@@ -30,6 +30,7 @@ typedef struct recipe_t {
int sn;
struct recipe_t **r;
int rn;
+ char sha1[41];
} recipe;
recipe * new_recipe();
@@ -42,6 +43,7 @@ void free_item(item * i);
void free_step(step * s);
void pprint_items(recipe * r);
+void listing(recipe * r);
void show(recipe * r);
void tojson(recipe * r);
void tohtml(recipe * r);
@@ -52,6 +54,11 @@ void torcp(recipe * r);
/**
* Copy metadata from `src` to `dst`
*/
+void copy_subrecipes(recipe * dst, recipe * src);
+
+/**
+ * Copy metadata from `src` to `dst`
+ */
void copy_metadata(recipe * dst, recipe * src);
/**