/*static char *SCCSID = "@(#)dem.c      6.27 92/03/21";*/

/*LINE TAGS BEGINNING WITH 'B' ARE FOR THE CRUISER RELEASE:  OS/2 VERSION 2.0*/

#define SCCSID "@(#)dem.c       6.27 92/03/21"

/***EM  dem.c - Initialize DOS emulation for a new VDM
 *
 *      IBM Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Version 1.16.1.11, for src/dos/mvdm/dem.c, dem, mer.krnl, currbld
 *
 *      SUMMARY
 *
 *      NOTES
 *
 *
 * REVISION HISTORY
*/
/*
;********************************************************************** ;BN001;
;    - 001   2/22/91     Mark Rogalski,   IBM                           ;BN001;
;      CP20 DCR 1329  Add DOS=UMB support                               ;BN001;
;  B001  DCR 1329 - 2/27/91 TMB -  Add support for loading DOS device   ;BN001;
;                   drivers and TSRs into UMBs.                         ;BN001;
;********************************************************************** ;BN001;
;  002   3/22/91     Mark Rogalski,   IBM                               ;BN002;
;  CP20 DCR 1360  Add DOS=HIGH support                                  ;BN002;
;********************************************************************** ;BN002;
;  003   5/22/91     Mark Rogalski,   IBM                               ;BN003;
;  CP20 PTR B723192 Correct way Dos Flag is set from advanced           ;BN003;
;  properties                                                           ;BN003;
;********************************************************************** ;BN003;
; 03May91 - G.L.Fulk                                                    ;BN004;
;                                                                       ;BN004;
; The "DEVICE=name args" and "DEVICEHIGH="name args" were not forming   ;BN004;
; a proper argument list for the INIT (function 0) call to the device   ;BN004;
; driver.  The rules are as follows:                                    ;BN004;
;    DEVICE=NAME          -- no args                                    ;BN004;
;    DEVICE=NAME ARG      -- space(s) between name and args             ;BN004;
;    DEVICE=NAME,ARG      -- no space between name and args             ;BN004;
; MVDM will place a CR,LF at the end of each DEVICE= entry.             ;BN004;
; The parm list should look like this:                                  ;BN004;
;    "NAME ",lf           -- no args                                    ;BN004;
;    "NAME ARG",cr,lf     -- space(s) between name and args             ;BN004;
;    "NAME ,ARG",cr,lf    -- no space between name and args             ;BN004;
;********************************************************************** ;BN004;
; 20Nov91 - G.L.Fulk                                                    ;BN005;
; "Buf1" should be used, not "Buf2".                                    ;BN005;
;********************************************************************** ;BN005;
; 06May91 - G.L.Fulk                                                    ;BN006;
; The fix (004) didn't quite work 100% due to a C Compiler bug.         ;BN006;
;********************************************************************** ;BN006;
; BN007   6/13/91     HARE, IBM                                         ;BN007;
;   CP20 PTR B722301  Added LASTDRIVE advanced property                 ;BN007;
;********************************************************************** ;BN007;
; 09Jan92 - G.L.Fulk                                                    ;BN008;
; Add NETX.COM to the default "fake" version list.                      ;BN008;
;********************************************************************** ;BN008;
; DCR 1371 - 7/12/91 Mark Rogalski                                      ;BN009;
; Add support for FSFILTER.                                             ;BN009;
;********************************************************************** ;BN009;
; 19Jun91 - G.L.Fulk                                                    ;BN011;
; Add support for a DOS ONLY "FILES=" statement in CONFIG.SYS.          ;BN011;
;********************************************************************** ;BN011;
; 02Jul91 - G.L.Fulk                                                    ;BN012;
; Allow the Files count to be modified from a user window.              ;BN012;
;********************************************************************** ;BN012;
; 12Feb92 - G.L.Fulk                                                    ;BN013;
; Hack to fix OS/2 File System's behavior when the volume label bit is  ;BN013;
; set on a Find First.                                                  ;BN013;
;********************************************************************** ;BN014;
; 14Mar92 - K. Lee                                                      ;BN014;
; Fix while loop in demGetAppName                                       ;BN014;
;      while (ch != 0x0a || ch != '\0')                                 ;BN014;
; The above loop returns TRUE always because one of the condition always;BN014;
; meet even if ch is 0x0a for it is NOT EQUAL to '\0'.                  ;BN014;
; PTR SM10819                                                           ;BN014;
;********************************************************************** ;BN014;
; BN015   3/20/92     HARE, IBM                                         ;BN015;
;   CP20 PTR SM11194  Change demGetNumber to get a number from the      ;BN015;
;                     property line properly.                           ;BN015;
;                     Jonathan Wagner from the seamless                 ;BN015;
;                     group wrote the code.                             ;BN015;
;                     Mark Fiechtner from the device drivers group      ;BN015;
;                     verified the fix to the code.                     ;BN015;
;********************************************************************** ;BN015;
; BN016   7/30/92     HARE, IBM                                         ;BN016;
;   R202  PMR 9X039   Moved call                                        ;BN016;
;                     VOID PASCAL demSetPTDAFilesCount(USHORT);         ;BN016;
;                     from DEMCreateVDM1 to DEMCreateVDM2.              ;BN016;
;                     This was moved to enable VMBOOT sessions to       ;BN016;
;                     also use FILE= config.sys to be used as well      ;BN016;
;                     as in DOSKRNL.                                    ;BN016;
;********************************************************************** ;BN016;
; ??Aug92 - ???                                                         ;BN017;
; Dual thread???                                                        ;BN017;
;********************************************************************** ;BN017;
; 21Aug92 - G.L.Fulk                                                    ;BN018;
; Add DOS_AUTOEXEC support.                                             ;BN018;
;********************************************************************** ;BN018;
; 28Aug92 - G.L.Fulk                                                    ;BN019;
; Open and close the event semaphores for DosStartSession.              ;BN019;
; EventSem "sem32\vdmstart\done".  Feature #26159.                      ;BN019;
;********************************************************************** ;BN019;
; 31Aug92 - G.L.Fulk                                                    ;BN020;
; Move FF hack variables to instance instead of global space.           ;BN020;
;********************************************************************** ;BN020;
; 29Sep92 - G.L.Fulk                                                    ;BN021;
; DOS_AUTOEXEC blank implies no Autoexec.bat to be executed.            ;BN021;
; So add a /D in this case.                                             ;BN021;
;********************************************************************** ;BN021;
; 12Nov92 - G.L.Fulk                                                    ;BN022;
; Allow DOS_AUTOEXEC to pass parameters.                                ;BN022;
;********************************************************************** ;BN022;
; 60501  1/25/93  Mark Rogalski                                         ;BN023;
; Add entry to fake version table for MSD.EXE.                          ;BN023;
;********************************************************************** ;BN023;
; 07Feb93 - Fulkgl                                                      ;61716;
; Change multi_autoexec to use /P: instead of /A.                       ;61716;
;********************************************************************** ;61716;
; 18Feb93 - Fulkgl                                                      ;58348;
; Add a new parameter to DosCreateVDM to pass current drive and sub-    ;58348;
; directory information.                                                ;58348;
;********************************************************************** ;58348;
; 02Apr93 - Fulkgl                                                      ;65296;
; Add NETX.EXE to the DOS_VERSION list.                                 ;65296;
;********************************************************************** ;65296;
; 06Apr93 - Fulkgl                                                      ;65583;
; Change the bad design of starting a VDM with ProgPath==ShellSpec and  ;65583;
; not including the ShellArgs.                                          ;65583;
;********************************************************************** ;65583;
; 13Apr93 - Fulkgl                                                      ;65622;
; Change the sync of DemStartSession to prevent video from hanging.     ;65622;
;********************************************************************** ;65622;
; 03Jun93 - Fulkgl                                                      ;70132;
; Correct bug if a 4th DOS_VERSION number is entered.  The logic needed ;70132;
; a AND instead of OR during a comparison.                              ;70132;
;********************************************************************** ;70132;
; 13Jul93 - Fulkgl                                                      ;70592;
; Pass in the current drive and sub-dir.                                ;70592;
;********************************************************************** ;70592;
; 07Sep94 - whh                                                         ;95072;
; Add a routine to parse label names                                    ;95072;
;**********************************************************************
; 19Sep94 - bjn
; Change segment names for page tuning
;**********************************************************************
; 19Jul96 - Gil Pratte - defect 141181
; Change the prototype for demSetPTDAFilesCount to return a ULONG
;**********************************************************************
; 02Aug96 - johnha,gkuo                                                ;160978;
; Remove return if FCBKeep > FCB. and set FCBKeep to FCB.              ;160978;
;**********************************************************************;160978;
*/


#define FNO_NUM  FNO_DEM_C
#include <fnomvdm.h>

#define DEMSTRICT
#define INCL_NONE
#define INCL_MVDMTYPES
#define INCL_VDHALL
#include <vdmvdh.h>
#include <limits.h>
#include <sft.h>
#include <kmode.h>
#include <dem.h>
#include <propname.h>
#include <string.h>
#include <seldesc.h>
#define FOR_EXEHDR      1
#include <newexe.h>
#include <exe386.h>
#include <ldrvars.h>
#include <advprop.h>                                                  /*BN011*/
#include <demsmg.h>                                                   /*BN019*/


extern VOID      PASCAL    demSetCDS(PVOID);                          /*70592*/
extern HOOKENTRY           VDMInt24Handler(PCRF pcrf);
extern INT       PASCAL    demTransPath(PSZ, ULONG);
extern VOID      PASCAL    VDMMstrncpyz(ULONG, PSZ, PSZ);
extern INT                 ldrNextLibPath();
extern PUCHAR              LDRLibPath;
extern VOID      PASCAL    demNoInheritFile(VOID);
extern ULONG     PASCAL    demSetPTDAFilesCount(USHORT);    /*BN011 141181 */
extern ULONG     PRIVENTRY vdmmGetValueFromString (PSZ,ULONG);
extern BOOL      PRIVENTRY vdmmStrcmpi(PSZ,PSZ);
extern ULONG     EXPENTRY  ValidateLastDrive(ULONG,HVDM,ULONG,CHAR *);/*BN007*/
extern ULONG     EXPENTRY  ValidateFilesCount(ULONG,HVDM,ULONG,CHAR *);/*BN012*/
extern ULONG     EXPENTRY  ValidateAutoexec(ULONG,HVDM,ULONG,CHAR *); /*BN018*/
extern INT       PASCAL    dem32OpenEventSem  (PSZ,PHEV);             /*BN019*/
extern INT       PASCAL    dem32CloseEventSem (HEV);                  /*BN019*/

extern struct InfoSegGDT SISData;
extern ULONG  vdmid_flVDM;
extern PSZ    pszDemCurrDir;                                          //70592
extern char   NumIO;                                                  /*BN007*/
extern char   NumIORipl;                                              /*BN007*/



#pragma BEGIN_MVDM_INIT_DATA
/*** Init Data ***/

/***LD+ szDefaultApps - An asciiz string which lists the apps
 *                      for whcih DOS version has to be faked.
 *      Format:
 *          ProgName,FakeMajorVer,FakeMinorVer,FakeCount
 *      ProgName       should follow 8.3 rule
 *      FakeMajorVer   Major Version number to be faked (3 for 3.X)
 *      FakeMinorVer   Minor Version number to be faked (10 for X.10)
 *      FakeCount      0     means give normal version (i.e. 20.0)
 *                     255   means always fake the version.
 *                     n     (where n < 255) means fake n times
 *                           before giving normal version.
 *      Notes:
 *                     a value bigger than 255 will be treated as 255.
 *                     any value which preceeds with a '-' will also
 *                     be treated as 255.
 */
/*;BN008; NETX.COM below*/
CHAR    szDefaultApps[] =
"DCJSS02.EXE,3,40,255\n\
DFIA0MOD.SYS,3,40,255\n\
DXMA0MOD.SYS,3,40,255\n\
EXCEL.EXE,10,10,4\n\
IBMCACHE.COM,3,40,255\n\
IBMCACHE.SYS,3,40,255\n\
ISAM.EXE,3,40,255\n\
ISAM2.EXE,3,40,255\n\
ISQL.EXE,3,40,255\n\
MSD.EXE,5,00,255\n\
NET3.COM,3,40,255\n\
NETX.COM,4,00,255\n\
NETX.EXE,5,00,255\n\
PSCPG.COM,3,40,255\n\
SAF.EXE,3,40,255\n\
WIN200.BIN,10,10,4";                                      /*BN023*/   /*65296*/

#pragma END_MVDM_INIT_DATA



#pragma BEGIN_MVDM_SWAP_DATA
/*** Global Data ***/

CHAR    demStartSession_CDS[68];                                      /*70592*/
CHAR    szAutoexec[] = PROP_NAME_AUTOEXEC;                            /*BN018*/
CHAR    szFakeVersion[] = PROP_NAME_FAKE_VERSION;
CHAR    szLastDrive[] = PROP_NAME_LASTDRIVE;                          /*BN007*/
CHAR    szDOSFiles[] = PROP_NAME_FILES;                               /*BN011*/
VPBOUND vpboundDOSFiles={MinFILESCount,MaxFILESCount,1};              /*BN011*/
PFAKEVERINFO pVerInfoHeadGlobal=NULL;
SZ      szRealTime = PROP_NAME_INT_DURING_IO;                         /*BN017*/
SZ      szVPICName = "VPIC";                                          /*BN017*/
HVDD    hvddVPIC=NULL;                                                /*BN017*/
PFN     pfnSlaveRequest=NULL;                                         /*BN017*/
DEMSTARTDATA demStartSession_data;                                    /*BN019*/
CHAR    szAutoexecSpec[] = "c:\\AUTOEXEC.BAT";                        /*BN021*/
/* CHAR demStartSession_CDS;                                            70592*/
ULONG   demSS_next = 0;                                               /*65622*/
ULONG   demSS_curr = 0;                                               /*65622*/

#pragma END_MVDM_SWAP_DATA



#pragma BEGIN_MVDM_INSTANCE_DATA
/*** EXTERNAL INSTANCE REFERENCES ***/

extern  SEL aseldemTemp[];
extern  USHORT segV86Kernel;
extern  SEL sftFCB;
extern  USHORT KeepCount;

#pragma END_MVDM_INSTANCE_DATA



#pragma BEGIN_MVDM_SWAP_INSTANCE
/*** INSTANCE DATA ***/

CHAR    demStartSession_CDS_lowmem[68];                               /*70592*/
PULONG  demFindFirstHack_head = NULL;                                 /*BN020*/
VMKHBI  demAllocBlkVMKHBI;                                            /*BN020*/

ULONG   demSS_us;                                                     /*65622*/
ULONG   demVDMStartCount;                                             /*65622*/
HEV     demVDMStartDoneHndl  = NULL;                                  /*BN019*/
CHAR    demVDMStartDoneSem[] = VDMStartDoneSem_name;                  /*BN019*/

extern  VPDOSDDTYPE vpDosDDHead;

PVOID pFCBSFT = (PVOID)NULL;

PSZ   pszV86KernelName = "DOSKRNL";       // V86 kernel name
CHAR  pszV86KernelSpec[MAXPATHLEN];       // V86 kernel with full path
PFAKEVERINFO    pVerInfoHeadLocal;        // VDM's fake version list head
USHORT          flVDMDEM=0;               // Dos Emulation Flags      /*BN017*/

#pragma END_MVDM_SWAP_INSTANCE



/*** LOCAL CONSTANTS ***/

#ifdef PMDEBUG
#define DEMLOCAL
#else
#define DEMLOCAL static
#endif

#define ARENA_PAD       0x8000
#define DOS_UMB_BIT        0x01                                 /*BN003*/
#define DOS_HIGH_BIT       0x02                                 /*BN003*/



/*** LOCAL MACROS ***/

#define MAKE_FPTR(dest, sel, off) { \
                    SEGMENTOF32(dest) = sel; \
                    OFFSETOF32(dest) = off; \
                    }

#define FAR16_FROM_FLAT(pSegOff, pFlat) { \
                    SEGMENTOF16(pSegOff) = (USHORT) (((ULONG) (pFlat)) >> 4); \
                    OFFSETOF16(pSegOff) = ((USHORT) (pFlat)) & (USHORT) 0xF; \
                    }

/*
 *  LOCAL FUNCTIONS
 */

DEMLOCAL INT PASCAL demLoadV86Kernel(PBYTE, ULONG, PULONG);
DEMLOCAL INT PASCAL demInitFCBSFT(USHORT, USHORT);
DEMLOCAL INT PASCAL demAllocFCBSFT(USHORT);
DEMLOCAL VOID PASCAL demFreeSelTemp(INT);
DEMLOCAL INT PASCAL demInitV86Area(PBYTE, ULONG, PBYTE, PSZ, PBYTE, USHORT);
DEMLOCAL INT PASCAL demGetV86Kernel(PHFILE,PULONG);

ULONG    EXPENTRY   demSetFakeVersion(ULONG,HVDM,ULONG,PCHAR);
ULONG    PRIVENTRY  demMakeFakeList(PSZ,PFAKEVERINFO *);
VOID     PRIVENTRY  demDestroyFakeList(PFAKEVERINFO);
PSZ      PRIVENTRY  demGetAppName(PSZ *,PUCHAR);
BOOL     PRIVENTRY  demProcessEntries (PSZ,ULONG,PUSHORT,PUCHAR);
BOOL     PRIVENTRY  demGetNumber(PSZ *,PUCHAR, CHAR);
VOID                demParseLabel (PSZ,PSZ);    /* #95072 */

#pragma BEGIN_MVDM_INIT_CODE

/***EP+ DEMInit - Initialize DOS Emulation
 *
 *      ENTRY
 *          None
 *
 *      EXIT
 *          SUCCESS - returns NO_ERROR
 *          FAILURE - returns !NO_ERROR
 *
 *      STEPS
 *          Register DOS_fVERSION_FAKE adv. property
 *
 *      CONTEXT
 *          System Initialization
 */

INT PRIVENTRY DEMInit(VOID)
{

    // Register the property
    if(VDHRegisterProperty (szFakeVersion,        //property name
                            NULL,                 //reserved (help file asciiz)
                            0L,                   //reserved (help id number)
                            VDMP_MLSTR,           //property type=MLE
                            VDMP_ORD_VERSION,     //property ordinal
                            0L,                   //flag=alter anytime
                            (PVOID)szDefaultApps, //default value
                            (PVOID)1023,          //validate data, 1023=maxsize
                            demSetFakeVersion) == FALSE){  //validation proc
        return(VDHGetError());
    }

    szAutoexecSpec[0] =(UCHAR)((((USHORT)'A')-1)+SISData.SIS_BootDrv);/*BN021*/
    if(VDHRegisterProperty (szAutoexec,                               /*BN018*/
                            NULL,                                     /*BN018*/
                            0L,                                       /*BN018*/
                            VDMP_STRING,                              /*BN018*/
                            VDMP_ORD_AUTOEXEC,                        /*BN018*/
                            VDMP_CREATE,                              /*BN018*/
                            (PVOID)szAutoexecSpec,                    /*BN021*/
                            (PVOID)MAX_AUTOEXEC_SIZE,                 /*BN018*/
                            ValidateAutoexec) == FALSE){              /*BN018*/
        return(VDHGetError());                                        /*BN018*/
    }                                                                 /*BN018*/

    if(VDHRegisterProperty (szLastDrive,                              /*BN007*/
                            NULL,                                     /*BN007*/
                            0L,                                       /*BN007*/
                            VDMP_STRING,                              /*BN007*/
                            VDMP_ORD_LASTDRIVE,                       /*BN007*/
                            VDMP_CREATE,                              /*BN007*/
                            (PVOID)"Z",                               /*BN007*/
                            (PVOID)PROPERTY_MAX_DRIVE_STR,            /*BN007*/
                            ValidateLastDrive) == FALSE){             /*BN007*/
        return(VDHGetError());                                        /*BN007*/
    }                                                                 /*BN007*/
                                                                      /*BN011*/
    if(VDHRegisterProperty (szDOSFiles,      /*name*/                 /*BN011*/
                            NULL,            /*no help file*/         /*BN011*/
                            0L,              /*no help id*/           /*BN011*/
                            VDMP_INT,        /*type=integer*/         /*BN011*/
                            VDMP_ORD_FILES,  /*ordinal*/              /*BN011*/
                            0,               /*flags*/                /*BC012*/
                            (PVOID)DefaultFILESCount,/*default value*//*BN011*/
                            (PVOID)&vpboundDOSFiles,  /*validation*/  /*BN011*/
                            ValidateFilesCount)/*verify/set function*//*BC012*/
                                               == FALSE){             /*BN011*/
        return(VDHGetError());                                        /*BN011*/
    }                                                                 /*BN011*/

    // Set up the internal list of fake entries.
    return (demMakeFakeList (szDefaultApps,&pVerInfoHeadGlobal));
} /*DEMInit()*/

#pragma END_MVDM_INIT_CODE




#pragma code_seg("SWAP32_23","CODE")
/***EP+ DEMCreateVDM1 - Early Dos emulation VDM creation routine
 *
 *      This routine is called before VDDs are called.  Allocates
 *      temporary selectors for DOS emulation.
 *
 *      ENTRY
 *          None.
 *
 *      EXIT
 *          SUCCESS
 *              Returns NO_ERROR
 *
 *          FAILURE
 *              Returns non-zero error code
 *
 *      CONTEXT
 *          VDM Task (VDM Create)
 *
 *      STEPS
 *          Allocate temporary selectors
 *          Initialize SFT/FCB structures.                      BN009
 *
 *      WARNINGS
 *          None
 */

INT PASCAL
DEMCreateVDM1()
{
    register int erc, i;
    USHORT cFCBs, cFCBsNoClose;                                       /*BN009*/
    int    rc;                                                        /*BN019*/

    /* We need some selectors before doing anything else */
    for (i=0; i<CSELDEMTEMP; i++) {
        if ((erc = SELAllocGDT(&aseldemTemp[i])) != NO_ERROR) {
            demFreeSelTemp(i);
            return(erc);
        }
    }

    // Set the current drive and sub-directory                          //70592
    if (pszDemCurrDir && *pszDemCurrDir)                                //70592
        demSetCDS(pszDemCurrDir);                                       //70592
                                                                        //70592
    /* moved this call here so both VMBOOT and DOSKRNL will get the *//*BN016*/
    /* files= config.sys support.                                   *//*BN016*/
    demSetPTDAFilesCount((USHORT)vdmQueryProperty(VDMP_ORD_FILES));   /*BN016*/

    /* We dont want to inherit any open file from parent process */
    demNoInheritFile();

    cFCBs = (USHORT)vdmQueryProperty(VDMP_ORD_FCB);                  /*BN009*/
    cFCBsNoClose = (USHORT)vdmQueryProperty(VDMP_ORD_FCB2);          /*BN009*/
                                                                     /*BN009*/
    /* Initialize the FCB SFT's */                                   /*BN009*/
    if((erc = demInitFCBSFT(cFCBs, cFCBsNoClose)) != NO_ERROR) {     /*BN009*/
        return(erc);                                                 /*BN009*/
    }                                                                /*BN009*/

    // If we have enabled the slave thread, get address of slave */   /*BN017*/
    if(VDHQueryProperty(szRealTime)) {                                /*BN017*/
        if(hvddVPIC==NULL)                                            /*BN017*/
            hvddVPIC = VDHOpenVDD(szVPICName);                        /*BN017*/
        if((hvddVPIC!=NULL)&&(pfnSlaveRequest==NULL))                 /*BN017*/
            VDHRequestVDD(hvddVPIC,                                   /*BN017*/
                          0,                                          /*BN017*/
                          VPIC_SLAVE_ADDRESS,                         /*BN017*/
                          NULL,                                       /*BN017*/
                          (PVOID)&pfnSlaveRequest);                   /*BN017*/
        if(pfnSlaveRequest!=NULL)                                     /*BN017*/
            flVDMDEM |= DEM_SLAVE_THREAD;                             /*BN017*/
        }                                                             /*BN017*/

    if (!demVDMStartDoneHndl) {                                       /*BN019*/
        rc=dem32OpenEventSem (  demVDMStartDoneSem,                   /*BN019*/
                               &demVDMStartDoneHndl );                /*BN019*/
        }                                                             /*BN019*/

    return(NO_ERROR);
} /*DEMCreateVDM1()*/

#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")

/***EP+ DEMCreateVDM2 - Initialize VDOS for a new VDM
 *
 *      Last step of VDM create-time initialization, sets up VDOS
 *
 *      ENTRY
 *          pszProgPath
 *              The program that the shell is to run
 *          pszProgArgs
 *              The arguments to the program that the shell is
 *              to run.
 *          pArena
 *              The base linear address of the memory arena.
 *          ulcbArena
 *              The size, in bytes, of the memory arena.
 *          cb
 *              The size, in bytes, of the failure cause buffer.
 *          pbuf
 *              The failure cause buffer.  If the failure cause does
 *              not fit in the supplied buffer, it is truncated on the
 *              right.
 *
 *      EXIT
 *          SUCCESS
 *              Returns NO_ERROR
 *
 *          FAILURE
 *              Returns non-zero error code
 *
 *      CONTEXT
 *          VDM Task (VDM Create)
 *
 *      STEPS
 *          Call demLoadV86Kernel() to load V86 kernel into memory
 *          Initialize V86 Registers (CS:IP/SS:SP)
 *          Initialize V86 Stack Parameters
 *
 *      NOTES
 *          We set up a list of parameters on the new V86 stack
 *          which refer to big strings in high V86 memory.  We
 *          tell the V86 Init code about this in the stack
 *          parameters.
 *
 *      WARNINGS
 *          None
 *
 *      CALLS
 *          demLoadV86Kernel
 *          KMSetClientRegister
 *
 */

INT PASCAL
DEMCreateVDM2(pszProgPath, pszProgArgs, pArena, ulcbArena, cb, pbuf)
PSZ   pszProgPath;
PSZ   pszProgArgs;
PBYTE pArena;
ULONG ulcbArena;
ULONG cb;
PSZ   pbuf;
{
// @SEC - reduce stack use - get these off the stack.
//  CHAR     szShellSpec[RMPATHLEN];                                     //@SEC
//  CHAR     abShellArgs[MAXSHELLARGS];                                  //@SEC
    PCHAR    pszShellSpec = NULL;                                        //@SEC
    PCHAR    pabShellArgs = NULL;                                        //@SEC
    ULONG    cbKernel;
    PCHAR    pV86KernelEntry;
    USHORT   iBreak;
    register PCHAR p;
    register INT i, erc = 0;                                             //@SEC
//61716 INT      i2,iCaps;               //BN021
    PCHAR    pszShell = NULL;        // returned by vdmQueryProperty     //@SEC
    PCHAR    pszAutoexec = NULL;  // qualified path of "autoexec.bat" /*BN018*/ //@SEC
    CHAR     Buf1[PMPATHLEN];        // Temp Buffer
    CHAR     Buf2[PMPATHLEN];        // Temp Buffer
    PSZ      pszFakeVerEntries=NULL; // BN015
    CHAR     ch1;                                                     /*61716*/
    PCHAR    p2,p1;                                                   /*61716*/

    // Initialize the failure buffer to a null string
    *pbuf = '\0';

    /* Align the arena to paragraph boundaries */
    p = (PCHAR) ( (((ULONG) pArena) + 0xF) & 0xFFFFFFF0 );
    ulcbArena -= ((ULONG) p) - ((ULONG) pArena);
    pArena = p;
    ulcbArena &= 0xFFFFFFF0;

    /* Load the V86 Kernel Code (aka MSDOS.SYS/IBMDOS.COM) */
    /* Leave an arbitrary amount for breathing space       */

    if ((erc = demLoadV86Kernel(pArena, ulcbArena - 8192,
      SSToDS(&cbKernel))) != NO_ERROR) {
        VDMMstrncpyz(cb, (PSZ)pszV86KernelSpec, pbuf);
        return(erc);
    }

    cbKernel = (cbKernel + 0xF) & 0xFFFFFFF0;
    pV86KernelEntry = pArena;
    pArena += cbKernel;
    ulcbArena -= cbKernel;

    /* Get the break flag */
    iBreak = (USHORT)vdmQueryProperty(VDMP_ORD_BREAK);

    /*
     *   Create the string to pass to COMMAND.COM
     *
     *   Make sure that the argument string does not exceed 126 bytes.
     *   The argument buffer is 128 bytes, which leaves one byte before
     *   the string for the string length (ala Pascal), and one byte
     *   after the string for an ASCII Carriage-Return (CR) character.
     */

    /* Get a pointer to the appropriate shell command line */
    /* Must sfree(pszShell) after done using it! */
    pszShell = (char *)vdmQueryProperty(VDMP_ORD_SHELL);

    if ((erc = VMAllocKHB(VM_HKH_PUB_SWAPRW,RMPATHLEN,(VMHOB)EM86MALLOCOWNER,  //@SEC
                         0,SSToDS(&pszShellSpec))) != 0)                       //@SEC
        goto FREE_MEM;   // will return erc to caller                          //@SEC
    // alloc pabShellArgs off of heap                                          //@SEC
    if ((erc = VMAllocKHB(VM_HKH_PUB_SWAPRW,RMPATHLEN,(VMHOB)EM86MALLOCOWNER,  //@SEC
                         0,SSToDS(&pabShellArgs))) != 0)                       //@SEC
        goto FREE_MEM;    // will return erc to caller                         //@SEC

    p = pszShell;

    /* copy the shell's pathname (which is up to a blank, tab or null) */
    for(i = 0; *p && *p != ' ' && *p != c_HT; i++, p++) {
        if(i >= RMPATHLEN) {
            erc = ERROR_BAD_ARGUMENTS;                                   //@SEC
            goto  FREE_MEM;  // will return erc to caller                //@SEC
        }
        pszShellSpec[i] = *p;                                            //@SEC
    }

    /* append a NULL to the shell's pathname */
    pszShellSpec[i] = '\0';                                              //@SEC

    /* TransPath the shell's pathname into Buf2 for later */
    if((erc = demTransPath(pszShellSpec, (ULONG)&Buf2)) != NO_ERROR) {   //@SEC
       goto FREE_MEM;         // will return error to caller             //@SEC
    }

    /* copy the shell's arguments into argument buffer */
    pabShellArgs[1] = ' ';                                               //@SEC
    for(i = 1; *p; i++, p++) {
        if (i >= MAXSHELLARGS) {
            erc = ERROR_BAD_ARGUMENTS;                                   //@SEC
            goto FREE_MEM;    // will return error to caller             //@SEC
        }
        pabShellArgs[i] = *p;                                            //@SEC
    }

    /* free shell path+args */
    // VMFreeKHB(VM_HKH_PUB_SWAPRW, pszShell);           //@SEC freed later on


/*
name of program = ShellSpec
Arguments to exec:          <ARGS>
if(ProgPath=NULL)      ShellArgs[' /P:"'autoexec'"']              ' 'ProgArgs
else                   ShellArgs[' /P:"'autoexec'"']' /C 'ProgPath' 'ProgArgs

if(ProgPath=NULL)      title changes ok?
else                   NoTitle bit
*/


    /* if a program name exists, this code depends on vdmmStrDup making
       the pointer NULL for zero length strings */
    if(pszProgPath != NULL) {

        vdmid_flVDM &= ~VDM_TITLECHANGE;    // No title Change for this VDM

        /* TransPath the program name into Buf1 for the string compare */
        if((erc = demTransPath(pszProgPath, (ULONG)&Buf1))  != NO_ERROR) {
            goto FREE_MEM;                                               //@SEC
        }

        /* if program name == shell name, just start shell */
        if(strcmp(SSToDS(&Buf2), SSToDS(&Buf1)) == 0) {
            pszProgPath = NULL;
//65583     i = 1;
        }
    }


    /* Multi-autoexec specification */                                /*BN018*/
    pszAutoexec = (char *)vdmQueryProperty(VDMP_ORD_AUTOEXEC);        /*BN018*/
                                                                      /*61716*/
    /* strip leading white space */                                   /*61716*/
    p1=pszAutoexec;                                                   /*61716*/
    while (' '==*p1) p1++;                                            /*61716*/
                                                                      /*61716*/
    /* If DOS_AUTOEXEC is not blank and not "*" then process: */      /*61716*/
    if ((*p1) && (('*'!=*p1) || (*(p1+1))) ) {                        /*61716*/
                                                                      /*61716*/
        /* copy " /P" into argument buffer */                         /*61716*/
        if ((i+2) >= MAXSHELLARGS) {                                  /*61716*/
            erc = ERROR_BAD_ARGUMENTS;                                   //@SEC
            goto  FREE_MEM;                                              //@SEC
        }                                                             /*61716*/
        pabShellArgs[i++] = ' ';                              //@SEC  /*61716*/
        pabShellArgs[i++] = '/';                              //@SEC  /*61716*/
        pabShellArgs[i++] = 'P';                              //@SEC  /*61716*/
                                                                      /*61716*/
        /* compare strings until EOLN or until a difference */        /*61716*/
        p2=p1;                                                        /*61716*/
        p=szAutoexecSpec;                                             /*61716*/
        while ((*p) && (*p2)) {                                       /*61716*/
            //convert character to upper case                         /*61716*/
            ch1 = *p2;                                                /*61716*/
            if (('a'<=ch1) && (ch1<='z')) ch1-='a'-'A';               /*61716*/
            if (ch1 != *p) break;                                     /*61716*/
            p2++;                                                     /*61716*/
            p++;                                                      /*61716*/
        }                                                             /*61716*/
                                                                      /*61716*/
        // *p2==0 *p==0  Strings are identical; no multi_Auto. /P     /*61716*/
        // *p2==0 *p!=0  Short name; multi_Auto, no quotes.    /P:psz /*61716*/
        // *p2!=0 *p==0  identical, plus appendages                   /*61716*/
        // *p2!=0 *p!=0  different                                    /*61716*/
                                                                      /*61716*/
        /* determine is quotes are needed */                          /*61716*/
        if (*p2) {                                                    /*61716*/
            if ((*p) || (' '!=*p2)) {                                 /*61716*/
                p=szAutoexecSpec;       /* force *p==TRUE */          /*61716*/
                /* skip until the EOLN or a blank is found */         /*61716*/
                while ((*p2) && (' '!=*p2)) p2++;                     /*61716*/
            }                                                         /*61716*/
            /* skip white space */                                    /*61716*/
            while (' '==*p2) p2++;                                    /*61716*/
            if (*p2) p=szAutoexecSpec;  /*if quotes, force mult_A*/   /*61716*/
        }                                                             /*61716*/
                                                                      /*61716*/
        // TRUE/FALSE *p2 == quotes needed                            /*61716*/
        // TRUE/FALSE *p  == multi_Autoexec                           /*61716*/
                                                                      /*61716*/
        /* do we have any multi-Autoexecs */                          /*61716*/
        if (*p) {                                                     /*61716*/
                                                                      /*61716*/
            /* write the colon to make " /P:" */                      /*61716*/
            if (i >= MAXSHELLARGS) {                                  /*61716*/
                erc = ERROR_BAD_ARGUMENTS;                               //@SEC
                goto FREE_MEM;                                           //@SEC
            }                                                         /*61716*/
            pabShellArgs[i++] = ':';                           //@SEC /*61716*/
            /* do we have an opening quote mark */                    /*61716*/
            if (*p2) {                                                /*61716*/
                if (i >= MAXSHELLARGS) {
                    erc = ERROR_BAD_ARGUMENTS;                 // @SEC
                    goto FREE_MEM;
                }                                                     /*61716*/
                pabShellArgs[i++] = '"';                       //@SEC /*61716*/
            }                                                         /*61716*/
                                                                      /*61716*/
            /* copy the DOS_AUTOEXEC setting into argument buffer */  /*61716*/
            while (*p1) {                                             /*61716*/
                if (i >= MAXSHELLARGS) {                              /*61716*/
                   erc = ERROR_BAD_ARGUMENTS;                            //@SEC
                   goto FREE_MEM;                                        //@SEC
                }                                                     /*61716*/
                pabShellArgs[i++] = *p1;                       //@SEC /*61716*/
                p1++;                                                 /*61716*/
            }                                                         /*61716*/
                                                                      /*61716*/
            /* do we have a closing quote mark */                     /*61716*/
            if (*p2) {                                                /*61716*/
                if (i >= MAXSHELLARGS) {                              /*61716*/
                   erc = ERROR_BAD_ARGUMENTS;                            //@SEC
                   goto FREE_MEM;                                        //@SEC
                }                                                     /*61716*/
                pabShellArgs[i++] = '"';                       //@SEC /*61716*/
            }                                                         /*61716*/
        }                                                             /*61716*/
                                                                      /*61716*/
    } /*if pszAutoexec*/                                              /*BN018*/
                                                                      /*BN021*/
    //@SEC VMFreeKHB(VM_HKH_PUB_SWAPRW, pszAutoexec);                 /*BN018*/


    /* if a program name exists */
    if(pszProgPath != NULL) {

        /* copy the "/C" option into argument buffer */
        for(p = " /C "; *p; i++, p++) {
            if (i >= MAXSHELLARGS) {
                erc = ERROR_BAD_ARGUMENTS;                               //@SEC
                goto FREE_MEM;                                           //@SEC
            }
            pabShellArgs[i] = *p;                                        //@SEC
        }

        /* copy the program name into argument buffer */
        for(p = pszProgPath; *p; i++, p++) {
            if (i >= MAXSHELLARGS) {
                erc = ERROR_BAD_ARGUMENTS;                               //@SEC
                goto FREE_MEM;                                           //@SEC
            }
            pabShellArgs[i] = *p;                                        //@SEC
        }
    }

    /* if program arguments exists */
    if(pszProgArgs != NULL) {
        pabShellArgs[i++] = ' ';                                         //@SEC
        /* copy the program arguments into the argument buffer */
        for(p = pszProgArgs; *p; i++, p++) {
            if (i >= MAXSHELLARGS) {
                erc = ERROR_BAD_ARGUMENTS;                               //@SEC
                goto FREE_MEM;                                           //@SEC
            }
            pabShellArgs[i] = *p;                                        //@SEC
        }
    }

    /* set the argument buffer's length and terminate with a CR, NULL */
    if(i + 1 >= MAXSHELLARGS) {
          erc = ERROR_BAD_ARGUMENTS;                                     //@SEC
          goto FREE_MEM;                                                 //@SEC
    }
    pabShellArgs[0] = (UCHAR) (i - 1);   /* set the length field */      //@SEC
    pabShellArgs[i] = '\r';              /* append a CR */               //@SEC
    pabShellArgs[i + 1] = '\0';          /* append a NULL */             //@SEC

    /* initialize the V86 registers and memory arena */
    if((erc = demInitV86Area(pArena, ulcbArena,
      pV86KernelEntry, pszShellSpec, pabShellArgs, iBreak))              //@SEC
        != NO_ERROR) {
           goto FREE_MEM;                                                //@SEC
    }
    VDHInstallIntHook(CURRENT_VDM, 0x24, VDMInt24Handler, VDH_ASM_HOOK);

    // Make fake version list for this VDM
    pszFakeVerEntries = (PSZ)vdmQueryProperty(VDMP_ORD_VERSION);
    if((erc = demMakeFakeList (pszFakeVerEntries,&pVerInfoHeadLocal))
            != NO_ERROR)
        goto FREE_MEM;                                                   //@SEC

    erc = NO_ERROR;                                                      //@SEC
    /* free shell path+args */                                           //@SEC
    // Will return erc to caller                                         //@SEC
    FREE_MEM:                                                            //@SEC
       if (pszShell)                                                     //@SEC
          VMFreeKHB(VM_HKH_PUB_SWAPRW, pszShell);                        //@SEC
                                                                         //@SEC
       if (pszShellSpec)   // need to free pszShellSpec ??               //@SEC
          VMFreeKHB(VM_HKH_PUB_SWAPRW, pszShellSpec);                    //@SEC
                                                                         //@SEC
       if (pabShellArgs)   // pabShellArgs needs to be freed             //@SEC
          VMFreeKHB(VM_HKH_PUB_SWAPRW, pabShellArgs);                    //@SEC
                                                                         //@SEC
       if (pszAutoexec)   // Need to free it                             //@SEC
          VMFreeKHB(VM_HKH_PUB_SWAPRW, pszAutoexec);                     //@SEC
                                                                         //@SEC
       if (pszFakeVerEntries)                                            //@SEC
          VMFreeKHB(VM_HKH_PUB_SWAPRW, pszFakeVerEntries);     // BN015  //@SEC
  return(erc);                                                           //@SEC

} /*DEMCreateVDM2()*/

#pragma code_seg()
#pragma code_seg("SWAP32_27","CODE")

/***EP+ DEMDestroyVDM1 - called for VDM creation failure cleanup
 *
 *      Called after DEMCreateVDM1 was called if a VDM creation
 *      fails or a VDM terminates.
 *
 *      ENTRY
 *          None.
 *
 *      EXIT
 *          None.
 *
 *      CONTEXT
 *          VDM Task
 *
 *      STEPS
 *          call file system to clean up search handles and FCB SFTs    BN009
 *          free FCB SFTs memory and selector                           BN009
 *          Free temporary selectors allocated
 *
 *      NOTES
 *          None
 *
 *      WARNINGS
 *          None
 *
 *      CALLS
 */

VOID PASCAL
DEMDestroyVDM1()
{
    int     rc;                                                       /*BN019*/
    PULONG  demNextHack;                                              /*BN013*/
    extern VOID FSAbortVDM(VOID);            /* moved from destroy VDM2 BN009*/

    FSAbortVDM();                                                     /*BN009*/
    if(pFCBSFT != NULL) {                                             /*BN009*/
        VMFreeKHB(VM_HKH_PUB_RESRW, pFCBSFT);                         /*BN009*/
        pFCBSFT = NULL;                                               /*BN009*/
    }                                                                 /*BN009*/
    if(sftFCB != 0) {                                                 /*BN009*/
        SELFreeGDT(sftFCB);                                           /*BN009*/
        sftFCB = 0;                                                   /*BN009*/
    }                                                                 /*BN009*/

    while (demFindFirstHack_head) {                                   /*BN013*/
        demNextHack = (PULONG) *demFindFirstHack_head;                /*BN013*/
        VMFreeKHB( VM_HKH_PUB_SWAPRW, demFindFirstHack_head );        /*BN013*/
        demFindFirstHack_head = demNextHack;                          /*BN013*/
        }                                                             /*BN013*/

    demFreeSelTemp(CSELDEMTEMP);

    if (demVDMStartDoneHndl) {                                        /*BN019*/
        rc=dem32CloseEventSem( demVDMStartDoneHndl );                 /*BN019*/
        }                                                             /*BN019*/

} /*DEMDestroyVDM1()*/


#pragma code_seg()
#pragma code_seg("SWAP32_27","CODE")
/***EP+ DEMDestroyVDM2 - called for VDM termination
 *
 *      VDM Termination handler for dos emulation.
 *
 *      ENTRY
 *          None.
 *
 *      EXIT
 *          None.
 *
 *      CONTEXT
 *          VDM Task
 *
 *      STEPS
 *          free debugger symbol map
 *
 *      NOTES
 *          None
 *
 *      WARNINGS
 *          None
 *
 *      CALLS
 *          free
 *          DebugFreeSymV86Krnl
 */

VOID PASCAL
DEMDestroyVDM2()
{
#ifdef PMDEBUG
    extern VOID DebugFreeSymV86Krnl(VOID);
#endif

    // Release Fake version entries
    demDestroyFakeList (pVerInfoHeadLocal);

#ifdef PMDEBUG
    DebugFreeSymV86Krnl();
#endif
} /*DEMDestroyVDM2()*/

#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/***LP+ demLoadV86Kernel - Load the V86 Kernel
 *
 *      This loads the V86 Kernel file into memory
 *
 *      ENTRY
 *          pBuffer
 *              Address of buffer into which the V86 Kernel is loaded
 *          cbBuffer
 *              Size of the buffer
 *          pcbKernel
 *              Size of the kernel (adjusted to paragraph boundary)
 *
 *      EXIT
 *          SUCCESS
 *              Returns NO_ERROR
 *
 *          FAILURE
 *              Returns non-zero error code
 *
 *      CONTEXT
 *          VDM Task (VDM Create)
 *
 *      STEPS
 *          Open V86 Kernel File
 *          Make sure fits in buffer
 *          Read the V86 Kernel File into the V86 Arena
 *
 *      NOTES
 *          None
 *
 *      WARNINGS
 *          The pBuffer buffer must lie on a paragraph boundary.
 *
 *      CALLS
 *          demOpenFile
 *          demCloseFile
 *          demReadFile
 *
 */

DEMLOCAL INT PASCAL
demLoadV86Kernel(pBuffer, cbBuffer, pcbKernel)
PBYTE pBuffer;
ULONG cbBuffer;
PULONG pcbKernel;
/*end*/
{
    ULONG cbKernel;
    HFILE hfileKernel;
    ULONG cbRead;
    INT erc;
#ifdef PMDEBUG
    extern void DebugLoadSymV86Krnl(PSZ, ULONG);
#endif

    demassert((((ULONG) pBuffer) & 0xF) == 0);
    *pcbKernel = 0;

    /* Search V86 kernel in libpath and load it */
    if((erc = demGetV86Kernel(SSToDS(&hfileKernel),SSToDS(&cbKernel)))
              != NO_ERROR) {
        return(erc);
    }

    /* See if we have enough memory to load it (and them some) */
    if(cbBuffer < cbKernel) {
        demCloseFile(hfileKernel);
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    /* Read in the V86 kernel */
    if((erc = demReadFile(hfileKernel, pBuffer, cbKernel, SSToDS(&cbRead)))
      != NO_ERROR) {
        demCloseFile(hfileKernel);
        return(erc);
    }
    if(cbRead != cbKernel) {
        demCloseFile(hfileKernel);
        return(ERROR_BUFFER_OVERFLOW);
    }
    if((erc = demCloseFile(hfileKernel)) != NO_ERROR) {
        return(erc);
    }
    *pcbKernel = (cbKernel + 0xF) & 0xFFFFFFF0;
#ifdef PMDEBUG
    DebugLoadSymV86Krnl(pszV86KernelSpec, ((ULONG) pBuffer) >> 4);
#endif
    return(NO_ERROR);

}

/***LP+ demGetV86Kernel - Locate V86 kernel using LIBPATH and load it.
 *
 *      From LIBPATH load the V86 kernel and return the size and file
 *      handle.
 *
 *      ENTRY
 *          phfile     - pointer where file handle will be returned
 *          pcb        - pointer where kernel size will be returned
 *
 *      EXIT
 *          SUCCESS
 *              Returns NO_ERROR
 *
 *          FAILURE
 *              Returns non-zero error code
 *
 *      CONTEXT
 *          Called from demLoadV86Kernel
 *
 *      STEPS
 *          Call loader to pass a possible path
 *          try to load it
 *          if success
 *              return size and handle
 *          else repeat loading next possible path
 *          if LIBPATH exhausted return error
 *
 *      NOTES
 *
 *      WARNINGS
 *          None
 *
 *      CALLS
 *          ldrNextLibPath
 *          demOpenFile
 */
DEMLOCAL INT PASCAL
demGetV86Kernel(phfile,pcb)
PHFILE  phfile;
PULONG  pcb;
/*end*/
{
ULONG   ichLibPath;             // LIBPATH string index
ULONG   rc;
USHORT  cbKrnlName;

    ichLibPath = (ULONG) LDRLibPath;
    cbKrnlName = (USHORT)(strlen(pszV86KernelName)+1);
    while ((ichLibPath != -1)) {
        if ((rc=ldrNextLibPath(
                SSToDS(&ichLibPath),
                pszV86KernelSpec,
                (ULONG)MAXPATHLEN,
                pszV86KernelName,
                cbKrnlName))
                == NO_ERROR){
            if(demOpenFile(pszV86KernelSpec,phfile,pcb) != NO_ERROR)
                continue;
            return NO_ERROR;    // return Success
        }
    }
    return ERROR_FILE_NOT_FOUND;
}

/***LP+ demInitFCBSFT - Alloc/Initialize FCB SFT's
 *
 *      Allocate the SFT's that are reserved for V86 mode FCB's,
 *      and initialize them.
 *
 *      ENTRY
 *          nFCB
 *              Number of FCB's to allocate
 *          nFCBNoClose
 *              Number of FCB's which aren't auto-closed
 *              string containing full pathspec of the program
 *
 *      EXIT
 *          SUCCESS
 *              Returns 0
 *
 *          FAILURE
 *              Returns non-zero error code
 *
 *      CONTEXT
 *          Called from DEMCreateVDM
 *
 *      STEPS
 *          Check for non-zero count
 *          Allocate memory for SFT's.
 *          Setup "selFCBSFT" and "cFCBKeep" for file systemm
 *          Initialize each SFT and flag each for FCB usage
 *
 *      NOTES
 *          It is acceptable to create a VDM in which FCB opens are
 *          disallowed by setting nFCB to zero.
 *
 *      WARNINGS
 *          None
 *
 *      CALLS
 */
DEMLOCAL INT PASCAL
demInitFCBSFT(cFCB, cFCBNoClose)
USHORT cFCB;
USHORT cFCBNoClose;
/*end*/
{
    sf_entry   *paSF;
    SFT        *pSFT;
    USHORT     iSFT;
    INT        erc;

    sftFCB = 0 ;                        /* initialize to no FCB's */
    KeepCount = 0;

    if (cFCBNoClose > cFCB)             /* check bad setup */
      {
       cFCB = cFCBNoClose;              /* take out error and modify 160978 */
      }

    if(cFCB == 0) {
        return(NO_ERROR);               /* if nothing to do */
    }

    /* Get the memory and the selector */
    if((erc = demAllocFCBSFT(cFCB)) != NO_ERROR) {
        return(erc);
    }

    /* Get SFT and sf_entry adressability */
    pSFT = (SFT *)pFCBSFT;
    paSF = (sf_entry *)((ULONG)pFCBSFT + sizeof(SFT));

    /* Initialize the header */
    pSFT->sft_count = cFCB;
    pSFT->sft_link = 0;
    pSFT->sft_inshutdown = 0;
    KeepCount = cFCBNoClose;

    /* Initialize the SFT's to indicate that they're for FCB's */
    for (iSFT=0; iSFT<cFCB; iSFT++) {
        paSF[iSFT].sf_ref_count = 0;

/* @SEC I think it's a bad idea to put a self SFN in an SFN that   */
/*     is not allocated in the normal kernel code.  If anybody     */
/*     calls SFFromSFN with this value then they will get the      */
/*     SFT that is in the normal kernel space, instead of          */
/*     the ones allocated in this GDT.         SteveD              */
/*      paSF[iSFT].sf_fsi.sfi_selfsfn = iSFT;                      */
        paSF[iSFT].sf_fsi.sfi_selfsfn = SFN_NULL;

        paSF[iSFT].sf_flags = SF_FCB;
    }
    return(NO_ERROR);
}

/***LP+ demAllocFCBSFT - allocate the FCB SFT memory for a VDM
 *
 *      Allocates some memory and a selector for the FCB SFTs.
 *
 *      ENTRY
 *          cFCB
 *              Number of FCBs.
 *
 *      EXIT
 *          SUCCESS
 *              Returns 0
 *
 *          FAILURE
 *              Returns non-zero error code
 *
 *      CONTEXT
 *          Called from kernel mode
 *
 *      STEPS
 *          Call heap manager to allocate memory
 *          Call selector manager to allocate and set an descriptor
 *
 *      NOTES
 *          None
 *
 *      WARNINGS
 *          None
 *
 *      CALLS
 *          VMAllocKHB      ;BUGBUG: Use SELAllocKHB instead of these three
 *          SELAllocGDT
 *          SELSetDescInfo
 */

DEMLOCAL INT PASCAL
demAllocFCBSFT(cFCB)
USHORT cFCB;
/*end*/
{
    ULONG cbFCBSFT;
    INT rc;
    /*BUGBUG: (LaleD): SELAllocKHB replaces all of the three below */
    cbFCBSFT = sizeof(SFT) + cFCB * sizeof(sf_entry);
    if((rc = VMAllocKHB(VM_HKH_PUB_RESRW, cbFCBSFT, (VMHOB)DEMFCBSFTOWNER, NA,
                        &pFCBSFT)) != NO_ERROR) {
        return(rc);
    }
    if((rc = SELAllocGDT(&sftFCB)) != NO_ERROR) {
        VMFreeKHB(VM_HKH_PUB_RESRW, pFCBSFT);
        return(rc);
    }
    SELSetDescInfo(SELSD_ALL,D_DATA0,sftFCB,cbFCBSFT,(ULONG) pFCBSFT,0,0,0);
    return(NO_ERROR);
}

/***LP+ demFreeSelTemp - Free Temporary Selectors
 *
 *      Free the temporary selectors
 *
 *      ENTRY
 *          cSel - Number of selectors to free
 *
 *      EXIT
 *          SUCCESS
 *              Return
 *
 *          FAILURE
 *              Panics
 *
 *      CONTEXT
 *          VDM Task (VDM Create)
 *
 *      STEPS
 *          call SELFreeGDT for each allocated GDT
 *
 *      NOTES
 *          None
 *
 *      WARNINGS
 *          None
 *
 *      CALLS
 *          SELFreeGDT
 *
 */

DEMLOCAL VOID PASCAL
demFreeSelTemp(cSel)
int cSel;
{
    int i;

    for (i=cSel-1; i>=0; i--) {
        if(SELFreeGDT(aseldemTemp[i]) != NO_ERROR) {
            Panic(("demFreeSelTemp"));
        }
    }
}

/***LP+ demInitV86Area - Set up V86 registers and memory
 *
 *      Set up V86 registers and parameters in memory
 *
 *      ENTRY
 *
 *      EXIT
 *          SUCCESS
 *              Returns NO_ERROR
 *
 *          FAILURE
 *              Returns non-zero error code
 *
 *      CONTEXT
 *          VDM Task (VDM Create)
 *
 *      STEPS
 *          Initialize V86 Stack Parameters
 *          Initialize V86 Registers (CS:IP/SS:SP)
 *
 *      NOTES
 *          We set up a list of parameters on the new V86 stack
 *          which refer to big strings in high V86 memory.  We
 *          tell the V86 Init code about this in the stack
 *          parameters.
 *
 *      WARNINGS
 *          None
 *
 *      CALLS
 *          KMSetClientRegister
 *          KMQueryClientRegister
 *
 *      MEMORY MAP
 *          +-------------------+  0
 *          |        IVT        |
 *          +-------------------+
 *          |      ROM DATA     |
 *          +-------------------+
 *          |        DCA        |
 *          +-------------------+
 *          |                   |
 *          |      DOSKRNL      |
 *          |                   |
 *          + - - - - - - - - - +
 *          |  pre-move V86INIT |
 *          +-------------------+ <--- v86i_segArenaHead
 *          |                   |  ^
 *          |                   |  |
 *          |                   |  |
 *          |                   |  |
 *          |                   | v86i_cParaArena
 *          +-------------------+  |
 *          |   moved V86INIT   |  |
 *          +-------------------+  | <---
 *          |     szzDOSDDs     |  |   ^
 *          +-------------------+  |   |
 *          |    szShellSpec    |  |   |
 *          +-------------------+  |   |
 *          |    szShellArgs    |  | v86i_cParaHigh
 *          +-------------------+  |   |         <- SS:SP
 *          |  v86 init stack   |  |   |
 *          + - - - - - - - - - +  |   |
 *          |   v86i_s packet   |  v   v
 *          +-------------------+ <------ RMSIZE <- SS:BP
 */

DEMLOCAL INT PASCAL
demInitV86Area(pArena, ulcbArena, pV86KernelEntry, pszShellSpec, pabShellArgs, iBreak)
PBYTE pArena;
ULONG ulcbArena;
PBYTE pV86KernelEntry;
PSZ pszShellSpec;
PBYTE pabShellArgs;
USHORT iBreak;
/*end*/
{
    register struct v86i_s *pv86i;
    ULONG v86_flags, cb;
    register PCHAR p;
    PCHAR pchScan;
    PCHAR pchSrc, pchDst;                                           /*;BC006;*/
    UCHAR uchBlankEntry, uchChar;                                   /*;BN006;*/
    extern UCHAR uchLastDrive;
    BYTE drive;
    PBYTE pszdrive=NULL;     // BN015
    PCHAR pszzDOSDD=NULL;    // BN015

    // Save the V86 Kernel segment for validity checking
    segV86Kernel = (USHORT) (((ULONG)pV86KernelEntry) >> 4);

    /* Copy some things to the end of the V86 memory arena.  The V86 Kernel
       knows not to use this area during initialization.  We start with
       v86i_s structure, the starting program name and parameters, and
       follow with the DOS character DD lists.  Since we're doing this
       backwards, the DD lists are in the lowest memory locations. */

    p = pArena + ulcbArena;             /* pointer to top of memory */

    /* Reserve space for V86i_s structure pointer */
    p -= sizeof(struct v86i_s);
    pv86i = (struct v86i_s *)p;
    if((ULONG)p < ((ULONG)pArena + ARENA_PAD)) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }
    pv86i->v86i_pszShellSpec = NULL;
    pv86i->v86i_pabShellArgs = NULL;
    pv86i->v86i_pszzDOSDDs = NULL;

    pszdrive = (PBYTE)vdmQueryProperty(VDMP_ORD_LASTDRIVE); // BN015
    drive = *pszdrive;                                      // BN015
//  drive = * (BYTE *) vdmQueryProperty(VDMP_ORD_LASTDRIVE);     /*BN007 BN015*/
    if(drive >= 'a' && drive <= 'z')                             /*BN007*/
        drive -= 'a';                                            /*BN007*/
    else                                                         /*BN007*/
        drive -= 'A';                                            /*BN007*/
    pv86i->v86i_ubLastDrive = drive;                             /*BN007*/
    if (pszdrive)                                          // BN015
    {                                                      // BN015
      VMFreeKHB(VM_HKH_PUB_SWAPRW, pszdrive);              // BN015
    }                                                      // BN015

    cb = pabShellArgs[0] + 2;           /* copy args into v86 memory */
    p -= (cb + 0x80);                   /* reserve 128 bytes for $EXEC */
    if((ULONG)p < ((ULONG)pArena + ARENA_PAD)) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }
    FAR16_FROM_FLAT(pv86i->v86i_pabShellArgs, p);
    memcpy(p, pabShellArgs, cb);

    cb = (strlen(pszShellSpec) + 1);    /* copy shellspec into v86 memory */
    p -= cb;
    if((ULONG)p < ((ULONG)pArena + ARENA_PAD)) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }
    FAR16_FROM_FLAT(pv86i->v86i_pszShellSpec,p);
    memcpy(p, pszShellSpec, cb);

    /* DOS DD list is in MLSTR format. That means DD<LF>DD<LF>...*/ /*;BC004;*/
    /* DD<LF>DD'\0'.  The DOSKRNL initialization code expects    */ /*;BC004;*/
    /* CRLF endings, so we size the string, copy it to the V86   */ /*;BC004;*/
    /* arena, and then change the line feeds to CR/LF.           */ /*;BC004;*/

    /* First, calculate the sizes of the DOS DD list */
    pszzDOSDD = (char *)vdmQueryProperty(VDMP_ORD_DOSDD); // get DOS DD list

    /* Now determine how much space the DOS drivers will take. */   /*;BC006;*/
    cb = strlen(pszzDOSDD) + 3;                                     /*;BN006;*/
    for (pchScan=pszzDOSDD; *pchScan != '\0'; pchScan++)            /*;BN006;*/
         if (*pchScan == 0x0a) cb++;                                /*;BN006;*/
    p -= cb;
    if ((ULONG)p < ((ULONG)pArena + ARENA_PAD)) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }
    FAR16_FROM_FLAT(pv86i->v86i_pszzDOSDDs,p);                      /*;BN004;*/

    /* Copy DD strings to arena */
    pchSrc = pszzDOSDD;                                             /*;BN006;*/
    pchDst = p;                                                     /*;BN006;*/
    uchBlankEntry = 0;                                              /*;BN006;*/
    do {                                                            /*;BN006;*/
       uchChar = *pchSrc;                                           /*;BN006;*/
       pchSrc++;                                                    /*;BN006;*/
       if ((' '!=uchChar) && (0x0a!=uchChar) && ('\0'!=uchChar))    /*;BN006;*/
            uchBlankEntry=1;                                        /*;BN006;*/
       if (uchBlankEntry)                                           /*;BN006;*/
            {                                                       /*;BN006;*/
            if (0x0a == uchChar)  uchBlankEntry=0;                  /*;BN006;*/
            *pchDst = 0x0d;                                         /*;BN006;*/
            if (0x0a == uchChar) pchDst++;                          /*;BN006;*/
            if ('\0' == uchChar) pchDst++;                          /*;BN006;*/
            if ('\0' != uchChar)                                    /*;BN006;*/
                 *pchDst = uchChar;                                 /*;BN006;*/
            else *pchDst = 0x0a;                                    /*;BN006;*/
            pchDst++;                                               /*;BN006;*/
            }                                                       /*;BN006;*/
       } while ('\0' != uchChar);                                   /*;BN006;*/
                                                                    /*;BN006;*/
    *pchDst = '\0';             /* end of DD list */                /*;BN004;*/

    if (pszzDOSDD)                                         // BN015
    {                                                      // BN015
      VMFreeKHB(VM_HKH_PUB_SWAPRW, pszzDOSDD);             // BN015
    }                                                      // BN015

    /* Set up arena pointers */
    pv86i->v86i_segArenaHead = (USHORT)(((ULONG) pArena) >> 4);
    pv86i->v86i_cParaArena = (USHORT)(ulcbArena >> 4);
    pv86i->v86i_cParaHigh =
      pv86i->v86i_cParaArena - (USHORT)(((ULONG)p - (ULONG)pArena) >> 4);

    /* Set reset of v86 init packet */
    pv86i->v86i_pSpecDD = vpDosDDHead;
    pv86i->v86i_ubCurDrive = pPTDACur->LCurDrv;
    pv86i->v86i_ubBootDrv = (CHAR)SISData.SIS_BootDrv;
    pv86i->v86i_fBreak = iBreak;
    pv86i->v86i_Dos_Flag = (BYTE)(vdmQueryProperty(VDMP_ORD_DOS_UMB) ?  /*BC002*/
                                      DOS_UMB_BIT  : 0);                /*BN003*/
    pv86i->v86i_Dos_Flag |= (BYTE)vdmQueryProperty(VDMP_ORD_DOS_HIGH) ? /*BC002*/
                                      DOS_HIGH_BIT : 0;                 /*BN003*/

    /* Initialize the V86 Mode registers */
    /* Point CS:IP to beginning of the kernel file */
    KMSetClientRegister(pTCBCur, KM_REG_EIP, 0);
    KMSetClientRegister(pTCBCur, KM_REG_CS, (ULONG)segV86Kernel);

    /* Point SS:SP to init stack */
    KMSetClientRegister(pTCBCur, KM_REG_ESP,
      ((ULONG)&pv86i->v86i_ausStack & 0xF) + sizeof(pv86i->v86i_ausStack));
    KMSetClientRegister(pTCBCur, KM_REG_SS, (ULONG)pv86i >> 4);

    /* Point BP to v86i_s structure */
    KMSetClientRegister(pTCBCur, KM_REG_EBP, (ULONG)pv86i & 0xF);

    /* Disable interrupts */
    KMQueryClientRegister(pTCBCur, KM_REG_EFLAG, SSToDS(&v86_flags));
    v86_flags = v86_flags & ~F_INTERRUPT;
    KMSetClientRegister(pTCBCur, KM_REG_EFLAG, v86_flags);
    return(NO_ERROR);
}

#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_23","CODE")
/***LP+ demMakeFakeList - Query the fake version property and make
 *                        the fake list.
 *
 *      ENTRY
 *          pszFakeString - Fake version property string
 *          pviHead       - list head
 *
 *      EXIT
 *          SUCCESS - return NO_ERROR
 *          FAILURE - return !NO_ERROR
 *
 *      CONTEXT
 *          Init
 *          Task
 */
ULONG   PRIVENTRY   demMakeFakeList (PSZ pszFakeString, PFAKEVERINFO *pviHead)
{
PSZ             pszAppName;
UCHAR           uchAppNameLen;
UCHAR           uchMajorVersion;
UCHAR           uchMinorVersion;
UCHAR           uchFakeVerCount;
PFAKEVERINFO    pfvInfo;
ULONG           rc;
ULONG           i,j;
CHAR            ch;


    *pviHead = NULL;

    // It is not a crime to pass a NULL string
    if(pszFakeString == NULL)
        return NO_ERROR;

    // Eat all white characters
    i=0; j=0;
    while (ch=pszFakeString[i++])
    {
        if ((' '!=ch) && ('\t'!=ch))
            pszFakeString[j++]=ch;
    };
    pszFakeString[j]=ch;

    // Now lets collect the fake entries
    while (*pszFakeString)
    {

        // First get the Name of app and its name length. if it is invalid
        // go process next entry.
        if ((pszAppName=demGetAppName (SSToDS(&pszFakeString),
                                       SSToDS(&uchAppNameLen))) == NULL)
            continue;

        // Now get the major fake version
        if (demGetNumber (SSToDS(&pszFakeString),
                          SSToDS(&uchMajorVersion),
                          ',') == FALSE)
            continue;

        // Now get the minor fake version
        if (demGetNumber (SSToDS(&pszFakeString),
                          SSToDS(&uchMinorVersion),
                          ',') == FALSE)
            continue;

        // Lastly get the fake count
        if (demGetNumber (SSToDS(&pszFakeString),
                          SSToDS(&uchFakeVerCount),
                          0x0a) == FALSE)
            continue;

        // Allocate the memory where all this info will be stored.
        if ((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW,
                              sizeof(FAKEVERINFO),
                              (VMHOB)DEMVEROWNER,
                              NA,
                              SSToDS(&pfvInfo))) != NO_ERROR)
            return rc;

        // Store the info
        pfvInfo->cbAppNameLen =  uchAppNameLen+(UCHAR)1;
        pfvInfo->uchMajorVer  =  uchMajorVersion;
        pfvInfo->uchMinorVer  =  uchMinorVersion;
        pfvInfo->uchFakeCount =  uchFakeVerCount;
        for (rc=0 ; rc < uchAppNameLen ; rc++)
            pfvInfo->szAppName[rc] = pszAppName[rc];
        pfvInfo->szAppName[rc] = '\0';

        // Put this info block on the list
        pfvInfo->next = *pviHead;
        *pviHead = pfvInfo;
    }
    return NO_ERROR;
} /*demMakeFakeList()*/
#pragma code_seg()
#pragma code_seg("SWAP32_27","CODE")
/***LP+ demDestroyFakeList - Release the memory of given fake list
 *
 *      ENTRY
 *          pviHead       - list head
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          Task
 */
VOID   PRIVENTRY   demDestroyFakeList (PFAKEVERINFO pviHead)
{
PFAKEVERINFO pvi;

    // Its not a crime to pass a empty list
    if (pviHead == NULL)
        return;

    // Enumerate the list and release the fake blocks
    while (pviHead != NULL){
        pvi = pviHead;
        pviHead = pviHead->next;
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pvi);
    }
    return;
} /*demDestroyFakeList()*/
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE

/***LP+  demSetFakeVersion - Set/Validate Fake version entries
 *
 *      This function is notified whenever fake entries are changed in
 *      a VDM. It only handles the setting of the values. Verification
 *      always succceeds as validation is done at setting time.
 *
 *      ENTRY
 *          op - operation to perform (set or validate)
 *          hvdm - target VDM
 *          cb - length of string
 *          pch - string
 *
 *      EXIT
 *          SUCCESS - 0
 *          FAILURE - non-zero error code
 *
 *      CONTEXT Task
 */

ULONG EXPENTRY demSetFakeVersion(ULONG op, HVDM hvdm, ULONG cb, CHAR *pch)
{
PFAKEVERINFO pfiHead;
ULONG        rc;

    cb;              // eliminate compiler warning

    // If validating the string, just return else set the list
    if (op == VDHPROP_SET)
    {

        // make a new list, in case we fail we will still have the old list
        if((rc = demMakeFakeList (pch,SSToDS (&pfiHead))) != NO_ERROR)
            return rc;

        // Destroy the old list
        demDestroyFakeList ((PFAKEVERINFO)*(pVDM(hvdm,
                                            PULONG,&pVerInfoHeadLocal)));

        // Store the new one
        *(pVDM(hvdm,PULONG,&pVerInfoHeadLocal)) = (ULONG)pfiHead;

    }

    return 0;
} /*demSetFakeVersion()*/

#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_23","CODE")
/***LP+ demGetAppName - Get the app name from the property line.
 *
 *      ENTRY
 *          ppszProp - Pointer to property string
 *          puchLen  - Length of APP name
 *
 *      EXIT
 *          SUCCESS - returns pointer to app name
 *                    ppszProp updated to next item
 *
 *          FAILURE - returns NULL
 *                    ppszProp updated to next string
 */
PSZ   PRIVENTRY demGetAppName (PSZ *pszProp,PUCHAR puchLen)
{
PSZ     pszName = *pszProp,pszTemp;
UCHAR   uchLen=0;
CHAR    ch;

    //Collect the App Name
    ch = *pszName++;
    while ((ch != ',')   &&
           (uchLen < 12) &&
           (ch != 0x0a)  &&
           (ch != '\0')  )
    {
        ch = *pszName++;
        uchLen++;
    }

    // Valid entry will have a ','.
    if (ch == ',')
    {
        pszTemp = *pszProp;
        *pszProp = pszName;
        *puchLen = uchLen;
        return pszTemp;
    }

    // FAILURE PATH: If name is too big then discard this entry
    if (uchLen >= 12)                                             /* BN014 */
    {
        while (ch != 0x0a && ch != '\0')                          /* BN014 */
           ch = *pszName++;
    }

    // If reached the end, pointer should point to null
    if (ch == '\0')
        *pszProp = --pszName;

    // If this entry ended in a bad format, process next entry.
    if (ch == 0x0a)
        *pszProp = pszName;

    return NULL;
} /*demGetAppName()*/


#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
/***LP+ demGetNumber - Get a number from property line
 *
 *      ENTRY
 *          ppszProp - Pointer to property string
 *          puchNum  - Pointer where number will be returned
 *          chEnd    - Character with which the field will end.
 *                     if chEnd is 0x0a then end character can be
 *                     null. This because the property string format
 *                     such that last item may end with null without
 *                     a Line feed.
 *
 *      EXIT
 *          SUCCESS - returns TRUE
 *                    ppszProp updated to next item
 *                    puchNum filled in
 *
 *          FAILURE - returns FALSE
 *                    ppszProp updated to next string
 */
BOOL   PRIVENTRY demGetNumber (PSZ *pszProp,PUCHAR puchNum,CHAR chEnd)
{
PSZ     pszNum = *pszProp;
PSZ     pszTemp;
UCHAR   uchLen=0;
UCHAR   chtemp;                  /* BN015 */
CHAR    ch;

    // Collect fake major version
    ch = *pszNum++;

    while ((ch != ',')  &&
           (ch != 0x0a) &&
           (ch != '\0') )
    {
        ch = *pszNum++;
        uchLen++;
    }

    // See comment in the header for chEnd
    if ((ch == chEnd) ||
        (chEnd == 0x0a && ch == '\0') )
    {
        if ((uchLen >= 4) || (**pszProp == '-'))
            uchLen=255;
        else
        {
            pszTemp = pszNum;
            chtemp = *(--pszTemp);                                  /* BN015 */
            *pszTemp = '\0';                                        /* BN015 */
            uchLen = (UCHAR)vdmmGetValueFromString(*pszProp,10);
            *pszTemp = chtemp;                                      /* BN015 */
        }

        if (ch == '\0')
            --pszNum;

        *pszProp = pszNum;
        *puchNum = uchLen;
        return TRUE;
    }

//70132    chtemp = *(--pszTemp);

    // FAILURE PATH
    if (ch == ',')
    {
        while ((ch != 0x0a) && (ch != '\0'))         //70132 AND not OR
            ch = *pszNum++;
    }

    if(ch == 0x0a)
        *pszProp = pszNum;
    if (ch == '\0')
        *pszProp = --pszNum;

    return FALSE;

} /*demGetNumber()*/

#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
/***LP++ demProcessEntries - Process the file name being execed in
 *                           fake entries and set the version accordingly
 *
 *      ENTRY
 *          pszFileName - File Name
 *          cbFileName  - File Nmae length including NULL
 *          pusVer      - Place where Version will be returned
 *          puchCount    - Place where fake count will be returned
 *
 *      EXIT
 *          SUCCESS - Name found in special entries
 *                   returns TRUE, pusVer and pchCount filled in
 *
 *          FAILURE
 *                  returns FALSE
 *
 *      CONTEXT
 *          Current VDM Task
 */

BOOL    PRIVENTRY demProcessEntries (pszFileName,cbFileName,pusVer,puchCount)
PSZ     pszFileName;
ULONG   cbFileName;
PUSHORT pusVer;
PUCHAR  puchCount;
{
PFAKEVERINFO pfi;
USHORT       usFakeVer;

    pfi = pVerInfoHeadLocal;
    while (pfi != NULL){
        if (pfi->cbAppNameLen == (UCHAR)cbFileName &&
            vdmmStrcmpi(pszFileName, pfi->szAppName) == TRUE){
            *puchCount = pfi->uchFakeCount;
            usFakeVer = (USHORT) (pfi->uchMinorVer << 8);
            *pusVer = usFakeVer + pfi->uchMajorVer;
            return TRUE;
        }

        pfi = pfi->next;
    }
    return FALSE;
} /*demProcessEntries()*/
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE

/***EP+  ValidateLastDrive - validate last drive
 *
 *      If the string identifies a valid last drive return VALIDDRIVE.
 *      Otherwise, return an error code.
 *
 *      ENTRY
 *          op - operation to perform (set or validate)
 *          hvdm - target VDM
 *          cb - length of string
 *          pch - string
 *
 *      EXIT
 *          SUCCESS - VALIDDRIVE
 *          FAILURE - INVALIDDRIVE
 *
 *
 *      CONTEXT Task
 */

ULONG EXPENTRY ValidateLastDrive(ULONG op, HVDM hvdm, ULONG cb, CHAR *pch)  /*BN007*/
{                                                                           /*BN007*/
                                                                            /*BN007*/
   char    drive;                                                           /*BN007*/
                                                                            /*BN007*/
   if(op == VDHPROP_VALIDATE){                                              /*BN007*/
     /* The first character is the drive. */                                /*BN007*/
      drive = pch[0];                                                       /*BN007*/
     /* Do upper case: the llibc cannot be linked with this module;  */     /*BN007*/
     /* therefore, the macro toupper() cannot be used.               */     /*BN007*/
     /*                                                              */     /*BN007*/
      if( (drive >= 'a') && (drive <= 'z') )                                /*BN007*/
         drive = drive - (char) 'a' + (char) 'A';                           /*BN007*/
     /* Allow the drive if it is an alpha character. */                     /*BN007*/
      if( (drive < 'A') || (drive > 'Z') )                                  /*BN007*/
         return INVALIDDRIVE;                                               /*BN007*/
     /* Allow the character string if it a single alpha char or a single */ /*BN007*/
     /* alpha char followed by a colon.                                  */ /*BN007*/
     /*                                                                  */ /*BN007*/
      if(   (pch[1] != '\0') &&                                             /*BN007*/
           !( (pch[1] == ':') && (pch[2] == '\0') )   )                     /*BN007*/
         return INVALIDDRIVE;                                               /*BN007*/
     /* Make sure that a lastdrive is not set less than the number of  */   /*BN007*/
     /* physical drives.                                               */   /*BN007*/
     /*                                                                */   /*BN007*/
      drive -= 'A';                                                         /*BN007*/
      if(drive < (char) (NumIO + NumIORipl - 1) )                           /*BN007*/
         return INVALIDDRIVE;                                               /*BN007*/
   }                                                                        /*BN007*/
   return VALIDDRIVE;                                                       /*BN007*/
   cb; hvdm;  /*this will prevent stupid C compiler messages*/
} /*ValidateLastDrive()*/

/***EP+  ValidateFilesCount - validate FILES= count
 *
 *      Always returns true for the validation of the FILE= count.
 *
 *      ENTRY
 *          op - operation to perform (set or validate)
 *          hvdm - target VDM
 *          cb - length of string
 *          pch - string
 *
 *      EXIT
 *          SUCCESS = 0 = FALSE
 *
 *
 *      CONTEXT Task
 */

ULONG EXPENTRY ValidateFilesCount(ULONG op,                           /*BN012*/
                                  HVDM hvdm,                          /*BN012*/
                                  ULONG cb,                           /*BN012*/
                                  CHAR *pch)                          /*BN012*/
{                                                                     /*BN012*/
  return FALSE;                                                       /*BN012*/
  cb; hvdm; op; pch; /*this will prevent stupid C compiler messages*/
} /*ValidateFilesCount()*/


/***EP+  ValidateAutoexec - validate DOS_AUTOEXEC string
*
*      Always returns true for the validation of the DOS_AUTOEXEC string.
*
*      ENTRY
*          op - operation to perform (set or validate)
*          hvdm - target VDM
*          cb - length of string
*          pch - string
*
*      EXIT
*          SUCCESS = 0 = FALSE
*
*
*      CONTEXT Task
*/

ULONG EXPENTRY ValidateAutoexec(ULONG op,                             /*BN018*/
                                HVDM hvdm,                            /*BN018*/
                                ULONG cb,                             /*BN018*/
                                CHAR *pch)                            /*BN018*/
{                                                                     /*BN018*/
  return FALSE;                                                       /*BN018*/
  cb; hvdm; op; pch; /*this will prevent stupid C compiler messages*/ /*BN018*/
} /*ValidateAutoexec()*/

/* Begin #95072 */

/***EP+  demParseLabel - parse the label string
*
*      ENTRY
*          s - source buffer (null terminated)
*          d - destination buffer (must be a least 12 chars)
*
*      EXIT
*
*      This routine will parse the source string as follows :
*
*      Source          Destination
*
*      A.B             "A       B"
*      AA.BB           "AA      BB"
*      123.45678       "123     456"
*      12345678.123    "12345678123"
*      12345678.       "12345678"
*      123456.         "123456"
*      1234567890      "12345678"
*      12345678901     "12345678"
*      123456789012    "12345678"
*      123456789.      "12345678"
*      123456789012.   "12345678"
*      1.12345678      "1       123"
*      1234.56789012   "1234    567"
*      123456789.12    "1234567812"
*      123456789.123   "12345678123"
*      12345678.1234   "12345678123"
*      1234567890.1    "123456781"
*      .1              ""
*      1234567890.1234567890 "12345678.123"
*
*/

VOID demParseLabel (PSZ register s, PSZ d)
{
  ULONG register i;
  INT   fName = 1;
  INT   fExt  = 1;

#define LABEL_LENGTH 11
#define EXT_SIZE      3

  CHAR  Temp[LABEL_LENGTH+1];
  PSZ   register t = SSToDS(Temp);

  for (i = 0 ; i < LABEL_LENGTH ; i++) t[i] = ' ';

  /*
   * copy filename part
   */

  i = 0;

  if (*s == '.')         // if the label begins with '.'
  {
    fName = fExt = 0;
  }

  while (fName)
  {
    if (*s == '.')
    {
      s++;
      if (i > (LABEL_LENGTH - EXT_SIZE) || *s)   // Truncate to 8 chars
      {                                          // or
        i = (LABEL_LENGTH - EXT_SIZE);           // pad with blanks.
      }
      fName = 0;
    }
    else
    {
      if (*s == '\0')
      {
        fName = fExt = 0;
      }
      else
      {
        if (i < (LABEL_LENGTH - EXT_SIZE))
        {
          t[i] = *s;
          i++;
        }
      }
      s++;
    };
  };

  /*
   * copy extension part
   */

  while (fExt)
  {
    if (!*s)
    {
      fExt = 0;
    }
    else
    {
      if (i < LABEL_LENGTH)
      {
        t[i] = *s;
        i++;
      }
    }
    s++;
  };

  t[i] = '\0';

  /*
   * Copy temp buffer to destination.
   */

  s = d;    // optimization (mov to register variable)

  while (*t)
  {
    *s = *t;

//    /*
//     * OS/2 label allows the following characters, DOS does not.
//     */
//
//      if (*s == '\\' || *s == '/' || *s == '[' || *s == ']' ||
//          *s == ':'  || *s == '|' || *s == '<' || *s == '>' ||
//          *s == '+'  || *s == ';'  || *s == '=' || *s == '"')
//      {
//        rc = ERROR_INVALID_NAME;  // '.' '?' '*' are checked in
//        break;                    // cook_label (filesys\fsinfo.asm)
//      }

    s++;
    t++;
  };
  *s = *t; // null

  return;
}

/* End #95072 */

#pragma END_MVDM_SWAP_CODE
