/* * 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] = {""}; /* * 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, DWORD* parse_type); 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 - perform certain actions on the wine registry.\n" "\n" "SYNOPSIS\n" " regapi commandName [-force] < file\n" "\n" "DESCRIPTION\n" " regapi modifies settings in the wine registry. It processes\n" " the given commandName for every line in the stdin data stream.\n" " Input data format may vary depending on the commandName\n" " (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. * * Note: Updated based on the algorithm used in 'server/registry.c' */ DWORD getDataType(LPSTR *lpValue, DWORD* parse_type) { struct data_type { const char *tag; int len; int type; int parse_type; }; static const struct data_type data_types[] = { /* actual type */ /* type to assume for parsing */ { "\"", 1, REG_SZ, REG_SZ }, { "str:\"", 5, REG_SZ, REG_SZ }, { "str(2):\"", 8, REG_EXPAND_SZ, REG_SZ }, { "str(7):\"", 8, REG_MULTI_SZ, REG_SZ }, { "hex:", 4, REG_BINARY, REG_BINARY }, { "dword:", 6, REG_DWORD, REG_DWORD }, { "hex(", 4, -1, REG_BINARY }, { NULL, 0, 0, 0 } }; const struct data_type *ptr; int type; for (ptr = data_types; ptr->tag; ptr++) { if (memcmp( ptr->tag, *lpValue, ptr->len )) continue; /* Found! */ *parse_type = ptr->parse_type; type=ptr->type; *lpValue+=ptr->len; if (type == -1) { char* end; /* "hex(xx):" is special */ *lpValue += 4; type = (int)strtoul( *lpValue , &end, 16 ); if (**lpValue=='\0' || *end!=')' || *(end+1)!=':') { type=REG_NONE; } else { *lpValue=end+2; } } return type; } return (**lpValue=='\0'?REG_SZ:REG_NONE); } /****************************************************************************** * 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; } /****************************************************************************** * This is a replacement for strsep which is not portable (missing on Solaris). */ static char* getToken(char** str, const char* delims) { char* token; if (*str==NULL) { /* No more tokens */ return NULL; } token=*str; while (**str!='\0') { if (strchr(delims,**str)!=NULL) { **str='\0'; (*str)++; return token; } (*str)++; } /* There is no other token */ *str=NULL; return token; } /****************************************************************************** * 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) { DWORD dw; char xbuf[9]; memcpy(xbuf,str,8); xbuf[8]='\0'; sscanf(xbuf,"%08lx",&dw); memcpy(buf,&dw,sizeof(DWORD)); return sizeof(DWORD); } /****************************************************************************** * 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; DWORD dw; if ( bufLen != sizeof(DWORD) ) return NULL; str = HeapAlloc(GetProcessHeap(), 0, (bufLen*2)+1); memcpy(&dw,buf,sizeof(DWORD)); sprintf(str, "%08lx", dw); /* 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,dwParseType; LPSTR lpsCurrentValue; LPSTR keyValue = getArg(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,&dwParseType); 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 ( dwParseType == REG_SZ) /* no conversion for string */ { dwLen = strlen(keyData); if (dwLen>0 && keyData[dwLen-1]=='"') { dwLen--; keyData[dwLen]='\0'; } lpbData = keyData; } else if (dwParseType == 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'; } } hRes=KEY_VALUE_ALREADY_SET; } if (keyValue != NULL) HeapFree(GetProcessHeap(), 0, keyValue); 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; }