int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it, int tag, int aclass)
{
	const ASN1_TEMPLATE *tt = NULL;
	unsigned char *p = NULL;
	int i, seqcontlen, seqlen;
	ASN1_STRING *strtmp;
	const ASN1_COMPAT_FUNCS *cf;
	const ASN1_EXTERN_FUNCS *ef;
	const ASN1_AUX *aux = it->funcs;
	ASN1_aux_cb *asn1_cb;
	if((it->itype != ASN1_ITYPE_PRIMITIVE) && !*pval) return 0;
	if(aux && aux->asn1_cb) asn1_cb = aux->asn1_cb;
	else asn1_cb = 0;

	switch(it->itype) {

		case ASN1_ITYPE_PRIMITIVE:
		if(it->templates)
			return ASN1_template_i2d(pval, out, it->templates);
		return asn1_i2d_ex_primitive(pval, out, it, tag, aclass);
		break;

		case ASN1_ITYPE_MSTRING:
		strtmp = (ASN1_STRING *)*pval;
		return asn1_i2d_ex_primitive(pval, out, it, -1, 0);

		case ASN1_ITYPE_CHOICE:
		if(asn1_cb && !asn1_cb(ASN1_OP_I2D_PRE, pval, it))
				return 0;
		i = asn1_get_choice_selector(pval, it);
		if((i >= 0) && (i < it->tcount)) {
			ASN1_VALUE **pchval;
			const ASN1_TEMPLATE *chtt;
			chtt = it->templates + i;
			pchval = asn1_get_field_ptr(pval, chtt);
			return ASN1_template_i2d(pchval, out, chtt);
		} 
		/* Fixme: error condition if selector out of range */
		if(asn1_cb && !asn1_cb(ASN1_OP_I2D_POST, pval, it))
				return 0;
		break;

		case ASN1_ITYPE_EXTERN:
		/* If new style i2d it does all the work */
		ef = it->funcs;
		return ef->asn1_ex_i2d(pval, out, it, tag, aclass);

		case ASN1_ITYPE_COMPAT:
		/* old style hackery... */
		cf = it->funcs;
		if(out) p = *out;
		i = cf->asn1_i2d(*pval, out);
		/* Fixup for IMPLICIT tag: note this messes up for tags > 30,
		 * but so did the old code. Tags > 30 are very rare anyway.
		 */
		if(out && (tag != -1))
			*p = aclass | tag | (*p & V_ASN1_CONSTRUCTED);
		return i;
		
		case ASN1_ITYPE_SEQUENCE:
		i = asn1_enc_restore(&seqcontlen, out, pval, it);
		/* An error occurred */
		if(i < 0) return 0;
		/* We have a valid cached encoding... */
		if(i > 0) return seqcontlen;
		/* Otherwise carry on */
		seqcontlen = 0;
		/* If no IMPLICIT tagging set to SEQUENCE, UNIVERSAL */
		if(tag == -1) {
			tag = V_ASN1_SEQUENCE;
			aclass = V_ASN1_UNIVERSAL;
		}
		if(asn1_cb && !asn1_cb(ASN1_OP_I2D_PRE, pval, it))
				return 0;
		/* First work out sequence content length */
		for(i = 0, tt = it->templates; i < it->tcount; tt++, i++) {
			const ASN1_TEMPLATE *seqtt;
			ASN1_VALUE **pseqval;
			seqtt = asn1_do_adb(pval, tt, 1);
			if(!seqtt) return 0;
			pseqval = asn1_get_field_ptr(pval, seqtt);
			/* FIXME: check for errors in enhanced version */
			/* FIXME: special handling of indefinite length encoding */
			seqcontlen += ASN1_template_i2d(pseqval, NULL, seqtt);
		}
		seqlen = ASN1_object_size(1, seqcontlen, tag);
		if(!out) return seqlen;
		/* Output SEQUENCE header */
		ASN1_put_object(out, 1, seqcontlen, tag, aclass);
		for(i = 0, tt = it->templates; i < it->tcount; tt++, i++) {
			const ASN1_TEMPLATE *seqtt;
			ASN1_VALUE **pseqval;
			seqtt = asn1_do_adb(pval, tt, 1);
			if(!seqtt) return 0;
			pseqval = asn1_get_field_ptr(pval, seqtt);
			/* FIXME: check for errors in enhanced version */
			ASN1_template_i2d(pseqval, out, seqtt);
		}
		if(asn1_cb  && !asn1_cb(ASN1_OP_I2D_POST, pval, it))
				return 0;
		return seqlen;

		default:
		return 0;
	}
	return 0;
}