[NTOS:KE/x64] Handle NMI vs swapgs race condition
[reactos.git] / base / applications / regedit / regedit.c
index 4601427..0e3cc27 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * 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
  */
 
-#include "regedit.h"
+#ifndef __REACTOS__
+#include <stdlib.h>
+#include <windows.h>
+#include <commctrl.h>
+#include <shellapi.h>
 
-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
-{
-    ACTION_UNDEF, ACTION_ADD, ACTION_EXPORT, ACTION_DELETE
-} REGEDIT_ACTION;
+#include "wine/debug.h"
+#include "main.h"
+#else
+#include "regedit.h"
+#endif
 
+WINE_DEFAULT_DEBUG_CHANNEL(regedit);
 
-LPCWSTR getAppName(void)
+static void output_writeconsole(const WCHAR *str, DWORD wlen)
 {
-    return L"regedit";
+#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
 }
 
-/******************************************************************************
- * 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)
+static void output_formatstring(const WCHAR *fmt, va_list va_args)
 {
-    WCHAR *s = *command_line;
-    size_t pos = 0; /* position of pointer "s" in *command_line */
-    file_name[0] = 0;
-
-    if (!s[0])
+    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);
+}
 
-    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 output_message(unsigned int id, ...)
+{
+    WCHAR fmt[1536];
+    va_list va_args;
 
-    if (s[0])
+    if (!LoadStringW(GetModuleHandleW(NULL), id, fmt, ARRAY_SIZE(fmt)))
     {
-        s++;
-        pos++;
-    }
-    while(s[0] && iswspace(s[0]))
-    {
-        s++;
-        pos++;
+        WINE_FIXME("LoadString failed with %ld\n", GetLastError());
+        return;
     }
-    (*command_line) += pos;
+    va_start(va_args, id);
+    output_formatstring(fmt, va_args);
+    va_end(va_args);
 }
 
-BOOL PerformRegAction(REGEDIT_ACTION action, LPWSTR s, BOOL silent)
+void WINAPIV error_exit(unsigned int id, ...)
 {
-    switch (action)
+    WCHAR fmt[1536];
+    va_list va_args;
+
+    if (!LoadStringW(GetModuleHandleW(NULL), id, fmt, ARRAY_SIZE(fmt)))
     {
-        case ACTION_ADD:
-        {
-            WCHAR szTitle[512], szText[512];
-            WCHAR filename[MAX_PATH];
-            FILE *fp;
+#ifndef __REACTOS__
+        WINE_FIXME("LoadString failed with %lu\n", GetLastError());
+#endif
+        return;
+    }
+    va_start(va_args, id);
+    output_formatstring(fmt, va_args);
+    va_end(va_args);
 
-            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(4);
-            }
+    exit(0); /* regedit.exe always terminates with error code zero */
+}
 
-            LoadStringW(hInst, IDS_APP_TITLE, szTitle, COUNT_OF(szTitle));
+typedef enum {
+    ACTION_ADD, ACTION_EXPORT, ACTION_DELETE
+} REGEDIT_ACTION;
 
-            while (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)
             {
-                /* Request import confirmation */
-                if (!silent)
+                WCHAR szText[512];
+                int choice;
+                UINT mbType = MB_YESNO;
+
+                LoadStringW(hInst, IDS_IMPORT_PROMPT, szText, ARRAY_SIZE(szText));
+
+                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;
 }