/* (C) 2003-2007 Willem Jan Hengeveld <itsme@xs4all.nl>
 * Web: http://www.xs4all.nl/~itsme/
 *      http://wiki.xda-developers.com/
 *
 * $Id$
 *
 * bug:  specifying invalid string with '-d'  can cause whole registry to be deleted
 *     pregutl -d hklm\software\somekey  0000101a
 */

// NOTE: boost 1.30 -> 1.33  : changed from regex_match param boost::regbase::icase 
//    to regex constructor param boost::regex_constants::icase

#include <util/wintypes.h>
#include "debug.h"
#include "args.h"
#include <stdio.h>
#include <string>
#include <map>
#include <vector>
#include "stringutils.h"
#include "vectorutils.h"

#ifndef _WIN32_WCE
#include "FileFunctions.h"
#include <boost/regex.hpp>
#endif

#ifdef WINCEREGUTL
#ifndef _WIN32_WCE
// include only for win32 version
#include <util/rapitypes.h>
#include "dllversion.h"
#endif
#endif

#define STD_NAME_BUF_SIZE   256
#define STD_VALUE_BUF_SIZE  32768

// this registry value type is specific to windows ce
#ifndef REG_MUI_SZ 
#define REG_MUI_SZ   (21)
#endif

#if defined(WIN32REGUTL) || defined(_WIN32_WCE)
#define __RegCloseKey        RegCloseKey
#define __RegCreateKeyEx     RegCreateKeyEx
#define __RegDeleteKey       RegDeleteKey
#define __RegDeleteValue     RegDeleteValue
#define __RegEnumKeyEx       RegEnumKeyEx
#define __RegEnumValue       RegEnumValue
#define __RegOpenKeyEx       RegOpenKeyEx
#define __RegQueryInfoKey    RegQueryInfoKey
#define __RegQueryValueEx    RegQueryValueEx
#define __RegSetValueEx      RegSetValueEx
#endif

#ifdef WINCEREGUTL
#define __RegCloseKey        CeRegCloseKey
#define __RegCreateKeyEx     CeRegCreateKeyEx
#define __RegDeleteKey       CeRegDeleteKey
#define __RegDeleteValue     CeRegDeleteValue
#define __RegEnumKeyEx       CeRegEnumKeyEx
#define __RegEnumValue       CeRegEnumValue
#define __RegOpenKeyEx       CeRegOpenKeyEx
#define __RegQueryInfoKey    CeRegQueryInfoKey
#define __RegQueryValueEx    CeRegQueryValueEx
#define __RegSetValueEx      CeRegSetValueEx
#endif

// todo:
//    - support 'REGEDIT4' fileformat header.
//    - support unicode files
//    - change 'r' option in non-recursive option
//    - add option to choose how to mix keys and values.
//         ( full cross product, or values per key. )
//    - support comments at end of line
//    - support [-HKEY\path]  delete registry key syntax
//    - support valuename=-  delete registry value syntax
//    - make it easier to specify quotes in string format
//      introduce 'string:' format specifier, which does not need delimiting quotes.
//    - fix sz and multisz decoding, to properly convert from utf16 -> utf8

std::string GetNameFromSetSpec(const std::string& spec, size_t start);
std::string GetValueSpecFromSetSpec(const std::string& spec, size_t start);

enum RegAction {
    DO_LOAD,
    DO_LIST,
    DO_DELETE,
#ifndef _WIN32_WCE
    DO_SET,
#endif
    DO_CREATE,
};

bool g_outputFullHex= false;    // do not try to print ascii from binary regblobs
bool g_outputAllDataHex= false; // do not parse registry data at all, always print the raw data.
bool g_outputAllKeysHex= false; // hex dump of keys too.
int g_maxDepth= -1;
int g_level= 0;
bool g_doFlushkeys= false;

std::string reghexdump(const ByteVector& data);


#ifdef WINCEREGUTL
bool ITRegistryFlush()
{
    DWORD outsize=0;
    BYTE *outbuf=NULL;

    HRESULT res= ItsutilsInvoke("ITRegistryFlush",
            0, NULL, &outsize, (BYTE**)&outbuf);
    if (outbuf!=NULL)
        RapiFree(outbuf);

    return res==0;
}
#endif

class RegistryKey;
/*
class RegistryData {
};
class DwordData : public RegistryData {
public:
    DwordData(ByteVector& data)
    {
    }
};
class BinaryData : public RegistryData {
public:
    BinaryData(ByteVector& data)
    {
    }
};
class StringData : public RegistryData {
public:
    StringData(ByteVector& data)
    {
    }
};
class StringListData : public RegistryData {
public:
    StringListData(ByteVector& data)
    {
    }
};
*/

StringList g_warnings;
void DumpWarnings() {
    for (size_t i= 0 ; i<g_warnings.size() ; i++)
        debug("WARNING: %hs\n", g_warnings[i].c_str());

    g_warnings.clear();
}


std::string unescape(const std::string& str);
class RegistryValue {
#ifndef _WIN32_WCE
private:
    const static boost::regex reDwordPattern;
    const static boost::regex reStringPattern_s;
    const static boost::regex reStringPattern_d;
    const static boost::regex reStringPattern_n;
    const static boost::regex reMultiPartStringPattern_s;
    const static boost::regex reMultiPartStringPattern_d;
    const static boost::regex reMultiStringPattern;
    const static boost::regex reBinaryPattern;
    const static boost::regex reFilePattern;

    enum DwordType  {
        DWTYPE_HEX=16,
        DWTYPE_BITS=2,
        DWTYPE_DEC=10,
        DWTYPE_OCT=8,
    };
public:

    // this function takes a hexadecimal string value,  like '81f01234'
    // and converts it to a REG_DWORD : { 0x34, 0x12, 0xf0, 0x81 } value
    static RegistryValue FromDwordValue(DwordType dwType, const std::string& valstr) {
        return RegistryValue(strtoul(stringptr(valstr), 0, dwType));
    }
    // this function takes an escaped string value like ab\'cd\"ef\"\n
    // and converts it to a REG_SZ, or REG_EXPAND_SZ : { "ab'cd\"ef\"\n" }
    static RegistryValue FromStringValue(DWORD dwValType, const std::string& escstr) {
        return RegistryValue(dwValType, unescape(escstr));
    }
    // this function takes an escaped string value like ab\'cd\"ef\"\n
    // and converts it to a REG_SZ : { "ab'cd\"ef\"\n" }
    static RegistryValue FromMultiStringValue(const std::string& multistr) {
        StringList list;

        bool bDoubleQuotes= multistr[0]=='\"';

        std::string::const_iterator i= multistr.begin();
        std::string::const_iterator iend= multistr.end();
        boost::smatch what;
        while (regex_search(i, iend, what, bDoubleQuotes?reMultiPartStringPattern_d:reMultiPartStringPattern_s)) {
            std::string escstr(what[1].first, what[1].second);
            list.push_back(unescape(escstr));

            i= what[0].second;
        }

        return RegistryValue(list);
    }
    static RegistryValue FromFile(DWORD dwValueType, const std::string& filename) {
        ByteVector bv;
        if (!LoadFileData(filename, bv))
            throw stringformat("ERROR reading data from file '%hs'", filename.c_str());

        return RegistryValue(dwValueType, bv);
    }
    // this function takes a string of format 00,11,22,33,44
    // and converts it to { 0x00, 0x11, 0x22, 0x33, 0x44 }
    static RegistryValue FromBinaryValue(DWORD dwValueType, const std::string& hexdata) {
        ByteVector bv;
        size_t i=0;
        while (i<hexdata.size()) {
            bv.push_back((BYTE)strtoul(&hexdata[i], 0, 16));

            i= hexdata.find(",", i);
            if (i==hexdata.npos)
                break;

            i++;
        }
        return RegistryValue(dwValueType, bv);
    }
    static DWORD typestr_to_valuetype(const std::string& typestr)
    {
        if (typestr=="none")                       return REG_NONE;
        if (typestr=="sz")                         return REG_SZ;
        if (typestr=="string")                     return REG_SZ;
        if (typestr=="expand_sz")                  return REG_EXPAND_SZ;
        if (typestr=="expandsz")                   return REG_EXPAND_SZ;
        if (typestr=="expand")                     return REG_EXPAND_SZ;
        if (typestr=="binary")                     return REG_BINARY;
        if (typestr=="hex")                        return REG_BINARY;
        if (typestr=="dword")                      return REG_DWORD;
        if (typestr=="dwordle")                    return REG_DWORD_LITTLE_ENDIAN;
        if (typestr=="qword")                      return REG_QWORD;
        if (typestr=="qwordle")                    return REG_QWORD_LITTLE_ENDIAN;
        if (typestr=="qword")                      return REG_QWORD;
        if (typestr=="dwordbe")                    return REG_DWORD_BIG_ENDIAN;
        if (typestr=="multisz")                    return REG_MULTI_SZ;
        if (typestr=="multi_sz")                   return REG_MULTI_SZ;
        if (typestr=="mui_sz")                     return REG_MUI_SZ;
        if (typestr=="link")                       return REG_LINK;
        if (typestr=="resourcelist")               return REG_RESOURCE_LIST;
        if (typestr=="resource_list")              return REG_RESOURCE_LIST;
        if (typestr=="rl")                         return REG_RESOURCE_LIST;
        if (typestr=="full_resource_descriptor")   return REG_FULL_RESOURCE_DESCRIPTOR;
        if (typestr=="frd")                        return REG_FULL_RESOURCE_DESCRIPTOR;
        if (typestr=="resource_requirements_list") return REG_RESOURCE_REQUIREMENTS_LIST;
        if (typestr=="rrl")                        return REG_RESOURCE_REQUIREMENTS_LIST;

        if (boost::regex_match(typestr, boost::regex("^\\s*[0-9]+\\s*$")))
            return strtoul(typestr.c_str(), 0, 0);

        throw stringformat("unknown value type: %hs", typestr.c_str());

    }
    static DwordType xlat_dword_type_string(const std::string& dword_type)
    {
        if (dword_type=="dword")   return DWTYPE_HEX;
        if (dword_type=="bitmask") return DWTYPE_BITS;
        if (dword_type=="bit")     return DWTYPE_BITS;
        if (dword_type=="bin")     return DWTYPE_BITS;
        if (dword_type=="dec")     return DWTYPE_DEC;
        if (dword_type=="oct")     return DWTYPE_OCT;

        throw stringformat("unknown dword format type: %hs", dword_type.c_str());
    }
    static DWORD xlat_string_type_string(const std::string& dword_type)
    {
        if (dword_type[0]=='s')     return REG_SZ;
        if (dword_type[0]=='e')     return REG_EXPAND_SZ;

        throw stringformat("unknown dword format type: %hs", dword_type.c_str());
    }

    static RegistryValue FromValueSpec(const std::string& spec)
    {
        boost::smatch what;
        if (boost::regex_match(spec, what, reDwordPattern)) {
            std::string dword_type(what[1].first, what[1].second);
            std::string valstr(what[2].first, what[2].second);
            DwordType dwType= xlat_dword_type_string(dword_type);
            return FromDwordValue(dwType, valstr);
        }
        else if (boost::regex_match(spec, what, reStringPattern_s)) {
            std::string escstr(what[1].first, what[1].second);
            return FromStringValue(REG_SZ, escstr);
        }
        else if (boost::regex_match(spec, what, reStringPattern_d)) {
            std::string escstr(what[1].first, what[1].second);
            return FromStringValue(REG_SZ, escstr);
        }
        else if (boost::regex_match(spec, what, reStringPattern_n)) {
            std::string str_type(what[1].first, what[1].second);
            std::string escstr(what[2].first, what[2].second);
            DWORD dwStrType= xlat_string_type_string(str_type);
            return FromStringValue(dwStrType, escstr);
        }
        else if (boost::regex_match(spec, what, reMultiStringPattern)) {
            std::string multistr(what[1].first, what[1].second);
            return FromMultiStringValue(multistr);
        }
        else if (boost::regex_match(spec, what, reBinaryPattern)) {
            std::string typestr= (what[1].matched)
                ? std::string(what[1].first, what[1].second)
                : "3";
            std::string hexdata(what[2].first, what[2].second);
            DWORD dwValueType= typestr_to_valuetype(typestr);
            return FromBinaryValue(dwValueType, hexdata);
        }
        else if (boost::regex_match(spec, what, reFilePattern)) {
            std::string typestr= (what[1].matched)
                ? std::string(what[1].first, what[1].second)
                : "3";
            std::string filename(what[2].first, what[2].second);
            DWORD dwValueType= typestr_to_valuetype(typestr);
            return FromFile(dwValueType, filename);
        }
		throw stringformat("unimplemented valuespec: '%hs'\ndid you escape all backslashes?", spec.c_str());
    }
#endif
public:
    RegistryValue()
        : m_dwType(REG_NONE)
    {
        //debug("new regvalue none\n");
    }
    RegistryValue(DWORD dwType, const ByteVector& data)
        : m_dwType(dwType), m_data(data)
    {
        //debug("new regvalue type %d\n", m_dwType);
    }
    RegistryValue(const RegistryValue& rv)
        : m_dwType(rv.GetType()), m_data(rv.GetData())
    {
        //debug("copy of regvalue type %d\n", m_dwType);
    }

    DWORD GetType() const
    {
        return m_dwType;
    }

    // binary value
    RegistryValue(const ByteVector& data)
        : m_dwType(REG_BINARY), m_data(data)
    {
    }
    ByteVector GetData() const
    {
        return m_data;
    }

    // dword value
    RegistryValue(DWORD dwValue)
        : m_dwType(REG_DWORD), m_data(BV_FromDword(dwValue))
    {
    }
    DWORD GetDword() const
    {
        if (m_dwType!=REG_DWORD) throw stringformat("GetDword: not a dword: %d", m_dwType);
        if (m_data.size()!=4) throw stringformat("GetDword: incorrect size: %d", m_data.size());

        return BV_GetDword(m_data);
    }

    // string value
    RegistryValue(DWORD dwValType, const std::string& strValue)
        : m_dwType(dwValType), m_data(BV_FromWString(ToWString(strValue)))
    {
        BV_AppendWord(m_data, 0); // add terminating (WCHAR)NUL
    }
    std::string GetString() const
    {
        if (m_dwType!=REG_SZ && m_dwType!=REG_EXPAND_SZ) throw stringformat("GetString: not a dword: %d", m_dwType);
        if (m_data.size()&1) {
            g_warnings.push_back(stringformat("GetString: uneven size: %d", m_data.size()));
        }

        bool bShortString= false;
        std::string str;
        // todo: make this convert properly to utf8
        for (size_t i=0 ; i < m_data.size() ; ++i)
        {
            if ((i&1)==1) {
                if (m_data[i]!=0) {
                    /* unicode character */
                    str += (char)m_data[i];
                }
            }
            else {
                if (m_data[i]!=0) {
                    str += (char)m_data[i];
                }
                else if (i!=m_data.size()-2) {
                    bShortString= true;
                }
            }
        }
        if (bShortString)
            g_warnings.push_back(stringformat("GetString: short string: %d chars in %d data", str.size(), m_data.size()));

        return str;
    }

    // string list value
    RegistryValue(const StringList& listValue)
        : m_dwType(REG_MULTI_SZ)
    {
        m_data.clear();
        ByteVector::iterator out= m_data.begin();
        for (StringList::const_iterator i= listValue.begin() ; i!=listValue.end() ; ++i)
        {
            BV_AppendWString(m_data, ToWString(*i));
            BV_AppendWord(m_data, 0); // add terminating (WCHAR)NUL
        }
        BV_AppendWord(m_data, 0); // add terminating (WCHAR)NUL
    }
    StringList GetStringList() const
    {
        if (m_dwType!=REG_MULTI_SZ) throw stringformat("GetStringList: not a multisz: %d", m_dwType);
		if (m_data.size()&1) {
            g_warnings.push_back(stringformat("GetStringList: uneven size: %d", m_data.size()));
        }

        bool bFoundUnicode= false;
        StringList list;
        std::string str;
        // todo: make this convert properly to utf8
        for (size_t i= 0 ; i<m_data.size() ; ++i)
        {
            if ((i&1)==1) {
                if (m_data[i]!=0) {
                    bFoundUnicode= true;
                    str += (char)m_data[i];
                }
            }
            else {
                if (m_data[i]==0)
                {
                    list.push_back(str);
                    str.erase();
                }
                else
                {
                    str += (char)m_data[i];
                }
            }
        }
        if (bFoundUnicode)
            g_warnings.push_back(stringformat("GetStringList: with unicode chars"));
        if (!str.empty()) {
            g_warnings.push_back(stringformat("GetStringList: no terminating NUL"));
        }
		if (list.empty()) {
			// ... nop ... empty multisz
		}
        else if (!list.back().empty()) {
            g_warnings.push_back(stringformat("GetStringList: no closing element"));
        }
        else
            list.pop_back();
        return list;
    }
    std::string quotestring(const std::string& str) const
    {
        std::string qstr= "\"";
        for (size_t i=0 ; i<str.size() ; i++)
        {
            if (str[i]=='\r') qstr += "\\r";
            else if (str[i]=='\n') qstr += "\\n";
            else if (str[i]=='\t') qstr += "\\t";
            else if (str[i]=='\0') qstr += "\\0";
            else if (str[i]<' ' || str[i]>'~')
                qstr+=stringformat("\\x%02x", (unsigned char)str[i]);
            else
                qstr+=str[i];
        }
        return qstr+"\"";
    }
    std::string MultiszAsString() const
    {
        StringList list= GetStringList();

        for (size_t i=0 ; i<list.size() ; ++i)
            list[i]= quotestring(list[i]);
        return std::string("multi_sz:")+JoinStringList(list, ",");
    }
	std::string DwordAsString() const
	{
		if (m_dwType!=REG_DWORD) throw stringformat("DwordAsString: not a dword: %d", m_dwType);
		if (m_data.size()==0)
			return stringformat("dword:");
		else if (m_data.size()!=sizeof(DWORD)) {
            g_warnings.push_back(stringformat("DwordAsString: invalid sized dword: %d", m_data.size()));
			return stringformat("hex(%d):", m_dwType)+ascdump(m_data);
		}
		return stringformat("dword:%08lx", *(DWORD*)vectorptr(m_data));
	}
    std::string AsString() const
    {
        if (g_outputAllDataHex) {
            return stringformat("hex(%d):", m_dwType)+reghexdump(m_data);
        }
        switch(m_dwType) {
case REG_NONE:		if (m_data.size()) { 
                        g_warnings.push_back(stringformat("AsString: none with data: %d bytes", m_data.size()));
                        return stringformat("hex(%d):", m_dwType)+ascdump(m_data);
                    } 
                    else
                        return "none";
case REG_SZ:		return quotestring(GetString());
case REG_EXPAND_SZ: return std::string("expand:")+quotestring(GetString());
case REG_BINARY:	if (g_outputFullHex)
						return std::string("hex:")+reghexdump(m_data);
					else 
						return std::string("hex:")+ascdump(m_data);
case REG_DWORD:		return DwordAsString();
//case REG_DWORD_BE: return stringformat("dwordbe:%08lx", *(DWORD*)vectorptr(m_data));
case REG_MULTI_SZ:	return MultiszAsString();
case REG_LINK:		return "link:"+ascdump(m_data);
case REG_RESOURCE_LIST: return "rsclist:"+ascdump(m_data);
case REG_FULL_RESOURCE_DESCRIPTOR: return "rscdesc:"+ascdump(m_data);
case REG_RESOURCE_REQUIREMENTS_LIST: return "rscreqslist:"+ascdump(m_data);
case REG_QWORD:		return "qword:"+ascdump(m_data);
default:			return stringformat("hex(%d):", m_dwType)+ascdump(m_data);
        }
    }

private:
    DWORD m_dwType;
    ByteVector m_data;
    //Value* m_value;
};
#ifndef _WIN32_WCE
#define SQUOTEESC  "[^'\\\\]|\\\\'"
#define DQUOTEESC  "[^\"\\\\]|\\\\\""
#define STRESCAPES "\\\\[\\\\0rtn]|\\\\x[0-9a-fA-F][0-9a-fA-F]"
const boost::regex RegistryValue::reDwordPattern= boost::regex("(dword|bit|bin|bitmask|dec|oct):\\s*(\\w+)", boost::regex_constants::icase);
const boost::regex RegistryValue::reStringPattern_s=         boost::regex("^\\s*'((?:" SQUOTEESC "|" STRESCAPES ")*)'", boost::regex_constants::icase);
const boost::regex RegistryValue::reStringPattern_d=        boost::regex("^\\s*\"((?:" DQUOTEESC "|" STRESCAPES ")*)\"", boost::regex_constants::icase);

// .. i don't remember if i internionally left out the surrounding quotes here.
const boost::regex RegistryValue::reStringPattern_n= boost::regex("^\\s*(string|sz|expand\\w*):((?:" SQUOTEESC "|" STRESCAPES ")*?)", boost::regex_constants::icase);
const boost::regex RegistryValue::reMultiPartStringPattern_s=    boost::regex("'((?:" SQUOTEESC "|" STRESCAPES ")*)'", boost::regex_constants::icase);
const boost::regex RegistryValue::reMultiPartStringPattern_d= boost::regex("\"((?:" DQUOTEESC "|" STRESCAPES ")*)\"", boost::regex_constants::icase);
const boost::regex RegistryValue::reMultiStringPattern= boost::regex("^\\s*multi_sz:\\s*(.*?)", boost::regex_constants::icase);
const boost::regex RegistryValue::reBinaryPattern= boost::regex("^\\s*hex(?:\\((\\w+)\\))?:\\s*(.*?)", boost::regex_constants::icase);
const boost::regex RegistryValue::reFilePattern= boost::regex("^\\s*file(?:\\((\\w+)\\))?:\\s*(.*?)", boost::regex_constants::icase);
#endif
typedef std::map<std::string,RegistryValue> RegistryValueMap;
typedef std::vector<RegistryKey> RegistryKeyList;


class RegistryKey {
public:
    static RegistryKey FromKeySpec(const std::string& keyspec)
    {
        HKEY hRoot;
        std::string path;

        size_t slashpos= keyspec.find_first_of("/\\");

        // 0x80000000 HKEY_CLASSES_ROOT        hkcr
        // 0x80000001 HKEY_CURRENT_USER        hkcu
        // 0x80000002 HKEY_LOCAL_MACHINE       hklm  hkpr
        // 0x80000003 HKEY_USERS               hku
        // 0x80000004 HKEY_PERFORMANCE_DATA    hkpd
        // 0x80000005 HKEY_CURRENT_CONFIG      hkcc
        // 0x80000006 HKEY_DYN_DATA            hkdd
        // 0x80000050 HKEY_PERFORMANCE_TEXT    hkpt
        // 0x80000060 HKEY_PERFORMANCE_NLSTEXT hkpn
        //
        if (stringicompare(keyspec.substr(0,slashpos), std::string("hkcr"))==0)
            hRoot= HKEY_CLASSES_ROOT;
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkey_classes_root"))==0)
            hRoot= HKEY_CLASSES_ROOT;
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkcu"))==0)
            hRoot= HKEY_CURRENT_USER;
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkey_current_user"))==0)
            hRoot= HKEY_CURRENT_USER;
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hklm"))==0)
            hRoot= HKEY_LOCAL_MACHINE;
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkey_local_machine"))==0)
            hRoot= HKEY_LOCAL_MACHINE;
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hku"))==0)
            hRoot= HKEY_USERS;
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkey_users"))==0)
            hRoot= HKEY_USERS;
#ifdef HKEY_PERFORMANCE_DATA
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkpd"))==0)
            hRoot= HKEY_PERFORMANCE_DATA;
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkey_performance_data"))==0)
            hRoot= HKEY_PERFORMANCE_DATA;
#endif
#ifdef HKEY_CURRENT_CONFIG
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkcc"))==0)
            hRoot= HKEY_CURRENT_CONFIG;
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkey_current_config"))==0)
            hRoot= HKEY_CURRENT_CONFIG;
#endif
#ifdef HKEY_DYN_DATA
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkdd"))==0)
            hRoot= HKEY_DYN_DATA;
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkey_dyn_data"))==0)
            hRoot= HKEY_DYN_DATA;
#endif
#ifdef HKEY_PERFORMANCE_TEXT
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkpt"))==0)
            hRoot= HKEY_PERFORMANCE_TEXT;
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkey_performance_text"))==0)
            hRoot= HKEY_PERFORMANCE_TEXT;
#endif
#ifdef HKEY_PERFORMANCE_NLSTEXT
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkpn"))==0)
            hRoot= HKEY_PERFORMANCE_NLSTEXT;
        else if (stringicompare(keyspec.substr(0,slashpos), std::string("hkey_performance_nlstext"))==0)
            hRoot= HKEY_PERFORMANCE_NLSTEXT;
#endif
        else if (isdigit(keyspec[0])) {
            hRoot= (HKEY)(0x80000000+strtoul(stringptr(keyspec), 0, 0));
        }
        else
            hRoot= HKEY_LOCAL_MACHINE;

        if (slashpos==keyspec.npos)
            path= "";
        else
            path= keyspec.substr(slashpos+1);

        //debug("FromKeySpec: %08lx - %s\n", hRoot, path.c_str());

        return RegistryKey(hRoot, path);
    }

    RegistryKey() : m_hRoot(0), m_path(), m_hKey(0), m_suppress_errors(false)
    {
    }

    RegistryKey(HKEY hRoot) : m_hRoot(hRoot), m_path(), m_hKey(0)
    {
    }

    RegistryKey(const RegistryKey& hKey) : m_hRoot(hKey.GetRoot()), m_path(hKey.GetPath()), m_hKey(0)
    {
    }
    RegistryKey& operator=(const RegistryKey& rk)
    {
        m_hRoot=rk.GetRoot();
        m_path= rk.GetPath();
        m_hKey= 0;
        return *this;
    }
    RegistryKey(const RegistryKey& hRoot, const std::string& path) : m_hRoot(hRoot.GetRoot()), m_hKey(0)
    {
        m_path= hRoot.GetPath();
        if (!m_path.empty())
            m_path += "\\";
        m_path += path;
    }
    ~RegistryKey()
    {
        Close();
    }

    void Close()
    {
        __RegCloseKey(m_hKey);
        m_hKey= 0;
    }
    bool Open()
    {
        if (m_hKey)
            return true;

        DWORD dwAccessTypes[5]= {0xf003f, 0x20019, KEY_ALL_ACCESS|DELETE, KEY_ALL_ACCESS, KEY_READ};

        LONG res=-1;
        for (int i=0 ; i<5 ; i++) {
            res= __RegOpenKeyEx(m_hRoot, ToWString(m_path).c_str(), 0, dwAccessTypes[i], &m_hKey);
            if (res==ERROR_SUCCESS)
                break;
        }
        if (res!=ERROR_SUCCESS)
        {
            if (!m_suppress_errors)
                error(res, "Failed to open key %hs:'%hs'", GetRootName().c_str(), m_path.c_str());
            m_hKey= 0;
            return false;
        }
        m_dwMaxSubKeyNameLength= STD_NAME_BUF_SIZE;
        m_dwMaxValueNameLength= STD_NAME_BUF_SIZE;
        m_dwMaxValueLength= STD_VALUE_BUF_SIZE;
/*
 *
 * this does not seem to work on ce/rapi
 *
        __RegQueryInfoKey(m_hKey, NULL, NULL, NULL, NULL, &m_dwMaxSubKeyNameLength,
                NULL, NULL, &m_dwMaxValueNameLength, &m_dwMaxValueLength, NULL, NULL);

        if ( m_dwMaxSubKeyNameLength==0 && m_dwMaxValueNameLength==0 && m_dwMaxValueLength==0)
        {
            debug("ERROR: noinfo\n");
            m_dwMaxSubKeyNameLength= STD_NAME_BUF_SIZE;
            m_dwMaxValueNameLength= STD_NAME_BUF_SIZE;
            m_dwMaxValueLength= STD_VALUE_BUF_SIZE;
        }
*/
        return true;
    }
    HKEY GetHKey()
    {
        Open();

        return m_hKey;
    }

    void info()
    {
        std::Wstring regclass; regclass.resize(256);
        DWORD cbClass= regclass.size();

        DWORD nSubKeys=0;
        DWORD maxSubKeyNameLength=0;
        DWORD maxClassLength=0;
        DWORD nValues=0;
        DWORD maxValueNameLength=0;
        DWORD maxValueLength=0;

        Open();

        // BUG: only the key and value counts are returned
        LONG res= __RegQueryInfoKey(m_hKey, stringptr(regclass), &cbClass, NULL, &nSubKeys,
                &maxSubKeyNameLength, &maxClassLength, &nValues,
                &maxValueNameLength, &maxValueLength, NULL, NULL);
        if (res != ERROR_SUCCESS)
        {
            error(res, "__RegQueryInfoKey(%hs:'%hs')", GetRootName().c_str(), m_path.c_str());
            return ;
        }
        regclass.resize(cbClass);

        debug("regclass='%hs'  nkeys=%d nvals=%d  maxkey=%d maxcls=%d maxname=%d maxval=%d\n",
                regclass.c_str(), nSubKeys, maxSubKeyNameLength,
                maxClassLength, nValues, maxValueNameLength,&maxValueLength);
    }

    // returns the roothive of this key
    HKEY GetRoot() const
    {
        return m_hRoot;
    }

    // returns the full path to this key
    std::string GetPath() const
    {
        return m_path;
    }

    // returns the name of this key
    std::string GetName() const
    {
        size_t pos= m_path.find_last_of("\\/");
        if (pos!=m_path.npos)
            return m_path.substr(pos+1);
        else
            return m_path;
    }

    // returns the name of the hive of this key
    std::string GetRootName() const
    {
        if (m_hRoot==HKEY_CLASSES_ROOT       ) return "HKCR";
        if (m_hRoot==HKEY_CURRENT_USER       ) return "HKCU";
        if (m_hRoot==HKEY_LOCAL_MACHINE      ) return "HKLM";
        if (m_hRoot==HKEY_USERS              ) return "HKU";
#ifdef HKEY_PERFORMANCE_DATA
        if (m_hRoot==HKEY_PERFORMANCE_DATA   ) return "HKPD";
#endif
#ifdef HKEY_CURRENT_CONFIG
        if (m_hRoot==HKEY_CURRENT_CONFIG     ) return "HKCC";
#endif
#ifdef HKEY_DYN_DATA
        if (m_hRoot==HKEY_DYN_DATA           ) return "HKDD";
#endif
#ifdef HKEY_PERFORMANCE_TEXT
        if (m_hRoot==HKEY_PERFORMANCE_TEXT   ) return "HKPT";
#endif
#ifdef HKEY_PERFORMANCE_NLSTEXT
        if (m_hRoot==HKEY_PERFORMANCE_NLSTEXT) return "HKPN";
#endif

        return "unknown";
    }

    // creates a new subkey
    bool CreateSubKey(const std::string& keyname)
    {
        if (!Open())
            return false;

        HKEY hk;
        DWORD disp;
        LONG res= __RegCreateKeyEx(m_hKey, ToWString(keyname).c_str(), 0, NULL, 0, 0, NULL, &hk, &disp);
        if (res != ERROR_SUCCESS)
        {
            error(res, "Failed to create subkey %hs:'%hs'\\'%hs'", GetRootName().c_str(), m_path.c_str(), keyname.c_str());
            return false;
        }
        __RegCloseKey(hk);
        return true;
    }


    // deletes a childless subkey from this key
    bool DeleteSubKey(const std::string& keyname)
    {
        if (!Open())
            return false;
        LONG res= __RegDeleteKey(m_hKey, ToWString(keyname).c_str());
        if (res != ERROR_SUCCESS)
        {
            error(res, "Failed to delete subkey %hs:'%hs'\\'%hs'", GetRootName().c_str(), m_path.c_str(), keyname.c_str());
            return false;
        }
        return true;
    }

    // delete all children
    bool DeleteChildren()
    {
        bool bRes= true;
        StringList children= GetSubKeyNames();
        for (StringList::iterator i= children.begin() ; i!=children.end() ; ++i)
        {
            bRes = bRes && DeleteSubKey(*i);
        }
        return bRes;
    }

    bool DeleteValue(const std::string& valuename)
    {
        if (!Open())
            return false;
        LONG res= __RegDeleteValue(m_hKey, ToWString(valuename).c_str());
        if (res != ERROR_SUCCESS)
        {
            error(res, "Failed to delete value %hs:'%hs'\\'%hs'", GetRootName().c_str(), m_path.c_str(), valuename.c_str());
            return false;
        }
        return true;
    }

    //-------------------------------------------------------------------
    // deletes this key and all it's children
    bool DeleteKey()
    {
        if (!DeleteChildren())
            return false;

        Close();

        if (!GetParent().DeleteSubKey(GetName()))
            return false;

        return true;
    }

    //-------------------------------------------------------------------
    // creates registry key
    bool CreateKey()
    {
        m_suppress_errors= true;
        if (Open()) {
            m_suppress_errors= false;
            Close();
            return true;
        }
        m_suppress_errors= false;

		if (m_path.empty())
			return false;

        if (!GetParent().CreateKey())
            return false;

        if (!GetParent().CreateSubKey(GetName()))
            return false;

        debug("created key %hs\\%hs\n", GetRootName().c_str(), GetPath().c_str());
        return true;
    }


    void DumpKey()
    {
        debug("[%hs\\%hs]\n", GetRootName().c_str(), GetPath().c_str());
        try {
            DumpValues();
        } catch(std::string &e) { debug("ERROR: %s\n", e.c_str()); }
        debug("\n");

        g_level++;
        if (g_maxDepth==-1 || g_maxDepth>=g_level) {
            try {
                DumpChildren();
            } catch(std::string &e) { debug("ERROR: %s\n", e.c_str()); }
        }
        g_level--;
    }

    //-------------------------------------------------------------------
    void DumpValues()
    {
        RegistryValueMap map= GetValues();

        for (RegistryValueMap::iterator i= map.begin() ; i!=map.end() ; ++i)
        {
            if (g_outputAllKeysHex) {
                std::wstring wkey= ToWString((*i).first);
                debug(";%hs\n", reghexdump(ByteVector((BYTE*)stringptr(wkey), (BYTE*)(stringptr(wkey)+wkey.size()))).c_str());
            }
            debug("%hs=%hs\n", (*i).first.c_str(), (*i).second.AsString().c_str());
            DumpWarnings();
        }
    }

    void DumpChildren()
    {
        RegistryKeyList list= GetSubKeys();

        for (RegistryKeyList::iterator i= list.begin() ; i!=list.end() ; ++i)
        {
            (*i).DumpKey();
        }
    }

    //-------------------------------------------------------------------
    // returns parent to this key
    RegistryKey GetParent()
    {
        size_t pos= m_path.find_last_of("\\/");
        if (pos!=m_path.npos)
            return RegistryKey(m_hRoot, m_path.substr(0, pos));
        else
            return RegistryKey(m_hRoot);
    }
    StringList GetSubKeyNames()
    {
        StringList list;
        if (!Open())
            throw stringformat("GetSubKeyNames(%hs:'%hs'): could not open", GetRootName().c_str(), m_path.c_str());

        for (DWORD i=0 ; true ; i++)
        {
            DWORD cbName = m_dwMaxSubKeyNameLength+1;
            std::Wstring wname; wname.resize(cbName);
            LONG res =__RegEnumKeyEx(m_hKey, i, stringptr(wname), &cbName, NULL, NULL, NULL, NULL);
            if (res==ERROR_NO_MORE_ITEMS)
                break;
            if (res!=ERROR_SUCCESS)
            {
                error(res, "__RegEnumKeyEx(%hs:'%hs', %d)", GetRootName().c_str(), GetPath().c_str(), i);
                continue;
            }
            wname.resize(cbName);
            list.push_back(ToString(wname));
        }
        return list;
    }
    RegistryKeyList GetSubKeys()
    {
        RegistryKeyList list;
        if (!Open())
            throw stringformat("GetSubKeys(%hs:'%hs'): could not open", GetRootName().c_str(), m_path.c_str());

        for (DWORD i=0 ; true ; i++)
        {
            DWORD cbName = m_dwMaxSubKeyNameLength+1;
            std::Wstring wname; wname.resize(cbName);
            LONG res =__RegEnumKeyEx(m_hKey, i, stringptr(wname), &cbName, NULL, NULL, NULL, NULL);
            if (res==ERROR_NO_MORE_ITEMS)
                break;
            if (res!=ERROR_SUCCESS)
            {
                error(res, "__RegEnumKeyEx(%hs:'%hs', %d)", GetRootName().c_str(), GetPath().c_str(), i);
                continue;
            }
            wname.resize(cbName);
            std::string aname= ToString(wname);
            if (aname.empty())
                debug("ERROR converting subkey %d of %s : %s\n", i, m_path.c_str(), hexdump((BYTE*)wname.c_str(), wname.size(), 2).c_str());
            else
                list.push_back(RegistryKey(*this, aname));
        }
        return list;
    }
    StringList GetValueNames()
    {
        StringList list;
        if (!Open())
            throw stringformat("GetValueNames(%hs:'%hs'): could not open", GetRootName().c_str(), m_path.c_str());

        for (DWORD i=0 ; true ; i++)
        {
            DWORD cbName = m_dwMaxValueNameLength+1;
            std::Wstring wname; wname.resize(cbName);
            LONG res= __RegEnumValue(m_hKey, i, stringptr(wname), &cbName, NULL, NULL, NULL, NULL);
            if (res==ERROR_NO_MORE_ITEMS)
                break;
            if (res==ERROR_MORE_DATA)
            {
                wname.resize(cbName);
                res= __RegEnumValue(m_hKey, i, stringptr(wname), &cbName, NULL, NULL, NULL, NULL);
            }
            if (res!=ERROR_SUCCESS)
            {
                error(res, "GetValueNames: __RegEnumValue(%hs:'%hs', %d)", GetRootName().c_str(), GetPath().c_str(), i);
                continue;
            }
            wname.resize(cbName);
            list.push_back(ToString(wname));
        }
        return list;
    }
    RegistryValueMap GetValues()
    {
        RegistryValueMap map;
        if (!Open())
            throw stringformat("GetValues(%hs:'%hs'): could not open", GetRootName().c_str(), m_path.c_str());

        for (DWORD i=0 ; true ; i++)
        {
            DWORD cbName = m_dwMaxValueNameLength+1;
            std::Wstring wname; wname.resize(cbName);
            DWORD cbData = m_dwMaxValueLength;
            ByteVector data; data.resize(cbData);
            DWORD dwType;
            LONG res= __RegEnumValue(m_hKey, i, stringptr(wname), &cbName, NULL, &dwType, vectorptr(data), &cbData);
            if (res==ERROR_NO_MORE_ITEMS)
                break;
            if (res==ERROR_MORE_DATA)
            {
                wname.resize(cbName);
                data.resize(cbData);
                res= __RegEnumValue(m_hKey, i, stringptr(wname), &cbName, NULL, &dwType, vectorptr(data), &cbData);
            }
            if (res!=ERROR_SUCCESS)
            {
                error(res, "GetValues: __RegEnumValue(%hs:'%hs', %d)", GetRootName().c_str(), GetPath().c_str(), i);
                continue;
            }
            wname.resize(cbName);
            data.resize(cbData);

            map[ToString(wname)]= RegistryValue(dwType, data);
        }
        return map;
    }
    RegistryValue GetValue(const std::string& valuename)
    {
        if (!Open())
            throw stringformat("GetValue(%hs:'%hs', '%hs'): could not open", GetRootName().c_str(), m_path.c_str(), valuename.c_str());
        DWORD cbData = m_dwMaxValueLength;
        ByteVector data; data.resize(cbData);
        DWORD dwType;
        LONG res= __RegQueryValueEx(m_hKey, ToWString(valuename).c_str(), NULL, &dwType, vectorptr(data), &cbData);
        if (res==ERROR_MORE_DATA)
        {
            data.resize(cbData);
            res= __RegQueryValueEx(m_hKey, ToWString(valuename).c_str(), NULL, &dwType, vectorptr(data), &cbData);
        }
        if (res!=ERROR_SUCCESS)
            throw stringformat("GetValue(%hs:'%hs', '%hs'): __RegQueryValueEx:%08lx", GetRootName().c_str(), GetPath().c_str(), valuename.c_str(), res);
        data.resize(cbData);
        return RegistryValue(dwType, data);
    }
    bool SetValue(const std::string& valuename, const RegistryValue& value)
    {
        if (!Open())
            throw stringformat("SetValue(%hs:'%hs', '%hs'): could not open", GetRootName().c_str(), m_path.c_str(), valuename.c_str());

        //debug("setting [%s] %s to %s\n", m_path.c_str(), valuename.c_str(), value.AsString().c_str());
        ByteVector data= value.GetData();
        LONG res= __RegSetValueEx(m_hKey, ToWString(valuename).c_str(), NULL, value.GetType(), vectorptr(data), data.size());
        if (res!=ERROR_SUCCESS)
            throw stringformat("SetValue(%hs:'%hs', '%hs'): __RegSetValueEx:%08lx", GetRootName().c_str(), GetPath().c_str(), valuename.c_str(), res);
        debug("set value %hs\\%hs  %hs : %hs\n", GetRootName().c_str(), GetPath().c_str(), valuename.c_str(), value.AsString().c_str());
        return true;
    }

private:
        HKEY m_hRoot;
        std::string m_path;

        HKEY m_hKey;
        DWORD m_dwMaxSubKeyNameLength;
        DWORD m_dwMaxValueNameLength;
        DWORD m_dwMaxValueLength;

        bool m_suppress_errors;
};

std::string reghexdump(const ByteVector& data)
{
    std::string str; 
	if (data.empty())
		return "";
	str.resize(data.size()*3-1);

    for (size_t i=0 ; i<data.size() ; i++)
    {
        if (i) str[3*i-1]=',';
        _snprintf(vectorptr(str)+3*i, 2, "%02x", data[i]);
    }
    return str;
}

size_t findendquote(const std::string& str, size_t pos, char quotechar)
{
    bool bEscaped= false;
    while (pos<str.size())
    {
        if (bEscaped)
        {
            bEscaped= false;
        }
        else if (str[pos]==quotechar)
        {
            return pos+1;
        }
        else if (str[pos]=='\\')
        {
            bEscaped= true;
        }
        ++pos;
    }
    return std::string::npos;
}
std::string unescape(const std::string& str)
{
    std::string result;
    size_t pos= 0;
    bool bEscaped= false;
    while (pos<str.size())
    {
        if (bEscaped)
        {
            switch(str[pos])
            {
                case 'n': result+='\n'; break;
                case 'r': result+='\r'; break;
                case 't': result+='\t'; break;
                case 'x': if (pos+2<str.size())
                          {
                              result += (char)strtoul(str.substr(pos+1,2).c_str(),0,16); 
                              pos+=2;
                          }
                          break;
                case '0':
                case '1':
                case '2': if (pos+2<str.size())
                          {
                              result += (char)strtoul(str.substr(pos,3).c_str(),0,8); 
                              pos+=2;
                          }
                          break;
                default:
                          result+=str[pos];
            }
            bEscaped= false;
        }
        else if (str[pos]=='\\')
        {
            bEscaped= true;
        }
        else {
            result += str[pos];
        }
        ++pos;
    }

    return result;
}
bool ReadLine(FILE *f, std::string& line)
{
    line.erase();
    line.resize(65536);

    char *p= fgets(stringptr(line), 65536, f);
    if (p==NULL) {
        line.erase();
        return false;
    }
    line.resize(strlen(p));
    // todo: handle long lines ( > 65536 )
    //  - read until line has EOLN

    while (line.size() && (line[line.size()-1]=='\r' || line[line.size()-1]=='\n')) {
        line.resize(line.size()-1);
    }
    return true;
}

#ifndef _WIN32_WCE
bool ProcessRegFile(const std::string& filename)
{
    FILE *f= fopen(filename.c_str(), "r");
    if (f==NULL) {
        perror(filename.c_str());
        return false;
    }

    RegistryKey curkey;
    std::string line;

    while (ReadLine(f, line)) {
        while (line.size() && isspace(line[line.size()-1])) {
            line.resize(line.size()-1);
        }

        if (line.size()==0)
            continue;
        else if (line=="REGEDIT4")
            continue;
        else if (line[0]=='[' && line[line.size()-1]==']') {
            curkey= RegistryKey::FromKeySpec(line.substr(1, line.size()-2));
            curkey.CreateKey();
        }
        else if (line[0]==';') {
            // skip comments.
        }
        else {
            // remove leading whitespace
            line.erase(0, line.find_first_not_of(" \t"));

            std::string valuename= GetNameFromSetSpec(line, 0);
            std::string valuespec= GetValueSpecFromSetSpec(line, 0);

            //debug("valline: %hs  = %hs\n", valuename.c_str(), valuespec.c_str());

            curkey.SetValue(valuename, RegistryValue::FromValueSpec(valuespec));
        }
    }

    fclose(f);
    return true;
}
#endif
bool IsValueNameSpec(const std::string& spec)
{
    if (spec[0]!=':')
        return false;
    if (spec.find('=')==std::string::npos)
        return true;

    if (spec[1]=='\'' || spec[1]=='\"')
    {
        size_t endquote= findendquote(spec, 2, spec[1]);

        if (endquote==std::string::npos)
        {
            throw stringformat("IsValueNameSpec: missing endquote in '%hs'", spec.c_str());
        }
        return endquote==spec.size();
    }
    return false;
}

std::string GetNameFromValueNameSpec(const std::string& spec, size_t start)
{
    if (spec[start]=='\'' || spec[start]=='\"')
    {
        size_t endquote= findendquote(spec, start+1, spec[start]);
        //debug("GetNameFromValueNameSpec:%s\n", spec.substr(1, endquote-3).c_str());
        return unescape(spec.substr(start, endquote-start-1));
    }
    //debug("GetNameFromValueNameSpec:%s\n", spec.substr(1).c_str());
    return spec.substr(start);
}

bool IsSetSpec(const std::string& spec)
{
    if (spec[0]!=':')
        return false;
    if (spec.find('=')==std::string::npos)
        return false;

    if (spec[1]=='\'' || spec[1]=='\"')
    {
        size_t endquote= findendquote(spec, 2, spec[1]);

        if (endquote==std::string::npos)
        {
            throw stringformat("IsSetSpec: missing endquote in '%hs'", spec.c_str());
        }
        //debug("isset: endq=%d size=%d c=%c\n", endquote, spec.size(), spec[endquote]);
        if (endquote==spec.size())
            return false;

        return (spec[endquote]=='=');
    }
    return true;
}
std::string GetNameFromSetSpec(const std::string& spec, size_t start)
{
    if (spec[start]=='\'' || spec[start]=='\"')
    {
        size_t endquote= findendquote(spec, start+1, spec[start]);
        //debug("GetNameFromSetSpec:%s\n", spec.substr(1, endquote-3).c_str());

        //todo: check for missing quote.
        return unescape(spec.substr(start+1, endquote-start-2));
    }
    size_t eqpos= spec.find('=');

    //debug("GetNameFromSetSpec:%s\n", spec.substr(1,eqpos-1).c_str());
    return spec.substr(start,eqpos-start);
}

std::string GetValueSpecFromSetSpec(const std::string& spec, size_t start)
{
    if (spec[start]=='\'' || spec[start]=='\"')
    {
        size_t endquote= findendquote(spec, start+1, spec[start]);
        //debug("GetValueSpecFromSetSpec:%s\n", spec.substr(endquote).c_str());

        //todo: check for missing quote.
        size_t eqpos= spec.find('=', endquote);
        return spec.substr(eqpos+1);
    }
    size_t eqpos= spec.find('=');

    //debug("GetValueSpecFromSetSpec:%s\n", spec.substr(eqpos+1).c_str());
    return spec.substr(eqpos+1);
}

bool IsRegFile(const std::string& filespec)
{
    return filespec[0]=='@';
}
std::string GetFilenameFromRegfileSpec(const std::string& filespec)
{
    return filespec.substr(1);
}

void checkparameters(RegAction action, const RegistryKeyList& keys, const StringList& names, const RegistryValueMap& values)
{
    if (action==DO_CREATE && names.size())
        throw std::string("checkparameters: create with name");
    if (action==DO_CREATE && values.size())
        throw std::string("checkparameters: create with 'name=value'");
    if (action==DO_CREATE && keys.size()==0)
        throw std::string("checkparameters: create without keys");
    if (action==DO_DELETE && values.size())
        throw std::string("checkparameters: delete with 'name=value'");
    if (action==DO_DELETE && keys.size()==0)
        throw std::string("checkparameters: delete without keys");
    if (action==DO_LIST && values.size())
        throw std::string("checkparameters: list with 'name=value'");
    if (action==DO_LIST && keys.size()==0)
        throw std::string("checkparameters: list without keys");
#ifndef _WIN32_WCE
    if (action==DO_SET && names.size())
        throw std::string("checkparameters: set value with bare name");
    if (action==DO_SET && values.size()==0)
        throw std::string("checkparameters: set without values");
#endif
}


#ifdef _WIN32_WCE
int WINAPI WinMain( HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPTSTR    lpCmdLine,
                   int       nCmdShow)
{
//    DebugOutputDebugString();
    DebugSetLogfile("regbk.log");
    debugt("starting registry dump\n");

    RegAction action= DO_LIST;
    bool fRecurse= false;

    RegistryKeyList keys;
    StringList names;
    RegistryValueMap values;
    StringList regfiles;

    StringList args;
    if (!SplitString(ToString(lpCmdLine), args, false))
    {
        error("Error in commandline");
        return false;
    }
    StringList keylist;
    g_outputFullHex= true;
    for (StringList::iterator i= args.begin() ; i!=args.end() ; ++i)
    {
        std::string& arg= *i;
        if (arg[0]=='-') switch(arg[1])
        {
            case 'X': g_outputFullHex= false; break;
        }
        else {
            keylist.push_back(arg);
        }
    }

    if (keylist.size()==0 || tolower(keylist[0])=="install") {
        if (keylist.size())
            DebugSetLogfile("\\SD Card\\regbk.log");
        RegistryKey::FromKeySpec("HKCU").DumpKey();
        RegistryKey::FromKeySpec("HKLM").DumpKey();
        RegistryKey::FromKeySpec("HKCR").DumpKey();
    }
    else {
        for (int i=0 ; i<keylist.size() ; i++)
            RegistryKey::FromKeySpec(keylist[i]).DumpKey();
    }

    debugt("finished registry dump\n");

    MessageBeep(MB_OK);
    return 0;
}
#else
void usage()
{
    printf("(C) 2003-2008 Willem jan Hengeveld  itsme@xs4all.nl\n");
    debug("Usage: pregutl [keys] [values] [@regfile]\n");
    debug("  -d   delete value or key\n");
    debug("  -c   create key\n");
    debug("  -s   set value\n");
    debug("  -Rmaxdepth   specify max recursion depth\n");
    debug("  -x   output hex keys as full hexdump. instead of trying to print ascii\n");
    debug("  -f   flush registry changes to disk\n");
    debug("\n");
    debug("key-values are specified as follows:\n");
    debug("    :valuename  or :'value name'\n");
    debug("    :valuename=value\n");
    debug("use an empty string for the default value\n");
    debug("\n");
    debug("values are specified as follows:\n");
    debug("    dword:01234567  or \"str\\n\\\"str\" or\n");
    debug("    multi_sz:\"...\",\"...\" or  hex:ff,11,22\n");
    debug("    note that you may also use single (') quotes instead\n");
    debug("    note also, that backslashes in strings must be escaped: \"\\\\windows\"\n");
    debug("    otherwise it would be almost impossible to enter quotes on the dos cmdline\n");
    debug("\n");
    debug("    strings may also be specified unquoted, with a 'string:' prefix\n");
    debug("    dword values may also be specified as 'dec:1234' or 'bitmask:1010101'\n");
    debug("\n");
    debug("    use 'file(<type>):filename' to read the value from <filename>\n");
    debug("\n");
    debug("    with hex() and file() a value type may be specified, either as a decimal nr\n");
    debug("    or as a typename: none, sz, binary, dword, ... etc\n");
    debug("\n");
    debug("keys are specified as follows:\n");
    debug("    HKLM\\Software\\HTC\n");
    debug("\n");
    debug("if multiple keys and keyvals are specified pregutl\n");
    debug("operates on all combinations\n");
}

int main(int argc, char *argv[])
{
    DebugStdOut();

    RegAction action= DO_LIST;

    RegistryKeyList keys;
    StringList names;
    RegistryValueMap values;
    StringList regfiles;

    try {
    for (int i=1 ; i<argc ; i++)
    {
        if (argv[i][0]=='-') switch(argv[i][1])
        {
            case 'd': action= DO_DELETE; break;
            case 'c': action= DO_CREATE; break;
            case 's': action= DO_SET; break;
            case 'f': g_doFlushkeys= true; break;
            case 'R': g_maxDepth= strtoul(argv[i]+2,0,0); break;
            case 'x': 
                      if (g_outputFullHex || argv[i][2]=='x') {
                          if (g_outputAllDataHex || argv[i][3]=='x') {
                              g_outputAllKeysHex= true;
                          }
                          else {
                              g_outputAllDataHex= true;
                          }
                      }
                      else {
                          g_outputFullHex= true;
                      }
                      break;
            default:
                usage();
                return 1;
        }
        else if (IsValueNameSpec(argv[i]))
        {
            names.push_back(GetNameFromValueNameSpec(argv[i], 1));
        }
        else if (IsSetSpec(argv[i]))
        {
            values[GetNameFromSetSpec(argv[i], 1)]= RegistryValue::FromValueSpec(GetValueSpecFromSetSpec(argv[i], 1));
        }
        else if (IsRegFile(argv[i]))
        {
            regfiles.push_back(GetFilenameFromRegfileSpec(argv[i]));
        }
        else
            keys.push_back(RegistryKey::FromKeySpec(std::string(argv[i])));
    }
    }
    catch(std::string &e) {
        debug("ERROR: %s\n", e.c_str());
        return 1;
    }
    if (!regfiles.empty())
        action= DO_LOAD;

    try {
        checkparameters(action, keys, names, values);
    }
    catch(std::string &e) {
        debug("ERROR: %s\n", e.c_str());
        return 1;
    }


#ifdef WINCEREGUTL
    if (g_doFlushkeys)
        CheckITSDll();
    else if (FAILED(CeRapiInit()))
    {
        debug( "Failed to init rapi\n");
        return 1;
    }
#endif

    try {
    for (RegistryKeyList::iterator i= keys.begin() ; i!=keys.end() ; ++i)
    {
        switch(action)
        {
        case DO_CREATE:
            try {
                (*i).CreateKey();
            } catch(std::string &e) { debug("WARNING: %s\n", e.c_str()); }
            break;
        case DO_DELETE:
            if (names.size())
                for (StringList::iterator j= names.begin() ; j!=names.end() ; ++j)
                    try {
                        (*i).DeleteValue(*j);
                    } catch(std::string &e) { debug("WARNING: %s\n", e.c_str()); }
            else
                try {
                    (*i).DeleteKey();
                } catch(std::string &e) { debug("WARNING: %s\n", e.c_str()); }
            break;
        case DO_LIST:
            try {
                (*i).DumpKey();
            } catch(std::string &e) { debug("WARNING: %s\n", e.c_str()); }
            break;
        case DO_SET:
            for (RegistryValueMap::iterator j= values.begin() ; j!=values.end() ; ++j)
                try {
                    (*i).SetValue((*j).first, (*j).second);
                } catch(std::string &e) { debug("WARNING: %s\n", e.c_str()); }
            break;
        case DO_LOAD:
            debug("ERROR: not expecting load here\n");
            break;
        }
    }
    for (StringList::iterator i= regfiles.begin() ; i!=regfiles.end() ; ++i)
    {
        try {
            ProcessRegFile(*i);
        } catch(std::string &e) { debug("WARNING: %s\n", e.c_str()); }
    }
    } catch(std::string &e) { debug("ERROR: %s\n", e.c_str()); return 1; }

#ifdef WINCEREGUTL
    if (g_doFlushkeys)
        ITRegistryFlush();

    StopItsutils();
#endif

    return 0;
}
#endif


