/*
===============================================================================
                           INSTALLATION PROGRAM
===============================================================================

Functional Descripton:

1.  Goals
=========

The goals of this install are:

    A. Provide consistent install behavior of the I18N code among the
       various products that install it.
    B. Reduce code redundancy caused by multiple products installing
       the same code in different directories on the user's system.
    C. Provide a syslevel file for support purposes.
    D. Remove knowledge of install details from other product's
       installation code.


2.  Overview
============

  This program installs the I18N files required for the runtime environment
  and configures the CONFIG.SYS information.

* It is a command line program (.EXE) which provides no user interface
* other than the command line parameters and return code.
*
* It is assumed that this program will be called by individual product
* installation programs.
*

  Functions:
  ==========

  This program's functions include:

    A.  Determine if this version of the I18N package should be installed
        by examining the environment variable, I18NDIR and the syslevel file,
        SYSLEVEL.I18.

    B.  Create all of the required I18N directories if they do not already
        exist.

    D.  Install the I18N runtime code (setloc1.dll) located in the path
        specified by the caller, if necessary.

    E.  Install the I18N locale files (*.dll) located in the path
        specified by the caller, if necessary.

    F.  Install the syslevel file, SYSLEVEL.I18, if necessary.

    F.  Update CONFIG.SYS with the I18N information if necessary.


  Installation Process
  ====================

    A.  If the I18NDIR environment variable is not found, then
        the I18N package has not been installed on the system or
        it is a down level version.  Install the new version using the
        drive specified by the caller.  If no drive letter
        is specified, then the boot drive will be used.  The default
        directory, IBMI18N, will be used.
        Create the directory if it does not exist.  Update config.sys.

        The following is installed in the IBMI18N directory:
          a) syslevel file, SYSLEVEL.I18
          b) \DLL\SETLOC1.DLL
          c) \LOCALE\all locale DLL's provided by the caller
          d) \LOCALE\ALIASES file

     B. If the I18NDIR environment variable exists, the directory specified
        in the environment variable will be used as the target directory.
        Compare the existing syslevel file with the syslevel file in the
        install package.

        If the install package is a newer version:

          a) Replace the SETLOC1.DLL.
          b) Replace the SYSLEVEL.I18 file.
          c) Replace existing locale DLLs.
          d) Add any new locale DLLs provided by the caller.
          e) Do not remove any existing locale DLLs.
          f) Replace the ALIASES file.

        If the install package is an older version or the same version: :

          a) Do not replace the SETLOC1.DLL.
          b) Do not replace the SYSLEVEL.I18 file.
          c) Do not replace existing locale DLLs.
          d) Add any new locale DLLs provided by the caller.
          e) Do not remove any existing locale DLLs.
          f) Do not replace the existing ALIASES file.

        It may seem strange that we want to continue with the install
        if the versions are the same or if the caller is installing
        an older version.  However, multiple products
        install this package and they don't all ship the same set of
        locale files.  So, we want to make sure that we always have
        a "union" of all the different locale files installed by the
        different products.

*       Note:  Disk Full Errors
*
*       If the following occurs:
*         - the program encounters a "disk full" condition while copying
*           the source files to the user's machine AND
*         - the caller specified a target drive that is different from
*           the drive on which the disk full error occurred AND
&         - the caller specified UPDCFG=Y (this is also the default)
&
*       then this install program will attempt to move the entire
*       I18NDIR directory to the specified target drive.  If successful
*       the I18NDIR environment variable in the config.sys file will be
*       updated to reflect this and the original I18NDIR directory will
*       be deleted.  If unsuccessful, the config.sys will not be updated
&       and a disk full error will be returned to the caller.



  CONFIG.SYS Changes
  ==================

    A.  If the I18NDIR environment variable is not found, then add it as
        follows:

           I18NDIR=x:\IBMI18N;

           where "x" is the drive letter specified by the calling
           application.  If no drive letter is specified, the boot
           drive is used.

*       If the I18NDIR environment variable is found, leave it alone
*       unless the I18N directory has been changed due to a disk full
*       error.  Then update it to reflect the new location of the
*       I18NDIR directory.

    B.  Generally, the LANG environment variable should not require
        updating or setting.

        If a value for the LANG environment variable is specified by the
        caller, add the LANG environment variable if it does not exist.
        If the LANG environment variable already exists, update it to
        the value specified by the caller.

        If no value is specified by the caller for the LANG environment
x       variable and either the variable is not defined in the config.sys
x       or it is invalid in the config.sys, then a default locale will be
x       determined.  The default locale is based on the system's country
x       code and keyboard settings.  The LANG environment variable will
x       be set to this default.

        Note: The I18N initialization code computes a default locale
              based on the system's country code and code page settings.
              So, the LANG variable is not usually necessary.  It may
              be desirable to set LANG if the value for LANG was
              obtained from user input or in countries, such as Belgium,
              where multiple languages are frequently used on the same
              machine.  If set, LANG should be of the form xx_xx
              (e.q. en_US) without a code page suffix, since this will
              be determined from the current process code page.

*       Note: If either the value of LANG specified by the caller or
*             the value for LANG already in the config.sys file is not
*             of the format xx_xx, it will be modified to meet this
*             format.  This is being done to ensure compatibility with
*             Visual Age C++.

    C.  No changes will be made to the config.sys file regarding the
        NLSPATH environment variable.  This variable is involved in the
        location of message files and this install program does NOT
        install any message files.

        Note: The NLSPATH environment variable contains a LIST of paths.
              Therefore when updating it, products should add to the list,
              not replace the list.

    D.  If the LOCPATH environment variable exists, the path to the
        I18N code will be added to the beginning of the LOCPATH
        list of paths unless it is already there.

        If the LOCPATH environment variable does not exist, add it.

    E.  Add the I18N code path to the beginning of LIBPATH unless it is
        already there.

    This program will NOT backup the config.sys file.  It assumes that the
    caller will handle this.


3.  Parameters
==============

&   i18ninst sourcepath DRIVE=x UPDCFG=x LANG=xx_xx ERASE=x CFGDRIVE=x

    A.  sourcepath --- Path containing code to be installed.
                       This parameter is REQUIRED and must be the 1st
                       parameter specified.

                       If the path does not contain a drive letter, the
                       current drive is used.

                       This program expects to find the following UNPACKED
*                      files and directories at the specified path:

                       SYSLEVEL.I18 file
                       \DLL\SETLOC1.DLL
                       \LOCALE\xxxxxxxx.DLL for each locale file.
                       \LOCALE\ALIASES

    B.  DRIVE=     --- Drive where code should be installed.

                       If a version of the I18N code equal to or newer than
                       this version is already installed, this program will
                       install using the same drive as the already existing
                       code and this parameter will be ignored.

                       If the I18N code is not installed or an
                       older version is already installed, this program will
                       install the code on the drive specified in the
                       directory, IBMI18N.

                       This parameter is optional.

                       If this parameter is not specified, the OS/2 install
                       drive is used.

*   C.  UPDCFG=x   --- This parameter specifies whether or not the config.sys
                       file should be updated.  CONFIG=Y indicates that it
                       should be updated.  CONFIG=N indicates that it should
                       not.

                       This parameter is optional.  If it is not
                       specified, the default is CONFIG=Y.
&
    D.  LANG=xx_xx --- This parameter specifies a value to set the LANG
                       environment variable to.  The config.sys file is
                       updated with this value if CONFIG=Y was specified.

                       This parameter is optional.

*   E.  ERASE=x    --- This parameter specifies whether or not this install
*                      program should erase the caller's source files and
*                      directory when the installation successfully
*                      completes.  ERASE=Y indicates that the files and
*                      directory should be erase.  ERASE=N indicates they
*                      should not.
*
*                      This parameter is optional.  If it is not
*                      specified the default is 'N'.

#   F.  CFGDRIVE=  --- This parameter specifies the drive where the config.sys
#                      file is located.  This parameter is optional.  If it
#                      is not specified, the boot drive is assumed.  This
#                      parameter is ignored if UPDCFG=N is specified.


4.  Installation prerequisites
==============================

*   A.  This program assumes that all source files are located in the
*       following directory structure:

        SYSLEVEL.I18
        \DLL\SETLOC1.DLL
        \LOCALE\locale DLL files
        \LOCALE\ALIASES

*       This program assumes that ONLY the I18N files are in this
*       directory.  Because different products install different locale
*       files and additional locale files may still be added, this
*       install program has no knowledge of the exact number of files
*       to expect or what the file names may be.

    B.  The config.sys file has already been backed up.


Changes:
    12/11/95 - GHB - Add exit call.
    12/18/95 - GHB - Trash getting into LANG value when reformatting it.
                     Needed to null terminate the string after calling
                     strncpy.
    12/28/95 - GHB - Add code in execute_update_config to default the
                     LANG value the same as SETLOC1 initialization.

===============================================================================
*/

/******************************************************************************
/***     Include files                                                     ***/
/*****************************************************************************/

#define INCL_BASE
#define INCL_DOS                        /* Use DOS function.                 */
#include <os2.h>                        /* Include the OS2 toolkit defs.     */

#include <stdlib.h>                     /* Include the standard library.     */
#include <io.h>                         /* Include standard input/output.    */
#include <stdio.h>                      /* Include standard input/output.    */
#include <string.h>                     /* Include standard string functions.*/
#include <ctype.h>                      /* Include standard C type functions.*/
#include <setinit.h>                    /* setloc1 locale table              */
#include "i18ninst.h"

/*****************************************************************************/
/***     Global defines                                                    ***/
/*****************************************************************************/

#define BUF_SIZE         256              /* Buffer size for general use     */
#define CONFIG_SIZE     4096              /* Buffer size for CONFIG.SYS use  */
#define SYSLEVEL_NAME   "SYSLEVEL.I18"    /* Syslevel file name              */
#define ALIASES_NAME    "ALIASES"         /* Aliases file name               */
#define DLL_NAME        "SETLOC1.DLL"     /* Setloc1 file name               */
#define I18N_COMPID     "562295400"       /* syslevel component id           */

#define I18N_DIR         "\\IBMI18N\\"    /* I18N directory.                 */
#define I18N_DLL_DIR     "DLL\\"          /* I18N DLL directory.             */
#define I18N_LOCALE_DIR  "LOCALE\\"       /* I18N locale file directory.     */
#define I18N_LIBPATH_VAR "LIBPATH="       /* I18N LIBPATH variable.          */
#define I18N_LANG_VAR    "LANG="          /* I18N LANG    variable.          */
#define I18N_LOCPATH_VAR "LOCPATH="       /* I18N LOCPATH variable.          */
#define I18N_I18NDIR_VAR "I18NDIR="       /* I18N I18NDIR variable.          */

#define CONFIG_SYS_FILEN       "\\CONFIG.SYS"   /* Config file: CONFIG.SYS   */
#define CONFIG_SYS_WORK_FILEN  "\\CONFIG18.TMP" /* Work file for CONFIG.SYS  */

/* Versions */
#define FIRST_TIME            0
#define OLDER_VERSION         1
#define NEW_VERSION           2
#define SAME_VERSION          3

/*****************************************************************************/
/***     Global Variables                                                  ***/
/*****************************************************************************/

char    parm_source[BUF_SIZE];          /* Input parm: source path           */
char    parm_lang[9];                   /* Input parm: default lang value    */
char    parm_updateconfig;              /* Input parm: update config?        */
char    parm_erase_source;              /* Input parm: erase source files?   */
char    parm_alt_drive;                 /* Alternate install drive           */
int     install_status;                 /* Install status                    */
BOOL    force_i18ndir;                  /* 1 = I18NDIR changed (disk full)   */
                                        /* 0 = unchanged                     */
int     return_code;                    /* Return code                       */

char    i18n_dir[BUF_SIZE];             /* I18N directory.               */
char    i18n_dll_dir[BUF_SIZE];         /* DLL directory.                */
char    i18n_locale_dir[BUF_SIZE];      /* Locale direcrtory.            */

char    config_sys_file[BUF_SIZE];      /* Config file: CONFIG.SYS   */
char    config_sys_work_file[BUF_SIZE]; /* Work file for CONFIG.SYS  */



/*-------------------------------------------------------------------------*/
/* Function Prototypes                                                     */
/*-------------------------------------------------------------------------*/
void copy_file(char *src_path, char *tar_path);
void initialize(int argc, char *argv[]);
void is_i18n_installed(void);
void execute_install_I18N(void);
void handle_disk_full(void);
void create_dirs(void);
void execute_update_config(void);
void add_dir_config(char *line_text, char *line_upper, char *add_dir);
void erase_source_files(void);
BOOL check_config(void);

APIRET APIENTRY DosReplaceModule(PSZ name, PSZ newName, PSZ bkupName);

/*
 *  Note about DosReplaceModule():
 *  When a DLL or EXE is in use by the system, the file is locked. It
 *  can not therefore be replaced on the harddisk by a newer copy.
 *  The API DosReplaceModule is to allow the replacement on the disk
 *  of the new module while the system continues to run the old
 *  module. The contents of the file <name> are cached by the system
 *  and the file is closed.  A backup copy of the file is created as
 *  <bkupName> for recovery purposes should the replace fail. The
 *  new module <newName> takes the place of the original module. The
 *  system will continue to use the cached old module until all
 *  references to it are released. The next reference to the module
 *  will cause a reload from disk.
 */

/*****************************************************************************/
/*****************************************************************************/
/***                                                                       ***/
/***     Main program                                                      ***/
/***                                                                       ***/
/*****************************************************************************/
/*****************************************************************************/
INT main(int argc, char *argv[])
{
  int save_rc;                          /* Return code from is_installed API.*/

  return_code = 0;
                                        /* Initialize parameter variables.   */
  initialize( argc, argv );

  if (return_code)
  {
    goto exit;
  }
                                        /* Determine if I18N is already      */
                                        /*  installed and whether or not     */
                                        /*  the new version should be.       */
  is_i18n_installed();
  if (return_code)
  {
    goto exit;
  }
                                        /* Install the I18N package.         */
  execute_install_I18N();

  /* If the config.sys file should not be updated due to prior errors   */
  /* AND the I18NDIR has not been moved due to disk full problems       */
  if (return_code && !force_i18ndir)
  {
    goto exit;
  }
                                        /* If requested to updated CONFIG.SYS*/
                                        /*  add I18N information.            */
  if ( parm_updateconfig == 'Y' )
  {
                                        /* Since config may be changed       */
                                        /* even though there were errors,    */
                                        /* don't want config errors to       */
                                        /* override previous errors.         */
     save_rc = return_code;
     execute_update_config();
     if (save_rc != NO_ERROR)
     {
       return_code = save_rc;
     }
                                        /* If everything successful and */
                                        /* requested that we erase the  */
                                        /* source files, call erase function */
     if (!return_code && parm_erase_source == 'Y')
     {
       erase_source_files();
     }
  }
                                        /* Terminate the program.            */
exit:
#if DEBUG
  printf ("Return Code: %i\n", return_code);
#endif
  exit(return_code);
}

/*****************************************************************************/
/***                                                                       ***/
/***     Initialization routine for program.                               ***/
/***                                                                       ***/
/*    Sets up defaults for optional input parameters.                        */
/*    Validates input parameters.                                            */
/*                                                                           */
/*    Return Codes: RC_BAD_SOURCE                                            */
/*                  RC_BAD_CONFIG_FLAG                                       */
/*                  RC_BAD_LANG                                              */
/*                  RC_MISSING_REQ_PARM                                      */
/*                  RC_BAD_PARM                                              */
/*                  RC_BAD_ERASE_FLAG                                        */
/*                  RC_BAD_DRIVE                                             */
/*                                                                           */
/*****************************************************************************/
void initialize(int argc, char *argv[])
{
  char    *ptrchar;
  APIRET  rc;
  int     count;
  char    temp_parm[BUF_SIZE];
  ULONG   drivenum, drivemap;
  char    parm_command[BUF_SIZE];         /* Input parm: invocation command    */
  char    boot_drive[3];                  /* Boot drive (OS/2 installed on).   */
  HFILE   fh;
  ULONG   action;


  /* Find boot drive OS/2 installed on.*/

  rc = DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
                        (PVOID)&drivenum, sizeof(drivenum) ) ;
  if ( rc == NO_ERROR )
  {
     boot_drive[0] = toupper( drivenum+'@');
  }
  else
  {
     boot_drive[0] = 'C' ;
  }

  boot_drive[1] = ':' ;
  boot_drive[2] = '\0' ;


                                        /* Process input parameters          */
  if (argc <= 1)
  {
    return_code = RC_MISSING_REQ_PARM;  /* Missing required parameter        */
    goto exit_init;
  }
                                        /* Set up defaults                   */
  strcpy (i18n_dir, boot_drive);
  parm_updateconfig = 'Y';
  parm_lang[0] = '\0';
  parm_erase_source = 'N';
  parm_alt_drive = boot_drive[0];
  sprintf( config_sys_file, "%s%s", boot_drive, CONFIG_SYS_FILEN ) ;
  sprintf( config_sys_work_file, "%s%s", boot_drive, CONFIG_SYS_WORK_FILEN ) ;

                                        /* 1st parm: required source path    */
  strcpy(temp_parm, argv[1]);
                                        /* If no drive specified, use the    */
                                        /* current drive.                    */
  parm_source[0] = '\0';
  if (temp_parm[1] != ':')
  {
    DosQueryCurrentDisk(&drivenum, &drivemap);
    parm_source[0] = drivenum + 'A' - 1;
    parm_source[1] = ':';
    parm_source[2] = '\0';
                                        /* If no starting backslash, add it  */
    if (temp_parm[0] != '\\')
    {
      strcat (parm_source, "\\");
    }
  }
  strcat (parm_source, temp_parm);
                                        /* If no trailing backslash, add it  */
  if (parm_source[strlen(parm_source)-1] != '\\')
  {
     strcat (parm_source, "\\");
  }

                                        /* Look at each optional parameter   */
  for (count=2; count<argc; count++)
  {
                                        /* Get parm string and convert to    */
                                        /* upper case.                       */
    strcpy(parm_command, argv[count]);
    strupr(parm_command);
                                        /* If it is the drive parm           */
    rc = strncmp(parm_command, "DRIVE=", 6);
    if (!rc)
    {
                                        /* Get value.                        */
      i18n_dir[0] = parm_command[6];
      i18n_dir[1] = ':';
      i18n_dir[2] = '\0';
      parm_alt_drive = i18n_dir[0];
      if (i18n_dir[0] == 'A' || i18n_dir[0] == 'B' ||
          (!isalpha((int)i18n_dir[0])) )
      {
        return_code = RC_BAD_DRIVE;
        goto exit_init;
      }
    }
    else
    {
                                        /* If it is the config parm          */
      rc = strncmp(parm_command, "UPDCFG=", 7);
      if (!rc)
      {
                                        /* Get value.  Return error if not   */
                                        /*  Y or N.                          */
        parm_updateconfig = parm_command[7];
        if (parm_updateconfig != 'Y' && parm_updateconfig != 'N')
        {
          return_code = RC_BAD_CONFIG_FLAG;
          goto exit_init;
        }
      }
      else
      {
                                        /* If it is the lang parm            */
        rc = strncmp(parm_command, "LANG=", 5);
        if (!rc)
        {
                                        /* Get value.                        */
          strcpy(temp_parm, &parm_command[5]);
                                        /* Want the lang parm to have the    */
                                        /* format, xx_xx.  So if the first   */
                                        /* 5 chars are xx_xx, save them and  */
                                        /* disregard anything that follows.  */
                                        /* if the first 4 chars are xxxx,    */
                                        /* convert them to xx_xx and         */
                                        /* disregard anything that follows.  */
          if (temp_parm[2] != '_')
          {
            if (  isalpha((int)temp_parm[0]) &&
                   isalpha((int)temp_parm[1]) &&
                   isalpha((int)temp_parm[2]) &&
                   isalpha((int)temp_parm[3])  )
            {
              strncpy (parm_lang, temp_parm, 2);
              parm_lang[2] = '\0';
              strcat (parm_lang, "_");
              strncat (parm_lang, &temp_parm[2], 2);
            }
            else
            {
              return_code = RC_BAD_LANG;
              goto exit_init;
            }
          }
          else
          {
            if (   isalpha((int)temp_parm[0]) &&
                   isalpha((int)temp_parm[1]) &&
                   isalpha((int)temp_parm[3]) &&
                   isalpha((int)temp_parm[4])  )
            {
              if (temp_parm[5] == '.')
              {
                strcpy (parm_lang, temp_parm);
              }
              else
              {
              strncpy (parm_lang, temp_parm, 5);
              parm_lang[5] = '\0';
              }
            }
            else
            {
              return_code = RC_BAD_LANG;
              goto exit_init;
            }
          }
        }
        else
        {
                                        /* If it is the erase  parm          */
          rc = strncmp(parm_command, "ERASE=", 6);
          if (!rc)
          {
                                        /* Get value.  Return error if not   */
                                        /*  Y or N.                          */
            parm_erase_source = parm_command[6];
            if (parm_erase_source != 'Y' && parm_erase_source != 'N')
            {
              return_code = RC_BAD_ERASE_FLAG;
              goto exit_init;
            }
          }
          else
          {
                                        /* If it is the config drive parm    */
            rc = strncmp(parm_command, "CFGDRIVE=", 9);
            if (!rc)
            {
                                        /* Return error if A,B or non        */
                                        /*  alpha specified.                 */
              if (parm_command[9] == 'A' || parm_command[9] == 'B' ||
                  (!isalpha((int)parm_command[9])) )
              {
                return_code = RC_BAD_DRIVE;
                goto exit_init;
              }
              else
              {
                                        /* Update the paths to config.sys    */
                                        /* that initially used the boot      */
                                        /* drive.                            */
                config_sys_file[0] = parm_command[9];
                config_sys_work_file[0] = parm_command[9];
              }
            }
            else
            {
              /* Do not recognize parameter */
              return_code = RC_BAD_PARM;
              goto exit_init;
            }
          }
        }
      }
    }
  }

exit_init:
  return;
}

/*****************************************************************************/
/***                                                                       ***/
/***     Query if I18N is already installed                                ***/
/***                                                                       ***/
/*   Get the value of the environment variable, I18NDIR.  If the variable    */
/*   does not exist, then either the I18N package has never been installed   */
/*   on this system or an old version that didn't use the environment        */
/*   variable is installed.  Set "install_status" to FIRST_TIME.             */
/*                                                                           */
/*   If the variable does exist, compare the version numbers in the          */
/*   SYSLEVEL.I18 file found in the source path and the one found in the     */
/*   path specified in the I18NDIR environment variable.                     */
/*                                                                           */
/*   Verify that the syslevel files are valid.                               */
/*                                                                           */
/*   If source path version newer than the installed version,                */
/*   set "install_status" to NEW_VERSION.                                    */
/*                                                                           */
/*   If source path version is older than or the same as the installed       */
/*   version, set "install_status" to OLDER_VERSION or SAME_VERSION.         */
/*                                                                           */
/*   Return Codes: RC_SOURCE_SYS                                             */
/*                 RC_CURRENT_SYS                                            */
/*                 RC_MEMORY_ERROR                                           */
/*                                                                           */
/*****************************************************************************/

void is_i18n_installed()
{

/*-------------------------------------------------------------------------*/
/* Syslevel file structure.  Char data type has been used to avoid the     */
/* requirement for a compiler pack option to allow ANSI portability to     */
/* platforms that don't have a structure pack option.  This data structure */
/* has been created based on information in a document titled "OS/2 2.0    */
/* Syslevel File Format (Unofficial)".                                     */
/* This structure has been copied from SOM.                                */
/*-------------------------------------------------------------------------*/
struct syslevel_stuff
{
    USHORT    SysNumber;
    char      Signature[8];
    char      Julian[5];
    char      SlfVersion[2];
    char      Reserved[16];
    char      TableOffset[4];
    char      SysId[2];
    char      SysEdition;
    char      SysVersion;
    char      SysModify;
    char      SysDate[2];
    char      CsdLevel[8];
    char      CsdPrev[8];
    char      SysName[80];
    char      CompId[9];
    char      RefreshLevel;
    char      Reserved1;
    char      Type[9];
    char      Reserved2[7];
    char      FileList[1];
};
  int     len, rc;
  long    filesize;
  char   *pathptr;
  FILE   *src_sysfile, *cur_sysfile;
  char    temp_path[BUF_SIZE];
  struct  syslevel_stuff *src_syslevel, *cur_syslevel;
  size_t  bytes_read;
  BOOL    var_found;

  /* Call function to check config.sys for variable */
  var_found = check_config();
  if (return_code)
    goto exit_is;

  /* If variable doesn't exist */
  if (!var_found)
  {
    /* Use default directory */
    strcat (i18n_dir, I18N_DIR);

    /* Set global flag indicating that environment variable was not found. */
    /* Set status to new version.                                          */
    install_status = FIRST_TIME;
    goto setup;
  }
  else
  {
    /* set global flag indicating that environment variable was found */

    /* Build path to syslevel file in the source directory */
    strcpy (temp_path, parm_source);
    strcat (temp_path, SYSLEVEL_NAME);

    /* Open the source syslevel file */
    src_sysfile = fopen(temp_path, "rb");
    if (!src_sysfile)
    {
      return_code = RC_SOURCE_SYS;
      goto exit_is;
    }

    /* Determine the size of the file and then allocate enough memory   */
    /* to hold the entire file.                                         */

    /* Move file pointer to EOF */
    rc = fseek(src_sysfile, 0L, SEEK_END);
    if (rc)
    {
       return_code = RC_SOURCE_SYS;
       goto close1;
    }

    /* Get file size */
    filesize = ftell(src_sysfile);
    if (filesize <= 0)
    {
       return_code = RC_SOURCE_SYS;
       goto close1;
    }

    /* Set file pointer back to beginning of file */
    rc = fseek(src_sysfile, 0, SEEK_SET);
    if (rc)
    {
       return_code = RC_SOURCE_SYS;
       goto close1;
    }

    src_syslevel = malloc(filesize);

    if (!src_syslevel)
    {
       return_code = RC_MEMORY_ERROR;
       goto close1;
    }

    /* Read contents of the syslevel file and verify that correct number */
    /* of bytes were read.                                               */

    bytes_read = fread(src_syslevel, 1, filesize, src_sysfile);
    if (bytes_read != filesize)
    {
       return_code = RC_SOURCE_SYS;
       goto free1;
    }

    /* Verify that the syslevel file is good. */
    if ( strncmp(src_syslevel->Signature, "SYSLEVEL", 8) )
    {
       return_code = RC_SOURCE_SYS;
       goto free1;
    }

    if ( strncmp(src_syslevel->CompId, I18N_COMPID, 9) )
    {
      return_code = RC_SOURCE_SYS;
      goto free1;
    }

    /* Build path to current syslevel file */
    strcpy (temp_path, i18n_dir);
    len = strlen(temp_path);
    if (temp_path[len-1] != '\\')
    {
       strcat (temp_path, "\\");
    }
    strcat (temp_path, SYSLEVEL_NAME);

    /* Open the current syslevel file */
    cur_sysfile = fopen(temp_path, "rb");
    if (!cur_sysfile)
    {
      return_code = RC_CURRENT_SYS;
      goto free1;
    }

    /* Determine the size of the file and then allocate enough memory   */
    /* to hold the entire file.                                         */

    /* Move file pointer to EOF */
    rc = fseek(cur_sysfile, 0, SEEK_END);
    if (rc)
    {
       return_code = RC_CURRENT_SYS;
       goto close2;
    }

    /* Get file size */
    filesize = ftell(cur_sysfile);
    if (filesize <= 0)
    {
       return_code = RC_CURRENT_SYS;
       goto close2;
    }

    /* Set file pointer back to beginning of file */
    rc = fseek(cur_sysfile, 0, SEEK_SET);
    if (rc)
    {
       return_code = RC_CURRENT_SYS;
       goto close2;
    }

    cur_syslevel = malloc(filesize);

    if (!cur_syslevel)
    {
       return_code = RC_MEMORY_ERROR;
       goto close2;
    }

    /* Read contents of the syslevel file and verify that correct number */
    /* of bytes were read.                                               */

    bytes_read = fread(cur_syslevel, 1, filesize, cur_sysfile);
    if (bytes_read != filesize)
    {
       return_code = RC_CURRENT_SYS;
       goto free2;
    }

    /* Verify that the syslevel file is good. */
    install_status = NEW_VERSION;
    if ( strncmp(cur_syslevel->Signature, "SYSLEVEL", 8) )
    {
       return_code = RC_CURRENT_SYS;
       goto free2;
    }

    if ( strncmp(cur_syslevel->CompId, I18N_COMPID, 9) )
    {
      return_code = RC_SOURCE_SYS;
      goto free2;
    }

    /* Compare versions */
/*
    if ( strcmp(src_syslevel->Type, cur_syslevel->Type) )
    {
       install_status = INCOMPAT_VERSION;
       goto free2;
    }
*/

    if ( src_syslevel->SysVersion < cur_syslevel->SysVersion )
    {
       install_status = OLDER_VERSION;
       goto free2;
    }

    if ( (src_syslevel->SysVersion == cur_syslevel->SysVersion) &&
         (src_syslevel->SysModify < cur_syslevel->SysModify) )
    {
       install_status = OLDER_VERSION;
       goto free2;
    }

    if ( (src_syslevel->SysVersion == cur_syslevel->SysVersion) &&
         (src_syslevel->SysModify == cur_syslevel->SysModify) &&
         (src_syslevel->RefreshLevel < cur_syslevel->RefreshLevel) )
    {
       install_status = OLDER_VERSION;
       goto free2;
    }

    if ( (src_syslevel->SysVersion == cur_syslevel->SysVersion) &&
         (src_syslevel->SysModify == cur_syslevel->SysModify) &&
         (src_syslevel->RefreshLevel == cur_syslevel->RefreshLevel) )
    {
       install_status = SAME_VERSION;
       goto free2;
    }

  }

free2:
  free(cur_syslevel);

close2:
  fclose(cur_sysfile);

free1:
  free(src_syslevel);

close1:
  fclose(src_sysfile);

setup:
  /* Initialize paths to dll and locale directories to be used later. */
  strcpy(i18n_dll_dir, i18n_dir);
  strcat(i18n_dll_dir, I18N_DLL_DIR);
  strcpy(i18n_locale_dir, i18n_dir);
  strcat(i18n_locale_dir, I18N_LOCALE_DIR);

exit_is:
  return;
}

/*****************************************************************************/
/***                                                                       ***/
/***     Install the I18N package.                                         ***/
/***                                                                       ***/
/*                                                                           */
/*  Creates I18N directories.                                                */
/*                                                                           */
/*  If installing a newer version:                                           */
/*      Copy new syslevel file                                               */
/*      Copy new setloc1.dll                                                 */
/*      Copy all locale dlls provided.                                       */
/*                                                                           */
/*  If installing the same version:                                          */
/*      Copy locale dlls provided.                                           */
/*                                                                           */
/*  If installing an older version:                                          */
/*      Copy locale dlls provided that are not already installed.            */
/*                                                                           */
/*****************************************************************************/
void execute_install_I18N()
{
  char    I18N_dir[BUF_SIZE];           /* Directory path.                   */
  char    I18N_dll_dir[BUF_SIZE];       /* Directory path.                   */
  char    I18N_locale_dir[BUF_SIZE];    /* Directory path.                   */
  char    temp_path[BUF_SIZE];
  char    src_path[BUF_SIZE];
  char    tar_path[BUF_SIZE];
  char    srcname[BUF_SIZE];
  HDIR    srchandle;
  ULONG   srccount;
  FILE   *fh;
  FILEFINDBUF3 srcbuffer;

  PEAOP2  eabuf;                        /* Extended attribute buffer.        */
  int     ret_code, rc, rc1;            /* Return code from is_installed API.*/

  /* Create installation directories on target drive */
  create_dirs();
  if (!return_code)
  {
    /* If this is a 1st time installation or we are installing a */
    /* newer version, install all files.                         */
    if (install_status == FIRST_TIME | install_status == NEW_VERSION)
    {

      /* Copy the ALIASES file */
      strcpy(src_path, parm_source);
      strcat(src_path, I18N_LOCALE_DIR);
      strcat(src_path, ALIASES_NAME);
      strcpy(tar_path, i18n_locale_dir);
      strcat(tar_path, ALIASES_NAME);
      copy_file (src_path, tar_path);

      /* If the target disk is full */
      if (return_code == RC_DISK_FULL)
      {
        /* Call function that attempts to resolve the disk full problem */
        handle_disk_full();

        /* If it can't be resolved, exit */
        if (return_code)
        {
          goto exit_install;
        }
        else
        {
          /* else try to copy the file again.  Have to rebuild the  */
          /* path because the directory may have been moved.        */
          strcpy(tar_path, i18n_locale_dir);
          strcat(tar_path, ALIASES_NAME);
          copy_file(src_path, tar_path);
          if (return_code)
          {
            goto exit_install;
          }
        }
      }
      /* else if any other I/O error occurred, exit */
      else
      {
        if (return_code)
        {
          goto exit_install;
        }
      }

      /* Copy the SETLOC1.DLL file */
      strcpy(src_path, parm_source);
      strcat(src_path, I18N_DLL_DIR);
      strcat(src_path, DLL_NAME);
      strcpy(tar_path, i18n_dll_dir);
      strcat(tar_path, DLL_NAME);
      copy_file (src_path, tar_path);

      /* If the target disk is full */
      if (return_code == RC_DISK_FULL)
      {
        /* Call function that attempts to resolve the disk full problem */
        handle_disk_full();

        /* If it can't be resolved, exit */
        if (return_code)
        {
          goto exit_install;
        }
        else
        {
          /* else try to copy the file again.  Have to rebuild the  */
          /* path because the directory may have been moved.        */
          strcpy(tar_path, i18n_dll_dir);
          strcat(tar_path, DLL_NAME);
          copy_file(src_path, tar_path);
          if (return_code)
          {
            goto exit_install;
          }
        }
      }
      /* else if any other I/O error occurred, exit */
      else
      {
        if (return_code)
        {
          goto exit_install;
        }
      }

      /* Copy all the locale DLL files */

      /* Get first DLL file name from source directory */
      strcpy( srcname, parm_source );
      strcat( srcname, I18N_LOCALE_DIR);
      strcat( srcname, "*.DLL" );
      srccount = 1;
      srchandle = HDIR_CREATE;
      rc = DosFindFirst(srcname, &srchandle, FILE_NORMAL, &srcbuffer,
                        sizeof(srcbuffer), &srccount, FIL_STANDARD);
      while ( rc == NO_ERROR )
      {
        /* Build path to source file */
        strcpy(src_path, parm_source);
        strcat(src_path, I18N_LOCALE_DIR);
        strcat(src_path, srcbuffer.achName);

        /* Build path to target file */
        strcpy(tar_path, i18n_locale_dir);
        strcat(tar_path, srcbuffer.achName);

        /* Copy the file */
        copy_file(src_path, tar_path);

        /* If the target disk is full */
        if (return_code == RC_DISK_FULL)
        {
          /* Call function that attempts to resolve the disk full problem */
          handle_disk_full();

          /* If it can't be resolved, exit */
          if (return_code)
          {
            goto close;
          }
          else
          {
            /* else try to copy the file again.  Have to rebuild the  */
            /* path because the directory may have been moved.        */

            strcpy(tar_path, i18n_locale_dir);
            strcat(tar_path, srcbuffer.achName);
            copy_file(src_path, tar_path);

            if (return_code)
            {
              goto close;
            }
          }
        }
        /* else if any other I/O error occurred, exit */
        else
        {
          if (return_code)
          {
            goto close;
          }
        }

        /* Get the next locale file from the source directory          */
        rc = DosFindNext( srchandle, &srcbuffer,
                          sizeof(srcbuffer), &srccount );
      } /* EndWhile */
      if (rc != NO_ERROR && rc != ERROR_NO_MORE_FILES)
      {
        return_code = RC_COPY_ERROR;
        goto exit_install;
      }

      /* Copy the syslevel file */
      strcpy(src_path, parm_source);
      strcat(src_path, SYSLEVEL_NAME);
      strcpy(tar_path, i18n_dir);
      strcat(tar_path, SYSLEVEL_NAME);
      copy_file(src_path, tar_path);

      /* If the target disk is full */
      if (return_code == RC_DISK_FULL)
      {
        /* Call function that attempts to resolve the disk full problem */
        handle_disk_full();

        /* If it can't be resolved, exit */
        if (return_code)
        {
          goto exit_install;
        }
        else
        {
          /* else try to copy the file again.  Have to rebuild the  */
          /* path because the directory may have been moved.        */
          strcpy(tar_path, i18n_dir);
          strcat(tar_path, SYSLEVEL_NAME);
          copy_file(src_path, tar_path);
          if (return_code)
          {
            goto exit_install;
          }
        }
      }
      /* else if any other I/O error occurred, exit */
      else
      {
        if (return_code)
        {
          goto exit_install;
        }
      }
    } /* Endif first install or newer version.


    /* If installing an older version or the same version */
    /* only want to copy the locale files that don't already exist. */

    if (install_status == OLDER_VERSION || install_status == SAME_VERSION)
    {
      /* Get first dll file name from source directory */
      strcpy( srcname, parm_source );
      strcat( srcname, I18N_LOCALE_DIR);
      strcat( srcname, "*.DLL" );
      srccount = 1;
      srchandle = HDIR_CREATE;
      rc = DosFindFirst(srcname, &srchandle, FILE_NORMAL, &srcbuffer,
                        sizeof(srcbuffer), &srccount, FIL_STANDARD);
      while ( rc == NO_ERROR )
      {
        /* If dll file name was found in the source directory, see  */
        /* if it exists already in the target directory.  If not, copy */
        /* it to the target directory.  If it does exist, do nothing.  */

        /* Build path to target file */
        strcpy(tar_path, i18n_locale_dir);
        strcat(tar_path, srcbuffer.achName);

        /* Attempt to open the target file */
        fh = fopen (tar_path, "r");

        /* If not found */
        if (!fh)
        {
          /* Copy the file */
          strcpy( src_path, parm_source );
          strcat( src_path, I18N_LOCALE_DIR);
          strcat( src_path, srcbuffer.achName);
          copy_file (src_path, tar_path);

          /* If the target disk is full */
          if (return_code == RC_DISK_FULL)
          {
            /* Call function that attempts to resolve the disk full problem */
            handle_disk_full();

            /* If it can't be resolved, exit */
            if (return_code)
            {
              fclose(fh);
              goto close;
            }
            else
            {
              /* else try to copy the file again.  Have to rebuild the  */
              /* path because the directory may have been moved.        */

              strcpy(tar_path, i18n_locale_dir);
              strcat(tar_path, srcbuffer.achName);
              copy_file(src_path, tar_path);

              if (return_code)
              {
                fclose(fh);
                goto close;
              }
            }
          }
          /* else if any other I/O error occurred, exit */
          else
          {
            if (return_code)
            {
              fclose(fh);
              goto close;
            }
          }
        }
        fclose (fh);

        /* Get the next locale file from the source directory          */
        rc = DosFindNext( srchandle, &srcbuffer,
                          sizeof(srcbuffer), &srccount );
      } /* EndWhile */
      if (rc != NO_ERROR && rc != ERROR_NO_MORE_FILES)
      {
        return_code = RC_COPY_ERROR;
        goto exit_install;
      }

    } /* Endif older or same version */

  }

close:
  DosFindClose(srchandle);

exit_install:
  return;
}

/*****************************************************************************/
/*                                                                           */
/* copy_file - Copies a file from one directory to another.                  */
/*                                                                           */
/*             If the file exists in the tar_path, it makes a temporary      */
/*             copy of it.  If the file in the src_path is successfully      */
/*             copied, the temporary copy is deleted.  If the file in the    */
/*             src_path is not successfully copied, the temporary file is    */
/*             renamed back to its original name.                            */
/*                                                                           */
/*             If the file being copied is a DLL and it exists in the        */
/*             target directory, the DosReplaceModule function is used in    */
/*             case the file is locked.                                      */
/*                                                                           */
/*             If a disk full error occurs, the disk full error is returned. */
/*             Any other error is mapped to RC_COPY_ERROR.                   */
/*                                                                           */
/* Return Codes:  RC_DISK_FULL                                               */
/*                RC_COPY_ERROR                                              */
/*                                                                           */
/*****************************************************************************/

void copy_file (char *src_path, char *tar_path)
{
  int len;
  int rc, rc1;
  char tempfile[BUF_SIZE];
  char *ptr;
  BOOL target_exists;                       /* 0 - target doesn't exist */
                                            /* 1 = target does exist    */

  target_exists = 1;

  /* If the file already exists in the target directory, copy it    */
  /* with a .TMP extension it so we won't wipe it out until a good  */
  /* copy occurs.                                                   */
  strcpy (tempfile, tar_path);
  ptr = strchr (tempfile, (int) '.');
  if (ptr)
  {
    strcpy (ptr, ".TMP");
  }
  else
  {
    strcat (tempfile, ".TMP");
  }
  rc = DosCopy (tar_path, tempfile, (ULONG) 0);
  if (rc == ERROR_FILE_NOT_FOUND || rc == ERROR_PATH_NOT_FOUND)
  {
    rc = NO_ERROR;
    target_exists = 0;
  }
  else
  {
    if (rc == ERROR_DISK_FULL)
    {
      return_code = RC_DISK_FULL;
      goto exit_copy;
    }
    if (rc)
    {
      return_code = RC_COPY_ERROR;
      goto exit_copy;
    }
  }

  /* Attempt to copy the file to the target directory */
  rc = DosCopy (src_path, tar_path, DCPY_EXISTING);

  /* If file locked */
  if (rc == ERROR_ACCESS_DENIED || rc == ERROR_SHARING_VIOLATION)
  {
    /* Determine if this is a DLL we are copying */
    strupr (src_path);
    len = strlen (src_path);
    rc = strcmp (&src_path[len-4], ".DLL");

    /* If this is a DLL, use the DosReplaceModule function  */
    if (!rc)
    {
      rc = DosReplaceModule(tar_path, src_path, NULL);
    }
  }

  if (rc == ERROR_DISK_FULL)
  {
    return_code = RC_DISK_FULL;
    goto cleanup;
  }
  if (rc != NO_ERROR)
  {
    return_code = RC_COPY_ERROR;
    goto cleanup;
  }

  /* At this point, file has been successfully copied to the target     */
  /* directory.  Delete the temporary copy of the file if it exists.    */
  /* Ignore error from delete.                                          */

  if (target_exists)
  {
    DosDelete (tempfile);
  }
  goto exit_copy;

cleanup:
  rc = DosMove (tempfile, tar_path);
  if (rc == ERROR_SHARING_VIOLATION)
    rc = DosDelete (tempfile);

exit_copy:
  return;
}
/*****************************************************************************/
/*                                                                           */
/*   handle_disk_full --  Handle disk full error.                            */
/*                                                                           */
/*   If installing package for the 1st time, then we are already using       */
/*   the drive specified by the caller or have defaulted to the boot drive,  */
/*   so we have nothing left to try.                                         */
/*                                                                           */
/*   If the code is already installed on the same drive that the user        */
/*   requested, there is nothing left to try.                                */
/*                                                                           */
/*   If the caller requested that the config.sys file NOT be updated,        */
/*   there is nothing left to try.                                           */
/*                                                                           */
/*   If we are trying to install on a drive other than the one specified     */
/*   by the caller, then attempt to copy the entire existing directory to    */
/*   the drive specified by the user.  If successful, delete the code        */
/*   from the original directory and also delete the directories.            */
/*                                                                           */
/*   Set flag indicating to the config.sys function that the I18NDIR         */
/*   environment variable needs updating.  Once the directories have been    */
/*   moved, must update this variable whether the remainder of the copies    */
/*   work or not.                                                            */
/*                                                                           */
/*   Will not report any errors if the directories were successfully         */
/*   copied but there were problems erasing the old directories.             */
/*                                                                           */
/*****************************************************************************/
void handle_disk_full ()
{
  char  src_path[BUF_SIZE];
  char  tar_path[BUF_SIZE];
  char  org_path[BUF_SIZE];
  char  tmp_path[BUF_SIZE];
  char  I18N_dll_dir[BUF_SIZE];
  char  I18N_locale_dir[BUF_SIZE];
  APIRET rc, rc1;
  char  srcname[BUF_SIZE];
  HDIR  srchandle;
  ULONG srccount;
  FILEFINDBUF3 srcbuffer;

  /* If already using drive specified by caller */
  if (install_status == FIRST_TIME)
  {
    goto exit_handle;
  }

  if (i18n_dir[0] == parm_alt_drive)
  {
    goto exit_handle;
  }

  if (parm_updateconfig == 'N')
  {
    goto exit_handle;
  }

  /* Modify install path */
  strcpy (org_path, i18n_dir);
  i18n_dir[0] = parm_alt_drive;
  i18n_locale_dir[0] = parm_alt_drive;
  i18n_dll_dir[0] = parm_alt_drive;

  /* Create directories on the alternate drive */
  create_dirs();

  if (!return_code)
  {
    /* Copy files from original target to new target */
    /* The copy will fail if any of the files already exist. */

    strcpy (src_path, org_path);
    src_path[strlen(src_path)-1] = '\0';
    strcpy (tar_path, i18n_dir);
    tar_path[strlen(tar_path)-1] = '\0';
    rc = DosCopy (src_path, tar_path, (ULONG) 0);
    if (rc)
    {
      return_code = RC_DISK_FULL;

      /* Erase files that got copied to target directory */
      /* Ignore errors.  We already have enough problems. */

      /* Erase the SETLOC1.DLL file */
      strcpy (src_path, tar_path);
      strcat (src_path, "\\");
      strcat (src_path, I18N_DLL_DIR);
      strcat (src_path, DLL_NAME);
      rc = DosDelete (src_path);

      /* Erase all files in the LOCALE directory */
      strcpy(srcname, tar_path);
      strcat (srcname, "\\");
      strcat(srcname, I18N_LOCALE_DIR);
      strcat(srcname, "*.*" );
      srccount = 1;
      srchandle = HDIR_CREATE;
      rc = DosFindFirst(srcname, &srchandle, FILE_NORMAL, &srcbuffer,
                        sizeof(srcbuffer), &srccount, FIL_STANDARD);
      while ( rc == NO_ERROR )
      {
        /* Erase the file */
        strcpy (src_path, tar_path);
        strcat (src_path, "\\");
        strcat (src_path, I18N_LOCALE_DIR);
        strcat (src_path, srcbuffer.achName);
        rc1 = DosDelete(src_path);

        /* Get the next file from the source directory          */
        rc = DosFindNext( srchandle, &srcbuffer,
                          sizeof(srcbuffer), &srccount );
      } /* EndWhile */
      DosFindClose(srchandle);

      /* Erase the syslevel file */
      strcpy (src_path, tar_path);
      strcat (src_path, "\\");
      strcat (src_path, SYSLEVEL_NAME);
      rc = DosDelete (src_path);

      /* Erase directories */
      sprintf( tmp_path, "%s%s%s", tar_path, "\\", I18N_DLL_DIR );
      tmp_path[strlen(tmp_path)-1] = '\0';
      rc = DosDeleteDir(tmp_path);

      sprintf( tmp_path, "%s%s%s", tar_path, "\\", I18N_LOCALE_DIR );
      tmp_path[strlen(tmp_path)-1] = '\0';
      rc = DosDeleteDir(tmp_path);

      rc = DosDeleteDir(tar_path);
    }
    else
    {
      /* Erase files from original directory */

      /* Erase the SETLOC1.DLL file */
      strcpy (src_path, org_path);
      strcat (src_path, I18N_DLL_DIR);
      strcat (src_path, DLL_NAME);
      rc = DosDelete (src_path);
      if (rc)
      {
        strcpy (tmp_path, org_path);
        strcat (tmp_path, SYSLEVEL_NAME);
        rc = DosReplaceModule(src_path, tmp_path, NULL);
        if (!rc)
        {
          rc = DosDelete(src_path);
        }
      }

      /* Erase all files in the LOCALE directory */
      strcpy(srcname, org_path);
      strcat(srcname, I18N_LOCALE_DIR);
      strcat(srcname, "*.*" );
      srccount = 1;
      srchandle = HDIR_CREATE;
      rc = DosFindFirst(srcname, &srchandle, FILE_NORMAL, &srcbuffer,
                        sizeof(srcbuffer), &srccount, FIL_STANDARD);
      while ( rc == NO_ERROR )
      {
        /* Erase the file */
        strcpy (src_path, org_path);
        strcat (src_path, I18N_LOCALE_DIR);
        strcat (src_path, srcbuffer.achName);
        rc1 = DosDelete(src_path);
        if (rc1)
        {
          strcpy (tmp_path, org_path);
          strcat (tmp_path, SYSLEVEL_NAME);
          rc1 = DosReplaceModule(src_path, tmp_path, NULL);
          if (!rc1)
          {
            rc1 = DosDelete(tmp_path);
          }
        }

        /* Get the next file from the source directory          */
        rc = DosFindNext( srchandle, &srcbuffer,
                          sizeof(srcbuffer), &srccount );
      } /* EndWhile */
      DosFindClose(srchandle);

      /* Erase the syslevel file */
      strcpy (src_path, org_path);
      strcat (src_path, SYSLEVEL_NAME);
      rc = DosDelete (src_path);

      /* Erase directories */
      sprintf( tmp_path, "%s%s", org_path, I18N_DLL_DIR );
      tmp_path[strlen(tmp_path)-1] = '\0';
      rc = DosDeleteDir(tmp_path);

      sprintf( tmp_path, "%s%s", org_path, I18N_LOCALE_DIR );
      tmp_path[strlen(tmp_path)-1] = '\0';
      rc = DosDeleteDir(tmp_path);

      org_path[strlen(org_path)-1] = '\0';
      rc = DosDeleteDir(org_path);

      /* Set flag to update I18NDIR environment variable */
      force_i18ndir = TRUE;
    }
  }
  else
  {
     return_code = RC_DISK_FULL;
  }

exit_handle:
  return;
}

/*****************************************************************************/
/*                                                                           */
/* create_dirs - Creates installation directories on target drive.           */
/*               Ignores "directory already exists" errors.                  */
/*                                                                           */
/* Return Codes:  RC_DIR_ERROR                                               */
/*                                                                           */
/*****************************************************************************/
void create_dirs ()
{
  PEAOP2  eabuf;                        /* Extended attribute buffer.        */
  int     rc;
  char    tmp_path[BUF_SIZE];
                                        /* Set installation directories      */
  eabuf = 0;
                                     /* Create all I18N directories, if   */
                                     /*  they do not already exist.       */
                                     /*  Handle if create fails.          */
  return_code = 0;
  strcpy(tmp_path, i18n_dir);
  if (tmp_path[strlen(tmp_path)-1] == '\\')
  {
    tmp_path[strlen(tmp_path)-1] = '\0';
  }
  rc = DosCreateDir( tmp_path, eabuf );
  if (!rc || rc == ERROR_ACCESS_DENIED)
  {
     strcpy(tmp_path, i18n_dll_dir);
     if (tmp_path[strlen(tmp_path)-1] == '\\')
     {
       tmp_path[strlen(tmp_path)-1] = '\0';
     }
     rc = DosCreateDir( tmp_path, eabuf );
     if (!rc || rc == ERROR_ACCESS_DENIED)
     {
        strcpy(tmp_path, i18n_locale_dir);
        if (tmp_path[strlen(tmp_path)-1] == '\\')
        {
          tmp_path[strlen(tmp_path)-1] = '\0';
        }
        rc = DosCreateDir( tmp_path, eabuf );
     }
  }
  if (rc && rc != ERROR_ACCESS_DENIED)
  {
    return_code = RC_DIR_ERROR;
  }
}

/*****************************************************************************/
/*                                                                           */
/*       Update the CONFIG.SYS file with the I18N statements.                */
/*                                                                           */
/*  If this install had to move the I18NDIR files into an alternate          */
/*  subdirectory due to a disk full condition, the I18NDIR environment       */
/*  variable must be updated.  This update must occur even if other          */
/*  errors prevent the other config.sys changes.                             */
/*                                                                           */
/*  This function checks the global "return_code" variable to determine      */
/*  if changes should be made.                                               */
/*                                                                           */
/*  This function updates/adds the following environment variables:          */
/*      LIBPATH                                                              */
/*      LOCPATH                                                              */
/*      LANG                                                                 */
/*      I18NDIR                                                              */
/*                                                                           */
/*  Return Codes:  RC_CONFIG_FAILED                                          */
/*                                                                           */
/*****************************************************************************/

void execute_update_config()
{
  FILE       *file_config;                 /* File for CONFIG.SYS update.    */
  FILE       *file_work;                   /* File for CONFIG.SYS update.    */
  char        config_record[CONFIG_SIZE];  /* Record from CONFIG.SYS file.   */
  char        config_upper[CONFIG_SIZE];   /* Record from CONFIG.SYS file.   */
  char        config_text[CONFIG_SIZE];    /* Record for CONFIG.SYS file.    */
  char        *ptrchar;
  BOOL        libpath_updated = FALSE;     /* Whether LIBPATH is done.       */
  BOOL        lang_updated = FALSE;        /* Whether LANG is done.          */
  BOOL        locpath_updated = FALSE;     /* Whether LOCPATH is done.       */
  int         ret_code;
  char        tmp_path[BUF_SIZE];
  char        temp_parm[BUF_SIZE];
  char       *ptr;
  BOOL        write_flag;                  /* Whether to write line back out */
                                           /* to config.sys or not           */
  COUNTRYCODE Country = {0};
  COUNTRYINFO CtryInfo = {0};
  char        szLocaleName[10] = {0};
  ULONG       ulInfoLen = 0;
  ULONG       aulCpList[8] = {0};
  ULONG       TableIndex = 0;
  ULONG       ulAction = 0;
  ULONG       ulKbdDataLen = 0;
  KBDDATA     KbdData = {0};
  HFILE       hKbd, hlocale;
  char        temp_path[BUF_SIZE];
  APIRET      rc = 0;

  /* Open input and output files.      */
  file_config = fopen( config_sys_file, "r" ) ;
  file_work = fopen( config_sys_work_file, "w+" ) ;
  if ( file_config && file_work )
  {
     /* Read from the input CONFIG.SYS,   */
     /*  writing out updated records.     */
     while ( fgets( config_record, sizeof(config_record), file_config ) != NULL )
     {
        write_flag = 1;
        /* Convert data to uppercase.       */
        strcpy( config_upper, config_record );
        strupr( config_upper );

        /* First get token from record.      */
        strcpy( config_text, config_upper );
        ptrchar = strtok( config_text, " " );

        /* If not a comment, analyze further.*/
        if ( ptrchar && strcmp( ptrchar, "REM" ) )
        {
          /* If LIBPATH line, add I18N path    */
          /*  information at beginning of data.*/
          if ( strstr( ptrchar, I18N_LIBPATH_VAR ) )
          {
            strcpy (tmp_path, i18n_dll_dir);
            tmp_path[strlen(tmp_path)-1] = '\0';
            add_dir_config( config_record, config_upper, tmp_path );
            libpath_updated = TRUE;
          }
          else
          {
            /* If SET line, look for variable.   */
            if ( !strcmp( ptrchar, "SET" ) )
            {
              /* If LOCPATH line, add I18N path    */
              /*  information at beginning of data.*/
              if ( strstr( config_upper, I18N_LOCPATH_VAR ) )
              {
                strcpy (tmp_path, i18n_locale_dir);
                tmp_path[strlen(tmp_path)-1] = '\0';
                add_dir_config( config_record, config_upper, tmp_path );
                locpath_updated = TRUE;
              }
              else
              {
                /* If LANG line and new LANG value specified, replace */
                /* with new value.                                    */
                /* If LANG line and new value not specified, force    */
                /* the format of the LANG value to be xx_xx.  If      */
                /* already specified as xx_xx. something, leave alone.*/
                if ( strstr( config_upper, I18N_LANG_VAR) )
                {
                  if (parm_lang[0] != '\0')
                  {
                    sprintf( config_record, "SET %s%s\n", I18N_LANG_VAR,
                             parm_lang );
                    lang_updated = TRUE;
                  }
                  else
                  {
                                        /* Want the lang parm to have the    */
                                        /* format, xx_xx.  So if the first   */
                                        /* 5 chars are xx_xx, save them and  */
                                        /* disregard anything that follows   */
                                        /* unless what follows is . something*/
                                        /* If the first 4 chars are xxxx,    */
                                        /* convert them to xx_xx and         */
                                        /* disregard anything that follows.  */
                    ptr = strstr(config_upper, "=");
                    ptr++;
                    strcpy (tmp_path, ptr);
                    if (tmp_path[2] != '_')
                    {
                      if (  isalpha((int)tmp_path[0]) &&
                            isalpha((int)tmp_path[1]) &&
                            isalpha((int)tmp_path[2]) &&
                            isalpha((int)tmp_path[3]) )
                      {
                        strncpy (temp_parm, tmp_path, 2);
                        temp_parm[2] = '\0';
                        strcat (temp_parm, "_");
                        strncat (temp_parm, &tmp_path[2], 2);
                        sprintf( config_record, "SET %s%s\n", I18N_LANG_VAR,
                                 temp_parm );

                        lang_updated = TRUE;
                      }
                      else
                      {
                        write_flag = 0;
                      }
                    }
                    else
                    {
                      if ( isalpha((int)tmp_path[0]) &&
                           isalpha((int)tmp_path[1]) &&
                           isalpha((int)tmp_path[3]) &&
                           isalpha((int)tmp_path[4]) )
                      {
                        if (tmp_path[5] == '.')
                        {
                          strcpy (temp_parm, tmp_path);
                          if (temp_parm[strlen(temp_parm)-1] == '\n')
                            temp_parm[strlen(temp_parm)-1] = '\0';
                        }
                        else
                        {
                          strncpy (temp_parm, tmp_path, 5);
                          temp_parm[5] = '\0';
                        }
                        sprintf( config_record, "SET %s%s\n", I18N_LANG_VAR,
                                 temp_parm );
                        lang_updated = TRUE;
                      }
                      else
                      {
                        write_flag = 0;
                      }
                    }
                  }
                } /* endif if lang */
              }
            }
          }

          /* Under normal circumstances, the I18NDIR variable does not    */
          /* get changed.  It only changes when the directories have been */
          /* moved due to a disk full problem.  If the files have been    */
          /* moved, the force_i18ndir flag is set.                        */
          if (force_i18ndir)
          {
            if ( strstr( config_upper, I18N_I18NDIR_VAR ) )
            {
              strcpy (tmp_path, i18n_dir);
              tmp_path[strlen(tmp_path)-1] = '\0';
              sprintf( config_record, "SET %s%s\n", I18N_I18NDIR_VAR, tmp_path);
            }
          }
        }
        /* Write out previous or updated line */
        if (write_flag)
        {
          ret_code = fputs( config_record, file_work );
          if ( ret_code == EOF ) return_code = RC_CONFIG_FAILED ;
        }

     } /* Endwhile */

     /* If LIBPATH is not updated, add new line at end of file.  */
     if ( !libpath_updated )
     {

       strcpy (tmp_path, i18n_dll_dir);
       tmp_path[strlen(tmp_path)-1] = '\0';
       sprintf( config_text, "%s%s\n",I18N_LIBPATH_VAR, tmp_path );
       ret_code = fputs( config_text, file_work );
       if ( ret_code == EOF ) return_code = RC_CONFIG_FAILED ;
     }

     /* If LOCPATH is not updated, add new line at end of file.          */
     if ( !locpath_updated )
     {
       strcpy (tmp_path, i18n_locale_dir);
       tmp_path[strlen(tmp_path)-1] = '\0';
       sprintf( config_text, "SET %s%s\n", I18N_LOCPATH_VAR, tmp_path );
       ret_code = fputs( config_text, file_work );
       if ( ret_code == EOF ) return_code = RC_CONFIG_FAILED ;
     }

     /* If I18NDIR does not already exist add new line at end of file.  */
     if ( install_status == FIRST_TIME )
     {
       strcpy (tmp_path, i18n_dir);
       tmp_path[strlen(tmp_path)-1] = '\0';
       sprintf( config_text, "SET %s%s\n", I18N_I18NDIR_VAR, tmp_path);
       ret_code = fputs( config_text, file_work );
       if ( ret_code == EOF ) return_code = RC_CONFIG_FAILED ;
     }

     /* if ok to update everything */
     if (!return_code)
     {
       /* If LANG has not been updated already */
       if (lang_updated == FALSE)
       {
         /* If LANG was specified by caller   */
         if (parm_lang[0] != '\0')
         {
           /* add new line at end of file.      */
           sprintf( config_text, "SET %s%s\n", I18N_LANG_VAR, parm_lang );
           ret_code = fputs( config_text, file_work );
           if ( ret_code == EOF )
              return_code = RC_CONFIG_FAILED ;
         }
         /* else LANG was not specified by caller and either it does */
         /* not exist in config.sys or it is incorrect in config.sys */
         else
         {
           /* Determine a default value for LANG.  This will be done      */
           /* the same way that the selloc1 initialization does it.       */
           /*                                                             */
           /* Get the current system country code.                        */
           /* Use the country id to look up a locale prefix in the locale */
           /* table.                                                      */
           /* If the id is not in the table, don't set LANG.              */
           /* If the id is in the table more than once,                   */
           /*   Get the keyboard id                                       */
           /*   Use the keyboard id to determine which of the locale      */
           /*   prefixes to use.                                          */
           /* Add LANG to config.sys.                                     */
           /*                                                             */

           if ( ! DosQueryCtryInfo( sizeof(CtryInfo), &Country,
                                    &CtryInfo, &ulInfoLen ) )
           {
             for ( TableIndex=0; TableIndex<=NUM_COUNTRIES; TableIndex++)
             {
               if ( LocaleTable[TableIndex].ulCountry == CtryInfo.country )
               {
                 if ( LocaleTable[TableIndex].szKeyboard[0] != '\0' )
                 {
                                          /* Get keyboard data only once. */
                   if ( KbdData.szKbdCtry[0] == '\0' )
                   {
                     KbdData.usKbdLen=sizeof(KbdData);
                     DosOpen ("KBD$", &hKbd, &ulAction, 0L, FILE_READONLY,
                             OPEN_ACTION_FAIL_IF_NEW |
                             OPEN_ACTION_OPEN_IF_EXISTS,
                             OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE |
                             OPEN_FLAGS_NOINHERIT, 0L);

                     DosDevIOCtl(hKbd, 0x0004, 0x007B, 0, 0, 0,
                                 &KbdData, sizeof(KbdData), &ulKbdDataLen);
                     DosClose(hKbd);
                   } /* Endif did we get keyboard info already? */

                 } /* Endif country id there more than once */

                 if ( ( LocaleTable[TableIndex].szKeyboard[0] == '\0' ) ||
                      (!strcmp( KbdData.szKbdCtry,
                                LocaleTable[TableIndex].szKeyboard )) )
                 {
                   strcpy (szLocaleName,
                           LocaleTable[TableIndex].szLocalePrefix);
                   szLocaleName[4] = szLocaleName[3];
                   szLocaleName[3] = szLocaleName[2];
                   szLocaleName[2] = '_';
                   sprintf( config_text, "SET %s%s\n", I18N_LANG_VAR,
                            szLocaleName);
                   ret_code = fputs( config_text, file_work );
                   if ( ret_code == EOF )
                     return_code = RC_CONFIG_FAILED;
                   break;
                 }
               } /* Endif matching country ID */
             } /* End for loop through locale table     */

           } /* Endif country id retrieved ok */
         } /* Endif LANG needs defaulting */
       } /* Endif LANG already updated */
     } /* Endif ok to update everything */
  }
  else /* couldn't open config.sys */
  {
     return_code = RC_CONFIG_FAILED;
  }

  /* Close files.                      */
  if ( file_config )
    fclose( file_config );
  if ( file_work )
    fclose( file_work );

  /* If no problems, replace the real  */
  /* CONFIG.SYS file with updates.     */
  if ( return_code == RC_NO_ERROR )
  {
    return_code = DosCopy (config_sys_work_file,
                           config_sys_file, DCPY_EXISTING);
  }

  /* Delete temporary file.            */
  remove( config_sys_work_file );
  if ( return_code != RC_NO_ERROR )
    return_code = RC_CONFIG_FAILED;

exit_config:
  return;
}

/*****************************************************************************/
/***                                                                       ***/
/***     Add the specified directory to the beginning of the passed        ***/
/***     CONFIG.SYS statement.                                             ***/
/***                                                                       ***/
/*****************************************************************************/

void add_dir_config(char *line_text, char *line_upper, char *add_dir)
{
  char        work_line[CONFIG_SIZE];       /* Work record for CONFIG.SYS.   */
  char        *ptrchar;
  char        *ptrchar2;
                                            /* If directory data to be added */
                                            /*  does not yet exist on this   */
                                            /*  line, then add it at the     */
                                            /*  beginning of library list.   */
  ptrchar = strstr( line_upper, add_dir );
  if ( !ptrchar )
  {
                                            /* Save current line up to and   */
                                            /*  including the "=".           */
     strcpy( work_line, line_text );
     ptrchar = strchr( work_line, '=' );
     *(ptrchar+1) = '\0';
                                            /* Find start of data after "=". */
     ptrchar = strchr( line_text, '=' );
     for( ptrchar2=ptrchar+1; *ptrchar2 && isspace(*ptrchar2); ++ptrchar2 );

     strcat( work_line, add_dir );          /* Append new directory data.    */
     strcat( work_line, ";" );              /* Append ";" library separator. */
     strcat( work_line, ptrchar2 );         /* Append original data.         */
     strcpy( line_text, work_line );        /* Reset CONFIG.SYS line         */
  }
}

/*****************************************************************************/
/***                                                                       ***/
/***     Erase source files.                                               ***/
/***                                                                       ***/
/***                                                                       ***/
/***                                                                       ***/
/*****************************************************************************/
void erase_source_files()
{
  char    srcname[BUF_SIZE];
  HDIR    srchandle;
  ULONG   srccount;
  FILEFINDBUF3 srcbuffer;
  int     rc, rc1;
  char    src_path[BUF_SIZE];

  /* Erase the syslevel file */
  strcpy(srcname, parm_source);
  strcat(srcname, SYSLEVEL_NAME);
  rc = DosDelete(srcname);
  if (rc != NO_ERROR)
  {
    return_code = RC_DELETE_SOURCE;
    goto exit_delete;
  }
  /* Erase the setloc1.dll */
  strcpy(srcname, parm_source);
  strcat(srcname, I18N_DLL_DIR);
  strcat(srcname, DLL_NAME);
  rc = DosDelete(srcname);
  if (rc != NO_ERROR)
  {
    return_code = RC_DELETE_SOURCE;
    goto exit_delete;
  }
  /* Erase all files in the caller's source LOCALE directory */
  strcpy(srcname, parm_source);
  strcat(srcname, I18N_LOCALE_DIR);
  strcat(srcname, "*.*" );
  srccount = 1;
  srchandle = HDIR_CREATE;
  rc = DosFindFirst(srcname, &srchandle, FILE_NORMAL, &srcbuffer,
                    sizeof(srcbuffer), &srccount, FIL_STANDARD);
  while ( rc == NO_ERROR )
  {
    /* Erase the file */
    strcpy (src_path, parm_source);
    strcat (src_path, I18N_LOCALE_DIR);
    strcat (src_path, srcbuffer.achName);
    rc1 = DosDelete(src_path);
    if (rc1 != NO_ERROR)
    {
      return_code = RC_DELETE_SOURCE;
      goto exit_delete;
    }

    /* Get the next file from the source directory          */
    rc = DosFindNext( srchandle, &srcbuffer,
                      sizeof(srcbuffer), &srccount );
  } /* EndWhile */
  if (rc != NO_ERROR && rc != ERROR_NO_MORE_FILES)
  {
    return_code = RC_DELETE_SOURCE;
    goto exit_delete;
  }

  /* Erase the directories */

  strcpy(srcname, parm_source);
  strcat(srcname, I18N_LOCALE_DIR);
  srcname[strlen(srcname)-1] = '\0';
  rc = DosDeleteDir(srcname);
  if (rc != NO_ERROR)
  {
    return_code = RC_DELETE_SOURCE;
    goto exit_delete;
  }
  strcpy(srcname, parm_source);
  strcat(srcname, I18N_DLL_DIR);
  srcname[strlen(srcname)-1] = '\0';
  rc = DosDeleteDir(srcname);
  if (rc != NO_ERROR)
  {
    return_code = RC_DELETE_SOURCE;
    goto exit_delete;
  }
  parm_source[strlen(parm_source)-1] = '\0';
  rc = DosDeleteDir(parm_source);
  if (rc != NO_ERROR)
  {
    return_code = RC_DELETE_SOURCE;
    goto exit_delete;
  }

exit_delete:
  return;
}

/*****************************************************************************/
/*                                                                           */
/*       Check CONFIG.SYS for I18NDIR environment variable.                  */
/*                                                                           */
/*  Can't check the environment for this variable because this package       */
/*  could be installed by several different products in a bundle without     */
/*  the system rebooting in between.  Each of the subsequent installations   */
/*  must be aware of the previous ones.                                      */
/*                                                                           */
/*****************************************************************************/

BOOL check_config()
{
  FILE       *file_config;                 /* File for CONFIG.SYS update.    */
  char        config_record[CONFIG_SIZE];  /* Record from CONFIG.SYS file.   */
  char        config_upper[CONFIG_SIZE];   /* Record from CONFIG.SYS file.   */
  char        config_text[CONFIG_SIZE];    /* Record for CONFIG.SYS file.    */
  char        *ptr, *ptrchar;
  BOOL        var_found;

  var_found = 0;

  /* Open config file */
  file_config = fopen( config_sys_file, "r" ) ;
  if ( file_config )
  {
     /* Read from the input CONFIG.SYS,   */
     /*  looking for the I18NDIR variable */
     while ( fgets( config_record, sizeof(config_record), file_config ) != NULL )
     {
        /* Convert data to uppercase.       */
        strcpy( config_upper, config_record );
        strupr( config_upper );

        /* First get token from record.      */
        strcpy( config_text, config_upper );
        ptrchar = strtok( config_text, " " );

        /* If not a comment, analyze further.*/
        if ( ptrchar && strcmp( ptrchar, "REM" ) )
        {
          /* If SET line, look for variable.   */
          if ( !strcmp( ptrchar, "SET" ) )
          {
            if ( strstr( config_upper, I18N_I18NDIR_VAR ) )
            {
              var_found = 1;
              ptr = strstr(config_upper, "=");
              ptr++;
              strcpy (i18n_dir, ptr);
              if (i18n_dir[strlen(i18n_dir)-1] == '\n')
              {
                strcpy (&i18n_dir[strlen(i18n_dir)-1], "\\");
              }
              else
              {
                strcat (i18n_dir, "\\");
              }
            }
          }
        }
     } /* Endwhile */

  }
  else
  {
     return_code = RC_CONFIG_FAILED;
  }

  /* Close files.                      */
  if ( file_config )
    fclose( file_config );

exit_check:
  return(var_found);
}
