2011-01-28 16:07:46 -08:00

315 lines
9.2 KiB
C++

// 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;
}
}