regproc.c 47.2 KB
Newer Older
1 2 3 4 5 6
/*
 * Registry processing routines. Routines, common for registry
 * processing frontends.
 *
 * Copyright 1999 Sylvain St-Germain
 * Copyright 2002 Andriy Palamarchuk
7
 * Copyright 2008 Alexander N. Sørnes <alex@thehandofagony.com>
8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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
21
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 23
 */

24
#include <limits.h>
25 26 27 28
#include <stdio.h>
#include <windows.h>
#include <winnt.h>
#include <winreg.h>
29
#include <assert.h>
30
#include <wine/unicode.h>
31 32 33 34 35
#include "regproc.h"

#define REG_VAL_BUF_SIZE        4096

/* maximal number of characters in hexadecimal data line,
36 37 38
 * including the indentation, but not including the '\' character
 */
#define REG_FILE_HEX_LINE_LEN   (2 + 25 * 3)
39

40
static const CHAR *reg_class_names[] = {
41
                                     "HKEY_LOCAL_MACHINE", "HKEY_USERS", "HKEY_CLASSES_ROOT",
42
                                     "HKEY_CURRENT_CONFIG", "HKEY_CURRENT_USER", "HKEY_DYN_DATA"
43
                                 };
44 45 46

#define REG_CLASS_NUMBER (sizeof(reg_class_names) / sizeof(reg_class_names[0]))

47 48
extern const WCHAR* reg_class_namesW[];

49
static HKEY reg_class_keys[REG_CLASS_NUMBER] = {
50
            HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CLASSES_ROOT,
51
            HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_DYN_DATA
52
        };
53 54 55

/* return values */
#define NOT_ENOUGH_MEMORY     1
56
#define IO_ERROR              2
57 58 59 60 61

/* processing macros */

/* common check of memory allocation results */
#define CHECK_ENOUGH_MEMORY(p) \
62 63
if (!(p)) \
{ \
64
    fprintf(stderr,"%s: file %s, line %d: Not enough memory\n", \
65 66 67
            getAppName(), __FILE__, __LINE__); \
    exit(NOT_ENOUGH_MEMORY); \
}
68

69
/******************************************************************************
70
 * Allocates memory and converts input from multibyte to wide chars
71 72
 * Returned string must be freed by the caller
 */
73
WCHAR* GetWideString(const char* strA)
74
{
75 76
    if(strA)
    {
77
        WCHAR* strW;
78 79 80 81 82 83 84 85
        int len = MultiByteToWideChar(CP_ACP, 0, strA, -1, NULL, 0);

        strW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
        CHECK_ENOUGH_MEMORY(strW);
        MultiByteToWideChar(CP_ACP, 0, strA, -1, strW, len);
        return strW;
    }
    return NULL;
86 87
}

88
/******************************************************************************
89
 * Allocates memory and converts input from multibyte to wide chars
90 91
 * Returned string must be freed by the caller
 */
92
static WCHAR* GetWideStringN(const char* strA, int chars, DWORD *len)
93 94 95
{
    if(strA)
    {
96
        WCHAR* strW;
97 98 99 100 101 102 103 104 105 106 107
        *len = MultiByteToWideChar(CP_ACP, 0, strA, chars, NULL, 0);

        strW = HeapAlloc(GetProcessHeap(), 0, *len * sizeof(WCHAR));
        CHECK_ENOUGH_MEMORY(strW);
        MultiByteToWideChar(CP_ACP, 0, strA, chars, strW, *len);
        return strW;
    }
    *len = 0;
    return NULL;
}

108
/******************************************************************************
109
 * Allocates memory and converts input from wide chars to multibyte
110 111
 * Returned string must be freed by the caller
 */
112
char* GetMultiByteString(const WCHAR* strW)
113
{
114 115
    if(strW)
    {
116
        char* strA;
117 118 119 120 121 122 123 124
        int len = WideCharToMultiByte(CP_ACP, 0, strW, -1, NULL, 0, NULL, NULL);

        strA = HeapAlloc(GetProcessHeap(), 0, len);
        CHECK_ENOUGH_MEMORY(strA);
        WideCharToMultiByte(CP_ACP, 0, strW, -1, strA, len, NULL, NULL);
        return strA;
    }
    return NULL;
125 126
}

127
/******************************************************************************
128
 * Allocates memory and converts input from wide chars to multibyte
129 130
 * Returned string must be freed by the caller
 */
131
static char* GetMultiByteStringN(const WCHAR* strW, int chars, DWORD* len)
132 133 134
{
    if(strW)
    {
135
        char* strA;
136 137 138 139 140 141 142 143 144 145 146
        *len = WideCharToMultiByte(CP_ACP, 0, strW, chars, NULL, 0, NULL, NULL);

        strA = HeapAlloc(GetProcessHeap(), 0, *len);
        CHECK_ENOUGH_MEMORY(strA);
        WideCharToMultiByte(CP_ACP, 0, strW, chars, strA, *len, NULL, NULL);
        return strA;
    }
    *len = 0;
    return NULL;
}

147 148 149
/******************************************************************************
 * Converts a hex representation of a DWORD into a DWORD.
 */
150
static BOOL convertHexToDWord(WCHAR* str, DWORD *dw)
151
{
152
    char buf[9];
153
    char dummy;
154 155 156

    WideCharToMultiByte(CP_ACP, 0, str, -1, buf, 9, NULL, NULL);
    if (lstrlenW(str) > 8 || sscanf(buf, "%x%c", dw, &dummy) != 1) {
157 158 159 160
        fprintf(stderr,"%s: ERROR, invalid hex value\n", getAppName());
        return FALSE;
    }
    return TRUE;
161 162 163
}

/******************************************************************************
164
 * Converts a hex comma separated values list into a binary string.
165
 */
166
static BYTE* convertHexCSVToHex(WCHAR *str, DWORD *size)
167
{
168
    WCHAR *s;
169 170 171
    BYTE *d, *data;

    /* The worst case is 1 digit + 1 comma per byte */
172
    *size=(lstrlenW(str)+1)/2;
173 174 175
    data=HeapAlloc(GetProcessHeap(), 0, *size);
    CHECK_ENOUGH_MEMORY(data);

176
    s = str;
177 178 179
    d = data;
    *size=0;
    while (*s != '\0') {
180
        UINT wc;
181
        WCHAR *end;
182

183
        wc = strtoulW(s,&end,16);
184
        if (end == s || wc > 0xff || (*end && *end != ',')) {
185
            char* strA = GetMultiByteString(s);
186
            fprintf(stderr,"%s: ERROR converting CSV hex stream. Invalid value at '%s'\n",
187
                    getAppName(), strA);
188
            HeapFree(GetProcessHeap(), 0, data);
189
            HeapFree(GetProcessHeap(), 0, strA);
190 191 192 193
            return NULL;
        }
        *d++ =(BYTE)wc;
        (*size)++;
194 195
        if (*end) end++;
        s = end;
196 197
    }

198
    return data;
199 200 201
}

/******************************************************************************
202 203
 * This function returns the HKEY associated with the data type encoded in the
 * value.  It modifies the input parameter (key value) in order to skip this
204 205 206 207
 * "now useless" data type information.
 *
 * Note: Updated based on the algorithm used in 'server/registry.c'
 */
208
static DWORD getDataType(LPWSTR *lpValue, DWORD* parse_type)
209
{
210 211 212 213 214 215 216 217
    struct data_type { const WCHAR *tag; int len; int type; int parse_type; };

    static const WCHAR quote[] = {'"'};
    static const WCHAR str[] = {'s','t','r',':','"'};
    static const WCHAR str2[] = {'s','t','r','(','2',')',':','"'};
    static const WCHAR hex[] = {'h','e','x',':'};
    static const WCHAR dword[] = {'d','w','o','r','d',':'};
    static const WCHAR hexp[] = {'h','e','x','('};
218

219
    static const struct data_type data_types[] = {                   /* actual type */  /* type to assume for parsing */
220 221 222 223 224 225
                { quote,       1,   REG_SZ,              REG_SZ },
                { str,         5,   REG_SZ,              REG_SZ },
                { str2,        8,   REG_EXPAND_SZ,       REG_SZ },
                { hex,         4,   REG_BINARY,          REG_BINARY },
                { dword,       6,   REG_DWORD,           REG_DWORD },
                { hexp,        4,   -1,                  REG_BINARY },
226 227
                { NULL,        0,    0,                  0 }
            };
228 229 230 231

    const struct data_type *ptr;
    int type;

232
    for (ptr = data_types; ptr->tag; ptr++) {
233
        if (strncmpW( ptr->tag, *lpValue, ptr->len ))
234 235 236 237 238 239 240
            continue;

        /* Found! */
        *parse_type = ptr->parse_type;
        type=ptr->type;
        *lpValue+=ptr->len;
        if (type == -1) {
241 242
            WCHAR* end;

243
            /* "hex(xx):" is special */
244
            type = (int)strtoulW( *lpValue , &end, 16 );
245 246 247
            if (**lpValue=='\0' || *end!=')' || *(end+1)!=':') {
                type=REG_NONE;
            } else {
248
                *lpValue = end + 2;
249 250 251 252
            }
        }
        return type;
    }
253 254
    *parse_type=REG_NONE;
    return REG_NONE;
255 256 257 258 259
}

/******************************************************************************
 * Replaces escape sequences with the characters.
 */
260
static void REGPROC_unescape_string(WCHAR* str)
261 262 263
{
    int str_idx = 0;            /* current character under analysis */
    int val_idx = 0;            /* the last character of the unescaped string */
264
    int len = lstrlenW(str);
265 266
    for (str_idx = 0; str_idx < len; str_idx++, val_idx++) {
        if (str[str_idx] == '\\') {
267
            str_idx++;
268
            switch (str[str_idx]) {
269 270 271 272 273 274 275 276
            case 'n':
                str[val_idx] = '\n';
                break;
            case '\\':
            case '"':
                str[val_idx] = str[str_idx];
                break;
            default:
277 278
                fprintf(stderr,"Warning! Unrecognized escape sequence: \\%c'\n",
                        str[str_idx]);
279 280 281 282 283 284 285 286 287 288
                str[val_idx] = str[str_idx];
                break;
            }
        } else {
            str[val_idx] = str[str_idx];
        }
    }
    str[val_idx] = '\0';
}

289
static BOOL parseKeyName(LPWSTR lpKeyName, HKEY *hKey, LPWSTR *lpKeyPath)
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 329 330 331 332 333 334
{
    WCHAR* lpSlash = NULL;
    unsigned int i, len;

    if (lpKeyName == NULL)
        return FALSE;

    for(i = 0; *(lpKeyName+i) != 0; i++)
    {
        if(*(lpKeyName+i) == '\\')
        {
            lpSlash = lpKeyName+i;
            break;
        }
    }

    if (lpSlash)
    {
        len = lpSlash-lpKeyName;
    }
    else
    {
        len = lstrlenW(lpKeyName);
        lpSlash = lpKeyName+len;
    }
    *hKey = NULL;

    for (i = 0; i < REG_CLASS_NUMBER; i++) {
        if (CompareStringW(LOCALE_USER_DEFAULT, 0, lpKeyName, len, reg_class_namesW[i], len) == CSTR_EQUAL &&
            len == lstrlenW(reg_class_namesW[i])) {
            *hKey = reg_class_keys[i];
            break;
        }
    }

    if (*hKey == NULL)
        return FALSE;


    if (*lpSlash != '\0')
        lpSlash++;
    *lpKeyPath = lpSlash;
    return TRUE;
}

335 336 337 338
/* Globals used by the setValue() & co */
static LPSTR currentKeyName;
static HKEY  currentKeyHandle = NULL;

339
/******************************************************************************
340
 * Sets the value with name val_name to the data in val_data for the currently
341 342 343 344 345 346
 * opened key.
 *
 * Parameters:
 * val_name - name of the registry value
 * val_data - registry value data
 */
347
static LONG setValue(WCHAR* val_name, WCHAR* val_data, BOOL is_unicode)
348
{
349
    LONG res;
350
    DWORD  dwDataType, dwParseType;
351
    LPBYTE lpbData;
352
    DWORD  dwData, dwLen;
353
    WCHAR del[] = {'-',0};
354 355 356 357

    if ( (val_name == NULL) || (val_data == NULL) )
        return ERROR_INVALID_PARAMETER;

358
    if (lstrcmpW(val_data, del) == 0)
359
    {
360
        res=RegDeleteValueW(currentKeyHandle,val_name);
361 362
        return (res == ERROR_FILE_NOT_FOUND ? ERROR_SUCCESS : res);
    }
363

364 365 366
    /* Get the data type stored into the value field */
    dwDataType = getDataType(&val_data, &dwParseType);

367
    if (dwParseType == REG_SZ)          /* no conversion for string */
368
    {
369
        REGPROC_unescape_string(val_data);
370 371 372 373
        /* Compute dwLen after REGPROC_unescape_string because it may
         * have changed the string length and we don't want to store
         * the extra garbage in the registry.
         */
374 375
        dwLen = lstrlenW(val_data);
        if (dwLen>0 && val_data[dwLen-1]=='"')
376 377
        {
            dwLen--;
378
            val_data[dwLen]='\0';
379
        }
380
        lpbData = (BYTE*) val_data;
381
        dwLen++;  /* include terminating null */
382
        dwLen = dwLen * sizeof(WCHAR); /* size is in bytes */
383 384
    }
    else if (dwParseType == REG_DWORD)  /* Convert the dword types */
385
    {
386 387 388 389
        if (!convertHexToDWord(val_data, &dwData))
            return ERROR_INVALID_DATA;
        lpbData = (BYTE*)&dwData;
        dwLen = sizeof(dwData);
390 391
    }
    else if (dwParseType == REG_BINARY) /* Convert the binary data */
392
    {
393 394 395
        lpbData = convertHexCSVToHex(val_data, &dwLen);
        if (!lpbData)
            return ERROR_INVALID_DATA;
396

397
        if((dwDataType == REG_MULTI_SZ || dwDataType == REG_EXPAND_SZ) && !is_unicode)
398 399 400 401 402 403
        {
            LPBYTE tmp = lpbData;
            lpbData = (LPBYTE)GetWideStringN((char*)lpbData, dwLen, &dwLen);
            dwLen *= sizeof(WCHAR);
            HeapFree(GetProcessHeap(), 0, tmp);
        }
404
    }
405 406 407 408 409
    else                                /* unknown format */
    {
        fprintf(stderr,"%s: ERROR, unknown data format\n", getAppName());
        return ERROR_INVALID_DATA;
    }
410

411
    res = RegSetValueExW(
412 413 414 415 416 417
               currentKeyHandle,
               val_name,
               0,                  /* Reserved */
               dwDataType,
               lpbData,
               dwLen);
418
    if (dwParseType == REG_BINARY)
419
        HeapFree(GetProcessHeap(), 0, lpbData);
420
    return res;
421 422 423
}

/******************************************************************************
424 425
 * A helper function for processRegEntry() that opens the current key.
 * That key must be closed by calling closeKey().
426
 */
427
static LONG openKeyW(WCHAR* stdInput)
428
{
429
    HKEY keyClass;
430
    WCHAR* keyPath;
431 432
    DWORD dwDisp;
    LONG res;
433

434 435 436
    /* Sanity checks */
    if (stdInput == NULL)
        return ERROR_INVALID_PARAMETER;
437

438
    /* Get the registry class */
439
    if (!parseKeyName(stdInput, &keyClass, &keyPath))
440
        return ERROR_INVALID_PARAMETER;
441

442
    res = RegCreateKeyExW(
443 444
               keyClass,                 /* Class     */
               keyPath,                  /* Sub Key   */
445 446 447 448 449 450 451 452 453
               0,                        /* MUST BE 0 */
               NULL,                     /* object type */
               REG_OPTION_NON_VOLATILE,  /* option, REG_OPTION_NON_VOLATILE ... */
               KEY_ALL_ACCESS,           /* access mask, KEY_ALL_ACCESS */
               NULL,                     /* security attribute */
               &currentKeyHandle,        /* result */
               &dwDisp);                 /* disposition, REG_CREATED_NEW_KEY or
                                                        REG_OPENED_EXISTING_KEY */

454
    if (res == ERROR_SUCCESS)
455
        currentKeyName = GetMultiByteString(stdInput);
456 457
    else
        currentKeyHandle = NULL;
458

459
    return res;
460 461 462 463 464 465

}

/******************************************************************************
 * Close the currently opened key.
 */
466
static void closeKey(void)
467
{
468 469 470 471 472 473
    if (currentKeyHandle)
    {
        HeapFree(GetProcessHeap(), 0, currentKeyName);
        RegCloseKey(currentKeyHandle);
        currentKeyHandle = NULL;
    }
474 475 476
}

/******************************************************************************
477
 * This function is a wrapper for the setValue function.  It prepares the
478
 * land and cleans the area once completed.
479 480 481 482 483
 * Note: this function modifies the line parameter.
 *
 * line - registry file unwrapped line. Should have the registry value name and
 *      complete registry value data.
 */
484
static void processSetValue(WCHAR* line, BOOL is_unicode)
485
{
486 487
    WCHAR* val_name;                   /* registry value name   */
    WCHAR* val_data;                   /* registry value data   */
488
    int line_idx = 0;                 /* current character under analysis */
489
    LONG res;
490 491

    /* get value name */
492
    while ( isspaceW(line[line_idx]) ) line_idx++;
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
    if (line[line_idx] == '@' && line[line_idx + 1] == '=') {
        line[line_idx] = '\0';
        val_name = line;
        line_idx++;
    } else if (line[line_idx] == '\"') {
        line_idx++;
        val_name = line + line_idx;
        while (TRUE) {
            if (line[line_idx] == '\\')   /* skip escaped character */
            {
                line_idx += 2;
            } else {
                if (line[line_idx] == '\"') {
                    line[line_idx] = '\0';
                    line_idx++;
                    break;
                } else {
                    line_idx++;
                }
            }
        }
514
        while ( isspaceW(line[line_idx]) ) line_idx++;
515
        if (line[line_idx] != '=') {
516
            char* lineA;
517
            line[line_idx] = '\"';
518 519 520
            lineA = GetMultiByteString(line);
            fprintf(stderr,"Warning! unrecognized line:\n%s\n", lineA);
            HeapFree(GetProcessHeap(), 0, lineA);
521 522 523 524
            return;
        }

    } else {
525 526 527
        char* lineA = GetMultiByteString(line);
        fprintf(stderr,"Warning! unrecognized line:\n%s\n", lineA);
        HeapFree(GetProcessHeap(), 0, lineA);
528 529 530
        return;
    }
    line_idx++;                   /* skip the '=' character */
531 532

    while ( isspaceW(line[line_idx]) ) line_idx++;
533
    val_data = line + line_idx;
534 535 536 537
    /* trim trailing blanks */
    line_idx = strlenW(val_data);
    while (line_idx > 0 && isspaceW(val_data[line_idx-1])) line_idx--;
    val_data[line_idx] = '\0';
538

539
    REGPROC_unescape_string(val_name);
540
    res = setValue(val_name, val_data, is_unicode);
541
    if ( res != ERROR_SUCCESS )
542 543 544
    {
        char* val_nameA = GetMultiByteString(val_name);
        char* val_dataA = GetMultiByteString(val_data);
545 546 547
        fprintf(stderr,"%s: ERROR Key %s not created. Value: %s, Data: %s\n",
                getAppName(),
                currentKeyName,
548 549 550 551 552
                val_nameA,
                val_dataA);
        HeapFree(GetProcessHeap(), 0, val_nameA);
        HeapFree(GetProcessHeap(), 0, val_dataA);
    }
553 554 555
}

/******************************************************************************
556 557
 * This function receives the currently read entry and performs the
 * corresponding action.
558
 * isUnicode affects parsing of REG_MULTI_SZ values
559
 */
560
static void processRegEntry(WCHAR* stdInput, BOOL isUnicode)
561 562 563 564 565 566
{
    /*
     * We encountered the end of the file, make sure we
     * close the opened key and exit
     */
    if (stdInput == NULL) {
567
        closeKey();
568 569 570 571 572
        return;
    }

    if      ( stdInput[0] == '[')      /* We are reading a new key */
    {
573
        WCHAR* keyEnd;
574
        closeKey();                    /* Close the previous key */
575

576 577
        /* Get rid of the square brackets */
        stdInput++;
578
        keyEnd = strrchrW(stdInput, ']');
579 580 581
        if (keyEnd)
            *keyEnd='\0';

582
        /* delete the key if we encounter '-' at the start of reg key */
583
        if ( stdInput[0] == '-')
584
        {
585 586
            delete_registry_key(stdInput + 1);
        } else if ( openKeyW(stdInput) != ERROR_SUCCESS )
587
        {
588
            char* stdInputA = GetMultiByteString(stdInput);
589
            fprintf(stderr,"%s: setValue failed to open key %s\n",
590 591
                    getAppName(), stdInputA);
            HeapFree(GetProcessHeap(), 0, stdInputA);
592
        }
593
    } else if( currentKeyHandle &&
594 595 596
               (( stdInput[0] == '@') || /* reading a default @=data pair */
                ( stdInput[0] == '\"'))) /* reading a new value=data pair */
    {
597
        processSetValue(stdInput, isUnicode);
598 599 600 601 602 603
    } else
    {
        /* Since we are assuming that the file format is valid we must be
         * reading a blank line which indicates the end of this key processing
         */
        closeKey();
604 605 606 607 608
    }
}

/******************************************************************************
 * Processes a registry file.
609 610 611 612
 * Correctly processes comments (in # form), line continuation.
 *
 * Parameters:
 *   in - input stream to read from
613
 *   first_chars - beginning of stream, read due to Unicode check
614
 */
615
static void processRegLinesA(FILE *in, char* first_chars)
616 617 618 619
{
    LPSTR line           = NULL;  /* line read from input stream */
    ULONG lineSize       = REG_VAL_BUF_SIZE;

620
    line = HeapAlloc(GetProcessHeap(), 0, lineSize);
621
    CHECK_ENOUGH_MEMORY(line);
622
    memcpy(line, first_chars, 2);
623

624
    while (!feof(in)) {
625
        LPSTR s; /* The pointer into line for where the current fgets should read */
626
        LPSTR check;
627
        WCHAR* lineW;
628
        s = line;
629 630 631 632 633 634 635

        if(first_chars)
        {
            s += 2;
            first_chars = NULL;
        }

636
        for (;;) {
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
            size_t size_remaining;
            int size_to_get;
            char *s_eol; /* various local uses */

            /* Do we need to expand the buffer ? */
            assert (s >= line && s <= line + lineSize);
            size_remaining = lineSize - (s-line);
            if (size_remaining < 2) /* room for 1 character and the \0 */
            {
                char *new_buffer;
                size_t new_size = lineSize + REG_VAL_BUF_SIZE;
                if (new_size > lineSize) /* no arithmetic overflow */
                    new_buffer = HeapReAlloc (GetProcessHeap(), 0, line, new_size);
                else
                    new_buffer = NULL;
                CHECK_ENOUGH_MEMORY(new_buffer);
                line = new_buffer;
                s = line + lineSize - size_remaining;
                lineSize = new_size;
                size_remaining = lineSize - (s-line);
            }
658

659 660 661 662
            /* Get as much as possible into the buffer, terminated either by
             * eof, error, eol or getting the maximum amount.  Abort on error.
             */
            size_to_get = (size_remaining > INT_MAX ? INT_MAX : size_remaining);
663

664
            check = fgets (s, size_to_get, in);
665 666

            if (check == NULL) {
667
                if (ferror(in)) {
668 669
                    perror ("While reading input");
                    exit (IO_ERROR);
670
                } else {
671 672 673 674 675 676 677 678
                    assert (feof(in));
                    *s = '\0';
                    /* It is not clear to me from the definition that the
                     * contents of the buffer are well defined on detecting
                     * an eof without managing to read anything.
                     */
                }
            }
679

680 681
            /* If we didn't read the eol nor the eof go around for the rest */
            s_eol = strchr (s, '\n');
682
            if (!feof (in) && !s_eol) {
683 684 685 686 687 688
                s = strchr (s, '\0');
                /* It should be s + size_to_get - 1 but this is safer */
                continue;
            }

            /* If it is a comment line then discard it and go around again */
689
            if (line [0] == '#') {
690
                s = line;
691
                continue;
692
            }
693

694
            /* Remove any line feed.  Leave s_eol on the \0 */
695
            if (s_eol) {
696 697 698
                *s_eol = '\0';
                if (s_eol > line && *(s_eol-1) == '\r')
                    *--s_eol = '\0';
699
            } else
700
                s_eol = strchr (s, '\0');
701

702
            /* If there is a concatenating \\ then go around again */
703
            if (s_eol > line && *(s_eol-1) == '\\') {
704 705
                int c;
                s = s_eol-1;
706 707 708 709 710 711 712 713

                do
                {
                    c = fgetc(in);
                } while(c == ' ' || c == '\t');

                if(c == EOF)
                {
714 715
                    fprintf(stderr,"%s: ERROR - invalid continuation.\n",
                            getAppName());
716 717 718 719 720 721
                }
                else
                {
                    *s = c;
                    s++;
                }
722 723 724
                continue;
            }

725 726
            lineW = GetWideString(line);

727
            break; /* That is the full virtual line */
728 729
        }

730
        processRegEntry(lineW, FALSE);
731
        HeapFree(GetProcessHeap(), 0, lineW);
732
    }
733
    processRegEntry(NULL, FALSE);
734 735 736 737

    HeapFree(GetProcessHeap(), 0, line);
}

738
static void processRegLinesW(FILE *in)
739 740 741
{
    WCHAR* buf           = NULL;  /* line read from input stream */
    ULONG lineSize       = REG_VAL_BUF_SIZE;
742
    size_t CharsInBuf = -1;
743

744 745
    WCHAR* s; /* The pointer into buf for where the current fgets should read */
    WCHAR* line; /* The start of the current line */
746 747 748 749 750

    buf = HeapAlloc(GetProcessHeap(), 0, lineSize * sizeof(WCHAR));
    CHECK_ENOUGH_MEMORY(buf);

    s = buf;
751
    line = buf;
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770

    while(!feof(in)) {
        size_t size_remaining;
        int size_to_get;
        WCHAR *s_eol = NULL; /* various local uses */

        /* Do we need to expand the buffer ? */
        assert (s >= buf && s <= buf + lineSize);
        size_remaining = lineSize - (s-buf);
        if (size_remaining < 2) /* room for 1 character and the \0 */
        {
            WCHAR *new_buffer;
            size_t new_size = lineSize + (REG_VAL_BUF_SIZE / sizeof(WCHAR));
            if (new_size > lineSize) /* no arithmetic overflow */
                new_buffer = HeapReAlloc (GetProcessHeap(), 0, buf, new_size * sizeof(WCHAR));
            else
                new_buffer = NULL;
            CHECK_ENOUGH_MEMORY(new_buffer);
            buf = new_buffer;
771
            line = buf;
772 773 774 775 776 777
            s = buf + lineSize - size_remaining;
            lineSize = new_size;
            size_remaining = lineSize - (s-buf);
        }

        /* Get as much as possible into the buffer, terminated either by
778
        * eof, error or getting the maximum amount.  Abort on error.
779 780 781
        */
        size_to_get = (size_remaining > INT_MAX ? INT_MAX : size_remaining);

782 783
        CharsInBuf = fread(s, sizeof(WCHAR), size_to_get - 1, in);
        s[CharsInBuf] = 0;
784

785
        if (CharsInBuf == 0) {
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
            if (ferror(in)) {
                perror ("While reading input");
                exit (IO_ERROR);
            } else {
                assert (feof(in));
                *s = '\0';
                /* It is not clear to me from the definition that the
                * contents of the buffer are well defined on detecting
                * an eof without managing to read anything.
                */
            }
        }

        /* If we didn't read the eol nor the eof go around for the rest */
        while(1)
        {
802 803 804 805 806 807 808 809 810
            s_eol = strchrW(line, '\n');

            if(!s_eol) {
                /* Move the stub of the line to the start of the buffer so
                 * we get the maximum space to read into, and so we don't
                 * have to recalculate 'line' if the buffer expands */
                MoveMemory(buf, line, (strlenW(line)+1) * sizeof(WCHAR));
                line = buf;
                s = strchrW(line, '\0');
811
                break;
812
            }
813

814
            /* If it is a comment line then discard it and go around again */
815 816
            if (*line == '#') {
                line = s_eol + 1;
817 818 819
                continue;
            }

820 821 822 823 824 825 826 827 828 829 830 831 832
            /* If there is a concatenating \\ then go around again */
            if ((*(s_eol-1) == '\\') ||
                (*(s_eol-1) == '\r' && *(s_eol-2) == '\\')) {
                WCHAR* NextLine = s_eol;

                while(*(NextLine+1) == ' ' || *(NextLine+1) == '\t')
                    NextLine++;

                NextLine++;

                if(*(s_eol-1) == '\r')
                    s_eol--;

833
                MoveMemory(s_eol - 1, NextLine, (CharsInBuf - (NextLine - s) + 1)*sizeof(WCHAR));
834 835 836 837 838
                CharsInBuf -= NextLine - s_eol + 1;
                s_eol = 0;
                continue;
            }

839 840 841 842 843 844 845 846 847 848
            /* Remove any line feed.  Leave s_eol on the \0 */
            if (s_eol) {
                *s_eol = '\0';
                if (s_eol > buf && *(s_eol-1) == '\r')
                    *(s_eol-1) = '\0';
            }

            if(!s_eol)
                break;

849 850
            processRegEntry(line, TRUE);
            line = s_eol + 1;
851 852 853 854 855
            s_eol = 0;
            continue; /* That is the full virtual line */
        }
    }

856
    processRegEntry(NULL, TRUE);
857 858 859 860

    HeapFree(GetProcessHeap(), 0, buf);
}

861 862 863 864 865 866
/****************************************************************************
 * REGPROC_print_error
 *
 * Print the message for GetLastError
 */

867
static void REGPROC_print_error(void)
868
{
869 870 871 872 873 874 875 876
    LPVOID lpMsgBuf;
    DWORD error_code;
    int status;

    error_code = GetLastError ();
    status = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                           NULL, error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
    if (!status) {
877
        fprintf(stderr,"%s: Cannot display message for error %d, status %d\n",
878
                getAppName(), error_code, GetLastError());
879 880 881
        exit(1);
    }
    puts(lpMsgBuf);
882
    LocalFree(lpMsgBuf);
883 884 885 886 887 888 889 890 891 892 893 894 895
    exit(1);
}

/******************************************************************************
 * Checks whether the buffer has enough room for the string or required size.
 * Resizes the buffer if necessary.
 *
 * Parameters:
 * buffer - pointer to a buffer for string
 * len - current length of the buffer in characters.
 * required_len - length of the string to place to the buffer in characters.
 *   The length does not include the terminating null character.
 */
896
static void REGPROC_resize_char_buffer(WCHAR **buffer, DWORD *len, DWORD required_len)
897 898
{
    required_len++;
899
    if (required_len > *len) {
900
        *len = required_len;
901 902
        if (!*buffer)
            *buffer = HeapAlloc(GetProcessHeap(), 0, *len * sizeof(**buffer));
903
        else
904
            *buffer = HeapReAlloc(GetProcessHeap(), 0, *buffer, *len * sizeof(**buffer));
905 906 907 908
        CHECK_ENOUGH_MEMORY(*buffer);
    }
}

909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
/******************************************************************************
 * Same as REGPROC_resize_char_buffer() but on a regular buffer.
 *
 * Parameters:
 * buffer - pointer to a buffer
 * len - current size of the buffer in bytes
 * required_size - size of the data to place in the buffer in bytes
 */
static void REGPROC_resize_binary_buffer(BYTE **buffer, DWORD *size, DWORD required_size)
{
    if (required_size > *size) {
        *size = required_size;
        if (!*buffer)
            *buffer = HeapAlloc(GetProcessHeap(), 0, *size);
        else
            *buffer = HeapReAlloc(GetProcessHeap(), 0, *buffer, *size);
        CHECK_ENOUGH_MEMORY(*buffer);
    }
}

929
/******************************************************************************
930
 * Prints string str to file
931
 */
932
static void REGPROC_export_string(WCHAR **line_buf, DWORD *line_buf_size, DWORD *line_len, WCHAR *str, DWORD str_len)
933
{
934
    DWORD i, pos;
935 936
    DWORD extra = 0;

937
    REGPROC_resize_char_buffer(line_buf, line_buf_size, *line_len + str_len + 10);
938 939

    /* escaping characters */
940
    pos = *line_len;
941
    for (i = 0; i < str_len; i++) {
942
        WCHAR c = str[i];
943
        switch (c) {
944
        case '\n':
945
            extra++;
946
            REGPROC_resize_char_buffer(line_buf, line_buf_size, *line_len + str_len + extra);
947 948
            (*line_buf)[pos++] = '\\';
            (*line_buf)[pos++] = 'n';
949
            break;
950

951 952
        case '\\':
        case '"':
953
            extra++;
954
            REGPROC_resize_char_buffer(line_buf, line_buf_size, *line_len + str_len + extra);
955 956
            (*line_buf)[pos++] = '\\';
            /* Fall through */
957

958
        default:
959
            (*line_buf)[pos++] = c;
960 961 962
            break;
        }
    }
963 964
    (*line_buf)[pos] = '\0';
    *line_len = pos;
965 966
}

967 968
static void REGPROC_export_binary(WCHAR **line_buf, DWORD *line_buf_size, DWORD *line_len, DWORD type, BYTE *value, DWORD value_size, BOOL unicode)
{
969
    DWORD hex_pos, data_pos;
970 971 972 973 974 975 976 977 978 979 980 981 982
    const WCHAR *hex_prefix;
    const WCHAR hex[] = {'h','e','x',':',0};
    WCHAR hex_buf[17];
    const WCHAR concat[] = {'\\','\n',' ',' ',0};
    DWORD concat_prefix, concat_len;
    const WCHAR newline[] = {'\n',0};
    CHAR* value_multibyte = NULL;

    if (type == REG_BINARY) {
        hex_prefix = hex;
    } else {
        const WCHAR hex_format[] = {'h','e','x','(','%','u',')',':',0};
        hex_prefix = hex_buf;
983
        sprintfW(hex_buf, hex_format, type);
984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
        if ((type == REG_SZ || type == REG_EXPAND_SZ || type == REG_MULTI_SZ) && !unicode)
        {
            value_multibyte = GetMultiByteStringN((WCHAR*)value, value_size / sizeof(WCHAR), &value_size);
            value = (BYTE*)value_multibyte;
        }
    }

    concat_len = lstrlenW(concat);
    concat_prefix = 2;

    hex_pos = *line_len;
    *line_len += lstrlenW(hex_prefix);
    data_pos = *line_len;
    *line_len += value_size * 3;
    /* - The 2 spaces that concat places at the start of the
     *   line effectively reduce the space available for data.
     * - If the value name and hex prefix are very long
     *   ( > REG_FILE_HEX_LINE_LEN) then we may overestimate
     *   the needed number of lines by one. But that's ok.
     * - The trailing linefeed takes the place of a comma so
     *   it's accounted for already.
     */
    *line_len += *line_len / (REG_FILE_HEX_LINE_LEN - concat_prefix) * concat_len;
    REGPROC_resize_char_buffer(line_buf, line_buf_size, *line_len);
    lstrcpyW(*line_buf + hex_pos, hex_prefix);
1009
    if (value_size)
1010
    {
1011 1012
        const WCHAR format[] = {'%','0','2','x',0};
        DWORD i, column;
1013

1014 1015 1016 1017 1018 1019 1020 1021
        column = data_pos; /* no line wrap yet */
        i = 0;
        while (1)
        {
            sprintfW(*line_buf + data_pos, format, (unsigned int)value[i]);
            data_pos += 2;
            if (++i == value_size)
                break;
1022

1023 1024 1025 1026 1027 1028 1029 1030 1031
            (*line_buf)[data_pos++] = ',';
            column += 3;

            /* wrap the line */
            if (column >= REG_FILE_HEX_LINE_LEN) {
                lstrcpyW(*line_buf + data_pos, concat);
                data_pos += concat_len;
                column = concat_prefix;
            }
1032 1033 1034
        }
    }
    lstrcpyW(*line_buf + data_pos, newline);
1035
    HeapFree(GetProcessHeap(), 0, value_multibyte);
1036 1037
}

1038 1039 1040
/******************************************************************************
 * Writes the given line to a file, in multi-byte or wide characters
 */
1041
static void REGPROC_write_line(FILE *file, const WCHAR* str, BOOL unicode)
1042
{
1043 1044 1045 1046 1047 1048
    if(unicode)
    {
        fwrite(str, sizeof(WCHAR), lstrlenW(str), file);
    } else
    {
        char* strA = GetMultiByteString(str);
1049
        fputs(strA, file);
1050 1051
        HeapFree(GetProcessHeap(), 0, strA);
    }
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
}

/******************************************************************************
 * Writes contents of the registry key to the specified file stream.
 *
 * Parameters:
 * file - writable file stream to export registry branch to.
 * key - registry branch to export.
 * reg_key_name_buf - name of the key with registry class.
 *      Is resized if necessary.
1062
 * reg_key_name_size - length of the buffer for the registry class in characters.
1063 1064
 * val_name_buf - buffer for storing value name.
 *      Is resized if necessary.
1065
 * val_name_size - length of the buffer for storing value names in characters.
1066 1067 1068 1069
 * val_buf - buffer for storing values while extracting.
 *      Is resized if necessary.
 * val_size - size of the buffer for storing values in bytes.
 */
1070
static void export_hkey(FILE *file, HKEY key,
1071 1072
                 WCHAR **reg_key_name_buf, DWORD *reg_key_name_size,
                 WCHAR **val_name_buf, DWORD *val_name_size,
1073
                 BYTE **val_buf, DWORD *val_size,
1074 1075
                 WCHAR **line_buf, DWORD *line_buf_size,
                 BOOL unicode)
1076 1077 1078 1079 1080 1081 1082 1083
{
    DWORD max_sub_key_len;
    DWORD max_val_name_len;
    DWORD max_val_size;
    DWORD curr_len;
    DWORD i;
    BOOL more_data;
    LONG ret;
1084
    WCHAR key_format[] = {'\n','[','%','s',']','\n',0};
1085 1086

    /* get size information and resize the buffers if necessary */
1087
    if (RegQueryInfoKeyW(key, NULL, NULL, NULL, NULL,
1088 1089
                        &max_sub_key_len, NULL,
                        NULL, &max_val_name_len, &max_val_size, NULL, NULL
1090
                       ) != ERROR_SUCCESS) {
1091 1092
        REGPROC_print_error();
    }
1093
    curr_len = strlenW(*reg_key_name_buf);
1094
    REGPROC_resize_char_buffer(reg_key_name_buf, reg_key_name_size,
1095
                               max_sub_key_len + curr_len + 1);
1096
    REGPROC_resize_char_buffer(val_name_buf, val_name_size,
1097
                               max_val_name_len);
1098
    REGPROC_resize_binary_buffer(val_buf, val_size, max_val_size);
1099
    REGPROC_resize_char_buffer(line_buf, line_buf_size, lstrlenW(*reg_key_name_buf) + 4);
1100
    /* output data for the current key */
1101
    sprintfW(*line_buf, key_format, *reg_key_name_buf);
1102
    REGPROC_write_line(file, *line_buf, unicode);
1103

1104 1105 1106
    /* print all the values */
    i = 0;
    more_data = TRUE;
1107
    while(more_data) {
1108
        DWORD value_type;
1109
        DWORD val_name_size1 = *val_name_size;
1110
        DWORD val_size1 = *val_size;
1111
        ret = RegEnumValueW(key, i, *val_name_buf, &val_name_size1, NULL,
1112
                           &value_type, *val_buf, &val_size1);
1113 1114 1115 1116 1117
        if (ret == ERROR_MORE_DATA) {
            /* Increase the size of the buffers and retry */
            REGPROC_resize_char_buffer(val_name_buf, val_name_size, val_name_size1);
            REGPROC_resize_binary_buffer(val_buf, val_size, val_size1);
        } else if (ret != ERROR_SUCCESS) {
1118
            more_data = FALSE;
1119
            if (ret != ERROR_NO_MORE_ITEMS) {
1120 1121 1122
                REGPROC_print_error();
            }
        } else {
1123
            DWORD line_len;
1124 1125
            i++;

1126
            if ((*val_name_buf)[0]) {
1127 1128
                const WCHAR val_start[] = {'"','%','s','"','=',0};

1129
                line_len = 3 + lstrlenW(*val_name_buf);
1130
                REGPROC_resize_char_buffer(line_buf, line_buf_size, line_len);
1131
                sprintfW(*line_buf, val_start, *val_name_buf);
1132
            } else {
1133
                const WCHAR std_val[] = {'@','=',0};
1134
                line_len = 2;
1135
                REGPROC_resize_char_buffer(line_buf, line_buf_size, line_len);
1136
                lstrcpyW(*line_buf, std_val);
1137
            }
1138

1139
            switch (value_type) {
1140
            case REG_SZ:
1141
            {
1142 1143 1144 1145 1146 1147 1148 1149 1150
                WCHAR* wstr = (WCHAR*)*val_buf;

                if (val_size1 < sizeof(WCHAR) || val_size1 % sizeof(WCHAR) ||
                    wstr[val_size1 / sizeof(WCHAR) - 1]) {
                    REGPROC_export_binary(line_buf, line_buf_size, &line_len, value_type, *val_buf, val_size1, unicode);
                } else {
                    const WCHAR start[] = {'"',0};
                    const WCHAR end[] = {'"','\n',0};
                    DWORD len;
1151

1152 1153 1154 1155
                    len = lstrlenW(start);
                    REGPROC_resize_char_buffer(line_buf, line_buf_size, line_len + len);
                    lstrcpyW(*line_buf + line_len, start);
                    line_len += len;
1156

1157 1158 1159 1160
                    /* At this point we know wstr is '\0'-terminated
                     * so we can substract 1 from the size
                     */
                    REGPROC_export_string(line_buf, line_buf_size, &line_len, wstr, val_size1 / sizeof(WCHAR) - 1);
1161

1162 1163 1164
                    REGPROC_resize_char_buffer(line_buf, line_buf_size, line_len + lstrlenW(end));
                    lstrcpyW(*line_buf + line_len, end);
                }
1165
                break;
1166
            }
1167 1168

            case REG_DWORD:
1169 1170 1171
            {
                WCHAR format[] = {'d','w','o','r','d',':','%','0','8','x','\n',0};

1172
                REGPROC_resize_char_buffer(line_buf, line_buf_size, line_len + 15);
1173
                sprintfW(*line_buf + line_len, format, *((DWORD *)*val_buf));
1174
                break;
1175
            }
1176 1177

            default:
1178 1179 1180
            {
                char* key_nameA = GetMultiByteString(*reg_key_name_buf);
                char* value_nameA = GetMultiByteString(*val_name_buf);
1181
                fprintf(stderr,"%s: warning - unsupported registry format '%d', "
1182 1183
                        "treat as binary\n",
                        getAppName(), value_type);
1184 1185 1186 1187 1188
                fprintf(stderr,"key name: \"%s\"\n", key_nameA);
                fprintf(stderr,"value name:\"%s\"\n\n", value_nameA);
                HeapFree(GetProcessHeap(), 0, key_nameA);
                HeapFree(GetProcessHeap(), 0, value_nameA);
            }
1189
                /* falls through */
1190
            case REG_EXPAND_SZ:
1191 1192
            case REG_MULTI_SZ:
                /* falls through */
1193 1194
            case REG_BINARY:
                REGPROC_export_binary(line_buf, line_buf_size, &line_len, value_type, *val_buf, val_size1, unicode);
1195
            }
1196
            REGPROC_write_line(file, *line_buf, unicode);
1197 1198
        }
    }
1199

1200 1201 1202
    i = 0;
    more_data = TRUE;
    (*reg_key_name_buf)[curr_len] = '\\';
1203
    while(more_data) {
1204
        DWORD buf_size = *reg_key_name_size - curr_len - 1;
1205

1206
        ret = RegEnumKeyExW(key, i, *reg_key_name_buf + curr_len + 1, &buf_size,
1207
                           NULL, NULL, NULL, NULL);
1208 1209 1210 1211
        if (ret == ERROR_MORE_DATA) {
            /* Increase the size of the buffer and retry */
            REGPROC_resize_char_buffer(reg_key_name_buf, reg_key_name_size, curr_len + 1 + buf_size);
        } else if (ret != ERROR_SUCCESS) {
1212
            more_data = FALSE;
1213
            if (ret != ERROR_NO_MORE_ITEMS) {
1214 1215 1216 1217 1218 1219
                REGPROC_print_error();
            }
        } else {
            HKEY subkey;

            i++;
1220
            if (RegOpenKeyW(key, *reg_key_name_buf + curr_len + 1,
1221
                           &subkey) == ERROR_SUCCESS) {
1222 1223
                export_hkey(file, subkey, reg_key_name_buf, reg_key_name_size,
                            val_name_buf, val_name_size, val_buf, val_size,
1224
                            line_buf, line_buf_size, unicode);
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236
                RegCloseKey(subkey);
            } else {
                REGPROC_print_error();
            }
        }
    }
    (*reg_key_name_buf)[curr_len] = '\0';
}

/******************************************************************************
 * Open file for export.
 */
1237
static FILE *REGPROC_open_export_file(WCHAR *file_name, BOOL unicode)
1238
{
1239
    FILE *file;
1240
    WCHAR dash = '-';
1241

1242
    if (strncmpW(file_name,&dash,1)==0)
1243 1244 1245
        file=stdout;
    else
    {
1246 1247
        CHAR* file_nameA = GetMultiByteString(file_name);
        file = fopen(file_nameA, "w");
1248 1249
        if (!file) {
            perror("");
1250 1251
            fprintf(stderr,"%s: Can't open file \"%s\"\n", getAppName(), file_nameA);
            HeapFree(GetProcessHeap(), 0, file_nameA);
1252 1253
            exit(1);
        }
1254
        HeapFree(GetProcessHeap(), 0, file_nameA);
1255
    }
1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266
    if(unicode)
    {
        const BYTE unicode_seq[] = {0xff,0xfe};
        const WCHAR header[] = {'W','i','n','d','o','w','s',' ','R','e','g','i','s','t','r','y',' ','E','d','i','t','o','r',' ','V','e','r','s','i','o','n',' ','5','.','0','0','\n'};
        fwrite(unicode_seq, sizeof(BYTE), sizeof(unicode_seq)/sizeof(unicode_seq[0]), file);
        fwrite(header, sizeof(WCHAR), sizeof(header)/sizeof(header[0]), file);
    } else
    {
        fputs("REGEDIT4\n", file);
    }

1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277
    return file;
}

/******************************************************************************
 * Writes contents of the registry key to the specified file stream.
 *
 * Parameters:
 * file_name - name of a file to export registry branch to.
 * reg_key_name - registry branch to export. The whole registry is exported if
 *      reg_key_name is NULL or contains an empty string.
 */
1278
BOOL export_registry_key(WCHAR *file_name, WCHAR *reg_key_name, DWORD format)
1279
{
1280 1281
    WCHAR *reg_key_name_buf;
    WCHAR *val_name_buf;
1282
    BYTE *val_buf;
1283
    WCHAR *line_buf;
1284 1285
    DWORD reg_key_name_size = KEY_MAX_LEN;
    DWORD val_name_size = KEY_MAX_LEN;
1286
    DWORD val_size = REG_VAL_BUF_SIZE;
1287
    DWORD line_buf_size = KEY_MAX_LEN + REG_VAL_BUF_SIZE;
1288
    FILE *file = NULL;
1289
    BOOL unicode = (format == REG_FORMAT_5);
1290 1291

    reg_key_name_buf = HeapAlloc(GetProcessHeap(), 0,
1292
                                 reg_key_name_size  * sizeof(*reg_key_name_buf));
1293
    val_name_buf = HeapAlloc(GetProcessHeap(), 0,
1294
                             val_name_size * sizeof(*val_name_buf));
1295
    val_buf = HeapAlloc(GetProcessHeap(), 0, val_size);
1296
    line_buf = HeapAlloc(GetProcessHeap(), 0, line_buf_size * sizeof(*line_buf));
1297
    CHECK_ENOUGH_MEMORY(reg_key_name_buf && val_name_buf && val_buf && line_buf);
1298

1299
    if (reg_key_name && reg_key_name[0]) {
1300
        HKEY reg_key_class;
1301
        WCHAR *branch_name = NULL;
1302 1303
        HKEY key;

1304
        REGPROC_resize_char_buffer(&reg_key_name_buf, &reg_key_name_size,
1305 1306
                                   lstrlenW(reg_key_name));
        lstrcpyW(reg_key_name_buf, reg_key_name);
1307 1308

        /* open the specified key */
1309
        if (!parseKeyName(reg_key_name, &reg_key_class, &branch_name)) {
1310
            CHAR* key_nameA = GetMultiByteString(reg_key_name);
1311
            fprintf(stderr,"%s: Incorrect registry class specification in '%s'\n",
1312 1313
                    getAppName(), key_nameA);
            HeapFree(GetProcessHeap(), 0, key_nameA);
1314 1315
            exit(1);
        }
1316
        if (!branch_name[0]) {
1317
            /* no branch - registry class is specified */
1318
            file = REGPROC_open_export_file(file_name, unicode);
1319
            export_hkey(file, reg_key_class,
1320 1321
                        &reg_key_name_buf, &reg_key_name_size,
                        &val_name_buf, &val_name_size,
1322 1323
                        &val_buf, &val_size, &line_buf,
                        &line_buf_size, unicode);
1324
        } else if (RegOpenKeyW(reg_key_class, branch_name, &key) == ERROR_SUCCESS) {
1325
            file = REGPROC_open_export_file(file_name, unicode);
1326
            export_hkey(file, key,
1327 1328
                        &reg_key_name_buf, &reg_key_name_size,
                        &val_name_buf, &val_name_size,
1329 1330
                        &val_buf, &val_size, &line_buf,
                        &line_buf_size, unicode);
1331 1332
            RegCloseKey(key);
        } else {
1333
            CHAR* key_nameA = GetMultiByteString(reg_key_name);
1334
            fprintf(stderr,"%s: Can't export. Registry key '%s' does not exist!\n",
1335 1336
                    getAppName(), key_nameA);
            HeapFree(GetProcessHeap(), 0, key_nameA);
1337 1338 1339
            REGPROC_print_error();
        }
    } else {
1340
        unsigned int i;
1341 1342

        /* export all registry classes */
1343
        file = REGPROC_open_export_file(file_name, unicode);
1344
        for (i = 0; i < REG_CLASS_NUMBER; i++) {
1345 1346
            /* do not export HKEY_CLASSES_ROOT */
            if (reg_class_keys[i] != HKEY_CLASSES_ROOT &&
1347
                    reg_class_keys[i] != HKEY_CURRENT_USER &&
1348 1349
                    reg_class_keys[i] != HKEY_CURRENT_CONFIG &&
                    reg_class_keys[i] != HKEY_DYN_DATA) {
1350
                lstrcpyW(reg_key_name_buf, reg_class_namesW[i]);
1351
                export_hkey(file, reg_class_keys[i],
1352 1353
                            &reg_key_name_buf, &reg_key_name_size,
                            &val_name_buf, &val_name_size,
1354 1355
                            &val_buf, &val_size, &line_buf,
                            &line_buf_size, unicode);
1356
            }
1357 1358 1359
        }
    }

1360
    if (file) {
1361 1362 1363
        fclose(file);
    }
    HeapFree(GetProcessHeap(), 0, reg_key_name);
1364
    HeapFree(GetProcessHeap(), 0, val_name_buf);
1365
    HeapFree(GetProcessHeap(), 0, val_buf);
1366
    HeapFree(GetProcessHeap(), 0, line_buf);
1367 1368 1369 1370 1371 1372
    return TRUE;
}

/******************************************************************************
 * Reads contents of the specified file into the registry.
 */
1373
BOOL import_registry_file(FILE* reg_file)
1374
{
1375 1376 1377 1378 1379 1380 1381 1382 1383 1384
    if (reg_file)
    {
        BYTE s[2];
        if (fread( s, 2, 1, reg_file) == 1)
        {
            if (s[0] == 0xff && s[1] == 0xfe)
            {
                processRegLinesW(reg_file);
            } else
            {
1385
                processRegLinesA(reg_file, (char*)s);
1386 1387
            }
        }
1388 1389 1390
        return TRUE;
    }
    return FALSE;
1391 1392 1393 1394 1395 1396 1397 1398 1399
}

/******************************************************************************
 * Removes the registry key with all subkeys. Parses full key name.
 *
 * Parameters:
 * reg_key_name - full name of registry branch to delete. Ignored if is NULL,
 *      empty, points to register key class, does not exist.
 */
1400
void delete_registry_key(WCHAR *reg_key_name)
1401 1402 1403 1404 1405 1406 1407
{
    WCHAR *key_name = NULL;
    HKEY key_class;

    if (!reg_key_name || !reg_key_name[0])
        return;

1408
    if (!parseKeyName(reg_key_name, &key_class, &key_name)) {
1409
        char* reg_key_nameA = GetMultiByteString(reg_key_name);
1410 1411 1412 1413 1414 1415
        fprintf(stderr,"%s: Incorrect registry class specification in '%s'\n",
                getAppName(), reg_key_nameA);
        HeapFree(GetProcessHeap(), 0, reg_key_nameA);
        exit(1);
    }
    if (!*key_name) {
1416
        char* reg_key_nameA = GetMultiByteString(reg_key_name);
1417 1418 1419 1420 1421 1422 1423 1424
        fprintf(stderr,"%s: Can't delete registry class '%s'\n",
                getAppName(), reg_key_nameA);
        HeapFree(GetProcessHeap(), 0, reg_key_nameA);
        exit(1);
    }

    RegDeleteTreeW(key_class, key_name);
}