Skip to content
Snippets Groups Projects
Commit 8e8078aa authored by Lev Walkin's avatar Lev Walkin
Browse files

clean-up; removed unnecessary field from type descriptor

parent 9821098d
No related branches found
No related tags found
No related merge requests found
Showing
with 108 additions and 95 deletions
......@@ -28,7 +28,6 @@ asn1_TYPE_descriptor_t asn1_DEF_ObjectDescriptor = {
asn1_DEF_ObjectDescriptor_tags,
sizeof(asn1_DEF_ObjectDescriptor_tags)
/ sizeof(asn1_DEF_ObjectDescriptor_tags[0]),
-1, /* Both ways are fine */
0, 0, /* No members */
0 /* No specifics */
};
......
......@@ -28,7 +28,6 @@ asn1_TYPE_descriptor_t asn1_DEF_PrintableString = {
asn1_DEF_PrintableString_tags,
sizeof(asn1_DEF_PrintableString_tags)
/ sizeof(asn1_DEF_PrintableString_tags[0]),
-1, /* Both ways are fine */
0, 0, /* No members */
0 /* No specifics */
};
......
......@@ -4,7 +4,6 @@
*/
#include <asn_internal.h>
#include <REAL.h>
#include <INTEGER.h>
#include <stdlib.h> /* for strtod(3) */
#include <math.h>
#include <errno.h>
......@@ -30,11 +29,11 @@ static ber_tlv_tag_t asn1_DEF_REAL_tags[] = {
};
asn1_TYPE_descriptor_t asn1_DEF_REAL = {
"REAL",
INTEGER_free,
ASN__PRIMITIVE_TYPE_free,
REAL_print,
asn_generic_no_constraint,
INTEGER_decode_ber, /* Implemented in terms of INTEGER type */
INTEGER_encode_der,
ber_decode_primitive,
der_encode_primitive,
0, /* Not implemented yet */
REAL_encode_xer,
0, /* Use generic outmost tag fetcher */
......@@ -42,7 +41,6 @@ asn1_TYPE_descriptor_t asn1_DEF_REAL = {
sizeof(asn1_DEF_REAL_tags) / sizeof(asn1_DEF_REAL_tags[0]),
asn1_DEF_REAL_tags, /* Same as above */
sizeof(asn1_DEF_REAL_tags) / sizeof(asn1_DEF_REAL_tags[0]),
0, /* Always in primitive form */
0, 0, /* No members */
0 /* No specifics */
};
......@@ -55,6 +53,28 @@ REAL__dump(double d, int canonical, asn_app_consume_bytes_f *cb, void *app_key)
const char *fmt = canonical?"%15E":"f";
ssize_t ret;
/*
* Check whether it is a special value.
*/
if(finite(d) == 0) {
if(isinf(d)) {
if(copysign(1.0, d) < 0.0) {
buf = "<MINUS-INFINITY/>";
buflen = 17;
} else {
buf = "<PLUS-INFINITY/>";
buflen = 16;
}
} else {
buf = "<NOT-A-NUMBER/>";
buflen = 15;
}
return (cb(buf, buflen, app_key) < 0) ? -1 : buflen;
}
/*
* Use the libc's double printing, hopefully they got it right.
*/
do {
ret = snprintf(buf, buflen, fmt, d);
if(ret < 0) {
......@@ -66,7 +86,7 @@ REAL__dump(double d, int canonical, asn_app_consume_bytes_f *cb, void *app_key)
break;
}
if(buf != local_buf) free(buf);
(void *)buf = MALLOC(buflen);
buf = (char *)MALLOC(buflen);
if(!buf) return -1;
} while(1);
......@@ -118,18 +138,20 @@ int
REAL_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
asn_app_consume_bytes_f *cb, void *app_key) {
const REAL_t *st = (const REAL_t *)sptr;
ssize_t ret;
double d;
(void)td; /* Unused argument */
(void)ilevel; /* Unused argument */
if(!st || !st->buf)
return cb("<absent>", 8, app_key);
if(asn1_REAL2double(st, &d))
return cb("<error>", 7, app_key);
ret = cb("<absent>", 8, app_key);
else if(asn1_REAL2double(st, &d))
ret = cb("<error>", 7, app_key);
else
ret = REAL__dump(d, 0, cb, app_key);
return (REAL__dump(d, 0, cb, app_key) < 0) ? -1 : 0;
return (ret < 0) ? -1 : 0;
}
asn_enc_rval_t
......@@ -338,7 +360,7 @@ asn1_double2REAL(REAL_t *st, double dbl_value) {
|| expval == INT_MAX /* catches finite() which catches isnan() */
) {
if(!st->buf || st->size < 2) {
(void *)ptr = MALLOC(2);
ptr = (uint8_t *)MALLOC(2);
if(!ptr) return -1;
st->buf = ptr;
}
......
......@@ -6,11 +6,9 @@
#define ASN_TYPE_REAL_H
#include <asn_application.h>
#include <ber_codec_prim.h>
typedef struct REAL {
uint8_t *buf; /* Buffer with REAL type encoding */
int size; /* Size of the buffer */
} REAL_t;
typedef ASN__PRIMITIVE_TYPE_t REAL_t;
extern asn1_TYPE_descriptor_t asn1_DEF_REAL;
......
......@@ -4,6 +4,7 @@
*/
#include <asn_internal.h>
#include <RELATIVE-OID.h>
#include <ber_codec_prim.h> /* Encoder and decoder of a primitive */
#include <limits.h> /* for CHAR_BIT */
#include <assert.h>
#include <errno.h>
......@@ -16,11 +17,11 @@ static ber_tlv_tag_t asn1_DEF_RELATIVE_OID_tags[] = {
};
asn1_TYPE_descriptor_t asn1_DEF_RELATIVE_OID = {
"RELATIVE-OID",
INTEGER_free,
ASN__PRIMITIVE_TYPE_free,
RELATIVE_OID_print,
asn_generic_no_constraint,
INTEGER_decode_ber, /* Implemented in terms of INTEGER type */
OBJECT_IDENTIFIER_encode_der,
ber_decode_primitive,
der_encode_primitive,
0, /* Not implemented yet */
RELATIVE_OID_encode_xer,
0, /* Use generic outmost tag fetcher */
......@@ -30,7 +31,6 @@ asn1_TYPE_descriptor_t asn1_DEF_RELATIVE_OID = {
asn1_DEF_RELATIVE_OID_tags, /* Same as above */
sizeof(asn1_DEF_RELATIVE_OID_tags)
/ sizeof(asn1_DEF_RELATIVE_OID_tags[0]),
0, /* Always in primitive form */
0, 0, /* No members */
0 /* No specifics */
};
......@@ -73,16 +73,16 @@ RELATIVE_OID_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
(void)ilevel; /* Unused argument */
if(!st || !st->buf)
return cb("<absent>", 8, app_key);
return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
/* Dump preamble */
if(cb("{ ", 2, app_key))
if(cb("{ ", 2, app_key) < 0)
return -1;
if(RELATIVE_OID__dump_body(st, cb, app_key) < 0)
return -1;
return cb(" }", 2, app_key);
return (cb(" }", 2, app_key) < 0) ? -1 : 0;
}
asn_enc_rval_t
......
......@@ -28,7 +28,6 @@ asn1_TYPE_descriptor_t asn1_DEF_T61String = {
asn1_DEF_T61String_tags,
sizeof(asn1_DEF_T61String_tags)
/ sizeof(asn1_DEF_T61String_tags[0]),
-1, /* Both ways are fine */
0, 0, /* No members */
0 /* No specifics */
};
......
......@@ -28,7 +28,6 @@ asn1_TYPE_descriptor_t asn1_DEF_TeletexString = {
asn1_DEF_TeletexString_tags,
sizeof(asn1_DEF_TeletexString_tags)
/ sizeof(asn1_DEF_TeletexString_tags[0]),
-1, /* Both ways are fine */
0, 0, /* No members */
0 /* No specifics */
};
......
......@@ -15,7 +15,9 @@
* UTCTime basic type description.
*/
static ber_tlv_tag_t asn1_DEF_UTCTime_tags[] = {
(ASN_TAG_CLASS_UNIVERSAL | (23 << 2))
(ASN_TAG_CLASS_UNIVERSAL | (23 << 2)), /* [UNIVERSAL 23] IMPLICIT ...*/
(ASN_TAG_CLASS_UNIVERSAL | (26 << 2)), /* [UNIVERSAL 26] IMPLICIT ...*/
(ASN_TAG_CLASS_UNIVERSAL | (4 << 2)) /* ... OCTET STRING */
};
asn1_TYPE_descriptor_t asn1_DEF_UTCTime = {
"UTCTime",
......@@ -29,11 +31,10 @@ asn1_TYPE_descriptor_t asn1_DEF_UTCTime = {
0, /* Use generic outmost tag fetcher */
asn1_DEF_UTCTime_tags,
sizeof(asn1_DEF_UTCTime_tags)
/ sizeof(asn1_DEF_UTCTime_tags[0]),
asn1_DEF_UTCTime_tags, /* Same as above */
/ sizeof(asn1_DEF_UTCTime_tags[0]) - 2,
asn1_DEF_UTCTime_tags,
sizeof(asn1_DEF_UTCTime_tags)
/ sizeof(asn1_DEF_UTCTime_tags[0]),
-1, /* Both ways are fine */
0, 0, /* No members */
0 /* No specifics */
};
......@@ -106,16 +107,16 @@ UTCTime_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
errno = EPERM;
if(asn_UT2time(st, &tm, 1) == -1 && errno != EPERM)
return cb("<bad-value>", 11, app_key);
return (cb("<bad-value>", 11, app_key) < 0) ? -1 : 0;
ret = snprintf(buf, sizeof(buf),
"%04d-%02d-%02d %02d:%02d%02d (GMT)",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
assert(ret > 0 && ret < (int)sizeof(buf));
return cb(buf, ret, app_key);
return (cb(buf, ret, app_key) < 0) ? -1 : 0;
} else {
return cb("<absent>", 8, app_key);
return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
}
}
......
......@@ -28,7 +28,6 @@ asn1_TYPE_descriptor_t asn1_DEF_UTF8String = {
asn1_DEF_UTF8String_tags,
sizeof(asn1_DEF_UTF8String_tags)
/ sizeof(asn1_DEF_UTF8String_tags[0]),
-1, /* Both ways are fine */
0, 0, /* No members */
0 /* No specifics */
};
......@@ -122,8 +121,8 @@ UTF8String_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
(void)ilevel; /* Unused argument */
if(st && st->buf) {
return cb(st->buf, st->size, app_key);
return (cb(st->buf, st->size, app_key) < 0) ? -1 : 0;
} else {
return cb("<absent>", 8, app_key);
return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
}
}
......@@ -28,7 +28,6 @@ asn1_TYPE_descriptor_t asn1_DEF_UniversalString = {
asn1_DEF_UniversalString_tags,
sizeof(asn1_DEF_UniversalString_tags)
/ sizeof(asn1_DEF_UniversalString_tags[0]),
-1, /* Both ways are fine */
0, 0, /* No members */
0 /* No specifics */
};
......@@ -120,7 +119,7 @@ UniversalString_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
(void)td; /* Unused argument */
(void)ilevel; /* Unused argument */
if(!st || !st->buf) return cb("<absent>", 8, app_key);
if(!st || !st->buf) return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
if(UniversalString__dump(st, cb, app_key) < 0)
return -1;
......
......@@ -28,7 +28,6 @@ asn1_TYPE_descriptor_t asn1_DEF_VideotexString = {
asn1_DEF_VideotexString_tags,
sizeof(asn1_DEF_VideotexString_tags)
/ sizeof(asn1_DEF_VideotexString_tags[0]),
-1, /* Both ways are fine */
0, 0, /* No members */
0 /* No specifics */
};
......
......@@ -28,7 +28,6 @@ asn1_TYPE_descriptor_t asn1_DEF_VisibleString = {
asn1_DEF_VisibleString_tags,
sizeof(asn1_DEF_VisibleString_tags)
/ sizeof(asn1_DEF_VisibleString_tags[0]),
-1, /* Both ways are fine */
0, 0, /* No members */
0 /* No specifics */
};
......
......@@ -31,7 +31,7 @@ int get_asn1c_environment_version(void); /* Run-time version */
__FILE__, __LINE__); \
} while(0)
#else /* !__GNUC__ */
extern void ASN_DEBUG_f(const char *fmt, ...);
void ASN_DEBUG_f(const char *fmt, ...);
#define ASN_DEBUG ASN_DEBUG_f
#endif /* __GNUC__ */
#else /* EMIT_ASN_DEBUG != 1 */
......@@ -60,13 +60,20 @@ static void ASN_DEBUG(const char *fmt, ...) { (void)fmt; };
|| __ASN_E_cbc(buf3, size3))
#define _i_ASN_TEXT_INDENT(nl, level) do { \
int __level = (level); \
int __nl = ((nl) != 0); \
int __i; \
if(__nl) _ASN_CALLBACK("\n", 1); \
for(__i = 0; __i < __level; __i++) \
_ASN_CALLBACK(" ", 4); \
er.encoded += __nl + 4 * __level; \
int __level = (level); \
int __nl = ((nl) != 0); \
int __i; \
if(__nl) _ASN_CALLBACK("\n", 1); \
for(__i = 0; __i < __level; __i++) \
_ASN_CALLBACK(" ", 4); \
er.encoded += __nl + 4 * __level; \
} while(0)
#define _i_INDENT(nl) do { \
int __i; \
if((nl) && cb("\n", 1, app_key) < 0) return -1; \
for(__i = 0; __i < ilevel; __i++) \
if(cb(" ", 4, app_key) < 0) return -1; \
} while(0)
#endif /* _ASN_INTERNAL_H_ */
......@@ -46,7 +46,7 @@ ber_decode(asn1_TYPE_descriptor_t *type_descriptor,
*/
ber_dec_rval_t
ber_check_tags(asn1_TYPE_descriptor_t *td, ber_dec_ctx_t *opt_ctx,
void *ptr, size_t size, int tag_mode,
void *ptr, size_t size, int tag_mode, int last_tag_form,
ber_tlv_len_t *last_length, int *opt_tlv_form) {
ssize_t consumed_myself = 0;
ssize_t tag_len;
......@@ -169,10 +169,10 @@ ber_check_tags(asn1_TYPE_descriptor_t *td, ber_dec_ctx_t *opt_ctx,
RETURN(RC_FAIL);
}
} else {
if(td->last_tag_form != tlv_constr
&& td->last_tag_form != -1) {
if(last_tag_form != tlv_constr
&& last_tag_form != -1) {
ASN_DEBUG("last_tag_form %d != %d",
td->last_tag_form, tlv_constr);
last_tag_form, tlv_constr);
RETURN(RC_FAIL);
}
}
......
......@@ -21,7 +21,7 @@ struct asn1_TYPE_descriptor_s; /* Forward declaration */
enum ber_dec_rval_code_e {
RC_OK, /* Decoded successfully */
RC_WMORE, /* More data expected, call again */
RC_FAIL, /* Failure to decode data */
RC_FAIL /* Failure to decode data */
};
typedef struct ber_dec_rval_s {
enum ber_dec_rval_code_e code; /* Result code */
......@@ -71,6 +71,7 @@ ber_dec_rval_t ber_check_tags(struct asn1_TYPE_descriptor_s *type_dsc,
ber_dec_ctx_t *opt_ctx, /* saved context */
void *ptr, size_t size,
int tag_mode, /* {-1,0,1}: IMPLICIT, no, EXPLICIT */
int last_tag_form, /* {-1,0:1}: any, primitive, constr */
ber_tlv_len_t *last_length,
int *opt_tlv_form);
......
......@@ -9,7 +9,7 @@ enum asn_tag_class {
ASN_TAG_CLASS_UNIVERSAL = 0, /* 0b00 */
ASN_TAG_CLASS_APPLICATION = 1, /* 0b01 */
ASN_TAG_CLASS_CONTEXT = 2, /* 0b10 */
ASN_TAG_CLASS_PRIVATE = 3, /* 0b11 */
ASN_TAG_CLASS_PRIVATE = 3 /* 0b11 */
};
typedef unsigned ber_tlv_tag_t; /* BER TAG from Tag-Length-Value */
......
......@@ -148,7 +148,7 @@ CHOICE_decode_ber(asn1_TYPE_descriptor_t *td,
if(tag_mode || td->tags_count) {
rval = ber_check_tags(td, ctx, ptr, size,
tag_mode, &ctx->left, 0);
tag_mode, -1, &ctx->left, 0);
if(rval.code != RC_OK) {
ASN_DEBUG("%s tagging check failed: %d",
td->name, rval.code);
......@@ -425,7 +425,7 @@ CHOICE_encode_der(asn1_TYPE_descriptor_t *td,
return erval;
/* Encode CHOICE with parent or my own tag */
ret = der_write_tags(td, erval.encoded, tag_mode, tag,
ret = der_write_tags(td, erval.encoded, tag_mode, 1, tag,
cb, app_key);
if(ret == -1) {
erval.encoded = -1;
......@@ -597,7 +597,7 @@ CHOICE_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
asn1_CHOICE_specifics_t *specs = (asn1_CHOICE_specifics_t *)td->specifics;
int present;
if(!sptr) return cb("<absent>", 8, app_key);
if(!sptr) return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
/*
* Figure out which CHOICE element is encoded.
......@@ -613,22 +613,22 @@ CHOICE_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
if(elm->flags & ATF_POINTER) {
memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset);
if(!memb_ptr) return cb("<absent>", 8, app_key);
if(!memb_ptr) return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
} else {
memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
}
/* Print member's name and stuff */
if(0) {
if(cb(elm->name, strlen(elm->name), app_key)
|| cb(": ", 2, app_key))
if(cb(elm->name, strlen(elm->name), app_key) < 0
|| cb(": ", 2, app_key) < 0)
return -1;
}
return elm->type->print_struct(elm->type, memb_ptr, ilevel,
cb, app_key);
} else {
return cb("<absent>", 8, app_key);
return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
}
}
......
......@@ -158,7 +158,7 @@ SEQUENCE_decode_ber(asn1_TYPE_descriptor_t *td,
*/
rval = ber_check_tags(td, ctx, ptr, size,
tag_mode, &ctx->left, 0);
tag_mode, 1, &ctx->left, 0);
if(rval.code != RC_OK) {
ASN_DEBUG("%s tagging check failed: %d",
td->name, rval.code);
......@@ -534,7 +534,7 @@ SEQUENCE_encode_der(asn1_TYPE_descriptor_t *td,
/*
* Encode the TLV for the sequence itself.
*/
ret = der_write_tags(td, computed_size, tag_mode, tag, cb, app_key);
ret = der_write_tags(td, computed_size, tag_mode, 1, tag, cb, app_key);
ASN_DEBUG("Wrote tags: %ld (+%ld)", (long)ret, (long)computed_size);
if(ret == -1) {
erval.encoded = -1;
......@@ -632,11 +632,11 @@ SEQUENCE_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
int edx;
int ret;
if(!sptr) return cb("<absent>", 8, app_key);
if(!sptr) return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
/* Dump preamble */
if(cb(td->name, strlen(td->name), app_key)
|| cb(" ::= {\n", 7, app_key))
if(cb(td->name, strlen(td->name), app_key) < 0
|| cb(" ::= {", 6, app_key) < 0)
return -1;
for(edx = 0; edx < td->elements_count; edx++) {
......@@ -651,27 +651,23 @@ SEQUENCE_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
}
/* Indentation */
for(ret = 0; ret < ilevel; ret++) cb(" ", 1, app_key);
_i_INDENT(1);
/* Print the member's name and stuff */
if(cb(elm->name, strlen(elm->name), app_key)
|| cb(": ", 2, app_key))
if(cb(elm->name, strlen(elm->name), app_key) < 0
|| cb(": ", 2, app_key) < 0)
return -1;
/* Print the member itself */
ret = elm->type->print_struct(elm->type, memb_ptr, ilevel + 4,
ret = elm->type->print_struct(elm->type, memb_ptr, ilevel + 1,
cb, app_key);
if(ret) return ret;
/* Print out the terminator */
ret = cb("\n", 1, app_key);
if(ret) return ret;
}
/* Indentation */
for(ret = 0; ret < ilevel - 4; ret++) cb(" ", 1, app_key);
ilevel--;
_i_INDENT(1);
return cb("}", 1, app_key);
return (cb("}", 1, app_key) < 0) ? -1 : 0;
}
void
......
......@@ -39,7 +39,7 @@ SEQUENCE_OF_encode_der(asn1_TYPE_descriptor_t *td, void *ptr,
/*
* Encode the TLV for the sequence itself.
*/
encoding_size = der_write_tags(td, computed_size, tag_mode, tag,
encoding_size = der_write_tags(td, computed_size, tag_mode, 1, tag,
cb, app_key);
if(encoding_size == -1) {
erval.encoded = -1;
......
......@@ -147,7 +147,7 @@ SET_decode_ber(asn1_TYPE_descriptor_t *td,
*/
rval = ber_check_tags(td, ctx, ptr, size,
tag_mode, &ctx->left, 0);
tag_mode, 1, &ctx->left, 0);
if(rval.code != RC_OK) {
ASN_DEBUG("%s tagging check failed: %d",
td->name, rval.code);
......@@ -533,7 +533,7 @@ SET_encode_der(asn1_TYPE_descriptor_t *td,
/*
* Encode the TLV for the sequence itself.
*/
ret = der_write_tags(td, computed_size, tag_mode, tag, cb, app_key);
ret = der_write_tags(td, computed_size, tag_mode, 1, tag, cb, app_key);
if(ret == -1) {
my_erval.encoded = -1;
my_erval.failed_type = td;
......@@ -633,11 +633,11 @@ SET_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
int edx;
int ret;
if(!sptr) return cb("<absent>", 8, app_key);
if(!sptr) return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
/* Dump preamble */
if(cb(td->name, strlen(td->name), app_key)
|| cb(" ::= {\n", 7, app_key))
if(cb(td->name, strlen(td->name), app_key) < 0
|| cb(" ::= {", 6, app_key) < 0)
return -1;
for(edx = 0; edx < td->elements_count; edx++) {
......@@ -651,27 +651,23 @@ SET_print(asn1_TYPE_descriptor_t *td, const void *sptr, int ilevel,
memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
}
/* Indentation */
for(ret = 0; ret < ilevel; ret++) cb(" ", 1, app_key);
_i_INDENT(1);
/* Print the member's name and stuff */
if(cb(elm->name, strlen(elm->name), app_key)
|| cb(": ", 2, app_key))
if(cb(elm->name, strlen(elm->name), app_key) < 0
|| cb(": ", 2, app_key) < 0)
return -1;
/* Print the member itself */
ret = elm->type->print_struct(elm->type, memb_ptr, ilevel + 4,
ret = elm->type->print_struct(elm->type, memb_ptr, ilevel + 1,
cb, app_key);
if(ret) return ret;
ret = cb("\n", 1, app_key);
if(ret) return ret;
}
/* Indentation */
for(ret = 0; ret < ilevel - 4; ret++) cb(" ", 1, app_key);
ilevel--;
_i_INDENT(1);
return cb("}", 1, app_key);
return (cb("}", 1, app_key) < 0) ? -1 : 0;
}
void
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment