/****************************************************************************
**
**  Name:  PLM86.CPP
**
**  Description:
**     PL/M 86 modules handling routines
**
**  Status:  PRELIMINARY
**
**  $Log:   S:/tbird/mt2_186/omf86/plm86.cpv  $
** 
**    Rev 1.3   19 Jun 1997 17:10:42   Judy
** Support Paradigm locate extended version which accept register variables.
** 
**    Rev 1.2   18 Jun 1997 14:53:28   Judy
** 
**    Rev 1.1   18 Apr 1997 09:29:44   Judy
** 
**    Rev 1.0   26 Feb 1997 11:40:36   Judy
** Initial revision.
** 
**    Rev 1.5   18 Oct 1996 11:53:50   hera
** 
**    Rev 1.4   18 Oct 1996 09:16:26   hera
** We do some changes for PLM having not list file.
** 
**    Rev 1.3   11 Oct 1996 16:25:30   hera
** We fixed the isModuleName() for PLM and set FixedFlag in ProcessCOMENT
** 
**    Rev 1.2   30 Sep 1996 10:27:06   hera
** 
**    Rev 1.1   25 Sep 1996 10:10:34   hera
** 
**    Rev 1.0   14 Jun 1996 16:37:06   Judy
** Initial revision.
** 
**  $Header:   S:/tbird/mt2_186/omf86/plm86.cpv   1.3   19 Jun 1997 17:10:42   Judy  $
**
**  Copyright (C) 1996 Microtek International.  All rights reserved.
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#ifndef __DIR_H
#include <dir.h>
#endif

#ifndef __IO_H
#include <io.h>
#endif

#ifndef __STRING_H
#include <string.h>
#endif

#ifndef __MEM_H
#include <mem.h>
#endif

#ifndef __STDLIB_H
#include <stdlib.h>
#endif

#ifndef _LOADER_
#include "loader.h"
#endif

#ifndef _HEAP_
#include "heap.h"
#endif

#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif

#ifndef __OMF86LDR__
#include "omf86ldr.h"
#endif

#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif

#ifndef __PLM86__
#include "plm86.h"
#endif

#ifndef __TYPE__
#include "type.h"
#endif

// include DArray class template to generate class instances
#include "darray.cpp"

//#ifndef __DEBUG__
//#define __DEBUG__
//#endif

#define HFILE_ERROR 0xFFFF
                    /****************************
                     *                          *
                     *     LOCAL DEFINITIONS    *
                     *                          *
		     ****************************/
//**Hera 10/19/96**/
HANDLE ListFile = HFILE_ERROR;
//**Hera**/
STATIC S16 CharsCnt;
STATIC LPSTR ListFileBuf = NULL;
STATIC LPSTR ListBufPtr  = NULL;
STATIC LINENUM SourceLine;
STATIC CHAR PathName[NAME_LEN];
STATIC CHAR OldModName[NAME_LEN];
STATIC BOOLEAN ListFileEnd;
BOOLEAN FirstComment;

// IMPORTANT:
// Tell the compiler that the template instances that follow will be
// defined elsewhere, and generate the necessary instances
#pragma option -Jgx -Jgd

// IMPORTANT:
// DArray<X> constructor and destructor will be called when
// the OMF86.DLL load and unload, so don't worry about how the static
// variables are initialized.  It's magic!
       
// Initialize module name objects -- ModNamesList
RETCODE DArray<MNAMESPTR>::LastError = GOOD;
MNAMESPTR DArray<MNAMESPTR>::BadIndex = 0;
DArray<MNAMESPTR> ModNamesList(DEFAULT_MAX_MNAMES);

// Initialize line numbers objects -- LineMap
RETCODE DArray<LINENUM>::LastError = GOOD;
LINENUM DArray<LINENUM>::BadIndex = 0;
DArray<LINENUM> LinesMap(DEFAULT_MAX_LINES);

                    /****************************
                     *                          *
                     *    EXTERNAL VARIABLES    *
                     *                          *
                     ****************************/

extern CHAR ModName[NAME_LEN+1];
extern LPLDRSTATBLOCK StatBlock;
extern LANGUAGE_TYPE LangType;
extern BOOLEAN Paradigm;
extern int ExtOmf;
                    /****************************
                     *                          *
                     *     LOCAL PROTOTYPES     *
                     *                          *
		     ****************************/
BOOLEAN FixedFlag = TRUE;//**Hera 4/10/97**/
PRIVATE RETCODE BuildLinesMap(U16);
PRIVATE RETCODE BuildModNamesList(VOID);
//PRIVATE LANGUAGE_TYPE GetLanguageType(LPSTR tmpStr);
PRIVATE RETCODE GetOneLine(LPSTR);
PRIVATE BOOLEAN IsModuleName(LPSTR);
PRIVATE RETCODE ReplaceModuleName(VOID);
PRIVATE RETCODE SkipOverHeader(S16);

                    /****************************
                     *                          *
                     *      EXECUTABLE CODE     *
                     *                          *
                     ****************************/

/******************************************************************************
**
** DestroyModNames
**
** Purpose: destroy the ModNamesList[].
**
******************************************************************************/
RETCODE DestroyModNames(VOID) {

  // get number of elements in list 
  U16 nameCounts = ModNamesList.Count();
  for (U16 i = 0; i < nameCounts; i++) {
     // delete the ModNameList[] object (i.e. MNAMES object)
     MNAMESPTR nameObj = ModNamesList[i];
     if (nameObj && (DArray<MNAMESPTR>::LastError == GOOD)) {
        delete nameObj;
        ModNamesList[i] = NULL;
     }
     ModNamesList.DecrementCount();
  }
  // return the last 
  return DArray<MNAMESPTR>::LastError;

} /* DestroyModNames */

/******************************************************************************
**
**  LoadLinenum
**
******************************************************************************/
RETCODE LoadLinenum(LINENUM_ENTRY *lineRec, U16 *oldOffset) {
RETCODE err;
LINENUM lineNum=(LINENUM)lineRec->lineNum;

   if (LangType == LANG_PLM) {
      if (lineNum >= (LINENUM)LinesMap.Count()) {
         if ((err=BuildLinesMap(lineNum)) != GOOD)
            return(err);
      }
      lineNum = LinesMap[lineNum-1];
   }
   if (lineNum) {
      if ((err=SymAddLinenum(lineNum, 1, lineRec->segment, lineRec->offset)) != GOOD)
         return(err);
      *oldOffset = lineRec->offset;

      StatBlock->numLines++;
   }
   return(GOOD);
}  /* LoadLinenum */

/******************************************************************************
**
**  GetPathName
**
******************************************************************************/
VOID GetPathName(VOID) {
LPSTR ptr;
S16 pos;

   // Get the path for later use (PL/M x86 counts on it)
   *PathName = '\0';
   if ((ptr = strrchr((LPSTR)StatBlock->curLoadFile, '\\')) != NULL) {
      lstrcpy((LPSTR)PathName, (LPSTR)StatBlock->curLoadFile);
      pos = (S16)((U32)ptr-(U32)(StatBlock->curLoadFile)+1);
      PathName[pos] = '\0';
   }
   return;
}  /* GetPathName */

/******************************************************************************
**
**  PlmModuleEnd
**
******************************************************************************/
VOID PlmModuleEnd(VOID)  {
#ifdef __DEBUG__
   if (LangType == LANG_PLM) {
      CHAR tmpStr[NAME_LEN];
      HANDLE hFile;

      sprintf(tmpStr, "%s%s.DBG", PathName, ModName);
      if ((hFile = (HANDLE)_lcreat(tmpStr, 0)) != HFILE_ERROR) {
         LOOP_VAR i;
         sprintf(tmpStr, "Old Module Name  %s\r\nNew Module Name: %s\r\n\r\n",
                 OldModName, ModName);
         _lwrite(hFile, tmpStr, strlen(tmpStr));
         for (i=0; i< LinesMap.Count(); i++) {
             sprintf(tmpStr, "%5d  %5d\r\n", i+1, LinesMap[i]);
             _lwrite(hFile, tmpStr, strlen(tmpStr));
         }
         _lclose(hFile);
      }
   }
#endif
   FirstComment = TRUE;
   if (ListFile != HFILE_ERROR)
      _lclose(ListFile);
   ListFile = HFILE_ERROR;
   ListFileEnd = FALSE;
} /* PlmModuleEnd */

/******************************************************************************
**
**  ProcessCOMENT
**
******************************************************************************/
RETCODE ProcessCOMENT(OMF86_FUNC_PARA *funcPara) {
RETCODE err;
CHAR tmpStr[NAME_LEN];
S16 len;
U16 cType;
S32 pos;

   if (!FirstComment || (!IsLoadSymbol(funcPara->flags)))
      return(GOOD);
   if ((err=OMFGetBytes(funcPara->hFile, (U8 *)&cType, sizeof(cType))) != GOOD)
      return(err);   
   if ((len = funcPara->endLoc+1 - CurrentLocation()) <= 0)
      return(GOOD);
   pos=CurrentLocation();    // <Judy 6/19/97>
   len = min(NAME_LEN-1, len);
   if ((err=OMFGetBytes(funcPara->hFile, (U8 *)tmpStr, (U16)len)) != GOOD)
      return(err);
   tmpStr[len+1] = '\0';
// if ((LangType=GetLanguageType(tmpStr)) == LANG_PLM) { /* It's a PL/M module */
   //Hera 6/13/97
   //FirstComment = FALSE;
   FixedFlag = TRUE;
   if (strstr(tmpStr, "Paradigm") != NULL && !Paradigm) 
      Paradigm = TRUE;
   else if(Paradigm && ExtOmf == -1) {
      if (cType == 0xC880 && ((U8)tmpStr[0] == 0x01))
	 ExtOmf = 1;
      else
	 ExtOmf = 0;
//    FirstComment = FALSE; <Judy 6/18/97>
   }
   // <Judy 6/18/97> process register variable for Paradigm Locate extension
   else if(Paradigm && cType == 0xC080) {
        SeekFile(funcPara->hFile, pos, SEEK_SET);
        ProcessRegVarExt(funcPara->hFile, funcPara->flags);
   }
   // eof <Judy 6/18/97>
/* else   <Judy 6/18/97>
      FirstComment = FALSE; */
   //Hera
   //**Hera 10/11/96**/   
   if (strstr(tmpStr, "PLM86") != NULL || strstr(tmpStr, "High C") != NULL
       || strstr(tmpStr, "mcc86") != NULL || strstr(tmpStr, "asm86") != NULL
       || strstr(tmpStr, "Microsoft ASM") != NULL/*Hera 4/3/97*/)
       FixedFlag = FALSE;     
   //**Hera**/
   if (strstr(tmpStr, "PLM86") != NULL) {
      LangType = LANG_PLM;
      /*  Get the module name */
      if (funcPara->oFlag == MOPEN) {
         sprintf(tmpStr, "%s%s.LST", PathName, ModName);
         if ((ListFile = (HANDLE)_lopen(tmpStr, READ)) == HFILE_ERROR)
            return(ER_CANT_OPEN_FILE);
      }
      else { // Initial Load
         lstrcpy(OldModName, ModName);
         if (!IsModuleName(ModName)) {
            if ((err = ReplaceModuleName()) != GOOD)
               return(err);
         }
      }
      /* Start processing the list file for line numbers */
      if (funcPara->oFlag == MOPEN || !IsLoadOnDemand(funcPara->flags)) {
         if (!ListFileBuf) {
            if ((ListFileBuf=(LPSTR)TMalloc(LDR_BUFSIZE+1)) == NULL)
               return(ER_NO_MEMORY);
         }
         ListBufPtr = ListFileBuf;
         LinesMap.Reset();
         SourceLine = 0;
         if (_llseek(ListFile, 0L, SEEK_SET) == -1)
            return(ER_BAD_SEEK);
         if ((CharsCnt = _lread(ListFile, ListFileBuf, LDR_BUFSIZE)) <= 0)
            return(ER_BADGET);
         if (CharsCnt < LDR_BUFSIZE)
            ListFileEnd = TRUE;
         ListFileBuf[CharsCnt] = '\0';
         /* skip over page headers : 3 lines */
         if ((*ListBufPtr == PAGE_BREAK) &&
             ((err=SkipOverHeader(PAGE_HEADER_BLANK)) != GOOD))
            return(err);
         /* skip over file header :  at least 6 lines */
         if ((err=SkipOverHeader(FILE_HEADER_BLANK)) != GOOD)
            return(err);
      }
   }
   return(GOOD);
}  /* ProcessCOMENT */

/************************* LOCAL FUNCTIONS ***********************************/

/******************************************************************************
**
**  BuildLinesMap
**
******************************************************************************/
PRIVATE RETCODE BuildLinesMap(U16 maxLine)  {
CHAR lineBuf[NAME_LEN+1];
LINENUM listLine;
RETCODE err;
U16 growSize = 0;

   while (LinesMap.Count() < maxLine) {
      // skip over page headers : 3 lines
      if ((*ListBufPtr == PAGE_BREAK) &&
          ((err=SkipOverHeader(PAGE_HEADER_BLANK)) != GOOD))
         return(err);
      if ((err = GetOneLine(lineBuf)) != GOOD)
         return(err);
      if (!lineBuf[0]) { /* blank line in source */
         SourceLine++;
         continue;
      }
      /* line belongs to an included file, not a source line */
      if (lineBuf[HEADER_POS] == HEADER_DELIMITER)
         continue;
      if ((lineBuf[3] != ' ') && sscanf(lineBuf, "%4d ", &listLine)) {
         LINENUM tmpLine=0;
         S16 cnt = LinesMap.Count(), extra;

         SourceLine++;
         if ((extra=(cnt+1)/10000) > 0)   // handle lines greater than 10000
            listLine += extra*10000;
         for (LOOP_VAR i = listLine; i > cnt; i--) {
            if ((i-cnt) == 1)
               tmpLine = SourceLine;
            // insert source line number into the global LinesMap[]
            while ((err = LinesMap.Insert(tmpLine)) != GOOD) {
               // if cannot grow LinesMap || reach the maximum number of lines
               // return error
               if (growSize || (err != ER_NO_MEMORY) ||
                   (LinesMap.Count() == MAX_LINES))
                  return err;
               // grow the LinesMap to hold the new line numbers
               growSize = LinesMap.Size() + DEFAULT_MAX_LINES;
               LinesMap.Grow(growSize);
            }
            // reset growSize to 0 to be reused
            growSize = 0;
         }
         continue;
      }
      else {  /* non-code lines */
         lineBuf[CONTROL_POS] = '\0';
         /* spiltted line, not a source line */
         if (!strcmp(lineBuf, SPLIT_LINE))
            continue;
         lineBuf[4] = '\0';
         /* warning or error, not a source line */
         if (!strcmp(lineBuf, WARNING_ERROR))
            continue;
         SourceLine++;
      }
   }
   return(GOOD);
} /* BuildLinesMap */

/******************************************************************************
**
**  BuildModNamesList
**
******************************************************************************/
PRIVATE RETCODE BuildModNamesList(VOID) {
CHAR nameList[NAME_LEN];
LPSTR ptr;
MNAMESPTR nameObj;
struct ffblk ffblk;
S16 done;
U16 growSize = 0;
RETCODE err=GOOD;

   lstrcpy(nameList, PathName);
   strcat(nameList, "*.LST");
   done = findfirst(nameList, &ffblk, 0);
   while (!done) {
      if ((ptr=strchr(ffblk.ff_name,'.')) != NULL)
         *ptr = '\0';
      // allocate buffer to hold the <tmpName> string
      if ((nameObj = new MNAMES(ffblk.ff_name)) == NULL) {
         DestroyModNames();
         return MNAMES::LastError;
      }
      // insert nameObj into the global ModNamesList[]
      while ((err = ModNamesList.Insert(nameObj)) != GOOD) {
         // if cannot grow ModNamesList || reach the maximum number of names
         // return error
         if (growSize || (err != ER_NO_MEMORY) ||
             (ModNamesList.Count() == MAX_MNAMES)) {
            delete nameObj;
            DestroyModNames();
            return err;
         }
         // grow the ModNamesList to hold the new MNAMES object
         growSize = ModNamesList.Size() + DEFAULT_MAX_MNAMES;
         ModNamesList.Grow(growSize);
      }
      // reset growSize to 0 to be reused
      growSize = 0;
      done = findnext(&ffblk);
   }
// if (!ModNamesList.Count())
//    err = ER_NO_PLM_LIST_FILES; /* Need to generate list files for PL/M modules */
   return(err);
} /* BuildModNamesList */

/******************************************************************************
**
**  GetLanguageType
**
******************************************************************************/
//PRIVATE LANGUAGE_TYPE GetLanguageType(LPSTR tmpStr) {
//
//   if (strstr(tmpStr, "ASM"))
//      return(LANG_ASM);
//   if ((strstr(tmpStr, " C")) || (strstr(tmpStr, "iC")))
//      return(LANG_C);
//   if ((strstr(tmpStr, "C++")) || (strstr(tmpStr, "CPP")))
//      return(LANG_CPP);
//   if (strstr(tmpStr, "PLM86"))
//      return(LANG_PLM);
//   return(LANG_UNKNOWN);
//}  /* GetLanguageType */

/******************************************************************************
**
**  IsModuleName
**
******************************************************************************/
PRIVATE BOOLEAN IsModuleName(LPSTR fileName) {
CHAR fileBuf[NAME_LEN];
HANDLE hFile;
S16 count;
int len;
LPSTR String;

   //**Hera 10/09/96**/
   sprintf(fileBuf, "%s%s.LST", PathName, fileName);
   if ((hFile = (HANDLE)_lopen(fileBuf, READ)) == HFILE_ERROR)
      return(FALSE);

   if ((count = _lread(hFile, (LPSTR)fileBuf, NAME_LEN-1)) > 0) {
      CHAR tmpStr[NAME_LEN];
      LPSTR ptr;

      fileBuf[count] = '\0';
      if (((ptr = strchr(fileBuf,'\r')) != NULL) ||
          ((ptr = strchr(fileBuf,'\n')) != NULL))
         *ptr = '\0';

      /* If $PAGING option is on, the module name is the string
      ** starting from 22nd char in the 1st line; otherwise,
      ** the module name is the last string in the 1st line.
      */
      *tmpStr= '\0';
      //if (*fileBuf == PAGE_BREAK) {
      //   lstrcpy(tmpStr, &fileBuf[CONTROL_POS]);
      //   if ((ptr = strchr(tmpStr, ' ')) != NULL)
      //      *ptr = '\0';
      //}
      //else {
      //   if ((ptr = strrchr(fileBuf, ' ')) != NULL)
      //       lstrcpy(tmpStr, (LPSTR)(ptr+1));
      //}

      len = 0;
      String = fileBuf;
      do{
	 if((ptr = strstr(String,"OF MODULE")) == NULL)
	 {
            len = len+strlen(String)+1;
	    String = fileBuf+len;
         }
	 else
	 {
	    lstrcpy(tmpStr,(LPSTR)(ptr+sizeof("OF MODULE")));
	    if((ptr = strstr(tmpStr,"\r\n")) != NULL)
	        *ptr='\0';
	    len = count;
         }
      }while(len < count);
	  
      /* If the module name is found in the first line of the list file,
      ** the list file belongs to that module. Let's replace the module
      ** name.
      */
      if (!stricmp(ModName, tmpStr)) {
         ListFile = hFile;
         return(TRUE);
      }
   }
   _lclose(hFile);
   return(FALSE);
   //**Hera**/

}  /* IsModuleName */

/******************************************************************************
**
**  GetOneLine
**
******************************************************************************/
PRIVATE RETCODE GetOneLine(LPSTR lineStr) {
LPSTR ptrEnd;
U32 fileOffset;
BOOLEAN done = FALSE;

   while (!done) {
      if ((ptrEnd = strchr(ListBufPtr, '\n')) != NULL) {
         S16 tmpValue, len;

         tmpValue = (S16)((U32)ptrEnd - (U32)ListBufPtr);
         if ((len = min(NAME_LEN, tmpValue)) > 0)
            _fmemcpy(lineStr, ListBufPtr, len);
         if (lineStr[len-1] == '\r') len--;
         lineStr[len] = '\0';
         tmpValue = CharsCnt - tmpValue - 1;
         if (tmpValue >= 0) {
            ListBufPtr = ptrEnd + 1;
            CharsCnt = tmpValue;
            return(GOOD);
         }
         done = TRUE;
      }
      if (ListFileEnd) // bad list file
         return(ER_BAD_FILE);
      fileOffset = tell(ListFile) - CharsCnt;
      if (_llseek(ListFile, fileOffset, SEEK_SET) == -1)
         return(ER_BAD_SEEK);
      if ((CharsCnt = _lread(ListFile, ListFileBuf, LDR_BUFSIZE)) <= 0)
         return(ER_BADGET);
      if (CharsCnt < LDR_BUFSIZE)
         ListFileEnd = TRUE;
      ListFileBuf[CharsCnt] = '\0';
      ListBufPtr = ListFileBuf;
   }
   return(GOOD);
}  /* GetOneLine */

/******************************************************************************
**
**  ReplaceModuleName
**
******************************************************************************/
PRIVATE RETCODE ReplaceModuleName(VOID) {
RETCODE err;
LOOP_VAR i;
//**Hera 10/18/96**/
LPSTR errstring;
//**Hera**/

   if (!ModNamesList.Count() && ((err = BuildModNamesList()) != GOOD))
      return(err);
   for (i=0; i < ModNamesList.Count(); i++) {
      if (!ModNamesList[i]->Used()) {
         if (IsModuleName(ModNamesList[i]->Name())) {
            ModNamesList[i]->Used(TRUE);
            lstrcpy(ModName, ModNamesList[i]->Name());
            return(GOOD);
         }
      }
   }
   //return(ER_INVALID_MODULE_NAME);
   //**Hera 10/18/96**/
   errstring = (char *)TMalloc(strlen(ModName)+5);
   strcpy(errstring,ModName);
   strcat(errstring,".LST");
   LdrWarning(ER_CANT_OPEN_SOURCE_FILE, errstring, NULL, NULL);
   err = 1;
   return err;
   //**Hera**/
} /* ReplaceModuleName */

/******************************************************************************
**
**  SkipOverHeader
**
******************************************************************************/
PRIVATE RETCODE SkipOverHeader(S16 blankLines) {
CHAR dummy[NAME_LEN+1];
RETCODE err;

   do {
      if ((err=GetOneLine(dummy)) != GOOD)
         return(err);
   } while(dummy[0]);
   for (LOOP_VAR i=0; i < blankLines; i++) {
      if ((err=GetOneLine(dummy)) != GOOD)
         return(err);
   }
   return(GOOD);
}  /* SkipOverHeader */

/******************************** E O F ***********************************/
