Created
October 5, 2011 15:14
-
-
Save kulp/1264689 to your computer and use it in GitHub Desktop.
Convert .REG (exported registry files) into .NSH-style registry commands
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
reg2nsh.c : Converts .REG (exported registry files) into .NSH-style | |
registry commands, for use with the NSIS ( http://nsis.sf.net/ ). | |
$Rev$ | |
$Date$ | |
Copyright (c) 2005 Darren Kulp under the terms of the MIT License | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the | |
Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included | |
in all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
//TODO: Add support for more key types like REG_MULTI_SZ by flagging their | |
// existence and appending NSIS functions that handle them. Reference | |
// http://nsis.sourceforge.net/Docs/AppendixD.html#D.6 to get this working. | |
// Note: This program is likely to remain undeveloped, since a (regrettably | |
// non-libre) version is available at http://aarrtteemm.nm.ru/. | |
/* Tabstops at 4 */ | |
#include "reg2nsh.h" | |
int main(int argc, char *argv[]) | |
{ | |
int i = 0, | |
header = 0, | |
lineno = 0, | |
havekey = 0; | |
FILE *inputfile = stdin; | |
FILE *outputfile = stdout; | |
regkey *rootkey = (regkey *)malloc(sizeof(regkey)); | |
regkey *subkey = (regkey *)malloc(sizeof(regkey)); | |
char *lastkey = (char *)malloc(MAX_KEY_LENGTH); | |
char *line = (char *)malloc(MAX_LINE_LENGTH); | |
char *tmp = (char *)malloc(MAX_LINE_LENGTH); | |
if (line == NULL || lastkey == NULL || tmp == NULL || | |
rootkey == NULL || subkey == NULL) { | |
fprintf(stderr, "malloc() failed at code line %d!\n", __LINE__); | |
//FIXME: Memory is not freed, but atexit() would requires global vars | |
return E_BAD_PTR; | |
} | |
for (i = 0; i < argc; i++) | |
if (STREQ(argv[i], "-h") || | |
STREQ(argv[i], "--help")) | |
return usage(argv[0]); | |
switch (argc) { | |
case 3: | |
outputfile = fopen(argv[2], "w"); | |
// fallthrough | |
case 2: | |
inputfile = fopen(argv[1], "r"); | |
// fallthrough | |
case 1: | |
break; | |
default: | |
return usage(argv[0]); | |
} | |
if (inputfile == NULL) { | |
fprintf(stderr, "Could not open input filehandle %d!\n", (int)inputfile); | |
return E_BAD_PTR; | |
} | |
if (outputfile == NULL) { | |
fprintf(stderr, "Could not open output filehandle %d!\n", (int)outputfile); | |
return E_BAD_PTR; | |
} | |
while (get_mogrified_line(inputfile, line) == E_OK) { | |
lineno++; | |
if (line == NULL) { | |
fprintf(stderr, "Bad pointer at input line %d!\n", lineno); | |
return E_BAD_PTR; | |
} | |
if (!header) | |
if (STREQN("Windows Registry Editor", line , 23) || //plain | |
STREQN("Windows Registry Editor", line + 2 , 23)) { //.REG | |
if (lineno == 1) { | |
header = 1; | |
continue; | |
} else { | |
fprintf(stderr, "Unexpected header at line %d!\n", lineno); | |
return E_BAD_INPUT; | |
} | |
} | |
// Is the line continued ("\" at the end)? | |
while (*(line + strlen(line) - 2) == '\\') { | |
if (get_mogrified_line(inputfile, tmp) == E_EOF) { | |
fprintf(stderr, "EOF reached: expected continued data line!\n"); | |
return E_BAD_INPUT; | |
} else | |
lineno++; | |
if (tmp == NULL) { | |
fprintf(stderr, "Bad pointer at data line %d!\n", lineno); | |
return E_BAD_PTR; | |
} | |
tmp += 2; // Skip spaces at beginning of continued-string line | |
*(line + strlen(line) - 2) = '\0'; // Append null | |
strcat(line, tmp); | |
} | |
if (STREQN("[", line, 1)) { // Is this a key? | |
strncpy(lastkey, line + 1, strlen(line) - 2); | |
*(lastkey + strlen(line) - 2) = '\0'; // Append a null | |
if (parsekeyname(lastkey, rootkey, subkey)) { | |
fprintf(stderr, "Cannot parse key at line %d!\n", lineno); | |
return E_BAD_INPUT; | |
} | |
havekey++; //FIXME: Is havekey useful > 1? | |
continue; | |
} else if (STREQN("\"", line, 1) || STREQN("@", line, 1)) { | |
if (!havekey) { | |
fprintf(stderr, "No key specified for values at line %d!\n", | |
lineno); | |
return E_BAD_INPUT; | |
} | |
if (printvalue(line, rootkey, subkey, outputfile)) { | |
fprintf(stderr, "Cannot parse value at line %d!\n", lineno); | |
return E_BAD_INPUT; | |
} | |
} else if (strlen(line) == 0) // Empty lines mean nothing | |
continue; | |
else { | |
fprintf(stderr, "Bad input at input line %d!\n", lineno); | |
return E_BAD_INPUT; | |
} | |
} | |
free(line); | |
free(lastkey); | |
free(rootkey); | |
free(subkey); | |
fclose(inputfile); | |
fclose(outputfile); | |
return 0; | |
} | |
int printvalue(char *value, regkey *rootkey, regkey *subkey, FILE *outputfile) | |
{ | |
/* | |
* Prints the NSH statement that corresponds to a value entry in the source | |
* REG file. | |
*/ | |
regvalue *current = parsevalue(value); | |
char *tmp; | |
if (current == NULL) { | |
fprintf(stderr, "Received invalid parse result!\n"); | |
return E_BAD_PTR; | |
} | |
switch (current->type /*& ~REG_HEX_FORMAT*/) { | |
case REG_EXPAND_SZ: | |
case REG_BINARY: | |
fprintf(outputfile, "WriteRegBin"); | |
nsh_line_out(rootkey->name, subkey->name, current->name, outputfile); | |
if ((tmp = flatten_binary_data(current->value)) == NULL) { | |
fprintf(stderr, "Cannot print invalid binary data!\n"); | |
return E_BAD_PTR; | |
} | |
fprintf(outputfile, " %s\n", tmp); | |
break; | |
case REG_DWORD: | |
fprintf(outputfile, "WriteRegDWORD"); | |
nsh_line_out(rootkey->name, subkey->name, current->name, outputfile); | |
// Squelch the 'dword:' | |
fprintf(outputfile, " 0x%s\n", current->value + 6); | |
break; | |
case REG_SZ: // Value contains '"' already | |
fprintf(outputfile, "WriteRegStr"); | |
nsh_line_out(rootkey->name, subkey->name, current->name, outputfile); | |
fprintf(outputfile, " %s\n", current->value); | |
break; | |
default: | |
fprintf(stderr, "Unknown value type %d!\n", current->type); | |
return E_BAD_INPUT; | |
} | |
return E_OK; | |
} | |
regvalue *parsevalue(char *value) | |
{ | |
/* | |
* Distinguishes semantic tokens from "data" ones while extracting the two | |
* fields (name and value) in each value struct. | |
*/ | |
regvalue *current = (regvalue *)malloc(sizeof(regvalue)); | |
char *tmp = (char *)malloc(MAX_LINE_LENGTH); | |
char *i = value, // tracks backslashes | |
*j = value, // finds '=', semantic or not | |
*k = value; // finds '"' | |
int noescapes = 0, // If no escaped characters in string | |
firstrun = 1; | |
int defaultkey = 0; | |
/* | |
* Find escaped chars (usually quote marks) and skip them. While not | |
* passing the real '=', find the second real quote. The '=' is just | |
* after that. | |
*/ | |
do { | |
if (!noescapes && (i = strchr(i,'\\')) == NULL) | |
noescapes = 1; | |
j = strchr(j, '='); | |
if (j == NULL) { | |
fprintf(stderr, "Error parsing value: no equals sign found\n"); | |
return NULL; | |
} | |
if (firstrun) { | |
firstrun = 0; | |
// Find the first quote mark, then skip it | |
k = strchr(k, '"'); | |
if (k > j) { | |
// Value name is probably '@' | |
if (*value == '@') { | |
// Default key, all right | |
defaultkey++; | |
k = j - 1; | |
break; | |
} | |
} | |
//FIXME: This k++ is not obvious | |
if (k != value || k++ == NULL) { | |
fprintf(stderr, "Garbage at beginning of line!\n"); | |
return NULL; | |
} | |
} | |
k = strchr(k,'"'); | |
if (!noescapes && k == i + 1) { // The '"' is escaped, not meaningful | |
// FIXME Quotes in values are not handled properly (need to be escaped with '$\' ) | |
i += 2; // Skip the escaped char and carry on | |
continue; | |
} | |
if (j == k + 1) // We found the end of the value name | |
break; | |
if (k > j) // That '=' was in the name | |
while (k > j) | |
j = strchr(++j,'='); | |
else | |
k++; | |
} while (1); | |
if (defaultkey) { | |
strcpy(current->name, ""); | |
} else { | |
strncpy(tmp, value + 1, (size_t)(k - value - 1)); | |
*(tmp + (k - value - 1)) = '\0'; // Chop off all but the value name | |
strcpy(current->name, tmp); | |
} | |
strncpy(tmp, j + 1, strlen(value) - (j - value + 1)); | |
*(tmp + (strlen(value) - (j - value + 1))) = '\0'; // Denote end of value | |
strcpy(current->value, tmp); | |
current->type = gettype(tmp); | |
if (current->type < 0) { | |
fprintf(stderr, "Unrecognizable value '%s' (type %d)!\n", value, | |
current->type); | |
return NULL; | |
} | |
free(tmp); | |
return current; | |
} | |
int gettype(char *value) | |
{ | |
/* | |
* Deduces the type of value passed in by its prefix. Not yet fully | |
* implemented. | |
*/ | |
int rval; | |
// Returns the type of value passed in | |
if (STREQN(value, "\"", 1)) rval = REG_SZ; else | |
if (STREQN(value, "dword", 5)) rval = REG_DWORD; else | |
if (STREQN(value, "hex", 3)) { | |
//TODO: add support for all these | |
if (STREQN(value + 3, ":", 1)) rval = REG_BINARY; else | |
// if (STREQN(value + 3, "(0):", 3)) rval = REG_NONE; else | |
if (STREQN(value + 3, "(1):", 3)) rval = REG_SZ; else | |
if (STREQN(value + 3, "(2):", 3)) rval = REG_EXPAND_SZ; else | |
if (STREQN(value + 3, "(3):", 3)) rval = REG_BINARY; /*else | |
if (STREQN(value + 3, "(4):", 3)) rval = REG_DWORD; else | |
if (STREQN(value + 3, "(5):", 3)) rval = REG_DWORD_BIG_ENDIAN; else | |
if (STREQN(value + 3, "(6):", 3)) rval = REG_LINK; else | |
if (STREQN(value + 3, "(7):", 3)) rval = REG_MULTI_SZ;*/ | |
rval |= REG_HEX_FORMAT; | |
} else | |
rval = -E_BAD_INPUT; | |
return rval; | |
} | |
int parsekeyname(char *value, regkey *rootkey, regkey *subkey) | |
{ | |
/* | |
* Parses the key name out of the appropriate input-file lines and returns | |
* pointers to structs describing the full key as a combination of a root | |
* key and a combined subkey. | |
*/ | |
char *loc = strchr(value, '\\'); // Marks the end of the base key | |
char *tmp = (char *)malloc(MAX_LINE_LENGTH); | |
if (loc == NULL) // dealing with root key only (no subkey) | |
loc = strlen(value) + value; | |
// Copy rootkey name into tmp | |
strncpy(tmp, value, (size_t)(loc - value)); | |
// strlen(rootkey name) == loc - value | |
*(tmp + (loc - value)) = '\0'; // Chop off all but rootkey name | |
if (STREQ("HKEY_USERS", tmp)) strcpy(tmp, "HKU" ); else | |
if (STREQ("HKEY_CLASSES_ROOT", tmp)) strcpy(tmp, "HKCR"); else | |
if (STREQ("HKEY_CURRENT_USER", tmp)) strcpy(tmp, "HKCU"); else | |
if (STREQ("HKEY_LOCAL_MACHINE", tmp)) strcpy(tmp, "HKLM"); else | |
if (STREQ("HKEY_CURRENT_CONFIG", tmp)) strcpy(tmp, "HKCC"); | |
else { | |
fprintf(stderr, "Unknown root key '%s'!\n", tmp); | |
return E_BAD_INPUT; | |
} | |
strcpy(rootkey->name, tmp); | |
rootkey->parent = NULL; | |
// Are we are dealing with a root key only? | |
if (loc == strlen(value) + value) { // Yes | |
strcpy(subkey->name, ""); | |
subkey->parent = NULL; | |
} else { // No | |
// Copy subkey name into tmp | |
strncpy(tmp, loc + 1, (size_t)(strlen(value) - (loc - value) - 1)); | |
*(tmp + (strlen(value) - (loc - value) - 1)) = '\0'; | |
strcpy(subkey->name, tmp); | |
subkey->parent = rootkey; | |
} | |
free(tmp); | |
return E_OK; | |
} | |
int usage(char *name) | |
{ | |
fprintf(stderr, "Converts a Windows Registry export file (.REG) to NSIS syntax\n\n"); | |
fprintf(stderr, "Usage: %s [inputfile [outputfile]]\n", name); | |
fprintf(stderr, "inputfile and outputfile default to stdin and stdout.\n"); | |
return E_BAD_ARGS; | |
} | |
char *flatten_binary_data(char *data) | |
{ | |
/* | |
* Return a comma-stripped version of the hex data passed in. | |
*/ | |
char *colon = strchr(data, ':'); | |
char *output = (char *)malloc(strlen(data)); | |
char *tmp; | |
if (colon == NULL) { | |
fprintf(stderr, "Cannot decode input binary data: no colon!\n"); | |
return NULL; | |
} else | |
colon++; | |
*output = '\0'; | |
tmp = strtok(colon, ","); | |
do | |
strcat(output, tmp); | |
while ((tmp = strtok(NULL, ",")) != NULL); | |
return output; | |
} | |
int stripline(char *input, char *output) | |
{ | |
/* | |
* Kills nulls and ending newlines in the input. | |
*/ | |
int i = 0, | |
j = 0; | |
for (i = 0; input[i] != '\n' && input[i] != '\015'; i++) { | |
if (input[i]) { | |
output[j++] = input[i]; | |
} | |
} | |
output[j] = '\0'; | |
return E_OK; | |
} | |
int get_mogrified_line(FILE *inputfile, char *line) | |
{ | |
/* | |
* Properly mogrifies the input lines and returns uniformly-formatted | |
* strings regardless of input file type. | |
*/ | |
char *output = (char *)malloc(MAX_LINE_LENGTH); | |
char *input = fgets(line, MAX_LINE_LENGTH - 1, inputfile); | |
int rval = E_OK; | |
if (output == NULL) { | |
fprintf(stderr, "Bad pointer at code line %d!", __LINE__); | |
return E_BAD_PTR; | |
} | |
if (input == NULL) { | |
output[0] = '\0'; | |
rval = E_EOF; | |
} else if (stripline(input, output) != E_OK) // EOF | |
rval = E_EOF; | |
if (rval == E_OK) { | |
strcpy(line,output); | |
strcpy(input,line); | |
} else { | |
free(output); | |
} | |
return rval; | |
} | |
void nsh_line_out(char *rkey, char *skey, char *value, FILE *outputfile) | |
{ | |
fprintf(outputfile, " %s \"%s\" \"%s\"", rkey, skey, value); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
reg2nsh.h : header for reg2nsh.c. | |
$Rev$ | |
$Date$ | |
Copyright (c) 2005 Darren Kulp under the terms of the MIT License | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the | |
Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included | |
in all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#ifdef HAVE_MALLOC_H | |
# ifdef DARWIN | |
# include <sys/malloc.h> | |
# endif | |
#endif | |
#define REG_NONE 0 | |
#define REG_SZ 1 | |
#define REG_EXPAND_SZ 2 | |
#define REG_BINARY 3 | |
#define REG_DWORD 4 | |
#define REG_DWORD_LITTLE_ENDIAN 4 | |
#define REG_DWORD_BIG_ENDIAN 5 | |
#define REG_LINK 6 | |
#define REG_MULTI_SZ 7 | |
// This arbitrary constant is ORed with values given in a hexadecimal format | |
#define REG_HEX_FORMAT 100 | |
//FIXME: Make this less arbitrary | |
#define MAX_LINE_LENGTH 2048 | |
// This constant is from the spec | |
#define MAX_KEY_LENGTH 256 | |
#define STREQ(a,b) (strcmp(a,b) == 0) | |
#define STREQN(a,b,c) (strncmp(a,b,c) == 0) | |
enum RETURNCODE { | |
E_OK = 0, | |
E_BAD_PTR, | |
E_BAD_INPUT, | |
E_BAD_ARGS, | |
E_EOF | |
}; | |
typedef struct _regkey { | |
struct _regkey *parent; | |
char name[MAX_KEY_LENGTH]; | |
} regkey; | |
typedef struct _regvalue { | |
short type; | |
char name[MAX_LINE_LENGTH]; | |
char value[MAX_LINE_LENGTH]; | |
} regvalue; | |
char *flatten_binary_data(char *data); | |
int usage(char *name); | |
int gettype(char *value); | |
int stripline(char *data, char *output); | |
int get_mogrified_line(FILE *inputfile, char *line); | |
int parsekeyname(char *value, regkey *rootkey, regkey *subkey); | |
int printvalue(char *value, regkey *rootkey, regkey *subkey, FILE *ouputfile); | |
regvalue *parsevalue(char *value); | |
void nsh_line_out(char *rkey, char *skey, char *value, FILE * outputfile); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment