XER decoding and asn_long2INTEGER()

diff --git a/skeletons/INTEGER.c b/skeletons/INTEGER.c
index fbd9272..c4fb972 100644
--- a/skeletons/INTEGER.c
+++ b/skeletons/INTEGER.c
@@ -1,10 +1,10 @@
 /*-
- * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
  * Redistribution and modifications are permitted subject to BSD license.
  */
 #include <asn_internal.h>
 #include <INTEGER.h>
-#include <ber_codec_prim.h>	/* Encoder and decoder of a primitive */
+#include <asn_codecs_prim.h>	/* Encoder and decoder of a primitive type */
 #include <assert.h>
 #include <errno.h>
 
@@ -22,7 +22,7 @@
 	asn_generic_no_constraint,
 	ber_decode_primitive,
 	INTEGER_encode_der,
-	0,				/* Not implemented yet */
+	INTEGER_decode_xer,
 	INTEGER_encode_xer,
 	0, /* Use generic outmost tag fetcher */
 	asn_DEF_INTEGER_tags,
@@ -176,6 +176,84 @@
 	return (ret < 0) ? -1 : 0;
 }
 
+/*
+ * Decode the chunk of XML text encoding INTEGER.
+ */
+static ssize_t
+INTEGER__xer_body_decode(INTEGER_t *st, void *chunk_buf, size_t chunk_size) {
+	long sign = 1;
+	long value;
+	char *lstart = (char *)chunk_buf;
+	char *lstop = chunk_buf + chunk_size;
+	enum {
+		ST_SKIPSPACE,
+		ST_WAITDIGITS,
+		ST_DIGITS,
+	} state = ST_SKIPSPACE;
+	/*
+	 * We may receive a tag here. But we aren't ready to deal with it yet.
+	 * So, just use stroul()-like code and serialize the result.
+	 */
+	for(value = 0; lstart < lstop; lstart++) {
+		int lv = *lstart;
+		switch(lv) {
+		case 0x09: case 0x0a: case 0x0d: case 0x20:
+			if(state == ST_SKIPSPACE) continue;
+			break;
+		case 0x2d:	/* '-' */
+			if(state == ST_SKIPSPACE) {
+				sign = -1;
+				state = ST_WAITDIGITS;
+				continue;
+			}
+			break;
+		case 0x2b:	/* '+' */
+			if(state == ST_SKIPSPACE) {
+				state = ST_WAITDIGITS;
+				continue;
+			}
+			break;
+		case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+		case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
+			if(state != ST_DIGITS) state = ST_DIGITS;
+
+			value = value * 10 + (lv - 0x30);
+			/* Check for two's complement overflow */
+			if(value < 0) {
+				/* Check whether it is a LONG_MIN */
+				if(sign == -1
+				&& value == ~((unsigned long)-1 >> 1)) {
+					sign = 0;
+				} else {
+					/* Overflow */
+					return -1;
+				}
+			}
+			continue;
+		}
+	}
+
+	if(state != ST_DIGITS)
+		return -1;	/* No digits */
+
+	value *= sign;	/* Change sign, if needed */
+
+	if(asn_long2INTEGER(st, value))
+		return -1;
+
+	return lstop - lstart;
+}
+
+asn_dec_rval_t
+INTEGER_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
+	asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname,
+		void *buf_ptr, size_t size) {
+
+	return xer_decode_primitive(opt_codec_ctx, td,
+		(ASN__PRIMITIVE_TYPE_t **)sptr, opt_mname,
+		buf_ptr, size, INTEGER__xer_body_decode);
+}
+
 asn_enc_rval_t
 INTEGER_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
 	int ilevel, enum xer_encoder_flags_e flags,
@@ -254,3 +332,48 @@
 	*lptr = l;
 	return 0;
 }
+
+int
+asn_long2INTEGER(INTEGER_t *st, long value) {
+	uint8_t *buf, *bp;
+	uint8_t *p;
+	uint8_t *pstart;
+	uint8_t *pend1;
+
+	if(!st) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	buf = MALLOC(sizeof(value));
+	if(!buf) return -1;
+
+	pstart = (uint8_t *)&value;
+	pend1 = pstart + sizeof(value) - 1;
+	/*
+	 * If the contents octet consists of more than one octet,
+	 * then bits of the first octet and bit 8 of the second octet:
+	 * a) shall not all be ones; and
+	 * b) shall not all be zero.
+	 */
+	for(p = pstart; p < pend1; p++) {
+		switch(*p) {
+		case 0x00: if((p[1] & 0x80) == 0)
+				continue;
+			break;
+		case 0xff: if((p[1] & 0x80))
+				continue;
+			break;
+		}
+		break;
+	}
+	/* Copy the integer body */
+	for(pstart = p, bp = buf; p <= pend1;)
+		*bp++ = *p++;
+
+	if(st->buf) FREEMEM(st->buf);
+	st->buf = buf;
+	st->size = p - pstart;
+
+	return 0;
+}