1 /* -*- indent-tabs-mode: nil -*- */
2 /*
3  * rubyext.c
4  *
5  * $Author$
6  * $Date$
7  *
8  * Copyright (C) 2003-2005 why the lucky stiff
9  */
10 
11 #include "ruby.h"
12 #include "syck.h"
13 #include <sys/types.h>
14 #include <time.h>
15 
16 typedef struct RVALUE {
17     union {
18 #if 0
19     struct {
20         unsigned long flags;    /* always 0 for freed obj */
21         struct RVALUE *next;
22     } free;
23 #endif
24     struct RBasic  basic;
25     struct RObject object;
26     struct RClass  klass;
27     /*struct RFloat  flonum;*/
28     /*struct RString string;*/
29     struct RArray  array;
30     /*struct RRegexp regexp;*/
31     struct RHash   hash;
32     /*struct RData   data;*/
33     struct RStruct rstruct;
34     /*struct RBignum bignum;*/
35     /*struct RFile   file;*/
36     } as;
37 } RVALUE;
38 
39 typedef struct {
40    long hash;
41    char *buffer;
42    long length;
43    long remaining;
44    int  printed;
45 } bytestring_t;
46 
47 #define RUBY_DOMAIN   "ruby.yaml.org,2002"
48 
49 #ifndef StringValue
50 #define StringValue(v) (v)
51 #endif
52 #ifndef rb_attr_get
53 #define rb_attr_get(o, i)  rb_ivar_get(o, i)
54 #endif
55 
56 #ifdef RARRAY_LEN
57 #undef T_SYMBOL
58 #define T_SYMBOL T_STRING
rb_str_len(VALUE s)59 static inline long  rb_str_len(VALUE s) {return RSTRING_LEN(s);}
rb_str_ptr(VALUE s)60 static inline char *rb_str_ptr(VALUE s) {return RSTRING_PTR(s);}
rb_ary_len(VALUE s)61 static inline long  rb_ary_len(VALUE s) {return  RARRAY_LEN(s);}
rb_ary_ptr(VALUE s)62 static inline VALUE *rb_ary_ptr(VALUE s) {return  RARRAY_PTR(s);}
63 #else
rb_str_len(VALUE s)64 static inline long  rb_str_len(VALUE s) {return rb_str_len(s);}
rb_str_ptr(VALUE s)65 static inline char *rb_str_ptr(VALUE s) {return rb_str_ptr(s);}
rb_ary_len(VALUE s)66 static inline long  rb_ary_len(VALUE s) {return  rb_ary_len(s);}
rb_ary_ptr(VALUE s)67 static inline VALUE *rb_ary_ptr(VALUE s) {return  rb_ary_ptr(s);}
68 #endif
69 
70 /*
71  * symbols and constants
72  */
73 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, s_each;
74 static ID s_tags, s_kind, s_name, s_options, s_type_id, s_type_id_set, s_style, s_style_set, s_value, s_value_set;
75 static VALUE sym_model, sym_generic, sym_input, sym_bytecode;
76 static VALUE sym_scalar, sym_seq, sym_map;
77 static VALUE sym_1quote, sym_2quote, sym_fold, sym_literal, sym_plain, sym_inline;
78 static VALUE cDate, cNode, cMap, cSeq, cScalar, cOut, cParser, cResolver, cPrivateType, cDomainType, cYObject, cBadAlias, cDefaultKey, cMergeKey, cEmitter;
79 static VALUE oDefaultResolver, oGenericResolver;
80 
81 /*
82  * my private collection of numerical oddities.
83  */
S_zero()84 static double S_zero()    { return 0.0; }
S_one()85 static double S_one() { return 1.0; }
S_inf()86 static double S_inf() { return S_one() / S_zero(); }
S_nan()87 static double S_nan() { return S_zero() / S_zero(); }
88 
89 static VALUE syck_node_transform( VALUE );
90 
91 /*
92  * handler prototypes
93  */
94 SYMID rb_syck_load_handler _((SyckParser *, SyckNode *));
95 void rb_syck_err_handler _((SyckParser *, char *));
96 SyckNode * rb_syck_bad_anchor_handler _((SyckParser *, char *));
97 void rb_syck_output_handler _((SyckEmitter *, char *, long));
98 void rb_syck_emitter_handler _((SyckEmitter *, st_data_t));
99 int syck_parser_assign_io _((SyckParser *, VALUE *));
100 VALUE syck_scalar_alloc _((VALUE class));
101 VALUE syck_seq_alloc _((VALUE class));
102 VALUE syck_map_alloc _((VALUE class));
103 
104 struct parser_xtra {
105     VALUE data;  /* Borrowed this idea from marshal.c to fix [ruby-core:8067] problem */
106     VALUE proc;
107     VALUE resolver;
108     int taint;
109 };
110 
111 struct emitter_xtra {
112     VALUE oid;
113     VALUE data;
114     VALUE port;
115 };
116 
117 /*
118  * Convert YAML to bytecode
119  */
120 VALUE
rb_syck_compile(VALUE self,VALUE port)121 rb_syck_compile(VALUE self, VALUE port)
122 {
123     SYMID oid;
124     int taint;
125     char *ret;
126     VALUE bc;
127     bytestring_t *sav;
128 
129     SyckParser *parser = syck_new_parser();
130     taint = syck_parser_assign_io(parser, &port);
131     syck_parser_handler( parser, syck_yaml2byte_handler );
132     syck_parser_error_handler( parser, NULL );
133     syck_parser_implicit_typing( parser, 0 );
134     syck_parser_taguri_expansion( parser, 0 );
135     oid = syck_parse( parser );
136     syck_lookup_sym( parser, oid, (char **)&sav );
137 
138     ret = S_ALLOCA_N( char, strlen( sav->buffer ) + 3 );
139     ret[0] = '\0';
140     strcat( ret, "D\n" );
141     strcat( ret, sav->buffer );
142 
143     syck_free_parser( parser );
144 
145     bc = rb_str_new2( ret );
146     if ( taint )      OBJ_TAINT( bc );
147     return bc;
148 }
149 
150 /*
151  * read from io.
152  */
153 long
rb_syck_io_str_read(char * buf,SyckIoStr * str,long max_size,long skip)154 rb_syck_io_str_read( char *buf, SyckIoStr *str, long max_size, long skip )
155 {
156     long len = 0;
157 
158     ASSERT( str != NULL );
159     max_size -= skip;
160 
161     if ( max_size <= 0 ) max_size = 0;
162     else
163     {
164         /*
165          * call io#read.
166          */
167         VALUE src = (VALUE)str->ptr;
168         VALUE n = LONG2NUM(max_size);
169         VALUE str2 = rb_funcall2(src, s_read, 1, &n);
170         if (!NIL_P(str2))
171         {
172             StringValue(str2);
173             len = rb_str_len(str2);
174             memcpy( buf + skip, rb_str_ptr(str2), len );
175         }
176     }
177     len += skip;
178     buf[len] = '\0';
179     return len;
180 }
181 
182 /*
183  * determine: are we reading from a string or io?
184  * (returns tainted? boolean)
185  */
186 int
syck_parser_assign_io(SyckParser * parser,VALUE * pport)187 syck_parser_assign_io(SyckParser *parser, VALUE *pport)
188 {
189     int taint = Qtrue;
190     VALUE tmp, port = *pport;
191     if (!NIL_P(tmp = rb_check_string_type(port))) {
192         taint = OBJ_TAINTED(port); /* original taintedness */
193         port = tmp;
194         syck_parser_str( parser, rb_str_ptr(port), rb_str_len(port), NULL );
195     }
196     else if (rb_respond_to(port, s_read)) {
197         if (rb_respond_to(port, s_binmode)) {
198             rb_funcall2(port, s_binmode, 0, 0);
199         }
200         syck_parser_str( parser, (char *)port, 0, rb_syck_io_str_read );
201     }
202     else {
203         rb_raise(rb_eTypeError, "instance of IO needed");
204     }
205     *pport = port;
206     return taint;
207 }
208 
209 /*
210  * Get value in hash by key, forcing an empty hash if nil.
211  */
212 VALUE
syck_get_hash_aref(VALUE hsh,VALUE key)213 syck_get_hash_aref(VALUE hsh, VALUE key)
214 {
215    VALUE val = rb_hash_aref( hsh, key );
216    if ( NIL_P( val ) )
217    {
218        val = rb_hash_new();
219        rb_hash_aset(hsh, key, val);
220    }
221    return val;
222 }
223 
224 /*
225  * creating timestamps
226  */
227 SYMID
rb_syck_mktime(char * str,long len)228 rb_syck_mktime(char *str, long len)
229 {
230     VALUE time;
231     char *ptr = str;
232     VALUE year = INT2FIX(0);
233     VALUE mon = INT2FIX(0);
234     VALUE day = INT2FIX(0);
235     VALUE hour = INT2FIX(0);
236     VALUE min = INT2FIX(0);
237     VALUE sec = INT2FIX(0);
238     long usec;
239 
240     /* Year*/
241     if ( ptr[0] != '\0' && len > 0 ) {
242         year = INT2FIX(strtol(ptr, NULL, 10));
243     }
244 
245     /* Month*/
246     ptr += 4;
247     if ( ptr[0] != '\0' && len > ptr - str ) {
248         while ( !ISDIGIT( *ptr ) ) ptr++;
249         mon = INT2FIX(strtol(ptr, NULL, 10));
250     }
251 
252     /* Day*/
253     ptr += 2;
254     if ( ptr[0] != '\0' && len > ptr - str ) {
255         while ( !ISDIGIT( *ptr ) ) ptr++;
256         day = INT2FIX(strtol(ptr, NULL, 10));
257     }
258 
259     /* Hour*/
260     ptr += 2;
261     if ( ptr[0] != '\0' && len > ptr - str ) {
262         while ( !ISDIGIT( *ptr ) ) ptr++;
263         hour = INT2FIX(strtol(ptr, NULL, 10));
264     }
265 
266     /* Minute */
267     ptr += 2;
268     if ( ptr[0] != '\0' && len > ptr - str ) {
269         while ( !ISDIGIT( *ptr ) ) ptr++;
270         min = INT2FIX(strtol(ptr, NULL, 10));
271     }
272 
273     /* Second */
274     ptr += 2;
275     if ( ptr[0] != '\0' && len > ptr - str ) {
276         while ( !ISDIGIT( *ptr ) ) ptr++;
277         sec = INT2FIX(strtol(ptr, NULL, 10));
278     }
279 
280     /* Millisecond */
281     ptr += 2;
282     if ( len > ptr - str && *ptr == '.' )
283     {
284         char padded[] = "000000";
285         char *end = ptr + 1;
286         while ( isdigit( *end ) ) end++;
287         MEMCPY(padded, ptr + 1, char, end - (ptr + 1));
288         usec = strtol(padded, NULL, 10);
289     }
290     else
291     {
292         usec = 0;
293     }
294 
295     /* Time Zone*/
296     while ( len > ptr - str && *ptr != 'Z' && *ptr != '+' && *ptr != '-' && *ptr != '\0' ) ptr++;
297     if ( len > ptr - str && ( *ptr == '-' || *ptr == '+' ) )
298     {
299         time_t tz_offset = strtol(ptr, NULL, 10) * 3600;
300         time_t tmp;
301 
302         while ( *ptr != ':' && *ptr != '\0' ) ptr++;
303         if ( *ptr == ':' )
304         {
305             ptr += 1;
306             if ( tz_offset < 0 )
307             {
308                 tz_offset -= strtol(ptr, NULL, 10) * 60;
309             }
310             else
311             {
312                 tz_offset += strtol(ptr, NULL, 10) * 60;
313             }
314         }
315 
316         /* Make TZ time*/
317         time = rb_funcall(rb_cTime, s_utc, 6, year, mon, day, hour, min, sec);
318         tmp = NUM2LONG(rb_funcall(time, s_to_i, 0)) - tz_offset;
319         return rb_funcall(rb_cTime, s_at, 2, LONG2NUM(tmp), LONG2NUM(usec));
320     }
321     else
322     {
323         /* Make UTC time*/
324         return rb_funcall(rb_cTime, s_utc, 7, year, mon, day, hour, min, sec, LONG2NUM(usec));
325     }
326 }
327 
328 /*
329  * handles merging of an array of hashes
330  * (see http://www.yaml.org/type/merge/)
331  */
332 VALUE
syck_merge_i(VALUE entry,VALUE hsh)333 syck_merge_i(VALUE entry, VALUE hsh)
334 {
335     VALUE tmp;
336     if ( !NIL_P(tmp = rb_check_convert_type(entry, T_HASH, "Hash", "to_hash")) )
337     {
338         entry = tmp;
339         rb_funcall( hsh, s_update, 1, entry );
340     }
341     return Qnil;
342 }
343 
344 /*
345  * default handler for ruby.yaml.org types
346  */
347 int
yaml_org_handler(SyckNode * n,VALUE * ref)348 yaml_org_handler(SyckNode *n, VALUE *ref)
349 {
350     char *type_id = n->type_id;
351     int transferred = 0;
352     long i = 0;
353     VALUE obj = Qnil;
354 
355     if ( type_id != NULL && strncmp( type_id, "tag:yaml.org,2002:", 18 ) == 0 )
356     {
357         type_id += 18;
358     }
359 
360     switch (n->kind)
361     {
362         case syck_str_kind:
363             transferred = 1;
364             if ( type_id == NULL )
365             {
366                 obj = rb_str_new( n->data.str->ptr, n->data.str->len );
367             }
368             else if ( strcmp( type_id, "null" ) == 0 )
369             {
370                 obj = Qnil;
371             }
372             else if ( strcmp( type_id, "binary" ) == 0 )
373             {
374                 VALUE arr;
375                 obj = rb_str_new( n->data.str->ptr, n->data.str->len );
376                 rb_funcall( obj, s_tr_bang, 2, rb_str_new2( "\n\t " ), rb_str_new2( "" ) );
377                 arr = rb_funcall( obj, s_unpack, 1, rb_str_new2( "m" ) );
378                 obj = rb_ary_shift( arr );
379             }
380             else if ( strcmp( type_id, "bool#yes" ) == 0 )
381             {
382                 obj = Qtrue;
383             }
384             else if ( strcmp( type_id, "bool#no" ) == 0 )
385             {
386                 obj = Qfalse;
387             }
388             else if ( strcmp( type_id, "int#hex" ) == 0 )
389             {
390                 syck_str_blow_away_commas( n );
391                 obj = rb_cstr2inum( n->data.str->ptr, 16 );
392             }
393             else if ( strcmp( type_id, "int#oct" ) == 0 )
394             {
395                 syck_str_blow_away_commas( n );
396                 obj = rb_cstr2inum( n->data.str->ptr, 8 );
397             }
398             else if ( strcmp( type_id, "int#base60" ) == 0 )
399             {
400                 char *ptr, *end;
401                 long sixty = 1;
402                 long total = 0;
403                 syck_str_blow_away_commas( n );
404                 ptr = n->data.str->ptr;
405                 end = n->data.str->ptr + n->data.str->len;
406                 while ( end > ptr )
407                 {
408                     long bnum = 0;
409                     char *colon = end - 1;
410                     while ( colon >= ptr && *colon != ':' )
411                     {
412                         colon--;
413                     }
414                     if ( colon >= ptr && *colon == ':' ) *colon = '\0';
415 
416                     bnum = strtol( colon + 1, NULL, 10 );
417                     total += bnum * sixty;
418                     sixty *= 60;
419                     end = colon;
420                 }
421                 obj = INT2FIX(total);
422             }
423             else if ( strncmp( type_id, "int", 3 ) == 0 )
424             {
425                 syck_str_blow_away_commas( n );
426                 obj = rb_cstr2inum( n->data.str->ptr, 10 );
427             }
428             else if ( strcmp( type_id, "float#base60" ) == 0 )
429             {
430                 char *ptr, *end;
431                 long sixty = 1;
432                 double total = 0.0;
433                 syck_str_blow_away_commas( n );
434                 ptr = n->data.str->ptr;
435                 end = n->data.str->ptr + n->data.str->len;
436                 while ( end > ptr )
437                 {
438                     double bnum = 0;
439                     char *colon = end - 1;
440                     while ( colon >= ptr && *colon != ':' )
441                     {
442                         colon--;
443                     }
444                     if ( colon >= ptr && *colon == ':' ) *colon = '\0';
445 
446                     bnum = strtod( colon + 1, NULL );
447                     total += bnum * sixty;
448                     sixty *= 60;
449                     end = colon;
450                 }
451                 obj = rb_float_new( total );
452             }
453             else if ( strcmp( type_id, "float#nan" ) == 0 )
454             {
455                 obj = rb_float_new( S_nan() );
456             }
457             else if ( strcmp( type_id, "float#inf" ) == 0 )
458             {
459                 obj = rb_float_new( S_inf() );
460             }
461             else if ( strcmp( type_id, "float#neginf" ) == 0 )
462             {
463                 obj = rb_float_new( -S_inf() );
464             }
465             else if ( strncmp( type_id, "float", 5 ) == 0 )
466             {
467                 double f;
468                 syck_str_blow_away_commas( n );
469                 obj = rb_str_new( n->data.str->ptr, n->data.str->len );
470                 obj = rb_funcall( obj, s_to_f, 0 );
471             }
472             else if ( strcmp( type_id, "timestamp#iso8601" ) == 0 )
473             {
474                 obj = rb_syck_mktime( n->data.str->ptr, n->data.str->len );
475             }
476             else if ( strcmp( type_id, "timestamp#spaced" ) == 0 )
477             {
478                 obj = rb_syck_mktime( n->data.str->ptr, n->data.str->len );
479             }
480             else if ( strcmp( type_id, "timestamp#ymd" ) == 0 )
481             {
482                 char *ptr = n->data.str->ptr;
483                 VALUE year, mon, day;
484 
485                 /* Year*/
486                 ptr[4] = '\0';
487                 year = INT2FIX(strtol(ptr, NULL, 10));
488 
489                 /* Month*/
490                 ptr += 4;
491                 while ( !ISDIGIT( *ptr ) ) ptr++;
492                 mon = INT2FIX(strtol(ptr, NULL, 10));
493 
494                 /* Day*/
495                 ptr += 2;
496                 while ( !ISDIGIT( *ptr ) ) ptr++;
497                 day = INT2FIX(strtol(ptr, NULL, 10));
498 
499                 if ( !cDate ) {
500                     /*
501                      * Load Date module
502                      */
503                     rb_require( "date" );
504                     cDate = rb_const_get( rb_cObject, rb_intern("Date") );
505                 }
506 
507                 obj = rb_funcall( cDate, s_new, 3, year, mon, day );
508             }
509             else if ( strncmp( type_id, "timestamp", 9 ) == 0 )
510             {
511                 obj = rb_syck_mktime( n->data.str->ptr, n->data.str->len );
512             }
513             else if ( strncmp( type_id, "merge", 5 ) == 0 )
514             {
515                 obj = rb_funcall( cMergeKey, s_new, 0 );
516             }
517             else if ( strncmp( type_id, "default", 7 ) == 0 )
518             {
519                 obj = rb_funcall( cDefaultKey, s_new, 0 );
520             }
521             else if ( n->data.str->style == scalar_plain &&
522                       n->data.str->len > 1 &&
523                       strncmp( n->data.str->ptr, ":", 1 ) == 0 )
524             {
525                 obj = rb_funcall( oDefaultResolver, s_transfer, 2,
526                                   rb_str_new2( "tag:ruby.yaml.org,2002:sym" ),
527                                   rb_str_new( n->data.str->ptr + 1, n->data.str->len - 1 ) );
528             }
529             else if ( strcmp( type_id, "str" ) == 0 )
530             {
531                 obj = rb_str_new( n->data.str->ptr, n->data.str->len );
532             }
533             else
534             {
535                 transferred = 0;
536                 obj = rb_str_new( n->data.str->ptr, n->data.str->len );
537             }
538         break;
539 
540         case syck_seq_kind:
541             if ( type_id == NULL || strcmp( type_id, "seq" ) == 0 )
542             {
543                 transferred = 1;
544             }
545             obj = rb_ary_new2( n->data.list->idx );
546             for ( i = 0; i < n->data.list->idx; i++ )
547             {
548                 rb_ary_store( obj, i, syck_seq_read( n, i ) );
549             }
550         break;
551 
552         case syck_map_kind:
553             if ( type_id == NULL || strcmp( type_id, "map" ) == 0 )
554             {
555                 transferred = 1;
556             }
557             obj = rb_hash_new();
558             for ( i = 0; i < n->data.pairs->idx; i++ )
559             {
560                 VALUE k = syck_map_read( n, map_key, i );
561                 VALUE v = syck_map_read( n, map_value, i );
562                 int skip_aset = 0;
563 
564                 /*
565                  * Handle merge keys
566                  */
567                 if ( rb_obj_is_kind_of( k, cMergeKey ) )
568                 {
569                     VALUE tmp;
570                     if ( !NIL_P(tmp = rb_check_convert_type(v, T_HASH, "Hash", "to_hash")) )
571                     {
572                         VALUE dup = rb_funcall( tmp, s_dup, 0 );
573                         rb_funcall( dup, s_update, 1, obj );
574                         obj = dup;
575                         skip_aset = 1;
576                     }
577                     else if ( !NIL_P(tmp = rb_check_array_type(v)) )
578                     {
579                         VALUE end = rb_ary_pop( tmp );
580                         VALUE tmph = rb_check_convert_type(end, T_HASH, "Hash", "to_hash");
581                         if ( !NIL_P(tmph) )
582                         {
583                             VALUE dup = rb_funcall( tmph, s_dup, 0 );
584                             tmp = rb_ary_reverse( tmp );
585                             rb_ary_push( tmp, obj );
586                             rb_iterate( rb_each, tmp, syck_merge_i, dup );
587                             obj = dup;
588                             skip_aset = 1;
589                         }
590                     }
591                 }
592                 else if ( rb_obj_is_kind_of( k, cDefaultKey ) )
593                 {
594                     rb_funcall( obj, s_default_set, 1, v );
595                     skip_aset = 1;
596                 }
597 
598                 if ( ! skip_aset )
599                 {
600                     rb_hash_aset( obj, k, v );
601                 }
602             }
603         break;
604     }
605 
606     *ref = obj;
607     return transferred;
608 }
609 
610 static void syck_node_mark( SyckNode *n );
611 
612 /*
613  * {native mode} node handler
614  * - Converts data into native Ruby types
615  */
616 SYMID
rb_syck_load_handler(SyckParser * p,SyckNode * n)617 rb_syck_load_handler(SyckParser *p, SyckNode *n)
618 {
619     VALUE obj = Qnil;
620     struct parser_xtra *bonus = (struct parser_xtra *)p->bonus;
621     VALUE resolver = bonus->resolver;
622     if ( NIL_P( resolver ) )
623     {
624         resolver = oDefaultResolver;
625     }
626 
627     /*
628      * Create node,
629      */
630     obj = rb_funcall( resolver, s_node_import, 1, Data_Wrap_Struct( cNode, NULL, NULL, n ) );
631 
632     /*
633      * ID already set, let's alter the symbol table to accept the new object
634      */
635     if (n->id > 0 && !NIL_P(obj))
636     {
637         MEMCPY((void *)n->id, (void *)obj, RVALUE, 1);
638         MEMZERO((void *)obj, RVALUE, 1);
639         obj = n->id;
640     }
641 
642     if ( bonus->taint)      OBJ_TAINT( obj );
643     if ( bonus->proc != 0 ) rb_funcall(bonus->proc, s_call, 1, obj);
644 
645     rb_hash_aset(bonus->data, INT2FIX(RHASH(bonus->data)->tbl->num_entries), obj);
646     return obj;
647 }
648 
649 /*
650  * friendly errors.
651  */
652 void
rb_syck_err_handler(SyckParser * p,char * msg)653 rb_syck_err_handler(SyckParser *p, char *msg)
654 {
655     char *endl = p->cursor;
656 
657     while ( *endl != '\0' && *endl != '\n' )
658         endl++;
659 
660     endl[0] = '\0';
661     rb_raise(rb_eArgError, "%s on line %d, col %d: `%s'",
662            msg,
663            p->linect,
664            p->cursor - p->lineptr,
665            p->lineptr);
666 }
667 
668 /*
669  * provide bad anchor object to the parser.
670  */
671 SyckNode *
rb_syck_bad_anchor_handler(SyckParser * p,char * a)672 rb_syck_bad_anchor_handler(SyckParser *p, char *a)
673 {
674     VALUE anchor_name = rb_str_new2( a );
675     SyckNode *badanc = syck_new_map( rb_str_new2( "name" ), anchor_name );
676     badanc->type_id = syck_strndup( "tag:ruby.yaml.org,2002:object:YAML::Syck::BadAlias", 53 );
677     return badanc;
678 }
679 
680 /*
681  * data loaded based on the model requested.
682  */
683 void
syck_set_model(VALUE p,VALUE input,VALUE model)684 syck_set_model(VALUE p, VALUE input, VALUE model)
685 {
686     SyckParser *parser;
687     Data_Get_Struct(p, SyckParser, parser);
688     syck_parser_handler( parser, rb_syck_load_handler );
689     /* WARN: gonna be obsoleted soon!! */
690     if ( model == sym_generic )
691     {
692         rb_funcall( p, s_set_resolver, 1, oGenericResolver );
693     }
694     syck_parser_implicit_typing( parser, 1 );
695     syck_parser_taguri_expansion( parser, 1 );
696 
697     if ( NIL_P( input ) )
698     {
699         input = rb_ivar_get( p, s_input );
700     }
701     if ( input == sym_bytecode )
702     {
703         syck_parser_set_input_type( parser, syck_bytecode_utf8 );
704     }
705     else
706     {
707         syck_parser_set_input_type( parser, syck_yaml_utf8 );
708     }
709     syck_parser_error_handler( parser, rb_syck_err_handler );
710     syck_parser_bad_anchor_handler( parser, rb_syck_bad_anchor_handler );
711 }
712 
713 static int
syck_st_mark_nodes(char * key,SyckNode * n,char * arg)714 syck_st_mark_nodes( char *key, SyckNode *n, char *arg )
715 {
716     if ( n != (void *)1 ) syck_node_mark( n );
717     return ST_CONTINUE;
718 }
719 
720 /*
721  * mark parser nodes
722  */
723 static void
syck_mark_parser(SyckParser * parser)724 syck_mark_parser(SyckParser *parser)
725 {
726     struct parser_xtra *bonus = (struct parser_xtra *)parser->bonus;
727     rb_gc_mark_maybe(parser->root);
728     rb_gc_mark_maybe(parser->root_on_error);
729         rb_gc_mark( bonus->data );
730         rb_gc_mark( bonus->proc );
731     rb_gc_mark( bonus->resolver );
732 
733     if ( parser->anchors != NULL )
734     {
735         st_foreach( parser->anchors, syck_st_mark_nodes, 0 );
736     }
737     if ( parser->bad_anchors != NULL )
738     {
739         st_foreach( parser->bad_anchors, syck_st_mark_nodes, 0 );
740     }
741 }
742 
743 /*
744  * Free the parser and any bonus attachment.
745  */
746 void
rb_syck_free_parser(SyckParser * p)747 rb_syck_free_parser(SyckParser *p)
748 {
749     S_FREE( p->bonus );
750     syck_free_parser(p);
751 }
752 
753 /*
754  * YAML::Syck::Parser.allocate
755  */
756 VALUE syck_parser_s_alloc _((VALUE));
757 VALUE
syck_parser_s_alloc(VALUE class)758 syck_parser_s_alloc(VALUE class)
759 {
760     VALUE pobj;
761     SyckParser *parser = syck_new_parser();
762 
763     parser->bonus = S_ALLOC( struct parser_xtra );
764     S_MEMZERO( parser->bonus, struct parser_xtra, 1 );
765 
766     pobj = Data_Wrap_Struct( class, syck_mark_parser, rb_syck_free_parser, parser );
767 
768     syck_parser_set_root_on_error( parser, Qnil );
769 
770     return pobj;
771 }
772 
773 /*
774  * YAML::Syck::Parser.initialize( resolver, options )
775  */
776 static VALUE
syck_parser_initialize(int argc,VALUE * argv,VALUE self)777 syck_parser_initialize(int argc, VALUE *argv, VALUE self)
778 {
779     VALUE options;
780     if (rb_scan_args(argc, argv, "01", &options) == 0)
781     {
782         options = rb_hash_new();
783     }
784     else
785     {
786         Check_Type(options, T_HASH);
787     }
788     rb_ivar_set(self, s_options, options);
789     rb_ivar_set(self, s_input, Qnil);
790     rb_ivar_set(self, s_resolver, Qnil);
791     return self;
792 }
793 
794 /*
795  * YAML::Syck::Parser.bufsize = Integer
796  */
797 static VALUE
syck_parser_bufsize_set(VALUE self,VALUE size)798 syck_parser_bufsize_set(VALUE self, VALUE size)
799 {
800     SyckParser *parser;
801 
802     if ( rb_respond_to( size, s_to_i ) ) {
803         int n = NUM2INT(rb_funcall(size, s_to_i, 0));
804         Data_Get_Struct(self, SyckParser, parser);
805         parser->bufsize = n;
806     }
807     return self;
808 }
809 
810 /*
811  * YAML::Syck::Parser.bufsize => Integer
812  */
813 static VALUE
syck_parser_bufsize_get(VALUE self)814 syck_parser_bufsize_get(VALUE self)
815 {
816     SyckParser *parser;
817 
818     Data_Get_Struct(self, SyckParser, parser);
819     return INT2FIX( parser->bufsize );
820 }
821 
822 /*
823  * YAML::Syck::Parser.load( IO or String )
824  */
825 VALUE
syck_parser_load(int argc,VALUE * argv,VALUE self)826 syck_parser_load(int argc, VALUE *argv, VALUE self)
827 {
828     VALUE port, proc, model, input;
829     SyckParser *parser;
830     struct parser_xtra *bonus;
831 
832     rb_scan_args(argc, argv, "11", &port, &proc);
833 
834     input = rb_hash_aref( rb_attr_get( self, s_options ), sym_input );
835     model = rb_hash_aref( rb_attr_get( self, s_options ), sym_model );
836     Data_Get_Struct(self, SyckParser, parser);
837     syck_set_model( self, input, model );
838 
839     bonus = (struct parser_xtra *)parser->bonus;
840     bonus->taint = syck_parser_assign_io(parser, &port);
841     bonus->data = rb_hash_new();
842     bonus->resolver = rb_attr_get( self, s_resolver );
843     if ( NIL_P( proc ) ) bonus->proc = 0;
844     else                 bonus->proc = proc;
845 
846     return syck_parse( parser );
847 }
848 
849 /*
850  * YAML::Syck::Parser.load_documents( IO or String ) { |doc| }
851  */
852 VALUE
syck_parser_load_documents(int argc,VALUE * argv,VALUE self)853 syck_parser_load_documents(int argc, VALUE *argv, VALUE self)
854 {
855     VALUE port, proc, v, input, model;
856     SyckParser *parser;
857     struct parser_xtra *bonus;
858 
859     rb_scan_args(argc, argv, "1&", &port, &proc);
860 
861     input = rb_hash_aref( rb_attr_get( self, s_options ), sym_input );
862     model = rb_hash_aref( rb_attr_get( self, s_options ), sym_model );
863     Data_Get_Struct(self, SyckParser, parser);
864     syck_set_model( self, input, model );
865 
866     bonus = (struct parser_xtra *)parser->bonus;
867     bonus->taint = syck_parser_assign_io(parser, &port);
868     bonus->resolver = rb_attr_get( self, s_resolver );
869     bonus->proc = 0;
870 
871     while ( 1 )
872     {
873         /* Reset hash for tracking nodes */
874         bonus->data = rb_hash_new();
875 
876         /* Parse a document */
877         v = syck_parse( parser );
878         if ( parser->eof == 1 )
879         {
880             break;
881         }
882 
883         /* Pass document to block */
884         rb_funcall( proc, s_call, 1, v );
885     }
886 
887     return Qnil;
888 }
889 
890 /*
891  * YAML::Syck::Parser#set_resolver
892  */
893 VALUE
syck_parser_set_resolver(VALUE self,VALUE resolver)894 syck_parser_set_resolver(VALUE self, VALUE resolver)
895 {
896     rb_ivar_set( self, s_resolver, resolver );
897     return self;
898 }
899 
900 /*
901  * YAML::Syck::Resolver.initialize
902  */
903 static VALUE
syck_resolver_initialize(VALUE self)904 syck_resolver_initialize(VALUE self)
905 {
906     VALUE tags = rb_hash_new();
907     rb_ivar_set(self, s_tags, rb_hash_new());
908     return self;
909 }
910 
911 /*
912  * YAML::Syck::Resolver#add_type
913  */
914 VALUE
syck_resolver_add_type(VALUE self,VALUE taguri,VALUE cls)915 syck_resolver_add_type(VALUE self, VALUE taguri, VALUE cls)
916 {
917     VALUE tags = rb_attr_get(self, s_tags);
918     rb_hash_aset( tags, taguri, cls );
919     return Qnil;
920 }
921 
922 /*
923  * YAML::Syck::Resolver#use_types_at
924  */
925 VALUE
syck_resolver_use_types_at(VALUE self,VALUE hsh)926 syck_resolver_use_types_at(VALUE self, VALUE hsh)
927 {
928     rb_ivar_set( self, s_tags, hsh );
929     return Qnil;
930 }
931 
932 /*
933  * YAML::Syck::Resolver#detect_implicit
934  */
935 VALUE
syck_resolver_detect_implicit(VALUE self,VALUE val)936 syck_resolver_detect_implicit(VALUE self, VALUE val)
937 {
938     char *type_id;
939     return rb_str_new2( "" );
940 }
941 
942 /*
943  * YAML::Syck::Resolver#node_import
944  */
945 VALUE
syck_resolver_node_import(VALUE self,VALUE node)946 syck_resolver_node_import(VALUE self, VALUE node)
947 {
948     SyckNode *n;
949     VALUE obj;
950     int i = 0;
951     Data_Get_Struct(node, SyckNode, n);
952 
953     switch (n->kind)
954     {
955         case syck_str_kind:
956             obj = rb_str_new( n->data.str->ptr, n->data.str->len );
957         break;
958 
959         case syck_seq_kind:
960             obj = rb_ary_new2( n->data.list->idx );
961             for ( i = 0; i < n->data.list->idx; i++ )
962             {
963                 rb_ary_store( obj, i, syck_seq_read( n, i ) );
964             }
965         break;
966 
967         case syck_map_kind:
968             obj = rb_hash_new();
969             for ( i = 0; i < n->data.pairs->idx; i++ )
970             {
971                 VALUE k = syck_map_read( n, map_key, i );
972                 VALUE v = syck_map_read( n, map_value, i );
973                 int skip_aset = 0;
974 
975                 /*
976                  * Handle merge keys
977                  */
978                 if ( rb_obj_is_kind_of( k, cMergeKey ) )
979                 {
980                     if ( rb_obj_is_kind_of( v, rb_cHash ) )
981                     {
982                         VALUE dup = rb_funcall( v, s_dup, 0 );
983                         rb_funcall( dup, s_update, 1, obj );
984                         obj = dup;
985                         skip_aset = 1;
986                     }
987                     else if ( rb_obj_is_kind_of( v, rb_cArray ) )
988                     {
989                         VALUE end = rb_ary_pop( v );
990                         if ( rb_obj_is_kind_of( end, rb_cHash ) )
991                         {
992                             VALUE dup = rb_funcall( end, s_dup, 0 );
993                             v = rb_ary_reverse( v );
994                             rb_ary_push( v, obj );
995                             rb_iterate( rb_each, v, syck_merge_i, dup );
996                             obj = dup;
997                             skip_aset = 1;
998                         }
999                     }
1000                 }
1001                 else if ( rb_obj_is_kind_of( k, cDefaultKey ) )
1002                 {
1003                     rb_funcall( obj, s_default_set, 1, v );
1004                     skip_aset = 1;
1005                 }
1006 
1007                 if ( ! skip_aset )
1008                 {
1009                     rb_hash_aset( obj, k, v );
1010                 }
1011             }
1012         break;
1013     }
1014 
1015     if ( n->type_id != NULL )
1016     {
1017         obj = rb_funcall( self, s_transfer, 2, rb_str_new2( n->type_id ), obj );
1018     }
1019     return obj;
1020 }
1021 
1022 /*
1023  * Set instance variables
1024  */
1025 VALUE
syck_set_ivars(VALUE vars,VALUE obj)1026 syck_set_ivars(VALUE vars, VALUE obj)
1027 {
1028     VALUE ivname = rb_ary_entry( vars, 0 );
1029     char *ivn;
1030     StringValue( ivname );
1031     ivn = S_ALLOCA_N( char, rb_str_len(ivname) + 2 );
1032     ivn[0] = '@';
1033     ivn[1] = '\0';
1034     strncat( ivn, rb_str_ptr(ivname), rb_str_len(ivname) );
1035     rb_iv_set( obj, ivn, rb_ary_entry( vars, 1 ) );
1036     return Qnil;
1037 }
1038 
1039 /*
1040  * YAML::Syck::Resolver#const_find
1041  */
1042 VALUE
syck_const_find(VALUE const_name)1043 syck_const_find(VALUE const_name)
1044 {
1045     VALUE tclass = rb_cObject;
1046     VALUE tparts = rb_str_split( const_name, "::" );
1047     int i = 0;
1048     for ( i = 0; i < rb_ary_len(tparts); i++ ) {
1049         VALUE tpart = rb_to_id( rb_ary_entry( tparts, i ) );
1050         if ( !rb_const_defined( tclass, tpart ) ) return Qnil;
1051         tclass = rb_const_get( tclass, tpart );
1052     }
1053     return tclass;
1054 }
1055 
1056 /*
1057  * YAML::Syck::Resolver#transfer
1058  */
1059 VALUE
syck_resolver_transfer(VALUE self,VALUE type,VALUE val)1060 syck_resolver_transfer(VALUE self, VALUE type, VALUE val)
1061 {
1062     if (NIL_P(type) || rb_str_len(StringValue(type)) == 0)
1063     {
1064         type = rb_funcall( self, s_detect_implicit, 1, val );
1065     }
1066 
1067     if ( ! (NIL_P(type) || rb_str_len(StringValue(type)) == 0) )
1068     {
1069         VALUE str_xprivate = rb_str_new2( "x-private" );
1070         VALUE colon = rb_str_new2( ":" );
1071         VALUE tags = rb_attr_get(self, s_tags);
1072         VALUE target_class = rb_hash_aref( tags, type );
1073         VALUE subclass = target_class;
1074         VALUE obj = Qnil;
1075 
1076         /*
1077          * Should no tag match exactly, check for subclass format
1078          */
1079         if ( NIL_P( target_class ) )
1080         {
1081             VALUE subclass_parts = rb_ary_new();
1082             VALUE parts = rb_str_split( type, ":" );
1083 
1084             while ( rb_ary_len(parts) > 1 )
1085             {
1086                 VALUE partial;
1087                 rb_ary_unshift( subclass_parts, rb_ary_pop( parts ) );
1088                 partial = rb_ary_join( parts, colon );
1089                 target_class = rb_hash_aref( tags, partial );
1090                 if ( NIL_P( target_class ) )
1091                 {
1092                     rb_str_append( partial, colon );
1093                     target_class = rb_hash_aref( tags, partial );
1094                 }
1095 
1096                 /*
1097                  * Possible subclass found, see if it supports subclassing
1098                  */
1099                 if ( ! NIL_P( target_class ) )
1100                 {
1101                     subclass = target_class;
1102                     if ( rb_ary_len(subclass_parts) > 0 && rb_respond_to( target_class, s_tag_subclasses ) &&
1103                          RTEST( rb_funcall( target_class, s_tag_subclasses, 0 ) ) )
1104                     {
1105                         VALUE subclass_v;
1106                         subclass = rb_ary_join( subclass_parts, colon );
1107                         subclass = rb_funcall( target_class, s_tag_read_class, 1, subclass );
1108                         subclass_v = syck_const_find( subclass );
1109 
1110                         if ( subclass_v != Qnil )
1111                         {
1112                             subclass = subclass_v;
1113                         }
1114                         else if ( rb_cObject == target_class && subclass_v == Qnil )
1115                         {
1116                             target_class = cYObject;
1117                             type = subclass;
1118                             subclass = cYObject;
1119                         }
1120                         else /* workaround for SEGV. real fix please */
1121                         {
1122                             rb_raise( rb_eTypeError, "invalid subclass" );
1123                         }
1124                     }
1125                     break;
1126                 }
1127             }
1128         }
1129 
1130         /* rb_raise(rb_eTypeError, "invalid typing scheme: %s given",
1131          *         scheme);
1132          */
1133 
1134         if ( rb_respond_to( target_class, s_call ) )
1135         {
1136             obj = rb_funcall( target_class, s_call, 2, type, val );
1137         }
1138         else
1139         {
1140             if ( rb_respond_to( target_class, s_yaml_new ) )
1141             {
1142                 obj = rb_funcall( target_class, s_yaml_new, 3, subclass, type, val );
1143             }
1144             else if ( !NIL_P( target_class ) )
1145             {
1146                 if ( subclass == rb_cBignum )
1147                 {
1148                     obj = rb_str2inum( val, 10 ); /* for yaml dumped by 1.8.3 [ruby-core:6159] */
1149                 }
1150                 else
1151                 {
1152                 obj = rb_obj_alloc( subclass );
1153                 }
1154 
1155                 if ( rb_respond_to( obj, s_yaml_initialize ) )
1156                 {
1157                     rb_funcall( obj, s_yaml_initialize, 2, type, val );
1158                 }
1159                 else if ( !NIL_P( obj ) && rb_obj_is_instance_of( val, rb_cHash ) )
1160                 {
1161                     rb_iterate( rb_each, val, syck_set_ivars, obj );
1162                 }
1163             }
1164             else
1165             {
1166                 VALUE parts = rb_str_split( type, ":" );
1167                 VALUE scheme = rb_ary_shift( parts );
1168                 if ( rb_str_cmp( scheme, str_xprivate ) == 0 )
1169                 {
1170                     VALUE name = rb_ary_join( parts, colon );
1171                     obj = rb_funcall( cPrivateType, s_new, 2, name, val );
1172                 }
1173                 else
1174                 {
1175                     VALUE domain = rb_ary_shift( parts );
1176                     VALUE name = rb_ary_join( parts, colon );
1177                     obj = rb_funcall( cDomainType, s_new, 3, domain, name, val );
1178                 }
1179             }
1180         }
1181         val = obj;
1182     }
1183 
1184     return val;
1185 }
1186 
1187 /*
1188  * YAML::Syck::Resolver#tagurize
1189  */
1190 VALUE
syck_resolver_tagurize(VALUE self,VALUE val)1191 syck_resolver_tagurize(VALUE self, VALUE val)
1192 {
1193     VALUE tmp = rb_check_string_type(val);
1194 
1195     if ( !NIL_P(tmp) )
1196     {
1197         char *taguri = syck_type_id_to_uri( rb_str_ptr(tmp) );
1198         val = rb_str_new2( taguri );
1199         S_FREE( taguri );
1200     }
1201 
1202     return val;
1203 }
1204 
1205 /*
1206  * YAML::Syck::DefaultResolver#detect_implicit
1207  */
1208 VALUE
syck_defaultresolver_detect_implicit(VALUE self,VALUE val)1209 syck_defaultresolver_detect_implicit(VALUE self, VALUE val)
1210 {
1211     char *type_id;
1212     VALUE tmp = rb_check_string_type(val);
1213 
1214     if ( !NIL_P(tmp) )
1215     {
1216         val = tmp;
1217         type_id = syck_match_implicit( rb_str_ptr(val), rb_str_len(val) );
1218         return rb_str_new2( type_id );
1219     }
1220 
1221     return rb_str_new2( "" );
1222 }
1223 
1224 /*
1225  * YAML::Syck::DefaultResolver#node_import
1226  */
1227 VALUE
syck_defaultresolver_node_import(VALUE self,VALUE node)1228 syck_defaultresolver_node_import(VALUE self, VALUE node)
1229 {
1230     SyckNode *n;
1231     VALUE obj;
1232     Data_Get_Struct( node, SyckNode, n );
1233     if ( !yaml_org_handler( n, &obj ) )
1234     {
1235         obj = rb_funcall( self, s_transfer, 2, rb_str_new2( n->type_id ), obj );
1236     }
1237     return obj;
1238 }
1239 
1240 /*
1241  * YAML::Syck::GenericResolver#node_import
1242  */
1243 VALUE
syck_genericresolver_node_import(VALUE self,VALUE node)1244 syck_genericresolver_node_import(VALUE self, VALUE node)
1245 {
1246     SyckNode *n;
1247     int i = 0;
1248     VALUE t = Qnil, obj = Qnil, v = Qnil, style = Qnil;
1249     Data_Get_Struct(node, SyckNode, n);
1250 
1251     if ( n->type_id != NULL )
1252     {
1253         t = rb_str_new2(n->type_id);
1254     }
1255 
1256     switch (n->kind)
1257     {
1258         case syck_str_kind:
1259         {
1260             v = rb_str_new( n->data.str->ptr, n->data.str->len );
1261             if ( n->data.str->style == scalar_1quote )
1262             {
1263                 style = sym_1quote;
1264             }
1265             else if ( n->data.str->style == scalar_2quote )
1266             {
1267                 style = sym_2quote;
1268             }
1269             else if ( n->data.str->style == scalar_fold )
1270             {
1271                 style = sym_fold;
1272             }
1273             else if ( n->data.str->style == scalar_literal )
1274             {
1275                 style = sym_literal;
1276             }
1277             else if ( n->data.str->style == scalar_plain )
1278             {
1279                 style = sym_plain;
1280             }
1281             obj = rb_funcall( cScalar, s_new, 3, t, v, style );
1282         }
1283         break;
1284 
1285         case syck_seq_kind:
1286             rb_iv_set(obj, "@kind", sym_seq);
1287             v = rb_ary_new2( syck_seq_count( n ) );
1288             for ( i = 0; i < syck_seq_count( n ); i++ )
1289             {
1290                 rb_ary_store( v, i, syck_seq_read( n, i ) );
1291             }
1292             if ( n->data.list->style == seq_inline )
1293             {
1294                 style = sym_inline;
1295             }
1296             obj = rb_funcall( cSeq, s_new, 3, t, v, style );
1297         break;
1298 
1299         case syck_map_kind:
1300             rb_iv_set(obj, "@kind", sym_map);
1301             v = rb_hash_new();
1302             for ( i = 0; i < syck_map_count( n ); i++ )
1303             {
1304                 rb_hash_aset( v, syck_map_read( n, map_key, i ), syck_map_read( n, map_value, i ) );
1305             }
1306             if ( n->data.pairs->style == map_inline )
1307             {
1308                 style = sym_inline;
1309             }
1310             obj = rb_funcall( cMap, s_new, 3, t, v, style );
1311         break;
1312     }
1313 
1314     return obj;
1315 }
1316 
1317 /*
1318  * YAML::Syck::BadAlias.initialize
1319  */
1320 VALUE
syck_badalias_initialize(VALUE self,VALUE val)1321 syck_badalias_initialize(VALUE self, VALUE val)
1322 {
1323     rb_iv_set( self, "@name", val );
1324     return self;
1325 }
1326 
1327 /*
1328  * YAML::Syck::BadAlias.<=>
1329  */
1330 VALUE
syck_badalias_cmp(VALUE alias1,VALUE alias2)1331 syck_badalias_cmp(VALUE alias1, VALUE alias2)
1332 {
1333     VALUE str1 = rb_ivar_get( alias1, s_name );
1334     VALUE str2 = rb_ivar_get( alias2, s_name );
1335     VALUE val = rb_funcall( str1, s_cmp, 1, str2 );
1336     return val;
1337 }
1338 
1339 /*
1340  * YAML::DomainType.initialize
1341  */
1342 VALUE
syck_domaintype_initialize(VALUE self,VALUE domain,VALUE type_id,VALUE val)1343 syck_domaintype_initialize(VALUE self, VALUE domain, VALUE type_id, VALUE val)
1344 {
1345     rb_iv_set( self, "@domain", domain );
1346     rb_iv_set( self, "@type_id", type_id );
1347     rb_iv_set( self, "@value", val );
1348     return self;
1349 }
1350 
1351 /*
1352  * YAML::Object.initialize
1353  */
1354 VALUE
syck_yobject_initialize(VALUE self,VALUE klass,VALUE ivars)1355 syck_yobject_initialize(VALUE self, VALUE klass, VALUE ivars)
1356 {
1357     rb_iv_set( self, "@class", klass );
1358     rb_iv_set( self, "@ivars", ivars );
1359     return self;
1360 }
1361 
1362 /*
1363  * YAML::PrivateType.initialize
1364  */
1365 VALUE
syck_privatetype_initialize(VALUE self,VALUE type_id,VALUE val)1366 syck_privatetype_initialize(VALUE self, VALUE type_id, VALUE val)
1367 {
1368     rb_iv_set( self, "@type_id", type_id );
1369     rb_iv_set( self, "@value", val );
1370     return self;
1371 }
1372 
1373 /*
1374  * Mark node contents.
1375  */
1376 static void
syck_node_mark(SyckNode * n)1377 syck_node_mark(SyckNode *n)
1378 {
1379     int i;
1380     rb_gc_mark_maybe( n->id );
1381     switch ( n->kind )
1382     {
1383         case syck_seq_kind:
1384             for ( i = 0; i < n->data.list->idx; i++ )
1385             {
1386                 rb_gc_mark( syck_seq_read( n, i ) );
1387             }
1388         break;
1389 
1390         case syck_map_kind:
1391             for ( i = 0; i < n->data.pairs->idx; i++ )
1392             {
1393                 rb_gc_mark( syck_map_read( n, map_key, i ) );
1394                 rb_gc_mark( syck_map_read( n, map_value, i ) );
1395             }
1396         break;
1397     }
1398 #if 0 /* maybe needed */
1399     if ( n->shortcut ) syck_node_mark( n->shortcut ); /* caution: maybe cyclic */
1400 #endif
1401 }
1402 
1403 /*
1404  * YAML::Syck::Scalar.allocate
1405  */
1406 VALUE
syck_scalar_alloc(VALUE class)1407 syck_scalar_alloc(VALUE class)
1408 {
1409     SyckNode *node = syck_alloc_str();
1410     VALUE obj = Data_Wrap_Struct( class, syck_node_mark, syck_free_node, node );
1411     node->id = obj;
1412     return obj;
1413 }
1414 
1415 /*
1416  * YAML::Syck::Scalar.initialize
1417  */
1418 VALUE
syck_scalar_initialize(VALUE self,VALUE type_id,VALUE val,VALUE style)1419 syck_scalar_initialize(VALUE self, VALUE type_id, VALUE val, VALUE style)
1420 {
1421     rb_iv_set( self, "@kind", sym_scalar );
1422     rb_funcall( self, s_type_id_set, 1, type_id );
1423     rb_funcall( self, s_value_set, 1, val );
1424     rb_funcall( self, s_style_set, 1, style );
1425     return self;
1426 }
1427 
1428 /*
1429  * YAML::Syck::Scalar.style=
1430  */
1431 VALUE
syck_scalar_style_set(VALUE self,VALUE style)1432 syck_scalar_style_set(VALUE self, VALUE style)
1433 {
1434     SyckNode *node;
1435     Data_Get_Struct( self, SyckNode, node );
1436 
1437     if ( NIL_P( style ) )
1438     {
1439         node->data.str->style = scalar_none;
1440     }
1441     else if ( style == sym_1quote )
1442     {
1443         node->data.str->style = scalar_1quote;
1444     }
1445     else if ( style == sym_2quote )
1446     {
1447         node->data.str->style = scalar_2quote;
1448     }
1449     else if ( style == sym_fold )
1450     {
1451         node->data.str->style = scalar_fold;
1452     }
1453     else if ( style == sym_literal )
1454     {
1455         node->data.str->style = scalar_literal;
1456     }
1457     else if ( style == sym_plain )
1458     {
1459         node->data.str->style = scalar_plain;
1460     }
1461 
1462     rb_iv_set( self, "@style", style );
1463     return self;
1464 }
1465 
1466 /*
1467  * YAML::Syck::Scalar.value=
1468  */
1469 VALUE
syck_scalar_value_set(VALUE self,VALUE val)1470 syck_scalar_value_set(VALUE  self, VALUE val)
1471 {
1472     SyckNode *node;
1473     Data_Get_Struct( self, SyckNode, node );
1474 
1475     StringValue( val );
1476     node->data.str->ptr = syck_strndup( rb_str_ptr(val), rb_str_len(val) );
1477     node->data.str->len = rb_str_len(val);
1478     node->data.str->style = scalar_none;
1479 
1480     rb_iv_set( self, "@value", val );
1481     return val;
1482 }
1483 
1484 /*
1485  * YAML::Syck::Seq.allocate
1486  */
1487 VALUE
syck_seq_alloc(VALUE class)1488 syck_seq_alloc(VALUE class)
1489 {
1490     SyckNode *node;
1491     VALUE obj;
1492     node = syck_alloc_seq();
1493     obj = Data_Wrap_Struct( class, syck_node_mark, syck_free_node, node );
1494     node->id = obj;
1495     return obj;
1496 }
1497 
1498 /*
1499  * YAML::Syck::Seq.initialize
1500  */
1501 VALUE
syck_seq_initialize(VALUE self,VALUE type_id,VALUE val,VALUE style)1502 syck_seq_initialize(VALUE self, VALUE type_id, VALUE val, VALUE style)
1503 {
1504     SyckNode *node;
1505     Data_Get_Struct( self, SyckNode, node );
1506 
1507     rb_iv_set( self, "@kind", sym_seq );
1508     rb_funcall( self, s_type_id_set, 1, type_id );
1509     rb_funcall( self, s_value_set, 1, val );
1510     rb_funcall( self, s_style_set, 1, style );
1511     return self;
1512 }
1513 
1514 /*
1515  * YAML::Syck::Seq.value=
1516  */
1517 VALUE
syck_seq_value_set(VALUE self,VALUE val)1518 syck_seq_value_set(VALUE self, VALUE val)
1519 {
1520     SyckNode *node;
1521     Data_Get_Struct( self, SyckNode, node );
1522 
1523     val = rb_check_array_type( val );
1524     if ( !NIL_P( val ) ) {
1525         int i;
1526         syck_seq_empty( node );
1527         for ( i = 0; i < rb_ary_len( val ); i++ )
1528         {
1529             syck_seq_add( node, rb_ary_entry(val, i) );
1530         }
1531     }
1532 
1533     rb_iv_set( self, "@value", val );
1534     return val;
1535 }
1536 
1537 /*
1538  * YAML::Syck::Seq.add
1539  */
1540 VALUE
syck_seq_add_m(VALUE self,VALUE val)1541 syck_seq_add_m(VALUE self, VALUE val)
1542 {
1543     SyckNode *node;
1544     VALUE emitter = rb_ivar_get( self, s_emitter );
1545     Data_Get_Struct( self, SyckNode, node );
1546 
1547     if ( rb_respond_to( emitter, s_node_export ) ) {
1548         val = rb_funcall( emitter, s_node_export, 1, val );
1549     }
1550     syck_seq_add( node, val );
1551     rb_ary_push( rb_ivar_get( self, s_value ), val );
1552 
1553     return self;
1554 }
1555 
1556 /*
1557  * YAML::Syck::Seq.style=
1558  */
1559 VALUE
syck_seq_style_set(VALUE self,VALUE style)1560 syck_seq_style_set(VALUE self, VALUE style)
1561 {
1562     SyckNode *node;
1563     Data_Get_Struct( self, SyckNode, node );
1564 
1565     if ( style == sym_inline )
1566     {
1567         node->data.list->style = seq_inline;
1568     }
1569     else
1570     {
1571         node->data.list->style = seq_none;
1572     }
1573 
1574     rb_iv_set( self, "@style", style );
1575     return self;
1576 }
1577 
1578 /*
1579  * YAML::Syck::Map.allocate
1580  */
1581 VALUE
syck_map_alloc(VALUE class)1582 syck_map_alloc(VALUE class)
1583 {
1584     SyckNode *node;
1585     VALUE obj;
1586     node = syck_alloc_map();
1587     obj = Data_Wrap_Struct( class, syck_node_mark, syck_free_node, node );
1588     node->id = obj;
1589     return obj;
1590 }
1591 
1592 /*
1593  * YAML::Syck::Map.initialize
1594  */
1595 VALUE
syck_map_initialize(VALUE self,VALUE type_id,VALUE val,VALUE style)1596 syck_map_initialize(VALUE self, VALUE type_id, VALUE val, VALUE style)
1597 {
1598     SyckNode *node;
1599     Data_Get_Struct( self, SyckNode, node );
1600 
1601     if ( !NIL_P( val ) )
1602     {
1603         VALUE hsh = rb_check_convert_type(val, T_HASH, "Hash", "to_hash");
1604         VALUE keys;
1605         int i;
1606         if ( NIL_P(hsh) )
1607         {
1608             rb_raise( rb_eTypeError, "wrong argument type" );
1609         }
1610 
1611         keys = rb_funcall( hsh, s_keys, 0 );
1612         for ( i = 0; i < rb_ary_len(keys); i++ )
1613         {
1614             VALUE key = rb_ary_entry(keys, i);
1615             syck_map_add( node, key, rb_hash_aref(hsh, key) );
1616         }
1617     }
1618 
1619     rb_iv_set( self, "@kind", sym_seq );
1620     rb_funcall( self, s_type_id_set, 1, type_id );
1621     rb_funcall( self, s_value_set, 1, val );
1622     rb_funcall( self, s_style_set, 1, style );
1623     return self;
1624 }
1625 
1626 /*
1627  * YAML::Syck::Map.value=
1628  */
1629 VALUE
syck_map_value_set(VALUE self,VALUE val)1630 syck_map_value_set(VALUE self, VALUE val)
1631 {
1632     SyckNode *node;
1633     Data_Get_Struct( self, SyckNode, node );
1634 
1635     if ( !NIL_P( val ) )
1636     {
1637         VALUE hsh = rb_check_convert_type(val, T_HASH, "Hash", "to_hash");
1638         VALUE keys;
1639         int i;
1640         if ( NIL_P(hsh) )
1641         {
1642             rb_raise( rb_eTypeError, "wrong argument type" );
1643         }
1644 
1645         syck_map_empty( node );
1646         keys = rb_funcall( hsh, s_keys, 0 );
1647         for ( i = 0; i < rb_ary_len(keys); i++ )
1648         {
1649             VALUE key = rb_ary_entry(keys, i);
1650             syck_map_add( node, key, rb_hash_aref(hsh, key) );
1651         }
1652     }
1653 
1654     rb_iv_set( self, "@value", val );
1655     return val;
1656 }
1657 
1658 /*
1659  * YAML::Syck::Map.add
1660  */
1661 VALUE
syck_map_add_m(VALUE self,VALUE key,VALUE val)1662 syck_map_add_m(VALUE self, VALUE key, VALUE val)
1663 {
1664     SyckNode *node;
1665     VALUE emitter = rb_ivar_get( self, s_emitter );
1666     Data_Get_Struct( self, SyckNode, node );
1667 
1668     if ( rb_respond_to( emitter, s_node_export ) ) {
1669         key = rb_funcall( emitter, s_node_export, 1, key );
1670         val = rb_funcall( emitter, s_node_export, 1, val );
1671     }
1672     syck_map_add( node, key, val );
1673     rb_hash_aset( rb_ivar_get( self, s_value ), key, val );
1674 
1675     return self;
1676 }
1677 
1678 /*
1679  * YAML::Syck::Map.style=
1680  */
1681 VALUE
syck_map_style_set(VALUE self,VALUE style)1682 syck_map_style_set(VALUE self, VALUE style)
1683 {
1684     SyckNode *node;
1685     Data_Get_Struct( self, SyckNode, node );
1686 
1687     if ( style == sym_inline )
1688     {
1689         node->data.pairs->style = map_inline;
1690     }
1691     else
1692     {
1693         node->data.pairs->style = map_none;
1694     }
1695 
1696     rb_iv_set( self, "@style", style );
1697     return self;
1698 }
1699 
1700 /*
1701  * Cloning method for all node types
1702  */
1703 VALUE
syck_node_init_copy(VALUE copy,VALUE orig)1704 syck_node_init_copy(VALUE copy, VALUE orig)
1705 {
1706     SyckNode *copy_n;
1707     SyckNode *orig_n;
1708 
1709     if ( copy == orig )
1710         return copy;
1711 
1712     if ( TYPE( orig ) != T_DATA )
1713     {
1714         rb_raise( rb_eTypeError, "wrong argument type" );
1715     }
1716 
1717     Data_Get_Struct( orig, SyckNode, orig_n );
1718     Data_Get_Struct( copy, SyckNode, copy_n );
1719     MEMCPY( copy_n, orig_n, SyckNode, 1 );
1720     return copy;
1721 }
1722 
1723 /*
1724  * YAML::Syck::Node#type_id=
1725  */
1726 VALUE
syck_node_type_id_set(VALUE self,VALUE type_id)1727 syck_node_type_id_set(VALUE self, VALUE type_id)
1728 {
1729     SyckNode *node;
1730     Data_Get_Struct( self, SyckNode, node );
1731 
1732     S_FREE( node->type_id );
1733 
1734     if ( !NIL_P( type_id ) ) {
1735         StringValue( type_id );
1736         node->type_id = syck_strndup( rb_str_ptr(type_id), rb_str_len(type_id) );
1737     }
1738 
1739     rb_iv_set( self, "@type_id", type_id );
1740     return type_id;
1741 }
1742 
1743 /*
1744  * YAML::Syck::Node.transform
1745  */
1746 VALUE
syck_node_transform(VALUE self)1747 syck_node_transform(VALUE self)
1748 {
1749     VALUE t;
1750     SyckNode *n;
1751     SyckNode *orig_n;
1752     Data_Get_Struct(self, SyckNode, orig_n);
1753     t = Data_Wrap_Struct( cNode, syck_node_mark, syck_free_node, 0 );
1754 
1755     switch (orig_n->kind)
1756     {
1757         case syck_map_kind:
1758             {
1759                 int i;
1760                 DATA_PTR(t) = n = syck_alloc_map();
1761                 for ( i = 0; i < orig_n->data.pairs->idx; i++ )
1762                 {
1763                     syck_map_add( n, rb_funcall( syck_map_read( orig_n, map_key, i ), s_transform, 0 ),
1764                                      rb_funcall( syck_map_read( orig_n, map_value, i ), s_transform, 0 ) );
1765                 }
1766             }
1767         break;
1768 
1769         case syck_seq_kind:
1770             {
1771                 int i;
1772                 DATA_PTR(t) = n = syck_alloc_seq();
1773                 for ( i = 0; i < orig_n->data.list->idx; i++ )
1774                 {
1775                     syck_seq_add( n, rb_funcall( syck_seq_read( orig_n, i ), s_transform, 0 ) );
1776                 }
1777             }
1778         break;
1779 
1780         case syck_str_kind:
1781             DATA_PTR(t) = n = syck_new_str2( orig_n->data.str->ptr, orig_n->data.str->len, orig_n->data.str->style );
1782         break;
1783     }
1784 
1785     if ( orig_n->type_id != NULL )
1786     {
1787         n->type_id = syck_strndup( orig_n->type_id, strlen( orig_n->type_id ) );
1788     }
1789     if ( orig_n->anchor != NULL )
1790     {
1791         n->anchor = syck_strndup( orig_n->anchor, strlen( orig_n->anchor ) );
1792     }
1793     n->id = t;
1794     return rb_funcall( oDefaultResolver, s_node_import, 1, t );
1795 }
1796 
1797 /*
1798  * Emitter callback: assembles YAML document events from
1799  * Ruby symbols.  This is a brilliant way to do it.
1800  * No one could possibly object.
1801  */
1802 void
rb_syck_emitter_handler(SyckEmitter * e,st_data_t data)1803 rb_syck_emitter_handler(SyckEmitter *e, st_data_t data)
1804 {
1805     SyckNode *n;
1806     Data_Get_Struct((VALUE)data, SyckNode, n);
1807 
1808     switch (n->kind)
1809     {
1810         case syck_map_kind:
1811             {
1812                 int i;
1813                 syck_emit_map( e, n->type_id, n->data.pairs->style );
1814                 for ( i = 0; i < n->data.pairs->idx; i++ )
1815                 {
1816                     syck_emit_item( e, syck_map_read( n, map_key, i ) );
1817                     syck_emit_item( e, syck_map_read( n, map_value, i ) );
1818                 }
1819                 syck_emit_end( e );
1820             }
1821         break;
1822 
1823         case syck_seq_kind:
1824             {
1825                 int i;
1826                 syck_emit_seq( e, n->type_id, n->data.list->style );
1827                 for ( i = 0; i < n->data.list->idx; i++ )
1828                 {
1829                     syck_emit_item( e, syck_seq_read( n, i ) );
1830                 }
1831                 syck_emit_end( e );
1832             }
1833         break;
1834 
1835         case syck_str_kind:
1836             {
1837                 syck_emit_scalar( e, n->type_id, n->data.str->style, 0, 0, 0, n->data.str->ptr, n->data.str->len );
1838             }
1839         break;
1840     }
1841 }
1842 
1843 /*
1844  * Handle output from the emitter
1845  */
1846 void
rb_syck_output_handler(SyckEmitter * emitter,char * str,long len)1847 rb_syck_output_handler(SyckEmitter * emitter, char *str, long len)
1848 {
1849     struct emitter_xtra *bonus = (struct emitter_xtra *)emitter->bonus;
1850     VALUE dest = bonus->port;
1851     if (TYPE(dest) == T_STRING) {
1852         rb_str_cat( dest, str, len );
1853     } else {
1854         rb_io_write( dest, rb_str_new( str, len ) );
1855     }
1856 }
1857 
1858 /*
1859  * Helper function for marking nodes in the anchor
1860  * symbol table.
1861  */
1862 void
syck_out_mark(VALUE emitter,VALUE node)1863 syck_out_mark(VALUE emitter, VALUE node)
1864 {
1865     SyckEmitter *emitterPtr;
1866     struct emitter_xtra *bonus;
1867     Data_Get_Struct(emitter, SyckEmitter, emitterPtr);
1868     bonus = (struct emitter_xtra *)emitterPtr->bonus;
1869     rb_ivar_set( node, s_emitter, emitter );
1870     /* syck_emitter_mark_node( emitterPtr, (st_data_t)node ); */
1871     if ( !NIL_P( bonus->oid ) ) {
1872         rb_hash_aset( bonus->data, bonus->oid, node );
1873     }
1874 }
1875 
1876 /*
1877  * Mark emitter values.
1878  */
1879 static void
syck_mark_emitter(SyckEmitter * emitter)1880 syck_mark_emitter(SyckEmitter *emitter)
1881 {
1882     struct emitter_xtra *bonus = (struct emitter_xtra *)emitter->bonus;
1883     rb_gc_mark( bonus->oid  );
1884         rb_gc_mark( bonus->data );
1885         rb_gc_mark( bonus->port );
1886     }
1887 
1888 /*
1889  * Free the emitter and any bonus attachment.
1890  */
1891 void
rb_syck_free_emitter(SyckEmitter * e)1892 rb_syck_free_emitter(SyckEmitter *e)
1893 {
1894     S_FREE( e->bonus );
1895     syck_free_emitter(e);
1896 }
1897 
1898 /*
1899  * YAML::Syck::Emitter.allocate
1900  */
1901 VALUE syck_emitter_s_alloc _((VALUE));
1902 VALUE
syck_emitter_s_alloc(VALUE class)1903 syck_emitter_s_alloc(VALUE class)
1904 {
1905     VALUE pobj;
1906     SyckEmitter *emitter = syck_new_emitter();
1907 
1908     emitter->bonus = S_ALLOC( struct emitter_xtra );
1909     S_MEMZERO( emitter->bonus, struct emitter_xtra, 1 );
1910 
1911     pobj = Data_Wrap_Struct( class, syck_mark_emitter, rb_syck_free_emitter, emitter );
1912     syck_emitter_handler( emitter, rb_syck_emitter_handler );
1913     syck_output_handler( emitter, rb_syck_output_handler );
1914 
1915     rb_ivar_set( pobj, s_out, rb_funcall( cOut, s_new, 1, pobj ) );
1916     return pobj;
1917 }
1918 
1919 /*
1920  * YAML::Syck::Emitter.reset( options )
1921  */
1922 VALUE
syck_emitter_reset(int argc,VALUE * argv,VALUE self)1923 syck_emitter_reset(int argc, VALUE *argv, VALUE self)
1924 {
1925     VALUE options, tmp;
1926     SyckEmitter *emitter;
1927     struct emitter_xtra *bonus;
1928 
1929     Data_Get_Struct(self, SyckEmitter, emitter);
1930     bonus = (struct emitter_xtra *)emitter->bonus;
1931 
1932     bonus->oid = Qnil;
1933     bonus->port = rb_str_new2( "" );
1934     bonus->data = rb_hash_new();
1935 
1936     if (rb_scan_args(argc, argv, "01", &options) == 0)
1937     {
1938         options = rb_hash_new();
1939         rb_ivar_set(self, s_options, options);
1940     }
1941     else if ( !NIL_P(tmp = rb_check_string_type(options)) )
1942     {
1943         bonus->port = tmp;
1944     }
1945     else if ( rb_respond_to( options, s_write ) )
1946     {
1947         bonus->port = options;
1948     }
1949     else
1950     {
1951         Check_Type(options, T_HASH);
1952         rb_ivar_set(self, s_options, options);
1953     }
1954 
1955     emitter->headless = 0;
1956     rb_ivar_set(self, s_level, INT2FIX(0));
1957     rb_ivar_set(self, s_resolver, Qnil);
1958     return self;
1959 }
1960 
1961 /*
1962  * YAML::Syck::Emitter.emit( object_id ) { |out| ... }
1963  */
1964 VALUE
syck_emitter_emit(int argc,VALUE * argv,VALUE self)1965 syck_emitter_emit(int argc, VALUE *argv, VALUE self)
1966 {
1967     VALUE oid, proc;
1968     char *anchor_name;
1969     SyckEmitter *emitter;
1970     struct emitter_xtra *bonus;
1971     SYMID symple;
1972     int level = FIX2INT(rb_ivar_get(self, s_level)) + 1;
1973     rb_ivar_set(self, s_level, INT2FIX(level));
1974 
1975     rb_scan_args(argc, argv, "1&", &oid, &proc);
1976     Data_Get_Struct(self, SyckEmitter, emitter);
1977     bonus = (struct emitter_xtra *)emitter->bonus;
1978 
1979     /* Calculate anchors, normalize nodes, build a simpler symbol table */
1980     bonus->oid = oid;
1981     if ( !NIL_P( oid ) && RTEST( rb_funcall( bonus->data, s_haskey, 1, oid ) ) ) {
1982         symple = rb_hash_aref( bonus->data, oid );
1983     } else {
1984         symple = rb_funcall( proc, s_call, 1, rb_ivar_get( self, s_out ) );
1985     }
1986     syck_emitter_mark_node( emitter, (st_data_t)symple );
1987 
1988     /* Second pass, build emitted string */
1989     level -= 1;
1990     rb_ivar_set(self, s_level, INT2FIX(level));
1991     if ( level == 0 )
1992     {
1993         syck_emit(emitter, (st_data_t)symple);
1994         syck_emitter_flush(emitter, 0);
1995 
1996         return bonus->port;
1997     }
1998 
1999     return symple;
2000 }
2001 
2002 /*
2003  * YAML::Syck::Emitter#node_export
2004  */
2005 VALUE
syck_emitter_node_export(VALUE self,VALUE node)2006 syck_emitter_node_export(VALUE self, VALUE node)
2007 {
2008     return rb_funcall( node, s_to_yaml, 1, self );
2009 }
2010 
2011 /*
2012  * YAML::Syck::Emitter#set_resolver
2013  */
2014 VALUE
syck_emitter_set_resolver(VALUE self,VALUE resolver)2015 syck_emitter_set_resolver(VALUE self, VALUE resolver)
2016 {
2017     rb_ivar_set( self, s_resolver, resolver );
2018     return self;
2019 }
2020 
2021 /*
2022  * YAML::Syck::Out::initialize
2023  */
2024 VALUE
syck_out_initialize(VALUE self,VALUE emitter)2025 syck_out_initialize(VALUE self, VALUE emitter)
2026 {
2027     rb_ivar_set( self, s_emitter, emitter );
2028     return self;
2029 }
2030 
2031 /*
2032  * YAML::Syck::Out::map
2033  */
2034 VALUE
syck_out_map(int argc,VALUE * argv,VALUE self)2035 syck_out_map(int argc, VALUE *argv, VALUE self)
2036 {
2037     VALUE type_id, style, map;
2038     if (rb_scan_args(argc, argv, "11", &type_id, &style) == 1) {
2039         style = Qnil;
2040     }
2041     map = rb_funcall( cMap, s_new, 3, type_id, rb_hash_new(), style );
2042     syck_out_mark( rb_ivar_get( self, s_emitter ), map );
2043     rb_yield( map );
2044     return map;
2045 }
2046 
2047 /*
2048  * YAML::Syck::Out::seq
2049  */
2050 VALUE
syck_out_seq(int argc,VALUE * argv,VALUE self)2051 syck_out_seq(int argc, VALUE *argv, VALUE self)
2052 {
2053     VALUE type_id, style, seq;
2054     if (rb_scan_args(argc, argv, "11", &type_id, &style) == 1) {
2055         style = Qnil;
2056     }
2057     seq = rb_funcall( cSeq, s_new, 3, type_id, rb_ary_new(), style );
2058     syck_out_mark( rb_ivar_get( self, s_emitter ), seq );
2059     rb_yield( seq );
2060     return seq;
2061 }
2062 
2063 /*
2064  * YAML::Syck::Out::scalar
2065 syck_out_scalar( self, type_id, str, style )
2066     VALUE self, type_id, str, style;
2067  */
2068 VALUE
syck_out_scalar(int argc,VALUE * argv,VALUE self)2069 syck_out_scalar(int argc, VALUE *argv, VALUE self)
2070 {
2071     VALUE type_id, str, style, scalar;
2072     if (rb_scan_args(argc, argv, "21", &type_id, &str, &style) == 2) {
2073         style = Qnil;
2074     }
2075     scalar = rb_funcall( cScalar, s_new, 3, type_id, str, style );
2076     syck_out_mark( rb_ivar_get( self, s_emitter ), scalar );
2077     return scalar;
2078 }
2079 
2080 /*
2081  * Initialize Syck extension
2082  */
2083 void
Init_syck()2084 Init_syck()
2085 {
2086     VALUE rb_yaml = rb_define_module( "YAML" );
2087     VALUE rb_syck = rb_define_module_under( rb_yaml, "Syck" );
2088     rb_define_const( rb_syck, "VERSION", rb_str_new2( SYCK_VERSION ) );
2089     rb_define_module_function( rb_syck, "compile", rb_syck_compile, 1 );
2090 
2091     /*
2092      * Global symbols
2093      */
2094     s_new = rb_intern("new");
2095     s_utc = rb_intern("utc");
2096     s_at = rb_intern("at");
2097     s_to_f = rb_intern("to_f");
2098     s_to_i = rb_intern("to_i");
2099     s_read = rb_intern("read");
2100     s_binmode = rb_intern("binmode");
2101     s_transfer = rb_intern("transfer");
2102     s_call = rb_intern("call");
2103     s_cmp = rb_intern("<=>");
2104     s_intern = rb_intern("intern");
2105     s_update = rb_intern("update");
2106     s_detect_implicit = rb_intern("detect_implicit");
2107     s_dup = rb_intern("dup");
2108     s_default_set = rb_intern("default=");
2109     s_match = rb_intern("match");
2110     s_push = rb_intern("push");
2111     s_haskey = rb_intern("has_key?");
2112     s_keys = rb_intern("keys");
2113     s_node_import = rb_intern("node_import");
2114     s_tr_bang = rb_intern("tr!");
2115     s_unpack = rb_intern("unpack");
2116     s_write = rb_intern("write");
2117     s_tag_read_class = rb_intern( "yaml_tag_read_class" );
2118     s_tag_subclasses = rb_intern( "yaml_tag_subclasses?" );
2119     s_emitter = rb_intern( "emitter" );
2120     s_set_resolver = rb_intern( "set_resolver" );
2121     s_node_export = rb_intern( "node_export" );
2122     s_to_yaml = rb_intern( "to_yaml" );
2123     s_transform = rb_intern( "transform" );
2124     s_yaml_new = rb_intern("yaml_new");
2125     s_yaml_initialize = rb_intern("yaml_initialize");
2126     s_each = rb_intern("each");
2127 
2128     s_tags = rb_intern("@tags");
2129     s_name = rb_intern("@name");
2130     s_options = rb_intern("@options");
2131     s_kind = rb_intern("@kind");
2132     s_type_id = rb_intern("@type_id");
2133     s_type_id_set = rb_intern("type_id=");
2134     s_resolver = rb_intern("@resolver");
2135     s_level = rb_intern( "@level" );
2136     s_style = rb_intern("@style");
2137     s_style_set = rb_intern("style=");
2138     s_value = rb_intern("@value");
2139     s_value_set = rb_intern("value=");
2140     s_out = rb_intern("@out");
2141     s_input = rb_intern("@input");
2142 
2143     sym_model = ID2SYM(rb_intern("Model"));
2144     sym_generic = ID2SYM(rb_intern("Generic"));
2145     sym_bytecode = ID2SYM(rb_intern("bytecode"));
2146     sym_map = ID2SYM(rb_intern("map"));
2147     sym_scalar = ID2SYM(rb_intern("scalar"));
2148     sym_seq = ID2SYM(rb_intern("seq"));
2149     sym_1quote = ID2SYM(rb_intern("quote1"));
2150     sym_2quote = ID2SYM(rb_intern("quote2"));
2151     sym_fold = ID2SYM(rb_intern("fold"));
2152     sym_literal = ID2SYM(rb_intern("literal"));
2153     sym_plain = ID2SYM(rb_intern("plain"));
2154     sym_inline = ID2SYM(rb_intern("inline"));
2155 
2156     /*
2157      * Define YAML::Syck::Resolver class
2158      */
2159     cResolver = rb_define_class_under( rb_syck, "Resolver", rb_cObject );
2160     rb_define_attr( cResolver, "tags", 1, 1 );
2161     rb_define_method( cResolver, "initialize", syck_resolver_initialize, 0 );
2162     rb_define_method( cResolver, "add_type", syck_resolver_add_type, 2 );
2163     rb_define_method( cResolver, "use_types_at", syck_resolver_use_types_at, 1 );
2164     rb_define_method( cResolver, "detect_implicit", syck_resolver_detect_implicit, 1 );
2165     rb_define_method( cResolver, "transfer", syck_resolver_transfer, 2 );
2166     rb_define_method( cResolver, "node_import", syck_resolver_node_import, 1 );
2167     rb_define_method( cResolver, "tagurize", syck_resolver_tagurize, 1 );
2168 
2169     rb_global_variable( &oDefaultResolver );
2170     oDefaultResolver = rb_funcall( cResolver, rb_intern( "new" ), 0 );
2171     rb_define_singleton_method( oDefaultResolver, "node_import", syck_defaultresolver_node_import, 1 );
2172     rb_define_singleton_method( oDefaultResolver, "detect_implicit", syck_defaultresolver_detect_implicit, 1 );
2173     rb_define_const( rb_syck, "DefaultResolver", oDefaultResolver );
2174     rb_global_variable( &oGenericResolver );
2175     oGenericResolver = rb_funcall( cResolver, rb_intern( "new" ), 0 );
2176     rb_define_singleton_method( oGenericResolver, "node_import", syck_genericresolver_node_import, 1 );
2177     rb_define_const( rb_syck, "GenericResolver", oGenericResolver );
2178 
2179     /*
2180      * Define YAML::Syck::Parser class
2181      */
2182     cParser = rb_define_class_under( rb_syck, "Parser", rb_cObject );
2183     rb_define_attr( cParser, "options", 1, 1 );
2184     rb_define_attr( cParser, "resolver", 1, 1 );
2185     rb_define_attr( cParser, "input", 1, 1 );
2186     rb_define_alloc_func( cParser, syck_parser_s_alloc );
2187     rb_define_method(cParser, "initialize", syck_parser_initialize, -1 );
2188     rb_define_method(cParser, "bufsize=", syck_parser_bufsize_set, 1 );
2189     rb_define_method(cParser, "bufsize", syck_parser_bufsize_get, 0 );
2190     rb_define_method(cParser, "load", syck_parser_load, -1);
2191     rb_define_method(cParser, "load_documents", syck_parser_load_documents, -1);
2192     rb_define_method(cParser, "set_resolver", syck_parser_set_resolver, 1);
2193 
2194     /*
2195      * Define YAML::Syck::Node class
2196      */
2197     cNode = rb_define_class_under( rb_syck, "Node", rb_cObject );
2198     rb_define_method( cNode, "initialize_copy", syck_node_init_copy, 1 );
2199     rb_define_attr( cNode, "emitter", 1, 1 );
2200     rb_define_attr( cNode, "resolver", 1, 1 );
2201     rb_define_attr( cNode, "kind", 1, 0 );
2202     rb_define_attr( cNode, "type_id", 1, 0 );
2203     rb_define_attr( cNode, "value", 1, 0 );
2204     rb_define_method( cNode, "type_id=", syck_node_type_id_set, 1 );
2205     rb_define_method( cNode, "transform", syck_node_transform, 0);
2206 
2207     /*
2208      * Define YAML::Syck::Scalar, YAML::Syck::Seq, YAML::Syck::Map --
2209      *     all are the publicly usable variants of YAML::Syck::Node
2210      */
2211     cScalar = rb_define_class_under( rb_syck, "Scalar", cNode );
2212     rb_define_alloc_func( cScalar, syck_scalar_alloc );
2213     rb_define_attr( cNode, "value", 1, 0 );
2214     rb_define_method( cScalar, "initialize", syck_scalar_initialize, 3 );
2215     rb_define_method( cScalar, "value=", syck_scalar_value_set, 1 );
2216     rb_define_method( cScalar, "style=", syck_scalar_style_set, 1 );
2217     cSeq = rb_define_class_under( rb_syck, "Seq", cNode );
2218     rb_define_alloc_func( cSeq, syck_seq_alloc );
2219     rb_define_method( cSeq, "initialize", syck_seq_initialize, 3 );
2220     rb_define_method( cSeq, "value=", syck_seq_value_set, 1 );
2221     rb_define_method( cSeq, "add", syck_seq_add_m, 1 );
2222     rb_define_method( cSeq, "style=", syck_seq_style_set, 1 );
2223     cMap = rb_define_class_under( rb_syck, "Map", cNode );
2224     rb_define_alloc_func( cMap, syck_map_alloc );
2225     rb_define_method( cMap, "initialize", syck_map_initialize, 3 );
2226     rb_define_method( cMap, "value=", syck_map_value_set, 1 );
2227     rb_define_method( cMap, "add", syck_map_add_m, 2 );
2228     rb_define_method( cMap, "style=", syck_map_style_set, 1 );
2229 
2230     /*
2231      * Define YAML::PrivateType class
2232      */
2233     cPrivateType = rb_define_class_under( rb_yaml, "PrivateType", rb_cObject );
2234     rb_define_attr( cPrivateType, "type_id", 1, 1 );
2235     rb_define_attr( cPrivateType, "value", 1, 1 );
2236     rb_define_method( cPrivateType, "initialize", syck_privatetype_initialize, 2);
2237 
2238     /*
2239      * Define YAML::DomainType class
2240      */
2241     cDomainType = rb_define_class_under( rb_yaml, "DomainType", rb_cObject );
2242     rb_define_attr( cDomainType, "domain", 1, 1 );
2243     rb_define_attr( cDomainType, "type_id", 1, 1 );
2244     rb_define_attr( cDomainType, "value", 1, 1 );
2245     rb_define_method( cDomainType, "initialize", syck_domaintype_initialize, 3);
2246 
2247     /*
2248      * Define YAML::Object class
2249      */
2250     cYObject = rb_define_class_under( rb_yaml, "Object", rb_cObject );
2251     rb_define_attr( cYObject, "class", 1, 1 );
2252     rb_define_attr( cYObject, "ivars", 1, 1 );
2253     rb_define_method( cYObject, "initialize", syck_yobject_initialize, 2);
2254     rb_define_method( cYObject, "yaml_initialize", syck_yobject_initialize, 2);
2255 
2256     /*
2257      * Define YAML::Syck::BadAlias class
2258      */
2259     cBadAlias = rb_define_class_under( rb_syck, "BadAlias", rb_cObject );
2260     rb_define_attr( cBadAlias, "name", 1, 1 );
2261     rb_define_method( cBadAlias, "initialize", syck_badalias_initialize, 1);
2262     rb_define_method( cBadAlias, "<=>", syck_badalias_cmp, 1);
2263     rb_include_module( cBadAlias, rb_const_get( rb_cObject, rb_intern("Comparable") ) );
2264 
2265     /*
2266      * Define YAML::Syck::MergeKey class
2267      */
2268     cMergeKey = rb_define_class_under( rb_syck, "MergeKey", rb_cObject );
2269 
2270     /*
2271      * Define YAML::Syck::DefaultKey class
2272      */
2273     cDefaultKey = rb_define_class_under( rb_syck, "DefaultKey", rb_cObject );
2274 
2275     /*
2276      * Define YAML::Syck::Out classes
2277      */
2278     cOut = rb_define_class_under( rb_syck, "Out", rb_cObject );
2279     rb_define_attr( cOut, "emitter", 1, 1 );
2280     rb_define_method( cOut, "initialize", syck_out_initialize, 1 );
2281     rb_define_method( cOut, "map", syck_out_map, -1 );
2282     rb_define_method( cOut, "seq", syck_out_seq, -1 );
2283     rb_define_method( cOut, "scalar", syck_out_scalar, -1 );
2284 
2285     /*
2286      * Define YAML::Syck::Emitter class
2287      */
2288     cEmitter = rb_define_class_under( rb_syck, "Emitter", rb_cObject );
2289     rb_define_attr( cEmitter, "level", 1, 1 );
2290     rb_define_alloc_func( cEmitter, syck_emitter_s_alloc );
2291     rb_define_method( cEmitter, "initialize", syck_emitter_reset, -1 );
2292     rb_define_method( cEmitter, "reset", syck_emitter_reset, -1 );
2293     rb_define_method( cEmitter, "emit", syck_emitter_emit, -1 );
2294     rb_define_method( cEmitter, "set_resolver", syck_emitter_set_resolver, 1);
2295     rb_define_method( cEmitter, "node_export", syck_emitter_node_export, 1);
2296 }
2297 
2298