void sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *data) { const char *node_name = mxmlGetElement(node); if (event == MXML_SAX_DATA) { if (! strcmp(current_tag[tag_ptr], "DistanceMeters")) { if (! strcmp(current_tag[tag_ptr-1], "Lap")) { const char *x = mxmlGetText(node, NULL); lap_distances[lap_count] = atof(x); lap_count++; } } } if (event == MXML_SAX_ELEMENT_OPEN) { tag_ptr++; strcpy(current_tag[tag_ptr], node_name); if(! strcmp(node_name, "Activity")) { const char *t = mxmlElementGetAttr(node, "Sport"); strcpy(sport, t); } } if (event == MXML_SAX_ELEMENT_CLOSE) { tag_ptr--; } }
static int l_get_name(lua_State *L) { mxml_node_t *node = l_checkMXML(L, 1); lua_pushstring(L, mxmlGetElement(node)); return 1; }
const char* IAPointsSerializer::retornoCarro_Callback(mxml_node_t *node, int where) { const char *name; name = mxmlGetElement(node); if (!strcmp(name,"IAPoints")) // Si la etiqueta es IAPoints le metemos un retorno de carro donde... if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_AFTER_CLOSE || where == MXML_WS_AFTER_OPEN) return ("\n"); if (!strcmp(name, "point")) // Si la etiqueta es point... { if (where == MXML_WS_BEFORE_OPEN) // Insertamos un tabulador antes de abrir <point> return ("\t"); if (where == MXML_WS_AFTER_OPEN || where == MXML_WS_BEFORE_CLOSE || where == MXML_WS_AFTER_CLOSE) // Insertamos retorno carro después de abrir etiqueta <point> return ("\n"); // y antes y después de cerrar <point> } return NULL; }
static const char* whitespaceCallback(mxml_node_t* node, int where) { const char* name = mxmlGetElement(node); if (where == MXML_WS_BEFORE_CLOSE) { if (!strcmp("tracks", name)) return NULL; return "\t"; } if (where == MXML_WS_AFTER_CLOSE) return "\n"; if (where == MXML_WS_BEFORE_OPEN) { if (!strcmp("key", name)) return "\t\t"; if (!strcmp("tracks", name)) return NULL; if (!strcmp("track", name)) return "\t"; if (!strcmp("group", name)) return "\t"; if (!strcmp("bookmark", name)) return "\t"; } if (where == MXML_WS_AFTER_OPEN) return "\n"; return NULL; }
// whitespace callback utility function used with mini-xml const char * mxmlWhitespaceCB(mxml_node_t *node, int loc) { const char *name; name = mxmlGetElement(node); if (loc == MXML_WS_BEFORE_OPEN) { // Single indentation if (!strcmp(name, "target") || !strcmp(name, "counters")) return("\n "); // Double indentation if (!strcmp(name, "counter")) return("\n "); // Avoid a carriage return on the first line of the xml file if (!strncmp(name, "?xml", 4)) return(NULL); // Default - no indentation return("\n"); } if (loc == MXML_WS_BEFORE_CLOSE) { // No indentation if (!strcmp(name, "captured")) return("\n"); // Single indentation if (!strcmp(name, "counters")) return("\n "); // Default - no carriage return return(NULL); } return(NULL); }
void main(void) { FILE *fp; char str[128]; const char *xml_path = "0:\\Zbin\\Utilities\\mxmltest\\test.xml"; fp = fopen(xml_path, "r"); if (fp == NULL) { sprintf(str, "Can't open file: %s", xml_path); ShowMSG(1, (int)str); return; } mxml_node_t *tree = mxmlLoadFile(NULL, fp, MXML_TEXT_CALLBACK); fclose(fp); if (tree == NULL) { ShowMSG(1, (int)"mxmlLoadFile = NULL"); return; } const char *log_path = "0:\\Zbin\\Log\\mxmltest.log"; fp = fopen(log_path, "a"); if (fp == NULL) { sprintf(str, "Can't open file: %s", log_path); ShowMSG(1, (int)str); mxmlDelete(tree); return; } mxml_node_t *node = tree; mxml_node_t *next = NULL; while (node != NULL) { switch (mxmlGetType(node)) { case MXML_ELEMENT: sprintf(str, "MXML_ELEMENT = %s\n", mxmlGetElement(node)); fwrite(str, sizeof(char), strlen(str), fp); break; case MXML_TEXT: sprintf(str, "MXML_TEXT = %s\n", mxmlGetText(node, 0)); fwrite(str, sizeof(char), strlen(str), fp); break; } next = mxmlGetFirstChild(node); if (next != NULL) { node = next; } else { next = mxmlGetNextSibling(node); if (next == NULL) { next = mxmlWalkNext(node, NULL, MXML_DESCEND); } node = next; } } fclose(fp); mxmlDelete(tree); }
std::unique_ptr<mxml_node_t, void (*)(mxml_node_t *)> getTree(lib::Span<const GatorCpu> clusters) { #include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len char path[PATH_MAX]; mxml_node_t *xml = NULL; FILE *fl; // Avoid unused variable warning (void) events_xml_len; // Load the provided or default events xml if (gSessionData.mEventsXMLPath) { strncpy(path, gSessionData.mEventsXMLPath, PATH_MAX); fl = lib::fopen_cloexec(path, "r"); if (fl) { xml = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK); if (xml == NULL) { logg.logError("Unable to parse %s", gSessionData.mEventsXMLPath); handleException(); } fclose(fl); } } if (xml == NULL) { logg.logMessage("Unable to locate events.xml, using default"); xml = mxmlLoadString(NULL, reinterpret_cast<const char *>(events_xml), MXML_NO_CALLBACK); } // Append additional events XML if (gSessionData.mEventsXMLAppend) { fl = lib::fopen_cloexec(gSessionData.mEventsXMLAppend, "r"); if (fl == NULL) { logg.logError("Unable to open additional events XML %s", gSessionData.mEventsXMLAppend); handleException(); } mxml_node_t *append = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK); if (append == NULL) { logg.logError("Unable to parse %s", gSessionData.mEventsXMLAppend); handleException(); } fclose(fl); mxml_node_t *events = mxmlFindElement(xml, xml, TAG_EVENTS, NULL, NULL, MXML_DESCEND); if (!events) { logg.logError("Unable to find <events> node in the events.xml, please ensure the first two lines of events XML starts with:\n" "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<events>"); handleException(); } XMLList *categoryList = NULL; XMLList *eventList = NULL; XMLList *counterSetList = NULL; { // Make list of all categories in xml mxml_node_t *node = xml; while (true) { node = mxmlFindElement(node, xml, TAG_CATEGORY, NULL, NULL, MXML_DESCEND); if (node == NULL) { break; } categoryList = new XMLList(categoryList, node); } // Make list of all events in xml node = xml; while (true) { node = mxmlFindElement(node, xml, TAG_EVENT, NULL, NULL, MXML_DESCEND); if (node == NULL) { break; } eventList = new XMLList(eventList, node); } // Make list of all counter_sets in xml node = xml; while (true) { node = mxmlFindElement(node, xml, TAG_COUNTER_SET, NULL, NULL, MXML_DESCEND); if (node == NULL) { break; } counterSetList = new XMLList(counterSetList, node); } } // Handle counter_sets for (mxml_node_t *node = strcmp(mxmlGetElement(append), TAG_COUNTER_SET) == 0 ? append : mxmlFindElement(append, append, TAG_COUNTER_SET, NULL, NULL, MXML_DESCEND), *next = mxmlFindElement(node, append, TAG_COUNTER_SET, NULL, NULL, MXML_DESCEND); node != NULL; node = next, next = mxmlFindElement(node, append, TAG_COUNTER_SET, NULL, NULL, MXML_DESCEND)) { const char * const name = mxmlElementGetAttr(node, ATTR_NAME); if (name == NULL) { logg.logError("Not all event XML counter_sets have the required name attribute"); handleException(); } // Replace any duplicate counter_sets bool replaced = false; for (XMLList *counterSet = counterSetList; counterSet != NULL; counterSet = counterSet->getPrev()) { const char * const name2 = mxmlElementGetAttr(counterSet->getNode(), ATTR_NAME); if (name2 == NULL) { logg.logError("Not all event XML nodes have the required title and name and parent name attributes"); handleException(); } if (strcmp(name, name2) == 0) { logg.logMessage("Replacing counter %s", name); mxml_node_t *parent = mxmlGetParent(counterSet->getNode()); mxmlDelete(counterSet->getNode()); mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node); counterSet->setNode(node); replaced = true; break; } } if (replaced) { continue; } // Add new counter_sets logg.logMessage("Appending counter_set %s", name); mxmlAdd(events, MXML_ADD_AFTER, mxmlGetLastChild(events), node); } // Handle events for (mxml_node_t *node = mxmlFindElement(append, append, TAG_EVENT, NULL, NULL, MXML_DESCEND), *next = mxmlFindElement(node, append, TAG_EVENT, NULL, NULL, MXML_DESCEND); node != NULL; node = next, next = mxmlFindElement(node, append, TAG_EVENT, NULL, NULL, MXML_DESCEND)) { const char * const category = mxmlElementGetAttr(mxmlGetParent(node), ATTR_NAME); const char * const title = mxmlElementGetAttr(node, ATTR_TITLE); const char * const name = mxmlElementGetAttr(node, ATTR_NAME); if (category == NULL || title == NULL || name == NULL) { logg.logError("Not all event XML nodes have the required title and name and parent name attributes"); handleException(); } // Replace any duplicate events for (XMLList *event = eventList; event != NULL; event = event->getPrev()) { const char * const category2 = mxmlElementGetAttr(mxmlGetParent(event->getNode()), ATTR_NAME); const char * const title2 = mxmlElementGetAttr(event->getNode(), ATTR_TITLE); const char * const name2 = mxmlElementGetAttr(event->getNode(), ATTR_NAME); if (category2 == NULL || title2 == NULL || name2 == NULL) { logg.logError("Not all event XML nodes have the required title and name and parent name attributes"); handleException(); } if (strcmp(category, category2) == 0 && strcmp(title, title2) == 0 && strcmp(name, name2) == 0) { logg.logMessage("Replacing counter %s %s: %s", category, title, name); mxml_node_t *parent = mxmlGetParent(event->getNode()); mxmlDelete(event->getNode()); mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node); event->setNode(node); break; } } } // Handle categories for (mxml_node_t *node = strcmp(mxmlGetElement(append), TAG_CATEGORY) == 0 ? append : mxmlFindElement(append, append, TAG_CATEGORY, NULL, NULL, MXML_DESCEND), *next = mxmlFindElement(node, append, TAG_CATEGORY, NULL, NULL, MXML_DESCEND); node != NULL; node = next, next = mxmlFindElement(node, append, TAG_CATEGORY, NULL, NULL, MXML_DESCEND)) { // After replacing duplicate events, a category may be empty if (mxmlGetFirstChild(node) == NULL) { continue; } const char * const name = mxmlElementGetAttr(node, ATTR_NAME); if (name == NULL) { logg.logError("Not all event XML category nodes have the required name attribute"); handleException(); } // Merge identically named categories bool merged = false; for (XMLList *category = categoryList; category != NULL; category = category->getPrev()) { const char * const name2 = mxmlElementGetAttr(category->getNode(), ATTR_NAME); if (name2 == NULL) { logg.logError("Not all event XML category nodes have the required name attribute"); handleException(); } if (strcmp(name, name2) == 0) { logg.logMessage("Merging category %s", name); while (true) { mxml_node_t *child = mxmlGetFirstChild(node); if (child == NULL) { break; } mxmlAdd(category->getNode(), MXML_ADD_AFTER, mxmlGetLastChild(category->getNode()), child); } merged = true; break; } } if (merged) { continue; } // Add new categories logg.logMessage("Appending category %s", name); mxmlAdd(events, MXML_ADD_AFTER, mxmlGetLastChild(events), node); } XMLList::free(eventList); XMLList::free(categoryList); XMLList::free(counterSetList); mxmlDelete(append); } // Resolve ${cluster} for (mxml_node_t *node = mxmlFindElement(xml, xml, TAG_EVENT, NULL, NULL, MXML_DESCEND), *next = mxmlFindElement( node, xml, TAG_EVENT, NULL, NULL, MXML_DESCEND); node != NULL; node = next, next = mxmlFindElement(node, xml, TAG_EVENT, NULL, NULL, MXML_DESCEND)) { const char *counter = mxmlElementGetAttr(node, ATTR_COUNTER); if (counter != NULL && strncmp(counter, CLUSTER_VAR, sizeof(CLUSTER_VAR) - 1) == 0) { for (const GatorCpu & cluster : clusters) { mxml_node_t *n = mxmlNewElement(mxmlGetParent(node), TAG_EVENT); copyMxmlElementAttrs(n, node); char buf[1 << 7]; snprintf(buf, sizeof(buf), "%s%s", cluster.getPmncName(), counter + sizeof(CLUSTER_VAR) - 1); mxmlElementSetAttr(n, ATTR_COUNTER, buf); } mxmlDelete(node); } } return {xml, &mxmlDelete}; }
//!!!!!!!!!!!!! yahoo int parse_weather_yahoo(weather_t *w, char *buffer_orig){ int code,retcode=0; char img_buf[MAX_BUF]; mxml_node_t *top, *tree, *node; const char *text; top = mxmlLoadString(NULL, buffer_orig, MXML_TEXT_CALLBACK); tree = mxmlFindElement(top, top, "item",NULL,NULL,MXML_DESCEND); node = mxmlFindElement(tree, top, "yweather:condition",NULL,NULL,MXML_DESCEND); text=mxmlElementGetAttr(node,"code"); code=get_code(text); mylog("Now code=%02d\n",code); if(code==NO_CODE) return RSS_RETRY; w->now_weather_text=codes[lang][code+1]; sprintf(img_buf,BASE_URL "%02d.gif",code); w->now_image_index=get_icon_index(img_buf); if(w->now_image_index==0) retcode=IMAGE_RETRY; else if(w->today_image_index<0) retcode=0; text=mxmlElementGetAttr(node,"temp"); sscanf(text,"%d",&(w->now_celsium)); node = mxmlFindElement(tree, top, "yweather:forecast",NULL,NULL,MXML_DESCEND); text=mxmlElementGetAttr(node,"code"); code=get_code(text); mylog("Today code=%02d\n",code); if(code==NO_CODE) return RSS_RETRY; w->today_forecast_text=codes[lang][code+1]; sprintf(img_buf,BASE_URL "%02d.gif",code); w->today_image_index=get_icon_index(img_buf); if(w->today_image_index==0) retcode=IMAGE_RETRY; else if(w->today_image_index<0) retcode=0; text=mxmlElementGetAttr(node,"low"); sscanf(text,"%d",&(w->today_celsium_low)); text=mxmlElementGetAttr(node,"high"); sscanf(text,"%d",&(w->today_celsium_high)); node = mxmlFindElement(node, top, "yweather:forecast",NULL,NULL,MXML_DESCEND); // node = mxmlFindElement(node, top, "yweather:forecast",NULL,NULL,MXML_NO_DESCEND); // node = mxmlWalkNext(node, top,MXML_NO_DESCEND); text=mxmlElementGetAttr(node,"code"); code=get_code(text); if(code==NO_CODE) return RSS_RETRY; w->tomorrow_forecast_text=codes[lang][code+1]; sprintf(img_buf,BASE_URL "%02d.gif",code); w->tomorrow_image_index=get_icon_index(img_buf); // w->tomorrow_image_index=code; if(w->tomorrow_image_index==0) retcode=IMAGE_RETRY; else if(w->today_image_index<0) retcode=0; text=mxmlElementGetAttr(node,"low"); sscanf(text,"%d",&(w->tomorrow_celsium_low)); text=mxmlElementGetAttr(node,"high"); sscanf(text,"%d",&(w->tomorrow_celsium_high)); #ifdef YAHOO_DYNAMIC_ICON_LOAD node = mxmlFindElement(tree, tree, "description",NULL,NULL,MXML_DESCEND); node=mxmlGetFirstChild(node); text=mxmlGetElement(node); img=strstr(text,"<img src=\""); if(img==NULL) return RSS_RETRY; img+=10; strncpy(img_buf,img,MAX_BUF); end=strstr(img_buf,"\""); if(end==NULL) return RSS_RETRY; *end='\0'; if(w->now_weather_icon==NULL) {w->now_weather_icon=malloc(1);w->now_weather_icon[0]='\0';} len=strlen(img_buf)+1; if(strncmp(w->now_weather_icon,img_buf,len)){ if(w->now_weather_icon) free(w->now_weather_icon); w->now_weather_icon=malloc(len); strncpy(w->now_weather_icon,img_buf,len); w->now_weather_icon[len]='\0'; w->now_image_index=get_icon_index(w->now_weather_icon); } #endif #ifdef DEBUG print_weather(w); #endif return retcode; }
static void parseXml(mxml_node_t* rootNode, TrackData* trackData) { struct track_key k; int g, i, foldedGroupCount = 0, is_key, track_index = 0; mxml_node_t* node = rootNode; free(trackData->bookmarks); trackData->bookmarks = NULL; trackData->bookmarkCount = 0; trackData->highlightRowStep = 8; // Traverse the tracks node data while (1) { node = mxmlWalkNext(node, rootNode, MXML_DESCEND); if (!node) break; switch (mxmlGetType(node)) { case MXML_ELEMENT: { const char* element_name = mxmlGetElement(node); if (!strcmp("bookmark", element_name)) { const char* row = mxmlElementGetAttr(node, "row"); if (row) TrackData_toggleBookmark(trackData, atoi(row)); } if (!strcmp("group", element_name)) { s_foldedGroupNames[foldedGroupCount++] = strdup(mxmlElementGetAttr(node, "name")); } if (!strcmp("tracks", element_name)) { const char* start_row = mxmlElementGetAttr(node, "startRow"); const char* end_row = mxmlElementGetAttr(node, "endRow"); const char* hlrow_step = mxmlElementGetAttr(node, "highlightRowStep"); if (start_row) trackData->startRow = atoi(start_row); if (end_row) trackData->endRow = atoi(end_row); if (hlrow_step) trackData->highlightRowStep = atoi(hlrow_step); } if (!strcmp("track", element_name)) { int i; struct sync_track* track; Track* t; // TODO: Create the new track/channel here const char* track_name = mxmlElementGetAttr(node, "name"); const char* color_text = mxmlElementGetAttr(node, "color"); const char* folded_text = mxmlElementGetAttr(node, "folded"); track_index = TrackData_createGetTrack(trackData, track_name); t = &trackData->tracks[track_index]; track = trackData->syncData.tracks[track_index]; if (!color_text && t->color == 0) { t->color = TrackData_getNextColor(trackData); } else { if (color_text) t->color = strtoul(color_text, 0, 16); } if (folded_text) { if (folded_text[0] == '1') t->folded = true; } // If we already have this track loaded we delete all the existing keys for (i = 0; i < track->num_keys; ++i) { int row = track->keys[i].row; RemoteConnection_sendDeleteKeyCommand(track->name, row); } free(track->keys); track->keys = 0; track->num_keys = 0; } else if (!strcmp("key", element_name)) { struct sync_track* track = trackData->syncData.tracks[track_index]; const char* row = mxmlElementGetAttr(node, "row"); const char* value = mxmlElementGetAttr(node, "value"); const char* interpolation = mxmlElementGetAttr(node, "interpolation"); k.row = atoi(row); k.value = (float)(atof(value)); k.type = (atoi(interpolation)); is_key = is_key_frame(track, k.row); assert(!is_key); sync_set_key(track, &k); RemoteConnection_sendSetKeyCommand(track->name, &k); } } default: break; } } TrackData_linkGroups(trackData); // Apply fold status on the groups for (i = 0; i < foldedGroupCount; ++i) { for (g = 0; g < trackData->groupCount; ++g) { Group* group = &trackData->groups[g]; const char* groupName = group->name; const char* foldedName = s_foldedGroupNames[i]; // groups with 1 track is handled as non-grouped if (group->trackCount == 1) continue; if (!strcmp(foldedName, groupName)) { trackData->groups[g].folded = true; break; } } free(s_foldedGroupNames[i]); } trackData->tracks[0].selected = true; }
//区域编号 节点编号 功能编号 (家电名称 家电类型 (空调等等 没有均为NULL)) int xml_add_node(char * area, char *node, char * fun, char *app, char * app_type) { mxml_node_t * xml = xml_root; mxml_node_t * xml_area; mxml_node_t * xml_node; mxml_node_t * xml_fun; mxml_node_t * xml_app; mxml_node_t * xml_app_type; const char * pelm; int ret = -1; XML_LOCK(); do { xml_area = mxmlFindElement(xml, xml, area, NULL, NULL, MXML_DESCEND_FIRST);//超找area区域 if (xml_area == NULL) break; xml_node = mxmlFindElement(xml_area, xml, node, NULL, NULL, MXML_DESCEND_FIRST);//查看是否有node节点 if (xml_node == NULL) { //没有找到添加 xml_node = mxmlNewElement(xml_area, node); //添加node节点 if (xml_node == NULL) break; ret = 0; } if (fun == NULL) break; //找到节点 xml_fun = mxmlFindElement(xml_node, xml, fun, NULL, NULL , MXML_DESCEND_FIRST);//查看是否已经有了 if (xml_fun == NULL) { //没有此功能号 xml_fun = mxmlNewElement(xml_node, fun); //添加fun功能结合节点 if (xml_fun == NULL) { ret = -1; break; } ret = 0; } if (app == NULL || app_type == NULL) break; pelm = mxmlGetElement(xml_fun); if (!strcmp(pelm, APPLIANCE)) { //名称匹配 是否是zigbee 转红外模块 //家电 xml_app = mxmlFindElement(xml_fun, xml, fun, NULL, NULL , MXML_DESCEND_FIRST);//查找app节点 if (xml_app == NULL) { //节点不存在则创建节点 xml_app = mxmlNewElement(xml_fun, app); if (xml_app == NULL) { ret = -1; break; } } xml_app_type = mxmlFindElement(xml_app, xml, app_type, NULL, NULL, MXML_DESCEND_FIRST);//查找app_type节点 if (xml_app_type == NULL) { //节点不存在创建节点 xml_app_type = mxmlNewElement(xml_fun, app_type); if (xml_app_type == NULL) ret = -1; break; } } } while (0); XML_UNLOCK(); return ret; }
mxml_node_t *EventsXML::getTree() { #include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len char path[PATH_MAX]; mxml_node_t *xml = NULL; FILE *fl; // Avoid unused variable warning (void)events_xml_len; // Load the provided or default events xml if (gSessionData->mEventsXMLPath) { strncpy(path, gSessionData->mEventsXMLPath, PATH_MAX); fl = fopen_cloexec(path, "r"); if (fl) { xml = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK); fclose(fl); } } if (xml == NULL) { logg->logMessage("Unable to locate events.xml, using default"); xml = mxmlLoadString(NULL, (const char *)events_xml, MXML_NO_CALLBACK); } // Append additional events XML if (gSessionData->mEventsXMLAppend) { fl = fopen_cloexec(gSessionData->mEventsXMLAppend, "r"); if (fl == NULL) { logg->logError("Unable to open additional events XML %s", gSessionData->mEventsXMLAppend); handleException(); } mxml_node_t *append = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK); fclose(fl); mxml_node_t *events = mxmlFindElement(xml, xml, "events", NULL, NULL, MXML_DESCEND); if (!events) { logg->logError("Unable to find <events> node in the events.xml, please ensure the first two lines of events XML starts with:\n" "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<events>"); handleException(); } XMLList *categoryList = NULL; XMLList *eventList = NULL; { // Make list of all categories in xml mxml_node_t *node = xml; while (true) { node = mxmlFindElement(node, xml, "category", NULL, NULL, MXML_DESCEND); if (node == NULL) { break; } categoryList = new XMLList(categoryList, node); } // Make list of all events in xml node = xml; while (true) { node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND); if (node == NULL) { break; } eventList = new XMLList(eventList, node); } } // Handle events for (mxml_node_t *node = mxmlFindElement(append, append, "event", NULL, NULL, MXML_DESCEND), *next = mxmlFindElement(node, append, "event", NULL, NULL, MXML_DESCEND); node != NULL; node = next, next = mxmlFindElement(node, append, "event", NULL, NULL, MXML_DESCEND)) { const char *const category = mxmlElementGetAttr(mxmlGetParent(node), "name"); const char *const title = mxmlElementGetAttr(node, "title"); const char *const name = mxmlElementGetAttr(node, "name"); if (category == NULL || title == NULL || name == NULL) { logg->logError("Not all event XML nodes have the required title and name and parent name attributes"); handleException(); } // Replace any duplicate events for (XMLList *event = eventList; event != NULL; event = event->getPrev()) { const char *const category2 = mxmlElementGetAttr(mxmlGetParent(event->getNode()), "name"); const char *const title2 = mxmlElementGetAttr(event->getNode(), "title"); const char *const name2 = mxmlElementGetAttr(event->getNode(), "name"); if (category2 == NULL || title2 == NULL || name2 == NULL) { logg->logError("Not all event XML nodes have the required title and name and parent name attributes"); handleException(); } if (strcmp(category, category2) == 0 && strcmp(title, title2) == 0 && strcmp(name, name2) == 0) { logg->logMessage("Replacing counter %s %s: %s", category, title, name); mxml_node_t *parent = mxmlGetParent(event->getNode()); mxmlDelete(event->getNode()); mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node); event->setNode(node); break; } } } // Handle categories for (mxml_node_t *node = strcmp(mxmlGetElement(append), "category") == 0 ? append : mxmlFindElement(append, append, "category", NULL, NULL, MXML_DESCEND), *next = mxmlFindElement(node, append, "category", NULL, NULL, MXML_DESCEND); node != NULL; node = next, next = mxmlFindElement(node, append, "category", NULL, NULL, MXML_DESCEND)) { // After replacing duplicate events, a category may be empty if (mxmlGetFirstChild(node) == NULL) { continue; } const char *const name = mxmlElementGetAttr(node, "name"); if (name == NULL) { logg->logError("Not all event XML categories have the required name attribute"); handleException(); } // Merge identically named categories bool merged = false; for (XMLList *category = categoryList; category != NULL; category = category->getPrev()) { const char *const name2 = mxmlElementGetAttr(category->getNode(), "name"); if (name2 == NULL) { logg->logError("Not all event XML categories have the required name attribute"); handleException(); } if (strcmp(name, name2) == 0) { logg->logMessage("Merging category %s", name); while (true) { mxml_node_t *child = mxmlGetFirstChild(node); if (child == NULL) { break; } mxmlAdd(category->getNode(), MXML_ADD_AFTER, mxmlGetLastChild(category->getNode()), child); } merged = true; break; } } if (merged) { continue; } // Add new categories logg->logMessage("Appending category %s", name); mxmlAdd(events, MXML_ADD_AFTER, mxmlGetLastChild(events), node); } XMLList::free(eventList); XMLList::free(categoryList); mxmlDelete(append); } return xml; }