/*
 *  ReactOS applications
 *  Copyright (C) 2001, 2002 ReactOS Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
/*
 * COPYRIGHT:   See COPYING in the top level directory
 * PROJECT:     ReactOS Userinit Logon Application
 * FILE:        base/system/userinit/userinit.c
 * PROGRAMMERS: Thomas Weidenmueller (w3seek@users.sourceforge.net)
 *              Herv� Poussineau (hpoussin@reactos.org)
 */

#include "userinit.h"

#define CMP_MAGIC  0x01234567

/* GLOBALS ******************************************************************/

HINSTANCE hInstance;


/* FUNCTIONS ****************************************************************/

LONG
ReadRegSzKey(
    IN HKEY hKey,
    IN LPCWSTR pszKey,
    OUT LPWSTR *pValue)
{
    LONG rc;
    DWORD dwType;
    DWORD cbData = 0;
    LPWSTR Value;

    TRACE("(%p, %s, %p)\n", hKey, debugstr_w(pszKey), pValue);

    rc = RegQueryValueExW(hKey, pszKey, NULL, &dwType, NULL, &cbData);
    if (rc != ERROR_SUCCESS)
    {
        WARN("RegQueryValueEx(%s) failed with error %lu\n", debugstr_w(pszKey), rc);
        return rc;
    }
    if (dwType != REG_SZ)
    {
        WARN("Wrong registry data type (%u vs %u)\n", dwType, REG_SZ);
        return ERROR_FILE_NOT_FOUND;
    }
    Value = (WCHAR*) HeapAlloc(GetProcessHeap(), 0, cbData + sizeof(WCHAR));
    if (!Value)
    {
        WARN("No memory\n");
        return ERROR_NOT_ENOUGH_MEMORY;
    }
    rc = RegQueryValueExW(hKey, pszKey, NULL, NULL, (LPBYTE)Value, &cbData);
    if (rc != ERROR_SUCCESS)
    {
        WARN("RegQueryValueEx(%s) failed with error %lu\n", debugstr_w(pszKey), rc);
        HeapFree(GetProcessHeap(), 0, Value);
        return rc;
    }
    /* NULL-terminate the string */
    Value[cbData / sizeof(WCHAR)] = L'\0';

    *pValue = Value;
    return ERROR_SUCCESS;
}

static BOOL
IsConsoleShell(VOID)
{
    HKEY ControlKey = NULL;
    LPWSTR SystemStartOptions = NULL;
    LPWSTR CurrentOption, NextOption; /* Pointers into SystemStartOptions */
    LONG rc;
    BOOL ret = FALSE;

    TRACE("()\n");

    rc = RegOpenKeyEx(
        HKEY_LOCAL_MACHINE,
        REGSTR_PATH_CURRENT_CONTROL_SET,
        0,
        KEY_QUERY_VALUE,
        &ControlKey);
    if (rc != ERROR_SUCCESS)
    {
        WARN("RegOpenKeyEx() failed with error %lu\n", rc);
        goto cleanup;
    }

    rc = ReadRegSzKey(ControlKey, L"SystemStartOptions", &SystemStartOptions);
    if (rc != ERROR_SUCCESS)
    {
        WARN("ReadRegSzKey() failed with error %lu\n", rc);
        goto cleanup;
    }

    /* Check for CONSOLE switch in SystemStartOptions */
    CurrentOption = SystemStartOptions;
    while (CurrentOption)
    {
        NextOption = wcschr(CurrentOption, L' ');
        if (NextOption)
            *NextOption = L'\0';
        if (_wcsicmp(CurrentOption, L"CONSOLE") == 0)
        {
            TRACE("Found 'CONSOLE' boot option\n");
            ret = TRUE;
            goto cleanup;
        }
        CurrentOption = NextOption ? NextOption + 1 : NULL;
    }

cleanup:
    if (ControlKey != NULL)
        RegCloseKey(ControlKey);
    HeapFree(GetProcessHeap(), 0, SystemStartOptions);
    TRACE("IsConsoleShell() returning %d\n", ret);
    return ret;
}

static BOOL
GetShell(
    OUT WCHAR *CommandLine, /* must be at least MAX_PATH long */
    IN HKEY hRootKey)
{
    HKEY hKey;
    DWORD Type, Size;
    WCHAR Shell[MAX_PATH];
    BOOL ConsoleShell = IsConsoleShell();
    LONG rc;

    TRACE("(%p, %p)\n", CommandLine, hRootKey);

    rc = RegOpenKeyExW(hRootKey, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
                       0, KEY_QUERY_VALUE, &hKey);
    if (rc != ERROR_SUCCESS)
    {
        WARN("RegOpenKeyEx() failed with error %lu\n", rc);
        return FALSE;
    }

    Size = sizeof(Shell);
    rc = RegQueryValueExW(hKey,
                          ConsoleShell ? L"ConsoleShell" : L"Shell",
                          NULL,
                          &Type,
                          (LPBYTE)Shell,
                          &Size);
    RegCloseKey(hKey);

    if (rc != ERROR_SUCCESS)
    {
        WARN("RegQueryValueEx() failed with error %lu\n", rc);
        return FALSE;
    }

    if ((Type == REG_SZ) || (Type == REG_EXPAND_SZ))
    {
        TRACE("Found command line %s\n", debugstr_w(Shell));
        wcscpy(CommandLine, Shell);
        return TRUE;
    }
    else
    {
        WARN("Wrong type %lu (expected %u or %u)\n", Type, REG_SZ, REG_EXPAND_SZ);
        return FALSE;
    }
}

static BOOL
StartProcess(
    IN LPCWSTR CommandLine)
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    WCHAR ExpandedCmdLine[MAX_PATH];

    TRACE("(%s)\n", debugstr_w(CommandLine));

    ExpandEnvironmentStringsW(CommandLine, ExpandedCmdLine, ARRAYSIZE(ExpandedCmdLine));

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_SHOWNORMAL;
    ZeroMemory(&pi, sizeof(pi));

    if (!CreateProcessW(NULL,
                        ExpandedCmdLine,
                        NULL,
                        NULL,
                        FALSE,
                        NORMAL_PRIORITY_CLASS,
                        NULL,
                        NULL,
                        &si,
                        &pi))
    {
        WARN("CreateProcessW() failed with error %lu\n", GetLastError());
        return FALSE;
    }

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return TRUE;
}

static BOOL
StartShell(VOID)
{
    WCHAR Shell[MAX_PATH];
    WCHAR szMsg[RC_STRING_MAX_SIZE];
    DWORD Type, Size;
    DWORD Value = 0;
    LONG rc;
    HKEY hKey;

    TRACE("()\n");

    /* Safe Mode shell run */
    rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                       L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option",
                       0, KEY_QUERY_VALUE, &hKey);
    if (rc == ERROR_SUCCESS)
    {
        Size = sizeof(Value);
        rc = RegQueryValueExW(hKey, L"UseAlternateShell", NULL,
                              &Type, (LPBYTE)&Value, &Size);
        RegCloseKey(hKey);

        if (rc == ERROR_SUCCESS)
        {
            if (Type == REG_DWORD)
            {
                if (Value)
                {
                    /* Safe Mode Alternate Shell required */
                    rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                                       L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot",
                                       0, KEY_READ, &hKey);
                    if (rc == ERROR_SUCCESS)
                    {
                        Size = sizeof(Shell);
                        rc = RegQueryValueExW(hKey, L"AlternateShell", NULL,
                                              &Type, (LPBYTE)Shell, &Size);
                        RegCloseKey(hKey);

                        if (rc == ERROR_SUCCESS)
                        {
                            if ((Type == REG_SZ) || (Type == REG_EXPAND_SZ))
                            {
                                TRACE("Key located - %s\n", debugstr_w(Shell));

                                /* Try to run alternate shell */
                                if (StartProcess(Shell))
                                {
                                    TRACE("Alternate shell started (Safe Mode)\n");
                                    return TRUE;
                                }
                            }
                            else
                            {
                                WARN("Wrong type %lu (expected %u or %u)\n",
                                     Type, REG_SZ, REG_EXPAND_SZ);
                            }
                        }
                        else
                        {
                            WARN("Alternate shell in Safe Mode required but not specified.");
                        }
                    }
                }
            }
            else
            {
                WARN("Wrong type %lu (expected %u)\n", Type, REG_DWORD);
            }
        }
    }

    /* Try to run shell in user key */
    if (GetShell(Shell, HKEY_CURRENT_USER) && StartProcess(Shell))
    {
        TRACE("Started shell from HKEY_CURRENT_USER\n");
        return TRUE;
    }

    /* Try to run shell in local machine key */
    if (GetShell(Shell, HKEY_LOCAL_MACHINE) && StartProcess(Shell))
    {
        TRACE("Started shell from HKEY_LOCAL_MACHINE\n");
        return TRUE;
    }

    /* Try default shell */
    if (IsConsoleShell())
    {
        *Shell = UNICODE_NULL;
        if (GetSystemDirectoryW(Shell, ARRAYSIZE(Shell) - 8))
            StringCchCatW(Shell, ARRAYSIZE(Shell), L"\\");
        StringCchCatW(Shell, ARRAYSIZE(Shell), L"cmd.exe");
    }
    else
    {
        *Shell = UNICODE_NULL;
        if (GetSystemWindowsDirectoryW(Shell, ARRAYSIZE(Shell) - 13))
            StringCchCatW(Shell, ARRAYSIZE(Shell), L"\\");
        StringCchCatW(Shell, ARRAYSIZE(Shell), L"explorer.exe");
    }

    if (!StartProcess(Shell))
    {
        WARN("Failed to start default shell '%s'\n", debugstr_w(Shell));
        LoadStringW(GetModuleHandle(NULL), IDS_SHELL_FAIL, szMsg, ARRAYSIZE(szMsg));
        MessageBoxW(NULL, szMsg, NULL, MB_OK);
        return FALSE;
    }
    return TRUE;
}

const WCHAR g_RegColorNames[][32] = {
    L"Scrollbar",             /* 00 = COLOR_SCROLLBAR */
    L"Background",            /* 01 = COLOR_DESKTOP */
    L"ActiveTitle",           /* 02 = COLOR_ACTIVECAPTION  */
    L"InactiveTitle",         /* 03 = COLOR_INACTIVECAPTION */
    L"Menu",                  /* 04 = COLOR_MENU */
    L"Window",                /* 05 = COLOR_WINDOW */
    L"WindowFrame",           /* 06 = COLOR_WINDOWFRAME */
    L"MenuText",              /* 07 = COLOR_MENUTEXT */
    L"WindowText",            /* 08 = COLOR_WINDOWTEXT */
    L"TitleText",             /* 09 = COLOR_CAPTIONTEXT */
    L"ActiveBorder",          /* 10 = COLOR_ACTIVEBORDER */
    L"InactiveBorder",        /* 11 = COLOR_INACTIVEBORDER */
    L"AppWorkSpace",          /* 12 = COLOR_APPWORKSPACE */
    L"Hilight",               /* 13 = COLOR_HIGHLIGHT */
    L"HilightText",           /* 14 = COLOR_HIGHLIGHTTEXT */
    L"ButtonFace",            /* 15 = COLOR_BTNFACE */
    L"ButtonShadow",          /* 16 = COLOR_BTNSHADOW */
    L"GrayText",              /* 17 = COLOR_GRAYTEXT */
    L"ButtonText",            /* 18 = COLOR_BTNTEXT */
    L"InactiveTitleText",     /* 19 = COLOR_INACTIVECAPTIONTEXT */
    L"ButtonHilight",         /* 20 = COLOR_BTNHIGHLIGHT */
    L"ButtonDkShadow",        /* 21 = COLOR_3DDKSHADOW */
    L"ButtonLight",           /* 22 = COLOR_3DLIGHT */
    L"InfoText",              /* 23 = COLOR_INFOTEXT */
    L"InfoWindow",            /* 24 = COLOR_INFOBK */
    L"ButtonAlternateFace",   /* 25 = COLOR_ALTERNATEBTNFACE */
    L"HotTrackingColor",      /* 26 = COLOR_HOTLIGHT */
    L"GradientActiveTitle",   /* 27 = COLOR_GRADIENTACTIVECAPTION */
    L"GradientInactiveTitle", /* 28 = COLOR_GRADIENTINACTIVECAPTION */
    L"MenuHilight",           /* 29 = COLOR_MENUHILIGHT */
    L"MenuBar"                /* 30 = COLOR_MENUBAR */
};

static COLORREF
StrToColorref(
    IN LPWSTR lpszCol)
{
    BYTE rgb[3];

    TRACE("(%s)\n", debugstr_w(lpszCol));

    rgb[0] = (BYTE)wcstoul(lpszCol, &lpszCol, 10);
    rgb[1] = (BYTE)wcstoul(lpszCol, &lpszCol, 10);
    rgb[2] = (BYTE)wcstoul(lpszCol, &lpszCol, 10);
    return RGB(rgb[0], rgb[1], rgb[2]);
}

static VOID
SetUserSysColors(VOID)
{
    HKEY hKey;
    INT i;
    WCHAR szColor[25];
    DWORD Type, Size;
    COLORREF crColor;
    LONG rc;

    TRACE("()\n");

    rc = RegOpenKeyExW(HKEY_CURRENT_USER, REGSTR_PATH_COLORS,
                       0, KEY_QUERY_VALUE, &hKey);
    if (rc != ERROR_SUCCESS)
    {
        WARN("RegOpenKeyEx() failed with error %lu\n", rc);
        return;
    }

    for (i = 0; i < ARRAYSIZE(g_RegColorNames); i++)
    {
        Size = sizeof(szColor);
        rc = RegQueryValueExW(hKey, g_RegColorNames[i], NULL, &Type,
                              (LPBYTE)szColor, &Size);
        if (rc == ERROR_SUCCESS && Type == REG_SZ)
        {
            crColor = StrToColorref(szColor);
            SetSysColors(1, &i, &crColor);
        }
        else
        {
            WARN("RegQueryValueEx(%s) failed with error %lu\n",
                debugstr_w(g_RegColorNames[i]), rc);
        }
    }

    RegCloseKey(hKey);
}

static VOID
SetUserWallpaper(VOID)
{
    HKEY hKey;
    DWORD Type, Size;
    WCHAR szWallpaper[MAX_PATH + 1];
    LONG rc;

    TRACE("()\n");

    rc = RegOpenKeyExW(HKEY_CURRENT_USER, REGSTR_PATH_DESKTOP,
                       0, KEY_QUERY_VALUE, &hKey);
    if (rc != ERROR_SUCCESS)
    {
        WARN("RegOpenKeyEx() failed with error %lu\n", rc);
        return;
    }

    Size = sizeof(szWallpaper);
    rc = RegQueryValueExW(hKey,
                          L"Wallpaper",
                          NULL,
                          &Type,
                          (LPBYTE)szWallpaper,
                          &Size);
    RegCloseKey(hKey);

    if (rc == ERROR_SUCCESS && Type == REG_SZ)
    {
        ExpandEnvironmentStringsW(szWallpaper, szWallpaper, ARRAYSIZE(szWallpaper));
        TRACE("Using wallpaper %s\n", debugstr_w(szWallpaper));

        /* Load and change the wallpaper */
        SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, szWallpaper, SPIF_SENDCHANGE);
    }
    else
    {
        /* Remove the wallpaper */
        TRACE("No wallpaper set in registry (error %lu)\n", rc);
        SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDCHANGE);
    }
}

static VOID
SetUserSettings(VOID)
{
    TRACE("()\n");

    UpdatePerUserSystemParameters(1, TRUE);
    SetUserSysColors();
    SetUserWallpaper();
}

typedef DWORD (WINAPI *PCMP_REPORT_LOGON)(DWORD, DWORD);

static VOID
NotifyLogon(VOID)
{
    HINSTANCE hModule;
    PCMP_REPORT_LOGON CMP_Report_LogOn;

    TRACE("()\n");

    hModule = LoadLibraryW(L"setupapi.dll");
    if (!hModule)
    {
        WARN("LoadLibrary() failed with error %lu\n", GetLastError());
        return;
    }

    CMP_Report_LogOn = (PCMP_REPORT_LOGON)GetProcAddress(hModule, "CMP_Report_LogOn");
    if (CMP_Report_LogOn)
        CMP_Report_LogOn(CMP_MAGIC, GetCurrentProcessId());
    else
        WARN("GetProcAddress() failed\n");

    FreeLibrary(hModule);
}

static BOOL
StartInstaller(IN LPCTSTR lpInstallerName)
{
    SYSTEM_INFO SystemInfo;
    SIZE_T cchInstallerNameLen;
    PWSTR ptr;
    DWORD dwAttribs;
    WCHAR Installer[MAX_PATH];
    WCHAR szMsg[RC_STRING_MAX_SIZE];

    cchInstallerNameLen = wcslen(lpInstallerName);
    if (ARRAYSIZE(Installer) < cchInstallerNameLen)
    {
        /* The buffer is not large enough to contain the installer file name */
        return FALSE;
    }

    /*
     * First, try to find the installer using the default drive, under
     * the directory whose name corresponds to the currently-running
     * CPU architecture.
     */
    GetSystemInfo(&SystemInfo);

    *Installer = UNICODE_NULL;
    /* Alternatively one can use SharedUserData->NtSystemRoot */
    GetSystemWindowsDirectoryW(Installer, ARRAYSIZE(Installer) - cchInstallerNameLen - 1);
    ptr = wcschr(Installer, L'\\');
    if (ptr)
        *++ptr = UNICODE_NULL;
    else
        *Installer = UNICODE_NULL;

    /* Append the corresponding CPU architecture */
    switch (SystemInfo.wProcessorArchitecture)
    {
        case PROCESSOR_ARCHITECTURE_INTEL:
            StringCchCatW(Installer, ARRAYSIZE(Installer), L"I386");
            break;

        case PROCESSOR_ARCHITECTURE_MIPS:
            StringCchCatW(Installer, ARRAYSIZE(Installer), L"MIPS");
            break;

        case PROCESSOR_ARCHITECTURE_ALPHA:
            StringCchCatW(Installer, ARRAYSIZE(Installer), L"ALPHA");
            break;

        case PROCESSOR_ARCHITECTURE_PPC:
            StringCchCatW(Installer, ARRAYSIZE(Installer), L"PPC");
            break;

        case PROCESSOR_ARCHITECTURE_SHX:
            StringCchCatW(Installer, ARRAYSIZE(Installer), L"SHX");
            break;

        case PROCESSOR_ARCHITECTURE_ARM:
            StringCchCatW(Installer, ARRAYSIZE(Installer), L"ARM");
            break;

        case PROCESSOR_ARCHITECTURE_IA64:
            StringCchCatW(Installer, ARRAYSIZE(Installer), L"IA64");
            break;

        case PROCESSOR_ARCHITECTURE_ALPHA64:
            StringCchCatW(Installer, ARRAYSIZE(Installer), L"ALPHA64");
            break;

        case PROCESSOR_ARCHITECTURE_AMD64:
            StringCchCatW(Installer, ARRAYSIZE(Installer), L"AMD64");
            break;

        // case PROCESSOR_ARCHITECTURE_MSIL: /* .NET CPU-independent code */
        case PROCESSOR_ARCHITECTURE_UNKNOWN:
        default:
            WARN("Unknown processor architecture %lu\n", SystemInfo.wProcessorArchitecture);
            SystemInfo.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
            break;
    }

    if (SystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_UNKNOWN)
        StringCchCatW(Installer, ARRAYSIZE(Installer), L"\\");
    StringCchCatW(Installer, ARRAYSIZE(Installer), lpInstallerName);

    dwAttribs = GetFileAttributesW(Installer);
    if ((dwAttribs != INVALID_FILE_ATTRIBUTES) &&
        !(dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
    {
        /* We have found the installer */
        if (StartProcess(Installer))
            return TRUE;
    }

    ERR("Failed to start the installer '%s', trying alternative.\n", debugstr_w(Installer));

    /*
     * We failed. Try to find the installer from either the current
     * ReactOS installation directory, or from our current directory.
     */
    *Installer = UNICODE_NULL;
    /* Alternatively one can use SharedUserData->NtSystemRoot */
    if (GetSystemWindowsDirectoryW(Installer, ARRAYSIZE(Installer) - cchInstallerNameLen - 1))
        StringCchCatW(Installer, ARRAYSIZE(Installer), L"\\");
    StringCchCatW(Installer, ARRAYSIZE(Installer), lpInstallerName);

    dwAttribs = GetFileAttributesW(Installer);
    if ((dwAttribs != INVALID_FILE_ATTRIBUTES) &&
        !(dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
    {
        /* We have found the installer */
        if (StartProcess(Installer))
            return TRUE;
    }

    /* We failed. Display an error message and quit. */
    ERR("Failed to start the installer '%s'.\n", debugstr_w(Installer));
    LoadStringW(GetModuleHandle(NULL), IDS_INSTALLER_FAIL, szMsg, ARRAYSIZE(szMsg));
    MessageBoxW(NULL, szMsg, NULL, MB_OK);
    return FALSE;
}

/* Used to get the shutdown privilege */
static BOOL
EnablePrivilege(LPCWSTR lpszPrivilegeName, BOOL bEnablePrivilege)
{
    BOOL   Success;
    HANDLE hToken;
    TOKEN_PRIVILEGES tp;

    Success = OpenProcessToken(GetCurrentProcess(),
                               TOKEN_ADJUST_PRIVILEGES,
                               &hToken);
    if (!Success) return Success;

    Success = LookupPrivilegeValueW(NULL,
                                    lpszPrivilegeName,
                                    &tp.Privileges[0].Luid);
    if (!Success) goto Quit;

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Attributes = (bEnablePrivilege ? SE_PRIVILEGE_ENABLED : 0);

    Success = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);

Quit:
    CloseHandle(hToken);
    return Success;
}


int WINAPI
wWinMain(IN HINSTANCE hInst,
         IN HINSTANCE hPrevInstance,
         IN LPWSTR lpszCmdLine,
         IN int nCmdShow)
{
    BOOL bIsLiveCD, Success = TRUE;
    STATE State;

    hInstance = hInst;

    bIsLiveCD = IsLiveCD();

Restart:
    SetUserSettings();

    if (bIsLiveCD)
    {
        State.NextPage = LOCALEPAGE;
        State.Run = SHELL;
    }
    else
    {
        State.NextPage = DONE;
        State.Run = SHELL;
    }

    if (State.NextPage != DONE) // && bIsLiveCD
    {
        RunLiveCD(&State);
    }

    switch (State.Run)
    {
        case SHELL:
            Success = StartShell();
            if (Success)
                NotifyLogon();
            break;

        case INSTALLER:
            Success = StartInstaller(L"reactos.exe");
            break;

        case REBOOT:
        {
            EnablePrivilege(SE_SHUTDOWN_NAME, TRUE);
            ExitWindowsEx(EWX_REBOOT, 0);
            EnablePrivilege(SE_SHUTDOWN_NAME, FALSE);
            Success = TRUE;
            break;
        }

        default:
            Success = FALSE;
            break;
    }

    /*
     * In LiveCD mode, go back to the main menu if we failed
     * to either start the shell or the installer.
     */
    if (bIsLiveCD && !Success)
        goto Restart;

    return 0;
}

/* EOF */