# HG changeset patch # User Michiel Broek # Date 1405195159 -7200 # Node ID 99c47a8a61cb386fa1c4d78a700220bb70023f65 # Parent e37b9c571a56856790ed8193ed407fa9815e9731 Added the mash sourcecode, this does nothing useful yet. diff -r e37b9c571a56 -r 99c47a8a61cb .hgignore --- a/.hgignore Sat Jul 12 19:22:13 2014 +0200 +++ b/.hgignore Sat Jul 12 21:59:19 2014 +0200 @@ -11,6 +11,7 @@ rc433/sniffer thermferm/thermferm dht11/dht11 +mash/mash syntax: glob *.o diff -r e37b9c571a56 -r 99c47a8a61cb mash/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/Makefile Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,65 @@ +# Makefile for the mbsePi-apps/thermferm. + +include ../Makefile.global + +SRCS = $(wildcard *.c) +HDRS = $(wildcard *.h) +OBJS = $(SRCS:.c=.o) +SLIBS = -lpthread +TARGET = mash +OTHER = Makefile + +############################################################################# + +.c.o: + ${CC} ${CFLAGS} ${INCLUDES} ${DEFINES} -c $< + +all: ${TARGET} + +mash: ${OBJS} ${SLIBS} + ${CC} -o mash ${OBJS} ${LDFLAGS} ${LIBS} ${SLIBS} + +clean: + rm -f ${TARGET} *.o *.h~ *.c~ core filelist Makefile.bak + +install: all + ${INSTALL} -c -s -g root -o root -m 0755 mash ${BINDIR} + +filelist: Makefile + BASE=`pwd`; \ + BASE=`basename $${BASE}`; \ + (for f in ${SRCS} ${HDRS} ${OTHER} ;do echo ${PACKAGE}-${VERSION}/$${BASE}/$$f; done) >filelist + +depend: + @rm -f Makefile.bak; \ + mv Makefile Makefile.bak; \ + sed -e '/^# DO NOT DELETE/,$$d' Makefile.bak >Makefile; \ + ${ECHO} '# DO NOT DELETE THIS LINE - MAKE DEPEND RELIES ON IT' \ + >>Makefile; \ + ${ECHO} '# Dependencies generated by make depend' >>Makefile; \ + for f in ${SRCS}; \ + do \ + ${ECHO} "Dependencies for $$f:\c"; \ + ${ECHO} "`basename $$f .c`.o:\c" >>Makefile; \ + for h in `sed -n -e \ + 's/^#[ ]*include[ ]*"\([^"]*\)".*/\1/p' $$f`; \ + do \ + ${ECHO} " $$h\c"; \ + ${ECHO} " $$h\c" >>Makefile; \ + done; \ + ${ECHO} " done."; \ + ${ECHO} "" >>Makefile; \ + done; \ + ${ECHO} '# End of generated dependencies' >>Makefile + +# DO NOT DELETE THIS LINE - MAKE DEPEND RELIES ON IT +# Dependencies generated by make depend +sensors.o: mash.h +lock.o: mash.h lock.h +beerxml.o: mash.h beerxml.h +lcd-pcf8574.o: mash.h lcd-pcf8574.h +mash.o: mash.h lock.h logger.h xutil.h beerxml.h +xutil.o: mash.h xutil.h +rdconfig.o: mash.h xutil.h +logger.o: mash.h xutil.h logger.h +# End of generated dependencies diff -r e37b9c571a56 -r 99c47a8a61cb mash/beerxml.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/beerxml.c Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,1531 @@ +/***************************************************************************** + * Copyright (C) 2014 + * + * Michiel Broek + * + * 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 EC-65K; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + +#include "mash.h" +#include "beerxml.h" + + +recipe_rec *recipes = NULL; + + +int parseStyle(xmlDocPtr, xmlNodePtr, style_rec **); +int parseEquipment(xmlDocPtr, xmlNodePtr, equipment_rec **); +int parseMashStep(xmlDocPtr, xmlNodePtr, mash_step **); +int parseMashSteps(xmlDocPtr, xmlNodePtr, mash_step **); +int parseMash(xmlDocPtr, xmlNodePtr, mash_profile **); +int parseFermentable(xmlDocPtr, xmlNodePtr, fermentable_rec **); +int parseFermentables(xmlDocPtr, xmlNodePtr, fermentable_rec **); + + + + +/* + * Parse style + */ +int parseStyle(xmlDocPtr doc, xmlNodePtr cur, style_rec **style) +{ + xmlChar *key; + style_rec *item; + float val; + + item = (style_rec *)malloc(sizeof(style_rec)); + item->name = item->category = item->category_number = item->style_letter = item-> style_guide = + item->type = item->notes = item->profile = item->ingredients = item->examples = NULL; + item->og_min = item->og_max = item->fg_min = item->fg_max = item->ibu_min = item->ibu_max = + item->color_min = item->color_max = item->carb_min = item->carb_max = + item->abv_min = item->abv_max = 0.0; + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (xmlStrcmp(key, (const xmlChar *)"1")) { + xmlFree(key); + return 1; + } + item->version = 1; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { + item->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CATEGORY"))) { + item->category = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CATEGORY_NUMBER"))) { + item->category_number = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"STYLE_LETTER"))) { + item->style_letter = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"STYLE_GUIDE"))) { + item->style_guide = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TYPE"))) { + item->type = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"OG_MIN"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->og_min = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"OG_MAX"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->og_max = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FG_MIN"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->fg_min = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FG_MAX"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->fg_max = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"IBU_MIN"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->ibu_min = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"IBU_MAX"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->ibu_max = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COLOR_MIN"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->color_min = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COLOR_MAX"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->color_max = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CARB_MIN"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->carb_min = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CARB_MAX"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->carb_max = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ABV_MIN"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->abv_min = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ABV_MAX"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->abv_max = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NOTES"))) { + item->notes = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"PROFILE"))) { + item->profile = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"INGREDIENTS"))) { + item->ingredients = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"EXAMPLES"))) { + item->examples = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + cur = cur->next; + } + + *style = item; + return 0; +} + + + +/* + * Parse equipment + */ +int parseEquipment(xmlDocPtr doc, xmlNodePtr cur, equipment_rec **equipment) +{ + xmlChar *key; + equipment_rec *item; + float val; + + item = (equipment_rec *)malloc(sizeof(equipment_rec)); + item->name = item->notes = NULL; + item->version = 0; + item->boil_size = item->batch_size = item->tun_volume = item->tun_weight = + item->tun_specific_heat = item->top_up_water = item->trub_chiller_loss = + item->evap_rate = item->boil_time = item->lauter_deadspace = + item->top_up_kettle = item->hop_utilization = 0.0; + item->calc_boil_volume = FALSE; + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (xmlStrcmp(key, (const xmlChar *)"1")) { + xmlFree(key); + return 1; + } + xmlFree(key); + item->version = 1; + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { + item->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BOIL_SIZE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->boil_size = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BATCH_SIZE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->batch_size = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TUN_VOLUME"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->tun_volume = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TUN_WEIGHT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->tun_weight = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TUN_SPECIFIC_HEAT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->tun_specific_heat = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TOP_UP_WATER"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->top_up_water = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TRUB_CHILLER_LOSS"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->trub_chiller_loss = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"EVAP_RATE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->evap_rate = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BOIL_TIME"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->boil_time = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CALC_BOIL_VOLUME"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (! xmlStrcmp(key, (const xmlChar *)"TRUE")) + item->calc_boil_volume = TRUE; + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"LAUTER_DEADSPACE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->lauter_deadspace = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HOP_UTILIZATION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->hop_utilization = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NOTES"))) { + item->notes = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + cur = cur->next; + } + + *equipment = item; + return 0; +} + + + +/* + * Parse a Mash step and add it to the linked list + */ +int parseMashStep(xmlDocPtr doc, xmlNodePtr cur, mash_step **mashstep) +{ + xmlChar *key; + mash_step *step, *tmp; + float val; + + cur = cur->xmlChildrenNode; + + step = (mash_step *)malloc(sizeof(mash_step)); + step->next = NULL; + step->name = NULL; + step->type = NULL; + step->infuse_amount = 0.0; + step->step_temp = 0.0; + step->end_temp = 0.0; + step->step_time = 0.0; + step->ramp_time = 0.0; + step->version = 0; + + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (xmlStrcmp(key, (const xmlChar *)"1")) { + xmlFree(key); + return 1; + } + step->version = 1; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { + step->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TYPE"))) { + step->type = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"INFUSE_AMOUNT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + step->infuse_amount = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"STEP_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + step->step_temp = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"STEP_TIME"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + step->step_time = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"END_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + step->end_temp = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"RAMP_TIME"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + step->ramp_time = val; + xmlFree(key); + } + + cur = cur->next; + } + + if (*mashstep == NULL) { + *mashstep = step; + } else { + for (tmp = *mashstep; tmp; tmp = tmp->next) { + if (tmp->next == NULL) { + tmp->next = step; + break; + } + } + } + return 0; +} + + + +/* + * Parse all Mash steps + */ +int parseMashSteps(xmlDocPtr doc, xmlNodePtr cur, mash_step **step) +{ + int rc; + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"MASH_STEP"))) { + if ((rc = parseMashStep(doc, cur, step))) + return 1; + } + cur = cur->next; + } + return 0; +} + + + +/* + * Parse Mash profile + */ +int parseMash(xmlDocPtr doc, xmlNodePtr cur, mash_profile **mash) +{ + xmlChar *key; + mash_profile *mashprofile; + float val; + + mashprofile = (mash_profile *)malloc(sizeof(mash_profile)); + mashprofile->name = NULL; + mashprofile->version = 0; + mashprofile->notes = NULL; + mashprofile->grain_temp = 0.0; + mashprofile->mash_steps = NULL; + mashprofile->tun_temp = 0.0; + mashprofile->sparge_temp = 0.0; + mashprofile->ph = 0.0; + mashprofile->tun_weight = 0.0; + mashprofile->tun_specific_heat = 0.0; + mashprofile->equip_adjust = FALSE; + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (xmlStrcmp(key, (const xmlChar *)"1")) { + xmlFree(key); + return 1; + } + mashprofile->version = 1; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { + mashprofile->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NOTES"))) { + mashprofile->notes = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"GRAIN_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + mashprofile->grain_temp = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TUN_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + mashprofile->tun_temp = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"SPARGE_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + mashprofile->sparge_temp = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"PH"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + mashprofile->ph = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TUN_WEIGHT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + mashprofile->tun_weight = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TUN_SPECIFIC_HEAT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + mashprofile->tun_specific_heat = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"EQUIP_ADJUST"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (! xmlStrcmp(key, (const xmlChar *)"TRUE")) + mashprofile->equip_adjust = TRUE; + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"MASH_STEPS"))) { + parseMashSteps(doc, cur, &(mashprofile)->mash_steps); + } + cur = cur->next; + } + + *mash = mashprofile; + return 0; +} + + + +/* + * Parse fermentable + */ +int parseFermentable(xmlDocPtr doc, xmlNodePtr cur, fermentable_rec **fr) +{ + xmlChar *key; + fermentable_rec *item, *tmp; + float val; + + item = (fermentable_rec *)malloc(sizeof(fermentable_rec)); + item->next = NULL; + item->name = item->type = item->notes = item->origin = item->supplier = NULL; + item->version = 0; + item->amount = item->yield = item->color = item->coarse_fine_diff = item->moisture = + item->diastatic_power = item->protein = item->max_in_batch = 0.0; + item->add_after_boil = item->recommend_mash = FALSE; + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (xmlStrcmp(key, (const xmlChar *)"1")) { + xmlFree(key); + return 1; + } + item->version = 1; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { + item->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NOTES"))) { + item->notes = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TYPE"))) { + item->type = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ORIGIN"))) { + item->origin = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"SUPPLIER"))) { + item->supplier = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AMOUNT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->amount = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"YIELD"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->yield = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COLOR"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->color = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ADD_AFTER_BOIL"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (! xmlStrcmp(key, (const xmlChar *)"TRUE")) + item->add_after_boil = TRUE; + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COARSE_FINE_DIFF"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->coarse_fine_diff = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"MOISTURE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->moisture = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"DIASTATIC_POWER"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->diastatic_power = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"PROTEIN"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->protein = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"MAX_IN_BATCH"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->max_in_batch = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"RECOMMEND_MASH"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (! xmlStrcmp(key, (const xmlChar *)"TRUE")) + item->recommend_mash = TRUE; + } + cur = cur->next; + } + + if (*fr == NULL) { + *fr = item; + } else { + for (tmp = *fr; tmp; tmp = tmp->next) { + if (tmp->next == NULL) { + tmp->next = item; + break; + } + } + } + + return 0; +} + + + +/* + * Parse fermentables + */ +int parseFermentables(xmlDocPtr doc, xmlNodePtr cur, fermentable_rec **fr) +{ + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FERMENTABLE"))) { + parseFermentable(doc, cur, fr); + } + cur = cur->next; + } + return 0; +} + + + +/* + * Parse hop + */ +int parseHop(xmlDocPtr doc, xmlNodePtr cur, hop_rec **hr) +{ + xmlChar *key; + hop_rec *item, *tmp; + float val; + + item = (hop_rec *)malloc(sizeof(hop_rec)); + item->next = NULL; + item->name = item->use = item->notes = item->type = item->form = + item->origin = item->substitutes = NULL; + item->version = 0; + item->alpha = item->amount = item->time = item->beta = item->hsi = + item->humulene = item->caryophyllene = item->cohumulone = item->myrcene = 0.0; + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (xmlStrcmp(key, (const xmlChar *)"1")) { + xmlFree(key); + return 1; + } + item->version = 1; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { + item->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ALPHA"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->alpha = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AMOUNT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->amount = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"USE"))) { + item->use = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TIME"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->time = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NOTES"))) { + item->notes = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TYPE"))) { + item->type = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FORM"))) { + item->form = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BETA"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->beta = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HSI"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->hsi = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ORIGIN"))) { + item->origin = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"SUBSTITITES"))) { + item->substitutes = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HUMULENE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->humulene = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CARYOPHYLLENE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->caryophyllene = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COHUMULONE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->cohumulone = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"MYRCENE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->myrcene = val; + xmlFree(key); + } + cur = cur->next; + } + + if (*hr == NULL) { + *hr = item; + } else { + for (tmp = *hr; tmp; tmp = tmp->next) { + if (tmp->next == NULL) { + tmp->next = item; + break; + } + } + } + + return 0; +} + + + +/* + * Parse hops + */ +int parseHops(xmlDocPtr doc, xmlNodePtr cur, hop_rec **hr) +{ + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HOP"))) { + parseHop(doc, cur, hr); + } + cur = cur->next; + } + return 0; +} + + + +/* + * Parse Yeast + */ +int parseYeast(xmlDocPtr doc, xmlNodePtr cur, yeast_rec **yr) +{ + xmlChar *key; + yeast_rec *item, *tmp; + float val; + int ival; + + item = (yeast_rec *)malloc(sizeof(yeast_rec)); + item->next = NULL; + item->name = item->type = item->form = item->laboratory = item->product_id = item->flocculation = item->notes = item->best_for = NULL; + item->amount = item->min_temperature = item->max_temperature = item->attenuation = 0.0; + item->amount_is_weight = item->add_to_secondary = FALSE; + item->version = item->times_cultured = item->max_reuse = 0; + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (xmlStrcmp(key, (const xmlChar *)"1")) { + xmlFree(key); + return 1; + } + item->version = 1; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { + item->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TYPE"))) { + item->type = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FORM"))) { + item->form = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AMOUNT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->amount = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AMOUNT_IS_WEIGHT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (! xmlStrcmp(key, (const xmlChar *)"TRUE")) + item->amount_is_weight = TRUE; + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"LABORATORY"))) { + item->laboratory = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"PRODUCT_ID"))) { + item->product_id = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"MIN_TEMPERATURE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->min_temperature = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"MAX_TEMPERATURE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->max_temperature = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FLOCCULATION"))) { + item->flocculation = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ATTENUATION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->attenuation = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NOTES"))) { + item->notes = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEST_FOR"))) { + item->best_for = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TIMES_CULTURED"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + item->times_cultured = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"MAX_REUSE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + item->max_reuse = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ADD_TO_SECONDARY"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (! xmlStrcmp(key, (const xmlChar *)"TRUE")) + item->add_to_secondary = TRUE; + } + cur = cur->next; + } + + if (*yr == NULL) { + *yr = item; + } else { + for (tmp = *yr; tmp; tmp = tmp->next) { + if (tmp->next == NULL) { + tmp->next = item; + break; + } + } + } + + return 0; +} + + + +/* + * Parse Yeasts + */ +int parseYeasts(xmlDocPtr doc, xmlNodePtr cur, yeast_rec **yr) +{ + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"YEAST"))) { + parseYeast(doc, cur, yr); + } + cur = cur->next; + } + return 0; +} + + + +/* + * Parse Misc + */ +int parseMisc(xmlDocPtr doc, xmlNodePtr cur, misc_rec **mr) +{ + xmlChar *key; + misc_rec *item, *tmp; + float val; + + item = (misc_rec *)malloc(sizeof(misc_rec)); + item->next = NULL; + item->name = item->type = item->use = item->use_for = item->notes = NULL; + item->amount = item->time = 0.0; + item->amount_is_weight = FALSE; + item->version = 0; + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (xmlStrcmp(key, (const xmlChar *)"1")) { + xmlFree(key); + return 1; + } + item->version = 1; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { + item->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TYPE"))) { + item->type = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"USE"))) { + item->use = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TIME"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->time = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AMOUNT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->amount = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AMOUNT_IS_WEIGHT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (! xmlStrcmp(key, (const xmlChar *)"TRUE")) + item->amount_is_weight = TRUE; + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"USE_FOR"))) { + item->use_for = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NOTES"))) { + item->notes = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + cur = cur->next; + } + + if (*mr == NULL) { + *mr = item; + } else { + for (tmp = *mr; tmp; tmp = tmp->next) { + if (tmp->next == NULL) { + tmp->next = item; + break; + } + } + } + + return 0; +} + + + +/* + * Parse Miscs + */ +int parseMiscs(xmlDocPtr doc, xmlNodePtr cur, misc_rec **mr) +{ + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"MISC"))) { + parseMisc(doc, cur, mr); + } + cur = cur->next; + } + return 0; +} + + + +/* + * Parse Water + */ +int parseWater(xmlDocPtr doc, xmlNodePtr cur, water_rec **wr) +{ + xmlChar *key; + water_rec *item, *tmp; + float val; + + item = (water_rec *)malloc(sizeof(water_rec)); + item->next = NULL; + item->name = item->notes = NULL; + item->amount = item->calcium = item->bicarbonate = item->sulfate = item->chloride = item->sodium = item->magnesium = item->ph = 0.0; + item->version = 0; + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (xmlStrcmp(key, (const xmlChar *)"1")) { + xmlFree(key); + return 1; + } + item->version = 1; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { + item->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AMOUNT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->amount = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CALCIUM"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->calcium = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BICARBONATE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->bicarbonate = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"SULFATE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->sulfate = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CHLORIDE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->chloride = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"SODIUM"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->sodium = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"MAGNESIUM"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->magnesium = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"PH"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + item->ph = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NOTES"))) { + item->notes = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + cur = cur->next; + } + + if (*wr == NULL) { + *wr = item; + } else { + for (tmp = *wr; tmp; tmp = tmp->next) { + if (tmp->next == NULL) { + tmp->next = item; + break; + } + } + } + + return 0; +} + + + +/* + * Parse Waters + */ +int parseWaters(xmlDocPtr doc, xmlNodePtr cur, water_rec **wr) +{ + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"WATER"))) { + parseWater(doc, cur, wr); + } + cur = cur->next; + } + return 0; +} + + + +/* + * Parse a recipe + */ +void parseRecipe(xmlDocPtr doc, xmlNodePtr cur) +{ + xmlChar *key; + recipe_rec *recipe, *tmp; + float val; + int ival; + + recipe = (recipe_rec *)malloc(sizeof(recipe_rec)); + recipe->next = NULL; + recipe->name = NULL; + recipe->brewer = NULL; + recipe->asst_brewer = NULL; + recipe->type = NULL; + recipe->style = NULL; + recipe->equipment = NULL; + recipe->batch_size = 0.0; + recipe->boil_time = 0.0; + recipe->efficiency = 0.0; + recipe->hops = NULL; + recipe->fermentables = NULL; + recipe->miscs = NULL; + recipe->yeasts = NULL; + recipe->waters = NULL; + recipe->notes = NULL; + recipe->taste_notes = NULL; + recipe->taste_rating = 0.0; + recipe->og = 0.0; + recipe->fg = 0.0; + recipe->fermentation_stages = 0; + recipe->primary_age = recipe->secondary_age = recipe->tertiary_age = recipe->age = 0; + recipe->primary_temp = recipe->secondary_temp = recipe->tertiary_temp = recipe->age_temp = 0.0; + recipe->date = NULL; + recipe->carbonation = 0.0; + recipe->forced_carbonation = FALSE; + recipe->priming_sugar_name = NULL; + recipe->carbonation_temp = 0.0; + recipe->priming_sugar_equiv = recipe->keg_priming_factor = 0.0; + + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { + recipe->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TYPE"))) { + recipe->type = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"STYLE"))) { + parseStyle(doc, cur, &(recipe)->style); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"EQUIPMENT"))) { + parseEquipment(doc, cur, &(recipe)->equipment); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BREWER"))) { + recipe->brewer = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ASST_BREWER"))) { + recipe->asst_brewer = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BATCH_SIZE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->batch_size = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BOIL_SIZE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->boil_size = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BOIL_TIME"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->boil_time = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"EFFICIENCY"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->efficiency = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HOPS"))) { + parseHops(doc, cur, &(recipe)->hops); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FERMENTABLES"))) { + parseFermentables(doc, cur, &(recipe)->fermentables); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"MISCS"))) { + parseMiscs(doc, cur, &(recipe)->miscs); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"YEASTS"))) { + parseYeasts(doc, cur, &(recipe)->yeasts); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"WATERS"))) { + parseWaters(doc, cur, &(recipe)->waters); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"MASH"))) { + parseMash(doc, cur, &(recipe)->mash); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"NOTES"))) { + recipe->notes = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TASTE_NOTES"))) { + recipe->taste_notes = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TASTE_RATING"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->taste_rating = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"OG"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->og = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FG"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->fg = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FERMENTATION_STAGES"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + recipe->fermentation_stages = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"PRIMARY_AGE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + recipe->primary_age = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"PRIMARY_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->primary_temp = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"SECONDARY_AGE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + recipe->secondary_age = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"SECONDARY_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->secondary_temp = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TERTIARY_AGE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + recipe->tertiary_age = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TERTIARY_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->tertiary_temp = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AGE"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + recipe->age = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AGE_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->age_temp = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"DATE"))) { + recipe->date = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CARBONATION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->carbonation = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FORCED_CARBONATION"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (! xmlStrcmp(key, (const xmlChar *)"TRUE")) + recipe->forced_carbonation = TRUE; + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"PRIMING_SUGAR_NAME"))) { + recipe->priming_sugar_name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CARBONATION_TEMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->carbonation_temp = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"PRIMING_SUGAR_EQUIV"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->priming_sugar_equiv = val; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"KEG_PRIMING_FACTOR"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &val) == 1) + recipe->keg_priming_factor = val; + xmlFree(key); + } + cur = cur->next; + } + + if (recipes == NULL) { + recipes = recipe; + } else { + for (tmp = recipes; tmp; tmp = tmp->next) { + if (tmp->next == NULL) { + tmp->next = recipe; + break; + } + } + } +} + + + +int parseBeerXML(char *docname) +{ + xmlDocPtr doc; + xmlNodePtr cur; + + if ((doc = xmlParseFile(docname)) == NULL) { + fprintf(stderr, "XML file %s errors.\n", docname); + return 1; + } + + if ((cur = xmlDocGetRootElement(doc)) == NULL) { + fprintf(stderr, "XML file %s empty.\n", docname); + xmlFreeDoc(doc); + return 1; + } + + if (xmlStrcmp(cur->name, (const xmlChar*)"RECIPES")) { + fprintf(stderr, "XML file %s is not a BeerXML file.\n", docname); + xmlFreeDoc(doc); + return 1; + } + + /* + * Parse all recipes + */ + cur = cur->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"RECIPE"))) { + parseRecipe(doc, cur); + } + cur = cur->next; + } + + xmlFreeDoc(doc); + return 0; +} + + + +/* + * For debugging purposes, dump the recipes + */ +void printBeerXML(void) +{ + recipe_rec *tmp0; + mash_step *tmp1; + fermentable_rec *tmp2; + equipment_rec *tmp3; + style_rec *tmp4; + hop_rec *tmp5; + yeast_rec *tmp6; + misc_rec *tmp7; + water_rec *tmp8; + int mashstep = 0; + float grain_weight = 0.0, water_grain_ratio, tun_water = 0.0, temp; + + for (tmp0 = recipes; tmp0; tmp0 = tmp0->next) { + printf("--------------------------------------------------------------------------------\n"); + printf("Recipe: %s\n", tmp0->name); + printf("Type: %s\n", tmp0->type); + if (tmp0->notes) + printf("Notes: %s\n", tmp0->notes); + if (tmp0->brewer || tmp0->asst_brewer) + printf("Brewer: %s Assistent brewer: %s\n", tmp0->brewer, tmp0->asst_brewer); + printf("Batch size: %5.1f Boil time: %5.1f Efficiency: %5.1f\n", tmp0->batch_size, tmp0->boil_time, tmp0->efficiency); + printf("Taste rate: %5.1f Taste notes: %s\n", tmp0->taste_rating, tmp0->taste_notes); + printf("Original G: %5.3f Final G: %5.3f\n", tmp0->og, tmp0->fg); + printf("\n"); + + if (tmp0->equipment) { + tmp3 = tmp0->equipment; + printf("Equipment: %s\n", tmp3->name); + if (tmp3->notes) + printf(" Notes: %s\n", tmp3->notes); + printf(" Boil size: %5.1f Batch size: %5.1f Boil time: %5.1f\n", tmp3->boil_size, tmp3->batch_size, tmp3->boil_time); + printf(" Tun volume: %5.1f Tun weight: %5.1f Specific heat: %5.1f\n", tmp3->tun_volume, tmp3->tun_weight, tmp3->tun_specific_heat); + printf(" TopUp: %5.1f Chiller loss: %5.1f Evaporation rate: %5.1f\n", tmp3->top_up_water, tmp3->trub_chiller_loss, tmp3->evap_rate); + printf(" Tun loss: %5.1f TopUp kettle: %5.1f Hop utilization: %5.1f\n", tmp3->lauter_deadspace, tmp3->top_up_kettle, tmp3->hop_utilization); + printf(" CalcBoilVol: %s\n", tmp3->calc_boil_volume ? "YES":"NO"); + printf("\n"); + } + + if (tmp0->style) { + tmp4 = tmp0->style; + printf("Style: %s-%s Type: %s\n", tmp4->style_letter, tmp4->name, tmp4->type); + if (tmp4->category || tmp4->category_number || tmp4->style_guide) + printf(" Category: %s Cat nr: %s Guide: %s\n", tmp4->category, tmp4->category_number, tmp4->style_guide); + printf(" OG: %5.3f - %5.3f\n", tmp4->og_min, tmp4->og_max); + printf(" FG: %5.3f - %5.3f\n", tmp4->fg_min, tmp4->fg_max); + printf(" Bittern: %5.1f - %5.1f IBU\n", tmp4->ibu_min, tmp4->ibu_max); + printf(" Color: %5.1f - %5.1f SRM\n", tmp4->color_min, tmp4->color_max); + printf(" Carb: %5.1f - %5.1f V-CO2\n", tmp4->carb_min, tmp4->carb_max); + printf(" ABV: %5.1f - %5.1f %%\n", tmp4->abv_min, tmp4->abv_max); + if (tmp4->notes) + printf(" Notes: %s\n", tmp4->notes); + if (tmp4->profile) + printf(" Profile: %s\n", tmp4->profile); + if (tmp4->examples) + printf(" Examples: %s\n", tmp4->examples); + printf("\n"); + } + + printf("Fermentables:\n"); + printf(" Type Name Kg Mash Moist\n"); + for (tmp2 = tmp0->fermentables; tmp2; tmp2 = tmp2->next) { + printf(" %-12s %-25s %6.3f %-5s %5.1f\n", tmp2->type, tmp2->name, tmp2->amount, + tmp2->recommend_mash ? "TRUE":"FALSE", tmp2->moisture); + if (tmp2->recommend_mash) + grain_weight += tmp2->amount; + } + printf("\n"); + + if (tmp0->hops) { + printf("Hops:\n"); + // 123456789 1234567890 1234567890123456789012345 123456 12345 12345 + printf(" Type Use Name Kg Time Alpha\n"); + for (tmp5 = tmp0->hops; tmp5; tmp5 = tmp5->next) { + printf(" %-9s %-10s %-25s %6.3f %5.1f %5.1f\n", tmp5->type, tmp5->use, tmp5->name, tmp5->amount, tmp5->time, tmp5->alpha); + } + printf("\n"); + } + + if (tmp0->miscs) { + printf("Miscs:\n"); + // 12345678901 123456789 1234567890123456789012345 123456 12345 12345 + printf(" Type Use Name Kg Time\n"); + for (tmp7 = tmp0->miscs; tmp7; tmp7 = tmp7->next) { + printf(" %-11s %-9s %-25s %6.3f %5.1f\n", tmp7->type, tmp7->use, tmp7->name, tmp7->amount, tmp7->time); + } + printf("\n"); + } + + if (tmp0->waters) { + printf("Wateren:\n"); + // 1234567890123456789012345 12345 12345 1234... + printf(" Name Ltrs PH Notes\n"); + for (tmp8 = tmp0->waters; tmp8; tmp8 = tmp8->next) { + printf(" %-25s %5.1f %5.1f %s\n", tmp8->name, tmp8->amount, tmp8->ph, tmp8->notes); + } + printf("\n"); + } + + printf("Mash profile: %s\n", tmp0->mash->name); + if (tmp0->mash->notes) + printf(" Notes: %s\n", tmp0->mash->notes); + printf(" Grain temp: %5.1f Tun temp: %5.1f Sparge temp: %5.1f\n", tmp0->mash->grain_temp, tmp0->mash->tun_temp, tmp0->mash->sparge_temp); + printf(" PH: %5.1f Tun weight: %6.2f Tun specific heat: %6.2f\n", tmp0->mash->ph, tmp0->mash->tun_weight, tmp0->mash->tun_specific_heat); + printf(" Equip adjust: %s\n", tmp0->mash->equip_adjust ? "TRUE":"FALSE"); + printf("\n"); + + if (tmp0->mash->mash_steps) { + mashstep = 0; + printf("st Name Type Temp Endtmp Dur Rmp Inf/Dec L/Kg\n"); + for (tmp1 = tmp0->mash->mash_steps; tmp1; tmp1 = tmp1->next) { + mashstep++; + tun_water += tmp1->infuse_amount; + water_grain_ratio = tun_water / grain_weight; + temp = tmp1->infuse_amount; +// if ((tmp1->infuse_amount == 0.0) && (tmp1->decotion_amt > 0.0)) +// temp = tmp1->decotion_amt; + printf("%2d %-16s %-12s %6.1f %6.1f %3.0f %3.0f %6.1f %6.2f\n", mashstep, tmp1->name, tmp1->type, + tmp1->step_temp, tmp1->end_temp, tmp1->step_time, tmp1->ramp_time, temp, water_grain_ratio); + } + printf("\n"); + } + + if (tmp0->yeasts) { + for (tmp6 = tmp0->yeasts; tmp6; tmp6 = tmp6->next) { + printf("Yeast: %s\n", tmp6->name); + printf(" Type: %-20s Form: %-20s Amount: %7.4f %s\n", + tmp6->type, tmp6->form, tmp6->amount, tmp6->amount_is_weight ? "Kg":"L"); + printf(" Lab: %-20s Product id: %-20s Flocculation: %s\n", tmp6->laboratory, tmp6->product_id, tmp6->flocculation); + printf(" Min temp: %5.1f Max temp: %5.1f Attenuation: %5.1f\n", + tmp6->min_temperature, tmp6->max_temperature, tmp6->attenuation); + if (tmp6->notes) + printf(" Notes: %s\n", tmp6->notes); + printf("\n"); + } + } + + printf("\n"); + } +} + + diff -r e37b9c571a56 -r 99c47a8a61cb mash/beerxml.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/beerxml.h Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,262 @@ +#ifndef _BEERXML_H +#define _BEERXML_H + + +/* + * See http://www.beerxml.com/beerxml.htm for more information. + * This standard isn't perfect, but it works. Note that some + * fields are not well defined. Some precentages are safe if you + * declare them as float instead of intergers. + * + * The function parseBeerXML() reads the file and puts the + * contents in the following tree: + * + * recipes recipe hops hop + * hop + * fermentables fermentable + * fermentable + * yeasts yeast + * yeast + * miscs misc + * misc + * waters water + * water + * style + * mash mash_step + * mash_step + * equipment + * recipe hops hop + * and so on. + */ + +typedef struct _hop_rec { + struct _hop_rec *next; + char *name; /* Name of the hops */ + int version; /* Should be 1 for this version of the standard */ + float alpha; /* Percent alpha of hops */ + float amount; /* Weight in Kilograms of the hops used. */ + char *use; /* "Boil", "Dry Hop", "Mash", "First Wort" or "Aroma" */ + float time; /* The time as measured in minutes. */ + char *notes; /* Textual notes about the hops. */ + char *type; /* "Bittering", "Aroma" or "Both" */ + char *form; /* "Pellet", "Plug" or "Leaf" */ + float beta; /* Hop beta percentage. */ + float hsi; /* Hop Stability Index */ + char *origin; /* Place of origin for the hops */ + char *substitutes; /* Substitutes that can be used for this hops */ + float humulene; /* Humulene level in percent. */ + float caryophyllene; /* Caryophyllene level in percent. */ + float cohumulone; /* Cohumulone level in percent */ + float myrcene; /* Myrcene level in percent */ +} hop_rec; + +typedef struct _fermentable_rec { + struct _fermentable_rec *next; + char *name; /* Name */ + int version; /* Record version */ + char *type; /* "Grain", "Sugar", "Extract", "Dry Extract" or "Adjunct" */ + char *notes; /* Notes */ + float amount; /* Weight in KG */ + float yield; /* Percent dry yield (fine grain) */ + float color; /* The color of the item in Lovibond */ + bool add_after_boil; /* May be TRUE. */ + char *origin; /* Country or place of origin */ + char *supplier; /* Supplier of the grain/extract/sugar */ + float coarse_fine_diff; /* Percent difference between the coarse grain yield and fine grain yield. */ + float moisture; /* Percent moisture in the grain. */ + float diastatic_power; /* The diastatic power of the grain */ + float protein; /* The percent protein in the grain. */ + float max_in_batch; /* The recommended maximum percentage */ + bool recommend_mash; /* TRUE if it is recommended to mash */ +} fermentable_rec; + +typedef struct _equipment_rec { + char *name; /* Name of the equipment profile */ + int version; /* Version of the equipment record. */ + float boil_size; /* The pre-boil volume used. */ + float batch_size; /* The target volume of the batch. */ + float tun_volume; /* Volume of the mash tun in liters. */ + float tun_weight; /* Weight of the mash tun in kilograms. */ + float tun_specific_heat; /* The specific heat of the mash tun. */ + float top_up_water; /* The amount of top up water normally added. */ + float trub_chiller_loss; /* The amount of wort normally lost. */ + float evap_rate; /* Perc of wort lost to evaporation per hour. */ + float boil_time; /* The normal amount of time one boils */ + bool calc_boil_volume; /* Flag denoting to calculate the boil size. */ + float lauter_deadspace; /* Amount lost to the lauter tun. */ + float top_up_kettle; /* Amount normally added to the boil */ + float hop_utilization; /* Large batch hop utilization. */ + char *notes; /* Notes associated with the equipment. */ +} equipment_rec; + +typedef struct _yeast_rec { + struct _yeast_rec *next; + char *name; /* Name of the yeast. */ + int version; /* Version of the standard. */ + char *type; /* “Ale”, “Lager”, “Wheat”, “Wine”, “Champagne” */ + char *form; /* “Liquid”, “Dry”, “Slant” or “Culture” */ + float amount; /* The amount of yeast, measured in liters. */ + bool amount_is_weight; /* TRUE if the amount measurement is a weight */ + char *laboratory; /* The laboratory that produced the yeast. */ + char *product_id; /* The manufacturer’s product ID label */ + float min_temperature; /* The minimum recommended temperature */ + float max_temperature; /* The maximum recommended temperature */ + char *flocculation; /* “Low”, “Medium”, “High” or “Very High” */ + float attenuation; /* Average attenuation for this yeast strain. */ + char *notes; /* Notes on this yeast strain. */ + char *best_for; /* Beerstyle this yeast is best suited for. */ + int times_cultured; /* Number of times this yeast has been reused */ + int max_reuse; /* Recommended maximum reuse times */ + bool add_to_secondary; /* Flag this yeast was added for a secondary */ +} yeast_rec; + +typedef struct _misc_rec { + struct _misc_rec *next; + char *name; /* Name of the misc item. */ + int version; /* Version number of this element. */ + char *type; /* “Spice”, “Fining”, “Water Agent”, “Herb”, “Flavor”, “Other” */ + char *use; /* “Boil”, “Mash”, “Primary”, “Secondary”, “Bottling” */ + float time; /* Time the misc was boiled, steeped, mashed, etc in minutes. */ + float amount; /* Amount of item used. Liters or Weight */ + bool amount_is_weight; /* TRUE if amount is weight */ + char *use_for; /* Desc. of what the ingredient is used for */ + char *notes; /* Detailed notes on the item including usage. */ +} misc_rec; + +typedef struct _water_rec { + struct _water_rec *next; + char *name; /* Name of the water profile */ + int version; /* Version of the water record. */ + float amount; /* Volume of water to use in a recipe in liters */ + float calcium; /* The amount of calcium (Ca) in ppm */ + float bicarbonate; /* The amount of bicarbonate (HCO3) in ppm. */ + float sulfate; /* The amount of Sulfate (SO4) in ppm. */ + float chloride; /* The amount of Chloride (Cl) in ppm. */ + float sodium; /* The amount of Sodium (Na) in ppm. */ + float magnesium; /* The amount of Magnesium (Mg) in ppm. */ + float ph; /* The PH of the water. */ + char *notes; /* Notes about the water profile. */ +} water_rec; + +typedef struct _style_rec { + char *name; /* Name of the style profile */ + char *category; /* Category that this style belongs to */ + int version; /* Version of the style record. */ + char *category_number; /* Number or identifier for this style category */ + char *style_letter; /* The specific style number or subcategory */ + char *style_guide; /* The name of the style guide */ + char *type; /* “Lager”, “Ale”, “Mead”, “Wheat”, “Mixed” or “Cider” */ + float og_min; /* Original gracity */ + float og_max; + float fg_min; /* Final gravity */ + float fg_max; + float ibu_min; /* Bitterness for this style as measured */ + float ibu_max; /* in International Bitterness Units (IBUs) */ + float color_min; /* The recommended color in SRM */ + float color_max; + float carb_min; /* The recommended carbonation for this style */ + float carb_max; /* in volumes of CO2 */ + float abv_min; /* Alcohol by volume as a percentage. */ + float abv_max; + char *notes; /* Description of the style, history */ + char *profile; /* Flavor and aroma profile for this style */ + char *ingredients; /* Suggested ingredients for this style */ + char *examples; /* Example beers of this style. */ +} style_rec; + +typedef struct _mash_step { + struct _mash_step *next; + char *name; /* Name of this step */ + int version; /* Record version */ + char *type; /* “Infusion”, “Temperature” or “Decoction” */ + float infuse_amount; /* Wateri vol. in liters to infuse in this step */ + float infuse_temp; /* niet officieel - berekenen */ + float step_temp; /* The target temperature for this step */ + float end_temp; /* The final temperature you can expect */ + float step_time; /* The number of minutes to spend */ + float ramp_time; /* Time to achieve the desired step temperature */ + float water_grain_ratio; /* niet officieel - berekenen */ + float decotion_amt; /* niet officieel */ +} mash_step; + +typedef struct _mash_profile { + char *name; /* Name of mash method */ + int version; /* Record version */ + char *notes; /* Additional notes */ + float grain_temp; /* The temperature of the grain */ + mash_step *mash_steps; /* List of mash steps */ + float tun_temp; /* Grain tun temperature */ + float sparge_temp; /* Temperature of the sparge water */ + float ph; /* PH of the sparge. */ + float tun_weight; /* Weight of the mash tun in kilograms */ + float tun_specific_heat; /* Specific heat of the tun material */ + bool equip_adjust; /* Adjust for tun heat loss */ +} mash_profile; + +typedef struct _brew_equipment { + char *name; /* Name of the equipment profile */ + float boil_size; /* The pre-boil volume for this equipment setup */ + float batch_size; /* The target volume of the batch */ + float tun_volume; /* Volume of the mash tun in liters. */ + float tun_weight; /* Weight of the mash tun in kilograms. */ + float tun_specific_heat; /* The specific heat of the mash tun. */ + float top_up_water; /* The amount of top up water b4 fermenting */ + float trub_chiller_loss; /* Amount of wort lost during cooling */ + float evap_rate; /* Perc of wort lost to evaporation per hour. */ + float boil_time; /* The normal amount of boil time for this eq. */ + bool calc_boil_volume; /* Should the program calculate the boil size. */ + float lauter_dead_space; /* Amount lost to the lauter tun. */ + float top_up_kettle; /* Amount added to the boil kettle b4 the boil. */ + float hop_utilization; /* Large batch hop utilization. */ + char *notes; /* Notes for this equipment. */ +} brew_equipment; + +typedef struct _recipe_rec { + struct _recipe_rec *next; + int version; /* Current version is 1 */ + char *name; /* Name of the recipe */ + char *type; /* “Extract”, “Partial Mash” or “All Grain” */ + style_rec *style; /* style record */ + equipment_rec *equipment; /* equipemnt record */ + char *brewer; /* Name of the brewer */ + char *asst_brewer; /* Name of the assistent brewer */ + float batch_size; /* Target size of the finished batch in liters. */ + float boil_size; /* Starting size for the main boil in liters */ + float boil_time; /* The total time to boil the wort in minutes. */ + float efficiency; /* The percent brewhouse efficiency */ + hop_rec *hops; /* hops */ + fermentable_rec *fermentables; /* Fermentables list */ + misc_rec *miscs; /* Misc ingredients */ + yeast_rec *yeasts; /* Yeasts */ + water_rec *waters; /* Water profiles */ + mash_profile *mash; /* Mash profile */ + char *notes; /* Notes associated with this recipe */ + char *taste_notes; /* Tasting notes – may be multiline. */ + float taste_rating; /* Number between zero and 50.0 (BJCP system) */ + float og; /* The measured original specific gravity. */ + float fg; /* The measured final gravity of the beer. */ + int fermentation_stages; /* The number of fermentation stages used */ + int primary_age; /* Time spent in the primary in days */ + float primary_temp; /* Temp in Celsius for the primary fermentation */ + int secondary_age; /* Time spent in the secondary in days. */ + float secondary_temp; /* Temp in C for the secondary fermentation. */ + int tertiary_age; /* Time spent in the third fermenter in days. */ + float tertiary_temp; /* Temperature in the tertiary fermenter. */ + int age; /* Time to age the beer in days after bottling. */ + float age_temp; /* Temperature for aging after bottling. */ + char *date; /* Date brewed in a easily recognizable format. */ + float carbonation; /* Volume of CO2 used to carbonate this beer. */ + bool forced_carbonation; /* TRUE if forced carbonated using CO2 pressure */ + char *priming_sugar_name; /* Text describing the priming agent. */ + float carbonation_temp; /* Temp for either bottling/forced carbonation. */ + float priming_sugar_equiv; /* Factor used to convert this priming agent */ + float keg_priming_factor; /* Used 2 factor amount of sugar for containers */ +} recipe_rec; + + +int parseBeerXML(char *); +void printBeerXML(void); + + +#endif + diff -r e37b9c571a56 -r 99c47a8a61cb mash/labview-lvm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/labview-lvm.c Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,93 @@ +/***************************************************************************** + * Copyright (C) 2008-2014 + * + * Michiel Broek + * + * 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 EC-65K; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + +#include "mash.h" +#include "xutil.h" +#include "labview-lvm.h" + + +char lvm_name[PATH_MAX] = ""; + + + +void create_lvm(char *filename, char *progname, char *data) +{ + FILE *lvm_file; + struct timeval now; + struct tm ptm; + int i; + + /* NOTE: in het voorbeeld wordt een , als separator gebruikt, hollandse locale ? */ + + snprintf(lvm_name, PATH_MAX, "/var/local/log/%s/%s", progname, filename); + + /* Rename excisting ... */ + + if (access(lvm_name, F_OK)) { + /* + * 2 options: + * Append, so return here. + * Rename file and create a new one + */ + } + + gettimeofday(&now, NULL); + localtime_r(&now.tv_sec, &ptm); + + if ((lvm_file = fopen(lvm_name, "w"))) { + + fprintf(lvm_file, "LabVIEW Measurement\r\n"); + fprintf(lvm_file, "Separator Tab\r\n"); + fprintf(lvm_file, "Decimal_Separator .\r\n"); + fprintf(lvm_file, "Writer_Version 2\r\n"); + fprintf(lvm_file, "Reader_Version 2\r\n"); + fprintf(lvm_file, "Multi_Headings No\r\n"); + fprintf(lvm_file, "Date %04d/%02d/%02d\r\n", ptm.tm_year + 1900, ptm.tm_mon + 1, ptm.tm_mday); + fprintf(lvm_file, "Time %02d:%02d:%02d.000\r\n", ptm.tm_hour, ptm.tm_min, ptm.tm_sec); + fprintf(lvm_file, "X_Columns One\r\n"); + fprintf(lvm_file, "Description Tcontrol Logfile\r\n"); + fprintf(lvm_file, "Time_Pref Absolute\r\n"); + fprintf(lvm_file, "Operator harrie\r\n"); + fprintf(lvm_file, "***End_of_Header***\r\n"); + fprintf(lvm_file, "\r\n"); + fprintf(lvm_file, "Channels 9\r\n"); + fprintf(lvm_file, "Samples 1 1 1 1 1 1 1 1 1\r\n"); + fprintf(lvm_file, "Date"); + for (i = 1; i <= 9; i++) + fprintf(lvm_file, " %04d/%02d/%02d", ptm.tm_year + 1900, ptm.tm_mon + 1, ptm.tm_mday); + fprintf(lvm_file, "\r\n"); + fprintf(lvm_file, "Time"); + for (i = 1; i <= 9; i++) + fprintf(lvm_file, " %02d:%02d:%02d.000", ptm.tm_hour, ptm.tm_min, ptm.tm_sec); + fprintf(lvm_file, "\r\n"); + fprintf(lvm_file, "X_Dimension Time Time Time Time Time Time Time Time Time\r\n"); + fprintf(lvm_file, "X0 0.0000000000000000E+0 0.0000000000000000E+0 0.0000000000000000E+0 0.0000000000000000E+0 0.0000000000000000E+0 0.0000000000000000E+0 0.0000000000000000E+0 0.0000000000000000E+0 0.0000000000000000E+0\r\n"); + fprintf(lvm_file, "Delta_X 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000\r\n"); + fprintf(lvm_file, "***End_of_Header***\r\n"); + fprintf(lvm_file, "X_Value Untitled Untitled 1 Untitled 2 Untitled 3 Untitled 4 Untitled 5 Untitled 6 Untitled 7 Untitled 8 Comment\r\n"); + } +} + + + + + diff -r e37b9c571a56 -r 99c47a8a61cb mash/labview-lvm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/labview-lvm.h Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,7 @@ +#ifndef _LABVIEW_LVM_H +#define _LABVIEW_LVM_H + + + + +#endif diff -r e37b9c571a56 -r 99c47a8a61cb mash/lcd-pcf8574.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/lcd-pcf8574.c Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,144 @@ +/* + * lcd-pcf8574.c: + * Text-based LCD driver library code + * This is designed to drive the HD44780U LCD display connected via + * a "LCM1602 IIC A0 A1 A2" board with a PCF8574 I2C controller. + * + * Copyright (c) 2012-2013 Gordon Henderson. + * Copyright (c) 2014 Michiel Broek. + *********************************************************************** + * + * mbsePi is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * mbsePi 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with wiringPi. If not, see . + *********************************************************************** + */ + +#include "mash.h" +#include "lcd-pcf8574.h" + + +#ifdef HAVE_WIRINGPI_H + +int lcdHandle; +unsigned char lcdbuf[MAX_LCDS][20][4]; + +struct lcdDataStruct +{ + int bits, rows, cols ; + int rsPin, strbPin ; + int dataPins [8] ; + int cx, cy ; +}; +extern struct lcdDataStruct *lcds [MAX_LCDS]; + + +/* + * Some LCD functions are extended shadow copies of the wiringPi functions. + * The difference is that the lcdbuf will be updated with the contents on + * the hardware display. This copy can then be used for a remote display + */ + + +/* + * setBacklight: + ********************************************************************************* + */ + +void setBacklight (int value) +{ + pinMode (AF_BACKLIGHT, OUTPUT) ; + digitalWrite (AF_BACKLIGHT, (value & 1)) ; +} + + + +/* + * initLCD: + ********************************************************************************* + */ + +int initLCD (int cols, int rows) +{ + int x, y; + + if (!((rows == 1) || (rows == 2) || (rows == 4))) { + fprintf (stderr, "rows must be 1, 2 or 4\n") ; + return EXIT_FAILURE ; + } + + if (!((cols == 16) || (cols == 20))) { + fprintf (stderr, "cols must be 16 or 20\n") ; + return EXIT_FAILURE ; + } + + pcf8574Setup(AF_BASE, 0x27) ; + pinMode (AF_RW, OUTPUT) ; + digitalWrite (AF_RW, LOW) ; // Not used with wiringPi - always in write mode + + /* + * The other control pins are initialised with lcdInit () + */ + lcdHandle = lcdInit (rows, cols, 4, AF_RS, AF_E, AF_DB4, AF_DB5, AF_DB6, AF_DB7, 0, 0, 0, 0) ; + if (lcdHandle < 0) { + fprintf (stderr, "lcdInit failed\n") ; + return -1 ; + } + + lcdClear (lcdHandle) ; + for (x = 0; x < 20; x++) + for (y = 0; y < 4; y++) + lcdbuf[lcdHandle][x][y] = ' '; + + setBacklight (1) ; + + return 0 ; +} + + + +void mb_lcdPutchar(const int fd, unsigned char data) +{ + struct lcdDataStruct *lcd = lcds[fd]; + + /* + * Write to our buffer first, then to the wiringPi driver. + * Writing to wiringPi updates the cursor position. + */ + lcdbuf[fd][lcd->cx][lcd->cy] = data; + lcdPutchar(fd, data); +} + + + +void mb_lcdPuts(const int fd, const char *string) +{ + while (*string) + mb_lcdPutchar (fd, *string++); +} + + + +void mb_lcdClear(const int fd) +{ + int x, y; + + lcdClear(fd); + for (x = 0; x < 20; x++) + for (y = 0; y < 4; y++) + lcdbuf[fd][x][y] = ' '; +} + + + +#endif + diff -r e37b9c571a56 -r 99c47a8a61cb mash/lcd-pcf8574.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/lcd-pcf8574.h Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,33 @@ +#ifndef _LCD_PCF8574_H +#define _LCD_PCF8574_H + + +#ifdef HAVE_WIRINGPI_H +/* wiringPi */ +#include +#include +#include + + +// Defines for the pcf8574 Pi LCD interface board +#define AF_BASE 100 + +#define AF_RS (AF_BASE + 0) +#define AF_RW (AF_BASE + 1) +#define AF_E (AF_BASE + 2) +#define AF_BACKLIGHT (AF_BASE + 3) +#define AF_DB4 (AF_BASE + 4) +#define AF_DB5 (AF_BASE + 5) +#define AF_DB6 (AF_BASE + 6) +#define AF_DB7 (AF_BASE + 7) + + +void setBacklight (int); +int initLCD (int, int); +void mb_lcdPutchar(const int, unsigned char); +void mb_lcdPuts(const int, const char *); +void mb_lcdClear(const int); + +#endif + +#endif diff -r e37b9c571a56 -r 99c47a8a61cb mash/lock.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/lock.c Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,134 @@ +/***************************************************************************** + * Copyright (C) 2014 + * + * Michiel Broek + * + * 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 EC-65K; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + +#include "mash.h" +#include "lock.h" + + +/* + * Put a lock on this program. + */ +int lockprog(char *name) +{ + char *tempfile, *lockfile; + FILE *fp; + pid_t oldpid; + + tempfile = calloc(PATH_MAX, sizeof(char)); + lockfile = calloc(PATH_MAX, sizeof(char)); + + snprintf(tempfile, PATH_MAX, "/var/run/%s.tmp", name); + snprintf(lockfile, PATH_MAX, "/var/run/%s.pid", name); + + if ((fp = fopen(tempfile, "w")) == NULL) { + perror(name); + printf("Can't create lockfile \"%s\"\n", tempfile); + free(tempfile); + free(lockfile); + return 1; + } + fprintf(fp, "%10u\n", getpid()); + fclose(fp); + + while (TRUE) { + if (link(tempfile, lockfile) == 0) { + unlink(tempfile); + free(tempfile); + free(lockfile); + return 0; + } + if ((fp = fopen(lockfile, "r")) == NULL) { + perror(name); + printf("Can't open lockfile \"%s\"\n", tempfile); + unlink(tempfile); + free(tempfile); + free(lockfile); + return 1; + } + if (fscanf(fp, "%u", &oldpid) != 1) { + perror(name); + printf("Can't read old pid from \"%s\"\n", tempfile); + fclose(fp); + unlink(tempfile); + free(tempfile); + free(lockfile); + return 1; + } + fclose(fp); + if (kill(oldpid,0) == -1) { + if (errno == ESRCH) { + printf("Stale lock found for pid %u\n", oldpid); + unlink(lockfile); + /* no return, try lock again */ + } else { + perror(name); + printf("Kill for %u failed\n",oldpid); + unlink(tempfile); + free(tempfile); + free(lockfile); + return 1; + } + } else { + printf("Another %s is already running, pid=%u\n", name, oldpid); + unlink(tempfile); + free(tempfile); + free(lockfile); + return 1; + } + } +} + + + +void ulockprog(char *name) +{ + char *lockfile; + pid_t oldpid; + FILE *fp; + + lockfile = calloc(PATH_MAX, sizeof(char)); + snprintf(lockfile, PATH_MAX, "/var/run/%s.pid", name); + + if ((fp = fopen(lockfile, "r")) == NULL) { + syslog(LOG_NOTICE, "Can't open lockfile \"%s\"", lockfile); + free(lockfile); + return; + } + + if (fscanf(fp, "%u", &oldpid) != 1) { + syslog(LOG_NOTICE, "Can't read old pid from \"%s\"", lockfile); + fclose(fp); + unlink(lockfile); + free(lockfile); + return; + } + + fclose(fp); + + if (oldpid == getpid()) { + (void)unlink(lockfile); + } + + free(lockfile); +} + + diff -r e37b9c571a56 -r 99c47a8a61cb mash/lock.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/lock.h Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,8 @@ +#ifndef _LOCK_H +#define _LOCK_H + + +int lockprog(char *); +void ulockprog(char *); + +#endif diff -r e37b9c571a56 -r 99c47a8a61cb mash/logger.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/logger.c Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,58 @@ +/***************************************************************************** + * Copyright (C) 2014 + * + * Michiel Broek + * + * 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 EC-65K; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + +#include "mash.h" +#include "xutil.h" +#include "logger.h" + + +void logger(char *filename, char *progname, char *data) +{ + struct timeval now; + struct tm ptm; + char *outstr = NULL, *name = NULL; + FILE *logfile; + + name = xstrcpy((char *)"/var/local/log/"); + name = xstrcat(name, progname); + name = xstrcat(name, (char *)"/"); + name = xstrcat(name, filename); + + gettimeofday(&now, NULL); + localtime_r(&now.tv_sec, &ptm); + + if ((logfile = fopen(name, "a+"))) { + outstr = calloc(10240, sizeof(char)); + snprintf(outstr, 10239, "%04d-%02d-%02d %02d:%02d,%s\n", ptm.tm_year + 1900, ptm.tm_mon + 1, ptm.tm_mday, ptm.tm_hour, ptm.tm_min, data); + fprintf(logfile, outstr); + fclose(logfile); + free(outstr); + outstr = NULL; + } else { + syslog(LOG_NOTICE, "logger: cannot open %s for writing", name); + } + + free(name); + name = NULL; +} + + diff -r e37b9c571a56 -r 99c47a8a61cb mash/logger.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/logger.h Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,6 @@ +#ifndef _LOGGER_H +#define _LOGGER_H + +void logger(char *, char *, char *); + +#endif diff -r e37b9c571a56 -r 99c47a8a61cb mash/mash.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/mash.c Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,330 @@ +/***************************************************************************** + * Copyright (C) 2014 + * + * Michiel Broek + * + * 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 EC-65K; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + +#include "mash.h" +#include "lock.h" +#include "logger.h" +#include "xutil.h" +#include "beerxml.h" + +#ifdef HAVE_WIRINGPI_H + + +int tempA = 80; +int tempB = 80; +int coolerA = 0; +int coolerB = 0; + +bool my_shutdown = false; +static pid_t pgrp, mypid; + +extern bool debug; +extern sys_config Config; +extern int lcdHandle; +extern unsigned char lcdbuf[MAX_LCDS][20][4]; +int lcdupdate; + + +void help(void); +void die(int); +void stopLCD(void); +int server(void); + + +void help(void) +{ + fprintf(stdout, "mbsePi-apps mash v%s starting\n\n", VERSION); + fprintf(stdout, "Usage: mash [-d] [-h]\n"); + fprintf(stdout, " -d --debug Debug and run in foreground\n"); + fprintf(stdout, " -h --help Display this help\n"); +} + + + +void die(int onsig) +{ + switch (onsig) { + case SIGHUP: syslog(LOG_NOTICE, "Got SIGHUP, shutting down"); + break; + case SIGINT: syslog(LOG_NOTICE, "Keyboard interrupt, shutting down"); + break; + case SIGTERM: syslog(LOG_NOTICE, "Got SIGTERM, shutting down"); + break; + default: syslog(LOG_NOTICE, "die() on signal %d", onsig); + } + + my_shutdown = true; +} + + + +void stopLCD(void) +{ + mb_lcdClear(lcdHandle); + setBacklight(0); +} + + + +int main(int argc, char *argv[]) +{ + int rc, c, i; + pid_t frk; + char buf[80]; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + {"debug", 0, 0, 'c'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "dh", long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'd': debug = true; + break; + case 'h': help(); + return 1; + } + } + + + parseBeerXML((char *)"/home/mbroek/Downloads/SalmoGold.xml"); + + return 0; + + openlog("mash", LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_USER); + syslog(LOG_NOTICE, "mbsePi-apps mash v%s starting", VERSION); + if (debug) + fprintf(stdout, "mbsePi-apps mash v%s starting\n", VERSION); + + if (rdconfig((char *)"mash.conf")) { + fprintf(stderr, "Error reading configuration\n"); + syslog(LOG_NOTICE, "halted"); + return 1; + } + + /* + * Catch all the signals we can, and ignore the rest. Note that SIGKILL can't be ignored + * but that's live. This daemon should only be stopped by SIGTERM. + * Don't catch SIGCHLD. + */ + for (i = 0; i < NSIG; i++) { + if ((i != SIGCHLD) && (i != SIGKILL) && (i != SIGSTOP)) + signal(i, (void (*))die); + } + + if (wiringPiSetup () ) + return 1; + + if ((rc = initLCD (16, 2))) { + fprintf(stderr, "Cannot initialize LCD display, rc=%d\n", rc); + return 1; + } + + lcdPosition(lcdHandle, 0, 0); + sprintf(buf, " Mash"); + mb_lcdPuts(lcdHandle, buf); + lcdPosition(lcdHandle, 0, 1); + sprintf(buf, " Version %s", VERSION); + mb_lcdPuts(lcdHandle, buf); + + if (debug) { + /* + * For debugging run in foreground. + */ + rc = server(); + } else { + /* + * Server initialization is complete. Now we can fork the + * daemon and return to the user. We need to do a setpgrp + * so that the daemon will no longer be assosiated with the + * users control terminal. This is done before the fork, so + * that the child will not be a process group leader. Otherwise, + * if the child were to open a terminal, it would become + * associated with that terminal as its control terminal. + */ + if ((pgrp = setpgid(0, 0)) == -1) { + syslog(LOG_NOTICE, "setpgpid failed"); + } + + frk = fork(); + switch (frk) { + case -1: + syslog(LOG_NOTICE, "Daemon fork failed: %s", strerror(errno)); + syslog(LOG_NOTICE, "Finished, rc=1"); + stopLCD(); + exit(1); + case 0: /* + * Run the daemon + */ + fclose(stdin); + if (open("/dev/null", O_RDONLY) != 0) { + syslog(LOG_NOTICE, "Reopen of stdin to /dev/null failed"); + _exit(2); + } + fclose(stdout); + if (open("/dev/null", O_WRONLY | O_APPEND | O_CREAT,0600) != 1) { + syslog(LOG_NOTICE, "Reopen of stdout to /dev/null failed"); + _exit(2); + } + fclose(stderr); + if (open("/dev/null", O_WRONLY | O_APPEND | O_CREAT,0600) != 2) { + syslog(LOG_NOTICE, "Reopen of stderr to /dev/null failed"); + _exit(2); + } + mypid = getpid(); + rc = server(); + break; + /* Not reached */ + default: + /* + * Here we detach this process and let the child + * run the deamon process. + */ + syslog(LOG_NOTICE, "Starting daemon with pid %d", frk); + exit(0); + } + } + + syslog(LOG_NOTICE, "Finished, rc=%d", rc); + return rc; +} + + + +int server(void) +{ + char buf[1024]; + w1_therm *tmp1; + int rc, run = 1, temp; + + if (lockprog((char *)"mash")) { + syslog(LOG_NOTICE, "Can't lock"); + return 1; + } + + rc = piThreadCreate(my_sensors_loop); + if (rc) { + fprintf(stderr, "my_sensors_loop thread didn't start rc=%d\n", rc); + syslog(LOG_NOTICE, "my_sensors_loop thread didn't start rc=%d", rc); + } + +// rc = piThreadCreate(my_server_loop); +// if (rc) { +// fprintf(stderr, "my_server_loop thread didn't start rc=%d\n", rc); +// syslog(LOG_NOTICE, "my_server_loop thread didn't start rc=%d", rc); +// } + + snprintf(buf, 1023, "temp,step,description"); + logger((char *)"mash.log", (char *)"mash", buf); + + do { + lcdupdate = FALSE; + + if (my_shutdown) + run = 0; + + tmp1 = Config.w1therms; +// if (((tmp1->lastval / 100) < (tempA - 5)) && (coolerA == 1)) { +// syslog(LOG_NOTICE, "Temperature A is %.1f, switched cooler off", (tmp1->lastval / 1000.0)); +// lcdupdate = TRUE; +// } +// if (((tmp1->lastval / 100) > (tempA + 5)) && (coolerA == 0)) { +// syslog(LOG_NOTICE, "Temperature A is %.1f, switched cooler on", (tmp1->lastval / 1000.0)); +// lcdupdate = TRUE; +// } + if (tmp1->update) { + tmp1->update = FALSE; + lcdupdate = TRUE; + } +// old1 = tmp1->next; +// tmp1 = old1; +// if (((tmp1->lastval / 100) < (tempB - 5)) && (coolerB == 1)) { +// syslog(LOG_NOTICE, "Temperature B is %.1f, switched cooler off", (tmp1->lastval / 1000.0)); +// lcdupdate = TRUE; +// } +// if (((tmp1->lastval / 100) > (tempB + 5)) && (coolerB == 0)) { +// syslog(LOG_NOTICE, "Temperature B is %.1f, switched cooler on", (tmp1->lastval / 1000.0)); +// lcdupdate = TRUE; +// } +// if (tmp1->update) { +// tmp1->update = FALSE; +// lcdupdate = TRUE; +// } + + if (run && lcdupdate) { + lcdPosition(lcdHandle, 0, 0); + tmp1 = Config.w1therms; + snprintf(buf, 16, "%4.1f %cC %s ", tmp1->lastval / 1000.0, 0xdf, tmp1->alias); + mb_lcdPuts(lcdHandle, buf); + temp = tmp1->lastval; +// old1 = tmp1->next; +// tmp1 = old1; + lcdPosition(lcdHandle, 0, 1); + snprintf(buf, 16, " "); + mb_lcdPuts(lcdHandle, buf); + snprintf(buf, 1023, "%.1f,%d,%s", temp / 1000.0, 1, (char *)"step description"); + logger((char *)"mash.log", (char *)"mash", buf); + } + usleep(100000); + + } while (run); + + if (debug) + fprintf(stdout, (char *)"Out of loop\n"); + + /* + * Give threads time to cleanup + */ + usleep(1500000); + + stopLCD(); + +// wrconfig((char *)"mash.conf"); + + ulockprog((char *)"mash"); + + if (debug) + fprintf(stdout, "Goodbye\n"); + + return 0; +} + +#else + + +int main(int argc, char *argv[]) +{ + parseBeerXML((char *)"/home/mbroek/Downloads/beerxml.xml"); + printBeerXML(); +// fprintf(stderr, "Compiled on a system without a wiringPi library.\n"); +// fprintf(stderr, "This program is useless and will do nothing.\n"); + return 0; +} + + +#endif diff -r e37b9c571a56 -r 99c47a8a61cb mash/mash.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/mash.h Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,82 @@ +#ifndef _MASH_H +#define _MASH_H + + +#define TRUE 1 +#define FALSE 0 + +#include "../config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* mosquitto */ +#include + +#define MBSE_SS(x) (x)?(x):"(null)" + + +/* rdconfig.c */ +typedef struct _key_list { + char *key; + int (*prc)(char **); + char **dest; +} key_list; + +typedef struct _w1_therm { + struct _w1_therm *next; + char *master; /* Master for this device */ + int bus; /* Reserved for ds2482-800 */ + char *name; /* Name of this device */ + char *alias; /* Friendly name */ + int present; /* 1=present, 0=absent */ + int lastval; /* Last valid value */ + int update; /* Value updated */ +} w1_therm; + +typedef struct _sys_config { + char *name; /* Configuration name */ + int my_port; /* my client/server port */ + w1_therm *w1therms; /* 1-wire temp sensors */ + int lcd_cols; /* LCD display columns */ + int lcd_rows; /* LCD display rows */ +} sys_config; + + +void killconfig(void); +int rdconfig(char *); +int wrconfig(char *); + + + +#ifdef HAVE_WIRINGPI_H + +PI_THREAD (my_sensors_loop); + +#endif + + +#endif + diff -r e37b9c571a56 -r 99c47a8a61cb mash/rdconfig.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/rdconfig.c Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,368 @@ +/***************************************************************************** + * Copyright (C) 2014 + * + * Michiel Broek + * + * 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 EC-65K; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + +#include "mash.h" +#include "xutil.h" + + +bool debug = FALSE; +static char *mypath; +static char *k, *v; +static int linecnt = 0; +sys_config Config; /* System configuration */ + + + +//static int getstr(char **); +static int getint(char **); +static int getw1(char **); +//static int getuch(char **); +//static int getfloat(char **); +//static int getbyt(char **); +//static int gethex(char **); + +#define XSTR(x) #x +#define STR(x) XSTR(x) + +/* + * System configuration table + */ +key_list keytab[] = { + {(char *)"w1therm", getw1, (char **)&Config.w1therms}, + {(char *)"lcd_cols", getint, (char **)&Config.lcd_cols}, + {(char *)"lcd_rows", getint, (char **)&Config.lcd_rows}, + {NULL, NULL, NULL} +}; + + + +void killconfig(void) +{ + w1_therm *tmp1, *old1; + + if (Config.name) + free(Config.name); + Config.name = NULL; + + for (tmp1 = Config.w1therms; tmp1; tmp1 = old1) { + old1 = tmp1->next; + if (tmp1->master) + free(tmp1->master); + if (tmp1->name) + free(tmp1->name); + if (tmp1->alias) + free(tmp1->alias); + free(tmp1); + } + Config.w1therms = NULL; + Config.my_port = 6554; + + Config.lcd_cols = 16; + Config.lcd_rows = 2; +} + + + +int wrconfig(char *config) +{ + int rc = 0; + FILE *fp; + w1_therm *tmp1; + + if (getenv((char *)"USER") == NULL) { + mypath = xstrcpy((char *)"/root"); + } else { + mypath = xstrcpy(getenv((char *)"HOME")); + } + mypath = xstrcat(mypath, (char *)"/mbsepi-apps/"); + mypath = xstrcat(mypath, config); + + if (debug) + fprintf(stdout, "Writing %s\n", mypath); + + if ((fp = fopen(mypath, "w")) == NULL) { + syslog(LOG_NOTICE, "could not rewrite %s", mypath); + return 1; + } + + fprintf(fp, "# Configuration file for thermferm %s\n", VERSION); + fprintf(fp, "\n"); + + fprintf(fp, "# LCD display\n"); + fprintf(fp, "#\n"); + fprintf(fp, "lcd_cols %d\n", Config.lcd_cols); + fprintf(fp, "lcd_rows %d\n", Config.lcd_rows); + fprintf(fp, "\n"); + + fprintf(fp, "# DS18B20 temperature sensors on the 1-wire bus.\n"); + fprintf(fp, "#\n"); + fprintf(fp, "# kwd master bus name alias\n"); + for (tmp1 = Config.w1therms; tmp1; tmp1 = tmp1->next) { + fprintf(fp, "w1therm %s %d %s %s\n", tmp1->master, tmp1->bus, tmp1->name, tmp1->alias); + } + fprintf(fp, "\n"); + + fprintf(fp, "# End of generated configuration\n"); + fclose(fp); + syslog(LOG_NOTICE, "Written %s rc=%d", mypath, rc); + free(mypath); + mypath = NULL; + + return rc; +} + + + +int rdconfig(char *config) +{ + char buf[256], *p; + FILE *fp; + int i, rc = 0; + + killconfig(); + + 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 *)"/mbsepi-apps/"); + mypath = xstrcat(mypath, config); + if ((fp = fopen(mypath, "r")) == NULL) { + /* + * Not in the users home directory + */ + free(mypath); + mypath = xstrcpy((char *)"/etc/mbsepi-apps/"); + mypath = xstrcat(mypath, config); + if ((fp = fopen(mypath, "r")) == NULL) { + /* + * Try /usr/local/etc + */ + free(mypath); + mypath = xstrcpy((char *)"/usr/local/etc/mbsepi-apps/"); + mypath = xstrcat(mypath, config); + if ((fp = fopen(mypath, "r")) == NULL) { + syslog(LOG_NOTICE, "rdconfig: could find %s", config); + return 1; + } + } + } + syslog(LOG_NOTICE, "rdconfig: using %s", mypath); + + linecnt = 0; + while (fgets(buf, sizeof(buf) -1, fp)) { + linecnt++; + if (*(p = buf + strlen(buf) -1) != '\n') { + syslog(LOG_NOTICE, "rdconfig: %s(%d): \"%s\" - line too long", mypath, linecnt, buf); + rc = 1; + break; + } + *p-- = '\0'; + while ((p >= buf) && isspace(*p)) + *p-- = '\0'; + k = buf; + while (*k && isspace(*k)) + k++; + p = k; + while (*p && !isspace(*p)) + p++; + *p++='\0'; + v = p; + while (*v && isspace(*v)) + v++; + + if ((*k == '\0') || (*k == '#')) { + continue; + } + + for (i = 0; keytab[i].key; i++) + if (strcasecmp(k,keytab[i].key) == 0) + break; + + if (keytab[i].key == NULL) { + syslog(LOG_NOTICE, "rdconfig: %s(%d): %s %s - unknown keyword", mypath, linecnt, MBSE_SS(k), MBSE_SS(v)); + rc = 1; + break; + } else if ((keytab[i].prc(keytab[i].dest))) { + rc = 1; + break; + } + + } + fclose(fp); + + free(mypath); + mypath = NULL; + + return rc; +} + + +/* +static int getstr(char **dest) +{ + if (debug) + syslog(LOG_NOTICE, "rdconfig: getstr: %s(%d): %s %s", mypath, linecnt, MBSE_SS(k), MBSE_SS(v)); + + *dest = xstrcpy(v); + return 0; +} +*/ + + +static int getint(char **dest) +{ + if (debug) + syslog(LOG_NOTICE, "rdconfig: getint: %s(%d): %s %s", mypath, linecnt, MBSE_SS(k), MBSE_SS(v)); + + if (strspn(v,"0123456789") != strlen(v)) + syslog(LOG_NOTICE, "rdconfig: %s(%d): %s %s - bad numeric", mypath, linecnt, MBSE_SS(k), MBSE_SS(v)); + else + *((int*)dest)=atoi(v); + return 0; +} + + + +static int getw1(char **dest) +{ + char *p, *q = NULL, *r = NULL; + w1_therm **tmpm; + int rc = 0, tmpp; + + for (p = v; *p && !isspace(*p); p++); + if (*p) + *p++ = '\0'; + while (*p && isspace(*p)) + p++; + if (*p == '\0') { + syslog(LOG_NOTICE, "rdconfig: %s(%d): less then two tokens", mypath, linecnt); + return 1; + } + + for (q = p; *q && !isspace(*q); q++); + if (*q && isspace(*q)) { + if (*q) + *q++ = '\0'; + while (*q && isspace(*q)) + q++; + + for (r = q; *r && !isspace(*r); r++); + if (*r) + *r++ = '\0'; + rc = sscanf(p, "%d", &tmpp); + if (rc != 1) { + syslog(LOG_NOTICE, "rdconfig: getw1: %s(%d): %s is not a integer value", mypath, linecnt, p); + return 1; + } + if (debug) + syslog(LOG_NOTICE, "rdconfig: getw1: %s(%d): %s %d %s %s", mypath, linecnt, v, tmpp, q, r); + } + + for (tmpm = (w1_therm**)dest; *tmpm; tmpm=&((*tmpm)->next)); + (*tmpm) = (w1_therm *) xmalloc(sizeof(w1_therm)); + (*tmpm)->next = NULL; + (*tmpm)->master = xstrcpy(v); + (*tmpm)->bus = tmpp; + (*tmpm)->name = xstrcpy(q); + (*tmpm)->alias = xstrcpy(r); + (*tmpm)->present = 0; + (*tmpm)->lastval = 0; + (*tmpm)->update = 0; + + return 0; +} + + +/* +static int getuch(char **dest) +{ + if (debug) + syslog(LOG_NOTICE, "rdconfig: getuch: %s(%d): %s %s", mypath, linecnt, MBSE_SS(k), MBSE_SS(v)); + + if (isalnum(v[0])) { + *((unsigned char*)dest) = v[0]; + } else { + syslog(LOG_NOTICE, "rdconfig: %s(%d): %s %s - bad character", mypath, linecnt, MBSE_SS(k), MBSE_SS(v)); + } + return 0; +} + + + +static int getfloat(char **dest) +{ + float val = 0.0; + int rc; + + if (debug) + syslog(LOG_NOTICE, "rdconfig: getfloat: %s(%d): %s %s", mypath, linecnt, MBSE_SS(k), MBSE_SS(v)); + + rc = sscanf(v, "%f", &val); + if (rc != 1) { + syslog(LOG_NOTICE, "rdconfig: %s(%d): %s %s - bad float value", mypath, linecnt, MBSE_SS(k), MBSE_SS(v)); + return 1; + } + *((float*)dest) = val; + + return 0; +} +*/ + +/* +static int getbyt(char **dest) +{ + Log_Msg("[rdconfig] getbyt: %s(%d): %s %s", mypath, linecnt, k, v); + if (strspn(v,"0123456789") != strlen(v)) + Log_Msg("[rdconfig] %s(%d): %s %s - bad numeric", mypath, linecnt, S(k), S(v)); + else + *((Uint8*)dest)=atoi(v); + return 0; +} +*/ + + +/* +static int gethex(char **dest) +{ + unsigned int val = 0; + int rc; + + Log_Msg("[rdconfig] gethex: %s(%d): %s %s", mypath, linecnt, k, v); + rc = sscanf(v, "%08x", &val); + if (rc != 1) { + Log_Msg("[rdconfig] %s(%d): %s %s - bad hex value", mypath, linecnt, S(k), S(v)); + return 1; + } + *((int*)dest) = val; + + return 0; +} +*/ + + diff -r e37b9c571a56 -r 99c47a8a61cb mash/sensors.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/sensors.c Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,134 @@ +/***************************************************************************** + * Copyright (C) 2014 + * + * Michiel Broek + * + * 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 EC-65K; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + +#include "mash.h" + +#ifdef HAVE_WIRINGPI_H + + +extern bool debug; +extern sys_config Config; +extern int my_shutdown; + + + +PI_THREAD (my_sensors_loop) +{ + w1_therm *tmp1, *old1; + char *device, line[60], *p = NULL; + FILE *fp; + int temp, rc, deviation; + + syslog(LOG_NOTICE, "Thread my_sensors_loop started"); + if (debug) + fprintf(stdout, "Thread my_sensors_loop started\n"); + + /* + * Loop forever until the external shutdown variable is set. + */ + for (;;) { + /* + * Here send our 1-wire sensors values + */ + for (tmp1 = Config.w1therms; tmp1; tmp1 = old1) { + old1 = tmp1->next; + + if (my_shutdown) { + syslog(LOG_NOTICE, "Thread my_sensors_loop stopped"); + if (debug) + fprintf(stdout, "Thread my_sensors_loop stopped\n"); + return 0; + } + + /* + * Build path to the on-wire sensor + */ + device = xstrcpy((char *)"/sys/bus/w1/devices/"); + device = xstrcat(device, tmp1->master); + device = xstrcat(device, (char *)"/"); + device = xstrcat(device, tmp1->name); + device = xstrcat(device, (char *)"/w1_slave"); + + /* + * Read sensor data + */ + if ((fp = fopen(device, "r"))) { + /* + * The output looks like: + * 72 01 4b 46 7f ff 0e 10 57 : crc=57 YES + * 72 01 4b 46 7f ff 0e 10 57 t=23125 + */ + fgets(line, 50, fp); + line[strlen(line)-1] = '\0'; + if ((line[36] == 'Y') && (line[37] == 'E')) { + /* + * CRC is Ok, continue + */ + fgets(line, 50, fp); + line[strlen(line)-1] = '\0'; + strtok(line, (char *)"="); + p = strtok(NULL, (char *)"="); + rc = sscanf(p, "%d", &temp); + if ((rc == 1) && (tmp1->lastval != temp)) { + /* + * It is possible to have read errors or extreme values. + * This can happen with bad connections so we compare the + * value with the previous one. If the difference is too + * much, we don't send that value. That also means that if + * the next value is ok again, it will be marked invalid too. + * Maximum error is 20 degrees for now. + */ + deviation = 20000; + if ( (tmp1->lastval == 0) || + (tmp1->lastval && (temp > (tmp1->lastval - deviation)) && (temp < (tmp1->lastval + deviation))) ) { + /* + * Temperature is changed and valid, set flag. + */ + tmp1->update = TRUE; + } else { + syslog(LOG_NOTICE, "deviation error deviation=%d, old=%d new=%d", deviation, tmp1->lastval, temp); + if (debug) { + fprintf(stdout, "deviation error deviation=%d, old=%d new=%d\n", deviation, tmp1->lastval, temp); + } + } + tmp1->lastval = temp; + } + } else { + syslog(LOG_NOTICE, "sensor %s/%s CRC error", tmp1->master, tmp1->name); + } + fclose(fp); + tmp1->present = 1; + } else { + tmp1->present = 0; + if (debug) + printf("sensor %s is missing\n", tmp1->name); + } + + free(device); + device = NULL; + } + } +} + + + +#endif diff -r e37b9c571a56 -r 99c47a8a61cb mash/xutil.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/xutil.c Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,73 @@ +/***************************************************************************** + * Copyright (C) 2008-2014 + * + * Michiel Broek + * + * 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 EC-65K; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + +#include "mash.h" +#include "xutil.h" + + +char *xmalloc(size_t size) +{ + char *tmp; + + tmp = malloc(size); + if (!tmp) + abort(); + + return tmp; +} + + + +char *xstrcpy(char *src) +{ + char *tmp; + + if (src == NULL) + return(NULL); + tmp = xmalloc(strlen(src)+1); + strcpy(tmp, src); + return tmp; +} + + + +char *xstrcat(char *src, char *add) +{ + char *tmp; + size_t size = 0; + + if ((add == NULL) || (strlen(add) == 0)) + return src; + if (src) + size = strlen(src); + size += strlen(add); + tmp = xmalloc(size + 1); + *tmp = '\0'; + if (src) { + strcpy(tmp, src); + free(src); + } + strcat(tmp, add); + return tmp; +} + + diff -r e37b9c571a56 -r 99c47a8a61cb mash/xutil.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mash/xutil.h Sat Jul 12 21:59:19 2014 +0200 @@ -0,0 +1,8 @@ +#ifndef _XUTIL_H +#define _XUTIL_H + +char *xmalloc(size_t); +char *xstrcpy(char *); +char *xstrcat(char *, char *); + +#endif