#include #include #include #include #include #include "parser.h" /* define default callbacks */ static void esi_parser_default_start_cb( const void *data, const char *name_start, size_t name_length, ESIAttribute *attributes, void *user_data ) { } static void esi_parser_default_end_cb( const void *data, const char *name_start, size_t name_length, void *user_data ) { } static void esi_parser_default_output_cp(const void *data, size_t length, void *user_data) { } #ifdef DEBUG static void debug_string( const char *msg, const char *str, size_t len ) { char *pstr = esi_strndup( str, len ); printf( "%s :'%s'\n", msg, pstr ); free( pstr ); } #endif static void esi_parser_echo_char( ESIParser *parser, char ch ) { parser->output_handler( (void*)&ch, 1, parser->user_data ); } static void esi_parser_echo_buffer( ESIParser *parser ) { // debug_string( "echobuffer", parser->echobuffer, parser->echobuffer_index+1 ); parser->output_handler( parser->echobuffer, parser->echobuffer_index+1, parser->user_data ); } static void esi_parser_echobuffer_clear( ESIParser *parser ) { parser->echobuffer_index = -1; } static void esi_parser_concat_to_echobuffer( ESIParser *parser, char ch ) { parser->echobuffer_index++; if( parser->echobuffer_allocated <= parser->echobuffer_index ){ // double the echobuffer size parser->echobuffer_allocated *= 2; parser->echobuffer = (char*)realloc( parser->echobuffer, parser->echobuffer_allocated ); } parser->echobuffer[parser->echobuffer_index] = ch; // debug_string( "echo buffer", parser->echobuffer, parser->echobuffer_index+1 ); } static void ltrim_pointer( const char **ptr, const char *bounds, size_t *len ) { // remove any spaces or = at the before the value while( isspace( **ptr ) || **ptr == '=' || **ptr == '"' || **ptr == '\'' && *len > 0 && (*ptr != bounds) ){ (*ptr)++; (*len)--; } } %%{ machine esi; action begin { parser->mark = p; // printf( "begin\n" ); } action finish { // printf( "finish\n" ); } # record the position of the start tag action see_start_tag { parser->tag_text = parser->mark+1; parser->tag_text_length = p - (parser->mark+1); // debug_string( "have esi start", parser->tag_text, parser->tag_text_length ); parser->mark = p; } action see_end_tag { // debug_string( "parsed esi tag", parser->tag_text, parser->tag_text_length ); parser->start_tag_handler( data, parser->tag_text, parser->tag_text_length, parser->attributes, parser->user_data ); parser->end_tag_handler( data, parser->tag_text, parser->tag_text_length, parser->user_data ); parser->tag_text = NULL; parser->tag_text_length = 0; } action see_block_start_with_attributes { // debug_string( "parsed esi tag", parser->tag_text, parser->tag_text_length ); parser->start_tag_handler( data, parser->tag_text, parser->tag_text_length, parser->attributes, parser->user_data ); parser->tag_text = NULL; parser->tag_text_length = 0; } action see_attribute_key { parser->attr_key = parser->mark; parser->attr_key_length = p - parser->mark; parser->mark = p; ltrim_pointer( &(parser->attr_key), p, &(parser->attr_key_length) ); // debug_string( "\tattribute key", parser->attr_key, parser->attr_key_length ); } action see_attribute_value { ESIAttribute *attr; parser->attr_value = parser->mark; parser->attr_value_length = p - parser->mark; parser->mark = p; ltrim_pointer( &(parser->attr_value), p, &(parser->attr_value_length) ); // allocate a new attribute attr = esi_attribute_new( parser->attr_key, parser->attr_key_length, parser->attr_value, parser->attr_value_length ); if( parser->attributes ){ parser->last->next = attr; parser->last = attr; } else{ parser->last = parser->attributes = attr; } // debug_string( "\tattribute value", parser->attr_value, parser->attr_value_length ); } action block_start_tag { parser->tag_text = parser->mark; parser->tag_text_length = p - parser->mark; parser->mark = p; parser->start_tag_handler( data, parser->tag_text, parser->tag_text_length, NULL, parser->user_data ); } action block_end_tag { parser->tag_text = parser->mark+2; parser->tag_text_length = p - (parser->mark+2); parser->mark = p; parser->end_tag_handler( data, parser->tag_text, parser->tag_text_length, parser->user_data ); } action echo { switch( cs ){ case 0: if( parser->prev_state != 12 && parser->prev_state != 7 ){ if( parser->echobuffer && (parser->prev_state != (esi_en_main +1)) && parser->prev_state != 60 ){ // stream echobuffer esi_parser_echo_buffer( parser ); } // printf( "[%d: %c]", cs, *p ); // stream the current character esi_parser_echo_char( parser, *p ); } // clear the echobuffer esi_parser_echobuffer_clear( parser ); break; default: // append characters to echobuffer esi_parser_concat_to_echobuffer( parser, *p ); } parser->prev_state = cs; } include esi_common_parser "common.rl"; }%% %%write data; char *esi_strndup( const char *str, size_t len ) { char *s = (char*)malloc(sizeof(char)*(len+1)); memcpy( s, str, len ); s[len] = '\0'; return s; } ESIAttribute *esi_attribute_new( const char *name, size_t name_length, const char *value, size_t value_length ) { ESIAttribute *attr = (ESIAttribute*)malloc(sizeof(ESIAttribute)); attr->name = esi_strndup(name, name_length); attr->value = esi_strndup(value, value_length); attr->next = NULL; return attr; } void esi_attribute_free( ESIAttribute *attribute ) { ESIAttribute *ptr; while( attribute ){ free( attribute->name ); free( attribute->value ); ptr = attribute->next; free( attribute ); attribute = ptr; } } ESIParser *esi_parser_new() { ESIParser *parser = (ESIParser*)malloc(sizeof(ESIParser)); parser->cs = esi_start; parser->mark = NULL; parser->overflow_data_size = 0; parser->overflow_data = NULL; /* allocate 1024 bytes for the echobuffer */ parser->echobuffer_allocated = 1024; parser->echobuffer_index = -1; parser->echobuffer = (char*)malloc(sizeof(char)*parser->echobuffer_allocated); parser->attributes = NULL; parser->last = NULL; parser->start_tag_handler = esi_parser_default_start_cb; parser->end_tag_handler = esi_parser_default_end_cb; parser->output_handler = esi_parser_default_output_cp; return parser; } void esi_parser_free( ESIParser *parser ) { if( parser->overflow_data ){ free( parser->overflow_data ); } free( parser->echobuffer ); esi_attribute_free( parser->attributes ); free( parser ); } void esi_parser_output_handler( ESIParser *parser, output_cb output_handler ) { parser->output_handler = output_handler; } int esi_parser_init( ESIParser *parser ) { int cs; %% write init; parser->prev_state = parser->cs = cs; return 0; } static int compute_offset( const char *mark, const char *data ) { if( mark ){ return mark - data; } return -1; } int esi_parser_execute( ESIParser *parser, const char *data, size_t length ) { int cs = parser->cs; const char *p = data; const char *pe = data + length; if( length == 0 ){ return cs; } if( parser->overflow_data && parser->overflow_data_size > 0 ){ // recompute mark, tag_text, attr_key, and attr_value since they all exist within overflow_data int mark_offset = compute_offset( parser->mark, parser->overflow_data ); int tag_text_offset = compute_offset( parser->tag_text, parser->overflow_data ); int attr_key_offset = compute_offset( parser->attr_key, parser->overflow_data ); int attr_value_offset = compute_offset( parser->attr_value, parser->overflow_data ); // debug_string("grow overflow buffer", parser->overflow_data, parser->overflow_data_size ); parser->overflow_data = (char*)realloc( parser->overflow_data, sizeof(char)*(parser->overflow_data_size+length) ); memcpy( parser->overflow_data+parser->overflow_data_size, data, length ); p = parser->overflow_data + parser->overflow_data_size; // in our new memory space mark will now be parser->mark = ( mark_offset > 0 ) ? parser->overflow_data + mark_offset : NULL; parser->tag_text = ( tag_text_offset > 0 ) ? parser->overflow_data + tag_text_offset : NULL; parser->attr_key = ( attr_key_offset > 0 ) ? parser->overflow_data + attr_key_offset : NULL; parser->attr_value = ( attr_value_offset > 0 ) ? parser->overflow_data + attr_value_offset : NULL; data = parser->overflow_data; parser->overflow_data_size = length = length + parser->overflow_data_size; pe = data + length; // debug_string( "overflow", parser->overflow_data, parser->overflow_data_size ); } if( !parser->mark ){ parser->mark = p; } // printf( "cs: %d, ", cs ); // debug_string( "data", data, length ); %% write exec; parser->cs = cs; if( cs != esi_start && cs != 0 ){ if( !parser->overflow_data ){ // recompute mark, tag_text, attr_key, and attr_value since they all exist within overflow_data int mark_offset = compute_offset( parser->mark, data ); int tag_text_offset = compute_offset( parser->tag_text, data ); int attr_key_offset = compute_offset( parser->attr_key, data ); int attr_value_offset = compute_offset( parser->attr_value, data ); parser->overflow_data = (char*)malloc( sizeof( char ) * length ); memcpy( parser->overflow_data, data, length ); parser->overflow_data_size = length; // in our new memory space mark will now be parser->mark = ( mark_offset > 0 ) ? parser->overflow_data + mark_offset : NULL; parser->tag_text = ( tag_text_offset > 0 ) ? parser->overflow_data + tag_text_offset : NULL; parser->attr_key = ( attr_key_offset > 0 ) ? parser->overflow_data + attr_key_offset : NULL; parser->attr_value = ( attr_value_offset > 0 ) ? parser->overflow_data + attr_value_offset : NULL; } }else if( parser->overflow_data ){ free( parser->overflow_data ); parser->overflow_data = NULL; parser->overflow_data_size = 0; } return cs; } int esi_parser_finish( ESIParser *parser ) { %% write eof; return 0; } void esi_parser_start_tag_handler( ESIParser *parser, start_tag_cb callback ) { parser->start_tag_handler = callback; } void esi_parser_end_tag_handler( ESIParser *parser, end_tag_cb callback ) { parser->end_tag_handler = callback; }