Ruby  2.0.0p247(2013-06-27revision41674)
ext/psych/psych_emitter.c
Go to the documentation of this file.
00001 #include <psych.h>
00002 
00003 VALUE cPsychEmitter;
00004 static ID id_write;
00005 static ID id_line_width;
00006 static ID id_indentation;
00007 static ID id_canonical;
00008 
00009 static void emit(yaml_emitter_t * emitter, yaml_event_t * event)
00010 {
00011     if(!yaml_emitter_emit(emitter, event))
00012         rb_raise(rb_eRuntimeError, "%s", emitter->problem);
00013 }
00014 
00015 static int writer(void *ctx, unsigned char *buffer, size_t size)
00016 {
00017     VALUE io = (VALUE)ctx;
00018     VALUE str = rb_str_new((const char *)buffer, (long)size);
00019     VALUE wrote = rb_funcall(io, id_write, 1, str);
00020     return (int)NUM2INT(wrote);
00021 }
00022 
00023 static void dealloc(void * ptr)
00024 {
00025     yaml_emitter_t * emitter;
00026 
00027     emitter = (yaml_emitter_t *)ptr;
00028     yaml_emitter_delete(emitter);
00029     xfree(emitter);
00030 }
00031 
00032 static VALUE allocate(VALUE klass)
00033 {
00034     yaml_emitter_t * emitter;
00035 
00036     emitter = xmalloc(sizeof(yaml_emitter_t));
00037 
00038     yaml_emitter_initialize(emitter);
00039     yaml_emitter_set_unicode(emitter, 1);
00040     yaml_emitter_set_indent(emitter, 2);
00041 
00042     return Data_Wrap_Struct(klass, 0, dealloc, emitter);
00043 }
00044 
00045 /* call-seq: Psych::Emitter.new(io, options = Psych::Emitter::OPTIONS)
00046  *
00047  * Create a new Psych::Emitter that writes to +io+.
00048  */
00049 static VALUE initialize(int argc, VALUE *argv, VALUE self)
00050 {
00051     yaml_emitter_t * emitter;
00052     VALUE io, options;
00053     VALUE line_width;
00054     VALUE indent;
00055     VALUE canonical;
00056 
00057     Data_Get_Struct(self, yaml_emitter_t, emitter);
00058 
00059     if (rb_scan_args(argc, argv, "11", &io, &options) == 2) {
00060         line_width = rb_funcall(options, id_line_width, 0);
00061         indent     = rb_funcall(options, id_indentation, 0);
00062         canonical  = rb_funcall(options, id_canonical, 0);
00063 
00064         yaml_emitter_set_width(emitter, NUM2INT(line_width));
00065         yaml_emitter_set_indent(emitter, NUM2INT(indent));
00066         yaml_emitter_set_canonical(emitter, Qtrue == canonical ? 1 : 0);
00067     }
00068 
00069     yaml_emitter_set_output(emitter, writer, (void *)io);
00070 
00071     return self;
00072 }
00073 
00074 /* call-seq: emitter.start_stream(encoding)
00075  *
00076  * Start a stream emission with +encoding+
00077  *
00078  * See Psych::Handler#start_stream
00079  */
00080 static VALUE start_stream(VALUE self, VALUE encoding)
00081 {
00082     yaml_emitter_t * emitter;
00083     yaml_event_t event;
00084     Data_Get_Struct(self, yaml_emitter_t, emitter);
00085     Check_Type(encoding, T_FIXNUM);
00086 
00087     yaml_stream_start_event_initialize(&event, (yaml_encoding_t)NUM2INT(encoding));
00088 
00089     emit(emitter, &event);
00090 
00091     return self;
00092 }
00093 
00094 /* call-seq: emitter.end_stream
00095  *
00096  * End a stream emission
00097  *
00098  * See Psych::Handler#end_stream
00099  */
00100 static VALUE end_stream(VALUE self)
00101 {
00102     yaml_emitter_t * emitter;
00103     yaml_event_t event;
00104     Data_Get_Struct(self, yaml_emitter_t, emitter);
00105 
00106     yaml_stream_end_event_initialize(&event);
00107 
00108     emit(emitter, &event);
00109 
00110     return self;
00111 }
00112 
00113 /* call-seq: emitter.start_document(version, tags, implicit)
00114  *
00115  * Start a document emission with YAML +version+, +tags+, and an +implicit+
00116  * start.
00117  *
00118  * See Psych::Handler#start_document
00119  */
00120 static VALUE start_document(VALUE self, VALUE version, VALUE tags, VALUE imp)
00121 {
00122     yaml_emitter_t * emitter;
00123     yaml_tag_directive_t * head = NULL;
00124     yaml_tag_directive_t * tail = NULL;
00125     yaml_event_t event;
00126     yaml_version_directive_t version_directive;
00127     Data_Get_Struct(self, yaml_emitter_t, emitter);
00128 
00129 
00130     Check_Type(version, T_ARRAY);
00131 
00132     if(RARRAY_LEN(version) > 0) {
00133         VALUE major = rb_ary_entry(version, (long)0);
00134         VALUE minor = rb_ary_entry(version, (long)1);
00135 
00136         version_directive.major = NUM2INT(major);
00137         version_directive.minor = NUM2INT(minor);
00138     }
00139 
00140     if(RTEST(tags)) {
00141         int i = 0;
00142 #ifdef HAVE_RUBY_ENCODING_H
00143         rb_encoding * encoding = rb_utf8_encoding();
00144 #endif
00145 
00146         Check_Type(tags, T_ARRAY);
00147 
00148         head  = xcalloc((size_t)RARRAY_LEN(tags), sizeof(yaml_tag_directive_t));
00149         tail  = head;
00150 
00151         for(i = 0; i < RARRAY_LEN(tags); i++) {
00152             VALUE tuple = RARRAY_PTR(tags)[i];
00153             VALUE name;
00154             VALUE value;
00155 
00156             Check_Type(tuple, T_ARRAY);
00157 
00158             if(RARRAY_LEN(tuple) < 2) {
00159                 xfree(head);
00160                 rb_raise(rb_eRuntimeError, "tag tuple must be of length 2");
00161             }
00162             name  = RARRAY_PTR(tuple)[0];
00163             value = RARRAY_PTR(tuple)[1];
00164 #ifdef HAVE_RUBY_ENCODING_H
00165             name = rb_str_export_to_enc(name, encoding);
00166             value = rb_str_export_to_enc(value, encoding);
00167 #endif
00168 
00169             tail->handle = (yaml_char_t *)StringValuePtr(name);
00170             tail->prefix = (yaml_char_t *)StringValuePtr(value);
00171 
00172             tail++;
00173         }
00174     }
00175 
00176     yaml_document_start_event_initialize(
00177             &event,
00178             (RARRAY_LEN(version) > 0) ? &version_directive : NULL,
00179             head,
00180             tail,
00181             imp ? 1 : 0
00182             );
00183 
00184     emit(emitter, &event);
00185 
00186     if(head) xfree(head);
00187 
00188     return self;
00189 }
00190 
00191 /* call-seq: emitter.end_document(implicit)
00192  *
00193  * End a document emission with an +implicit+ ending.
00194  *
00195  * See Psych::Handler#end_document
00196  */
00197 static VALUE end_document(VALUE self, VALUE imp)
00198 {
00199     yaml_emitter_t * emitter;
00200     yaml_event_t event;
00201     Data_Get_Struct(self, yaml_emitter_t, emitter);
00202 
00203     yaml_document_end_event_initialize(&event, imp ? 1 : 0);
00204 
00205     emit(emitter, &event);
00206 
00207     return self;
00208 }
00209 
00210 /* call-seq: emitter.scalar(value, anchor, tag, plain, quoted, style)
00211  *
00212  * Emit a scalar with +value+, +anchor+, +tag+, and a +plain+ or +quoted+
00213  * string type with +style+.
00214  *
00215  * See Psych::Handler#scalar
00216  */
00217 static VALUE scalar(
00218         VALUE self,
00219         VALUE value,
00220         VALUE anchor,
00221         VALUE tag,
00222         VALUE plain,
00223         VALUE quoted,
00224         VALUE style
00225         ) {
00226     yaml_emitter_t * emitter;
00227     yaml_event_t event;
00228 #ifdef HAVE_RUBY_ENCODING_H
00229     rb_encoding *encoding;
00230 #endif
00231     Data_Get_Struct(self, yaml_emitter_t, emitter);
00232 
00233     Check_Type(value, T_STRING);
00234 
00235 #ifdef HAVE_RUBY_ENCODING_H
00236     encoding = rb_utf8_encoding();
00237 
00238     value = rb_str_export_to_enc(value, encoding);
00239 
00240     if(!NIL_P(anchor)) {
00241         Check_Type(anchor, T_STRING);
00242         anchor = rb_str_export_to_enc(anchor, encoding);
00243     }
00244 
00245     if(!NIL_P(tag)) {
00246         Check_Type(tag, T_STRING);
00247         tag = rb_str_export_to_enc(tag, encoding);
00248     }
00249 #endif
00250 
00251     yaml_scalar_event_initialize(
00252             &event,
00253             (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)),
00254             (yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)),
00255             (yaml_char_t*)StringValuePtr(value),
00256             (int)RSTRING_LEN(value),
00257             plain ? 1 : 0,
00258             quoted ? 1 : 0,
00259             (yaml_scalar_style_t)NUM2INT(style)
00260             );
00261 
00262     emit(emitter, &event);
00263 
00264     return self;
00265 }
00266 
00267 /* call-seq: emitter.start_sequence(anchor, tag, implicit, style)
00268  *
00269  * Start emitting a sequence with +anchor+, a +tag+, +implicit+ sequence
00270  * start and end, along with +style+.
00271  *
00272  * See Psych::Handler#start_sequence
00273  */
00274 static VALUE start_sequence(
00275         VALUE self,
00276         VALUE anchor,
00277         VALUE tag,
00278         VALUE implicit,
00279         VALUE style
00280         ) {
00281     yaml_emitter_t * emitter;
00282     yaml_event_t event;
00283 
00284 #ifdef HAVE_RUBY_ENCODING_H
00285     rb_encoding * encoding = rb_utf8_encoding();
00286 
00287     if(!NIL_P(anchor)) {
00288         Check_Type(anchor, T_STRING);
00289         anchor = rb_str_export_to_enc(anchor, encoding);
00290     }
00291 
00292     if(!NIL_P(tag)) {
00293         Check_Type(tag, T_STRING);
00294         tag = rb_str_export_to_enc(tag, encoding);
00295     }
00296 #endif
00297 
00298     Data_Get_Struct(self, yaml_emitter_t, emitter);
00299 
00300     yaml_sequence_start_event_initialize(
00301             &event,
00302             (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)),
00303             (yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)),
00304             implicit ? 1 : 0,
00305             (yaml_sequence_style_t)NUM2INT(style)
00306             );
00307 
00308     emit(emitter, &event);
00309 
00310     return self;
00311 }
00312 
00313 /* call-seq: emitter.end_sequence
00314  *
00315  * End sequence emission.
00316  *
00317  * See Psych::Handler#end_sequence
00318  */
00319 static VALUE end_sequence(VALUE self)
00320 {
00321     yaml_emitter_t * emitter;
00322     yaml_event_t event;
00323     Data_Get_Struct(self, yaml_emitter_t, emitter);
00324 
00325     yaml_sequence_end_event_initialize(&event);
00326 
00327     emit(emitter, &event);
00328 
00329     return self;
00330 }
00331 
00332 /* call-seq: emitter.start_mapping(anchor, tag, implicit, style)
00333  *
00334  * Start emitting a YAML map with +anchor+, +tag+, an +implicit+ start
00335  * and end, and +style+.
00336  *
00337  * See Psych::Handler#start_mapping
00338  */
00339 static VALUE start_mapping(
00340         VALUE self,
00341         VALUE anchor,
00342         VALUE tag,
00343         VALUE implicit,
00344         VALUE style
00345         ) {
00346     yaml_emitter_t * emitter;
00347     yaml_event_t event;
00348 #ifdef HAVE_RUBY_ENCODING_H
00349     rb_encoding *encoding;
00350 #endif
00351     Data_Get_Struct(self, yaml_emitter_t, emitter);
00352 
00353 #ifdef HAVE_RUBY_ENCODING_H
00354     encoding = rb_utf8_encoding();
00355 
00356     if(!NIL_P(anchor)) {
00357         Check_Type(anchor, T_STRING);
00358         anchor = rb_str_export_to_enc(anchor, encoding);
00359     }
00360 
00361     if(!NIL_P(tag)) {
00362         Check_Type(tag, T_STRING);
00363         tag = rb_str_export_to_enc(tag, encoding);
00364     }
00365 #endif
00366 
00367     yaml_mapping_start_event_initialize(
00368             &event,
00369             (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)),
00370             (yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)),
00371             implicit ? 1 : 0,
00372             (yaml_mapping_style_t)NUM2INT(style)
00373             );
00374 
00375     emit(emitter, &event);
00376 
00377     return self;
00378 }
00379 
00380 /* call-seq: emitter.end_mapping
00381  *
00382  * Emit the end of a mapping.
00383  *
00384  * See Psych::Handler#end_mapping
00385  */
00386 static VALUE end_mapping(VALUE self)
00387 {
00388     yaml_emitter_t * emitter;
00389     yaml_event_t event;
00390     Data_Get_Struct(self, yaml_emitter_t, emitter);
00391 
00392     yaml_mapping_end_event_initialize(&event);
00393 
00394     emit(emitter, &event);
00395 
00396     return self;
00397 }
00398 
00399 /* call-seq: emitter.alias(anchor)
00400  *
00401  * Emit an alias with +anchor+.
00402  *
00403  * See Psych::Handler#alias
00404  */
00405 static VALUE alias(VALUE self, VALUE anchor)
00406 {
00407     yaml_emitter_t * emitter;
00408     yaml_event_t event;
00409     Data_Get_Struct(self, yaml_emitter_t, emitter);
00410 
00411 #ifdef HAVE_RUBY_ENCODING_H
00412     if(!NIL_P(anchor)) {
00413         Check_Type(anchor, T_STRING);
00414         anchor = rb_str_export_to_enc(anchor, rb_utf8_encoding());
00415     }
00416 #endif
00417 
00418     yaml_alias_event_initialize(
00419             &event,
00420             (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor))
00421             );
00422 
00423     emit(emitter, &event);
00424 
00425     return self;
00426 }
00427 
00428 /* call-seq: emitter.canonical = true
00429  *
00430  * Set the output style to canonical, or not.
00431  */
00432 static VALUE set_canonical(VALUE self, VALUE style)
00433 {
00434     yaml_emitter_t * emitter;
00435     Data_Get_Struct(self, yaml_emitter_t, emitter);
00436 
00437     yaml_emitter_set_canonical(emitter, Qtrue == style ? 1 : 0);
00438 
00439     return style;
00440 }
00441 
00442 /* call-seq: emitter.canonical
00443  *
00444  * Get the output style, canonical or not.
00445  */
00446 static VALUE canonical(VALUE self)
00447 {
00448     yaml_emitter_t * emitter;
00449     Data_Get_Struct(self, yaml_emitter_t, emitter);
00450 
00451     return (emitter->canonical == 0) ? Qfalse : Qtrue;
00452 }
00453 
00454 /* call-seq: emitter.indentation = level
00455  *
00456  * Set the indentation level to +level+.  The level must be less than 10 and
00457  * greater than 1.
00458  */
00459 static VALUE set_indentation(VALUE self, VALUE level)
00460 {
00461     yaml_emitter_t * emitter;
00462     Data_Get_Struct(self, yaml_emitter_t, emitter);
00463 
00464     yaml_emitter_set_indent(emitter, NUM2INT(level));
00465 
00466     return level;
00467 }
00468 
00469 /* call-seq: emitter.indentation
00470  *
00471  * Get the indentation level.
00472  */
00473 static VALUE indentation(VALUE self)
00474 {
00475     yaml_emitter_t * emitter;
00476     Data_Get_Struct(self, yaml_emitter_t, emitter);
00477 
00478     return INT2NUM(emitter->best_indent);
00479 }
00480 
00481 /* call-seq: emitter.line_width
00482  *
00483  * Get the preferred line width.
00484  */
00485 static VALUE line_width(VALUE self)
00486 {
00487     yaml_emitter_t * emitter;
00488     Data_Get_Struct(self, yaml_emitter_t, emitter);
00489 
00490     return INT2NUM(emitter->best_width);
00491 }
00492 
00493 /* call-seq: emitter.line_width = width
00494  *
00495  * Set the preferred line with to +width+.
00496  */
00497 static VALUE set_line_width(VALUE self, VALUE width)
00498 {
00499     yaml_emitter_t * emitter;
00500     Data_Get_Struct(self, yaml_emitter_t, emitter);
00501 
00502     yaml_emitter_set_width(emitter, NUM2INT(width));
00503 
00504     return width;
00505 }
00506 
00507 void Init_psych_emitter()
00508 {
00509     VALUE psych     = rb_define_module("Psych");
00510     VALUE handler   = rb_define_class_under(psych, "Handler", rb_cObject);
00511     cPsychEmitter   = rb_define_class_under(psych, "Emitter", handler);
00512 
00513     rb_define_alloc_func(cPsychEmitter, allocate);
00514 
00515     rb_define_method(cPsychEmitter, "initialize", initialize, -1);
00516     rb_define_method(cPsychEmitter, "start_stream", start_stream, 1);
00517     rb_define_method(cPsychEmitter, "end_stream", end_stream, 0);
00518     rb_define_method(cPsychEmitter, "start_document", start_document, 3);
00519     rb_define_method(cPsychEmitter, "end_document", end_document, 1);
00520     rb_define_method(cPsychEmitter, "scalar", scalar, 6);
00521     rb_define_method(cPsychEmitter, "start_sequence", start_sequence, 4);
00522     rb_define_method(cPsychEmitter, "end_sequence", end_sequence, 0);
00523     rb_define_method(cPsychEmitter, "start_mapping", start_mapping, 4);
00524     rb_define_method(cPsychEmitter, "end_mapping", end_mapping, 0);
00525     rb_define_method(cPsychEmitter, "alias", alias, 1);
00526     rb_define_method(cPsychEmitter, "canonical", canonical, 0);
00527     rb_define_method(cPsychEmitter, "canonical=", set_canonical, 1);
00528     rb_define_method(cPsychEmitter, "indentation", indentation, 0);
00529     rb_define_method(cPsychEmitter, "indentation=", set_indentation, 1);
00530     rb_define_method(cPsychEmitter, "line_width", line_width, 0);
00531     rb_define_method(cPsychEmitter, "line_width=", set_line_width, 1);
00532 
00533     id_write       = rb_intern("write");
00534     id_line_width  = rb_intern("line_width");
00535     id_indentation = rb_intern("indentation");
00536     id_canonical   = rb_intern("canonical");
00537 }
00538 /* vim: set noet sws=4 sw=4: */
00539