#include <stdio.h> #include <string.h> #include <stdlib.h> #include "libparsifal/parsifal.h" #include "zenstory.h" int StartElement(void *UserData, const XMLCH *uri, const XMLCH *localName, const XMLCH *qName, LPXMLVECTOR atts) { STORYPARSER *parser = (STORYPARSER*)UserData; if (parser->inMixedContent) { parser->inMixedContent++; /* could call mixed content legal tag check routine here e.g. if (!isvalidmixedcontenttag(qName, parser->state)) return sin(); */ return XML_OK; } else { int *pstate = STACK_PEEK(parser->stateStack); parser->state = (pstate) ? *pstate : NONE; } if (parser->state == NONE && !strcmp(qName, "stories")) { parser->state = STORIES; /* set initial state (root element) */ } else if (parser->state == STORIES && !strcmp(qName, "story")) { /* initialize new story: */ parser->curStory = XMLVector_Append(parser->stories, NULL); ASSERT_MEM_ABORT(parser->curStory); parser->curStory->title = (char*)NULL; parser->curStory->author = (char*)NULL; parser->curStory->body = (char*)NULL; parser->state = STORY; } /* these would normally be done with a little more short circuiting: */ else if (parser->state == STORY && !strcmp(qName, "title")) { parser->inContent++; /* STORY_TITLE tag allows normal content */ parser->state = STORY_TITLE; } else if (parser->state == STORY && !strcmp(qName, "author")) { parser->inContent++; /* STORY_AUTHOR tag allows normal content */ parser->state = STORY_AUTHOR; } else if (parser->state == STORY && !strcmp(qName, "body")) { parser->inMixedContent++; /* STORY_BODY tag allows mixed content */ parser->state = STORY_BODY; } else { printf("Unexpected tag: %s\nFile is not in zen story format!\n", qName); return XML_ABORT; } /* push the new state: */ ASSERT_MEM_ABORT(STACK_PUSH(parser->stateStack, &parser->state)); return XML_OK; } int EndElement(void *UserData, const XMLCH *uri, const XMLCH *localName, const XMLCH *qName) { STORYPARSER *parser = (STORYPARSER*)UserData; if (parser->inContent) parser->inContent = 0; else if (parser->inMixedContent) { /* if this is last mixed content tag (the tag that put us in mixed content), we POP the state, otherwise we just decrement the counter and return OK: */ if ((--parser->inMixedContent) > 0) return XML_OK; } if (STACK_POP(parser->stateStack, &parser->state)) { /* test the endTag here by popping and testing parser->state (which is the state that is ending) */ XMLCH *s = (XMLCH*)NULL; switch(parser->state) { case STORY_TITLE: /* you should check duplicate title etc. here */ s = strdup(XMLStringbuf_ToString(&parser->textBuf)); ASSERT_MEM_ABORT(s); parser->curStory->title = s; break; case STORY_AUTHOR: s = strdup(XMLStringbuf_ToString(&parser->textBuf)); ASSERT_MEM_ABORT(s); parser->curStory->author = s; break; case STORY_BODY: s = strdup(XMLStringbuf_ToString(&parser->textBuf)); ASSERT_MEM_ABORT(s); parser->curStory->body = s; break; } if (s) { /* normalize buffer, note that XMLNormalizeBuf doesn't nul terminate the buffer: */ int len = XMLNormalizeBuf(s, parser->textBuf.len); if (len < parser->textBuf.len) s[len] = '\0'; /* we'll reuse Stringbuf just setting its length to 0: */ ASSERT_MEM_ABORT(XMLStringbuf_SetLength(&parser->textBuf, 0)); } } return XML_OK; } int Characters(void *UserData, const XMLCH *Chars, int cbChars) { STORYPARSER *parser = (STORYPARSER*)UserData; if (parser->inContent || parser->inMixedContent) { /* if either inContent or inMixedContent is set, append data into Stringbuf: */ ASSERT_MEM_ABORT(XMLStringbuf_Append(&parser->textBuf, (XMLCH*)Chars, cbChars)); } else { /* this tag cannot contain character data: */ puts("Character data was not expected here!"); return XML_ABORT; } return XML_OK; } void ErrorHandler(LPXMLPARSER parser) { if (parser->ErrorCode != ERR_XMLP_ABORT) printf("Parsing error: %s\n", parser->ErrorString); printf("ErrorLine: %d ErrorColumn: %d\n", parser->ErrorLine, parser->ErrorColumn); } int cstream(BYTE *buf, int cBytes, int *cBytesActual, void *inputData) { *cBytesActual = fread(buf, 1, cBytes, (FILE*)inputData); return (*cBytesActual < cBytes); } int main(int argc, char* argv[]) { STORYPARSER sparser; LPXMLPARSER parser; if (!XMLParser_Create(&parser)) { puts("Error creating parser!"); return 1; } if (!XMLVector_Create(&sparser.stateStack, 6, sizeof(int))) { puts("Error creating stateStack in main()"); return 1; } if (!XMLVector_Create(&sparser.stories, 6, sizeof(ZENSTORY))) { puts("Error creating stories vector in main()"); return 1; } /* init Stringbuf: blockSize 256, no pre-allocation: */ XMLStringbuf_Init(&sparser.textBuf, 256, 0); sparser.parser = parser; sparser.inMixedContent = sparser.inContent = 0; parser->UserData = &sparser; parser->errorHandler = ErrorHandler; parser->startElementHandler = StartElement; parser->endElementHandler = EndElement; parser->charactersHandler = Characters; if (XMLParser_Parse(parser, cstream, stdin, 0)) { /* present the stories (we'll free the strings in the same loop) : */ int i; ZENSTORY *story; for (i=0; i<sparser.stories->length; i++) { story = XMLVector_Get(sparser.stories, i); if (story) { if (story->title) { printf("title: %s\n", story->title); free(story->title); } if (story->author) { printf("author: %s\n", story->author); free(story->author); } if (story->body) { printf("story: %s\n",story->body); free(story->body); } } } } XMLParser_Free(parser); XMLStringbuf_Free(&sparser.textBuf); XMLVector_Free(sparser.stories); XMLVector_Free(sparser.stateStack); return 0; }