1#include <psych.h> 2 3VALUE cPsychEmitter; 4static ID id_write; 5static ID id_line_width; 6static ID id_indentation; 7static ID id_canonical; 8 9static void emit(yaml_emitter_t * emitter, yaml_event_t * event) 10{ 11 if(!yaml_emitter_emit(emitter, event)) 12 rb_raise(rb_eRuntimeError, "%s", emitter->problem); 13} 14 15static int writer(void *ctx, unsigned char *buffer, size_t size) 16{ 17 VALUE io = (VALUE)ctx; 18 VALUE str = rb_str_new((const char *)buffer, (long)size); 19 VALUE wrote = rb_funcall(io, id_write, 1, str); 20 return (int)NUM2INT(wrote); 21} 22 23static void dealloc(void * ptr) 24{ 25 yaml_emitter_t * emitter; 26 27 emitter = (yaml_emitter_t *)ptr; 28 yaml_emitter_delete(emitter); 29 xfree(emitter); 30} 31 32static VALUE allocate(VALUE klass) 33{ 34 yaml_emitter_t * emitter; 35 36 emitter = xmalloc(sizeof(yaml_emitter_t)); 37 38 yaml_emitter_initialize(emitter); 39 yaml_emitter_set_unicode(emitter, 1); 40 yaml_emitter_set_indent(emitter, 2); 41 42 return Data_Wrap_Struct(klass, 0, dealloc, emitter); 43} 44 45/* call-seq: Psych::Emitter.new(io, options = Psych::Emitter::OPTIONS) 46 * 47 * Create a new Psych::Emitter that writes to +io+. 48 */ 49static VALUE initialize(int argc, VALUE *argv, VALUE self) 50{ 51 yaml_emitter_t * emitter; 52 VALUE io, options; 53 VALUE line_width; 54 VALUE indent; 55 VALUE canonical; 56 57 Data_Get_Struct(self, yaml_emitter_t, emitter); 58 59 if (rb_scan_args(argc, argv, "11", &io, &options) == 2) { 60 line_width = rb_funcall(options, id_line_width, 0); 61 indent = rb_funcall(options, id_indentation, 0); 62 canonical = rb_funcall(options, id_canonical, 0); 63 64 yaml_emitter_set_width(emitter, NUM2INT(line_width)); 65 yaml_emitter_set_indent(emitter, NUM2INT(indent)); 66 yaml_emitter_set_canonical(emitter, Qtrue == canonical ? 1 : 0); 67 } 68 69 yaml_emitter_set_output(emitter, writer, (void *)io); 70 71 return self; 72} 73 74/* call-seq: emitter.start_stream(encoding) 75 * 76 * Start a stream emission with +encoding+ 77 * 78 * See Psych::Handler#start_stream 79 */ 80static VALUE start_stream(VALUE self, VALUE encoding) 81{ 82 yaml_emitter_t * emitter; 83 yaml_event_t event; 84 Data_Get_Struct(self, yaml_emitter_t, emitter); 85 Check_Type(encoding, T_FIXNUM); 86 87 yaml_stream_start_event_initialize(&event, (yaml_encoding_t)NUM2INT(encoding)); 88 89 emit(emitter, &event); 90 91 return self; 92} 93 94/* call-seq: emitter.end_stream 95 * 96 * End a stream emission 97 * 98 * See Psych::Handler#end_stream 99 */ 100static VALUE end_stream(VALUE self) 101{ 102 yaml_emitter_t * emitter; 103 yaml_event_t event; 104 Data_Get_Struct(self, yaml_emitter_t, emitter); 105 106 yaml_stream_end_event_initialize(&event); 107 108 emit(emitter, &event); 109 110 return self; 111} 112 113/* call-seq: emitter.start_document(version, tags, implicit) 114 * 115 * Start a document emission with YAML +version+, +tags+, and an +implicit+ 116 * start. 117 * 118 * See Psych::Handler#start_document 119 */ 120static VALUE start_document(VALUE self, VALUE version, VALUE tags, VALUE imp) 121{ 122 yaml_emitter_t * emitter; 123 yaml_tag_directive_t * head = NULL; 124 yaml_tag_directive_t * tail = NULL; 125 yaml_event_t event; 126 yaml_version_directive_t version_directive; 127 Data_Get_Struct(self, yaml_emitter_t, emitter); 128 129 130 Check_Type(version, T_ARRAY); 131 132 if(RARRAY_LEN(version) > 0) { 133 VALUE major = rb_ary_entry(version, (long)0); 134 VALUE minor = rb_ary_entry(version, (long)1); 135 136 version_directive.major = NUM2INT(major); 137 version_directive.minor = NUM2INT(minor); 138 } 139 140 if(RTEST(tags)) { 141 int i = 0; 142#ifdef HAVE_RUBY_ENCODING_H 143 rb_encoding * encoding = rb_utf8_encoding(); 144#endif 145 146 Check_Type(tags, T_ARRAY); 147 148 head = xcalloc((size_t)RARRAY_LEN(tags), sizeof(yaml_tag_directive_t)); 149 tail = head; 150 151 for(i = 0; i < RARRAY_LEN(tags); i++) { 152 VALUE tuple = RARRAY_PTR(tags)[i]; 153 VALUE name; 154 VALUE value; 155 156 Check_Type(tuple, T_ARRAY); 157 158 if(RARRAY_LEN(tuple) < 2) { 159 xfree(head); 160 rb_raise(rb_eRuntimeError, "tag tuple must be of length 2"); 161 } 162 name = RARRAY_PTR(tuple)[0]; 163 value = RARRAY_PTR(tuple)[1]; 164#ifdef HAVE_RUBY_ENCODING_H 165 name = rb_str_export_to_enc(name, encoding); 166 value = rb_str_export_to_enc(value, encoding); 167#endif 168 169 tail->handle = (yaml_char_t *)StringValuePtr(name); 170 tail->prefix = (yaml_char_t *)StringValuePtr(value); 171 172 tail++; 173 } 174 } 175 176 yaml_document_start_event_initialize( 177 &event, 178 (RARRAY_LEN(version) > 0) ? &version_directive : NULL, 179 head, 180 tail, 181 imp ? 1 : 0 182 ); 183 184 emit(emitter, &event); 185 186 if(head) xfree(head); 187 188 return self; 189} 190 191/* call-seq: emitter.end_document(implicit) 192 * 193 * End a document emission with an +implicit+ ending. 194 * 195 * See Psych::Handler#end_document 196 */ 197static VALUE end_document(VALUE self, VALUE imp) 198{ 199 yaml_emitter_t * emitter; 200 yaml_event_t event; 201 Data_Get_Struct(self, yaml_emitter_t, emitter); 202 203 yaml_document_end_event_initialize(&event, imp ? 1 : 0); 204 205 emit(emitter, &event); 206 207 return self; 208} 209 210/* call-seq: emitter.scalar(value, anchor, tag, plain, quoted, style) 211 * 212 * Emit a scalar with +value+, +anchor+, +tag+, and a +plain+ or +quoted+ 213 * string type with +style+. 214 * 215 * See Psych::Handler#scalar 216 */ 217static VALUE scalar( 218 VALUE self, 219 VALUE value, 220 VALUE anchor, 221 VALUE tag, 222 VALUE plain, 223 VALUE quoted, 224 VALUE style 225 ) { 226 yaml_emitter_t * emitter; 227 yaml_event_t event; 228#ifdef HAVE_RUBY_ENCODING_H 229 rb_encoding *encoding; 230#endif 231 Data_Get_Struct(self, yaml_emitter_t, emitter); 232 233 Check_Type(value, T_STRING); 234 235#ifdef HAVE_RUBY_ENCODING_H 236 encoding = rb_utf8_encoding(); 237 238 value = rb_str_export_to_enc(value, encoding); 239 240 if(!NIL_P(anchor)) { 241 Check_Type(anchor, T_STRING); 242 anchor = rb_str_export_to_enc(anchor, encoding); 243 } 244 245 if(!NIL_P(tag)) { 246 Check_Type(tag, T_STRING); 247 tag = rb_str_export_to_enc(tag, encoding); 248 } 249#endif 250 251 yaml_scalar_event_initialize( 252 &event, 253 (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)), 254 (yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)), 255 (yaml_char_t*)StringValuePtr(value), 256 (int)RSTRING_LEN(value), 257 plain ? 1 : 0, 258 quoted ? 1 : 0, 259 (yaml_scalar_style_t)NUM2INT(style) 260 ); 261 262 emit(emitter, &event); 263 264 return self; 265} 266 267/* call-seq: emitter.start_sequence(anchor, tag, implicit, style) 268 * 269 * Start emitting a sequence with +anchor+, a +tag+, +implicit+ sequence 270 * start and end, along with +style+. 271 * 272 * See Psych::Handler#start_sequence 273 */ 274static VALUE start_sequence( 275 VALUE self, 276 VALUE anchor, 277 VALUE tag, 278 VALUE implicit, 279 VALUE style 280 ) { 281 yaml_emitter_t * emitter; 282 yaml_event_t event; 283 284#ifdef HAVE_RUBY_ENCODING_H 285 rb_encoding * encoding = rb_utf8_encoding(); 286 287 if(!NIL_P(anchor)) { 288 Check_Type(anchor, T_STRING); 289 anchor = rb_str_export_to_enc(anchor, encoding); 290 } 291 292 if(!NIL_P(tag)) { 293 Check_Type(tag, T_STRING); 294 tag = rb_str_export_to_enc(tag, encoding); 295 } 296#endif 297 298 Data_Get_Struct(self, yaml_emitter_t, emitter); 299 300 yaml_sequence_start_event_initialize( 301 &event, 302 (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)), 303 (yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)), 304 implicit ? 1 : 0, 305 (yaml_sequence_style_t)NUM2INT(style) 306 ); 307 308 emit(emitter, &event); 309 310 return self; 311} 312 313/* call-seq: emitter.end_sequence 314 * 315 * End sequence emission. 316 * 317 * See Psych::Handler#end_sequence 318 */ 319static VALUE end_sequence(VALUE self) 320{ 321 yaml_emitter_t * emitter; 322 yaml_event_t event; 323 Data_Get_Struct(self, yaml_emitter_t, emitter); 324 325 yaml_sequence_end_event_initialize(&event); 326 327 emit(emitter, &event); 328 329 return self; 330} 331 332/* call-seq: emitter.start_mapping(anchor, tag, implicit, style) 333 * 334 * Start emitting a YAML map with +anchor+, +tag+, an +implicit+ start 335 * and end, and +style+. 336 * 337 * See Psych::Handler#start_mapping 338 */ 339static VALUE start_mapping( 340 VALUE self, 341 VALUE anchor, 342 VALUE tag, 343 VALUE implicit, 344 VALUE style 345 ) { 346 yaml_emitter_t * emitter; 347 yaml_event_t event; 348#ifdef HAVE_RUBY_ENCODING_H 349 rb_encoding *encoding; 350#endif 351 Data_Get_Struct(self, yaml_emitter_t, emitter); 352 353#ifdef HAVE_RUBY_ENCODING_H 354 encoding = rb_utf8_encoding(); 355 356 if(!NIL_P(anchor)) { 357 Check_Type(anchor, T_STRING); 358 anchor = rb_str_export_to_enc(anchor, encoding); 359 } 360 361 if(!NIL_P(tag)) { 362 Check_Type(tag, T_STRING); 363 tag = rb_str_export_to_enc(tag, encoding); 364 } 365#endif 366 367 yaml_mapping_start_event_initialize( 368 &event, 369 (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)), 370 (yaml_char_t *)(NIL_P(tag) ? NULL : StringValuePtr(tag)), 371 implicit ? 1 : 0, 372 (yaml_mapping_style_t)NUM2INT(style) 373 ); 374 375 emit(emitter, &event); 376 377 return self; 378} 379 380/* call-seq: emitter.end_mapping 381 * 382 * Emit the end of a mapping. 383 * 384 * See Psych::Handler#end_mapping 385 */ 386static VALUE end_mapping(VALUE self) 387{ 388 yaml_emitter_t * emitter; 389 yaml_event_t event; 390 Data_Get_Struct(self, yaml_emitter_t, emitter); 391 392 yaml_mapping_end_event_initialize(&event); 393 394 emit(emitter, &event); 395 396 return self; 397} 398 399/* call-seq: emitter.alias(anchor) 400 * 401 * Emit an alias with +anchor+. 402 * 403 * See Psych::Handler#alias 404 */ 405static VALUE alias(VALUE self, VALUE anchor) 406{ 407 yaml_emitter_t * emitter; 408 yaml_event_t event; 409 Data_Get_Struct(self, yaml_emitter_t, emitter); 410 411#ifdef HAVE_RUBY_ENCODING_H 412 if(!NIL_P(anchor)) { 413 Check_Type(anchor, T_STRING); 414 anchor = rb_str_export_to_enc(anchor, rb_utf8_encoding()); 415 } 416#endif 417 418 yaml_alias_event_initialize( 419 &event, 420 (yaml_char_t *)(NIL_P(anchor) ? NULL : StringValuePtr(anchor)) 421 ); 422 423 emit(emitter, &event); 424 425 return self; 426} 427 428/* call-seq: emitter.canonical = true 429 * 430 * Set the output style to canonical, or not. 431 */ 432static VALUE set_canonical(VALUE self, VALUE style) 433{ 434 yaml_emitter_t * emitter; 435 Data_Get_Struct(self, yaml_emitter_t, emitter); 436 437 yaml_emitter_set_canonical(emitter, Qtrue == style ? 1 : 0); 438 439 return style; 440} 441 442/* call-seq: emitter.canonical 443 * 444 * Get the output style, canonical or not. 445 */ 446static VALUE canonical(VALUE self) 447{ 448 yaml_emitter_t * emitter; 449 Data_Get_Struct(self, yaml_emitter_t, emitter); 450 451 return (emitter->canonical == 0) ? Qfalse : Qtrue; 452} 453 454/* call-seq: emitter.indentation = level 455 * 456 * Set the indentation level to +level+. The level must be less than 10 and 457 * greater than 1. 458 */ 459static VALUE set_indentation(VALUE self, VALUE level) 460{ 461 yaml_emitter_t * emitter; 462 Data_Get_Struct(self, yaml_emitter_t, emitter); 463 464 yaml_emitter_set_indent(emitter, NUM2INT(level)); 465 466 return level; 467} 468 469/* call-seq: emitter.indentation 470 * 471 * Get the indentation level. 472 */ 473static VALUE indentation(VALUE self) 474{ 475 yaml_emitter_t * emitter; 476 Data_Get_Struct(self, yaml_emitter_t, emitter); 477 478 return INT2NUM(emitter->best_indent); 479} 480 481/* call-seq: emitter.line_width 482 * 483 * Get the preferred line width. 484 */ 485static VALUE line_width(VALUE self) 486{ 487 yaml_emitter_t * emitter; 488 Data_Get_Struct(self, yaml_emitter_t, emitter); 489 490 return INT2NUM(emitter->best_width); 491} 492 493/* call-seq: emitter.line_width = width 494 * 495 * Set the preferred line with to +width+. 496 */ 497static VALUE set_line_width(VALUE self, VALUE width) 498{ 499 yaml_emitter_t * emitter; 500 Data_Get_Struct(self, yaml_emitter_t, emitter); 501 502 yaml_emitter_set_width(emitter, NUM2INT(width)); 503 504 return width; 505} 506 507void Init_psych_emitter() 508{ 509 VALUE psych = rb_define_module("Psych"); 510 VALUE handler = rb_define_class_under(psych, "Handler", rb_cObject); 511 cPsychEmitter = rb_define_class_under(psych, "Emitter", handler); 512 513 rb_define_alloc_func(cPsychEmitter, allocate); 514 515 rb_define_method(cPsychEmitter, "initialize", initialize, -1); 516 rb_define_method(cPsychEmitter, "start_stream", start_stream, 1); 517 rb_define_method(cPsychEmitter, "end_stream", end_stream, 0); 518 rb_define_method(cPsychEmitter, "start_document", start_document, 3); 519 rb_define_method(cPsychEmitter, "end_document", end_document, 1); 520 rb_define_method(cPsychEmitter, "scalar", scalar, 6); 521 rb_define_method(cPsychEmitter, "start_sequence", start_sequence, 4); 522 rb_define_method(cPsychEmitter, "end_sequence", end_sequence, 0); 523 rb_define_method(cPsychEmitter, "start_mapping", start_mapping, 4); 524 rb_define_method(cPsychEmitter, "end_mapping", end_mapping, 0); 525 rb_define_method(cPsychEmitter, "alias", alias, 1); 526 rb_define_method(cPsychEmitter, "canonical", canonical, 0); 527 rb_define_method(cPsychEmitter, "canonical=", set_canonical, 1); 528 rb_define_method(cPsychEmitter, "indentation", indentation, 0); 529 rb_define_method(cPsychEmitter, "indentation=", set_indentation, 1); 530 rb_define_method(cPsychEmitter, "line_width", line_width, 0); 531 rb_define_method(cPsychEmitter, "line_width=", set_line_width, 1); 532 533 id_write = rb_intern("write"); 534 id_line_width = rb_intern("line_width"); 535 id_indentation = rb_intern("indentation"); 536 id_canonical = rb_intern("canonical"); 537} 538/* vim: set noet sws=4 sw=4: */ 539