1 /* 2 * Windows regedit.exe registry editor implementation. 3 * 4 * Copyright 2002 Andriy Palamarchuk 5 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 6 */ 7 8 #ifndef __REACTOS__ 9 #include <stdlib.h> 10 #include <windows.h> 11 #include <commctrl.h> 12 #include <shellapi.h> 13 14 #include "wine/debug.h" 15 #include "main.h" 16 #else 17 #include "regedit.h" 18 #endif 19 20 WINE_DEFAULT_DEBUG_CHANNEL(regedit); 21 22 static void output_writeconsole(const WCHAR *str, DWORD wlen) 23 { 24 #ifdef __REACTOS__ 25 /* This is win32gui application, don't ever try writing to console. 26 * For the console version we have a separate reg.exe application. */ 27 MessageBoxW(NULL, str, NULL, MB_ICONERROR); 28 #else 29 DWORD count; 30 31 if (!WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, wlen, &count, NULL)) 32 { 33 DWORD len; 34 char *msgA; 35 36 /* WriteConsole() fails on Windows if its output is redirected. If this occurs, 37 * we should call WriteFile() with OEM code page. 38 */ 39 len = WideCharToMultiByte(GetOEMCP(), 0, str, wlen, NULL, 0, NULL, NULL); 40 msgA = malloc(len); 41 if (!msgA) return; 42 43 WideCharToMultiByte(GetOEMCP(), 0, str, wlen, msgA, len, NULL, NULL); 44 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE); 45 free(msgA); 46 } 47 #endif 48 } 49 50 static void output_formatstring(const WCHAR *fmt, va_list va_args) 51 { 52 WCHAR *str; 53 DWORD len; 54 55 #ifdef __REACTOS__ 56 SetLastError(NO_ERROR); 57 #endif 58 len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER, 59 fmt, 0, 0, (WCHAR *)&str, 0, &va_args); 60 #ifdef __REACTOS__ 61 if (len == 0 && GetLastError() != NO_ERROR) 62 #else 63 if (len == 0 && GetLastError() != ERROR_NO_WORK_DONE) 64 #endif 65 { 66 WINE_FIXME("Could not format string: le=%lu, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt)); 67 return; 68 } 69 output_writeconsole(str, len); 70 LocalFree(str); 71 } 72 73 void WINAPIV output_message(unsigned int id, ...) 74 { 75 WCHAR fmt[1536]; 76 va_list va_args; 77 78 if (!LoadStringW(GetModuleHandleW(NULL), id, fmt, ARRAY_SIZE(fmt))) 79 { 80 WINE_FIXME("LoadString failed with %ld\n", GetLastError()); 81 return; 82 } 83 va_start(va_args, id); 84 output_formatstring(fmt, va_args); 85 va_end(va_args); 86 } 87 88 void WINAPIV error_exit(unsigned int id, ...) 89 { 90 WCHAR fmt[1536]; 91 va_list va_args; 92 93 if (!LoadStringW(GetModuleHandleW(NULL), id, fmt, ARRAY_SIZE(fmt))) 94 { 95 #ifndef __REACTOS__ 96 WINE_FIXME("LoadString failed with %lu\n", GetLastError()); 97 #endif 98 return; 99 } 100 va_start(va_args, id); 101 output_formatstring(fmt, va_args); 102 va_end(va_args); 103 104 exit(0); /* regedit.exe always terminates with error code zero */ 105 } 106 107 typedef enum { 108 ACTION_ADD, ACTION_EXPORT, ACTION_DELETE 109 } REGEDIT_ACTION; 110 111 #ifdef __REACTOS__ 112 static void PerformRegAction(REGEDIT_ACTION action, WCHAR **argv, int *i, BOOL silent) 113 #else 114 static void PerformRegAction(REGEDIT_ACTION action, WCHAR **argv, int *i) 115 #endif 116 { 117 switch (action) { 118 case ACTION_ADD: { 119 WCHAR *filename = argv[*i]; 120 WCHAR *realname = NULL; 121 FILE *reg_file; 122 123 #ifdef __REACTOS__ 124 /* Request import confirmation */ 125 if (!silent) 126 { 127 WCHAR szText[512]; 128 int choice; 129 UINT mbType = MB_YESNO; 130 131 LoadStringW(hInst, IDS_IMPORT_PROMPT, szText, ARRAY_SIZE(szText)); 132 133 if (argv[*i + 1] != NULL) 134 { 135 /* Enable three buttons if there's another file coming */ 136 mbType = MB_YESNOCANCEL; 137 } 138 139 choice = InfoMessageBox(NULL, mbType | MB_ICONQUESTION, szTitle, szText, filename); 140 switch (choice) 141 { 142 case IDNO: 143 return; 144 case IDCANCEL: 145 /* The cancel case is useful if the user is importing more than one registry file 146 * at a time, and wants to back out anytime during the import process. This way, the 147 * user doesn't have to resort to ending the regedit process abruptly just to cancel 148 * the operation. 149 * To achieve this, we skip all further command line arguments. 150 */ 151 *i = INT_MAX - 1; 152 return; 153 default: 154 break; 155 } 156 } 157 #endif 158 if (!lstrcmpW(filename, L"-")) 159 reg_file = stdin; 160 else 161 { 162 int size; 163 164 size = SearchPathW(NULL, filename, NULL, 0, NULL, NULL); 165 if (size > 0) 166 { 167 realname = malloc(size * sizeof(WCHAR)); 168 size = SearchPathW(NULL, filename, NULL, size, realname, NULL); 169 } 170 if (size == 0) 171 { 172 output_message(STRING_FILE_NOT_FOUND, filename); 173 free(realname); 174 return; 175 } 176 reg_file = _wfopen(realname, L"rb"); 177 if (reg_file == NULL) 178 { 179 _wperror(L"regedit"); 180 output_message(STRING_CANNOT_OPEN_FILE, filename); 181 free(realname); 182 return; 183 } 184 } 185 import_registry_file(reg_file); 186 if (realname) 187 { 188 free(realname); 189 fclose(reg_file); 190 } 191 break; 192 } 193 case ACTION_DELETE: 194 delete_registry_key(argv[*i]); 195 break; 196 case ACTION_EXPORT: { 197 WCHAR *filename = argv[*i]; 198 WCHAR *key_name = argv[++(*i)]; 199 200 if (key_name && *key_name) 201 export_registry_key(filename, key_name, REG_FORMAT_5); 202 else 203 export_registry_key(filename, NULL, REG_FORMAT_5); 204 break; 205 } 206 default: 207 #ifdef __REACTOS__ 208 output_message(STRING_UNHANDLED_ACTION); 209 #else 210 error_exit(STRING_UNHANDLED_ACTION); 211 #endif 212 break; 213 } 214 } 215 216 BOOL ProcessCmdLine(WCHAR *cmdline) 217 { 218 WCHAR **argv; 219 int argc, i; 220 REGEDIT_ACTION action = ACTION_ADD; 221 #ifdef __REACTOS__ 222 BOOL silent = FALSE; 223 #endif 224 225 argv = CommandLineToArgvW(cmdline, &argc); 226 227 if (!argv) 228 return FALSE; 229 230 if (argc == 1) 231 { 232 LocalFree(argv); 233 return FALSE; 234 } 235 236 for (i = 1; i < argc; i++) 237 { 238 if (argv[i][0] != '/' && argv[i][0] != '-') 239 break; /* No flags specified. */ 240 241 if (!argv[i][1] && argv[i][0] == '-') 242 break; /* '-' is a filename. It indicates we should use stdin. */ 243 244 if (argv[i][1] && argv[i][2] && argv[i][2] != ':') 245 break; /* This is a file path beginning with '/'. */ 246 247 switch (towupper(argv[i][1])) 248 { 249 case '?': 250 error_exit(STRING_USAGE); 251 break; 252 case 'D': 253 action = ACTION_DELETE; 254 break; 255 case 'E': 256 action = ACTION_EXPORT; 257 break; 258 case 'C': 259 case 'L': 260 case 'M': 261 case 'R': 262 /* unhandled */; 263 break; 264 case 'S': 265 #ifdef __REACTOS__ 266 silent = TRUE; 267 break; 268 #endif 269 case 'V': 270 /* ignored */; 271 break; 272 default: 273 output_message(STRING_INVALID_SWITCH, argv[i]); 274 error_exit(STRING_HELP); 275 } 276 } 277 278 if (i == argc) 279 { 280 switch (action) 281 { 282 case ACTION_ADD: 283 case ACTION_EXPORT: 284 output_message(STRING_NO_FILENAME); 285 break; 286 case ACTION_DELETE: 287 output_message(STRING_NO_REG_KEY); 288 break; 289 } 290 error_exit(STRING_HELP); 291 } 292 293 for (; i < argc; i++) 294 #ifdef __REACTOS__ 295 PerformRegAction(action, argv, &i, silent); 296 #else 297 PerformRegAction(action, argv, &i); 298 #endif 299 300 LocalFree(argv); 301 302 return TRUE; 303 } 304