/*=========================================================================
 *
 * FILEUTIL.CPP - 28 November 1998, Dave Humphrey
 *
 * Common file related procedures and functions which I use often.
 *
 *=======================================================================*/

	/* Required includes */
#include "genutil.h"
 
 
/*=========================================================================
 *
 * Begin Variable Definitions
 *
 *=======================================================================*/

  CLogFile SystemLog;	/* The default log file for all applications */

	/* Reduces the amount of log output for some of the functions
	 * in this module */
  boolean QuietLogOutput = FALSE;

/*=========================================================================
 *		End of Variable Definitions
 *=======================================================================*/


/*---------- MSDOS Specific Routines ------------------------------------*/

#ifdef __MSDOS__

#undef  __FUNC__
#define __FUNC__ "CheckDisk()"
/*=========================================================================
 *
 * Function - boolean CheckDisk (NewDrive);
 *
 * Returns TRUE if there is a disk in the specified drive.  Drive 0 is A.
 * This is DOS specific routine.
 *
 *=======================================================================*/
boolean CheckDisk (const int NewDrive) {
  struct find_t FileBlock;
  int InitialDisk;

	/* Save the current drive and attempt to change drives */
  InitialDisk = getdisk();
  setdisk (NewDrive);

	/* Try and find files in drive...no files means no disk. If there is
	 * an error hopefully the DOS error handler will be called with the
	 * familiar 'Ignore, Retry, Abort' message. Return value appropiately */
  if (_dos_findfirst("*.*", FA_DIREC, &FileBlock) != 0) {
    setdisk (InitialDisk);
    return (FALSE);
   }

	/* Change Was succesful */
  return (TRUE);
 }
/*=========================================================================
 *		End of Function check_disk()
 *=======================================================================*/
#endif

/*---------- End of if __MSDOS__ ----------------------------------------*/


#undef  __FUNC__
#define __FUNC__ "CLogFile::Close()"
/*===========================================================================
 *
 * Class CLogFile Method - boolean Close (void);
 *
 * Closes the log file, if currently open.  Returns TRUE on success or FALSE
 * if no log file is currently open.
 *
 *=========================================================================*/
boolean CLogFile::Close (void) {

	/* Is the log file currently open? */
  if (LogFileHandle == NULL) return (FALSE);

  	/* Display some status messages */
  Printf ("Closing Log File...");
  Printf ("\tHeap is %s", GetHeapStatusString());
  Printf ("\tUsed/Free/Total Memory: %lu/%lu/%lu bytes", GetUsedMemory(), GetFreeMemory(), GetTotalMemory());
  
  #ifdef _DEBUG
  #ifdef _WIN32
    Printf ("\tCrtDumpMemoryLeaks returns %d", _CrtDumpMemoryLeaks());
  #endif
  #endif

	/* Close log file */
  fclose (LogFileHandle);
  LogFileHandle = NULL;
  NumTabs = 0;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CLogFile::Close()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CLogFile::Open()"
/*===========================================================================
 *
 * Class CLogFile Method - boolean Open (pFilename, Append);
 *
 * Attempts to create a new log file using the given filename.  Any current
 * log file is closed.  If the Append flag is TRUE and the log file currently
 * exists, log entries will be appended to the end of the file.  If FALSE,
 * the log file will be overwritten if it exists (the default).  Returns
 * TRUE if the log file was successfully opened.
 *
 *=========================================================================*/
boolean CLogFile::Open (const char *pFilename, const boolean Append) {
  char DateString[21];

	/* Close the current log file */
  Close();

	/* Attempt to open file for output, depending on the append flag */
  if (Append)
    LogFileHandle = fopen(pFilename, "at");
  else
    LogFileHandle = fopen(pFilename, "wt");

	/* Ensure the file was successfully opened */
  if (LogFileHandle == NULL) {
    ErrorHandler.SetError(ERR_FILE);
    return (FALSE);
   }

	/* Get the current Date and write it to file */
  _strdate (DateString);
  
  Printf ("Opened Log File '%s' on %s", pFilename, DateString);

	/* Display the initial memory status */
  Printf ("\tHeap is %s", GetHeapStatusString());
  Printf ("\tUsed/Free/Total Memory: %lu/%lu/%lu bytes", GetUsedMemory(), GetFreeMemory(), GetTotalMemory());

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CLogFile::Open()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CLogFile::Open()"
/*===========================================================================
 *
 * Class CLogFile Method - boolean OpenTemp (pFilename, Append);
 *
 * Same as the Open() method but attempts to append the temp directory as
 * given by the envirornment variables.  So, if the filename passed was
 * 'SomeApp.LOG' and the Temp variable was 'C:\TEMP', the log would be
 * opened as 'C:\TEMP\SomeApp.Log'.
 *
 *=========================================================================*/
boolean CLogFile::OpenTemp (const char *pFilename, const boolean Append) {
  char   LogFilename[512];
  char*  pTempPath;

	/* Get the temporary directory path */
  pTempPath = getenv("TEMP");
  if (pTempPath == NULL) getenv("TMP");

	/* No temp path exists, just use the given filename */  
  if (pTempPath == NULL) {
    strnncpy (LogFilename, pFilename, 500);
   }
  else {
    strnncpy (LogFilename, pTempPath, 500);
    TerminatePath(LogFilename);
    strncat (LogFilename, pFilename, 511 - strlen(LogFilename));
   }

	/* Call the Open() method with the new filename */
  return Open(LogFilename, Append);
 }
/*===========================================================================
 *		End of Class Method CLogFile::Open()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CLogFile::Printf()"
/*===========================================================================
 *
 * Class CLogFile Method - boolean Printf (pString, ...);
 *
 * Outputs a log file entry in the usual printf() format.  Returns TRUE
 * on success, or FALSE on error.
 *
 *=========================================================================*/
boolean CLogFile::Printf (const char *pString, ...) {
  char    TimeString[16];
  va_list args;
  int     LoopCounter;

	/* Ensure the log file is currently open and ensure valid input */
  if (!IsOpen() || pString == NULL) return (FALSE);

	/* Get the current Time and write to file has a header text */
  _strtime (TimeString);
  fprintf (LogFileHandle, "%s - ", TimeString);

	/* Output the tabs, if any */
  for (LoopCounter = 0; LoopCounter < NumTabs; LoopCounter++) {
    fputc('\t', LogFileHandle);
   }

	/* Print the arugment list to file */
  va_start (args, pString);
  vfprintf (LogFileHandle, pString, args);

  	/* Output to the optional hook procedure */
  if (pHookProc != NULL) pHookProc(pString, args);

  va_end (args);

	/* A linefeed */
  fprintf (LogFileHandle, "\n");

	/* Flush the file stream to make sure the written characters are written
	 * to the file. This is in case the program crashes and isn't closed. If
	 * this did happen without the following line, we'd probably lose some data. */
  fflush (LogFileHandle);
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CLogFile::Printf()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CLogFile::Printf()"
/*===========================================================================
 *
 * Class CLogFile Method - boolean Printf (pFile, pString, ...);
 *
 * Outputs a log file entry in the usual printf() format to the log file
 * and the given stream.  Returns TRUE on success, or FALSE on error.
 *
 *=========================================================================*/
boolean CLogFile::Printf (FILE* pFile, const char *pString, ...) {
  char    TimeString[16];
  va_list args;
  int     LoopCounter;

	/* Ensure the log file is currently open and ensure valid input */
  if (!IsOpen() || pString == NULL) return (FALSE);

	/* Get the current Time and write to file has a header text */
  _strtime (TimeString);
  fprintf (LogFileHandle, "%s - ", TimeString);

	/* Output the tabs, if any */
  for (LoopCounter = 0; LoopCounter < NumTabs; LoopCounter++) {
    fputc('\t', LogFileHandle);
    fputc('\t', pFile);
   }

	/* Print the arugment list to file */
  va_start (args, pString);
  vfprintf (LogFileHandle, pString, args);
  if (pFile != NULL) vfprintf (pFile, pString, args);

  	/* Output to the optional hook procedure */
  if (pHookProc != NULL) pHookProc(pString, args);

  va_end (args);

	/* Flush the file stream to make sure the written characters are written
	 * to the file. This is in case the program crashes and isn't closed. If
	 * this did happen without the following line, we'd probably lose some data. */
  fflush (LogFileHandle);
  fflush (pFile);
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CLogFile::Printf()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "ChangeDirectory()"
/*=========================================================================
 *
 * Function - ChangeDirectory (pPath);
 *
 * Returns TRUE if the specified path is a valid directory.  Does this
 * by attempting to change paths.  If successful the current path is
 * changed to the given directory.
 *
 *=======================================================================*/
boolean ChangeDirectory (const char* pPath) {

	/* Make sure the given path is valid */
  if (pPath == NULL || *pPath == '\0') {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

	/* Attempt to change directory */
  if (chdir(pPath)) return(FALSE);

  return(TRUE);
 }
/*=========================================================================
 *		End of Function ChangeDirectory()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "CompareExtension()"
/*===========================================================================
 *
 * Function - boolean CompareExtension (pFilename, pString);
 *
 * Compares the extension of the given filename with the given string
 * and returns true if they match (case insensitive). The '.' extension
 * character is not included. 
 *
 *=========================================================================*/
boolean CompareExtension (const char* pFilename, const char* pString) {
  size_t      StringIndex;
  int	      Result;

	/* Ensure valid input */
  if (pFilename == NULL || pString == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

	/* Find the end of the filename */
  StringIndex = strlen(pFilename) - 1;

	/* Find the last '.' character before the path starts */
  while (pFilename[StringIndex] != PATH_CHARACTER && StringIndex != 0) {

	/* Is the current character the '.'? */
    if (pFilename[StringIndex] == '.') {

		/* Compare the extension starting at the next character in filename */
      Result = stricmp(pFilename + StringIndex + 1, pString);
      if (Result == 0) return (TRUE);
      return (FALSE);
     }

    StringIndex--;
   }

	/* No extension found */
  return (FALSE);
 }
/*===========================================================================
 *		End of Function CompareExtension()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CopyFile()"
/*=========================================================================
 *
 * Function - boolean CopyFile (const char* pInputFile, const char* pOutputFile);
 *
 * Copies the given input file to the output file.  Returns FALSE on any
 * error.
 *
 *=======================================================================*/
boolean CopyFile (const char* pInputFile, const char* pOutputFile) {
  FILE* 	pInputHandle;
  FILE* 	pOutputHandle;
  unsigned char Buffer[COPYFILE_BUFFERSIZE];
  size_t  	ReadSize;
  size_t	WriteSize;

	/* Ensure valid input */
  if (pInputFile == NULL || pOutputFile == NULL) {
    SET_EXT_ERROR2(ERR_NULL, "Invalid filename(s) input!");
    return (FALSE);
   }

	/* Attempt to open input file */
  pInputHandle = openfile(pInputFile, "rb");

  if (pInputHandle == NULL) {
    SET_EXT_ERROR3(ERR_FILE, "Failed to open input file '%s'!", pInputFile);
    return (FALSE);
   }

	/* Attempt to open output file */
  pOutputHandle = openfile(pOutputFile, "wb");

  if (pOutputHandle == NULL) {
    SET_EXT_ERROR3(ERR_FILE, "Failed to open output file '%s'!", pOutputFile);
    fclose (pInputHandle);
    return (FALSE);
   }

	/* Read and copy file in parts until finished */
  do {
		/* Input data from file */
    ReadSize = fread(Buffer, 1, COPYFILE_BUFFERSIZE, pInputHandle);

		/* Output to file */
    WriteSize = fwrite(Buffer, 1, ReadSize, pOutputHandle);

    if (WriteSize != ReadSize) {
      fclose (pInputHandle);
      fclose (pOutputHandle);
      SET_EXT_ERROR2(ERR_WRITE, "Failed to write to output file!");
      return (FALSE);
     }

  } while (ReadSize == COPYFILE_BUFFERSIZE);

	/* Close files */
  fclose (pInputHandle);
  fclose (pOutputHandle);
  return (TRUE);
 }
/*=========================================================================
 *		End of Function CopyFile()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "CreateNewExtension()"
/*========================================================================
 *
 * Function - char* CreateNewExtension (pOldFile, pNewFile, pNewExt, MaxSize);
 *
 * Creates a copies the given filename into the new string, replacing the
 * old extension with the new one.  Returns NULL on any error or the
 * new string on success.  The input extension can include the '.' character
 * or not.  If it doesn't, one is automatically inserted.
 *
 *======================================================================*/
char* CreateNewExtension (const char* pOldFile, char* pNewFile, const char* pNewExt, const size_t MaxSize) {

	/* Ensure valid input */
  if (MaxSize < 4 || pOldFile == NULL || pNewFile == NULL || pNewExt == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (NULL);
   }
	/* Create the new filename */
  strnncpy(pNewFile, pOldFile, MaxSize-4);
  RemoveExt(pNewFile);

	/* Add the extension, ensuring the '.' character is present */
  if (*pNewExt != '.') chrcat(pNewFile, '.');
  strcat(pNewFile, pNewExt);

  return (pNewFile);
 }
/*========================================================================
 *		End of Function CreateNewExtension()
 *======================================================================*/


#undef  __FUNC__
#define __FUNC__ "CreatePath()"
/*=========================================================================
 *
 * Function - char *CreatePath (pNewPath, pString, MaxStringLength);
 *
 * Creates a path from the given string.  Copies up to MaxStringLength
 * bytes into the path string and ensures that the given path ends in
 * a '\' or '/' character.
 *
 *=======================================================================*/
char *CreatePath (char* pNewPath, const char* pString, const size_t MaxStringLength) {

	/* Ensure all input is valid */
  if (pNewPath == NULL || pString == NULL || MaxStringLength == 0) return (pNewPath);

	/* Copy the given string into the new path */
  strnncpy (pNewPath, pString, MaxStringLength-1);

	/* Ensure the path ends with a path character */
  TerminatePath(pNewPath);
  return (pNewPath);
 }
/*=========================================================================
 *		End of Function CreatePath()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "ExtractFilename()"
/*=========================================================================
 *
 * Function - char *ExtractFilename (pFilename, pPath, MaxStringLength);
 *
 * Copies just the filename from the given path into the given file (up
 * to MaxStringLength bytes).
 *
 *=======================================================================*/
char* ExtractFilename (char* pFilename, char* pPath, const size_t MaxStringLength) {

	/* Find the start of the filename */
  pPath = FindFilename (pPath);
  if (pPath == NULL) return (pFilename);

	/* Copy the filename into the given buffer */
  strnncpy (pFilename, pPath, MaxStringLength);
  return (pFilename);
 }
/*=========================================================================
 *		End of Function ExtractFilename()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "ExtractPath()"
/*=========================================================================
 *
 * Function - char *ExtractPath (pPath, pString, MaxStringLength);
 *
 * Copies just the path from the given string and copies into the 
 * specified path (at most MaxStringLength bytes).  Returns a pointer to
 * the new path string.
 *
 *=======================================================================*/
char* ExtractPath (char* pPath, const char* pString, const size_t MaxStringLength) {
  size_t StringIndex;

	/* Ensure all the input is valid */
  if (pString == NULL || pPath == NULL || MaxStringLength == 0) {
    SET_EXT_ERROR(ERR_NULL);
    return (pPath);
   }

	/* Copy the string into the new path */
  strnncpy (pPath, pString, MaxStringLength);
  StringIndex = strlen(pPath);

	/* Remove any filename from the new path, if any */
  while (StringIndex != 0) {
    StringIndex--;

    if (pPath[StringIndex] == PATH_CHARACTER) {
      StringIndex++;
      break;
     }
   }

  pPath[StringIndex] = 0;
  return (pPath);
 }
/*=========================================================================
 *		End of Function ExtractPath()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "FileExists()"
/*=========================================================================
 *
 * Function - boolean FileExists (pFilename);
 *
 * Returns TRUE if the specified file exists and can be opened for reading.
 *
 *=======================================================================*/
boolean FileExists (const char* pFilename) {
  FILE* FileHandle;	/* File pointer */

	/* Attempt to open the file for reading */
  if (pFilename == NULL || *pFilename == NULL_CHAR) return (FALSE);
  if (!(FileHandle = fopen(pFilename, "r"))) return(FALSE);

	/* File was opened and therefore exists, close and return success */
  fclose(FileHandle);
  return(TRUE);
 }
/*=========================================================================
 *		End of Function FileExists)
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "FindFilename()"
/*=========================================================================
 *
 * Function - char *FindFilename (pPath);
 *
 * Returns a pointer to the first character in the filename in the given 
 * complete path.
 *
 *=======================================================================*/
char* FindFilename (char* pPath) {
  size_t StringIndex;

	/* Ensure the input is valid */
  if (pPath == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (pPath);
   }

	/* Start at the end of the given path */
  StringIndex = strlen(pPath);

  while (StringIndex != 0) {
    StringIndex--;

    if (pPath[StringIndex] == '\\') {
      StringIndex++;
      break;
     }
   }

  return (pPath+StringIndex);
 }
/*=========================================================================
 *		End of Function ExtractFilename()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "GetFilesize()"
/*=========================================================================
 *
 * Function - long GetFilesize (pFilename);
 *
 * Returns the size, in bytes, of the specified filename.  Returns 
 * INVALID_FILESIZE on any error.
 *
 *=======================================================================*/
long GetFilesize (const char* pFilename) {
  FILE* FileHandle;
  long  FileSize;

	/* Attempt to open the file */
  if (!(FileHandle = fopen(pFilename, "r"))) return (INVALID_FILESIZE);

	/* Jump to the end of the file and record file size*/
  fseek (FileHandle, 0, SEEK_END);
  FileSize = ftell(FileHandle);
  fclose(FileHandle);

	/* Return filesize... */
  return (FileSize);
 }
/*=========================================================================
 *		End of Function GetFilesize()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "GetFilesize()"
/*=========================================================================
 *
 * Function - long GetFilesize (FileHandle);
 *
 * Returns the size a currently open file.  Returns INVALID_FILESIZE 
 * on any error.
 *
 *=======================================================================*/
long GetFilesize (FILE* FileHandle) {
  long FileSize;
  long InitialOffset;

	/* Make sure the file pointer is valid */
  if (FileHandle == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (INVALID_FILESIZE);
   }
  
	/* Save the current offset in the file */
  InitialOffset = ftell(FileHandle);

	/* Move to end of file and save offset */
  fseek (FileHandle, 0, SEEK_END);
  FileSize = ftell(FileHandle);

	/* Return to initial offset */
  fseek (FileHandle, InitialOffset, SEEK_SET);
  return (FileSize);
 }
/*=========================================================================
 *		End of Function GetFilesize()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "HasExtension()"
/*=========================================================================
 *
 * Function - boolean HasExtension (pFilename);
 *
 * Returns TRUE if the given filename contains an extension.
 *
 *=======================================================================*/
boolean HasExtension (const char* pFilename) {
  size_t StringIndex;

	/* Check for invalid input */
  if (pFilename == NULL || *pFilename == '\0') {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

	/* Find the end of the filename */
  StringIndex = strlen(pFilename) - 1;

	/* Look for an extension character before any path information */
  while (pFilename[StringIndex] != 0) {

    if (pFilename[StringIndex] == '.')
      return (TRUE);
    else if (pFilename[StringIndex] == PATH_CHARACTER)
      return (FALSE);

    StringIndex--;
   }

	/* No extension found */
  return (FALSE);
 }
/*=========================================================================
 *		End of Function HasExtension()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "HasPath()"
/*=========================================================================
 *
 * Function - boolean HasPath (pString);
 *
 * Returns TRUE if the given string contains path information.
 *
 *=======================================================================*/
boolean HasPath (const char* pString) {

	/* Make sure string is valid */
  if (pString == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

  while (*pString) {
    if (*pString == '\\' || *pString == '/' || *pString == ':') return (TRUE);
    pString++;
   }

  return (FALSE);
 }
/*=========================================================================
 *		End of Function HasPath()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "IsDirectory()"
/*=========================================================================
 *
 * Function - boolean IsDirectory (pPath);
 *
 * Returns TRUE if the specified path is a valid but does not change
 * change directories.
 *
 *=======================================================================*/
boolean IsDirectory(const char* pPath) {
  char InitialPath[_MAX_PATH+1];

	/* Save the initial directory */
  if (getcwd(InitialPath, _MAX_PATH) == NULL) {
    SET_EXT_ERROR(ERR_STRING);
    return (FALSE);
   }

	/* Attempt to change directory */
  if (chdir(pPath)) return(FALSE);

	/* Restore the initial path and return success */
  chdir(InitialPath);
  return(TRUE);
 }
/*=========================================================================
 *		End of Function IsDirectory()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "IsFileWriteable()"
/*========================================================================
 *
 * Function - boolean IsFileWriteable (pFilename);
 *
 * Returns TRUE if the given file can be written to.
 *
 *======================================================================*/
boolean IsFileWriteable (const char* pFilename) {
  FILE* pFileHandle;

	/* Ensure valid input */
  if (pFilename == NULL) return (FALSE);

	/* Attempt to open the file for writing */
  pFileHandle = fopen (pFilename, "ab");
  if (pFileHandle == NULL) return (FALSE);

	/* Close the now open file and return success */
  fclose (pFileHandle);
  return (TRUE);
 }
/*========================================================================
 *		End of Function IsFileWriteable(0
 *======================================================================*/
 

#undef  __FUNC__
#define __FUNC__ "IsWildcard()"   
/*=========================================================================
 *
 * Function - boolean iswildcard (pString);
 *
 * Returns TRUE if the given string has any '*' or '?' wildcard characters
 * in it.
 *
 *=======================================================================*/
boolean IsWildcard (const char* pString) {

	/* Make sure the given string is valid */
  if (pString == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

	/* Search entire string for a wildcard character */
  while (*pString != NULL_CHAR) {
    if (*pString == '*' || *pString == '?') return(TRUE);
    pString++;
   }

	/* No wildcard characters found */
  return (FALSE);
 }
/*=========================================================================
 *		End of Function IsWildcard()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "MakeExtDirectory()"
/*===========================================================================
 *
 * Function - boolean MakeExtDriectory (pNewPath);
 *
 * Makes the given directory.  Supports making of multiple sub-dirs at
 * once.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean MakeExtDirectory (const char* pNewPath) {
  char* pAllocatedPath;
  char* pPath;
  char* pSubPath;
  char* pPathStart;

	/* Make sure the input path is valid */
  if (pNewPath == NULL || *pNewPath == NULL_CHAR) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

	/* Parse out the first dir level of the given path */
  pAllocatedPath = CreateString(pNewPath);
  pPathStart = pAllocatedPath;
  pPath = strtok(pAllocatedPath, "\\");

	/* Ignore the drive parameter, if any */
  if (pPath[1] == ':' && pPath[2] == NULL_CHAR) {
    pSubPath = pPath;
    pPath = strtok(NULL, "\\");
    pSubPath[2] = PATH_CHARACTER;
   }
  
	/* Parse out each subpath from the given string */
  while (pPath != NULL) {
   
	/* Does the sub-path currently exist? */
    if (!IsDirectory(pPathStart)) {

		/* Path doesn't exist, try to create it */
      if (mkdir(pPathStart) == -1) {
        ErrorHandler.SetError(ERR_SYSTEM);
	DestroyPointer(pAllocatedPath);
        return (FALSE);
       }
     }

    pSubPath = pPath;
    pPath = strtok(NULL, "\\");
    if (pPath != NULL) pPath[-1] = PATH_CHARACTER;
   }

  DestroyPointer(pAllocatedPath);
  return (TRUE);
 }
/*===========================================================================
 *		End of Function MakeExtDirectory()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "openfile()"
/*=========================================================================
 *
 * Function - FILE *openfile (filename, mode, ExitOnError);
 *
 * A function I use to open files as per fopen().  Returns a handle to the
 * open file or NULL on error.  If ExitOnError is TRUE program will halt on 
 * a critical error (default is FALSE).  Writes log entries as required.
 *
 *=======================================================================*/
FILE* openfile (const char* pFilename, const char* pMode, const boolean ExitOnError) {
  FILE* FileHandle;

	/* Ensure valid input */
  if (pFilename == NULL || pMode == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

	/* Output log file entry */
  if (!QuietLogOutput) 
    SystemLog.Printf ("Attempting to open file '%s' in mode '%s'", pFilename, pMode);

	/* Attempt to open the filename in the given mode */
  if (!(FileHandle = fopen(pFilename, pMode))) {

		/* Output the log entry and set error code on failure */
    SET_EXT_ERROR4(ERR_FILE, "Failed to open file '%s' in mode '%s'!", pFilename, pMode);

		/* Halt the program if required */
    if (ExitOnError) 
      ErrorHandler.Exit("Failed to open the file '%s' in mode '%s'!", pFilename, pMode);
    else
      return (NULL);
   }

	/* A successful open, return the file pointer */
  return (FileHandle);
 }
/*=========================================================================
 *		End of Function openfile()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "read_eol()"
/*=========================================================================
 *
 * Function - int read_eol (FileHandle, pString, MaxStringLength);
 *
 * Reads up to MaxStringLength characters or a the first Carriage Return
 * from the given file f into the given string.  If string is NULL 
 * characters are merely read and not stored in string.  Returns READ_OK 
 * on success, READ_MSL if max string length was reached, or READ_EOF 
 * on end-of-file.
 *
 *=======================================================================*/
int read_eol (FILE* FileHandle, char* pString, const size_t MaxStringLength) {
  int    InputChar;
  size_t StringLength = 0;

	/* Ignore any invalid file handle input */
  if (FileHandle == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (READ_EOF);
   }

	/* Read in the 1st character from file */
  InputChar = fgetc(FileHandle);

	/* Loop until an EOF marker is found, a Carriage Return is found or
	 * the loaded string length exceeds the maximum allowable */
  while (InputChar != EOF && InputChar != CR && (pString == NULL || StringLength < MaxStringLength || MaxStringLength == 0)) {

	/* If buffer is NULL, this means that we want to read to the end of
	 * the current line in a file, ignoring all the data on that line */
    if (pString != NULL) {
      pString[StringLength] = InputChar;
      StringLength++;
     }

	/* Read the next character */
    InputChar = fgetc(FileHandle);
   }

	/* Make sure the string is terminated */
  if (pString != NULL) pString[StringLength] = NULL_CHAR;

	/* Determine what to return */
  if (InputChar == EOF)
    return (READ_EOF);

	/* Max String Length Reached */
  else if (pString && StringLength >= MaxStringLength) {
    SystemLog.Printf ("FILEUTIL.CPP - read_eol(): Max string length exceeded, %u!", MaxStringLength);
    return (READ_MSL);
   }

	/* Otherwise, return success */
  return(READ_OK);
 }
/*=========================================================================
 *		End of Function read_eol()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "read_word()"
/*=========================================================================
 *
 * Function - int read_word (FileHandle, pString, MaxStringLength);
 *
 * Attempt to read in a word from the given file into a string (up to
 * MaxStringLength characters).  If string is NULL, bytes are just read and 
 * not stored.  Returns READ_EOF on end-of-file, READ_MSL if max string 
 * length was reaced, READ_OK on success, or READ_EOL on end of line found.
 * White space is considered any space, end-of-line or TAB character.
 *
 *=======================================================================*/
int read_word (FILE* FileHandle, char* pString, const unsigned int MaxStringLength) {
  int    InputChar;
  size_t StringLength = 0;

	/* Make sure the file handle is valid */
  if (FileHandle == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (READ_EOF);
   }

	/* Ignore any initial spaces and tabs from file */
  do {
    InputChar = fgetc(FileHandle);
  } while (InputChar == ' ' && InputChar == 9 && InputChar != EOF && InputChar != CR);
    
	/* Loop until a space is read, a Carriage return is read, an EOF
	 * marker is read, or max_string_length characters have been read */
  while (InputChar != ' ' && InputChar != 9 && InputChar != EOF && InputChar != CR && (pString == NULL || StringLength < MaxStringLength)) {

	/* Add the string buffer, but only if it was supplied */
    if (pString != NULL) {
      pString[StringLength] = InputChar;
      StringLength++;
     }

	/* Get the next character from the file */
    InputChar = fgetc(FileHandle);
   }

	/* Make sure the string is null terminated */
  if (pString == NULL) pString[StringLength] = NULL_CHAR;

	/* Determine what code to return */
  if (InputChar == EOF)
    return (READ_EOF);
  else if (pString != NULL && StringLength >= MaxStringLength) {
    SystemLog.Printf ("FILEUTIL.CPP - read_word(): Max string length exceeded, %u!", MaxStringLength);
    return (READ_MSL);
   }
  else if (InputChar == CR)
    return (READ_EOL);

	/* Return a success */
  return (READ_OK);
 }
/*=========================================================================
 *		End of Function read_word()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "read_file()"
/*=========================================================================
 *
 * Function - char *read_file (pFilename);
 *
 * Attempts to read in an entire file and returns an allocated pointer.
 * On error returns NULL.  Can only read a file up to UINT_MAX in size.
 *
 *=======================================================================*/
char* read_file (const char* pFilename) {
  FILE* FileHandle;
  char* pFileData;
  long  FileSize;
  long  InputSize;

	/* Attempt to Open file in read-only binary mode */
  FileHandle = openfile(pFilename, "rb");
  if (FileHandle == NULL) return (NULL);

	/* Retrieve the size of the file in bytes and ensure a valid size */
  FileSize = GetFilesize(FileHandle);

  if ((unsigned long) FileSize > (unsigned long)UINT_MAX) {
    SET_EXT_ERROR3(ERR_UINTMAX, "File larger than %u in size specified!", UINT_MAX);
    fclose (FileHandle);
    return (NULL);
   }

	/* Attempt to allocate memory to hold file data*/
  pFileData = CreateString((size_t)(FileSize + 1));

	/* Read in the file all at once */
  InputSize = fread (pFileData, sizeof(char), (size_t)FileSize, FileHandle);

	/* Ensure the proper number of bytes read */
  if (InputSize != FileSize) {
    SET_EXT_ERROR4(ERR_READ, "Expected %ld bytes but read only %ld!", FileSize, InputSize);
   }

	/* Close file and return pointer */
  fclose (FileHandle);
  return (pFileData);
 }
/*=========================================================================
 *		End of Function read_file()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "read_int()"
/*=========================================================================
 *
 * Function - int read_int (pFileHandle);
 *
 * Reads an integer from the specified file.  Sets the error code on any
 * error to the appropiate value and returns 0.  Any previous error 
 * information is cleared.
 *
 *=======================================================================*/
int read_int (FILE* pFileHandle) {
  int     InputInteger;
  boolean Result;

	/* Clear any error information */
  ErrorHandler.SetError();

  Result = read_int(pFileHandle, InputInteger);
  if (Result) return (InputInteger);
  return (0);
 }
/*=========================================================================
 *		End of Function read_int()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "read_long()"
/*=========================================================================
 *
 * Function - long read_long (pFileHandle);
 *
 * Reads a long integer from the specified file.  Set the appropiate error
 * code on any error and returns 0. Any previous error information is cleared.
 *
 *=======================================================================*/
long read_long (FILE* pFileHandle) {
  long    InputInteger;
  boolean Result;

	  /* Clear any error information */
  ErrorHandler.SetError();

  Result = read_long(pFileHandle, InputInteger);
  if (Result) return (InputInteger);
  return (0);
 }
/*=========================================================================
 *		End of Function read_long()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "read_short()"
/*=========================================================================
 *
 * Function - short read_short (pFileHandle);
 *
 * Reads a short integer from the specified file.  Sets the appropiate
 * error code on any error and returns 0. Any previous error information
 * is cleared.
 *
 *=======================================================================*/
short read_short (FILE* pFileHandle) {
  short   InputInteger;
  boolean Result;

	  /* Clear any error information */
  ErrorHandler.SetError();

  Result = read_short(pFileHandle, InputInteger);
  if (Result) return (InputInteger);
  return (0);
 }
/*=========================================================================
 *		End of Function read_short()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "read_motlong()"
/*=========================================================================
 *
 * Function - long read_motlong (pFileHandle);
 *
 * Reads a long integer from the specified file using the Motorolal byte
 * order.  Sets the appropiate error code on any error and returns 0.
 * Any previous error information is cleared.
 *
 *=======================================================================*/
long read_motlong (FILE* pFileHandle) {
  long    InputInteger;
  boolean Result;

	/* Clear any error information */
  ErrorHandler.SetError();

  Result = read_motlong(pFileHandle, InputInteger);
  if (Result) return (InputInteger);
  return (0);
 }
/*=========================================================================
 *		End of Function read_motlong()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "read_int()"
/*=========================================================================
 *
 * Function - boolean read_int (pFileHandle, Value);
 *
 * Reads an integer from the specified file. Returns FALSE on any error.
 * The size of an integer depends on the platform compiled under. In DOS
 * it is usually 16 bit, in Windows it is 32 bit.
 *
 *=======================================================================*/
boolean read_int (FILE* pFileHandle, int& Value) {
  size_t InputSize;

	/* Make sure the file handle is valid */
  if (pFileHandle == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

	/* Read in the integer value */
  InputSize = fread(&Value, 1, sizeof(int), pFileHandle);

	/* Check for any read error */
  if (InputSize != sizeof(int)) {
    SET_EXT_ERROR4(ERR_READ, "Input only %u of %u bytes!", InputSize, sizeof(int));
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function read_int()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "read_long()"
/*=========================================================================
 *
 * Function - boolean read_long (pFileHandle, Value);
 *
 * Reads a long integer from the specified file.  Returns FALSE on any
 * error.  A long integer may be different sizes on various platforms,
 * though it is usually 32 bit.
 *
 *=======================================================================*/
boolean read_long (FILE* pFileHandle, long& Value) {
  size_t InputSize;

	/* Make sure the file handle is valid */
  if (pFileHandle == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

	/* Read the value */
  InputSize = fread (&Value, 1, sizeof(long), pFileHandle);

	/* Ensure the value was correctly read */
  if (InputSize != sizeof(long)) {
    SET_EXT_ERROR4(ERR_READ, "Input only %u of %u bytes!", InputSize, sizeof(long));
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function read_long()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "read_short()"
/*=========================================================================
 *
 * Function - boolean read_short (pFileHandle, Value);
 *
 * Reads a short integer from the specified file.  Returns FALSE on any
 * error.  A short integer is usually 16 bit but may depend on the 
 * platform compiled under.
 *
 *=======================================================================*/
boolean read_short (FILE* pFileHandle, short& Value) {
  size_t InputSize;

	/* Make sure the file handle is valid */
  if (pFileHandle == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

	/* Read in the integer value */
  InputSize = fread(&Value, 1, sizeof(short), pFileHandle);

	/* Check for any read error */
  if (InputSize != sizeof(short)) {
    SET_EXT_ERROR4(ERR_READ, "Input only %u of %u bytes!", InputSize, sizeof(short));
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function read_short()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "read_motlong()"
/*=========================================================================
 *
 * Function - long read_motlong (pFileHandle, Value);
 *
 * Reads a long integer from the specified file using the Motorolal byte
 * order.  Returns FALSE on any error.  A long integer
 * is usually 32 bit but may depend on the platform compiled under.
 *
 *=======================================================================*/
boolean read_motlong (FILE* pFileHandle, long& Value) {
  unsigned char InputData[sizeof(long)];
  size_t	InputSize;
  
	/* Make sure the file handle is valid */
  if (pFileHandle == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

	/* Read in the integer value */
  InputSize = fread(&InputData, 1, sizeof(long), pFileHandle);

	/* Check for any read error */
  if (InputSize != sizeof(long)) {
    SET_EXT_ERROR4(ERR_READ, "Input only %u of %u bytes!", InputSize, sizeof(long));
    return (FALSE);
   }

	/* Compute the proper long integer value */
  Value = (long) (((unsigned long)InputData[3])      + (((unsigned long)InputData[2])<<8) + 
  		 (((unsigned long)InputData[1])<<16) + (((unsigned long)InputData[0])<<32));

  return (TRUE);
 }
/*=========================================================================
 *		End of Function read_motlong()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "read_textfile()"
/*=========================================================================
 *
 * Function - char *read_textfile (pFilename);
 *
 * Just like the read_file() function except for the case of a text file.
 * Attempts to read in an entire file and returns an allocated pointer.
 * On error returns NULL.  Can only read a file up to UINT_MAX in size.
 *
 *=======================================================================*/
char* read_textfile (const char *pFilename) {
  FILE* FileHandle;
  char* pFileData;
  long  FileSize;
  long  InputSize;

	/* Attempt to Open file in read-only text mode */
  FileHandle = openfile(pFilename, "rt");
  if (FileHandle == NULL) return (NULL);

	/* Retrieve the size of the file in bytes and ensure a valid size */
  FileSize = GetFilesize(FileHandle);

  if ((unsigned long) FileSize > (unsigned long)UINT_MAX) {
    SET_EXT_ERROR3(ERR_UINTMAX, "File larger than %u in size specified!", UINT_MAX);
    fclose (FileHandle);
    return (NULL);
   }

	/* Attempt to allocate memory to hold file data*/
  pFileData = CreateString((size_t)(FileSize + 1));

	/* Read in the file all at once */
  InputSize = fread (pFileData, sizeof(char), (size_t)FileSize, FileHandle);

	/* Ensure the proper number of bytes read */
  if (InputSize != FileSize) {
    SET_EXT_ERROR4(ERR_READ, "Expected %ld bytes but read only %ld!", FileSize, InputSize);
   }

	/* Close file and return pointer */
  fclose (FileHandle);
  return (pFileData);
 }
/*=========================================================================
 *		End of Function read_textfile()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "RemoveExt()"
/*=========================================================================
 *
 * Function - char *RemoveExt (pFilename);
 *
 * Removes the extension from the specified filename, if present. Returns
 * the pointer to the string.
 *
 *=======================================================================*/
char *RemoveExt (char *pFilename) {
  size_t StringIndex;

	/* Ignore any invalid input */
  if (pFilename == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (NULL);
   }

	/* Find the end of the filename */
  StringIndex = strlen(pFilename) - 1;

	/* Find the last '.' character before the path starts */
  while (pFilename[StringIndex] != PATH_CHARACTER && StringIndex != 0) {

	/* Is the current character the '.'? Truncate string and return if so */
    if (pFilename[StringIndex] == '.') {
      pFilename[StringIndex] = NULL_CHAR;
      return (pFilename);
     }

    StringIndex--;
   }

	/* No extension found in string */
  return (pFilename);
 }
/*=========================================================================
 *		End of Function RemoveExt()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "TerminatePath()"
/*=========================================================================
 *
 * Function - char *TerminatePath (pString);
 *
 * Ensures that the given path ends in a '\' or '/' character.  Returns 
 * a pointer to the string.  Assumes that the string is large enough for
 * another character to be added.
 *
 *=======================================================================*/
char* TerminatePath (char* pString) {
  size_t LastChar;

	/* Ignore invalid input */
  if (pString == NULL || *pString == '\0') return (pString);

	/* Find the last character in the string */
  LastChar = strlen(pString) - 1;

	/* Ensure the last character is a path character */
  if (pString[LastChar] != PATH_CHARACTER) {
    pString[LastChar+1] = PATH_CHARACTER;
    pString[LastChar+2] = 0;
   }

  return (pString);
 }
/*=========================================================================
 *		End of Function TerminatePath()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "write_short()"
/*=========================================================================
 *
 * Function - boolean write_short (FileHandle, OutputValue);
 *
 * Writes a short integer to a file. Returns FALSE on any error.
 *
 *=======================================================================*/
boolean write_short (FILE* FileHandle, const short OutputValue) {
  size_t OutputSize;

	/* Output the data */
  OutputSize = fwrite(&OutputValue, sizeof(short), 1, FileHandle);
  
	/* Ensure the data was properly output */
  if (OutputSize != 1) {
    SET_EXT_ERROR(ERR_WRITE);
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function write_short()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "write_int()"
/*=========================================================================
 *
 * Function - boolean write_int (FileHandle, OutputValue);
 *
 * Writes an  integer to a file. Returns FALSE on any error.
 *
 *=======================================================================*/
boolean write_long (FILE* FileHandle, const int OutputValue) {
  size_t OutputSize;

	/* Output the data */
  OutputSize = fwrite(&OutputValue, sizeof(int), 1, FileHandle);
  
	/* Ensure the data was properly output */
  if (OutputSize != 1) {
    ErrorHandler.SetError(ERR_WRITE);
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function write_int()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "write_long()"
/*=========================================================================
 *
 * Function - boolean write_long (FileHandle, OutputValue);
 *
 * Writes a long integer to a file. Returns FALSE on any error.
 *
 *=======================================================================*/
boolean write_long (FILE* FileHandle, const long OutputValue) {
  size_t OutputSize;

	/* Output the data */
  OutputSize = fwrite(&OutputValue, sizeof(long), 1, FileHandle);
  
	/* Ensure the data was properly output */
  if (OutputSize != 1) {
    SET_EXT_ERROR(ERR_WRITE);
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function write_long()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "write_motlong()"
/*=========================================================================
 *
 * Function - boolean write_motlong (FileHandle, OutputValue);
 *
 * Writes a long integer to a file using the Motorolal byte order.
 * Returns FALSE on any error.
 *
 *=======================================================================*/
boolean write_motlong (FILE* FileHandle, const long OutputValue) {
  unsigned char OutputData[4];
  size_t	OutputSize;

	/* Create the output buffer */
  OutputData[0] = (unsigned char) ((OutputValue >> 24) & 0xFF);
  OutputData[1] = (unsigned char) ((OutputValue >> 16) & 0xFF);
  OutputData[2] = (unsigned char) ((OutputValue >> 8) & 0xFF);
  OutputData[3] = (unsigned char) (OutputValue & 0xFF);

	/* Output the data */
  OutputSize = fwrite (OutputData, sizeof(long), 1, FileHandle);
  
	/* Ensure the data was properly output */
  if (OutputSize != 1) {
    SET_EXT_ERROR(ERR_WRITE);
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function write_motlong()
 *=======================================================================*/


#undef  __FUNC__
#define __FUNC__ "write_textfile()"
/*=========================================================================
 *
 * Function - boolean write_textfile (filename, string);
 *
 * Writes the specified string pointer to the given filename returning TRUE
 * on success.  If the file already exists it is overwritten.
 *
 *=======================================================================*/
boolean write_textfile (const char* pFilename, const char* pString) {
  size_t StringSize;
  size_t OutputSize;
  FILE*  FileHandle;

	/* Attempt to open file */
  FileHandle = openfile(pFilename, "wt");

	/* Make sure the file was properly opened */
  if (FileHandle == NULL) return (FALSE);

	/* Write the pointer if specified */
  if (pString != NULL) {
    StringSize = strlen(pString);
    OutputSize = fwrite (pString, sizeof(char), StringSize, FileHandle);
    
		/* Ensure the string was output successfully */
    if (OutputSize != StringSize) {
      SET_EXT_ERROR4(ERR_WRITE, "Wrote only %u bytes but expected %u!", OutputSize, StringSize);
      fclose (FileHandle);
      return (FALSE);
     }
   }

  fclose (FileHandle);
  return (TRUE);
 }
/*=========================================================================
 *		End of Function write_textfile()
 *=======================================================================*/