1 /* 2 * Windows regedit.exe registry editor implementation. 3 * 4 * Copyright (C) 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 Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21 #include "regedit.h" 22 23 static const LPCWSTR usage = 24 L"Usage:\n" 25 L" regedit filenames\n" 26 L" regedit /E filename [regpath]\n" 27 L" regedit /D regpath\n" 28 L"\n" 29 L"filenames - List of registry files names\n" 30 L"filename - Registry file name\n" 31 L"regpath - Name of the registry key\n" 32 L"\n" 33 L"When is called without any switches adds contents of the specified\n" 34 L"registry files to the registry.\n" 35 L"\n" 36 L"Switches:\n" 37 L" /E - Exports contents of the specified registry key to the specified\n" 38 L" file. Exports the whole registry if no key is specified.\n" 39 L" /D - Deletes specified registry key\n" 40 L" /S - Silent execution, can be used with any other switch.\n" 41 L" The only existing mode, exists for compatibility with Windows regedit.\n" 42 L" /V - Advanced mode, can be used with any other switch.\n" 43 L" Ignored, exists for compatibility with Windows regedit.\n" 44 L" /L - Location of system.dat file. Can be used with any other switch.\n" 45 L" Ignored. Exists for compatibility with Windows regedit.\n" 46 L" /R - Location of user.dat file. Can be used with any other switch.\n" 47 L" Ignored. Exists for compatibility with Windows regedit.\n" 48 L" /? - Print this help. Any other switches are ignored.\n" 49 L" /C - Create registry from. Not implemented.\n" 50 L"\n" 51 L"The switches are case-insensitive, can be prefixed either by '-' or '/'.\n" 52 L"This program is command-line compatible with Microsoft Windows\n" 53 L"regedit.\n"; 54 55 typedef enum 56 { 57 ACTION_UNDEF, ACTION_ADD, ACTION_EXPORT, ACTION_DELETE 58 } REGEDIT_ACTION; 59 60 61 LPCWSTR getAppName(void) 62 { 63 return L"regedit"; 64 } 65 66 /****************************************************************************** 67 * Copies file name from command line string to the buffer. 68 * Rewinds the command line string pointer to the next non-space character 69 * after the file name. 70 * Buffer contains an empty string if no filename was found; 71 * 72 * params: 73 * command_line - command line current position pointer 74 * where *s[0] is the first symbol of the file name. 75 * file_name - buffer to write the file name to. 76 */ 77 void get_file_name(LPWSTR *command_line, LPWSTR file_name) 78 { 79 WCHAR *s = *command_line; 80 size_t pos = 0; /* position of pointer "s" in *command_line */ 81 file_name[0] = 0; 82 83 if (!s[0]) 84 { 85 return; 86 } 87 88 if (s[0] == L'"') 89 { 90 s++; 91 (*command_line)++; 92 while(s[0] != L'"') 93 { 94 if (!s[0]) 95 { 96 fwprintf(stderr, L"%s: Unexpected end of file name!\n", getAppName()); 97 exit(1); 98 } 99 s++; 100 pos++; 101 } 102 } 103 else 104 { 105 while(s[0] && !iswspace(s[0])) 106 { 107 s++; 108 pos++; 109 } 110 } 111 memcpy(file_name, *command_line, pos * sizeof(WCHAR)); 112 /* remove the last backslash */ 113 if (file_name[pos - 1] == L'\\') 114 { 115 file_name[pos - 1] = L'\0'; 116 } 117 else 118 { 119 file_name[pos] = L'\0'; 120 } 121 122 if (s[0]) 123 { 124 s++; 125 pos++; 126 } 127 while(s[0] && iswspace(s[0])) 128 { 129 s++; 130 pos++; 131 } 132 (*command_line) += pos; 133 } 134 135 BOOL PerformRegAction(REGEDIT_ACTION action, LPWSTR s, BOOL silent) 136 { 137 switch (action) 138 { 139 case ACTION_ADD: 140 { 141 WCHAR szText[512]; 142 WCHAR filename[MAX_PATH]; 143 LPWSTR command_line = s; 144 UINT count = 0, mbType = MB_YESNO; 145 FILE *fp; 146 147 get_file_name(&command_line, filename); 148 while (filename[0]) 149 { 150 count++; 151 get_file_name(&command_line, filename); 152 } 153 154 if (count == 0) 155 { 156 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, L"No file name is specified."); 157 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, usage); 158 exit(4); 159 } 160 161 if (count > 1) 162 { 163 /* Enable three buttons if we have more than one file */ 164 mbType = MB_YESNOCANCEL; 165 } 166 167 get_file_name(&s, filename); 168 while (filename[0]) 169 { 170 /* Request import confirmation */ 171 if (!silent) 172 { 173 int choice; 174 175 LoadStringW(hInst, IDS_IMPORT_PROMPT, szText, COUNT_OF(szText)); 176 177 choice = InfoMessageBox(NULL, mbType | MB_ICONQUESTION, szTitle, szText, filename); 178 179 switch (choice) 180 { 181 case IDNO: 182 goto cont; 183 case IDCANCEL: 184 /* The cancel case is useful if the user is importing more than one registry file 185 at a time, and wants to back out anytime during the import process. This way, the 186 user doesn't have to resort to ending the regedit process abruptly just to cancel 187 the operation. */ 188 return TRUE; 189 default: 190 break; 191 } 192 } 193 194 /* Open the file */ 195 fp = _wfopen(filename, L"r"); 196 197 /* Import it */ 198 if (fp == NULL || !import_registry_file(fp)) 199 { 200 /* Error opening the file */ 201 if (!silent) 202 { 203 LoadStringW(hInst, IDS_IMPORT_ERROR, szText, COUNT_OF(szText)); 204 InfoMessageBox(NULL, MB_OK | MB_ICONERROR, szTitle, szText, filename); 205 } 206 } 207 else 208 { 209 /* Show successful import */ 210 if (!silent) 211 { 212 LoadStringW(hInst, IDS_IMPORT_OK, szText, COUNT_OF(szText)); 213 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, szText, filename); 214 } 215 } 216 217 /* Close the file */ 218 if (fp) fclose(fp); 219 220 cont: 221 get_file_name(&s, filename); 222 } 223 break; 224 } 225 226 case ACTION_DELETE: 227 { 228 WCHAR reg_key_name[KEY_MAX_LEN]; 229 get_file_name(&s, reg_key_name); 230 if (!reg_key_name[0]) 231 { 232 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, L"No registry key is specified for removal."); 233 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, usage); 234 exit(6); 235 } 236 delete_registry_key(reg_key_name); 237 break; 238 } 239 240 case ACTION_EXPORT: 241 { 242 WCHAR filename[MAX_PATH]; 243 244 filename[0] = L'\0'; 245 get_file_name(&s, filename); 246 if (!filename[0]) 247 { 248 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, L"No file name is specified."); 249 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, usage); 250 exit(7); 251 } 252 253 if (s[0]) 254 { 255 WCHAR reg_key_name[KEY_MAX_LEN]; 256 get_file_name(&s, reg_key_name); 257 export_registry_key(filename, reg_key_name, REG_FORMAT_4); 258 } 259 else 260 { 261 export_registry_key(filename, NULL, REG_FORMAT_4); 262 } 263 break; 264 } 265 266 default: 267 fwprintf(stderr, L"%s: Unhandled action!\n", getAppName()); 268 exit(8); 269 break; 270 } 271 272 return TRUE; 273 } 274 275 /** 276 * Process unknown switch. 277 * 278 * Params: 279 * chu - the switch character in upper-case. 280 * s - the command line string where s points to the switch character. 281 */ 282 static void error_unknown_switch(WCHAR chu, LPWSTR s) 283 { 284 if (iswalpha(chu)) 285 { 286 fwprintf(stderr, L"%s: Undefined switch /%c!\n", getAppName(), chu); 287 } 288 else 289 { 290 fwprintf(stderr, L"%s: Alphabetic character is expected after '%c' " 291 L"in switch specification\n", getAppName(), *(s - 1)); 292 } 293 exit(1); 294 } 295 296 BOOL ProcessCmdLine(LPWSTR lpCmdLine) 297 { 298 BOOL silent = FALSE; 299 REGEDIT_ACTION action = ACTION_UNDEF; 300 LPWSTR s = lpCmdLine; /* command line pointer */ 301 WCHAR ch = *s; /* current character */ 302 303 while (ch && ((ch == L'-') || (ch == L'/'))) 304 { 305 WCHAR chu; 306 WCHAR ch2; 307 308 s++; 309 ch = *s; 310 ch2 = *(s + 1); 311 chu = towupper(ch); 312 if (!ch2 || iswspace(ch2)) 313 { 314 if (chu == L'S') 315 { 316 /* Silence dialogs */ 317 silent = TRUE; 318 } 319 else if (chu == L'V') 320 { 321 /* Ignore this switch */ 322 } 323 else 324 { 325 switch (chu) 326 { 327 case L'D': 328 action = ACTION_DELETE; 329 break; 330 case L'E': 331 action = ACTION_EXPORT; 332 break; 333 case L'?': 334 InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, usage); 335 exit(3); 336 break; 337 default: 338 error_unknown_switch(chu, s); 339 break; 340 } 341 } 342 s++; 343 } 344 else 345 { 346 if (ch2 == L':') 347 { 348 switch (chu) 349 { 350 case L'L': 351 /* fall through */ 352 case L'R': 353 s += 2; 354 while (*s && !iswspace(*s)) 355 { 356 s++; 357 } 358 break; 359 default: 360 error_unknown_switch(chu, s); 361 break; 362 } 363 } 364 else 365 { 366 /* this is a file name, starting from '/' */ 367 s--; 368 break; 369 } 370 } 371 /* skip spaces to the next parameter */ 372 ch = *s; 373 while (ch && iswspace(ch)) 374 { 375 s++; 376 ch = *s; 377 } 378 } 379 380 if (*s && action == ACTION_UNDEF) 381 action = ACTION_ADD; 382 383 if (action != ACTION_UNDEF) 384 return PerformRegAction(action, s, silent); 385 else 386 return FALSE; 387 } 388