update for HEAD-2003021201
[reactos.git] / apps / tests / regdump / regproc.c
1 /*
2  * Registry processing routines. Routines, common for registry
3  * processing frontends.
4  *
5  * Copyright 1999 Sylvain St-Germain
6  * Copyright 2002 Andriy Palamarchuk
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #ifdef WIN32_REGDBG
24 #include <windows.h>
25 #include <tchar.h>
26 #ifndef __GNUC__
27 #include <ntsecapi.h>
28 #else
29 #include <ctype.h>
30 #endif
31 #include <limits.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <assert.h>
35 //#include <winreg.h>
36 #include "regdump.h"
37 #else
38
39 #define WIN32_LEAN_AND_MEAN     // Exclude rarely-used stuff from Windows headers
40 #include <windows.h>
41 #include <commctrl.h>
42 #include <stdlib.h>
43 #include <tchar.h>
44 #include <process.h>
45 #include <stdio.h>
46 #include <wchar.h>
47     
48 #include <ctype.h>
49 #include <limits.h>
50 #include <winnt.h>
51 #include <winreg.h>
52 #include <assert.h>
53
54 #endif
55
56 #include "regproc.h"
57
58
59 #define REG_VAL_BUF_SIZE        4096
60
61 /* Delimiters used to parse the "value" to query queryValue*/
62 #define QUERY_VALUE_MAX_ARGS  1
63
64 /* maximal number of characters in hexadecimal data line,
65    not including '\' character */
66 #define REG_FILE_HEX_LINE_LEN   76
67
68 /* Globals used by the api setValue, queryValue */
69 static LPTSTR currentKeyName   = NULL;
70 static HKEY  currentKeyClass  = 0;
71 static HKEY  currentKeyHandle = 0;
72 static BOOL  bTheKeyIsOpen    = FALSE;
73
74 static TCHAR *reg_class_names[] = {
75     _T("HKEY_LOCAL_MACHINE"),
76     _T("HKEY_USERS"),
77     _T("HKEY_CLASSES_ROOT"),
78     _T("HKEY_CURRENT_CONFIG"),
79     _T("HKEY_CURRENT_USER")
80 };
81
82 #define REG_CLASS_NUMBER (sizeof(reg_class_names) / sizeof(reg_class_names[0]))
83
84 static HKEY reg_class_keys[REG_CLASS_NUMBER] = {
85     HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CLASSES_ROOT,
86     HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER
87 };
88
89 /* return values */
90 #define NOT_ENOUGH_MEMORY     1
91 #define IO_ERROR              2
92
93 /* processing macros */
94
95 /* common check of memory allocation results */
96 #ifdef UNICODE
97 #define CHECK_ENOUGH_MEMORY(p) \
98     if (!(p)) \
99     { \
100         _tprintf(_T("file %S, line %d: Not enough memory"), __FILE__, __LINE__); \
101         assert(0);\
102         exit(NOT_ENOUGH_MEMORY); \
103     }
104 #else
105 #define CHECK_ENOUGH_MEMORY(p) \
106     if (!(p)) \
107     { \
108         _tprintf(_T("file %s, line %d: Not enough memory"), __FILE__, __LINE__); \
109         assert(0);\
110         exit(NOT_ENOUGH_MEMORY); \
111     }
112 #endif
113
114 #ifdef UNICODE
115 #define _TEOF WEOF 
116 #else
117 #define _TEOF EOF 
118 #endif
119
120 /******************************************************************************
121  * This is a replacement for strsep which is not portable (missing on Solaris).
122  */
123 #if 0
124 /* DISABLED */
125 char* getToken(char** str, const char* delims)
126 {
127     char* token;
128
129     if (*str==NULL) {
130         /* No more tokens */
131         return NULL;
132     }
133
134     token=*str;
135     while (**str!='\0') {
136         if (strchr(delims,**str)!=NULL) {
137             **str='\0';
138             (*str)++;
139             return token;
140         }
141         (*str)++;
142     }
143     /* There is no other token */
144     *str=NULL;
145     return token;
146 }
147 #endif
148
149 /******************************************************************************
150  * Copies file name from command line string to the buffer.
151  * Rewinds the command line string pointer to the next non-spece character
152  * after the file name.
153  * Buffer contains an empty string if no filename was found;
154  *
155  * params:
156  * command_line - command line current position pointer
157  *      where *s[0] is the first symbol of the file name.
158  * file_name - buffer to write the file name to.
159  */
160 void get_file_nameA(CHAR **command_line, CHAR *file_name, int max_filename)
161 {
162     CHAR *s = *command_line;
163     int pos = 0;                /* position of pointer "s" in *command_line */
164     file_name[0] = 0;
165
166     if (!s[0]) {
167         return;
168     }
169     if (s[0] == '"') {
170         s++;
171         (*command_line)++;
172         while (s[0] != '"') {
173             if (!s[0]) {
174                 _tprintf(_T("Unexpected end of file name!\n"));
175                 assert(0);
176                 //exit(1);
177             }
178             s++;
179             pos++;
180         }
181     } else {
182         while (s[0] && !isspace(s[0])) {
183             s++;
184             pos++;
185         }
186     }
187     memcpy(file_name, *command_line, pos * sizeof((*command_line)[0]));
188     /* remove the last backslash */
189     if (file_name[pos - 1] == '\\') {
190         file_name[pos - 1] = '\0';
191     } else {
192         file_name[pos] = '\0';
193     }
194     if (s[0]) {
195         s++;
196         pos++;
197     }
198     while (s[0] && isspace(s[0])) {
199         s++;
200         pos++;
201     }
202     (*command_line) += pos;
203 }
204
205 void get_file_nameW(CHAR** command_line, WCHAR* filename, int max_filename)
206 {
207     CHAR filenameA[_MAX_PATH];
208     int len;
209
210     get_file_nameA(command_line, filenameA, _MAX_PATH);
211     len = strlen(filenameA);
212     OemToCharBuffW(filenameA, filename, max_filename);
213     filename[len] = _T('\0');
214 /*
215     UNICODE_STRING UnicodeString;
216     ANSI_STRING AnsiString;
217     CHAR filenameA[_MAX_PATH];
218
219     get_file_nameA(command_line, filenameA, _MAX_PATH);
220
221     //RtlInitAnsiString(&AnsiString, filenameA);
222     UnicodeString.Buffer = filename;
223     UnicodeString.MaximumLength = max_filename;//MAX_PATH;
224     RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE);
225  */
226 }
227
228 /******************************************************************************
229  * Converts a hex representation of a DWORD into a DWORD.
230  */
231 DWORD convertHexToDWord(TCHAR* str, BYTE* buf)
232 {
233     DWORD dw;
234     TCHAR xbuf[9];
235
236     memcpy(xbuf, str, 8 * sizeof(TCHAR));
237     xbuf[88 * sizeof(TCHAR)] = '\0';
238     _stscanf(xbuf, _T("%08lx"), &dw);
239     memcpy(buf, &dw, sizeof(DWORD));
240     return sizeof(DWORD);
241 }
242
243 /******************************************************************************
244  * Converts a hex buffer into a hex comma separated values
245  */
246 TCHAR* convertHexToHexCSV(BYTE* buf, ULONG bufLen)
247 {
248     TCHAR* str;
249     TCHAR* ptrStr;
250     BYTE* ptrBuf;
251
252     ULONG current = 0;
253     str = HeapAlloc(GetProcessHeap(), 0, (bufLen+1)*2*sizeof(TCHAR));
254     memset(str, 0, (bufLen+1)*2);
255     ptrStr = str;  /* Pointer to result  */
256     ptrBuf = buf;  /* Pointer to current */
257     while (current < bufLen) {
258         BYTE bCur = ptrBuf[current++];
259         TCHAR res[3];
260         _stprintf(res, _T("%02x"), (unsigned int)*&bCur);
261         _tcscat(str, res);
262         _tcscat(str, _T(","));
263     }
264     /* Get rid of the last comma */
265     str[_tcslen(str)-1] = _T('\0');
266     return str;
267 }
268
269 /******************************************************************************
270  * Converts a hex buffer into a DWORD string
271  */
272 TCHAR* convertHexToDWORDStr(BYTE* buf, ULONG bufLen)
273 {
274     TCHAR* str;
275     DWORD dw;
276
277     if (bufLen != sizeof(DWORD)) return NULL;
278     str = HeapAlloc(GetProcessHeap(), 0, ((bufLen*2)+1)*sizeof(TCHAR));
279     memcpy(&dw, buf, sizeof(DWORD));
280     _stprintf(str, _T("%08lx"), dw);
281     /* Get rid of the last comma */
282     return str;
283 }
284
285 /******************************************************************************
286  * Converts a hex comma separated values list into a hex list.
287  * The Hex input string must be in exactly the correct form.
288  */
289 DWORD convertHexCSVToHex(TCHAR* str, BYTE* buf, ULONG bufLen)
290 {
291     TCHAR* s = str;  /* Pointer to current */
292     CHAR* b = buf;  /* Pointer to result  */
293     ULONG strLen    = _tcslen(str);
294     ULONG strPos    = 0;
295     DWORD byteCount = 0;
296
297     memset(buf, 0, bufLen);
298     /*
299      * warn the user if we are here with a string longer than 2 bytes that does
300      * not contains ",".  It is more likely because the data is invalid.
301      */
302     if ((strLen > 2) && (_tcschr(str, _T(',')) == NULL)) {
303         _tprintf(_T("WARNING converting CSV hex stream with no comma, ") \
304                  _T("input data seems invalid.\n"));
305     }
306     if (strLen > 3*bufLen) {
307         _tprintf(_T("ERROR converting CSV hex stream.  Too long\n"));
308     }
309     while (strPos < strLen) {
310         TCHAR xbuf[3];
311         TCHAR wc;
312         memcpy(xbuf, s, 2);
313         xbuf[3] = _T('\0');
314         _stscanf(xbuf, _T("%02x"), (UINT*)&wc);
315         if (byteCount < bufLen)
316             *b++ = (unsigned char)wc;
317         s += 3;
318         strPos += 3;
319         ++byteCount;
320     }
321     return byteCount;
322 }
323
324 /******************************************************************************
325  * This function returns the HKEY associated with the data type encoded in the
326  * value.  It modifies the input parameter (key value) in order to skip this
327  * "now useless" data type information.
328  *
329  * Note: Updated based on the algorithm used in 'server/registry.c'
330  */
331 DWORD getDataType(LPTSTR* lpValue, DWORD* parse_type)
332 {
333     struct data_type { const TCHAR *tag; int len; int type; int parse_type; };
334
335     static const struct data_type data_types[] =
336     {                   /* actual type */  /* type to assume for parsing */
337         { _T("\""),        1,   REG_SZ,              REG_SZ },
338         { _T("str:\""),    5,   REG_SZ,              REG_SZ },
339 //        { _T("str(2):\""), 8,   REG_EXPAND_SZ,       REG_SZ },
340         { _T("expand:\""), 8,   REG_EXPAND_SZ,       REG_EXPAND_SZ },
341         { _T("hex:"),      4,   REG_BINARY,          REG_BINARY },
342         { _T("dword:"),    6,   REG_DWORD,           REG_DWORD },
343         { _T("hex("),      4,   -1,                  REG_BINARY },
344         { NULL,            0,    0,                  0 }
345     };
346
347     const struct data_type *ptr;
348     int type;
349
350     for (ptr = data_types; ptr->tag; ptr++) {
351         if (memcmp(ptr->tag, *lpValue, ptr->len))
352             continue;
353
354         /* Found! */
355         *parse_type = ptr->parse_type;
356         type = ptr->type;
357         *lpValue += ptr->len;
358         if (type == -1) {
359             TCHAR* end;
360             /* "hex(xx):" is special */
361             type = (int)_tcstoul(*lpValue , &end, 16);
362             if (**lpValue == _T('\0') || *end != _T(')') || *(end+1) != _T(':')) {
363                 type = REG_NONE;
364             } else {
365                 *lpValue = end + 2;
366             }
367         }
368         return type;
369     }
370     return (**lpValue == _T('\0') ? REG_SZ : REG_NONE);
371 }
372
373 /******************************************************************************
374  * Returns an allocated buffer with a cleaned copy (removed the surrounding
375  * dbl quotes) of the passed value.
376  */
377 LPTSTR getArg(LPTSTR arg)
378 {
379   LPTSTR tmp = NULL;
380   ULONG len;
381
382   if (arg == NULL) return NULL;
383
384   // Get rid of surrounding quotes
385   len = _tcslen(arg);
386   if (arg[len-1] == _T('\"')) arg[len-1] = _T('\0');
387   if (arg[0]     == _T('\"')) arg++;
388   tmp = HeapAlloc(GetProcessHeap(), 0, (_tcslen(arg)+1) * sizeof(TCHAR));
389   _tcscpy(tmp, arg);
390   return tmp;
391 }
392
393 /******************************************************************************
394  * Replaces escape sequences with the characters.
395  */
396 void REGPROC_unescape_string(LPTSTR str)
397 {
398     int str_idx = 0;            /* current character under analysis */
399     int val_idx = 0;            /* the last character of the unescaped string */
400     int len = _tcslen(str);
401     for (str_idx = 0; str_idx < len; str_idx++, val_idx++) {
402         if (str[str_idx] == _T('\\')) {
403             str_idx++;
404             switch (str[str_idx]) {
405             case _T('n'):
406                 str[val_idx] = _T('\n');
407                 break;
408             case _T('\\'):
409             case _T('"'):
410                 str[val_idx] = str[str_idx];
411                 break;
412             default:
413                 _tprintf(_T("Warning! Unrecognized escape sequence: \\%c'\n"), str[str_idx]);
414                 str[val_idx] = str[str_idx];
415                 break;
416             }
417         } else {
418             str[val_idx] = str[str_idx];
419         }
420     }
421     str[val_idx] = _T('\0');
422 }
423
424 /******************************************************************************
425  * Sets the value with name val_name to the data in val_data for the currently
426  * opened key.
427  *
428  * Parameters:
429  * val_name - name of the registry value
430  * val_data - registry value data
431  */
432 HRESULT setValue(LPTSTR val_name, LPTSTR val_data)
433 {
434   HRESULT hRes;
435   DWORD   dwDataType, dwParseType;
436   LPBYTE lpbData;
437   BYTE   convert[KEY_MAX_LEN];
438   BYTE *bBigBuffer = 0;
439   DWORD  dwLen;
440
441   if ((val_name == NULL) || (val_data == NULL))
442     return ERROR_INVALID_PARAMETER;
443
444   /* Get the data type stored into the value field */
445   dwDataType = getDataType(&val_data, &dwParseType);
446
447 //  if (dwParseType == REG_EXPAND_SZ) {
448 //  }
449 //  if (dwParseType == REG_SZ || dwParseType == REG_EXPAND_SZ) { /* no conversion for string */
450
451   if (dwParseType == REG_SZ) {       /* no conversion for string */
452     dwLen = _tcslen(val_data);
453     if (dwLen > 0 && val_data[dwLen-1] == _T('"')) {
454       dwLen--;
455       val_data[dwLen] = _T('\0');
456     }
457     dwLen++;
458     dwLen *= sizeof(TCHAR);
459     REGPROC_unescape_string(val_data);
460     lpbData = val_data;
461   } else if (dwParseType == REG_DWORD) { /* Convert the dword types */
462     dwLen   = convertHexToDWord(val_data, convert);
463     lpbData = convert;
464   } else {                               /* Convert the hexadecimal types */
465     int b_len = _tcslen(val_data)+2/3;
466     if (b_len > KEY_MAX_LEN) {
467       bBigBuffer = HeapAlloc (GetProcessHeap(), 0, b_len * sizeof(TCHAR));
468       if (bBigBuffer == NULL) {
469           return ERROR_REGISTRY_IO_FAILED;
470       }
471       CHECK_ENOUGH_MEMORY(bBigBuffer);
472       dwLen = convertHexCSVToHex(val_data, bBigBuffer, b_len);
473       lpbData = bBigBuffer;
474     } else {
475       dwLen   = convertHexCSVToHex(val_data, convert, KEY_MAX_LEN);
476       lpbData = convert;
477     }
478   }
479   hRes = RegSetValueEx(currentKeyHandle, val_name,
480           0, /* Reserved */dwDataType, lpbData, dwLen);
481
482     _tprintf(_T("  Value: %s, Data: %s\n"), val_name, lpbData);
483
484
485   if (bBigBuffer)
486       HeapFree(GetProcessHeap(), 0, bBigBuffer);
487   return hRes;
488 }
489
490
491 /******************************************************************************
492  * Open the key
493  */
494 HRESULT openKey(LPTSTR stdInput)
495 {
496   DWORD   dwDisp;
497   HRESULT hRes;
498
499   /* Sanity checks */
500   if (stdInput == NULL)
501     return ERROR_INVALID_PARAMETER;
502
503   /* Get the registry class */
504   currentKeyClass = getRegClass(stdInput); /* Sets global variable */
505   if (currentKeyClass == (HKEY)ERROR_INVALID_PARAMETER)
506     return (HRESULT)ERROR_INVALID_PARAMETER;
507
508   /* Get the key name */
509   currentKeyName = getRegKeyName(stdInput); /* Sets global variable */
510   if (currentKeyName == NULL)
511     return ERROR_INVALID_PARAMETER;
512
513   hRes = RegCreateKeyEx(
514           currentKeyClass,          /* Class     */
515           currentKeyName,           /* Sub Key   */
516           0,                        /* MUST BE 0 */
517           NULL,                     /* object type */
518           REG_OPTION_NON_VOLATILE,  /* option, REG_OPTION_NON_VOLATILE ... */
519           KEY_ALL_ACCESS,           /* access mask, KEY_ALL_ACCESS */
520           NULL,                     /* security attribute */
521           &currentKeyHandle,        /* result */
522           &dwDisp);                 /* disposition, REG_CREATED_NEW_KEY or
523                                                     REG_OPENED_EXISTING_KEY */
524
525   if (hRes == ERROR_SUCCESS)
526     bTheKeyIsOpen = TRUE;
527
528   return hRes;
529
530 }
531
532 /******************************************************************************
533  * Extracts from [HKEY\some\key\path] or HKEY\some\key\path types of line
534  * the key name (what starts after the first '\')
535  */
536 LPTSTR getRegKeyName(LPTSTR lpLine)
537 {
538   LPTSTR keyNameBeg;
539   TCHAR  lpLineCopy[KEY_MAX_LEN];
540
541   if (lpLine == NULL)
542     return NULL;
543
544   _tcscpy(lpLineCopy, lpLine);
545   keyNameBeg = _tcschr(lpLineCopy, _T('\\'));    /* The key name start by '\' */
546   if (keyNameBeg) {
547       LPTSTR keyNameEnd;
548
549       keyNameBeg++;                             /* is not part of the name */
550       keyNameEnd = _tcschr(lpLineCopy, _T(']'));
551       if (keyNameEnd) {
552           *keyNameEnd = _T('\0');               /* remove ']' from the key name */
553       }
554   } else {
555       keyNameBeg = lpLineCopy + _tcslen(lpLineCopy); /* branch - empty string */
556   }
557   currentKeyName = HeapAlloc(GetProcessHeap(), 0, (_tcslen(keyNameBeg)+1)*sizeof(TCHAR));
558   CHECK_ENOUGH_MEMORY(currentKeyName);
559   _tcscpy(currentKeyName, keyNameBeg);
560   return currentKeyName;
561 }
562
563 /******************************************************************************
564  * Extracts from [HKEY\some\key\path] or HKEY\some\key\path types of line
565  * the key class (what ends before the first '\')
566  */
567 HKEY getRegClass(LPTSTR lpClass)
568 {
569   LPTSTR classNameEnd;
570   LPTSTR classNameBeg;
571   int i;
572
573   TCHAR lpClassCopy[KEY_MAX_LEN];
574
575   if (lpClass == NULL)
576     return (HKEY)ERROR_INVALID_PARAMETER;
577
578   _tcsncpy(lpClassCopy, lpClass, KEY_MAX_LEN);
579
580   classNameEnd  = _tcschr(lpClassCopy, _T('\\'));    /* The class name ends by '\' */
581   if (!classNameEnd) {                          /* or the whole string */
582       classNameEnd = lpClassCopy + _tcslen(lpClassCopy);
583       if (classNameEnd[-1] == _T(']')) {
584           classNameEnd--;
585       }
586   }
587   *classNameEnd = _T('\0');                       /* Isolate the class name */
588   if (lpClassCopy[0] == _T('[')) {
589       classNameBeg = lpClassCopy + 1;
590   } else {
591       classNameBeg = lpClassCopy;
592   }
593   for (i = 0; i < REG_CLASS_NUMBER; i++) {
594       if (!_tcscmp(classNameBeg, reg_class_names[i])) {
595           return reg_class_keys[i];
596       }
597   }
598   return (HKEY)ERROR_INVALID_PARAMETER;
599 }
600
601 /******************************************************************************
602  * Close the currently opened key.
603  */
604 void closeKey(VOID)
605 {
606     RegCloseKey(currentKeyHandle);
607     HeapFree(GetProcessHeap(), 0, currentKeyName); /* Allocated by getKeyName */
608     bTheKeyIsOpen    = FALSE;
609     currentKeyName   = NULL;
610     currentKeyClass  = 0;
611     currentKeyHandle = 0;
612 }
613
614 /******************************************************************************
615  * This function is the main entry point to the setValue type of action.  It
616  * receives the currently read line and dispatch the work depending on the
617  * context.
618  */
619 void doSetValue(LPTSTR stdInput)
620 {
621   /*
622    * We encountered the end of the file, make sure we
623    * close the opened key and exit
624    */
625   if (stdInput == NULL) {
626     if (bTheKeyIsOpen != FALSE)
627       closeKey();
628     return;
629   }
630
631   if (stdInput[0] == _T('[')) {     /* We are reading a new key */
632       if (bTheKeyIsOpen != FALSE) {
633           closeKey();                    /* Close the previous key before */
634       }
635       if (openKey(stdInput) != ERROR_SUCCESS) {
636           _tprintf(_T("doSetValue failed to open key %s\n"), stdInput);
637       }
638   } else if ((bTheKeyIsOpen) &&
639             ((stdInput[0] == _T('@')) || /* reading a default @=data pair */
640              (stdInput[0] == _T('\"')))) { /* reading a new value=data pair */
641     processSetValue(stdInput);
642   } else {             /* since we are assuming that the file format is */
643     if (bTheKeyIsOpen) /* valid we must be reading a blank line which  */
644       closeKey();      /* indicate end of this key processing */
645   }
646 }
647
648 /******************************************************************************
649  * This funtion is the main entry point to the queryValue type of action.  It
650  * receives the currently read line and dispatch the work depending on the
651  * context.
652  */
653 void doQueryValue(LPTSTR stdInput) {
654   /*
655    * We encoutered the end of the file, make sure we
656    * close the opened key and exit
657    */
658   if (stdInput == NULL) {
659     if (bTheKeyIsOpen != FALSE)
660       closeKey();
661     return;
662   }
663
664   if (stdInput[0] == _T('[')) {     /* We are reading a new key */
665     if (bTheKeyIsOpen != FALSE)
666       closeKey();                    /* Close the previous key before */
667     if (openKey(stdInput) != ERROR_SUCCESS ) {
668       _tprintf(_T("doQueryValue failed to open key %s\n"), stdInput);
669     }
670   }
671   else if( (bTheKeyIsOpen) &&
672            ((stdInput[0] == _T('@')) || /* reading a default @=data pair */
673            (stdInput[0] == _T('\"')))) { /* reading a new value=data pair */
674     processQueryValue(stdInput);
675   } else {                /* since we are assuming that the file format is */
676     if (bTheKeyIsOpen)    /* valid we must be reading a blank line which  */
677       closeKey();         /* indicate end of this key processing */
678   }
679 }
680
681 /******************************************************************************
682  * This funtion is the main entry point to the deletetValue type of action.  It
683  * receives the currently read line and dispatch the work depending on the
684  * context.
685  */
686 void doDeleteValue(LPTSTR line) {
687   _tprintf(_T("deleteValue not yet implemented\n"));
688 }
689
690 /******************************************************************************
691  * This funtion is the main entry point to the deleteKey type of action.  It
692  * receives the currently read line and dispatch the work depending on the
693  * context.
694  */
695 void doDeleteKey(LPTSTR line)   {
696   _tprintf(_T("deleteKey not yet implemented\n"));
697 }
698
699 /******************************************************************************
700  * This funtion is the main entry point to the createKey type of action.  It
701  * receives the currently read line and dispatch the work depending on the
702  * context.
703  */
704 void doCreateKey(LPTSTR line)   {
705   _tprintf(_T("createKey not yet implemented\n"));
706 }
707
708 /******************************************************************************
709  * This function is a wrapper for the setValue function.  It prepares the
710  * land and clean the area once completed.
711  * Note: this function modifies the line parameter.
712  *
713  * line - registry file unwrapped line. Should have the registry value name and
714  *      complete registry value data.
715  */
716 void processSetValue(LPTSTR line)
717 {
718   LPTSTR val_name;                   /* registry value name   */
719   LPTSTR val_data;                   /* registry value data   */
720
721   int line_idx = 0;                 /* current character under analysis */
722   HRESULT hRes = 0;
723
724   /* get value name */
725   if (line[line_idx] == _T('@') && line[line_idx + 1] == _T('=')) {
726       line[line_idx] = _T('\0');
727       val_name = line;
728       line_idx++;
729   } else if (line[line_idx] == _T('\"')) {
730       line_idx++;
731       val_name = line + line_idx;
732       while (TRUE) {
733           if (line[line_idx] == _T('\\')) {  /* skip escaped character */
734               line_idx += 2;
735           } else {
736               if (line[line_idx] == _T('\"')) {
737                   line[line_idx] = _T('\0');
738                   line_idx++;
739                   break;
740               } else {
741                   line_idx++;
742               }
743           }
744       }
745       if (line[line_idx] != _T('=')) {
746           line[line_idx] = _T('\"');
747           _tprintf(_T("Warning! uncrecognized line:\n%s\n"), line);
748           return;
749       }
750   } else {
751       _tprintf(_T("Warning! unrecognized line:\n%s\n"), line);
752       return;
753   }
754   line_idx++;                   /* skip the '=' character */
755   val_data = line + line_idx;
756   REGPROC_unescape_string(val_name);
757
758     _tprintf(_T("Key: %s, Value: %s, Data: %s\n"), currentKeyName, val_name, val_data);
759
760   hRes = setValue(val_name, val_data);
761   if (hRes != ERROR_SUCCESS) {
762     _tprintf(_T("ERROR Key %s not created. Value: %s, Data: %s\n"), currentKeyName, val_name, val_data);
763   }
764 }
765
766 /******************************************************************************
767  * This function is a wrapper for the queryValue function.  It prepares the
768  * land and clean the area once completed.
769  */
770 void processQueryValue(LPTSTR cmdline)
771 {
772   _tprintf(_T("ERROR!!! - temporary disabled"));
773   //exit(1);
774   return;
775 #if 0
776   LPSTR   argv[QUERY_VALUE_MAX_ARGS];/* args storage    */
777   LPSTR   token      = NULL;         /* current token analized */
778   ULONG   argCounter = 0;            /* counter of args */
779   INT     counter;
780   HRESULT hRes       = 0;
781   LPSTR   keyValue   = NULL;
782   LPSTR   lpsRes     = NULL;
783
784   /*
785    * Init storage and parse the line
786    */
787   for (counter = 0; counter < QUERY_VALUE_MAX_ARGS; counter++)
788     argv[counter] = NULL;
789
790   while ((token = getToken(&cmdline, queryValueDelim[argCounter])) != NULL) {
791     argv[argCounter++] = getArg(token);
792     if (argCounter == QUERY_VALUE_MAX_ARGS)
793       break;  /* Stop processing args no matter what */
794   }
795
796   /* The value we look for is the first token on the line */
797   if (argv[0] == NULL)
798     return; /* SHOULD NOT HAPPEN */
799   else
800     keyValue = argv[0];
801
802   if ((keyValue[0] == '@') && (_tcslen(keyValue) == 1)) {
803     LONG lLen = KEY_MAX_LEN;
804     TCHAR* lpsData = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,KEY_MAX_LEN);
805     /*
806      * We need to query the key default value
807      */
808     hRes = RegQueryValue(currentKeyHandle, currentKeyName, (LPBYTE)lpsData, &lLen);
809     if (hRes == ERROR_MORE_DATA) {
810         lpsData = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpsData, lLen);
811         hRes = RegQueryValue(currentKeyHandle, currentKeyName, (LPBYTE)lpsData, &lLen);
812     }
813     if (hRes == ERROR_SUCCESS) {
814       lpsRes = HeapAlloc(GetProcessHeap(), 0, lLen);
815       strncpy(lpsRes, lpsData, lLen);
816       lpsRes[lLen-1]='\0';
817     }
818   } else {
819     DWORD dwLen = KEY_MAX_LEN;
820     BYTE* lpbData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, KEY_MAX_LEN);
821     DWORD dwType;
822     /*
823      * We need to query a specific value for the key
824      */
825     hRes = RegQueryValueEx(
826              currentKeyHandle,
827              keyValue,
828              0,
829              &dwType,
830              (LPBYTE)lpbData,
831              &dwLen);
832
833     if (hRes == ERROR_MORE_DATA) {
834         lpbData = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpbData, dwLen * sizeof(TCHAR));
835         hRes = RegQueryValueEx(currentKeyHandle, keyValue, NULL, &dwType, (LPBYTE)lpbData, &dwLen);
836     }
837
838     if (hRes == ERROR_SUCCESS) {
839       /*
840        * Convert the returned data to a displayable format
841        */
842       switch (dwType) {
843         case REG_SZ:
844         case REG_EXPAND_SZ:
845           lpsRes = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(TCHAR));
846           strncpy(lpsRes, lpbData, dwLen);
847           lpsRes[dwLen-1] = '\0';
848           break;
849         case REG_DWORD:
850           lpsRes = convertHexToDWORDStr(lpbData, dwLen);
851           break;
852         default:
853           lpsRes = convertHexToHexCSV(lpbData, dwLen);
854           break;
855       }
856     }
857
858     HeapFree(GetProcessHeap(), 0, lpbData);
859   }
860   if (hRes == ERROR_SUCCESS) {
861     _tprintf(_T("Value \"%s\" = \"%s\" in key [%s]\n"), keyValue, lpsRes, currentKeyName);
862
863   } else {
864     _tprintf(_T("ERROR Value \"%s\" not found. for key \"%s\"\n"), keyValue, currentKeyName);
865   }
866
867   /*
868    * Do some cleanup
869    */
870   for (counter=0; counter<argCounter; counter++)
871     if (argv[counter] != NULL)
872       HeapFree(GetProcessHeap(), 0, argv[counter]);
873
874   if (lpsRes != NULL)
875     HeapFree(GetProcessHeap(), 0, lpsRes);
876 #endif
877 }
878
879 /******************************************************************************
880  * Calls command for each line of a registry file.
881  * Correctly processes comments (in # form), line continuation.
882  *
883  * Parameters:
884  *   in - input stream to read from
885  *   command - command to be called for each line
886  */
887 void processRegLines(FILE *in, CommandAPI command)
888 {
889     LPTSTR line     = NULL;  /* line read from input stream */
890     ULONG lineSize = REG_VAL_BUF_SIZE;
891
892     line = HeapAlloc(GetProcessHeap(), 0, lineSize * sizeof(TCHAR));
893     CHECK_ENOUGH_MEMORY(line);
894
895     while (!feof(in)) {
896         LPTSTR s; /* The pointer into line for where the current fgets should read */
897         s = line;
898         for (;;) {
899             size_t size_remaining;
900             int size_to_get;
901             TCHAR *s_eol; /* various local uses */
902
903             /* Do we need to expand the buffer ? */
904             assert (s >= line && s <= line + lineSize);
905             size_remaining = lineSize - (s-line);
906             if (size_remaining < 2) { /* room for 1 character and the \0 */
907                 TCHAR *new_buffer;
908                 size_t new_size = lineSize + REG_VAL_BUF_SIZE;
909                 if (new_size > lineSize) /* no arithmetic overflow */
910                     new_buffer = HeapReAlloc (GetProcessHeap(), 0, line, new_size * sizeof(TCHAR));
911                 else
912                     new_buffer = NULL;
913                 CHECK_ENOUGH_MEMORY(new_buffer);
914                 line = new_buffer;
915                 s = line + lineSize - size_remaining;
916                 lineSize = new_size;
917                 size_remaining = lineSize - (s-line);
918             }
919
920             /* Get as much as possible into the buffer, terminated either by
921              * eof, error, eol or getting the maximum amount.  Abort on error.
922              */
923 //
924 // This line is surely foobar, don't want to read INT_MAX in buffer at s, it's never going to be that big...
925 //            size_to_get = (size_remaining > INT_MAX ? INT_MAX : size_remaining);
926 //
927 // Looks as if 'lineSize' contains the number of characters of buffer size
928 //
929             size_to_get = (size_remaining > lineSize ? lineSize : size_remaining);
930
931             if (NULL == _fgetts(s, size_to_get, in)) {
932                 if (ferror(in)) {
933                     //_tperror(_T("While reading input"));
934                     perror ("While reading input");
935                     //exit(IO_ERROR);
936                     return;
937                 } else {
938                     assert (feof(in));
939                     *s = _T('\0');
940                     /* It is not clear to me from the definition that the
941                      * contents of the buffer are well defined on detecting
942                      * an eof without managing to read anything.
943                      */
944                 }
945             }
946
947             /* If we didn't read the eol nor the eof go around for the rest */
948             s_eol = _tcschr (s, _T('\n'));
949             if (!feof (in) && !s_eol) {
950                 s = _tcschr (s, _T('\0'));
951                 /* It should be s + size_to_get - 1 but this is safer */
952                 continue;
953             }
954
955             /* If it is a comment line then discard it and go around again */
956             if (line [0] == _T('#')) {
957                 s = line;
958                 continue;
959             }
960
961             /* Remove any line feed.  Leave s_eol on the \0 */
962             if (s_eol) {
963                 *s_eol = _T('\0');
964                 if (s_eol > line && *(s_eol-1) == _T('\r'))
965                     *--s_eol = _T('\0');
966             } else {
967                 s_eol = _tcschr (s, _T('\0'));
968             }
969             /* If there is a concatenating \\ then go around again */
970             if (s_eol > line && *(s_eol-1) == _T('\\')) {
971                 int c;
972                 s = s_eol-1;
973                 /* The following error protection could be made more self-
974                  * correcting but I thought it not worth trying.
975                  */
976
977                 if ((c = _fgettc(in)) == _TEOF || c != _T(' ') ||
978                     (c = _fgettc(in)) == _TEOF || c != _T(' '))
979                     _tprintf(_T("ERROR - invalid continuation.\n"));
980                 continue;
981             }
982             break; /* That is the full virtual line */
983         }
984         command(line);
985     }
986     command(NULL);
987     HeapFree(GetProcessHeap(), 0, line);
988 }
989
990 /******************************************************************************
991  * This funtion is the main entry point to the registerDLL action.  It
992  * receives the currently read line, then loads and registers the requested DLLs
993  */
994 void doRegisterDLL(LPTSTR stdInput)
995 {
996     HMODULE theLib = 0;
997     UINT retVal = 0;
998
999     /* Check for valid input */
1000     if (stdInput == NULL) return;
1001
1002     /* Load and register the library, then free it */
1003     theLib = LoadLibrary(stdInput);
1004     if (theLib) {
1005         FARPROC lpfnDLLRegProc = GetProcAddress(theLib, "DllRegisterServer");
1006         if (lpfnDLLRegProc) {
1007             retVal = (*lpfnDLLRegProc)();
1008         } else {
1009             _tprintf(_T("Couldn't find DllRegisterServer proc in '%s'.\n"), stdInput);
1010         }
1011         if (retVal != S_OK) {
1012             _tprintf(_T("Couldn't find DllRegisterServer proc in '%s'.\n"), stdInput);
1013         }
1014         FreeLibrary(theLib);
1015     } else {
1016         _tprintf(_T("Could not load DLL '%s'.\n"), stdInput);
1017     }
1018 }
1019
1020 /******************************************************************************
1021  * This funtion is the main entry point to the unregisterDLL action.  It
1022  * receives the currently read line, then loads and unregisters the requested DLLs
1023  */
1024 void doUnregisterDLL(LPTSTR stdInput)
1025 {
1026     HMODULE theLib = 0;
1027     UINT retVal = 0;
1028
1029     /* Check for valid input */
1030     if (stdInput == NULL) return;
1031
1032     /* Load and unregister the library, then free it */
1033     theLib = LoadLibrary(stdInput);
1034     if (theLib) {
1035         FARPROC lpfnDLLRegProc = GetProcAddress(theLib, "DllUnregisterServer");
1036         if (lpfnDLLRegProc) {
1037             retVal = (*lpfnDLLRegProc)();
1038         } else {
1039             _tprintf(_T("Couldn't find DllUnregisterServer proc in '%s'.\n"), stdInput);
1040         }
1041         if (retVal != S_OK) {
1042             _tprintf(_T("DLLUnregisterServer error 0x%x in '%s'.\n"), retVal, stdInput);
1043         }
1044         FreeLibrary(theLib);
1045     } else {
1046         _tprintf(_T("Could not load DLL '%s'.\n"), stdInput);
1047     }
1048 }
1049
1050 /****************************************************************************
1051  * REGPROC_print_error
1052  *
1053  * Print the message for GetLastError
1054  */
1055
1056 void REGPROC_print_error(VOID)
1057 {
1058     LPVOID lpMsgBuf;
1059     DWORD error_code;
1060     int status;
1061
1062     error_code = GetLastError ();
1063     status = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
1064                            NULL, error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
1065     if (!status) {
1066         _tprintf(_T("Cannot display message for error %ld, status %ld\n"), error_code, GetLastError());
1067     } else {
1068         _tprintf(_T("REGPROC_print_error() - "));
1069         puts(lpMsgBuf);
1070         LocalFree((HLOCAL)lpMsgBuf);
1071     }
1072     //exit(1);
1073 }
1074
1075 /******************************************************************************
1076  * Checks whether the buffer has enough room for the string or required size.
1077  * Resizes the buffer if necessary.
1078  *
1079  * Parameters:
1080  * buffer - pointer to a buffer for string
1081  * len - current length of the buffer in characters.
1082  * required_len - length of the string to place to the buffer in characters.
1083  *   The length does not include the terminating null character.
1084  */
1085 void REGPROC_resize_char_buffer(TCHAR **buffer, DWORD *len, DWORD required_len)
1086 {
1087     required_len++;
1088     if (required_len > *len) {
1089         *len = required_len;
1090         *buffer = HeapReAlloc(GetProcessHeap(), 0, *buffer, *len * sizeof(**buffer));
1091         CHECK_ENOUGH_MEMORY(*buffer);
1092     }
1093 }
1094
1095 /******************************************************************************
1096  * Prints string str to file
1097  */
1098 void REGPROC_export_string(FILE *file, TCHAR *str)
1099 {
1100     size_t len = _tcslen(str);
1101     size_t i;
1102
1103     /* escaping characters */
1104     for (i = 0; i < len; i++) {
1105         TCHAR c = str[i];
1106         switch (c) {
1107         //case _T('\\'): _fputts(_T("\\\\"), file); break;
1108         case _T('\"'): _fputts(_T("\\\""), file); break;
1109         case _T('\n'): _fputts(_T("\\\n"), file); break;
1110         default:       _fputtc(c, file);          break;
1111         }
1112     }
1113 }
1114
1115 /******************************************************************************
1116  * Writes contents of the registry key to the specified file stream.
1117  *
1118  * Parameters:
1119  * file - writable file stream to export registry branch to.
1120  * key - registry branch to export.
1121  * reg_key_name_buf - name of the key with registry class.
1122  *      Is resized if necessary.
1123  * reg_key_name_len - length of the buffer for the registry class in characters.
1124  * val_name_buf - buffer for storing value name.
1125  *      Is resized if necessary.
1126  * val_name_len - length of the buffer for storing value names in characters.
1127  * val_buf - buffer for storing values while extracting.
1128  *      Is resized if necessary.
1129  * val_size - size of the buffer for storing values in bytes.
1130  */
1131 void export_hkey(FILE *file, HKEY key,
1132                  TCHAR **reg_key_name_buf, DWORD *reg_key_name_len,
1133                  TCHAR **val_name_buf, DWORD *val_name_len,
1134                  BYTE **val_buf, DWORD *val_size)
1135 {
1136     DWORD max_sub_key_len;
1137     DWORD max_val_name_len;
1138     DWORD max_val_size;
1139     DWORD curr_len;
1140     DWORD i;
1141     BOOL more_data;
1142     LONG ret;
1143
1144     /* get size information and resize the buffers if necessary */
1145     if (RegQueryInfoKey(key, NULL, NULL, NULL, NULL, &max_sub_key_len, NULL,
1146                         NULL, &max_val_name_len, &max_val_size, NULL, NULL) != ERROR_SUCCESS) {
1147         REGPROC_print_error();
1148     }
1149     curr_len = _tcslen(*reg_key_name_buf);
1150     REGPROC_resize_char_buffer(reg_key_name_buf, reg_key_name_len, max_sub_key_len + curr_len + 1);
1151     REGPROC_resize_char_buffer(val_name_buf, val_name_len, max_val_name_len);
1152     if (max_val_size > *val_size) {
1153         *val_size = max_val_size;
1154         *val_buf = HeapReAlloc(GetProcessHeap(), 0, *val_buf, *val_size * sizeof(TCHAR));
1155         CHECK_ENOUGH_MEMORY(val_buf);
1156     }
1157     /* output data for the current key */
1158     _fputts(_T("\n["), file);
1159     _fputts(*reg_key_name_buf, file);
1160     _fputts(_T("]\n"), file);
1161     /* print all the values */
1162     i = 0;
1163     more_data = TRUE;
1164     while (more_data) {
1165         DWORD value_type;
1166         DWORD val_name_len1 = *val_name_len;
1167         DWORD val_size1 = *val_size;
1168         ret = RegEnumValue(key, i, *val_name_buf, &val_name_len1, NULL, &value_type, *val_buf, &val_size1);
1169         if (ret != ERROR_SUCCESS) {
1170             more_data = FALSE;
1171             if (ret != ERROR_NO_MORE_ITEMS) {
1172                 REGPROC_print_error();
1173             }
1174         } else {
1175             i++;
1176             if ((*val_name_buf)[0]) {
1177                 _fputts(_T("\""), file);
1178                 REGPROC_export_string(file, *val_name_buf);
1179                 _fputts(_T("\"="), file);
1180             } else {
1181                 _fputts(_T("@="), file);
1182             }
1183             switch (value_type) {
1184             case REG_EXPAND_SZ:
1185                 _fputts(_T("expand:"), file);
1186             case REG_SZ:
1187                 _fputts(_T("\""), file);
1188                 REGPROC_export_string(file, *val_buf);
1189                 _fputts(_T("\"\n"), file);
1190                 break;
1191             case REG_DWORD:
1192                 _ftprintf(file, _T("dword:%08lx\n"), *((DWORD *)*val_buf));
1193                 break;
1194             default:
1195 /*
1196                 _tprintf(_T("warning - unsupported registry format '%ld', ") \
1197                          _T("treating as binary\n"), value_type);
1198                 _tprintf(_T("key name: \"%s\"\n"), *reg_key_name_buf);
1199                 _tprintf(_T("value name:\"%s\"\n\n"), *val_name_buf);
1200  */
1201                 /* falls through */
1202             case REG_MULTI_SZ:
1203                 /* falls through */
1204             case REG_BINARY:
1205             {
1206                 DWORD i1;
1207                 TCHAR *hex_prefix;
1208                 TCHAR buf[20];
1209                 int cur_pos;
1210
1211                 if (value_type == REG_BINARY) {
1212                     hex_prefix = _T("hex:");
1213                 } else {
1214                     hex_prefix = buf;
1215                     _stprintf(buf, _T("hex(%ld):"), value_type);
1216                 }
1217                 /* position of where the next character will be printed */
1218                 /* NOTE: yes, _tcslen("hex:") is used even for hex(x): */
1219                 cur_pos = _tcslen(_T("\"\"=")) + _tcslen(_T("hex:")) +
1220                     _tcslen(*val_name_buf);
1221                 _fputts(hex_prefix, file);
1222                 for (i1 = 0; i1 < val_size1; i1++) {
1223                     _ftprintf(file, _T("%02x"), (unsigned int)(*val_buf)[i1]);
1224                     if (i1 + 1 < val_size1) {
1225                         _fputts(_T(","), file);
1226                     }
1227                     cur_pos += 3;
1228                     /* wrap the line */
1229                     if (cur_pos > REG_FILE_HEX_LINE_LEN) {
1230                         _fputts(_T("\\\n  "), file);
1231                         cur_pos = 2;
1232                     }
1233                 }
1234                 _fputts(_T("\n"), file);
1235                 break;
1236             }
1237             }
1238         }
1239     }
1240     i = 0;
1241     more_data = TRUE;
1242     (*reg_key_name_buf)[curr_len] = _T('\\');
1243     while (more_data) {
1244         DWORD buf_len = *reg_key_name_len - curr_len;
1245         ret = RegEnumKeyEx(key, i, *reg_key_name_buf + curr_len + 1, &buf_len, NULL, NULL, NULL, NULL);
1246         if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
1247             more_data = FALSE;
1248             if (ret != ERROR_NO_MORE_ITEMS) {
1249                 REGPROC_print_error();
1250             }
1251         } else {
1252             HKEY subkey;
1253
1254             i++;
1255             if (RegOpenKey(key, *reg_key_name_buf + curr_len + 1, &subkey) == ERROR_SUCCESS) {
1256                 export_hkey(file, subkey, reg_key_name_buf, reg_key_name_len, val_name_buf, val_name_len, val_buf, val_size);
1257                 RegCloseKey(subkey);
1258             } else {
1259                 REGPROC_print_error();
1260             }
1261         }
1262     }
1263     (*reg_key_name_buf)[curr_len] = _T('\0');
1264 }
1265 /*
1266 #define REG_NONE                    ( 0 )   // No value type
1267 #define REG_SZ                      ( 1 )   // Unicode nul terminated string
1268 #define REG_EXPAND_SZ               ( 2 )   // Unicode nul terminated string
1269                                             // (with environment variable references)
1270 #define REG_BINARY                  ( 3 )   // Free form binary
1271 #define REG_DWORD                   ( 4 )   // 32-bit number
1272 #define REG_DWORD_LITTLE_ENDIAN     ( 4 )   // 32-bit number (same as REG_DWORD)
1273 #define REG_DWORD_BIG_ENDIAN        ( 5 )   // 32-bit number
1274 #define REG_LINK                    ( 6 )   // Symbolic Link (unicode)
1275 #define REG_MULTI_SZ                ( 7 )   // Multiple Unicode strings
1276 #define REG_RESOURCE_LIST           ( 8 )   // Resource list in the resource map
1277 #define REG_FULL_RESOURCE_DESCRIPTOR ( 9 )  // Resource list in the hardware description
1278 #define REG_RESOURCE_REQUIREMENTS_LIST ( 10 )
1279
1280  */
1281 /******************************************************************************
1282  * Open file for export.
1283  */
1284 FILE *REGPROC_open_export_file(TCHAR *file_name)
1285 {
1286 //_CRTIMP FILE * __cdecl _wfopen(const wchar_t *, const wchar_t *);
1287
1288 //FILE* fopen (const char* szFileName, const char* szMode);
1289 //FILE* _wfopen(const wchar_t *file, const wchar_t *mode);
1290
1291     FILE *file = _tfopen(file_name, _T("w"));
1292     if (!file) {
1293         perror("");
1294         _tprintf(_T("REGPROC_open_export_file(%s) - Can't open file.\n"), file_name);
1295         //exit(1);
1296         return NULL;
1297     }
1298     _fputts(_T("REGEDIT4\n"), file);
1299     return file;
1300 }
1301
1302 /******************************************************************************
1303  * Writes contents of the registry key to the specified file stream.
1304  *
1305  * Parameters:
1306  * file_name - name of a file to export registry branch to.
1307  * reg_key_name - registry branch to export. The whole registry is exported if
1308  *      reg_key_name is NULL or contains an empty string.
1309  */
1310 BOOL export_registry_key(TCHAR* file_name, TCHAR* reg_key_name)
1311 {
1312     HKEY reg_key_class;
1313
1314     TCHAR *reg_key_name_buf;
1315     TCHAR *val_name_buf;
1316     BYTE *val_buf;
1317     DWORD reg_key_name_len = KEY_MAX_LEN;
1318     DWORD val_name_len = KEY_MAX_LEN;
1319     DWORD val_size = REG_VAL_BUF_SIZE;
1320     FILE *file = NULL;
1321
1322     //_tprintf(_T("export_registry_key(%s, %s)\n"), reg_key_name, file_name);
1323
1324     reg_key_name_buf = HeapAlloc(GetProcessHeap(), 0, reg_key_name_len * sizeof(*reg_key_name_buf));
1325     val_name_buf = HeapAlloc(GetProcessHeap(), 0, val_name_len * sizeof(*val_name_buf));
1326     val_buf = HeapAlloc(GetProcessHeap(), 0, val_size);
1327     CHECK_ENOUGH_MEMORY(reg_key_name_buf && val_name_buf && val_buf);
1328
1329     if (reg_key_name && reg_key_name[0]) {
1330         TCHAR *branch_name;
1331         HKEY key;
1332
1333         REGPROC_resize_char_buffer(&reg_key_name_buf, &reg_key_name_len,
1334                                    _tcslen(reg_key_name));
1335         _tcscpy(reg_key_name_buf, reg_key_name);
1336
1337         /* open the specified key */
1338         reg_key_class = getRegClass(reg_key_name);
1339         if (reg_key_class == (HKEY)ERROR_INVALID_PARAMETER) {
1340             _tprintf(_T("Incorrect registry class specification in '%s\n"), reg_key_name);
1341             //exit(1);
1342             return FALSE;
1343         }
1344         branch_name = getRegKeyName(reg_key_name);
1345         CHECK_ENOUGH_MEMORY(branch_name);
1346         if (!branch_name[0]) {
1347             /* no branch - registry class is specified */
1348             file = REGPROC_open_export_file(file_name);
1349             export_hkey(file, reg_key_class,
1350                         &reg_key_name_buf, &reg_key_name_len,
1351                         &val_name_buf, &val_name_len,
1352                         &val_buf, &val_size);
1353         } else if (RegOpenKey(reg_key_class, branch_name, &key) == ERROR_SUCCESS) {
1354             file = REGPROC_open_export_file(file_name);
1355             export_hkey(file, key,
1356                         &reg_key_name_buf, &reg_key_name_len,
1357                         &val_name_buf, &val_name_len,
1358                         &val_buf, &val_size);
1359             RegCloseKey(key);
1360         } else {
1361             _tprintf(_T("Can't export. Registry key '%s does not exist!\n"), reg_key_name);
1362             REGPROC_print_error();
1363         }
1364         HeapFree(GetProcessHeap(), 0, branch_name);
1365     } else {
1366         int i;
1367
1368         /* export all registry classes */
1369         file = REGPROC_open_export_file(file_name);
1370         for (i = 0; i < REG_CLASS_NUMBER; i++) {
1371             /* do not export HKEY_CLASSES_ROOT */
1372             if (reg_class_keys[i] != HKEY_CLASSES_ROOT &&
1373                 reg_class_keys[i] != HKEY_CURRENT_USER &&
1374                 reg_class_keys[i] != HKEY_CURRENT_CONFIG) {
1375                 _tcscpy(reg_key_name_buf, reg_class_names[i]);
1376                 export_hkey(file, reg_class_keys[i],
1377                             &reg_key_name_buf, &reg_key_name_len,
1378                             &val_name_buf, &val_name_len,
1379                             &val_buf, &val_size);
1380             }
1381         }
1382     }
1383     if (file) {
1384         fclose(file);
1385     }
1386 //    HeapFree(GetProcessHeap(), 0, reg_key_name);
1387     HeapFree(GetProcessHeap(), 0, val_buf);
1388     HeapFree(GetProcessHeap(), 0, val_name_buf);
1389     HeapFree(GetProcessHeap(), 0, reg_key_name_buf);
1390     return TRUE;
1391 }
1392
1393 /******************************************************************************
1394  * Reads contents of the specified file into the registry.
1395  */
1396 BOOL import_registry_file(LPTSTR filename)
1397 {
1398     FILE* reg_file = _tfopen(filename, _T("r"));
1399
1400     if (reg_file) {
1401         processRegLines(reg_file, doSetValue);
1402         return TRUE;
1403     }
1404     return FALSE;
1405 }
1406
1407 /******************************************************************************
1408  * Recursive function which removes the registry key with all subkeys.
1409  */
1410 BOOL delete_branch(HKEY key, TCHAR** reg_key_name_buf, DWORD* reg_key_name_len)
1411 {
1412     HKEY branch_key;
1413     DWORD max_sub_key_len;
1414     DWORD subkeys;
1415     DWORD curr_len;
1416     LONG ret;
1417     long int i;
1418
1419     if (RegOpenKey(key, *reg_key_name_buf, &branch_key) != ERROR_SUCCESS) {
1420         REGPROC_print_error();
1421         return FALSE;
1422     }
1423
1424     /* get size information and resize the buffers if necessary */
1425     if (RegQueryInfoKey(branch_key, NULL, NULL, NULL, &subkeys, &max_sub_key_len,
1426                         NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
1427         REGPROC_print_error();
1428         RegCloseKey(branch_key);
1429         return FALSE;
1430     }
1431     curr_len = _tcslen(*reg_key_name_buf);
1432     REGPROC_resize_char_buffer(reg_key_name_buf, reg_key_name_len, max_sub_key_len + curr_len + 1);
1433
1434     (*reg_key_name_buf)[curr_len] = '\\';
1435     for (i = subkeys - 1; i >= 0; i--) {
1436         DWORD buf_len = *reg_key_name_len - curr_len;
1437         ret = RegEnumKeyEx(branch_key, i, *reg_key_name_buf + curr_len + 1, &buf_len, NULL, NULL, NULL, NULL);
1438         if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA && ret != ERROR_NO_MORE_ITEMS) {
1439             REGPROC_print_error();
1440             RegCloseKey(branch_key);
1441             return FALSE;
1442         } else {
1443             delete_branch(key, reg_key_name_buf, reg_key_name_len);
1444         }
1445     }
1446     (*reg_key_name_buf)[curr_len] = '\0';
1447     RegCloseKey(branch_key);
1448     RegDeleteKey(key, *reg_key_name_buf);
1449     return TRUE;
1450 }
1451
1452 /******************************************************************************
1453  * Removes the registry key with all subkeys. Parses full key name.
1454  *
1455  * Parameters:
1456  * reg_key_name - full name of registry branch to delete. Ignored if is NULL,
1457  *      empty, points to register key class, does not exist.
1458  */
1459 void delete_registry_key(TCHAR* reg_key_name)
1460 {
1461     TCHAR* branch_name;
1462     DWORD branch_name_len;
1463     HKEY reg_key_class;
1464     HKEY branch_key;
1465
1466     if (!reg_key_name || !reg_key_name[0]) {
1467         return;
1468     }
1469     /* open the specified key */
1470     reg_key_class = getRegClass(reg_key_name);
1471     if (reg_key_class == (HKEY)ERROR_INVALID_PARAMETER) {
1472         _tprintf(_T("Incorrect registry class specification in '%s'\n"), reg_key_name);
1473         //exit(1);
1474         return;
1475     }
1476     branch_name = getRegKeyName(reg_key_name);
1477     CHECK_ENOUGH_MEMORY(branch_name);
1478     branch_name_len = _tcslen(branch_name);
1479     if (!branch_name[0]) {
1480         _tprintf(_T("Can't delete registry class '%s'\n"), reg_key_name);
1481         //exit(1);
1482         return;
1483     }
1484     if (RegOpenKey(reg_key_class, branch_name, &branch_key) == ERROR_SUCCESS) {
1485         /* check whether the key exists */
1486         RegCloseKey(branch_key);
1487         delete_branch(reg_key_class, &branch_name, &branch_name_len);
1488     }
1489     HeapFree(GetProcessHeap(), 0, branch_name);
1490 }
1491