/*static char *SCCSID = "@(#)vdmprop.c  6.7 91/10/17";*/

#define SCCSID "@(#)vdmprop.c   6.7 91/10/17"


/***    vdmprop.c
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Title:
 *          VDM Properties engine
 *
 *      Description:
 *          Interfaces and code to manage VDM properties.
 *
 *      Kernel Exports:
 *          vdmPropertyInit            - Initialize VDM property data
 *          vdmSetPropertyDefaults     - Set default property values
 *          vdmCreatePropertyInstance  - Create initial property values
 *          vdmSetPropertyInstance     - Set instance var with HPRIN
 *          vdmDestroyPropertyInstance - Destroy property list for current VDM
 *          vdmQueryProperty           - Query property value
 *
 *      VDD and API Exports:
 *          VDHRegisterProperty  - Register VDD property name with VDMM
 *          VDHQueryProperty     - Query VDD property value
 *          VR32QueryDOSProperty - Query DOS property information
 *          VR32SetDOSProperty   - Set/Validate DOS property value
 *
 *      Modification History:
 *          03-Nov-1989 bens    Initial version.
 *
 *   -  001     2/22/91   Mark Rogalski,   IBM
 *      CP20 DCR 1329  Add DOS=UMB  and DEVICEHIGH support
 *
 *   -  002     3/22/91   Mark Rogalski,   IBM
 *      CP20 DCR 1360  Add DOS=HIGH support
 *
 *   -  BN003   6/13/91   HARE, IBM
 *      CP20 B722301   Added LASTDRIVE advanced property
;***********************************************************************;BN011;
; 19Jun91 - G.L.Fulk                                                    ;BN011;
; Add support for a DOS ONLY "FILES=" statement in CONFIG.SYS.          ;BN011;
;***********************************************************************;BN011;
;***********************************************************************;BN012;
; 04Apr93 - R206 66756 - S. Barnes                                      ;BN012;
; Enforce a rule stipulated by VDHRegisterProperty                      ;BN012;
;***********************************************************************;BN012;

 14June93 - Mohamed Benyounes #69663. Handling of default case in SkipValue().

 */

#define FNO_NUM  FNO_VDMPROP_C
#include <fnomvdm.h>

#define  VDDSTRICT
#define  INCL_NONE
#define  INCL_VDH

#include <vdmvdh.h>
#include <limits.h>
#include <kmode.h>
#include <string.h>
#include <advprop.h>                                                  /*BN011*/


extern struct InfoSegGDT SISData;
extern UCHAR  uchLastDrive;                                     /*BN003*/

#define DOS_UMB_BIT        0x01                                 /*BN002*/
#define DOS_HIGH_BIT       0x02                                 /*BN002*/


//* Private data structures

/*  DEFAULT_BEFORE_REGISTER - magic value for pprop->idHelp
 *
 *  The kernel (VDMM) sets the default property values for the
 *  "standard" properties (DOS size, shell, etc.) that VDMM needs.
 *
 *  This is done *before* VDDs are loaded, and VBIOS is the VDD
 *  responsible for calling VDHRegisterProperty to set the name,
 *  bounds, etc.
 *
 *  So, we use pprop->idHelp == DEFAULT_BEFORE_REGISTER to indicate
 *  a PROPERTY which has been given a default value, along with vptype
 *  and vpord, but which is not fully complete.
 *
 */
#define DEFAULT_BEFORE_REGISTER -1


/***    PROPERTY - property record
 *
 *      Records all relevant information for a registered VDD property.
 */
typedef struct prop { // prop
    char *      pszName;            // asciiz property name
    char *      pszHelp;            // asciiz help file name
    ULONG       idHelp;             // help id
    VPTYPE      vptype;             // property type
    VPORD       vpord;              // property ordinal
    ULONG       ulFlags;            // property flags
    int         cbValid;            // size of pvValid
    void *      pvValid;            // validation information
    int         cbValue;            // size of value (for VDDPROP_ENUM/STRING)
    void *      pvValue;            // default value
    PFNVDHRP    pfnvdhrp;           // set/validate function
    struct prop *pprop;             // next property in list
    BOOL        fScan;              // bit for scanning
} PROPERTY;
typedef PROPERTY *PPROPERTY; // pprop


/***    PROPINST - property instance
 *
 *      Each VDM has a chain of these to record the value of its properties.
 */
typedef struct propinst { // prin
    PPROPERTY   pprop;              // property definition
    void *      pvValue;            // current value
    struct propinst *pprin;         // next property instance

} PROPINST;
typedef PROPINST *PPROPINST; // pprin


// global data - START

HKSEM     hksemPropertyBits; // serialize property scan bits
PPROPERTY ppropList=NULL;  // list of properties

// global data - END



#pragma BEGIN_MVDM_INSTANCE_DATA

HKSEM     hksemPropertyValues;   // lock per-VDM property value list
PPROPINST pprinList=(PPROPINST)NULL; // per-VDM property value list

#pragma END_MVDM_INSTANCE_DATA



int            checkPSZ(char *pch,char *pchEnd);
void           clearScanBits(void);
void           destroyPropInst(PPROPINST pprinList);
char *         findEnum(PPROPERTY pprop, char *pchValue, int cbValue);
ULONG EXPENTRY fnvdhrpAlwaysGood(ULONG, HVDM, ULONG, PSZ);
char *         getValue(ULONG *pul, char *pchValue, char *pchEnd, PPROPERTY pprop, BOOL *fUser);
PPROPINST      PRINFind(PPROPINST pprin, PPROPERTY pprop);
PPROPERTY      PROPCreate(char *psz);
VOID           PROPDestroy(PPROPERTY pprop);
PPROPERTY      PROPFind(PPROPERTY ppropList,char *pszName);
PPROPERTY      PROPFindNameOrOrdinal(PPROPERTY ppropList,char *pszName);
PPROPERTY      PROPFindOrdinal(PPROPERTY ppropList,VPORD vpord);
char *         putBytes(char *pch, char *pchTail, char *pchValue, int cchValue);
ULONG          queryPropertyDetail(char *pszName,ULONG cb,char *pchStart);
ULONG          queryPropertyNames(SGID sgid,ULONG cb,char *pch);
ULONG          queryPropertyValue(SGID sgid,char *pszName,ULONG cb,char *pch);
ULONG          setPropertyDefault(VPORD vpord,VPTYPE vptype,ULONG cb,CHAR *pch);
char *         skipValue(char *pchValue, char *pchEnd, VPTYPE vptype);
void *         swpAlloc(int cb);
char *         swpStrDup(char *psz);
char *         swpStrNDup(char *pch, int cch);
#define        swpFree(pv)    VMFreeKHB(VM_HKH_PUB_SWAPRW,pv)
void           swpFreez(void *pv);
char *         myStrChr(char *psz, char ch);

// BUGBUG 17-Nov-1989 bens - compiler bug causes CVK to not understand symbols
//    #pragma BEGIN_MVDM_INIT_CODE
//tw
#pragma BEGIN_MVDM_SWAP_CODE

/***EP+ vdmPropertyInit - Initialize VDM property data
 *
 *      ENTRY
 *          none
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *
 *      CONTEXT
 *          VDM Task
 */
ULONG vdmPropertyInit(void)
{
    if (KSEMCreate(&hksemPropertyBits,KRNL_MUTEXSEM) != 0)
        return FALSE;
    return TRUE;
}


/***EP+ vdmSetPropertyDefaults - Set default property value
 *
 *      ENTRY
 *          pvdmconfig - VDMCONFIG with values from CONFIG.SYS processing.
 *
 *      EXIT-SUCCESS
 *          returns 0
 *
 *      EXIT-FAILURE
 *          returns error code
 *              ERROR_INVALID_DATA
 *              ERROR_BUFFER_OVERFLOW
 *              ERROR_ENVVAR_NOT_FOUND
 */

ULONG vdmSetPropertyDefaults(PVDMCONFIG pvdmconfig)
{
    ULONG   ul;
    char    *pch;
    int     cb;
    ULONG   rc;
    char    ld[2];                                                /*BN003*/

    // Initialize global property list
    if(!vdmPropertyInit())
        return  ERROR_NOT_ENOUGH_MEMORY;

    pch = pvdmconfig->pszSHELL;

    if(pch == NULL) {           // Consruct default shell
        pch = "A:\\OS2\\MDOS\\COMMAND.COM /P";
        *pch = ((CHAR)SISData.SIS_BootDrv) + (CHAR)('A' - 1);
    }

    rc = setPropertyDefault(VDMP_ORD_SHELL,VDMP_STRING,strlen(pch)+1,pch);
    if (rc != NO_ERROR)
        goto Exit;

    ul = pvdmconfig->usRMSIZE;
    if (ul == 0)
        ul = RMSIZE_DEFAULT;
    rc = setPropertyDefault(VDMP_ORD_RMSIZE,VDMP_INT,sizeof(ULONG),SSToDS(&ul));
    if (rc != NO_ERROR)
        goto Exit;

    ul = pvdmconfig->nFCBs;
    if (ul == USHRT_MAX)
        ul = CFCBS_DEFAULT;
    rc = setPropertyDefault(VDMP_ORD_FCB,VDMP_INT,sizeof(ULONG),SSToDS(&ul));
    if (rc != NO_ERROR)
        goto Exit;

    ul = pvdmconfig->nFCBsNoClose;
    if (ul == USHRT_MAX)
        ul = CFCBS_NOCLOSE_DEFAULT;
    rc = setPropertyDefault(VDMP_ORD_FCB2,VDMP_INT,sizeof(ULONG),SSToDS(&ul));
    if (rc != NO_ERROR)
        goto Exit;
                                                                      /*BN011*/
    ul = pvdmconfig->nFILES;                                          /*BN011*/
    if (ul == USHRT_MAX)                                              /*BN011*/
        ul = DefaultFILESCount;                                       /*BN011*/
    rc = setPropertyDefault(VDMP_ORD_FILES,                           /*BN011*/
                            VDMP_INT,sizeof(ULONG),SSToDS(&ul));      /*BN011*/
    if (rc != NO_ERROR)                                               /*BN011*/
        goto Exit;                                                    /*BN011*/

    ul = pvdmconfig->iBREAK;
    rc = setPropertyDefault(VDMP_ORD_BREAK,VDMP_BOOL,sizeof(ULONG),SSToDS(&ul));
    if (rc != NO_ERROR)
        goto Exit;

    ul = pvdmconfig->Dos_Flags & DOS_UMB_BIT;                                        /*BN001*/
    rc = setPropertyDefault(VDMP_ORD_DOS_UMB,VDMP_BOOL,sizeof(ULONG),SSToDS(&ul));   /*BN001*/
    if (rc != NO_ERROR)                                                              /*BN001*/
        goto Exit;                                                                   /*BN001*/

    ul = pvdmconfig->Dos_Flags & DOS_HIGH_BIT;                                       /*BN002*/
    rc = setPropertyDefault(VDMP_ORD_DOS_HIGH,VDMP_BOOL,sizeof(ULONG),SSToDS(&ul));  /*BN002*/
    if (rc != NO_ERROR)                                                              /*BN002*/
        goto Exit;                                                                   /*BN002*/

   /* Create default last drive letter from previously configured last  */           /*BN003*/
   /* drive from config.sys.                                            */           /*BN003*/
   /*                                                                   */           /*BN003*/
    pch = SSToDS(&ld[0]);                                                            /*BN003*/
    ld[0] = 'A' + (char) uchLastDrive;                                               /*BN003*/
    ld[1] = '\0';                                                                    /*BN003*/
    rc = setPropertyDefault(VDMP_ORD_LASTDRIVE, VDMP_STRING,                         /*BN003*/
                            strlen(pch) + 1, pch);                                   /*BN003*/
    if(rc != NO_ERROR)                                                               /*BN003*/
       goto Exit;                                                                    /*BN003*/

    //  DOS DDs are sent to us as ASCIIZZ format.  The properties code
    //  prefers just ASCIIZ strings, and the user interface code prefers
    //  a line feed (0x0A) as a line separator (in the Multi-Line Edit
    //  class).  So, we change the NULLs to line feeds before initializing
    //  the default value.

    pch = pvdmconfig->pszzDOSDDs;
    if (pch != NULL) {
        while (*pch != '\0') {
            cb = strlen(pch);           // length of line
            *(pch+cb) = 0x0a;           // change NULL to line feed
            pch += cb+1;                // next line
        }
        *(pch-1) = '\0';                // Make ASCIIZZ to MLSTR
        pch = pvdmconfig->pszzDOSDDs;
        cb = strlen(pch)+1;             // count NULL terminator
        rc = setPropertyDefault(VDMP_ORD_DOSDD,VDMP_MLSTR,cb,pch);
        if (rc != NO_ERROR)
            goto Exit;
    }
Exit:
    return rc;
}


/***    setPropertyDefault - Set default property value
 *
 *      Create a PROPERTY record if it doesn't already exist, and
 *      then set the default value.  The VPTYPE is necessary here,
 *      in the event that the PROPERTY doesn't yet exist, because
 *      we need to parse the value correctly.
 *
 *      ENTRY
 *          vpord   - Property ordinal (see VPORD).
 *          vptype  - Property type (see VPTYPE).
 *          cb      - Length of pch buffer in bytes.
 *                    The value depends upon the VDD property type:
 *                      VDMP_BOOL
 *                      VDMP_INT
 *                         sizeof(ULONG) == 4.
 *                      VDMP_ENUM
 *                      VDMP_STRING
 *                      VDMP_MLSTR
 *                         Length of ASCIIZ string, including NULL terminator.
 *          pch     = Property Value.
 *                    The format depends on the VDD property type:
 *                      VDMP_BOOL
 *                         pch points to a BOOL;
 *                         Value 0 is FALSE; non-zero is TRUE.
 *                      VDMP_INT
 *                         pch points to a ULONG;  Only the low (USHORT)
 *                         portion is used.  The high half of the ULONG
 *                         is ignored.
 *                      VDMP_ENUM
 *                      VDMP_STRING
 *                      VDMP_MLSTR
 *                         pch points to a zero-terminated string.
 *
 *      EXIT-SUCCESS
 *          returns 0
 *
 *      EXIT-FAILURE
 *          returns error code
 *              ERROR_INVALID_DATA
 *                  value specified is invalid for this property.
 *                  If session id != 0, the property value for the specified
 *                      VDM is reset to the default value.
 *              ERROR_BUFFER_OVERFLOW
 *                  pch,cb is too small for this property.
 *              ERROR_ENVVAR_NOT_FOUND
 *                  pszName not a registered VDD property.
 *              ERROR_NOT_ENOUGH_MEMORY
 *                  not enough memory to create PROPERTY.
 */
ULONG   setPropertyDefault(VPORD vpord,VPTYPE vptype,ULONG cb,CHAR *pch)
/*end*/
{
    char        achName[10];
    BOOL        f;
    BOOL        fUser;
    char *      pchNext;
    PPROPERTY   pprop;
    void *      pvValid;
    void *      pvDefault;
    ULONG       ulReturn=0;
    VPBOUND     vpb;

    // Does property already exist?
    pprop = PROPFindOrdinal(ppropList,vpord);

    if (pprop == NULL) {                // NO, have to create a PROPERTY
        achName[0] = '#';
        achName[1] = (char)(((USHORT)'0')+vpord);
        achName[2] = '\0';              // temporary name is "#n"

        // set up parms for VDHRegisterProperty
        // NOTE: we pick maximally weak bounds for VDMP_INT

        switch (vptype) {
            case VDMP_BOOL:
                pvDefault = (void *) *(ULONG *) pch; // get default value
                pvValid = NULL;
                break;

            case VDMP_INT:
                pvDefault = (void *) *(ULONG *) pch; // get default value
                vpb.min = 0;                    // minimum validation
                vpb.max = 32767;                // minimum validation
                vpb.step = 1;                   // minimum validation
                pvValid = SSToDS(&vpb);
                break;

            case VDMP_STRING:
            case VDMP_MLSTR:
                pvValid = (void *)cb;           // use string size for max size
                pvDefault = pch;
                break;
        }

        f = VDHRegisterProperty(SSToDS(achName),// name
                                NULL,           // help file
                                NULL,           // help id
                                vptype,         // type
                                vpord,          // ordinal
                                NULL,           // flags
                                pvDefault,      // default value
                                pvValid,        // validation
                                fnvdhrpAlwaysGood // set/validate function
                                );
        if (!f) {
            ulReturn = VDHGetError();
            goto Exit;
        }

        // Set flag for VDHRegisterProperty
        pprop = PROPFindOrdinal(ppropList,vpord);
        pprop->idHelp = DEFAULT_BEFORE_REGISTER;
    }
    else {                              // PROPERTY already exists
        // parse value
        pchNext = getValue(SSToDS((ULONG *)&pvDefault), pch, pch+cb-1,
                    pprop, SSToDS(&fUser));
        if (pchNext == NULL) {
            ulReturn = ERROR_BUFFER_OVERFLOW;
            goto Exit;
        }

        // set value
        switch (pprop->vptype) {
            case VDMP_ENUM:
            case VDMP_STRING:
            case VDMP_MLSTR:
                swpFree(pprop->pvValue);  // free old string
                pprop->pvValue = pvDefault;
                pprop->cbValue = strlen(pprop->pvValue)+1;  // count NULL
                break;

            case VDMP_BOOL:
            case VDMP_INT:
                pprop->pvValue = pvDefault;
                break;
        }
    }

Exit:
    return ulReturn;
}


/***    fnvdhrpAlwaysGood - always say that a string is good
 *
 *      This function is called for  an unregistered property name is
 *      encountered.
 *
 *      ENTRY
 *          ignored
 *
 *      EXIT
 *          returns 0
 */

ULONG EXPENTRY fnvdhrpAlwaysGood(ULONG op, HVDM hvdm, ULONG cb, PSZ psz)
{
    op;                    // eliminate copiler warnings
    hvdm;
    cb;
    psz;

    return 0;
}


// BUGBUG 17-Nov-1989 bens - compiler bug causes CVK to not understand symbols
// #pragma END_MVDM_INIT_CODE
#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_23","CODE")
/***EP+ vdmCreatePropertyInstance - Create initial property values for a VDM
 *
 *      This function is intended to be called by DosCreateVDM before
 *      any VDDs are called.
 *
 *      The supplied property values are merged with the default property
 *      values to produce the property value list for the current VDM.
 *      A supplied property value overrides the default value.
 *
 *      For a supplied property value which is invalid, the default value
 *      is used.  No error is returned in this case.
 *
 *      ENTRY
 *          ppropbuf - buffer containing property name/value pairs.
 *                The format of the buffer is:
 *                  psz1   - 1st property name
 *                  value1 - value of property; format depends upon
 *                           property type:
 *                             VDMP_BOOL
 *                               ULONG interpreted as a BOOL.
 *                             VDMP_INT
 *                               ULONG.
 *                             VDMP_ENUM
 *                               ASCIIZ string.
 *                             VDMP_STRING
 *                               ASCIIZ string.
 *                             VDMP_MLSTR
 *                               ASCIIZ string, linefeed (0x0A) is
 *                               line separator.
 *                  psz2   - 2nd property name
 *                  value2 - value of 2nd property
 *                  ...
 *          phprin - location to return property instance handle
 *
 *      EXIT-SUCCESS
 *          returns 0, phprin filled in.
 *
 *      EXIT-FAILURE
 *          returns error code:
 *              ERROR_INSUFFICIENT_BUFFER
 *                  A property name or value was incompletely specified
 *                  at the point the end of the buffer was reached.
 *              ERROR_NOT_ENOUGH_MEMORY
 *                  Couldn't allocate memory for internal data.
 *              ERROR_BAD_FORMAT
 *                  An unknown property name was used, and either there
 *                  was insufficient memory in the user buffer, or the
 *                  system could not allocate more memory.
 *
 *      CONTEXT
 *          Task
 */
ULONG vdmCreatePropertyInstance(PPROPERTYBUFFER ppropbuf,HPROPINST *phprin)

/*end*/
{
    char      *pch;
    char      *pchEnd;
    char      *pchNext;
    int       cbName;
    int       cb;
    PPROPERTY pprop;
    PPROPINST pprin=NULL;
    PPROPINST pprinList=NULL;
    VPTYPE    vptype;
    ULONG     ulReturn=0;               // assume success
    BOOL      fUser;
    VMLH      hlock;
    BOOL      fLockDone=FALSE;

    // grab mutex for ppropList scan bits
    ulReturn = KSEMRequestMutex(hksemPropertyBits,-1);
    if (ulReturn != 0)
        goto Exit;
    clearScanBits();

    if (ppropbuf == NULL)               // user-specified values?
        goto GetDefaults;               //  NO, go get defaults

    ulReturn = TKFuDWord(SSToDS(&cb),(ULONG*)ppropbuf); // get buffer size
    if (ulReturn != 0)
        goto Exit;

    if (cb < sizeof(ppropbuf->cb))      // Buffer long enough?
        goto GetDefaults;               //  No, go get defaults

    pch = (char *)ppropbuf;             // start of buffer
    pchEnd = pch+cb-1;                  // last byte in buffer

    // Lock buffer so that it remains addressable
    ulReturn = VMLockMem((ULONG)pch, cb, PG_R, SSToDS(&hlock),
                             (struct pagelist_s *) VM_NO_ADDR,
                             (ulong_t *) VM_NO_ADDR);
    if (ulReturn != 0)
        goto Exit;
    fLockDone = TRUE;

    pch = (char *)&(ppropbuf->achPropBuf);  // start of <type,name,value>

    // Create property value list for supplied properties
    while (pch <= pchEnd) {

        // Get property type
        if (pch+sizeof(VPTYPE)-1 > pchEnd) {
            ulReturn = ERROR_INSUFFICIENT_BUFFER;
            goto Exit;
        }
        vptype = *(VPTYPE *)pch;    // get type
        pch += sizeof(VPTYPE);

        // Get Name
        cbName = checkPSZ(pch,pchEnd);  // length includes NULL!
        if (cbName == 0) {
            ulReturn = ERROR_INSUFFICIENT_BUFFER;
            goto Exit;
        }

        // Find property
        pprop = PROPFindNameOrOrdinal(ppropList,pch);
        if (pprop != NULL) {            // have a registered property name
            // Allocate property value structure
            pprin = swpAlloc(sizeof(PROPINST));
            if (pprin == NULL) {
                ulReturn = ERROR_NOT_ENOUGH_MEMORY;
                goto Exit;
            }

            // Get value
            pchNext = getValue((ULONG *)&(pprin->pvValue),pch+cbName,pchEnd,
                            pprop,SSToDS(&fUser));
            if (pchNext == NULL) {
                ulReturn = ERROR_INSUFFICIENT_BUFFER;
                goto Exit;
            }
            pprin->pprop = pprop;           // set link to property

            // mark property, no need to get default
            pprop->fScan = TRUE;

            // Add property value to list for this VDM
            // Use LIFO order because it is easy
            pprin->pprin = pprinList;
            pprinList = pprin;
        }
        else {
            // We cannot find the property name, ignore the value.
            //
            // Rationale
            // ---------
            // Consider the case where a user has an installable
            // VDD that registers a parameter, then uses the shell to
            // modify these parameters for a DOS app.  Later, the user
            // removes the VDD from config.sys and reboots.  Now, if the
            // user starts that DOS app, it will contain a parameter
            // which was *not* registered for this boot.  Rather than
            // forcing the user to edit the saved configuration, we
            // will ignore any properties which are not registered.
            //
            pchNext = skipValue(pch+cbName,pchEnd,vptype);
            if (pchNext == NULL) {
                ulReturn = ERROR_BAD_FORMAT;
                goto Exit;
            }
        }

        // Parse next pair
        pch = pchNext;
        pprin = NULL;                   // don't let exit free pprin
    }

    // Use default values for remaining properties
GetDefaults:
    for (pprop=ppropList; pprop != NULL; pprop = pprop->pprop) {
        if (!pprop->fScan) {    // unspecified property, use default
            pprin = swpAlloc(sizeof(PROPINST));
            if (pprin == NULL) {
                ulReturn = ERROR_NOT_ENOUGH_MEMORY;
                goto Exit;
            }
            pprin->pprop = pprop;       // set link to property
            switch (pprop->vptype) {    // set value
                case VDMP_BOOL:
                case VDMP_INT:
                    pprin->pvValue = pprop->pvValue;
                    break;

                case VDMP_STRING:
                case VDMP_MLSTR:
                case VDMP_ENUM:
                    pprin->pvValue = swpStrDup(pprop->pvValue);
                    if (pprin->pvValue == NULL) {
                        ulReturn = ERROR_NOT_ENOUGH_MEMORY;
                        goto Exit;
                    }
                    break;
            }
            pprin->pprin = pprinList;   // Add value to list
            pprinList = pprin;

            pprin = NULL;               // don't let exit free pprin
        }
    }

Exit:
    KSEMReleaseMutex(hksemPropertyBits); // done with scan bits
    if (fLockDone)
        VMUnlock(SSToDS(&hlock));       // release lock on user buffer

    if (ulReturn != 0) {                // free allocated resources
        if (pprin != NULL)              // failed allocated string
            swpFree(pprin);
        destroyPropInst(pprinList);     // destroy propinst list, if any
        pprinList = NULL;               // list is empty
    }
    *(PPROPINST *)phprin = pprinList;   // return propinst list
    return ulReturn;
}

/***EP+ vdmSetPropertyInstance - Set instance variable with property list
 *
 *      Set instance variable with property instance list.
 *
 *      ENTRY
 *          hprin - pprin created by vdmCreatePropertyInstance.
 *
 *      EXIT-SUCCESS
 *          none
 *
 *      EXIT-FAILURE
 *          none
 *
 *      CONTEXT
 *          VDM Task
 */
void  vdmSetPropertyInstance(HPROPINST hprin)
{
    pprinList = (PPROPINST)hprin;
}

#pragma code_seg()
#pragma code_seg("SWAP32_27","CODE")
/***EP+ vdmDestroyPropertyInstance - Destroy property list for current VDM
 *
 *      This function is intended to be called when destroying a VDM.
 *
 *      ENTRY
 *          pprinList - possibly non-empty list of PPRINs.
 *
 *      EXIT-SUCCESS
 *          none
 *
 *      EXIT-FAILURE
 *          none
 *
 *      CONTEXT
 *          VDM Task
 */
void vdmDestroyPropertyInstance(void)
/*end*/
{
     destroyPropInst(pprinList);
}



/***EP+ destroyPropInst - Destroy a property list
 *
 *      ENTRY
 *          pprinList - possibly non-empty list of PPRINs.
 *
 *      EXIT-SUCCESS
 *          none
 *
 *      EXIT-FAILURE
 *          none
 *
 *      CONTEXT
 *          VDM Task
 */
void destroyPropInst(PPROPINST pprinList)
{
    PPROPINST   pprin;
    PPROPINST   pprinNext;

    pprin=pprinList;
    while (pprin != NULL) {
        switch (pprin->pprop->vptype) { // destroy value, if necessary
            case VDMP_BOOL:
            case VDMP_INT:
                break;

            case VDMP_STRING:
            case VDMP_MLSTR:
            case VDMP_ENUM:
                swpFree(pprin->pvValue);
                break;
        }
        pprinNext = pprin->pprin;
        swpFree(pprin);
        pprin = pprinNext;
    }
}

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

/***EP+ vdmQueryProperty - Query VDD property value using property ordinal
 *
 *      Returns the current value for the specified VDM.
 *
 *      ENTRY
 *          vpord - Property ordinal.  [See prop.h.]
 *
 *      EXIT-SUCCESS
 *          returns value.  Format depends on property type:
 *              VDMP_BOOL
 *                 Return value is a BOOL.
 *                 Value 0 is FALSE; non-zero is TRUE.
 *              VDMP_INT
 *                 Return value is a ULONG.  It is guaranteed to be
 *                 valid with respect to the bounding information specified
 *                 in the VDHRegisterVDD call.
 *              VDMP_ENUM
 *                 Return value is a pointer to a null-terminated string.
 *                 The string is guaranteed to be one of those specified
 *                 in the VDHRegisterVDD call.
 *              VDMP_STRING
 *              VDMP_MLSTR
 *                 Return value is a pointer to a null-terminated string.
 *                 The string is guaranteed to be at most as long as the
 *                 limit specified in the VDHRegisterVDD call.
 *
 *              NOTE:  For property types VDMP_ENUM, VDMP_STRING, and
 *                     VDMP_MLSTR, a pointer to the current value is
 *                     returned, thus avoiding any race conditions
 *                     with an OS/2 process changing the value.
 *
 *                     Therefore, the caller must call !!!free!!! to free
 *                     the string after it is done with it.
 *
 *      EXIT-FAILURE
 *          Returns NULL for property types VDMP_ENUM, VDMP_STRING, and
 *          VDMP_MLSTR, if there is insufficient memory to create a copy of
 *          the string value.  The default property value should be used
 *          in this case.
 *
 *          Otherwise, the system will halt if pszName is invalid or
 *          not a registered VDD name.
 *
 *      CONTEXT
 *          VDM Task
 *
 *      NOTE
 *          The caller can assume that the property value is valid.  The
 *          system validates all types except VDMP_STRING and VDMP_MLSTR,
 *          using the validation values passed on the VDHRegisterProperty
 *          call.  The string types are validated by calling the VDD
 *          function registered via VDHRegisterProperty.
 */
ULONG vdmQueryProperty(VPORD vpord)
/*end*/
{
    PPROPERTY   pprop;
    PPROPINST   pprin;
    char *      pch;
    int         cb;
    ULONG       ul;

    pprop = PROPFindOrdinal(ppropList,vpord);
    if (pprop == NULL)
        Panic(("vdmQueryProperty:non-standard ordinal"));

    pprin = PRINFind(pprinList,pprop);  // get value to change
    if (pprin == NULL)
        Panic(("vdmQueryProperty:could not find property instance"));

    switch (pprop->vptype) {
        case VDMP_BOOL:
        case VDMP_INT:
            ul = (ULONG)(USHORT)pprin->pvValue;
            break;

        case VDMP_ENUM:
        case VDMP_STRING:
        case VDMP_MLSTR:
            cb = strlen(pprin->pvValue) + 1; // count NULL!
            pch = swpAlloc(cb);
            if (pch != NULL)
                memcpy(pch,pprin->pvValue,cb);
            ul = (ULONG)pch;
    }
    return ul;
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE

/***    skipValue - skip property value in user buffer
 *
 *      This function is called when an unregistered property name is
 *      encountered.
 *
 *      ENTRY
 *          pchValue - ptr to possible value
 *          pchEnd   - ptr to last valid byte (pchValue <= pchEnd)
 *          vptype   - property type
 *
 *      EXIT-SUCCESS
 *          returns pchNext, points after end of value
 *
 *      EXIT-FAILURE
 *          returns NULL, bad value format, or buffer too small.
 */

char *skipValue(char *pchValue, char *pchEnd, VPTYPE vptype)
{
    int     cb;

    switch (vptype) {
        case VDMP_BOOL:
        case VDMP_INT:
            if (pchValue+sizeof(ULONG)-1 > pchEnd) // check buffer room
                return NULL;    // not enough room for ULONG
            cb = sizeof(ULONG);
            break;

        case VDMP_STRING:
        case VDMP_MLSTR:
        case VDMP_ENUM:
            cb = checkPSZ(pchValue,pchEnd);  // length includes NULL!
            if (cb == 0)
                return NULL;    // string past end of buffer
            break;
        default:                //69663
            return NULL;        //69663
    }
    return pchValue+cb;         // points after value
}

#pragma END_MVDM_SWAP_CODE
#pragma  code_seg("SWAP32_26","CODE")

/***    getValue - parse property value from user buffer
 *
 *      For all but VDMP_INT properties, an invalid user value causes
 *      the default value to be returned.  For a VDMP_INT property,
 *      the user supply is rounded down to be a multiple of the "step"
 *      value, and then increased or decreased (if necessary) to be
 *      within the "min" and "max" boundaries (as specified in
 *      VDHRegisterProperty).
 *
 *      ENTRY
 *          pul      - ptr to ULONG to receive value
 *          pchValue - ptr to possible value
 *          pchEnd   - ptr to last valid byte (pchValue <= pchEnd)
 *          pprop    - property information
 *
 *      EXIT-SUCCESS
 *          returns pchNext, points after end of value
 *          pul filled in.
 *              For VDMP_STRING, VDMP_MLSTR, and VDMP_ENUM, this
 *                  is a pointer to a null-terminated string allocated
 *                  from the swappable heap.
 *
 *                  WARNING: Caller is responsible for freeing this
 *                           pointer!
 *
 *              For VDMP_BOOL and VDMP_INT this is BOOL and ULONG,
 *                  respectively.
 *          fUser = TRUE if user value was used.
 *                = FALSE if user value was NOT used.
 *
 *      EXIT-FAILURE
 *          returns NULL, value exceeded limit of pchEnd,
 *              or not enough memory.
 */
char *getValue(ULONG *pul, char *pchValue, char *pchEnd, PPROPERTY pprop, BOOL *fUser)
{
    ULONG    ul;
    int      cb;
    PVPBOUND pvpb;
    char     *pch;

    *fUser = TRUE;                      // assume user value used
    switch (pprop->vptype) {
        case VDMP_BOOL:
        case VDMP_INT:
            if (pchValue+sizeof(ULONG)-1 > pchEnd) // check buffer room
                return NULL;    // not enough room for ULONG
            ul = (USHORT)*((ULONG *)pchValue); // get value
            if (pprop->vptype == VDMP_BOOL) {
                if (ul > 1) {           // map TRUE to 1
                    ul = 1;
                }
            } else {
                pvpb = pprop->pvValid;
                if ((USHORT)ul < pvpb->min) { // put it in range
                    ul = pvpb->min;
                    *fUser = FALSE;     // didn't use user value
                }
                else if ((USHORT)ul > pvpb->max) {
                    ul = pvpb->max;
                    *fUser = FALSE;     // didn't use user value
                }
                else {
                     ul = ((ul-pvpb->min)/pvpb->step)*pvpb->step + pvpb->min;
                }
            }
            cb = sizeof(ULONG);
            break;

        case VDMP_STRING:
        case VDMP_MLSTR:
        case VDMP_ENUM:
            cb = checkPSZ(pchValue,pchEnd);  // length includes NULL!
            if (cb == 0)
                return NULL;            // string past end of buffer
            if (pprop->vptype != VDMP_ENUM) {   // string value
                if ((cb <= (int)(ULONG)(USHORT)pprop->pvValid) &&
                  !((*(pprop->pfnvdhrp))(VDHPROP_VALIDATE, 0L,
                    (ULONG)cb, pchValue))) {

                    // string is short enough, and VDD says it is valid
                    ul = (ULONG)swpStrNDup(pchValue,cb); // use user value
                }
                else {                  // string is too long
                    ul = (ULONG)swpStrDup(pprop->pvValue); // use default value
                    *fUser = FALSE;     // didn't use user value
                }
            }
            else {                      // enumerated type
                pch = findEnum(pprop,pchValue,cb);
                if (pch == NULL) {      // no match
                    ul = (ULONG)swpStrDup(pprop->pvValue); // use default value
                    *fUser = FALSE;     // didn't use user value
                }
                else
                    ul = (ULONG)swpStrDup(pch); // use user value
            }
            if (ul == 0)                // out of memory
                return NULL;            // act as if bad buffer format
            cb = strlen((char *)ul)+1;  // get length of string selected
            break;
    }
    *pul = ul;                          // return value
    return pchValue+cb;                 // return ptr to next byte in buffer
}


/***    checkPSZ - find end of ASCIIZ string in user buffer
 *
 *      Verifies that an ASCIIZ string is contained entirely in
 *      a user buffer
 *
 *      ENTRY
 *          pch - start of ASCIIZ string
 *          pchEnd - last byte in user buffer
 *
 *      EXIT-SUCCESS
 *          return length of ASCIIZ string, including NULL terminator
 *
 *      EXIT-FAILURE
 *          return NULL
 */
int checkPSZ(char *pch,char *pchEnd)
{
    char *pchSave=pch;

    while (pch<=pchEnd && *pch != '\0')
        pch++;

    if (pch <= pchEnd)
        return pch-pchSave+1;    // count that NULL
    else
        return NULL;
}


/***    findEnum - find enum value in property structure
 *
 *      ENTRY
 *          pprop    - property (of type VDMP_ENUM) to search
 *          pchValue - string to look for
 *          cb       - length of pchValue (including NULL)
 *
 *      EXIT-SUCCESS
 *          return pch of string in pprop (gets case correct)
 *
 *      EXIT-FAILURE
 *          return NULL
 */
char *findEnum(PPROPERTY pprop, char *pchValue, int cbValue)
{
    char    *pch;
    int     cb;

//assert(pprop->vptype == VDMP_ENUM);

    pch = pprop->pvValid;
    while (*pch != '\0') {
         // Have to be careful to look only as far as we know the
         // string goes.  User may edit the buffer after we have
         // done the strlen!
         if (strncmp(pchValue,pch,cbValue) == 0)
            return pch;     // found match
         cb = strlen(pch);  // advance
         pch += cb+1;
    }
    return NULL;    // no match
}


#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/***EP+ VDHRegisterProperty - Register VDD property name with VDMM
 *
 *      Register a VDD property.
 *
 *      The PM Shell will use the DosQueryDOSProperty API to enumerate all
 *      registered properties, and to determine their type and constraints
 *      (enumerated types and integer types).
 *
 *      VDHQueryProperty is used to get the value of a VDD property
 *      for a particular VDM.
 *
 *      ENTRY
 *          pszName - Asciiz property name.  Maximum length is 40
 *                    characters.
 *          pszHelp - asciiz help file name.  Should not specify
 *                    fully-qualified path.
 *                    NULL if no help file available.
 *          idHelp  - Help id number.
 *          vptype  - Property type (see VPTYPE).
 *          vpord   - Property ordinal (see VPORD).
 *          ulFlags - Property flags
 *                      VDMP_CREATE
 *                          The property may only be specified at
 *                          VDM creation.  Any change to the property
 *                          after VDM creation is ignored.
 *          pvDefault - Default value, format depends upon vptype:
 *                    VDMP_BOOL
 *                      pvDefault is interpreted as a BOOL.
 *                    VDMP_INT
 *                      pvDefault is interpreted as a ULONG.  Only the
 *                      low half (USHORT) is used.  The high half is
 *                      is ignored.
 *                    VDMP_ENUM
 *                      pvDefault is a pointer to an ASCIIZ string.
 *                    VDMP_STRING
 *                    VDMP_MLSTR
 *                      pvDefault is a pointer to an ASCIIZ string.
 *
 *          pvValid - The meaning of this field depends upon vptype:
 *                    VDMP_BOOL
 *                      NULL - booleans are easily validated by caller.
 *                    VDMP_INT
 *                      pvValid is a pointer to a VPBOUND structure.
 *                    VDMP_ENUM
 *                      pvValid is a pointer to a set of ASCIIZ strings,
 *                      terminated by a zero byte, which is the allowed
 *                      set of responses.  The shell will use this to
 *                      construct a list/combo box for the user to pick
 *                      from.  Empty ("\0") responses are not allowed
 *                      and terminated the ASCIIZ string (does not return
 *                      an error).
 *                    VDMP_STRING
 *                    VDMP_MLSTR
 *                      pvValid is a ULONG that is the maximum allowed
 *                      string length, including NULL terminator.
 *
 *          pfnvdhrp - Function to validate this property and accept
 *                    changes to this property for a running VDM.
 *                    Ignored if VDMP_CREATE is specified in
 *                    conjunction with VDMP_BOOL, VDMP_INT, or VDMP_ENUM.
 *                    See PFNVDHRP definition for details.
 *                    Note: This function is required for any property
 *                    that does not specify VDMP_CREATE.
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *          VDHGetError can return:
 *              ERROR_OUT_OF_MEMORY
 *                  Cannot allocate memory to remember property info.
 *              ERROR_DUPLICATE_NAME
 *                  This name is already registered.
 *              ERROR_INVALID_PARAMETER
 *                  pfnvdhrp is null when it has to be provided.
 *          The system will halt if pszName, pszHelp, pvValid, or pfn
 *          pointers are invalid, or if ulFlags or vptype are invalid,
 *          or if VPBOUND has invalid contents, or if the max string
 *          length for a VDMP_STRING is less than the length of the
 *          default string value.
 *
 *      CONTEXT
 *          Init
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 */
BOOL VDHENTRY VDHRegisterProperty(PSZ pszName,
                        PSZ  pszHelp,
                        ULONG idHelp,
                        VPTYPE vptype,
                        VPORD vpord,
                        ULONG ulFlags,
                        PVOID pvDefault,
                        PVOID pvValid,
                        PFNVDHRP pfnvdhrp
                        )
/*end*/
{
    PPROPERTY   pprop=NULL;
//  PPROPERTY   pprop1;
    char *      pch;
//  int         cb;
    PVPBOUND    pvpb;

    // Check for duplicate name
    if (PROPFind(ppropList,pszName) != NULL) {
        VDHSetError(ERROR_DUPLICATE_NAME);
        return NULL;
    }

    // Check for null validation-set function when not create time only
    if ((pfnvdhrp == NULL) && !(ulFlags & VDMP_CREATE)) {              //BN012
#ifdef  VDHSTRICT                                                      //BN012
        Panic(("VDHRegisterProperty: Invalid Parameter"));             //BN012
#endif                                                                 //BN012
        VDHSetError(ERROR_INVALID_PARAMETER);                          //BN012
        return NULL;                                                   //BN012
    }                                                                  //BN012

    // VDMM creates PROPERTY records for "standard" properties *before*
    // any VDDs are loaded.  We look for the property by ordinal first.
    //
    // NOTE: By definition, property ordinals only apply to properties
    //       that the kernel (VDMM) needs.

    if (vpord != VDMP_ORD_OTHER) {
        // look for existing property
        pprop = PROPFindOrdinal(ppropList,vpord);
        if (pprop != NULL) {
            // was default set by kernel?
            if (pprop->idHelp != DEFAULT_BEFORE_REGISTER) { // no, fail
                VDHSetError(ERROR_DUPLICATE_NAME);
                return NULL;
            }

            // Use existing PROPERTY structure
            swpFree(pprop->pszName);        // free temporary name
            pprop->pszName = swpStrDup(pszName);   // copy real name
            if (pprop->pszName == NULL) {   // not enough memory
                VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
                return NULL;                // not enough memory
            }
        }
    }

    if (pprop == NULL) {                // No property record, yet
        pprop = PROPCreate(pszName);    // Create propery record
        if (pprop == NULL) {
            VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
            return NULL;                // not enough memory
        }
    }

    // set almost all scalars
    pprop->vpord = vpord;               // set ordinal
    pprop->vptype = vptype;             // set type
    pprop->ulFlags = ulFlags;           // set flags
    pprop->pfnvdhrp = pfnvdhrp;         // set function

    // set help file
    if (pszHelp == NULL)
        pprop->pszHelp = NULL;
    else {
        pprop->pszHelp = swpStrDup(pszHelp);// set help file
        if (pprop->pszHelp == NULL) {
            PROPDestroy(pprop);
            VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
            return NULL;                // not enough memory
        }
    }

    switch (vptype) {
        case VDMP_BOOL:
            pprop->cbValid = 0;         // no validation info for user
            pprop->cbValue = 0;         // no value buffer

            // only save default if not already set
            if (pprop->idHelp != DEFAULT_BEFORE_REGISTER)
                pprop->pvValue = pvDefault; // save default value
            break;

        case VDMP_INT:
            if (pprop->pvValid != NULL) // setDefaultProperty created it!
                pvpb = pprop->pvValid;  // get existing pvpb
            else {
                pvpb = swpAlloc(sizeof(VPBOUND));
                if (pvpb == NULL) {
                    PROPDestroy(pprop);
                    VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
                    return NULL;
                }
                pprop->cbValid = sizeof(VPBOUND);
                pprop->pvValid = pvpb;      // set bounding info pointer
                pprop->cbValue = 0;         // no value buffer
            }
            memcpy(pvpb,pvValid,sizeof(VPBOUND));

            // only save default if not already set
            if (pprop->idHelp != DEFAULT_BEFORE_REGISTER)
                pprop->pvValue = pvDefault; // save default value
            break;

        case VDMP_STRING:
        case VDMP_MLSTR:
            pprop->cbValid = 0;     // no validation info for user
            pprop->pvValid = pvValid;   // save max string length

            // only save default if not already set
            if (pprop->idHelp != DEFAULT_BEFORE_REGISTER) {
                pprop->cbValue = strlen(pvDefault)+1;   // get length
                if (pprop->cbValue > (int)pvValid) {
                    Panic(("VDHRegisterProperty: default string too long"));
                }
                pprop->pvValue = swpStrDup(pvDefault); // save default
                if (pprop->pvValue == NULL) {
                    PROPDestroy(pprop);
                    VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
                    return NULL;
                }
            }
            break;

        case VDMP_ENUM:
            // compute length of asciizz block

            for (pch=pvValid; *pch != '\0'; pch=myStrChr(pch,'\0')+1)
                ;
            pprop->cbValid = pch - pvValid + 1; // block length, including NULL

            pch = swpAlloc(pprop->cbValid);
            if (pch == NULL) {
                PROPDestroy(pprop);
                VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
                return NULL;
            }
            memcpy(pch,pvValid,pprop->cbValid); // copy asciizz block
            pprop->pvValid = pch;       // save pointer

            // save default value

            // only save default if not already set
            if (pprop->idHelp != DEFAULT_BEFORE_REGISTER) {
                pprop->pvValue = swpStrDup(pvDefault); // save default
                pprop->cbValue = strlen(pprop->pvValue)+1;
                if (pprop->pvValue == NULL) {
                    PROPDestroy(pprop);
                    VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
                    return NULL;
                }
            }
            break;

        default:
            Panic(("VDHRegisterProperty: vptype invalid"));
    }

    // Only add property to list if it is not already there

    if (pprop->idHelp != DEFAULT_BEFORE_REGISTER) {
        // add new property to list.  Use LIFO because it is easy.
        pprop->pprop = ppropList;
        ppropList = pprop;
    }

    // set help id, clear DEFAULT_BEFORE_REGISTER setting
    pprop->idHelp = idHelp;             // set help id
    return  TRUE;
}

#pragma END_MVDM_SWAP_CODE
#pragma  code_seg("SWAP32_23","CODE")
/***EP+ VDHQueryProperty - Query VDD property value
 *
 *      Returns the current value for the specified VDM.
 *
 *      ENTRY
 *          pszName - Asciiz property name.  Maximum length is 40
 *                    characters, including NULL terminator.
 *
 *      EXIT-SUCCESS
 *          returns value.  Format depends on property type:
 *              VDMP_BOOL
 *                 Return value is a BOOL.
 *                 Value 0 is FALSE; non-zero is TRUE.
 *              VDMP_INT
 *                 Return value is a ULONG.  It is guaranteed to be
 *                 valid with respect to the bounding information specified
 *                 in the VDHRegisterVDD call.  Only the low half (USHORT)
 *                 of the ULONG is significant.  The high half is always 0.
 *              VDMP_ENUM
 *                 Return value is a pointer to a null-terminated string.
 *                 The string is guaranteed to be one of those specified
 *                 in the VDHRegisterVDD call.
 *              VDMP_STRING
 *              VDMP_MLSTR
 *                 Return value is a pointer to a null-terminated string.
 *                 The string is guaranteed to be at most as long as the
 *                 limit specified in the VDHRegisterVDD call.
 *
 *              NOTE:  For property types VDMP_ENUM, VDMP_STRING, and
 *                     VDMP_MLSTR, the VDD is returned a pointer to the
 *                     current value, thus avoiding any race conditions
 *                     with  an OS/2 process changing the value.
 *
 *                     Therefore, the VDD must call VDHFreeMem to free
 *                     the string after the VDD is done with it.
 *
 *      EXIT-FAILURE
 *          Returns NULL for property types VDMP_ENUM, VDMP_STRING, and
 *          VDMP_MLSTR, if there is insufficient memory to create a copy of
 *          the string value.  The VDD should use the default property value
 *          in this case.  NULL is also returned if pszName is invalid or
 *          not a registered VDD name.
 *
 *      CONTEXT
 *          VDM Task
 *
 *      VDM TERMINATION IMPLICATIONS
 *          All memory allocated by a VDD for use by the terminating
 *          VDM should be freed using VDHFreeMem.
 *
 *      NOTE
 *          A VDD can assume that the property value is valid.  The
 *          system validates all types except VDMP_STRING and VDMP_MLSTR,
 *          using the validation values passed on the VDHRegisterProperty
 *          call.  The string types are validated by calling the VDD
 *          function registered via VDHRegisterProperty.
 */
ULONG VDHENTRY VDHQueryProperty(PSZ pszName)
/*end*/
{
    PPROPERTY   pprop;
    PPROPINST   pprin;
    char *      pch;
    int         cb;
    ULONG       ul;

    pprop = PROPFindNameOrOrdinal(ppropList,pszName);
    if (!pprop)
        return 0;

    pprin = PRINFind(pprinList,pprop);  // Find value
    if (pprin == NULL)
        Panic(("VDHQueryProperty:No property instance record"));

    switch (pprop->vptype) {
        case VDMP_BOOL:
        case VDMP_INT:
            ul = (ULONG)(USHORT)pprin->pvValue;
            break;

        case VDMP_ENUM:
        case VDMP_STRING:
        case VDMP_MLSTR:
            cb = strlen(pprin->pvValue) + 1; // count NULL!
            pch = VDHAllocMem(cb+1,VDHAM_SWAPPABLE);
            if (pch != NULL)
                memcpy(pch,pprin->pvValue,cb);
            ul = (ULONG)pch;
    }
    return ul;
}

// #pragma BEGIN_MVDM_SWAP_CODE       don't need

/***EP+ VR32QueryDOSProperty - Query DOS property information
 *
 *      ENTRY
 *          sef     - system entry frame
 *          sesid   - Session ID of VDM.  If NULL, then global VDD
 *                    property information is returned (see below).
 *                    If session id is that of a VDM, then information
 *                    specific to that VDM is returned.
 *                    If session is not that of a VDM, an error is returned.
 *          pszName - Asciiz property name.  Maximum length is 40 bytes.
 *                    The value of pszName and session id control what kind
 *                    of information is returned in pch.
 *          cb      - Length of pch buffer in bytes
 *          pch     - Output buffer.  Contents are determined as follows
 *
 *                    pszName sesid pch contents returned
 *                    ------- ----  -----------------------------
 *                    NULL    NULL  List of all registered VDD property
 *                                  names, in ASCIIZZ format.
 *
 *                    NULL    VDM   List of only those VDD properties
 *                                  names that can be modified for a
 *                                  running VDM.  ASCIIZZ format.
 *
 *                    valid   NULL  Detailed information for this property.
 *                                  See VPINFO type.
 *
 *                    valid   VDM   Value of specified property for this
 *                                  VDM. See DosSetDOSProperty for the
 *                                  format of the returned value.
 *
 *      EXIT-SUCCESS
 *          returns 0
 *
 *      EXIT-FAILURE
 *          returns error code
 *              ERROR_BUFFER_OVERFLOW
 *                  pch buffer was too small.  You should increase the size
 *                  of pch and retry the call.
 *              ERROR_ENVVAR_NOT_FOUND
 *                  pszName not a registered VDD property.
 *              ERROR_INVALID_SESSION_ID
 *                  session id is not that of a VDM.
 */

RETCODE CDECL VR32QueryDOSProperty(THUNKSEF sef,ULONG sesid,PSZ pszName,ULONG cb,PSZ pch)
/*end*/
{
    ULONG ul;
    VMLH hlock;
    char *pszNameGood=NULL;
    SGID sgid = (USHORT)(sesid & 0xff);   // low byte session id to is the SGID

    sef;                   // eliminate compiler warning

    // don't let zero length buffers thru to VMLockMem
    if (cb == 0) {
        return ERROR_BUFFER_OVERFLOW;
    }

    // lock and validate user buffer
    ul = VMLockMem((ULONG) pch, cb, PG_W, SSToDS(&hlock),
                       (struct pagelist_s *) VM_NO_ADDR,
                       (ulong_t *) VM_NO_ADDR);
    if (ul != 0)
        return ul;                      // lock failed

    if (pszName == NULL)                // return name list
        ul = queryPropertyNames(sgid,cb,pch);
    else {
        // TKFuBuffz *requires* resident memory
        ul = VMAllocKHB(VM_HKH_PUB_RESRW, MAX_PROPERTY_NAME_LENGTH,
                        (VMHOB)VDDSTROWNER, NA, SSToDS(&pszNameGood));
        if (ul != 0)
            goto Exit;                  // not enough memory

        // Get name into kernel buffer
        ul = TKFuBuffz(NULL,pszNameGood,pszName,
                        MAX_PROPERTY_NAME_LENGTH,TK_UM_FATAL);
        if (ul != 0)
            goto Exit;                  // bad property name buffer

        if (sesid == NULL)              // return property detail
            ul = queryPropertyDetail(pszNameGood,cb,pch);
        else // return property value
            ul = queryPropertyValue(sgid,pszNameGood,cb,pch);
    }
Exit:
    if (pszNameGood != NULL)
        VMFreeKHB(VM_HKH_PUB_RESRW,pszNameGood);
    VMUnlock(SSToDS(&hlock));       // release lock on user buffer
    return ul;
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/***EP+ VR32SetDOSProperty - Set/Validate DOS property value
 *
 *      ENTRY
 *          sef     - system entry frame
 *          sesid   - Session ID of VDM for which to set property.
 *                    If NULL, this function only checks the
 *                    validity of the property value, independent
 *                    of any VDM.  Validation is only supported for
 *                    VDMP_STRING and VDMP_MLSTR types, since
 *                    DosQueryDOSProperty returns information
 *                    sufficient for the caller to validate all other
 *                    property types.
 *          pszName - Asciiz property name.  Maximum length is 40 bytes.
 *          cb      - Length of pch buffer in bytes.
 *                    The value depends upon the VDD property type:
 *                      VDMP_BOOL
 *                      VDMP_INT
 *                         sizeof(ULONG) == 4.
 *                      VDMP_ENUM
 *                      VDMP_STRING
 *                      VDMP_MLSTR
 *                         Length of ASCIIZ string, including NULL terminator.
 *          pch     = Property Value.
 *                    The format depends on the VDD property type:
 *                      VDMP_BOOL
 *                         pch points to a BOOL;
 *                         Value 0 is FALSE; non-zero is TRUE.  Non-zero
 *                         values are adjusted to 1, no error is returned.
 *                      VDMP_INT
 *                         pch points to a ULONG;  Only the low (USHORT)
 *                         portion is used.  The high half of the ULONG
 *                         is ignored.
 *                      VDMP_ENUM
 *                      VDMP_STRING
 *                      VDMP_MLSTR
 *                         pch points to a zero-terminated string.
 *
 *      EXIT-SUCCESS
 *          returns 0
 *              If session id != 0, the property value for the specified
 *                  VDM is set to the specified value.
 *
 *      EXIT-FAILURE
 *          returns error code
 *              ERROR_INVALID_FUNCTION
 *                  If session id == 0 and type is not VDMP_STRING or
 *                  VDMP_MLSTR or if a create time only property is set.
 *              ERROR_INVALID_DATA
 *                  value specified is invalid for this property.
 *                  If session id != 0, the property value for the specified
 *                      VDM is set to the min value if < min or to the max
 *                      value if > max.
 *              ERROR_BUFFER_OVERFLOW
 *                  pch,cb is too small for this property.
 *              ERROR_ENVVAR_NOT_FOUND
 *                  pszName not a registered VDD property.
 *              ERROR_INVALID_SESSION_ID
 *                  session id is not that of a VDM.
 */

RETCODE CDECL VR32SetDOSProperty(THUNKSEF sef,ULONG sesid,PSZ pszName,ULONG cb,PSZ pch)
/*end*/
{
    ULONG ul=0;
    PPROPERTY pprop;
    PPROPINST pprin;
    char *pchNext;
    void *pvValue;
    BOOL fUser;
    HVDM hvdm;
    int cbValue;
    char *pszNameGood=NULL;
    VMLH hlock;
    SGID sgid = (USHORT)(sesid & 0xff);   // low byte session id to is the SGID

    sef;                   // eliminate compiler warning

    // don't let zero length buffers thru to VMLockMem
    if (cb == 0) {
        return ERROR_BUFFER_OVERFLOW;
    }

    // lock and validate user buffer
    ul = VMLockMem((ULONG)pch, cb, PG_W, SSToDS(&hlock),
                    (struct pagelist_s *) VM_NO_ADDR,
                    (ulong_t *) VM_NO_ADDR);
    if (ul != 0)
        return ul;                      // lock failed

    // TKFuBuffz *requires* resident memory
    ul = VMAllocKHB(VM_HKH_PUB_RESRW, MAX_PROPERTY_NAME_LENGTH,
                    (VMHOB)VDDSTROWNER, NA, SSToDS(&pszNameGood));
    if (ul != 0)
        goto Exit;                  // not enough memory

    // Get name into kernel buffer
    ul = TKFuBuffz(NULL,pszNameGood,pszName,
                    MAX_PROPERTY_NAME_LENGTH,TK_UM_FATAL);
    if (ul != 0)
        goto Exit;                  // bad property name buffer

    pprop = PROPFindNameOrOrdinal(ppropList,pszNameGood);
    if (pprop == NULL) {
        ul = ERROR_ENVVAR_NOT_FOUND;
        goto Exit;
    }

    // parse value
    pchNext = getValue(SSToDS((ULONG *)&pvValue), pch, pch+cb-1,
                pprop, SSToDS(&fUser));
    if (pchNext == NULL) {
        ul = ERROR_BUFFER_OVERFLOW;
        goto Exit;
    }

    if (sesid == NULL) {                // value was validated
        switch (pprop->vptype) {
            case VDMP_STRING:
            case VDMP_MLSTR:
                swpFree(pvValue);       // free local copy of value
                break;
            case VDMP_ENUM:
                swpFree(pvValue);       // free local copy of value
            default:
                ul = ERROR_INVALID_FUNCTION;
                break;
        }
    }
    else {                              // set value
        hvdm = VDHHandleFromSGID(sgid); // get HVDM
        if (hvdm == 0) {
            ul = VDHGetError();         // not a VDM
            goto Exit;
        }
        if (pprop->ulFlags & VDMP_CREATE) {
            ul = ERROR_INVALID_FUNCTION;
            goto Exit;
        }
        pprin = REFHVDM(hvdm, PPROPINST, pprinList); // get property list
        pprin = PRINFind(pprin,pprop); // get value to change
        if (pprin == NULL)
            Panic(("VR32SetDOSProperty:No property instance record"));

        switch (pprop->vptype) {
            case VDMP_ENUM:
            case VDMP_STRING:
            case VDMP_MLSTR:
                swpFree(pprin->pvValue);  // free old string
                pprin->pvValue = pvValue;

// BUGBUG sudeepb 30-April-1990:
//
// MLSTR can be passed by user as string1<LF>string2<LF>string3'\0'
//                             or string1<LF>string2<LF>string3<LF>'\0'
// Properties will store MLSTR in first format (i.e. without trailing <LF>).
// The following code has been changed to take care of this. But it has to
// be scrutanized when we take care of DBCS in all the MVDM code.
//
// BUGBUG
                cbValue = strlen(pvValue);

                if(((PCHAR)pvValue)[cbValue-1] == 0x0a)
                    ((PCHAR)pvValue)[cbValue-1] = '\0';
                else
                    cbValue++;            // Add one for NULL

                break;

            case VDMP_BOOL:
            case VDMP_INT:
                pprin->pvValue = pvValue;
                cbValue = 0;                 // cb is ignored
                break;
        }
        // Tell VDD of value change
        (*(pprop->pfnvdhrp))(VDHPROP_SET,hvdm,(ULONG)cbValue,pprin->pvValue);
    }

    if (!fUser)                         // user value was bad
        ul = ERROR_INVALID_DATA;
Exit:
    if (pszNameGood != NULL)            // free copy of name
        VMFreeKHB(VM_HKH_PUB_RESRW,pszNameGood);
    VMUnlock(SSToDS(&hlock));           // release lock on user buffer
    return ul;
}


/***LP+ queryPropertyNames - Return property names
 *
 *      ENTRY
 *          sgid - Screen group ID of VDM (0 => return all names)
 *          cb  - Length of pch buffer in bytes.
 *          pch - Output buffer.  Contents are list of asiiz strings,
 *                terminated with a null character.
 *
 *      EXIT-SUCCESS
 *          returns 0
 *
 *      EXIT-FAILURE
 *          returns error code
 *              ERROR_BUFFER_OVERFLOW
 *                  Buffer too small to contain all the names.
 */
ULONG queryPropertyNames(SGID sgid, ULONG cb, char *pch)
/*end*/
{
    PPROPERTY   pprop;
    int         cbName;
    char *      pchSave=pch;

    for (pprop=ppropList; pprop!=NULL; pprop=pprop->pprop) {
        if ((sgid != 0) && (pprop->ulFlags & VDMP_CREATE))
            continue;   // don't return create-time properties
        cbName = strlen(pprop->pszName)+1;  // count zero terminator
        if (cb >= (ULONG)cbName) {             // room for name in buffer'
            memcpy(pch,pprop->pszName,cbName); // copy name
            cb -= cbName;               // adjust size remaining in buffer
            pch += cbName;              // advance buffer pointer
        }
        else {                          // not enough room
            *pchSave = '\0';            // make buffer look empty
            return ERROR_BUFFER_OVERFLOW;
        }
    }
    if (cb > 0) {
        *pch = '\0';                // terminate strings
        return NO_ERROR;
    }
    else {                          // not enough room for terminal 0
        return ERROR_BUFFER_OVERFLOW;
    }
}

#pragma END_MVDM_SWAP_CODE
#pragma code_seg ("SWAP32_23","CODE")
/***LP+ queryPropertyDetail - Return detailed property info
 *
 *      ENTRY   pszName - property name
 *              cb  - Length of pch buffer in bytes.
 *              pch - Output buffer, receive VPINFO structure.
 *
 *      EXIT-SUCCESS
 *          returns 0
 *
 *      EXIT-FAILURE
 *          returns error code
 *              ERROR_BUFFER_OVERFLOW
 *                  Buffer too small to contain all the names.
 *              ERROR_ENVVAR_NOT_FOUND
 *                  pszName not a registered VDD property.
 */
ULONG queryPropertyDetail(char *pszName, ULONG cb, char *pchStart)
/*end*/
{
    PPROPERTY   pprop;
    PVPINFO     pvpinfo;
//  void *      pvValid;
//  void *      pvValue;
    char *      pchTail;
    char *      pch;
    int         cch;

    pch = pchStart;
    pvpinfo = (PVPINFO)pch;

    pprop = PROPFindNameOrOrdinal(ppropList,pszName);
    if (pprop == NULL)
        return ERROR_ENVVAR_NOT_FOUND;

    pchTail = pch+cb;                   // first byte after user buffer

    if (pch+sizeof(VPINFO) > pchTail)
        return ERROR_BUFFER_OVERFLOW;

    // copy fixed fields
    pvpinfo->vi_vptype   = pprop->vptype;
    pvpinfo->vi_vpord    = pprop->vpord;
    pvpinfo->vi_ulFlags  = pprop->ulFlags;
    pvpinfo->vi_ulHelpID = pprop->idHelp;

    // copy variable fields
    pch += sizeof(VPINFO);              // start of variable fields

    // copy help file name
    if (pprop->pszHelp == NULL) {       // no help string
        pch = putBytes(pch,pchTail,(char *)&pprop->pszHelp,1); // store NULL string
        if (pch == NULL)                // no room for NULL string
            return ERROR_BUFFER_OVERFLOW;
    }
    else {
        cch = strlen(pprop->pszHelp)+1;  // count NULL in length
        pch = putBytes(pch,pchTail,pprop->pszHelp,cch); // store string
        if (pch == NULL)                // no room for NULL string
            return ERROR_BUFFER_OVERFLOW;
    }

    // copy validation info and default value
    switch (pprop->vptype) {
        case VDMP_BOOL:
            // no validation info to store
            // store value
            pch = putBytes(pch,pchTail,(char *)&pprop->pvValue,
                      sizeof(ULONG));   // store value
            if (pch == NULL)            // no room for store
                return ERROR_BUFFER_OVERFLOW;
            break;

        case VDMP_INT:
            // store validation info
            pch = putBytes(pch,pchTail,pprop->pvValid,
                      sizeof(VPBOUND));     // store bounds
            if (pch == NULL)                // no room for store
                return ERROR_BUFFER_OVERFLOW;

            // store value
            pch = putBytes(pch,pchTail,(char *)&pprop->pvValue,
                      sizeof(ULONG));   // store value
            if (pch == NULL)            // no room for store
                return ERROR_BUFFER_OVERFLOW;
            break;

        case VDMP_ENUM:
            // store validation info
            pch = putBytes(pch,pchTail,pprop->pvValid,
                      pprop->cbValid);  // store enumerations strings
            if (pch == NULL)            // no room for store
                return ERROR_BUFFER_OVERFLOW;

            // store value
            pch = putBytes(pch,pchTail,pprop->pvValue,
                      pprop->cbValue);  // store value
            if (pch == NULL)            // no room for store
                return ERROR_BUFFER_OVERFLOW;
            break;

        case VDMP_STRING:
        case VDMP_MLSTR:
            // store string length
            pch = putBytes(pch,pchTail,(char *)&pprop->pvValid,
                      sizeof(ULONG));  // store max string length
            if (pch == NULL)            // no room for store
                return ERROR_BUFFER_OVERFLOW;

            // store value
            pch = putBytes(pch,pchTail,pprop->pvValue,
                      pprop->cbValue);  // store value
            if (pch == NULL)            // no room for store
                return ERROR_BUFFER_OVERFLOW;
            break;
    }

    return NO_ERROR;
}
#pragma code_seg()
#pragma code_seg("SWAP32_26","CODE")

char *putBytes(char *pch, char *pchTail, char *pchValue, int cchValue)
{
    if (pch+cchValue <= pchTail) {
        memcpy(pch,pchValue,cchValue); // copy value
        return pch+cchValue;
    }
    else
        return NULL;
}
/***LP+ queryPropertyValue - Return a property value
 *
 *      ENTRY   sgid    - Screen group ID of VDM
 *              pszName - Property name
 *              cb      - Length of pch buffer in bytes.
 *              pch     - Output buffer, receive VPINFO structure.
 *
 *      EXIT-SUCCESS
 *          returns 0
 *
 *      EXIT-FAILURE
 *          returns error code
 *              ERROR_BUFFER_OVERFLOW
 *                  Buffer too small to contain all the names.
 *              ERROR_ENVVAR_NOT_FOUND
 *                  pszName not a registered VDD property.
 */
ULONG queryPropertyValue(SGID sgid, char *pszName, ULONG cb, char *pch)
{
    PPROPERTY   pprop;
    PPROPINST   pprin;
    int         cbValue;
    HVDM        hvdm;

    pprop = PROPFindNameOrOrdinal(ppropList,pszName);
    if (pprop == NULL)
        return ERROR_ENVVAR_NOT_FOUND;

    // get pprinList from sgid
    hvdm = VDHHandleFromSGID(sgid);     // get HVDM
    if (hvdm == 0)
        return VDHGetError();
    pprin = REFHVDM(hvdm, PPROPINST, pprinList); // get property list

    // find value to change

    pprin = PRINFind(pprin,pprop);  // get value to change
    if (pprin == NULL)
        Panic(("queryPropertyValue:No property instance record"));

    switch (pprop->vptype) {
        case VDMP_BOOL:
        case VDMP_INT:
            if (cb < sizeof(ULONG))
                return ERROR_BUFFER_OVERFLOW;
            *((ULONG*)pch) = (ULONG)(USHORT)pprin->pvValue;
            break;

        case VDMP_ENUM:
        case VDMP_STRING:
        case VDMP_MLSTR:
            cbValue = strlen(pprin->pvValue)+1;
            if (cb < (ULONG)cbValue)
                return ERROR_BUFFER_OVERFLOW;
            memcpy(pch,pprin->pvValue,cbValue);
            break;
    }
    return NO_ERROR;
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE

/***    PROPCreate - create a PROPERTY record
 *
 *      ENTRY   psz - asciiz property name
 *
 *      EXIT-SUCCESS
 *              return non-NULL PPROPERTY
 *      EXIT-FAILURE
 *              return NULL (not enough memory)
 *
 */
PPROPERTY PROPCreate(char *psz)
{
    PPROPERTY pprop;

    pprop = swpAlloc(sizeof(PROPERTY));
    if (pprop == NULL)
        return NULL;

    pprop->pszName = swpStrDup(psz);   // copy name, save pointer
    if (pprop->pszName == NULL) {   // not enough memory
        swpFree(pprop);               // free property record
        return NULL;                // indicate failure
    }

    // init remaining fields, to make clean-up easy and catch
    //    bugs using unset fields.

    pprop->pszHelp = NULL;
    pprop->idHelp  = 0;
    pprop->vptype  = -1;
    pprop->vpord   = -1;
    pprop->ulFlags = 0;
    pprop->cbValid = 0;
    pprop->pvValid = NULL;
    pprop->cbValue = 0;
    pprop->pvValue = NULL;
    pprop->pfnvdhrp = (PFNVDHRP)-1;
    pprop->pprop   = NULL;

    return pprop;
}


/***    PROPDestroy - destroy a PROPERTY record
 *
 *      ENTRY   pprop - property to destroy
 *
 *      EXIT    none
 */
VOID PROPDestroy(PPROPERTY pprop)
{
    swpFreez(pprop->pszName);
    swpFreez(pprop->pszHelp);
    switch(pprop->vptype) {
        case VDMP_BOOL:
            break;

        case VDMP_INT:
            swpFreez(pprop->pvValid);
            break;

        case VDMP_ENUM:
            swpFreez(pprop->pvValid);
            swpFreez(pprop->pvValue);
            break;

        case VDMP_STRING:
        case VDMP_MLSTR:
            swpFreez(pprop->pvValue);
            break;
    }
    swpFree(pprop);
}

#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_23","CODE")
PPROPERTY PROPFindNameOrOrdinal(PPROPERTY ppropList,char *pszName)
{
    VPORD   vpord=0;
    char    ch;

    if (*pszName == '#') {          // lookup by ordinal
        pszName++;
        while (*pszName != '\0') {
            ch = *pszName;
            if (('0' <= ch) && (ch <= '9')) {
                vpord = (VPORD)((vpord*10) + (USHORT)(ch-'0'));
                pszName++;
            }
            else
                return NULL;        // invalid ordinal
        }
        return PROPFindOrdinal(ppropList,vpord);
    }
    else                            // lookup by name
        return PROPFind(ppropList,pszName);
}

PPROPERTY PROPFind(PPROPERTY ppropList, char *pszName)
{
    PPROPERTY   pprop;

    for (pprop=ppropList; pprop!=NULL; pprop=pprop->pprop) {
        if (strcmp(pszName,pprop->pszName) == 0)
            return pprop;
    }
    return NULL;
}


PPROPERTY PROPFindOrdinal(PPROPERTY ppropList, VPORD vpord)
{
    PPROPERTY   pprop;

    for (pprop=ppropList; pprop!=NULL; pprop=pprop->pprop) {
        if (pprop->vpord == vpord)
            return pprop;
    }
    return NULL;
}


PPROPINST PRINFind(PPROPINST pprin, PPROPERTY pprop)
{
    while (pprin != NULL && pprin->pprop != pprop) {
        pprin = pprin->pprin;
    }
    return pprin;
}

// #pragma END_MVDM_SWAP_CODE
#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
void clearScanBits(void)
{
    PPROPERTY   pprop;

    for (pprop=ppropList; pprop!=NULL; pprop=pprop->pprop) {
        pprop->fScan = FALSE;
    }
}
#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
/***    swpAlloc - allocate swappable memory
 *
 *      Allocate memory from swappalbe heap.
 *
 *      ENTRY
 *          cb - size to allocate, in bytes.
 *
 *      EXIT-SUCCESS
 *          return pv of allocated memory.
 *
 *      EXIT-FAILURE
 *          return NULL, not enough memory
 */
void *swpAlloc(int cb)
{
    void   *pv;

    if (VMAllocKHB(VM_HKH_PUB_SWAPRW, cb, (VMHOB)VDHSWAPOWNER, NA,
                   SSToDS(&pv)) == 0) // alloc succeeded
        return pv;                  // return pointer
    else
        return 0;                   // alloc failed
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/***    swpFreez - free pointer, if non-zero
 *
 *      Only free point if it is not NULL.
 *
 *      ENTRY
 *          pv - pointer to free
 *
 *      EXIT-SUCCESS
 *          none
 *
 *      EXIT-FAILURE
 *          none
 */
void swpFreez(void *pv)
{
    if (pv != NULL)
        swpFree(pv);
}

#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_23","CODE")
/***    swpStrDup - duplicate character string
 *
 *      Allocate memory from heap, copy string, set NULL terminator.
 *
 *      ENTRY
 *          pch - string to duplicate.
 *
 *      EXIT-SUCCESS
 *          return pch of duplicated string
 *
 *      EXIT-FAILURE
 *          return NULL, not enough memory
 */
char *swpStrDup(char *pch)
{
    char    *pchDst;
    int     cb;

    cb = strlen(pch)+1;             // length, including NULL terminator
    pchDst = swpAlloc(cb+1);
    if (pchDst != NULL) {
        memcpy(pchDst,pch,cb);      // copy up to end, including NULL
    }
    return pchDst;
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE

/***    swpStrNDup - duplicate character string of specified length
 *
 *      Allocate memory from heap, copy string, set NULL terminator.
 *
 *      ENTRY
 *          pch - string to duplicate.  Not trusted to have NULL terminator.
 *          cb  - length of pch to copy (includes NULL terminator)
 *
 *      EXIT-SUCCESS
 *          return pch of duplicated string
 *
 *      EXIT-FAILURE
 *          return NULL, not enough memory
 */
char *swpStrNDup(char *pch, int cch)
{
    char    *pchDst;

    pchDst = swpAlloc(cch);
    cch--;                              // string length, excluding NULL
    if (pchDst != NULL) {
        memcpy(pchDst,pch,cch);         // copy string
        *(pchDst+cch) = '\0';           // set NULL (don't trust source string)
    }
    return pchDst;
}


/***    myStrChr - search for character in string
 *
 *      Same function as C run-time strchr
 *
 *      ENTRY
 *          psz - asciiz string to search
 *          ch  - character to search for ('\0' is allowed)
 *
 *      EXIT-SUCCESS
 *          returns non-zero pointer to first occurence of *ch* in *psz*.
 *
 *      EXIT-FAILURE
 *          returns NULL
 */
char *myStrChr(char *psz, char ch)
{
    while (*psz != ch) {                // scan for character
        if (*psz == '\0')               // end of string
            return NULL;                //  indicate failure
        else
            psz++;                      // next character in string
    }
    return psz;                         // found character
}
#pragma END_MVDM_SWAP_CODE
