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"); + } +} + +