// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. // // This file is part of the VNC system. // // The VNC system is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, // USA. // // If the source code for the VNC system is not available from the place // whence you received this file, check http://www.uk.research.att.com/vnc or contact // the authors on vnc@uk.research.att.com for information on obtaining it. // // MRU maintains a list of 'Most Recently Used' strings in the registry // #include "MRU.h" static const TCHAR * INDEX_VAL_NAME = _T("index"); static const int MRU_MAX_ITEM_LENGTH = 256; MRU::MRU(LPTSTR keyname, unsigned int maxnum) { DWORD dispos; // Create the registry key. If unsuccessful all other methods will be no-ops. if ( RegCreateKeyEx(HKEY_CURRENT_USER, keyname, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &m_hRegKey, &dispos) != ERROR_SUCCESS ) { m_hRegKey = NULL; } m_index[0] = _T('\0'); m_maxnum = maxnum; // Read the index entry ReadIndex(); Tidy(); } // Add the item specified at the front of the list // Move it there if not already. If this makes the // list longer than the maximum, older ones are deleted. void MRU::AddItem(LPTSTR txt) { if (m_hRegKey == NULL) return; // We don't add empty items. if (_tcslen(txt) == 0) return; // Read each value in index, // noting which is the first unused id TCHAR id = _T('\0'); TCHAR firstUnusedId = _T('A'); TCHAR itembuf[MRU_MAX_ITEM_LENGTH+1]; // Find first unused letter. while (_tcschr(m_index, firstUnusedId) != NULL) firstUnusedId++; for (int i = 0; i < (int) _tcslen(m_index); i++) { // Does this entry already contain the item we're adding if (GetItem(i, itembuf, MRU_MAX_ITEM_LENGTH) != 0) { // If a value matches the txt specified, move it to the front. if (_tcscmp(itembuf, txt) == 0) { id = m_index[i]; for (int j = i; j > 0; j--) m_index[j] = m_index[j-1]; m_index[0] = id; WriteIndex(); // That's all we need to do. return; } } } // If we haven't found it, then we need to create a new entry and put it // at the front of the index. TCHAR valname[2]; valname[0] = firstUnusedId; valname[1] = _T('\0'); RegSetValueEx(m_hRegKey, valname, NULL, REG_SZ, (CONST BYTE *) txt, (_tcslen(txt) + 1) * sizeof(TCHAR)); // move all the current ids up one for (int j = _tcslen(m_index); j >= 0; j--) m_index[j] = m_index[j-1]; // and insert this one at the front m_index[0] = firstUnusedId; WriteIndex(); // Tidy, to truncate index if too long. Tidy(); } // How many items are on the list? int MRU::NumItems() { if (m_hRegKey == NULL) return 0; // return the length of index return _tcslen(m_index); } // Return them in order. 0 is the newest. // NumItems()-1 is the oldest. // Returns length, or 0 if unsuccessful. int MRU::GetItem(int index, LPTSTR buf, int buflen) { if (m_hRegKey == NULL) return 0; if (index > NumItems() - 1) return 0; TCHAR valname[2]; valname[0] = m_index[index]; valname[1] = _T('\0'); DWORD valtype; DWORD dwbuflen = buflen; if ( RegQueryValueEx( m_hRegKey, valname, NULL, &valtype, (LPBYTE) buf, &dwbuflen) != ERROR_SUCCESS) return 0; if (valtype != REG_SZ) return 0; // should really be an assert // May not be one byte per char, so we won't use dwbuflen return _tcslen(buf); } // Remove the specified item if it exists. // Only one copy will be removed, but nothing should occur more than once. void MRU::RemoveItem(LPTSTR txt) { if (m_hRegKey == NULL) return; TCHAR itembuf[MRU_MAX_ITEM_LENGTH+1]; for (int i = 0; i < NumItems(); i++) { GetItem(i, itembuf, MRU_MAX_ITEM_LENGTH); if (_tcscmp(itembuf, txt) == 0) { RemoveItem(i); break; } } } // Remove the item with the given index. // If this is greater than NumItems()-1 it will be ignored. void MRU::RemoveItem(int index) { if (m_hRegKey == NULL) return; if (index > NumItems()-1) return; TCHAR valname[2]; valname[0] = m_index[index]; valname[1] = _T('\0'); RegDeleteValue(m_hRegKey, valname); for (unsigned int i = index; i <= _tcslen(m_index); i++) m_index[i] = m_index[i+1]; WriteIndex(); } // Load the index string from the registry void MRU::ReadIndex() { if (m_hRegKey == NULL) return; // read the index DWORD valtype; DWORD dwindexlen = sizeof(m_index); if (RegQueryValueEx( m_hRegKey, INDEX_VAL_NAME, NULL, &valtype, (LPBYTE) m_index, &dwindexlen) == ERROR_SUCCESS) { } else { // If index entry doesn't exist, create it WriteIndex(); } } // Save the index string to the registry void MRU::WriteIndex() { if (m_hRegKey == NULL) return; RegSetValueEx(m_hRegKey, INDEX_VAL_NAME, NULL, REG_SZ, (CONST BYTE *)m_index, (_tcslen(m_index) + 1) * sizeof(TCHAR)); } // Tidy is called from time to time to preserve the integrity of the MRU // list. It does three things: // * Check the length and integrity of the index // * Check that all other values in the registry key have an // entry in the index and delete them from registry if not. // * Check that all entries in the index have a corresponding // value in the registry key and delete them from index if not. void MRU::Tidy() { if (m_hRegKey == NULL) return; int i; // First some checks on the index itself. // Truncate the index. m_index[m_maxnum] = _T('\0'); // Check that no entry occurs more than once. DWORD seenCharMask = 0; for (i = 0; m_index[i] != _T('\0'); i++) { DWORD mask = 1 << (m_index[i]-_T('A')); if (seenCharMask & mask) { // The current character has been used before. // Delete it and move everything up. for (int j = i; m_index[j] != _T('\0'); j++) m_index[j] = m_index[j+1]; // Start next check at new current character i--; } seenCharMask |= mask; } // Then check that all entries in registry have entry in index. TCHAR valname[256]; DWORD valtype; DWORD valnamelen; DWORD numValues; RegQueryInfoKey ( m_hRegKey, NULL, NULL, NULL, NULL, NULL, NULL, &numValues, NULL, NULL, NULL, NULL); // We're being good here. The documentation says we shouldn't // modify a key while enumerating its values. So this array will // hold the names of keys which should be deleted TCHAR **dudValues = new TCHAR* [numValues]; for (i = 0; i < (int) numValues; i++) dudValues[i] = NULL; unsigned int numDudValues = 0; i = 0; while (1) { valnamelen = 255; if (RegEnumValue(m_hRegKey, i, valname, &valnamelen, NULL, &valtype, NULL, NULL) == ERROR_NO_MORE_ITEMS) break; i++; // ignore the index if (_tcscmp(valname, INDEX_VAL_NAME) == 0) continue; // Record as invalid other non-single-char entries if (_tcslen(valname) > 1) { dudValues[numDudValues++] = _tcsdup(valname); continue; } // Record as invalid if not in the index if (_tcschr(m_index, valname[0]) == 0) { dudValues[numDudValues++] = _tcsdup(valname); continue; } } // Now delete the invalid ones for (i = 0; i < (int) numDudValues; i++) { RegDeleteValue(m_hRegKey, dudValues[i]); free(dudValues[i]); } delete[] dudValues; // Lastly, check that all entries in the index have a corresponding // entry in registry and delete them if not. for (i = _tcslen(m_index)-1; i >= 0; i--) { TCHAR itembuf[MRU_MAX_ITEM_LENGTH+1]; if (GetItem(i, itembuf, MRU_MAX_ITEM_LENGTH) == 0) { for (unsigned int j = i; j <= _tcslen(m_index)-1; j++) m_index[j] = m_index[j+1]; } } // Save any changes to the index. WriteIndex(); } MRU::~MRU() { if (m_hRegKey != NULL) { Tidy(); RegCloseKey(m_hRegKey); m_hRegKey = NULL; } }