/* * $Id$ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include "rxml.h" /* Typedefs/Defines */ #define RXML_ERROR(a,b) { (a)->stdio_errno=errno; (a)->ecode=(b); return 0; }; #define RXML_MAX_LINE 1024 #define RXML_MAX_TEXT 1024 #define RXML_MAX_TAG 256 typedef struct _RXML { RXML_EVTHANDLER handler; char *fname; FILE *fhandle; void *udata; int stdio_errno; int ecode; int line; char *estring; } RXML; #ifndef FALSE # define FALSE 0 # define TRUE 1 #endif /* FALSE */ #define E_RXML_SUCCESS 0x00 #define E_RXML_OPEN 0x01 | 0x80 #define E_RXML_READ 0x02 | 0x80 #define E_RXML_NEST 0x03 #define E_RXML_SPLIT 0x04 #define E_RXML_CLOSE 0x05 #define E_RXML_TAGSIZE 0x06 #define E_RXML_ENTITY 0x07 char *rxml_estrings[] = { "Success", "Could not open xml file: ", "Error reading file: ", "Parse error: Nested tags", "Parse error: Tag split over multiple lines", "Parse error: Unexpected '>'", "Parse error: tag too big", "Parse error: bad entity encoding", NULL }; /* Forwards */ static int rxml_decode_string(char *string); /** * decode an xml entity-encoded string in-place * * @param string string to encode * @returns 1 on success, 0 on error */ int rxml_decode_string(char *string) { char *src, *dst; int len; int cval; src=dst=string; while(*src) { if((*src) == '&') { len = (int)strlen(src); if((len > 3) && (strncmp(src,">",4) == 0)) { *dst++ = '>'; src += 4; } else if((len > 3) && (strncmp(src,"<",4) == 0)) { *dst++ = '<'; src += 4; } else if((len > 4) && (strncmp(src,"&",5) == 0)) { *dst++ = '&'; src += 5; } else if((len > 5) && (strncmp(src,""",6) == 0)) { *dst++ = '"'; src += 6; } else if((len > 5) && (strncmp(src,"'",6) == 0)) { *dst ++ = '\''; src += 6; } else { /* &#xx; */ if(!sscanf((char*)&src[2],"%d;",&cval)) return FALSE; *dst++ = cval; if(src[3] == ';') { src += 4; } else if(src[4] == ';') { src += 5; } else if(src[5] == ';') { src += 6; } else { return FALSE; } } } else { *dst++=*src++; } } *dst = '\0'; return TRUE; } /** * open a file * * @param vp pointer to the opaque xml struct * @param file filename of file to open * @param handler handler function for events * @param udata opaque data structure to pass to the callback */ int rxml_open(RXMLHANDLE *vp, char *file, RXML_EVTHANDLER handler, void *udata) { RXML *pnew; pnew=(RXML*)malloc(sizeof(RXML)); if(!pnew) { *vp = NULL; return FALSE; } memset(pnew,0x0,sizeof(RXML)); *vp = pnew; pnew->handler = handler; pnew->fname = file; pnew->fhandle = fopen(file,"rb"); pnew->udata = udata; pnew->line = 0; if(!pnew->fhandle) RXML_ERROR(pnew,E_RXML_OPEN); if(pnew->handler) pnew->handler(RXML_EVT_OPEN, pnew->udata, pnew->fname); return TRUE; } /** * close the currently open file * * @param vp xml struct containing info about the file to close */ int rxml_close(RXMLHANDLE vp) { RXML *ph = (RXML*)vp; if(ph->handler) ph->handler(RXML_EVT_CLOSE,ph->udata,ph->fname); if(ph->fhandle) fclose(ph->fhandle); if(ph->estring) free(ph->estring); free(ph); return TRUE; } /** * return a string describing the last error * * @param vp handle of the document with the error */ char *rxml_errorstring(RXMLHANDLE vp) { RXML *ph = (RXML*)vp; int len; char *estring=NULL; if(!ph) { return "Malloc error"; } if(ph->estring) free(ph->estring); len = (int)strlen(rxml_estrings[ph->ecode]) + 16; if((ph->ecode & 0x80)) { estring=strerror(ph->stdio_errno); len += (int)strlen(estring); } ph->estring=(char*)malloc(len); if(!ph->estring) return "Double-fault malloc error"; if((ph->ecode & 0x80)) { snprintf(ph->estring,len,"%s%s",rxml_estrings[ph->ecode],estring); } else { if(strncmp(rxml_estrings[ph->ecode],"Parse",5)==0) { snprintf(ph->estring, len, "%s (Line:%d)", rxml_estrings[ph->ecode], ph->line); } else { snprintf(ph->estring,len, "%s", rxml_estrings[ph->ecode]); } } return ph->estring; } /** * walk through the xml file, sending events to the * callback handler. * * @param vp opaque doc struct of the doc to parse */ int rxml_parse(RXMLHANDLE vp) { char linebuffer[RXML_MAX_LINE]; char tagbuffer[RXML_MAX_TAG]; char textbuffer[RXML_MAX_TEXT]; int in_text=0; int text_offset=0; int size; int offset; int in_tag=0; int tag_end=0; int tag_start=0; int single_tag; RXML *ph = (RXML*)vp; ph->line = 0; textbuffer[0] = '\0'; /* walk through and read row by row */ while(fgets(linebuffer,sizeof(linebuffer),ph->fhandle) != NULL) { ph->line++; offset=0; size=(int)strlen(linebuffer); in_text=0; text_offset=0; while(offset < size) { switch(linebuffer[offset]) { case '<': if(in_tag) RXML_ERROR(ph, E_RXML_NEST); in_tag=TRUE; tag_start=offset+1; tag_end=FALSE; if(linebuffer[tag_start] == '/') { tag_end = TRUE; offset++; tag_start++; } offset++; in_text=0; break; case '>': if(!in_tag) RXML_ERROR(ph, E_RXML_CLOSE); in_tag=FALSE; if((offset - tag_start) > RXML_MAX_TAG) RXML_ERROR(ph, E_RXML_TAGSIZE); strncpy(tagbuffer,&linebuffer[tag_start],offset-tag_start); tagbuffer[offset-tag_start] = '\0'; if(tag_end) { /* send the text before the tag end */ if((ph->handler) && (strlen(textbuffer))) { if(!rxml_decode_string(textbuffer)) RXML_ERROR(ph,E_RXML_ENTITY); ph->handler(RXML_EVT_TEXT,ph->udata,textbuffer); } } in_text=1; text_offset=0; textbuffer[0] = '\0'; single_tag=0; if(tagbuffer[strlen(tagbuffer)-1] == '/') { tagbuffer[strlen(tagbuffer)-1] = '\0'; single_tag=1; } if(ph->handler) ph->handler(tag_end ? RXML_EVT_END : RXML_EVT_BEGIN, ph->udata,tagbuffer); /* send a follow-up end on a - style tag */ if((single_tag) && (ph->handler)) ph->handler(RXML_EVT_END,ph->udata,tagbuffer); offset++; break; default: if((in_text) && (text_offset < (sizeof(textbuffer)-1))) { /* get rid of EOL */ if((linebuffer[offset] != '\r') && (linebuffer[offset] != '\n')) { textbuffer[text_offset] = linebuffer[offset]; text_offset++; textbuffer[text_offset] = '\x0'; } } else if (in_text) { /* should warn of an overflow */ } offset++; break; } } } ph->stdio_errno=errno; if(ferror(ph->fhandle)) RXML_ERROR(ph,E_RXML_READ); return TRUE; }