Added command line tool to access the registry.
diff --git a/configure b/configure
index a83b473..79d4c8b 100755
--- a/configure
+++ b/configure
@@ -4643,6 +4643,7 @@
programs/notepad/Makefile
programs/progman/Makefile
programs/regtest/Makefile
+programs/regapi/Makefile
programs/view/Makefile
programs/winhelp/Makefile
programs/winver/Makefile
@@ -4806,6 +4807,7 @@
programs/notepad/Makefile
programs/progman/Makefile
programs/regtest/Makefile
+programs/regapi/Makefile
programs/view/Makefile
programs/winhelp/Makefile
programs/winver/Makefile
diff --git a/configure.in b/configure.in
index e2fa923..536f2fa 100644
--- a/configure.in
+++ b/configure.in
@@ -668,6 +668,7 @@
programs/notepad/Makefile
programs/progman/Makefile
programs/regtest/Makefile
+programs/regapi/Makefile
programs/view/Makefile
programs/winhelp/Makefile
programs/winver/Makefile
diff --git a/programs/Makefile.in b/programs/Makefile.in
index aa0b4f2..a6b80b6 100644
--- a/programs/Makefile.in
+++ b/programs/Makefile.in
@@ -5,6 +5,7 @@
control \
notepad \
progman \
+ regapi \
regtest \
view \
winhelp \
diff --git a/programs/regapi/.cvsignore b/programs/regapi/.cvsignore
new file mode 100644
index 0000000..be405f5
--- /dev/null
+++ b/programs/regapi/.cvsignore
@@ -0,0 +1,2 @@
+Makefile
+regapi
diff --git a/programs/regapi/Makefile.in b/programs/regapi/Makefile.in
new file mode 100644
index 0000000..885a013
--- /dev/null
+++ b/programs/regapi/Makefile.in
@@ -0,0 +1,32 @@
+DEFS = -DWINELIB
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR = @srcdir@
+VPATH = @srcdir@
+MODULE = none
+PROGRAMS = regapi
+ALL_LIBS = $(WINELIB) $(X_LIBS) $(XLIB) $(LIBS)
+RCFLAGS = -w32 -h
+WRCEXTRA = -A -p $*
+
+C_SRCS = \
+ regapi.c
+
+all: $(PROGRAMS)
+
+depend::
+
+@MAKE_RULES@
+
+regapi: $(OBJS)
+ $(CC) -o regapi $(OBJS) $(LDOPTIONS) $(ALL_LIBS)
+
+install: dummy
+ $(INSTALL_PROGRAM) regapi $(bindir)/regapi
+
+uninstall: dummy
+ $(RM) $(bindir)/regapi
+
+dummy:
+
+### Dependencies:
diff --git a/programs/regapi/README b/programs/regapi/README
new file mode 100644
index 0000000..2c82fe0
--- /dev/null
+++ b/programs/regapi/README
@@ -0,0 +1,89 @@
+
+Registry Command Line API Tool
+------------------------------
+
+ This progam is intended to fill a particular need. I needed to make the
+ wine registry look like it would have been if my application would have
+ been installed by its installation program. Since this was not possible I
+ took the following approach.
+
+ 1 - Use regedit to export my full Windows registry before I install my
+ application.
+
+ 2 - Use regedit to export my full Windows registry after I had install my
+ application.
+
+ 3 - Generate the differences between the two image. What I obtain from the
+ diff is what I need to apply to the wine registry.
+
+ Obvisouly the process is not that straight forward to solve, first,
+ you don't get the diff between two Windows regedit exported .reg file by
+ doing a simple diff. What I had to do is a little more complex, but not
+ that much...
+
+ (Assuming that the registry picture files are
+ named ./before.reg and ./after.reg)
+
+ 1 - Parse the before.reg and after.reg file into regFixer.pl, in order to
+ obtain lines in the form [HKEY\Sub1\Sub2\...\Subn]"Value"="Data"
+ (where "Data" can be prefixed by the type identifyer : hex:, hex(0000000?)
+ or dword:)
+
+ 2 - Generate the diff between the before.reg.fix and after.reg.fix
+ into app.diff
+
+ Now we have a app.reg file that contain what has been done by installing the
+ application. To this we extract the part's that we are interested in using
+ grep (and fix it with sed) and put that into app.added by example
+ ( let say we keep the added values only ).
+
+ At this point we know which registry entry to add to the wine registry. It
+ only remains to take the format we have and reset it into a format similar
+ to the one we get from regedit.
+
+ I say "similar" because there is a tiny difference between Windows regedit
+ export format and the format actualy required by the tool.
+
+ The problem with this (and it is not a big one) is that regedit export long
+ data streams onto many lines, and since I dont have tons of time I setup
+ another Perl script (regRestorer.pl) that fixes this (this could easily
+ be done in C obviously) (left as excercise ;-) ).
+
+ So, once you parsed app.added into regRestorer.pl you get a app.reg ready to
+ process by regapi.
+
+ So, this package comes with a few pieces:
+
+ regFixer.pl - Will convert the export of regedit
+ into something "diff-able"
+
+ regRestorer.pl - Will convert "cleaned" diff file into
+ something "regapi-able"
+
+ regSet - Will do the procedure explained herein
+ for the added key only.
+
+
+FAQ
+---
+
+ Quick Start Guide
+ -----------------
+ 1 - Get a snapshot of your windows registry in before.reg, (regedit/export)
+ 2 - Install you're application,
+ 3 - Get a snapshot of your windows registry in after.reg.
+ 4 - Invoke ./regSet.sh before.reg after.reg
+
+
+ Adding key I have in a regedit export file (nothing to diff with...)
+ ------------------------------------------
+ 1 - Invoke ./regSet.sh /dev/null myRegistry.reg
+
+ regapi help
+ -----------
+ 1 - regapi has some sort of "man page like" help in it, simply invoke it
+ without any arguments.
+
+Hope this is of any use to you.
+
+Sylvain St-Germain.
diff --git a/programs/regapi/regFixer.pl b/programs/regapi/regFixer.pl
new file mode 100755
index 0000000..62a030b
--- /dev/null
+++ b/programs/regapi/regFixer.pl
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+
+# This script takes as STDIN an output from the Registry
+# (export from regedit.exe) and prefixes every subkey-value
+# pair by their hkey,key data member
+#
+# Copyright 1999 Sylvain St-Germain
+#
+
+${prefix} = "";
+${line} = "";
+
+LINE: while(<>) {
+ chomp; # Get rid of 0x0a
+ chop; # Get rid of 0x0d
+
+ next LINE if(/^$/); # This is an empty line
+
+ if( /^\[/ ) {
+ ${prefix} = ${_}; # assign the prefix for the forthcomming section
+ next LINE;
+ }
+ s/\\\\/\\/g; # Still some more substitutions... To fix paths...
+
+ s/^ //; # Get rid of the stupid two spaces at the begining
+ # they are there in the case of a multi-line thing
+
+ if (/\\$/) { # The line ends with '\', it means it is a multi
+ s/\\$//; # line thing, remove it.
+
+ ${line} = "${line}${_}";# Add the current line to the line to output
+ next LINE; # process the next line
+ }
+
+ ${line} = "${line}${_}"; # Set line to the multi line thing+the current line
+
+ print "${prefix}${line}\n";
+ ${line} = ""; # start over...
+}
+
+
+
diff --git a/programs/regapi/regRestorer.pl b/programs/regapi/regRestorer.pl
new file mode 100755
index 0000000..02525e4
--- /dev/null
+++ b/programs/regapi/regRestorer.pl
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+
+# This script takes as STDIN an output from the regFixer.pl
+# and reformat the file as if it would have been exported from the registry
+#
+# Note: the output file is not exactly the one we get when exporting from
+# regedit, the tiny difference is that multilines (which were
+# single-linized by regFixer.pl) values are not re-multi-linized by
+# this script.
+#
+# Copyright 1999 Sylvain St-Germain
+#
+
+${newkey} = "";
+${key} = "";
+${data} = "";
+
+# I do not validate the integrity of the file, I am assuming that
+# the input file comes from the output of regFixer.pl, therefore things
+# should be ok, if they are not, investigate and send me an email...
+
+LINE: while(<>) {
+ chomp; # get rid of new line
+
+ next LINE if(/^$/); # This is an empty line
+
+ (${key}, ${data}, ${rest})= split /]/,$_ ; # Extract the values from the line
+
+ ${key} = "${key}]"; # put the ']' back there...
+
+ if (${rest} ne "") # concat we had more than 1 ']'
+ { # on the line
+ ${data} = "${data}]${rest}";
+ }
+
+ if (${key} eq ${newkey})
+ {
+ print "${data}\n";
+ }
+ else
+ {
+ ${newkey} = ${key}; # assign it
+
+ print "\n";
+ print "${key}\n";
+ print "${data}\n";
+ }
+}
+
+
+
diff --git a/programs/regapi/regSet.sh b/programs/regapi/regSet.sh
new file mode 100755
index 0000000..a4e26ae
--- /dev/null
+++ b/programs/regapi/regSet.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+# This script is the receipe to generate the key that have to be created like
+# if an applicaiton was installed by its installer. It processes using a
+# registry based on the picture of the registry before the application is
+# installed and the picture of the registry after the application is installed.
+#
+# Copyright 1999 Sylvain St-Germain
+#
+
+if [ $# -ne 2 ]; then
+ echo "$0 Usage: "
+ echo " You must provide 2 arguments."
+ echo " 1 - Registry output before the application's installation."
+ echo " 2 - Registry output after the application's installation."
+ echo
+ exit 1
+fi
+
+echo "Assuming that $1 is the \"before\" file..."
+echo "Assuming that $2 is the \"after\" file..."
+
+#
+# do not attempt to regFix.pl /dev/null ...
+#
+echo "Fixing exported registry files..."
+if [ $1 != "/dev/null" ]; then
+ cat $1 | ./regFixer.pl > $1.fix
+fi
+
+cat $2 | ./regFixer.pl > $2.fix
+
+#
+# diff accordingly depending on /dev/null
+#
+echo "Diffing..."
+if [ $1 != "/dev/null" ]; then
+ diff $1.fix $2.fix > $2.diff
+else
+ diff /dev/null $2.fix > $2.diff
+fi
+#
+# Keep only added lines
+#
+echo "Grepping keys to add and generating cleaned fixed registry file."
+cat $2.diff | grep '^> ' | sed -e 's/^> //' > $2.toAdd
+
+#
+# Restore the file format to the regedit export 'like' format
+#
+echo "Restoring key's in the regedit export format..."
+cat $2.toAdd | ./regRestorer.pl > $2.toAdd.final
+
+echo "Cleaning..."
+rm $1.fix $2.fix >/dev/null 2>&1
+rm $2.diff >/dev/null 2>&1
+rm $2.toAdd >/dev/null 2>&1
+mv $2.toAdd.final $2.toAdd
+
+echo "Operation completed, result file is $2.toAdd"
+
+exit 0
diff --git a/programs/regapi/regapi.c b/programs/regapi/regapi.c
new file mode 100644
index 0000000..59c74d3
--- /dev/null
+++ b/programs/regapi/regapi.c
@@ -0,0 +1,983 @@
+/*
+ * Command line Registry implementation
+ *
+ * Copyright 1999 Sylvain St-Germain
+ *
+ * Note: Please consult the README file for more information.
+ *
+ */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <windows.h>
+#include <winreg.h>
+#include <winerror.h>
+#include <winnt.h>
+#include <string.h>
+#include <shell.h>
+
+/******************************************************************************
+ * Defines and consts
+ */
+#define IDENTICAL 0
+#define COMMAND_COUNT 5
+
+#define KEY_MAX_LEN 1024
+#define STDIN_MAX_LEN 2048
+
+/* Return values */
+#define COMMAND_NOT_FOUND -1
+#define SUCCESS 0
+#define NOT_ENOUGH_MEMORY 1
+#define KEY_VALUE_ALREADY_SET 2
+#define COMMAND_NOT_SUPPORTED 3
+
+/* Generic global */
+static BOOL bForce = FALSE; /* Is set to TRUE when -force is
+ passed on the command line */
+
+/* Globals used by the api setValue, queryValue */
+static LPSTR currentKeyName = NULL;
+static HKEY currentKeyClass = NULL;
+static HKEY currentKeyHandle = NULL;
+static BOOL bTheKeyIsOpen = FALSE;
+
+/* Delimitors used to parse the "value"="data" pair for setValue*/
+#define SET_VALUE_MAX_ARGS 2
+/* Delimitors used to parse the "value" to query queryValue*/
+#define QUERY_VALUE_MAX_ARGS 1
+
+static const char *setValueDelim[SET_VALUE_MAX_ARGS] = {"=", ""};
+static const char *queryValueDelim[QUERY_VALUE_MAX_ARGS] = {""};
+
+/* Array used to extract the data type from a string in getDataType. */
+typedef struct tagDataTypeMap
+{
+ char mask[15];
+ DWORD dataType;
+} dataTypeMap;
+
+static const dataTypeMap typeMap[] =
+{
+ {"hex:", REG_BINARY},/* could be REG_NONE (?) */
+ {"dword:", REG_DWORD},
+ {"hex(0):", REG_NONE},
+ {"hex(1):", REG_SZ},
+ {"hex(2):", REG_EXPAND_SZ},
+ {"hex(3):", REG_BINARY},
+ {"hex(4):", REG_DWORD},
+ {"hex(5):", REG_DWORD_BIG_ENDIAN},
+ {"hex(6):", REG_LINK},
+ {"hex(7):", REG_MULTI_SZ},
+ {"hex(8):", REG_RESOURCE_LIST},
+ {"hex(9):", REG_FULL_RESOURCE_DESCRIPTOR},
+ {"hex(80000000):", REG_NONE},
+ {"hex(80000001):", REG_SZ},
+ {"hex(80000002):", REG_EXPAND_SZ},
+ {"hex(80000003):", REG_BINARY},
+ {"hex(80000004):", REG_DWORD},
+ {"hex(80000005):", REG_DWORD_BIG_ENDIAN},
+ {"hex(80000006):", REG_LINK},
+ {"hex(80000007):", REG_MULTI_SZ},
+ {"hex(80000008):", REG_RESOURCE_LIST},
+ {"hex(80000009):", REG_FULL_RESOURCE_DESCRIPTOR},
+ {"hex(8000000a):", REG_BINARY}, /* REG_RESOURCE_REQUIREMENTS_LIST}, !Exist */
+ {"hex(8000000A):", REG_BINARY}, /* REG_RESOURCE_REQUIREMENTS_LIST}, !Exist */
+};
+const static int LAST_TYPE_MAP = sizeof(typeMap)/sizeof(dataTypeMap);
+
+
+/*
+ * Forward declaration
+ */
+typedef void (*commandAPI)(LPSTR lpsLine);
+
+static void doSetValue(LPSTR lpsLine);
+static void doDeleteValue(LPSTR lpsLine);
+static void doCreateKey(LPSTR lpsLine);
+static void doDeleteKey(LPSTR lpsLine);
+static void doQueryValue(LPSTR lpsLine);
+
+/*
+ * current suuported api
+ */
+static const char* commandNames[COMMAND_COUNT] = {
+ "setValue",
+ "deleteValue",
+ "createKey",
+ "deleteKey",
+ "queryValue"
+};
+
+/*
+ * Pointers to processing entry points
+ */
+static const commandAPI commandAPIs[COMMAND_COUNT] = {
+ doSetValue,
+ doDeleteValue,
+ doCreateKey,
+ doDeleteKey,
+ doQueryValue
+};
+
+/*
+ * This array controls the registry saving needs at the end of the process
+ */
+static const BOOL commandSaveRegistry[COMMAND_COUNT] = {
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ FALSE
+};
+
+/*
+ * Generic prototyes
+ */
+static HKEY getDataType(LPSTR *lpValue);
+static LPSTR getRegKeyName(LPSTR lpLine);
+static HKEY getRegClass(LPSTR lpLine);
+static LPSTR getArg(LPSTR arg);
+static INT getCommand(LPSTR commandName);
+static DWORD convertHexToDWord(char *str, BYTE *buf);
+static DWORD convertHexCSVToHex(char *str, BYTE *buf, ULONG bufLen);
+static LPSTR convertHexToHexCSV( BYTE *buf, ULONG len);
+static LPSTR convertHexToDWORDStr( BYTE *buf, ULONG len);
+static HRESULT openKey(LPSTR stdInput);
+static void closeKey();
+
+/*
+ * api setValue prototypes
+ */
+static void processSetValue(LPSTR cmdline);
+static HRESULT setValue(LPSTR *argv);
+
+/*
+ * api queryValue prototypes
+ */
+static void processQueryValue(LPSTR cmdline);
+
+/*
+ * Help Text displyed when invalid parameters are provided
+ */
+static char helpText[] = "
+NAME
+ regapi - provide a command line interface to the wine registry.
+
+SYNOPSYS
+ regapi commandName [-force] < file
+
+DESCRIPTION
+ regapi allows editing the wine resgistry. It processes the given
+ commandName for every line in the stdin data stream. Input data
+ format may vary depending on the commandName see INPUT FILE FORMAT.
+
+OPTIONS
+ commandName
+ Instruct regapi about what action to perform on the data stream.
+ Currently, only setValue and queryValue are supported and
+ implemented.
+
+ -force
+ When provided the action will be performed anyway. This may
+ have a different meaning depending on the context. For example,
+ when providing -force to setValue, the value is set even if it
+ was previously set to another value.
+
+ < file
+ STDIN chanel, provide a file name with line of the appropriate
+ format.
+
+INPUT FILE FORMAT
+
+ setValue
+ The input file format required by the setValue command is similar
+ to the one obtained from regedit.exe export option. The only
+ difference is that multi line values are not supported, the
+ value data must be on a single line.
+
+ [KEY_CLASS\\Some\\Path\\For\\A\\Key]
+ \"Value1\"=\"Data1\"
+ \"Value2\"=\"Data2\"
+ \"Valuen\"=\"Datan\"
+ ...
+
+ queryValue
+ The input file format required by the queryValue command is
+ similar to the one required by setValue. The only
+ difference is that you only provide the value name.
+
+ [KEY_CLASS\\Some\\Path\\For\\A\\Key]
+ \"Value1\"
+ \"Value2\"
+ \"Valuen\"
+ ...
+ February 1999.
+";
+
+
+/******************************************************************************
+ * This funtion returns the HKEY associated with the data type encoded in the
+ * value. It modify the input parameter (key value) in order to skip this
+ * "now useless" data type information.
+ */
+HKEY getDataType(LPSTR *lpValue)
+{
+ INT counter = 0;
+ DWORD dwReturn = REG_SZ;
+
+ for (; counter < LAST_TYPE_MAP; counter++)
+ {
+ LONG len = strlen(typeMap[counter].mask);
+ if ( strncmp( *lpValue, typeMap[counter].mask, len) == IDENTICAL)
+ {
+ /*
+ * We found it, modify the value's pointer in order to skip the data
+ * type identifier, set the return value and exit the loop.
+ */
+ (*lpValue) += len;
+ dwReturn = typeMap[counter].dataType;
+ break;
+ }
+ }
+
+ return dwReturn;
+}
+/******************************************************************************
+ * Extracts from a [HKEY\some\key\path] type of line the key name (what starts
+ * after the first '\' and end before the ']'
+ */
+LPSTR getRegKeyName(LPSTR lpLine)
+{
+ LPSTR keyNameBeg = NULL;
+ LPSTR keyNameEnd = NULL;
+ char lpLineCopy[KEY_MAX_LEN];
+
+ if (lpLine == NULL)
+ return NULL;
+
+ strcpy(lpLineCopy, lpLine);
+
+ keyNameBeg = strstr(lpLineCopy, "\\"); /* The key name start by '\' */
+ keyNameBeg++; /* but is not part of the key name */
+ keyNameEnd = strstr(lpLineCopy, "]"); /* The key name end by ']' */
+ *keyNameEnd = NULL; /* Isolate the key name */
+
+ currentKeyName = HeapAlloc(GetProcessHeap(), 0, strlen(keyNameBeg)+1);
+ if (currentKeyName != NULL)
+ strcpy(currentKeyName, keyNameBeg);
+
+ return currentKeyName;
+}
+
+/******************************************************************************
+ * Extracts from a [HKEY/some/key/path] type of line the key class (what
+ * starts after the '[' and end before the first '\'
+ */
+static HKEY getRegClass(LPSTR lpClass)
+{
+ LPSTR classNameEnd;
+ LPSTR classNameBeg;
+
+ char lpClassCopy[KEY_MAX_LEN];
+
+ if (lpClass == NULL)
+ return ERROR_INVALID_PARAMETER;
+
+ strcpy(lpClassCopy, lpClass);
+
+ classNameEnd = strstr(lpClassCopy, "\\"); /* The class name end by '\' */
+ *classNameEnd = NULL; /* Isolate the class name */
+ classNameBeg = &lpClassCopy[1]; /* Skip the '[' */
+
+ if (strcmp( classNameBeg, "HKEY_LOCAL_MACHINE") == IDENTICAL )
+ return HKEY_LOCAL_MACHINE;
+ else if (strcmp( classNameBeg, "HKEY_USERS") == IDENTICAL )
+ return HKEY_USERS;
+ else if (strcmp( classNameBeg, "HKEY_CLASSES_ROOT") == IDENTICAL )
+ return HKEY_CLASSES_ROOT;
+ else if (strcmp( classNameBeg, "HKEY_CURRENT_CONFIG") == IDENTICAL )
+ return HKEY_CURRENT_CONFIG;
+ else if (strcmp( classNameBeg, "HKEY_CURRENT_USER") == IDENTICAL )
+ return HKEY_CURRENT_USER;
+ else
+ return ERROR_INVALID_PARAMETER;
+}
+
+/******************************************************************************
+ * Returns an allocated buffer with a cleaned copy (removed the surrounding
+ * dbl quotes) of the passed value.
+ */
+static LPSTR getArg( LPSTR arg)
+{
+ LPSTR tmp = NULL;
+ ULONG len;
+
+ if (arg == NULL)
+ return NULL;
+
+ /*
+ * Get rid of surrounding quotes
+ */
+ len = strlen(arg);
+
+ if( arg[len-1] == '\"' ) arg[len-1] = NULL;
+ if( arg[0] == '\"' ) arg++;
+
+ tmp = HeapAlloc(GetProcessHeap(), 0, strlen(arg)+1);
+ strcpy(tmp, arg);
+
+ return tmp;
+}
+
+/******************************************************************************
+ * Returns the index in the commands array of the command to process.
+ */
+static INT getCommand(LPSTR commandName)
+{
+ INT count;
+ for (count=0; count < COMMAND_COUNT; count++)
+ if ( strcmp(commandName, commandNames[count]) == IDENTICAL)
+ return count;
+
+ return COMMAND_NOT_FOUND;
+}
+
+/******************************************************************************
+ * Converts a hex representation of a DWORD into a DWORD.
+ */
+static DWORD convertHexToDWord(char *str, BYTE *buf)
+{
+ char *s = str; /* Pointer to current */
+ char *b = buf; /* Pointer to result */
+ ULONG strPos = 0;
+
+ memset(buf, 0, 4);
+
+ while (strPos < 4) /* 8 byte in a DWORD */
+ {
+ char xbuf[3];
+ char wc;
+
+ memcpy(xbuf,s,2); xbuf[2]='\0';
+ sscanf(xbuf,"%02x",(UINT*)&wc);
+ *b++ =(unsigned char)wc;
+
+ s+=2;
+ strPos+=1;
+ }
+
+ return 4; /* always 4 byte for the word */
+}
+
+/******************************************************************************
+ * Converts a hex buffer into a hex coma separated values
+ */
+static char* convertHexToHexCSV(BYTE *buf, ULONG bufLen)
+{
+ char* str;
+ char* ptrStr;
+ BYTE* ptrBuf;
+
+ ULONG current = 0;
+
+ str = HeapAlloc(GetProcessHeap(), 0, (bufLen+1)*2);
+ memset(str, 0, (bufLen+1)*2);
+ ptrStr = str; /* Pointer to result */
+ ptrBuf = buf; /* Pointer to current */
+
+ while (current < bufLen)
+ {
+ BYTE bCur = ptrBuf[current++];
+ char res[3];
+
+ sprintf(res, "%02x", (unsigned int)*&bCur);
+ strcat(str, res);
+ strcat(str, ",");
+ }
+
+ /* Get rid of the last coma */
+ str[strlen(str)-1] = NULL;
+ return str;
+}
+
+/******************************************************************************
+ * Converts a hex buffer into a DWORD string
+ */
+static char* convertHexToDWORDStr(BYTE *buf, ULONG bufLen)
+{
+ char* str;
+ char* ptrStr;
+ BYTE* ptrBuf;
+
+ ULONG current = 0;
+
+ str = HeapAlloc(GetProcessHeap(), 0, (bufLen*2)+1);
+ memset(str, 0, (bufLen*2)+1);
+ ptrStr = str; /* Pointer to result */
+ ptrBuf = buf; /* Pointer to current */
+
+ while (current < bufLen)
+ {
+ BYTE bCur = ptrBuf[current++];
+ char res[3];
+
+ sprintf(res, "%02x", (unsigned int)*&bCur);
+ strcat(str, res);
+ }
+
+ /* Get rid of the last coma */
+ return str;
+}
+/******************************************************************************
+ * Converts a hex coma separated values list into a hex list.
+ */
+static DWORD convertHexCSVToHex(char *str, BYTE *buf, ULONG bufLen)
+{
+ char *s = str; /* Pointer to current */
+ char *b = buf; /* Pointer to result */
+
+ ULONG strLen = strlen(str);
+ ULONG strPos = 0;
+ DWORD byteCount = 0;
+
+ memset(buf, 0, bufLen);
+
+ /*
+ * warn the user if we are here with a string longer than 2 bytes that does
+ * not contains ",". It is more likely because the data is invalid.
+ */
+ if ( ( strlen(str) > 2) && ( strstr(str, ",") == NULL) )
+ printf("regapi: WARNING converting CSV hex stream with no coma, "
+ "input data seems invalid.\n");
+
+ while (strPos < strLen)
+ {
+ char xbuf[3];
+ char wc;
+
+ memcpy(xbuf,s,2); xbuf[3]='\0';
+ sscanf(xbuf,"%02x",(UINT*)&wc);
+ *b++ =(unsigned char)wc;
+
+ s+=3;
+ strPos+=3;
+ byteCount++;
+ }
+
+ return byteCount;
+}
+
+
+/******************************************************************************
+ * Sets the value in argv[0] to the data in argv[1] for the currently
+ * opened key.
+ */
+static HRESULT setValue(LPSTR *argv)
+{
+ HRESULT hRes;
+ DWORD dwSize = KEY_MAX_LEN;
+ DWORD dwType = NULL;
+ DWORD dwDataType;
+
+ CHAR lpsCurrentValue[KEY_MAX_LEN];
+
+ LPSTR keyValue = argv[0];
+ LPSTR keyData = argv[1];
+
+ /* Make some checks */
+ if ( (keyValue == NULL) || (keyData == NULL) )
+ return ERROR_INVALID_PARAMETER;
+
+ /*
+ * Default registry values are encoded in the input stream as '@' but as
+ * blank in the wine registry.
+ */
+ if( (keyValue[0] == '@') && (strlen(keyValue) == 1) )
+ keyValue[0] = NULL;
+
+ /* Get the data type stored into the value field */
+ dwDataType = getDataType(&keyData);
+
+ memset(lpsCurrentValue, 0, KEY_MAX_LEN);
+ hRes = RegQueryValueExA(
+ currentKeyHandle,
+ keyValue,
+ NULL,
+ &dwType,
+ (LPBYTE)lpsCurrentValue,
+ &dwSize);
+
+ if( ( strlen(lpsCurrentValue) == 0 ) || /* The value is not existing */
+ ( bForce )) /* -force option */
+ {
+ LPBYTE lpbData;
+ BYTE convert[KEY_MAX_LEN];
+ DWORD dwLen;
+
+ if ( dwDataType == REG_SZ ) /* no convertion for string */
+ {
+ dwLen = strlen(keyData);
+ lpbData = keyData;
+ }
+ else if (dwDataType == REG_DWORD) /* Convert the dword types */
+ {
+ dwLen = convertHexToDWord(keyData, convert);
+ lpbData = convert;
+ }
+ else /* Convert the hexadecimal types */
+ {
+ dwLen = convertHexCSVToHex(keyData, convert, KEY_MAX_LEN);
+ lpbData = convert;
+ }
+
+ hRes = RegSetValueEx(
+ currentKeyHandle,
+ keyValue,
+ 0, /* Reserved */
+ dwDataType,
+ lpbData,
+ dwLen);
+ }
+ else
+ {
+ /* return the current value data into argv[1] */
+ if (argv[1] != NULL)
+ {
+ HeapFree(GetProcessHeap(), 0, argv[1]);
+ argv[1] = HeapAlloc(GetProcessHeap(), 0, dwSize+1);
+
+ if ( argv[1] != NULL )
+ strncpy(argv[1], lpsCurrentValue, dwSize);
+ }
+
+ return KEY_VALUE_ALREADY_SET;
+ }
+ return hRes;
+}
+
+
+/******************************************************************************
+ * Open the key
+ */
+static HRESULT openKey( LPSTR stdInput)
+{
+ DWORD dwDisp;
+ HRESULT hRes;
+
+ /* Sanity checks */
+ if (stdInput == NULL)
+ return ERROR_INVALID_PARAMETER;
+
+ /* Get the registry class */
+ currentKeyClass = getRegClass(stdInput); /* Sets global variable */
+ if (currentKeyClass == ERROR_INVALID_PARAMETER)
+ return ERROR_INVALID_PARAMETER;
+
+ /* Get the key name */
+ currentKeyName = getRegKeyName(stdInput); /* Sets global variable */
+ if (currentKeyName == NULL)
+ return ERROR_INVALID_PARAMETER;
+
+ hRes = RegCreateKeyEx(
+ currentKeyClass, /* Class */
+ currentKeyName, /* Sub Key */
+ 0, /* MUST BE 0 */
+ NULL, /* object type */
+ REG_OPTION_NON_VOLATILE, /* option, REG_OPTION_NON_VOLATILE ... */
+ KEY_ALL_ACCESS, /* access mask, KEY_ALL_ACCESS */
+ NULL, /* security attribute */
+ ¤tKeyHandle, /* result */
+ &dwDisp); /* disposition, REG_CREATED_NEW_KEY or
+ REG_OPENED_EXISTING_KEY */
+
+ if (hRes == ERROR_SUCCESS)
+ bTheKeyIsOpen = TRUE;
+
+ return hRes;
+
+}
+/******************************************************************************
+ * This function is a wrapper arround the setValue function. It prepares the
+ * land and clean the area once completed.
+ */
+static void processSetValue(LPSTR cmdline)
+{
+ LPSTR argv[SET_VALUE_MAX_ARGS]; /* args storage */
+
+ LPSTR token = NULL; /* current token analized */
+ ULONG argCounter = 0; /* counter of args */
+ INT counter;
+ HRESULT hRes = NULL;
+
+ /*
+ * Init storage and parse the line
+ */
+ for (counter=0; counter<SET_VALUE_MAX_ARGS; counter++)
+ argv[counter]=NULL;
+
+ while( (token = strsep(&cmdline, setValueDelim[argCounter])) != NULL )
+ {
+ argv[argCounter++] = getArg(token);
+
+ if (argCounter == SET_VALUE_MAX_ARGS)
+ break; /* Stop processing args no matter what */
+ }
+
+ hRes = setValue(argv);
+ if ( hRes == ERROR_SUCCESS )
+ printf(
+ "regapi: Value \"%s\" has been set to \"%s\" in key [%s]\n",
+ argv[0],
+ argv[1],
+ currentKeyName);
+
+ else if ( hRes == KEY_VALUE_ALREADY_SET )
+ printf(
+ "regapi: Value \"%s\" already set to \"%s\" in key [%s]\n",
+ argv[0],
+ argv[1],
+ currentKeyName);
+
+ else
+ printf("regapi: ERROR Key %s not created. Value: %s, Data: %s\n",
+ currentKeyName,
+ argv[0],
+ argv[1]);
+
+ /*
+ * Do some cleanup
+ */
+ for (counter=0; counter<argCounter; counter++)
+ if (argv[counter] != NULL)
+ HeapFree(GetProcessHeap(), 0, argv[counter]);
+}
+
+/******************************************************************************
+ * This function is a wrapper arround the queryValue function. It prepares the
+ * land and clean the area once completed.
+ */
+static void processQueryValue(LPSTR cmdline)
+{
+ LPSTR argv[QUERY_VALUE_MAX_ARGS];/* args storage */
+ LPSTR token = NULL; /* current token analized */
+ ULONG argCounter = 0; /* counter of args */
+ INT counter;
+ HRESULT hRes = NULL;
+ LPSTR keyValue = NULL;
+ LPSTR lpsRes = NULL;
+
+ /*
+ * Init storage and parse the line
+ */
+ for (counter=0; counter<QUERY_VALUE_MAX_ARGS; counter++)
+ argv[counter]=NULL;
+
+ while( (token = strsep(&cmdline, queryValueDelim[argCounter])) != NULL )
+ {
+ argv[argCounter++] = getArg(token);
+
+ if (argCounter == QUERY_VALUE_MAX_ARGS)
+ break; /* Stop processing args no matter what */
+ }
+
+ /* The value we look for is the first token on the line */
+ if ( argv[0] == NULL )
+ return; /* SHOULD NOT OCCURS */
+ else
+ keyValue = argv[0];
+
+ if( (keyValue[0] == '@') && (strlen(keyValue) == 1) )
+ {
+ LONG lLen = KEY_MAX_LEN;
+ CHAR lpsData[KEY_MAX_LEN];
+ /*
+ * We need to query the key default value
+ */
+ hRes = RegQueryValue(
+ currentKeyHandle,
+ currentKeyName,
+ (LPBYTE)lpsData,
+ &lLen);
+
+ if (hRes == ERROR_SUCCESS)
+ {
+ lpsRes = HeapAlloc( GetProcessHeap(), 0, lLen);
+ strncpy(lpsRes, lpsData, lLen);
+ }
+ }
+ else
+ {
+ DWORD dwLen = KEY_MAX_LEN;
+ BYTE lpbData[KEY_MAX_LEN];
+ DWORD dwType;
+ /*
+ * We need to query a specific value for the key
+ */
+ hRes = RegQueryValueEx(
+ currentKeyHandle,
+ keyValue,
+ 0,
+ &dwType,
+ (LPBYTE)lpbData,
+ &dwLen);
+
+ if (hRes == ERROR_SUCCESS)
+ {
+ /*
+ * Convert the returned data to a displayable format
+ */
+ switch ( dwType )
+ {
+ case REG_SZ:
+ {
+ lpsRes = HeapAlloc( GetProcessHeap(), 0, dwLen);
+ strncpy(lpsRes, lpbData, dwLen);
+ break;
+ }
+ case REG_DWORD:
+ {
+ lpsRes = convertHexToDWORDStr(lpbData, dwLen);
+ break;
+ }
+ default:
+ {
+ lpsRes = convertHexToHexCSV(lpbData, dwLen);
+ break;
+ }
+ }
+ }
+ }
+
+
+ if ( hRes == ERROR_SUCCESS )
+ printf(
+ "regapi: Value \"%s\" = \"%s\" in key [%s]\n",
+ keyValue,
+ lpsRes,
+ currentKeyName);
+
+ else
+ printf("regapi: ERROR Value \"%s\" not found. for key \"%s\"\n",
+ keyValue,
+ currentKeyName);
+
+ /*
+ * Do some cleanup
+ */
+ for (counter=0; counter<argCounter; counter++)
+ if (argv[counter] != NULL)
+ HeapFree(GetProcessHeap(), 0, argv[counter]);
+
+ if (lpsRes != NULL)
+ HeapFree(GetProcessHeap(), 0, lpsRes);
+
+}
+
+/******************************************************************************
+ * Close the currently opened key.
+ */
+static void closeKey()
+{
+ RegCloseKey(currentKeyHandle);
+
+ HeapFree(GetProcessHeap(), 0, currentKeyName); /* Allocated by getKeyName */
+
+ bTheKeyIsOpen = FALSE;
+
+ currentKeyName = NULL;
+ currentKeyClass = NULL;
+ currentKeyHandle = NULL;
+}
+
+/******************************************************************************
+ * This funtion is the main entry point to the setValue type of action. It
+ * receives the currently read line and dispatch the work depending on the
+ * context.
+ */
+static void doSetValue(LPSTR stdInput)
+{
+ /*
+ * We encoutered the end of the file, make sure we
+ * close the opened key and exit
+ */
+ if (stdInput == NULL)
+ {
+ if (bTheKeyIsOpen != FALSE)
+ closeKey();
+
+ return;
+ }
+
+ if ( stdInput[0] == '[') /* We are reading a new key */
+ {
+ if ( bTheKeyIsOpen != FALSE )
+ closeKey(); /* Close the previous key before */
+
+ if ( openKey(stdInput) != ERROR_SUCCESS )
+ printf ("regapi: doSetValue failed to open key %s\n", stdInput);
+ }
+ else if( ( bTheKeyIsOpen ) &&
+ (( stdInput[0] == '@') || /* reading a default @=data pair */
+ ( stdInput[0] == '\"'))) /* reading a new value=data pair */
+ {
+ processSetValue(stdInput);
+ }
+ else /* since we are assuming that the */
+ { /* file format is valid we must */
+ if ( bTheKeyIsOpen ) /* be reading a blank line which */
+ closeKey(); /* indicate end of this key processing */
+ }
+}
+
+/******************************************************************************
+ * This funtion is the main entry point to the queryValue type of action. It
+ * receives the currently read line and dispatch the work depending on the
+ * context.
+ */
+static void doQueryValue(LPSTR stdInput) {
+ /*
+ * We encoutered the end of the file, make sure we
+ * close the opened key and exit
+ */
+ if (stdInput == NULL)
+ {
+ if (bTheKeyIsOpen != FALSE)
+ closeKey();
+
+ return;
+ }
+
+ if ( stdInput[0] == '[') /* We are reading a new key */
+ {
+ if ( bTheKeyIsOpen != FALSE )
+ closeKey(); /* Close the previous key before */
+
+ if ( openKey(stdInput) != ERROR_SUCCESS )
+ printf ("regapi: doSetValue failed to open key %s\n", stdInput);
+ }
+ else if( ( bTheKeyIsOpen ) &&
+ (( stdInput[0] == '@') || /* reading a default @=data pair */
+ ( stdInput[0] == '\"'))) /* reading a new value=data pair */
+ {
+ processQueryValue(stdInput);
+ }
+ else /* since we are assuming that the */
+ { /* file format is valid we must */
+ if ( bTheKeyIsOpen ) /* be reading a blank line which */
+ closeKey(); /* indicate end of this key processing */
+ }
+}
+
+/******************************************************************************
+ * This funtion is the main entry point to the deletetValue type of action. It
+ * receives the currently read line and dispatch the work depending on the
+ * context.
+ */
+static void doDeleteValue(LPSTR line) {
+ printf ("regapi: deleteValue not yet implemented\n");
+}
+/******************************************************************************
+ * This funtion is the main entry point to the deleteKey type of action. It
+ * receives the currently read line and dispatch the work depending on the
+ * context.
+ */
+static void doDeleteKey(LPSTR line) {
+ printf ("regapi: deleteKey not yet implemented\n");
+}
+/******************************************************************************
+ * This funtion is the main entry point to the createKey type of action. It
+ * receives the currently read line and dispatch the work depending on the
+ * context.
+ */
+static void doCreateKey(LPSTR line) {
+ printf ("regapi: createKey not yet implemented\n");
+}
+
+/******************************************************************************
+ * MAIN - The main simply validate the first parameter (command to perform)
+ * It then read the STDIN lines by lines forwarding their processing
+ * to the appropriate method.
+ */
+int PASCAL WinMain (HANDLE inst, HANDLE prev, LPSTR cmdline, int show)
+{
+ LPSTR token = NULL; /* current token analized */
+ LPSTR stdInput = NULL; /* line read from stdin */
+ INT cmdIndex = -1; /* index of the command in array */
+
+ stdInput = HeapAlloc(GetProcessHeap(), 0, STDIN_MAX_LEN);
+ if (stdInput == NULL)
+ return NOT_ENOUGH_MEMORY;
+
+ /*
+ * get the command, should be the first arg (modify cmdLine)
+ */
+ token = strsep(&cmdline, " ");
+ if (token != NULL)
+ {
+ cmdIndex = getCommand(token);
+ if (cmdIndex == COMMAND_NOT_FOUND)
+ {
+ printf("regapi: Command \"%s\" is not supported.\n", token);
+ printf(helpText);
+ return COMMAND_NOT_SUPPORTED;
+ }
+ }
+ else
+ {
+ printf(
+ "regapi: The first item on the command line must be the command name.\n");
+ printf(helpText);
+ return COMMAND_NOT_SUPPORTED;
+ }
+
+ /*
+ * check to see weather we force the action
+ * (meaning differ depending on the command performed)
+ */
+ if ( cmdline != NULL ) /* will be NULL if '-force' is not provided */
+ if ( strstr(cmdline, "-force") != NULL )
+ bForce = TRUE;
+
+ printf("Processing stdin...\n");
+
+ while ( TRUE )
+ {
+ /*
+ * read a line
+ */
+ stdInput = fgets(stdInput, STDIN_MAX_LEN, stdin);
+
+ /*
+ * Make some handy generic stuff here...
+ */
+ if ( stdInput != NULL )
+ {
+ stdInput[strlen(stdInput) -1] = NULL; /* get rid of new line */
+
+ if( stdInput[0] == '#' ) /* this is a comment, skip */
+ continue;
+ }
+
+ /*
+ * We process every lines even the NULL (last) line, to indicate the
+ * end of the processing to the specific process.
+ */
+ commandAPIs[cmdIndex](stdInput);
+
+ if (stdInput == NULL) /* EOF encountered */
+ break;
+ }
+
+ /*
+ * Save the registry only if it was modified
+ */
+ if ( commandSaveRegistry[cmdIndex] != FALSE )
+ SHELL_SaveRegistry();
+
+ HeapFree(GetProcessHeap(), 0, stdInput);
+ return SUCCESS;
+}
+
+
+