/*
* Windows regedit.exe registry editor implementation.
*
- * Copyright (C) 2002 Andriy Palamarchuk
+ * Copyright 2002 Andriy Palamarchuk
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+#ifndef __REACTOS__
+#include <stdlib.h>
+#include <windows.h>
+#include <commctrl.h>
+#include <shellapi.h>
+
+#include "wine/debug.h"
+#include "main.h"
+#else
#include "regedit.h"
+#endif
+WINE_DEFAULT_DEBUG_CHANNEL(regedit);
-static const LPCWSTR usage =
- L"Usage:\n"
- L" regedit filenames\n"
- L" regedit /E filename [regpath]\n"
- L" regedit /D regpath\n"
- L"\n"
- L"filenames - List of registry files names\n"
- L"filename - Registry file name\n"
- L"regpath - Name of the registry key\n"
- L"\n"
- L"When is called without any switches adds contents of the specified\n"
- L"registry files to the registry.\n"
- L"\n"
- L"Switches:\n"
- L" /E - Exports contents of the specified registry key to the specified\n"
- L" file. Exports the whole registry if no key is specified.\n"
- L" /D - Deletes specified registry key\n"
- L" /S - Silent execution, can be used with any other switch.\n"
- L" The only existing mode, exists for compatibility with Windows regedit.\n"
- L" /V - Advanced mode, can be used with any other switch.\n"
- L" Ignored, exists for compatibility with Windows regedit.\n"
- L" /L - Location of system.dat file. Can be used with any other switch.\n"
- L" Ignored. Exists for compatibility with Windows regedit.\n"
- L" /R - Location of user.dat file. Can be used with any other switch.\n"
- L" Ignored. Exists for compatibility with Windows regedit.\n"
- L" /? - Print this help. Any other switches are ignored.\n"
- L" /C - Create registry from. Not implemented.\n"
- L"\n"
- L"The switches are case-insensitive, can be prefixed either by '-' or '/'.\n"
- L"This program is command-line compatible with Microsoft Windows\n"
- L"regedit.\n";
-
-typedef enum
+static void output_writeconsole(const WCHAR *str, DWORD wlen)
{
- ACTION_UNDEF, ACTION_ADD, ACTION_EXPORT, ACTION_DELETE
-} REGEDIT_ACTION;
-
+#ifdef __REACTOS__
+ /* This is win32gui application, don't ever try writing to console.
+ * For the console version we have a separate reg.exe application. */
+ MessageBoxW(NULL, str, NULL, MB_ICONERROR);
+#else
+ DWORD count;
+
+ if (!WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, wlen, &count, NULL))
+ {
+ DWORD len;
+ char *msgA;
+
+ /* WriteConsole() fails on Windows if its output is redirected. If this occurs,
+ * we should call WriteFile() with OEM code page.
+ */
+ len = WideCharToMultiByte(GetOEMCP(), 0, str, wlen, NULL, 0, NULL, NULL);
+ msgA = malloc(len);
+ if (!msgA) return;
+
+ WideCharToMultiByte(GetOEMCP(), 0, str, wlen, msgA, len, NULL, NULL);
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE);
+ free(msgA);
+ }
+#endif
+}
-LPCWSTR getAppName(void)
+static void output_formatstring(const WCHAR *fmt, va_list va_args)
{
- return L"regedit";
+ WCHAR *str;
+ DWORD len;
+
+#ifdef __REACTOS__
+ SetLastError(NO_ERROR);
+#endif
+ len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ fmt, 0, 0, (WCHAR *)&str, 0, &va_args);
+#ifdef __REACTOS__
+ if (len == 0 && GetLastError() != NO_ERROR)
+#else
+ if (len == 0 && GetLastError() != ERROR_NO_WORK_DONE)
+#endif
+ {
+ WINE_FIXME("Could not format string: le=%lu, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt));
+ return;
+ }
+ output_writeconsole(str, len);
+ LocalFree(str);
}
-/******************************************************************************
- * Copies file name from command line string to the buffer.
- * Rewinds the command line string pointer to the next non-space character
- * after the file name.
- * Buffer contains an empty string if no filename was found;
- *
- * params:
- * command_line - command line current position pointer
- * where *s[0] is the first symbol of the file name.
- * file_name - buffer to write the file name to.
- */
-void get_file_name(LPWSTR *command_line, LPWSTR file_name)
+void WINAPIV output_message(unsigned int id, ...)
{
- WCHAR *s = *command_line;
- size_t pos = 0; /* position of pointer "s" in *command_line */
- file_name[0] = 0;
+ WCHAR fmt[1536];
+ va_list va_args;
- if (!s[0])
+ if (!LoadStringW(GetModuleHandleW(NULL), id, fmt, ARRAY_SIZE(fmt)))
{
+ WINE_FIXME("LoadString failed with %ld\n", GetLastError());
return;
}
+ va_start(va_args, id);
+ output_formatstring(fmt, va_args);
+ va_end(va_args);
+}
- if (s[0] == L'"')
- {
- s++;
- (*command_line)++;
- while(s[0] != L'"')
- {
- if (!s[0])
- {
- fwprintf(stderr, L"%s: Unexpected end of file name!\n", getAppName());
- exit(1);
- }
- s++;
- pos++;
- }
- }
- else
- {
- while(s[0] && !iswspace(s[0]))
- {
- s++;
- pos++;
- }
- }
- memcpy(file_name, *command_line, pos * sizeof(WCHAR));
- /* remove the last backslash */
- if (file_name[pos - 1] == L'\\')
- {
- file_name[pos - 1] = L'\0';
- }
- else
- {
- file_name[pos] = L'\0';
- }
+void WINAPIV error_exit(unsigned int id, ...)
+{
+ WCHAR fmt[1536];
+ va_list va_args;
- if (s[0])
- {
- s++;
- pos++;
- }
- while(s[0] && iswspace(s[0]))
+ if (!LoadStringW(GetModuleHandleW(NULL), id, fmt, ARRAY_SIZE(fmt)))
{
- s++;
- pos++;
+#ifndef __REACTOS__
+ WINE_FIXME("LoadString failed with %lu\n", GetLastError());
+#endif
+ return;
}
- (*command_line) += pos;
+ va_start(va_args, id);
+ output_formatstring(fmt, va_args);
+ va_end(va_args);
+
+ exit(0); /* regedit.exe always terminates with error code zero */
}
-BOOL PerformRegAction(REGEDIT_ACTION action, LPWSTR s, BOOL silent)
-{
- switch (action)
- {
- case ACTION_ADD:
- {
- WCHAR szTitle[512], szText[512];
- WCHAR filename[MAX_PATH];
- FILE *fp;
+typedef enum {
+ ACTION_ADD, ACTION_EXPORT, ACTION_DELETE
+} REGEDIT_ACTION;
- get_file_name(&s, filename);
- if (!filename[0])
+#ifdef __REACTOS__
+static void PerformRegAction(REGEDIT_ACTION action, WCHAR **argv, int *i, BOOL silent)
+#else
+static void PerformRegAction(REGEDIT_ACTION action, WCHAR **argv, int *i)
+#endif
+{
+ switch (action) {
+ case ACTION_ADD: {
+ WCHAR *filename = argv[*i];
+ WCHAR *realname = NULL;
+ FILE *reg_file;
+
+#ifdef __REACTOS__
+ /* Request import confirmation */
+ if (!silent)
{
- InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, L"No file name is specified.");
- InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, usage);
- exit(4);
- }
+ WCHAR szText[512];
+ int choice;
+ UINT mbType = MB_YESNO;
- LoadStringW(hInst, IDS_APP_TITLE, szTitle, COUNT_OF(szTitle));
+ LoadStringW(hInst, IDS_IMPORT_PROMPT, szText, ARRAY_SIZE(szText));
- while (filename[0])
- {
- /* Request import confirmation */
- if (!silent)
+ if (argv[*i + 1] != NULL)
{
- int choice;
-
- LoadStringW(hInst, IDS_IMPORT_PROMPT, szText, COUNT_OF(szText));
-
- choice = InfoMessageBox(NULL, MB_YESNOCANCEL | MB_ICONWARNING, szTitle, szText, filename);
-
- switch (choice)
- {
- case IDNO:
- goto cont;
- case IDCANCEL:
- /* The cancel case is useful if the user is importing more than one registry file
- at a time, and wants to back out anytime during the import process. This way, the
- user doesn't have to resort to ending the regedit process abruptly just to cancel
- the operation. */
- return TRUE;
- default:
- break;
- }
+ /* Enable three buttons if there's another file coming */
+ mbType = MB_YESNOCANCEL;
}
- /* Open the file */
- fp = _wfopen(filename, L"r");
+ choice = InfoMessageBox(NULL, mbType | MB_ICONQUESTION, szTitle, szText, filename);
+ switch (choice)
+ {
+ case IDNO:
+ return;
+ case IDCANCEL:
+ /* The cancel case is useful if the user is importing more than one registry file
+ * at a time, and wants to back out anytime during the import process. This way, the
+ * user doesn't have to resort to ending the regedit process abruptly just to cancel
+ * the operation.
+ * To achieve this, we skip all further command line arguments.
+ */
+ *i = INT_MAX - 1;
+ return;
+ default:
+ break;
+ }
+ }
+#endif
+ if (!lstrcmpW(filename, L"-"))
+ reg_file = stdin;
+ else
+ {
+ int size;
- /* Import it */
- if (fp == NULL || !import_registry_file(fp))
+ size = SearchPathW(NULL, filename, NULL, 0, NULL, NULL);
+ if (size > 0)
{
- /* Error opening the file */
- if (!silent)
- {
- LoadStringW(hInst, IDS_IMPORT_ERROR, szText, COUNT_OF(szText));
- InfoMessageBox(NULL, MB_OK | MB_ICONERROR, szTitle, szText, filename);
- }
+ realname = malloc(size * sizeof(WCHAR));
+ size = SearchPathW(NULL, filename, NULL, size, realname, NULL);
}
- else
+ if (size == 0)
{
- /* Show successful import */
- if (!silent)
- {
- LoadStringW(hInst, IDS_IMPORT_OK, szText, COUNT_OF(szText));
- InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, szTitle, szText, filename);
- }
+ output_message(STRING_FILE_NOT_FOUND, filename);
+ free(realname);
+ return;
+ }
+ reg_file = _wfopen(realname, L"rb");
+ if (reg_file == NULL)
+ {
+ _wperror(L"regedit");
+ output_message(STRING_CANNOT_OPEN_FILE, filename);
+ free(realname);
+ return;
}
-
- /* Close the file */
- if (fp) fclose(fp);
-
-cont:
- get_file_name(&s, filename);
}
- break;
- }
-
- case ACTION_DELETE:
- {
- WCHAR reg_key_name[KEY_MAX_LEN];
- get_file_name(&s, reg_key_name);
- if (!reg_key_name[0])
+ import_registry_file(reg_file);
+ if (realname)
{
- InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, L"No registry key is specified for removal.");
- InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, usage);
- exit(6);
+ free(realname);
+ fclose(reg_file);
}
- delete_registry_key(reg_key_name);
break;
}
+ case ACTION_DELETE:
+ delete_registry_key(argv[*i]);
+ break;
+ case ACTION_EXPORT: {
+ WCHAR *filename = argv[*i];
+ WCHAR *key_name = argv[++(*i)];
- case ACTION_EXPORT:
- {
- WCHAR filename[MAX_PATH];
-
- filename[0] = L'\0';
- get_file_name(&s, filename);
- if (!filename[0])
- {
- InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, L"No file name is specified.");
- InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, usage);
- exit(7);
- }
-
- if (s[0])
- {
- WCHAR reg_key_name[KEY_MAX_LEN];
- get_file_name(&s, reg_key_name);
- export_registry_key(filename, reg_key_name, REG_FORMAT_4);
- }
+ if (key_name && *key_name)
+ export_registry_key(filename, key_name, REG_FORMAT_5);
else
- {
- export_registry_key(filename, NULL, REG_FORMAT_4);
- }
+ export_registry_key(filename, NULL, REG_FORMAT_5);
break;
}
-
- default:
- fwprintf(stderr, L"%s: Unhandled action!\n", getAppName());
- exit(8);
- break;
+ default:
+#ifdef __REACTOS__
+ output_message(STRING_UNHANDLED_ACTION);
+#else
+ error_exit(STRING_UNHANDLED_ACTION);
+#endif
+ break;
}
-
- return TRUE;
}
-/**
- * Process unknown switch.
- *
- * Params:
- * chu - the switch character in upper-case.
- * s - the command line string where s points to the switch character.
- */
-static void error_unknown_switch(WCHAR chu, LPWSTR s)
+BOOL ProcessCmdLine(WCHAR *cmdline)
{
- if (iswalpha(chu))
+ WCHAR **argv;
+ int argc, i;
+ REGEDIT_ACTION action = ACTION_ADD;
+#ifdef __REACTOS__
+ BOOL silent = FALSE;
+#endif
+
+ argv = CommandLineToArgvW(cmdline, &argc);
+
+ if (!argv)
+ return FALSE;
+
+ if (argc == 1)
{
- fwprintf(stderr, L"%s: Undefined switch /%c!\n", getAppName(), chu);
+ LocalFree(argv);
+ return FALSE;
}
- else
+
+ for (i = 1; i < argc; i++)
{
- fwprintf(stderr, L"%s: Alphabetic character is expected after '%c' "
- L"in switch specification\n", getAppName(), *(s - 1));
- }
- exit(1);
-}
+ if (argv[i][0] != '/' && argv[i][0] != '-')
+ break; /* No flags specified. */
-BOOL ProcessCmdLine(LPWSTR lpCmdLine)
-{
- BOOL silent = FALSE;
- REGEDIT_ACTION action = ACTION_UNDEF;
- LPWSTR s = lpCmdLine; /* command line pointer */
- WCHAR ch = *s; /* current character */
+ if (!argv[i][1] && argv[i][0] == '-')
+ break; /* '-' is a filename. It indicates we should use stdin. */
- while (ch && ((ch == L'-') || (ch == L'/')))
- {
- WCHAR chu;
- WCHAR ch2;
-
- s++;
- ch = *s;
- ch2 = *(s + 1);
- chu = towupper(ch);
- if (!ch2 || iswspace(ch2))
- {
- if (chu == L'S')
- {
- /* Silence dialogs */
- silent = TRUE;
- }
- else if (chu == L'V')
- {
- /* Ignore this switch */
- }
- else
- {
- switch (chu)
- {
- case L'D':
- action = ACTION_DELETE;
- break;
- case L'E':
- action = ACTION_EXPORT;
- break;
- case L'?':
- InfoMessageBox(NULL, MB_OK | MB_ICONINFORMATION, NULL, usage);
- exit(3);
- break;
- default:
- error_unknown_switch(chu, s);
- break;
- }
- }
- s++;
- }
- else
+ if (argv[i][1] && argv[i][2] && argv[i][2] != ':')
+ break; /* This is a file path beginning with '/'. */
+
+ switch (towupper(argv[i][1]))
{
- if (ch2 == L':')
- {
- switch (chu)
- {
- case L'L':
- /* fall through */
- case L'R':
- s += 2;
- while (*s && !iswspace(*s))
- {
- s++;
- }
- break;
- default:
- error_unknown_switch(chu, s);
- break;
- }
- }
- else
- {
- /* this is a file name, starting from '/' */
- s--;
- break;
- }
+ case '?':
+ error_exit(STRING_USAGE);
+ break;
+ case 'D':
+ action = ACTION_DELETE;
+ break;
+ case 'E':
+ action = ACTION_EXPORT;
+ break;
+ case 'C':
+ case 'L':
+ case 'M':
+ case 'R':
+ /* unhandled */;
+ break;
+ case 'S':
+#ifdef __REACTOS__
+ silent = TRUE;
+ break;
+#endif
+ case 'V':
+ /* ignored */;
+ break;
+ default:
+ output_message(STRING_INVALID_SWITCH, argv[i]);
+ error_exit(STRING_HELP);
}
- /* skip spaces to the next parameter */
- ch = *s;
- while (ch && iswspace(ch))
+ }
+
+ if (i == argc)
+ {
+ switch (action)
{
- s++;
- ch = *s;
+ case ACTION_ADD:
+ case ACTION_EXPORT:
+ output_message(STRING_NO_FILENAME);
+ break;
+ case ACTION_DELETE:
+ output_message(STRING_NO_REG_KEY);
+ break;
}
+ error_exit(STRING_HELP);
}
- if (*s && action == ACTION_UNDEF)
- action = ACTION_ADD;
+ for (; i < argc; i++)
+#ifdef __REACTOS__
+ PerformRegAction(action, argv, &i, silent);
+#else
+ PerformRegAction(action, argv, &i);
+#endif
- if (action != ACTION_UNDEF)
- return PerformRegAction(action, s, silent);
- else
- return FALSE;
+ LocalFree(argv);
+
+ return TRUE;
}