diff options
Diffstat (limited to 'src/parser.c')
-rw-r--r-- | src/parser.c | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 0000000..df8d80b --- /dev/null +++ b/src/parser.c @@ -0,0 +1,263 @@ +#include <libgen.h> + +#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) +{ + fdebug("^ title\n"); + int rc = 0; + + if (s[0] == '@' && strlen(s) > 1) { + char tmp[LINE_SIZE] = ""; + strcpy(tmp, ++s); + trim(tmp); + r->title = strdup(tmp); + } else { + r->title = strdup(r->filename); + rc = 1; + } + *type = ITEMS; + return rc; +} + +static const int +parse_item(const char * s, recipe * r, pt * type, char * error) +{ + fdebug("^ item\n"); + if (!strcmp(s, "---")) { + *type = STEPS; + return 0; + } + + /* 1 - 9 in ascii */ + if ((s[0] > 48 && s[0] < 58) || s[0] == '!') { + fdebug("INCLUDING: %s\n", s); + + char tmp[LINE_SIZE] = ""; + char path[LINE_SIZE] = ""; + + char *endptr; + int n = (int)strtol(s, &endptr, 10); + if (endptr[0] != '!') { + sprintf(error, "malformed include: %s", s); + return 1; + } + + strcpy(tmp, ++endptr); + trim(tmp); + + if (!strlen(tmp)) { + sprintf(error, "malformed include: %s", s); + return 1; + } + + if (tmp[0] == '/' || tmp[0] == '~') { + strcpy(path, tmp); + } else { + strcpy(path, r->path); + strcat(path, "/"); + strcat(path, tmp); + } + + recipe * rr = parse(path, r->filename); + if (!rr) { + sprintf(error, "Couldn't include %s", path); + return 1; + } + rr->n = n; + //copy_items(r, rr); + new_subrecipe(r, rr); + //free_recipe(rr); + + return 0; + } + + int l = strlen(s); + int val = 1; /* key vs value flag */ + int itemc = 0; + int c = 0; + char buf[LINE_SIZE] = ""; + for (int i = 0; i < l; i++) { + if (val) { + if (s[i] == ',' || s[i] == '=') { + buf[c++] = '\0'; + if (!strlen(buf)) { + sprintf(error, "malformed ingredient: %s", s); + return 1; + } + trim(buf); + if (strlen(buf)) { + new_item(r); + r->i[r->in - 1]->name = strdup(buf); + itemc++; + buf[0] = '\0'; + c = 0; + } + if (s[i] == '=') + val = 0; + continue; + } + } + buf[c++] = s[i]; + } + + buf[c] = '\0'; + trim(buf); + + if (!itemc) { + sprintf(error, "malformed ingderient: %s", buf); + return 1; + } + + if (!strlen(buf)) { + sprintf(error, "empty ingredient quantity: %s", s); + return 1; + } + + for (int i = 0; i < itemc; i++) { + r->i[r->in - 1 - i]->qty = strdup(buf); + } + + return 0; +} + +static const int +parse_step(const char * s, recipe * r, char * error) +{ + fdebug("^ step\n"); + + if ((s[0] == '>' || + s[0] == '-' || + s[0] == '+') && strlen(s+1)) { + new_step(r); + r->s[r->sn - 1]->type = s[0] == '>' ? COOK : s[0] == '-' ? PREP : SERVE; + char *tmp = strdup(s+1); + trim(tmp); + r->s[r->sn - 1]->inst = tmp; + return 0; + } + + sprintf(error, "malformed step: %s", s); + return 1; +} + +static const int +handle_line(const char * s, recipe * r, char * error, pt * type, const char * prev) +{ + fdebug("Handling line: %s\n", s); + + if (strlen(s) == 0) return 0; + if (s[0] == '#') return 0; + + switch (*type) { + case TITLE: + if (parse_title(s, r, type)) + if (parse_item(s, r, type, error)) + return 1; + break; + case ITEMS: + if (parse_item(s, r, type, error)) + return 1; + break; + case STEPS: + if (parse_step(s, r, error)) + return 1; + } + + return 0; +} + +static const int +next_escaped_line(FILE *f, char * line, int * lino, char * error) { + if (!fgets(line, LINE_SIZE, f)) + return 1; + (*lino)++; + + if (line[strlen(line) - 1] != '\n' && !feof(f)) { + sprintf(error, "line exceeds %d characters", LINE_SIZE); + return -1; + } + + trim(line); + int l = strlen(line); + + /* Last char is `\n` check if */ + /* (last - 1) is `\` */ + while (l > 0 && line[l - 1] == '\\') { + char tmp[LINE_SIZE] = ""; + + if (!fgets(tmp, LINE_SIZE, f)) + /* return line on EOF */ + break; + (*lino)++; + + trim(tmp); + if (strlen(tmp) + l >= LINE_SIZE) { + sprintf(error, "line exceeds %d characters", LINE_SIZE); + return -1; + } + + /* remove `\` */ + line[l - 1] = '\0'; + strcat(line, tmp); + l = strlen(line); + } + return 0; +} + +recipe * +parse(char * path, const char * prev) +{ + fdebug("Parsing: %s\n", path); + + FILE *f; + if (!strcmp(path, "-")) { + f = stdin; + } else { + char tmp[LINE_SIZE] = ""; + if (path[0] == '~') { + strcpy(tmp, getenv("HOME")); + strcat(tmp, path + 1); + } else { + strcpy(tmp, path); + } + f = fopen(tmp, "r"); + if (!f) return NULL; + } + + recipe * r = new_recipe(); + char line[LINE_SIZE] = ""; + char *dirc, *bname; + + dirc = strdup(path); + bname = strdup(path); + r->path = strdup(dirname(dirc)); + r->filename = strdup(basename(bname)); + free(dirc); free(bname); + + /* (re)init px */ + char error[LINE_SIZE] = ""; + int lino = 0; + enum parser_type type = TITLE; + + while (!next_escaped_line(f, line, &lino, error)) { + if (handle_line(line, r, error, &type, prev)) + break; + } + + if (strlen(error) > 0) { + fprintf(stderr, "%s:%d: %s\n", path, lino, error); + free_recipe(r); + return NULL; + } + + return r; +} |