home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
xwphescr.zip
/
XWPH0208.ZIP
/
src
/
helpers
/
xprf.c
< prev
next >
Wrap
C/C++ Source or Header
|
2002-06-05
|
32KB
|
949 lines
/*
*@@sourcefile xprf.c:
* contains replacement profile (INI) functions.
* This file is new with V0.9.5 (2000-08-10) [umoeller].
*
* These functions are quite similar to the regular Prf*
* APIs, with the following advantages:
*
* -- They do not use shared memory, but the default
* memory allocation functions (malloc() etc.).
* This greatly reduces the pressure on the process's
* address space which apparently the default Prf*
* functions are causing with large INI files.
*
* -- Profiles are written out to disk in one flush, not
* in the 32 KB chunks that the regular Prf* functions
* use.
*
* Other differences:
*
* -- They cannot be used interchangably. Once you open
* a profile using xprfOpenProfile, you must use the
* extended functions.
*
* -- The INI file is kept open after xprfOpenProfile
* until xprfCloseProfile is called. Besides, we use
* DosProtectOpen so that a second open of the same INI
* file will always fail until xprfCloseProfile is called
* (sorta like a mutex for profiles).
* This is independent of the process or thread which
* is doing the second open.
*
* This is because since we are not using shared memory,
* some other sort of protection had to be introduced
* to make sure no two processes operate on the same INI.
*
* Still, these functions are NOT thread-safe for the same profile.
* If you open the profile on one thread and write and read
* concurrently on two threads, there's no protection, and everything
* will blow up. The functions are reentrant though, so for different
* profiles there will be no problems.
*
* -- All changes to the INI files using xprfWriteProfileData
* are only made to the file in memory. Only xprfCloseProfile
* will flush the changes to disk. (I am not sure how the
* default PrfCloseProfile handles this on files other than
* HINI_USER and HINI_SYSTEM.)
*
* -- HINI_USER and HINI_SYSTEM don't work. To retrieve data
* from them, use the standard Prf* functions. You can however
* use the new functions to create a duplicate of these files.
*
* -- One similarity: All data items are limited to 64K,
* as with the standard profiles. This is not a limitation
* of the code, but of the INI file format, which uses
* USHORT's for all item length specifications.
*
* Thanks go out to Carsten Arnold for sending the INI file
* format information (xprf.h) to me, which enabled me to
* create this in the first place.
*
* Usage: All OS/2 programs.
*
* Function prefixes:
* -- xprf* replacement profile (INI) functions
*
* Note: Version numbering in this file relates to XWorkplace version
* numbering.
*
*@@header "helpers\xprf.h"
*@@added V0.9.5 (2000-08-10) [umoeller]
*/
/*
* Copyright (C) 2000 Ulrich Möller.
* This file is part of the "XWorkplace helpers" source package.
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, in version 2 as it comes in the
* "COPYING" file of the XWorkplace main distribution.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define OS2EMX_PLAIN_CHAR
// this is needed for "os2emx.h"; if this is defined,
// emx will define PSZ as _signed_ char, otherwise
// as unsigned char
#define INCL_DOSERRORS
#define INCL_WINSHELLDATA
#include <os2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "setup.h" // code generation and debugging options
#include "helpers\linklist.h"
#include "helpers\prfh.h"
#include "helpers\xprf.h"
#pragma hdrstop
/*
*@@category: Helpers\Profile (INI) replacement functions
* See xprf.c.
*/
/* ******************************************************************
*
* Declarations
*
********************************************************************/
/*
*@@ XINIAPPDATA:
* application structure in XINI.
*/
typedef struct _XINIAPPDATA
{
PSZ pszAppName;
LINKLIST llKeys; // contains PXINIKEYDATA pointers
ULONG cKeys; // count of items on list
} XINIAPPDATA, *PXINIAPPDATA;
/*
*@@ XINIKEYDATA:
* key/data structure in XINIAPPDATA.
*/
typedef struct _XINIKEYDATA
{
PSZ pszKeyName;
PBYTE pbData;
ULONG cbData;
} XINIKEYDATA, *PXINIKEYDATA;
/* ******************************************************************
*
* Helpers
*
********************************************************************/
/*
* FindApp:
* attempts to find the specified application
* in the given profile data.
* Returns NULL if not found.
*
* Private helper.
*/
static PXINIAPPDATA FindApp(PXINI pXIni, // in: profile opened with xprfOpenProfile
const char *pcszApp)
{
PLISTNODE pAppNode = lstQueryFirstNode(&pXIni->llApps);
while (pAppNode)
{
PXINIAPPDATA pAppDataThis = (PXINIAPPDATA)pAppNode->pItemData;
if (!strcmp(pAppDataThis->pszAppName, pcszApp))
return (pAppDataThis);
pAppNode = pAppNode->pNext;
}
return NULL;
}
/*
* CreateApp:
* creates a new application in the specified
* profile structure and appends it to the list.
* This does NOT check for whether the app is
* already in the profile.
*
* Private helper.
*/
static PXINIAPPDATA CreateApp(PXINI pXIni, // in: profile opened with xprfOpenProfile
const char *pcszApp)
{
PXINIAPPDATA pAppData;
if (pAppData = (PXINIAPPDATA)malloc(sizeof(XINIAPPDATA)))
{
pAppData->pszAppName = strdup(pcszApp);
lstInit(&pAppData->llKeys, FALSE);
pAppData->cKeys = 0;
// store in INI's apps list
lstAppendItem(&pXIni->llApps, pAppData);
pXIni->cApps++;
}
return (pAppData);
}
/*
* FindKey:
* attempts to find the specified key
* in the given application.
* Returns NULL if not found.
*
* Private helper.
*/
static PXINIKEYDATA FindKey(PXINIAPPDATA pAppData,
const char *pcszKey)
{
PLISTNODE pKeyNode = lstQueryFirstNode(&pAppData->llKeys);
while (pKeyNode)
{
PXINIKEYDATA pKeyDataThis = (PXINIKEYDATA)pKeyNode->pItemData;
if (!strcmp(pKeyDataThis->pszKeyName, pcszKey))
return (pKeyDataThis);
pKeyNode = pKeyNode->pNext;
}
return NULL;
}
/*
* CreateKey:
* creates a new key in the specified application
* structure and appends it to the list.
* This does NOT check for whether the key is
* already in the application.
*
* Private helper.
*/
static PXINIKEYDATA CreateKey(PXINIAPPDATA pAppData,
const char *pcszKey, // in: key name
PBYTE pbData, // in: data for key
ULONG cbData) // in: sizeof (*pbData)
{
PXINIKEYDATA pKeyData;
if (pKeyData = (PXINIKEYDATA)malloc(sizeof(XINIKEYDATA)))
{
pKeyData->pszKeyName = strdup(pcszKey);
if (pKeyData->pbData = (PBYTE)malloc(cbData))
{
memcpy(pKeyData->pbData, pbData, cbData);
pKeyData->cbData = cbData;
// store in app's keys list
lstAppendItem(&pAppData->llKeys, pKeyData);
pAppData->cKeys++;
}
else
{
// malloc failed:
free(pKeyData);
pKeyData = 0;
}
}
return (pKeyData);
}
/*
* FreeKey:
* frees the specified key. Does not remove
* the key from the keys list in XINIAPPDATA
* however.
*
* Private helper.
*/
static VOID FreeKey(PXINIKEYDATA pKeyDataThis)
{
if (pKeyDataThis->pszKeyName)
free(pKeyDataThis->pszKeyName);
if (pKeyDataThis->pbData)
free(pKeyDataThis->pbData);
free(pKeyDataThis);
}
/*
* FreeApp:
* frees the specified application. Does not remove
* the application from the application list in XINI
* however.
*
* Private helper.
*/
static VOID FreeApp(PXINIAPPDATA pAppDataThis)
{
PLISTNODE pKeyNode = lstQueryFirstNode(&pAppDataThis->llKeys);
while (pKeyNode)
{
PXINIKEYDATA pKeyDataThis = (PXINIKEYDATA)pKeyNode->pItemData;
FreeKey(pKeyDataThis);
pKeyNode = pKeyNode->pNext;
}
if (pAppDataThis->pszAppName)
free(pAppDataThis->pszAppName);
lstClear(&pAppDataThis->llKeys);
free(pAppDataThis);
}
/*
* FreeINI:
* cleans up the specified ini structure entirely.
*
* Private helper.
*/
static BOOL FreeINI(PXINI pXIni) // in: profile opened with xprfOpenProfile
{
BOOL brc = FALSE;
if (pXIni)
{
PLISTNODE pAppNode = lstQueryFirstNode(&pXIni->llApps);
while (pAppNode)
{
PXINIAPPDATA pAppDataThis = (PXINIAPPDATA)pAppNode->pItemData;
FreeApp(pAppDataThis);
pAppNode = pAppNode->pNext;
}
lstClear(&pXIni->llApps);
free(pXIni);
}
return brc;
}
/* ******************************************************************
*
* Read/write data
*
********************************************************************/
/*
* ReadINI:
* reads in the entire INI file from disk and
* initializes all internal data structures.
*
* Private helper.
*/
static BOOL ReadINI(PXINI pXIni) // in: profile opened with xprfOpenProfile
{
BOOL brc = FALSE;
FILESTATUS3 fs3;
if (DosProtectQueryFileInfo(pXIni->hFile,
FIL_STANDARD,
&fs3,
sizeof(fs3),
pXIni->hLock)
== NO_ERROR)
{
PBYTE pbFileData;
if (pbFileData = (PBYTE)malloc(fs3.cbFile))
{
ULONG ulSet = 0;
APIRET arc = DosProtectSetFilePtr(pXIni->hFile,
0,
FILE_BEGIN,
&ulSet,
pXIni->hLock);
if ( (arc == NO_ERROR)
&& (ulSet == 0)
)
{
ULONG cbRead = 0;
arc = DosProtectRead(pXIni->hFile,
pbFileData,
fs3.cbFile,
&cbRead,
pXIni->hLock);
if ( (arc == NO_ERROR)
&& (cbRead == fs3.cbFile)
)
{
PINIFILE_HEADER pHeader = (PINIFILE_HEADER)pbFileData;
if (pHeader->magic == 0xFFFFFFFF)
{
ULONG ulAppOfs = pHeader->offFirstApp;
// it was a valid profile, so return TRUE
brc = TRUE;
// create-applications loop
while ((ulAppOfs) && (brc))
{
// application struct
PINIFILE_APP pApp = (PINIFILE_APP)(pbFileData + ulAppOfs);
ULONG ulKeysOfs = pApp->offFirstKeyInApp;
PXINIAPPDATA pIniApp
= CreateApp(pXIni,
(PBYTE)(pbFileData + pApp->offAppName));
if (!pIniApp)
{
brc = FALSE;
break;
}
// create-keys loop
while ((ulKeysOfs) && (brc))
{
PINIFILE_KEY pKey = (PINIFILE_KEY)(pbFileData + ulKeysOfs);
PXINIKEYDATA pIniKey
= CreateKey(pIniApp,
(PSZ)(pbFileData + pKey->offKeyName),
(PBYTE)(pbFileData + pKey->offKeyData),
pKey->lenKeyData);
if (!pIniKey)
{
brc = FALSE;
break;
}
// next key; can be null
ulKeysOfs = pKey->offNextKeyInApp;
}
// next application; can be null
ulAppOfs = pApp->offNextApp;
}
}
}
}
free(pbFileData);
}
}
return brc;
}
/*
* WriteINI:
* writes the entire data structure back to disk.
* Does not close the file.
*
* Private helper.
*/
static BOOL WriteINI(PXINI pXIni) // in: profile opened with xprfOpenProfile
{
BOOL brc = FALSE;
ULONG ulTotalFileSize = sizeof(INIFILE_HEADER);
ULONG ulSet = 0;
PBYTE pbData2Write = NULL;
// check how much memory we'll need:
// one INIFILE_HEADER
// + for each app: one INIFILE_APP plus app name length
// + for each key: one INIFILE_KEY plus key name length plus data length
// go thru all apps
PLISTNODE pAppNode = lstQueryFirstNode(&pXIni->llApps);
while (pAppNode)
{
PLISTNODE pKeyNode;
PXINIAPPDATA pAppDataThis = (PXINIAPPDATA)pAppNode->pItemData;
// one INIFILE_APP plus app name length for each app
ulTotalFileSize += sizeof(INIFILE_APP)
+ strlen(pAppDataThis->pszAppName)
+ 1; // null terminator
// for each app, go thru all keys
pKeyNode = lstQueryFirstNode(&pAppDataThis->llKeys);
while (pKeyNode)
{
PXINIKEYDATA pKeyDataThis = (PXINIKEYDATA)pKeyNode->pItemData;
ulTotalFileSize += sizeof(INIFILE_KEY)
+ strlen(pKeyDataThis->pszKeyName)
+ pKeyDataThis->cbData
+ 1; // null terminator
pKeyNode = pKeyNode->pNext;
}
pAppNode = pAppNode->pNext;
}
// allocate buffer for total size
if (pbData2Write = (PBYTE)malloc(ulTotalFileSize))
{
APIRET arc = NO_ERROR;
// set header in buffer
PINIFILE_HEADER pHeader = (PINIFILE_HEADER)pbData2Write;
// pointer into buffer for current write
// (used below)
ULONG ulCurOfs = sizeof(INIFILE_HEADER);
// 1) set up header
pHeader->magic = 0xFFFFFFFF;
if (pXIni->cApps)
// we have any applications:
pHeader->offFirstApp = sizeof(INIFILE_HEADER);
// offset of first application in file
else
// empty INI file:
pHeader->offFirstApp = 0;
pHeader->lenFile = ulTotalFileSize;
pHeader->filler1 = 0;
pHeader->filler2 = 0;
// 2) for-all-applications loop
pAppNode = lstQueryFirstNode(&pXIni->llApps);
while (pAppNode)
{
PXINIAPPDATA pAppDataThis = (PXINIAPPDATA)pAppNode->pItemData;
ULONG cbAppName = strlen(pAppDataThis->pszAppName) + 1;
// layout of application entry in file:
// -- struct PINIFILE_APP (ulCurOfs right now)
// -- application name (null-terminated)
// -- INIFILE_KEY 1
// -- INIFILE_KEY 2 ...
// -- next struct PINIFILE_APP
// make pointer to application entry
PINIFILE_APP pIniAppCurrent = (PINIFILE_APP)(pbData2Write + ulCurOfs);
// write out application entry
// pIniAppCurrent->offNextApp = 0;
// pIniAppCurrent->offFirstKeyInApp = ulCurOfs + cbAppName;
pIniAppCurrent->filler1 = 0;
pIniAppCurrent->lenAppName
= pIniAppCurrent->_lenAppName
= cbAppName;
// set offset to application name: put this right after the application
ulCurOfs += sizeof(INIFILE_APP);
pIniAppCurrent->offAppName = ulCurOfs;
// write app name (null-terminated)
memcpy(pbData2Write + ulCurOfs,
pAppDataThis->pszAppName,
cbAppName);
ulCurOfs += cbAppName;
// ulCurOfs points to INIFILE_KEY entry now
if (pAppDataThis->cKeys)
{
// we have keys:
PLISTNODE pKeyNode = lstQueryFirstNode(&pAppDataThis->llKeys);
pIniAppCurrent->offFirstKeyInApp = ulCurOfs;
// 3) for-all-keys loop per application
while (pKeyNode)
{
PXINIKEYDATA pKeyDataThis = (PXINIKEYDATA)pKeyNode->pItemData;
ULONG cbKeyName = strlen(pKeyDataThis->pszKeyName) + 1;
PINIFILE_KEY pIniKeyCurrent = (PINIFILE_KEY)(pbData2Write + ulCurOfs);
pIniKeyCurrent->filler1 = 0;
ulCurOfs += sizeof(INIFILE_KEY);
// has offset to key name now
// a) key name
pIniKeyCurrent->lenKeyName
= pIniKeyCurrent->_lenKeyName
= cbKeyName;
pIniKeyCurrent->offKeyName = ulCurOfs;
memcpy(pbData2Write + ulCurOfs,
pKeyDataThis->pszKeyName,
cbKeyName);
ulCurOfs += cbKeyName;
// has offset to data now
// b) data
pIniKeyCurrent->lenKeyData
= pIniKeyCurrent->_lenKeyData
= pKeyDataThis->cbData;
pIniKeyCurrent->offKeyData = ulCurOfs;
memcpy(pbData2Write + ulCurOfs,
pKeyDataThis->pbData,
pKeyDataThis->cbData);
ulCurOfs += pKeyDataThis->cbData;
// points to after all key data now;
// this receives either the next key/data block
// or the next application or nothing
// ofs of next key:
if (pKeyNode->pNext)
pIniKeyCurrent->offNextKeyInApp = ulCurOfs;
else
// last key:
pIniKeyCurrent->offNextKeyInApp = 0;
pKeyNode = pKeyNode->pNext;
}
// ulCurOfs points to after the last keys entry now
}
else
// no keys:
pIniAppCurrent->offFirstKeyInApp = 0;
// done with keys (there may be none!);
// now set offset to next app in current application
if (pAppNode->pNext)
pIniAppCurrent->offNextApp = ulCurOfs;
else
// this was the last one:
pIniAppCurrent->offNextApp = 0;
// next app
pAppNode = pAppNode->pNext;
}
// write out everything
if (!(arc = DosProtectSetFilePtr(pXIni->hFile,
0,
FILE_BEGIN,
&ulSet,
pXIni->hLock)))
{
ULONG cbWritten = 0;
if ( (!(arc = DosProtectWrite(pXIni->hFile,
pbData2Write,
ulTotalFileSize,
&cbWritten,
pXIni->hLock)))
&& (!(arc = DosProtectSetFileSize(pXIni->hFile,
ulTotalFileSize,
pXIni->hLock)))
)
brc = TRUE;
}
free(pbData2Write);
}
return brc;
}
/* ******************************************************************
*
* API Functions
*
********************************************************************/
/*
*@@ xprfOpenProfile:
* opens an extended profile (XINI). This is similar to
* PrfOpenProfile, but cannot be used interchangably.
* See the top of this file (xprf.c) for general remarks.
*
* If the full path is not specified, the current
* directory is used. This uses DosProtectOpen to
* open the profile, so see CPREF for additional remarks.
*
* If the specified profile does not exist yet, it is
* created by this function.
*
* Note that you must not open the system profiles
* (OS2.INI and OS2SYS.INI) using this function because
* this would keep OS/2 from updating them.
*
* This returns 0 (NO_ERROR) on success. Otherwise either
* an OS/2 error code (ERROR_*) or one of the profile error
* codes defined in prfh.h is returned.
*/
APIRET xprfOpenProfile(const char *pcszFilename, // in: profile name
PXINI *ppxini) // out: profile handle
{
APIRET arc = NO_ERROR;
PXINI pXIni = NULL;
if (pcszFilename)
if (strlen(pcszFilename) < CCHMAXPATH - 1)
{
HFILE hFile = NULLHANDLE;
ULONG ulAction = 0;
FHLOCK hLock = 0;
arc = DosProtectOpen((PSZ)pcszFilename,
&hFile,
&ulAction,
1024, // initial size
FILE_NORMAL,
OPEN_ACTION_CREATE_IF_NEW
| OPEN_ACTION_OPEN_IF_EXISTS,
OPEN_FLAGS_FAIL_ON_ERROR
| OPEN_FLAGS_NO_CACHE
| OPEN_FLAGS_SEQUENTIAL
| OPEN_SHARE_DENYREADWRITE
| OPEN_ACCESS_READWRITE,
NULL, // no EAs
&hLock);
if (arc == NO_ERROR)
{
pXIni = (PXINI)malloc(sizeof(XINI));
if (!pXIni)
arc = ERROR_NOT_ENOUGH_MEMORY;
else
{
// OK: initialize XINI
memset(pXIni, 0, sizeof(XINI));
memcpy(pXIni->acMagic, XINI_MAGIC_BYTES, sizeof(XINI_MAGIC_BYTES));
strcpy(pXIni->szFilename, pcszFilename);
pXIni->hFile = hFile;
pXIni->hLock = hLock;
lstInit(&pXIni->llApps, FALSE);
if (ulAction == FILE_CREATED)
// file newly created: rewrite on close
pXIni->fDirty = TRUE;
else
// file existed: read data
if (!ReadINI(pXIni))
{
// error:
FreeINI(pXIni);
pXIni = NULL;
arc = PRFERR_READ;
}
if ((pXIni) && (ppxini))
*ppxini = pXIni;
}
}
}
return arc;
}
/*
*@@ xprfQueryProfileData:
* similar to PrfQueryProfileData.
*
* @@todo: Still to be written.
*/
/* BOOL xprfQueryProfileData(PXINI hIni, // in: profile opened with xprfOpenProfile
const char *pcszApp,
const char *pcszKey,
PVOID pBuffer,
PULONG pulBufferMax)
{
BOOL brc = FALSE;
return brc;
} */
/*
*@@ xprfWriteProfileData:
* writes data into an extended profile (XINI).
* This operates similar to PrfWriteProfileData,
* that is:
*
* -- If all of pcszApp, pcszKey, pData, and
* ulDataLen are specified, the given data is
* written into the specified application/key
* pair.
*
* -- If pcszApp and pcszKey are specified, but
* pData and ulDataLen are null, the specified
* key is deleted.
*
* -- If pcszApp is specified, but all of pcszKey,
* pData, and ulDataLen are null, the entire
* specified application is deleted.
*
* You cannot specify HINI_SYSTEM or HINI_USER for
* hINi.
*
* Note that if data has been added or removed,
* the INI file on disk is not updated automatically.
* Instead, our memory copy of it is only marked
* as "dirty" so that the file will be rewritten
* on xprfCloseProfile.
*/
BOOL xprfWriteProfileData(PXINI hIni, // in: profile opened with xprfOpenProfile
const char *pcszApp, // in: application name
const char *pcszKey, // in: key name or NULL
PVOID pData, // in: data to write or NULL
ULONG ulDataLen) // in: sizeof(*pData) or null
{
BOOL brc = FALSE;
if (hIni)
{
PXINI pXIni = (PXINI)hIni;
if (memcmp(pXIni->acMagic, XINI_MAGIC_BYTES, sizeof(XINI_MAGIC_BYTES))
== 0)
{
// check if application exists
PXINIAPPDATA pAppData = FindApp(pXIni,
pcszApp);
// now check: does caller want entire application deleted?
if (!pcszKey)
{
// yes, delete application: did we find it?
if (pAppData)
{
// yes: kill that
FreeApp(pAppData);
// and remove from list
brc = lstRemoveItem(&pXIni->llApps, pAppData);
pXIni->cApps--;
// rewrite profile on close
pXIni->fDirty = TRUE;
}
else
// application doesn't exist:
brc = TRUE;
}
else
{
// caller has specified key:
// does caller want a key to be deleted?
if (!ulDataLen)
{
// yes: delete key:
if (pAppData)
{
// app exists:
// find key
PXINIKEYDATA pKeyData = FindKey(pAppData,
pcszKey);
if (pKeyData)
{
// key exists: kill that
FreeKey(pKeyData);
// and remove from app's keys list
brc = lstRemoveItem(&pAppData->llKeys, pKeyData);
pAppData->cKeys--;
// rewrite profile on close
pXIni->fDirty = TRUE;
}
else
// key doesn't even exist:
brc = TRUE;
}
else
// app doesn't even exist:
brc = TRUE;
}
else
{
// key and data specified: let's add something...
if (!pAppData)
// app doesn't exist yet:
// create
pAppData = CreateApp(pXIni,
pcszApp);
if (pAppData)
{
// found or created app:
// check if key exists
PXINIKEYDATA pKeyData = FindKey(pAppData,
pcszKey);
if (!pKeyData)
// doesn't exist yet:
// create
pKeyData = CreateKey(pAppData,
pcszKey,
(PBYTE)pData,
ulDataLen);
if (pKeyData)
{
// mark as dirty
pXIni->fDirty = TRUE;
brc = TRUE;
}
}
}
}
}
}
return brc;
}
/*
*@@ xprfCloseProfile:
* closes a profile opened with xprfOpenProfile.
* If the profile is "dirty", that is, if any data
* has been changed using xprfWriteProfileData, the
* file is written back to disk before closing.
*
* You cannot specify HINI_SYSTEM or HINI_USER for
* hINi.
*/
BOOL xprfCloseProfile(PXINI hIni) // in: profile opened with xprfOpenProfile
{
BOOL brc = FALSE;
if (hIni)
{
PXINI pXIni = (PXINI)hIni;
if (memcmp(pXIni->acMagic, XINI_MAGIC_BYTES, sizeof(XINI_MAGIC_BYTES))
== 0)
{
brc = TRUE;
if (pXIni->fDirty)
brc = WriteINI(pXIni);
if (brc)
{
APIRET arc = DosProtectClose(pXIni->hFile,
pXIni->hLock);
if (arc == NO_ERROR)
{
pXIni->hFile = 0;
pXIni->hLock = 0;
FreeINI(pXIni);
}
else
brc = FALSE;
}
}
}
return brc;
}