///////////////////////////////////////////////////////////////////////////// // Copyright (C) 1993-1998 Microsoft Corporation. All Rights Reserved. // // MODULE: find.cpp // // PURPOSE: // #include "pch.hxx" #include "mru.h" #define NTHSTRING(p, n) (*((LPTSTR FAR *)((LPBYTE)p) + n)) #define NTHDATA(p, n) (*((LPBYTE FAR *)((LPBYTE)p) + n)) #define NUM_OVERHEAD 3 #define MAX_MRU_INDEXSTR 15 // For Binary data, we stick the size of the data at the beginning and store // the whole thing in one go. // Use this macro to get the original size of the data #define DATASIZE(p) (*((LPDWORD) p)) // Use this macro to get a pointer to the original data #define DATAPDATA(p) (p + sizeof(DWORD)) #define DATAPDATAEX(p) ((LPDWORD)(((DWORD_PTR) p) + sizeof(DWORD))) #define MAX_CHAR 126 #define BASE_CHAR TEXT('a') CMRUList::CMRUList() { m_uMax = 0; m_fFlags = 0; m_pszSubKey = 0; m_hKey = 0; m_rgpszMRU = NULL; m_pszOrder = NULL; } CMRUList::~CMRUList() { SafeMemFree(m_pszSubKey); SafeMemFree(m_pszOrder); if (m_hKey) RegCloseKey(m_hKey); FreeList(); } const TCHAR c_szRegMRU[] = _T("MRU List"); // // FUNCTION: CMRUList::CreateList() // // PURPOSE: Creates and initializes the MRUL list // // PARAMETERS: // UINT uMaxEntries // UINT fFlags // LPCSTR pszSubKey // // RETURN VALUE: // BOOL // BOOL CMRUList::CreateList(UINT uMaxEntries, UINT fFlags, LPCSTR pszSubKey) { TraceCall("CMRUList::CreateList"); return (CreateListLazy(uMaxEntries, fFlags, pszSubKey, NULL, 0, NULL)); } // // FUNCTION: CreateListLazy() // // PURPOSE: Initializes the MRU list by telling the class how many entries // to store, where they are stored, and some flags. // // PARAMETERS: // [in] uMaxEntries // [in] fFlags // [in] pszSubKey // // RETURN VALUE: // BOOL // BOOL CMRUList::CreateListLazy(UINT uMaxEntries, UINT fFlags, LPCSTR pszSubKey, const void *pData, UINT cbData, LPINT piSlot) { TCHAR szOrder[126]; DWORD dwType; DWORD cb; LPTSTR pszNewOrder; LPTSTR pszTemp; TCHAR sz[10]; LPBYTE pVal; DWORD dwDisp = 0; TraceCall("CreateList"); // Save some of this m_uMax = uMaxEntries; m_fFlags = fFlags; m_pszSubKey = PszDupA(pszSubKey); // Make sure uMax is < 126 so we don't use extended chars if (m_uMax > MAX_CHAR - BASE_CHAR) m_uMax = MAX_CHAR - BASE_CHAR; // Open the registry if (ERROR_SUCCESS != AthUserCreateKey(pszSubKey, KEY_ALL_ACCESS, &m_hKey, &dwDisp)) goto exit; // Do we already have a stored MRU Index? cb = ARRAYSIZE(szOrder); if (ERROR_SUCCESS != RegQueryValueEx(m_hKey, c_szRegMRU, NULL, &dwType, (LPBYTE) szOrder, &cb)) { // If we didn't find it then do this to initialize the list to be empty. *szOrder = 0; } // Uppercase is not allowed CharLower(szOrder); // Allocate room for the order list and the list of strings. if (!MemAlloc((LPVOID *) &m_rgpszMRU, uMaxEntries * sizeof(LPTSTR))) goto exit; ZeroMemory(m_rgpszMRU, uMaxEntries * sizeof(LPTSTR)); // Allocate the order list if (!MemAlloc((LPVOID *) &m_pszOrder, sizeof(TCHAR) * (m_uMax + 1))) goto exit; ZeroMemory(m_pszOrder, (m_uMax + 1) * sizeof(TCHAR)); // Traverse through the MRU list, adding strings to the end of the list. for (pszTemp = szOrder, pszNewOrder = m_pszOrder; ; ++pszTemp) { // Stop when we get to the end of the list sz[0] = *pszTemp; sz[1] = 0; if (!sz[0]) break; // Check if in range and if we already have used this letter if ((UINT) (sz[0] - BASE_CHAR) >= m_uMax || m_rgpszMRU[sz[0] - BASE_CHAR]) continue; // Get the value from the registry cb = 0; // First find the size if ((RegQueryValueEx(m_hKey, sz, NULL, &dwType, NULL, &cb) != ERROR_SUCCESS) || (dwType != REG_SZ)) continue; cb *= sizeof(TCHAR); if (!MemAlloc((LPVOID *) &pVal, cb)) continue; // Now really get it if (RegQueryValueEx(m_hKey, sz, NULL, &dwType, (LPBYTE) pVal, &cb) != ERROR_SUCCESS) continue; // Note that blank elements are not allowed in the list if (*((LPTSTR) pVal)) { m_rgpszMRU[sz[0] - BASE_CHAR] = (LPTSTR) pVal; *pszNewOrder++ = sz[0]; } else MemFree(pVal); } // NULL terminate the order list *pszNewOrder = '\0'; if (pData && piSlot) { // If we failed to find it, put -1 in it if (!(m_fFlags & MRU_LAZY)) { *piSlot = -1; } } exit: if (!m_rgpszMRU && m_hKey) { RegCloseKey(m_hKey); m_hKey = NULL; } return (TRUE); } BOOL CMRUList::_IsSameData(BYTE FAR *pVal, const void FAR *pData, UINT cbData) { int cbUseSize; // If there's something other than a mem compare, don't require the sizes // to be equal in order to complete. if (m_fFlags & MRU_BINARY) { if (DATASIZE(pVal) != cbData) return (FALSE); return (0 == _IMemCmp(pData, DATAPDATA(pVal), cbData)); } else { return (0 == lstrcmpi((LPCSTR) pData, (LPCSTR) DATAPDATA(pVal))); } } int CDECL CMRUList::_IMemCmp(const void *pBuf1, const void *pBuf2, size_t cb) { UINT i; const BYTE *lpb1, *lpb2; Assert(pBuf1); Assert(pBuf2); lpb1 = (const BYTE *)pBuf1; lpb2 = (const BYTE *)pBuf2; for (i=0; i < cb; i++) { if (*lpb1 > *lpb2) return 1; else if (*lpb1 < *lpb2) return -1; lpb1++; lpb2++; } return 0; } // // FUNCTION: CMRUList::FreeList() // // PURPOSE: // // PARAMETERS: // void // // RETURN VALUE: // void // void CMRUList::FreeList(void) { int i; LPBYTE *pTemp; TraceCall("CMRUList::FreeList"); if (m_fFlags & MRU_BINARY) pTemp = &NTHDATA(m_rgpszMRU, 0); else pTemp = (LPBYTE *) &NTHSTRING(m_rgpszMRU, 0); if (m_fFlags & MRU_ORDERDIRTY) { // _SaveOrder(); } for (i = m_uMax - 1; i >= 0; --i, ++pTemp) { SafeMemFree(*pTemp); } SafeMemFree(m_rgpszMRU); } // // FUNCTION: CMRUList::AddString() // // PURPOSE: Writes the specified string into the MRU list // // PARAMETERS: // [in] pszString - string to add // // RETURN VALUE: // Returns -1 if it was not inserted. // int CMRUList::AddString(LPCSTR pszString) { TCHAR cFirst; int iSlot = -1; LPTSTR *ppszTemp; LPTSTR pszTemp = 0; int i; BOOL fShouldWrite; TraceCall("CMRUList::AddString"); fShouldWrite = !(m_fFlags & MRU_CACHEWRITE); // Check to see if the string already exists in the list for (i = 0, ppszTemp = m_rgpszMRU; (UINT) i < m_uMax; i++, ppszTemp++) { if (*ppszTemp) { if (0 == lstrcmpi(pszString, (LPCTSTR) *ppszTemp)) { // Found it, so don't do the write cFirst = i + BASE_CHAR; iSlot = i; goto found; } } } // Attempt to find an unsed entry. Count up the used entries at the same // time. for (i = 0, ppszTemp = m_rgpszMRU; ; i++, ppszTemp++) { // If we hit the end of the list if ((UINT) i >= m_uMax) { // Use the entry at the end of the order array cFirst = m_pszOrder[m_uMax - 1]; ppszTemp = &(m_rgpszMRU[cFirst - BASE_CHAR]); break; } // Is the entry empty? if (!*ppszTemp) { cFirst = i + BASE_CHAR; break; } } // Copy the string if (_SetPtr(ppszTemp, pszString)) { TCHAR szTemp[2]; iSlot = (int) (cFirst - BASE_CHAR); szTemp[0] = cFirst; szTemp[1] = '\0'; RegSetValueEx(m_hKey, szTemp, 0L, REG_SZ, (CONST BYTE *) pszString, sizeof(TCHAR) * (lstrlen(pszString) + 1)); fShouldWrite = TRUE; } found: // Remove any previous reference to cFirst pszTemp = StrChr(m_pszOrder, cFirst); if (pszTemp) { DWORD cchSize = (lstrlen(pszTemp) + 1); StrCpyN(pszTemp, (pszTemp + 1), cchSize); } // If we moved or inserted, update the order array if (iSlot != -1) { // Shift everything over and put cFirst at the front MoveMemory(m_pszOrder + 1, m_pszOrder, m_uMax * sizeof(TCHAR)); m_pszOrder[0] = cFirst; } // If we need to write, do it if (fShouldWrite) { RegSetValueEx(m_hKey, c_szRegMRU, 0L, REG_SZ, (CONST BYTE *) m_pszOrder, sizeof(TCHAR) * (lstrlen(m_pszOrder) + 1)); m_fFlags &= ~MRU_ORDERDIRTY; } else { m_fFlags |= MRU_ORDERDIRTY; } return (iSlot); } // // FUNCTION: CMRUList::RemoveString() // // PURPOSE: Removes the specified string from the MRU list // // PARAMETERS: // [in] pszString - string to remove // // RETURN VALUE: // Returns -1 if it was not removed. // int CMRUList::RemoveString(LPCSTR pszString) { INT iRet = -1; BOOL fShouldWrite = FALSE; int i = 0; LPTSTR * ppszTemp = NULL; TCHAR cFirst = '\0'; LPTSTR pszTemp = 0; TCHAR szTemp[2]; TraceCall("CMRUList::RemoveString"); if (NULL == pszString) { iRet = -1; goto exit; } fShouldWrite = !(m_fFlags & MRU_CACHEWRITE); // See if the string is in the MRU list for (i = 0, ppszTemp = m_rgpszMRU; (UINT) i < m_uMax; i++, ppszTemp++) { if (*ppszTemp) { if (0 == lstrcmpi(pszString, (LPCTSTR) *ppszTemp)) { // Found it, so don't do the write cFirst = i + BASE_CHAR; iRet = i; break; } } } // We didn't find anything if ((UINT) i >= m_uMax) { iRet = -1; goto exit; } // Remove any previous reference to cFirst pszTemp = StrChr(m_pszOrder, cFirst); if (pszTemp) { DWORD cchSize = (lstrlen(pszTemp) + 1); StrCpyN(pszTemp, (pszTemp + 1), cchSize); } // Copy the string if (_SetPtr(ppszTemp, NULL)) { szTemp[0] = cFirst; szTemp[1] = '\0'; RegDeleteValue(m_hKey, szTemp); fShouldWrite = TRUE; } // If we need to write, do it if (fShouldWrite) { RegSetValueEx(m_hKey, c_szRegMRU, 0L, REG_SZ, (CONST BYTE *) m_pszOrder, sizeof(TCHAR) * (lstrlen(m_pszOrder) + 1)); m_fFlags &= ~MRU_ORDERDIRTY; } else { m_fFlags |= MRU_ORDERDIRTY; } exit: return (iRet); } int CMRUList::EnumList(int nItem, LPTSTR psz, UINT uLen) { int nItems = -1; LPTSTR pszTemp; LPBYTE pData; if (m_rgpszMRU) { nItems = lstrlen(m_pszOrder); if (nItems < 0 || !psz) return (nItems); if (nItem < nItems) { pszTemp = m_rgpszMRU[m_pszOrder[nItem] - BASE_CHAR]; if (!pszTemp) return (-1); StrCpyN(psz, pszTemp, uLen); nItems = lstrlen(pszTemp); } else { nItems = -1; } } return (nItems); } BOOL CMRUList::_SetPtr(LPSTR * ppszCurrent, LPCSTR pszNew) { int cchLength; LPSTR pszOld; LPSTR pszNewCopy = NULL; if (pszNew) { cchLength = lstrlenA(pszNew); // alloc a new buffer w/ room for the null terminator MemAlloc((LPVOID *) &pszNewCopy, ((cchLength + 1) * sizeof(TCHAR))); if (!pszNewCopy) return FALSE; StrCpyNA(pszNewCopy, pszNew, cchLength + 1); } pszOld = (LPSTR) InterlockedExchangePointer((LPVOID *)ppszCurrent, (LPVOID *)pszNewCopy); if (pszOld) MemFree(pszOld); return TRUE; }