XER can decode long values

diff --git a/ChangeLog b/ChangeLog
index 5435667..6c6a465 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,14 +3,14 @@
 
 	* skeletons/standard-modules directory is now used for standard types.
 	* Fixed class field access problem (Test case 98)
-	  (Severity: medim; Security impact: none)
+	  (Severity: medium; Security impact: none)
 	* Refactored Information Object Classes parsing.
 	* Refactored Parameterization support.
 	* [typedef enum foo {}] foo_e; is now e_foo, see #1287989
 	* Refactored ValueSetTypeAssignment parsing.
-	* First release of PER encoder (does not encode SETs yet).
 	* asn-decoder-template.c renamed into converter-sample.c
 	* MEGACO (Media Gateway Control Protocol) decoder sample added.
+	* First release of PER encoder (does not encode SETs yet).
 
 0.9.20:	2006-Mar-06
 
diff --git a/examples/sample.makefile.regen b/examples/sample.makefile.regen
index 134e1f2..0e28ab6 100755
--- a/examples/sample.makefile.regen
+++ b/examples/sample.makefile.regen
@@ -35,6 +35,19 @@
 	echo "	@touch ${ASN1PDU}.c"
 	echo "	make"
 	echo
+	echo 'check: ${TARGET}'
+	echo "	@if test -f ./sample-${ASN1PDU}-1.[db]er ; then \\"
+	echo "	for f in ./sample-${ASN1PDU}-*.[db]er; do \\"
+	echo '	echo "Recoding $$f into XER and back..."; \'
+	echo '	./${TARGET} -iber -oxer $$f > ./.tmp.1.$$ || exit 2; \'
+	echo '	./${TARGET} -ixer -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \'
+	echo '	diff ./.tmp.1.$$ ./.tmp.2.$$ || exit 2; \'
+	echo '	rm -f ./.tmp.[12].$$; \'
+	echo '	done; fi'
+	echo '	@echo ================'
+	echo '	@echo All tests passed'
+	echo '	@echo ================'
+	echo
 	echo "distclean: clean"
 	echo '	rm -f $(ASN_MODULE_SOURCES) $(ASN_MODULE_HEADERS)'
 	echo '	rm -f $(ASN_CONVERTER_SOURCES) $(ASN_CONVERTER_HEADERS)'
diff --git a/examples/sample.source.LDAP3/Makefile b/examples/sample.source.LDAP3/Makefile
index f7dfc64..5483981 100644
--- a/examples/sample.source.LDAP3/Makefile
+++ b/examples/sample.source.LDAP3/Makefile
@@ -196,6 +196,19 @@
 	@touch LDAPMessage.c
 	make
 
+check: ${TARGET}
+	@if test -f ./sample-LDAPMessage-1.[db]er ; then \
+	for f in ./sample-LDAPMessage-*.[db]er; do \
+	echo "Recoding $$f into XER and back..."; \
+	./${TARGET} -iber -oxer $$f > ./.tmp.1.$$ || exit 2; \
+	./${TARGET} -ixer -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \
+	diff ./.tmp.1.$$ ./.tmp.2.$$ || exit 2; \
+	rm -f ./.tmp.[12].$$; \
+	done; fi
+	@echo ================
+	@echo All tests passed
+	@echo ================
+
 distclean: clean
 	rm -f $(ASN_MODULE_SOURCES) $(ASN_MODULE_HEADERS)
 	rm -f $(ASN_CONVERTER_SOURCES) $(ASN_CONVERTER_HEADERS)
diff --git a/examples/sample.source.MEGACO/Makefile b/examples/sample.source.MEGACO/Makefile
index d922a9d..b1415a2 100644
--- a/examples/sample.source.MEGACO/Makefile
+++ b/examples/sample.source.MEGACO/Makefile
@@ -318,6 +318,19 @@
 	@touch MegacoMessage.c
 	make
 
+check: ${TARGET}
+	@if test -f ./sample-MegacoMessage-1.[db]er ; then \
+	for f in ./sample-MegacoMessage-*.[db]er; do \
+	echo "Recoding $$f into XER and back..."; \
+	./${TARGET} -iber -oxer $$f > ./.tmp.1.$$ || exit 2; \
+	./${TARGET} -ixer -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \
+	diff ./.tmp.1.$$ ./.tmp.2.$$ || exit 2; \
+	rm -f ./.tmp.[12].$$; \
+	done; fi
+	@echo ================
+	@echo All tests passed
+	@echo ================
+
 distclean: clean
 	rm -f $(ASN_MODULE_SOURCES) $(ASN_MODULE_HEADERS)
 	rm -f $(ASN_CONVERTER_SOURCES) $(ASN_CONVERTER_HEADERS)
diff --git a/examples/sample.source.PKIX1/Makefile b/examples/sample.source.PKIX1/Makefile
index 30e8951..26ca748 100644
--- a/examples/sample.source.PKIX1/Makefile
+++ b/examples/sample.source.PKIX1/Makefile
@@ -378,6 +378,19 @@
 	@touch Certificate.c
 	make
 
+check: ${TARGET}
+	@if test -f ./sample-Certificate-1.[db]er ; then \
+	for f in ./sample-Certificate-*.[db]er; do \
+	echo "Recoding $$f into XER and back..."; \
+	./${TARGET} -iber -oxer $$f > ./.tmp.1.$$ || exit 2; \
+	./${TARGET} -ixer -oxer ./.tmp.1.$$ > ./.tmp.2.$$ || exit 2; \
+	diff ./.tmp.1.$$ ./.tmp.2.$$ || exit 2; \
+	rm -f ./.tmp.[12].$$; \
+	done; fi
+	@echo ================
+	@echo All tests passed
+	@echo ================
+
 distclean: clean
 	rm -f $(ASN_MODULE_SOURCES) $(ASN_MODULE_HEADERS)
 	rm -f $(ASN_CONVERTER_SOURCES) $(ASN_CONVERTER_HEADERS)
diff --git a/skeletons/INTEGER.c b/skeletons/INTEGER.c
index 3643465..5a0ecce 100644
--- a/skeletons/INTEGER.c
+++ b/skeletons/INTEGER.c
@@ -297,6 +297,20 @@
 		INTEGER__compar_value2enum);
 }
 
+static int
+INTEGER_st_prealloc(INTEGER_t *st, int min_size) {
+	void *p = MALLOC(min_size + 1);
+	if(p) {
+		void *b = st->buf;
+		st->size = 0;
+		st->buf = p;
+		FREEMEM(b);
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
 /*
  * Decode the chunk of XML text encoding INTEGER.
  */
@@ -310,11 +324,19 @@
 	const char *lstop = lstart + chunk_size;
 	enum {
 		ST_SKIPSPACE,
+		ST_SKIPSPHEX,
 		ST_WAITDIGITS,
 		ST_DIGITS,
+		ST_HEXDIGIT1,
+		ST_HEXDIGIT2,
+		ST_HEXCOLON,
 		ST_EXTRASTUFF
 	} state = ST_SKIPSPACE;
 
+	if(chunk_size)
+		ASN_DEBUG("INTEGER body %d 0x%2x..0x%2x",
+			chunk_size, *lstart, lstop[-1]);
+
 	/*
 	 * We may have received a tag here. It will be processed inline.
 	 * Use strtoul()-like code and serialize the result.
@@ -323,7 +345,19 @@
 		int lv = *lp;
 		switch(lv) {
 		case 0x09: case 0x0a: case 0x0d: case 0x20:
-			if(state == ST_SKIPSPACE) continue;
+			switch(state) {
+			case ST_SKIPSPACE:
+			case ST_SKIPSPHEX:
+				continue;
+			case ST_HEXCOLON:
+				if(xer_is_whitespace(lp, lstop - lp)) {
+					lp = lstop - 1;
+					continue;
+				}
+				break;
+			default:
+				break;
+			}
 			break;
 		case 0x2d:	/* '-' */
 			if(state == ST_SKIPSPACE) {
@@ -340,7 +374,24 @@
 			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;
+			switch(state) {
+			case ST_DIGITS: break;
+			case ST_SKIPSPHEX:	/* Fall through */
+			case ST_HEXDIGIT1:
+				value = (lv - 0x30) << 4;
+				state = ST_HEXDIGIT2;
+				continue;
+			case ST_HEXDIGIT2:
+				value += (lv - 0x30);
+				state = ST_HEXCOLON;
+				st->buf[st->size++] = value;
+				continue;
+			case ST_HEXCOLON:
+				return XPBD_BROKEN_ENCODING;
+			default:
+				state = ST_DIGITS;
+				break;
+			}
 
 		    {
 			long new_value = value * 10;
@@ -381,22 +432,86 @@
 				ASN_DEBUG("Unknown identifier for INTEGER");
 			}
 			return XPBD_BROKEN_ENCODING;
+		case 0x3a:	/* ':' */
+			if(state == ST_HEXCOLON) {
+				/* This colon is expected */
+				state = ST_HEXDIGIT1;
+				continue;
+			} else if(state == ST_DIGITS) {
+				/* The colon here means that we have
+				 * decoded the first two hexadecimal
+				 * places as a decimal value.
+				 * Switch decoding mode. */
+				ASN_DEBUG("INTEGER re-evaluate as hex form");
+				if(INTEGER_st_prealloc(st, (chunk_size/3) + 1))
+					return XPBD_SYSTEM_FAILURE;
+				state = ST_SKIPSPHEX;
+				lp = lstart - 1;
+				continue;
+			} else {
+				ASN_DEBUG("state %d at %d", state, lp - lstart);
+				break;
+			}
+		/* [A-Fa-f] */
+		case 0x41:case 0x42:case 0x43:case 0x44:case 0x45:case 0x46:
+		case 0x61:case 0x62:case 0x63:case 0x64:case 0x65:case 0x66:
+			switch(state) {
+			case ST_SKIPSPHEX:
+			case ST_SKIPSPACE: /* Fall through */
+			case ST_HEXDIGIT1:
+				value = lv - ((lv < 0x61) ? 0x41 : 0x61);
+				value += 10;
+				value <<= 4;
+				state = ST_HEXDIGIT2;
+				continue;
+			case ST_HEXDIGIT2:
+				value += lv - ((lv < 0x61) ? 0x41 : 0x61);
+				value += 10;
+				st->buf[st->size++] = value;
+				state = ST_HEXCOLON;
+				continue;
+			case ST_DIGITS:
+				ASN_DEBUG("INTEGER re-evaluate as hex form");
+				if(INTEGER_st_prealloc(st, (chunk_size/3) + 1))
+					return XPBD_SYSTEM_FAILURE;
+				state = ST_SKIPSPHEX;
+				lp = lstart - 1;
+				continue;
+			default:
+				break;
+			}
+			break;
 		}
 
 		/* Found extra non-numeric stuff */
+		ASN_DEBUG("Found non-numeric 0x%2x at %d",
+			lv, lp - lstart);
 		state = ST_EXTRASTUFF;
 		break;
 	}
 
-	if(state != ST_DIGITS) {
+	switch(state) {
+	case ST_DIGITS:
+		/* Everything is cool */
+		break;
+	case ST_HEXCOLON:
+		st->buf[st->size] = 0;	/* Just in case termination */
+		return XPBD_BODY_CONSUMED;
+	case ST_HEXDIGIT1:
+	case ST_HEXDIGIT2:
+	case ST_SKIPSPHEX:
+		return XPBD_BROKEN_ENCODING;
+	default:
 		if(xer_is_whitespace(lp, lstop - lp)) {
 			if(state != ST_EXTRASTUFF)
 				return XPBD_NOT_BODY_IGNORE;
-			/* Fall through */
+			break;
 		} else {
-			ASN_DEBUG("No useful digits in output");
+			ASN_DEBUG("INTEGER: No useful digits (state %d)",
+				state);
 			return XPBD_BROKEN_ENCODING;	/* No digits */
 		}
+		break;
 	}
 
 	value *= sign;	/* Change sign, if needed */
diff --git a/skeletons/converter-sample.c b/skeletons/converter-sample.c
index c8ee34f..d146f32 100644
--- a/skeletons/converter-sample.c
+++ b/skeletons/converter-sample.c
@@ -35,8 +35,10 @@
  * Open file and parse its contens.
  */
 static void *data_decode_from_file(asn_TYPE_descriptor_t *asnTypeOfPDU,
-	const char *fname, ssize_t suggested_bufsize);
+	FILE *f, const char *filename, ssize_t suggested_bufsize);
 static int write_out(const void *buffer, size_t size, void *key);
+static FILE *argument_to_file(char *av[], int idx);
+static char *argument_to_name(char *av[], int idx);
 
        int opt_debug;	/* -d */
 static int opt_check;	/* -c */
@@ -71,7 +73,7 @@
 }
 
 int
-main(int ac, char **av) {
+main(int ac, char *av[]) {
 	static asn_TYPE_descriptor_t *pduType = &PDU_Type;
 	ssize_t suggested_bufsize = 8192;  /* close or equal to stdio buffer */
 	int number_of_iterations = 1;
@@ -206,15 +208,17 @@
 	   * Process all files in turn.
 	   */
 	  for(ac_i = 0; ac_i < ac; ac_i++) {
-		char *fname = av[ac_i];
-		void *structure;
 		asn_enc_rval_t erv;
+		void *structure;	/* Decoded structure */
+		FILE *file = argument_to_file(av, ac_i);
+		char *name = argument_to_name(av, ac_i);
 
 		/*
 		 * Decode the encoded structure from file.
 		 */
 		structure = data_decode_from_file(pduType,
-				fname, suggested_bufsize);
+				file, name, suggested_bufsize);
+		if(file && file != stdin) fclose(file);
 		if(!structure) {
 			/* Error message is already printed */
 			exit(EX_DATAERR);
@@ -227,14 +231,14 @@
 			if(asn_check_constraints(pduType, structure,
 				errbuf, &errlen)) {
 				fprintf(stderr, "%s: ASN.1 constraint "
-					"check failed: %s\n", fname, errbuf);
+					"check failed: %s\n", name, errbuf);
 				exit(EX_DATAERR);
 			}
 		}
 
 		switch(oform) {
 		case OUT_NULL:
-			fprintf(stderr, "%s: decoded successfully\n", fname);
+			fprintf(stderr, "%s: decoded successfully\n", name);
 			break;
 		case OUT_TEXT:	/* -otext */
 			asn_fprint(stdout, pduType, structure);
@@ -242,7 +246,7 @@
 		case OUT_XER:	/* -oxer */
 			if(xer_fprint(stdout, pduType, structure)) {
 				fprintf(stderr, "%s: Cannot convert into XML\n",
-					fname);
+					name);
 				exit(EX_UNAVAILABLE);
 			}
 			break;
@@ -250,7 +254,7 @@
 			erv = der_encode(pduType, structure, write_out, stdout);
 			if(erv.encoded < 0) {
 				fprintf(stderr, "%s: Cannot convert into DER\n",
-					fname);
+					name);
 				exit(EX_UNAVAILABLE);
 			}
 			break;
@@ -258,7 +262,7 @@
 			erv = uper_encode(pduType, structure, write_out, stdout);
 			if(erv.encoded < 0) {
 				fprintf(stderr, "%s: Cannot convert into Unaligned PER\n",
-					fname);
+					name);
 				exit(EX_UNAVAILABLE);
 			}
 			break;
@@ -325,7 +329,8 @@
 	DynamicBuffer.length += bySize;
 }
 
-static void *data_decode_from_file(asn_TYPE_descriptor_t *pduType, const char *fname, ssize_t suggested_bufsize) {
+static void *
+data_decode_from_file(asn_TYPE_descriptor_t *pduType, FILE *file, const char *filename, ssize_t suggested_bufsize) {
 	static char *fbuf;
 	static ssize_t fbuf_size;
 	static asn_codec_ctx_t s_codec_ctx;
@@ -333,26 +338,18 @@
 	void *structure = 0;
 	asn_dec_rval_t rval;
 	size_t rd;
-	FILE *fp;
+
+	if(!file) {
+		fprintf(stderr, "%s: %s\n", filename, strerror(errno));
+		return 0;
+	}
 
 	if(opt_stack) {
 		s_codec_ctx.max_stack_size = opt_stack;
 		opt_codec_ctx = &s_codec_ctx;
 	}
 
-	if(strcmp(fname, "-")) {
-		DEBUG("Processing file %s", fname);
-		fp = fopen(fname, "r");
-	} else {
-		DEBUG("Processing %s", "standard input");
-		fname = "stdin";
-		fp = stdin;
-	}
-
-	if(!fp) {
-		fprintf(stderr, "%s: %s\n", fname, strerror(errno));
-		return 0;
-	}
+	DEBUG("Processing %s", filename);
 
 	/* prepare the file buffer */
 	if(fbuf_size != suggested_bufsize) {
@@ -374,7 +371,7 @@
 	rval.code = RC_WMORE;
 	rval.consumed = 0;
 
-	while((rd = fread(fbuf, 1, fbuf_size, fp)) || !feof(fp)) {
+	while((rd = fread(fbuf, 1, fbuf_size, file)) || !feof(file)) {
 		char  *i_bptr;
 		size_t i_size;
 
@@ -425,7 +422,6 @@
 		case RC_OK:
 			DEBUG("RC_OK, finishing up with %ld",
 				(long)rval.consumed);
-			if(fp != stdin) fclose(fp);
 			return structure;
 		case RC_WMORE:
 			/*
@@ -448,14 +444,12 @@
 		break;
 	}
 
-	fclose(fp);
-
 	/* Clean up partially decoded structure */
 	ASN_STRUCT_FREE(*pduType, structure);
 
 	fprintf(stderr, "%s: "
 		"Decode failed past byte %ld: %s\n",
-		fname, (long)(DynamicBuffer.bytes_shifted
+		filename, (long)(DynamicBuffer.bytes_shifted
 			+ DynamicBuffer.offset + rval.consumed),
 		(rval.code == RC_WMORE)
 			? "Unexpected end of input"
@@ -469,3 +463,27 @@
 	FILE *fp = (FILE *)key;
 	return (fwrite(buffer, 1, size, fp) == size) ? 0 : -1;
 }
+
+static int argument_is_stdin(char *av[], int idx) {
+	if(strcmp(av[idx], "-")) {
+		return 0;	/* Certainly not <stdin> */
+	} else {
+		/* This might be <stdin>, unless `./program -- -` */
+		if(strcmp(av[-1], "--"))
+			return 1;
+		else
+			return 0;
+	}
+}
+
+static FILE *argument_to_file(char *av[], int idx) {
+	return argument_is_stdin(av, idx)
+		? stdin
+		: fopen(av[idx], "r");
+}
+
+static char *argument_to_name(char *av[], int idx) {
+	return argument_is_stdin(av, idx)
+		? "standard input"
+		: av[idx];
+}