/**************************************************************************\
* install.c -- install c_*.nls conversion tables.
*
*         Steve Firebaugh
*         Microsoft Developer Support
*         Copyright (c) 1992, 1993 Microsoft Corporation
*
*
*
* Note, this module must have UNICODE defined because the registry
*  code will not work without it.
*
\**************************************************************************/
#define UNICODE


#include <windows.h>
#include <commdlg.h>
#include <string.h>
#include <stdio.h>
#include "uconvert.h"
#include "install.h"

/**************************************************************************\
*  Global variables.
\**************************************************************************/

/* This is the registry key that we store conversion table information under. */
TCHAR NlsRegEntryStr[]=TEXT("SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage");

/* MessageBox strings */
TCHAR szMBERROR[]= TEXT("Error");


/***************************************************************************\
*    FUNCTION: InstallTableProc
*
* Dialog window procedure for the Install *.nls tables dialog.
*
\***************************************************************************/
LRESULT CALLBACK InstallTableProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{


  switch (message) {

    /**********************************************************************\
    *  WM_INITDIALOG
    *
    * Fill dialog with a list of the currently existing tables.
    \**********************************************************************/
    case WM_INITDIALOG:
      if (!ListInstalledTables (GetDlgItem (hwnd, DID_LISTBOX), LB_ADDSTRING, FALSE))
        EndDialog (hwnd, FALSE);
    break;

    case WM_COMMAND:
      switch (wParam) {
        case IDCANCEL:
        case IDOK:
          EndDialog (hwnd, TRUE);
        break;


        /**********************************************************************\
        *  WM_COMMAND, BID_ADD
        *
        * Use common dialog, get new *.nls name, and try to install it.
        \**********************************************************************/
        case BID_ADD:
          if (GetTableFileNames(hwnd))
            ListInstalledTables (GetDlgItem (hwnd, DID_LISTBOX), LB_ADDSTRING, FALSE);
        break;
      }
    break; /* end WM_COMMAND */


    case WM_SYSCOMMAND:
      if (wParam == SC_CLOSE)
        EndDialog (hwnd, TRUE);
    break; /* end WM_SYSCOMMAND */


  } /* end switch */
  return FALSE;
}



/***************************************************************************\
*    FUNCTION: GetTableFileNames
*
* Throw up a common dialog to the user, and let them search for the *.nls
*  file to install.
*
* LIMITATION:  Currently only works for one file at a time.
*  Should rewrite to accept multiple files.
*
\***************************************************************************/
int GetTableFileNames (HWND hwnd)
{
    OPENFILENAME OpenFileName;


    /* buffers for the file names. */
    TCHAR szFile[MAX_PATH],szFileTitle[MAX_PATH];
    TCHAR szFilter[MAX_PATH], buffer[50];
    TCHAR *p;

    /* Build up the correct filter strings for OPENFILENAME structure */

    p = szFilter;
    lstrcpy (buffer,TEXT("Conversion Table (*.nls)"));
    lstrcpy (p,buffer);
    p += lstrlen (buffer) +1;
    lstrcpy (buffer,TEXT("*.nls"));
    lstrcpy (p,buffer);
    p += lstrlen (buffer) +1;

    lstrcpy (p,TEXT("\0"));


    wsprintf (szFile, TEXT(""));
    wsprintf (szFileTitle, TEXT(""));

    OpenFileName.lStructSize       = sizeof(OPENFILENAME);
    OpenFileName.hwndOwner         = hwnd;
    OpenFileName.hInstance         = NULL;
    OpenFileName.lpstrFilter       = szFilter;
    OpenFileName.lpstrCustomFilter = NULL;
    OpenFileName.nMaxCustFilter    = 0L;
    OpenFileName.nFilterIndex      = 1L;
    OpenFileName.lpstrFile         = szFile;
    OpenFileName.nMaxFile          = MAX_PATH;
    OpenFileName.lpstrFileTitle    = szFileTitle;
    OpenFileName.nMaxFileTitle     = MAX_PATH;
    OpenFileName.lpstrInitialDir   = NULL;
    OpenFileName.lpstrTitle        = TEXT("Locate c_*.nls files from distribution media.");

    OpenFileName.nFileOffset       = 0;
    OpenFileName.nFileExtension    = 0;
    OpenFileName.lpstrDefExt       = NULL;

    OpenFileName.lCustData         = 0;
    OpenFileName.lpfnHook          = NULL;
    OpenFileName.lpTemplateName    = NULL;

//    OpenFileName.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT;
    OpenFileName.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST ;

    if (!GetOpenFileName(&OpenFileName)) return 0;


    return (InstallFile (szFile,szFileTitle));
}




/***************************************************************************\
*    FUNCTION: InstallFile
*
* Given full path name, and just file name, copy that file into the
*  system directory, and change the registry to indicate that the file
*  has now been "installed."
*
\***************************************************************************/
int InstallFile (TCHAR* szPathAndName, TCHAR* szName)
{
TCHAR szTargetFile[MAX_PATH], buffer[MAX_PATH];
TCHAR keyname[MAX_PATH];
HKEY hKey;
int cp, nChar;
LONG rValue;

    /* First verify that they have selected a valid file name. */
    CharLowerBuff (szName,lstrlen (szName));
    if (myScanf (szName, &cp) != 1) {
      MessageBox (NULL, TEXT("Incorrect file type.\n Need c_*.nls."),
                        szMBERROR, MB_ICONSTOP | MB_OK);
      return FALSE;
    }




    /* Build up a complete path name for the target file.
     *  Get the system directory, and prepend it before szName.
     */
    GetSystemDirectory (buffer, MAX_PATH);
    nChar = wsprintf (szTargetFile, TEXT("%s\\%s"), buffer, szName);

    if (nChar >= MAX_PATH) {
      MessageBox (NULL, TEXT("File name overflow."),szMBERROR, MB_ICONSTOP | MB_OK);
      return FALSE;
    }



    /* Now, try to open the registry for writing... This may fail if
     *  the current user has insufficient privilege, or it may fail
     *  for other, unforeseen, reasons.
     */
    rValue = RegOpenKeyEx (HKEY_LOCAL_MACHINE, NlsRegEntryStr, 0, KEY_SET_VALUE, &hKey);
    if (rValue == ERROR_ACCESS_DENIED) {
      MessageBox (NULL, TEXT("Log on as Administrator to complete this action."),
               TEXT("Access Denied, Insufficient Privilege") , MB_ICONSTOP | MB_OK);
      return FALSE;
    }
    if (rValue != ERROR_SUCCESS) {
      MessageBox (NULL, TEXT("RegOpenKeyEx() failed."),szMBERROR, MB_ICONSTOP | MB_OK);
      return FALSE;
    }


    /* Try to copy file... one reason for failure is file already
     *  exists.  If so, query the user to try again.
     *  If fails again, just report problem and exit.
     */
    if (!CopyFile (szPathAndName, szTargetFile, TRUE)) {

      /* if failure was from existing file, query user preference
       *  regarding replacing it.
       */
      if (GetLastError() == ERROR_FILE_EXISTS) {
        if (MessageBox (NULL, TEXT("File already exists.\n Replace existing?"),
                        TEXT("Warning"), MB_ICONEXCLAMATION | MB_YESNO) == IDNO) {
          goto close_and_exit;
        } else {
          if (!CopyFile (szPathAndName, szTargetFile, FALSE)) {
            MessageBox (NULL, TEXT("File copy failed again."),
                          szMBERROR, MB_ICONSTOP | MB_OK);
            goto close_and_exit;
          }
        }

      /* no duplicate file, CopyFile() failed for other reasons
       *  report failure and return.
       */
      } else {
        MessageBox (NULL, TEXT("File copy failed."),
                      szMBERROR, MB_ICONSTOP | MB_OK);
        goto close_and_exit;

      }
    }


    /* Finally, write the new key value to the registry. */
    if (myScanf (szName, &cp) == 1) {
      wsprintf (keyname, TEXT("%d"), cp);
      RegSetValueEx (hKey, keyname, 0, REG_SZ, (LPBYTE)szName,
                     (DWORD)((lstrlen(szName) +1)*sizeof(TCHAR)));

    } else
      MessageBox (NULL, szName, TEXT("Parsing file title failed."), MB_ICONSTOP | MB_OK);



close_and_exit:

    RegCloseKey (hKey);

    return TRUE;
}


/***************************************************************************\
*    FUNCTION: ListInstalledTables
*
* Display the *.nls conversion tables currently installed, according to the
*  registry.  Display either the file name, or just the codepage number.
*
* hwndFill - listbox or combobox to fill with names.
* message - LB_ADDSTRING or CB_ADDSTRING
* NumberOnly - FALSE then use full file name, TRUE then just use number.
*
* CRITERION for table being installed:
*  value in registry is c_* where * is number.
*
\***************************************************************************/
int ListInstalledTables (HWND hwndFill, UINT message, int NumberOnly)
{

  TCHAR szKeyname[MAX_PATH], szValue[MAX_PATH];
  DWORD cBytesName, cBytesValue, iSubKey;
  int  cp;
  HKEY hKey;

  /* open the registry key for reading.  If failure, report and exit. */
  if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, NlsRegEntryStr, 0, KEY_QUERY_VALUE, &hKey)) {
    MessageBox (NULL, TEXT("RegOpenKeyEx() failed."), szMBERROR, MB_ICONSTOP | MB_OK);
    return FALSE;
  }


  /* empty the current contents. */
  if (message == LB_ADDSTRING)
    SendMessage (hwndFill, LB_RESETCONTENT, 0, 0);
  else if (message == CB_ADDSTRING)
    SendMessage (hwndFill, CB_RESETCONTENT, 0, 0);


  iSubKey = 0;
  cBytesName = cBytesValue = MAX_PATH;
  while (!RegEnumValue (hKey, iSubKey, szKeyname, &cBytesName, NULL, NULL, (LPBYTE)szValue, &cBytesValue)) {

    if (myScanf (szValue, &cp) == 1) {

      /* if we are to display only the number, then reformat szValue string */
      if (NumberOnly)
        wsprintf (szValue, TEXT("%d"), cp);


      SendMessage (hwndFill, message, 0 ,(LPARAM) szValue);
    }

    iSubKey++;
    cBytesName = cBytesValue = MAX_PATH;  // undoc.ed feature, must be set each time.
  }

  RegCloseKey (hKey);

  return TRUE;
}








/***************************************************************************\
*    FUNCTION: myScanf
*
* Convert a string into a number (like sscanf).
*  However, this function works independent of UNICODE turned on.
*
* NOT a general function... looking for "c_%d.nls"
*
\***************************************************************************/
int myScanf (TCHAR* pSource, int* pValue)
{
char ansibuffer[MAX_PATH];
int iStrLen;

  iStrLen = lstrlen (pSource);
  if (iStrLen == 0) return 0;

#ifdef UNICODE
  WideCharToMultiByte (CP_ACP, 0, pSource, -1,
           ansibuffer, MAX_PATH, NULL, NULL);
#else
  lstrcpy (ansibuffer, pSource);
#endif

  CharLowerBuffA (ansibuffer,lstrlenA (ansibuffer));

  return (sscanf (ansibuffer, "c_%d.nls", pValue)); // leave off TEXT()
}
