2391 lines
60 KiB
C
2391 lines
60 KiB
C
|
|
/*
|
|
* UP.C
|
|
*
|
|
* User Profile routines
|
|
*
|
|
* These are the routines which read and write INI files.
|
|
*
|
|
* Exported routines:
|
|
*
|
|
* GetProfileString
|
|
* GetPrivateProfileString
|
|
* GetProfileInt
|
|
* GetPrivateProfileInt
|
|
* WriteProfileString
|
|
* WritePrivateProfileString
|
|
*
|
|
* Note the parameter "lpSection" used to be known as "lpApplicationName".
|
|
* The code always referred to sections, so the parameter has been changed.
|
|
*
|
|
* Rewritten 6/90 for C 6.0.
|
|
*/
|
|
|
|
#include "kernel.h"
|
|
|
|
/*
|
|
* Required definitions for exported routines:
|
|
*/
|
|
|
|
#define API _far _pascal _loadds
|
|
|
|
HANDLE API IGlobalAlloc(WORD, DWORD);
|
|
HANDLE API IGlobalFree(HANDLE);
|
|
LPSTR API IGlobalLock(HANDLE);
|
|
HANDLE API IGlobalReAlloc(HANDLE, DWORD, WORD);
|
|
BOOL API IGlobalUnlock(HANDLE);
|
|
|
|
/* #pragma optimize("t", off) */
|
|
|
|
/* This ensures that only one selector is required in PMODE */
|
|
#define MAXBUFLEN 0xFFE0L
|
|
|
|
#define SPACE ' '
|
|
#define TAB '\t'
|
|
#define LINEFEED '\n'
|
|
#define CR '\r'
|
|
#define SECT_LEFT '['
|
|
#define SECT_RIGHT ']'
|
|
#define CTRLZ ('Z'-'@')
|
|
|
|
/* Constants for WriteProfileString - DON'T CHANGE THESE */
|
|
#define NOSECTION 0
|
|
#define NOKEY 1
|
|
#define NEWRESULT 2
|
|
#define REMOVESECTION 3
|
|
#define REMOVEKEY 4
|
|
|
|
/* Flags about a file kept in ProInfo
|
|
* If the PROUNCLEAN label is changed, its value must also be
|
|
* changed in I21ENTRY.ASM, where it is assumed to be 2.
|
|
*/
|
|
#define PROCOMMENTS 1 /* contains comments */
|
|
#define PROUNCLEAN 2 /* has not been written */
|
|
#define PROMATCHES 4 /* buffer matches disk copy */
|
|
#define PROREADONLY 8 /* Read only file */
|
|
#define PRO_CREATED 16 /* File was just created */
|
|
|
|
/* Sharing violation. */
|
|
#define SHARINGVIOLATION 0x0020
|
|
|
|
/* For forcing variables into the current code segment */
|
|
#define CODESEG _based(_segname("_CODE"))
|
|
/* Hide disgusting _based syntax */
|
|
#define BASED_ON_LP(x) _based((_segment)x)
|
|
#define BASED_ON_SEG(x) _based(x)
|
|
#define SEGMENT _segment
|
|
|
|
/* Externals assumed to be in DGROUP */
|
|
extern PROINFO WinIniInfo;
|
|
extern PROINFO PrivateProInfo;
|
|
extern LPSTR lpWindowsDir;
|
|
extern int cBytesWinDir;
|
|
extern int WinFlags;
|
|
extern char fBooting;
|
|
extern char fProfileDirty;
|
|
extern char fProfileMaybeStale;
|
|
extern char fAnnoyEarle;
|
|
extern char fBooting;
|
|
extern LPSTR curDTA;
|
|
extern BYTE fWriteOutProfilesReenter;
|
|
|
|
/* Forward definitions to keep compiler happy */
|
|
/* _fastcall may save some space on internal routines */
|
|
LPSTR _fastcall BufferInit(PROINFO *, int);
|
|
LPSTR _fastcall LockBuffer(PROINFO *);
|
|
void _fastcall UnlockBuffer(PROINFO *);
|
|
LPSTR _fastcall FreeBuffer(PROINFO *);
|
|
LPSTR _fastcall PackBuffer(PROINFO *, int, int);
|
|
void _fastcall FlushDirtyFile(PROINFO *);
|
|
int GetInt(PROINFO *, LPSTR, LPSTR, int);
|
|
int GetString(PROINFO *, LPSTR, LPSTR, LPSTR, LPSTR, int);
|
|
LPSTR FindString(PROINFO *, LPSTR, LPSTR);
|
|
LPSTR FindSection(LPSTR, LPSTR);
|
|
LPSTR FindKey(LPSTR, LPSTR);
|
|
int WriteString(PROINFO *, LPSTR, LPSTR, LPSTR);
|
|
void strcmpi(void);
|
|
int MyStrlen(void);
|
|
void API WriteOutProfiles(void);
|
|
PROINFO * SetPrivateProInfo(LPSTR,LPSTR);
|
|
int GetSection(PROINFO*, LPSTR, LPSTR, int);
|
|
int IsItTheSame(LPSTR, LPSTR);
|
|
int Cstrlen(LPSTR);
|
|
int MakeRoom(LPSTR, int, int*);
|
|
int InsertSection(LPSTR, LPSTR, short);
|
|
int InsertKey(LPSTR, LPSTR, short);
|
|
int InsertResult(LPSTR, LPSTR, short);
|
|
int DeleteSection(LPSTR, PROINFO*);
|
|
int DeleteKey(LPSTR, PROINFO*);
|
|
|
|
/* External KERNEL routines */
|
|
void _far _pascal FarMyLower();
|
|
|
|
int API lstrOriginal(LPSTR,LPSTR); /* lstrcmp in disguise */
|
|
|
|
#ifdef FE_SB
|
|
// Delacred in kernel.h already
|
|
// void _far _pascal AnsiPrev(LPSTR,LPSTR);
|
|
void _far _pascal FarMyIsDBCSLeadByte();
|
|
#endif
|
|
|
|
char CODESEG WinIniStr[] = "WIN.INI";
|
|
|
|
/* DOS FindFirst/FindNext structure (43h, 44h) */
|
|
typedef struct tagFILEINFO
|
|
{
|
|
BYTE fiReserved[21];
|
|
BYTE fiAttribute;
|
|
WORD fiFileTime;
|
|
WORD fiFileDate;
|
|
DWORD fiSize;
|
|
BYTE fiFileName[13];
|
|
} FILEINFO;
|
|
|
|
/*
|
|
* Get[Private]ProfileInt
|
|
*
|
|
* Parameters:
|
|
* lpSection Pointer to section to match in INI file
|
|
* lpKeyName Pointer to key string to match in file
|
|
* nDefault Default value to return if not found
|
|
* [lpFile File to use for Private INI]
|
|
*
|
|
* Returns:
|
|
* nDefault section/keyname not found
|
|
* number found in file if section/keyname found
|
|
*/
|
|
int API
|
|
IGetProfileInt(lpSection, lpKeyName, nDefault)
|
|
LPSTR lpSection;
|
|
LPSTR lpKeyName;
|
|
int nDefault;
|
|
{
|
|
int nReturn;
|
|
|
|
/* Make sure we don't try to flush INI files on DOS calls */
|
|
++fWriteOutProfilesReenter;
|
|
|
|
/* Reread INI file first if necessary */
|
|
FlushDirtyFile(&WinIniInfo);
|
|
|
|
nReturn = GetInt(&WinIniInfo, lpSection, lpKeyName, nDefault);
|
|
|
|
--fWriteOutProfilesReenter;
|
|
|
|
return nReturn;
|
|
}
|
|
|
|
|
|
int API
|
|
IGetPrivateProfileInt(lpSection, lpKeyName, nDefault, lpFile)
|
|
LPSTR lpSection;
|
|
LPSTR lpKeyName;
|
|
int nDefault;
|
|
LPSTR lpFile;
|
|
{
|
|
PROINFO *pProInfo;
|
|
char Buffer[128];
|
|
int nReturn;
|
|
|
|
/* Make sure we don't try to flush INI files on DOS calls */
|
|
++fWriteOutProfilesReenter;
|
|
|
|
pProInfo = SetPrivateProInfo(lpFile, (LPSTR)Buffer);
|
|
|
|
/* Reread INI file first if necessary */
|
|
FlushDirtyFile(pProInfo);
|
|
|
|
nReturn = GetInt(pProInfo, lpSection, lpKeyName, nDefault);
|
|
--fWriteOutProfilesReenter;
|
|
|
|
return nReturn;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get[Private]ProfileString
|
|
*
|
|
* Parameters:
|
|
* lpSection Pointer to section to match in INI file
|
|
* lpKeyName Pointer to key string to match in file
|
|
* lpDefault Default string to return if not found
|
|
* lpResult String to fill in
|
|
* nSize Max number of characters to copy
|
|
* [lpFile] File to use for Private INI
|
|
*
|
|
* Returns:
|
|
* string from file or lpDefault copied to lpResult
|
|
* < nSize - 2 Number of characters copied to lpResult
|
|
* nSize - 2 lpResult was not big enough
|
|
*/
|
|
int API
|
|
IGetProfileString(lpSection, lpKeyName, lpDefault, lpResult, nSize)
|
|
LPSTR lpSection;
|
|
LPSTR lpKeyName;
|
|
LPSTR lpDefault;
|
|
LPSTR lpResult;
|
|
int nSize;
|
|
{
|
|
int nReturn;
|
|
|
|
/* Make sure we don't try to flush INI files on DOS calls */
|
|
++fWriteOutProfilesReenter;
|
|
|
|
/* Reread INI file first if necessary */
|
|
FlushDirtyFile(&WinIniInfo);
|
|
|
|
nReturn = GetString(&WinIniInfo, lpSection, lpKeyName, lpDefault,
|
|
lpResult, nSize);
|
|
--fWriteOutProfilesReenter;
|
|
|
|
return nReturn;
|
|
}
|
|
|
|
|
|
int API
|
|
IGetPrivateProfileString(lpSection, lpKeyName, lpDefault, lpResult, nSize, lpFile)
|
|
LPSTR lpSection;
|
|
LPSTR lpKeyName;
|
|
LPSTR lpDefault;
|
|
LPSTR lpResult;
|
|
int nSize;
|
|
LPSTR lpFile;
|
|
{
|
|
PROINFO *pProInfo;
|
|
char Buffer[128];
|
|
int nReturn;
|
|
|
|
/* Make sure we don't try to flush INI files on DOS calls */
|
|
++fWriteOutProfilesReenter;
|
|
|
|
pProInfo = SetPrivateProInfo(lpFile, (LPSTR)Buffer);
|
|
|
|
/* Reread INI file first if necessary */
|
|
FlushDirtyFile(pProInfo);
|
|
|
|
nReturn = GetString(pProInfo, lpSection, lpKeyName, lpDefault,
|
|
lpResult, nSize);
|
|
|
|
--fWriteOutProfilesReenter;
|
|
|
|
return nReturn;
|
|
}
|
|
|
|
|
|
/*
|
|
* Write[Private]ProfileString
|
|
*
|
|
* Parameters:
|
|
* lpSection Pointer to section to match/add to INI file
|
|
* lpKeyName Pointer to key string to match/add to file
|
|
* lpString String to add to file
|
|
* [lpFile] File to use for Private INI
|
|
*
|
|
* Returns:
|
|
* 0 Failed
|
|
* 1 Success
|
|
*/
|
|
int API
|
|
IWriteProfileString(lpSection, lpKeyName, lpString)
|
|
LPSTR lpSection;
|
|
LPSTR lpKeyName;
|
|
LPSTR lpString;
|
|
{
|
|
int nReturn;
|
|
|
|
/* Make sure we don't try to flush INI files on DOS calls */
|
|
++fWriteOutProfilesReenter;
|
|
|
|
/* Reread INI file first if necessary */
|
|
FlushDirtyFile(&WinIniInfo);
|
|
|
|
nReturn = WriteString(&WinIniInfo, lpSection, lpKeyName, lpString);
|
|
|
|
--fWriteOutProfilesReenter;
|
|
|
|
return nReturn;
|
|
}
|
|
|
|
|
|
int API
|
|
IWritePrivateProfileString(lpSection, lpKeyName, lpString, lpFile)
|
|
LPSTR lpSection;
|
|
LPSTR lpKeyName;
|
|
LPSTR lpString;
|
|
LPSTR lpFile;
|
|
{
|
|
PROINFO *pProInfo;
|
|
char Buffer[128];
|
|
int nReturn;
|
|
|
|
/* Make sure we don't try to flush INI files on DOS calls */
|
|
++fWriteOutProfilesReenter;
|
|
|
|
pProInfo = SetPrivateProInfo(lpFile, (LPSTR)Buffer);
|
|
|
|
/* Reread INI file first if necessary */
|
|
FlushDirtyFile(pProInfo);
|
|
|
|
nReturn = WriteString(pProInfo, lpSection, lpKeyName, lpString);
|
|
|
|
--fWriteOutProfilesReenter;
|
|
|
|
return nReturn;
|
|
}
|
|
|
|
|
|
/* FlushDirtyFile
|
|
* Rereads a file if it has been "dirtied" by another task. To
|
|
* see if the file has been dirtied, we check the time/date
|
|
* stamp.
|
|
*/
|
|
|
|
void _fastcall FlushDirtyFile(PROINFO *pProInfo)
|
|
{
|
|
FILEINFO FileInfo;
|
|
DWORD dwSaveDTA;
|
|
|
|
/* We only have to do this if the file COULD have changed and
|
|
* that we already have something cached. Also, there's
|
|
* no need to do this at boot time because this is a
|
|
* safeguard against the USER doing something bad!
|
|
*/
|
|
if (fBooting || !fProfileMaybeStale || !pProInfo->lpBuffer)
|
|
return;
|
|
|
|
/* The OFSTRUCT in the PROINFO buffer should have the most recent
|
|
* date and time when the file was opened. We just compare the
|
|
* current date and time to this.
|
|
*/
|
|
_asm
|
|
{
|
|
;** Save old DTA and point to our structure
|
|
mov ah,2fh ;Get DTA. Int21 code calls DOS only
|
|
int 21h ; if necessary. DTA in ES:BX
|
|
jc RDF_FlushIt ;Problem, so better flush it
|
|
mov WORD PTR dwSaveDTA[2],es ;Save for later
|
|
mov WORD PTR dwSaveDTA[0],bx
|
|
mov ah,1ah ;Set DTA
|
|
push ds ;Can't do a SetKernelDS so push/pop
|
|
push ss ;Get SS=DS
|
|
pop ds
|
|
lea dx,FileInfo ;Point DTA to our structure
|
|
int 21h ;Set the DTA
|
|
pop ds
|
|
jc RDF_FlushIt ;Problem, so just flush it
|
|
|
|
;** Do a FindFirst on the file to get date and time reliably
|
|
xor cx,cx ;Normal file
|
|
mov si,pProInfo ;Point to pathname with DS:DX
|
|
lea dx,[si].ProBuf
|
|
add dx,8 ;(offset of szPathName)
|
|
mov ah,4eh ;Find first
|
|
int 21h ;Call DOS
|
|
jc RDF_FlushIt ;Can't find, so better flush it
|
|
|
|
;** Put DTA back
|
|
push ds
|
|
lds dx,dwSaveDTA ;DS:DX points to old DTA
|
|
mov ah,1ah
|
|
int 21h
|
|
pop ds
|
|
|
|
;** Compare the date and time
|
|
lea bx,FileInfo ;Point to FILEINFO
|
|
mov dx,ss:[bx + 24] ;Date in FILEINFO structure
|
|
mov cx,ss:[bx + 22] ;Tile in FILEINFO structure
|
|
mov si,pProInfo ;Point to OFSTRUCT with DS:SI
|
|
lea si,[si].ProBuf
|
|
cmp [si + 4],dx ;Same date as original?
|
|
jne RDF_FlushIt ;No
|
|
cmp [si + 6],cx ;Same time as original?
|
|
je RDF_NoFlush ;No
|
|
}
|
|
|
|
/* Force a file reread */
|
|
RDF_FlushIt:
|
|
FreeBuffer(pProInfo);
|
|
RDF_NoFlush:
|
|
|
|
/* Clear the dirty flag */
|
|
fProfileMaybeStale = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* SetPrivateProInfo
|
|
*
|
|
* Force a private profile into the windows directory if necessary.
|
|
* Check if it is the same file as is currently cached.
|
|
* If not, discard the cached file.
|
|
* Sets up the PrivateProInfo data structure.
|
|
*
|
|
* Parameters:
|
|
* lpFile Pointer to filename to be used as a profile
|
|
* Buffer Buffer to parse filename into
|
|
*
|
|
* Returns:
|
|
* PROINFO * Pointer to information about ini file
|
|
*/
|
|
PROINFO *
|
|
SetPrivateProInfo(lpFile, Buffer)
|
|
LPSTR lpFile;
|
|
LPSTR Buffer;
|
|
{
|
|
OFSTRUCT NewFileBuf;
|
|
char c;
|
|
char fQualified = 0;
|
|
char BASED_ON_LP(lpFile) *psrc;
|
|
int Count = 0;
|
|
|
|
/* Get rid of annoying warnings with this ugly cast */
|
|
psrc = (char BASED_ON_LP(lpFile)*)(WORD)(DWORD)lpFile;
|
|
|
|
/* For those who insist on using private routines for WIN.INI */
|
|
if ( lstrOriginal(lpFile, (LPSTR)WinIniStr) == 0
|
|
|| lstrOriginal(lpFile, WinIniInfo.ProBuf.szPathName) == 0 ) {
|
|
return(&WinIniInfo);
|
|
}
|
|
|
|
/*
|
|
* Following code is from ForcePrivatePro
|
|
*
|
|
* If the filename given is not qualified, we force
|
|
* it into the windows directory.
|
|
*/
|
|
#ifdef FE_SB
|
|
_asm {
|
|
;Apr.26,1990 by AkiraK
|
|
cld
|
|
push ds ;save kernel DS
|
|
xor ax,ax
|
|
mov bx,'/' shl 8 + '\\' ; '/' or '\'
|
|
xor dx,dx
|
|
lds si,lpFile ; first get length of string
|
|
mov cx,si
|
|
mov al,ds:[si]
|
|
call FarMyIsDBCSLeadByte
|
|
jnc fpp_s1
|
|
cmp byte ptr ds:[si+1],':' ;
|
|
jnz fpp_s1
|
|
inc dx
|
|
fpp_s1:
|
|
|
|
fpp_l1:
|
|
lodsb
|
|
or al,al
|
|
jz fpp_got_length
|
|
cmp al,bh
|
|
jz fpp_qualified
|
|
cmp al,bl
|
|
jz fpp_qualified
|
|
fpp_s2:
|
|
call FarMyIsDBCSLeadByte
|
|
jc fpp_l1
|
|
inc si
|
|
jmp fpp_l1
|
|
|
|
fpp_qualified:
|
|
inc dx
|
|
jmp fpp_s2
|
|
fpp_got_length:
|
|
;; mov fQualified, dx
|
|
mov fQualified, dl ; a byte variable
|
|
sub si, cx
|
|
mov Count, si
|
|
pop ds ;recover kernel DS
|
|
}
|
|
#else
|
|
/* Drive specified? */
|
|
if ( *(psrc+1) == ':' )
|
|
fQualified++;
|
|
while ( c = *psrc++ ) {
|
|
/* Look for path separators */
|
|
if ( c == '/' || c == '\\' )
|
|
fQualified++;
|
|
Count++;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Now copy filename to buffer.
|
|
* Prepend Windows directory if not qualified.
|
|
*/
|
|
_asm {
|
|
cld
|
|
push ds
|
|
les di, Buffer ; Destination is Buffer
|
|
cmp fQualified, 0
|
|
jnz Qualified
|
|
mov cx, cBytesWinDir ; Pick up Windows directory
|
|
lds si, lpWindowsDir
|
|
rep movsb ; Copy it
|
|
mov al, '\\'
|
|
cmp es:[di-1], al ; BUG FIX: if in root, don't
|
|
je Qualified ; add separator
|
|
stosb ; Add path separator
|
|
Qualified:
|
|
lds si, lpFile ; Now add Filename we were given
|
|
mov cx, Count
|
|
inc cx ; Allow for NULL
|
|
rep movsb
|
|
pop ds
|
|
}
|
|
#ifdef NOTNOW
|
|
if ( !fBooting && fQualified ) {
|
|
/*
|
|
* Use OpenFile to generate pathname for
|
|
* comparison with the cached pathname.
|
|
* OF_EXIST ensures we get a complete pathname
|
|
* We cannot use OF_PARSE, it does not search the path.
|
|
* We only do this if the pathname we were given was
|
|
* qualified since in other cases we force the file
|
|
* into the windows directory and therefore know
|
|
* that Buffer contains the complete pathname.
|
|
*/
|
|
NewFileBuf.szPathName[0] = 0;
|
|
OpenFile(Buffer, &NewFileBuf, OF_EXIST);
|
|
}
|
|
#endif
|
|
/* Now see if the filename matches the cached filename */
|
|
_asm {
|
|
cld
|
|
xor cx, cx
|
|
lea si, word ptr [PrivateProInfo.ProBuf] ; Cached INI OFSTRUCT
|
|
mov cl, [si].cBytes
|
|
lea si, word ptr [si].szPathName ; Cached filename
|
|
sub cx, 8 ; Get its length
|
|
UseOriginal: ; Use the filename they gave us
|
|
les di, Buffer ; while booting
|
|
xor bl, bl
|
|
call strcmpi ; Ignore case while booting
|
|
jmp short DoWeDiscardIt
|
|
JustCompare:
|
|
; Not booting, compare OFSTRUCTS
|
|
; Note OpenFile forced upper case
|
|
push ss
|
|
pop es ; NewFileBuf is on SS
|
|
lea di, word ptr NewFileBuf.szPathName[0];
|
|
rep cmpsb ; Compare filenames
|
|
DoWeDiscardIt:
|
|
jz WeHaveItCached ; Don't discard if names match
|
|
}
|
|
/*
|
|
* The cached file is not the right one,
|
|
* so we discard the saved file.
|
|
*/
|
|
FreeBuffer(&PrivateProInfo);
|
|
|
|
WeHaveItCached:
|
|
/* Save pointer to FileName - buffer may have been discarded */
|
|
PrivateProInfo.lpProFile = Buffer;
|
|
return(&PrivateProInfo);
|
|
}
|
|
|
|
|
|
/*
|
|
* GetInt - search file and return an integer
|
|
*
|
|
* Parameters:
|
|
* pProInfo Pointer to information on the INI file
|
|
* lpSection Pointer to section to match in INI file
|
|
* lpKeyName Pointer to key string to match in file
|
|
* nDefault Default value to return if not found
|
|
*
|
|
* Returns:
|
|
* see GetProfileInt
|
|
*/
|
|
int
|
|
GetInt(pProInfo, lpSection, lpKeyName, nDefault)
|
|
PROINFO *pProInfo;
|
|
LPSTR lpSection;
|
|
LPSTR lpKeyName;
|
|
int nDefault;
|
|
{
|
|
LPSTR lpResult;
|
|
|
|
lpResult = FindString(pProInfo, lpSection, lpKeyName);
|
|
if (lpResult) {
|
|
/* We found a string, convert to int */
|
|
register int c;
|
|
int radix = 10;
|
|
BOOL fNeg = FALSE;
|
|
|
|
// Skip spaces
|
|
while (*lpResult == ' ' || *lpResult == '\t')
|
|
++lpResult;
|
|
|
|
nDefault = 0;
|
|
|
|
while ((c = *lpResult++) != 0) {
|
|
|
|
// Watch for change in sign
|
|
//
|
|
if (c == '-') {
|
|
fNeg = !fNeg;
|
|
continue;
|
|
}
|
|
|
|
// Lower case the character if it's a letter.
|
|
//
|
|
if (c >= 'A' && c <= 'Z')
|
|
c += ('a' - 'A');
|
|
|
|
// deal with hex constants
|
|
//
|
|
if (c == 'x') {
|
|
radix = 16;
|
|
continue;
|
|
}
|
|
|
|
c -= '0';
|
|
if (c > 9)
|
|
c += '0' - 'a' + 10;
|
|
|
|
if (c < 0 || c >= radix)
|
|
break;
|
|
|
|
nDefault = nDefault * radix + c;
|
|
}
|
|
if (fNeg)
|
|
nDefault = -nDefault;
|
|
}
|
|
UnlockBuffer(pProInfo);
|
|
return(nDefault);
|
|
}
|
|
|
|
|
|
/*
|
|
* GetString - Search file for a specific Section and KeyName
|
|
*
|
|
* Parameters:
|
|
* pProInfo Pointer to information on the INI file
|
|
* lpSection Pointer to section to match in INI file
|
|
* lpKeyName Pointer to key string to match in file
|
|
* lpDefault Default string to return if not found
|
|
* lpResult String to fill in
|
|
* nSize Max number of characters to copy
|
|
*
|
|
* Returns:
|
|
* see GetProfileString
|
|
*/
|
|
GetString(pProInfo, lpSection, lpKeyName, lpDefault, lpResult, nSize)
|
|
PROINFO *pProInfo;
|
|
LPSTR lpSection;
|
|
LPSTR lpKeyName;
|
|
LPSTR lpDefault;
|
|
LPSTR lpResult;
|
|
int nSize;
|
|
{
|
|
int nFound;
|
|
LPSTR lpFound;
|
|
|
|
if ( !lpKeyName ) {
|
|
nFound = GetSection(pProInfo, lpSection, lpResult, nSize);
|
|
if ( nFound == -1 )
|
|
goto CopyDefault; /* Yes, I know! */
|
|
} else {
|
|
lpFound = FindString(pProInfo, lpSection, lpKeyName);
|
|
if ( lpFound )
|
|
lpDefault = lpFound;
|
|
CopyDefault:
|
|
_asm {
|
|
xor ax, ax ; Return value
|
|
cmp word ptr lpDefault[2], 0 ; Check for null default
|
|
je SavedMe
|
|
les di, lpDefault
|
|
call MyStrlen ; Returns length in CX
|
|
|
|
; Fix for #10907 -- Used to GP fault on zero length str.
|
|
or cx,cx ; No characters in string?
|
|
je strdone
|
|
|
|
#ifdef FE_SB
|
|
; Get last character behind terminator
|
|
push si
|
|
les si, lpDefault ; SI = front of string
|
|
gps_dbcs_l1:
|
|
mov al, es:[si]
|
|
call FarMyIsDBCSLeadByte
|
|
cmc
|
|
adc si, 1
|
|
cmp si, di
|
|
jb gps_dbcs_l1
|
|
pop si
|
|
#else
|
|
add di, cx
|
|
mov al, es:[di-1] ; Final character in string
|
|
#endif
|
|
les di, lpDefault
|
|
cmp cx, 2 ; strlen > 2
|
|
jb strdone
|
|
; Strip off single and double quotes
|
|
mov ah, es:[di]
|
|
cmp ah, al ; First character == last character?
|
|
jne strdone
|
|
cmp al, '\''
|
|
je strq
|
|
cmp al, '"'
|
|
jne strdone
|
|
strq:
|
|
sub cx, 2 ; Lose those quotes
|
|
inc di
|
|
strdone:
|
|
; CX has length of string
|
|
mov dx, nSize
|
|
dec dx ; Allow for null
|
|
cmp cx, dx ; See if enough room
|
|
jbe HaveRoom
|
|
mov cx, dx
|
|
HaveRoom:
|
|
cld
|
|
push ds
|
|
push es
|
|
pop ds
|
|
mov si, di ; DS:SI has string to return
|
|
les di, lpResult
|
|
mov ax, cx ; Save length of string
|
|
rep movsb ; Copy the string
|
|
mov byte ptr es:[di], 0 ; Null terminate the string
|
|
pop ds
|
|
SavedMe:
|
|
mov nFound, ax ; We will return this
|
|
}
|
|
}
|
|
|
|
UnlockBuffer(pProInfo);
|
|
return(nFound);
|
|
}
|
|
|
|
|
|
/*
|
|
* GetSection - find a section and copy all KeyNames to lpResult
|
|
*
|
|
* Parameters:
|
|
* pProInfo pointer to info on the file
|
|
* lpSection pointer to the section name we want
|
|
* lpResult where the KeyNames will go
|
|
* nSize size of lpResult buffer
|
|
*
|
|
* Returns:
|
|
* int Number of characters copied, -1 for failure
|
|
*/
|
|
int
|
|
GetSection(pProInfo, lpSection, lpResult, nSize)
|
|
PROINFO *pProInfo;
|
|
LPSTR lpSection;
|
|
LPSTR lpResult;
|
|
int nSize;
|
|
{
|
|
LPSTR lp;
|
|
|
|
lp = BufferInit(pProInfo, READ);
|
|
if ( !lp )
|
|
return(-1); /* No buffer, (no file, no memory etc.) */
|
|
|
|
nSize--; /* Room for terminating NULL */
|
|
|
|
lp = FindSection(lp, lpSection);
|
|
if ( !lp )
|
|
return(-1);
|
|
|
|
_asm {
|
|
push ds
|
|
lds si, lpResult ; DS:SI is where we store the result
|
|
les di, lp ; ES:DI points to the section in buffer
|
|
xor dx, dx ; Count of characters in the result
|
|
KeyNameLoop:
|
|
mov bx, di ; Save start of line
|
|
cmp es:[di], ';' ; Is this a comment line?
|
|
jne KeyNameNextCh ; no, check this line out
|
|
cld
|
|
mov cx, -1
|
|
mov al, LINEFEED
|
|
repne scasb ; Skip to end of the line
|
|
jmp KeyNameLoop
|
|
KeyNameNextCh:
|
|
mov al, es:[di] ; Get next character
|
|
#ifdef FE_SB
|
|
call FarMyIsDBCSLeadByte
|
|
cmc ; if the char is lead byte of DBCS,
|
|
adc di, 1 ; then di += 2, else di += 1
|
|
#else
|
|
inc di
|
|
#endif
|
|
cmp al, '='
|
|
je FoundEquals
|
|
cmp al, LINEFEED
|
|
je KeyNameLoop ; Ignore lines without an '='
|
|
cmp al, SECT_LEFT
|
|
je EndSection ; Done if end of section
|
|
or al, al ; or if end of buffer (NULL)
|
|
jne KeyNameNextCh ; On to the next character
|
|
jmp EndSection
|
|
FoundEquals:
|
|
mov di, bx ; Back to the start of the line
|
|
CopyLoop:
|
|
mov al, es:[di] ; Pick up next character in line
|
|
inc di
|
|
cmp al, '=' ; Is it the '='?
|
|
jne LeaveItAlone
|
|
xor al, al ; yes, replace with NULL
|
|
LeaveItAlone:
|
|
mov [si], al ; Put it in the result string
|
|
inc dx ; Number of characters in the result
|
|
inc si
|
|
cmp dx, nSize ; Overflowed?
|
|
jb NoProblem
|
|
dec dx ; yes, ignore this character
|
|
dec si
|
|
NoProblem:
|
|
#ifdef FE_SB
|
|
call FarMyIsDBCSLeadByte
|
|
jc NoProblem1
|
|
mov al, es:[di]
|
|
inc di
|
|
mov [si], al
|
|
inc dx
|
|
inc si
|
|
cmp dx, nSize
|
|
jb NoProblem1
|
|
dec si
|
|
dec dx
|
|
NoProblem1:
|
|
#endif
|
|
or al, al ; Was this the '='
|
|
jne CopyLoop
|
|
SkipLine:
|
|
mov al, es:[di] ; Skip the rest of the line
|
|
inc di
|
|
cmp al, LINEFEED
|
|
jne SkipLine
|
|
jmp KeyNameLoop
|
|
|
|
EndSection:
|
|
mov byte ptr [si], 0 ; Terminate with NULL
|
|
or dx, dx ; Did we copy anything?
|
|
jz NothingFound ; no, no hack
|
|
#ifdef FE_SB
|
|
;AnsiPrev API has been moved to USER and it is not the
|
|
;right time to invoke any USER's APIs as we might be called
|
|
;while USER is still on the bed.
|
|
; push dx
|
|
; push word ptr lpResult[2]
|
|
; push word ptr lpResult[0]
|
|
; push ds
|
|
; push si
|
|
; call AnsiPrev
|
|
; mov si, ax
|
|
; mov byte ptr [si], 0
|
|
; mov byte ptr [si+1], 0
|
|
; pop dx
|
|
;-----------------------------------------------------------
|
|
push es
|
|
push di
|
|
push bx
|
|
les di,lpResult ;string head
|
|
ScanLoop:
|
|
mov bx,di ;"prev" char position
|
|
mov al,es:[di]
|
|
call FarMyIsDBCSLeadByte
|
|
cmc
|
|
adc di, 1 ;+2 if DBCS, +1 if not
|
|
cmp di,si ;have we hit the point yet?
|
|
jb ScanLoop ;nope,
|
|
;The output of this routine looks like:
|
|
;<name 1>,0,<name2>,0,.... <name n>,0,0
|
|
; the very last 0 tells the end of story.
|
|
mov es:[bx],0 ;this is safe
|
|
mov es:[bx+1],0 ;Hmmmmm
|
|
pop bx
|
|
pop di
|
|
pop es
|
|
#else
|
|
mov byte ptr [si-1], 0 ; Hack - if we hit nSize, we
|
|
#endif
|
|
; and extra NULL
|
|
NothingFound:
|
|
pop ds
|
|
mov nSize, dx
|
|
}
|
|
return(nSize);
|
|
}
|
|
|
|
|
|
/*
|
|
* FindString - look for section name and key name
|
|
*
|
|
* Parameters:
|
|
* pProInfo Pointer to info on the file
|
|
* lp Pointer to the buffer containing the file
|
|
* lpSection Pointer to the section name we are looking for
|
|
* lpKeyName Pointer the the KeyName we want
|
|
*
|
|
* Returns:
|
|
* LPSTR Pointer to the start of the result string
|
|
* NULL for failure
|
|
*/
|
|
LPSTR
|
|
FindString(pProInfo, lpSection, lpKeyName)
|
|
PROINFO *pProInfo;
|
|
LPSTR lpSection;
|
|
LPSTR lpKeyName;
|
|
{
|
|
LPSTR lp;
|
|
|
|
if ( lp = BufferInit(pProInfo, READ) )
|
|
if ( lp = FindSection(lp, lpSection) )
|
|
lp = FindKey(lp, lpKeyName);
|
|
return(lp);
|
|
}
|
|
|
|
|
|
/*
|
|
* FindSection - look for a section name enclosed in '[' and ']'
|
|
*
|
|
* Parameters:
|
|
* lp Pointer to the buffer containing the file
|
|
* lpSection Pointer to the section name we are looking for
|
|
*
|
|
* Returns:
|
|
* LPSTR Pointer to the start of the section for success
|
|
* NULL for failure
|
|
*/
|
|
LPSTR
|
|
FindSection(lp, lpSection)
|
|
LPSTR lp;
|
|
LPSTR lpSection;
|
|
{
|
|
WORD wCount;
|
|
WORD wTrailCount;
|
|
WORD fLead;
|
|
LPSTR lpstr;
|
|
WORD wSegLen;
|
|
|
|
/* Remove leading whitespace from section names and compute
|
|
* a length count that doesn't include trailing whitespace.
|
|
* We use this below to force a TRUE compare even though
|
|
* the program put garbage on the end.
|
|
*/
|
|
for (lpstr = lpSection, fLead = 1, wCount = wTrailCount = 0 ;
|
|
*lpstr ; ++lpstr)
|
|
{
|
|
/* If we haven't passed leading space yet... */
|
|
if (fLead)
|
|
{
|
|
if (*lpstr == SPACE || *lpstr == TAB)
|
|
++lpSection;
|
|
else
|
|
{
|
|
fLead = 0;
|
|
++wCount;
|
|
++wTrailCount;
|
|
}
|
|
}
|
|
|
|
/* Otherwise this might be trailing space... */
|
|
else
|
|
{
|
|
/* wCount always has correct count, wTrailCount
|
|
* never counts whitespace until another
|
|
* character is encountered. This allows
|
|
* a count of characters excluding trailing
|
|
* whitespace.
|
|
*/
|
|
++wCount;
|
|
if (*lpstr != SPACE && *lpstr != TAB)
|
|
wTrailCount = wCount;
|
|
}
|
|
}
|
|
wCount = wTrailCount;
|
|
|
|
_asm {
|
|
lsl cx,WORD PTR lp[2] ; Get max possible search len
|
|
mov wSegLen,cx ; Save for quick access later
|
|
push ds
|
|
les di, lp
|
|
SectionLoop:
|
|
cmp byte ptr es:[di], SECT_LEFT ; ie '['
|
|
jne NotThisLine
|
|
inc di ; Skip the '['
|
|
|
|
;** Check the length of the string
|
|
push di ; Save because we're going to trash
|
|
mov cx,wSegLen ; Get segment length
|
|
sub cx,di ; Subtract off the distance into seg
|
|
mov dx,cx ; Save in DX
|
|
mov al,SECT_RIGHT ; Stop when we encouter this
|
|
#ifdef FE_SB
|
|
;SECT_RIGHT is a legal DBCS second byte
|
|
;and we have to emulate DBCS "repne scasb" here.
|
|
fsScanSectRight:
|
|
dec cx ;
|
|
jz fsScanFinish ; reach to end of segment
|
|
scasb ;
|
|
je fsScanFinish ; find it!
|
|
call FarMyIsDBCSLeadByte
|
|
jc fsScanSectRight
|
|
inc di ; skip DBCS 2nd byte
|
|
dec cx
|
|
jnz short fsScanSectRight
|
|
fsScanFinish:
|
|
|
|
#else
|
|
repne scasb ; Compare until we find it
|
|
#endif
|
|
sub dx,cx ; Get true string len
|
|
dec dx
|
|
pop di
|
|
cmp dx,wCount ; Same length?
|
|
jne NotThisLine
|
|
|
|
;** Now compare the strings. Note that strcmpi returns a
|
|
;** pointer just past the failed char
|
|
lds si, lpSection
|
|
mov bl, SECT_RIGHT ; Compare up to '['
|
|
call strcmpi
|
|
je HereItIs
|
|
|
|
;** Even if we failed, it might match less trailing whitespace
|
|
sub ax,di ; Get length at first mismatch
|
|
cmp ax,wCount ; Make sure we mismatched at end
|
|
jne NotThisLine ; We didn't so get out
|
|
add di,ax ; Bump pointers to end
|
|
add si,ax
|
|
mov al,es:[di - 1] ; Compare last chars
|
|
cmp al,ds:[si - 1] ; Do they match?
|
|
jne NotThisLine ; Yes
|
|
|
|
HereItIs:
|
|
mov al, LINEFEED ; Skip the rest of the line
|
|
mov cx, -1
|
|
repne scasb ; Scans ES:[DI]
|
|
|
|
mov ax, di
|
|
mov dx, es ; Return pointer to section
|
|
jmp FoundIt
|
|
|
|
NotThisLine:
|
|
mov al, LINEFEED ; Skip the rest of the line
|
|
mov cx, -1 ; Scans ES:[DI]
|
|
repne scasb
|
|
|
|
cmp byte ptr es:[di], 0 ; End of the file?
|
|
jne SectionLoop ; nope, continue
|
|
xor ax, ax
|
|
xor dx, dx ; Return 0
|
|
FoundIt:
|
|
pop ds
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* FindKey - Find a KeyName given a pointer to the start of a section
|
|
*
|
|
* Parameters:
|
|
* lp Pointer to start of a section
|
|
* lpKeyName Pointer the the KeyName we want
|
|
*
|
|
* Returns:
|
|
* LPSTR Pointer to the string following the KeyName
|
|
* NULL if KeyName not found
|
|
*/
|
|
LPSTR
|
|
FindKey(lp, lpKeyName)
|
|
LPSTR lp;
|
|
LPSTR lpKeyName;
|
|
{
|
|
WORD wCount;
|
|
WORD wTrailCount;
|
|
WORD fLead;
|
|
LPSTR lpstr;
|
|
WORD wSegLen;
|
|
|
|
/* Remove leading whitespace from key names and compute
|
|
* a length count that doesn't include trailing whitespace.
|
|
* We use this below to force a TRUE compare even though
|
|
* the program put garbage on the end.
|
|
*/
|
|
for (lpstr = lpKeyName, fLead = 1, wCount = wTrailCount = 0 ;
|
|
*lpstr ; ++lpstr)
|
|
{
|
|
/* If we haven't passed leading space yet... */
|
|
if (fLead)
|
|
{
|
|
if (*lpstr == SPACE || *lpstr == TAB)
|
|
++lpKeyName;
|
|
else
|
|
{
|
|
fLead = 0;
|
|
++wCount;
|
|
++wTrailCount;
|
|
}
|
|
}
|
|
|
|
/* Otherwise this might be trailing space... */
|
|
else
|
|
{
|
|
/* wCount always has correct count, wTrailCount
|
|
* never counts whitespace until another
|
|
* character is encountered. This allows
|
|
* a count of characters excluding trailing
|
|
* whitespace.
|
|
*/
|
|
++wCount;
|
|
if (*lpstr != SPACE && *lpstr != TAB)
|
|
wTrailCount = wCount;
|
|
}
|
|
}
|
|
wCount = wTrailCount;
|
|
|
|
_asm {
|
|
push ds
|
|
mov ax, word ptr lpKeyName
|
|
or ax, word ptr lpKeyName[2]
|
|
jz NoMatch ; Return zero if lpKeyName is 0
|
|
lsl cx,WORD PTR lp[2] ; Get max possible search len
|
|
mov wSegLen,cx ; Save for quick access later
|
|
les di, lp
|
|
|
|
;** See if we're at the end of the section
|
|
FindKeyNext:
|
|
mov al,es:[di] ; Get next character
|
|
or al,al
|
|
jz NoMatch ; End of the file
|
|
cmp al,SECT_LEFT
|
|
je NoMatch ; End of the section
|
|
cmp al,CR ; Blank line?
|
|
je NotThisKey ; Yes, skip this one
|
|
|
|
;** Check the length of the string
|
|
push di ; Save because we're going to trash
|
|
mov cx,wSegLen ; Get segment length
|
|
sub cx,di ; Subtract off the distance into seg
|
|
mov dx,cx ; Save in DX
|
|
mov al,'=' ; Stop when we encouter this
|
|
repne scasb ; Compare until we find it
|
|
sub dx,cx ; Get true string len
|
|
dec dx
|
|
pop di
|
|
cmp dx,wCount ; Same length?
|
|
jne NotThisKey
|
|
|
|
;** Now compare the strings. Note that strcmpi returns a
|
|
;** pointer just past the failed char.
|
|
mov bl,'=' ; Compare until we hit this
|
|
lds si,lpKeyName
|
|
call strcmpi
|
|
mov bx,di ; Save DI value for below
|
|
mov di,ax
|
|
je FoundKey
|
|
|
|
;** Even if we failed, it might match less trailing whitespace
|
|
sub ax,bx ; Get length at first mismatch
|
|
cmp ax,wCount ; Make sure we mismatched at end
|
|
jne NotThisKey ; Lengths at mismatch must match
|
|
add bx,ax ; Bump pointers to end
|
|
add si,ax
|
|
mov al,es:[bx - 1] ; Get last char that should match
|
|
cmp al,ds:[si - 1] ; Does it match?
|
|
je FoundKey ; Yes
|
|
|
|
NotThisKey:
|
|
mov al, LINEFEED
|
|
mov cx, -1
|
|
repne scasb ; Scan to the end of the line
|
|
jmp FindKeyNext
|
|
|
|
NoMatch:
|
|
xor ax, ax
|
|
xor dx, dx
|
|
jmp AndReturn
|
|
FoundKey:
|
|
inc di ; Skip the '='
|
|
mov ax, di ; Return the pointer
|
|
mov dx, es
|
|
AndReturn:
|
|
pop ds
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* MyStrlen - returns length of a string excluding trailing spaces and CR
|
|
*
|
|
* Paremeters:
|
|
* ES:DI pointer to string
|
|
*
|
|
* Returns:
|
|
* CX number of characters in string
|
|
*
|
|
*/
|
|
int
|
|
MyStrlen()
|
|
{
|
|
_asm {
|
|
; SPACE, CR, NULL never in DBCS lead byte, so we are safe here
|
|
push ax
|
|
mov cx, di ; CX = start of string
|
|
dec di
|
|
str1:
|
|
inc di
|
|
mov al, es:[di] ; Get next character
|
|
cmp al, CR
|
|
ja str1 ; Not CR or NULL
|
|
str2:
|
|
cmp di, cx ; Back at the start?
|
|
jbe str3 ; yes
|
|
dec di ; Previous character
|
|
cmp byte ptr es:[di], SPACE
|
|
je str2 ; skip spaces
|
|
inc di ; Back to CR or NULL
|
|
str3:
|
|
cmp es:[di], al
|
|
je maybe_in_code ; PMODE hack
|
|
mov es:[di], al ; Zap trailing spaces
|
|
maybe_in_code:
|
|
neg cx ; Calculate length
|
|
add cx, di
|
|
pop ax
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Cstrlen - returns length of a string excluding trailing spaces and CR
|
|
* This is a C callable interface to MyStrLen
|
|
*
|
|
* Paremeters:
|
|
* lp pointer to string
|
|
*
|
|
* Returns:
|
|
* number of characters in string
|
|
*
|
|
*/
|
|
int
|
|
Cstrlen(lp)
|
|
LPSTR lp;
|
|
{
|
|
_asm {
|
|
xor di, di ; Persuade compiler to save DI
|
|
les di, lp
|
|
call MyStrlen
|
|
mov ax, cx
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* strcmpi - internal case insensitive string compare
|
|
*
|
|
* Parameters:
|
|
* ES:DI & DS:SI Strings to be compared
|
|
* BL Character to terminate on
|
|
* DS:SI is null terminated
|
|
*
|
|
* Returns:
|
|
* ZF indicates strings equal
|
|
* AX pointer to next character in ES:DI string
|
|
* or failed character in case of mismatch
|
|
*/
|
|
void
|
|
strcmpi()
|
|
{
|
|
_asm {
|
|
#ifdef FE_SB
|
|
;Apr.26,1990 by AkiraK
|
|
; Copied directly from USERPRO.ASM
|
|
sti_l1:
|
|
mov al,es:[di]
|
|
cmp al,bl
|
|
jz sti_s1
|
|
|
|
call FarMyLower
|
|
mov cl,al
|
|
|
|
mov al,ds:[si]
|
|
call FarMyLower
|
|
|
|
inc si
|
|
inc di
|
|
|
|
cmp al,cl
|
|
jnz sti_exit
|
|
|
|
call FarMyIsDBCSLeadByte
|
|
jc sti_l1
|
|
|
|
mov al,es:[di]
|
|
cmp al,ds:[si]
|
|
jnz sti_exit
|
|
|
|
inc si
|
|
inc di
|
|
jmp short sti_l1
|
|
|
|
sti_s1:
|
|
mov al,ds:[si]
|
|
or al,al
|
|
sti_exit:
|
|
mov ax, di
|
|
#else
|
|
stci10:
|
|
mov al,es:[di] ; Get next character
|
|
cmp al,bl ; Character to terminate on?
|
|
jnz stci15 ; no, compare it
|
|
mov al,[si] ; yes, strings equal if at end
|
|
or al,al
|
|
jmp stciex
|
|
stci15:
|
|
call FarMyLower ; Ensure both characters lower case
|
|
mov cl,[si]
|
|
xchg al,cl
|
|
call FarMyLower
|
|
inc si
|
|
inc di
|
|
cmp al,cl ; Still matching chars?
|
|
jz stci10 ; Yes, go try the next char.
|
|
stciex:
|
|
mov ax,di ; Return pointer to next character
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* BufferInit
|
|
*
|
|
* Parameters:
|
|
* pProInfo Pointer to structure describing an INI file
|
|
* OpenFlags READ_WRITE if we are writing to the file
|
|
*
|
|
* Returns:
|
|
* Pointer to start of buffer on success
|
|
* (LPSTR)0 Failure
|
|
*
|
|
* Open or create the INI file as necessary
|
|
* Get a buffer in memory for the file
|
|
* Read the INI file into the buffer
|
|
* Strip unwanted spaces comments and ^Zs from the buffer
|
|
*/
|
|
LPSTR _fastcall
|
|
BufferInit(pProInfo, OpenFlags)
|
|
PROINFO *pProInfo;
|
|
int OpenFlags;
|
|
{
|
|
LPSTR BufAddr;
|
|
long llen;
|
|
unsigned short len;
|
|
int fh;
|
|
int hNew;
|
|
BYTE byLastDrive; /* Cache last drive read from */
|
|
|
|
/* Ensure we have a handle for the buffer */
|
|
if ( pProInfo->hBuffer == 0 )
|
|
return(0L);
|
|
/* If the buffer is already filled, return */
|
|
if ( (BufAddr = LockBuffer(pProInfo)) != (LPSTR)NULL )
|
|
return(BufAddr);
|
|
|
|
pProInfo->ProFlags = 0;
|
|
|
|
/* Remember the last drive read from to see if we have to reread
|
|
* the cluster size.
|
|
*/
|
|
byLastDrive = *pProInfo->ProBuf.szPathName;
|
|
|
|
if ( pProInfo == &PrivateProInfo ) {
|
|
/* Open a PRIVATE profile */
|
|
fh = OpenFile(pProInfo->lpProFile, &pProInfo->ProBuf, READ_WRITE+OF_SHARE_DENY_WRITE);
|
|
if ( fh == -1 ) {
|
|
/* Attempt to open for read. */
|
|
if ( !OpenFlags ){
|
|
pProInfo->ProFlags |= PROREADONLY;
|
|
fh = OpenFile(pProInfo->lpProFile, &pProInfo->ProBuf, READ+OF_SHARE_DENY_WRITE);
|
|
/* If this fails, try compatibility mode. */
|
|
if ( (fh == -1) && (pProInfo->ProBuf.nErrCode == SHARINGVIOLATION) ){
|
|
fh = OpenFile(pProInfo->lpProFile, &pProInfo->ProBuf, READ);
|
|
}
|
|
}else{
|
|
/* If the open failed and we are writing, silently create the file.
|
|
* If the open failed because of sharing violation, try compatibility mode instead.
|
|
*/
|
|
if ( pProInfo->ProBuf.nErrCode != SHARINGVIOLATION ){
|
|
OpenFlags |= OF_CREATE;
|
|
}
|
|
fh = OpenFile(pProInfo->lpProFile, &pProInfo->ProBuf, OpenFlags);
|
|
}
|
|
}
|
|
} else {
|
|
/* Open WIN.INI */
|
|
if ( OpenFlags )
|
|
OpenFlags |= OF_CREATE;
|
|
if ( pProInfo->ProBuf.cBytes ) {
|
|
/* If previously found, reopen, don't create */
|
|
OpenFlags |= OF_REOPEN+OF_PROMPT|OF_CANCEL|OF_SHARE_DENY_WRITE;
|
|
OpenFlags &= ~OF_CREATE;
|
|
}
|
|
fh = OpenFile(pProInfo->lpProFile, &pProInfo->ProBuf, OpenFlags|READ_WRITE);
|
|
if ( (fh == -1) && !(OpenFlags & (READ_WRITE|OF_CREATE)) ) {
|
|
pProInfo->ProFlags |= PROREADONLY;
|
|
fh = OpenFile(pProInfo->lpProFile, &pProInfo->ProBuf, OpenFlags+OF_SHARE_DENY_WRITE);
|
|
}
|
|
/* Sharing violation. Let's try compatibility mode. */
|
|
if ( (fh == -1) && ( pProInfo->ProBuf.nErrCode == SHARINGVIOLATION ) ){
|
|
OpenFlags &= ~OF_SHARE_DENY_WRITE;
|
|
fh = OpenFile(pProInfo->lpProFile, &pProInfo->ProBuf, OpenFlags);
|
|
}
|
|
}
|
|
pProInfo->FileHandle = fh;
|
|
|
|
/* If we are using a different drive than the last call or this is
|
|
* the first time, clear cluster size so we reread it on next
|
|
* call to WriteString.
|
|
*/
|
|
if (byLastDrive != *pProInfo->ProBuf.szPathName)
|
|
pProInfo->wClusterSize = 0;
|
|
|
|
if ( fh == -1 )
|
|
goto ReturnNull;
|
|
|
|
/* Seek to end of file, allow for CR, LF and NULL */
|
|
llen = _llseek(fh, 0L, 2);
|
|
if (!llen)
|
|
pProInfo->ProFlags |= PRO_CREATED;
|
|
llen += 3;
|
|
if ( llen > MAXBUFLEN )
|
|
llen = MAXBUFLEN; /* Limit to plenty less than 64k */
|
|
|
|
/* Now get a buffer */
|
|
hNew = IGlobalReAlloc(pProInfo->hBuffer, llen, GMEM_ZEROINIT);
|
|
if ( !hNew ) {
|
|
ReturnNull:
|
|
return( pProInfo->lpBuffer = (LPSTR)0 );
|
|
}
|
|
|
|
/* And now read in the file */
|
|
pProInfo->hBuffer = hNew;
|
|
LockBuffer(pProInfo);
|
|
_llseek(fh, 0L, 0); /* Seek to beginning of file */
|
|
*(int _far *)pProInfo->lpBuffer = 0x2020; /* Bogus spaces */
|
|
|
|
len = _lread(fh, pProInfo->lpBuffer, (short)llen-3);
|
|
if ( len == -1 ) {
|
|
UnlockBuffer(pProInfo);
|
|
return( FreeBuffer(pProInfo) );
|
|
}
|
|
if ( len < 2 )
|
|
len = 2; /* Prevent faults in PackBuffer */
|
|
return( PackBuffer(pProInfo, len, OpenFlags & READ_WRITE) );
|
|
}
|
|
|
|
|
|
/*
|
|
* LockBuffer - Lock the buffer containing the file. Make it
|
|
* moveable and non-discardable.
|
|
* Instead of locking the buffer, we're just going to make it
|
|
* non-discardable and moveable. This is preferable to locking it
|
|
* because all we really care about is that it doesn't get discarded.
|
|
*
|
|
* Parameter:
|
|
* pProInfo Pointer to info describing INI file
|
|
*
|
|
* Returns:
|
|
* LPSTR Pointer to buffer containing file
|
|
*/
|
|
LPSTR _fastcall
|
|
LockBuffer(pProInfo)
|
|
PROINFO *pProInfo;
|
|
{
|
|
/* We only have to lock the block if it's marked dirty. Otherwise
|
|
* it's already unlocked.
|
|
*/
|
|
if (!(pProInfo->ProFlags & PROUNCLEAN))
|
|
{
|
|
/* Make the block non-discardable */
|
|
IGlobalReAlloc(pProInfo->hBuffer, 0L,
|
|
GMEM_MODIFY + GMEM_MOVEABLE);
|
|
|
|
/* All we need here is to dereference the handle. Since
|
|
* this block is now non-discardable, this is all that
|
|
* IGlobalLock() really does.
|
|
*/
|
|
pProInfo->lpBuffer = IGlobalLock(pProInfo->hBuffer);
|
|
IGlobalUnlock(pProInfo->hBuffer);
|
|
}
|
|
|
|
return pProInfo->lpBuffer;
|
|
}
|
|
|
|
|
|
/*
|
|
* UnlockBuffer - unlock the buffer, make it discardable and close the file.
|
|
* We don't really have to unlock the buffer (we weren't before anyway
|
|
* even though the comment says so)
|
|
*
|
|
* Parameter:
|
|
* pProInfo Pointer to info describing INI file
|
|
*
|
|
* Returns:
|
|
* nothing
|
|
*/
|
|
void _fastcall
|
|
UnlockBuffer(pProInfo)
|
|
PROINFO *pProInfo;
|
|
{
|
|
int fh;
|
|
|
|
if (!(pProInfo->ProFlags & PROUNCLEAN))
|
|
IGlobalReAlloc(pProInfo->hBuffer, 0L, GMEM_DISCARDABLE+GMEM_MODIFY);
|
|
fh = pProInfo->FileHandle;
|
|
pProInfo->FileHandle = -1;
|
|
if (fh != -1)
|
|
_lclose(fh);
|
|
}
|
|
|
|
|
|
/*
|
|
* FreeBuffer - discards the CONTENTS of a buffer containing an INI file
|
|
*
|
|
* Parameter:
|
|
* pProInfo Pointer to info describing INI file
|
|
*
|
|
* Returns:
|
|
* (LPSTR)0
|
|
*/
|
|
LPSTR _fastcall
|
|
FreeBuffer(pProInfo)
|
|
PROINFO *pProInfo;
|
|
{
|
|
if ( pProInfo->ProFlags & PROUNCLEAN )
|
|
WriteOutProfiles();
|
|
/* Make the buffer discardable */
|
|
IGlobalReAlloc(pProInfo->hBuffer, 0L, GMEM_DISCARDABLE+GMEM_MODIFY);
|
|
|
|
/* Make it zero length, shared, moveable and below the line */
|
|
IGlobalReAlloc(pProInfo->hBuffer, 0L, GMEM_MOVEABLE);
|
|
|
|
pProInfo->ProFlags = 0;
|
|
return( pProInfo->lpBuffer = (LPSTR)0 );
|
|
}
|
|
|
|
|
|
/*
|
|
* PackBuffer - strip blanks comments and ^Zs from an INI file
|
|
*
|
|
* Parameters:
|
|
* pProInfo Pointer to info describing INI file
|
|
* Count Number of characters in the buffer
|
|
* writing Flag indicating we are writing to the file
|
|
*
|
|
* Returns:
|
|
* LPSTR Pointer to the packed buffer
|
|
*
|
|
* NOTE: The use of Count here is DUMB. We should stick a NULL
|
|
* at the end, check for it and toss all the checks on Count.
|
|
*/
|
|
LPSTR _fastcall
|
|
PackBuffer(pProInfo, Count, fKeepComments)
|
|
PROINFO *pProInfo;
|
|
int Count;
|
|
int fKeepComments;
|
|
{
|
|
LPSTR Buffer;
|
|
char BASED_ON_LP(Buffer) *psrc;
|
|
char BASED_ON_LP(Buffer) *pdst;
|
|
char BASED_ON_LP(Buffer) *LastValid;
|
|
char nextc;
|
|
|
|
Buffer = pProInfo->lpBuffer;
|
|
psrc = pdst = (char BASED_ON_LP(Buffer)*)(WORD)(DWORD)Buffer;
|
|
|
|
if ( WinFlags & WF_PMODE )
|
|
fKeepComments = 1;
|
|
|
|
if ( fKeepComments )
|
|
pProInfo->ProFlags |= PROCOMMENTS;
|
|
|
|
while ( Count ) {
|
|
/* Strip leading spaces and tabs */
|
|
nextc = *psrc;
|
|
if ( nextc == SPACE || nextc == TAB ) {
|
|
/* TAB or SPACE never in lead byte of DBCS, so loop is safe */
|
|
Count--;
|
|
psrc++;
|
|
continue;
|
|
}
|
|
|
|
/* Process non-blanks */
|
|
LastValid = pdst;
|
|
do {
|
|
nextc = *psrc++;
|
|
Count--;
|
|
/* Strip comments if real mode and not writing */
|
|
if ( nextc == ';' && !fKeepComments ) {
|
|
while ( Count && nextc != LINEFEED ) {
|
|
/* LINEFEED never in lead byte of DBCS, so loop is safe */
|
|
nextc = *psrc++;
|
|
Count--;
|
|
}
|
|
break;
|
|
}
|
|
/* Copy this character */
|
|
*pdst++ = nextc;
|
|
#ifdef FE_SB
|
|
if ( Count && CIsDBCSLeadByte(nextc) ) {
|
|
*pdst++ = *psrc++;
|
|
Count--;
|
|
}
|
|
#endif
|
|
if ( nextc == '=' ) {
|
|
/* Skip preceeding spaces and tabs */
|
|
pdst = LastValid;
|
|
/* New home for the '=' */
|
|
*pdst++ = nextc;
|
|
/* Skip spaces and tabs again */
|
|
while ( Count ) {
|
|
nextc = *psrc;
|
|
if ( nextc != SPACE && nextc != TAB )
|
|
break;
|
|
Count--;
|
|
psrc++;
|
|
}
|
|
/* Copy remainder of line */
|
|
while ( Count ) {
|
|
Count--;
|
|
/* LINEFEED never in lead byte of DBCS, so loop is safe */
|
|
if ( (*pdst++ = *psrc++) == LINEFEED )
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* End of file or line? */
|
|
if ( Count == 0 || nextc == LINEFEED )
|
|
break;
|
|
|
|
/* Strip trailing spaces */
|
|
if ( nextc == SPACE || nextc == TAB )
|
|
continue;
|
|
|
|
LastValid = pdst;
|
|
} while ( Count );
|
|
/* Here if end of line or file */
|
|
}
|
|
/* Here if end of file; skip trailing ^Zs */
|
|
for ( ; ; ) {
|
|
if ( pdst == Buffer )
|
|
break;
|
|
if ( *--pdst != CTRLZ ) {
|
|
pdst++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*pdst++ = CR;
|
|
*pdst++ = LINEFEED;
|
|
*pdst++ = 0;
|
|
|
|
IGlobalUnlock(pProInfo->hBuffer);
|
|
IGlobalReAlloc(pProInfo->hBuffer, (long)((LPSTR)pdst - Buffer), 0);
|
|
Buffer = LockBuffer(pProInfo);
|
|
pProInfo->BufferLen = (unsigned)pdst;
|
|
return(Buffer);
|
|
}
|
|
|
|
|
|
#ifdef FE_SB
|
|
/*
|
|
* C interface to FarMyIsDBCSLeadByte
|
|
*
|
|
* Parameter:
|
|
* c character to be tested
|
|
*
|
|
* Returns:
|
|
* 1 It is a lead byte
|
|
* 0 It isn't a lead byte
|
|
*/
|
|
CIsDBCSLeadByte(c)
|
|
char c;
|
|
{
|
|
_asm {
|
|
mov al, c
|
|
call FarMyIsDBCSLeadByte
|
|
cmc ; Set carry if lead byte
|
|
mov ax, 0 ; Set return value to 0, preserve flags
|
|
adc al, 0 ; Set to one if carry set
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* WriteString
|
|
*
|
|
* Adds/deletes sections/lines in an INI file
|
|
*
|
|
* Parameters:
|
|
* pProInfo pointer to info on the file
|
|
* lpSection pointer to the section name we want
|
|
* lpKeyName key name to change or add
|
|
* NULL means delete section
|
|
* lpString string to add to file
|
|
* NULL means delete line
|
|
*
|
|
* Returns:
|
|
* bResult Success/Fail
|
|
*/
|
|
WriteString(pProInfo, lpSection, lpKeyName, lpString)
|
|
PROINFO *pProInfo;
|
|
LPSTR lpSection;
|
|
LPSTR lpKeyName;
|
|
LPSTR lpString;
|
|
{
|
|
LPSTR ptrTmp;
|
|
short WhatIsMissing;
|
|
short nchars;
|
|
short fh;
|
|
long fp;
|
|
short SectLen = 0;
|
|
short KeyLen = 0;
|
|
short ResultLen = 0;
|
|
SEGMENT BufferSeg;
|
|
register char BASED_ON_SEG(BufferSeg) *bp;
|
|
|
|
/* Debugging noise */
|
|
/* Assert that we have something to do! */
|
|
if ( (SEGMENT)lpSection == NULL && (SEGMENT)lpKeyName == NULL
|
|
&& (SEGMENT)lpString == NULL ) {
|
|
FreeBuffer(pProInfo); /* FEATURE! */
|
|
return(0);
|
|
}
|
|
|
|
/* If buffer does not already contain comments, free it */
|
|
if ( !(pProInfo->ProFlags & PROCOMMENTS) )
|
|
FreeBuffer(pProInfo);
|
|
|
|
/* Read the file into a buffer, preserving comments */
|
|
ptrTmp = BufferInit(pProInfo, READ_WRITE);
|
|
if ( !ptrTmp )
|
|
return(0);
|
|
|
|
/* Abort now if read only file */
|
|
if ( pProInfo->ProFlags & PROREADONLY )
|
|
goto GrodyError;
|
|
|
|
/* Set bp to point in buffer where we will add stuff */
|
|
BufferSeg = (SEGMENT)ptrTmp;
|
|
bp = pProInfo->BufferLen + (char BASED_ON_SEG(BufferSeg)*)
|
|
(WORD)(DWORD)ptrTmp - 1;
|
|
|
|
/*
|
|
* Now see what we have to do to the file by
|
|
* searching for section and keyname.
|
|
*/
|
|
nchars = 0;
|
|
|
|
/* See if section exists */
|
|
if ( !(ptrTmp = FindSection(ptrTmp, lpSection)) ) {
|
|
/* No Section. If deleting anything, stop now */
|
|
if ( !lpKeyName || !lpString )
|
|
goto NothingToDo;
|
|
/* Need to add section and keyname */
|
|
WhatIsMissing = NOSECTION;
|
|
} else {
|
|
/* Found the section, save pointer to it */
|
|
bp = (char BASED_ON_SEG(BufferSeg)*)(WORD)(DWORD)ptrTmp;
|
|
/* If lpKeyName NULL, delete the section */
|
|
if ( !lpKeyName ) {
|
|
WhatIsMissing = REMOVESECTION;
|
|
} else {
|
|
/* Look for the keyname in the section */
|
|
if ( !(ptrTmp = FindKey(bp, lpKeyName)) ) {
|
|
/* No KeyName, stop if deleting it */
|
|
if ( !lpString )
|
|
goto NothingToDo;
|
|
WhatIsMissing = NOKEY;
|
|
/* Insert new keyname
|
|
at the end of the section */
|
|
while ( *bp && (*bp != SECT_LEFT || *(bp-1) != LINEFEED) )
|
|
bp++;
|
|
} else {
|
|
/* Found the keyname, save pointer */
|
|
bp = (char BASED_ON_SEG(BufferSeg)*)
|
|
(WORD)(DWORD)ptrTmp;
|
|
/* NULL lpString means delete it */
|
|
if ( !lpString )
|
|
WhatIsMissing = REMOVEKEY;
|
|
else {
|
|
/*
|
|
* Compare the existing string with the
|
|
* string we are supposed to replace it
|
|
* with. If they are the same, there
|
|
* is no need to rewrite the file, so
|
|
* we abort now.
|
|
*/
|
|
if ( !IsItTheSame((LPSTR)bp, lpString) )
|
|
goto NothingToDo;
|
|
|
|
/*
|
|
* Count characters in old result.
|
|
* The file will be shrinking by
|
|
* this many characters.
|
|
*/
|
|
while ( *bp++ != CR )
|
|
nchars--;
|
|
bp = (char BASED_ON_SEG(BufferSeg)*)
|
|
(WORD)(DWORD)ptrTmp;
|
|
WhatIsMissing = NEWRESULT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we will be adding to the file, grow the buffer
|
|
* to the size we will need, then make an appropriate
|
|
* sized hole in the buffer.
|
|
*/
|
|
switch ( WhatIsMissing ) {
|
|
|
|
case NOSECTION:
|
|
/* Need to add section */
|
|
SectLen = Cstrlen(lpSection);
|
|
nchars = SectLen + 4; /* for []<CR><LF> */
|
|
/* Fall through for KeyName and result */
|
|
|
|
case NOKEY:
|
|
/* Need to add key name */
|
|
KeyLen = Cstrlen(lpKeyName);
|
|
nchars += KeyLen + 3; /* for =<CR><LF> */
|
|
|
|
/* For new key or section, skip back to previous line */
|
|
while ( bp > pProInfo->lpBuffer ) {
|
|
bp--;
|
|
if ( *bp != CR && *bp != LINEFEED )
|
|
break;
|
|
}
|
|
if ( bp != pProInfo->lpBuffer )
|
|
bp += 3;
|
|
/* Fall through for result */
|
|
|
|
/* If not at start of buffer, add room for extra CR/LF */
|
|
if ((WORD)bp && WhatIsMissing == NOSECTION)
|
|
nchars += 2;
|
|
|
|
case NEWRESULT:
|
|
/* Need to change/add result */
|
|
/* nchars may be -<current length of result> */
|
|
ResultLen = Cstrlen(lpString);
|
|
nchars += ResultLen;
|
|
|
|
/* Grow the buffer if necessary */
|
|
if ( nchars > 0 ) {
|
|
IGlobalUnlock(pProInfo->hBuffer);
|
|
|
|
fp = nchars + (long)pProInfo->BufferLen;
|
|
/* Ensure buffer will be plenty less than 64k */
|
|
/* and grow to new size */
|
|
if ( fp > MAXBUFLEN || !IGlobalReAlloc(pProInfo->hBuffer, fp, 0) ) {
|
|
/* Undo above Unlock */
|
|
IGlobalLock(pProInfo->hBuffer);
|
|
goto GrodyError;
|
|
}
|
|
pProInfo->lpBuffer = IGlobalLock(pProInfo->hBuffer);
|
|
BufferSeg = (SEGMENT)pProInfo->lpBuffer;
|
|
}
|
|
|
|
/* In order to fix bug #4672 and other ugly things
|
|
* that happen when we run out of disk space,
|
|
* we want to see if there is room to write the
|
|
* buffer. We know that the file can actually only
|
|
* grow on cluster boundaries, but rather than get
|
|
* the cluster size. If we don't have the cluster
|
|
* size yet, we have to get it from DOS.
|
|
*/
|
|
if (!pProInfo->wClusterSize)
|
|
{
|
|
WORD wTemp;
|
|
|
|
/* Get drive letter */
|
|
wTemp = *pProInfo->ProBuf.szPathName - 'A' + 1;
|
|
_asm
|
|
{
|
|
mov ah,1ch ;Drive parameters
|
|
mov dl,BYTE PTR wTemp
|
|
push ds
|
|
int 21h
|
|
pop ds
|
|
cmp al,0ffh ;Error?
|
|
jnz DPOk ;No
|
|
mov al,1
|
|
mov cx,512 ;Default
|
|
DPOk: cbw ;Secs per cluster WORD
|
|
mul cx ;AX = bytes/cluster
|
|
mov wTemp,ax
|
|
}
|
|
if (!wTemp)
|
|
pProInfo->wClusterSize = 512;
|
|
else
|
|
pProInfo->wClusterSize = wTemp;
|
|
}
|
|
|
|
/* Now see if we're going past a cluster length */
|
|
if ((pProInfo->ProFlags & PRO_CREATED) ||
|
|
(((pProInfo->BufferLen + nchars) ^ pProInfo->BufferLen)
|
|
& ~(pProInfo->wClusterSize - 1)))
|
|
{
|
|
int fh;
|
|
|
|
/* Make sure that we only do this once for a newly-
|
|
* created file because this will handle the
|
|
* growing to one cluster case.
|
|
*/
|
|
pProInfo->ProFlags &= ~PRO_CREATED;
|
|
fh = pProInfo->FileHandle;
|
|
|
|
/* Make sure the file is open and exists. If not,
|
|
* we have to open the file. We are guaranteed
|
|
* at least that the file exists in this case.
|
|
* Note that UnlockBuffer closes the file
|
|
* that we open here.
|
|
*/
|
|
if (fh == -1)
|
|
{
|
|
fh = OpenFile(pProInfo->lpProFile,&pProInfo->ProBuf,OF_REOPEN+READ_WRITE+OF_SHARE_DENY_WRITE);
|
|
/* Sharing violation. Let's try compabitility mode. */
|
|
if ( (fh == -1) && (pProInfo->ProBuf.nErrCode == SHARINGVIOLATION ) ){
|
|
fh = OpenFile(pProInfo->lpProFile,&pProInfo->ProBuf,OF_REOPEN+READ_WRITE);
|
|
}
|
|
pProInfo->FileHandle = fh;
|
|
}
|
|
|
|
/* Try to grow the file to the right length */
|
|
if(_llseek(fh, pProInfo->BufferLen + nchars, 0) !=
|
|
pProInfo->BufferLen + nchars ||
|
|
_lwrite(fh, " ", 1) != 1)
|
|
goto GrodyError;
|
|
}
|
|
|
|
/* Now, make room in the buffer for this new stuff */
|
|
if ( nchars )
|
|
MakeRoom((LPSTR)bp, nchars, &pProInfo->BufferLen);
|
|
|
|
/* Now copy in the new info */
|
|
switch ( WhatIsMissing ) {
|
|
case NOSECTION:
|
|
/* Create the new section */
|
|
(int)bp = InsertSection((LPSTR)bp, lpSection, SectLen);
|
|
/* FALL THROUGH */
|
|
|
|
case NOKEY:
|
|
(int)bp = InsertKey((LPSTR)bp, lpKeyName, KeyLen);
|
|
/* FALL THROUGH */
|
|
|
|
case NEWRESULT:
|
|
(int) bp = InsertResult((LPSTR)bp, lpString, ResultLen);
|
|
}
|
|
break;
|
|
|
|
/* Handle deleting sections or KeyNames */
|
|
case REMOVESECTION:
|
|
DeleteSection((LPSTR)bp, pProInfo);
|
|
break;
|
|
|
|
case REMOVEKEY:
|
|
DeleteKey((LPSTR)bp, pProInfo);
|
|
break;
|
|
}
|
|
|
|
pProInfo->ProFlags |= PROUNCLEAN;
|
|
fProfileDirty = 1;
|
|
|
|
NothingToDo:
|
|
UnlockBuffer(pProInfo);
|
|
return(1);
|
|
|
|
/* I really hate the GOTO, but in order to clean up, this is much
|
|
* more efficient...
|
|
*/
|
|
GrodyError:
|
|
UnlockBuffer(pProInfo);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* WriteOutProfiles
|
|
*
|
|
* Called on a task switch or at exit time
|
|
*
|
|
* If we have a dirty profile buffer, write it.
|
|
*/
|
|
void API
|
|
WriteOutProfiles(void)
|
|
{
|
|
LPSTR ptrTmp;
|
|
int fh;
|
|
PROINFO *pProInfo;
|
|
int nwritten;
|
|
|
|
/* Make sure that we don't get called through a DOS call. This
|
|
* flag is tested in I21ENTRY.ASM in the Int 21h hook to see
|
|
* if the profiles should be flushed.
|
|
*/
|
|
++fWriteOutProfilesReenter;
|
|
|
|
for ( pProInfo = &WinIniInfo; ; pProInfo = &PrivateProInfo ) {
|
|
if ( !(pProInfo->ProFlags & PROUNCLEAN) )
|
|
goto NoWrite;
|
|
if (
|
|
/* Try read/write with sharing flags, then try compabitility mode, then try to create it. */
|
|
( (fh = OpenFile(NULL, &pProInfo->ProBuf, OF_REOPEN | READ_WRITE | OF_SHARE_DENY_WRITE)) == -1)
|
|
&& ( (fh = OpenFile(NULL, &pProInfo->ProBuf, OF_REOPEN | READ_WRITE)) == -1)
|
|
&& ( (fh = OpenFile(NULL, &pProInfo->ProBuf, OF_REOPEN | OF_CREATE)) == -1) ){
|
|
goto NoWrite;
|
|
}
|
|
pProInfo->FileHandle = fh;
|
|
|
|
/* Finally write the file */
|
|
ptrTmp = pProInfo->lpBuffer;
|
|
nwritten = _lwrite(fh, ptrTmp, pProInfo->BufferLen-3);
|
|
if ( nwritten == pProInfo->BufferLen-3 ) {
|
|
_lwrite(fh, ptrTmp, 0); /* Mark end of file */
|
|
pProInfo->ProFlags &= ~(PROUNCLEAN | PRO_CREATED);
|
|
UnlockBuffer(pProInfo);
|
|
} else {
|
|
_lclose(fh);
|
|
}
|
|
NoWrite:
|
|
if ( pProInfo == &PrivateProInfo )
|
|
break;
|
|
}
|
|
fProfileDirty = 0;
|
|
|
|
--fWriteOutProfilesReenter;
|
|
}
|
|
|
|
|
|
/*
|
|
* See if two character strings are the same.
|
|
* Special routine since one is terminated with <CR>.
|
|
* Returns zero if the strings match.
|
|
*/
|
|
IsItTheSame(CRstring, NullString)
|
|
LPSTR CRstring;
|
|
LPSTR NullString;
|
|
{
|
|
_asm {
|
|
push ds
|
|
les di, CRstring ; CR terminated string
|
|
lds si, NullString ; Null terminated string
|
|
xor ah, ah ; High byte of return value
|
|
stci10:
|
|
mov al,es:[di] ; Get next character
|
|
cmp al,0Dh ; CR?
|
|
jnz stci15 ; no, compare it
|
|
mov al,[si] ; yes, strings equal if at end
|
|
jmp stciex
|
|
stci15:
|
|
mov cl,[si]
|
|
inc si
|
|
inc di
|
|
cmp al,cl ; Still matching chars?
|
|
jz stci10 ; Yes, go try the next char.
|
|
mov al, 1 ; Didn't match
|
|
stciex:
|
|
pop ds
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Create or close a hole in the buffer.
|
|
* Used to create room for a new section,
|
|
* keyname or result and to remove unwanted
|
|
* sections, keynames or results.
|
|
*
|
|
* Parameters:
|
|
* lp position in buffer to add/remove characters
|
|
* nchars number of characters to add/remove
|
|
* pAdjust pointer to variable containing current
|
|
* size of the buffer
|
|
*
|
|
* Side effects:
|
|
* *pAdjust is changed by nchars
|
|
*
|
|
* Returns:
|
|
* nothing
|
|
*/
|
|
MakeRoom(lp, nChars, pAdjust)
|
|
LPSTR lp;
|
|
short nChars;
|
|
int *pAdjust;
|
|
{
|
|
short BufLen = *pAdjust;
|
|
|
|
if ( nChars < 0 )
|
|
_asm {
|
|
push ds
|
|
les di, lp ; Where characters will be taken
|
|
push es
|
|
pop ds
|
|
mov si, di ; End of area
|
|
sub si, nChars ; Remember nChars is negative
|
|
mov cx, BufLen
|
|
sub cx, si ; Calculate # characters to move
|
|
cld
|
|
rep movsb ; Copy characters down
|
|
pop ds
|
|
} else _asm {
|
|
push ds
|
|
mov es, word ptr lp[2] ; Get segment to copy in
|
|
mov ds, word ptr lp[2]
|
|
mov si, BufLen ; We will be moving backwards
|
|
mov cx, si
|
|
dec si ; Adjust pointer for move
|
|
mov di, si ; so start at end of the buffer
|
|
sub cx, word ptr lp ; Number of characters to move
|
|
add di, nChars
|
|
std ; Backwards move
|
|
rep movsb ; Copy characters up
|
|
cld
|
|
pop ds
|
|
}
|
|
*pAdjust += nChars;
|
|
}
|
|
|
|
|
|
/*
|
|
* Delete a section from the buffer,
|
|
* preserving comments since they may
|
|
* relate to the next section
|
|
*
|
|
* Parameters:
|
|
* lp pointer to section returned by FindSection
|
|
* pProInfo pointer to INI file info
|
|
*
|
|
* Returns:
|
|
* nothing
|
|
*/
|
|
DeleteSection(lp, pProInfo)
|
|
LPSTR lp;
|
|
PROINFO *pProInfo;
|
|
{
|
|
int nRemoved;
|
|
char BASED_ON_LP(lp) *SectEnd;
|
|
|
|
_asm {
|
|
cld
|
|
push ds
|
|
lds si, lp
|
|
BackToStart:
|
|
dec si ; Skip backwards to start of the section
|
|
cmp ds:[si], SECT_LEFT
|
|
jne BackToStart
|
|
|
|
mov di, si
|
|
push ds
|
|
pop es ; ES:DI points to start of section
|
|
inc si ; DS:SI points to the '[', skip it
|
|
RemoveLoop:
|
|
lodsb ; Get next character in section
|
|
cmp al, ';' ; Is it a comment
|
|
jne NotComment
|
|
|
|
CopyComment:
|
|
stosb ; Save this character
|
|
cmp al, LINEFEED ; Copy to end of the line
|
|
je RemoveLoop
|
|
lodsb ; And get the next one
|
|
jmp CopyComment
|
|
|
|
NotComment:
|
|
cmp al, SECT_LEFT ; So is it the next section?
|
|
je EndSection
|
|
or al, al ; or the end of the buffer?
|
|
jne SkipLine
|
|
sub si, 2 ; Extra CR & LF at end of buffer
|
|
jmp short EndSection
|
|
|
|
SkipLine:
|
|
cmp al, LINEFEED ; Nothing interesting, so skip line
|
|
je RemoveLoop
|
|
lodsb
|
|
jmp SkipLine
|
|
|
|
EndSection:
|
|
dec si ; Back to the character
|
|
mov SectEnd, si ; where the search stopped
|
|
mov word ptr lp, di ; End of copied comments (if any)
|
|
sub si, di
|
|
mov nRemoved, si ; Number of characters removed
|
|
pop ds
|
|
}
|
|
|
|
MakeRoom(lp, -nRemoved, &pProInfo->BufferLen);
|
|
}
|
|
|
|
|
|
/*
|
|
* Delete a keyname from the buffer
|
|
*
|
|
* Parameters:
|
|
* lp pointer to keyname returned by FindKey
|
|
* pProInfo pointer to INI file info
|
|
*
|
|
* Returns:
|
|
* nothing
|
|
*/
|
|
DeleteKey(lp, pProInfo)
|
|
LPSTR lp;
|
|
PROINFO *pProInfo;
|
|
{
|
|
int nRemoved;
|
|
char BASED_ON_LP(lp) *KeyEnd;
|
|
|
|
_asm {
|
|
cld
|
|
les di, lp
|
|
BackToStart:
|
|
dec di ; Skip backwards to start of the line
|
|
cmp es:[di], LINEFEED
|
|
jne BackToStart
|
|
inc di
|
|
mov word ptr lp, di ; Save start of the line
|
|
|
|
mov cx, -1
|
|
mov al, LINEFEED
|
|
repne scasb ; Scan to end of the line
|
|
sub di, word ptr lp
|
|
mov nRemoved, di ; Length of line
|
|
}
|
|
MakeRoom(lp, -nRemoved, &pProInfo->BufferLen);
|
|
}
|
|
|
|
|
|
/*
|
|
* Insert a new section in the buffer.
|
|
* A hole has already been created for it.
|
|
* This merely copies the string, places
|
|
* '[]'s around it and a CR, LF after it.
|
|
* Returns a pointer to immediately
|
|
* after the section header in the buffer.
|
|
*
|
|
* Parameters:
|
|
* lpDest pointer to where to add the section
|
|
* lpSrc pointer to the section name
|
|
* count length of lpSrc
|
|
*/
|
|
InsertSection(lpDest, lpSrc, count)
|
|
LPSTR lpDest;
|
|
LPSTR lpSrc;
|
|
short count;
|
|
{
|
|
_asm {
|
|
cld
|
|
push ds
|
|
les di, lpDest
|
|
lds si, lpSrc
|
|
or di,di ; If at start of buffer, no prefix
|
|
jz IS_SkipPrefix
|
|
mov ax, LINEFEED SHL 8 + CR ; Prefix with CR/LF
|
|
stosw
|
|
IS_SkipPrefix:
|
|
mov al, SECT_LEFT ; '[' first
|
|
stosb
|
|
mov cx, count ; Now the section name
|
|
rep movsb
|
|
mov al, SECT_RIGHT ; and the ']'
|
|
stosb
|
|
mov ax, LINEFEED SHL 8 + CR ; finally, CR, LF
|
|
stosw
|
|
pop ds
|
|
mov ax, di ; Return pointer to char after header
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Insert a new keyname in the buffer.
|
|
* This copies the keyname and adds
|
|
* an '='. It is assumed that InsertResult()
|
|
* will terminate the line.
|
|
* A pointer to the buffer immediately after
|
|
* the '=' is returned.
|
|
*
|
|
* Parameters:
|
|
* lpDest pointer to where to add the keyname
|
|
* lpSrc pointer to the keyname
|
|
* count length of lpSrc
|
|
*/
|
|
InsertKey(lpDest, lpSrc, count)
|
|
LPSTR lpDest;
|
|
LPSTR lpSrc;
|
|
short count;
|
|
{
|
|
_asm {
|
|
cld
|
|
push ds
|
|
les di, lpDest
|
|
lds si, lpSrc
|
|
mov cx, count ; Copy the KeyName
|
|
rep movsb
|
|
mov al, '=' ; add the '='
|
|
stosb
|
|
mov ax, di ; Pointer to char after the '='
|
|
pop ds
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Add a new result string to the buffer.
|
|
* It assumes that the keyname and '=' are
|
|
* already there. This routine may be
|
|
* overwriting an existing result. The result
|
|
* is terminated with a CR, LR.
|
|
*
|
|
* Parameters:
|
|
* lpDest pointer to where to add the result
|
|
* lpSrc pointer to the result
|
|
* count length of lpSrc
|
|
*/
|
|
InsertResult(lpDest, lpSrc, count)
|
|
LPSTR lpDest;
|
|
LPSTR lpSrc;
|
|
short count;
|
|
{
|
|
_asm {
|
|
cld
|
|
push ds
|
|
les di, lpDest
|
|
lds si, lpSrc
|
|
mov cx, count ; Copy the result
|
|
rep movsb
|
|
mov ax, LINEFEED SHL 8 + CR ; finally, CR, LF
|
|
stosw ; This may overwrite existing CR, LF
|
|
mov ax, di
|
|
pop ds
|
|
}
|
|
}
|
|
|
|
/*
|
|
* GetFileAttr
|
|
*
|
|
* DOS call to Get file attributes
|
|
GetFileAttr(szFile)
|
|
LPSTR szFile;
|
|
{
|
|
_asm {
|
|
int 3
|
|
xor cx, cx ; In case of failure
|
|
lds dx, szFile
|
|
mov ax, 4300h
|
|
int 21h
|
|
mov ax, cx
|
|
}
|
|
}
|
|
*/
|