regedit.c 9.97 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Windows regedit.exe registry editor implementation.
 *
 * 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
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22
 */

#include <ctype.h>
#include <stdio.h>
23
#include <stdlib.h>
24 25 26
#include <windows.h>
#include "regproc.h"

27
static const char *usage =
28 29 30 31 32 33 34 35
    "Usage:\n"
    "    regedit filename\n"
    "    regedit /E filename [regpath]\n"
    "    regedit /D regpath\n"
    "\n"
    "filename - registry file name\n"
    "regpath - name of the registry key\n"
    "\n"
36 37
    "When called without any switches, adds the content of the specified\n"
    "file to the registry\n"
38 39 40 41 42 43
    "\n"
    "Switches:\n"
    "    /E - exports contents of the specified registry key to the specified\n"
    "	file. Exports the whole registry if no key is specified.\n"
    "    /D - deletes specified registry key\n"
    "    /S - silent execution, can be used with any other switch.\n"
44
    "	Default. The only existing mode, exists for compatibility with Windows regedit.\n"
45 46 47 48 49 50 51
    "    /V - advanced mode, can be used with any other switch.\n"
    "	Ignored, exists for compatibility with Windows regedit.\n"
    "    /L - location of system.dat file. Can be used with any other switch.\n"
    "	Ignored. Exists for compatibility with Windows regedit.\n"
    "    /R - location of user.dat file. Can be used with any other switch.\n"
    "	Ignored. Exists for compatibility with Windows regedit.\n"
    "    /? - print this help. Any other switches are ignored.\n"
52
    "    /C - create registry from file. Not implemented.\n"
53 54 55
    "\n"
    "The switches are case-insensitive, can be prefixed either by '-' or '/'.\n"
    "This program is command-line compatible with Microsoft Windows\n"
56
    "regedit.\n";
57 58 59 60 61

typedef enum {
    ACTION_UNDEF, ACTION_ADD, ACTION_EXPORT, ACTION_DELETE
} REGEDIT_ACTION;

62

63 64 65 66 67
const CHAR *getAppName(void)
{
    return "regedit";
}

68 69 70 71 72
/******************************************************************************
 * 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;
73
 *
74 75 76 77
 * 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.
78
 */
79
static void get_file_name(CHAR **command_line, CHAR *file_name)
80
{
81 82 83
    CHAR *s = *command_line;
    int pos = 0;                /* position of pointer "s" in *command_line */
    file_name[0] = 0;
84

85 86 87
    if (!s[0]) {
        return;
    }
88

89
    if (s[0] == '"') {
90
        s++;
91 92 93 94 95 96
        (*command_line)++;
        while(s[0] != '"') {
            if (!s[0]) {
                fprintf(stderr,"%s: Unexpected end of file name!\n",
                        getAppName());
                exit(1);
97 98
            }
            s++;
99
            pos++;
100
        }
101 102
    } else {
        while(s[0] && !isspace(s[0])) {
103
            s++;
104
            pos++;
105 106
        }
    }
107 108 109 110 111 112 113
    memcpy(file_name, *command_line, pos * sizeof((*command_line)[0]));
    /* remove the last backslash */
    if (file_name[pos - 1] == '\\') {
        file_name[pos - 1] = '\0';
    } else {
        file_name[pos] = '\0';
    }
114

115 116 117 118 119 120 121 122 123
    if (s[0]) {
        s++;
        pos++;
    }
    while(s[0] && isspace(s[0])) {
        s++;
        pos++;
    }
    (*command_line) += pos;
124
}
125

126
static BOOL PerformRegAction(REGEDIT_ACTION action, LPSTR s)
127
{
128 129 130 131
    switch (action) {
    case ACTION_ADD: {
            CHAR filename[MAX_PATH];
            FILE *reg_file;
132

133 134
            get_file_name(&s, filename);
            if (!filename[0]) {
135
                fprintf(stderr,"%s: No file name was specified\n", getAppName());
136
                fprintf(stderr,usage);
137
                exit(1);
138 139
            }

140
            while(filename[0]) {
141
                char* realname = NULL;
142 143

                if (strcmp(filename, "-") == 0)
144
                {
145
                    reg_file = stdin;
146
                }
147
                else
148
                {
149 150
                    int size;

151 152
                    size = SearchPathA(NULL, filename, NULL, 0, NULL, NULL);
                    if (size > 0)
153
                    {
154 155
                        realname = HeapAlloc(GetProcessHeap(), 0, size);
                        size = SearchPathA(NULL, filename, NULL, size, realname, NULL);
156
                    }
157
                    if (size == 0)
158 159 160 161 162
                    {
                        fprintf(stderr, "%s: File not found \"%s\" (%d)\n",
                                getAppName(), filename, GetLastError());
                        exit(1);
                    }
163
                    reg_file = fopen(realname, "rb");
164
                    if (reg_file == NULL)
165 166
                    {
                        perror("");
167
                        fprintf(stderr, "%s: Can't open file \"%s\"\n", getAppName(), filename);
168 169
                        exit(1);
                    }
170
                }
171
                import_registry_file(reg_file);
172 173 174 175 176
                if (realname)
                {
                    HeapFree(GetProcessHeap(),0,realname);
                    fclose(reg_file);
                }
177 178 179 180 181
                get_file_name(&s, filename);
            }
            break;
        }
    case ACTION_DELETE: {
182 183 184
            CHAR reg_key_name[KEY_MAX_LEN];

            get_file_name(&s, reg_key_name);
185
            if (!reg_key_name[0]) {
186
                fprintf(stderr,"%s: No registry key was specified for removal\n",
187 188 189
                        getAppName());
                fprintf(stderr,usage);
                exit(1);
190 191
            } else
            {
192
                WCHAR* reg_key_nameW = GetWideString(reg_key_name);
193 194
                delete_registry_key(reg_key_nameW);
                HeapFree(GetProcessHeap(), 0, reg_key_nameW);
195 196 197 198 199
            }
            break;
        }
    case ACTION_EXPORT: {
            CHAR filename[MAX_PATH];
200
            WCHAR* filenameW;
201 202 203 204

            filename[0] = '\0';
            get_file_name(&s, filename);
            if (!filename[0]) {
205
                fprintf(stderr,"%s: No file name was specified\n", getAppName());
206 207 208 209
                fprintf(stderr,usage);
                exit(1);
            }

210
            filenameW = GetWideString(filename);
211 212
            if (s[0]) {
                CHAR reg_key_name[KEY_MAX_LEN];
213
                WCHAR* reg_key_nameW;
214 215

                get_file_name(&s, reg_key_name);
216
                reg_key_nameW = GetWideString(reg_key_name);
217
                export_registry_key(filenameW, reg_key_nameW, REG_FORMAT_4);
218
                HeapFree(GetProcessHeap(), 0, reg_key_nameW);
219
            } else {
220
                export_registry_key(filenameW, NULL, REG_FORMAT_4);
221
            }
222
            HeapFree(GetProcessHeap(), 0, filenameW);
223
            break;
224 225
        }
    default:
226
        fprintf(stderr,"%s: Unhandled action!\n", getAppName());
227 228 229
        exit(1);
        break;
    }
230
    return TRUE;
231
}
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262

/**
 * 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(char chu, char *s)
{
    if (isalpha(chu)) {
        fprintf(stderr,"%s: Undefined switch /%c!\n", getAppName(), chu);
    } else {
        fprintf(stderr,"%s: Alphabetic character is expected after '%c' "
                "in switch specification\n", getAppName(), *(s - 1));
    }
    exit(1);
}

BOOL ProcessCmdLine(LPSTR lpCmdLine)
{
    REGEDIT_ACTION action = ACTION_UNDEF;
    LPSTR s = lpCmdLine;        /* command line pointer */
    CHAR ch = *s;               /* current character */

    while (ch && ((ch == '-') || (ch == '/'))) {
        char chu;
        char ch2;

        s++;
        ch = *s;
263 264 265 266 267 268
        if (!ch || isspace(ch))
        {
            /* '-' is a file name. It indicates we should use stdin */
            s--;
            break;
        }
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
        ch2 = *(s+1);
        chu = toupper(ch);
        if (!ch2 || isspace(ch2)) {
            if (chu == 'S' || chu == 'V') {
                /* ignore these switches */
            } else {
                switch (chu) {
                case 'D':
                    action = ACTION_DELETE;
                    break;
                case 'E':
                    action = ACTION_EXPORT;
                    break;
                case '?':
                    fprintf(stderr,usage);
                    exit(0);
                    break;
                default:
                    error_unknown_switch(chu, s);
                    break;
                }
            }
            s++;
        } else {
            if (ch2 == ':') {
                switch (chu) {
                case 'L':
                    /* fall through */
                case 'R':
                    s += 2;
                    while (*s && !isspace(*s)) {
                        s++;
                    }
                    break;
                default:
                    error_unknown_switch(chu, s);
                    break;
                }
            } else {
                /* this is a file name, starting from '/' */
                s--;
                break;
            }
        }
        /* skip spaces to the next parameter */
        ch = *s;
        while (ch && isspace(ch)) {
            s++;
            ch = *s;
        }
    }

    if (*s && action == ACTION_UNDEF)
        action = ACTION_ADD;

    if (action == ACTION_UNDEF)
        return FALSE;

    return PerformRegAction(action, s);
}