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