/****************************** Module Header *******************************
*
* Module Name: mode.c
*
* Generic routines to support EPM modes
*
* Copyright (c) Netlabs EPM Distribution Project 2002
*
* $Id$
*
* ===========================================================================
*
* This file is part of the Netlabs EPM Distribution package and 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, in version 2 as it comes in the "COPYING" file of the
* Netlabs EPM Distribution.  This library 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.
*
****************************************************************************/

#define INCL_DOS
#define INCL_WIN
#define INCL_ERRORS
#include <os2.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <search.h>

#include "macros.h"
#include "nepmd.h"
#include "libreg.h"
#include "init.h"
#include "file.h"
#include "instval.h"

#include "mode.h"

// defines for strings used only once
#define SEARCHMASK_EPMKWDSFILES "%s\\epmkwds\\epmkwds.*"

#ifdef UNUSED
// defs for glob search
#define GLOBSEARCH_WILDCARD    '*'
#endif  // UNUSED

#define E_MAX_STRLEN           1600
#ifdef UNUSED
#define MAX_REG_PATH           (_MAX_PATH)

// reg keys
#define REGKEY_EXTENSIONS      "Extensions"
#define REGKEY_BASENAMES       "Basenames"
#endif  // UNUSED

// QueryDefaultMode is now implemented in E and extended.

// -----------------------------------------------------------------------------

static PSZ _strWord( const PSZ pszString, const PSZ pszWord)
{
         PSZ           pszResult = NULL;
         PSZ           p;
         PSZ           eow;
         BOOL          fStartWord;
         BOOL          fEndWord;
do
   {
   // check parm
   if ((!pszString) || (!pszWord))
      break;

   p = strstr( pszString, pszWord);
   while (p)
      {
      // search end of word
      eow = p;
      while (*eow > 32)
         { eow++; }
      fEndWord = (eow - p == strlen( pszWord));

      // check start of word
      fStartWord = ((p == pszString) || (*(p - 1) == ' '));

      // if a word, then found
      if ((fStartWord) && (fEndWord))
         {
         pszResult = p;
         break;
         }

      // search again
      p = strstr( p + 1, pszWord);
      }

   } while (FALSE);

return pszResult;
}

// -----------------------------------------------------------------------------
#ifdef UNUSED

static PSZ _globMatch( const PSZ pszNamelist, const PSZ pszName)
{
         PSZ           pszResult = NULL;
         PSZ           pszCopy   = NULL;
         PSZ           p;
         PSZ           pszWildcardPos;

do
   {
   // check parm
   if ((!pszNamelist) || (!pszName))
      break;

   // if no wildcard in list, exit
   if (!strchr( pszNamelist, GLOBSEARCH_WILDCARD))
      break;

   // create a copy
   pszCopy = strdup( pszNamelist);
   if (!pszCopy)
      break;

   // get through all names - don't use strtok here
   p = pszCopy;
   while ((*p) && !(pszResult))
      {
      do
         {
         // skip blanks
         while (*p == ' ') { p++; }

         // does name contain the wildcard char?
         pszWildcardPos = strchr( p, GLOBSEARCH_WILDCARD);
         if (!pszWildcardPos)
            break;

         // check the filename
         if (!strnicmp( p, pszName, pszWildcardPos - p))
            {
            pszResult = p;
            break;
            }

         } while (FALSE);

      // skip all nonblanks
      while (*p > ' ') { p++; }

      } // while (*p)

   } while (FALSE);

// cleanup
if (pszCopy) free( pszCopy);
return pszResult;
}

#endif  // UNUSED
// -----------------------------------------------------------------------------
#ifdef UNUSED

static PSZ _stripblanks( PSZ pszString)
{
         PSZ           p = pszString;

do
   {
   if (!p)
      break;

   while ((*p != 0) && (*p <= 32))
      { p++;}
   strcpy( pszString, p);

   p = pszString;
   if (*p != 0)
      {
      p += strlen( p) - 1;
      while ((*p <= 32) && (p >= pszString))
         {
         *p = 0;
         p--;
         }
      }

   } while (FALSE);

return pszString;
}

#endif  // UNUSED
// -----------------------------------------------------------------------------
#ifdef UNUSED

static APIRET _searchFile( PSZ pszSearchPathName, PSZ pszBuffer, ULONG ulBuffLen, PSZ pszSearchMask, ...)
{
         APIRET         rc = NO_ERROR;
         CHAR           szSourceName[ _MAX_PATH];
         va_list        arg_ptr;

va_start (arg_ptr, pszSearchMask);
vsprintf( szSourceName, pszSearchMask, arg_ptr);
rc = DosSearchPath( SEARCH_IGNORENETERRS  |
                       SEARCH_ENVIRONMENT |
                       SEARCH_CUR_DIRECTORY,
                   pszSearchPathName,
                   szSourceName,
                   pszBuffer,
                   ulBuffLen);
return rc;

}

#endif  // UNUSED
// -----------------------------------------------------------------------------
#ifdef UNUSED

static BOOL _checkSpecialFile( PSZ pszFilename, PBYTE pbSig, ULONG ulSigLen)
{
         BOOL           fIsSpecialFile = FALSE;
         APIRET         rc = NO_ERROR;

         ULONG          ulAction;
         ULONG          ulBytesRead;
         HFILE          hfile = NULLHANDLE;
         PBYTE          pbData = NULL;

do
   {
   // check parm
   if ((!pszFilename) ||
       (!pbSig))
      break;

   // open file
   rc = DosOpen( pszFilename,
                 &hfile,
                 &ulAction,
                 0,
                 0,
                 OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS,
                 OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READONLY,
                 NULL);
   if (rc != NO_ERROR)
      break;

   // get memory for file contents
   pbData = malloc( ulSigLen);
   if (!pbData)
      break;

   // read header
   rc = DosRead( hfile, pbData, ulSigLen, &ulBytesRead);
   if ((rc != NO_ERROR) || (ulSigLen != ulBytesRead))
      break;

   // compare header
   fIsSpecialFile = (!memcmp( pbSig, pbData, ulSigLen));

   } while (FALSE);

// cleanup
if (pbData) free( pbData);
if (hfile) DosClose( hfile);
return fIsSpecialFile;
}

#endif  // UNUSED
// -----------------------------------------------------------------------------
#ifdef UNUSED

static BOOL _checkExtprocScript( PSZ pszFilename, PSZ pszBuffer, ULONG ulBuffLen)
{
         BOOL           fIsSpecialFile = FALSE;
         APIRET         rc = NO_ERROR;
         FILE          *pfile = NULL;
         CHAR           szLine[ 128];

         PSZ            p;
         PSZ            pszExtproc;

do
   {
   // check parm
   if ((!pszFilename) ||
       (!pszBuffer))
      break;

   // init var
   memset( pszBuffer, 0, ulBuffLen);

   // open file
   pfile = fopen( pszFilename, "r");
   if (!pfile)
      break;

   while (TRUE)
      {
      // read a line
      if (!fgets( szLine, sizeof( szLine),pfile))
         break;

      // skip empty lines
      _stripblanks( szLine);
      if (szLine[ 0] == 0)
         continue;
      strupr( szLine);

      // check first word - it must be EXTPROC!
      p = strtok( szLine, " ");
      if (strcmp( p, "EXTPROC"))
         break;

      // second word must be given - this the external processor
      p = strtok( NULL, " ");
      if (!p)
         break;

      // strip off path and extension
      pszExtproc = Filespec( p, FILESPEC_NAME);
      p = Filespec( p, FILESPEC_EXTENSION);
      if (p)
         *(p - 1) = 0;
      fIsSpecialFile = TRUE;
      break;
      }
   if (!fIsSpecialFile)
      break;

   // hand over result
   if (strlen( pszExtproc) + 1 > ulBuffLen)
      break;
   strcpy( pszBuffer, pszExtproc);

   } while (FALSE);

// cleanup
if (pfile) fclose( pfile);
return fIsSpecialFile;
}

#endif  // UNUSED
// -----------------------------------------------------------------------------
#ifdef UNUSED

// Determines the default mode for Filename.
// It searches Extension or Basename of Filename in ExtensionList or
// BasenameList and decides which mode to use.
// For special modes, it checks also the file signature.
// Support for <userdir>\epmkwds\epmkwds.* files is added.

APIRET QueryDefaultMode( HCONFIG hconfig, PSZ pszFilename,
                         PSZ pszBuffer, ULONG ulBuffLen)
{
         APIRET         rc = NO_ERROR;

static   PSZ            pszRexxSig = "/*";
static   PSZ            pszIniSig = "\xFF\xFF\xFF\xFF\x14\x00";

         PSZ            pszBasename = NULL;
         PSZ            pszExtension = NULL;
         PSZ            pszNameMode = NULL;
         PSZ            pszExtMode = NULL;
         PSZ            pszEpmkwdsMode = NULL;
         PSZ            pszMode = NULL;
         PSZ            pszNext = NULL;
         PSZ            pszThisMode = NULL;
         PSZ            pszThisModeType = NULL;
         CHAR           szModeList[ E_MAX_STRLEN + 1] = "";
         PSZ            pszCopyList = NULL;
         CHAR           szExtensionList[ _MAX_PATH] = "";
         CHAR           szBasenameList[ _MAX_PATH] = "";
         CHAR           szTmpMode[ _MAX_PATH] = "";
static   PSZ            pszRegPathTemplate = "\\NEPMD\\User\\Mode\\%s\\%s";
         CHAR           szRegPath[ MAX_REG_PATH] = "";
         BOOL           fNextIsMode = TRUE;

do
   {
   // init return value first
   if (pszBuffer)
      memset( pszBuffer, 0, ulBuffLen);

   // check parms
      if (!pszBuffer || !pszFilename)
      {
      rc = ERROR_INVALID_PARAMETER;
      break;
      }

   // -----------------------------------------------------

   // get basename and extension of file
   pszBasename = Filespec( pszFilename, FILESPEC_NAME);
   if (!pszBasename)
      {
      pszBasename = NULL;
      rc = ERROR_INVALID_PARAMETER;
      break;
      }
   pszBasename = strdup( pszBasename);
   if (!pszBasename)
      {
      rc = ERROR_NOT_ENOUGH_MEMORY;
      break;
      }
   pszExtension = Filespec( pszBasename, FILESPEC_EXTENSION);
   if (pszExtension)
      // cut off extension from basename
      *(pszExtension - 1) = 0;
   else
      pszExtension = "";

   // upercase names
   strupr( pszBasename);
   strupr( pszExtension);
   //DPRINTF(( "QueryDefaultMode: -------- %s, %s, %s\n", pszBasename, pszExtension, pszFilename));

   // -----------------------------------------------------

   // check for .CMD files
   if (!strcmp( pszExtension, "CMD"))
      {
      if (_checkSpecialFile( pszFilename, pszRexxSig, strlen( pszRexxSig)))
         pszExtMode = strdup( "REXX");
      else if (_checkExtprocScript( pszFilename, szTmpMode, sizeof( szTmpMode)))
         pszExtMode = strdup( szTmpMode);
      }

   // check for .INI files
   if (!strcmp( pszExtension, "INI"))
      {
      // dont act on true OS/2 INI Files!
      //if (_checkSpecialFile( pszFilename, (PBYTE)&ulIniSig, sizeof( ulIniSig)))
      if (_checkSpecialFile( pszFilename, pszIniSig, strlen( pszIniSig)))
         //rc = ERROR_PATH_NOT_FOUND;  // this would cause mode = INI as well
         pszExtMode = strdup( "BIN");
      else
         pszExtMode = strdup( "INI");
      }

   // -----------------------------------------------------

   if (!pszExtMode)
      {
      // Query mode list with " (T)" appended, T = type = (U|D|E)
      // The type is used for EpmkwdsMode to check if file extension
      // matches the extension of the epmkwds.* file.
      rc = QueryModeList( hconfig, szModeList, sizeof( szModeList), 1);

      pszCopyList = strdup( szModeList);
      pszNext = strtok( pszCopyList, " ");
      while( pszNext)
         {
         if (pszNext[ 0] != '(')
            {
            // Mode: <mode>
            pszThisMode = pszNext;
            // Reset ThisType
            pszThisModeType = "";
            }
         else
            {
            // Type: (<type>)
            strncpy( pszThisModeType, pszNext + 1, 1);

            //DPRINTF(("QueryDefaultMode:   - pszThisMode = %s, pszThisModeType = %s\n", pszThisMode, pszThisModeType));
            // Pair complete, process further

            // read settings
            sprintf( szRegPath, pszRegPathTemplate, pszThisMode, REGKEY_EXTENSIONS);
            QueryConfigValue( hconfig, szRegPath, szExtensionList);
            sprintf( szRegPath, pszRegPathTemplate, pszThisMode, REGKEY_BASENAMES);
            QueryConfigValue( hconfig, szRegPath, szBasenameList);

            // make all values uppercase
            strupr( szExtensionList);
            strupr( szBasenameList);

            // check extension, name and type
            if ((!pszExtMode) &&
                (pszExtension) &&
                (*pszExtension) &&
                (szExtensionList) &&
                ((_globMatch( szExtensionList, pszExtension)) ||
                 (_strWord( szExtensionList, pszExtension))))
               pszExtMode = strdup( pszThisMode);

            if ((!pszNameMode) &&
                (pszBasename) &&
                (*pszBasename) &&
                (szBasenameList) &&
                ((_globMatch( szBasenameList, pszBasename)) ||
                 (_strWord( szBasenameList, pszBasename))))
               pszNameMode = strdup( pszThisMode);

            if ((!strcmp( pszThisModeType, "E")) &&
                (!strcmp( pszExtension, pszThisMode)))
               pszEpmkwdsMode = strdup( pszThisMode);

            // extension or epmkwds mode found? then break here
            if (pszExtMode)
               {
               //DPRINTF(( "QueryDefaultMode: - ExtMode found\n"));
               break;
               }
            if (pszEpmkwdsMode)
               {
               //DPRINTF(( "QueryDefaultMode: - EpmkwdsMode found\n"));
               break;
               }
            else if (pszNameMode)
               {
               //DPRINTF(( "QueryDefaultMode: - NameMode found\n"));
               }
            }

         pszNext = strtok( NULL, " ");
         }
      if (pszCopyList) free( pszCopyList);

      } // if (!pszExtMode)

   // -----------------------------------------------------

   // prefer ext mode over name mode
   pszMode = NULL;
   do
      {
      if (pszExtMode)
         {
         pszMode = pszExtMode;
         break;
         }
      if (pszNameMode)
         {
         pszMode = pszNameMode;
         break;
         }
      if (pszEpmkwdsMode)
         {
         pszMode = pszEpmkwdsMode;
         break;
         }
      } while (FALSE);

   // break if no mode found
   if (!pszMode)
      {
      rc = ERROR_PATH_NOT_FOUND;
      break;
      }
   //DPRINTF(( "QueryDefaultMode: pszMode = %s, %s\n", pszMode, pszFilename));

   // hand over result
   strcpy( pszBuffer, pszMode);

   } while (FALSE);

// cleanup
if (pszNameMode)    free( pszNameMode);
if (pszExtMode)     free( pszExtMode);
if (pszEpmkwdsMode) free( pszEpmkwdsMode);
if (pszBasename)    free( pszBasename);
return rc;
}

#endif  // UNUSED
// -----------------------------------------------------------------------------

// Returns a space-delimited list of all modes:
//    <mode1> <mode2> ...
// Support for <userdir>\epmkwds\epmkwds.* files is added.
// If fAppendType = 1, an extended ModeList is returned: After each mode the
// mode type is appended with the syntax:
//    <mode1> (<type1>) <mode2> (<type2>) ...
// The enclosing with parentheses is required to keep the _strWord function
// working.
//    Type
//     U    user-configured
//     D    configured completely by defaults.dat
//     E    epmkwds

APIRET QueryModeList( HCONFIG hconfig, PSZ pszBuffer, ULONG ulBuffLen,
                      BOOL fAppendType)
{
         APIRET         rc = NO_ERROR;
static   PSZ            pszContainerKey = "\\NEPMD\\User\\Mode";
         APIRET         rcx = 0;
         CHAR           szUserModeList[ E_MAX_STRLEN + 1] = "";
         CHAR           szDefaultsModeList[ E_MAX_STRLEN + 1] = "";
         CHAR           szModeList[ E_MAX_STRLEN + 1] = "";
         ULONG          ulModeListLen = 0;
static   PSZ            pszKeyPath = "\\NEPMD\\Var\\DefaultsImport\\ModeList";
static   PSZ            pszModeTypeTemplate = " (%s)";
         CHAR           szModeTypeAppendix[ 4 + 1] = "";
         PSZ            pszCopyList = NULL;
         PSZ            pszNext = NULL;

         CHAR           szUserDir[ _MAX_PATH] = "";
         CHAR           szSearchMask[ _MAX_PATH] = "";
         CHAR           szFile[ _MAX_PATH] = "";
         HDIR           hdir = NULLHANDLE;
         PSZ            pszExtension = NULL;

do
   {
   // init return value first
   if (pszBuffer)
      memset( pszBuffer, 0, ulBuffLen);

   // check parms
   if (!pszBuffer)
      {
      rc = ERROR_INVALID_PARAMETER;
      break;
      }

   // Return a list of entries for a specified key value of RegContainers.
   // This converts the \0 chars of a container key to the specified separator
   // string.
   rcx = GetContainerList( hconfig, pszContainerKey, " ",
                           szUserModeList, sizeof( szUserModeList));
   //DPRINTF(( "szUserModeList = %s, rcx = %i\n", szUserModeList, rcx));

   // Start with szUserModeList
   sprintf( szModeTypeAppendix, pszModeTypeTemplate, "U");

   pszCopyList = strdup( szUserModeList);
   pszNext = strtok( pszCopyList, " ");
   while( pszNext)
      {
      if (!_strWord( szModeList, pszNext))
         {
         if (strlen( szModeList))
            strcat( szModeList, " ");
         strcat( szModeList, pszNext);

         if (fAppendType)
            strcat( szModeList, szModeTypeAppendix);
         }

      pszNext = strtok( NULL, " ");
      }
   if (pszCopyList) free( pszCopyList);

   QueryConfigValue( hconfig, pszKeyPath, szDefaultsModeList);
   //DPRINTF(( "szDefaultsModeList = %s\n", szDefaultsModeList));

   // Append modes from szDefaultsModeList if not already added
   sprintf( szModeTypeAppendix, pszModeTypeTemplate, "D");

   pszCopyList = strdup( szDefaultsModeList);
   pszNext = strtok( pszCopyList, " ");
   while( pszNext)
      {
      if (!_strWord( szModeList, pszNext))
         {
         if (strlen( szModeList))
            strcat( szModeList, " ");
         strcat( szModeList, pszNext);

         if (fAppendType)
            strcat( szModeList, szModeTypeAppendix);
         }

      pszNext = strtok( NULL, " ");
      }
   if (pszCopyList) free( pszCopyList);

   // Search for UserDir\epmkwds\epmkwds.* and append extensions as modes
   // if not already added. No errors from here.
   do
      {
      rcx = QueryInstValue( NEPMD_INSTVALUE_USERDIR, szUserDir, sizeof( szUserDir));
      if (rcx != NO_ERROR)
         break;
      if (!strlen( szUserDir))
         break;

      sprintf( szModeTypeAppendix, pszModeTypeTemplate, "E");

      sprintf( szSearchMask, SEARCHMASK_EPMKWDSFILES, szUserDir);

      hdir = HDIR_CREATE;
      while (rcx == NO_ERROR)
         {
         // search it
         rcx = GetNextFile( szSearchMask, &hdir, szFile, sizeof( szFile));
         if (rcx != NO_ERROR)
            break;

         pszExtension = Filespec( szFile, FILESPEC_EXTENSION);
         strupr( pszExtension);

         if (!_strWord( szModeList, pszExtension))
            {
            if (strlen( szModeList))
               strcat( szModeList, " ");
            strcat( szModeList, pszExtension);

            if (fAppendType)
               strcat( szModeList, szModeTypeAppendix);
            }
         }
      DosFindClose( hdir);

      // handle special errors - not required, rcx is not reported
      if (rcx = ERROR_NO_MORE_FILES)
         rcx = NO_ERROR;

      } while (FALSE);

   // hand over result
   ulModeListLen = MIN( strlen( szModeList), ulBuffLen - 1);
   strncpy( pszBuffer, szModeList, ulModeListLen);
   pszBuffer[ ulModeListLen] = 0;

   } while (FALSE);

return rc;
}

