/* asminp.c -- microsoft 80x86 assembler
**
** microsoft (r) macro assembler
** copyright (c) microsoft corp 1986.  all rights reserved
**
** randy nevin
**
** 10/90 - Quick conversion to 32 bit by Jeff Spencer
*/

#define ASMINP		/* prevent external declaration of _asmctype_ */

#include <stdio.h>
#include <io.h>
#include <dos.h>
#include <share.h>
#include <memory.h>
#include "asm86.h"
#include "asmfcn.h"
#include "asmctype.h"
#include "asmmsg.h"
#include "asmfcn.h"
#include <fcntl.h>

#define DEBFLAG F_INP

#if defined CPDOS && !defined OS2_2 && !defined OS2_NT
unsigned short _far _pascal DosRead( unsigned short, unsigned char far *, unsigned short, unsigned short far *);
#endif


VOID PASCAL getphysline (void);
SHORT PASCAL CODESIZE readmore (void);
SHORT PASCAL CODESIZE incomment( char * );

extern	UCHAR _asmctype_[];
extern	char  _asmcupper_[];
extern	char  _asmTokenMap_[];


/***	skipblanks - skip blanks
 *
 *	skipblanks ()
 *
 *	Returns - the terminating character
 */


#ifndef M8086OPT

UCHAR CODESIZE
skipblanks ()
{
	while (ISBLANK (NEXTC ()))
		;
	return(*--lbufp);
}

#endif


/***	scanatom - extract next atom into name
 *
 *	hash = scanatom (pos)
 *
 *	Entry	pos = SCEND  if position at first character after token
 *		      SCSKIP if position before terminator and not set delim
 *	Exit	naim.pszName = next token zero terminated
 *		       upper case if caseflag = CASEU or CASEX
 *		       case read from file if caseflag = CASEL
 *		naim.pszLowerCase = name in case read from file
 *		naim.usHash = hash value of token in naim.pszName
 *		naim.ucCount = length of string
 *		begatom = pointer to first character of token
 *		endatom = pointer to character after end of token
 *	Returns void
 *	Calls	skipblanks
 */

#ifndef M8086OPT

#define rNEXTC()	(*rlbp++)
#define rPEEKC()	(*rlbp)
#define rBACKC()	(rlbp--)
#define rSKIPC()	(rlbp++)


SHORT PASCAL CODESIZE
scanatom (
	char pos
){
	register char *ptr = naim.pszName;
	register char *lptr = naim.pszLowerCase;
	register char *rlbp = lbufp;
	register char cc;
	register char *n;
	register SHORT h;
	long  tokLen;

	while (ISBLANK (rNEXTC ()))
		;
	rBACKC ();
	h = 0;
	/* Start of atom */
	begatom = rlbp;
	if (LEGAL1ST (rPEEKC ())) {
		n = lptr + SYMMAX;
		cc = rNEXTC ();
		if( cc == '.' ){  /* Special case token starting with dot */
		    h = *ptr++ = *lptr++ = cc;
		    cc = rNEXTC ();
		}
		if (caseflag == CASEL)

			do {
			   h += MAP(*ptr++ = *lptr++ = cc);
			 } while (TOKLEGAL( cc = rNEXTC() ) && lptr < n);
		else
			do {
			   h += (*ptr++ = MAP( *lptr++ = cc ));
			} while (TOKLEGAL( cc = rNEXTC() ) && lptr < n);

		if (TOKLEGAL (cc))
			/* Atom longer than table entry, discard remaining chars */
			while (TOKLEGAL (cc = rNEXTC ()))
				;
		rBACKC ();
		endatom = rlbp;
		if (ISBLANK (cc) && pos != SCEND) {	/* skipblanks() */
			while (ISBLANK (rNEXTC ()))
				;
			rBACKC ();
		}
	}
	*ptr = *lptr = '\0';
	naim.ucCount = lptr - naim.pszLowerCase;
	naim.usHash = h;
	lbufp = rlbp;
	tokLen = lptr - naim.pszLowerCase;    /* Using tokLen gets around a C386 6.00.60 bug */
	return( (SHORT) tokLen );  /* Return length of token */
}

#endif /* M8086OPT */




/***	readfile - read from input or include file
 *
 *	ptr = readfile ();
 *
 *	Entry	none
 *	Exit	lbuf = next input line
 *		lbufp = start of lbuf
 *		line counter for file incremented
 *		linessrc incremented
 *	Returns pointer to end of line
 *	Calls	error
 */


VOID PASCAL CODESIZE
readfile ()
{
	register FCB * pFCBT;

	getline();

	pFCBCur->line++;

	if (srceof) {

	    if (!pFCBCur->pFCBParent) {
		    errorc (E_EOF);
		    fprintf (ERRFILE,__NMSG_TEXT(ER_EO2));

		    if (fSimpleSeg && pcsegment)
			endCurSeg();

		    longjmp(forceContext, 1);

	    } else {

		popcontext = TRUE;

		closefile();

		if (crefing && pass2)
		    fprintf( crf.fil, "\8%s", pFCBCur->fname );
	    }
	    srceof = 0;
	}
	else
	    linessrc++;
}




/***	getline - read from input or include file
 *
 *	getline()
 *
 *	Returns in lbuf the next complete logical line. A logical line
 *	may consist of one or more lines connected via the \ continuation
 *	character.  This is done as follows. Data is copied from
 *	pFCBCur->tmpbuf. If necessary more data is copied into the
 *	buffer via readmore(). After an entire physical line is read
 *	it is tested as to whether the line is continued on the next
 *	physical line. If not the line is returned in lbuf. Otherwise
 *	the physical line is copied to linebuffer and a call to listline
 *	is made. At which point another physical line is cancatenated
 *	to the line or lines already in lbuf.
 *
 *	Entry	pFCBCur = File currently reading from.
 *		pFCBCur->ctmpbuf = Number of bytes available in buffer
 *				   0 = necessary to read data from disk.
 *		pFCBCur->ptmpbuf = Next position in buffer to copy from.
 *		pFCBCur->line	 = Number of physical line in file
 *
 *	Exit  - lbuf[] holds a complete logical line, with a space appended.
 *	      - linebuffer[] holds last physical line.
 *	      -	lbufp points to the beginning of lbuf.
 *	      - linebp points to null terminator at the end
 *		of the logical line in lbuf.
 *	      -	linelength is number of bytes of last physical line.
 *	      -	pFCBCur->ctmpbuf & ptmpbuf & line are updated.
 *	      -	srceof is true if the end of file was encountered, in
 *		which case the physical line is a null string, and
 *		the logical line is a single space character.
 */

VOID CODESIZE
getline()
{
	char FAR	*p;
	register char	*pchTmp;
	char		*pchPhysLine;
	INT		fFoundEOL;  /* True, if endof line copied */
	register INT	count;
	INT		fLineContinued;
	INT		fGotSome;

	lbufp = lbuf;	 /* Init lbufp for other routines */
	pchPhysLine = lbuf;
	fGotSome = FALSE; // nothing seen yet
	errorlineno = pFCBCur->line + 1;
	pchTmp = lbuf;	 // Where to copy the line

	//if( pFCBMain->line == 126-1 ){
	//    _asm int 3
	//}

	do{

	    fFoundEOL = FALSE;
	    do{

		/* If the buffer is empty fill it */
		if( !pFCBCur->ctmpbuf ){
		    if( readmore() ){	 // TRUE if at EOF
			if( !fGotSome ){
			    srceof = TRUE;
			    linebuffer[0] = '\0';
			    linelength = 0;
			    linebp = lbuf;
			    lbuf[0] = '\0';
			    return;
			}else{
			    pchTmp++;  /* Negate pchTmp-- following this loop */
			    break;    /* Break fFoundEOL loop */
			}
		    }
		}
		fGotSome = TRUE;

		/* Find next LF in buffer */
		p = _fmemchr( pFCBCur->ptmpbuf, '\n', pFCBCur->ctmpbuf );
		if( p ){  /* If LF was found */
		    count = (p - pFCBCur->ptmpbuf) + 1;
		    fFoundEOL = TRUE;
		}else{
		    count = pFCBCur->ctmpbuf;
		}

		/* Check if physical or logical line too long */
		if( (pchTmp - lbuf) + count >= LBUFMAX ||
		    (pchTmp - pchPhysLine) + count >= LINEMAX-4 ){

		    /* Update the position in the buffer */
		    pFCBCur->ptmpbuf += count;	// Update where copying from
		    pFCBCur->ctmpbuf -= count;

		    errorc( E_LNL );	    /* Log the error */

		    /* Return a null string line */
		    linebuffer[0] = '\0';
		    linelength = 0;
		    linebp = lbuf;
		    lbuf[0] = ' ';
		    lbuf[1] = '\0';
		    return;
		}else{
		    /* Copy the line, and update pointers */
		    fMemcpy( pchTmp, pFCBCur->ptmpbuf, count );
		    pchTmp += count;	    // Update where copying to
		    pFCBCur->ctmpbuf -= count;	// Update # bytes left in buffer
		    pFCBCur->ptmpbuf += count;	// Update where copying from
		}

	    }while( !fFoundEOL );

	    pchTmp--; /* Move back to last character (LF) */


/* Strip Carriage Returns that precede LFs */
	    if( *(pchTmp-1) == '\r' ){
		pchTmp--; /* Throw out Carriage return */
	    }

#ifdef MSDOS
    /* Strip Multiple Control-Zs */
	    while( *(pchTmp - 1) == 0x1A ){  /* Check for ^Z */
		pchTmp--;
	    }
#endif
	    if( pchTmp < lbuf ){   /* Remotely possible if Blank line */
		pchTmp = lbuf;
	    }

	    linelength = pchTmp - pchPhysLine;
	    if( !pass2 || listconsole || lsting ){
		memcpy( linebuffer, pchPhysLine, linelength );
	    }
	    *( linebuffer + linelength ) = '\0'; //Null terminate the physical line

	    if( *(pchTmp - 1) == '\\' && !incomment( pchTmp ) ){
		pchPhysLine = --pchTmp;  /* Overwrite the '\' */
		fCrefline = FALSE;
		listline();
		fCrefline = TRUE;
		pFCBCur->line++;	/* Line count it physical line count */
		fLineContinued = TRUE;
	    }else{
		fLineContinued = FALSE;
	    }
	}while( fLineContinued );
	*pchTmp++ = ' ';	    /* Replace line feed with space */
	*pchTmp = '\0';		    /* Null terminate line */
	linebp = pchTmp;
	if( lbuf[0] == 12 ){	    /* Overwrite leading ctrl-L with space */
	    lbuf[0] = ' ';
	}
	/* At this point linebp - lbuf == strlen( lbuf )	*/
}

/***	readmore - read from disk into buffer
 *
 *
 *
 *	Entry	pFCBCur = File currently reading from.
 *		pFCBCur->cbbuf = Size of buffer to read into.
 *		pFCBCur->buf = Address of buffer to read into.
 *		pFCBCur->fh = File handle to read from.
 *
 *	Exit	return = TRUE: Not at end of file
 *		   pFCBCur->ptmpbuf = First byte of buffer.
 *		   pFCBCur->ctmpbuf = Number of bytes in buffer.
 *		return = FALSE: At end of file
 *		   No other variables changed.
 */

SHORT PASCAL CODESIZE
readmore ()
{
	SHORT		cb;
	SHORT		fEOF = FALSE;

	/* If the file has been temporarily closed reopen it */
	if( pFCBCur->fh == FH_CLOSED ){
	    if( (pFCBCur->fh = tryOneFile( pFCBCur->fname )) == -1 ){	 /* Open the file */
		TERMINATE1(ER_ULI, EX_UINP, save);  /* Report unable to access file */
	    }
	    /* Seek to old position */
	    if( _lseek( pFCBCur->fh, pFCBCur->savefilepos, SEEK_SET ) == -1L ){
		TERMINATE1(ER_ULI, EX_UINP, save);  /* Report unable to access file */
	    }
	}

#if !defined CPDOS || defined OS2_2 || defined OS2_NT
        cb = _read( pFCBCur->fh, pFCBCur->buf, pFCBCur->cbbuf );
#else
	if( DosRead( pFCBCur->fh, pFCBCur->buf, pFCBCur->cbbuf, &cb ) ){
	    cb = -1;
	}
#endif
	if( cb == 0 ){
	    fEOF = TRUE;	/* End of file found */
	}else if( cb == (SHORT)-1 ){
	    TERMINATE1(ER_ULI, EX_UINP, save);	/* Report unable to access file error */
	}else{
	    /* Setup the buffer pointers */
	    pFCBCur->ptmpbuf = pFCBCur->buf;  /* Init ptr to start of buffer */
	    pFCBCur->ctmpbuf = cb;
	}
	return( fEOF );
}




/***	incomment - Checks a line ending in \ to determine if the \ is in a
 *		    comment and is therefore not a comment line.
 *
 *	Entry	Assumes lbuf contains partial logical line ending in a \.
 *		pchEnd - points within lbuf to the terminating LF.
 *	Methode Checks that line is not in a COMMENT directive's scope.
 *		Then checks if the line contains a semicolon. If not, \
 *		IS continuation. If a semicolon is found, line must be
 *		scanned carefully to determine if the semicolon is a
 *		comment delimeter or is in a string or is a character
 *		constant If it is not a comment delimeter, \ IS continuation.
 *		Otherwise, \ is part of comment, and is NOT a continuation.
 *	Exit	Returns true if the \ is in a comment
 *		Returns false if the \ is not in a comment, and is therefore
 *		a continuation character.
 *
 *	Calls	memchr
 *
 *	Created: 9/90 - Jeff Spencer, translated from asm code in asmhelp.asm
 */

SHORT PASCAL CODESIZE
incomment(
	char * pchTmp	   /* Points to terminating LF in lbuf */
){
    SHORT		fContSearch;
    unsigned char *	pchSearch;
    unsigned char *	pchSemi;
    unsigned char	chClose;
    static unsigned char szComment[] = "COMMENT";



    pchTmp--;	    /* Point to '\' character */
    if( handler == HCOMMENT ){	    /* If within comment directive */
	return( TRUE );
    }

    fContSearch = TRUE;
    pchSearch = lbuf;

    do{
	if( pchSemi = memchr( pchSearch, ';', pchTmp - pchSearch )){	/* Check for a semicolon */
	    do{
		chClose = '\0';
		switch( *pchSearch++ ){
		  case ';':
		    /* Semicolon is not in quotes, return in comment */
		    return( TRUE );
		  case '\"':
		    chClose = '\"';
		    break;
		  case '\'':
		    chClose = '\'';
		    break;
		  case '<':
		    chClose = '>';
		    break;
		}
		/* Below the word quote is used to mean the chClose character */
		if( chClose ){
		    if( !(pchSearch = memchr( pchSearch, chClose, pchTmp - pchSearch ) ) ){
			fContSearch = FALSE; /* No matching quote, not a comment */
		    }else{
			if( pchSearch < pchSemi){
			    /* Semicolon is in quotes */
			    pchSearch++; /* Move past quote just found
			    break;  // Look for another semicolon */
			}else{
			    /* Semicolon is past this set of quotes */
			    /* Continue, Scanning */
			}
		    }
		}
	    }while( fContSearch && pchSearch < pchTmp );
	}else{
	    /* No Semicolon in the line, or it's in quotes  */
	    fContSearch = FALSE;
	}
    }while( fContSearch );

    /* At this point we know that the \ is not in a semicolon	**
    ** delimited comment. However, we still have to make sure	**
    ** that the comment keyword doesn't appear at the begining  **
    ** of the line.						*/

    /* Skip leading white space */
    pchSearch = lbuf;
    while( *pchSearch == ' ' || *pchSearch == '\t' ){
	pchSearch++;
    }
    for( pchTmp = szComment; *pchTmp; ){
	if( *pchSearch++ != _asmTokenMap_[*pchTmp++] ){
	    return( FALSE );	    /* First word isn't "comment" */
	}
    }
    return( TRUE ); /* comment keyword at start of line, return in comment */
}

/****	closeFile
 *
 *	closeFile ()
 *
 *	Entry	Assumes valid pFCBCur->fh or FH_CLOSED
 *	Returns
 *	Calls	close()
 *	Note	Closes current file - i.e.  pFCBCur
 *		and marks all fields in pFCBCur appropriately
 */

closefile()
{
    register FCB *pFCBOld;

    #ifdef BCBOPT
    BCB * pBCBT;

    if ((pBCBT = pFCBCur->pBCBCur) && pBCBT->pbuf)
	pBCBT->filepos = 0;			/* EOF */
    #endif

    if( pFCBCur->fh != FH_CLOSED ){   /* Check to see if the file is already closed */
         _close(pFCBCur->fh);
    }
    pFCBOld = pFCBCur;
    pFCBCur = pFCBCur->pFCBParent;  /* Remove from bidirectional linked list */
    pFCBCur->pFCBChild = NULL;

    _ffree( pFCBOld->buf);  /* Free FCB buffer */
    _ffree( (UCHAR *)pFCBOld );      /* Free FCB */
}