/* * 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: ;,0,,0,.... ,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 [] */ /* Fall through for KeyName and result */ case NOKEY: /* Need to add key name */ KeyLen = Cstrlen(lpKeyName); nchars += KeyLen + 3; /* for = */ /* 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 - */ 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 . * 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 } } */