/* * xml.c * * Copyright (c) Chris Putnam 2004-2018 * * Source code released under the GPL version 2 * */ #include #include #include #include "is_ws.h" #include "strsearch.h" #include "xml.h" char *xml_pns = NULL; void xml_init( xml *node ) { str_init( &(node->tag) ); str_init( &(node->value) ); slist_init( &(node->attributes) ); slist_init( &(node->attribute_values) ); node->down = NULL; node->next = NULL; } static xml * xml_new( void ) { xml *node = ( xml * ) malloc( sizeof( xml ) ); if ( node ) xml_init( node ); return node; } static void xml_delete( xml *node ) { xml_free( node ); free( node ); } void xml_free( xml *node ) { str_free( &(node->tag) ); str_free( &(node->value) ); slist_free( &(node->attributes) ); slist_free( &(node->attribute_values) ); if ( node->down ) xml_delete( node->down ); if ( node->next ) xml_delete( node->next ); } enum { XML_DESCRIPTOR, XML_COMMENT, XML_OPEN, XML_CLOSE, XML_OPENCLOSE }; static int xml_is_terminator( char *p, int *type ) { if ( *p=='>' ) { return 1; } else if ( *p=='/' && *(p+1)=='>' ) { if ( *type==XML_OPENCLOSE ) return 1; else if ( *type==XML_OPEN ) { *type = XML_OPENCLOSE; return 1; } } else if ( *p=='?' && *(p+1)=='>' && *type==XML_DESCRIPTOR ) { return 1; } else if ( *p=='!' && *(p+1)=='>' && *type==XML_COMMENT ) { return 1; } return 0; } static int xml_add_attribute( xml *node, char *attribute, char *attribute_value ) { str *s; if ( attribute ) s = slist_addc( &(node->attributes), attribute ); else s = slist_addc( &(node->attributes), "" ); if ( s==NULL ) return 0; if ( attribute_value ) s = slist_addc( &(node->attribute_values), attribute_value ); else s = slist_addc( &(node->attribute_values), "" ); if ( s==NULL ) { (void) slist_remove( &(node->attributes), node->attributes.n-1 ); return 0; } return 1; } static char * xml_processattrib( char *p, xml *node, int *type ) { char quote_character = '\"'; int inquotes = 0; str aname, aval; str_init( &aname ); str_init( &aval ); while ( *p && !xml_is_terminator( p, type ) ) { /* get attribute name */ while ( *p==' ' || *p=='\t' ) p++; while ( *p && !strchr( "= \t", *p ) && !xml_is_terminator( p, type ) ){ str_addchar( &aname, *p ); p++; } /* equals sign */ while ( *p==' ' || *p=='\t' ) p++; if ( *p=='=' ) p++; while ( *p==' ' || *p=='\t' ) p++; /* get attribute value */ if ( *p=='\"' || *p=='\'' ) { if ( *p=='\'' ) quote_character = *p; inquotes=1; p++; } while ( *p && ((!xml_is_terminator(p,type) && !strchr("= \t", *p ))||inquotes)){ if ( *p==quote_character ) inquotes=0; else str_addchar( &aval, *p ); p++; } if ( str_has_value( &aname ) ) { xml_add_attribute( node, str_cstr( &aname ), str_cstr( &aval ) ); } str_empty( &aname ); str_empty( &aval ); } str_free( &aname ); str_free( &aval ); return p; } /* * xml_processtag * * start right after '<' * * * XML_COMMENT * XML_DESCRIPTOR * XML_OPEN * XML_CLOSE * XML_OPENCLOSE */ static char * xml_processtag( char *p, xml *node, int *type ) { str tag; str_init( &tag ); if ( *p=='!' ) { *type = XML_COMMENT; while ( *p && *p!='>' ) p++; } else if ( *p=='?' ) { *type = XML_DESCRIPTOR; p++; /* skip '?' */ while ( *p && !strchr( " \t", *p ) && !xml_is_terminator(p,type) ) str_addchar( &tag, *p++ ); if ( *p==' ' || *p=='\t' ) p = xml_processattrib( p, node, type ); } else if ( *p=='/' ) { *type = XML_CLOSE; while ( *p && !strchr( " \t", *p ) && !xml_is_terminator(p,type) ) str_addchar( &tag, *p++ ); if ( *p==' ' || *p=='\t' ) p = xml_processattrib( p, node, type ); } else { *type = XML_OPEN; while ( *p && !strchr( " \t", *p ) && !xml_is_terminator(p,type) ) str_addchar( &tag, *p++ ); if ( *p==' ' || *p=='\t' ) p = xml_processattrib( p, node, type ); } while ( *p && *p!='>' ) p++; if ( *p=='>' ) p++; str_strcpy( &(node->tag), &tag ); str_free( &tag ); return p; } static void xml_appendnode( xml *onode, xml *nnode ) { if ( !onode->down ) onode->down = nnode; else { xml *p = onode->down; while ( p->next ) p = p->next; p->next = nnode; } } char * xml_parse( char *p, xml *onode ) { int type, is_style = 0; xml *nnode; while ( *p ) { /* retain white space for