// *********************************************************************
//    Copyright (c) 1989-2002  Warren Furlow
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// *********************************************************************

// *********************************************************************
// WFile.cpp: implementation file
//    This class is for reading tokens out of a text file.
// *********************************************************************

#include"stdafx.h"
#include"WFile.h"

//********************************
// Constructor - sets buffer size
//********************************
WFile::WFile(
  int nBufferSizeIn)
  {
  pBuffer=(char*)malloc(nBufferSizeIn);
  if (pBuffer)
    nBufferSize=nBufferSizeIn;
  else
    nBufferSize=0;
  hFileHandle=INVALID_HANDLE_VALUE;
  Rewind();
  }


//********************************
WFile::~WFile()
  {
  if (hFileHandle!=INVALID_HANDLE_VALUE)
    CloseHandle(hFileHandle);
  free(pBuffer);
  }


//********************************
// go back to start of file
//********************************
void WFile::Rewind()
  {
  if (hFileHandle!=INVALID_HANDLE_VALUE)
    {
    nFileSize=GetFileSize(hFileHandle,NULL);
    SetFilePointer(hFileHandle,0,NULL,FILE_BEGIN);
    }
  else
    nFileSize=0;
  strCurrentLine.Empty();
  pDataEnd=pBuffer;
  pchCurrent=NULL;
  pszLineStart=NULL;
  pszLineEnd=NULL;
  nLineNumber=0;
  }


//********************************
// opens a file
//********************************
flag WFile::Open(
  const char *szFileName)
  {
  hFileHandle=CreateFile(szFileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
  Rewind();
  return(hFileHandle!=INVALID_HANDLE_VALUE);
  }


//********************************
void WFile::Close()
  {
  if (hFileHandle!=INVALID_HANDLE_VALUE)
    {
    CloseHandle(hFileHandle);
    hFileHandle=INVALID_HANDLE_VALUE;
    }
  Rewind();
  }


//********************************
// sets up one line
//********************************
char *WFile::GetLine()
  {
  // read first block from file
  if (pBuffer==pDataEnd)
    {
    if (!ReadFile(hFileHandle,pBuffer,nBufferSize,&nBytesRead,NULL)||!nBytesRead)
      return(NULL);
    pDataEnd=pBuffer+nBytesRead-1;          // pointer to last char in data
    pszLineStart=pszLineEnd=pBuffer-2;      // set these back by two
    }

  // advance past last cr lf
  pszLineEnd+=2;
  pszLineStart=pszLineEnd;

  // scan up to next cr, leave pszLineEnd pointing at cr
  // or if there is no cr leave pszLineEnd pointing 1 past pDataEnd (place after last char)
  while ( (*(pszLineEnd)!='\r') && (pszLineEnd<=pDataEnd) )
    pszLineEnd++;

  // check for end of buffer
  // this may cut a line in half
  if (pszLineEnd>=pDataEnd)
    {
    char *pch1,*pch2;

    // move last line in buffer to first of buffer
    pch2=pBuffer;
    pch1=pszLineStart;
    while (pch1<=pDataEnd)
      *pch2++=*pch1++;

    // read a new block into pBuffer after the part moved from the old end
    ReadFile(hFileHandle,pch2,nBufferSize-static_cast<int>(pch2-pBuffer),&nBytesRead,NULL);
    pDataEnd=pch2+nBytesRead-1;

    // this is the end of file if there is nothing in buffer and nothing
    // read from file
    if ( ((pch2==pBuffer)&&(nBytesRead==0)) || (nBytesRead==-1) )
      return(NULL);

    // set pszLineEnd and pszLineStart for the first line in pBufferStart
    pszLineEnd=pszLineStart=pBuffer;

    // this makes sure if the first char in the buf is a lf, it gets thrown away
    if (*pszLineStart=='\n')
      pszLineStart++;

    // scan up to next cr, leave pszLineEnd pointing at cr
    // or if there is no cr leave pszLineEnd pointing 1 past pBufferEnd (place after last char)
    while ( (*(pszLineEnd)!='\r') && (pszLineEnd<=pDataEnd) )
      pszLineEnd++;

    // if there is no more in file and there is no terminating cr lf,
    // stick a cr lf on the end
    if (*(pszLineEnd)!='\r')
      {
      pDataEnd=pszLineEnd;
      *(pDataEnd)='\r';
      pDataEnd++;
      *(pDataEnd)='\n';
      }
    }

  pchCurrent=pszLineStart;
  nLineNumber++;
  *pszLineEnd=0;
  strCurrentLine=pchCurrent;  // make a copy of the current line
  return(pchCurrent);         // success, not EOF
  }


//********************************
// Gets a token from current line
// there are two possibilities:  there is a token to scan or there
// is not and the eoln is next
//
// if there is a token:  scans from pchCurrent to end of token.
// Puts a null after token and returns a pointer to the start of the token
//
// if there is no token: scans from pchCurrent to end of line.
// Puts a null in place of eoln and returns a pointer to this null
// pchCurrent will be equal to pszLineEnd in this case
//********************************
char *WFile::GetToken()
  {
  SkipWhiteSpace();
  char *pch=pchCurrent;          // save pointer to first char of token or eoln
  while (*pchCurrent>' ')        // advance past token or do nothing if eoln
    pchCurrent++;
  *(pchCurrent)=0;               // null terminate
  return(pch);
  }


//********************************
// Gets a string
// this expects the string to be delimited on both sides by the char passed in
// for example a double quoted string
//********************************
char *WFile::GetString(char chDelimiter)
  {
  while ( (*pchCurrent!=chDelimiter) && !EOLN() )  // skip over everything until first delimiter is found
    pchCurrent++;
  if (*pchCurrent==chDelimiter)                    // advance past first delimiter
    pchCurrent++;
  char *pch1=pchCurrent;                           // save pointer to first char of string or eoln
  while (*pchCurrent!=chDelimiter && !EOLN() )     // advance until second delimiter or do nothing if eoln
    pchCurrent++;
  char *pch2=pchCurrent;                           // save pointer to second delimiter or eoln
  if (*pchCurrent==chDelimiter)                    // advance past second delimiter
    pchCurrent++;
  *pch2=*(pchCurrent)=0;                           // null terminate at second delimiter
  return(pch1);
  }


//********************************
// this advances pchCurrent from the current position to the first non-white
// space char. (which would either be a token or eoln.)
//
// if pchCurrent is currently pointing to a non-white space then this does nothing
//
// if there is a token: moves pchCurrent to first char of token
//
// if there is no token: moves pchCurrent to eoln but does not change the eoln
// pchCurrent will be equal to pszLineEnd in this case
//
// this functions only under get_line()
//********************************
void WFile::SkipWhiteSpace()
  {
  while ( (*pchCurrent<=' ') && !EOLN() )
    pchCurrent++;
  }


//********************************
// if there is a token: moves pchCurrent to first char of white space after
// token but does not place a null
//
// if there is no token: moves pchCurrent to eoln but does not change the eoln
// pchCurrent will be equal to pszLineEnd in this case
//
// if pchCurrent is currently pointing to a non-white space char (a token)
// then that token is skipped
//********************************
void WFile::SkipToken()
  {
  SkipWhiteSpace();
  while (*pchCurrent>' ')        // advance past token or do nothing if eoln
    pchCurrent++;
  }
