/* *@@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-2021 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 #include #include #include #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. * *@@changed V1.0.0 (2002-09-17) [umoeller]: now returning APIRET */ STATIC APIRET FindApp(PXINI pXIni, // in: profile opened with xprfOpenProfile const char *pcszApp, PXINIAPPDATA *ppAppData) { PLISTNODE pAppNode = lstQueryFirstNode(&pXIni->llApps); while (pAppNode) { PXINIAPPDATA pAppDataThis = (PXINIAPPDATA)pAppNode->pItemData; if (!strcmp(pAppDataThis->pszAppName, pcszApp)) { *ppAppData = pAppDataThis; return NO_ERROR; } pAppNode = pAppNode->pNext; } return PRFERR_INVALID_APP_NAME; } /* * 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. * *@@changed V1.0.0 (2002-09-17) [umoeller]: now returning APIRET */ STATIC APIRET CreateApp(PXINI pXIni, // in: profile opened with xprfOpenProfile const char *pcszApp, PXINIAPPDATA *ppAppData) { 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); *ppAppData = pAppData; return NO_ERROR; } return ERROR_NOT_ENOUGH_MEMORY; } /* * FindKey: * attempts to find the specified key * in the given application. * Returns NULL if not found. * * Private helper. * *@@changed V1.0.0 (2002-09-17) [umoeller]: now returning APIRET */ STATIC APIRET FindKey(PXINIAPPDATA pAppData, const char *pcszKey, PXINIKEYDATA *ppKeyData) { PLISTNODE pKeyNode = lstQueryFirstNode(&pAppData->llKeys); while (pKeyNode) { PXINIKEYDATA pKeyDataThis = (PXINIKEYDATA)pKeyNode->pItemData; if (!strcmp(pKeyDataThis->pszKeyName, pcszKey)) { *ppKeyData = pKeyDataThis; return NO_ERROR; } pKeyNode = pKeyNode->pNext; } return PRFERR_INVALID_KEY_NAME; } /* * 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. * *@@changed V1.0.0 (2002-09-17) [umoeller]: now returning APIRET */ STATIC APIRET CreateKey(PXINIAPPDATA pAppData, const char *pcszKey, // in: key name PBYTE pbData, // in: data for key ULONG cbData, // in: sizeof (*pbData) PXINIKEYDATA *ppKeyData) // out: new key data { 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++; *ppKeyData = pKeyData; return NO_ERROR; } else // malloc failed: free(pKeyData); } return ERROR_NOT_ENOUGH_MEMORY; } /* * 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); } /* *@@ FreeKeyIfExists: * *@@added V1.0.0 (2002-09-17) [umoeller] */ STATIC VOID FreeKeyIfExists(PXINI pXIni, // in: profile opened with xprfOpenProfile PXINIAPPDATA pAppData, PCSZ pcszKey) { // find key PXINIKEYDATA pKeyData; if (!FindKey(pAppData, pcszKey, &pKeyData)) { // key exists: kill that FreeKey(pKeyData); // and remove from app's keys list lstRemoveItem(&pAppData->llKeys, pKeyData); pAppData->cKeys--; // rewrite profile on close pXIni->fDirty = TRUE; } // else key doesn't exist: // nothing to do } /* * 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 VOID FreeINI(PXINI pXIni) // in: profile opened with xprfOpenProfile { if (pXIni) { PLISTNODE pAppNode = lstQueryFirstNode(&pXIni->llApps); while (pAppNode) { PXINIAPPDATA pAppDataThis = (PXINIAPPDATA)pAppNode->pItemData; FreeApp(pAppDataThis); pAppNode = pAppNode->pNext; } lstClear(&pXIni->llApps); free(pXIni); } } /* ****************************************************************** * * Read/write data * ********************************************************************/ /* * ReadINI: * reads in the entire INI file from disk and * initializes all internal data structures. * * Private helper. * *@@changed V1.0.0 (2002-09-17) [umoeller]: now returning APIRET *@@changed WarpIN V1.0.25 (2021-07-04) [pr]: add limit checking */ STATIC APIRET ReadINI(PXINI pXIni) // in: profile opened with xprfOpenProfile { APIRET arc; FILESTATUS3 fs3; if (!(arc = DosQueryFileInfo(pXIni->hFile, FIL_STANDARD, &fs3, sizeof(fs3)))) { PBYTE pbFileData; if (!(pbFileData = (PBYTE)malloc(fs3.cbFile))) arc = ERROR_NOT_ENOUGH_MEMORY; else { ULONG ulSet = 0; if (!(arc = DosSetFilePtr(pXIni->hFile, 0, FILE_BEGIN, &ulSet))) { ULONG cbRead = 0; if (!(arc = DosRead(pXIni->hFile, pbFileData, fs3.cbFile, &cbRead))) { if (cbRead != fs3.cbFile || cbRead < sizeof(INIFILE_HEADER)) arc = ERROR_NO_DATA; else { PINIFILE_HEADER pHeader = (PINIFILE_HEADER)pbFileData; if (pHeader->magic == 0xFFFFFFFF) { ULONG ulAppOfs = pHeader->offFirstApp; // create-applications loop while ((ulAppOfs) && (ulAppOfs + sizeof(INIFILE_APP) <= cbRead) && (!arc)) { // application struct PINIFILE_APP pApp = (PINIFILE_APP)(pbFileData + ulAppOfs); ULONG ulKeysOfs = pApp->offFirstKeyInApp; PXINIAPPDATA pIniApp; if (arc = CreateApp(pXIni, (PBYTE)(pbFileData + pApp->offAppName), &pIniApp)) break; // create-keys loop while ((ulKeysOfs) && (ulKeysOfs + sizeof(INIFILE_KEY) <= cbRead) && (!arc)) { PINIFILE_KEY pKey = (PINIFILE_KEY)(pbFileData + ulKeysOfs); PXINIKEYDATA pIniKey; if (arc = CreateKey(pIniApp, (PSZ)(pbFileData + pKey->offKeyName), (PBYTE)(pbFileData + pKey->offKeyData), pKey->lenKeyData, &pIniKey)) break; // next key; can be null ulKeysOfs = pKey->offNextKeyInApp; } // next application; can be null ulAppOfs = pApp->offNextApp; } } } } } free(pbFileData); } } return arc; } /* * WriteINI: * writes the entire data structure back to disk. * Does not close the file. * * Private helper. * *@@changed V1.0.0 (2002-09-17) [umoeller]: now returning APIRET */ STATIC APIRET WriteINI(PXINI pXIni) // in: profile opened with xprfOpenProfile { APIRET arc = NO_ERROR; 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)) { // 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 (lstCountItems(&pXIni->llApps)) // 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 = DosSetFilePtr(pXIni->hFile, 0, FILE_BEGIN, &ulSet))) { ULONG cbWritten = 0; if (!(arc = DosWrite(pXIni->hFile, pbData2Write, ulTotalFileSize, &cbWritten))) { if (!(arc = DosSetFileSize(pXIni->hFile, ulTotalFileSize))) ; } } free(pbData2Write); } return arc; } /* ****************************************************************** * * 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(PCSZ pcszFilename, // in: profile name PXINI *ppxini) // out: profile handle { APIRET arc = NO_ERROR; PXINI pXIni = NULL; ULONG ulFilenameLen; if ( (!pcszFilename) || (!ppxini) ) arc = ERROR_INVALID_PARAMETER; // V1.0.0 (2002-09-17) [umoeller] else if (!(ulFilenameLen = strlen(pcszFilename))) arc = ERROR_INVALID_PARAMETER; // V1.0.0 (2002-09-17) [umoeller] else if (ulFilenameLen >= CCHMAXPATH - 1) arc = ERROR_FILENAME_EXCED_RANGE; else { HFILE hFile = NULLHANDLE; ULONG ulAction = 0; // FHLOCK hLock = 0; // WarpIN V1.0.20 (2011-07-08) [pr]: add NOINHERIT if (!(arc = DosOpen((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_SEQUENTIAL | OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE, NULL))) { if (!(pXIni = (PXINI)malloc(sizeof(XINI)))) arc = ERROR_NOT_ENOUGH_MEMORY; else { // OK: initialize XINI memset(pXIni, 0, sizeof(XINI)); memcpy(pXIni->acMagic, XINI_MAGIC_BYTES, sizeof(XINI_MAGIC_BYTES)); memcpy(pXIni->szFilename, pcszFilename, ulFilenameLen + 1); 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 (arc = ReadINI(pXIni)) // error: FreeINI(pXIni); } if (!arc) *ppxini = pXIni; else DosClose(hFile); } } return arc; } /* *@@ xprfQueryProfileSize: * returns the size of INI data, similarly to * what PrfQueryProfileSize does. * * If (pszAppName == NULL), this returns the size of * the buffer required to hold the enumerated list * of application names. pszKey is then ignored. * * Otherwise, if (pszKeyName == NULL), this returns * the size of the buffer required to hold the * enumerated list of _key_ names for the given * application. * * If both pszAppName and pszKeyName are not NULL, this * returns the data size of the given key. * * Returns: * * -- NO_ERROR * *@@added V1.0.0 (2002-09-17) [umoeller] */ APIRET xprfQueryProfileSize(PXINI pXIni, // in: profile opened with xprfOpenProfile PCSZ pcszAppName, // in: application name or NULL PCSZ pcszKeyName, // in: key name or NULL PULONG pulDataLen) // out: size of requested data { APIRET arc = NO_ERROR; ULONG ulDataLen = 0; if (!pcszAppName) { PLISTNODE pAppNode = lstQueryFirstNode(&pXIni->llApps); while (pAppNode) { PXINIAPPDATA pAppDataThis = (PXINIAPPDATA)pAppNode->pItemData; ulDataLen += strlen(pAppDataThis->pszAppName) + 1; pAppNode = pAppNode->pNext; } // extra byte for terminating extra null ++ulDataLen; } else { // app specified: PXINIAPPDATA pAppData; if (!(arc = FindApp(pXIni, pcszAppName, &pAppData))) { // app exists: if (!pcszKeyName) { // app != NULL, but key == NULL: // return size of keys list PLISTNODE pKeyNode = lstQueryFirstNode(&pAppData->llKeys); while (pKeyNode) { PXINIKEYDATA pKeyDataThis = (PXINIKEYDATA)pKeyNode->pItemData; ulDataLen += strlen(pKeyDataThis->pszKeyName) + 1; pKeyNode = pKeyNode->pNext; } // extra byte for terminating extra null ++ulDataLen; } else { // both app and key specified: PXINIKEYDATA pKeyData; if (!(arc = FindKey(pAppData, pcszKeyName, &pKeyData))) ulDataLen = pKeyData->cbData; } } } if (pulDataLen) *pulDataLen = ulDataLen; return arc; } /* *@@ xprfQueryProfileData: * reads data from the given XINI, similarly to * what PrfQueryProfileData does. * * If (pszAppName == NULL), this returns the * enumerated list of application names. * pszKey is then ignored. *pulBufferMax * receives the total size of the list excluding * the final NULL character. * * Otherwise, if (pszKeyName == NULL), this the * enumerated list of _key_ names for the given * application. *pulBufferMax receives the total * size of the list excluding the final NULL character. * * If both pszAppName and pszKeyName are not NULL, * this returns the data of the given key. * pulBufferMax receives the bytes copied. * * Returns: * * -- NO_ERROR */ APIRET xprfQueryProfileData(PXINI pXIni, // in: profile opened with xprfOpenProfile PCSZ pcszAppName, // in: application name PCSZ pcszKeyName, // in: key name or NULL PVOID pBuffer, // in: buffer to receive data PULONG pulBufferMax) // in: buffer size, out: size of written data { APIRET arc = NO_ERROR; ULONG ulDataLen = 0; if (!pcszAppName) { PLISTNODE pAppNode = lstQueryFirstNode(&pXIni->llApps); PBYTE pbTarget = (PSZ)pBuffer; ULONG cbCopied = 0; while (pAppNode) { PXINIAPPDATA pAppDataThis = (PXINIAPPDATA)pAppNode->pItemData; ULONG cbThis = strlen(pAppDataThis->pszAppName) + 1; if (cbCopied + cbThis > *pulBufferMax) break; else { memcpy(pbTarget + cbCopied, pAppDataThis->pszAppName, cbThis); cbCopied += cbThis; } pAppNode = pAppNode->pNext; } // extra byte for terminating extra null pbTarget[cbCopied] = '\0'; ulDataLen = cbCopied; // PMREF says terminating null is not counted } else { // app specified: PXINIAPPDATA pAppData; if (!(arc = FindApp(pXIni, pcszAppName, &pAppData))) { // app exists: if (!pcszKeyName) { // app != NULL, but key == NULL: // return size of keys list PLISTNODE pKeyNode = lstQueryFirstNode(&pAppData->llKeys); PBYTE pbTarget = (PSZ)pBuffer; ULONG cbCopied = 0; while (pKeyNode) { PXINIKEYDATA pKeyDataThis = (PXINIKEYDATA)pKeyNode->pItemData; ULONG cbThis = strlen(pKeyDataThis->pszKeyName) + 1; if (cbCopied + cbThis > *pulBufferMax) break; else { memcpy(pbTarget + cbCopied, pKeyDataThis->pszKeyName, cbThis); cbCopied += cbThis; } pKeyNode = pKeyNode->pNext; } // extra byte for terminating extra null pbTarget[cbCopied] = '\0'; ulDataLen = cbCopied; // PMREF says terminating null is not counted } else { // both app and key specified: PXINIKEYDATA pKeyData; if (!(arc = FindKey(pAppData, pcszKeyName, &pKeyData))) { ulDataLen = min(pKeyData->cbData, *pulBufferMax); memcpy(pBuffer, pKeyData->pbData, ulDataLen); } } } } if (pulBufferMax) *pulBufferMax = ulDataLen; return arc; } /* *@@ xprfQueryProfileInt: * reads data from the given XINI, similarly to * what PrfQueryProfileInt does. * * You cannot specify HINI_SYSTEM or HINI_USER for * hINi. * *@@added WarpIN V1.0.18 (2008-10-04) [pr] */ LONG xprfQueryProfileInt(PXINI pXIni, PCSZ pcszApp, PCSZ pcszKey, LONG lDefault) { char szBuffer[20]; LONG lVal = lDefault; ULONG ulSize = sizeof(szBuffer) - 1; if (pcszApp && pcszApp[0] && pcszKey && pcszKey[0] && !xprfQueryProfileData(pXIni, pcszApp, pcszKey, szBuffer, &ulSize)) { szBuffer[ulSize] = '\0'; lVal = atol(szBuffer); } return lVal; } /* *@@ 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. * * Returns: * * -- NO_ERROR */ APIRET xprfWriteProfileData(PXINI pXIni, // in: profile opened with xprfOpenProfile PCSZ pcszApp, // in: application name PCSZ pcszKey, // in: key name or NULL PVOID pData, // in: data to write or NULL ULONG ulDataLen) // in: sizeof(*pData) or null { APIRET arc = NO_ERROR; if ( (!pXIni) || (memcmp(pXIni->acMagic, XINI_MAGIC_BYTES, sizeof(XINI_MAGIC_BYTES))) ) arc = ERROR_INVALID_PARAMETER; else { // check if application exists PXINIAPPDATA pAppData; if (FindApp(pXIni, pcszApp, &pAppData)) // not found: pAppData = NULL; // 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 lstRemoveItem(&pXIni->llApps, pAppData); // rewrite profile on close pXIni->fDirty = TRUE; } // else application doesn't exist: // nothing to do return NO_ERROR } else { // caller has specified key: // does caller want a key to be deleted? if (!ulDataLen) { // yes: delete key: if (pAppData) { // app exists: FreeKeyIfExists(pXIni, pAppData, pcszKey); } // else app doesn't even exist: // nothing to do, return NO_ERROR } else { // key and data specified: let's add something... if (!pAppData) // app doesn't exist yet: // create arc = CreateApp(pXIni, pcszApp, &pAppData); if (!arc) { // found or created app: // delete existing key if it exists PXINIKEYDATA pKeyData; FreeKeyIfExists(pXIni, pAppData, pcszKey); // now create new key if (!(arc = CreateKey(pAppData, pcszKey, (PBYTE)pData, ulDataLen, &pKeyData))) // mark as dirty pXIni->fDirty = TRUE; } } } } return arc; } /* *@@ xprfWriteProfileString: * writes string into an extended profile (XINI). * This operates similar to PrfWriteProfileString. * * You cannot specify HINI_SYSTEM or HINI_USER for * hINi. * *@@added WarpIN V1.0.18 (2008-10-04) [pr] */ APIRET xprfWriteProfileString(PXINI pXIni, // in: profile opened with xprfOpenProfile PCSZ pcszApp, // in: application name PCSZ pcszKey, // in: key name or NULL PCSZ pcszString) // in: string to write or NULL { ULONG ulDataLen = 0; if (pcszString) ulDataLen = strlen(pcszString) + 1; return xprfWriteProfileData(pXIni, pcszApp, pcszKey, (PVOID) pcszString, ulDataLen); } /* *@@ 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. * * Returns: * * -- NO_ERROR */ APIRET xprfCloseProfile(PXINI pXIni) // in: profile opened with xprfOpenProfile { APIRET arc = NO_ERROR; if ( (!pXIni) || (memcmp(pXIni->acMagic, XINI_MAGIC_BYTES, sizeof(XINI_MAGIC_BYTES))) ) arc = ERROR_INVALID_PARAMETER; else { if (pXIni->fDirty) arc = WriteINI(pXIni); if (!arc) { if (!(arc = DosClose(pXIni->hFile))) { pXIni->hFile = 0; FreeINI(pXIni); } } } return arc; } /* *@@ xprfQueryKeysForApp: * the equivalent of prfhQueryKeysForApp for * XINI files. * *@@added V1.0.0 (2002-09-17) [umoeller] *@@changed WarpIN V1.0.18 (2008-10-04) [pr]: fixed inverted logic bugs */ APIRET xprfQueryKeysForApp(PXINI hIni, // in: INI handle PCSZ pcszApp, // in: application to query list for (or NULL for applications list) PSZ *ppszKeys) // out: keys list (newly allocated) { APIRET arc = NO_ERROR; PSZ pKeys = NULL; ULONG ulSizeOfKeysList = 0; // get size of keys list for pszApp if (xprfQueryProfileSize(hIni, pcszApp, NULL, &ulSizeOfKeysList)) arc = PRFERR_KEYSLIST; else { if (ulSizeOfKeysList == 0) ulSizeOfKeysList = 1; // V0.9.19 (2002-04-11) [pr] if (!(pKeys = (PSZ)malloc(ulSizeOfKeysList))) arc = ERROR_NOT_ENOUGH_MEMORY; else { *pKeys = 0; if (xprfQueryProfileData(hIni, pcszApp, NULL, pKeys, &ulSizeOfKeysList)) arc = PRFERR_KEYSLIST; } } if (!arc) // V0.9.12 (2001-05-12) [umoeller] *ppszKeys = pKeys; else if (pKeys) free(pKeys); return arc; } /* *@@ xprfhQueryProfileData: * the equivalent of prfhQueryProfileData for * XINI files. * *@@added WarpIN V1.0.18 (2008-10-04) [pr] */ PSZ xprfhQueryProfileData(PXINI pXIni, // in: INI handle PCSZ pcszApp, // in: application to query PCSZ pcszKey, // in: key to query PULONG pcbBuf) // out: size of the returned buffer; ptr can be NULL { PSZ pData = NULL; ULONG ulSizeOfData; // get size of data for pcszApp/pcszKey if ( (!xprfQueryProfileSize(pXIni, (PSZ)pcszApp, (PSZ)pcszKey, &ulSizeOfData)) && (ulSizeOfData) && (pData = (PSZ)malloc(ulSizeOfData)) ) { if (xprfQueryProfileData(pXIni, (PSZ)pcszApp, (PSZ)pcszKey, pData, &ulSizeOfData)) { free(pData); pData = NULL; } } if (pcbBuf) *pcbBuf = ulSizeOfData; return pData; }