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