Initial revision

diff --git a/skeletons/ber_decoder.c b/skeletons/ber_decoder.c
new file mode 100644
index 0000000..135e791
--- /dev/null
+++ b/skeletons/ber_decoder.c
@@ -0,0 +1,230 @@
+/*-
+ * Copyright (c) 2003 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#include <constr_TYPE.h>
+#include <assert.h>
+
+#define	ADVANCE(num_bytes)	do {			\
+		size_t num = num_bytes;			\
+		ptr += num;				\
+		size -= num;				\
+		consumed_myself += num;			\
+	} while(0)
+#define	RETURN(_code)	do {				\
+		ber_dec_rval_t rval;			\
+		rval.code = _code;			\
+		rval.consumed = consumed_myself;	\
+		return rval;				\
+	} while(0)
+
+/*
+ * The BER decoder of any type.
+ */
+ber_dec_rval_t
+ber_decode(asn1_TYPE_descriptor_t *type_descriptor,
+	void **struct_ptr, void *ptr, size_t size) {
+
+	/*
+	 * Invoke type-specific decoder.
+	 */
+	return type_descriptor->ber_decoder(type_descriptor,
+		struct_ptr,	/* Pointer to the destination structure */
+		ptr, size,	/* Buffer and its size */
+		0		/* Default tag mode is 0 */
+		);
+}
+
+/*
+ * Check the set of <TL<TL<TL...>>> tags matches the definition.
+ */
+ber_dec_rval_t
+ber_check_tags(asn1_TYPE_descriptor_t *head, ber_dec_ctx_t *ctx,
+		void *ptr, size_t size, int tag_mode,
+		ber_tlv_len_t *last_length, int *opt_tlv_form) {
+	ssize_t consumed_myself = 0;
+	ssize_t tag_len;
+	ssize_t len_len;
+	ber_tlv_tag_t tlv_tag;
+	ber_tlv_len_t tlv_len;
+	ber_tlv_len_t limit_len = -1;
+	int expect_00_terminators = 0;
+	int tlv_constr = -1;	/* If CHOICE, opt_tlv_form is not given */
+	int tagno;
+
+	/*
+	 * So what does all this tags_impl_skip stuff mean?
+	 * Imagine two types,
+	 * 	A ::= [5] IMPLICIT	T
+	 * 	B ::= [2] EXPLICIT	T
+	 * Where T is defined as
+	 *	T ::= [4] IMPLICIT SEQUENCE { ... }
+	 * 
+	 * Let's say, we are starting to decode type A, given the
+	 * following TLV stream: <5> <0>. What does this mean?
+	 * It means that the type A contains type T which is,
+	 * in turn, empty.
+	 * Remember though, that we are still in A. We cannot
+	 * just pass control to the type T decoder. Why? Because
+	 * the type T decoder expects <4> <0>, not <5> <0>.
+	 * So, we must make sure we are going to receive <5> while
+	 * still in A, then pass control to the T decoder, indicating
+	 * that the tag <4> was implicitly skipped. The decoder of T
+	 * hence will be prepared to treat <4> as valid tag, and decode
+	 * it appropriately.
+	 */
+
+	/*
+	 * We have a list of tags that must occur in the stream:
+	 * 	{A,B,C}
+	 * However, it may be indicated that the type is
+	 * implicitly tagged in the caller, so it really boils down to the
+	 *	{I,B,C} or even {I,C}
+	 * This is because the implicit tag at above structure may replace 
+	 * zero or more (or every) tags which follow it. We don't care
+	 * about the precise number, as it is already computed for us
+	 * by the ASN.1 compiler and placed into head->tags_impl_skip.
+	 * So let's suppose the only tag left after implicit tagging is {I}.
+	 * Yet, the table we have is {A,B,C} and head->tags_impl_skip=3.
+	 * We need to check at least one tag in the loop, so the loop range
+	 * is modified so it will be invoked at least one time.
+	 */
+	tagno = ctx->step	/* Continuing where left previously */
+		+ (tag_mode==-1?(head->tags_impl_skip-1):0)
+		+ (tag_mode==1?-1:0)
+		;
+	//assert(head->tags_count >= 1); ?May not be the case for CHOICE!
+	assert(tagno < head->tags_count);	/* At least one loop */
+	for((void)tagno; tagno < head->tags_count; tagno++, ctx->step++) {
+
+		/*
+		 * Fetch and process T from TLV.
+		 */
+		tag_len = ber_fetch_tag(ptr, size, &tlv_tag);
+			ASN_DEBUG("Fetching tag from {%p,%ld} %02X: "
+				"len %ld, tag %s",
+				ptr, (long)size,
+				*(uint8_t *)ptr, (long)tag_len,
+				ber_tlv_tag_string(tlv_tag));
+		switch(tag_len) {
+		case -1: RETURN(RC_FAIL);
+		case 0: RETURN(RC_WMORE);
+		}
+
+		tlv_constr = BER_TLV_CONSTRUCTED(ptr);
+
+		/*
+		 * If {I}, don't check anything.
+		 * If {I,B,C}, check B and C unless we're at I.
+		 */
+		if(tag_mode != 0 && ctx->step == 0) {
+			/*
+			 * We don't expect tag to match here.
+			 * It's just because we don't know how the tag
+			 * is supposed to look like.
+			 */
+		} else {
+		    assert(tagno >= 0);	/* Guaranteed by the code above */
+		    if(tlv_tag != head->tags[tagno]) {
+			/*
+			 * Unexpected tag. Too bad.
+			 */
+		    	ASN_DEBUG("Expected: %s, expectation failed",
+				ber_tlv_tag_string(head->tags[tagno]));
+			RETURN(RC_FAIL);
+		    }
+		}
+
+		/*
+		 * Attention: if there are more tags expected,
+		 * ensure that the current tag is presented
+		 * in constructed form (it contains other tags!).
+		 * If this one is the last one, check that the tag form
+		 * matches the one given in descriptor.
+		 */
+		if(tagno < (head->tags_count - 1)) {
+			if(tlv_constr == 0) {
+				RETURN(RC_FAIL);
+			}
+		} else {
+			if(head->last_tag_form != tlv_constr
+			&& head->last_tag_form != -1) {
+				RETURN(RC_FAIL);
+			}
+		}
+
+		/*
+		 * Fetch and process L from TLV.
+		 */
+		len_len = ber_fetch_length(tlv_constr,
+			ptr + tag_len, size - tag_len, &tlv_len);
+		switch(len_len) {
+		case -1: RETURN(RC_FAIL);
+		case 0: RETURN(RC_WMORE);
+		}
+
+		/*
+		 * FIXME
+		 * As of today, the chain of tags
+		 * must either contain several indefinite length TLVs,
+		 * or several definite length ones.
+		 * No mixing is allowed.
+		 */
+		if(tlv_len == -1) {
+			/*
+			 * Indefinite length.
+			 */
+			if(limit_len == -1) {
+				expect_00_terminators++;
+			} else {
+				ASN_DEBUG("Unexpected indefinite length "
+					"in a chain of definite lengths");
+				RETURN(RC_FAIL);
+			}
+			ADVANCE(tag_len + len_len);
+			continue;
+		} else {
+			if(expect_00_terminators) {
+				ASN_DEBUG("Unexpected definite length "
+					"in a chain of indefinite lengths");
+				RETURN(RC_FAIL);
+			}
+		}
+
+		/*
+		 * Check that multiple TLVs specify ever decreasing length,
+		 * which is consistent.
+		 */
+		if(limit_len == -1) {
+			limit_len    = tlv_len + tag_len + len_len;
+		} else if(limit_len != tlv_len + tag_len + len_len) {
+			/*
+			 * Inner TLV specifies length which is inconsistent
+			 * with the outer TLV's length value.
+			 */
+			ASN_DEBUG("Outer TLV is %d and inner is %d",
+				limit_len, tlv_len);
+			RETURN(RC_FAIL);
+		}
+
+		ADVANCE(tag_len + len_len);
+
+		limit_len -= (tag_len + len_len);
+		if(size > limit_len) {
+			/*
+			 * Make sure that we won't consume more bytes
+			 * from the large buffer than the inferred limit.
+			 */
+			size = limit_len;
+		}
+	}
+
+	if(opt_tlv_form)
+		*opt_tlv_form = tlv_constr;
+	if(expect_00_terminators)
+		*last_length = -expect_00_terminators;
+	else
+		*last_length = tlv_len;
+
+	RETURN(RC_OK);
+}