//*************************************************************
//
//  Envvar.c   -   Environment Variables property sheet page
//
//  Microsoft Confidential
//  Copyright (c) Microsoft Corporation 1996
//  All rights reserved
//
//*************************************************************
#include <sysdm.h>

// C Runtime
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>


//==========================================================================
//                             Local Definitions
//==========================================================================
#define LB_SYSVAR   1
#define LB_USERVAR  2

#define BUFZ        4096
#define MAX_VALUE_LEN     1024
//==========================================================================
//                            Typedefs and Structs
//==========================================================================

//  Environment variables structure
typedef struct
{
//    DWORD  dwLocation;
    DWORD  dwType;
    LPTSTR szValueName;
    LPTSTR szValue;
    LPTSTR szExpValue;
} ENVARS;

//  Registry valuename linked-list structure
typedef struct _regval
{
    struct _regval *prvNext;
    LPTSTR szValueName;
} REGVAL;


//==========================================================================
//                             Local Functions
//==========================================================================
void EVDoCommand(HWND hDlg, HWND hwndCtl, int idCtl, int iNotify );
void EVDoItemChanged(HWND hDlg, int idCtl );
void EVSave(HWND hDlg);
void EVCleanUp (HWND hDlg);
int  FindVar (HWND hwndLB, LPTSTR szVar);
void ClearAllSelections (HWND hCtrl);

//==========================================================================
//                      "Global" Variables for this page
//==========================================================================
BOOL bEditSystemVars = FALSE;
DWORD cxLBSysVars = 0;
BOOL bUserVars = TRUE;

//
// Help ID's
//

DWORD aEnvVarsHelpIds[] = {
    IDC_ENVVAR_SYS_LB_SYSVARS,    (IDH_ENV + 0),
    IDC_ENVVAR_SYS_USERENV,       (IDH_ENV + 1),
    IDC_ENVVAR_SYS_LB_USERVARS,   (IDH_ENV + 2),
    IDC_ENVVAR_SYS_VAR,           (IDH_ENV + 3),
    IDC_ENVVAR_SYS_VALUE,         (IDH_ENV + 4),
    IDC_ENVVAR_SYS_SETUV,         (IDH_ENV + 5),
    IDC_ENVVAR_SYS_DELUV,         (IDH_ENV + 6),
    0, 0
};

TCHAR szUserEnv[] = TEXT( "Environment" );
TCHAR szSysEnv[]  = TEXT( "System\\CurrentControlSet\\Control\\Session Manager\\Environment" );


//*************************************************************
//
//  CreateEnvVarsPage()
//
//  Purpose:    Creates the Environment Variables page
//
//  Parameters: hInst   -   hInstance
//
//
//  Return:     hPage if successful
//              NULL if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              11/21/95    ericflo    Created
//
//*************************************************************

HPROPSHEETPAGE CreateEnvVarsPage (HINSTANCE hInst)
{
    PROPSHEETPAGE psp;

    psp.dwSize = SIZEOF(PROPSHEETPAGE);
    psp.dwFlags = 0;
    psp.hInstance = hInst;
    psp.pszTemplate = MAKEINTRESOURCE(IDD_ENVVARS);
    psp.pfnDlgProc = EnvVarsDlgProc;
    psp.pszTitle = NULL;
    psp.lParam = 0;

    return CreatePropertySheetPage(&psp);
}

//*************************************************************
//
//  InitEnvVarsDlg()
//
//  Purpose:    Initializes the environment variables page
//
//  Parameters: hDlg    -   dialog box handle
//
//  Return:     (BOOL) TRUE if successful
//                     FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              11/25/95    ericflo    Created
//
//*************************************************************

BOOL InitEnvVarsDlg (HWND hDlg)
{
    TCHAR szBuffer1[200];
    TCHAR szBuffer2[300];
    TCHAR szUserName[MAX_USER_NAME];
    DWORD dwSize = MAX_USER_NAME;
    HWND hwndTemp;
    HKEY hkeyEnv;
    TCHAR  *pszValue;
    HANDLE hKey;
    DWORD dwBufz, dwValz, dwIndex, dwType;
    LONG Error;
    TCHAR   szTemp[MAX_PATH];
    LPTSTR  pszString;
    ENVARS *penvar;
    int     n;
    LV_COLUMN col;
    LV_ITEM item;
    RECT rect;
    int cxFirstCol;


    HourGlass (TRUE);


    //
    // Create the first column
    //

    LoadString (hInstance, SYSTEM + 50, szBuffer1, 200);

    if (!GetClientRect (GetDlgItem(hDlg, IDC_ENVVAR_SYS_LB_SYSVARS), &rect)) {
        rect.right = 300;
    }

    cxFirstCol = (int)(rect.right * .3);

    col.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
    col.fmt = LVCFMT_LEFT;
    col.cx = cxFirstCol;
    col.pszText = szBuffer1;
    col.iSubItem = 0;

    SendDlgItemMessage (hDlg, IDC_ENVVAR_SYS_LB_SYSVARS, LVM_INSERTCOLUMN,
                        0, (LPARAM) &col);

    SendDlgItemMessage (hDlg, IDC_ENVVAR_SYS_LB_USERVARS, LVM_INSERTCOLUMN,
                        0, (LPARAM) &col);


    //
    // Create the second column
    //

    LoadString (hInstance, SYSTEM + 51, szBuffer1, 200);

    col.cx = rect.right - cxFirstCol - GetSystemMetrics(SM_CYHSCROLL);
    col.iSubItem = 1;

    SendDlgItemMessage (hDlg, IDC_ENVVAR_SYS_LB_SYSVARS, LVM_INSERTCOLUMN,
                        1, (LPARAM) &col);

    SendDlgItemMessage (hDlg, IDC_ENVVAR_SYS_LB_USERVARS, LVM_INSERTCOLUMN,
                        1, (LPARAM) &col);


    ////////////////////////////////////////////////////////////////////
    // Display System Variables from registry in listbox
    ////////////////////////////////////////////////////////////////////

    hwndTemp = GetDlgItem (hDlg, IDC_ENVVAR_SYS_LB_SYSVARS);

    hKey = MemAlloc (LPTR, BUFZ*SIZEOF(TCHAR));
    pszString = (LPTSTR) MemAlloc (LPTR, BUFZ*sizeof(TCHAR));

    bEditSystemVars = FALSE;
    cxLBSysVars = 0;
    hkeyEnv = NULL;

    //  Try to open the System Environment variables area with
    //  Read AND Write permission.  If successful, then we allow
    //  the User to edit them the same as their own variables

    if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, szSysEnv, 0, KEY_READ | KEY_WRITE, &hkeyEnv) != ERROR_SUCCESS) {

        //  On failure, just try to open it for reading
        if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, szSysEnv, 0, KEY_READ, &hkeyEnv) != ERROR_SUCCESS) {
            hkeyEnv = NULL;
        }

    } else {

        bEditSystemVars = TRUE;
    }

    if (hkeyEnv)
    {
        pszValue = (TCHAR *) hKey;
        dwBufz = ARRAYSIZE(szTemp);
        dwValz = BUFZ * SIZEOF(TCHAR);
        dwIndex = 0;

        //  Read all values until an error is encountered

        while (!RegEnumValue(hkeyEnv,
                             dwIndex++, // Index'th value name/data
                             szTemp,    // Ptr to ValueName buffer
                             &dwBufz,   // Size of ValueName buffer
                             NULL,      // Title index return
                             &dwType,   // Type code of entry
                    (LPBYTE) pszValue,  // Ptr to ValueData buffer
                             &dwValz))  // Size of ValueData buffer
        {
            if ((dwType != REG_SZ) && (dwType != REG_EXPAND_SZ))
                goto SysLoop;

            //
            //  Clip length of returned Environment variable string
            //  to MAX_VALUE_LEN-1, as necessary.
            //

            pszValue[MAX_VALUE_LEN-1] = TEXT('\0');

            ExpandEnvironmentStrings (pszValue, pszString, BUFZ);

            penvar = (ENVARS *) MemAlloc (LPTR, SIZEOF(ENVARS));

            penvar->dwType      = dwType;
            penvar->szValueName = CloneString( szTemp );
            penvar->szValue     = CloneString( pszValue );
            penvar->szExpValue  = CloneString( pszString );


            item.mask = LVIF_TEXT | LVIF_PARAM;
            item.iItem = (dwIndex - 1);
            item.iSubItem = 0;
            item.pszText = penvar->szValueName;
            item.lParam = (LPARAM) penvar;

            n = SendMessage (hwndTemp, LVM_INSERTITEM, 0, (LPARAM) &item);

            if (n != -1) {
                item.mask = LVIF_TEXT;
                item.iItem = n;
                item.iSubItem = 1;
                item.pszText = penvar->szExpValue;

                SendMessage (hwndTemp, LVM_SETITEMTEXT, n, (LPARAM) &item);
            }

SysLoop:
            //  Reset vars for next iteration

            dwBufz = ARRAYSIZE(szTemp);
            dwValz = BUFZ * SIZEOF(TCHAR);
        }
        RegCloseKey (hkeyEnv);
    }


    ////////////////////////////////////////////////////////////////////
    //  Display USER variables from registry in listbox
    ////////////////////////////////////////////////////////////////////


    //
    // Set the "User Environments for <username>" string
    //

    if (GetUserName(szUserName, &dwSize) &&
        LoadString (hInstance, IDS_USERENVVARS, szBuffer1, 200)) {

        wsprintf (szBuffer2, szBuffer1, szUserName);
        SetDlgItemText (hDlg, IDC_ENVVAR_SYS_USERENV, szBuffer2);
    }


    Error = RegCreateKey (HKEY_CURRENT_USER, szUserEnv, &hkeyEnv);

    if (Error == ERROR_SUCCESS)
    {
        hwndTemp = GetDlgItem (hDlg, IDC_ENVVAR_SYS_LB_USERVARS);

        pszValue = (TCHAR *) hKey;
        dwBufz = ARRAYSIZE(szTemp);
        dwValz = BUFZ * SIZEOF(TCHAR);
        dwIndex = 0;


        //  Read all values until an error is encountered

        while (!RegEnumValue(hkeyEnv,
                             dwIndex++, // Index'th value name/data
                             szTemp,    // Ptr to ValueName buffer
                             &dwBufz,   // Size of ValueName buffer
                             NULL,      // Title index return
                             &dwType,   // Type code of entry
                    (LPBYTE) pszValue,  // Ptr to ValueData buffer
                             &dwValz))  // Size of ValueData buffer
        {
            if ((dwType != REG_SZ) && (dwType != REG_EXPAND_SZ))
                goto UserLoop;

            //
            //  Clip length of returned Environment variable string
            //  to MAX_VALUE_LEN-1, as necessary.
            //

            pszValue[MAX_VALUE_LEN-1] = TEXT('\0');

            ExpandEnvironmentStrings (pszValue, pszString, BUFZ);

            penvar = (ENVARS *) MemAlloc (LPTR, sizeof(ENVARS));

            penvar->dwType      = dwType;
            penvar->szValueName = CloneString (szTemp);
            penvar->szValue     = CloneString (pszValue);
            penvar->szExpValue  = CloneString (pszString);

            item.mask = LVIF_TEXT | LVIF_PARAM;
            item.iItem = (dwIndex - 1);
            item.iSubItem = 0;
            item.pszText = penvar->szValueName;
            item.lParam = (LPARAM) penvar;

            n = SendMessage (hwndTemp, LVM_INSERTITEM, 0, (LPARAM) &item);

            if (n != -1) {
                item.mask = LVIF_TEXT;
                item.iItem = n;
                item.iSubItem = 1;
                item.pszText = penvar->szExpValue;

                SendMessage (hwndTemp, LVM_SETITEMTEXT, n, (LPARAM) &item);
            }

UserLoop:
            //  Reset vars for next iteration

            dwBufz = ARRAYSIZE(szTemp);
            dwValz = BUFZ * SIZEOF(TCHAR);

        }
        RegCloseKey (hkeyEnv);
    }
    else
    {
        //  Report opening USER Environment key
        if (MsgBoxParam (hDlg, SYSTEM+8, INITS+1,
                          MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
        {
            //  Free allocated memory since we are returning from here
            MemFree ((LPVOID)hKey);
            MemFree (pszString);

            HourGlass (FALSE);
            return FALSE;
        }
    }

    //
    // Select the first items in the listviews
    // It is important to set the User listview first, and
    // then the system.  When the system listview is set,
    // we will receive a LVN_ITEMCHANGED notification and
    // clear the focus in the User listview.  But when someone
    // tabs to the control the arrow keys will work correctly.
    //

    item.mask = LVIF_STATE;
    item.iItem = 0;
    item.iSubItem = 0;
    item.state = LVIS_SELECTED | LVIS_FOCUSED;
    item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;

    SendDlgItemMessage (hDlg, IDC_ENVVAR_SYS_LB_USERVARS,
                        LVM_SETITEMSTATE, 0, (LPARAM) &item);

    SendDlgItemMessage (hDlg, IDC_ENVVAR_SYS_LB_SYSVARS,
                        LVM_SETITEMSTATE, 0, (LPARAM) &item);


    // EM_LIMITTEXT of VARIABLE and VALUE editbox

    SendDlgItemMessage (hDlg, IDC_ENVVAR_SYS_VAR, EM_LIMITTEXT, MAX_PATH-1, 0L);
    SendDlgItemMessage (hDlg, IDC_ENVVAR_SYS_VALUE, EM_LIMITTEXT, MAX_VALUE_LEN-1, 0L);

    //  Remove text from Editboxes
    SetDlgItemText (hDlg, IDC_ENVVAR_SYS_VAR, g_szNull);
    SetDlgItemText (hDlg, IDC_ENVVAR_SYS_VALUE, g_szNull);


    EnableWindow (GetDlgItem (hDlg, IDC_ENVVAR_SYS_SETUV), FALSE);
    EnableWindow (GetDlgItem (hDlg, IDC_ENVVAR_SYS_DELUV), FALSE);

    MemFree ((LPVOID)hKey);
    MemFree (pszString);

    // Set extended LV style for whole line selection
    SendDlgItemMessage(hDlg, IDC_ENVVAR_SYS_LB_SYSVARS, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
    SendDlgItemMessage(hDlg, IDC_ENVVAR_SYS_LB_USERVARS, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);

    HourGlass (FALSE);


    ///////////////////
    // Return succes //
    ///////////////////
    return TRUE;
}


//*************************************************************
//
//  EnvVarsDlgProc()
//
//  Purpose:    Dialog box procedure for Environment Variables tab
//
//  Parameters: hDlg    -   handle to the dialog box
//              uMsg    -   window message
//              wParam  -   wParam
//              lParam  -   lParam
//
//  Return:     TRUE if message was processed
//              FALSE if not
//
//  Comments:
//
//  History:    Date        Author     Comment
//              11/21/95    ericflo    Created
//
//*************************************************************

BOOL APIENTRY EnvVarsDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

    switch (uMsg)
    {
    case WM_INITDIALOG:

        if (!InitEnvVarsDlg(hDlg)) {
            EndDialog (hDlg, 0);
        }
        break;


    case WM_NOTIFY:

        switch (((NMHDR FAR*)lParam)->code)
        {
        case LVN_ITEMCHANGED:
            EVDoItemChanged (hDlg, (int)wParam);
            break;

        case LVN_COLUMNCLICK:
            EVDoItemChanged (hDlg, (int) wParam);
            break;

        case NM_SETFOCUS:
            if (wParam == IDC_ENVVAR_SYS_LB_USERVARS) {
                bUserVars = TRUE;
            } else {
                bUserVars = FALSE;
            }
            EVDoItemChanged (hDlg, (int) wParam);
            break;


        case PSN_APPLY:
            {
            PSHNOTIFY *lpNotify = (PSHNOTIFY *) lParam;

            EVSave(hDlg);

            SetWindowLong (hDlg, DWL_MSGRESULT, PSNRET_NOERROR);
            return TRUE;
            }


        case PSN_RESET:
            SetWindowLong (hDlg, DWL_MSGRESULT, PSNRET_NOERROR);
            return TRUE;

        default:
            return FALSE;
        }
        break;


    case WM_COMMAND:
        EVDoCommand(hDlg, (HWND)lParam, LOWORD(wParam), HIWORD(wParam));
        break;

    case WM_DESTROY:
        EVCleanUp (hDlg);
        break;

    case WM_HELP:      // F1
        WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, HELP_FILE, HELP_WM_HELP, (DWORD) (LPSTR) aEnvVarsHelpIds);
        break;

    case WM_CONTEXTMENU:      // right mouse click
        WinHelp((HWND) wParam, HELP_FILE, HELP_CONTEXTMENU, (DWORD) (LPSTR) aEnvVarsHelpIds);
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

////////////////////////////////////////////////////////////////////////////
//  EVDoCommand
//
//  Process commands for the Environment Vars page of the system app
//
//  History:
//  10-Jan-1996 JonPa   Started it
//  18-Jan-1996 EricFlo Finished it
////////////////////////////////////////////////////////////////////////////
void EVDoCommand(HWND hDlg, HWND hwndCtl, int idCtl, int iNotify )
{
    TCHAR   szTemp2[MAX_PATH];
    int     i, n;
    TCHAR  *bBuffer;
    TCHAR  *pszTemp;
    LPTSTR  pszString;
    HWND    hwndTemp;
    ENVARS *penvar;
    LV_ITEM item;

    switch (idCtl) {

        case IDC_ENVVAR_SYS_VALUE:
        case IDC_ENVVAR_SYS_VAR:

            //  IF focus is being set to one of these controls, enable
            //  new buttons as appropriate
            //  ELSE allow "Enter" key to simply choose the IDOK button

            //  If the USER activates or clicks in either Variable or Value
            //  editbox, then change "Set" to the DefPushbutton


            if (iNotify == EN_SETFOCUS)
            {
                if (GetDlgItemText (hDlg, IDC_ENVVAR_SYS_VAR, szTemp2, ARRAYSIZE(szTemp2)))
                {
                    EnableWindow (GetDlgItem (hDlg, IDC_ENVVAR_SYS_SETUV), TRUE);
                    EnableWindow (GetDlgItem (hDlg, IDC_ENVVAR_SYS_DELUV), TRUE);
                    SetDefButton (hDlg, IDC_ENVVAR_SYS_SETUV);
                }
                else
                {
                    EnableWindow (GetDlgItem (hDlg, IDC_ENVVAR_SYS_SETUV), FALSE);
                    EnableWindow (GetDlgItem (hDlg, IDC_ENVVAR_SYS_DELUV), FALSE);
                    SetDefButton (GetParent(hDlg), IDOK);
                }
            }
            else if (iNotify == EN_KILLFOCUS)
            {
                SetDefButton (GetParent(hDlg), IDOK);
            }
            break;




        case IDC_ENVVAR_SYS_DELUV:
            // Delete listbox entry that matches value in IDC_ENVVAR_SYS_VAR
            //  If found, delete entry else ignore

            GetDlgItemText (hDlg, IDC_ENVVAR_SYS_VAR, szTemp2, ARRAYSIZE(szTemp2));

            if (szTemp2[0] == TEXT('\0'))
                break;

            //  Determine which Listbox is active (SYSTEM or USER vars)

            hwndTemp = GetDlgItem (hDlg, bUserVars ? IDC_ENVVAR_SYS_LB_USERVARS :
                                                     IDC_ENVVAR_SYS_LB_SYSVARS);

            n = FindVar (hwndTemp, szTemp2);

            if (n != -1)
            {
                // Free existing strings (listbox and ours)

                item.mask = LVIF_PARAM;
                item.iItem = n;
                item.iSubItem = 0;


                if (SendMessage (hwndTemp, LVM_GETITEM, 0, (LPARAM) &item)) {
                    penvar = (ENVARS *) item.lParam;

                } else {
                    penvar = NULL;
                }


                if (penvar) {
                    MemFree (penvar->szValueName);
                    MemFree (penvar->szValue);
                    MemFree (penvar->szExpValue);
                    MemFree ((LPVOID) penvar);
                }

                SendMessage (hwndTemp, LVM_DELETEITEM, n, 0L);
                PropSheet_Changed(GetParent(hDlg), hDlg);

                //  Fix selection state in listview
                if (n > 0) {
                    n--;
                }

                item.mask = LVIF_STATE;
                item.iItem = n;
                item.iSubItem = 0;
                item.state = LVIS_SELECTED | LVIS_FOCUSED;
                item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;

                SendDlgItemMessage (hDlg, IDC_ENVVAR_SYS_LB_USERVARS,
                                    LVM_SETITEMSTATE, n, (LPARAM) &item);


                //  Remove text from Editboxes
                SetDlgItemText (hDlg, IDC_ENVVAR_SYS_VAR, g_szNull);
                SetDlgItemText (hDlg, IDC_ENVVAR_SYS_VALUE, g_szNull);

                //  Disable useless controls
                EnableWindow (GetDlgItem (hDlg, IDC_ENVVAR_SYS_SETUV), FALSE);
                EnableWindow (GetDlgItem (hDlg, IDC_ENVVAR_SYS_DELUV), FALSE);

                //  Reset OK as "DefPushbutton" and re-enable keybd input
                SetDefButton (GetParent(hDlg), IDOK);
                SetFocus (GetDlgItem (GetParent(hDlg), IDOK));
            }
            break;

        case IDC_ENVVAR_SYS_SETUV:

            //  Set the Environment variable in IDC_ENVVAR_SYS_VAR
            //  Also add or change the registry entry

            GetDlgItemText (hDlg, IDC_ENVVAR_SYS_VAR, szTemp2, ARRAYSIZE(szTemp2));

            //  Strip trailing whitespace from end of Env Variable

            i = lstrlen(szTemp2) - 1;

            while (i >= 0)
            {
                if (_istspace(szTemp2[i]))
                    szTemp2[i--] = TEXT('\0');
                else
                    break;
            }

            if (szTemp2[0] == TEXT('\0'))
                break;

            bBuffer = (TCHAR *) MemAlloc (LPTR, BUFZ * sizeof(TCHAR));
            pszString = (LPTSTR) MemAlloc (LPTR, BUFZ * sizeof(TCHAR));

            GetDlgItemText (hDlg, IDC_ENVVAR_SYS_VALUE, bBuffer, BUFZ);

            //  Determine which Listbox is active (SYSTEM or USER vars)

            hwndTemp = GetDlgItem (hDlg, bUserVars ? IDC_ENVVAR_SYS_LB_USERVARS :
                                                     IDC_ENVVAR_SYS_LB_SYSVARS);

            n = FindVar (hwndTemp, szTemp2);

            if (n != -1)
            {
                // Free existing strings (listview and ours)

                item.mask = LVIF_PARAM;
                item.iItem = n;
                item.iSubItem = 0;

                if (SendMessage (hwndTemp, LVM_GETITEM, 0, (LPARAM) &item)) {
                    penvar = (ENVARS *) item.lParam;

                } else {
                    penvar = NULL;
                }


                if (penvar) {
                    MemFree (penvar->szValueName);
                    MemFree (penvar->szValue);
                    MemFree (penvar->szExpValue);
                }

                SendMessage (hwndTemp, LVM_DELETEITEM, n, 0L);
            }
            else
            {
                //  Get some storage for new Env Var
                penvar = (ENVARS *) MemAlloc (LPTR, sizeof(ENVARS));
            }

            //  If there are two '%' chars in string, then this is a
            //  REG_EXPAND_SZ style environment string

            pszTemp = _tcspbrk (bBuffer, TEXT("%"));

            if (pszTemp && _tcspbrk (pszTemp, TEXT("%")))
                penvar->dwType = REG_EXPAND_SZ;
            else
                penvar->dwType = REG_SZ;

            ExpandEnvironmentStrings (bBuffer, pszString, BUFZ);

            penvar->szValueName = CloneString (szTemp2);
            penvar->szValue     = CloneString (bBuffer);
            penvar->szExpValue  = CloneString (pszString);


            item.mask = LVIF_TEXT | LVIF_PARAM;
            item.iItem = ListView_GetItemCount(hwndTemp);
            item.iSubItem = 0;
            item.pszText = penvar->szValueName;
            item.lParam = (LPARAM) penvar;

            n = SendMessage (hwndTemp, LVM_INSERTITEM, 0, (LPARAM) &item);

            if (n != -1) {
                item.mask = LVIF_TEXT;
                item.iItem = n;
                item.iSubItem = 1;
                item.pszText = penvar->szExpValue;

                SendMessage (hwndTemp, LVM_SETITEMTEXT, n, (LPARAM) &item);
                PropSheet_Changed(GetParent(hDlg), hDlg);

                //  Set selection state to new item
                ClearAllSelections(hwndTemp);

                item.mask = LVIF_STATE;
                item.iItem = n;
                item.iSubItem = 0;
                item.state = LVIS_SELECTED | LVIS_FOCUSED;
                item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;

                SendDlgItemMessage (hDlg, IDC_ENVVAR_SYS_LB_USERVARS,
                                    LVM_SETITEMSTATE, n, (LPARAM) &item);
            }


            //  Remove text from Editboxes after add
            SetDlgItemText (hDlg, IDC_ENVVAR_SYS_VAR, g_szNull);
            SetDlgItemText (hDlg, IDC_ENVVAR_SYS_VALUE, g_szNull);

            MemFree (bBuffer);
            MemFree (pszString);

            // Set user input back to VARIABLE field
            SetFocus (GetDlgItem (hDlg, IDC_ENVVAR_SYS_VAR));

            break;


        default:
            break;
    }
}

void ClearAllSelections (HWND hCtrl)
{
    int i,n;
    LV_ITEM item;

    item.mask = LVIF_STATE;
    item.iSubItem = 0;
    item.state = 0;
    item.stateMask = LVIS_FOCUSED;

    n = SendMessage (hCtrl, LVM_GETITEMCOUNT, 0, 0L);

    if (n != LB_ERR)
    {
        for (i = 0; i < n; i++)
        {
            item.iItem = i;
            SendMessage (hCtrl, LVM_SETITEMSTATE, i, (LPARAM) &item);
        }
    }
}

int GetSelectedItem (HWND hCtrl)
{
    int i, n;

    n = SendMessage (hCtrl, LVM_GETITEMCOUNT, 0, 0L);

    if (n != LB_ERR)
    {
        for (i = 0; i < n; i++)
        {
            if (SendMessage (hCtrl, LVM_GETITEMSTATE,
                             i, (LPARAM) LVIS_SELECTED) == LVIS_SELECTED) {
                return i;
            }
        }
    }

    return -1;
}



////////////////////////////////////////////////////////////////////////////
//  EVDoItemChanged
//
//  Process notify's for the Environment Vars page of the system app
//
//  History:
//  19-Jan-1996 EricFlo Finished it
////////////////////////////////////////////////////////////////////////////
void EVDoItemChanged(HWND hDlg, int idCtl)
{
    int     selection;
    HWND    hwndTemp;
    ENVARS *penvar;
    LV_ITEM item;

    switch (idCtl) {

        case IDC_ENVVAR_SYS_LB_SYSVARS:
            if (!bEditSystemVars)
                return;

            /* Fall through */

        case IDC_ENVVAR_SYS_LB_USERVARS:

            hwndTemp = GetDlgItem (hDlg, idCtl);

            //
            //  Clear the selection from the other listbox
            //  so the user doens't have to figure out which
            //  one he is editing
            //

            ClearAllSelections (GetDlgItem(hDlg,
                               (idCtl == IDC_ENVVAR_SYS_LB_USERVARS) ?
                               IDC_ENVVAR_SYS_LB_SYSVARS : IDC_ENVVAR_SYS_LB_USERVARS));


            selection = GetSelectedItem (hwndTemp);

            if (selection != -1)
            {
                item.mask = LVIF_PARAM;
                item.iItem = selection;
                item.iSubItem = 0;

                if (SendMessage (hwndTemp, LVM_GETITEM, 0, (LPARAM) &item)) {
                    penvar = (ENVARS *) item.lParam;

                } else {
                    penvar = NULL;
                }

                if (penvar) {
                    SetDlgItemText (hDlg, IDC_ENVVAR_SYS_VAR, penvar->szValueName);
                    SetDlgItemText (hDlg, IDC_ENVVAR_SYS_VALUE, penvar->szValue);

                    //  Enable DELETE button
                    EnableWindow (GetDlgItem (hDlg, IDC_ENVVAR_SYS_DELUV), TRUE);
                }
            }
            else
            {
                //  Else  we are only deselecting an item so...
                //  simply remove all text from Editboxes
                SetDlgItemText (hDlg, IDC_ENVVAR_SYS_VAR, g_szNull);
                SetDlgItemText (hDlg, IDC_ENVVAR_SYS_VALUE, g_szNull);

                //  Disable buttons
                EnableWindow (GetDlgItem (hDlg, IDC_ENVVAR_SYS_DELUV), FALSE);
                EnableWindow (GetDlgItem (hDlg, IDC_ENVVAR_SYS_SETUV), FALSE);
            }
            break;
    }
}




////////////////////////////////////////////////////////////////////////////
//  SetLBWidthEx
//
//  Set the width of the listbox, in pixels, acording to the size of the
//  string passed in.
//
//  Note: this function is also used by the Virtual Memory dialog
//
//  History:
//  11-Jan-1996 JonPa   Created from SetGenLBWidth
////////////////////////////////////////////////////////////////////////////

DWORD SetLBWidthEx (HWND hwndLB, LPTSTR szBuffer, DWORD cxCurWidth, DWORD cxExtra)
{
    HDC     hDC;
    SIZE    Size;
    LONG    cx;
    HFONT   hfont, hfontOld;

    // Get the new Win4.0 thin dialog font
    hfont = (HFONT)SendMessage(hwndLB, WM_GETFONT, 0, 0);

    hDC = GetDC(hwndLB);

    // if we got a font back, select it in this clean hDC
    if (hfont != NULL)
        hfontOld = SelectObject(hDC, hfont);


    // If cxExtra is 0, then give our selves a little breathing space.
    if (cxExtra == 0) {
        GetTextExtentPoint(hDC, TEXT("1234"), 4 /* lstrlen("1234") */, &Size);
        cxExtra = Size.cx;
    }

    // Set scroll width of listbox

    GetTextExtentPoint(hDC, szBuffer, lstrlen(szBuffer), &Size);

    Size.cx += cxExtra;

    // Get the name length and adjust the longest name

    if ((DWORD) Size.cx > cxCurWidth)
    {
        cxCurWidth = Size.cx;
        SendMessage (hwndLB, LB_SETHORIZONTALEXTENT, (DWORD)Size.cx, 0L);
    }

    // retstore the original font if we changed it
    if (hfont != NULL)
        SelectObject(hDC, hfontOld);

    ReleaseDC(NULL, hDC);

    return cxCurWidth;
}

/*************************************************************************\
*
* LPTSTR CloneString( LPTSTR pszSrc );
*
* Makes a copy of a string.  The new string is copyied to a newly alloced
* buffer and that buffer is returned
*
\*************************************************************************/

LPTSTR CloneString( LPTSTR pszSrc ) {
    LPTSTR pszDst = NULL;

    if (pszSrc != NULL) {
        pszDst = MemAlloc(LMEM_FIXED, (lstrlen(pszSrc)+1) * SIZEOF(TCHAR));
        if (pszDst) {
            lstrcpy( pszDst, pszSrc );
        }
    }

    return pszDst;
}
////////////////////////////////////////////////////////////////////////////
//  FindVar
//
//  Find the USER Environment variable that matches passed string
//  and return its listview index or -1
//
////////////////////////////////////////////////////////////////////////////

int FindVar (HWND hwndLV, LPTSTR szVar)
{
    LV_FINDINFO FindInfo;


    FindInfo.flags = LVFI_STRING;
    FindInfo.psz = szVar;

    return (SendMessage (hwndLV, LVM_FINDITEM, (WPARAM) -1, (LPARAM) &FindInfo));
}

////////////////////////////////////////////////////////////////////////////
//  EVSave
//
//  Saves the environment variables
//
//  History:
//  19-Jan-1996 EricFlo Wrote it
////////////////////////////////////////////////////////////////////////////
void EVSave(HWND hDlg)
{
    TCHAR   szTemp[MAX_PATH];
    int     selection;
    int     i, n;
    TCHAR  *bBuffer;
    TCHAR  *pszTemp;
    LPTSTR  pszString;
    HWND    hwndTemp;
    ENVARS *penvar;
    REGVAL *prvFirst;
    REGVAL *prvRegVal;
    HKEY    hkeyEnv;
    DWORD   dwBufz, dwIndex, dwType;
    LV_ITEM item;

    HourGlass (TRUE);

    /////////////////////////////////////////////////////////////////
    //  Set all new USER environment variables to current values
    //  but delete all old environment variables first
    /////////////////////////////////////////////////////////////////

    if (RegOpenKeyEx (HKEY_CURRENT_USER, szUserEnv, 0,
                     KEY_READ | KEY_WRITE, &hkeyEnv)
            == ERROR_SUCCESS)
    {
        dwBufz = ARRAYSIZE(szTemp) * sizeof(TCHAR);
        dwIndex = 0;

        //  Delete all values of type REG_SZ & REG_EXPAND_SZ under key

        //  First: Make a linked list of all USER Env string vars

        prvFirst = (REGVAL *) NULL;

        while (!RegEnumValue(hkeyEnv,
                             dwIndex++, // Index'th value name/data
                             szTemp,    // Ptr to ValueName buffer
                             &dwBufz,   // Size of ValueName buffer
                             NULL,      // Title index return
                             &dwType,   // Type code of entry
                             NULL,      // Ptr to ValueData buffer
                             NULL))     // Size of ValueData buffer
        {
            if ((dwType != REG_SZ) && (dwType != REG_EXPAND_SZ))
                continue;

            if (prvFirst)
            {
                prvRegVal->prvNext = (REGVAL *) MemAlloc (LPTR, sizeof(REGVAL));
                prvRegVal = prvRegVal->prvNext;
            }
            else        // First time thru
            {
                prvFirst = prvRegVal = (REGVAL *) MemAlloc (LPTR, sizeof(REGVAL));
            }

            prvRegVal->prvNext = NULL;
            prvRegVal->szValueName = CloneString (szTemp);

            // Reset vars for next call

            dwBufz = ARRAYSIZE(szTemp) * sizeof(TCHAR);
        }

        //  Now traverse the list, deleting them all

        prvRegVal = prvFirst;

        while (prvRegVal)
        {
            RegDeleteValue (hkeyEnv, prvRegVal->szValueName);

            MemFree (prvRegVal->szValueName);

            prvFirst  = prvRegVal;
            prvRegVal = prvRegVal->prvNext;

            MemFree ((LPVOID) prvFirst);
        }

        ///////////////////////////////////////////////////////////////
        //  Set all new USER environment variables to current values
        ///////////////////////////////////////////////////////////////

        hwndTemp = GetDlgItem (hDlg, IDC_ENVVAR_SYS_LB_USERVARS);

        if ((n = SendMessage (hwndTemp, LVM_GETITEMCOUNT, 0, 0L)) != LB_ERR)
        {

            item.mask = LVIF_PARAM;
            item.iSubItem = 0;

            for (i = 0; i < n; i++)
            {

                item.iItem = i;

                if (SendMessage (hwndTemp, LVM_GETITEM, 0, (LPARAM) &item)) {
                    penvar = (ENVARS *) item.lParam;

                } else {
                    penvar = NULL;
                }

                if (penvar) {
                    if (RegSetValueEx (hkeyEnv,
                                       penvar->szValueName,
                                       0L,
                                       penvar->dwType,
                              (LPBYTE) penvar->szValue,
                                       (lstrlen (penvar->szValue)+1) * sizeof(TCHAR)))
                    {
                        //  Report error trying to set registry values

                        if (MsgBoxParam (hDlg, SYSTEM+9, INITS+1,
                            MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
                            break;
                    }
                }
            }
        }

        RegFlushKey (hkeyEnv);
        RegCloseKey (hkeyEnv);
    }
    else
    {
        //  Report opening USER Environment key
        if (MsgBoxParam (hDlg, SYSTEM+8, INITS+1,
                       MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
            goto Exit;
    }

    /////////////////////////////////////////////////////////////////
    //  Set all new SYSTEM environment variables to current values
    //  but delete all old environment variables first
    /////////////////////////////////////////////////////////////////

    if (!bEditSystemVars)
        goto SkipSystemVars;

    if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                       szSysEnv,
                       0,
                       KEY_READ | KEY_WRITE,
                       &hkeyEnv)
            == ERROR_SUCCESS)
    {
        dwBufz = ARRAYSIZE(szTemp) * sizeof(TCHAR);
        dwIndex = 0;

        //  Delete all values of type REG_SZ & REG_EXPAND_SZ under key

        //  First: Make a linked list of all Env string vars

        prvFirst = (REGVAL *) NULL;

        while (!RegEnumValue(hkeyEnv,
                             dwIndex++, // Index'th value name/data
                             szTemp,    // Ptr to ValueName buffer
                             &dwBufz,   // Size of ValueName buffer
                             NULL,      // Title index return
                             &dwType,   // Type code of entry
                             NULL,      // Ptr to ValueData buffer
                             NULL))     // Size of ValueData buffer
        {
            if ((dwType != REG_SZ) && (dwType != REG_EXPAND_SZ))
                continue;

            if (prvFirst)
            {
                prvRegVal->prvNext = (REGVAL *) MemAlloc (LPTR, sizeof(REGVAL));
                prvRegVal = prvRegVal->prvNext;
            }
            else        // First time thru
            {
                prvFirst = prvRegVal = (REGVAL *) MemAlloc (LPTR, sizeof(REGVAL));
            }

            prvRegVal->prvNext = NULL;
            prvRegVal->szValueName = CloneString (szTemp);

            // Reset vars for next call

            dwBufz = ARRAYSIZE(szTemp) * sizeof(TCHAR);
        }

        //  Now traverse the list, deleting them all

        prvRegVal = prvFirst;

        while (prvRegVal)
        {
            RegDeleteValue (hkeyEnv, prvRegVal->szValueName);

            MemFree (prvRegVal->szValueName);

            prvFirst  = prvRegVal;
            prvRegVal = prvRegVal->prvNext;

            MemFree ((LPVOID) prvFirst);
        }

        ///////////////////////////////////////////////////////////////
        //  Set all new SYSTEM environment variables to current values
        ///////////////////////////////////////////////////////////////

        hwndTemp = GetDlgItem (hDlg, IDC_ENVVAR_SYS_LB_SYSVARS);

        if ((n = SendMessage (hwndTemp, LVM_GETITEMCOUNT, 0, 0L)) != LB_ERR)
        {
            item.mask = LVIF_PARAM;
            item.iSubItem = 0;

            for (i = 0; i < n; i++)
            {
                item.iItem = i;

                if (SendMessage (hwndTemp, LVM_GETITEM, 0, (LPARAM) &item)) {
                    penvar = (ENVARS *) item.lParam;

                } else {
                    penvar = NULL;
                }

                if (penvar) {
                    if (RegSetValueEx (hkeyEnv,
                                       penvar->szValueName,
                                       0L,
                                       penvar->dwType,
                              (LPBYTE) penvar->szValue,
                                       (lstrlen (penvar->szValue)+1) * sizeof(TCHAR)))
                    {
                        //  Report error trying to set registry values

                        if (MsgBoxParam (hDlg, SYSTEM+9, INITS+1,
                            MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
                            break;
                    }
                }
            }
        }

        RegFlushKey (hkeyEnv);
        RegCloseKey (hkeyEnv);
    }
    else
    {
        //  Report opening SYSTEM Environment key
        if (MsgBoxParam (hDlg, SYSTEM+21, INITS+1,
                       MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
            goto Exit;
    }

SkipSystemVars:

    // Send public message announcing change to Environment
    SendMessageTimeout( (HWND)-1, WM_WININICHANGE, 0L, (LONG)szUserEnv,
                                            SMTO_ABORTIFHUNG, 1000, NULL );


Exit:

    HourGlass (FALSE);
}


////////////////////////////////////////////////////////////////////////////
//  EVCleanUp
//
//  Frees memory allocated for environment variables
//
//  History:
//  19-Jan-1996 EricFlo Wrote it
////////////////////////////////////////////////////////////////////////////
void EVCleanUp (HWND hDlg)
{
    int     i, n;
    HWND    hwndTemp;
    ENVARS *penvar;
    LV_ITEM item;


    //
    //  Free alloc'd strings and memory for UserEnvVars list box items
    //

    hwndTemp = GetDlgItem (hDlg, IDC_ENVVAR_SYS_LB_USERVARS);
    n = SendMessage (hwndTemp, LVM_GETITEMCOUNT, 0, 0L);

    item.mask = LVIF_PARAM;
    item.iSubItem = 0;

    for (i = 0; i < n; i++) {

        item.iItem = i;

        if (SendMessage (hwndTemp, LVM_GETITEM, 0, (LPARAM) &item)) {
            penvar = (ENVARS *) item.lParam;

        } else {
            penvar = NULL;
        }

        if (penvar) {
            MemFree (penvar->szValueName);
            MemFree (penvar->szValue);
            MemFree (penvar->szExpValue);
            MemFree ((LPVOID) penvar);
        }
    }


    //
    //  Free alloc'd strings and memory for SysEnvVars list box items
    //

    hwndTemp = GetDlgItem (hDlg, IDC_ENVVAR_SYS_LB_SYSVARS);
    n = SendMessage (hwndTemp, LVM_GETITEMCOUNT, 0, 0L);

    for (i = 0; i < n; i++) {

        item.iItem = i;

        if (SendMessage (hwndTemp, LVM_GETITEM, 0, (LPARAM) &item)) {
            penvar = (ENVARS *) item.lParam;

        } else {
            penvar = NULL;
        }

        if (penvar) {
            MemFree (penvar->szValueName);
            MemFree (penvar->szValue);
            MemFree (penvar->szExpValue);
            MemFree ((LPVOID) penvar);
        }
    }
}