Commit 0fab1a6f authored by Lev Walkin's avatar Lev Walkin

relaxed XER processing rules for whitespace

parent 9a0f8fad
0.9.12: 2005-Mar-05 0.9.12: 2005-Mar-09
* Fixed a name clash in produced constraint checking code. * Fixed a name clash in produced constraint checking code.
* #includes are now in single quotes (to solve a name * #includes are now in single quotes (to solve a name
clash with system's <time.h> on a Win32 system). clash with system's <time.h> on a Win32 system).
* Small refinement of XML DTD generation (`asn1c -X`).
* Relaxed XER processing rules to skip extra whitespace
in some more places. It also skips XML comments (although
prohibited by X.693, #8.2.1).
(Test case 70) (Severity: medium, Security impact: none)
Reported by <Dominique.Nerriec@alcatel.fr>.
0.9.11: 2005-Mar-04 0.9.11: 2005-Mar-04
...@@ -53,7 +59,7 @@ ...@@ -53,7 +59,7 @@
identifiers (./tests/68-*-OK.asn1). identifiers (./tests/68-*-OK.asn1).
* ber_dec_rval_t renamed into asn_dec_rval_t: more generality. * ber_dec_rval_t renamed into asn_dec_rval_t: more generality.
* Extensions in CHOICE types are properly marked as non-pointers * Extensions in CHOICE types are properly marked as non-pointers
(Test case 59) (Severity: medium, Secruity impact: medium) (Test case 59) (Severity: medium, Security impact: medium)
Reported by <roman.pfender@sdm.de>. Reported by <roman.pfender@sdm.de>.
* Tagged CHOICE type is now supported again. * Tagged CHOICE type is now supported again.
(Test case 59) (Severity: low, Security impact: low) (Test case 59) (Severity: low, Security impact: low)
...@@ -77,7 +83,7 @@ ...@@ -77,7 +83,7 @@
optionals-laden indefinite length structure. The code was previously optionals-laden indefinite length structure. The code was previously
refusing to parse such structures. refusing to parse such structures.
* Fixed explicitly tagged ANY type encoding and decoding * Fixed explicitly tagged ANY type encoding and decoding
(Severity: medium, Secruity impact: low). (Severity: medium, Security impact: low).
* Fixed CHOICE code spin when indefinite length structures appear * Fixed CHOICE code spin when indefinite length structures appear
in the extensions (Severity: medium, Security impact: medium). in the extensions (Severity: medium, Security impact: medium).
Reported by <siden@ul-gsm.ru>. Reported by <siden@ul-gsm.ru>.
...@@ -95,7 +101,7 @@ ...@@ -95,7 +101,7 @@
* Added -fno-constraints option to asn1c, which disabled generation of * Added -fno-constraints option to asn1c, which disabled generation of
ASN.1 subtype constraints checking code. ASN.1 subtype constraints checking code.
* Added ASN1C_ENVIRONMENT_VERSION and get_asn1c_environment_version(). * Added ASN1C_ENVIRONMENT_VERSION and get_asn1c_environment_version().
* Fixed ANY type decoding (Severity: high, Secruity impact: low). * Fixed ANY type decoding (Severity: high, Security impact: low).
* Fixed BER decoder restartability problem with certain primitive * Fixed BER decoder restartability problem with certain primitive
types (BOOLEAN, INTEGER, and REAL). The problem occured when the types (BOOLEAN, INTEGER, and REAL). The problem occured when the
encoding of such type is split between several bytes. encoding of such type is split between several bytes.
......
...@@ -133,16 +133,14 @@ BOOLEAN_encode_der(asn_TYPE_descriptor_t *td, void *sptr, ...@@ -133,16 +133,14 @@ BOOLEAN_encode_der(asn_TYPE_descriptor_t *td, void *sptr,
/* /*
* Decode the chunk of XML text encoding INTEGER. * Decode the chunk of XML text encoding INTEGER.
*/ */
static ssize_t static enum xer_pbd_rval
BOOLEAN__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf, size_t chunk_size) { BOOLEAN__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
BOOLEAN_t *st = (BOOLEAN_t *)sptr; BOOLEAN_t *st = (BOOLEAN_t *)sptr;
char *p = (char *)chunk_buf; const char *p = (const char *)chunk_buf;
(void)td; (void)td;
if(chunk_size == 0) return -1; if(chunk_size && p[0] == 0x3c /* '<' */) {
if(p[0] == 0x3c /* '<' */) {
switch(xer_check_tag(chunk_buf, chunk_size, "false")) { switch(xer_check_tag(chunk_buf, chunk_size, "false")) {
case XCT_BOTH: case XCT_BOTH:
/* "<false/>" */ /* "<false/>" */
...@@ -151,19 +149,20 @@ BOOLEAN__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf, ...@@ -151,19 +149,20 @@ BOOLEAN__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf,
case XCT_UNKNOWN_BO: case XCT_UNKNOWN_BO:
if(xer_check_tag(chunk_buf, chunk_size, "true") if(xer_check_tag(chunk_buf, chunk_size, "true")
!= XCT_BOTH) != XCT_BOTH)
return -1; return XPBD_BROKEN_ENCODING;
/* "<true/>" */ /* "<true/>" */
*st = 1; /* Or 0xff as in DER?.. */ *st = 1; /* Or 0xff as in DER?.. */
break; break;
default: default:
return -1; return XPBD_BROKEN_ENCODING;
} }
return XPBD_BODY_CONSUMED;
} else { } else {
if(!xer_is_whitespace(chunk_buf, chunk_size)) if(xer_is_whitespace(chunk_buf, chunk_size))
return -1; return XPBD_NOT_BODY_IGNORE;
else
return XPBD_BROKEN_ENCODING;
} }
return chunk_size;
} }
......
...@@ -299,8 +299,8 @@ INTEGER__map_value2enum(asn_INTEGER_specifics_t *specs, long value) { ...@@ -299,8 +299,8 @@ INTEGER__map_value2enum(asn_INTEGER_specifics_t *specs, long value) {
/* /*
* Decode the chunk of XML text encoding INTEGER. * Decode the chunk of XML text encoding INTEGER.
*/ */
static ssize_t static enum xer_pbd_rval
INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf, size_t chunk_size) { INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
INTEGER_t *st = (INTEGER_t *)sptr; INTEGER_t *st = (INTEGER_t *)sptr;
long sign = 1; long sign = 1;
long value; long value;
...@@ -345,7 +345,7 @@ INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf, ...@@ -345,7 +345,7 @@ INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf,
if(new_value / 10 != value) if(new_value / 10 != value)
/* Overflow */ /* Overflow */
return -1; return XPBD_DECODER_LIMIT;
value = new_value + (lv - 0x30); value = new_value + (lv - 0x30);
/* Check for two's complement overflow */ /* Check for two's complement overflow */
...@@ -357,7 +357,7 @@ INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf, ...@@ -357,7 +357,7 @@ INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf,
sign = 1; sign = 1;
} else { } else {
/* Overflow */ /* Overflow */
return -1; return XPBD_DECODER_LIMIT;
} }
} }
} }
...@@ -377,20 +377,26 @@ INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf, ...@@ -377,20 +377,26 @@ INTEGER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf,
} }
ASN_DEBUG("Unknown identifier for INTEGER"); ASN_DEBUG("Unknown identifier for INTEGER");
} }
return -1; return XPBD_BROKEN_ENCODING;
} }
break; break;
} }
if(state != ST_DIGITS) if(state != ST_DIGITS) {
return -1; /* No digits */ if(xer_is_whitespace(chunk_buf, chunk_size)) {
return XPBD_NOT_BODY_IGNORE;
} else {
ASN_DEBUG("No useful digits in output");
return XPBD_BROKEN_ENCODING; /* No digits */
}
}
value *= sign; /* Change sign, if needed */ value *= sign; /* Change sign, if needed */
if(asn_long2INTEGER(st, value)) if(asn_long2INTEGER(st, value))
return -1; return XPBD_SYSTEM_FAILURE;
return lp - lstart; return XPBD_BODY_CONSUMED;
} }
asn_dec_rval_t asn_dec_rval_t
......
...@@ -67,13 +67,15 @@ NULL_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, ...@@ -67,13 +67,15 @@ NULL_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
} }
static ssize_t static enum xer_pbd_rval
NULL__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf, size_t chunk_size) { NULL__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
(void)td; (void)td;
(void)sptr; (void)sptr;
if(xer_is_whitespace(chunk_buf, chunk_size)) if(xer_is_whitespace(chunk_buf, chunk_size))
return chunk_size; return XPBD_BODY_CONSUMED;
return -1; else
return XPBD_BROKEN_ENCODING;
} }
asn_dec_rval_t asn_dec_rval_t
......
...@@ -263,8 +263,8 @@ OBJECT_IDENTIFIER__dump_body(const OBJECT_IDENTIFIER_t *st, asn_app_consume_byte ...@@ -263,8 +263,8 @@ OBJECT_IDENTIFIER__dump_body(const OBJECT_IDENTIFIER_t *st, asn_app_consume_byte
return wrote_len; return wrote_len;
} }
static ssize_t static enum xer_pbd_rval
OBJECT_IDENTIFIER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf, size_t chunk_size) { OBJECT_IDENTIFIER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
OBJECT_IDENTIFIER_t *st = (OBJECT_IDENTIFIER_t *)sptr; OBJECT_IDENTIFIER_t *st = (OBJECT_IDENTIFIER_t *)sptr;
char *endptr; char *endptr;
long s_arcs[10]; long s_arcs[10];
...@@ -275,27 +275,31 @@ OBJECT_IDENTIFIER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void * ...@@ -275,27 +275,31 @@ OBJECT_IDENTIFIER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *
(void)td; (void)td;
arcs_count = OBJECT_IDENTIFIER_parse_arcs( arcs_count = OBJECT_IDENTIFIER_parse_arcs(
(const char *)chunk_buf, chunk_size, arcs, 10, &endptr); (const char *)chunk_buf, chunk_size, arcs,
if(arcs_count <= 0) sizeof(s_arcs)/sizeof(s_arcs[0]), &endptr);
return -1; /* Expecting more than zero arcs */ if(arcs_count <= 0) {
if(arcs_count > 10) { /* Expecting more than zero arcs */
return XPBD_BROKEN_ENCODING;
}
if((size_t)arcs_count > sizeof(s_arcs)/sizeof(s_arcs[0])) {
arcs = (long *)MALLOC(arcs_count * sizeof(long)); arcs = (long *)MALLOC(arcs_count * sizeof(long));
if(!arcs) return -1; if(!arcs) return XPBD_SYSTEM_FAILURE;
ret = OBJECT_IDENTIFIER_parse_arcs( ret = OBJECT_IDENTIFIER_parse_arcs(
(const char *)chunk_buf, chunk_size, (const char *)chunk_buf, chunk_size,
arcs, arcs_count, &endptr); arcs, arcs_count, &endptr);
if(ret != arcs_count) if(ret != arcs_count)
return -1; /* assert?.. */ return XPBD_SYSTEM_FAILURE; /* assert?.. */
} }
/* /*
* Convert arcs into BER representation. * Convert arcs into BER representation.
*/ */
ret = OBJECT_IDENTIFIER_set_arcs(st, arcs, sizeof(*arcs), arcs_count); ret = OBJECT_IDENTIFIER_set_arcs(st, arcs, sizeof(*arcs), arcs_count);
if(ret) return -1; if(ret) return XPBD_BROKEN_ENCODING;
if(arcs != s_arcs) FREEMEM(arcs); if(arcs != s_arcs) FREEMEM(arcs);
return endptr - (char *)chunk_buf; return XPBD_BODY_CONSUMED;
} }
asn_dec_rval_t asn_dec_rval_t
......
...@@ -691,7 +691,7 @@ static struct OCTET_STRING__xer_escape_table_s { ...@@ -691,7 +691,7 @@ static struct OCTET_STRING__xer_escape_table_s {
}; };
static int static int
OS__check_escaped_control_char(void *buf, int size) { OS__check_escaped_control_char(const void *buf, int size) {
size_t i; size_t i;
/* /*
* Inefficient algorithm which translates the escape sequences * Inefficient algorithm which translates the escape sequences
...@@ -709,7 +709,7 @@ OS__check_escaped_control_char(void *buf, int size) { ...@@ -709,7 +709,7 @@ OS__check_escaped_control_char(void *buf, int size) {
} }
static int static int
OCTET_STRING__handle_control_chars(void *struct_ptr, void *chunk_buf, size_t chunk_size) { OCTET_STRING__handle_control_chars(void *struct_ptr, const void *chunk_buf, size_t chunk_size) {
/* /*
* This might be one of the escape sequences * This might be one of the escape sequences
* for control characters. Check it out. * for control characters. Check it out.
...@@ -778,11 +778,11 @@ OCTET_STRING_encode_xer_utf8(asn_TYPE_descriptor_t *td, void *sptr, ...@@ -778,11 +778,11 @@ OCTET_STRING_encode_xer_utf8(asn_TYPE_descriptor_t *td, void *sptr,
/* /*
* Convert from hexadecimal format (cstring): "AB CD EF" * Convert from hexadecimal format (cstring): "AB CD EF"
*/ */
static ssize_t OCTET_STRING__convert_hexadecimal(void *sptr, void *chunk_buf, size_t chunk_size, int have_more) { static ssize_t OCTET_STRING__convert_hexadecimal(void *sptr, const void *chunk_buf, size_t chunk_size, int have_more) {
OCTET_STRING_t *st = (OCTET_STRING_t *)sptr; OCTET_STRING_t *st = (OCTET_STRING_t *)sptr;
char *chunk_stop = (char *)chunk_buf; const char *chunk_stop = (const char *)chunk_buf;
char *p = chunk_stop; const char *p = chunk_stop;
char *pend = p + chunk_size; const char *pend = p + chunk_size;
unsigned int clv = 0; unsigned int clv = 0;
int half = 0; /* Half bit */ int half = 0; /* Half bit */
uint8_t *buf; uint8_t *buf;
...@@ -800,7 +800,7 @@ static ssize_t OCTET_STRING__convert_hexadecimal(void *sptr, void *chunk_buf, si ...@@ -800,7 +800,7 @@ static ssize_t OCTET_STRING__convert_hexadecimal(void *sptr, void *chunk_buf, si
* than chunk_size, then it'll be equivalent to "ABC0". * than chunk_size, then it'll be equivalent to "ABC0".
*/ */
for(; p < pend; p++) { for(; p < pend; p++) {
int ch = *(unsigned char *)p; int ch = *(const unsigned char *)p;
switch(ch) { switch(ch) {
case 0x09: case 0x0a: case 0x0c: case 0x0d: case 0x09: case 0x0a: case 0x0c: case 0x0d:
case 0x20: case 0x20:
...@@ -849,16 +849,16 @@ static ssize_t OCTET_STRING__convert_hexadecimal(void *sptr, void *chunk_buf, si ...@@ -849,16 +849,16 @@ static ssize_t OCTET_STRING__convert_hexadecimal(void *sptr, void *chunk_buf, si
assert(st->size <= _ns); assert(st->size <= _ns);
st->buf[st->size] = 0; /* Courtesy termination */ st->buf[st->size] = 0; /* Courtesy termination */
return (chunk_stop - (char *)chunk_buf); /* Converted size */ return (chunk_stop - (const char *)chunk_buf); /* Converted size */
} }
/* /*
* Convert from binary format: "00101011101" * Convert from binary format: "00101011101"
*/ */
static ssize_t OCTET_STRING__convert_binary(void *sptr, void *chunk_buf, size_t chunk_size, int have_more) { static ssize_t OCTET_STRING__convert_binary(void *sptr, const void *chunk_buf, size_t chunk_size, int have_more) {
BIT_STRING_t *st = (BIT_STRING_t *)sptr; BIT_STRING_t *st = (BIT_STRING_t *)sptr;
char *p = (char *)chunk_buf; const char *p = (const char *)chunk_buf;
char *pend = p + chunk_size; const char *pend = p + chunk_size;
int bits_unused = st->bits_unused & 0x7; int bits_unused = st->bits_unused & 0x7;
uint8_t *buf; uint8_t *buf;
...@@ -880,7 +880,7 @@ static ssize_t OCTET_STRING__convert_binary(void *sptr, void *chunk_buf, size_t ...@@ -880,7 +880,7 @@ static ssize_t OCTET_STRING__convert_binary(void *sptr, void *chunk_buf, size_t
* Convert series of 0 and 1 into the octet string. * Convert series of 0 and 1 into the octet string.
*/ */
for(; p < pend; p++) { for(; p < pend; p++) {
int ch = *(unsigned char *)p; int ch = *(const unsigned char *)p;
switch(ch) { switch(ch) {
case 0x09: case 0x0a: case 0x0c: case 0x0d: case 0x09: case 0x0a: case 0x0c: case 0x0d:
case 0x20: case 0x20:
...@@ -918,9 +918,9 @@ static ssize_t OCTET_STRING__convert_binary(void *sptr, void *chunk_buf, size_t ...@@ -918,9 +918,9 @@ static ssize_t OCTET_STRING__convert_binary(void *sptr, void *chunk_buf, size_t
* Something like strtod(), but with stricter rules. * Something like strtod(), but with stricter rules.
*/ */
static int static int
OS__strtoent(int base, char *buf, char *end, int32_t *return_value) { OS__strtoent(int base, const char *buf, const char *end, int32_t *ret_value) {
int32_t val = 0; int32_t val = 0;
char *p; const char *p;
for(p = buf; p < end; p++) { for(p = buf; p < end; p++) {
int ch = *p; int ch = *p;
...@@ -939,7 +939,7 @@ OS__strtoent(int base, char *buf, char *end, int32_t *return_value) { ...@@ -939,7 +939,7 @@ OS__strtoent(int base, char *buf, char *end, int32_t *return_value) {
val = val * base + (ch - 0x61 + 10); val = val * base + (ch - 0x61 + 10);
break; break;
case 0x3b: /* ';' */ case 0x3b: /* ';' */
*return_value = val; *ret_value = val;
return (p - buf) + 1; return (p - buf) + 1;
default: default:
return -1; /* Character set error */ return -1; /* Character set error */
...@@ -953,10 +953,10 @@ OS__strtoent(int base, char *buf, char *end, int32_t *return_value) { ...@@ -953,10 +953,10 @@ OS__strtoent(int base, char *buf, char *end, int32_t *return_value) {
/* /*
* Convert from the plain UTF-8 format, expanding entity references: "2 &lt; 3" * Convert from the plain UTF-8 format, expanding entity references: "2 &lt; 3"
*/ */
static ssize_t OCTET_STRING__convert_entrefs(void *sptr, void *chunk_buf, size_t chunk_size, int have_more) { static ssize_t OCTET_STRING__convert_entrefs(void *sptr, const void *chunk_buf, size_t chunk_size, int have_more) {
OCTET_STRING_t *st = (OCTET_STRING_t *)sptr; OCTET_STRING_t *st = (OCTET_STRING_t *)sptr;
char *p = (char *)chunk_buf; const char *p = (const char *)chunk_buf;
char *pend = p + chunk_size; const char *pend = p + chunk_size;
uint8_t *buf; uint8_t *buf;
/* Reallocate buffer */ /* Reallocate buffer */
...@@ -970,7 +970,7 @@ static ssize_t OCTET_STRING__convert_entrefs(void *sptr, void *chunk_buf, size_t ...@@ -970,7 +970,7 @@ static ssize_t OCTET_STRING__convert_entrefs(void *sptr, void *chunk_buf, size_t
* Convert series of 0 and 1 into the octet string. * Convert series of 0 and 1 into the octet string.
*/ */
for(; p < pend; p++) { for(; p < pend; p++) {
int ch = *(unsigned char *)p; int ch = *(const unsigned char *)p;
int len; /* Length of the rest of the chunk */ int len; /* Length of the rest of the chunk */
if(ch != 0x26 /* '&' */) { if(ch != 0x26 /* '&' */) {
...@@ -981,10 +981,10 @@ static ssize_t OCTET_STRING__convert_entrefs(void *sptr, void *chunk_buf, size_t ...@@ -981,10 +981,10 @@ static ssize_t OCTET_STRING__convert_entrefs(void *sptr, void *chunk_buf, size_t
/* /*
* Process entity reference. * Process entity reference.
*/ */
len = chunk_size - (p - (char *)chunk_buf); len = chunk_size - (p - (const char *)chunk_buf);
if(len == 1 /* "&" */) goto want_more; if(len == 1 /* "&" */) goto want_more;
if(p[1] == 0x23 /* '#' */) { if(p[1] == 0x23 /* '#' */) {
char *pval; /* Pointer to start of digits */ const char *pval; /* Pointer to start of digits */
int32_t val; /* Entity reference value */ int32_t val; /* Entity reference value */
int base; int base;
...@@ -1078,7 +1078,7 @@ static ssize_t OCTET_STRING__convert_entrefs(void *sptr, void *chunk_buf, size_t ...@@ -1078,7 +1078,7 @@ static ssize_t OCTET_STRING__convert_entrefs(void *sptr, void *chunk_buf, size_t
*buf++ = ch; *buf++ = ch;
continue; continue;
} }
chunk_size = (p - (char *)chunk_buf); chunk_size = (p - (const char *)chunk_buf);
/* Processing stalled: need more data */ /* Processing stalled: need more data */
break; break;
} }
...@@ -1098,9 +1098,9 @@ OCTET_STRING__decode_xer(asn_codec_ctx_t *opt_codec_ctx, ...@@ -1098,9 +1098,9 @@ OCTET_STRING__decode_xer(asn_codec_ctx_t *opt_codec_ctx,
asn_TYPE_descriptor_t *td, void **sptr, asn_TYPE_descriptor_t *td, void **sptr,
const char *opt_mname, void *buf_ptr, size_t size, const char *opt_mname, void *buf_ptr, size_t size,
int (*opt_unexpected_tag_decoder) int (*opt_unexpected_tag_decoder)
(void *struct_ptr, void *chunk_buf, size_t chunk_size), (void *struct_ptr, const void *chunk_buf, size_t chunk_size),
ssize_t (*body_receiver) ssize_t (*body_receiver)
(void *struct_ptr, void *chunk_buf, size_t chunk_size, (void *struct_ptr, const void *chunk_buf, size_t chunk_size,
int have_more) int have_more)
) { ) {
OCTET_STRING_t *st = (OCTET_STRING_t *)*sptr; OCTET_STRING_t *st = (OCTET_STRING_t *)*sptr;
......
...@@ -257,17 +257,17 @@ REAL_encode_xer(asn_TYPE_descriptor_t *td, void *sptr, ...@@ -257,17 +257,17 @@ REAL_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
/* /*
* Decode the chunk of XML text encoding REAL. * Decode the chunk of XML text encoding REAL.
*/ */
static ssize_t static enum xer_pbd_rval
REAL__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf, size_t chunk_size) { REAL__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
REAL_t *st = (REAL_t *)sptr; REAL_t *st = (REAL_t *)sptr;
double value; double value;
char *xerdata = (char *)chunk_buf; const char *xerdata = (const char *)chunk_buf;
char *endptr = 0; char *endptr = 0;
char *b; char *b;
(void)td; (void)td;
if(!chunk_size) return -1; if(!chunk_size) return XPBD_BROKEN_ENCODING;
/* /*
* Decode an XMLSpecialRealValue: <MINUS-INFINITY>, etc. * Decode an XMLSpecialRealValue: <MINUS-INFINITY>, etc.
...@@ -292,33 +292,34 @@ REAL__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf, si ...@@ -292,33 +292,34 @@ REAL__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf, si
case -1: dv = - INFINITY; break; case -1: dv = - INFINITY; break;
case 0: dv = NAN; break; case 0: dv = NAN; break;
case 1: dv = INFINITY; break; case 1: dv = INFINITY; break;
default: return -1; default: return XPBD_SYSTEM_FAILURE;
} }
if(asn_double2REAL(st, dv)) return -1; if(asn_double2REAL(st, dv))
return XPBD_SYSTEM_FAILURE;
return chunk_size; return XPBD_BODY_CONSUMED;
} }
ASN_DEBUG("Unknown XMLSpecialRealValue"); ASN_DEBUG("Unknown XMLSpecialRealValue");
return -1; return XPBD_BROKEN_ENCODING;
} }
/* /*
* Copy chunk into the nul-terminated string, and run strtod. * Copy chunk into the nul-terminated string, and run strtod.
*/ */
b = (char *)MALLOC(chunk_size + 1); b = (char *)MALLOC(chunk_size + 1);
if(!b) return -1; if(!b) return XPBD_SYSTEM_FAILURE;
memcpy(b, chunk_buf, chunk_size); memcpy(b, chunk_buf, chunk_size);
b[chunk_size] = 0; /* nul-terminate */ b[chunk_size] = 0; /* nul-terminate */
value = strtod(b, &endptr); value = strtod(b, &endptr);
free(b); free(b);
if(endptr == b) return -1; if(endptr == b) return XPBD_BROKEN_ENCODING;
if(asn_double2REAL(st, value)) if(asn_double2REAL(st, value))
return -1; return XPBD_SYSTEM_FAILURE;
return endptr - b; return XPBD_BODY_CONSUMED;
} }
asn_dec_rval_t asn_dec_rval_t
......
...@@ -86,8 +86,8 @@ RELATIVE_OID_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, ...@@ -86,8 +86,8 @@ RELATIVE_OID_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
return (cb(" }", 2, app_key) < 0) ? -1 : 0; return (cb(" }", 2, app_key) < 0) ? -1 : 0;
} }
static ssize_t static enum xer_pbd_rval
RELATIVE_OID__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk_buf, size_t chunk_size) { RELATIVE_OID__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
RELATIVE_OID_t *st = (RELATIVE_OID_t *)sptr; RELATIVE_OID_t *st = (RELATIVE_OID_t *)sptr;
char *endptr; char *endptr;
long s_arcs[6]; long s_arcs[6];
...@@ -99,27 +99,30 @@ RELATIVE_OID__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk ...@@ -99,27 +99,30 @@ RELATIVE_OID__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, void *chunk
arcs_count = OBJECT_IDENTIFIER_parse_arcs( arcs_count = OBJECT_IDENTIFIER_parse_arcs(
(const char *)chunk_buf, chunk_size, (const char *)chunk_buf, chunk_size,
arcs, 6, &endptr); arcs, sizeof(s_arcs)/sizeof(s_arcs[0]), &endptr);
if(arcs_count < 0) if(arcs_count < 0) {
return -1; /* Expecting at least zero arcs */ /* Expecting at least zero arcs */
if(arcs_count > 6) { return XPBD_BROKEN_ENCODING;
}
if((size_t)arcs_count > sizeof(s_arcs)/sizeof(s_arcs[0])) {
arcs = (long *)MALLOC(arcs_count * sizeof(long)); arcs = (long *)MALLOC(arcs_count * sizeof(long));
if(!arcs) return -1; if(!arcs) return XPBD_SYSTEM_FAILURE;
ret = OBJECT_IDENTIFIER_parse_arcs( ret = OBJECT_IDENTIFIER_parse_arcs(
(const char *)chunk_buf, chunk_size, (const char *)chunk_buf, chunk_size,
arcs, arcs_count, &endptr); arcs, arcs_count, &endptr);
if(ret != arcs_count) if(ret != arcs_count)
return -1; /* assert?.. */ return XPBD_SYSTEM_FAILURE; /* assert?.. */
} }
/* /*
* Convert arcs into BER representation. * Convert arcs into BER representation.
*/ */
ret = RELATIVE_OID_set_arcs(st, arcs, sizeof(*arcs), arcs_count); ret = RELATIVE_OID_set_arcs(st, arcs, sizeof(*arcs), arcs_count);
if(ret) return -1; if(ret) return XPBD_BROKEN_ENCODING;
if(arcs != s_arcs) FREEMEM(arcs); if(arcs != s_arcs) FREEMEM(arcs);
return endptr - (char *)chunk_buf; return XPBD_BODY_CONSUMED;
} }
asn_dec_rval_t asn_dec_rval_t
......
...@@ -148,17 +148,16 @@ ASN__PRIMITIVE_TYPE_free(asn_TYPE_descriptor_t *td, void *sptr, ...@@ -148,17 +148,16 @@ ASN__PRIMITIVE_TYPE_free(asn_TYPE_descriptor_t *td, void *sptr,
struct xdp_arg_s { struct xdp_arg_s {
asn_TYPE_descriptor_t *type_descriptor; asn_TYPE_descriptor_t *type_descriptor;
void *struct_key; void *struct_key;
ssize_t (*prim_body_decode)(asn_TYPE_descriptor_t *td, xer_primitive_body_decoder_f *prim_body_decoder;
void *struct_key, void *chunk_buf, size_t chunk_size);
int decoded_something; int decoded_something;
int want_more; int want_more;
}; };
static ssize_t static int
xer_decode__unexpected_tag(void *key, void *chunk_buf, size_t chunk_size) { xer_decode__unexpected_tag(void *key, const void *chunk_buf, size_t chunk_size) {
struct xdp_arg_s *arg = (struct xdp_arg_s *)key; struct xdp_arg_s *arg = (struct xdp_arg_s *)key;
ssize_t decoded; enum xer_pbd_rval bret;
if(arg->decoded_something) { if(arg->decoded_something) {
if(xer_is_whitespace(chunk_buf, chunk_size)) if(xer_is_whitespace(chunk_buf, chunk_size))
...@@ -169,21 +168,28 @@ xer_decode__unexpected_tag(void *key, void *chunk_buf, size_t chunk_size) { ...@@ -169,21 +168,28 @@ xer_decode__unexpected_tag(void *key, void *chunk_buf, size_t chunk_size) {
return -1; return -1;
} }
decoded = arg->prim_body_decode(arg->type_descriptor, bret = arg->prim_body_decoder(arg->type_descriptor,
arg->struct_key, chunk_buf, chunk_size); arg->struct_key, chunk_buf, chunk_size);
if(decoded < 0) { switch(bret) {
return -1; case XPBD_SYSTEM_FAILURE:
} else { case XPBD_DECODER_LIMIT:
case XPBD_BROKEN_ENCODING:
break;
case XPBD_BODY_CONSUMED:
/* Tag decoded successfully */ /* Tag decoded successfully */
arg->decoded_something = 1; arg->decoded_something = 1;
/* Fall through */
case XPBD_NOT_BODY_IGNORE: /* Safe to proceed further */
return 0; return 0;
} }
return -1;
} }
static ssize_t static ssize_t
xer_decode__body(void *key, void *chunk_buf, size_t chunk_size, int have_more) { xer_decode__body(void *key, const void *chunk_buf, size_t chunk_size, int have_more) {
struct xdp_arg_s *arg = (struct xdp_arg_s *)key; struct xdp_arg_s *arg = (struct xdp_arg_s *)key;
ssize_t decoded; enum xer_pbd_rval bret;
if(arg->decoded_something) { if(arg->decoded_something) {
if(xer_is_whitespace(chunk_buf, chunk_size)) if(xer_is_whitespace(chunk_buf, chunk_size))
...@@ -207,14 +213,22 @@ xer_decode__body(void *key, void *chunk_buf, size_t chunk_size, int have_more) { ...@@ -207,14 +213,22 @@ xer_decode__body(void *key, void *chunk_buf, size_t chunk_size, int have_more) {
return -1; return -1;
} }
decoded = arg->prim_body_decode(arg->type_descriptor, bret = arg->prim_body_decoder(arg->type_descriptor,
arg->struct_key, chunk_buf, chunk_size); arg->struct_key, chunk_buf, chunk_size);
if(decoded < 0) { switch(bret) {
return -1; case XPBD_SYSTEM_FAILURE:
} else { case XPBD_DECODER_LIMIT:
case XPBD_BROKEN_ENCODING:
break;
case XPBD_BODY_CONSUMED:
/* Tag decoded successfully */
arg->decoded_something = 1; arg->decoded_something = 1;
return decoded; /* Fall through */
case XPBD_NOT_BODY_IGNORE: /* Safe to proceed further */
return chunk_size;
} }
return -1;
} }
...@@ -225,8 +239,7 @@ xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx, ...@@ -225,8 +239,7 @@ xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx,
size_t struct_size, size_t struct_size,
const char *opt_mname, const char *opt_mname,
void *buf_ptr, size_t size, void *buf_ptr, size_t size,
ssize_t (*prim_body_decode)(asn_TYPE_descriptor_t *td, xer_primitive_body_decoder_f *prim_body_decoder
void *struct_key, void *chunk_buf, size_t chunk_size)
) { ) {
const char *xml_tag = opt_mname ? opt_mname : td->xml_tag; const char *xml_tag = opt_mname ? opt_mname : td->xml_tag;
asn_struct_ctx_t s_ctx; asn_struct_ctx_t s_ctx;
...@@ -249,7 +262,7 @@ xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx, ...@@ -249,7 +262,7 @@ xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx,
memset(&s_ctx, 0, sizeof(s_ctx)); memset(&s_ctx, 0, sizeof(s_ctx));
s_arg.type_descriptor = td; s_arg.type_descriptor = td;
s_arg.struct_key = *sptr; s_arg.struct_key = *sptr;
s_arg.prim_body_decode = prim_body_decode; s_arg.prim_body_decoder = prim_body_decoder;
s_arg.decoded_something = 0; s_arg.decoded_something = 0;
s_arg.want_more = 0; s_arg.want_more = 0;
...@@ -260,9 +273,16 @@ xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx, ...@@ -260,9 +273,16 @@ xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx,
case RC_OK: case RC_OK:
if(!s_arg.decoded_something) { if(!s_arg.decoded_something) {
char ch; char ch;
/* Opportunity has come and gone. Where's the result? */ ASN_DEBUG("Primitive body is not recognized, "
if(prim_body_decode(s_arg.type_descriptor, "supplying empty one");
s_arg.struct_key, &ch, 0) != 0) { /*
* Decoding opportunity has come and gone.
* Where's the result?
* Try to feed with empty body, see if it eats it.
*/
if(prim_body_decoder(s_arg.type_descriptor,
s_arg.struct_key, &ch, 0)
!= XPBD_BODY_CONSUMED) {
/* /*
* This decoder does not like empty stuff. * This decoder does not like empty stuff.
*/ */
......
...@@ -16,6 +16,20 @@ asn_struct_free_f ASN__PRIMITIVE_TYPE_free; ...@@ -16,6 +16,20 @@ asn_struct_free_f ASN__PRIMITIVE_TYPE_free;
ber_type_decoder_f ber_decode_primitive; ber_type_decoder_f ber_decode_primitive;
der_type_encoder_f der_encode_primitive; der_type_encoder_f der_encode_primitive;
/*
* A callback specification for the xer_decode_primitive() function below.
*/
enum xer_pbd_rval {
XPBD_SYSTEM_FAILURE, /* System failure (memory shortage, etc) */
XPBD_DECODER_LIMIT, /* Hit some decoder limitation or deficiency */
XPBD_BROKEN_ENCODING, /* Encoding of a primitive body is broken */
XPBD_NOT_BODY_IGNORE, /* Not a body format, but safe to ignore */
XPBD_BODY_CONSUMED, /* Body is recognized and consumed */
};
typedef enum xer_pbd_rval (xer_primitive_body_decoder_f)
(asn_TYPE_descriptor_t *td, void *struct_ptr,
const void *chunk_buf, size_t chunk_size);
/* /*
* Specific function to decode simple primitive types. * Specific function to decode simple primitive types.
* Also see xer_decode_general() in xer_decoder.h * Also see xer_decode_general() in xer_decoder.h
...@@ -25,8 +39,7 @@ asn_dec_rval_t xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx, ...@@ -25,8 +39,7 @@ asn_dec_rval_t xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx,
void **struct_ptr, size_t struct_size, void **struct_ptr, size_t struct_size,
const char *opt_mname, const char *opt_mname,
void *buf_ptr, size_t size, void *buf_ptr, size_t size,
ssize_t (*prim_body_decode)(asn_TYPE_descriptor_t *td, xer_primitive_body_decoder_f *prim_body_decoder
void *struct_ptr, void *chunk_buf, size_t chunk_size) );
);
#endif /* ASN_CODECS_PRIM_H */ #endif /* ASN_CODECS_PRIM_H */
...@@ -33,12 +33,12 @@ xer_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, ...@@ -33,12 +33,12 @@ xer_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
struct xer__cb_arg { struct xer__cb_arg {
pxml_chunk_type_e chunk_type; pxml_chunk_type_e chunk_type;
size_t chunk_size; size_t chunk_size;
void *chunk_buf; const void *chunk_buf;
int callback_not_invoked; int callback_not_invoked;
}; };
static int static int
xer__token_cb(pxml_chunk_type_e type, void *_chunk_data, size_t _chunk_size, void *key) { xer__token_cb(pxml_chunk_type_e type, const void *_chunk_data, size_t _chunk_size, void *key) {
struct xer__cb_arg *arg = (struct xer__cb_arg *)key; struct xer__cb_arg *arg = (struct xer__cb_arg *)key;
arg->chunk_type = type; arg->chunk_type = type;
arg->chunk_size = _chunk_size; arg->chunk_size = _chunk_size;
...@@ -51,7 +51,7 @@ xer__token_cb(pxml_chunk_type_e type, void *_chunk_data, size_t _chunk_size, voi ...@@ -51,7 +51,7 @@ xer__token_cb(pxml_chunk_type_e type, void *_chunk_data, size_t _chunk_size, voi
* Fetch the next token from the XER/XML stream. * Fetch the next token from the XER/XML stream.
*/ */
ssize_t ssize_t
xer_next_token(int *stateContext, void *buffer, size_t size, pxer_chunk_type_e *ch_type) { xer_next_token(int *stateContext, const void *buffer, size_t size, pxer_chunk_type_e *ch_type) {
struct xer__cb_arg arg; struct xer__cb_arg arg;
int new_stateContext = *stateContext; int new_stateContext = *stateContext;
ssize_t ret; ssize_t ret;
...@@ -151,7 +151,7 @@ xer_check_tag(const void *buf_ptr, int size, const char *need_tag) { ...@@ -151,7 +151,7 @@ xer_check_tag(const void *buf_ptr, int size, const char *need_tag) {
#undef ADVANCE #undef ADVANCE
#define ADVANCE(num_bytes) do { \ #define ADVANCE(num_bytes) do { \
size_t num = (num_bytes); \ size_t num = (num_bytes); \
buf_ptr = ((char *)buf_ptr) + num; \ buf_ptr = ((const char *)buf_ptr) + num; \
size -= num; \ size -= num; \
consumed_myself += num; \ consumed_myself += num; \
} while(0) } while(0)
...@@ -186,11 +186,11 @@ xer_decode_general(asn_codec_ctx_t *opt_codec_ctx, ...@@ -186,11 +186,11 @@ xer_decode_general(asn_codec_ctx_t *opt_codec_ctx,
asn_struct_ctx_t *ctx, /* Type decoder context */ asn_struct_ctx_t *ctx, /* Type decoder context */
void *struct_key, void *struct_key,
const char *xml_tag, /* Expected XML tag */ const char *xml_tag, /* Expected XML tag */
void *buf_ptr, size_t size, const void *buf_ptr, size_t size,
int (*opt_unexpected_tag_decoder) int (*opt_unexpected_tag_decoder)
(void *struct_key, void *chunk_buf, size_t chunk_size), (void *struct_key, const void *chunk_buf, size_t chunk_size),
ssize_t (*body_receiver) ssize_t (*body_receiver)
(void *struct_key, void *chunk_buf, size_t chunk_size, (void *struct_key, const void *chunk_buf, size_t chunk_size,
int have_more) int have_more)
) { ) {
...@@ -301,9 +301,9 @@ xer_decode_general(asn_codec_ctx_t *opt_codec_ctx, ...@@ -301,9 +301,9 @@ xer_decode_general(asn_codec_ctx_t *opt_codec_ctx,
int int
xer_is_whitespace(void *chunk_buf, size_t chunk_size) { xer_is_whitespace(const void *chunk_buf, size_t chunk_size) {
char *p = (char *)chunk_buf; const char *p = (const char *)chunk_buf;
char *pend = p + chunk_size; const char *pend = p + chunk_size;
for(; p < pend; p++) { for(; p < pend; p++) {
switch(*p) { switch(*p) {
......
...@@ -43,11 +43,11 @@ asn_dec_rval_t xer_decode_general(asn_codec_ctx_t *opt_codec_ctx, ...@@ -43,11 +43,11 @@ asn_dec_rval_t xer_decode_general(asn_codec_ctx_t *opt_codec_ctx,
asn_struct_ctx_t *ctx, /* Type decoder context */ asn_struct_ctx_t *ctx, /* Type decoder context */
void *struct_key, /* Treated as opaque pointer */ void *struct_key, /* Treated as opaque pointer */
const char *xml_tag, /* Expected XML tag name */ const char *xml_tag, /* Expected XML tag name */
void *buf_ptr, size_t size, const void *buf_ptr, size_t size,
int (*opt_unexpected_tag_decoder) int (*opt_unexpected_tag_decoder)
(void *struct_key, void *chunk_buf, size_t chunk_size), (void *struct_key, const void *chunk_buf, size_t chunk_size),
ssize_t (*body_receiver) ssize_t (*body_receiver)
(void *struct_key, void *chunk_buf, size_t chunk_size, (void *struct_key, const void *chunk_buf, size_t chunk_size,
int have_more) int have_more)
); );
...@@ -64,7 +64,7 @@ asn_dec_rval_t xer_decode_general(asn_codec_ctx_t *opt_codec_ctx, ...@@ -64,7 +64,7 @@ asn_dec_rval_t xer_decode_general(asn_codec_ctx_t *opt_codec_ctx,
PXER_COMMENT, /* A comment, may be part of */ PXER_COMMENT, /* A comment, may be part of */
} pxer_chunk_type_e; } pxer_chunk_type_e;
ssize_t xer_next_token(int *stateContext, ssize_t xer_next_token(int *stateContext,
void *buffer, size_t size, pxer_chunk_type_e *_ch_type); const void *buffer, size_t size, pxer_chunk_type_e *_ch_type);
/* /*
* This function checks the buffer against the tag name is expected to occur. * This function checks the buffer against the tag name is expected to occur.
...@@ -88,7 +88,7 @@ xer_check_tag_e xer_check_tag(const void *buf_ptr, int size, ...@@ -88,7 +88,7 @@ xer_check_tag_e xer_check_tag(const void *buf_ptr, int size,
* 1: Whitespace or empty string * 1: Whitespace or empty string
* 0: Non-whitespace * 0: Non-whitespace
*/ */
int xer_is_whitespace(void *chunk_buf, size_t chunk_size); int xer_is_whitespace(const void *chunk_buf, size_t chunk_size);
/* /*
* Skip the series of anticipated extensions. * Skip the series of anticipated extensions.
......
...@@ -58,6 +58,7 @@ _charclass[256] = { ...@@ -58,6 +58,7 @@ _charclass[256] = {
#define LANGLE 0x3c /* '<' */ #define LANGLE 0x3c /* '<' */
#define CEQUAL 0x3d /* '=' */ #define CEQUAL 0x3d /* '=' */
#define RANGLE 0x3e /* '>' */ #define RANGLE 0x3e /* '>' */
#define CQUEST 0x3f /* '?' */
/* Invoke token callback */ /* Invoke token callback */
#define TOKEN_CB_CALL(type, _ns, _current_too, _final) do { \ #define TOKEN_CB_CALL(type, _ns, _current_too, _final) do { \
...@@ -88,14 +89,14 @@ _charclass[256] = { ...@@ -88,14 +89,14 @@ _charclass[256] = {
/* /*
* Parser itself * Parser itself
*/ */
ssize_t pxml_parse(int *stateContext, void *xmlbuf, size_t size, pxml_callback_f *cb, void *key) { ssize_t pxml_parse(int *stateContext, const void *xmlbuf, size_t size, pxml_callback_f *cb, void *key) {
pstate_e state = (pstate_e)*stateContext; pstate_e state = (pstate_e)*stateContext;
char *chunk_start = (char *)xmlbuf; const char *chunk_start = (const char *)xmlbuf;
char *p = chunk_start; const char *p = chunk_start;
char *end = p + size; const char *end = p + size;
for(; p < end; p++) { for(; p < end; p++) {
int C = *(unsigned char *)p; int C = *(const unsigned char *)p;
switch(state) { switch(state) {
case ST_TEXT: case ST_TEXT:
/* /*
...@@ -229,6 +230,6 @@ ssize_t pxml_parse(int *stateContext, void *xmlbuf, size_t size, pxml_callback_f ...@@ -229,6 +230,6 @@ ssize_t pxml_parse(int *stateContext, void *xmlbuf, size_t size, pxml_callback_f
finish: finish:
*stateContext = (int)state; *stateContext = (int)state;
return chunk_start - (char *)xmlbuf; return chunk_start - (const char *)xmlbuf;
} }
...@@ -32,7 +32,7 @@ typedef enum { ...@@ -32,7 +32,7 @@ typedef enum {
* The chunk is NOT '\0'-terminated. * The chunk is NOT '\0'-terminated.
*/ */
typedef int (pxml_callback_f)(pxml_chunk_type_e _type, typedef int (pxml_callback_f)(pxml_chunk_type_e _type,
void *_chunk_data, size_t _chunk_size, void *_key); const void *_chunk_data, size_t _chunk_size, void *_key);
/* /*
* Parse the given buffer as it were a chunk of XML data. * Parse the given buffer as it were a chunk of XML data.
...@@ -41,7 +41,7 @@ typedef int (pxml_callback_f)(pxml_chunk_type_e _type, ...@@ -41,7 +41,7 @@ typedef int (pxml_callback_f)(pxml_chunk_type_e _type,
* It will always be lesser than or equal to the specified _size. * It will always be lesser than or equal to the specified _size.
* The next invocation of this function must account the difference. * The next invocation of this function must account the difference.
*/ */
ssize_t pxml_parse(int *_stateContext, void *_buf, size_t _size, ssize_t pxml_parse(int *_stateContext, const void *_buf, size_t _size,
pxml_callback_f *cb, void *_key); pxml_callback_f *cb, void *_key);
#endif /* _XER_SUPPORT_H_ */ #endif /* _XER_SUPPORT_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment