From 11dbedfaad4a9a9521ece2198a8dc491678b1902 Mon Sep 17 00:00:00 2001 From: shyouhei Date: Wed, 29 Aug 2007 04:06:12 +0000 Subject: add tag v1_8_6_5001 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v1_8_6_5001@13304 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ruby_1_8_6/ext/syck/rubyext.c | 2367 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2367 insertions(+) create mode 100644 ruby_1_8_6/ext/syck/rubyext.c (limited to 'ruby_1_8_6/ext/syck/rubyext.c') diff --git a/ruby_1_8_6/ext/syck/rubyext.c b/ruby_1_8_6/ext/syck/rubyext.c new file mode 100644 index 0000000000..078de4f78d --- /dev/null +++ b/ruby_1_8_6/ext/syck/rubyext.c @@ -0,0 +1,2367 @@ +/* -*- indent-tabs-mode: nil -*- */ +/* + * rubyext.c + * + * $Author$ + * $Date$ + * + * Copyright (C) 2003-2005 why the lucky stiff + */ + +#include "ruby.h" +#include "syck.h" +#include +#include + +typedef struct RVALUE { + union { +#if 0 + struct { + unsigned long flags; /* always 0 for freed obj */ + struct RVALUE *next; + } free; +#endif + struct RBasic basic; + struct RObject object; + struct RClass klass; + /*struct RFloat flonum;*/ + /*struct RString string;*/ + struct RArray array; + /*struct RRegexp regexp;*/ + struct RHash hash; + /*struct RData data;*/ + struct RStruct rstruct; + /*struct RBignum bignum;*/ + /*struct RFile file;*/ + } as; +} RVALUE; + +typedef struct { + long hash; + char *buffer; + long length; + long remaining; + int printed; +} bytestring_t; + +#define RUBY_DOMAIN "ruby.yaml.org,2002" + +/* + * symbols and constants + */ +static ID s_new, s_utc, s_at, s_to_f, s_to_i, s_read, s_binmode, s_call, s_cmp, s_transfer, s_update, s_dup, s_haskey, s_match, s_keys, s_unpack, s_tr_bang, s_default_set, s_tag_read_class, s_tag_subclasses, s_resolver, s_push, s_emitter, s_level, s_detect_implicit, s_node_import, s_out, s_input, s_intern, s_transform, s_yaml_new, s_yaml_initialize, s_node_export, s_to_yaml, s_write, s_set_resolver; +static ID s_tags, s_domain, s_kind, s_name, s_options, s_type_id, s_type_id_set, s_style, s_style_set, s_value, s_value_set; +static VALUE sym_model, sym_generic, sym_input, sym_bytecode; +static VALUE sym_scalar, sym_seq, sym_map; +static VALUE sym_1quote, sym_2quote, sym_fold, sym_literal, sym_plain, sym_inline; +static VALUE cDate, cNode, cMap, cSeq, cScalar, cOut, cParser, cResolver, cPrivateType, cDomainType, cYObject, cBadAlias, cDefaultKey, cMergeKey, cEmitter; +static VALUE oDefaultResolver, oGenericResolver; + +/* + * my private collection of numerical oddities. + */ +static double S_zero() { return 0.0; } +static double S_one() { return 1.0; } +static double S_inf() { return S_one() / S_zero(); } +static double S_nan() { return S_zero() / S_zero(); } + +static VALUE syck_node_transform( VALUE ); + +/* + * handler prototypes + */ +SYMID rb_syck_load_handler _((SyckParser *, SyckNode *)); +void rb_syck_err_handler _((SyckParser *, char *)); +SyckNode * rb_syck_bad_anchor_handler _((SyckParser *, char *)); +void rb_syck_output_handler _((SyckEmitter *, char *, long)); +void rb_syck_emitter_handler _((SyckEmitter *, st_data_t)); +int syck_parser_assign_io _((SyckParser *, VALUE *)); +VALUE syck_scalar_alloc _((VALUE class)); +VALUE syck_seq_alloc _((VALUE class)); +VALUE syck_map_alloc _((VALUE class)); + +struct parser_xtra { + VALUE data; /* Borrowed this idea from marshal.c to fix [ruby-core:8067] problem */ + VALUE proc; + VALUE resolver; + int taint; +}; + +struct emitter_xtra { + VALUE oid; + VALUE data; + VALUE port; +}; + +/* + * Convert YAML to bytecode + */ +VALUE +rb_syck_compile(self, port) + VALUE self, port; +{ + SYMID oid; + int taint; + char *ret; + VALUE bc; + bytestring_t *sav; + + SyckParser *parser = syck_new_parser(); + taint = syck_parser_assign_io(parser, &port); + syck_parser_handler( parser, syck_yaml2byte_handler ); + syck_parser_error_handler( parser, NULL ); + syck_parser_implicit_typing( parser, 0 ); + syck_parser_taguri_expansion( parser, 0 ); + oid = syck_parse( parser ); + syck_lookup_sym( parser, oid, (char **)&sav ); + + ret = S_ALLOCA_N( char, strlen( sav->buffer ) + 3 ); + ret[0] = '\0'; + strcat( ret, "D\n" ); + strcat( ret, sav->buffer ); + + syck_free_parser( parser ); + + bc = rb_str_new2( ret ); + if ( taint ) OBJ_TAINT( bc ); + return bc; +} + +/* + * read from io. + */ +long +rb_syck_io_str_read( char *buf, SyckIoStr *str, long max_size, long skip ) +{ + long len = 0; + + ASSERT( str != NULL ); + max_size -= skip; + + if ( max_size <= 0 ) max_size = 0; + else + { + /* + * call io#read. + */ + VALUE src = (VALUE)str->ptr; + VALUE n = LONG2NUM(max_size); + VALUE str2 = rb_funcall2(src, s_read, 1, &n); + if (!NIL_P(str2)) + { + StringValue(str2); + len = RSTRING(str2)->len; + memcpy( buf + skip, RSTRING(str2)->ptr, len ); + } + } + len += skip; + buf[len] = '\0'; + return len; +} + +/* + * determine: are we reading from a string or io? + * (returns tainted? boolean) + */ +int +syck_parser_assign_io(parser, pport) + SyckParser *parser; + VALUE *pport; +{ + int taint = Qtrue; + VALUE tmp, port = *pport; + if (!NIL_P(tmp = rb_check_string_type(port))) { + taint = OBJ_TAINTED(port); /* original taintedness */ + port = tmp; + syck_parser_str( parser, RSTRING(port)->ptr, RSTRING(port)->len, NULL ); + } + else if (rb_respond_to(port, s_read)) { + if (rb_respond_to(port, s_binmode)) { + rb_funcall2(port, s_binmode, 0, 0); + } + syck_parser_str( parser, (char *)port, 0, rb_syck_io_str_read ); + } + else { + rb_raise(rb_eTypeError, "instance of IO needed"); + } + *pport = port; + return taint; +} + +/* + * Get value in hash by key, forcing an empty hash if nil. + */ +VALUE +syck_get_hash_aref(hsh, key) + VALUE hsh, key; +{ + VALUE val = rb_hash_aref( hsh, key ); + if ( NIL_P( val ) ) + { + val = rb_hash_new(); + rb_hash_aset(hsh, key, val); + } + return val; +} + +/* + * creating timestamps + */ +SYMID +rb_syck_mktime(str, len) + char *str; + long len; +{ + VALUE time; + char *ptr = str; + VALUE year = INT2FIX(0); + VALUE mon = INT2FIX(0); + VALUE day = INT2FIX(0); + VALUE hour = INT2FIX(0); + VALUE min = INT2FIX(0); + VALUE sec = INT2FIX(0); + long usec; + + /* Year*/ + if ( ptr[0] != '\0' && len > 0 ) { + year = INT2FIX(strtol(ptr, NULL, 10)); + } + + /* Month*/ + ptr += 4; + if ( ptr[0] != '\0' && len > ptr - str ) { + while ( !ISDIGIT( *ptr ) ) ptr++; + mon = INT2FIX(strtol(ptr, NULL, 10)); + } + + /* Day*/ + ptr += 2; + if ( ptr[0] != '\0' && len > ptr - str ) { + while ( !ISDIGIT( *ptr ) ) ptr++; + day = INT2FIX(strtol(ptr, NULL, 10)); + } + + /* Hour*/ + ptr += 2; + if ( ptr[0] != '\0' && len > ptr - str ) { + while ( !ISDIGIT( *ptr ) ) ptr++; + hour = INT2FIX(strtol(ptr, NULL, 10)); + } + + /* Minute */ + ptr += 2; + if ( ptr[0] != '\0' && len > ptr - str ) { + while ( !ISDIGIT( *ptr ) ) ptr++; + min = INT2FIX(strtol(ptr, NULL, 10)); + } + + /* Second */ + ptr += 2; + if ( ptr[0] != '\0' && len > ptr - str ) { + while ( !ISDIGIT( *ptr ) ) ptr++; + sec = INT2FIX(strtol(ptr, NULL, 10)); + } + + /* Millisecond */ + ptr += 2; + if ( len > ptr - str && *ptr == '.' ) + { + char padded[] = "000000"; + char *end = ptr + 1; + while ( isdigit( *end ) ) end++; + MEMCPY(padded, ptr + 1, char, end - (ptr + 1)); + usec = strtol(padded, NULL, 10); + } + else + { + usec = 0; + } + + /* Time Zone*/ + while ( len > ptr - str && *ptr != 'Z' && *ptr != '+' && *ptr != '-' && *ptr != '\0' ) ptr++; + if ( len > ptr - str && ( *ptr == '-' || *ptr == '+' ) ) + { + time_t tz_offset = strtol(ptr, NULL, 10) * 3600; + time_t tmp; + + while ( *ptr != ':' && *ptr != '\0' ) ptr++; + if ( *ptr == ':' ) + { + ptr += 1; + if ( tz_offset < 0 ) + { + tz_offset -= strtol(ptr, NULL, 10) * 60; + } + else + { + tz_offset += strtol(ptr, NULL, 10) * 60; + } + } + + /* Make TZ time*/ + time = rb_funcall(rb_cTime, s_utc, 6, year, mon, day, hour, min, sec); + tmp = NUM2LONG(rb_funcall(time, s_to_i, 0)) - tz_offset; + return rb_funcall(rb_cTime, s_at, 2, LONG2NUM(tmp), LONG2NUM(usec)); + } + else + { + /* Make UTC time*/ + return rb_funcall(rb_cTime, s_utc, 7, year, mon, day, hour, min, sec, LONG2NUM(usec)); + } +} + +/* + * handles merging of an array of hashes + * (see http://www.yaml.org/type/merge/) + */ +VALUE +syck_merge_i( entry, hsh ) + VALUE entry, hsh; +{ + VALUE tmp; + if ( !NIL_P(tmp = rb_check_convert_type(entry, T_HASH, "Hash", "to_hash")) ) + { + entry = tmp; + rb_funcall( hsh, s_update, 1, entry ); + } + return Qnil; +} + +/* + * default handler for ruby.yaml.org types + */ +int +yaml_org_handler( n, ref ) + SyckNode *n; + VALUE *ref; +{ + char *type_id = n->type_id; + int transferred = 0; + long i = 0; + VALUE obj = Qnil; + + if ( type_id != NULL && strncmp( type_id, "tag:yaml.org,2002:", 18 ) == 0 ) + { + type_id += 18; + } + + switch (n->kind) + { + case syck_str_kind: + transferred = 1; + if ( type_id == NULL ) + { + obj = rb_str_new( n->data.str->ptr, n->data.str->len ); + } + else if ( strcmp( type_id, "null" ) == 0 ) + { + obj = Qnil; + } + else if ( strcmp( type_id, "binary" ) == 0 ) + { + VALUE arr; + obj = rb_str_new( n->data.str->ptr, n->data.str->len ); + rb_funcall( obj, s_tr_bang, 2, rb_str_new2( "\n\t " ), rb_str_new2( "" ) ); + arr = rb_funcall( obj, s_unpack, 1, rb_str_new2( "m" ) ); + obj = rb_ary_shift( arr ); + } + else if ( strcmp( type_id, "bool#yes" ) == 0 ) + { + obj = Qtrue; + } + else if ( strcmp( type_id, "bool#no" ) == 0 ) + { + obj = Qfalse; + } + else if ( strcmp( type_id, "int#hex" ) == 0 ) + { + syck_str_blow_away_commas( n ); + obj = rb_cstr2inum( n->data.str->ptr, 16 ); + } + else if ( strcmp( type_id, "int#oct" ) == 0 ) + { + syck_str_blow_away_commas( n ); + obj = rb_cstr2inum( n->data.str->ptr, 8 ); + } + else if ( strcmp( type_id, "int#base60" ) == 0 ) + { + char *ptr, *end; + long sixty = 1; + long total = 0; + syck_str_blow_away_commas( n ); + ptr = n->data.str->ptr; + end = n->data.str->ptr + n->data.str->len; + while ( end > ptr ) + { + long bnum = 0; + char *colon = end - 1; + while ( colon >= ptr && *colon != ':' ) + { + colon--; + } + if ( colon >= ptr && *colon == ':' ) *colon = '\0'; + + bnum = strtol( colon + 1, NULL, 10 ); + total += bnum * sixty; + sixty *= 60; + end = colon; + } + obj = INT2FIX(total); + } + else if ( strncmp( type_id, "int", 3 ) == 0 ) + { + syck_str_blow_away_commas( n ); + obj = rb_cstr2inum( n->data.str->ptr, 10 ); + } + else if ( strcmp( type_id, "float#base60" ) == 0 ) + { + char *ptr, *end; + long sixty = 1; + double total = 0.0; + syck_str_blow_away_commas( n ); + ptr = n->data.str->ptr; + end = n->data.str->ptr + n->data.str->len; + while ( end > ptr ) + { + double bnum = 0; + char *colon = end - 1; + while ( colon >= ptr && *colon != ':' ) + { + colon--; + } + if ( colon >= ptr && *colon == ':' ) *colon = '\0'; + + bnum = strtod( colon + 1, NULL ); + total += bnum * sixty; + sixty *= 60; + end = colon; + } + obj = rb_float_new( total ); + } + else if ( strcmp( type_id, "float#nan" ) == 0 ) + { + obj = rb_float_new( S_nan() ); + } + else if ( strcmp( type_id, "float#inf" ) == 0 ) + { + obj = rb_float_new( S_inf() ); + } + else if ( strcmp( type_id, "float#neginf" ) == 0 ) + { + obj = rb_float_new( -S_inf() ); + } + else if ( strncmp( type_id, "float", 5 ) == 0 ) + { + double f; + syck_str_blow_away_commas( n ); + f = strtod( n->data.str->ptr, NULL ); + obj = rb_float_new( f ); + } + else if ( strcmp( type_id, "timestamp#iso8601" ) == 0 ) + { + obj = rb_syck_mktime( n->data.str->ptr, n->data.str->len ); + } + else if ( strcmp( type_id, "timestamp#spaced" ) == 0 ) + { + obj = rb_syck_mktime( n->data.str->ptr, n->data.str->len ); + } + else if ( strcmp( type_id, "timestamp#ymd" ) == 0 ) + { + char *ptr = n->data.str->ptr; + VALUE year, mon, day; + + /* Year*/ + ptr[4] = '\0'; + year = INT2FIX(strtol(ptr, NULL, 10)); + + /* Month*/ + ptr += 4; + while ( !ISDIGIT( *ptr ) ) ptr++; + mon = INT2FIX(strtol(ptr, NULL, 10)); + + /* Day*/ + ptr += 2; + while ( !ISDIGIT( *ptr ) ) ptr++; + day = INT2FIX(strtol(ptr, NULL, 10)); + + if ( !cDate ) { + /* + * Load Date module + */ + rb_require( "date" ); + cDate = rb_const_get( rb_cObject, rb_intern("Date") ); + } + + obj = rb_funcall( cDate, s_new, 3, year, mon, day ); + } + else if ( strncmp( type_id, "timestamp", 9 ) == 0 ) + { + obj = rb_syck_mktime( n->data.str->ptr, n->data.str->len ); + } + else if ( strncmp( type_id, "merge", 5 ) == 0 ) + { + obj = rb_funcall( cMergeKey, s_new, 0 ); + } + else if ( strncmp( type_id, "default", 7 ) == 0 ) + { + obj = rb_funcall( cDefaultKey, s_new, 0 ); + } + else if ( n->data.str->style == scalar_plain && + n->data.str->len > 1 && + strncmp( n->data.str->ptr, ":", 1 ) == 0 ) + { + obj = rb_funcall( oDefaultResolver, s_transfer, 2, + rb_str_new2( "tag:ruby.yaml.org,2002:sym" ), + rb_str_new( n->data.str->ptr + 1, n->data.str->len - 1 ) ); + } + else if ( strcmp( type_id, "str" ) == 0 ) + { + obj = rb_str_new( n->data.str->ptr, n->data.str->len ); + } + else + { + transferred = 0; + obj = rb_str_new( n->data.str->ptr, n->data.str->len ); + } + break; + + case syck_seq_kind: + if ( type_id == NULL || strcmp( type_id, "seq" ) == 0 ) + { + transferred = 1; + } + obj = rb_ary_new2( n->data.list->idx ); + for ( i = 0; i < n->data.list->idx; i++ ) + { + rb_ary_store( obj, i, syck_seq_read( n, i ) ); + } + break; + + case syck_map_kind: + if ( type_id == NULL || strcmp( type_id, "map" ) == 0 ) + { + transferred = 1; + } + obj = rb_hash_new(); + for ( i = 0; i < n->data.pairs->idx; i++ ) + { + VALUE k = syck_map_read( n, map_key, i ); + VALUE v = syck_map_read( n, map_value, i ); + int skip_aset = 0; + + /* + * Handle merge keys + */ + if ( rb_obj_is_kind_of( k, cMergeKey ) ) + { + VALUE tmp; + if ( !NIL_P(tmp = rb_check_convert_type(v, T_HASH, "Hash", "to_hash")) ) + { + VALUE dup = rb_funcall( tmp, s_dup, 0 ); + rb_funcall( dup, s_update, 1, obj ); + obj = dup; + skip_aset = 1; + } + else if ( !NIL_P(tmp = rb_check_array_type(v)) ) + { + VALUE end = rb_ary_pop( tmp ); + VALUE tmph = rb_check_convert_type(end, T_HASH, "Hash", "to_hash"); + if ( !NIL_P(tmph) ) + { + VALUE dup = rb_funcall( tmph, s_dup, 0 ); + tmp = rb_ary_reverse( tmp ); + rb_ary_push( tmp, obj ); + rb_iterate( rb_each, tmp, syck_merge_i, dup ); + obj = dup; + skip_aset = 1; + } + } + } + else if ( rb_obj_is_kind_of( k, cDefaultKey ) ) + { + rb_funcall( obj, s_default_set, 1, v ); + skip_aset = 1; + } + + if ( ! skip_aset ) + { + rb_hash_aset( obj, k, v ); + } + } + break; + } + + *ref = obj; + return transferred; +} + +static void syck_node_mark( SyckNode *n ); + +/* + * {native mode} node handler + * - Converts data into native Ruby types + */ +SYMID +rb_syck_load_handler(p, n) + SyckParser *p; + SyckNode *n; +{ + VALUE obj = Qnil; + struct parser_xtra *bonus = (struct parser_xtra *)p->bonus; + VALUE resolver = bonus->resolver; + if ( NIL_P( resolver ) ) + { + resolver = oDefaultResolver; + } + + /* + * Create node, + */ + obj = rb_funcall( resolver, s_node_import, 1, Data_Wrap_Struct( cNode, NULL, NULL, n ) ); + + /* + * ID already set, let's alter the symbol table to accept the new object + */ + if (n->id > 0 && !NIL_P(obj)) + { + MEMCPY((void *)n->id, (void *)obj, RVALUE, 1); + MEMZERO((void *)obj, RVALUE, 1); + obj = n->id; + } + + if ( bonus->taint) OBJ_TAINT( obj ); + if ( bonus->proc != 0 ) rb_funcall(bonus->proc, s_call, 1, obj); + + rb_hash_aset(bonus->data, INT2FIX(RHASH(bonus->data)->tbl->num_entries), obj); + return obj; +} + +/* + * friendly errors. + */ +void +rb_syck_err_handler(p, msg) + SyckParser *p; + char *msg; +{ + char *endl = p->cursor; + + while ( *endl != '\0' && *endl != '\n' ) + endl++; + + endl[0] = '\0'; + rb_raise(rb_eArgError, "%s on line %d, col %d: `%s'", + msg, + p->linect, + p->cursor - p->lineptr, + p->lineptr); +} + +/* + * provide bad anchor object to the parser. + */ +SyckNode * +rb_syck_bad_anchor_handler(p, a) + SyckParser *p; + char *a; +{ + VALUE anchor_name = rb_str_new2( a ); + SyckNode *badanc = syck_new_map( rb_str_new2( "name" ), anchor_name ); + badanc->type_id = syck_strndup( "tag:ruby.yaml.org,2002:object:YAML::Syck::BadAlias", 53 ); + return badanc; +} + +/* + * data loaded based on the model requested. + */ +void +syck_set_model( p, input, model ) + VALUE p, input, model; +{ + SyckParser *parser; + Data_Get_Struct(p, SyckParser, parser); + syck_parser_handler( parser, rb_syck_load_handler ); + /* WARN: gonna be obsoleted soon!! */ + if ( model == sym_generic ) + { + rb_funcall( p, s_set_resolver, 1, oGenericResolver ); + } + syck_parser_implicit_typing( parser, 1 ); + syck_parser_taguri_expansion( parser, 1 ); + + if ( NIL_P( input ) ) + { + input = rb_ivar_get( p, s_input ); + } + if ( input == sym_bytecode ) + { + syck_parser_set_input_type( parser, syck_bytecode_utf8 ); + } + else + { + syck_parser_set_input_type( parser, syck_yaml_utf8 ); + } + syck_parser_error_handler( parser, rb_syck_err_handler ); + syck_parser_bad_anchor_handler( parser, rb_syck_bad_anchor_handler ); +} + +static int +syck_st_mark_nodes( char *key, SyckNode *n, char *arg ) +{ + if ( n != (void *)1 ) syck_node_mark( n ); + return ST_CONTINUE; +} + +/* + * mark parser nodes + */ +static void +syck_mark_parser(parser) + SyckParser *parser; +{ + struct parser_xtra *bonus = (struct parser_xtra *)parser->bonus; + rb_gc_mark_maybe(parser->root); + rb_gc_mark_maybe(parser->root_on_error); + rb_gc_mark( bonus->data ); + rb_gc_mark( bonus->proc ); + rb_gc_mark( bonus->resolver ); + + if ( parser->anchors != NULL ) + { + st_foreach( parser->anchors, syck_st_mark_nodes, 0 ); + } + if ( parser->bad_anchors != NULL ) + { + st_foreach( parser->bad_anchors, syck_st_mark_nodes, 0 ); + } +} + +/* + * Free the parser and any bonus attachment. + */ +void +rb_syck_free_parser(p) + SyckParser *p; +{ + S_FREE( p->bonus ); + syck_free_parser(p); +} + +/* + * YAML::Syck::Parser.allocate + */ +VALUE syck_parser_s_alloc _((VALUE)); +VALUE +syck_parser_s_alloc(class) + VALUE class; +{ + VALUE pobj; + SyckParser *parser = syck_new_parser(); + + parser->bonus = S_ALLOC( struct parser_xtra ); + S_MEMZERO( parser->bonus, struct parser_xtra, 1 ); + + pobj = Data_Wrap_Struct( class, syck_mark_parser, rb_syck_free_parser, parser ); + + syck_parser_set_root_on_error( parser, Qnil ); + + return pobj; +} + +/* + * YAML::Syck::Parser.initialize( resolver, options ) + */ +static VALUE +syck_parser_initialize(argc, argv, self) + int argc; + VALUE *argv; + VALUE self; +{ + VALUE options; + if (rb_scan_args(argc, argv, "01", &options) == 0) + { + options = rb_hash_new(); + } + else + { + Check_Type(options, T_HASH); + } + rb_ivar_set(self, s_options, options); + rb_ivar_set(self, s_input, Qnil); + return self; +} + +/* + * YAML::Syck::Parser.bufsize = Integer + */ +static VALUE +syck_parser_bufsize_set( self, size ) + VALUE self, size; +{ + SyckParser *parser; + + if ( rb_respond_to( size, s_to_i ) ) { + int n = NUM2INT(rb_funcall(size, s_to_i, 0)); + Data_Get_Struct(self, SyckParser, parser); + parser->bufsize = n; + } + return self; +} + +/* + * YAML::Syck::Parser.bufsize => Integer + */ +static VALUE +syck_parser_bufsize_get( self ) + VALUE self; +{ + SyckParser *parser; + + Data_Get_Struct(self, SyckParser, parser); + return INT2FIX( parser->bufsize ); +} + +/* + * YAML::Syck::Parser.load( IO or String ) + */ +VALUE +syck_parser_load(argc, argv, self) + int argc; + VALUE *argv; + VALUE self; +{ + VALUE port, proc, model, input; + SyckParser *parser; + struct parser_xtra *bonus; + + rb_scan_args(argc, argv, "11", &port, &proc); + + input = rb_hash_aref( rb_attr_get( self, s_options ), sym_input ); + model = rb_hash_aref( rb_attr_get( self, s_options ), sym_model ); + Data_Get_Struct(self, SyckParser, parser); + syck_set_model( self, input, model ); + + bonus = (struct parser_xtra *)parser->bonus; + bonus->taint = syck_parser_assign_io(parser, &port); + bonus->data = rb_hash_new(); + bonus->resolver = rb_attr_get( self, s_resolver ); + if ( NIL_P( proc ) ) bonus->proc = 0; + else bonus->proc = proc; + + return syck_parse( parser ); +} + +/* + * YAML::Syck::Parser.load_documents( IO or String ) { |doc| } + */ +VALUE +syck_parser_load_documents(argc, argv, self) + int argc; + VALUE *argv; + VALUE self; +{ + VALUE port, proc, v, input, model; + SyckParser *parser; + struct parser_xtra *bonus; + + rb_scan_args(argc, argv, "1&", &port, &proc); + + input = rb_hash_aref( rb_attr_get( self, s_options ), sym_input ); + model = rb_hash_aref( rb_attr_get( self, s_options ), sym_model ); + Data_Get_Struct(self, SyckParser, parser); + syck_set_model( self, input, model ); + + bonus = (struct parser_xtra *)parser->bonus; + bonus->taint = syck_parser_assign_io(parser, &port); + bonus->resolver = rb_attr_get( self, s_resolver ); + bonus->proc = 0; + + while ( 1 ) + { + /* Reset hash for tracking nodes */ + bonus->data = rb_hash_new(); + + /* Parse a document */ + v = syck_parse( parser ); + if ( parser->eof == 1 ) + { + break; + } + + /* Pass document to block */ + rb_funcall( proc, s_call, 1, v ); + } + + return Qnil; +} + +/* + * YAML::Syck::Parser#set_resolver + */ +VALUE +syck_parser_set_resolver( self, resolver ) + VALUE self, resolver; +{ + rb_ivar_set( self, s_resolver, resolver ); + return self; +} + +/* + * YAML::Syck::Resolver.initialize + */ +static VALUE +syck_resolver_initialize( self ) + VALUE self; +{ + VALUE tags = rb_hash_new(); + rb_ivar_set(self, s_tags, rb_hash_new()); + return self; +} + +/* + * YAML::Syck::Resolver#add_type + */ +VALUE +syck_resolver_add_type( self, taguri, cls ) + VALUE self, taguri, cls; +{ + VALUE tags = rb_attr_get(self, s_tags); + rb_hash_aset( tags, taguri, cls ); + return Qnil; +} + +/* + * YAML::Syck::Resolver#use_types_at + */ +VALUE +syck_resolver_use_types_at( self, hsh ) + VALUE self, hsh; +{ + rb_ivar_set( self, s_tags, hsh ); + return Qnil; +} + +/* + * YAML::Syck::Resolver#detect_implicit + */ +VALUE +syck_resolver_detect_implicit( self, val ) + VALUE self, val; +{ + char *type_id; + return rb_str_new2( "" ); +} + +/* + * YAML::Syck::Resolver#node_import + */ +VALUE +syck_resolver_node_import( self, node ) + VALUE self, node; +{ + SyckNode *n; + VALUE obj; + int i = 0; + Data_Get_Struct(node, SyckNode, n); + + switch (n->kind) + { + case syck_str_kind: + obj = rb_str_new( n->data.str->ptr, n->data.str->len ); + break; + + case syck_seq_kind: + obj = rb_ary_new2( n->data.list->idx ); + for ( i = 0; i < n->data.list->idx; i++ ) + { + rb_ary_store( obj, i, syck_seq_read( n, i ) ); + } + break; + + case syck_map_kind: + obj = rb_hash_new(); + for ( i = 0; i < n->data.pairs->idx; i++ ) + { + VALUE k = syck_map_read( n, map_key, i ); + VALUE v = syck_map_read( n, map_value, i ); + int skip_aset = 0; + + /* + * Handle merge keys + */ + if ( rb_obj_is_kind_of( k, cMergeKey ) ) + { + if ( rb_obj_is_kind_of( v, rb_cHash ) ) + { + VALUE dup = rb_funcall( v, s_dup, 0 ); + rb_funcall( dup, s_update, 1, obj ); + obj = dup; + skip_aset = 1; + } + else if ( rb_obj_is_kind_of( v, rb_cArray ) ) + { + VALUE end = rb_ary_pop( v ); + if ( rb_obj_is_kind_of( end, rb_cHash ) ) + { + VALUE dup = rb_funcall( end, s_dup, 0 ); + v = rb_ary_reverse( v ); + rb_ary_push( v, obj ); + rb_iterate( rb_each, v, syck_merge_i, dup ); + obj = dup; + skip_aset = 1; + } + } + } + else if ( rb_obj_is_kind_of( k, cDefaultKey ) ) + { + rb_funcall( obj, s_default_set, 1, v ); + skip_aset = 1; + } + + if ( ! skip_aset ) + { + rb_hash_aset( obj, k, v ); + } + } + break; + } + + if ( n->type_id != NULL ) + { + obj = rb_funcall( self, s_transfer, 2, rb_str_new2( n->type_id ), obj ); + } + return obj; +} + +/* + * Set instance variables + */ +VALUE +syck_set_ivars( vars, obj ) + VALUE vars, obj; +{ + VALUE ivname = rb_ary_entry( vars, 0 ); + char *ivn; + StringValue( ivname ); + ivn = S_ALLOCA_N( char, RSTRING(ivname)->len + 2 ); + ivn[0] = '@'; + ivn[1] = '\0'; + strncat( ivn, RSTRING(ivname)->ptr, RSTRING(ivname)->len ); + rb_iv_set( obj, ivn, rb_ary_entry( vars, 1 ) ); + return Qnil; +} + +/* + * YAML::Syck::Resolver#const_find + */ +VALUE +syck_const_find( const_name ) + VALUE const_name; +{ + VALUE tclass = rb_cObject; + VALUE tparts = rb_str_split( const_name, "::" ); + int i = 0; + for ( i = 0; i < RARRAY(tparts)->len; i++ ) { + VALUE tpart = rb_to_id( rb_ary_entry( tparts, i ) ); + if ( !rb_const_defined( tclass, tpart ) ) return Qnil; + tclass = rb_const_get( tclass, tpart ); + } + return tclass; +} + +/* + * YAML::Syck::Resolver#transfer + */ +VALUE +syck_resolver_transfer( self, type, val ) + VALUE self, type, val; +{ + if (NIL_P(type) || RSTRING(StringValue(type))->len == 0) + { + type = rb_funcall( self, s_detect_implicit, 1, val ); + } + + if ( ! (NIL_P(type) || RSTRING(StringValue(type))->len == 0) ) + { + VALUE str_xprivate = rb_str_new2( "x-private" ); + VALUE colon = rb_str_new2( ":" ); + VALUE tags = rb_attr_get(self, s_tags); + VALUE target_class = rb_hash_aref( tags, type ); + VALUE subclass = target_class; + VALUE obj = Qnil; + + /* + * Should no tag match exactly, check for subclass format + */ + if ( NIL_P( target_class ) ) + { + VALUE subclass_parts = rb_ary_new(); + VALUE parts = rb_str_split( type, ":" ); + + while ( RARRAY(parts)->len > 1 ) + { + VALUE partial; + rb_ary_unshift( subclass_parts, rb_ary_pop( parts ) ); + partial = rb_ary_join( parts, colon ); + target_class = rb_hash_aref( tags, partial ); + if ( NIL_P( target_class ) ) + { + rb_str_append( partial, colon ); + target_class = rb_hash_aref( tags, partial ); + } + + /* + * Possible subclass found, see if it supports subclassing + */ + if ( ! NIL_P( target_class ) ) + { + subclass = target_class; + if ( RARRAY(subclass_parts)->len > 0 && rb_respond_to( target_class, s_tag_subclasses ) && + RTEST( rb_funcall( target_class, s_tag_subclasses, 0 ) ) ) + { + VALUE subclass_v; + subclass = rb_ary_join( subclass_parts, colon ); + subclass = rb_funcall( target_class, s_tag_read_class, 1, subclass ); + subclass_v = syck_const_find( subclass ); + + if ( subclass_v != Qnil ) + { + subclass = subclass_v; + } + else if ( rb_cObject == target_class && subclass_v == Qnil ) + { + target_class = cYObject; + type = subclass; + subclass = cYObject; + } + else /* workaround for SEGV. real fix please */ + { + rb_raise( rb_eTypeError, "invalid subclass" ); + } + } + break; + } + } + } + + /* rb_raise(rb_eTypeError, "invalid typing scheme: %s given", + * scheme); + */ + + if ( rb_respond_to( target_class, s_call ) ) + { + obj = rb_funcall( target_class, s_call, 2, type, val ); + } + else + { + if ( rb_respond_to( target_class, s_yaml_new ) ) + { + obj = rb_funcall( target_class, s_yaml_new, 3, subclass, type, val ); + } + else if ( !NIL_P( target_class ) ) + { + if ( subclass == rb_cBignum ) + { + obj = rb_str2inum( val, 10 ); /* for yaml dumped by 1.8.3 [ruby-core:6159] */ + } + else + { + obj = rb_obj_alloc( subclass ); + } + + if ( rb_respond_to( obj, s_yaml_initialize ) ) + { + rb_funcall( obj, s_yaml_initialize, 2, type, val ); + } + else if ( !NIL_P( obj ) && rb_obj_is_instance_of( val, rb_cHash ) ) + { + rb_iterate( rb_each, val, syck_set_ivars, obj ); + } + } + else + { + VALUE parts = rb_str_split( type, ":" ); + VALUE scheme = rb_ary_shift( parts ); + if ( rb_str_cmp( scheme, str_xprivate ) == 0 ) + { + VALUE name = rb_ary_join( parts, colon ); + obj = rb_funcall( cPrivateType, s_new, 2, name, val ); + } + else + { + VALUE domain = rb_ary_shift( parts ); + VALUE name = rb_ary_join( parts, colon ); + obj = rb_funcall( cDomainType, s_new, 3, domain, name, val ); + } + } + } + val = obj; + } + + return val; +} + +/* + * YAML::Syck::Resolver#tagurize + */ +VALUE +syck_resolver_tagurize( self, val ) + VALUE self, val; +{ + VALUE tmp = rb_check_string_type(val); + + if ( !NIL_P(tmp) ) + { + char *taguri = syck_type_id_to_uri( RSTRING(tmp)->ptr ); + val = rb_str_new2( taguri ); + S_FREE( taguri ); + } + + return val; +} + +/* + * YAML::Syck::DefaultResolver#detect_implicit + */ +VALUE +syck_defaultresolver_detect_implicit( self, val ) + VALUE self, val; +{ + char *type_id; + VALUE tmp = rb_check_string_type(val); + + if ( !NIL_P(tmp) ) + { + val = tmp; + type_id = syck_match_implicit( RSTRING(val)->ptr, RSTRING(val)->len ); + return rb_str_new2( type_id ); + } + + return rb_str_new2( "" ); +} + +/* + * YAML::Syck::DefaultResolver#node_import + */ +VALUE +syck_defaultresolver_node_import( self, node ) + VALUE self, node; +{ + SyckNode *n; + VALUE obj; + Data_Get_Struct( node, SyckNode, n ); + if ( !yaml_org_handler( n, &obj ) ) + { + obj = rb_funcall( self, s_transfer, 2, rb_str_new2( n->type_id ), obj ); + } + return obj; +} + +/* + * YAML::Syck::GenericResolver#node_import + */ +VALUE +syck_genericresolver_node_import( self, node ) + VALUE self, node; +{ + SyckNode *n; + int i = 0; + VALUE t = Qnil, obj = Qnil, v = Qnil, style = Qnil; + Data_Get_Struct(node, SyckNode, n); + + if ( n->type_id != NULL ) + { + t = rb_str_new2(n->type_id); + } + + switch (n->kind) + { + case syck_str_kind: + { + v = rb_str_new( n->data.str->ptr, n->data.str->len ); + if ( n->data.str->style == scalar_1quote ) + { + style = sym_1quote; + } + else if ( n->data.str->style == scalar_2quote ) + { + style = sym_2quote; + } + else if ( n->data.str->style == scalar_fold ) + { + style = sym_fold; + } + else if ( n->data.str->style == scalar_literal ) + { + style = sym_literal; + } + else if ( n->data.str->style == scalar_plain ) + { + style = sym_plain; + } + obj = rb_funcall( cScalar, s_new, 3, t, v, style ); + } + break; + + case syck_seq_kind: + rb_iv_set(obj, "@kind", sym_seq); + v = rb_ary_new2( syck_seq_count( n ) ); + for ( i = 0; i < syck_seq_count( n ); i++ ) + { + rb_ary_store( v, i, syck_seq_read( n, i ) ); + } + if ( n->data.list->style == seq_inline ) + { + style = sym_inline; + } + obj = rb_funcall( cSeq, s_new, 3, t, v, style ); + break; + + case syck_map_kind: + rb_iv_set(obj, "@kind", sym_map); + v = rb_hash_new(); + for ( i = 0; i < syck_map_count( n ); i++ ) + { + rb_hash_aset( v, syck_map_read( n, map_key, i ), syck_map_read( n, map_value, i ) ); + } + if ( n->data.pairs->style == map_inline ) + { + style = sym_inline; + } + obj = rb_funcall( cMap, s_new, 3, t, v, style ); + break; + } + + return obj; +} + +/* + * YAML::Syck::BadAlias.initialize + */ +VALUE +syck_badalias_initialize( self, val ) + VALUE self, val; +{ + rb_iv_set( self, "@name", val ); + return self; +} + +/* + * YAML::Syck::BadAlias.<=> + */ +VALUE +syck_badalias_cmp( alias1, alias2 ) + VALUE alias1, alias2; +{ + VALUE str1 = rb_ivar_get( alias1, s_name ); + VALUE str2 = rb_ivar_get( alias2, s_name ); + VALUE val = rb_funcall( str1, s_cmp, 1, str2 ); + return val; +} + +/* + * YAML::DomainType.initialize + */ +VALUE +syck_domaintype_initialize( self, domain, type_id, val ) + VALUE self, domain, type_id, val; +{ + rb_iv_set( self, "@domain", domain ); + rb_iv_set( self, "@type_id", type_id ); + rb_iv_set( self, "@value", val ); + return self; +} + +/* + * YAML::Object.initialize + */ +VALUE +syck_yobject_initialize( self, klass, ivars ) + VALUE self, klass, ivars; +{ + rb_iv_set( self, "@class", klass ); + rb_iv_set( self, "@ivars", ivars ); + return self; +} + +/* + * YAML::PrivateType.initialize + */ +VALUE +syck_privatetype_initialize( self, type_id, val ) + VALUE self, type_id, val; +{ + rb_iv_set( self, "@type_id", type_id ); + rb_iv_set( self, "@value", val ); + return self; +} + +/* + * Mark node contents. + */ +static void +syck_node_mark( n ) + SyckNode *n; +{ + int i; + rb_gc_mark_maybe( n->id ); + switch ( n->kind ) + { + case syck_seq_kind: + for ( i = 0; i < n->data.list->idx; i++ ) + { + rb_gc_mark( syck_seq_read( n, i ) ); + } + break; + + case syck_map_kind: + for ( i = 0; i < n->data.pairs->idx; i++ ) + { + rb_gc_mark( syck_map_read( n, map_key, i ) ); + rb_gc_mark( syck_map_read( n, map_value, i ) ); + } + break; + } +#if 0 /* maybe needed */ + if ( n->shortcut ) syck_node_mark( n->shortcut ); /* caution: maybe cyclic */ +#endif +} + +/* + * YAML::Syck::Scalar.allocate + */ +VALUE +syck_scalar_alloc( class ) + VALUE class; +{ + SyckNode *node = syck_alloc_str(); + VALUE obj = Data_Wrap_Struct( class, syck_node_mark, syck_free_node, node ); + node->id = obj; + return obj; +} + +/* + * YAML::Syck::Scalar.initialize + */ +VALUE +syck_scalar_initialize( self, type_id, val, style ) + VALUE self, type_id, val, style; +{ + rb_iv_set( self, "@kind", sym_scalar ); + rb_funcall( self, s_type_id_set, 1, type_id ); + rb_funcall( self, s_value_set, 1, val ); + rb_funcall( self, s_style_set, 1, style ); + return self; +} + +/* + * YAML::Syck::Scalar.style= + */ +VALUE +syck_scalar_style_set( self, style ) + VALUE self, style; +{ + SyckNode *node; + Data_Get_Struct( self, SyckNode, node ); + + if ( NIL_P( style ) ) + { + node->data.str->style = scalar_none; + } + else if ( style == sym_1quote ) + { + node->data.str->style = scalar_1quote; + } + else if ( style == sym_2quote ) + { + node->data.str->style = scalar_2quote; + } + else if ( style == sym_fold ) + { + node->data.str->style = scalar_fold; + } + else if ( style == sym_literal ) + { + node->data.str->style = scalar_literal; + } + else if ( style == sym_plain ) + { + node->data.str->style = scalar_plain; + } + + rb_iv_set( self, "@style", style ); + return self; +} + +/* + * YAML::Syck::Scalar.value= + */ +VALUE +syck_scalar_value_set( self, val ) + VALUE self, val; +{ + SyckNode *node; + Data_Get_Struct( self, SyckNode, node ); + + StringValue( val ); + node->data.str->ptr = syck_strndup( RSTRING(val)->ptr, RSTRING(val)->len ); + node->data.str->len = RSTRING(val)->len; + node->data.str->style = scalar_none; + + rb_iv_set( self, "@value", val ); + return val; +} + +/* + * YAML::Syck::Seq.allocate + */ +VALUE +syck_seq_alloc( class ) + VALUE class; +{ + SyckNode *node; + VALUE obj; + node = syck_alloc_seq(); + obj = Data_Wrap_Struct( class, syck_node_mark, syck_free_node, node ); + node->id = obj; + return obj; +} + +/* + * YAML::Syck::Seq.initialize + */ +VALUE +syck_seq_initialize( self, type_id, val, style ) + VALUE self, type_id, val, style; +{ + SyckNode *node; + Data_Get_Struct( self, SyckNode, node ); + + rb_iv_set( self, "@kind", sym_seq ); + rb_funcall( self, s_type_id_set, 1, type_id ); + rb_funcall( self, s_value_set, 1, val ); + rb_funcall( self, s_style_set, 1, style ); + return self; +} + +/* + * YAML::Syck::Seq.value= + */ +VALUE +syck_seq_value_set( self, val ) + VALUE self, val; +{ + SyckNode *node; + Data_Get_Struct( self, SyckNode, node ); + + val = rb_check_array_type( val ); + if ( !NIL_P( val ) ) { + int i; + syck_seq_empty( node ); + for ( i = 0; i < RARRAY( val )->len; i++ ) + { + syck_seq_add( node, rb_ary_entry(val, i) ); + } + } + + rb_iv_set( self, "@value", val ); + return val; +} + +/* + * YAML::Syck::Seq.add + */ +VALUE +syck_seq_add_m( self, val ) + VALUE self, val; +{ + SyckNode *node; + VALUE emitter = rb_ivar_get( self, s_emitter ); + Data_Get_Struct( self, SyckNode, node ); + + if ( rb_respond_to( emitter, s_node_export ) ) { + val = rb_funcall( emitter, s_node_export, 1, val ); + } + syck_seq_add( node, val ); + rb_ary_push( rb_ivar_get( self, s_value ), val ); + + return self; +} + +/* + * YAML::Syck::Seq.style= + */ +VALUE +syck_seq_style_set( self, style ) + VALUE self, style; +{ + SyckNode *node; + Data_Get_Struct( self, SyckNode, node ); + + if ( style == sym_inline ) + { + node->data.list->style = seq_inline; + } + else + { + node->data.list->style = seq_none; + } + + rb_iv_set( self, "@style", style ); + return self; +} + +/* + * YAML::Syck::Map.allocate + */ +VALUE +syck_map_alloc( class ) + VALUE class; +{ + SyckNode *node; + VALUE obj; + node = syck_alloc_map(); + obj = Data_Wrap_Struct( class, syck_node_mark, syck_free_node, node ); + node->id = obj; + return obj; +} + +/* + * YAML::Syck::Map.initialize + */ +VALUE +syck_map_initialize( self, type_id, val, style ) + VALUE self, type_id, val, style; +{ + SyckNode *node; + Data_Get_Struct( self, SyckNode, node ); + + if ( !NIL_P( val ) ) + { + VALUE hsh = rb_check_convert_type(val, T_HASH, "Hash", "to_hash"); + VALUE keys; + int i; + if ( NIL_P(hsh) ) + { + rb_raise( rb_eTypeError, "wrong argument type" ); + } + + keys = rb_funcall( hsh, s_keys, 0 ); + for ( i = 0; i < RARRAY(keys)->len; i++ ) + { + VALUE key = rb_ary_entry(keys, i); + syck_map_add( node, key, rb_hash_aref(hsh, key) ); + } + } + + rb_iv_set( self, "@kind", sym_seq ); + rb_funcall( self, s_type_id_set, 1, type_id ); + rb_funcall( self, s_value_set, 1, val ); + rb_funcall( self, s_style_set, 1, style ); + return self; +} + +/* + * YAML::Syck::Map.value= + */ +VALUE +syck_map_value_set( self, val ) + VALUE self, val; +{ + SyckNode *node; + Data_Get_Struct( self, SyckNode, node ); + + if ( !NIL_P( val ) ) + { + VALUE hsh = rb_check_convert_type(val, T_HASH, "Hash", "to_hash"); + VALUE keys; + int i; + if ( NIL_P(hsh) ) + { + rb_raise( rb_eTypeError, "wrong argument type" ); + } + + syck_map_empty( node ); + keys = rb_funcall( hsh, s_keys, 0 ); + for ( i = 0; i < RARRAY(keys)->len; i++ ) + { + VALUE key = rb_ary_entry(keys, i); + syck_map_add( node, key, rb_hash_aref(hsh, key) ); + } + } + + rb_iv_set( self, "@value", val ); + return val; +} + +/* + * YAML::Syck::Map.add + */ +VALUE +syck_map_add_m( self, key, val ) + VALUE self, key, val; +{ + SyckNode *node; + VALUE emitter = rb_ivar_get( self, s_emitter ); + Data_Get_Struct( self, SyckNode, node ); + + if ( rb_respond_to( emitter, s_node_export ) ) { + key = rb_funcall( emitter, s_node_export, 1, key ); + val = rb_funcall( emitter, s_node_export, 1, val ); + } + syck_map_add( node, key, val ); + rb_hash_aset( rb_ivar_get( self, s_value ), key, val ); + + return self; +} + +/* + * YAML::Syck::Map.style= + */ +VALUE +syck_map_style_set( self, style ) + VALUE self, style; +{ + SyckNode *node; + Data_Get_Struct( self, SyckNode, node ); + + if ( style == sym_inline ) + { + node->data.pairs->style = map_inline; + } + else + { + node->data.pairs->style = map_none; + } + + rb_iv_set( self, "@style", style ); + return self; +} + +/* + * Cloning method for all node types + */ +VALUE +syck_node_init_copy( copy, orig ) + VALUE copy, orig; +{ + SyckNode *copy_n; + SyckNode *orig_n; + + if ( copy == orig ) + return copy; + + if ( TYPE( orig ) != T_DATA ) + { + rb_raise( rb_eTypeError, "wrong argument type" ); + } + + Data_Get_Struct( orig, SyckNode, orig_n ); + Data_Get_Struct( copy, SyckNode, copy_n ); + MEMCPY( copy_n, orig_n, SyckNode, 1 ); + return copy; +} + +/* + * YAML::Syck::Node#type_id= + */ +VALUE +syck_node_type_id_set( self, type_id ) + VALUE self, type_id; +{ + SyckNode *node; + Data_Get_Struct( self, SyckNode, node ); + + S_FREE( node->type_id ); + + if ( !NIL_P( type_id ) ) { + StringValue( type_id ); + node->type_id = syck_strndup( RSTRING(type_id)->ptr, RSTRING(type_id)->len ); + } + + rb_iv_set( self, "@type_id", type_id ); + return type_id; +} + +/* + * YAML::Syck::Node.transform + */ +VALUE +syck_node_transform( self ) + VALUE self; +{ + VALUE t; + SyckNode *n; + SyckNode *orig_n; + Data_Get_Struct(self, SyckNode, orig_n); + t = Data_Wrap_Struct( cNode, syck_node_mark, syck_free_node, 0 ); + + switch (orig_n->kind) + { + case syck_map_kind: + { + int i; + DATA_PTR(t) = n = syck_alloc_map(); + for ( i = 0; i < orig_n->data.pairs->idx; i++ ) + { + syck_map_add( n, rb_funcall( syck_map_read( orig_n, map_key, i ), s_transform, 0 ), + rb_funcall( syck_map_read( orig_n, map_value, i ), s_transform, 0 ) ); + } + } + break; + + case syck_seq_kind: + { + int i; + DATA_PTR(t) = n = syck_alloc_seq(); + for ( i = 0; i < orig_n->data.list->idx; i++ ) + { + syck_seq_add( n, rb_funcall( syck_seq_read( orig_n, i ), s_transform, 0 ) ); + } + } + break; + + case syck_str_kind: + DATA_PTR(t) = n = syck_new_str2( orig_n->data.str->ptr, orig_n->data.str->len, orig_n->data.str->style ); + break; + } + + if ( orig_n->type_id != NULL ) + { + n->type_id = syck_strndup( orig_n->type_id, strlen( orig_n->type_id ) ); + } + if ( orig_n->anchor != NULL ) + { + n->anchor = syck_strndup( orig_n->anchor, strlen( orig_n->anchor ) ); + } + n->id = t; + return rb_funcall( oDefaultResolver, s_node_import, 1, t ); +} + +/* + * Emitter callback: assembles YAML document events from + * Ruby symbols. This is a brilliant way to do it. + * No one could possibly object. + */ +void +rb_syck_emitter_handler(e, data) + SyckEmitter *e; + st_data_t data; +{ + SyckNode *n; + Data_Get_Struct((VALUE)data, SyckNode, n); + + switch (n->kind) + { + case syck_map_kind: + { + int i; + syck_emit_map( e, n->type_id, n->data.pairs->style ); + for ( i = 0; i < n->data.pairs->idx; i++ ) + { + syck_emit_item( e, syck_map_read( n, map_key, i ) ); + syck_emit_item( e, syck_map_read( n, map_value, i ) ); + } + syck_emit_end( e ); + } + break; + + case syck_seq_kind: + { + int i; + syck_emit_seq( e, n->type_id, n->data.list->style ); + for ( i = 0; i < n->data.list->idx; i++ ) + { + syck_emit_item( e, syck_seq_read( n, i ) ); + } + syck_emit_end( e ); + } + break; + + case syck_str_kind: + { + syck_emit_scalar( e, n->type_id, n->data.str->style, 0, 0, 0, n->data.str->ptr, n->data.str->len ); + } + break; + } +} + +/* + * Handle output from the emitter + */ +void +rb_syck_output_handler( emitter, str, len ) + SyckEmitter *emitter; + char *str; + long len; +{ + struct emitter_xtra *bonus = (struct emitter_xtra *)emitter->bonus; + VALUE dest = bonus->port; + if (TYPE(dest) == T_STRING) { + rb_str_cat( dest, str, len ); + } else { + rb_io_write( dest, rb_str_new( str, len ) ); + } +} + +/* + * Helper function for marking nodes in the anchor + * symbol table. + */ +void +syck_out_mark( emitter, node ) + VALUE emitter, node; +{ + SyckEmitter *emitterPtr; + struct emitter_xtra *bonus; + Data_Get_Struct(emitter, SyckEmitter, emitterPtr); + bonus = (struct emitter_xtra *)emitterPtr->bonus; + rb_ivar_set( node, s_emitter, emitter ); + /* syck_emitter_mark_node( emitterPtr, (st_data_t)node ); */ + if ( !NIL_P( bonus->oid ) ) { + rb_hash_aset( bonus->data, bonus->oid, node ); + } +} + +/* + * Mark emitter values. + */ +static void +syck_mark_emitter(emitter) + SyckEmitter *emitter; +{ + struct emitter_xtra *bonus = (struct emitter_xtra *)emitter->bonus; + rb_gc_mark( bonus->oid ); + rb_gc_mark( bonus->data ); + rb_gc_mark( bonus->port ); +} + +/* + * Free the emitter and any bonus attachment. + */ +void +rb_syck_free_emitter(e) + SyckEmitter *e; +{ + S_FREE( e->bonus ); + syck_free_emitter(e); +} + +/* + * YAML::Syck::Emitter.allocate + */ +VALUE syck_emitter_s_alloc _((VALUE)); +VALUE +syck_emitter_s_alloc(class) + VALUE class; +{ + VALUE pobj; + SyckEmitter *emitter = syck_new_emitter(); + + emitter->bonus = S_ALLOC( struct emitter_xtra ); + S_MEMZERO( emitter->bonus, struct emitter_xtra, 1 ); + + pobj = Data_Wrap_Struct( class, syck_mark_emitter, rb_syck_free_emitter, emitter ); + syck_emitter_handler( emitter, rb_syck_emitter_handler ); + syck_output_handler( emitter, rb_syck_output_handler ); + + rb_ivar_set( pobj, s_out, rb_funcall( cOut, s_new, 1, pobj ) ); + return pobj; +} + +/* + * YAML::Syck::Emitter.reset( options ) + */ +VALUE +syck_emitter_reset( argc, argv, self ) + int argc; + VALUE *argv; + VALUE self; +{ + VALUE options, tmp; + SyckEmitter *emitter; + struct emitter_xtra *bonus; + + Data_Get_Struct(self, SyckEmitter, emitter); + bonus = (struct emitter_xtra *)emitter->bonus; + + bonus->oid = Qnil; + bonus->port = rb_str_new2( "" ); + bonus->data = rb_hash_new(); + + if (rb_scan_args(argc, argv, "01", &options) == 0) + { + options = rb_hash_new(); + rb_ivar_set(self, s_options, options); + } + else if ( !NIL_P(tmp = rb_check_string_type(options)) ) + { + bonus->port = tmp; + } + else if ( rb_respond_to( options, s_write ) ) + { + bonus->port = options; + } + else + { + Check_Type(options, T_HASH); + rb_ivar_set(self, s_options, options); + } + + emitter->headless = 0; + rb_ivar_set(self, s_level, INT2FIX(0)); + rb_ivar_set(self, s_resolver, Qnil); + return self; +} + +/* + * YAML::Syck::Emitter.emit( object_id ) { |out| ... } + */ +VALUE +syck_emitter_emit( argc, argv, self ) + int argc; + VALUE *argv; + VALUE self; +{ + VALUE oid, proc; + char *anchor_name; + SyckEmitter *emitter; + struct emitter_xtra *bonus; + SYMID symple; + int level = FIX2INT(rb_ivar_get(self, s_level)) + 1; + rb_ivar_set(self, s_level, INT2FIX(level)); + + rb_scan_args(argc, argv, "1&", &oid, &proc); + Data_Get_Struct(self, SyckEmitter, emitter); + bonus = (struct emitter_xtra *)emitter->bonus; + + /* Calculate anchors, normalize nodes, build a simpler symbol table */ + bonus->oid = oid; + if ( !NIL_P( oid ) && RTEST( rb_funcall( bonus->data, s_haskey, 1, oid ) ) ) { + symple = rb_hash_aref( bonus->data, oid ); + } else { + symple = rb_funcall( proc, s_call, 1, rb_ivar_get( self, s_out ) ); + } + syck_emitter_mark_node( emitter, (st_data_t)symple ); + + /* Second pass, build emitted string */ + level -= 1; + rb_ivar_set(self, s_level, INT2FIX(level)); + if ( level == 0 ) + { + syck_emit(emitter, (st_data_t)symple); + syck_emitter_flush(emitter, 0); + + return bonus->port; + } + + return symple; +} + +/* + * YAML::Syck::Emitter#node_export + */ +VALUE +syck_emitter_node_export( self, node ) + VALUE self, node; +{ + return rb_funcall( node, s_to_yaml, 1, self ); +} + +/* + * YAML::Syck::Emitter#set_resolver + */ +VALUE +syck_emitter_set_resolver( self, resolver ) + VALUE self, resolver; +{ + rb_ivar_set( self, s_resolver, resolver ); + return self; +} + +/* + * YAML::Syck::Out::initialize + */ +VALUE +syck_out_initialize( self, emitter ) + VALUE self, emitter; +{ + rb_ivar_set( self, s_emitter, emitter ); + return self; +} + +/* + * YAML::Syck::Out::map + */ +VALUE +syck_out_map( argc, argv, self ) + int argc; + VALUE *argv; + VALUE self; +{ + VALUE type_id, style, map; + if (rb_scan_args(argc, argv, "11", &type_id, &style) == 1) { + style = Qnil; + } + map = rb_funcall( cMap, s_new, 3, type_id, rb_hash_new(), style ); + syck_out_mark( rb_ivar_get( self, s_emitter ), map ); + rb_yield( map ); + return map; +} + +/* + * YAML::Syck::Out::seq + */ +VALUE +syck_out_seq( argc, argv, self ) + int argc; + VALUE *argv; + VALUE self; +{ + VALUE type_id, style, seq; + if (rb_scan_args(argc, argv, "11", &type_id, &style) == 1) { + style = Qnil; + } + seq = rb_funcall( cSeq, s_new, 3, type_id, rb_ary_new(), style ); + syck_out_mark( rb_ivar_get( self, s_emitter ), seq ); + rb_yield( seq ); + return seq; +} + +/* + * YAML::Syck::Out::scalar +syck_out_scalar( self, type_id, str, style ) + VALUE self, type_id, str, style; + */ +VALUE +syck_out_scalar( argc, argv, self ) + int argc; + VALUE *argv; + VALUE self; +{ + VALUE type_id, str, style, scalar; + if (rb_scan_args(argc, argv, "21", &type_id, &str, &style) == 2) { + style = Qnil; + } + scalar = rb_funcall( cScalar, s_new, 3, type_id, str, style ); + syck_out_mark( rb_ivar_get( self, s_emitter ), scalar ); + return scalar; +} + +/* + * Initialize Syck extension + */ +void +Init_syck() +{ + VALUE rb_yaml = rb_define_module( "YAML" ); + VALUE rb_syck = rb_define_module_under( rb_yaml, "Syck" ); + rb_define_const( rb_syck, "VERSION", rb_str_new2( SYCK_VERSION ) ); + rb_define_module_function( rb_syck, "compile", rb_syck_compile, 1 ); + + /* + * Global symbols + */ + s_new = rb_intern("new"); + s_utc = rb_intern("utc"); + s_at = rb_intern("at"); + s_to_f = rb_intern("to_f"); + s_to_i = rb_intern("to_i"); + s_read = rb_intern("read"); + s_binmode = rb_intern("binmode"); + s_transfer = rb_intern("transfer"); + s_call = rb_intern("call"); + s_cmp = rb_intern("<=>"); + s_intern = rb_intern("intern"); + s_update = rb_intern("update"); + s_detect_implicit = rb_intern("detect_implicit"); + s_dup = rb_intern("dup"); + s_default_set = rb_intern("default="); + s_match = rb_intern("match"); + s_push = rb_intern("push"); + s_haskey = rb_intern("has_key?"); + s_keys = rb_intern("keys"); + s_node_import = rb_intern("node_import"); + s_tr_bang = rb_intern("tr!"); + s_unpack = rb_intern("unpack"); + s_write = rb_intern("write"); + s_tag_read_class = rb_intern( "yaml_tag_read_class" ); + s_tag_subclasses = rb_intern( "yaml_tag_subclasses?" ); + s_emitter = rb_intern( "emitter" ); + s_set_resolver = rb_intern( "set_resolver" ); + s_node_export = rb_intern( "node_export" ); + s_to_yaml = rb_intern( "to_yaml" ); + s_transform = rb_intern( "transform" ); + s_yaml_new = rb_intern("yaml_new"); + s_yaml_initialize = rb_intern("yaml_initialize"); + + s_tags = rb_intern("@tags"); + s_name = rb_intern("@name"); + s_options = rb_intern("@options"); + s_kind = rb_intern("@kind"); + s_type_id = rb_intern("@type_id"); + s_type_id_set = rb_intern("type_id="); + s_resolver = rb_intern("@resolver"); + s_level = rb_intern( "@level" ); + s_style = rb_intern("@style"); + s_style_set = rb_intern("style="); + s_value = rb_intern("@value"); + s_value_set = rb_intern("value="); + s_out = rb_intern("@out"); + s_input = rb_intern("@input"); + + sym_model = ID2SYM(rb_intern("Model")); + sym_generic = ID2SYM(rb_intern("Generic")); + sym_bytecode = ID2SYM(rb_intern("bytecode")); + sym_map = ID2SYM(rb_intern("map")); + sym_scalar = ID2SYM(rb_intern("scalar")); + sym_seq = ID2SYM(rb_intern("seq")); + sym_1quote = ID2SYM(rb_intern("quote1")); + sym_2quote = ID2SYM(rb_intern("quote2")); + sym_fold = ID2SYM(rb_intern("fold")); + sym_literal = ID2SYM(rb_intern("literal")); + sym_plain = ID2SYM(rb_intern("plain")); + sym_inline = ID2SYM(rb_intern("inline")); + + /* + * Define YAML::Syck::Resolver class + */ + cResolver = rb_define_class_under( rb_syck, "Resolver", rb_cObject ); + rb_define_attr( cResolver, "tags", 1, 1 ); + rb_define_method( cResolver, "initialize", syck_resolver_initialize, 0 ); + rb_define_method( cResolver, "add_type", syck_resolver_add_type, 2 ); + rb_define_method( cResolver, "use_types_at", syck_resolver_use_types_at, 1 ); + rb_define_method( cResolver, "detect_implicit", syck_resolver_detect_implicit, 1 ); + rb_define_method( cResolver, "transfer", syck_resolver_transfer, 2 ); + rb_define_method( cResolver, "node_import", syck_resolver_node_import, 1 ); + rb_define_method( cResolver, "tagurize", syck_resolver_tagurize, 1 ); + + rb_global_variable( &oDefaultResolver ); + oDefaultResolver = rb_funcall( cResolver, rb_intern( "new" ), 0 ); + rb_define_singleton_method( oDefaultResolver, "node_import", syck_defaultresolver_node_import, 1 ); + rb_define_singleton_method( oDefaultResolver, "detect_implicit", syck_defaultresolver_detect_implicit, 1 ); + rb_define_const( rb_syck, "DefaultResolver", oDefaultResolver ); + rb_global_variable( &oGenericResolver ); + oGenericResolver = rb_funcall( cResolver, rb_intern( "new" ), 0 ); + rb_define_singleton_method( oGenericResolver, "node_import", syck_genericresolver_node_import, 1 ); + rb_define_const( rb_syck, "GenericResolver", oGenericResolver ); + + /* + * Define YAML::Syck::Parser class + */ + cParser = rb_define_class_under( rb_syck, "Parser", rb_cObject ); + rb_define_attr( cParser, "options", 1, 1 ); + rb_define_attr( cParser, "resolver", 1, 1 ); + rb_define_attr( cParser, "input", 1, 1 ); + rb_define_alloc_func( cParser, syck_parser_s_alloc ); + rb_define_method(cParser, "initialize", syck_parser_initialize, -1 ); + rb_define_method(cParser, "bufsize=", syck_parser_bufsize_set, 1 ); + rb_define_method(cParser, "bufsize", syck_parser_bufsize_get, 0 ); + rb_define_method(cParser, "load", syck_parser_load, -1); + rb_define_method(cParser, "load_documents", syck_parser_load_documents, -1); + rb_define_method(cParser, "set_resolver", syck_parser_set_resolver, 1); + + /* + * Define YAML::Syck::Node class + */ + cNode = rb_define_class_under( rb_syck, "Node", rb_cObject ); + rb_define_method( cNode, "initialize_copy", syck_node_init_copy, 1 ); + rb_define_attr( cNode, "emitter", 1, 1 ); + rb_define_attr( cNode, "resolver", 1, 1 ); + rb_define_attr( cNode, "kind", 1, 0 ); + rb_define_attr( cNode, "type_id", 1, 0 ); + rb_define_attr( cNode, "value", 1, 0 ); + rb_define_method( cNode, "type_id=", syck_node_type_id_set, 1 ); + rb_define_method( cNode, "transform", syck_node_transform, 0); + + /* + * Define YAML::Syck::Scalar, YAML::Syck::Seq, YAML::Syck::Map -- + * all are the publicly usable variants of YAML::Syck::Node + */ + cScalar = rb_define_class_under( rb_syck, "Scalar", cNode ); + rb_define_alloc_func( cScalar, syck_scalar_alloc ); + rb_define_attr( cNode, "value", 1, 0 ); + rb_define_method( cScalar, "initialize", syck_scalar_initialize, 3 ); + rb_define_method( cScalar, "value=", syck_scalar_value_set, 1 ); + rb_define_method( cScalar, "style=", syck_scalar_style_set, 1 ); + cSeq = rb_define_class_under( rb_syck, "Seq", cNode ); + rb_define_alloc_func( cSeq, syck_seq_alloc ); + rb_define_method( cSeq, "initialize", syck_seq_initialize, 3 ); + rb_define_method( cSeq, "value=", syck_seq_value_set, 1 ); + rb_define_method( cSeq, "add", syck_seq_add_m, 1 ); + rb_define_method( cSeq, "style=", syck_seq_style_set, 1 ); + cMap = rb_define_class_under( rb_syck, "Map", cNode ); + rb_define_alloc_func( cMap, syck_map_alloc ); + rb_define_method( cMap, "initialize", syck_map_initialize, 3 ); + rb_define_method( cMap, "value=", syck_map_value_set, 1 ); + rb_define_method( cMap, "add", syck_map_add_m, 2 ); + rb_define_method( cMap, "style=", syck_map_style_set, 1 ); + + /* + * Define YAML::PrivateType class + */ + cPrivateType = rb_define_class_under( rb_yaml, "PrivateType", rb_cObject ); + rb_define_attr( cPrivateType, "type_id", 1, 1 ); + rb_define_attr( cPrivateType, "value", 1, 1 ); + rb_define_method( cPrivateType, "initialize", syck_privatetype_initialize, 2); + + /* + * Define YAML::DomainType class + */ + cDomainType = rb_define_class_under( rb_yaml, "DomainType", rb_cObject ); + rb_define_attr( cDomainType, "domain", 1, 1 ); + rb_define_attr( cDomainType, "type_id", 1, 1 ); + rb_define_attr( cDomainType, "value", 1, 1 ); + rb_define_method( cDomainType, "initialize", syck_domaintype_initialize, 3); + + /* + * Define YAML::Object class + */ + cYObject = rb_define_class_under( rb_yaml, "Object", rb_cObject ); + rb_define_attr( cYObject, "class", 1, 1 ); + rb_define_attr( cYObject, "ivars", 1, 1 ); + rb_define_method( cYObject, "initialize", syck_yobject_initialize, 2); + rb_define_method( cYObject, "yaml_initialize", syck_yobject_initialize, 2); + + /* + * Define YAML::Syck::BadAlias class + */ + cBadAlias = rb_define_class_under( rb_syck, "BadAlias", rb_cObject ); + rb_define_attr( cBadAlias, "name", 1, 1 ); + rb_define_method( cBadAlias, "initialize", syck_badalias_initialize, 1); + rb_define_method( cBadAlias, "<=>", syck_badalias_cmp, 1); + rb_include_module( cBadAlias, rb_const_get( rb_cObject, rb_intern("Comparable") ) ); + + /* + * Define YAML::Syck::MergeKey class + */ + cMergeKey = rb_define_class_under( rb_syck, "MergeKey", rb_cObject ); + + /* + * Define YAML::Syck::DefaultKey class + */ + cDefaultKey = rb_define_class_under( rb_syck, "DefaultKey", rb_cObject ); + + /* + * Define YAML::Syck::Out classes + */ + cOut = rb_define_class_under( rb_syck, "Out", rb_cObject ); + rb_define_attr( cOut, "emitter", 1, 1 ); + rb_define_method( cOut, "initialize", syck_out_initialize, 1 ); + rb_define_method( cOut, "map", syck_out_map, -1 ); + rb_define_method( cOut, "seq", syck_out_seq, -1 ); + rb_define_method( cOut, "scalar", syck_out_scalar, -1 ); + + /* + * Define YAML::Syck::Emitter class + */ + cEmitter = rb_define_class_under( rb_syck, "Emitter", rb_cObject ); + rb_define_attr( cEmitter, "level", 1, 1 ); + rb_define_alloc_func( cEmitter, syck_emitter_s_alloc ); + rb_define_method( cEmitter, "initialize", syck_emitter_reset, -1 ); + rb_define_method( cEmitter, "reset", syck_emitter_reset, -1 ); + rb_define_method( cEmitter, "emit", syck_emitter_emit, -1 ); + rb_define_method( cEmitter, "set_resolver", syck_emitter_set_resolver, 1); + rb_define_method( cEmitter, "node_export", syck_emitter_node_export, 1); +} + -- cgit v1.2.3