Thu, 17 Dec 2015 22:45:54 +0100
Completed the recipe editor.
/***************************************************************************** * Copyright (C) 2015 * * Michiel Broek <mbroek at mbse dot eu> * * This file is part of the mbsePi-apps * * This 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 2, or (at your option) any * later version. * * mbsePi-apps 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 ThermFerm; see the file COPYING. If not, write to the Free * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *****************************************************************************/ #include "brewco.h" #include "rdrecipes.h" #include "util.h" #include "xutil.h" extern int debug; a_recipe *recipes = NULL; const char SKIPYN[2][4] = { "NO", "YES" }; #define MY_ENCODING "utf-8" int do_wrrecipes(void); int do_wrrecipes(void) { int i, rc = 0; FILE *fp; char *mypath = NULL; a_recipe *recipe; xmlTextWriterPtr writer; xmlBufferPtr buf; /* * Create a new XML buffer, to which the XML document will be written */ if ((buf = xmlBufferCreate()) == NULL) { syslog(LOG_NOTICE, "wrrecipes: error creating the xml buffer"); return 1; } /* * Create a new XmlWriter for memory, with no compression. */ if ((writer = xmlNewTextWriterMemory(buf, 0)) == NULL) { syslog(LOG_NOTICE, "wrrecipes: error creating the xml writer"); return 1; } /* * Use indentation instead of one long line */ if ((rc = xmlTextWriterSetIndent(writer, 2)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error setting Indent"); return 1; } /* * Start the document with the xml default for the version, * encoding ISO 8859-1 and the default for the standalone * declaration. */ if ((rc = xmlTextWriterStartDocument(writer, NULL, MY_ENCODING, NULL)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterStartDocument"); return 1; } /* * Start an element named "BREWCO". Since thist is the first * element, this will be the root element of the document. */ if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "BREWCO")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterStartElement"); return 1; } if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "RECIPES")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterStartElement"); return 1; } for (recipe = recipes; recipe; recipe = recipe->next) { if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "RECIPE")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterStartElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", recipe->uuid)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "CODE", "%s", recipe->code)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", recipe->name)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "BOILTIME", "%d", recipe->boiltime)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "STARTTIME", "%d", (int)recipe->starttime)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "ENDTIME", "%d", (int)recipe->endtime)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } /* * 8 Mash steps */ if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "MASHSTEPS")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterStartElement"); return 1; } for (i =0; i < 8; i++) { if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "MASHSTEP")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterStartElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", recipe->mash[i].name)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MIN", "%d", recipe->mash[i].min)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MAX", "%d", recipe->mash[i].max)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "CANSKIP", "%s", recipe->mash[i].canskip ? "YES":"NO")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "SETPOINT", "%f", recipe->mash[i].setpoint)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "SKIP", "%s", recipe->mash[i].skip ? "YES":"NO")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "DURATION", "%d", recipe->mash[i].duration)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterEndElement(writer)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterEndElement"); return 1; } } if ((rc = xmlTextWriterEndElement(writer)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterEndElement"); return 1; } /* * 10 Hop additions */ if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "HOPADDITIONS")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterStartElement"); return 1; } for (i = 0; i < 10; i++) { if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "HOPADDITION")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterStartElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", recipe->hops[i].name)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "SKIP", "%s", recipe->hops[i].skip ? "YES":"NO")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "BOILTIME", "%d", recipe->hops[i].boiltime)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterEndElement(writer)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterEndElement"); return 1; } } if ((rc = xmlTextWriterEndElement(writer)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterEndElement"); return 1; } /* * 3 Hop stands */ if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "HOPSTANDS")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterStartElement"); return 1; } for (i = 0; i < 3; i++) { if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "HOPSTAND")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterStartElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", recipe->hopstand[i].name)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MIN", "%d", recipe->hopstand[i].min)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MAX", "%d", recipe->hopstand[i].max)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HOLD", "%s", recipe->hopstand[i].hold ? "YES":"NO")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "SETPOINT", "%f", recipe->hopstand[i].setpoint)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "SKIP", "%s", recipe->hopstand[i].skip ? "YES":"NO")) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "DURATION", "%d", recipe->hopstand[i].duration)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterWriteFormatElement"); return 1; } if ((rc = xmlTextWriterEndElement(writer)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterEndElement"); return 1; } } if ((rc = xmlTextWriterEndElement(writer)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterEndElement"); return 1; } /* * Close the element named RECIPE. */ if ((rc = xmlTextWriterEndElement(writer)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterEndElement"); return 1; } } /* * Close the element named RECIPES. */ if ((rc = xmlTextWriterEndElement(writer)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterEndElement"); return 1; } /* * All done, close any open elements */ if ((rc = xmlTextWriterEndDocument(writer)) < 0) { syslog(LOG_NOTICE, "wrrecipes: error at xmlTextWriterEndDocument"); return 1; } xmlFreeTextWriter(writer); /* * Now write the XML recipes */ if (getenv((char *)"USER") == NULL) { mypath = xstrcpy((char *)"/root"); } else { mypath = xstrcpy(getenv((char *)"HOME")); } mypath = xstrcat(mypath, (char *)"/.brewco/etc/"); mkdirs(mypath, 0755); mypath = xstrcat(mypath, (char *)"recipes.xml"); if (debug) fprintf(stdout, "Writing %s\n", mypath); if ((fp = fopen(mypath, "w")) == NULL) { syslog(LOG_NOTICE, "could not rewrite %s", mypath); free(mypath); return 1; } free(mypath); fprintf(fp, "%s", (const char *) buf->content); fclose(fp); xmlBufferFree(buf); return 0; } int wrrecipes(void) { int rc; rc = do_wrrecipes(); syslog(LOG_NOTICE, "Rewritten recipes, rc=%d", rc); return rc; } int parseRecipe(xmlDocPtr doc, xmlNodePtr cur) { xmlChar *key; xmlNodePtr s1cur, s2cur; a_recipe *recipe, *tmp; int i, j, ival; float fval; recipe = (a_recipe *)malloc(sizeof(a_recipe)); memset(recipe, 0, sizeof(a_recipe)); recipe->next = NULL; cur = cur->xmlChildrenNode; while (cur != NULL) { if ((!xmlStrcmp(cur->name, (const xmlChar *)"UUID"))) { recipe->uuid = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); } if ((!xmlStrcmp(cur->name, (const xmlChar *)"CODE"))) { recipe->code = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); } if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { recipe->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); } if ((!xmlStrcmp(cur->name, (const xmlChar *)"BOILTIME"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%d", &ival) == 1) recipe->boiltime = ival; xmlFree(key); } if ((!xmlStrcmp(cur->name, (const xmlChar *)"STARTTIME"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%d", &ival) == 1) recipe->starttime = (time_t)ival; xmlFree(key); } if ((!xmlStrcmp(cur->name, (const xmlChar *)"ENDTIME"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%d", &ival) == 1) recipe->endtime = (time_t)ival; xmlFree(key); } if ((!xmlStrcmp(cur->name, (const xmlChar *)"MASHSTEPS"))) { s1cur = cur->xmlChildrenNode; i = 0; while (s1cur != NULL) { if ((!xmlStrcmp(s1cur->name, (const xmlChar *)"MASHSTEP"))) { s2cur = s1cur->xmlChildrenNode; while (s2cur != NULL) { if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"NAME"))) { recipe->mash[i].name = (char *)xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"MIN"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%d", &ival) == 1) recipe->mash[i].min = ival; xmlFree(key); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"MAX"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%d", &ival) == 1) recipe->mash[i].max = ival; xmlFree(key); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"CANSKIP"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); for (j = 0; j < 2; j++) { if (! xmlStrcmp(key, (const xmlChar *)SKIPYN[j])) { recipe->mash[i].canskip = j; break; } } xmlFree(key); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"SETPOINT"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) recipe->mash[i].setpoint = fval; xmlFree(key); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"SKIP"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); for (j = 0; j < 2; j++) { if (! xmlStrcmp(key, (const xmlChar *)SKIPYN[j])) { recipe->mash[i].skip = j; break; } } xmlFree(key); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"DURATION"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%d", &ival) == 1) recipe->mash[i].duration = ival; xmlFree(key); } s2cur = s2cur->next; } i++; } s1cur = s1cur->next; } } if ((!xmlStrcmp(cur->name, (const xmlChar *)"HOPADDITIONS"))) { s1cur = cur->xmlChildrenNode; i = 0; while (s1cur != NULL) { if ((!xmlStrcmp(s1cur->name, (const xmlChar *)"HOPADDITION"))) { s2cur = s1cur->xmlChildrenNode; while (s2cur != NULL) { if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"NAME"))) { recipe->hops[i].name = (char *)xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"SKIP"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); for (j = 0; j < 2; j++) { if (! xmlStrcmp(key, (const xmlChar *)SKIPYN[j])) { recipe->hops[i].skip = j; break; } } xmlFree(key); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"BOILTIME"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%d", &ival) == 1) recipe->hops[i].boiltime = ival; xmlFree(key); } s2cur = s2cur->next; } i++; } s1cur = s1cur->next; } } if ((!xmlStrcmp(cur->name, (const xmlChar *)"HOPSTANDS"))) { s1cur = cur->xmlChildrenNode; i = 0; while (s1cur != NULL) { if ((!xmlStrcmp(s1cur->name, (const xmlChar *)"HOPSTAND"))) { s2cur = s1cur->xmlChildrenNode; while (s2cur != NULL) { if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"NAME"))) { recipe->hopstand[i].name = (char *)xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"MIN"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%d", &ival) == 1) recipe->hopstand[i].min = ival; xmlFree(key); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"MAX"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%d", &ival) == 1) recipe->hopstand[i].max = ival; xmlFree(key); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"HOLD"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); for (j = 0; j < 2; j++) { if (! xmlStrcmp(key, (const xmlChar *)SKIPYN[j])) { recipe->hopstand[i].hold = j; break; } } xmlFree(key); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"SETPOINT"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) recipe->hopstand[i].setpoint = fval; xmlFree(key); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"SKIP"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); for (j = 0; j < 2; j++) { if (! xmlStrcmp(key, (const xmlChar *)SKIPYN[j])) { recipe->hopstand[i].skip = j; break; } } xmlFree(key); } if ((!xmlStrcmp(s2cur->name, (const xmlChar *)"DURATION"))) { key = xmlNodeListGetString(doc, s2cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%d", &ival) == 1) recipe->hopstand[i].duration = ival; xmlFree(key); } s2cur = s2cur->next; } i++; } s1cur = s1cur->next; } } cur = cur->next; } if (recipes == NULL) { recipes = recipe; } else { for (tmp = recipes; tmp; tmp = tmp->next) { if (tmp->next == NULL) { tmp->next = recipe; break; } } } return 0; } int parseRecipes(xmlDocPtr doc, xmlNodePtr cur) { cur = cur->xmlChildrenNode; while (cur != NULL) { if ((!xmlStrcmp(cur->name, (const xmlChar *)"RECIPE"))) { parseRecipe(doc, cur); } cur = cur->next; } return 0; } /* * Returns: * 0 - All is well, recipes loaded. * 1 - Something went wrong */ int rdrecipes(void) { int i; char *mypath; xmlDocPtr doc; xmlNodePtr cur; a_recipe *recipe; syslog(LOG_NOTICE, "HOME='%s' USER='%s' LOGNAME='%s'", MBSE_SS(getenv((char *)"HOME")), MBSE_SS(getenv((char *)"USER")), MBSE_SS(getenv((char *)"LOGNAME"))); /* * Search config file */ if (getenv((char *)"USER") == NULL) { mypath = xstrcpy((char *)"/root"); } else { mypath = xstrcpy(getenv((char *)"HOME")); } mypath = xstrcat(mypath, (char *)"/.brewco/etc/"); mkdirs(mypath, 0755); mypath = xstrcat(mypath, (char *)"recipes.xml"); /* * Free possible old recipes list */ for (recipe = recipes; recipe; recipe = recipe->next) { if (recipe->uuid) free(recipe->uuid); if (recipe->code) free(recipe->code); if (recipe->name) free(recipe->name); for (i = 0; i < 8; i++) if (recipe->mash[i].name) free(recipe->mash[i].name); for (i = 0; i < 10; i++) if (recipe->hops[i].name) free(recipe->hops[i].name); for (i = 0; i < 3; i++) if (recipe->hopstand[i].name) free(recipe->hopstand[i].name); free(recipe); } recipes = NULL; /* * See if we have a recipes file. */ if (file_exist(mypath, W_OK)) { syslog(LOG_NOTICE, "rdrecipes: %s not found, good.", mypath); free(mypath); return 0; } if ((doc = xmlParseFile(mypath)) == NULL) { syslog(LOG_NOTICE, "rdrecipes: %s not found, we may need some.", mypath); free(mypath); return 0; } syslog(LOG_NOTICE, "rdrecipes: using %s", mypath); if ((cur = xmlDocGetRootElement(doc)) == NULL) { syslog(LOG_NOTICE, "XML file %s empty.", mypath); xmlFreeDoc(doc); return 1; } if (xmlStrcmp(cur->name, (const xmlChar*)"BREWCO")) { syslog(LOG_NOTICE, "XML file %s is not a valid configuration file.", mypath); xmlFreeDoc(doc); return 1; } /* * Parse recipes */ cur = cur->xmlChildrenNode; while (cur != NULL) { if ((!xmlStrcmp(cur->name, (const xmlChar *)"RECIPES"))) { parseRecipes(doc, cur); } cur = cur->next; } xmlFreeDoc(doc); free(mypath); mypath = NULL; return 0; }