/*  asndebin.c
* ===========================================================================
*
*                            PUBLIC DOMAIN NOTICE                          
*               National Center for Biotechnology Information
*                                                                          
*  This software/database is a "United States Government Work" under the   
*  terms of the United States Copyright Act.  It was written as part of    
*  the author's official duties as a United States Government employee and 
*  thus cannot be copyrighted.  This software/database is freely available 
*  to the public for use. The National Library of Medicine and the U.S.    
*  Government have not placed any restriction on its use or reproduction.  
*                                                                          
*  Although all reasonable efforts have been taken to ensure the accuracy  
*  and reliability of the software and data, the NLM and the U.S.          
*  Government do not and cannot warrant the performance or results that    
*  may be obtained by using this software or data. The NLM and the U.S.    
*  Government disclaim all warranties, express or implied, including       
*  warranties of performance, merchantability or fitness for any particular
*  purpose.                                                                
*                                                                          
*  Please cite the author in any work or product based on this material.   
*
* ===========================================================================
*
* File Name:  asndebin.c
*
* Author:  James Ostell
*
* Version Creation Date: 3/4/91
*
* $Revision: 6.16 $
*
* File Description:
*   Special binary form (BER) decoder for ASN.1
*
* Modifications:  
* --------------------------------------------------------------------------
* Date     Name        Description of modification
* -------  ----------  -----------------------------------------------------
*
* ==========================================================================
*/

#include "asnbuild.h"

#define ASNDEBIN_EOF -2

static void RelaxVisibleStringUTF8 (AsnIoPtr aip, AsnTypePtr atp) {

  Int2  isa = 0;
  Uint1 fix_utf8_in;

  if (aip == NULL || atp == NULL) return;
  if (atp->type != NULL) {
    isa = atp->type->isa;
  } else {
    isa = atp->isa;
  }
  if (! ISA_STRINGTYPE (isa)) return;
  if (aip->tagclass != atp->tagclass) return;
  if (aip->tagnumber == atp->tagnumber) return;

  /* !!! CXX-3341 temporarily allow UTF8/VisibleString flexibility !!! */
  fix_utf8_in = aip->fix_utf8_in;
  if (fix_utf8_in == 3) return;

  if (aip->tagnumber == TAG_VISIBLESTRING && atp->tagnumber == TAG_UTF8STRING) {
    aip->tagnumber = atp->tagnumber;
  } else if (aip->tagnumber == TAG_UTF8STRING && atp->tagnumber == TAG_VISIBLESTRING) {
    aip->tagnumber = atp->tagnumber;

    if (fix_utf8_in == 0) {
      aip->utf8_read = TRUE;
    } else if (fix_utf8_in == 1) {
      if (! aip->utf8_read) {
        AsnIoErrorMsg(aip, 107);
      }
      aip->utf8_read = TRUE;
    } else if (fix_utf8_in == 2) {
      AsnIoErrorMsg(aip, 107);
      aip->utf8_read = TRUE;
    }
  }
}

/*****************************************************************************
*
*   AsnTypePtr AsnBinReadId(aip, atp)
*   	reads identifier for next value
*
*****************************************************************************/
NLM_EXTERN AsnTypePtr LIBCALL  AsnBinReadId (AsnIoPtr aip, AsnTypePtr atp)
{
	Int4 used;

	Int2 isa;
	AsnTypePtr atp2, atp3, atp4;
	Boolean found;
    PstackPtr psp, prevpsp, currpsp;

	used = 0;

	if (! aip->type_indent)    /* just starting or ending reading */
	{
		if (atp == NULL)       /* starting an unknown item */
		{
			AsnIoErrorMsg(aip, 1);
			return NULL;
		}

		used = AsnDeBinScanTag(aip);   /* read the tag */

		if (used == ASNDEBIN_EOF)      /* end of file */
			return NULL;
                /* see RT toolbox ticket 15030485 for the below fix */

		atp2 = AsnFindBaseType(atp);   /* find the base type */
		if (atp2 == NULL)
			return atp2;
		if (atp2->type == NULL)
			return atp2->type;
		isa = atp2->type->isa;


		if (atp2->type->isa != CHOICE_TYPE)
		{
			atp2 = atp2->type;         /* will be primitive tag */

            RelaxVisibleStringUTF8 (aip, atp2);

			if ((aip->tagclass != atp2->tagclass) ||
				(aip->tagnumber != atp2->tagnumber))
				found = FALSE;
			else
				found = TRUE;
		}
		else
		{
			atp2 = (AsnTypePtr) atp->branch;      /* look for a match among choices */
			found = FALSE;
			while ((atp2 != NULL) && (! found))
			{
                RelaxVisibleStringUTF8 (aip, atp2);

				if ((aip->tagclass == atp2->tagclass) &&
					(aip->tagnumber == atp2->tagnumber))
					found = TRUE;
				else
					atp2 = atp2->next;
			}
			aip->tagsaved = TRUE;    /* will need tag for which CHOICE next round */
		}

		if (! found)
		{
			AsnIoErrorMsg(aip, 2, AsnErrGetTypeName(atp->name));
			return NULL;
		}

        psp = & aip->typestack[0];
        psp->len = aip->length;
        psp->type = atp;
        psp->resolved = FALSE;
        psp->tag_indef = FALSE;

		return atp;
	}

    prevpsp = & aip->typestack[aip->type_indent - 1];   /* previous level */
    currpsp = prevpsp + 1;                              /* current level */

							/** end of definate length struct ****/
	if (prevpsp->len == 0)
	{
		prevpsp->resolved = TRUE;
		return prevpsp->type;
	}

	if (currpsp->type != NULL)  /* reading a struct */
	{
		if (! aip->tagsaved)       /* already read it? */
		{
			used = AsnDeBinScanTag(aip);    /* read the tag */
			if (! AsnDeBinDecr(used, aip))  /* decrement length counter */
				return NULL;
		}

		if ((aip->length == 0) && (aip->tagclass == 0) &&
			(aip->tagnumber == 0))     /* length 0, could be end of contents */
		{
			if (prevpsp->len == INDEFINATE)
			{
                prevpsp->resolved = TRUE;
				aip->tagsaved = FALSE;
				if (prevpsp->tag_indef)      /* indefinate user tag? */
				{
					used = AsnDeBinScanTag(aip);   /* should be 00 for tag */
					if (! AsnDeBinDecr(used, aip))
						return NULL;
					if ((aip->length == 0) && (aip->tagclass == 0) &&
						(aip->tagnumber == 0))
						prevpsp->tag_indef = FALSE;
					else
					{
						AsnIoErrorMsg(aip, 3, AsnErrGetTypeName(prevpsp->type->name));
						return NULL;
					}
				}
				return prevpsp->type;
			}
		}
		else
			aip->tagsaved = TRUE;
	}


	if (aip->type_indent)     /* check for SEQOF and SETOF */
	{
		atp = prevpsp->type;
		atp2 = AsnFindBaseType(atp);
		isa = atp2->type->isa;
		if ((isa == SEQOF_TYPE) || (isa == SETOF_TYPE))
		{
			atp = (AsnTypePtr) atp2->branch;   /* set of what type? */
			currpsp->type = atp;
            currpsp->len = INDEFINATE;
            currpsp->resolved = FALSE;
            currpsp->tag_indef = FALSE;
			atp2 = AsnFindBaseType(atp);
			isa = atp2->type->isa;
			atp2 = atp2->type;        /* get universal type */
			used = AsnDeBinScanTag(aip);   /* read the tag */
			if (! AsnDeBinDecr(used, aip))
				return NULL;

            RelaxVisibleStringUTF8 (aip, atp2);

			if ((aip->tagclass != atp2->tagclass) ||
				(aip->tagnumber != atp2->tagnumber))
			{     
				if ((aip->length == 0) && (aip->tagclass == 0) &&
					(aip->tagnumber == 0))     /* length 0, could be end of contents */
				{      /* this handles an empty SEQ OF/SET OF ***/
					if (prevpsp->len == INDEFINATE)
					{
            		    prevpsp->resolved = TRUE;
						aip->tagsaved = FALSE;
						if (prevpsp->tag_indef)      /* indefinate user tag? */
						{
							used = AsnDeBinScanTag(aip);   /* should be 00 for tag */
							if (! AsnDeBinDecr(used, aip))
								return NULL;
							if ((aip->length == 0) && (aip->tagclass == 0) &&
								(aip->tagnumber == 0))
								prevpsp->tag_indef = FALSE;
							else
							{
								AsnIoErrorMsg(aip, 3, AsnErrGetTypeName(prevpsp->type->name));
								return NULL;
							}
						}
						return prevpsp->type;
					}
				}
				else if (isa == CHOICE_TYPE) /* no tag */
				{
					aip->tagsaved = TRUE;
				}
				else
				{
					AsnIoErrorMsg(aip, 4, AsnErrGetTypeName(atp->name));
					return NULL;
				}
			}
			currpsp->len = aip->length;
			return atp;   /* no type identifier */
		}
	}

	used = AsnDeBinScanTag(aip);    /* read the tag */
	if (! AsnDeBinDecr(used, aip))  /* decrement length counter */
		return NULL;

	if ((aip->length == 0) && (aip->tagclass == 0) &&
		(aip->tagnumber == 0))     /* length 0, could be end of contents */
	{
		if (prevpsp->len == INDEFINATE)
		{
            prevpsp->resolved = TRUE;
			aip->tagsaved = FALSE;
			if (prevpsp->tag_indef)      /* indefinate user tag? */
			{
				used = AsnDeBinScanTag(aip);   /* should be 00 for tag */
				if (! AsnDeBinDecr(used, aip))
					return NULL;
				if ((aip->length == 0) && (aip->tagclass == 0) &&
					(aip->tagnumber == 0))
					prevpsp->tag_indef = FALSE;
				else
				{
					AsnIoErrorMsg(aip, 3, AsnErrGetTypeName(prevpsp->type->name));
					return NULL;
				}
			}
			return prevpsp->type;
		}
	}

	atp2 = AsnFindBaseType(prevpsp->type);
	atp = AsnDeBinFindElement(aip, (AsnTypePtr) atp2->branch);
	if (atp == NULL)
	{
		AsnIoErrorMsg(aip, 5, AsnErrGetTypeName(prevpsp->type->name));
		return NULL;
	}

	if (atp2->type->isa == SEQ_TYPE)      /* check sequence order */
	{
		atp3 = currpsp->type;
		if (atp3 != NULL)
		{
			atp4 = (AsnTypePtr) atp2->branch;
			atp3 = atp3->next;
			while (atp4 != atp3)
			{
				if (atp == atp4)
				{
					AsnIoErrorMsg(aip, 6, AsnErrGetTypeName(atp->name), AsnErrGetTypeName(atp2->name));
					return NULL;
				}
				atp4 = atp4->next;
			}
		}
		else
			atp3 = (AsnTypePtr) atp2->branch;

		while ((atp3 != NULL) && (atp3 != atp))
		{
			if (! (atp3->optional || atp3->hasdefault))
			{
				AsnIoErrorMsg(aip, 7, AsnErrGetTypeName(atp3->name), AsnErrGetTypeName(atp2->name));
				return NULL;
			}
			atp3 = atp3->next;
		}
	}

	currpsp->type = atp;
	currpsp->len = aip->length;
	currpsp->resolved = FALSE;    /* mark first use */
	if ((atp->tagclass != TAG_UNIVERSAL) && (atp->tagclass != TAG_NONE)
		&& (aip->length == INDEFINATE))
			currpsp->tag_indef = TRUE;
	return atp;
}

/*****************************************************************************
*
*   Boolean AsnDeBinDecr(used, aip)
*   	decrement all levels above level with used bytes
*
*****************************************************************************/
NLM_EXTERN Boolean AsnDeBinDecr (Int4 used, AsnIoPtr aip)
{
	PstackPtr	psp, psp0;

	if (used == ASNDEBIN_EOF) return FALSE; /* avoid infinite loop in higher levels */

	if (used <= 0) /* tag saved or INDEFINATE */
		return TRUE;

	psp = (psp0 = aip->typestack) + aip->type_indent;
	while (psp > psp0) {
		--psp;
		if (psp->len == INDEFINATE) /* if one INDEFINATE, assume all are */
			return TRUE;
		if ((psp->len -= used) < 0) {
			AsnIoErrorMsg(aip, 8, (int)(psp - psp0) /* level */);
			return FALSE;
		}
	}
	return TRUE;
}

/**** old version ***********************
NLM_EXTERN Boolean AsnDeBinDecr (Int4 used, AsnIoPtr aip)
{
	Int2 level;

	if (used == INDEFINATE)
		return TRUE;

	level = aip->type_indent;
	while (level)
	{
		level--;
		if (aip->typestack[level].len != INDEFINATE)
		{
			aip->typestack[level].len -= used;
			if (aip->typestack[level].len < 0)
			{
				AsnIoErrorMsg(aip, 8, level);
				return FALSE;
			}
		}
	}
	return TRUE;
}
*****************************************/
/*****************************************************************************
*
*   Int4 AsnDeBinScanTag(aip)
*   	scan the next tag-length element
*   	return bytes used from input
*
*****************************************************************************/
NLM_EXTERN Int4 AsnDeBinScanTag (AsnIoPtr aip)
{
	Int4 used;
	Uint4 number;
	Int2 bytes, lenbytes;
	BytePtr bp;
	Byte octet, bit8_7, bit5_1;
	Boolean bit6;

	used = 0;             /* no bytes read yet */
	if (aip->tagsaved)    /* return currently saved tag */
	{
		aip->tagsaved = FALSE;
		return used;        /* no additional bytes read */
	}

	bytes = aip->bytes - aip->offset;    /* current location in buffer */
	if (! bytes)
	{
		bytes = AsnIoReadBlock(aip);
		if (bytes == 0)
			return ASNDEBIN_EOF;
	}
	bp = aip->buf + aip->offset;

						/*** read first octet of tag-id ******/
	octet = *bp;
	bp++; bytes--; used++;

	bit5_1 	= octet & (Byte)0x1f;    /* short form tag number */
	bit6 = (Byte)((octet >> 5) & 0x01);     /*  constructed? */
	bit8_7 = (Byte)((octet >> 6) & 0x03);   /* tag class */

	switch (bit8_7)         /* get tag class */
	{
		case 0x0:
			aip->tagclass = TAG_UNIVERSAL;
			break;
		case 0x1:
			aip->tagclass = TAG_APPLICATION;
			break;
		case 0x2:
			aip->tagclass = TAG_CONTEXT;
			break;
		case 0x3:
			aip->tagclass = TAG_PRIVATE;
			break;
	}

	aip->constructed = bit6;     /* constructed? */

							/*** get the tag-id ****/
	if (bit5_1 != 0x1f)    /* short form */
	{
		aip->tagnumber = bit5_1;    /* that's it */
	}
	else                   /* long form */
	{
		number = 0;     /* have to build it up */
		do
		{
			if (! bytes)
			{
				bytes = AsnIoReadBlock(aip);
                if (! bytes)
                    {AsnIoErrorMsg(aip, 17); return 0;}
				bp = aip->buf;
			}
			octet = *bp;
			bp++; bytes--; used++;
			number = (number << 7) + (octet & 0x7f);
		}
		while ((octet & 0x80) != 0);
		aip->tagnumber = (Int2) number;
	}

								/*** scan the length, if present */
	if (! bytes)
	{
		bytes = AsnIoReadBlock(aip);
        if (! bytes)
             {AsnIoErrorMsg(aip, 17); return 0;}
		bp = aip->buf;
	}

	octet = *bp;
	bp++; bytes--; used++;

	if (! (octet & 0x80))             /* short form used */
		aip->length = octet & 0x7f;
	else if ((octet & 0x7f) == 0x00)    /* indefinate form used */
		aip->length = INDEFINATE;
	else                                 /* long form used */
	{
		number = 0;
		for (lenbytes = (Int2)(octet & 0x7f); lenbytes > 0; lenbytes--)
		{
			if (! bytes)
			{
				bytes = AsnIoReadBlock(aip);
                if (! bytes)
                    {AsnIoErrorMsg(aip, 17); return 0;}
				bp = aip->buf;
			}
			octet = *bp;
			bp++; bytes--; used++;
			number = (number << 8) + octet;
		}
		aip->length = number;
	}

	aip->offset = aip->bytes - bytes;
	aip->used = used;
	return used;
}

/*****************************************************************************
*
*   Int2 AsnBinReadVal(aip, atp, valueptr)
*   	read the value pointed at by atp
*         this should be the same as aip->typestack[aip->type_indent].type
*   	returns START_STRUCT
*               END_STRUCT
*   			1 if ok
*   			0 if an error occurred
*   	if (valueptr == NULL)
*   		just checks that value is of proper type
*
*****************************************************************************/
static CharPtr AsnDeBinReadStringUTF8 (AsnIoPtr aip, AsnTypePtr atp);

NLM_EXTERN Int2 LIBCALL  AsnBinReadVal (AsnIoPtr aip, AsnTypePtr atp, DataValPtr valueptr)
{
	Int4 used;
	Int2 isa, retval;
	Boolean terminalvalue;      /* set if read a terminal value */
	AsnTypePtr atp2;
	Boolean read_value;
	DataVal fake_value;
	AsnTypePtr base_type, curr_type;
	int next_type;
    PstackPtr currpsp, prevpsp;

	currpsp = & aip->typestack[aip->type_indent];
    curr_type = currpsp->type;
    if (aip->type_indent)
        prevpsp = currpsp - 1;
    else
        prevpsp = NULL;

	if (atp == NULL)
		return 0;

	base_type = AsnFindBaseType(atp);   
    if (base_type == NULL)     /* not found */
    {
        base_type = atp;     
        while (base_type->type != NULL)
            base_type = base_type->type;
        if (base_type->imported)
		{
            AsnIoErrorMsg(aip,9, AsnErrGetTypeName(atp->name), AsnErrGetTypeName(base_type->name));
			return 0;
		}
       else
		{
            AsnIoErrorMsg(aip,10, AsnErrGetTypeName(atp->name), AsnErrGetTypeName(base_type->name));
			return 0;
		}
    }
	isa = base_type->type->isa;

	if (valueptr == NULL)       /* just check the value */
	{
		read_value = FALSE;
		valueptr = &fake_value;
	}
	else
		read_value = TRUE;

	retval = 1;       /* normal return */
	terminalvalue = TRUE;    /* most values are terminal values */

	if (ISA_STRINGTYPE(isa) && isa != UTF8STRING_TYPE)
		isa = GENERALSTRING_TYPE;

	switch (isa)
	{
		case SEQ_TYPE:
		case SET_TYPE:
		case SETOF_TYPE:
		case SEQOF_TYPE:
			terminalvalue = FALSE;  /* handled by AsnTypeSetIndent() */
			if ((prevpsp == NULL) || (! prevpsp->resolved))   /* open brace */  
			{
				AsnTypeSetIndent(TRUE, aip, atp);
				while (aip->tagclass != TAG_UNIVERSAL)  /* read to struct */
				{
					used = AsnDeBinScanTag(aip);
					if (! AsnDeBinDecr(used, aip))
						return 0;
				}
				retval = START_STRUCT;
				valueptr->intvalue = START_STRUCT;
				terminalvalue = FALSE;
			}
			else
			{			   /* close brace already read in AsnBinReadId */
				switch (isa)
				{
					case SEQOF_TYPE:
					case SETOF_TYPE:
						break;
					case SEQ_TYPE:       /* check that no more waiting */
        				if (curr_type != NULL)   /* check next one */
		        			atp2 = curr_type->next;
				        else                     /* empty sequence written */
        					atp2 = (AsnTypePtr) base_type->branch;
						while (atp2 != NULL)
						{
							if (! (atp2->optional || atp2->hasdefault))
							{
								AsnIoErrorMsg(aip, 7, AsnErrGetTypeName(atp2->name), AsnErrGetTypeName(atp->name));
								return 0;
							}
							atp2 = atp2->next;
						}
						break;
					default:
						break;
				}
				next_type = aip->type_indent - 2;   /* end any choices */
				while ((next_type >= 0) &&
					(AsnFindBaseIsa(aip->typestack[next_type].type) == CHOICE_TYPE))
				{
                    currpsp = & aip->typestack[next_type];
                    atp2 = currpsp->type;
					if ((atp2->tagclass != TAG_NONE) &&
						(currpsp->tag_indef))
					{
						used = AsnDeBinScanTag(aip);   /* should be 00 for tag */
						if (! AsnDeBinDecr(used, aip))
							return 0;
						if ((aip->length == 0) && (aip->tagclass == 0) &&
							(aip->tagnumber == 0))
							currpsp->tag_indef = FALSE;
						else
						{
							AsnIoErrorMsg(aip, 3, AsnErrGetTypeName(atp2->name));
							return 0;
						}
					}
					next_type--;
				}
				AsnTypeSetIndent(FALSE, aip, curr_type);  
				retval = END_STRUCT;
				valueptr->intvalue = END_STRUCT;
			}
			break;
		case CHOICE_TYPE:
			AsnTypeSetIndent(TRUE, aip, atp);  /* nest down to type */
			terminalvalue = FALSE;
			break;
		default:           /* set up for primitive type */
			base_type = AsnFindBaseType(atp);   
			base_type = base_type->type;        /* point at the type itself */

            RelaxVisibleStringUTF8 (aip, base_type);

			while ((aip->tagclass != base_type->tagclass) ||
				(aip->tagnumber != base_type->tagnumber))/* read to element */
			{
				used = AsnDeBinScanTag(aip);
				if (! AsnDeBinDecr(used, aip))
					return 0;

                RelaxVisibleStringUTF8 (aip, base_type);
			}
			
			if ((aip->bytes - aip->offset) == 0)
            {
                if (! AsnIoReadBlock(aip))
                    {AsnIoErrorMsg(aip, 17); return 0;}
            }
			switch (isa)
			{
				case BOOLEAN_TYPE:
					valueptr->boolvalue = AsnDeBinReadBoolean(aip, atp);
					break;
				case INTEGER_TYPE:
					valueptr->intvalue = AsnDeBinReadBigInt(aip, atp);
					break;
				case ENUM_TYPE:
					valueptr->intvalue = AsnDeBinReadInteger(aip, atp);
					break;
				case BIGINT_TYPE: 
                                        valueptr->bigintvalue = AsnDeBinReadBigInt(aip, atp);
                                        break;
				case REAL_TYPE:
					valueptr->realvalue = AsnDeBinReadReal(aip);
					break;
				case GENERALSTRING_TYPE:
					if (read_value)
					{
						valueptr->ptrvalue = AsnDeBinReadString(aip, atp);
						if (valueptr->ptrvalue == NULL)
							return 0;
					}
					else
						AsnDeBinSkipString(aip);
					break;
				case UTF8STRING_TYPE:
					if (read_value)
					{
						valueptr->ptrvalue = AsnDeBinReadStringUTF8(aip, atp);
						if (valueptr->ptrvalue == NULL)
							return 0;
					}
					else
						AsnDeBinSkipString(aip);
					break;
				case NULL_TYPE:
					AsnDeBinReadNull(aip, atp);
					break;
				case OCTETS_TYPE:
				case STRSTORE_TYPE:
					if (read_value)
					{
						valueptr->ptrvalue = AsnDeBinReadOctets(aip);
						if (valueptr->ptrvalue == NULL) return 0;
					}
					else
						AsnDeBinSkipOctets(aip);
					break;
				default:
					AsnIoErrorMsg(aip, 11, AsnErrGetTypeName(atp->name));
					return 0;
			}
			if (currpsp->tag_indef)     /* indefinate user tag */
			{
				used = AsnDeBinScanTag(aip);   /* should be 00 for tag */
				if (! AsnDeBinDecr(used, aip))
					return 0;
				if ((aip->length == 0) && (aip->tagclass == 0) &&
					(aip->tagnumber == 0))
					currpsp->tag_indef = FALSE;
				else
				{
					AsnIoErrorMsg(aip, 3, AsnErrGetTypeName(atp->name));
					return 0;
				}
			}
	}

	if (terminalvalue)       /* pop out of any CHOICE nests */
	{
	  	next_type = aip->type_indent - 1;   /* end any choices */
		if ((next_type >= 0) &&
			(AsnFindBaseIsa(aip->typestack[next_type].type) == CHOICE_TYPE))
		{
			while ((next_type >= 0) &&
				(AsnFindBaseIsa(aip->typestack[next_type].type) == CHOICE_TYPE))
			{
                currpsp = & aip->typestack[next_type];
				atp2 = currpsp->type;
				if ((curr_type->tagclass != TAG_NONE) &&
					(currpsp->tag_indef))
				{
					used = AsnDeBinScanTag(aip);   /* should be 00 for tag */
					if (! AsnDeBinDecr(used, aip))
						return 0;
					if ((aip->length == 0) && (aip->tagclass == 0) &&
						(aip->tagnumber == 0))
						currpsp->tag_indef = FALSE;
					else
					{
						AsnIoErrorMsg(aip, 3, AsnErrGetTypeName(atp2->name));
						return 0;
					}
				}
				next_type--;
			}
			AsnTypeSetIndent(FALSE, aip, curr_type);
		}
	}

	return retval;
}

/*****************************************************************************
*
*   AsnTypePtr AsnDeBinFindType(aip, amp)
*   	returns pointer to type definition in amp
*   	or NULL if not found
*
*****************************************************************************/
NLM_EXTERN AsnTypePtr AsnDeBinFindType (AsnIoPtr aip, AsnModulePtr amp)
{
	AsnTypePtr atp;
	
	while (amp != NULL)
	{
		atp = AsnDeBinFindElement(aip, amp->types);
		if (atp != NULL)
			return atp;

		amp = amp->next;
	}
	return NULL;
}				

/*****************************************************************************
*
*   AsnTypePtr AsnDeBinFindElement(aip, atp)
*   	finds an element in a list of elements
*   	(elements of SEQ, SET, CHOICE)
*
*****************************************************************************/
NLM_EXTERN AsnTypePtr AsnDeBinFindElement (AsnIoPtr aip, AsnTypePtr atp)
{
	while (atp != NULL)
	{
        RelaxVisibleStringUTF8 (aip, atp);

		if ((aip->tagclass == atp->tagclass) &&
			(aip->tagnumber == atp->tagnumber))
			return atp;
		else
			atp = atp->next;
	}
	return NULL;
}


/*****************************************************************************
*
*   Int4 AsnDeBinReadInteger(aip, atp)
*   	expects an INTEGER or ENUMERATED next
*   	assumes none of it has already been read
*   	does not advance to next token
*   	atp points to the definition of this integer for named values
*
*****************************************************************************/
NLM_EXTERN Int4 AsnDeBinReadInteger (AsnIoPtr aip, AsnTypePtr atp)
{
	Int2 bytes, len;
	Uint4 number;
	BytePtr bp;
	AsnValxNodePtr avnp;
	Int4 value;
    AsnTypePtr atp2;

	bytes = aip->bytes - aip->offset;
	bp = aip->buf + aip->offset;
	len = (Int2) aip->length;
	aip->tagsaved = FALSE;

	if ((*bp & 0x80) != 0)      /* negative number */
		number = (Uint4)(-1);
	else
		number = 0;

	while (len)
	{
		number = (number << 8) | (*bp & 0xff);
		bp++; bytes--; len--;
		if (! bytes)
		{
			bytes = AsnIoReadBlock(aip);
            if (! bytes)
                {AsnIoErrorMsg(aip, 17); return 0;}
			bp = aip->buf;
		}
	}

	value = (Int4) number;

	if (! AsnDeBinDecr(aip->length, aip))
		return 0;
	aip->offset = aip->bytes - bytes;

	if (AsnFindBaseIsa(atp) != ENUM_TYPE)
		return value;

			/******************** read a named integer value *********/

    atp2 = AsnFindBaseType(atp);
	if (atp2->branch != NULL)       /* named values */
	{
		avnp = (AsnValxNodePtr) atp2->branch;
		while (avnp != NULL)
		{
			if (avnp->intvalue == value)
				return value;
			avnp = avnp->next;
		}
	}

	AsnIoErrorMsg(aip, 12, value, AsnErrGetTypeName(atp->name));

	return 0;
}

/*****************************************************************************
*
*   Int8 AsnDeBinReadBigInt(aip, atp)
*   	expects an INTEGER next
*   	assumes none of it has already been read
*   	does not advance to next token
*   	atp points to the definition of this integer for named values
*
*****************************************************************************/
NLM_EXTERN Int8 AsnDeBinReadBigInt (AsnIoPtr aip, AsnTypePtr atp)
{
	Int2 bytes, len;
	Uint8 number;
	BytePtr bp;
	AsnValxNodePtr avnp;
	Int8 value;
    AsnTypePtr atp2;

	bytes = aip->bytes - aip->offset;
	bp = aip->buf + aip->offset;
	len = (Int2) aip->length;
	aip->tagsaved = FALSE;

	if ((*bp & 0x80) != 0)      /* negative number */
		number = (Uint8)(-1);
	else
		number = 0;

	while (len)
	{
		number = (number << 8) | (*bp & 0xff);
		bp++; bytes--; len--;
		if (! bytes)
		{
			bytes = AsnIoReadBlock(aip);
            if (! bytes)
                {AsnIoErrorMsg(aip, 17); return 0;}
			bp = aip->buf;
		}
	}

	value = (Int8) number;

	if (! AsnDeBinDecr(aip->length, aip))
		return 0;
	aip->offset = aip->bytes - bytes;

	if (AsnFindBaseIsa(atp) != ENUM_TYPE)
		return value;

			/******************** read a named integer value *********/

    atp2 = AsnFindBaseType(atp);
	if (atp2->branch != NULL)       /* named values */
	{
		avnp = (AsnValxNodePtr) atp2->branch;
		while (avnp != NULL)
		{
			if (avnp->intvalue == value)
				return value;
			avnp = avnp->next;
		}
	}

	AsnIoErrorMsg(aip, 12, value, AsnErrGetTypeName(atp->name));

	return (Int8)0;
}

/*****************************************************************************
*
*   Boolean AsnDeBinReadBoolean(aip, atp)
*   	expects a BOOLEAN next
*   	assumes none of it has already been read
*   	does not advance to next token
*   	atp points to the definition of this BOOLEN
*
*****************************************************************************/
NLM_EXTERN Boolean AsnDeBinReadBoolean (AsnIoPtr aip, AsnTypePtr atp)
{
	BytePtr bp;
	Byte octet;
	Boolean result = TRUE;

	aip->tagsaved = FALSE;
	if (aip->length != 1)
	{
		AsnIoErrorMsg(aip, 13, AsnErrGetTypeName(atp->name));
		return FALSE;
	}

	bp = aip->buf + aip->offset;
	octet = *bp;

	if (octet == 0)
		result = FALSE;
	else
		result = TRUE;

	aip->offset++;
	AsnDeBinDecr((Int4)1, aip);

	return result;
}

/*****************************************************************************
*
*   Int4 AsnDeBinReadString(aip, atp)
*   	expects any String type next
*   	assumes none of it has already been read
*   	does not advance to next token
*   	atp points to the definition of this String
*   	packs String to single value, if segmented on input
*
*****************************************************************************/
static CharPtr AsnDeBinReadStringEx (AsnIoPtr aip, AsnTypePtr atp, Uint1 fix_non_print)
{
	Int4 len;
	register int bytes, amount;
	register BytePtr bp;
	register CharPtr tmp;
	CharPtr result;

	aip->tagsaved = FALSE;
	len = aip->length;
	AsnDeBinDecr(len, aip);
	result = (CharPtr) MemGet((size_t)(len + 1), FALSE);
	if (result == NULL)
	{
		AsnIoErrorMsg(aip, 14, len, AsnErrGetTypeName(atp->name));
		return NULL;
	}
	result[len] = '\0';

	bp = aip->buf + aip->offset;
	bytes = (int)(aip->bytes - aip->offset);
	tmp = result;

	while (len)
	{
		if ((Int4)bytes >= len)
			amount = (int) len;
		else
			amount = bytes;
		bytes -= amount;
		len -= (Int4)amount;
		while (amount)
		{
			if ((fix_non_print != 2) && ((* bp < ' ') || (*bp > '~')))
			{
				*tmp = '\0';
				if ((fix_non_print == 0) || (fix_non_print == 3))
				{
					AsnIoErrorMsg(aip, 106, (int)(*bp), result);
				}
				*bp = '#';   /* repair string with # */
			}
			*tmp = *bp;
			tmp++; bp++; amount--;
		}
		if (! bytes)
		{
			bytes = (int) AsnIoReadBlock(aip);
            if (! bytes)
                {AsnIoErrorMsg(aip, 17); return NULL;}
			bp = aip->buf;
		}
	}
	aip->offset = aip->bytes - (Int2) bytes;
	return result;
}

NLM_EXTERN CharPtr AsnDeBinReadString (AsnIoPtr aip, AsnTypePtr atp)

{
  return AsnDeBinReadStringEx (aip, atp, aip->fix_non_print);
}

static CharPtr AsnDeBinReadStringUTF8 (AsnIoPtr aip, AsnTypePtr atp)

{
  return AsnDeBinReadStringEx (aip, atp, 2);
}

/*****************************************************************************
*
*   void AsnDeBinSkipString(aip)
*   	expects any String type next
*   	assumes none of it has already been read
*   	does not advance to next token
*   	just reads past end of string
*
*****************************************************************************/
NLM_EXTERN void AsnDeBinSkipString (AsnIoPtr aip)
{
	Int4 len;
	register int bytes, amount;
	register BytePtr bp, tmp;
	int bad;

	aip->tagsaved = FALSE;
	len = aip->length;
	AsnDeBinDecr(len, aip);

	bp = aip->buf + aip->offset;
	tmp = bp;
	bytes = (int)(aip->bytes - aip->offset);

	while (len)
	{
		if ((Int4)bytes >= len)
			amount = (int) len;
		else
			amount = bytes;
		bytes -= amount;
		len -= (Int4)amount;
		while (amount)
		{
			if ((* bp < ' ') || (*bp > '~'))
			{
				bad = (int)(*bp);
				*bp = '\0';
				AsnIoErrorMsg(aip, 106, bad, (CharPtr)tmp);
				aip->io_failure = TRUE;
			}
			bp++; amount--;
		}
		if (! bytes)
		{
			bytes = (int) AsnIoReadBlock(aip);
            if (! bytes)
                {AsnIoErrorMsg(aip, 17); return;}
			bp = aip->buf;
			tmp = bp;
		}
	}
	aip->offset = aip->bytes - (Int2) bytes;
	return;
}

/*****************************************************************************
*
*    AsnDeBinReadOctets(aip)
*   	expects any OCTETS type next
*   	assumes none of it has already been read
*   	does not advance to next token
*
*****************************************************************************/
NLM_EXTERN ByteStorePtr AsnDeBinReadOctets (AsnIoPtr aip)
{
	Int4 len;
	Int2 bytes, amount;
	BytePtr bp;
	ByteStorePtr bsp;
	
	aip->tagsaved = FALSE;
	len = aip->length;
	AsnDeBinDecr(len, aip);
	bsp = BSNew((Uint4)len);
	if (bsp == NULL) return bsp;

	bp = aip->buf + aip->offset;
	bytes = aip->bytes - aip->offset;

	while (len)
	{
		if ((Int4)bytes >= len)
			amount = (Int2) len;
		else
			amount = bytes;
		BSWrite(bsp, bp, amount);
		len -= amount;
		bytes -= amount;
		if (! bytes)
		{
			bytes = AsnIoReadBlock(aip);
            if (! bytes)
                {AsnIoErrorMsg(aip, 17); return NULL;}
			bp = aip->buf;
		}
	}
	aip->offset = aip->bytes - bytes;
	return bsp;
}

/*****************************************************************************
*
*    AsnDeBinSkipOctets(aip)
*   	expects any OCTETS type next
*   	assumes none of it has already been read
*   	does not advance to next token
*
*****************************************************************************/
NLM_EXTERN void AsnDeBinSkipOctets (AsnIoPtr aip)
{
	Int4 len;
	Int2 bytes, amount;
	
	aip->tagsaved = FALSE;
	len = aip->length;
	AsnDeBinDecr(len, aip);

	bytes = aip->bytes - aip->offset;

	while (len)
	{
		if ((Int4)bytes >= len)
			amount = (Int2) len;
		else
			amount = bytes;
		len -= amount;
		bytes -= amount;
		if (! bytes)
		{
			bytes = AsnIoReadBlock(aip);
            if (! bytes)
                {AsnIoErrorMsg(aip, 17); return ;}
		}
	}
	aip->offset = aip->bytes - bytes;
	return;
}

/*****************************************************************************
*
*   FloatHi AsnDeBinReadReal(aip)
*   	expects a REAL next
*   	assumes none of it has already been read
*   	does not advance to next token
*
*****************************************************************************/
NLM_EXTERN FloatHi AsnDeBinReadReal (AsnIoPtr aip)
{
	double result = 0.0;
	Int4 len;
	Int2 bytes, amount;
	BytePtr bp;
	char buf[200], *tmp;

	aip->tagsaved = FALSE;
	len = aip->length;
	AsnDeBinDecr(len, aip);

	bp = aip->buf + aip->offset;
	bytes = aip->bytes - aip->offset;
	tmp = buf;

	while (len)
	{
		if ((Int4)bytes >= len)
			amount = (Int2) len;
		else
			amount = bytes;
		MemCopy(tmp, bp, (size_t)amount);
		tmp += amount;
		len -= amount;
		bytes -= amount;
		if (! bytes)
		{
			bytes = AsnIoReadBlock(aip);
            if (! bytes)
                {AsnIoErrorMsg(aip, 17); return (FloatHi)result;}
			bp = aip->buf;
		}
	}
	aip->offset = aip->bytes - bytes;

	if (buf[0] != '\0')        /* not decimal encoding */
	{
		AsnIoErrorMsg(aip, 15);
		return (FloatHi)result;
	}

	*tmp = '\0';
	                         /* range check base 10 exponents assuming doubles */
	                         
	if ((tmp = StringStr((buf+1), "e")) != NULL)
	{
		int n = atoi(tmp + 1);
		if (n < DBL_MIN_10_EXP)     /** too small, return 0.0 **/
			return (FloatHi)result;
		else if (n > DBL_MAX_10_EXP)   /** too big, return the max */
			return (FloatHi)DBL_MAX;
	}
	
	if (sscanf((buf + 1), "%lf", &result) != 1) {
		result = 0.0;
	}
	return (FloatHi)result;
}

/*****************************************************************************
*
*   void AsnDeBinReadNull(aip, atp)
*
*****************************************************************************/
NLM_EXTERN void AsnDeBinReadNull (AsnIoPtr aip, AsnTypePtr atp)
{
	aip->tagsaved = FALSE;
	if (aip->length != 0)
		AsnIoErrorMsg(aip, 16, AsnErrGetTypeName(atp->name));
		
	return;
}

/*****************************************************************************
*
*   void AsnDeBinSkipStruct(aip)
*   	skips current structure and all substructures
*       Does NOT read following element
*
*****************************************************************************/
NLM_EXTERN void AsnDeBinSkipStruct (AsnIoPtr aip)
{
	aip->tagsaved = FALSE;
	AsnIoErrorMsg(aip, 0);
	return;
}

