Create a branch for network fixes.
[reactos.git] / base / applications / 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #include <regedit.h>
24
25 #define REG_VAL_BUF_SIZE 4096
26
27 /* maximal number of characters in hexadecimal data line,
28 not including '\' character */
29 #define REG_FILE_HEX_LINE_LEN 76
30
31 /* Globals used by the api setValue */
32 static LPSTR currentKeyName = NULL;
33 static HKEY currentKeyClass = 0;
34 static HKEY currentKeyHandle = 0;
35 static BOOL bTheKeyIsOpen = FALSE;
36
37 static const CHAR *app_name = "UNKNOWN";
38
39 static const CHAR *reg_class_names[] = {
40 "HKEY_LOCAL_MACHINE", "HKEY_USERS", "HKEY_CLASSES_ROOT",
41 "HKEY_CURRENT_CONFIG", "HKEY_CURRENT_USER", "HKEY_DYN_DATA"
42 };
43
44 #define REG_CLASS_NUMBER (sizeof(reg_class_names) / sizeof(reg_class_names[0]))
45
46 static HKEY reg_class_keys[REG_CLASS_NUMBER] = {
47 HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CLASSES_ROOT,
48 HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_DYN_DATA
49 };
50
51 /* return values */
52 #define NOT_ENOUGH_MEMORY 1
53 #define IO_ERROR 2
54
55 /* processing macros */
56
57 /* common check of memory allocation results */
58 #define CHECK_ENOUGH_MEMORY(p) \
59 if (!(p)) \
60 { \
61 fprintf(stderr,"%s: file %s, line %d: Not enough memory\n", \
62 getAppName(), __FILE__, __LINE__); \
63 exit(NOT_ENOUGH_MEMORY); \
64 }
65
66 /******************************************************************************
67 * This is a replacement for strsep which is not portable (missing on Solaris).
68 */
69 #if 0
70 /* DISABLED */
71 char* getToken(char** str, const char* delims)
72 {
73 char* token;
74
75 if (*str==NULL) {
76 /* No more tokens */
77 return NULL;
78 }
79
80 token=*str;
81 while (**str!='\0') {
82 if (strchr(delims,**str)!=NULL) {
83 **str='\0';
84 (*str)++;
85 return token;
86 }
87 (*str)++;
88 }
89 /* There is no other token */
90 *str=NULL;
91 return token;
92 }
93 #endif
94
95 /******************************************************************************
96 * Copies file name from command line string to the buffer.
97 * Rewinds the command line string pointer to the next non-space character
98 * after the file name.
99 * Buffer contains an empty string if no filename was found;
100 *
101 * params:
102 * command_line - command line current position pointer
103 * where *s[0] is the first symbol of the file name.
104 * file_name - buffer to write the file name to.
105 */
106 void get_file_name(CHAR **command_line, CHAR *file_name)
107 {
108 CHAR *s = *command_line;
109 int pos = 0; /* position of pointer "s" in *command_line */
110 file_name[0] = 0;
111
112 if (!s[0]) {
113 return;
114 }
115
116 if (s[0] == '"') {
117 s++;
118 (*command_line)++;
119 while(s[0] != '"') {
120 if (!s[0]) {
121 fprintf(stderr,"%s: Unexpected end of file name!\n",
122 getAppName());
123 exit(1);
124 }
125 s++;
126 pos++;
127 }
128 } else {
129 while(s[0] && !isspace(s[0])) {
130 s++;
131 pos++;
132 }
133 }
134 memcpy(file_name, *command_line, pos * sizeof((*command_line)[0]));
135 /* remove the last backslash */
136 if (file_name[pos - 1] == '\\') {
137 file_name[pos - 1] = '\0';
138 } else {
139 file_name[pos] = '\0';
140 }
141
142 if (s[0]) {
143 s++;
144 pos++;
145 }
146 while(s[0] && isspace(s[0])) {
147 s++;
148 pos++;
149 }
150 (*command_line) += pos;
151 }
152
153
154 /******************************************************************************
155 * Converts a hex representation of a DWORD into a DWORD.
156 */
157 static BOOL convertHexToDWord(char* str, DWORD *dw)
158 {
159 char dummy;
160 if (strlen(str) > 8 || sscanf(str, "%lx%c", dw, &dummy) != 1) {
161 fprintf(stderr,"%s: ERROR, invalid hex value\n", getAppName());
162 return FALSE;
163 }
164 return TRUE;
165 }
166
167 /******************************************************************************
168 * Converts a hex comma separated values list into a binary string.
169 */
170 static BYTE* convertHexCSVToHex(char *str, DWORD *size)
171 {
172 char *s;
173 BYTE *d, *data;
174
175 /* The worst case is 1 digit + 1 comma per byte */
176 *size=(strlen(str)+1)/2;
177 data=HeapAlloc(GetProcessHeap(), 0, *size);
178 CHECK_ENOUGH_MEMORY(data);
179
180 s = str;
181 d = data;
182 *size=0;
183 while (*s != '\0') {
184 UINT wc;
185 char dummy;
186
187 if (s[1] != ',' && s[1] != '\0' && s[2] != ',' && s[2] != '\0') {
188 fprintf(stderr,"%s: ERROR converting CSV hex stream. Invalid sequence at '%s'\n",
189 getAppName(), s);
190 HeapFree(GetProcessHeap(), 0, data);
191 return NULL;
192 }
193 if (sscanf(s, "%x%c", &wc, &dummy) < 1 || dummy != ',') {
194 fprintf(stderr,"%s: ERROR converting CSV hex stream. Invalid value at '%s'\n",
195 getAppName(), s);
196 HeapFree(GetProcessHeap(), 0, data);
197 return NULL;
198 }
199 *d++ =(BYTE)wc;
200 (*size)++;
201 /* Skip one or two digits and any comma */
202 while (*s && *s!=',') s++;
203 if (*s) s++;
204 }
205
206 return data;
207 }
208
209 /******************************************************************************
210 * This function returns the HKEY associated with the data type encoded in the
211 * value. It modifies the input parameter (key value) in order to skip this
212 * "now useless" data type information.
213 *
214 * Note: Updated based on the algorithm used in 'server/registry.c'
215 */
216 DWORD getDataType(LPSTR *lpValue, DWORD* parse_type)
217 {
218 struct data_type { const char *tag; int len; int type; int parse_type; };
219
220 static const struct data_type data_types[] = { /* actual type */ /* type to assume for parsing */
221 { "\"", 1, REG_SZ, REG_SZ },
222 { "str:\"", 5, REG_SZ, REG_SZ },
223 { "str(2):\"", 8, REG_EXPAND_SZ, REG_SZ },
224 { "hex:", 4, REG_BINARY, REG_BINARY },
225 { "dword:", 6, REG_DWORD, REG_DWORD },
226 { "hex(", 4, -1, REG_BINARY },
227 { NULL, 0, 0, 0 }
228 };
229
230 const struct data_type *ptr;
231 int type;
232
233 for (ptr = data_types; ptr->tag; ptr++) {
234 if (memcmp( ptr->tag, *lpValue, ptr->len ))
235 continue;
236
237 /* Found! */
238 *parse_type = ptr->parse_type;
239 type=ptr->type;
240 *lpValue+=ptr->len;
241 if (type == -1) {
242 char* end;
243 /* "hex(xx):" is special */
244 type = (int)strtoul( *lpValue , &end, 16 );
245 if (**lpValue=='\0' || *end!=')' || *(end+1)!=':') {
246 type=REG_NONE;
247 } else {
248 *lpValue=end+2;
249 }
250 }
251 return type;
252 }
253 *parse_type=REG_NONE;
254 return REG_NONE;
255 }
256
257 /******************************************************************************
258 * Returns an allocated buffer with a cleaned copy (removed the surrounding
259 * dbl quotes) of the passed value.
260 */
261 LPSTR getArg( LPSTR arg)
262 {
263 LPSTR tmp = NULL;
264 size_t len;
265
266 if (arg == NULL)
267 return NULL;
268
269 /*
270 * Get rid of surrounding quotes
271 */
272 len = strlen(arg);
273
274 if( arg[len-1] == '\"' ) arg[len-1] = '\0';
275 if( arg[0] == '\"' ) arg++;
276
277 tmp = HeapAlloc(GetProcessHeap(), 0, strlen(arg)+1);
278 CHECK_ENOUGH_MEMORY(tmp);
279 strcpy(tmp, arg);
280
281 return tmp;
282 }
283
284 /******************************************************************************
285 * Replaces escape sequences with the characters.
286 */
287 static void REGPROC_unescape_string(LPSTR str)
288 {
289 size_t str_idx = 0; /* current character under analysis */
290 size_t val_idx = 0; /* the last character of the unescaped string */
291 size_t len = strlen(str);
292 for (str_idx = 0; str_idx < len; str_idx++, val_idx++) {
293 if (str[str_idx] == '\\') {
294 str_idx++;
295 switch (str[str_idx]) {
296 case 'n':
297 str[val_idx] = '\n';
298 break;
299 case '\\':
300 case '"':
301 str[val_idx] = str[str_idx];
302 break;
303 default:
304 fprintf(stderr,"Warning! Unrecognized escape sequence: \\%c'\n",
305 str[str_idx]);
306 str[val_idx] = str[str_idx];
307 break;
308 }
309 } else {
310 str[val_idx] = str[str_idx];
311 }
312 }
313 str[val_idx] = '\0';
314 }
315
316 /******************************************************************************
317 * Sets the value with name val_name to the data in val_data for the currently
318 * opened key.
319 *
320 * Parameters:
321 * val_name - name of the registry value
322 * val_data - registry value data
323 */
324 LONG setValue(LPSTR val_name, LPSTR val_data)
325 {
326 LONG res;
327 DWORD dwDataType, dwParseType;
328 LPBYTE lpbData;
329 DWORD dwData, dwLen;
330
331 if ( (val_name == NULL) || (val_data == NULL) )
332 return ERROR_INVALID_PARAMETER;
333
334 if (strcmp(val_data, "-") == 0)
335 {
336 res=RegDeleteValueA(currentKeyHandle,val_name);
337 return (res == ERROR_FILE_NOT_FOUND ? ERROR_SUCCESS : res);
338 }
339
340 /* Get the data type stored into the value field */
341 dwDataType = getDataType(&val_data, &dwParseType);
342
343 if (dwParseType == REG_SZ) /* no conversion for string */
344 {
345 dwLen = (DWORD) strlen(val_data);
346 if (dwLen>0 && val_data[dwLen-1]=='"')
347 {
348 dwLen--;
349 val_data[dwLen]='\0';
350 }
351 dwLen++;
352 REGPROC_unescape_string(val_data);
353 lpbData = (LPBYTE)val_data;
354 }
355 else if (dwParseType == REG_DWORD) /* Convert the dword types */
356 {
357 if (!convertHexToDWord(val_data, &dwData))
358 return ERROR_INVALID_DATA;
359 lpbData = (BYTE*)&dwData;
360 dwLen = sizeof(dwData);
361 }
362 else if (dwParseType == REG_BINARY) /* Convert the binary data */
363 {
364 lpbData = convertHexCSVToHex(val_data, &dwLen);
365 if (!lpbData)
366 return ERROR_INVALID_DATA;
367 }
368 else /* unknown format */
369 {
370 fprintf(stderr,"%s: ERROR, unknown data format\n", getAppName());
371 return ERROR_INVALID_DATA;
372 }
373
374 res = RegSetValueExA(
375 currentKeyHandle,
376 val_name,
377 0, /* Reserved */
378 dwDataType,
379 lpbData,
380 dwLen);
381
382 if (dwParseType == REG_BINARY)
383 HeapFree(GetProcessHeap(), 0, lpbData);
384 return res;
385 }
386
387
388 /******************************************************************************
389 * Open the key
390 */
391 LONG openKey( LPSTR stdInput)
392 {
393 DWORD dwDisp;
394 LONG res;
395
396 /* Sanity checks */
397 if (stdInput == NULL)
398 return ERROR_INVALID_PARAMETER;
399
400 /* Get the registry class */
401 if (!getRegClass(stdInput, &currentKeyClass)) /* Sets global variable */
402 return ERROR_INVALID_PARAMETER;
403
404 /* Get the key name */
405 currentKeyName = getRegKeyName(stdInput); /* Sets global variable */
406 if (currentKeyName == NULL)
407 return ERROR_INVALID_PARAMETER;
408
409 res = RegCreateKeyExA(
410 currentKeyClass, /* Class */
411 currentKeyName, /* Sub Key */
412 0, /* MUST BE 0 */
413 NULL, /* object type */
414 REG_OPTION_NON_VOLATILE, /* option, REG_OPTION_NON_VOLATILE ... */
415 KEY_ALL_ACCESS, /* access mask, KEY_ALL_ACCESS */
416 NULL, /* security attribute */
417 &currentKeyHandle, /* result */
418 &dwDisp); /* disposition, REG_CREATED_NEW_KEY or
419 REG_OPENED_EXISTING_KEY */
420
421 if (res == ERROR_SUCCESS)
422 bTheKeyIsOpen = TRUE;
423
424 return res;
425
426 }
427
428 /******************************************************************************
429 * Extracts from [HKEY\some\key\path] or HKEY\some\key\path types of line
430 * the key name (what starts after the first '\')
431 */
432 LPSTR getRegKeyName(LPSTR lpLine)
433 {
434 LPSTR keyNameBeg;
435 char lpLineCopy[KEY_MAX_LEN];
436
437 if (lpLine == NULL)
438 return NULL;
439
440 strcpy(lpLineCopy, lpLine);
441
442 keyNameBeg = strchr(lpLineCopy, '\\'); /* The key name start by '\' */
443 if (keyNameBeg) {
444 keyNameBeg++;
445 if (lpLine[0] == '[') /* need to find matching ']' */
446 {
447 LPSTR keyNameEnd;
448
449 keyNameEnd = strrchr(lpLineCopy, ']');
450 if (keyNameEnd) {
451 *keyNameEnd = '\0'; /* remove ']' from the key name */
452 }
453 }
454 } else {
455 keyNameBeg = lpLineCopy + strlen(lpLineCopy); /* branch - empty string */
456 }
457 currentKeyName = HeapAlloc(GetProcessHeap(), 0, strlen(keyNameBeg) + 1);
458 CHECK_ENOUGH_MEMORY(currentKeyName);
459 strcpy(currentKeyName, keyNameBeg);
460 return currentKeyName;
461 }
462
463 /******************************************************************************
464 * Extracts from [HKEY\some\key\path] or HKEY\some\key\path types of line
465 * the key class (what ends before the first '\')
466 */
467 BOOL getRegClass(LPSTR lpClass, HKEY* hkey)
468 {
469 LPSTR classNameEnd;
470 LPSTR classNameBeg;
471 unsigned int i;
472
473 char lpClassCopy[KEY_MAX_LEN];
474
475 if (lpClass == NULL)
476 return FALSE;
477
478 lstrcpynA(lpClassCopy, lpClass, KEY_MAX_LEN);
479
480 classNameEnd = strchr(lpClassCopy, '\\'); /* The class name ends by '\' */
481 if (!classNameEnd) /* or the whole string */
482 {
483 classNameEnd = lpClassCopy + strlen(lpClassCopy);
484 if (classNameEnd[-1] == ']')
485 {
486 classNameEnd--;
487 }
488 }
489 *classNameEnd = '\0'; /* Isolate the class name */
490 if (lpClassCopy[0] == '[') {
491 classNameBeg = lpClassCopy + 1;
492 } else {
493 classNameBeg = lpClassCopy;
494 }
495
496 for (i = 0; i < REG_CLASS_NUMBER; i++) {
497 if (!strcmp(classNameBeg, reg_class_names[i])) {
498 *hkey = reg_class_keys[i];
499 return TRUE;
500 }
501 }
502 return FALSE;
503 }
504
505 /******************************************************************************
506 * Close the currently opened key.
507 */
508 void closeKey(void)
509 {
510 RegCloseKey(currentKeyHandle);
511
512 HeapFree(GetProcessHeap(), 0, currentKeyName); /* Allocated by getKeyName */
513
514 bTheKeyIsOpen = FALSE;
515
516 currentKeyName = NULL;
517 currentKeyClass = 0;
518 currentKeyHandle = 0;
519 }
520
521 /******************************************************************************
522 * This function is the main entry point to the setValue type of action. It
523 * receives the currently read line and dispatch the work depending on the
524 * context.
525 */
526 void doSetValue(LPSTR stdInput)
527 {
528 /*
529 * We encountered the end of the file, make sure we
530 * close the opened key and exit
531 */
532 if (stdInput == NULL) {
533 if (bTheKeyIsOpen != FALSE)
534 closeKey();
535
536 return;
537 }
538
539 if ( stdInput[0] == '[') /* We are reading a new key */
540 {
541 if ( bTheKeyIsOpen != FALSE )
542 closeKey(); /* Close the previous key before */
543
544 /* delete the key if we encounter '-' at the start of reg key */
545 if ( stdInput[1] == '-')
546 {
547 int last_chr = strlen(stdInput) - 1;
548
549 /* skip leading "[-" and get rid of trailing "]" */
550 if (stdInput[last_chr] == ']')
551 stdInput[last_chr] = '\0';
552 delete_registry_key(stdInput+2);
553 return;
554 }
555
556 if ( openKey(stdInput) != ERROR_SUCCESS )
557 fprintf(stderr,"%s: setValue failed to open key %s\n",
558 getAppName(), stdInput);
559 } else if( ( bTheKeyIsOpen ) &&
560 (( stdInput[0] == '@') || /* reading a default @=data pair */
561 ( stdInput[0] == '\"'))) /* reading a new value=data pair */
562 {
563 processSetValue(stdInput);
564 } else /* since we are assuming that the */
565 { /* file format is valid we must */
566 if ( bTheKeyIsOpen ) /* be reading a blank line which */
567 closeKey(); /* indicate end of this key processing */
568 }
569 }
570
571 /******************************************************************************
572 * This function is the main entry point to the deleteValue type of action. It
573 * receives the currently read line and dispatch the work depending on the
574 * context.
575 */
576 void doDeleteValue(LPSTR line)
577 {
578 UNREFERENCED_PARAMETER(line);
579 fprintf(stderr,"%s: deleteValue not yet implemented\n", getAppName());
580 }
581
582 /******************************************************************************
583 * This function is the main entry point to the deleteKey type of action. It
584 * receives the currently read line and dispatch the work depending on the
585 * context.
586 */
587 void doDeleteKey(LPSTR line)
588 {
589 UNREFERENCED_PARAMETER(line);
590 fprintf(stderr,"%s: deleteKey not yet implemented\n", getAppName());
591 }
592
593 /******************************************************************************
594 * This function is the main entry point to the createKey type of action. It
595 * receives the currently read line and dispatch the work depending on the
596 * context.
597 */
598 void doCreateKey(LPSTR line)
599 {
600 UNREFERENCED_PARAMETER(line);
601 fprintf(stderr,"%s: createKey not yet implemented\n", getAppName());
602 }
603
604 /******************************************************************************
605 * This function is a wrapper for the setValue function. It prepares the
606 * land and clean the area once completed.
607 * Note: this function modifies the line parameter.
608 *
609 * line - registry file unwrapped line. Should have the registry value name and
610 * complete registry value data.
611 */
612 void processSetValue(LPSTR line)
613 {
614 LPSTR val_name; /* registry value name */
615 LPSTR val_data; /* registry value data */
616
617 int line_idx = 0; /* current character under analysis */
618 LONG res;
619
620 /* get value name */
621 if (line[line_idx] == '@' && line[line_idx + 1] == '=') {
622 line[line_idx] = '\0';
623 val_name = line;
624 line_idx++;
625 } else if (line[line_idx] == '\"') {
626 line_idx++;
627 val_name = line + line_idx;
628 while (TRUE) {
629 /* check if the line is unterminated (otherwise it may loop forever!) */
630 if (line[line_idx] == '\0') {
631 fprintf(stderr,"Warning! unrecognized line:\n%s\n", line);
632 return;
633 } else
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 res = setValue(val_name, val_data);
662 if ( res != 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 size_t 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 = (int) (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 = LoadLibraryA(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 = LoadLibraryA(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 %ld, status %ld\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 = (DWORD) 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 = RegEnumValueA(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:%08lx\n", *((DWORD *)*val_buf));
1005 break;
1006
1007 default:
1008 fprintf(stderr,"%s: warning - unsupported registry format '%ld', "
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(%ld):", 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 = (int) (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 = RegEnumKeyExA(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 (RegOpenKeyA(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(const TCHAR *file_name)
1089 {
1090 FILE *file = _tfopen(file_name, _T("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(const TCHAR *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 (DWORD) strlen(reg_key_name));
1133 strcpy(reg_key_name_buf, reg_key_name);
1134
1135 /* open the specified key */
1136 if (!getRegClass(reg_key_name, &reg_key_class)) {
1137 fprintf(stderr,"%s: Incorrect registry class specification in '%s'\n",
1138 getAppName(), reg_key_name);
1139 exit(1);
1140 }
1141 branch_name = getRegKeyName(reg_key_name);
1142 CHECK_ENOUGH_MEMORY(branch_name);
1143 if (!branch_name[0]) {
1144 /* no branch - registry class is specified */
1145 file = REGPROC_open_export_file(file_name);
1146 export_hkey(file, reg_key_class,
1147 &reg_key_name_buf, &reg_key_name_len,
1148 &val_name_buf, &val_name_len,
1149 &val_buf, &val_size);
1150 } else if (RegOpenKeyA(reg_key_class, branch_name, &key) == ERROR_SUCCESS) {
1151 file = REGPROC_open_export_file(file_name);
1152 export_hkey(file, key,
1153 &reg_key_name_buf, &reg_key_name_len,
1154 &val_name_buf, &val_name_len,
1155 &val_buf, &val_size);
1156 RegCloseKey(key);
1157 } else {
1158 fprintf(stderr,"%s: Can't export. Registry key '%s' does not exist!\n",
1159 getAppName(), reg_key_name);
1160 REGPROC_print_error();
1161 }
1162 HeapFree(GetProcessHeap(), 0, branch_name);
1163 } else {
1164 unsigned int i;
1165
1166 /* export all registry classes */
1167 file = REGPROC_open_export_file(file_name);
1168 for (i = 0; i < REG_CLASS_NUMBER; i++) {
1169 /* do not export HKEY_CLASSES_ROOT */
1170 if (reg_class_keys[i] != HKEY_CLASSES_ROOT &&
1171 reg_class_keys[i] != HKEY_CURRENT_USER &&
1172 reg_class_keys[i] != HKEY_CURRENT_CONFIG &&
1173 reg_class_keys[i] != HKEY_DYN_DATA) {
1174 strcpy(reg_key_name_buf, reg_class_names[i]);
1175 export_hkey(file, reg_class_keys[i],
1176 &reg_key_name_buf, &reg_key_name_len,
1177 &val_name_buf, &val_name_len,
1178 &val_buf, &val_size);
1179 }
1180 }
1181 }
1182
1183 if (file) {
1184 fclose(file);
1185 }
1186 HeapFree(GetProcessHeap(), 0, val_buf);
1187 return TRUE;
1188 }
1189
1190 /******************************************************************************
1191 * Reads contents of the specified file into the registry.
1192 */
1193 BOOL import_registry_file(LPTSTR filename)
1194 {
1195 FILE* reg_file = _tfopen(filename, _T("r"));
1196
1197 if (reg_file) {
1198 unsigned char ch1 = fgetc(reg_file);
1199 unsigned char ch2 = fgetc(reg_file);
1200
1201 /* detect UTF-16.LE or UTF-16.BE format */
1202 if ((ch1 == 0xff && ch2 == 0xfe) ||
1203 (ch1 == 0xfe && ch2 == 0xff)) {
1204 /* TODO: implement support for UNICODE files! */
1205 } else {
1206 /* restore read point to the first line */
1207 fseek(reg_file, 0L, SEEK_SET);
1208 processRegLines(reg_file, doSetValue);
1209 }
1210 fclose(reg_file);
1211 return TRUE;
1212 }
1213 return FALSE;
1214 }
1215
1216 /******************************************************************************
1217 * Recursive function which removes the registry key with all subkeys.
1218 */
1219 static void delete_branch(HKEY key,
1220 CHAR **reg_key_name_buf, DWORD *reg_key_name_len)
1221 {
1222 HKEY branch_key;
1223 DWORD max_sub_key_len;
1224 DWORD subkeys;
1225 DWORD curr_len;
1226 LONG ret;
1227 long int i;
1228
1229 if (RegOpenKeyA(key, *reg_key_name_buf, &branch_key) != ERROR_SUCCESS) {
1230 REGPROC_print_error();
1231 }
1232
1233 /* get size information and resize the buffers if necessary */
1234 if (RegQueryInfoKey(branch_key, NULL, NULL, NULL,
1235 &subkeys, &max_sub_key_len,
1236 NULL, NULL, NULL, NULL, NULL, NULL
1237 ) != ERROR_SUCCESS) {
1238 REGPROC_print_error();
1239 }
1240 curr_len = (DWORD) strlen(*reg_key_name_buf);
1241 REGPROC_resize_char_buffer(reg_key_name_buf, reg_key_name_len,
1242 max_sub_key_len + curr_len + 1);
1243
1244 (*reg_key_name_buf)[curr_len] = '\\';
1245 for (i = subkeys - 1; i >= 0; i--) {
1246 DWORD buf_len = *reg_key_name_len - curr_len;
1247
1248 ret = RegEnumKeyExA(branch_key, i, *reg_key_name_buf + curr_len + 1,
1249 &buf_len, NULL, NULL, NULL, NULL);
1250 if (ret != ERROR_SUCCESS &&
1251 ret != ERROR_MORE_DATA &&
1252 ret != ERROR_NO_MORE_ITEMS) {
1253 REGPROC_print_error();
1254 } else {
1255 delete_branch(key, reg_key_name_buf, reg_key_name_len);
1256 }
1257 }
1258 (*reg_key_name_buf)[curr_len] = '\0';
1259 RegCloseKey(branch_key);
1260 RegDeleteKeyA(key, *reg_key_name_buf);
1261 }
1262
1263 /******************************************************************************
1264 * Removes the registry key with all subkeys. Parses full key name.
1265 *
1266 * Parameters:
1267 * reg_key_name - full name of registry branch to delete. Ignored if is NULL,
1268 * empty, points to register key class, does not exist.
1269 */
1270 void delete_registry_key(CHAR *reg_key_name)
1271 {
1272 CHAR *branch_name;
1273 DWORD branch_name_len;
1274 HKEY reg_key_class;
1275 HKEY branch_key;
1276
1277 if (!reg_key_name || !reg_key_name[0])
1278 return;
1279 /* open the specified key */
1280 if (!getRegClass(reg_key_name, &reg_key_class)) {
1281 fprintf(stderr,"%s: Incorrect registry class specification in '%s'\n",
1282 getAppName(), reg_key_name);
1283 exit(1);
1284 }
1285 branch_name = getRegKeyName(reg_key_name);
1286 CHECK_ENOUGH_MEMORY(branch_name);
1287 branch_name_len = (DWORD) strlen(branch_name);
1288 if (!branch_name[0]) {
1289 fprintf(stderr,"%s: Can't delete registry class '%s'\n",
1290 getAppName(), reg_key_name);
1291 exit(1);
1292 }
1293 if (RegOpenKeyA(reg_key_class, branch_name, &branch_key) == ERROR_SUCCESS) {
1294 /* check whether the key exists */
1295 RegCloseKey(branch_key);
1296 delete_branch(reg_key_class, &branch_name, &branch_name_len);
1297 }
1298 HeapFree(GetProcessHeap(), 0, branch_name);
1299 }
1300
1301 /******************************************************************************
1302 * Sets the application name. Then application name is used in the error
1303 * reporting.
1304 */
1305 void setAppName(const CHAR *name)
1306 {
1307 app_name = name;
1308 }
1309
1310 const CHAR *getAppName(void)
1311 {
1312 return app_name;
1313 }
1314
1315 LONG RegCopyKey(HKEY hDestKey, LPCTSTR lpDestSubKey, HKEY hSrcKey, LPCTSTR lpSrcSubKey)
1316 {
1317 LONG lResult;
1318 DWORD dwDisposition;
1319 HKEY hDestSubKey = NULL;
1320 HKEY hSrcSubKey = NULL;
1321 DWORD dwIndex, dwType, cbName, cbData;
1322 TCHAR szSubKey[256];
1323 TCHAR szValueName[256];
1324 BYTE szValueData[512];
1325
1326 FILETIME ft;
1327
1328 /* open the source subkey, if specified */
1329 if (lpSrcSubKey)
1330 {
1331 lResult = RegOpenKeyEx(hSrcKey, lpSrcSubKey, 0, KEY_ALL_ACCESS, &hSrcSubKey);
1332 if (lResult)
1333 goto done;
1334 hSrcKey = hSrcSubKey;
1335 }
1336
1337 /* create the destination subkey */
1338 lResult = RegCreateKeyEx(hDestKey, lpDestSubKey, 0, NULL, 0, KEY_WRITE, NULL,
1339 &hDestSubKey, &dwDisposition);
1340 if (lResult)
1341 goto done;
1342
1343 /* copy all subkeys */
1344 dwIndex = 0;
1345 do
1346 {
1347 cbName = sizeof(szSubKey) / sizeof(szSubKey[0]);
1348 lResult = RegEnumKeyEx(hSrcKey, dwIndex++, szSubKey, &cbName, NULL, NULL, NULL, &ft);
1349 if (lResult == ERROR_SUCCESS)
1350 {
1351 lResult = RegCopyKey(hDestSubKey, szSubKey, hSrcKey, szSubKey);
1352 if (lResult)
1353 goto done;
1354 }
1355 }
1356 while(lResult == ERROR_SUCCESS);
1357
1358 /* copy all subvalues */
1359 dwIndex = 0;
1360 do
1361 {
1362 cbName = sizeof(szValueName) / sizeof(szValueName[0]);
1363 cbData = sizeof(szValueData) / sizeof(szValueData[0]);
1364 lResult = RegEnumValue(hSrcKey, dwIndex++, szValueName, &cbName, NULL, &dwType, szValueData, &cbData);
1365 if (lResult == ERROR_SUCCESS)
1366 {
1367 lResult = RegSetValueEx(hDestSubKey, szValueName, 0, dwType, szValueData, cbData);
1368 if (lResult)
1369 goto done;
1370 }
1371 }
1372 while(lResult == ERROR_SUCCESS);
1373
1374 lResult = ERROR_SUCCESS;
1375
1376 done:
1377 if (hSrcSubKey)
1378 RegCloseKey(hSrcSubKey);
1379 if (hDestSubKey)
1380 RegCloseKey(hDestSubKey);
1381 if (lResult != ERROR_SUCCESS)
1382 SHDeleteKey(hDestKey, lpDestSubKey);
1383 return lResult;
1384
1385 }
1386
1387 LONG RegMoveKey(HKEY hDestKey, LPCTSTR lpDestSubKey, HKEY hSrcKey, LPCTSTR lpSrcSubKey)
1388 {
1389 LONG lResult;
1390
1391 if (!lpSrcSubKey)
1392 return ERROR_INVALID_FUNCTION;
1393
1394 lResult = RegCopyKey(hDestKey, lpDestSubKey, hSrcKey, lpSrcSubKey);
1395 if (lResult == ERROR_SUCCESS)
1396 SHDeleteKey(hSrcKey, lpSrcSubKey);
1397
1398 return lResult;
1399 }
1400
1401 LONG RegRenameKey(HKEY hKey, LPCTSTR lpSubKey, LPCTSTR lpNewName)
1402 {
1403 LPCTSTR s;
1404 LPTSTR lpNewSubKey = NULL;
1405 LONG Ret = 0;
1406
1407 if (!lpSubKey)
1408 return Ret;
1409
1410 s = _tcsrchr(lpSubKey, _T('\\'));
1411 if (s)
1412 {
1413 s++;
1414 lpNewSubKey = (LPTSTR) HeapAlloc(GetProcessHeap(), 0, (s - lpSubKey + _tcslen(lpNewName) + 1) * sizeof(TCHAR));
1415 if (lpNewSubKey != NULL)
1416 {
1417 memcpy(lpNewSubKey, lpSubKey, (s - lpSubKey) * sizeof(TCHAR));
1418 _tcscpy(lpNewSubKey + (s - lpSubKey), lpNewName);
1419 lpNewName = lpNewSubKey;
1420 }
1421 else
1422 return ERROR_NOT_ENOUGH_MEMORY;
1423 }
1424
1425 Ret = RegMoveKey(hKey, lpNewName, hKey, lpSubKey);
1426
1427 if (lpNewSubKey)
1428 {
1429 HeapFree(GetProcessHeap(), 0, lpNewSubKey);
1430 }
1431 return Ret;
1432 }
1433
1434 LONG RegRenameValue(HKEY hKey, LPCTSTR lpSubKey, LPCTSTR lpDestValue, LPCTSTR lpSrcValue)
1435 {
1436 LONG lResult;
1437 HKEY hSubKey = NULL;
1438 DWORD dwType, cbData;
1439 BYTE data[512];
1440
1441 if (lpSubKey)
1442 {
1443 lResult = RegOpenKey(hKey, lpSubKey, &hSubKey);
1444 if (lResult != ERROR_SUCCESS)
1445 goto done;
1446 hKey = hSubKey;
1447 }
1448
1449 cbData = sizeof(data);
1450 lResult = RegQueryValueEx(hKey, lpSrcValue, NULL, &dwType, data, &cbData);
1451 if (lResult != ERROR_SUCCESS)
1452 goto done;
1453
1454 lResult = RegSetValueEx(hKey, lpDestValue, 0, dwType, data, cbData);
1455 if (lResult != ERROR_SUCCESS)
1456 goto done;
1457
1458 RegDeleteValue(hKey, lpSrcValue);
1459
1460 done:
1461 if (hSubKey)
1462 RegCloseKey(hSubKey);
1463 return lResult;
1464 }
1465
1466 LONG RegQueryStringValue(HKEY hKey, LPCTSTR lpSubKey, LPCTSTR lpValueName, LPTSTR pszBuffer, DWORD dwBufferLen)
1467 {
1468 LONG lResult;
1469 HKEY hSubKey = NULL;
1470 DWORD cbData, dwType;
1471
1472 if (lpSubKey)
1473 {
1474 lResult = RegOpenKey(hKey, lpSubKey, &hSubKey);
1475 if (lResult != ERROR_SUCCESS)
1476 goto done;
1477 hKey = hSubKey;
1478 }
1479
1480 cbData = (dwBufferLen - 1) * sizeof(*pszBuffer);
1481 lResult = RegQueryValueEx(hKey, lpValueName, NULL, &dwType, (LPBYTE) pszBuffer, &cbData);
1482 if (lResult != ERROR_SUCCESS)
1483 goto done;
1484 if (dwType != REG_SZ)
1485 {
1486 lResult = -1;
1487 goto done;
1488 }
1489
1490 pszBuffer[cbData / sizeof(*pszBuffer)] = '\0';
1491
1492 done:
1493 if (lResult != ERROR_SUCCESS)
1494 pszBuffer[0] = '\0';
1495 if (hSubKey)
1496 RegCloseKey(hSubKey);
1497 return lResult;
1498 }
1499
1500 /******************************************************************************
1501 * Searching
1502 */
1503
1504 static LONG RegNextKey(HKEY hKey, LPTSTR lpSubKey, size_t iSubKeyLength)
1505 {
1506 LONG lResult;
1507 LPTSTR s;
1508 LPCTSTR pszOriginalKey;
1509 TCHAR szKeyName[256];
1510 HKEY hSubKey, hBaseKey;
1511 DWORD dwIndex = 0;
1512 DWORD cbName;
1513 FILETIME ft;
1514 BOOL bFoundKey = FALSE;
1515
1516 /* Try accessing a subkey */
1517 if (RegOpenKeyEx(hKey, lpSubKey, 0, KEY_ALL_ACCESS, &hSubKey) == ERROR_SUCCESS)
1518 {
1519 cbName = (DWORD) iSubKeyLength - _tcslen(lpSubKey) - 1;
1520 lResult = RegEnumKeyEx(hSubKey, 0, lpSubKey + _tcslen(lpSubKey) + 1,
1521 &cbName, NULL, NULL, NULL, &ft);
1522 RegCloseKey(hSubKey);
1523
1524 if (lResult == ERROR_SUCCESS)
1525 {
1526 lpSubKey[_tcslen(lpSubKey)] = '\\';
1527 bFoundKey = TRUE;
1528 }
1529 }
1530
1531 if (!bFoundKey)
1532 {
1533 /* Go up and find the next sibling key */
1534 do
1535 {
1536 s = _tcsrchr(lpSubKey, TEXT('\\'));
1537 if (s)
1538 {
1539 *s = '\0';
1540 pszOriginalKey = s + 1;
1541
1542 hBaseKey = NULL;
1543 RegOpenKeyEx(hKey, lpSubKey, 0, KEY_ALL_ACCESS, &hBaseKey);
1544 }
1545 else
1546 {
1547 pszOriginalKey = lpSubKey;
1548 hBaseKey = hKey;
1549 }
1550
1551 if (hBaseKey)
1552 {
1553 dwIndex = 0;
1554 do
1555 {
1556 lResult = RegEnumKey(hBaseKey, dwIndex++, szKeyName, sizeof(szKeyName) / sizeof(szKeyName[0]));
1557 }
1558 while((lResult == ERROR_SUCCESS) && _tcscmp(szKeyName, pszOriginalKey));
1559
1560 if (lResult == ERROR_SUCCESS)
1561 {
1562 lResult = RegEnumKey(hBaseKey, dwIndex++, szKeyName, sizeof(szKeyName) / sizeof(szKeyName[0]));
1563 if (lResult == ERROR_SUCCESS)
1564 {
1565 bFoundKey = TRUE;
1566 _sntprintf(lpSubKey + _tcslen(lpSubKey), iSubKeyLength - _tcslen(lpSubKey), _T("\\%s"), szKeyName);
1567 }
1568 }
1569 RegCloseKey(hBaseKey);
1570 }
1571 }
1572 while(!bFoundKey);
1573 }
1574 return bFoundKey ? ERROR_SUCCESS : ERROR_NO_MORE_ITEMS;
1575 }
1576
1577 static BOOL RegSearchCompare(LPCTSTR s1, LPCTSTR s2, DWORD dwSearchFlags)
1578 {
1579 BOOL bResult;
1580 if (dwSearchFlags & RSF_WHOLESTRING)
1581 {
1582 if (dwSearchFlags & RSF_MATCHCASE)
1583 bResult = !_tcscmp(s1, s2);
1584 else
1585 bResult = !_tcsicmp(s1, s2);
1586 }
1587 else
1588 {
1589 if (dwSearchFlags & RSF_MATCHCASE)
1590 bResult = (_tcsstr(s1, s2) != NULL);
1591 else
1592 {
1593 /* My kingdom for _tcsistr() */
1594 bResult = FALSE;
1595 while(*s1)
1596 {
1597 if (!_tcsnicmp(s1, s2, _tcslen(s2)))
1598 {
1599 bResult = TRUE;
1600 break;
1601 }
1602 s1++;
1603 }
1604 }
1605 }
1606 return bResult;
1607 }
1608
1609 LONG RegSearch(HKEY hKey, LPTSTR lpSubKey, size_t iSubKeyLength,
1610 LPCTSTR pszSearchString, DWORD dwValueIndex,
1611 DWORD dwSearchFlags, BOOL (*pfnCallback)(LPVOID), LPVOID lpParam)
1612 {
1613 LONG lResult;
1614 LPCTSTR s;
1615
1616 UNREFERENCED_PARAMETER(dwValueIndex);
1617
1618 if (dwSearchFlags & (RSF_LOOKATVALUES | RSF_LOOKATDATA))
1619 return ERROR_CALL_NOT_IMPLEMENTED; /* NYI */
1620
1621 do
1622 {
1623 if (pfnCallback)
1624 {
1625 if (pfnCallback(lpParam))
1626 return ERROR_OPERATION_ABORTED;
1627 }
1628
1629 lResult = RegNextKey(hKey, lpSubKey, iSubKeyLength);
1630 if (lResult != ERROR_SUCCESS)
1631 return lResult;
1632
1633 s = _tcsrchr(lpSubKey, TEXT('\\'));
1634 s = s ? s + 1 : lpSubKey;
1635 }
1636 while(!(dwSearchFlags & RSF_LOOKATKEYS) || !RegSearchCompare(s, pszSearchString, dwSearchFlags));
1637
1638 return ERROR_SUCCESS;
1639 }
1640
1641 /******************************************************************************
1642 * Key naming and parsing
1643 */
1644
1645 BOOL RegKeyGetName(LPTSTR pszDest, size_t iDestLength, HKEY hRootKey, LPCTSTR lpSubKey)
1646 {
1647 LPCTSTR pszRootKey;
1648
1649 if (hRootKey == HKEY_CLASSES_ROOT)
1650 pszRootKey = TEXT("HKEY_CLASSES_ROOT");
1651 else if (hRootKey == HKEY_CURRENT_USER)
1652 pszRootKey = TEXT("HKEY_CURRENT_USER");
1653 else if (hRootKey == HKEY_LOCAL_MACHINE)
1654 pszRootKey = TEXT("HKEY_LOCAL_MACHINE");
1655 else if (hRootKey == HKEY_USERS)
1656 pszRootKey = TEXT("HKEY_USERS");
1657 else if (hRootKey == HKEY_CURRENT_CONFIG)
1658 pszRootKey = TEXT("HKEY_CURRENT_CONFIG");
1659 else if (hRootKey == HKEY_DYN_DATA)
1660 pszRootKey = TEXT("HKEY_DYN_DATA");
1661 else
1662 return FALSE;
1663
1664 if (lpSubKey[0])
1665 _sntprintf(pszDest, iDestLength, TEXT("%s\\%s"), pszRootKey, lpSubKey);
1666 else
1667 _sntprintf(pszDest, iDestLength, TEXT("%s"), pszRootKey);
1668 return TRUE;
1669 }
1670
1671
1672