/* * Command line Registry implementation * * Copyright 1999 Sylvain St-Germain * * Note: Please consult the README file for more information. * */ #include #include #include #include #include #include #include /****************************************************************************** * Defines and consts */ #define IDENTICAL 0 #define COMMAND_COUNT 7 #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 = 0; static HKEY currentKeyHandle = 0; static BOOL bTheKeyIsOpen = FALSE; /* Delimiters used to parse the "value"="data" pair for setValue*/ #define SET_VALUE_MAX_ARGS 2 /* Delimiters 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(10):", REG_RESOURCE_REQUIREMENTS_LIST}, {"hex(80000000):", 0x80000000}, {"hex(80000001):", 0x80000001}, {"hex(80000002):", 0x80000002}, {"hex(80000003):", 0x80000003}, {"hex(80000004):", 0x80000004}, {"hex(80000005):", 0x80000005}, {"hex(80000006):", 0x80000006}, {"hex(80000007):", 0x80000007}, {"hex(80000008):", 0x80000008}, {"hex(80000009):", 0x80000000}, {"hex(8000000a):", 0x8000000A} }; 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); static void doRegisterDLL(LPSTR lpsLine); static void doUnregisterDLL(LPSTR lpsLine); /* * Currently supported api */ static const char* commandNames[COMMAND_COUNT] = { "setValue", "deleteValue", "createKey", "deleteKey", "queryValue", "registerDLL", "unregisterDLL" }; /* * Pointers to processing entry points */ static const commandAPI commandAPIs[COMMAND_COUNT] = { doSetValue, doDeleteValue, doCreateKey, doDeleteKey, doQueryValue, doRegisterDLL, doUnregisterDLL }; /* * This array controls the registry saving needs at the end of the process */ static const BOOL commandSaveRegistry[COMMAND_COUNT] = { TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE }; /* * Generic prototypes */ static DWORD 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 displayed when invalid parameters are provided */ static char helpText[] = "NAME\n" " regapi - provide a command line interface to the wine registry.\n" "\n" "SYNOPSIS\n" " regapi commandName [-force] < file\n" "\n" "DESCRIPTION\n" " regapi allows editing the wine registry. It processes the given\n" " commandName for every line in the stdin data stream. Input data\n" " format may vary depending on the commandName see INPUT FILE FORMAT.\n" "\n" "OPTIONS\n" " commandName\n" " Instruct regapi about what action to perform on the data stream.\n" " Currently, only setValue and queryValue are supported and\n" " implemented.\n" "\n" " -force\n" " When provided the action will be performed anyway. This may\n" " have a different meaning depending on the context. For example,\n" " when providing -force to setValue, the value is set even if it\n" " was previously set to another value.\n" "\n" " < file\n" " STDIN channel, provide a file name with line of the appropriate\n" " format.\n" "\n" "INPUT FILE FORMAT\n" "\n" " setValue\n" " The input file format required by the setValue command is similar\n" " to the one obtained from regedit.exe export option. The only\n" " difference is that multi line values are not supported, the\n" " value data must be on a single line.\n" "\n" " [KEY_CLASS\\Some\\Path\\For\\A\\Key]\n" " \"Value1\"=\"Data1\"\n" " \"Value2\"=\"Data2\"\n" " \"Valuen\"=\"Datan\"\n" " ...\n" "\n" " queryValue\n" " The input file format required by the queryValue command is\n" " similar to the one required by setValue. The only\n" " difference is that you only provide the value name.\n" "\n" " [KEY_CLASS\\Some\\Path\\For\\A\\Key]\n" " \"Value1\"\n" " \"Value2\"\n" " \"Valuen\"\n" " ...\n" " registerDLL\n" " The input file format is a list of DLLs to register\n" "\n" " unregisterDLL\n" " The input file format is a list of DLLs to unregister\n" " February 1999.\n" ; /****************************************************************************** * This function returns the HKEY associated with the data type encoded in the * value. It modifies the input parameter (key value) in order to skip this * "now useless" data type information. */ DWORD getDataType(LPSTR *lpValue) { INT counter = 0; DWORD dwReturn = REG_SZ; for (; counter < LAST_TYPE_MAP; counter++) { LONG len = strlen(typeMap[counter].mask); if ( strncasecmp( *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 = '\0'; /* 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 ends before the first '\' */ static HKEY getRegClass(LPSTR lpClass) { LPSTR classNameEnd; LPSTR classNameBeg; char lpClassCopy[KEY_MAX_LEN]; if (lpClass == NULL) return (HKEY)ERROR_INVALID_PARAMETER; strcpy(lpClassCopy, lpClass); classNameEnd = strstr(lpClassCopy, "\\"); /* The class name end by '\' */ *classNameEnd = '\0'; /* 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 (HKEY)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] = '\0'; 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 comma 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 comma */ str[strlen(str)-1] = '\0'; 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 comma */ return str; } /****************************************************************************** * Converts a hex comma 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 comma, " "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 = 0; DWORD dwDataType; LPSTR lpsCurrentValue; LPSTR keyValue = argv[0]; LPSTR keyData = argv[1]; /* Make some checks */ if ( (keyValue == NULL) || (keyData == NULL) ) return ERROR_INVALID_PARAMETER; lpsCurrentValue=HeapAlloc(GetProcessHeap(), 0,KEY_MAX_LEN); /* * 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] = '\0'; /* 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); while(hRes==ERROR_MORE_DATA){ dwSize+=KEY_MAX_LEN; lpsCurrentValue=HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,lpsCurrentValue,dwSize); 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); argv[1][dwSize]='\0'; } } 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 == (HKEY)ERROR_INVALID_PARAMETER) return (HRESULT)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 for 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 = 0; /* * Init storage and parse the line */ for (counter=0; counter currentSize){ stdInput=HeapReAlloc(GetProcessHeap(),0,stdInput,strlen(stdInput)+STDIN_MAX_LEN); currentSize+=STDIN_MAX_LEN; } strcat(stdInput,nextLine+2); } } /* * 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; } #if 0 /* * Save the registry only if it was modified */ if ( commandSaveRegistry[cmdIndex] != FALSE ) SHELL_SaveRegistry(); #endif HeapFree(GetProcessHeap(), 0, nextLine); HeapFree(GetProcessHeap(), 0, stdInput); return SUCCESS; }