
/*
 *@@sourcefile shut_pages.c:
 *      dialogs for XShutdown, such as the confirmation
 *      and "reboot actions" etc. dialogs.
 *
 *      This has been separated from shutdown.c with
 *      V1.0.0 (2002-09-13) [umoeller].
 *
 *
 *      All the functions in this file have the xsd* prefix.
 *
 *@@header "startshut\shutdown.h"
 */

/*
 *      Copyright (C) 1997-2012 Ulrich M”ller.
 *      This file is part of the XWorkplace source package.
 *      XWorkplace is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published
 *      by the Free Software Foundation, in version 2 as it comes in the
 *      "COPYING" file of the XWorkplace main distribution.
 *      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.
 */

#pragma strings(readonly)

/*
 *  Suggested #include order:
 *  1)  os2.h
 *  2)  C library headers
 *  3)  setup.h (code generation and debugging options)
 *  4)  headers in helpers\
 *  5)  at least one SOM implementation header (*.ih)
 *  6)  dlgids.h, headers in shared\ (as needed)
 *  7)  headers in implementation dirs (e.g. filesys\, as needed)
 *  8)  #pragma hdrstop and then more SOM headers which crash with precompiled headers
 */

#define INCL_DOS
#define INCL_DOSERRORS
#define INCL_DOSDEVIOCTL
#define INCL_WIN
#include <os2.h>

// C library headers
#include <stdio.h>              // needed for except.h
#include <setjmp.h>             // needed for except.h
#include <assert.h>             // needed for except.h
#include <stdarg.h>

// generic headers
#include "setup.h"                      // code generation and debugging options

// headers in /helpers
#include "helpers\comctl.h"             // common controls (window procs)
#include "helpers\dialog.h"             // dialog helpers
#include "helpers\dosh.h"               // Control Program helper routines
#include "helpers\except.h"             // exception handling
#include "helpers\exeh.h"               // executable helpers
#include "helpers\gpih.h"               // GPI helper routines
#include "helpers\linklist.h"           // linked list helper routines
#include "helpers\prfh.h"               // INI file helper routines
#include "helpers\standards.h"          // some standard macros
#include "helpers\threads.h"            // thread helpers
#include "helpers\winh.h"               // PM helper routines

// SOM headers which don't crash with prec. header files
#include "xfldr.ih"                     // needed for shutdown folder

// XWorkplace implementation headers
#include "dlgids.h"                     // all the IDs that are shared with NLS
#include "shared\common.h"              // the majestic XWorkplace include file
#include "shared\helppanels.h"          // all XWorkplace help panel IDs
#include "shared\kernel.h"              // XWorkplace Kernel
#include "shared\notebook.h"            // generic XWorkplace notebook handling
#include "xwpapi.h"                     // public XWorkplace definitions

// headers in /hook
#include "hook\xwphook.h"

// #include "filesys\fdrmenus.h"           // shared folder menu logic
#include "filesys\object.h"             // XFldObject implementation
#include "filesys\xthreads.h"           // extra XWorkplace threads

#include "startshut\apm.h"              // APM power-off for XShutdown
#include "startshut\archives.h"         // archiving declarations
#include "startshut\shutdown.h"         // XWorkplace eXtended Shutdown
#include "startshut\bootlist.h"         // AirBoot & Bootmanager/LVM

// other SOM headers
#pragma hdrstop
#include <wpdesk.h>                     // WPDesktop; includes WPFolder also
#include "shared\wpsh.h"                // some pseudo-SOM functions (WPS helper routines)

void _Optlink   fntBootList(void *arg);
BOOL CheckAirBoot(PBOOTLIST pbl);
BOOL GetVolList(HFILE hDrive, PAIRBOOTVOLREC pVolRec,
                PBOOTLIST pbl, ULONG cVols);
ULONG ReadDiskSector(HFILE hDrive, ULONG sector, PBYTE pBuf);
BOOL CheckBootMgr(PBOOTLIST pbl);

/* ******************************************************************
 *
 *   Global variables
 *
 ********************************************************************/

static ULONG            G_ulConfirmHelpPanel = NULLHANDLE;

static PFNWP            G_pfnwpFrameOrig = NULL;
static PXBITMAP         G_pbmDim = NULLHANDLE;

/* ******************************************************************
 *
 *   XShutdown confirmation
 *
 ********************************************************************/

/*
 * fnwpConfirm:
 *      dlg proc for XShutdown confirmation windows.
 */

STATIC MRESULT EXPENTRY fnwpConfirm(HWND hwndDlg, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    MRESULT mrc = MPNULL;
    switch (msg)
    {
        case WM_CONTROL:
            if (    (SHORT2FROMMP(mp1) == BN_CLICKED)
                 || (SHORT2FROMMP(mp1) == BN_DBLCLICKED)
               )
                switch (SHORT1FROMMP(mp1)) // usItemID
                {
                    case ID_SDDI_SHUTDOWNONLY:
                        WinEnableControl(hwndDlg, ID_SDDI_BOOTMGR, FALSE);
                    break;

                    case ID_SDDI_REBOOTTO:
                        WinEnableControl(hwndDlg, ID_SDDI_BOOTMGR, TRUE);
                    break;
                }

            mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
        break;

        case WM_HELP:
            cmnDisplayHelp(NULL,
                           G_ulConfirmHelpPanel);
        break;

        default:
            mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
    }
    return mrc;
}

/*
 *@@ fnwpDimScreen:
 */

STATIC MRESULT EXPENTRY fnwpDimScreen(HWND hwndFrame, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    MRESULT mrc = 0;

    switch (msg)
    {
        case WM_PAINT:
        {
            HPS hps = WinBeginPaint(hwndFrame, NULLHANDLE, NULL);
            POINTL ptl = {0,0};
            if (G_pbmDim)
                WinDrawBitmap(hps,
                              G_pbmDim->hbm,
                              NULL,
                              &ptl,
                              0,
                              0,
                              DBM_NORMAL); // DBM_HALFTONE);
            WinEndPaint(hps);
        }
        break;

        case WM_DESTROY:
            gpihDestroyXBitmap(&G_pbmDim);
            mrc = G_pfnwpFrameOrig(hwndFrame, msg, mp1, mp2);
        break;

        default:
            mrc = G_pfnwpFrameOrig(hwndFrame, msg, mp1, mp2);
    }

    return mrc;
}

/*
 *@@ CreateDimScreenWindow:
 */

STATIC HWND CreateDimScreenWindow(VOID)
{
    HWND hwnd;

    FRAMECDATA  fcdata;

    HPS hpsScreen;
    RECTL rcl;
    HAB hab = winhMyAnchorBlock();      // expensive, but what the heck

    // before capturing the screen, sleep a millisecond,
    // because otherwise windows are not fully repainted
    // if the desktop menu was open, and we get that
    // into the screenshot then... so give the windows
    // time to repaint
    winhSleep(10);

    // go capture screen
    hpsScreen = WinGetScreenPS(HWND_DESKTOP);
    rcl.xLeft = 0;
    rcl.yBottom = 0;
    rcl.xRight = winhQueryScreenCX();
    rcl.yTop = winhQueryScreenCY();
    if (G_pbmDim = gpihCreateBmpFromPS(hab,
                                       hpsScreen,
                                       &rcl))
    {
        POINTL  ptl = {0, 0};
        GpiMove(G_pbmDim->hpsMem, &ptl);  // still 0, 0
        ptl.x = rcl.xRight - 1;
        ptl.y = rcl.yTop - 1;
        GpiSetColor(G_pbmDim->hpsMem, RGBCOL_BLACK);
        GpiSetPattern(G_pbmDim->hpsMem, PATSYM_HALFTONE);
        GpiBox(G_pbmDim->hpsMem,
               DRO_FILL, // interior only
               &ptl,
               0, 0);    // no corner rounding
    }
    WinReleasePS(hpsScreen);

    fcdata.cb            = sizeof(FRAMECDATA);
    fcdata.flCreateFlags = FCF_NOBYTEALIGN;
    fcdata.hmodResources = NULLHANDLE;
    fcdata.idResources   = 0;

    hwnd = WinCreateWindow(HWND_DESKTOP,
                           WC_FRAME,
                           "",
                           0,       // flags
                           0,
                           0,
                           winhQueryScreenCX(),
                           winhQueryScreenCY(),
                           NULLHANDLE,
                           HWND_TOP,
                           0,
                           &fcdata,
                           NULL);

    G_pfnwpFrameOrig = WinSubclassWindow(hwnd, fnwpDimScreen);
    WinShowWindow(hwnd, TRUE);

    return (hwnd);
}

#define DLG_WIDTH   (3 * STD_BUTTON_WIDTH + 2 * COMMON_SPACING + 10)

#define CONTROLDEF_ICON_WIDEST(hptr, id) { WC_STATIC, (PCSZ)(hptr), \
    WS_VISIBLE | SS_ICON | DT_LEFT | DT_VCENTER, \
    id, CTL_COMMON_FONT, {SZL_AUTOSIZE, SZL_AUTOSIZE}, 3 * COMMON_SPACING }


static CONTROLDEF
    ShutdownIcon = CONTROLDEF_ICON_WIDEST(0, ID_SDDI_ICON),
    ConfirmShutText = CONTROLDEF_TEXT_WORDBREAK(
                            LOAD_STRING,
                            ID_SDDI_CONFIRM_TEXT,
                            DLG_WIDTH - 20),
    RebootOptGroup = LOADDEF_GROUP(
                            ID_SDDI_REBOOTOPT_GROUP,
                            SZL_AUTOSIZE),
    ShutdownOnlyRadio = CONTROLDEF_FIRST_AUTORADIO(
                            LOAD_STRING,
                            ID_SDDI_SHUTDOWNONLY,
                            SZL_AUTOSIZE,
                            SZL_AUTOSIZE),
    RebootToRadio = CONTROLDEF_NEXT_AUTORADIO(
                            LOAD_STRING,
                            ID_SDDI_REBOOTTO,
                            SZL_AUTOSIZE,
                            SZL_AUTOSIZE),
    BootMgrCombobox = CONTROLDEF_DROPDOWNLIST(
                            ID_SDDI_BOOTMGR,
                            80,
                            50),

    Spacing4x1 = CONTROLDEF_SPACING(4, 1),

    EmptyTrashCB = CONTROLDEF_AUTOCHECKBOX(
                            LOAD_STRING,
                            ID_SDDI_EMPTYTRASHCAN,
                            SZL_AUTOSIZE,
                            SZL_AUTOSIZE),
    OKButton = CONTROLDEF_DEFPUSHBUTTON(
                            0,
                            DID_OK,
                            STD_BUTTON_WIDTH,
                            STD_BUTTON_HEIGHT),
    CancelButton = CONTROLDEF_PUSHBUTTON(
                            0,
                            DID_CANCEL,
                            STD_BUTTON_WIDTH,
                            STD_BUTTON_HEIGHT),
    HelpButton = CONTROLDEF_HELPPUSHBUTTON(
                            LOAD_STRING,
                            DID_HELP,
                            STD_BUTTON_WIDTH,
                            STD_BUTTON_HEIGHT);

static const DLGHITEM dlgConfirmShutdown[] =
    {
        START_TABLE,
            START_ROW(ROW_VALIGN_CENTER),
                CONTROL_DEF(&ShutdownIcon),
                CONTROL_DEF(&ConfirmShutText),

            START_ROW(0),
                START_GROUP_TABLE_EXT(&RebootOptGroup, TABLE_INHERIT_SIZE),
                    START_ROW(0),
                        CONTROL_DEF(&ShutdownOnlyRadio),
                    START_ROW(0),
                        CONTROL_DEF(&Spacing4x1),
                    START_ROW(ROW_VALIGN_CENTER),
                        CONTROL_DEF(&RebootToRadio),
                        CONTROL_DEF(&BootMgrCombobox),
                END_TABLE,

            START_ROW(0),
                CONTROL_DEF(&Spacing4x1),
            START_ROW(ROW_VALIGN_CENTER),
                CONTROL_DEF(&EmptyTrashCB),
            START_ROW(0),
                CONTROL_DEF(&Spacing4x1),

            START_ROW(ROW_VALIGN_CENTER),
                CONTROL_DEF(&OKButton),
                CONTROL_DEF(&CancelButton),
                CONTROL_DEF(&HelpButton),
        END_TABLE,
    };

/*
 *@@ xsdIsUEFI:
 *      this determines whether the system was booted under
 *      UEFI by using the OEMHLP_GETFBDATA call as a proxy;
 *      it succeeds under UEFI and fails under BIOS/CSM
 */

// taken from oemhlpex.h

#define OEMHLP_GETFBDATA       0x0012
//  Parameter - none
//  Data      - buffer for OEMHLP_FBDATA

typedef struct _OEMHLP_FBDATA    {  /* OEMHLP_GETFBDATA packet */
   unsigned short       Size;  // size of this structure (in/out)
   unsigned char       Flags;  // OEMHLP_FBF_*
   unsigned char      BitsPP;  // bits per pixel (should be 32)
   unsigned long     VMemLin;  // video memory linear address (in shared arena)
   unsigned long    VMemPhys;  // video memory physical address
   unsigned long       Pitch;  // scan line length
   unsigned short      SizeX,  // screen width
                       SizeY;  // screen height
   unsigned long     RedMask;
   unsigned long   GreenMask;
   unsigned long    BlueMask;
} OEMHLP_FBDATA;

#define OEMHLP_FBF_ACTIVE  0x01   // data is valid (graphics mode active)
#define OEMHLP_FBF_WCSET   0x02   // write combine is on (information flag)

BOOL    xsdIsUEFI(void)
{
    ULONG       rc;
    ULONG       ul;
    HFILE       hndl;
    OEMHLP_FBDATA fbd;

    rc = DosOpen("\\DEV\\OEMHLP$", &hndl, &ul, 0, FILE_NORMAL,
                 OPEN_ACTION_OPEN_IF_EXISTS, OPEN_SHARE_DENYNONE, NULL);
    if (rc) {
        fprintf(stderr, "xsdIsUEFI: DosOpen(\\DEV\\OEMHLP$) - rc= %d\n", rc);
        return FALSE;
    }

    ul = sizeof(OEMHLP_FBDATA);
    memset(&fbd, 0, ul);
    fbd.Size = ul;
    rc = DosDevIOCtl(hndl, IOCTL_OEMHLP, OEMHLP_GETFBDATA,
                     0, 0, 0, &fbd, ul, &ul);
    DosClose(hndl);

    return !rc;
}

/*
 *@@ xsdConfirmShutdown:
 *      this displays the eXtended Shutdown (not Restart Desktop)
 *      confirmation box. Returns MBID_YES/NO.
 */

ULONG xsdConfirmShutdown(PSHUTDOWNPARAMS psdParms)
{
    ULONG       ulReturn = MBID_NO;
    BOOL        fStore = FALSE;
    HWND        hwndConfirm = NULLHANDLE,
                hwndDim;
    PBOOTLIST   pbl = 0;

    TRY_LOUD(excpt1)
    {
        HMODULE     hmodResource = cmnQueryMainResModuleHandle();
        ULONG       ulKeyLength;
        USHORT      ctr;
        PSZ         p = NULL;
        ULONG       ulID;
        PDLGHITEM   paNew;

        // create a thread to get the AirBoot or Boot Manager menu;
        // if this fails, do cleanup now as a signal to the code below
        // note: this intentionally avoids the overhead of thrCreate()

        if (!xsdIsUEFI() &&
            (pbl = (PBOOTLIST)calloc(1, sizeof(BOOTLIST))) != 0 &&
            (DosCreateEventSem(0, &pbl->hev, 0, FALSE) ||
             _beginthread(fntBootList, 0, 0xd000, pbl) < 0))
        {
            if (pbl->hev)
                DosCloseEventSem(pbl->hev);
            free(pbl);
            pbl = 0;
        }
        else
            // give the thread a chance to start
            DosSleep(10);

        hwndDim = CreateDimScreenWindow();

        G_ulConfirmHelpPanel = ID_XSH_XSHUTDOWN_CONFIRM;

        // use the dialog formatter
        if (!cmnLoadDialogStrings(dlgConfirmShutdown,
                                  ARRAYITEMCOUNT(dlgConfirmShutdown),
                                  &paNew))
        {
            HPOINTER hptrShutdown
                        = WinLoadPointer(HWND_DESKTOP, hmodResource, ID_SDICON);
            ShutdownIcon.pcszText = (char*)hptrShutdown;

            OKButton.pcszText = cmnGetString(DID_OK);
            CancelButton.pcszText = cmnGetString(DID_CANCEL);

            if (!dlghCreateDlg(&hwndConfirm,
                               hwndDim,
                               FCF_FIXED_DLG,
                               fnwpConfirm,
                               cmnGetString(ID_SDDI_CONFIRM_TITLE),
                               paNew,
                               ARRAYITEMCOUNT(dlgConfirmShutdown),
                               NULL,
                               cmnQueryDefaultFont()))
            {
                WinPostMsg(hwndConfirm,
                           WM_SETICON,
                           (MPARAM)hptrShutdown,
                           NULL);

                if (cmnTrashCanReady())
                {
                    if (psdParms->optEmptyTrashCan)
                        winhSetDlgItemChecked(hwndConfirm, ID_SDDI_EMPTYTRASHCAN, TRUE);
                }
                else
                    // trash can not ready: disable item
                    WinEnableControl(hwndConfirm, ID_SDDI_EMPTYTRASHCAN, FALSE);

                // check radio button
                ulID = (psdParms->optReboot) ? ID_SDDI_REBOOTTO : ID_SDDI_SHUTDOWNONLY;
                winhSetDlgItemChecked(hwndConfirm, ulID, TRUE);
                winhSetDlgItemFocus(hwndConfirm, ulID);
                WinEnableControl(hwndConfirm, ID_SDDI_BOOTMGR,
                                 (ulID == ID_SDDI_REBOOTTO));

                // set shut-down/power-off text
                if (psdParms->optPowerOff)
                   WinSetDlgItemText(hwndConfirm, ID_SDDI_SHUTDOWNONLY,
                                     cmnGetString(ID_SDDI_POWEROFFONLY));

                cmnSetControlsFont(hwndConfirm, 1, 5000);
                winhCenterWindow(hwndConfirm);      // still hidden

                // filling the combobox with reboot selections is
                // deferred as long as possible to help ensure the
                // thread collecting these items has completed;
                // if it hasn't - or something has gone wrong -
                // the user will only see 'Reboot' with no options

                if (!pbl || WinWaitEventSem(pbl->hev, 3000) ||
                    !pbl->status || pbl->status > 2 || !pbl->cVols) {
                    psdParms->ulReboot = REBOOT_RESTART;
                    p = "Reboot"; // this is just filler
                    WinShowWindow(WinWindowFromID(hwndConfirm, ID_SDDI_BOOTMGR), FALSE);
                }
                else {
                    psdParms->ulReboot = pbl->status;
                    p = psdParms->ulReboot == REBOOT_AIRBOOT ? "AirBoot" : "BootManager";
                }

                WinSendDlgItemMsg(hwndConfirm, ID_SDDI_BOOTMGR, LM_INSERTITEM,
                                  (MPARAM)LIT_END, (MPARAM)p);

                if (psdParms->ulReboot != REBOOT_RESTART) {
                    for (ctr = 0; ctr < pbl->cVols; ctr++)
                        WinSendDlgItemMsg(hwndConfirm, ID_SDDI_BOOTMGR,
                                          LM_INSERTITEM, (MPARAM)LIT_END,
                                          (MPARAM)pbl->achVol[ctr]);

                    ctr = cmnQuerySetting(susLastRebootExt);
                    if (ctr > pbl->cVols)
                        ctr = 0;
                }
                else
                    ctr = 0;

                WinSendDlgItemMsg(hwndConfirm, ID_SDDI_BOOTMGR,
                                  LM_SELECTITEM, (MPARAM)ctr, (MPARAM)TRUE);

                // go!!
                ulReturn = WinProcessDlg(hwndConfirm);

                if (ulReturn == DID_OK)
                {
        #ifndef __NOXSHUTDOWN__
                    ULONG flShutdown = cmnQuerySetting(sflXShutdown);
        #endif

                    // check empty trash
                    psdParms->optEmptyTrashCan
                        = (winhIsDlgItemChecked(hwndConfirm, ID_SDDI_EMPTYTRASHCAN) != 0);

                    if (winhIsDlgItemChecked(hwndConfirm, ID_SDDI_REBOOTTO))
                    {
                        USHORT usSelected;

                        usSelected = (USHORT)WinSendDlgItemMsg(hwndConfirm,
                                        ID_SDDI_BOOTMGR, LM_QUERYSELECTION,
                                        (MPARAM)LIT_CURSOR, 0);

                        // selecting the first item will trigger a standard reboot
                        if (usSelected == 0)
                            psdParms->ulReboot = REBOOT_RESTART;
                        else
                            WinSendDlgItemMsg(hwndConfirm, ID_SDDI_BOOTMGR,
                                              LM_QUERYITEMTEXT,
                                              MPFROM2SHORT(usSelected, CCHMAXPATH),
                                              (MPARAM)psdParms->szRebootCommand);

                        psdParms->optReboot = TRUE;
        #ifndef __NOXSHUTDOWN__
                        flShutdown |= XSD_REBOOT;
        #endif
                        cmnSetSetting(susLastRebootExt, usSelected);
                    }

                    else {
                        // standard shutdown:
                        psdParms->optReboot = FALSE;
        #ifndef __NOXSHUTDOWN__
                        flShutdown &= ~XSD_REBOOT;
        #endif
                    }

        #ifndef __NOXSHUTDOWN__
                    cmnSetSetting(sflXShutdown, flShutdown);
        #endif
                }
            }
            CATCH(excpt1)
            {
            } END_CATCH();
        }
    }

    if (hwndConfirm)
        WinDestroyWindow(hwndConfirm);
    WinDestroyWindow(hwndDim);

    // if the bootlist thread both started and concluded successfully,
    // do cleanup - otherwise, leave things dangling to avoid a crash
    if (pbl && pbl->status) {
        DosCloseEventSem(pbl->hev);
        free(pbl);
    }

    return (ulReturn);
}

static CONTROLDEF
    ConfirmWPSText = CONTROLDEF_TEXT_WORDBREAK(
                            LOAD_STRING,
                            ID_SDDI_CONFIRMWPS_TEXT,
                            SZL_AUTOSIZE),
    RestartOptGroup = LOADDEF_GROUP(
                            ID_SDDI_RESTARTWPS_GROUP,
                            SZL_AUTOSIZE),
    CloseAllSessionsCB = LOADDEF_AUTOCHECKBOX(ID_SDDI_WPS_CLOSEWINDOWS),
#ifndef __NOXWPSTARTUP__
    StartupFoldersCB = LOADDEF_AUTOCHECKBOX(ID_SDDI_WPS_STARTUPFOLDER),
#endif
    ArchiveOnceCB = LOADDEF_AUTOCHECKBOX(ID_SDDI_ARCHIVEONCE);

static const DLGHITEM dlgConfirmRestartDesktop[] =
    {
        START_TABLE,
            START_ROW(ROW_VALIGN_CENTER),
                CONTROL_DEF(&ShutdownIcon),
                CONTROL_DEF(&ConfirmWPSText),

            START_ROW(0),
                START_GROUP_TABLE_EXT(&RestartOptGroup, TABLE_INHERIT_SIZE),
                    START_ROW(0),
                        CONTROL_DEF(&CloseAllSessionsCB),
#ifndef __NOXWPSTARTUP__
                    START_ROW(0),
                        CONTROL_DEF(&StartupFoldersCB),
#endif
                    START_ROW(0),
                        CONTROL_DEF(&ArchiveOnceCB),
                END_TABLE,

            START_ROW(0),
                CONTROL_DEF(&G_Spacing),

            START_ROW(ROW_VALIGN_CENTER),
                CONTROL_DEF(&OKButton),
                CONTROL_DEF(&CancelButton),
                CONTROL_DEF(&HelpButton),
        END_TABLE
    };

/*
 *@@ xsdConfirmRestartWPS:
 *      this displays the "Restart Desktop"
 *      confirmation box. Returns MBID_OK/CANCEL.
 */

ULONG xsdConfirmRestartWPS(PSHUTDOWNPARAMS psdParms)
{
    ULONG       ulReturn = MBID_CANCEL;
    HWND        hwndConfirm;
    HMODULE     hmodResource = cmnQueryMainResModuleHandle();
    HWND        hwndDim = CreateDimScreenWindow();
    PDLGHITEM   paNew;

    G_ulConfirmHelpPanel = ID_XSH_RESTARTWPS_CONFIRM;

    if (!cmnLoadDialogStrings(dlgConfirmRestartDesktop,
                              ARRAYITEMCOUNT(dlgConfirmRestartDesktop),
                              &paNew))
    {
        HPOINTER hptrShutdown
                    = WinLoadPointer(HWND_DESKTOP, hmodResource, ID_SDICON);
        ShutdownIcon.pcszText = (char*)hptrShutdown;

        OKButton.pcszText = cmnGetString(DID_OK);
        CancelButton.pcszText = cmnGetString(DID_CANCEL);

        if (!dlghCreateDlg(&hwndConfirm,
                           hwndDim,
                           FCF_FIXED_DLG,
                           fnwpConfirm,
                           cmnGetString(ID_SDDI_CONFIRMWPS_TITLE),
                           paNew,
                           ARRAYITEMCOUNT(dlgConfirmRestartDesktop),
                           NULL,
                           cmnQueryDefaultFont()))
        {
            WinSendMsg(hwndConfirm,
                       WM_SETICON,
                       (MPARAM)hptrShutdown,
                       NULL);

            // if (psdParms->ulRestartWPS == 2)
            if (psdParms->ulCloseMode == SHUT_LOGOFF)
            {
                // logoff:
                psdParms->optWPSCloseWindows = TRUE;
                psdParms->optWPSReuseStartupFolder = TRUE;
                WinEnableControl(hwndConfirm, ID_SDDI_WPS_CLOSEWINDOWS, FALSE);
    #ifndef __NOXWPSTARTUP__
                WinEnableControl(hwndConfirm, ID_SDDI_WPS_STARTUPFOLDER, FALSE);
    #endif
                // replace confirmation text
                WinSetDlgItemText(hwndConfirm, ID_SDDI_CONFIRMWPS_TEXT,
                                  cmnGetString(ID_XSSI_XSD_CONFIRMLOGOFFMSG)) ; // pszXSDConfirmLogoffMsg
            }

            winhSetDlgItemChecked(hwndConfirm, ID_SDDI_WPS_CLOSEWINDOWS, psdParms->optWPSCloseWindows);
    #ifndef __NOXWPSTARTUP__
            winhSetDlgItemChecked(hwndConfirm, ID_SDDI_WPS_STARTUPFOLDER, psdParms->optWPSCloseWindows);
    #endif
            winhCenterWindow(hwndConfirm);      // still hidden
            WinShowWindow(hwndConfirm, TRUE);

            // *** go!
            ulReturn = WinProcessDlg(hwndConfirm);

            if (ulReturn == DID_OK)
            {
    #ifndef __NOXSHUTDOWN__
                ULONG fl = cmnQuerySetting(sflXShutdown);
    #endif

                psdParms->optWPSCloseWindows = winhIsDlgItemChecked(hwndConfirm,
                                                                    ID_SDDI_WPS_CLOSEWINDOWS);
                if (psdParms->ulCloseMode != SHUT_LOGOFF)
                {
                    // regular restart Desktop:
                    // save close windows/startup folder settings
    #ifndef __NOXSHUTDOWN__
                    if (psdParms->optWPSCloseWindows)
                        fl |= XSD_WPS_CLOSEWINDOWS;
                    else
                        fl &= ~XSD_WPS_CLOSEWINDOWS;
    #endif
    #ifndef __NOXWPSTARTUP__
                    psdParms->optWPSReuseStartupFolder = winhIsDlgItemChecked(hwndConfirm,
                                                                              ID_SDDI_WPS_STARTUPFOLDER);
    #endif
                }

    #ifndef __NOXSHUTDOWN__
                // save settings if XSHUTDOWN is enabled
                // changed V1.0.11 (2016-10-29) [rwalsh]:
                cmnSetSetting(sflXShutdown, fl);
    #endif

                // V0.9.19 (2002-04-17) [umoeller]
                if (winhIsDlgItemChecked(hwndConfirm,
                                         ID_SDDI_ARCHIVEONCE))
                {
                    PARCHIVINGSETTINGS pArcSettings = arcQuerySettings();
                    pArcSettings->ulArcFlags |= ARCF_NEXT;
                    arcSaveSettings();
                }
            }

            WinDestroyWindow(hwndConfirm);
        }

        free(paNew);
    }

    WinDestroyWindow(hwndDim);

    return (ulReturn);
}

/* ******************************************************************
 *
 *   Auto-close details
 *
 ********************************************************************/

/*
 *@@ xsdLoadAutoCloseItems:
 *      this gets the list of VIO windows which
 *      are to be closed automatically from OS2.INI
 *      and appends AUTOCLOSELISTITEM's to the given
 *      list accordingly.
 *
 *      If hwndListbox != NULLHANDLE, the program
 *      titles are added to that listbox as well.
 *
 *      Returns the no. of items which were added.
 *
 *@@added V0.9.1 (99-12-10) [umoeller]
 */

USHORT xsdLoadAutoCloseItems(PLINKLIST pllItems,   // in: list of AUTOCLOSELISTITEM's to append to
                             HWND hwndListbox)     // in: listbox to add items to or NULLHANDLE if none
{
    USHORT      usItemCount = 0;
    ULONG       ulKeyLength;
    PSZ         p, pINI;

    // get existing items from INI
    if (PrfQueryProfileSize(HINI_USER,
                            (PSZ)INIAPP_XWORKPLACE,
                            (PSZ)INIKEY_AUTOCLOSE,
                            &ulKeyLength))
    {
        // printf("Size: %d\n", ulKeyLength);
        // items exist: evaluate
        pINI = malloc(ulKeyLength);
        if (pINI)
        {
            PrfQueryProfileData(HINI_USER,
                                (PSZ)INIAPP_XWORKPLACE,
                                (PSZ)INIKEY_AUTOCLOSE,
                                pINI,
                                &ulKeyLength);
            p = pINI;
            //printf("%s\n", p);
            while (strlen(p))
            {
                PAUTOCLOSELISTITEM pliNew = malloc(sizeof(AUTOCLOSELISTITEM));
                strcpy(pliNew->szItemName, p);
                lstAppendItem(pllItems, // pData->pllAutoClose,
                              pliNew);

                if (hwndListbox)
                    WinSendMsg(hwndListbox,
                               LM_INSERTITEM,
                               (MPARAM)LIT_END,
                               (MPARAM)p);

                p += (strlen(p)+1);

                if (strlen(p))
                {
                    pliNew->usAction = *((PUSHORT)p);
                    p += sizeof(USHORT);
                }

                usItemCount++;
            }
            free(pINI);
        }
    }

    return (usItemCount);
}

/*
 *@@ xsdWriteAutoCloseItems:
 *      reverse to xsdLoadAutoCloseItems, this writes the
 *      auto-close items back to OS2.INI.
 *
 *      This returns 0 only if no error occurred. If something
 *      != 0 is returned, that's the index of the list item
 *      which was found to be invalid.
 *
 *@@added V0.9.1 (99-12-10) [umoeller]
 */

USHORT xsdWriteAutoCloseItems(PLINKLIST pllItems)
{
    USHORT  usInvalid = 0;
    PSZ     pINI, p;
    // BOOL    fValid = TRUE;
    ULONG   ulItemCount = lstCountItems(pllItems);

    // store data in INI
    if (ulItemCount)
    {
        pINI = malloc(
                    sizeof(AUTOCLOSELISTITEM)
                  * ulItemCount);
        memset(pINI, 0,
                    sizeof(AUTOCLOSELISTITEM)
                  * ulItemCount);
        if (pINI)
        {
            PLISTNODE pNode = lstQueryFirstNode(pllItems);
            USHORT          usCurrent = 0;
            p = pINI;
            while (pNode)
            {
                PAUTOCLOSELISTITEM pli = pNode->pItemData;
                if (strlen(pli->szItemName) == 0)
                {
                    usInvalid = usCurrent;
                    break;
                }

                strcpy(p, pli->szItemName);
                p += (strlen(p)+1);
                *((PUSHORT)p) = pli->usAction;
                p += sizeof(USHORT);

                pNode = pNode->pNext;
                usCurrent++;
            }

            PrfWriteProfileData(HINI_USER,
                                (PSZ)INIAPP_XWORKPLACE,
                                (PSZ)INIKEY_AUTOCLOSE,
                                pINI,
                                (p - pINI + 2));

            free (pINI);
        }
    } // end if (pData->pliAutoClose)
    else
        // no items: delete INI key
        PrfWriteProfileData(HINI_USER,
                            (PSZ)INIAPP_XWORKPLACE,
                            (PSZ)INIKEY_AUTOCLOSE,
                            NULL, 0);

    return (usInvalid);
}

/*
 *@@ fnwpAutoCloseDetails:
 *      dlg func for "Auto-Close Details".
 *      This gets called from the notebook callbacks
 *      for the "XDesktop" notebook page
 *      (fncbDesktop1ItemChanged, xfdesk.c).
 *
 *@@changed V0.9.0 [umoeller]: adjusted for new linklist.c functions
 *@@changed V0.9.19 (2002-05-23) [umoeller]: removed Ctrl+C option, which never worked
 *@@changed V0.9.19 (2002-05-23) [umoeller]: now using dialog formatter
 */

STATIC MRESULT EXPENTRY fnwpAutoCloseDetails(HWND hwndDlg, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    MRESULT mrc = (MPARAM)NULL;

    PAUTOCLOSEWINDATA pData = (PAUTOCLOSEWINDATA)WinQueryWindowPtr(hwndDlg, QWL_USER);

    switch (msg)
    {
        case WM_INITDLG:
        {
            // create window data in QWL_USER
            pData = malloc(sizeof(AUTOCLOSEWINDATA));
            memset(pData, 0, sizeof(AUTOCLOSEWINDATA));
            pData->pllAutoClose = lstCreate(TRUE);  // auto-free items
            pData->usItemCount = 0;

            pData->usItemCount = xsdLoadAutoCloseItems(pData->pllAutoClose,
                                                      WinWindowFromID(hwndDlg,
                                                                      ID_XSDI_XRB_LISTBOX));

            winhSetEntryFieldLimit(WinWindowFromID(hwndDlg, ID_XSDI_XRB_ITEMNAME),
                                   100-1);

            WinSetWindowULong(hwndDlg, QWL_USER, (ULONG)pData);

            WinPostMsg(hwndDlg, XM_UPDATE, MPNULL, MPNULL);
        }
        break;

        /*
         * WM_CONTROL:
         *
         */

        case WM_CONTROL:
            switch (SHORT1FROMMP(mp1))
            {
                /*
                 * ID_XSDI_XRB_LISTBOX:
                 *      listbox was clicked on:
                 *      update other controls with new data
                 */

                case ID_XSDI_XRB_LISTBOX:
                    if (SHORT2FROMMP(mp1) == LN_SELECT)
                        WinSendMsg(hwndDlg, XM_UPDATE, MPNULL, MPNULL);
                break;

                /*
                 * ID_XSDI_XRB_ITEMNAME:
                 *      user changed item title: update data
                 */

                case ID_XSDI_XRB_ITEMNAME:
                    if (SHORT2FROMMP(mp1) == EN_KILLFOCUS)
                    {
                        if (pData)
                        {
                            if (pData->pliSelected)
                            {
                                WinQueryDlgItemText(hwndDlg, ID_XSDI_XRB_ITEMNAME,
                                                    sizeof(pData->pliSelected->szItemName)-1,
                                                    pData->pliSelected->szItemName);
                                WinSendDlgItemMsg(hwndDlg, ID_XSDI_XRB_LISTBOX,
                                                  LM_SETITEMTEXT,
                                                  (MPARAM)pData->sSelected,
                                                  (MPARAM)(pData->pliSelected->szItemName));
                            }
                        }
                    }
                break;

                // radio buttons
                case ID_XSDI_ACL_WMCLOSE:
                // case ID_XSDI_ACL_CTRL_C:     removed V0.9.19 (2002-05-23) [umoeller]
                case ID_XSDI_ACL_KILLSESSION:
                case ID_XSDI_ACL_SKIP:
                    if (SHORT2FROMMP(mp1) == BN_CLICKED)
                    {
                        if (pData)
                        {
                            if (pData->pliSelected)
                            {
                                pData->pliSelected->usAction =
                                    (SHORT1FROMMP(mp1) == ID_XSDI_ACL_WMCLOSE)
                                        ? ACL_WMCLOSE
                                    // : (SHORT1FROMMP(mp1) == ID_XSDI_ACL_CTRL_C)
                                    //     ? ACL_CTRL_C
                                    : (SHORT1FROMMP(mp1) == ID_XSDI_ACL_KILLSESSION)
                                        ? ACL_KILLSESSION
                                    : ACL_SKIP;
                            }
                        }
                    }
                break;

                default:
                    mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
            }
        break;

        case XM_UPDATE:
        {
            // posted from various locations to wholly update
            // the dlg items
            if (pData)
            {
                pData->pliSelected = NULL;
                pData->sSelected = (USHORT)WinSendDlgItemMsg(hwndDlg,
                        ID_XSDI_XRB_LISTBOX,
                        LM_QUERYSELECTION,
                        (MPARAM)LIT_CURSOR,
                        MPNULL);
                //printf("  Selected: %d\n", pData->sSelected);
                if (pData->sSelected != LIT_NONE)
                {
                    pData->pliSelected = (PAUTOCLOSELISTITEM)lstItemFromIndex(
                                               pData->pllAutoClose,
                                               pData->sSelected);
                }

                if (pData->pliSelected)
                {
                    WinSetDlgItemText(hwndDlg, ID_XSDI_XRB_ITEMNAME,
                            pData->pliSelected->szItemName);

                    switch (pData->pliSelected->usAction)
                    {
                        case ACL_WMCLOSE:
                            winhSetDlgItemChecked(hwndDlg,
                                ID_XSDI_ACL_WMCLOSE, 1); break;
                        // case ACL_CTRL_C:     removed V0.9.19 (2002-05-23) [umoeller]
                        //     winhSetDlgItemChecked(hwndDlg,
                        //         ID_XSDI_ACL_CTRL_C, 1); break;
                        case ACL_KILLSESSION:
                            winhSetDlgItemChecked(hwndDlg,
                                ID_XSDI_ACL_KILLSESSION, 1); break;
                        case ACL_SKIP:
                            winhSetDlgItemChecked(hwndDlg,
                                ID_XSDI_ACL_SKIP, 1); break;
                    }
                }
                else
                    WinSetDlgItemText(hwndDlg,
                                      ID_XSDI_XRB_ITEMNAME,
                                      "");

                WinEnableControl(hwndDlg, ID_XSDI_XRB_ITEMNAME,
                            (pData->pliSelected != NULL));

                WinEnableControl(hwndDlg, ID_XSDI_ACL_WMCLOSE,
                            (pData->pliSelected != NULL));
                // WinEnableControl(hwndDlg, ID_XSDI_ACL_CTRL_C,
                //             (pData->pliSelected != NULL));
                WinEnableControl(hwndDlg, ID_XSDI_ACL_KILLSESSION,
                            (pData->pliSelected != NULL));
                WinEnableControl(hwndDlg, ID_XSDI_ACL_SKIP,
                            (pData->pliSelected != NULL));

                WinEnableControl(hwndDlg,
                                  ID_XSDI_XRB_DELETE,
                                  (   (pData->usItemCount > 0)
                                   && (pData->pliSelected)
                                  ));
            }
        }
        break;

        case WM_COMMAND:
            switch (SHORT1FROMMP(mp1))
            {

                /*
                 * ID_XSDI_XRB_NEW:
                 *      create new item
                 */

                case ID_XSDI_XRB_NEW:
                {
                    PAUTOCLOSELISTITEM pliNew = malloc(sizeof(AUTOCLOSELISTITEM));
                    strcpy(pliNew->szItemName, "???");
                    pliNew->usAction = ACL_SKIP;
                    lstAppendItem(pData->pllAutoClose,
                                  pliNew);

                    pData->usItemCount++;
                    WinSendDlgItemMsg(hwndDlg, ID_XSDI_XRB_LISTBOX,
                                      LM_INSERTITEM,
                                      (MPARAM)LIT_END,
                                      (MPARAM)pliNew->szItemName);
                    WinSendDlgItemMsg(hwndDlg, ID_XSDI_XRB_LISTBOX,
                                      LM_SELECTITEM, // will cause XM_UPDATE
                                      (MPARAM)(lstCountItems(
                                              pData->pllAutoClose)),
                                      (MPARAM)TRUE);
                    winhSetDlgItemFocus(hwndDlg, ID_XSDI_XRB_ITEMNAME);
                    WinSendDlgItemMsg(hwndDlg, ID_XSDI_XRB_ITEMNAME,
                                      EM_SETSEL,
                                      MPFROM2SHORT(0, 1000), // select all
                                      MPNULL);
                }
                break;

                /*
                 * ID_XSDI_XRB_DELETE:
                 *      delete selected item
                 */

                case ID_XSDI_XRB_DELETE:
                {
                    //printf("WM_COMMAND ID_XSDI_XRB_DELETE BN_CLICKED\n");
                    if (pData)
                    {
                        if (pData->pliSelected)
                        {
                            lstRemoveItem(pData->pllAutoClose,
                                          pData->pliSelected);
                            WinSendDlgItemMsg(hwndDlg, ID_XSDI_XRB_LISTBOX,
                                              LM_DELETEITEM,
                                              (MPARAM)pData->sSelected,
                                              MPNULL);
                        }
                        WinPostMsg(hwndDlg, XM_UPDATE, MPNULL, MPNULL);
                    }
                    winhSetDlgItemFocus(hwndDlg, ID_XSDI_XRB_LISTBOX);
                }
                break;

                /*
                 * DID_OK:
                 *      store data in INI and dismiss dlg
                 */

                case DID_OK:
                {
                    USHORT usInvalid;
                    if (usInvalid = xsdWriteAutoCloseItems(pData->pllAutoClose))
                    {
                        WinAlarm(HWND_DESKTOP, WA_ERROR);
                        WinSendDlgItemMsg(hwndDlg, ID_XSDI_XRB_LISTBOX,
                                          LM_SELECTITEM,
                                          (MPARAM)usInvalid,
                                          (MPARAM)TRUE);
                    }
                    else
                        // dismiss dlg
                        mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
                }
                break;

                default:  // includes DID_CANCEL
                    mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
                break;
            }
        break;

        case WM_HELP:
            cmnDisplayHelp(NULL,
                           ID_XFH_AUTOCLOSEDETAILS);
        break;

        /*
         * WM_DESTROY:
         *      clean up allocated memory
         */

        case WM_DESTROY:
        {
            lstFree(&pData->pllAutoClose);
            free(pData);
            mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
        }
        break;

        default:
            mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
        break;
    }
    return mrc;
}

#define ACL_WIDTH       200
#define ACL_LB_WIDTH    (ACL_WIDTH - COMMON_SPACING - STD_BUTTON_WIDTH)

static CONTROLDEF
    ACLIcon = CONTROLDEF_ICON_WIDEST(NULLHANDLE, ID_SDDI_ICON),
    ACLIntro = LOADDEF_TEXT_WORDBREAK(ID_XSDI_ACL_INTRO, ACL_WIDTH - 30),
    ACLListbox = CONTROLDEF_LISTBOX(ID_XSDI_XRB_LISTBOX, ACL_LB_WIDTH, 60),
    ACLNew = LOADDEF_NOFOCUSBUTTON(ID_XSDI_XRB_NEW),
    ACLDelete = LOADDEF_NOFOCUSBUTTON(ID_XSDI_XRB_DELETE),
    ACLSessionTitle = LOADDEF_TEXT(ID_XSDI_ACL_SESSIONTITLE),
    ACLEntryField = CONTROLDEF_ENTRYFIELD("", ID_XSDI_XRB_ITEMNAME, ACL_WIDTH, SZL_AUTOSIZE),
    ACLDoWhatGroup = LOADDEF_GROUP(ID_XSDI_ACL_DOWHATGROUP, SZL_AUTOSIZE),
    ACLSkipRadio = LOADDEF_FIRST_AUTORADIO(ID_XSDI_ACL_SKIP),
    ACLWMCloseRadio = LOADDEF_NEXT_AUTORADIO(ID_XSDI_ACL_WMCLOSE),
    ACLKillRadio = LOADDEF_NEXT_AUTORADIO(ID_XSDI_ACL_KILLSESSION);

static const DLGHITEM G_dlgAutoCloseDetails[] =
    {
        START_TABLE,
            START_ROW(ROW_VALIGN_CENTER),
                CONTROL_DEF(&ACLIcon),
                CONTROL_DEF(&ACLIntro),
            START_ROW(ROW_VALIGN_CENTER),
                CONTROL_DEF(&ACLListbox),
                START_TABLE,
                    START_ROW(0),
                        CONTROL_DEF(&ACLNew),
                    START_ROW(0),
                        CONTROL_DEF(&ACLDelete),
                END_TABLE,
            START_ROW(0),
                CONTROL_DEF(&G_Spacing),
            START_ROW(0),
                CONTROL_DEF(&ACLSessionTitle),
            START_ROW(0),
                CONTROL_DEF(&ACLEntryField),
            START_ROW(0),
                CONTROL_DEF(&G_Spacing),
            START_ROW(0),
                START_GROUP_TABLE(&ACLDoWhatGroup),
                    START_ROW(0),
                        CONTROL_DEF(&ACLSkipRadio),
                    START_ROW(0),
                        CONTROL_DEF(&ACLWMCloseRadio),
                    START_ROW(0),
                        CONTROL_DEF(&ACLKillRadio),
                END_TABLE,
            START_ROW(0),
                CONTROL_DEF(&G_Spacing),
            START_ROW(0),
                CONTROL_DEF(&G_OKButton),
                CONTROL_DEF(&G_CancelButton),
                CONTROL_DEF(&G_HelpButton),
        END_TABLE
    };

/*
 *@@ xsdShowAutoCloseDetails:
 *      displays the "auto-close details" dialog.
 *
 *@@added V0.9.19 (2002-05-23) [umoeller]
 */

VOID xsdShowAutoCloseDetails(HWND hwndOwner)
{
    PDLGHITEM paNew;
    if (!cmnLoadDialogStrings(G_dlgAutoCloseDetails,
                              ARRAYITEMCOUNT(G_dlgAutoCloseDetails),
                              &paNew))
    {
        HWND hwndDlg;
        char *psz;

        if (!DosScanEnv("COMSPEC", &psz))
            ACLIcon.pcszText = (char*)WinLoadFileIcon(psz, FALSE);
        else
            ACLIcon.pcszText = (char*)WinLoadPointer(HWND_DESKTOP,
                                                     cmnQueryNLSModuleHandle(FALSE),
                                                     ID_SDICON);

        if (!dlghCreateDlg(&hwndDlg,
                           hwndOwner,
                           FCF_FIXED_DLG,
                           fnwpAutoCloseDetails,
                           cmnGetString(ID_XSD_AUTOCLOSE), // "Auto-Close Non-PM Sessions"
                           paNew,
                           ARRAYITEMCOUNT(G_dlgAutoCloseDetails),
                           NULL,
                           cmnQueryDefaultFont()))
        {
            winhCenterWindow(hwndDlg);      // still hidden
            WinProcessDlg(hwndDlg);
            WinDestroyWindow(hwndDlg);
        }

        free(paNew);
    }
}

/* ------------------------------------------------------------------------- */
/*
 *@@ fntBootList:
 *  this thread is started at the beginning of xsdConfirmShutdown()
 *  and has no more than 3 seconds to complete.
 *
 *  It attempts to read the items on either Boot Manager's or AirBoot's
 *  menu and copies them to the BOOTLIST structure provided as the thread's
 *  arg. Upon completion, it posts an event semaphore provided by the caller.
 *  
 *  Retrieving Boot Manager's menu requires the use of LVM which limits
 *  its functionality to Warp v4.5x. AirBoot's menu should be usable on
 *  any version of OS/2 provided the user has installed AB v1.02 or later.
 *
 *  Portions of this code were taken from "Styler".
 *  (C) 2004 Alessandro Felice Cantatore
 *  Licensed under the GPL v2 or later.
 */

void _Optlink   fntBootList(void *arg)
{
    PBOOTLIST   pbl = (PBOOTLIST)arg;

    // this thread has to finish ASAP to ensure
    // the reboot list gets populated
    DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);

    if (CheckAirBoot(pbl))
        pbl->status = 2;
    else
    if (CheckBootMgr(pbl))
        pbl->status = 1;
    else
        pbl->status = 3;

    DosPostEventSem(pbl->hev);

    return;
}

/* ------------------------------------------------------------------------- */

BOOL CheckAirBoot(PBOOTLIST pbl)
{
    BOOL    fRtn = FALSE;
    APIRET  rc;
    HFILE   hDrive = (HFILE)-1;
    ULONG   cVols;
    char    *pErr = "unknown error";
    PAIRBOOTVOLREC pVolRec;
    BYTE    abSector[1024];

do {
    if (DosPhysicalDisk(INFO_GETIOCTLHANDLE, &hDrive, 2, "1:", 3)) {
        pErr = "DosPhysicalDisk failed\n";
        break;
    }

    // read the first track
    if (ReadDiskSector(hDrive, 0, abSector)) {
        pErr = "unable to read sector 1\n";
        break;
    }

    // is the AiRBOOT signature at offset 2 ?
    if (memcmp(AB_SIGNATURE, abSector + 2, 7))
        break;

    // check the airboot version (>= 1.02)
    if ((((PAIRBOOTMAIN)abSector)->verMaj << 8) +
         ((PAIRBOOTMAIN)abSector)->verMin < AB_VALIDVER) {
        pErr = "this requires AirBoot v1.02 or later\n";
        break;
    }

    // read the sector containing the current AIRBOOT configuration
    // to get the count of the volumes
    if (ReadDiskSector(hDrive, 54, abSector)) {
        pErr = "unable to read sector 55\n";
        break;
    }

    // check the airboot configuration signature
    if (memcmp(abSector, AB_CFGSIGNATURE, 13)) {
        pErr = "AirBoot config signature is missing\n";
        break;
    }

    // get the count of the volumes detected by airboot
    if  (!GetVolList(hDrive, (PAIRBOOTVOLREC)abSector, pbl,
                     ((PAIRBOOTCFG)abSector)->cVolumes)) {
        pErr = "unable to read sector 56 or 57\n";
        break;
    }

    fRtn = TRUE;

} while (FALSE);

    if (!fRtn)
        fputs(pErr, stderr);

    if (hDrive != (HFILE)-1)
        DosPhysicalDisk(INFO_FREEIOCTLHANDLE, NULL, 0, &hDrive, 2);

    return fRtn;
}

/* ------------------------------------------------------------------------- */

BOOL GetVolList(HFILE hDrive, PAIRBOOTVOLREC pVolRec,
                PBOOTLIST pbl, ULONG cVols)
{
    INT     i, j, k;
    char    *ptr;

    // read the airboot volume records
    if (ReadDiskSector(hDrive, 55, (PBYTE)pVolRec) ||
        ReadDiskSector(hDrive, 56, ((PBYTE)pVolRec) + 512))
        return FALSE;

    if (cVols > AB_MAXVOLS)
        cVols = AB_MAXVOLS;

    for (i = 0, j = 0; i < cVols; i++) {

        if (!(pVolRec[i].flag & AB_BOOTABLEFL))
            continue;

        ptr = pbl->achVol[j];
        memcpy(ptr, pVolRec[i].name, 11);
        ptr[11] = 0;

        // strip trailing spaces
        for (k = 10; k >= 0; k--) {
            if (ptr[k] == 0)
                continue;
            if (ptr[k] != ' ')
                break;
            ptr[k] = 0;
        }
        j++;
    }

    pbl->cVols = j;

    return TRUE;
}

/* ------------------------------------------------------------------------- */

ULONG   ReadDiskSector(HFILE hDrive, ULONG sector, PBYTE pBuf)
{
    ULONG        ulParm, cbParm, cbData;
    TRACKLAYOUT  tl;

    static DEVICEPARAMETERBLOCK dpb = {0,0,0,0,0,0,0,0};

    // get the disk geometry - this shouldn't change during a session
    if (!dpb.cCylinders) {
        ULONG   rc;
        BYTE    cmd;

        cmd = 0;
        cbParm = sizeof(cmd);
        cbData = sizeof(dpb);
        rc = DosDevIOCtl(hDrive, IOCTL_PHYSICALDISK, PDSK_GETPHYSDEVICEPARAMS,
                         &cmd, cbParm, &cbParm, &dpb, cbData, &cbData);
        if (rc)
            return rc;
    }

    cbParm = sizeof(TRACKLAYOUT);
    cbData = 512;

    tl.bCommand = 0;
    tl.usHead = (sector / dpb.cSectorsPerTrack) % dpb.cHeads;
    tl.usCylinder = sector / (dpb.cHeads * dpb.cSectorsPerTrack);
    tl.usFirstSector = 0;
    tl.cSectors = 1;
    tl.TrackTable[0].usSectorNumber = sector % dpb.cSectorsPerTrack + 1;
    tl.TrackTable[0].usSectorSize = 512;

    return (ULONG)DosDevIOCtl(hDrive, IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
                              &tl, cbParm, &cbParm, pBuf, cbData, &cbData);
}

/* ------------------------------------------------------------------------- */

BOOL CheckBootMgr(PBOOTLIST pbl)
{
    BOOL            fRtn = FALSE;
    BOOL            fEngine = FALSE;
    ULONG           rc, i, j;
    HMODULE         hmodLVM = 0;
    char            *ptr;
    char            *pErr = "unknown error";
    char            failName[16];
    OPEN_LVM_ENGINE                 Open_LVM_Engine = 0;
    CLOSE_LVM_ENGINE                Close_LVM_Engine = 0;
    FREE_ENGINE_MEMORY              Free_Engine_Memory = 0;
    GET_BOOT_MANAGER_HANDLE         Get_Boot_Manager_Handle = 0;
    GET_BOOT_MANAGER_MENU           Get_Boot_Manager_Menu = 0;
    GET_VOLUME_INFORMATION          Get_Volume_Information = 0;
    GET_PARTITION_INFORMATION       Get_Partition_Information = 0;
    Boot_Manager_Menu               bmm;
    Boot_Manager_Menu_Item          *bmmi;
    Volume_Information_Record       vir;
    Partition_Information_Record    pir;

do {
    rc = DosLoadModule(failName, sizeof(failName), "LVM", &hmodLVM);
    if (rc) {
        pErr = "DosLoadModule failed\n";
        break;
    }

    if (DosQueryProcAddr(hmodLVM, ORD_OPEN_LVM_ENGINE,          0, (PFN*)&Open_LVM_Engine) ||
        DosQueryProcAddr(hmodLVM, ORD_CLOSE_LVM_ENGINE,         0, (PFN*)&Close_LVM_Engine) ||
        DosQueryProcAddr(hmodLVM, ORD_FREE_ENGINE_MEMORY,       0, (PFN*)&Free_Engine_Memory) ||
        DosQueryProcAddr(hmodLVM, ORD_GET_BOOT_MANAGER_HANDLE,  0, (PFN*)&Get_Boot_Manager_Handle) ||
        DosQueryProcAddr(hmodLVM, ORD_GET_BOOT_MANAGER_MENU,    0, (PFN*)&Get_Boot_Manager_Menu) ||
        DosQueryProcAddr(hmodLVM, ORD_GET_VOLUME_INFORMATION,   0, (PFN*)&Get_Volume_Information) ||
        DosQueryProcAddr(hmodLVM, ORD_GET_PARTITION_INFORMATION,0, (PFN*)&Get_Partition_Information)) {
        pErr = "DosQueryProcAddr failed\n";
        break;
    }

    Open_LVM_Engine(FALSE, &rc);
    if (rc) {
        pErr = "Open_LVM_Engine failed\n";
        break;
    }
    fEngine = TRUE;

    if (!Get_Boot_Manager_Handle(&rc)) {
        pErr = "Get_Boot_Manager_Handle failed\n";
        break;
    }

    bmm = Get_Boot_Manager_Menu(&rc);
    if (rc) {
        pErr = "Get_Boot_Manager_Menu failed\n";
        break;
    }

    for (i = 0, j = 0, bmmi = bmm.Menu_Items; i < bmm.Count; i++, bmmi++)
    {
        if (bmmi->Volume) {
            ptr = vir.Volume_Name;
            pErr = "Get_Volume_Information";
            vir = Get_Volume_Information(bmmi->Handle, &rc);
        }
        else {
            ptr = pir.Partition_Name;
            pErr = "Get_Partition_Information";
            pir = Get_Partition_Information(bmmi->Handle, &rc);
        }
        if (rc)
            break;

        memcpy(pbl->achVol[j], ptr, 20);
        j++;
    }

    Free_Engine_Memory(bmm.Menu_Items);
    pbl->cVols = j;
    if (!rc)
        fRtn = TRUE;

} while (FALSE);

    if (!fRtn && pErr)
        fputs(pErr, stderr);

    if (fEngine)
        Close_LVM_Engine();

    if (hmodLVM)
        DosFreeModule(hmodLVM);

    return fRtn;
}


