#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#define NEED_newRV_noinc
#include "ppport.h"
#include <yaml.h>
#define SCALAR_STYLE_PLAIN ":"
#define SCALAR_STYLE_DOUBLEQUOTED "\""
#define SCALAR_STYLE_SINGLEQUOTED "'"
#define SCALAR_STYLE_LITERAL "|"
#define SCALAR_STYLE_FOLDED ">"
char *
parser_error_msg(yaml_parser_t *parser, char *problem)
{
char *msg;
if (!problem)
problem = (char *)parser->problem;
msg = form(
"YAML::PP::LibYAML Error: %swas found at ",
(problem ? form("The problem:\n\n %s\n\n", problem) : "A problem ")
);
if (
parser->problem_mark.line ||
parser->problem_mark.column
)
msg = form("%s, line: %lu, column: %lu\n",
msg,
(unsigned long)parser->problem_mark.line + 1,
(unsigned long)parser->problem_mark.column + 1
);
else
msg = form("%s\n", msg);
if (parser->context)
msg = form("%s%s at line: %lu, column: %lu\n",
msg,
parser->context,
(unsigned long)parser->context_mark.line + 1,
(unsigned long)parser->context_mark.column + 1
);
return msg;
}
HV *
libyaml_to_perl_event(yaml_event_t *event)
{
dTHX;
HV *perl_event;
HV *perl_version_directive;
AV *perl_tag_directives;
HV *perl_tag_directive;
HV *perl_start_mark;
HV *perl_end_mark;
yaml_event_type_t type;
char *perl_event_anchor;
char *perl_event_tag;
char *perl_event_type;
yaml_mark_t start_mark;
yaml_mark_t end_mark;
SV *hash_ref_start;
SV *hash_ref_end;
SV *scalar_value;
yaml_tag_directive_t *tag_directive;
perl_event = newHV();
type = event->type;
perl_event_anchor = NULL;
perl_event_tag = NULL;
if (type == YAML_NO_EVENT)
croak("%s", "Unexpected event YAML_NO_EVENT");
else if (type == YAML_STREAM_START_EVENT)
perl_event_type = "stream_start_event";
else if (type == YAML_STREAM_END_EVENT)
perl_event_type = "stream_end_event";
else if (type == YAML_DOCUMENT_START_EVENT) {
perl_event_type = "document_start_event";
if (event->data.document_start.implicit)
hv_store(
perl_event, "implicit", 8,
newSViv( 1 ), 0
);
if (event->data.document_start.version_directive) {
perl_version_directive = newHV();
hv_store(
perl_version_directive, "major", 5,
newSViv( event->data.document_start.version_directive->major ), 0
);
hv_store(
perl_version_directive, "minor", 5,
newSViv( event->data.document_start.version_directive->minor ), 0
);
hv_store(
perl_event, "version_directive", 17,
newRV_noinc((SV *)perl_version_directive), 0
);
}
if (event->data.document_start.tag_directives.start) {
perl_tag_directives = newAV();
for (tag_directive = event->data.document_start.tag_directives.start;
tag_directive != event->data.document_start.tag_directives.end;
tag_directive ++) {
perl_tag_directive = newHV();
hv_store(
perl_tag_directive, "handle", 6,
newSVpv( (char *)tag_directive->handle, strlen((char *)tag_directive->handle)), 0
);
hv_store(
perl_tag_directive, "prefix", 6,
newSVpv( (char *)tag_directive->prefix, strlen((char *)tag_directive->prefix)), 0
);
av_push(perl_tag_directives, newRV_noinc((SV *)perl_tag_directive));
}
hv_store(
perl_event, "tag_directives", 14,
newRV_noinc((SV *)perl_tag_directives), 0
);
}
}
else if (type == YAML_DOCUMENT_END_EVENT) {
perl_event_type = "document_end_event";
if (event->data.document_end.implicit)
hv_store(
perl_event, "implicit", 8,
newSViv( 1 ), 0
);
}
else if (type == YAML_MAPPING_START_EVENT) {
perl_event_type = "mapping_start_event";
if (event->data.mapping_start.anchor)
perl_event_anchor = event->data.mapping_start.anchor;
if (event->data.mapping_start.tag)
perl_event_tag = event->data.mapping_start.tag;
hv_store(
perl_event, "style", 5,
newSViv( event->data.mapping_start.style ), 0
);
}
else if (type == YAML_MAPPING_END_EVENT)
perl_event_type = "mapping_end_event";
else if (type == YAML_SEQUENCE_START_EVENT) {
perl_event_type = "sequence_start_event";
if (event->data.sequence_start.anchor)
perl_event_anchor = event->data.sequence_start.anchor;
if (event->data.sequence_start.tag)
perl_event_tag = event->data.sequence_start.tag;
hv_store(
perl_event, "style", 5,
newSViv( event->data.sequence_start.style ), 0
);
}
else if (type == YAML_SEQUENCE_END_EVENT)
perl_event_type = "sequence_end_event";
else if (type == YAML_SCALAR_EVENT) {
perl_event_type = "scalar_event";
if (event->data.scalar.anchor)
perl_event_anchor = event->data.scalar.anchor;
if (event->data.scalar.tag)
perl_event_tag = event->data.scalar.tag;
switch (event->data.scalar.style) {
case YAML_ANY_SCALAR_STYLE:
abort();
}
hv_store(
perl_event, "style", 5,
newSViv( event->data.scalar.style ),
0
);
scalar_value = newSVpv( event->data.scalar.value, event->data.scalar.length );
(void)sv_utf8_decode(scalar_value);
hv_store( perl_event, "value", 5, scalar_value, 0 );
}
else if (type == YAML_ALIAS_EVENT) {
perl_event_type = "alias_event";
hv_store(
perl_event, "value", 5,
newSVpv( event->data.alias.anchor, strlen(event->data.alias.anchor) ),
0
);
}
else
abort();
hv_store(
perl_event, "name", 4,
newSVpv( perl_event_type, strlen(perl_event_type) ),
0
);
if (perl_event_anchor) {
hv_store(
perl_event, "anchor", 6,
newSVpv( perl_event_anchor, strlen(perl_event_anchor) ),
0
);
}
if (perl_event_tag) {
hv_store(
perl_event, "tag", 3,
newSVpv( perl_event_tag, strlen(perl_event_tag) ),
0
);
}
start_mark = event->start_mark;
end_mark = event->end_mark;
perl_start_mark = newHV();
perl_end_mark = newHV();
hv_store( perl_start_mark, "line", 4, newSViv( start_mark.line ), 0 );
hv_store( perl_start_mark, "column", 6, newSViv( start_mark.column ), 0 );
hash_ref_start = newRV_noinc((SV *)perl_start_mark);
hv_store( perl_event, "start", 5, hash_ref_start, 0 );
hv_store( perl_end_mark, "line", 4, newSViv( end_mark.line ), 0 );
hv_store( perl_end_mark, "column", 6, newSViv( end_mark.column ), 0 );
hash_ref_end = newRV_noinc((SV *)perl_end_mark);
hv_store( perl_event, "end", 3, hash_ref_end, 0 );
return perl_event;
}
int
parse_events(yaml_parser_t *parser, AV *perl_events)
{
dTHX;
dXCPT;
yaml_event_t event;
HV *perl_event;
yaml_event_type_t type;
XCPT_TRY_START
{
while (1) {
if (!yaml_parser_parse(parser, &event)) {
croak("%s", parser_error_msg(parser, NULL));
}
type = event.type;
perl_event = libyaml_to_perl_event(&event);
av_push(perl_events, newRV_noinc( (SV *)perl_event));
yaml_event_delete(&event);
if (type == YAML_STREAM_END_EVENT)
break;
}
} XCPT_TRY_END
XCPT_CATCH
{
yaml_parser_delete(parser);
yaml_event_delete(&event);
XCPT_RETHROW;
}
return 1;
}
int
perl_to_libyaml_event(yaml_emitter_t *emitter, HV *perl_event)
{
dTHX;
dXCPT;
yaml_event_t event;
HV *perl_version_directive;
SV **event_hashref;
int ok;
SV *perl_type;
char *type;
SV **val;
int plain_implicit, quoted_implicit;
STRLEN len;
char *scalar_value;
char *anchor_name;
char *tag_name;
yaml_scalar_style_t style = YAML_ANY_SCALAR_STYLE;
int major = 0;
int minor = 0;
int implicit;
yaml_version_directive_t *version_directive;
implicit = 0;
plain_implicit = quoted_implicit = 1;
style = YAML_ANY_SCALAR_STYLE;
tag_name = NULL;
anchor_name = NULL;
XCPT_TRY_START
{
val = hv_fetch(perl_event, "name", 4, TRUE);
if (val && SvOK(*val) && SvPOK( *val )) {
type = SvPV(*val, len);
}
else {
croak("%s\n", "event name not defined");
}
val = hv_fetch(perl_event, "anchor", 6, TRUE);
if (val && SvOK(*val) && (SvPOK( *val ) || SvIOK( *val ))) {
anchor_name = SvPV(*val, len);
}
val = hv_fetch(perl_event, "tag", 3, TRUE);
if (val && SvOK(*val) && SvPOK( *val )) {
tag_name = SvPV(*val, len);
plain_implicit = quoted_implicit = 0;
}
val = hv_fetch(perl_event, "style", 5, TRUE);
if (val && SvOK(*val) && SvIOK( *val )) {
style = SvIV(*val);
}
if (strEQ(type, "stream_start_event")) {
ok = yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING);
}
else if (strEQ(type, "stream_end_event")) {
ok = yaml_stream_end_event_initialize(&event);
}
else if (strEQ(type, "document_start_event")) {
version_directive = NULL;
val = hv_fetch(perl_event, "version_directive", 17, TRUE);
if (val && SvOK(*val) && SvROK( *val )) {
perl_version_directive = (HV *)SvRV(*val);
val = hv_fetch(perl_version_directive, "major", 5, TRUE);
if (val && SvOK(*val) && (SvIOK( *val ) || SvPOK( *val )) ) {
major = SvIV(*val);
}
val = hv_fetch(perl_version_directive, "minor", 5, TRUE);
if (val && SvOK(*val) && (SvIOK( *val ) || SvPOK( *val )) ) {
minor = SvIV(*val);
}
if (major && minor) {
version_directive = Perl_malloc(sizeof(yaml_version_directive_t));
version_directive->major = major;
version_directive->minor = minor;
}
}
val = hv_fetch(perl_event, "implicit", 8, TRUE);
if (val && SvOK(*val) && SvIOK( *val )) {
implicit = SvIV(*val);
}
ok = yaml_document_start_event_initialize(&event, version_directive, NULL, NULL, implicit);
}
else if (strEQ(type, "document_end_event")) {
val = hv_fetch(perl_event, "implicit", 8, TRUE);
if (val && SvOK(*val) && SvIOK( *val )) {
implicit = SvIV(*val);
}
ok = yaml_document_end_event_initialize(&event, implicit);
}
else if (strEQ(type, "mapping_start_event")) {
ok = yaml_mapping_start_event_initialize(
&event, anchor_name, tag_name, 0, style);
}
else if (strEQ(type, "mapping_end_event")) {
ok = yaml_mapping_end_event_initialize(&event);
}
else if (strEQ(type, "sequence_start_event")) {
ok = yaml_sequence_start_event_initialize(
&event, anchor_name, tag_name, 0, style);
}
else if (strEQ(type, "sequence_end_event")) {
ok = yaml_sequence_end_event_initialize(&event);
}
else if (strEQ(type, "scalar_event")) {
val = hv_fetch(perl_event, "value", 5, TRUE);
if (val && SvOK(*val) && SvPOK( *val )) {
scalar_value = SvPVutf8(*val, len);
}
else {
croak("%s\n", "scalar value not defined");
}
ok = yaml_scalar_event_initialize(
&event, anchor_name, tag_name,
(unsigned char *) scalar_value, strlen(scalar_value), plain_implicit, quoted_implicit, style);
}
else if (strEQ(type, "alias_event")) {
val = hv_fetch(perl_event, "value", 5, TRUE);
if (val && SvOK(*val) && (SvPOK( *val ) || SvIOK( *val ))) {
scalar_value = SvPV(*val, len);
}
else {
croak("%s\n", "alias name not defined");
}
ok = yaml_alias_event_initialize(&event, scalar_value);
}
if (!ok)
croak("%s at %s: %s\n", "ERROR creating event", type, emitter->problem);
if (!yaml_emitter_emit(emitter, &event))
croak("%s at %s: %s\n", "ERROR", type, emitter->problem);
} XCPT_TRY_END
XCPT_CATCH
{
yaml_emitter_delete(emitter);
yaml_event_delete(&event);
XCPT_RETHROW;
}
return 1;
}
int
emit_events(yaml_emitter_t *emitter, AV *perl_events)
{
dTHX;
dXCPT;
HV *perl_event;
SV **event_hashref;
int i;
XCPT_TRY_START
{
for (i = 0; i <= av_len(perl_events); i++) {
event_hashref = av_fetch(perl_events, i, 0);
perl_event = (HV *)SvRV(*event_hashref);
perl_to_libyaml_event(emitter, perl_event);
}
yaml_emitter_delete(emitter);
} XCPT_TRY_END
XCPT_CATCH
{
yaml_emitter_delete(emitter);
XCPT_RETHROW;
}
return 1;
}
int
append_output(void *yaml, unsigned char *buffer, size_t size)
{
dTHX;
sv_catpvn((SV *)yaml, (const char *)buffer, (STRLEN)size);
return 1;
}