#include #include static VALUE rb_mXTemplate; static VALUE rb_mXPath; static VALUE rb_cXNode; static VALUE rb_mUtil; static VALUE rb_cSanitizedString; static VALUE PredefinedEntity; static VALUE PredefinedStringsRegex; static VALUE PredefinedEntitiesRegex; static VALUE RevPredefinedEntity; static VALUE EntityAmp; static VALUE SanitizedAmp; static VALUE empty_str; static VALUE quot_regexp; static ID i_to_s, i_gsub, i_new, i_dup, i_gtgt, i_collect, i_strip; static ID ii_name, ii_attrs, ii_children, ii_data_path, ii_propagation; static ID ii_exname, ii_exattr, ii_expand, ii_option; static VALUE rb_xt_sanitize_i(VALUE obj) { return rb_funcall(obj, i_gsub, 1, PredefinedStringsRegex); } static VALUE rb_xt_sanitize_ii(VALUE s, VALUE data) { if( strcmp(RSTRING(s)->ptr, RSTRING(EntityAmp)->ptr) == 0 ){ return s; } else{ return rb_hash_aref(RevPredefinedEntity, s); } } static VALUE rb_xt_sanitize(VALUE m, VALUE obj) { VALUE str; if( rb_obj_is_kind_of(obj, rb_cSanitizedString) ){ return obj; } else{ obj = rb_funcall(obj, i_to_s, 0); obj = rb_funcall(obj, i_gsub, 2, EntityAmp, SanitizedAmp); obj = rb_iterate(rb_xt_sanitize_i, obj, rb_xt_sanitize_ii, Qnil); return rb_funcall(rb_cSanitizedString, i_new, 1, obj); } } static VALUE rb_xt_unsanitize_i(VALUE obj) { return rb_funcall(obj, i_gsub, 1, PredefinedEntitiesRegex); } static VALUE rb_xt_unsanitize_ii(VALUE s, VALUE data) { return rb_hash_aref(PredefinedEntity, s); } static VALUE rb_xt_unsanitize(VALUE m, VALUE str) { if( rb_obj_is_kind_of(str, rb_cSanitizedString) ){ str = rb_iterate(rb_xt_unsanitize_i, str, rb_xt_unsanitize_ii, Qnil); return rb_funcall(rb_cString, i_new, 1, str); } else{ return str; } } static VALUE rb_xt_path_split(VALUE m, VALUE path) { char *str; int len; int i; int l; int s; VALUE ids; Check_Type(path, T_STRING); len = RSTRING(path)->len; str = ALLOCA_N(char,len+1); memcpy(str,RSTRING(path)->ptr,len+1); ids = rb_ary_new(); l = 0; s = 0; for( i=0; i < len; i++ ){ switch( str[i] ){ case '{': case '[': l++; break; case '}': case ']': l--; break; case '/': if( l == 0 ){ rb_ary_push(ids, rb_tainted_str_new(str + s, i - s)); s = i + 1; } break; } } rb_ary_push(ids, rb_tainted_str_new(str + s, i - s)); if( str[0] == '/' ){ rb_ary_store(ids, 0, rb_const_get(rb_mXPath, rb_intern("RootNode"))); } return ids; } static VALUE rb_xt_args_split(VALUE m, VALUE path) { char *str; int len; int i; int l; int s; VALUE ids; VALUE v; int escape, inref; Check_Type(path, T_STRING); path = rb_xt_unsanitize(m,path); len = RSTRING(path)->len; if( len == 0 ){ return rb_ary_new(); } str = ALLOCA_N(char,len+1); memcpy(str,RSTRING(path)->ptr,len+1); ids = rb_ary_new(); l = 0; s = 0; escape = 0; inref = 0; for( i=0; i < len; i++ ){ switch( str[i] ){ case '\'': case '"': if( escape ){ escape = 0; } else{ if( l ){ l = 0; } else{ l = 1; } } break; case '\\': escape = 1; break; case ',': if( !l ){ v = rb_tainted_str_new(str + s, i - s); v = rb_funcall(v, i_strip, 0); v = rb_funcall(v, i_gsub, 2, quot_regexp, empty_str); if( RSTRING(v)->len > 0 ) rb_ary_push(ids, v); s = i + 1; } break; } } v = rb_tainted_str_new(str + s, i - s); v = rb_funcall(v, i_strip, 0); v = rb_funcall(v, i_gsub, 2, quot_regexp, empty_str); if( RSTRING(v)->len > 0 ) rb_ary_push(ids, v); return ids; } static VALUE rb_xt_cond_split(VALUE m, VALUE path) { char *str; int len; int i; int l; int s; VALUE xs; Check_Type(path, T_STRING); len = RSTRING(path)->len; str = ALLOCA_N(char,len+1); memcpy(str,RSTRING(path)->ptr,len+1); xs = rb_ary_new(); l = 0; s = 0; for( i=0; i < len; i++ ){ switch( str[i] ){ case '{': case '[': if( l == 0 ){ if( i == 0 ){ rb_ary_push(xs, rb_str_new2("")); } else if( i != s ){ VALUE tmp = rb_tainted_str_new(str + s, i - s); if( OBJ_TAINTED(str) ){ OBJ_TAINT(tmp); }; rb_ary_push(xs, tmp); } s = i; } l ++; break; case '}': case ']': l--; if( l == 0 ){ VALUE tmp = rb_tainted_str_new(str + s, i - s + 1); if( OBJ_TAINTED(str) ){ OBJ_TAINT(tmp); }; rb_ary_push(xs, tmp); s = i + 1; } break; } } if( s != i ){ VALUE tmp = rb_tainted_str_new(str + s, i - s); if( OBJ_TAINTED(str) ){ OBJ_TAINT(tmp); }; rb_ary_push(xs, tmp); } return xs; } static VALUE rb_xt_xnode_deep_dup_i(VALUE children) { return rb_funcall(children, i_collect, 0); } static VALUE rb_xt_xnode_deep_dup(int, VALUE[], VALUE); static VALUE rb_xt_xnode_deep_dup_ii(VALUE child, VALUE data) { VALUE node = data; if( rb_obj_is_kind_of(child, rb_cXNode) ){ VALUE argv[1] = {node}; return rb_xt_xnode_deep_dup(1, argv, child); } else{ return child; } } static VALUE rb_xt_xnode_deep_dup(int argc, VALUE argv[], VALUE xnode) { VALUE newnode; VALUE name, attrs, children, parent, data_path; VALUE propagation, exname, exattr, expand, option; rb_scan_args(argc, argv, "01", &parent); name = rb_ivar_get(xnode, ii_name); if( ! NIL_P(name) ) name = rb_funcall(name, i_dup, 0); attrs = rb_ivar_get(xnode, ii_attrs); if( ! NIL_P(name) ) attrs = rb_funcall(attrs, i_dup, 0); children = Qnil; data_path = rb_ivar_get(xnode, ii_data_path); if( ! NIL_P(data_path) ) data_path = rb_funcall(data_path, i_dup, 0); propagation = rb_ivar_get(xnode, ii_propagation); exname = rb_ivar_get(xnode, ii_exname); if( ! NIL_P(exname) ) exname = rb_funcall(exname, i_dup, 0); exattr = rb_ivar_get(xnode, ii_exattr); if( ! NIL_P(exattr) ) exattr = rb_funcall(exattr, i_dup, 0); expand = rb_ivar_get(xnode, ii_expand); option = rb_funcall(rb_ivar_get(xnode, ii_option), i_dup, 0); newnode = rb_funcall(rb_cXNode, i_new, 10, name, attrs, Qnil, parent, data_path, propagation, exname, exattr, expand, option); children = rb_iterate(rb_xt_xnode_deep_dup_i, rb_ivar_get(xnode, ii_children), rb_xt_xnode_deep_dup_ii, newnode); rb_ivar_set(newnode, ii_children, children); return newnode; } static VALUE rb_xt_xnode_dump_i(VALUE attrs) { return rb_funcall(attrs, i_collect, 0); } static VALUE rb_xt_xnode_dump_ii(VALUE ary, VALUE data) { VALUE attr, val, str; Check_Type(ary, T_ARRAY); attr = RARRAY(ary)->ptr[0]; val = RARRAY(ary)->ptr[1]; str = rb_str_new2(""); rb_str_concat(str, attr); rb_str_cat2(str, "=\""); rb_str_concat(str, val); rb_str_cat2(str, "\""); return str; } static VALUE rb_xt_xnode_dump(VALUE xnode, VALUE io) { VALUE name = rb_ivar_get(xnode,ii_name); VALUE children = rb_ivar_get(xnode, ii_children); int i, len; VALUE child; VALUE tag; if( ! NIL_P(name) ){ VALUE attrs = rb_ivar_get(xnode, ii_attrs); attrs = rb_iterate(rb_xt_xnode_dump_i, attrs, rb_xt_xnode_dump_ii, Qnil); if( RARRAY(attrs)->len > 0 ){ attrs = rb_str_concat(rb_str_new2(" "), rb_ary_join(attrs, rb_str_new2(" "))); } else{ attrs = rb_str_new2(""); } if( rb_funcall(children, rb_intern("empty?"), 0) ){ tag = rb_str_new2("<"); rb_str_concat(tag, name); rb_str_concat(tag, attrs); rb_str_cat2(tag, " />"); rb_funcall(io, i_gtgt, 1, tag); return io; } else{ tag = rb_str_new2("<"); rb_str_concat(tag, name); rb_str_concat(tag, attrs); rb_str_cat2(tag, ">"); rb_funcall(io, i_gtgt, 1, tag); } } Check_Type(children,T_ARRAY); len = RARRAY(children)->len; for( i=0; i < len; i++ ){ child = RARRAY(children)->ptr[i]; if( rb_obj_is_kind_of(child, rb_cXNode) ){ rb_xt_xnode_dump(child, io); } else{ rb_funcall(io, i_gtgt, 1, child); } } if( ! NIL_P(name) ){ tag = rb_tainted_str_new2(""); rb_funcall(io, i_gtgt, 1, tag); } return io; } void Init_xtemplate_ext() { i_to_s = rb_intern("to_s"); i_gsub = rb_intern("gsub"); i_new = rb_intern("new"); i_dup = rb_intern("dup"); i_gtgt = rb_intern("<<"); i_collect = rb_intern("collect"); i_strip = rb_intern("strip"); ii_name = rb_intern("@name"); ii_attrs = rb_intern("@attrs"); ii_children = rb_intern("@children"); ii_data_path = rb_intern("@data_path"); ii_propagation = rb_intern("@propagation"); ii_exname = rb_intern("@exname"); ii_exattr = rb_intern("@exattr"); ii_expand = rb_intern("@expand"); ii_option = rb_intern("@option"); rb_mXTemplate = rb_eval_string("::XTemplate"); rb_mXPath = rb_eval_string("::XTemplate::XPath"); rb_cXNode = rb_eval_string("::XTemplate::XNode"); rb_mUtil = rb_eval_string("::XTemplate::Util"); rb_cSanitizedString = rb_eval_string("::XTemplate::SanitizedString"); PredefinedStringsRegex = rb_eval_string("::XTemplate::Util::PredefinedStringsRegex"); PredefinedEntitiesRegex = rb_eval_string("::XTemplate::Util::PredefinedEntitiesRegex"); PredefinedEntity = rb_eval_string("::XTemplate::Util::PredefinedEntity"); RevPredefinedEntity = rb_eval_string("::XTemplate::Util::RevPredefinedEntity"); EntityAmp = rb_eval_string("::XTemplate::Util::EntityAmp"); SanitizedAmp = rb_eval_string("::XTemplate::Util::SanitizedAmp"); empty_str = rb_tainted_str_new2(""); rb_gc_register_address(&empty_str); quot_regexp = rb_eval_string("/(\\A['\"])|([\"']\\z)/"); rb_gc_register_address("_regexp); rb_define_module_function(rb_mXPath, "path_split", rb_xt_path_split, 1); rb_define_module_function(rb_mXPath, "cond_split", rb_xt_cond_split, 1); rb_define_module_function(rb_mXPath, "args_split", rb_xt_args_split, 1); rb_define_module_function(rb_mUtil, "sanitize", rb_xt_sanitize, 1); rb_define_module_function(rb_mUtil, "unsanitize", rb_xt_unsanitize, 1); rb_define_method(rb_cXNode, "dump", rb_xt_xnode_dump, 1); rb_define_method(rb_cXNode, "deep_dup", rb_xt_xnode_deep_dup, -1); }