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