/*  asnlex.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: asnlex.c
*
* Author:  James Ostell
*
* Version Creation Date: 3/4/91
*
* $Revision: 6.11 $
*
* File Description:
*   Routines for parsing ASN.1 value nototation (text) messages
*
* Modifications:  
* --------------------------------------------------------------------------
* Date     Name        Description of modification
* -------  ----------  -----------------------------------------------------
*
* ==========================================================================
*/

/*****************************************************************************
*
*   asnlex.c
*   	special text lexxer for asn.1
*
*****************************************************************************/

#include "asnbuild.h"


/*****************************************************************************
*
*   Int2 AsnLexScan(aip, name)
*     scans until name ::= to start reading asn.1 past garbage
*
*****************************************************************************/
static Int2 AsnLexScan PROTO((AsnIoPtr aip, CharPtr name));

/*****************************************************************************
*
*   AsnTypePtr AsnTxtReadId(aip, amp, atp)
*   	reads identifier for next value
*
*****************************************************************************/
NLM_EXTERN AsnTypePtr LIBCALL  AsnTxtReadId (AsnIoPtr aip, AsnModulePtr amp, AsnTypePtr atp)
{
	Int2 token, isa;
	AsnTypePtr atp2, atp3, atp4;
    PstackPtr currpsp, prevpsp;
	Boolean is_ref;

	if (! aip->type_indent)    /* just starting reading */
	{
		if (atp == NULL)       /* starting an unknown item */
		{
			token = AsnLexWord(aip);
			if (token == EOF_TOKEN)
				return NULL;

			if (token != REF)
			{
				AsnIoErrorMsg(aip, 26, aip->linenumber);
				return NULL;
			}
	
			atp = AsnLexFindType(aip, amp);
			if (atp == NULL)
			{
				AsnIoErrorMsg(aip, 27, aip->linenumber);
				return NULL;
			}

			token = AsnLexWord(aip);
			if (token != ISDEF)
			{
				AsnIoErrorMsg(aip, 28, aip->linenumber);
				return NULL;
			}
		}
		else		   /* reading a known, possibly internal value */
		{
			if ((atp->name != NULL) &&    /* do we need to read a type ref? */
				(IS_UPPER(*atp->name)))
				is_ref = TRUE;
			else
				is_ref = FALSE;

			if ((is_ref) && (aip->scan_for_start))
			{
				token = AsnLexScan(aip, atp->name);  /* scan to start */
				if (token == EOF_TOKEN)
					return NULL;
				if (token != ISDEF)
				{
					AsnIoErrorMsg(aip, 28, aip->linenumber);
					return NULL;
				}
			}
			else if ((is_ref) || (aip->bytes))   /* reading an internal value */
			{
				token = AsnLexWord(aip);
				if (token == EOF_TOKEN)
					return NULL;

				if (! StrMatch(atp->name, aip->word, aip->wordlen))
				{
					AsnIoErrorMsg(aip, 29,AsnErrGetTypeName(atp->name), aip->linenumber);
					return NULL;
				}
				if (token == REF)        /* read of a non-internal value */
				{
					token = AsnLexWord(aip);
					if (token != ISDEF)
					{
						AsnIoErrorMsg(aip, 28, aip->linenumber);
						return NULL;
					}
				}
			}
			
		}

		aip->typestack[0].type = atp;         /* load first type */
		aip->typestack[0].resolved = FALSE;
	
		return atp;
	}

    currpsp = & aip->typestack[aip->type_indent];
    prevpsp = currpsp - 1;

	if (currpsp->type != NULL)   /* reading in a struct */
	{
		token = AsnLexWord(aip);
		switch (token)
		{
			case COMMA:
				break;
			case END_STRUCT:
				return prevpsp->type;

			default:
				AsnIoErrorMsg(aip, 30, aip->linenumber);
				return NULL;
		}
	}

	    /* check for SEQOF and SETOF */
	
	atp = prevpsp->type;
	atp2 = AsnFindBaseType(atp);
	isa = atp2->type->isa;
	if ((isa == SEQOF_TYPE) || (isa == SETOF_TYPE))
	{
		token = AsnLexWord(aip);
        if (token == END_STRUCT)    /* empty set */
            return prevpsp->type;
        aip->tagsaved = TRUE;       /* not empty, note not read */
		atp = (AsnTypePtr) atp2->branch;   /* set of what type? */
		currpsp->type = atp;
		currpsp->resolved = FALSE;
		return atp;   /* no type identifier */
	}
	

	token = AsnLexWord(aip);

	if (token == EOF_TOKEN)
	{
		AsnIoErrorMsg(aip, 17 );
		return NULL;
	}

    if ((token == END_STRUCT) && ((isa == SEQ_TYPE) || (isa == SET_TYPE)))
        return prevpsp->type;   /* empty SET/SEQ */

	if (token != IDENT)
	{
		AsnIoErrorMsg(aip, 31, aip->linenumber);
		return NULL;
	}

	atp = AsnLexFindElement(aip, (AsnTypePtr) atp2->branch);
	if (atp == NULL)
	{
		AsnIoErrorMsg(aip, 32, AsnErrGetTypeName(prevpsp->type->name), aip->linenumber);
		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, 33,AsnErrGetTypeName(atp->name), AsnErrGetTypeName(atp2->name), aip->linenumber);
					return NULL;
				}
				atp4 = atp4->next;
			}
		}
		else
			atp3 = (AsnTypePtr) atp2->branch;

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

	currpsp->type = atp;
	currpsp->resolved = FALSE;    /* mark first use */
	return atp;
}

/*****************************************************************************
*
*   Int2 AsnTxtReadVal(aip, atp, valueptr)
*   	read the value pointed at by atp
*   	returns START_STRUCT
*               END_STRUCT
*   			or 1 if ok
*   			0 if an error
*   	if (valueptr == NULL)
*   		just checks that value is of proper type
*
*****************************************************************************/

static Pointer AsnLexReadStringUTF8 (AsnIoPtr aip, AsnTypePtr atp);
static Boolean AsnLexSkipStringUTF8 (AsnIoPtr aip, AsnTypePtr atp);

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

	retval = 1;  /* assume success */
    currpsp = & aip->typestack[aip->type_indent];
	curr_type = currpsp->type;

	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,35, AsnErrGetTypeName(atp->name), base_type->name, aip->linenumber);
			return 0;
		}
       else
		{
            AsnIoErrorMsg(aip,36, AsnErrGetTypeName(atp->name), base_type->name, aip->linenumber);
			return 0;
		}
    }
	isa = base_type->type->isa;

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

	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 (aip->token != END_STRUCT)   /* should be open brace */
			{
				token = AsnLexWord(aip);    /* read open brace */
				if (token != START_STRUCT)
				{
					AsnIoErrorMsg(aip, 37, aip->linenumber);
					return 0;
				}
				AsnTypeSetIndent(TRUE, aip, atp);  
				retval = START_STRUCT;
				valueptr->intvalue = START_STRUCT;
				terminalvalue = FALSE;
			}
			else
			{			   /* close brace already read in AsnTxtReadId */
				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, 34, AsnErrGetTypeName(atp2->name), base_type->name, aip->linenumber);
								return 0;
							}
							atp2 = atp2->next;
						}
						break;
					default:
						break;
				}
				AsnTypeSetIndent(FALSE, aip, atp);  
				retval = END_STRUCT;
				valueptr->intvalue = END_STRUCT;
			}
			break;
		case CHOICE_TYPE:
			AsnTypeSetIndent(TRUE, aip, atp);  /* nest down to type */
			terminalvalue = FALSE;
			break;
		case BOOLEAN_TYPE:
			valueptr->boolvalue = AsnLexReadBoolean(aip, atp);
			break;
		case INTEGER_TYPE:
			valueptr->intvalue = AsnLexReadBigInt(aip, atp);
			break;
		case ENUM_TYPE:
			valueptr->intvalue = AsnLexReadInteger(aip, atp);
			break;
		case BIGINT_TYPE:
			valueptr->bigintvalue = AsnLexReadBigInt(aip, atp);
			break;
		case REAL_TYPE:
			valueptr->realvalue = AsnLexReadReal(aip, atp);
			break;
		case GENERALSTRING_TYPE:
			if (read_value)
			{
				valueptr->ptrvalue = AsnLexReadString(aip, atp);
				if (valueptr->ptrvalue == NULL)
					return 0;
			}
			else
			{
				if (! AsnLexSkipString(aip, atp))
					return 0;
			}
			break;
		case UTF8STRING_TYPE:
			if (read_value)
			{
				valueptr->ptrvalue = AsnLexReadStringUTF8(aip, atp);
				if (valueptr->ptrvalue == NULL)
					return 0;
			}
			else
			{
				if (! AsnLexSkipStringUTF8(aip, atp))
					return 0;
			}
			break;
		case NULL_TYPE:
			AsnLexReadNull(aip, atp);
			break;
		case OCTETS_TYPE:
			if (read_value)
			{
				valueptr->ptrvalue = AsnLexReadOctets(aip, atp);
				if (valueptr->ptrvalue == NULL) return 0;
			}
			else
				AsnLexSkipOctets(aip, atp);
			break;
		case STRSTORE_TYPE:
			if (read_value)
			{
				valueptr->ptrvalue = (ByteStorePtr) AsnLexReadString(aip, atp);
				if (valueptr->ptrvalue == NULL)
					return 0;
			}
			else
			{
				if (! AsnLexSkipString(aip, atp))
					return 0;
			}
			break;
		default:
			AsnIoErrorMsg(aip, 38,  AsnErrGetTypeName(atp->name),aip->linenumber);
			return 0;
	}

	if (terminalvalue)       /* pop out of any CHOICE nests */
	{
		while ((aip->type_indent) &&
			(AsnFindBaseIsa(aip->typestack[aip->type_indent - 1].type) == CHOICE_TYPE))
			AsnTypeSetIndent(FALSE, aip, atp);
	}

	return retval;
}

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

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

/*****************************************************************************
*
*   AsnTypePtr AsnLexFindElement(aip, atp)
*   	finds an element in a list of elements
*   	(elements of SEQ, SET, CHOICE)
*
*****************************************************************************/
NLM_EXTERN AsnTypePtr AsnLexFindElement (AsnIoPtr aip, AsnTypePtr atp)
{
	while (atp != NULL)
	{
		if (StrMatch(atp->name, aip->word, aip->wordlen))   /* it matches */
			return atp;
		else
			atp = atp->next;
	}
	return NULL;
}


/*****************************************************************************
*
*   Int4 AsnLexReadInteger(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 AsnLexReadInteger (AsnIoPtr aip, AsnTypePtr atp)
{
	Int2 token;
	AsnValxNodePtr avnp;
	AsnTypePtr atp2;

	token = AsnLexWord(aip);      /* read the integer */

	if (token == NUMBER)    /* just a number */
	{
		return AsnLexInteger(aip);
	}

	if (token != IDENT)    /* not number or named value */
	{
		AsnIoErrorMsg(aip, 39, AsnErrGetTypeName(atp->name), aip->linenumber);
		return 0;
	}

			/******************** read a named integer value *********/
	atp2 = AsnFindBaseType(atp);
	if (atp2->branch != NULL)       /* named values */
	{
		avnp = (AsnValxNodePtr) atp2->branch;
		while (avnp != NULL)
		{
			if (StrMatch(avnp->name, aip->word, aip->wordlen))
				return avnp->intvalue;
			avnp = avnp->next;
		}
	}

	if (atp2->type->isa == ENUM_TYPE)   /* enumerated MUST match named value */
	{
		AsnIoErrorMsg(aip, 40, AsnErrGetTypeName(atp->name), aip->linenumber);
		return 0;
	}

		   /******************* could it be a previously defined value? ***/

	AsnIoErrorMsg(aip, 41, AsnErrGetTypeName(atp->name), aip->linenumber);

	return 0;
}

/*****************************************************************************
*
*   Int8 AsnLexReadBigInt(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 AsnLexReadBigInt (AsnIoPtr aip, AsnTypePtr atp)
{
	Int2 token;
	AsnValxNodePtr avnp;
	AsnTypePtr atp2;

	token = AsnLexWord(aip);      /* read the integer */

	if (token == NUMBER)    /* just a number */
	{
		return AsnLexBigInt(aip);
	}

	if (token != IDENT)    /* not number or named value */
	{
		AsnIoErrorMsg(aip, 39, AsnErrGetTypeName(atp->name), aip->linenumber);
		return 0;
	}

			/******************** read a named integer value *********/
	atp2 = AsnFindBaseType(atp);
	if (atp2->branch != NULL)       /* named values */
	{
		avnp = (AsnValxNodePtr) atp2->branch;
		while (avnp != NULL)
		{
			if (StrMatch(avnp->name, aip->word, aip->wordlen))
				return avnp->intvalue;
			avnp = avnp->next;
		}
	}

	if (atp2->type->isa == ENUM_TYPE)   /* enumerated MUST match named value */
	{
		AsnIoErrorMsg(aip, 40, AsnErrGetTypeName(atp->name), aip->linenumber);
		return 0;
	}

		   /******************* could it be a previously defined value? ***/

	AsnIoErrorMsg(aip, 41, AsnErrGetTypeName(atp->name), aip->linenumber);

	return (Int8)0;
}

/*****************************************************************************
*
*   Boolean AsnLexReadBoolean(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 AsnLexReadBoolean (AsnIoPtr aip, AsnTypePtr atp)
{
	AsnLexWord(aip);      /* read the boolean */

	if (StrMatch("TRUE", aip->word, aip->wordlen))
		return TRUE;
	else if (StrMatch("FALSE", aip->word, aip->wordlen))
		return FALSE;

	AsnIoErrorMsg(aip, 42, AsnErrGetTypeName(atp->name), aip->linenumber);

	return FALSE;      /* just for lint */
}

/*****************************************************************************
*
*   Int4 AsnLexReadString(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
*   	if atp->type->isa == a string type
*   		packs String to single value, if segmented on input
*       otherwise returns the stringstoreptr itself
*
*****************************************************************************/
static Int2 AsnLexWordEx (AsnIoPtr aip, Uint1 fix_non_print);

static Pointer AsnLexReadStringEx (AsnIoPtr aip, AsnTypePtr atp, Uint1 fix_non_print)
{
	Int2 token;
	ByteStorePtr ssp;
	CharPtr result=NULL;
	Int4 bytes;

	token = AsnLexWordEx (aip, fix_non_print);      /* read the start */
	if (token != START_STRING)
	{
		AsnIoErrorMsg(aip, 43, AsnErrGetTypeName(atp->name), aip->linenumber);
		return NULL;
	}

	ssp = BSNew(0);
	if (ssp == NULL) return result;
	token = AsnLexWordEx (aip, fix_non_print);   /* read the string(s) */
	while (token == IN_STRING)
	{
		bytes = BSWrite(ssp, aip->word, (Int4)aip->wordlen);
		if (bytes != (Int4)(aip->wordlen))
		{
			BSFree(ssp);
			return result;
		}
		token = AsnLexWordEx (aip, fix_non_print);
	}

	if (token == ERROR_TOKEN) return NULL;

	if (token != END_STRING)
	{
		AsnIoErrorMsg(aip, 44, AsnErrGetTypeName(atp->name), aip->linenumber);
		return NULL;
	}
	if (AsnFindBaseIsa(atp) == STRSTORE_TYPE)    /* string store */
		return ssp;

	result = (CharPtr) BSMerge(ssp, NULL);
	BSFree(ssp);

	return result;
}

NLM_EXTERN Pointer AsnLexReadString (AsnIoPtr aip, AsnTypePtr atp)

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

static Pointer AsnLexReadStringUTF8 (AsnIoPtr aip, AsnTypePtr atp)

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

/*****************************************************************************
*
*   void AsnLexSkipString(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
*   	just reads past end of string
*
*****************************************************************************/

static Boolean AsnLexSkipStringEx (AsnIoPtr aip, AsnTypePtr atp, Uint1 fix_non_print)
{
	Int2 token;

	token = AsnLexWordEx(aip, fix_non_print);      /* read the start */
	if (token != START_STRING)
	{
		AsnIoErrorMsg(aip, 43, AsnErrGetTypeName(atp->name), aip->linenumber);
		return FALSE;
	}

	token = AsnLexWordEx(aip, fix_non_print);   /* read the string(s) */
	while (token == IN_STRING)
		token = AsnLexWord(aip);

	if (token == ERROR_TOKEN) return FALSE;

	if (token != END_STRING)
		AsnIoErrorMsg(aip, 44, AsnErrGetTypeName(atp->name), aip->linenumber);

	return TRUE;
}

NLM_EXTERN Boolean AsnLexSkipString (AsnIoPtr aip, AsnTypePtr atp)

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

static Boolean AsnLexSkipStringUTF8 (AsnIoPtr aip, AsnTypePtr atp)

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

/*****************************************************************************
*
*   ByteStorePtr AsnLexReadOctets(aip, atp)
*   	expects Octets type next
*   	assumes none of it has already been read
*   	does not advance to next token
*   	atp points to the definition of this OCTET STRING
*
*****************************************************************************/
NLM_EXTERN ByteStorePtr AsnLexReadOctets (AsnIoPtr aip, AsnTypePtr atp)
{
	Int2 token, len;
	ByteStorePtr ssp = NULL;
	Byte tbuf[500]; /* was 101 - changed to handle occasional hand-edited ASN.1? */
	Int4 bytes, left, added;

	token = AsnLexWord(aip);      /* read the start */
	if (token != START_BITHEX)
	{
		AsnIoErrorMsg(aip, 45, AsnErrGetTypeName(atp->name), aip->linenumber);
		return NULL;
	}
	ssp = BSNew(0);
	if (ssp == NULL) return ssp;
	token = AsnLexWord(aip);   /* read the octet(s) */
	left = 0;
	while (token == IN_BITHEX)
	{
		len = (Int2)(aip->wordlen + left);
		MemCopy((tbuf + left), aip->word, (len - left));
		tbuf[len] = '\0';
		added = AsnTypeStringToHex(tbuf, len, tbuf, &left);
		if (added < 0)
		{
			AsnIoErrorMsg(aip, 46, AsnErrGetTypeName(atp->name), aip->linenumber);
			return NULL;
		}
		if (added)
		{
			bytes = BSWrite(ssp, tbuf, added);
			if (bytes != added)
			{
				ssp = BSFree(ssp);
				return ssp;
			}
		}
		if (left)   /* left a char */
		{
			MemCopy(tbuf, ((aip->word)+(aip->wordlen - left)),left);
		}

		token = AsnLexWord(aip);
	}
	
	if (token != OCTETS)
	{
		AsnIoErrorMsg(aip, 47, AsnErrGetTypeName(atp->name), aip->linenumber);
		return NULL;
	}

	return ssp;
}

/*****************************************************************************
*
*   void AsnLexSkipOctets(aip, atp)
*   	expects Octets type next
*   	assumes none of it has already been read
*   	does not advance to next token
*   	atp points to the definition of this OCTET STRING
*
*****************************************************************************/
NLM_EXTERN void AsnLexSkipOctets (AsnIoPtr aip, AsnTypePtr atp)
{
	Int2 token, left, len;
    Int4 line;

	token = AsnLexWord(aip);      /* read the start */
	if (token != START_BITHEX)
	{
		AsnIoErrorMsg(aip, 45, AsnErrGetTypeName(atp->name), aip->linenumber);
		return;
	}

    line = aip->linenumber;
	token = AsnLexWord(aip);   /* read the octet(s) */
    left = 0;
	while (token == IN_BITHEX)
	{
        len = aip->wordlen + left;
        left = len % 2;
		token = AsnLexWord(aip);
	}

    if (left)
	{
        AsnIoErrorMsg(aip, 48, AsnErrGetTypeName(atp->name), line);
		return;
	}
	
	if (token != OCTETS)
		AsnIoErrorMsg(aip, 47, AsnErrGetTypeName(atp->name), aip->linenumber);

	return;
}

/*****************************************************************************
*
*   FloatHi AsnLexReadReal(aip, atp)
*   	expects a REAL next
*   	assumes none of it has already been read
*   	does not advance to next token
*   	atp points to the definition of this REAL
*
*****************************************************************************/
NLM_EXTERN FloatHi AsnLexReadReal (AsnIoPtr aip, AsnTypePtr atp)
{
	Int2 token;
	double mantissa, result;
	int	base, exponent;
	char buf[100];

	result = 0.0;
	token = AsnLexWord(aip);      /* read the { */
	if (token != START_STRUCT)
	{
		AsnIoErrorMsg(aip, 49, AsnErrGetTypeName(atp->name), aip->linenumber);
		return (FloatHi)result;
	}

	token = AsnLexWord(aip);      /* read mantissa */
	if (token != NUMBER)
	{
		AsnIoErrorMsg(aip, 50, AsnErrGetTypeName(atp->name), aip->linenumber);
		return (FloatHi)result;
	}
	MemCopy(buf, aip->word, (size_t)aip->wordlen);
	buf[aip->wordlen] = '\0';
	mantissa = atof(buf);

	token = AsnLexWord(aip);      /* read comma */
	if (token != COMMA)
	{
		AsnIoErrorMsg(aip, 51, AsnErrGetTypeName(atp->name), aip->linenumber);
		return (FloatHi)result;
	}

	token = AsnLexWord(aip);      /* read base */
	if (token != NUMBER)
	{
		AsnIoErrorMsg(aip, 52, AsnErrGetTypeName(atp->name), aip->linenumber);
		return (FloatHi)result;
	}
	MemCopy(buf, aip->word, (size_t)aip->wordlen);
	buf[aip->wordlen] = '\0';
	base = atoi(buf);
	if ((base != 2) && (base != 10))
	{
		AsnIoErrorMsg(aip, 53, AsnErrGetTypeName(atp->name), aip->linenumber);
		return (FloatHi)result;
	}

	token = AsnLexWord(aip);
	if (token != COMMA)
	{
		AsnIoErrorMsg(aip, 54, AsnErrGetTypeName(atp->name), aip->linenumber);
		return (FloatHi)result;
	}

	token = AsnLexWord(aip);
	if (token != NUMBER)
	{
		AsnIoErrorMsg(aip, 55, AsnErrGetTypeName(atp->name), aip->linenumber);
		return (FloatHi)result;
	}
	MemCopy(buf, aip->word, (size_t)aip->wordlen);
	buf[aip->wordlen] = '\0';
	exponent = atoi(buf);

	token = AsnLexWord(aip);
	if (token != END_STRUCT)
	{
		AsnIoErrorMsg(aip, 56, AsnErrGetTypeName(atp->name), aip->linenumber);
		return (FloatHi)result;
	}
    
    if (base == 10)      /* range checking only on base 10, for doubles */
    {
    	if (exponent > DBL_MAX_10_EXP)   /* exponent too big */
    		return	(FloatHi)DBL_MAX;
    	else if (exponent < DBL_MIN_10_EXP)  /* exponent too small */
    		return (FloatHi)result;
    }
    	
	result = mantissa * Nlm_Powi((double)base, exponent);

	return (FloatHi)result;
}

/*****************************************************************************
*
*   void AsnLexReadNull(aip, atp)
*
*****************************************************************************/
NLM_EXTERN void AsnLexReadNull (AsnIoPtr aip, AsnTypePtr atp)
{
	AsnLexWord(aip);
	if (! StrMatch("NULL", aip->word, aip->wordlen))
	{
		AsnIoErrorMsg(aip, 57, AsnErrGetTypeName(atp->name), aip->linenumber);
		return;
	}
	return;
}

/*****************************************************************************
*
*   CharPtr AsnLexSaveWord(aip)
*   	create a new null terminated string from
*       a non null terminated string (word) of length (len)
*
*****************************************************************************/
NLM_EXTERN CharPtr AsnLexSaveWord (AsnIoPtr aip)
{
	CharPtr ptr;

	ptr = (CharPtr) MemNew((size_t)(aip->wordlen + 1));
	MemCopy(ptr, aip->word, (size_t)aip->wordlen);
	return ptr;
}

/*****************************************************************************
*
*   void AsnLexSkipStruct(aip)
*   	skips current structure and all substructures
*       Does NOT read following element
*
*****************************************************************************/
NLM_EXTERN void AsnLexSkipStruct (AsnIoPtr aip)
{
	int type_indent = 1;
	Int2 token;

	while (type_indent)
	{
		token = AsnLexWord(aip);
		switch (token)
		{
			case START_STRUCT:       /* another substructure */
				type_indent++;
				break;
			case END_STRUCT: 		/* a close brace */
				type_indent--;
				break;
			case EOF_TOKEN:
				AsnIoErrorMsg(aip, 17);
				return;
			default:
				break;
		}
	}

	return;
}

/*****************************************************************************
*
*   Int4 AsnLexInteger(aip)
*
*****************************************************************************/
NLM_EXTERN Int4 AsnLexInteger (AsnIoPtr aip)
{
	char tbuf[11];
	Int4 value;

	MemCopy(tbuf, aip->word, (size_t)aip->wordlen);
	tbuf[aip->wordlen] = '\0';
	value = atol(tbuf);
	return value;
}

/*****************************************************************************
*
*   Int8 AsnLexBigInt(aip)
*
*****************************************************************************/
NLM_EXTERN Int8 AsnLexBigInt (AsnIoPtr aip)
{
	char tbuf[40];
	Int8 value;
	const char * result;

	MemCopy(tbuf, aip->word, (size_t)aip->wordlen);
	tbuf[aip->wordlen] = '\0';
	value = Nlm_StringToInt8 (tbuf, &result);
	return value;
}

/*****************************************************************************
*
*   Int2 AsnLexWord(aip)
*   	reads words, punctuation
*   	returns tokens defined in asntypes.h
*   	does not return ASN reserved words - use AsnLexWord() for that
*
*****************************************************************************/

static Int2 AsnLexWordEx (AsnIoPtr aip, Uint1 fix_non_print)
{
	register CharPtr pos;
	Int1 state;
	Int2 token;
	register int linepos, len;
	int done;

    if (aip->tagsaved)     /* had to read ahead */
    {
        aip->tagsaved = FALSE;
        return aip->token;
    }

    if (! aip->bytes)        /* no data loaded */
		AsnIoGets(aip);
		
	linepos = aip->linepos;
	pos = aip->linebuf + linepos;
	state = aip->state;
	len = 0;

	while (*pos == '\n' || *pos == '\r')    /* skip empty lines */
	{
		pos = AsnIoGets(aip);   /* get a line */

		if (pos == NULL)
			return EOF_TOKEN;
	}
	
	if (state == IN_STRING_STATE)
	{
		aip->word = pos;
		if ((* pos == '\"') && (*(pos + 1) != '\"'))    /* end of string */
		{
			token = END_STRING;
			pos++;
			state = 0;        /* reset state */
		}
		else
		{
			token = IN_STRING;
			while ((* pos != '\"') && (* pos != '\n') && (* pos != '\r'))
			{
				if ((fix_non_print != 2) && ((*pos < ' ') || (*pos > '~')))
				{
					done = (int)(*pos);
					*pos = '\0';
					if ((fix_non_print == 0) || (fix_non_print == 3))
					{
						AsnIoErrorMsg(aip, 106, done, aip->word);
					}
					done = 0;
					*pos = '#';   /* replace with # */
				}
				pos++; len++;
			}

			if ((*pos != '\n') && (*pos != '\r') && (* (pos + 1) == '\"')) /* internal quote */
			{
				len++;        /* include in previous string */
				pos += 2;     /* point to rest of string */
			}
		}
	}
	else if (state == IN_BITHEX_STATE)
	{
		aip->word = pos;
		if (*pos == '\'')  			  /* end of binhex */
		{
			state = 0;              /* set to normal */
			pos++;                       /* move past quote */
			while (IS_WHITESP(*pos))
			{
				if (*pos == '\n' || *pos == '\r')    /* skip empty lines */
				{
					pos = AsnIoGets(aip);   /* get a line */
					
					if (pos == NULL)
					return EOF_TOKEN;
				}
				else
					pos++;
			}
			if (* pos == 'H')
				token = OCTETS;
			else if (* pos == 'B')
				token = ASNBITS;
			else
			{
				AsnIoErrorMsg(aip, 58, aip->linenumber);
				token = ERROR_TOKEN;
			}
			pos++;         /* move past H or B */
		}
		else
		{
			token = IN_BITHEX;
			while ((* pos != '\'') && (* pos != '\n') && (* pos != '\r'))
			{
				pos++; len++;
			}
		}   
	}
	else              /* normal scanning */
	{
		while ((* pos <= ' ') || ((*pos == '-') && (*(pos+1) == '-')))     /* skip leading white space */
		{
			if (*pos == '\n' || *pos == '\r')
			{
				pos = AsnIoGets(aip);

				if (pos == NULL)
					return EOF_TOKEN;
			}
			else if ((*pos == '-') && (*(pos+1) == '-'))   /* skip comments */
			{
				pos += 2;
				done = 0;
				while (! done)
				{
					if ((*pos == '-') && (*(pos +1) == '-'))
					{
						pos += 2;
						done = 1;
					}
					else if (*pos == '\n' || *pos == '\r')
						done = 1;
					else
						pos++;
				}
			}
			else
				pos++;
			
		}

		aip->word = pos;
		if (* pos == '\"')
		{
			token = START_STRING;
			state = IN_STRING_STATE;
		}
		else if (* pos == '\'')
		{
			token = START_BITHEX;
			state = IN_BITHEX_STATE;
		}
		else if (* pos == ',')
			token = COMMA;
		else if (* pos == '{')
			token = START_STRUCT;
		else if (* pos == '}')
			token = END_STRUCT;
		else if (* pos == '[')
			token = START_TAG;
		else if (* pos == ']')
			token = END_TAG;
		else if (* pos == '(')
			token = OPEN_PAREN;
		else if (* pos == ')')
			token = CLOSE_PAREN;
		else if (* pos == ';')
			token = SEMI_COLON;
		else if (* pos == ':')
		{
			if ((*(pos + 1) == ':') && (*(pos + 2) == '='))
			{
				token = ISDEF;
				pos += 2;
				len = 3;
			}
			else
			{
				AsnIoErrorMsg(aip, 59, *pos, aip->linenumber);
				token = ERROR_TOKEN;
			}
		}
		else if (IS_UPPER(*pos))  /* a reference or keyword */
		{
			token = REF;
			while ((IS_ALPHANUM(*pos)) || (*pos == '-'))
			{
				pos++; len++;
			}
			pos--;    /* move back for increment at end */
			len--;
		}
		else if (IS_LOWER(*pos))  /* an identifier or valuereference */
		{
			token = IDENT;
			while ((IS_ALPHANUM(*pos)) || (*pos == '-'))
			{
				pos++; len++;
			}
			pos--;		  /* move back for increment at end */
			len--;
		}
		else if ((IS_DIGIT(*pos)) || ((*pos == '-') && (IS_DIGIT(*(pos+1)))))
		{
			token = NUMBER;
			if (*pos == '-')
			{
				pos++; len++;
			}

			while (IS_DIGIT(*pos))
			{
				pos++; len++;
			}
			pos--;    /* move back for increment at end */
			len--;
		}
		else
		{
			AsnIoErrorMsg(aip, 59, *pos, aip->linenumber);
			token = ERROR_TOKEN;
		}
		len++; pos++;     /* move over last symbol */
	}

	aip->linepos = pos - aip->linebuf;
/******************** check on MSWIN
	linepos = 0;
	while (pos != linebuf)
	{
		linepos++;
		pos++;
	}
	aip->linepos = linepos;
**********************/
	aip->state = state;
	aip->wordlen = len;
	aip->token = token;
	return token;
}

NLM_EXTERN Int2 AsnLexWord (AsnIoPtr aip)

{
  return AsnLexWordEx (aip, aip->fix_non_print);
}

static Int2 AsnLexWordUTF8 (AsnIoPtr aip)

{
  return AsnLexWordEx (aip, 2);
}

/*****************************************************************************
*
*   Boolean StrMatch(a, b, len)
*   	returns TRUE if a matches b for EXACTLY len
*       and a has a '\0' after that
*
*****************************************************************************/
NLM_EXTERN Boolean StrMatch (CharPtr a, CharPtr b, Int2 len)
{
	if ((a == NULL) || (b == NULL))
		return FALSE;
		
	while (len)
	{
		if (*a != *b)
			return FALSE;
		a++; b++; len--;
	}
	if (*a != '\0')
		return FALSE;
	return TRUE;
}


/*****************************************************************************
*
*   Int2 AsnLexScan(aip, name)
*     scans until name ::= to start reading asn.1 past garbage
*
*****************************************************************************/
static Int2 AsnLexScan (AsnIoPtr aip, CharPtr name)
{
	register CharPtr pos;
	Int2 token = 0;
	register int linepos, len;
	int done;
	Boolean started = FALSE, matched_ref = FALSE;

	if (aip->type_indent)   /* only for start */
		return ERROR_TOKEN;

   if (! aip->bytes)        /* no data loaded */
		AsnIoGets(aip);
		
	linepos = aip->linepos;
	pos = aip->linebuf + linepos;
	len = 0;

	while (*pos == '\n' || *pos == '\r')    /* skip empty lines */
	{
		pos = AsnIoGets(aip);   /* get a line */

		if (pos == NULL)
			return EOF_TOKEN;
	}

	while (! started)
	{
		while ((* pos <= ' ') || ((*pos == '-') && (*(pos+1) == '-')))     /* skip leading white space */
		{
			if (*pos == '\n' || *pos == '\r')
			{
				pos = AsnIoGets(aip);

				if (pos == NULL)
					return EOF_TOKEN;
			}
			else if ((*pos == '-') && (*(pos+1) == '-'))   /* skip comments */
			{
				pos += 2;
				done = 0;
				while (! done)
				{
					if ((*pos == '-') && (*(pos +1) == '-'))
					{
						pos += 2;
						done = 1;
					}
					else if (*pos == '\n' || *pos == '\r')
						done = 1;
					else
						pos++;
				}
			}
			else
				pos++;
			
		}

		aip->word = pos;
		len = 0;
		if (* pos == ':')
		{
			if ((*(pos + 1) == ':') && (*(pos + 2) == '='))
			{
				token = ISDEF;
				pos += 2;
				len = 3;
				if (matched_ref)
					started = TRUE;
				else
				{
					AsnIoErrorMsg(aip, 59, *pos, aip->linenumber);
					return ERROR_TOKEN;
				}
			}
			else
				matched_ref = FALSE;
		}
		else if (IS_UPPER(*pos))  /* a reference or keyword */
		{
			token = REF;
			while ((IS_ALPHANUM(*pos)) || (*pos == '-'))
			{
				pos++; len++;
			}
			pos--;    /* move back for increment at end */
			aip->wordlen = len;
         if (StrMatch(name, aip->word, (Int2)len))
				matched_ref = TRUE;
			else
				matched_ref = FALSE;
		}
		else
			matched_ref = FALSE;
		pos++;     /* move over last symbol */
	}

	                  /* found it , do normal return after ::= */

	aip->linepos = pos - aip->linebuf;
	aip->wordlen = len;
	aip->token = token;
	return token;
}


