1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/ext/psych/parser.c
tenderlove 6c56c40b44 * ext/psych/parser.c (parse): release event objects to plug memory
leak.  Thanks Mark J. Titorenko!

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@31930 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2011-06-06 00:41:24 +00:00

399 lines
10 KiB
C

#include <psych.h>
VALUE cPsychParser;
VALUE ePsychSyntaxError;
static ID id_read;
static ID id_path;
static ID id_empty;
static ID id_start_stream;
static ID id_end_stream;
static ID id_start_document;
static ID id_end_document;
static ID id_alias;
static ID id_scalar;
static ID id_start_sequence;
static ID id_end_sequence;
static ID id_start_mapping;
static ID id_end_mapping;
#define PSYCH_TRANSCODE(_str, _yaml_enc, _internal_enc) \
do { \
rb_enc_associate_index((_str), (_yaml_enc)); \
if(_internal_enc) \
(_str) = rb_str_export_to_enc((_str), (_internal_enc)); \
} while (0)
static int io_reader(void * data, unsigned char *buf, size_t size, size_t *read)
{
VALUE io = (VALUE)data;
VALUE string = rb_funcall(io, id_read, 1, INT2NUM(size));
*read = 0;
if(! NIL_P(string)) {
void * str = (void *)StringValuePtr(string);
*read = (size_t)RSTRING_LEN(string);
memcpy(buf, str, *read);
}
return 1;
}
static void dealloc(void * ptr)
{
yaml_parser_t * parser;
parser = (yaml_parser_t *)ptr;
yaml_parser_delete(parser);
xfree(parser);
}
static VALUE allocate(VALUE klass)
{
yaml_parser_t * parser;
parser = xmalloc(sizeof(yaml_parser_t));
yaml_parser_initialize(parser);
return Data_Wrap_Struct(klass, 0, dealloc, parser);
}
/*
* call-seq:
* parser.parse(yaml)
*
* Parse the YAML document contained in +yaml+. Events will be called on
* the handler set on the parser instance.
*
* See Psych::Parser and Psych::Parser#handler
*/
static VALUE parse(VALUE self, VALUE yaml)
{
yaml_parser_t * parser;
yaml_event_t event;
int done = 0;
int tainted = 0;
#ifdef HAVE_RUBY_ENCODING_H
int encoding = rb_utf8_encindex();
rb_encoding * internal_enc = rb_default_internal_encoding();
#endif
VALUE handler = rb_iv_get(self, "@handler");
Data_Get_Struct(self, yaml_parser_t, parser);
if (OBJ_TAINTED(yaml)) tainted = 1;
if(rb_respond_to(yaml, id_read)) {
yaml_parser_set_input(parser, io_reader, (void *)yaml);
if (RTEST(rb_obj_is_kind_of(yaml, rb_cIO))) tainted = 1;
} else {
StringValue(yaml);
yaml_parser_set_input_string(
parser,
(const unsigned char *)RSTRING_PTR(yaml),
(size_t)RSTRING_LEN(yaml)
);
}
while(!done) {
if(!yaml_parser_parse(parser, &event)) {
VALUE path;
size_t line = parser->mark.line;
size_t column = parser->mark.column;
if(rb_respond_to(yaml, id_path))
path = rb_funcall(yaml, id_path, 0);
else
path = rb_str_new2("<unknown>");
yaml_parser_delete(parser);
yaml_parser_initialize(parser);
rb_raise(ePsychSyntaxError, "(%s): couldn't parse YAML at line %d column %d",
StringValuePtr(path),
(int)line, (int)column);
}
switch(event.type) {
case YAML_STREAM_START_EVENT:
rb_funcall(handler, id_start_stream, 1,
INT2NUM((long)event.data.stream_start.encoding)
);
break;
case YAML_DOCUMENT_START_EVENT:
{
/* Get a list of tag directives (if any) */
VALUE tag_directives = rb_ary_new();
/* Grab the document version */
VALUE version = event.data.document_start.version_directive ?
rb_ary_new3(
(long)2,
INT2NUM((long)event.data.document_start.version_directive->major),
INT2NUM((long)event.data.document_start.version_directive->minor)
) : rb_ary_new();
if(event.data.document_start.tag_directives.start) {
yaml_tag_directive_t *start =
event.data.document_start.tag_directives.start;
yaml_tag_directive_t *end =
event.data.document_start.tag_directives.end;
for(; start != end; start++) {
VALUE handle = Qnil;
VALUE prefix = Qnil;
if(start->handle) {
handle = rb_str_new2((const char *)start->handle);
if (tainted) OBJ_TAINT(handle);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(handle, encoding, internal_enc);
#endif
}
if(start->prefix) {
prefix = rb_str_new2((const char *)start->prefix);
if (tainted) OBJ_TAINT(prefix);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(prefix, encoding, internal_enc);
#endif
}
rb_ary_push(tag_directives, rb_ary_new3((long)2, handle, prefix));
}
}
rb_funcall(handler, id_start_document, 3,
version, tag_directives,
event.data.document_start.implicit == 1 ? Qtrue : Qfalse
);
}
break;
case YAML_DOCUMENT_END_EVENT:
rb_funcall(handler, id_end_document, 1,
event.data.document_end.implicit == 1 ? Qtrue : Qfalse
);
break;
case YAML_ALIAS_EVENT:
{
VALUE alias = Qnil;
if(event.data.alias.anchor) {
alias = rb_str_new2((const char *)event.data.alias.anchor);
if (tainted) OBJ_TAINT(alias);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(alias, encoding, internal_enc);
#endif
}
rb_funcall(handler, id_alias, 1, alias);
}
break;
case YAML_SCALAR_EVENT:
{
VALUE anchor = Qnil;
VALUE tag = Qnil;
VALUE plain_implicit, quoted_implicit, style;
VALUE val = rb_str_new(
(const char *)event.data.scalar.value,
(long)event.data.scalar.length
);
if (tainted) OBJ_TAINT(val);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(val, encoding, internal_enc);
#endif
if(event.data.scalar.anchor) {
anchor = rb_str_new2((const char *)event.data.scalar.anchor);
if (tainted) OBJ_TAINT(anchor);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(anchor, encoding, internal_enc);
#endif
}
if(event.data.scalar.tag) {
tag = rb_str_new2((const char *)event.data.scalar.tag);
if (tainted) OBJ_TAINT(tag);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(tag, encoding, internal_enc);
#endif
}
plain_implicit =
event.data.scalar.plain_implicit == 0 ? Qfalse : Qtrue;
quoted_implicit =
event.data.scalar.quoted_implicit == 0 ? Qfalse : Qtrue;
style = INT2NUM((long)event.data.scalar.style);
rb_funcall(handler, id_scalar, 6,
val, anchor, tag, plain_implicit, quoted_implicit, style);
}
break;
case YAML_SEQUENCE_START_EVENT:
{
VALUE anchor = Qnil;
VALUE tag = Qnil;
VALUE implicit, style;
if(event.data.sequence_start.anchor) {
anchor = rb_str_new2((const char *)event.data.sequence_start.anchor);
if (tainted) OBJ_TAINT(anchor);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(anchor, encoding, internal_enc);
#endif
}
tag = Qnil;
if(event.data.sequence_start.tag) {
tag = rb_str_new2((const char *)event.data.sequence_start.tag);
if (tainted) OBJ_TAINT(tag);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(tag, encoding, internal_enc);
#endif
}
implicit =
event.data.sequence_start.implicit == 0 ? Qfalse : Qtrue;
style = INT2NUM((long)event.data.sequence_start.style);
rb_funcall(handler, id_start_sequence, 4,
anchor, tag, implicit, style);
}
break;
case YAML_SEQUENCE_END_EVENT:
rb_funcall(handler, id_end_sequence, 0);
break;
case YAML_MAPPING_START_EVENT:
{
VALUE anchor = Qnil;
VALUE tag = Qnil;
VALUE implicit, style;
if(event.data.mapping_start.anchor) {
anchor = rb_str_new2((const char *)event.data.mapping_start.anchor);
if (tainted) OBJ_TAINT(anchor);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(anchor, encoding, internal_enc);
#endif
}
if(event.data.mapping_start.tag) {
tag = rb_str_new2((const char *)event.data.mapping_start.tag);
if (tainted) OBJ_TAINT(tag);
#ifdef HAVE_RUBY_ENCODING_H
PSYCH_TRANSCODE(tag, encoding, internal_enc);
#endif
}
implicit =
event.data.mapping_start.implicit == 0 ? Qfalse : Qtrue;
style = INT2NUM((long)event.data.mapping_start.style);
rb_funcall(handler, id_start_mapping, 4,
anchor, tag, implicit, style);
}
break;
case YAML_MAPPING_END_EVENT:
rb_funcall(handler, id_end_mapping, 0);
break;
case YAML_NO_EVENT:
rb_funcall(handler, id_empty, 0);
break;
case YAML_STREAM_END_EVENT:
rb_funcall(handler, id_end_stream, 0);
done = 1;
break;
}
yaml_event_delete(&event);
}
return self;
}
/*
* call-seq:
* parser.external_encoding=(encoding)
*
* Set the encoding for this parser to +encoding+
*/
static VALUE set_external_encoding(VALUE self, VALUE encoding)
{
yaml_parser_t * parser;
VALUE exception;
Data_Get_Struct(self, yaml_parser_t, parser);
if(parser->encoding) {
exception = rb_const_get_at(mPsych, rb_intern("Exception"));
rb_raise(exception, "don't set the encoding twice!");
}
yaml_parser_set_encoding(parser, NUM2INT(encoding));
return encoding;
}
/*
* call-seq:
* parser.mark # => #<Psych::Parser::Mark>
*
* Returns a Psych::Parser::Mark object that contains line, column, and index
* information.
*/
static VALUE mark(VALUE self)
{
VALUE mark_klass;
VALUE args[3];
yaml_parser_t * parser;
Data_Get_Struct(self, yaml_parser_t, parser);
mark_klass = rb_const_get_at(cPsychParser, rb_intern("Mark"));
args[0] = INT2NUM(parser->mark.index);
args[1] = INT2NUM(parser->mark.line);
args[2] = INT2NUM(parser->mark.column);
return rb_class_new_instance(3, args, mark_klass);
}
void Init_psych_parser()
{
#if 0
mPsych = rb_define_module("Psych");
#endif
cPsychParser = rb_define_class_under(mPsych, "Parser", rb_cObject);
rb_define_alloc_func(cPsychParser, allocate);
/* Any encoding: Let the parser choose the encoding */
rb_define_const(cPsychParser, "ANY", INT2NUM(YAML_ANY_ENCODING));
/* UTF-8 Encoding */
rb_define_const(cPsychParser, "UTF8", INT2NUM(YAML_UTF8_ENCODING));
/* UTF-16-LE Encoding with BOM */
rb_define_const(cPsychParser, "UTF16LE", INT2NUM(YAML_UTF16LE_ENCODING));
/* UTF-16-BE Encoding with BOM */
rb_define_const(cPsychParser, "UTF16BE", INT2NUM(YAML_UTF16BE_ENCODING));
ePsychSyntaxError = rb_define_class_under(mPsych, "SyntaxError", rb_eSyntaxError);
rb_define_method(cPsychParser, "parse", parse, 1);
rb_define_method(cPsychParser, "mark", mark, 0);
rb_define_method(cPsychParser, "external_encoding=", set_external_encoding, 1);
id_read = rb_intern("read");
id_path = rb_intern("path");
id_empty = rb_intern("empty");
id_start_stream = rb_intern("start_stream");
id_end_stream = rb_intern("end_stream");
id_start_document = rb_intern("start_document");
id_end_document = rb_intern("end_document");
id_alias = rb_intern("alias");
id_scalar = rb_intern("scalar");
id_start_sequence = rb_intern("start_sequence");
id_end_sequence = rb_intern("end_sequence");
id_start_mapping = rb_intern("start_mapping");
id_end_mapping = rb_intern("end_mapping");
}
/* vim: set noet sws=4 sw=4: */