#include #include "types.h" #include "util.h" recipe * new_recipe() { recipe * r; r = (recipe *) malloc(sizeof(recipe)); if (!r) die("Couldn't allocate memory for recipe"); r->n = 1; r->i = NULL; r->in = 0; r->s = NULL; r->sn = 0; r->r = NULL; r->rn = 0; r->filename = NULL; r->path = NULL; r->title = NULL; r->sha1[0] = '\0'; return r; } void new_subrecipe(recipe * r, recipe * src) { r->r = (recipe **)realloc(r->r, (r->rn + 1) * sizeof(recipe *)); if (!r->r) die("Couldn't allocate memory for subricepie"); r->r[r->rn] = src; r->rn++; } void new_item(recipe * r) { r->i = (item **)realloc(r->i, (r->in + 1) * sizeof(item *)); if (!r->i) die("Couldn't allocate memory for item"); r->i[r->in] = (item *)malloc(sizeof(item)); if (!r->i[r->in]) die("Couldn't allocate memory for item"); r->i[r->in]->name = NULL; r->i[r->in]->qty = NULL; r->in++; } void free_item(item * i) { if (!i) return; if (i->name) free(i->name); if (i->qty) free(i->qty); free(i); } void new_step(recipe * r) { r->s = (step **)realloc(r->s, (r->sn + 1) * sizeof(step *)); if (!r->s) die("Couldn't allocate memory for step"); r->s[r->sn] = (step *)malloc(sizeof(step)); if (!r->s[r->sn]) die("Couldn't allocate memory for step"); r->s[r->sn]->inst = NULL; r->s[r->sn]->duration = NULL; r->s[r->sn]->result = NULL; r->s[r->sn]->type = 0; r->sn++; } void free_step(step * s) { if (!s) return; if (s->inst) free(s->inst); if (s->duration) free(s->duration); if (s->result) free(s->result); free(s); } void free_recipe(recipe * r) { if (!r) return; if (r->i) { for (int i = 0; i < r->in; i++) free_item(r->i[i]); free(r->i); } if (r->s) { for (int i = 0; i < r->sn; i++) free_step(r->s[i]); free(r->s); } if (r->r) { for (int i = 0; i < r->rn; i++) free_recipe(r->r[i]); free(r->r); } if (r->filename) free(r->filename); if (r->path) free(r->path); if (r->title) free(r->title); free(r); } void pprint_items(recipe * r) { printf("Ingredients for %s:\n", r->title); for (int i = 0; i < r->in; i++) { if (strlen(r->i[i]->qty)) { printf("%s: %s\n", r->i[i]->name, r->i[i]->qty); } else { printf("%s\n", r->i[i]->name); } } printf("\n"); } char * last_n(const char * s, size_t n) { size_t length = strlen( s ); return ( char * )( length < n ? s : s + length - n ); } void listing(recipe * r, const char * fmt) { if (!strcmp(fmt, "title") || !strcmp(fmt, "t")) { printf("%s\n", r->title); } else if (!strcmp(fmt, "path") || !strcmp(fmt, "p")) { printf("%s/%s\n", r->path, r->filename); } else if (!strcmp(fmt, "hash") || !strcmp(fmt, "h")) { printf("%s\n", strcmp(r->sha1, "") ? r->sha1 : "neval"); } else if (!strcmp(fmt, "shorthash") || !strcmp(fmt, "s")) { printf("%.*s\n", 8, strcmp(r->sha1, "") ? r->sha1 : "neval"); } else { printf("%.*s %10.10s %d:%d\t%d\t%s\n", 8, (strcmp(r->sha1, "") ? r->sha1 : "neval"), basename(r->path), r->in, r->sn, r->rn, r->title); } } void show(recipe * r) { printf("Filename\t%s\n", r->filename); printf("Dirname\t\t%s\n", r->path); printf("Title\t\t%s\n\n", r->title); for (int i = 0; i < r->in; i++) printf("I:%s\t%s\n", r->i[i]->name, r->i[i]->qty); if (r->sn) { printf("\nSteps:\n~~~~~~\n\n"); for (int i = 0; i < r->sn; i++) printf("%s: %s\n", r->s[i]->type == PREP ? "PREP" : "COOK", r->s[i]->inst); } } void tojson(recipe * r) { printf("{\"filename\":\"%s\",", r->filename); printf("\"path\":\"%s\",", r->path); printf("\"title\":\"%s\",", r->title); 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; for (; i < r->rn - 1; i++) { tojson(r->r[i]); printf(","); } tojson(r->r[i]); printf("]"); } if (r->in) { printf(",\"ingredients\":{"); int i = 0; for (; i < r->in - 1; i++) printf("\"%s\":\"%s\",", r->i[i]->name, r->i[i]->qty); printf("\"%s\":\"%s\"}", r->i[i]->name, r->i[i]->qty); } if (r->sn) { printf(",\"steps\":["); int i = 0; for (; i < r->sn - 1; i++) printf("{\"inst\":\"%s\",\"duration\":\"%s\",\"result\":\"%s\",\"type\":\"%s\"},", r->s[i]->inst, r->s[i]->duration, r->s[i]->result, r->s[i]->type == 0 ? "prep" : (r->s[i]->type == 1 ? "cook" : "serve") ); printf("{\"inst\":\"%s\",\"duration\":\"%s\",\"result\":\"%s\",\"type\":\"%s\"}]", r->s[i]->inst, r->s[i]->duration, r->s[i]->result, r->s[i]->type == 0 ? "prep" : (r->s[i]->type == 1 ? "cook" : "serve") ); } printf("}"); } void tohtml(recipe * r) { printf("
\n"); printf("\n", r->path, r->filename); printf("

%s

\n\n", r->title); if (r->rn) { printf("\n"); } if (r->in) { printf("\n"); } if (r->sn) { printf("

---


\n"); printf("\n"); } printf("
\n
\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/%s\n", (r->sha1[0] != '\0') ? "# ": "", r->r[i]->path, r->r[i]->filename); } for (int i = 0; i < r->in; i++) { if (strlen(r->i[i]->qty)) { printf("%s = %s\n", r->i[i]->name, r->i[i]->qty); } else { printf("%s\n", r->i[i]->name); } } if (r->sn) { printf("\n---\n\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("%c %s\n", c, r->s[i]->inst); } } } void copy_metadata(recipe * dst, recipe * src) { if (!dst || !src) return; 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 copy_items(recipe * dst, recipe * src) { if (!dst || !src) return; for (int i = 0; i < src->in; i++) { new_item(dst); dst->i[dst->in - 1]->name = strdup(src->i[i]->name); dst->i[dst->in - 1]->qty = strdup(src->i[i]->qty); } } void copy_steps(recipe * dst, recipe * src) { if (!dst || !src) return; for (int i = 0; i < src->sn; i++) { new_step(dst); dst->s[dst->sn - 1]->inst = strdup(src->s[i]->inst); dst->s[dst->sn - 1]->type = src->s[i]->type; } } void copy_subrecipes(recipe * dst, recipe * src, int shallow_copy) { 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; if (!shallow_copy) { copy_items(r, src->r[i]); copy_steps(r, src->r[i]); } new_subrecipe(dst, r); } } static void join_subrecipe_items(recipe * dst, recipe * src) { if (!src || !dst) return; for (int i = 0; i < src->rn; i++) { join_subrecipe_items(dst, src->r[i]); for (int j = 0; j < src->r[i]->in; j++) { new_item(dst); dst->i[dst->in - 1]->name = strdup(src->r[i]->i[j]->name); if (src->r[i]->n > 1) { if (strcmp(src->r[i]->i[j]->qty, "")) { char tmp[2048] = ""; sprintf(tmp, "%d X ( %s )", src->r[i]->n, src->r[i]->i[j]->qty); dst->i[dst->in - 1]->qty = strdup(tmp); } else { dst->i[dst->in - 1]->qty = strdup(""); } } else { dst->i[dst->in - 1]->qty = strdup(src->r[i]->i[j]->qty); } } } } static void join_subrecipe_steps(recipe * dst, recipe * src) { if (!src || !dst) return; for (int i = 0; i < src->rn; i++) { join_subrecipe_steps(dst, src->r[i]); for (int j = 0; j < src->r[i]->sn; j++) { new_step(dst); dst->s[dst->sn - 1]->inst = strdup(src->r[i]->s[j]->inst); dst->s[dst->sn - 1]->type = src->r[i]->s[j]->type; } } } void merge_steps(recipe * dst, recipe * src) { /* Join all steps in src's subrecipes to dst */ join_subrecipe_steps(dst, src); /* Copy src steps as well to dst */ copy_steps(dst, src); } static int item_exists(const char * name, const recipe * r) { for (int i = 0; i < r->in; i++) if (!strcmp(r->i[i]->name, name)) return i; return -1; } /** * Sum all top level item qtys in `src` and add them to `dst` * making sure each item name exists only once. */ static void distinct_sum_items(recipe * dst, recipe * src) { if (!dst || !src) return; for (int i = 0; i < src->in; i++) { int n = item_exists(src->i[i]->name, dst); if (n != -1) { if (!strcmp(dst->i[n]->qty, "") && !strcmp(src->i[i]->qty, "")) { // noop } else if (!strcmp(dst->i[n]->qty, "") || !strcmp(src->i[i]->qty, "")) { char tmp[FOOD_MAX_ARRAY] = ""; strcat(tmp, dst->i[n]->qty); strcat(tmp, src->i[i]->qty); free(dst->i[n]->qty); dst->i[n]->qty = strdup(tmp); } else { char tmp[FOOD_MAX_ARRAY] = ""; strcat(tmp, dst->i[n]->qty); strcat(tmp, " + "); strcat(tmp, src->i[i]->qty); free(dst->i[n]->qty); dst->i[n]->qty = strdup(tmp); } } else { new_item(dst); dst->i[dst->in - 1]->name = strdup(src->i[i]->name); dst->i[dst->in - 1]->qty = strdup(src->i[i]->qty); } } } void merge_items(recipe * dst, recipe * src) { recipe * _r = new_recipe(); /* Join all items in src's subrecipes to tmp */ join_subrecipe_items(_r, src); /* Copy src items as well to tmp */ copy_items(_r, src); /* disticnt items to dst */ distinct_sum_items(dst, _r); free_recipe(_r); }