#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <string.h>
#include "jq.h"
#include "jv.h"
#define JQ_VERSION "1.6"
jv my_jv_input(pTHX_
void
* arg) {
if
(arg == NULL) {
return
jv_null();
}
SV *
const
p_sv = arg;
SvGETMAGIC(p_sv);
if
(SvTYPE(p_sv) == SVt_NULL || (SvTYPE(p_sv) < SVt_PVAV && !SvOK(p_sv))) {
return
jv_null();
}
else
if
(SvROK(p_sv) && SvTYPE(SvRV(p_sv)) == SVt_IV) {
return
jv_bool((
bool
)SvTRUE(SvRV(p_sv)));
}
else
if
(SvROK(p_sv) && sv_derived_from(p_sv,
"JSON::PP::Boolean"
)) {
return
jv_bool((
bool
)SvTRUE(SvRV(p_sv)));
}
else
if
(SvIOK(p_sv)) {
return
jv_number(SvIV(p_sv));
}
else
if
(SvUOK(p_sv)) {
return
jv_number(SvUV(p_sv));
}
else
if
(SvNOK(p_sv)) {
return
jv_number(SvNV(p_sv));
}
else
if
(SvPOK(p_sv)) {
STRLEN len;
char
* p_pv = SvUTF8(p_sv) ? SvPVutf8(p_sv, len) : SvPV(p_sv, len);
return
jv_string_sized(p_pv, len);
}
else
if
(SvROK(p_sv) && SvTYPE(SvRV(p_sv)) == SVt_PVAV) {
jv jval = jv_array();
AV * p_av = (AV *)SvRV(p_sv);
SSize_t len = av_len(p_av);
if
(len < 0) {
return
jval;
}
SSize_t i;
for
(i = 0; i <= len; i++) {
jval = jv_array_append(jval, my_jv_input(aTHX_ *av_fetch(p_av, i, 0)));
}
return
jval;
}
else
if
(SvROK(p_sv) && SvTYPE(SvRV(p_sv)) == SVt_PVHV) {
jv jval = jv_object();
HV * p_hv = (HV *)SvRV(p_sv);
I32 len = hv_iterinit(p_hv);
I32 i;
for
(i = 0; i < len; i++) {
char
* key = NULL;
I32 klen = 0;
SV * val = hv_iternextsv(p_hv, &key, &klen);
jval = jv_object_set(jval, jv_string_sized(key, klen), my_jv_input(aTHX_ val));
}
return
jval;
}
else
{
croak(
"cannot convert perl object to json format: SvTYPE == %i"
, SvTYPE(p_sv));
}
}
void
* my_jv_output(pTHX_ jv jval) {
jv_kind kind = jv_get_kind(jval);
if
(kind == JV_KIND_NULL) {
return
newSV(0);
}
else
if
(kind == JV_KIND_FALSE) {
SV * sv_false = newSV(0);
return
sv_setref_iv(sv_false,
"JSON::PP::Boolean"
, 0);
}
else
if
(kind == JV_KIND_TRUE) {
SV * sv_true = newSV(0);
return
sv_setref_iv(sv_true,
"JSON::PP::Boolean"
, 1);
}
else
if
(kind == JV_KIND_NUMBER) {
double
val = jv_number_value(jval);
SV * p_sv = newSV(0);
if
(jv_is_integer(jval)) {
sv_setiv(p_sv, val);
}
else
{
sv_setnv(p_sv, val);
}
return
p_sv;
}
else
if
(kind == JV_KIND_STRING) {
return
newSVpvf(
"%s"
, jv_string_value(jval));
}
else
if
(kind == JV_KIND_ARRAY) {
AV * p_av = newAV();
SSize_t len = (SSize_t)jv_array_length(jv_copy(jval));
av_extend(p_av, len - 1);
SSize_t i;
for
(i = 0; i < len; i++) {
jv val = jv_array_get(jv_copy(jval), i);
av_push(p_av, (SV *)my_jv_output(aTHX_ val));
jv_free(val);
}
return
newRV_noinc((SV *)p_av);
}
else
if
(kind == JV_KIND_OBJECT) {
HV * p_hv = newHV();
int
iter = jv_object_iter(jval);
while
(jv_object_iter_valid(jval, iter)) {
jv key = jv_object_iter_key(jval, iter);
jv val = jv_object_iter_value(jval, iter);
if
(jv_get_kind(key) != JV_KIND_STRING) {
croak(
"cannot take non-string type as hash key: JV_KIND == %i"
, jv_get_kind(key));
}
const
char
* k = jv_string_value(key);
int
klen = jv_string_length_bytes(key);
SV * v = (SV *)my_jv_output(aTHX_ val);
hv_store(p_hv, k, klen, v, 0);
jv_free(key);
jv_free(val);
iter = jv_object_iter_next(jval, iter);
}
return
newRV_noinc((SV *)p_hv);
}
else
{
croak(
"un-supported jv object type: JV_KIND == %i"
, kind);
}
}
static
void
my_error_cb(
void
* errors, jv jerr) {
dTHX;
jerr = jv_copy(jerr);
av_push((AV *)errors, newSVpvn(jv_string_value(jerr), jv_string_length_bytes(jerr)));
}
static
void
my_debug_cb(
void
* data, jv input) {
dTHX;
int
dumpopts = *(
int
*)data;
jv_dumpf(JV_ARRAY(jv_string(
"DEBUG:"
), input), stderr, dumpopts);
fprintf
(stderr,
"\n"
);
}
static
inline
void
assert_isa(pTHX_ SV * self) {
if
(!sv_isa(self,
"JSON::JQ"
)) {
croak(
"self is not a JSON::JQ object"
);
}
}
static
const
char
*skip_shebang(
const
char
*p) {
if
(
strncmp
(p,
"#!"
,
sizeof
(
"#!"
) - 1) != 0)
return
p;
const
char
*n =
strchr
(p,
'\n'
);
if
(n == NULL || n[1] !=
'#'
)
return
p;
n =
strchr
(n + 1,
'\n'
);
if
(n == NULL || n[1] ==
'#'
|| n[1] ==
'\0'
|| n[-1] !=
'\\'
|| n[-2] ==
'\\'
)
return
p;
n =
strchr
(n + 1,
'\n'
);
if
(n == NULL)
return
p;
return
n+1;
}
MODULE = JSON::JQ PACKAGE = JSON::JQ
PROTOTYPES: DISABLE
int
JV_PRINT_INDENT_FLAGS(n)
int
n
CODE:
RETVAL = JV_PRINT_INDENT_FLAGS(n);
OUTPUT:
RETVAL
void
_init(self)
HV * self
INIT:
jq_state * _jq = NULL;
SV * sv_jq;
HV * hv_attr;
char
* script;
AV * av_err;
int
compiled = 0;
CODE:
assert_isa(aTHX_ ST(0));
_jq = jq_init();
if
(_jq == NULL) {
croak(
"cannot malloc jq engine"
);
}
else
{
sv_jq = newSV(0);
sv_setiv(sv_jq, PTR2IV(_jq));
SvREADONLY_on(sv_jq);
hv_stores(self,
"_jq"
, sv_jq);
}
av_err = (AV *)SvRV(*hv_fetchs(self,
"_errors"
, 0));
jq_set_error_cb(_jq, my_error_cb, av_err);
int
dumpopts = (
int
)SvIV(*hv_fetchs(self,
"_dumpopts"
, 0));
jq_set_debug_cb(_jq, my_debug_cb, &dumpopts);
hv_attr = (HV *)SvRV(*hv_fetchs(self,
"_attribute"
, 0));
I32 len = hv_iterinit(hv_attr);
I32 i;
for
(i = 0; i < len; i++) {
char
* key = NULL;
I32 klen = 0;
SV * val = hv_iternextsv(hv_attr, &key, &klen);
jq_set_attr(_jq, jv_string_sized(key, klen), my_jv_input(aTHX_ val));
}
jq_set_attr(_jq, jv_string(
"VERSION_DIR"
), jv_string(JQ_VERSION));
jv args = my_jv_input(aTHX_ *hv_fetchs(self,
"variable"
, 0));
if
(hv_exists(self,
"script_file"
, 11)) {
jv data = jv_load_file(SvPV_nolen(*hv_fetchs(self,
"script_file"
, 0)), 1);
if
(!jv_is_valid(data)) {
data = jv_invalid_get_msg(data);
my_error_cb(av_err, data);
jv_free(data);
XSRETURN_NO;
}
compiled = jq_compile_args(_jq, skip_shebang(jv_string_value(data)), args);
jv_free(data);
}
else
{
script = SvPV_nolen(*hv_fetchs(self,
"script"
, 0));
compiled = jq_compile_args(_jq, script, args);
}
if
(compiled) {
if
(SvTRUE(get_sv(
"JSON::JQ::DUMP_DISASM"
, 0))) {
jq_dump_disassembly(_jq, 0);
printf
(
"\n"
);
}
XSRETURN_YES;
}
else
{
XSRETURN_NO;
}
int
_process(self, sv_input, av_output)
HV * self
SV * sv_input
AV * av_output
INIT:
jq_state * _jq = NULL;
SV * sv_jq;
AV * av_err;
CODE:
assert_isa(aTHX_ ST(0));
sv_jq = *hv_fetchs(self,
"_jq"
, 0);
_jq = INT2PTR(jq_state *, SvIV(sv_jq));
jv jv_input = my_jv_input(aTHX_ sv_input);
int
jq_flags = (
int
)SvIV(*hv_fetchs(self,
"jq_flags"
, 0));
jq_start(_jq, jv_input, jq_flags);
jv result;
av_err = (AV *)SvRV(*hv_fetchs(self,
"_errors"
, 0));
av_clear(av_err);
int
ret = 14;
while
(jv_is_valid(result = jq_next(_jq))) {
av_push(av_output, (SV *)my_jv_output(aTHX_ result));
if
(jv_get_kind(result) == JV_KIND_FALSE || jv_get_kind(result) == JV_KIND_NULL) {
ret = 11;
}
else
{
ret = 0;
}
}
if
(jq_halted(_jq)) {
jv exit_code = jq_get_exit_code(_jq);
if
(!jv_get_kind(exit_code)) {
ret = 0;
}
else
if
(jv_get_kind(exit_code) == JV_KIND_NUMBER) {
ret = jv_number_value(exit_code);
}
else
{
ret = 5;
}
jv_free(exit_code);
jv error_message = jq_get_error_message(_jq);
if
(jv_get_kind(error_message) == JV_KIND_STRING) {
my_error_cb(av_err, error_message);
}
else
if
(jv_get_kind(error_message) == JV_KIND_NULL) {
}
else
if
(jv_is_valid(error_message)) {
error_message = jv_dump_string(jv_copy(error_message), 0);
my_error_cb(av_err, error_message);
}
else
{
}
jv_free(error_message);
}
else
if
(jv_invalid_has_msg(jv_copy(result))) {
jv msg = jv_invalid_get_msg(jv_copy(result));
if
(jv_get_kind(msg) == JV_KIND_STRING) {
av_push(av_err, newSVpvf(
"jq: error: %s"
, jv_string_value(msg)));
}
else
{
msg = jv_dump_string(msg, 0);
av_push(av_err, newSVpvf(
"jq: error (not a string): %s"
, jv_string_value(msg)));
}
ret = 5;
jv_free(msg);
}
jv_free(result);
RETVAL = ret;
OUTPUT:
RETVAL
void
DESTROY(self)
HV * self
INIT:
jq_state * _jq = NULL;
SV * sv_jq;
CODE:
assert_isa(aTHX_ ST(0));
sv_jq = *hv_fetchs(self,
"_jq"
, 0);
_jq = INT2PTR(jq_state *, SvIV(sv_jq));
if
(_jq != NULL) {
if
(SvTRUE(get_sv(
"JSON::JQ::DEBUG"
, 0))) {
fprintf
(stderr,
"destroying jq object: %p\n"
, _jq);
}
jq_teardown(&_jq);
}