/* -*- mode: C; buf-file-style: "bsd" -*- */
#include "porbit-perl.h"
#include "errors.h"
#include "exttypes.h"
#include "globals.h"
#include "types.h"
#define RECV_BUFFER_LEFT(buf) \
(((guchar *)buf->message_body + 12 + GIOP_MESSAGE_BUFFER(buf)->message_header.message_size) - (guchar *)buf->cur)
static CORBA_boolean
buf_getn (GIOPRecvBuffer *buf, void *dest, size_t n)
{
buf->cur = ALIGN_ADDRESS(buf->cur, n);
if (RECV_BUFFER_LEFT(buf) < n) {
warn ("incomplete message received");
return CORBA_FALSE;
}
buf->decoder(dest, (buf->cur), n);
buf->cur = ((guchar *)buf->cur) + n;
return CORBA_TRUE;
}
static SV *
get_short (GIOPRecvBuffer *buf)
{
CORBA_short v;
if (buf_getn (buf, &v, sizeof (v)))
return newSViv(v);
else
return NULL;
}
static SV *
get_long (GIOPRecvBuffer *buf)
{
CORBA_long v;
if (buf_getn (buf, &v, sizeof (v)))
return newSViv(v);
else
return NULL;
}
static SV *
get_ushort (GIOPRecvBuffer *buf)
{
CORBA_unsigned_short v;
if (buf_getn (buf, &v, sizeof (v)))
return newSViv(v);
else
return NULL;
}
static SV *
get_ulong (GIOPRecvBuffer *buf)
{
CORBA_unsigned_long v;
if (buf_getn (buf, &v, sizeof (v))) {
SV *sv = newSV(0);
sv_setuv (sv, v);
return sv;
} else
return NULL;
}
static SV *
get_float (GIOPRecvBuffer *buf)
{
CORBA_float v;
/* FIXME: typical ORBit float/double breakage
*/
if (buf_getn (buf, &v, sizeof (v)))
return newSVnv((double)v);
else
return NULL;
}
static SV *
get_double (GIOPRecvBuffer *buf)
{
CORBA_double v;
/* FIXME: typical ORBit float/double breakage
*/
if (buf_getn (buf, &v, sizeof (v)))
return newSVnv(v);
else
return NULL;
}
static SV *
get_boolean (GIOPRecvBuffer *buf)
{
CORBA_octet v;
if (buf_getn (buf, &v, sizeof (v)))
return newSVsv(v?&PL_sv_yes:&PL_sv_no);
else
return NULL;
}
static SV *
get_char (GIOPRecvBuffer *buf)
{
CORBA_char v;
if (buf_getn (buf, &v, sizeof (v)))
return newSVpv((char *)&v,1);
else
return NULL;
}
static SV *
get_octet (GIOPRecvBuffer *buf)
{
CORBA_octet v;
if (buf_getn (buf, &v, sizeof (v)))
return newSViv(v);
else
return NULL;
}
static SV *
get_longlong (GIOPRecvBuffer *buf)
{
CORBA_long_long v;
if (buf_getn (buf, &v, sizeof (v)))
return porbit_ll_from_longlong (v);
else
return NULL;
}
static SV *
get_ulonglong (GIOPRecvBuffer *buf)
{
CORBA_unsigned_long_long v;
if (buf_getn (buf, &v, sizeof (v)))
return porbit_ull_from_ulonglong (v);
else
return NULL;
}
static SV *
get_longdouble (GIOPRecvBuffer *buf)
{
CORBA_long_double v;
/* FIXME: typical ORBit float/double breakage
*/
if (buf_getn (buf, &v, sizeof (v)))
return porbit_ld_from_longdouble (v);
else
return NULL;
}
static SV *
get_enum (GIOPRecvBuffer *buf, CORBA_TypeCode tc)
{
CORBA_unsigned_long v;
if (!buf_getn (buf, &v, sizeof (v)))
return NULL;
if (v > tc->sub_parts) {
warn ("enumeration received with invalid value");
return NULL;
}
return newSVpv((char *)tc->subnames[v], 0);
}
static SV *
get_struct (GIOPRecvBuffer *buf, CORBA_TypeCode tc)
{
CORBA_unsigned_long i;
HV *hv = newHV();
for (i = 0; i < tc->sub_parts; i++) {
SV *val = porbit_get_sv (buf, tc->subtypes[i]);
if (!val)
goto error;
hv_store (hv, (char *)tc->subnames[i], strlen(tc->subnames[i]), val, 0);
}
return newRV_noinc((SV *)hv);
error:
hv_undef (hv);
return NULL;
}
static SV *
get_sequence (GIOPRecvBuffer *buf, CORBA_TypeCode tc)
{
CORBA_unsigned_long len, i;
char *strbuf;
SV *res;
/* FIXME: Check the length of the typecode
*/
if (!buf_getn (buf, &len, sizeof (len)))
return NULL;
if (tc->subtypes[0]->kind == CORBA_tk_octet ||
tc->subtypes[0]->kind == CORBA_tk_char) {
CORBA_long left = RECV_BUFFER_LEFT (buf);
if (left < 0 || (CORBA_unsigned_long)left < len) {
warn ("incomplete message received");
return NULL;
}
res = newSV(len+1);
SvCUR_set(res, len);
SvPOK_on (res);
strbuf = SvPVX(res);
memcpy (strbuf, buf->cur, len);
buf->cur = ((guchar *)buf->cur) + len;
/* NULL terminate it, just to be safe.
*/
strbuf[len] = '\0';
} else {
AV *av = newAV();
av_extend(av, len);
res = newRV_noinc((SV *)av);
for (i = 0; i < len; i++) {
SV *elem = porbit_get_sv (buf, tc->subtypes[0]);
if (!elem)
goto error;
av_store (av, i, elem);
}
}
return res;
error:
SvREFCNT_dec (res);
return NULL;
}
static SV *
get_array (GIOPRecvBuffer *buf, CORBA_TypeCode tc)
{
CORBA_unsigned_long i;
SV *res;
AV *av;
av = newAV();
av_extend(av, tc->length);
res = newRV_noinc((SV *)av);
for (i = 0; i < tc->length; i++) {
SV *elem = porbit_get_sv (buf, tc->subtypes[0]);
if (!elem)
goto error;
av_store (av, i, elem);
}
return res;
error:
SvREFCNT_dec (res);
return NULL;
}
SV *
porbit_get_exception (GIOPRecvBuffer *buf, CORBA_TypeCode tc,
CORBA_exception_type type,
CORBA_OperationDescription *opr)
{
CORBA_unsigned_long str_len, minor, completion_status;
char *repoid;
AV *av;
g_return_val_if_fail (type != CORBA_NO_EXCEPTION, NULL);
/* Get the repoid
*/
if (!buf_getn (buf, &str_len, sizeof (str_len)))
return NULL;
if (RECV_BUFFER_LEFT (buf) < str_len) {
warn ("incomplete message received");
return NULL;
}
if (*((char *)buf->cur + str_len - 1) != '\0') {
warn ("Unterminated repository ID in exception");
return NULL;
}
repoid = (char *)buf->cur;
buf->cur = (guchar *)buf->cur + str_len;
if (type == CORBA_USER_EXCEPTION) {
CORBA_unsigned_long i;
if (!tc && opr) {
for (i=0; i < opr->exceptions._length; i++) {
if (strcmp (opr->exceptions._buffer[i].id, repoid) == 0) {
tc = opr->exceptions._buffer[i].type;
break;
}
}
}
if (!tc) {
warn ("Unknown exception of type '%s' received", repoid);
return porbit_system_except ("IDL:omg.org/CORBA/UNKNOWN:1.0",
0, CORBA_COMPLETED_MAYBE);
}
av = newAV();
for (i = 0; i < tc->sub_parts; i++) {
SV *val = porbit_get_sv (buf, tc->subtypes[i]);
if (!val) {
av_undef (av);
return NULL;
}
av_push (av, newSVpv(tc->subnames[i], 0));
av_push (av, val);
}
return porbit_user_except (repoid, newRV_noinc((SV *)av));
} else {
/* System exception */
/* HACK: Older ORBit versions are buggy and omit the minor */
buf->cur = ALIGN_ADDRESS(buf->cur, sizeof(&minor));
if (RECV_BUFFER_LEFT(buf) >= sizeof(&completion_status) &&
RECV_BUFFER_LEFT(buf) < sizeof(&minor) + sizeof(&completion_status)) {
minor = 0;
} else {
if (!buf_getn (buf, &minor, sizeof (&minor))) {
warn ("Error demarshalling system exception");
return NULL;
}
}
if (!buf_getn (buf, &completion_status, sizeof (&completion_status))) {
warn ("Error demarshalling system exception");
return NULL;
}
return porbit_system_except (repoid, minor, completion_status);
}
}
static SV *
get_objref (GIOPRecvBuffer *buf, CORBA_TypeCode tc)
{
CORBA_Object obj = ORBit_demarshal_object(buf, porbit_orb);
/* FIXME: Check type against tc? Would be expensive.
*/
return porbit_objref_to_sv (obj);
}
static SV *
get_union (GIOPRecvBuffer *buf, CORBA_TypeCode tc)
{
AV *av;
SV *discriminator;
CORBA_long arm;
discriminator = porbit_get_sv (buf, tc->discriminator);
if (!discriminator)
return NULL;
av = newAV();
av_push (av, discriminator);
arm = porbit_union_find_arm (tc, discriminator);
if (arm >= 0) {
SV *res = porbit_get_sv (buf, tc->subtypes[arm]);
if (!res)
goto error;
av_push (av,res);
} else {
av_push (av, newSVsv(&PL_sv_undef));
}
return newRV_noinc((SV *)av);
error:
av_undef (av);
return NULL;
}
static SV *
get_any (GIOPRecvBuffer *buf, CORBA_TypeCode tc)
{
AV *av;
CORBA_TypeCode res_tc;
SV *temp, *value, *result;
HV *stash;
ORBit_decode_CORBA_TypeCode(&res_tc, buf);
av = newAV();
temp = newSV(0);
av_push (av, sv_setref_pv (temp, "CORBA::TypeCode", (void *)res_tc));
value = porbit_get_sv (buf, res_tc);
if (!value) {
av_undef (av);
return NULL;
}
av_push (av, value);
result = newRV_noinc((SV *)av);
stash = gv_stashpv("CORBA::Any", TRUE);
return sv_bless (result, stash);
}
static SV *
get_alias (GIOPRecvBuffer *buf, CORBA_TypeCode tc)
{
return porbit_get_sv (buf, tc->subtypes[0]);
}
static SV *
get_string (GIOPRecvBuffer *buf, CORBA_TypeCode tc)
{
char *strbuf;
SV *res = NULL;
CORBA_unsigned_long len;
if (!buf_getn (buf, &len, sizeof (len)))
return NULL;
if (len == 0) {
warn ("string received with illegal 0 length");
return NULL;
}
if (tc->length != 0 && len-1 > tc->length) {
warn ("string received is longer than typecode allows");
return NULL;
}
if (RECV_BUFFER_LEFT (buf) < len) {
warn ("incomplete message received");
return NULL;
}
res = newSV(len);
SvCUR_set(res, len-1);
SvPOK_on (res);
strbuf = SvPVX(res);
memcpy (strbuf, buf->cur, len);
buf->cur = (guchar *)buf->cur + len;
/* This should already be a NULL according to the spec
* but we'll play it safe here.
*/
strbuf[len-1] = '\0';
return res;
}
static SV *
get_fixed (GIOPRecvBuffer *buf, CORBA_TypeCode tc)
{
int wire_length = (tc->digits + 2) / 2;
SV *digits_sv;
int index, i, count;
/* If we have an even number of digits, first half-octet is 0 */
gboolean offset = (tc->digits % 2 == 0);
dSP;
if (RECV_BUFFER_LEFT (buf) < wire_length) {
warn ("incomplete message received");
return NULL;
}
digits_sv = newSV(tc->digits+1);
SvCUR_set (digits_sv, tc->digits+1);
SvPOK_on(digits_sv);
index = 1;
for (i = 0; i < wire_length; i++) {
CORBA_octet c = *(CORBA_octet *)buf->cur;
buf->cur = (guchar *)buf->cur + 1;
if (!(i == 0 && offset))
SvPVX(digits_sv)[index++] = '0' + ((c & 0xf0) >> 4);
if (i == wire_length - 1)
SvPVX(digits_sv)[0] = ((c & 0xf) == 0xd) ? '-' : '+';
else
SvPVX(digits_sv)[index++] = '0' + (c & 0xf);
}
PUSHMARK(sp);
XPUSHs (sv_2mortal (newSVpv ("CORBA::Fixed", 0)));
XPUSHs (sv_2mortal (digits_sv));
XPUSHs (sv_2mortal (newSViv(tc->scale)));
PUTBACK;
count = perl_call_method("new", G_SCALAR);
SPAGAIN;
if (count != 1) {
warn ("CORBA::Fixed::new returned %d items", count);
while (count--)
(void)POPs;
return NULL;
}
PUTBACK;
return newSVsv(POPs);
}
static SV *
get_typecode (GIOPRecvBuffer *buf, CORBA_TypeCode tc)
{
CORBA_TypeCode res_tc;
SV *res;
ORBit_decode_CORBA_TypeCode(&res_tc, buf);
CORBA_Object_duplicate((CORBA_Object)res_tc, NULL);
res = newSV(0);
return sv_setref_pv (res, "CORBA::TypeCode", (void *)res_tc);
}
SV *
porbit_get_sv (GIOPRecvBuffer *buf, CORBA_TypeCode tc)
{
switch (tc->kind) {
case CORBA_tk_null:
return newSVsv(&PL_sv_undef);
case CORBA_tk_void:
return NULL;
case CORBA_tk_short:
return get_short (buf);
case CORBA_tk_long:
return get_long (buf);
case CORBA_tk_ushort:
return get_ushort (buf);
case CORBA_tk_ulong:
return get_ulong (buf);
case CORBA_tk_float:
return get_float (buf);
case CORBA_tk_double:
return get_double (buf);
case CORBA_tk_char:
return get_char (buf);
case CORBA_tk_boolean:
return get_boolean (buf);
case CORBA_tk_octet:
return get_octet (buf);
case CORBA_tk_struct:
return get_struct (buf, tc);
case CORBA_tk_except:
/* This should never be hit, but we implement it just in case
*/
return porbit_get_exception (buf, tc, CORBA_USER_EXCEPTION, NULL);
case CORBA_tk_objref:
return get_objref (buf, tc);
case CORBA_tk_enum:
return get_enum (buf, tc);
case CORBA_tk_sequence:
return get_sequence (buf, tc);
case CORBA_tk_union:
return get_union (buf, tc);
case CORBA_tk_alias:
return get_alias (buf, tc);
case CORBA_tk_string:
return get_string (buf, tc);
case CORBA_tk_array:
return get_array (buf, tc);
case CORBA_tk_longlong:
return get_longlong (buf);
case CORBA_tk_ulonglong:
return get_ulonglong (buf);
case CORBA_tk_longdouble:
return get_longdouble (buf);
case CORBA_tk_TypeCode:
return get_typecode (buf, tc);
case CORBA_tk_any:
return get_any (buf, tc);
case CORBA_tk_fixed:
return get_fixed (buf, tc);
case CORBA_tk_wchar:
case CORBA_tk_wstring:
case CORBA_tk_Principal:
default:
warn ("Unsupported input typecode %d\n", tc->kind);
return NULL;
}
}