[MKHIVE] Sync a bit more of reginf.c with the corresponding setupapi code.
[reactos.git] / sdk / tools / mkhive / reginf.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2003, 2006 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS hive maker
22 * FILE: tools/mkhive/reginf.c
23 * PURPOSE: Inf file import code
24 * PROGRAMMERS: Eric Kohl
25 * Hervé Poussineau
26 */
27
28 /* INCLUDES *****************************************************************/
29
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33
34 #define NDEBUG
35 #include "mkhive.h"
36
37 #define FLG_ADDREG_BINVALUETYPE 0x00000001
38 #define FLG_ADDREG_NOCLOBBER 0x00000002
39 #define FLG_ADDREG_DELVAL 0x00000004
40 #define FLG_ADDREG_APPEND 0x00000008
41 #define FLG_ADDREG_KEYONLY 0x00000010
42 #define FLG_ADDREG_OVERWRITEONLY 0x00000020
43 #define FLG_ADDREG_KEYONLY_COMMON 0x00002000
44 #define FLG_DELREG_KEYONLY_COMMON FLG_ADDREG_KEYONLY_COMMON
45 #define FLG_ADDREG_DELREG_BIT 0x00008000
46
47 #define FLG_ADDREG_TYPE_SZ 0x00000000
48 #define FLG_ADDREG_TYPE_MULTI_SZ 0x00010000
49 #define FLG_ADDREG_TYPE_EXPAND_SZ 0x00020000
50 #define FLG_ADDREG_TYPE_BINARY (0x00000000 | FLG_ADDREG_BINVALUETYPE)
51 #define FLG_ADDREG_TYPE_DWORD (0x00010000 | FLG_ADDREG_BINVALUETYPE)
52 #define FLG_ADDREG_TYPE_NONE (0x00020000 | FLG_ADDREG_BINVALUETYPE)
53 #define FLG_ADDREG_TYPE_MASK (0xFFFF0000 | FLG_ADDREG_BINVALUETYPE)
54
55
56 static const WCHAR HKCR[] = {'H','K','C','R',0};
57 static const WCHAR HKCU[] = {'H','K','C','U',0};
58 static const WCHAR HKLM[] = {'H','K','L','M',0};
59 static const WCHAR HKU[] = {'H','K','U',0};
60 static const WCHAR HKR[] = {'H','K','R',0};
61 static const WCHAR BCD[] = {'B','C','D',0};
62
63 static const WCHAR HKCRPath[] = {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\','S','O','F','T','W','A','R','E','\\','C','l','a','s','s','e','s','\\',0};
64 static const WCHAR HKCUPath[] = {'\\','R','e','g','i','s','t','r','y','\\','U','s','e','r','\\','.','D','E','F','A','U','L','T','\\',0};
65 static const WCHAR HKLMPath[] = {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\',0};
66 static const WCHAR HKUPath[] = {'\\','R','e','g','i','s','t','r','y','\\','U','s','e','r','\\',0};
67 static const WCHAR BCDPath[] = {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e','\\',0};
68
69 static const WCHAR AddReg[] = {'A','d','d','R','e','g',0};
70 static const WCHAR DelReg[] = {'D','e','l','R','e','g',0};
71
72 /* FUNCTIONS ****************************************************************/
73
74 static BOOL
75 get_root_key(PWCHAR Name)
76 {
77 if (!strcmpiW(Name, HKCR))
78 {
79 strcpyW(Name, HKCRPath);
80 return TRUE;
81 }
82
83 if (!strcmpiW(Name, HKCU))
84 {
85 strcpyW(Name, HKCUPath);
86 return TRUE;
87 }
88
89 if (!strcmpiW(Name, HKLM))
90 {
91 strcpyW(Name, HKLMPath);
92 return TRUE;
93 }
94
95 if (!strcmpiW(Name, HKU))
96 {
97 strcpyW(Name, HKUPath);
98 return TRUE;
99 }
100
101 if (!strcmpiW(Name, BCD))
102 {
103 strcpyW(Name, BCDPath);
104 return TRUE;
105 }
106
107 #if 0
108 if (!strcmpiW(Name, HKR))
109 return FALSE;
110 #endif
111
112 return FALSE;
113 }
114
115
116 /***********************************************************************
117 * append_multi_sz_value
118 *
119 * Append a multisz string to a multisz registry value.
120 */
121 // NOTE: Synced with setupapi/install.c ; see also usetup/registry.c
122 static VOID
123 append_multi_sz_value(
124 IN HKEY KeyHandle,
125 IN PCWSTR ValueName,
126 IN PCWSTR Strings,
127 IN ULONG StringSize) // In characters
128 {
129 ULONG Size, Total; // In bytes
130 ULONG Type;
131 PWCHAR Buffer;
132 PWCHAR p;
133 size_t len;
134 LONG Error;
135
136 Error = RegQueryValueExW(KeyHandle,
137 ValueName,
138 NULL,
139 &Type,
140 NULL,
141 &Size);
142 if ((Error != ERROR_SUCCESS) || (Type != REG_MULTI_SZ))
143 return;
144
145 Buffer = malloc(Size + StringSize * sizeof(WCHAR));
146 if (Buffer == NULL)
147 return;
148
149 Error = RegQueryValueExW(KeyHandle,
150 ValueName,
151 NULL,
152 NULL,
153 (PUCHAR)Buffer,
154 &Size);
155 if (Error != ERROR_SUCCESS)
156 goto done;
157
158 /* compare each string against all the existing ones */
159 Total = Size;
160 while (*Strings != 0)
161 {
162 len = strlenW(Strings) + 1;
163
164 for (p = Buffer; *p != 0; p += strlenW(p) + 1)
165 if (!strcmpiW(p, Strings))
166 break;
167
168 if (*p == 0) /* not found, need to append it */
169 {
170 memcpy(p, Strings, len * sizeof(WCHAR));
171 p[len] = 0;
172 Total += len * sizeof(WCHAR);
173 }
174 Strings += len;
175 }
176
177 if (Total != Size)
178 {
179 DPRINT("setting value '%S' to '%S'\n", ValueName, Buffer);
180 RegSetValueExW(KeyHandle,
181 ValueName,
182 0,
183 REG_MULTI_SZ,
184 (PUCHAR)Buffer,
185 Total + sizeof(WCHAR));
186 }
187
188 done:
189 free(Buffer);
190 }
191
192
193 /***********************************************************************
194 * do_reg_operation
195 *
196 * Perform an add/delete registry operation depending on the flags.
197 */
198 static BOOL
199 do_reg_operation(
200 IN HKEY KeyHandle,
201 IN PCWSTR ValueName,
202 IN PINFCONTEXT Context,
203 IN ULONG Flags)
204 {
205 WCHAR EmptyStr = 0;
206 ULONG Type;
207 ULONG Size;
208 LONG Error;
209
210 if (Flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL)) /* deletion */
211 {
212 if (ValueName && *ValueName && !(Flags & FLG_DELREG_KEYONLY_COMMON))
213 {
214 // NOTE: We don't currently handle deleting sub-values inside multi-strings.
215 RegDeleteValueW(KeyHandle, ValueName);
216 }
217 else
218 {
219 RegDeleteKeyW(KeyHandle, NULL);
220 }
221 return TRUE;
222 }
223
224 if (Flags & (FLG_ADDREG_KEYONLY | FLG_ADDREG_KEYONLY_COMMON))
225 return TRUE;
226
227 if (Flags & (FLG_ADDREG_NOCLOBBER | FLG_ADDREG_OVERWRITEONLY))
228 {
229 Error = RegQueryValueExW(KeyHandle,
230 ValueName,
231 NULL,
232 NULL,
233 NULL,
234 NULL);
235
236 if ((Error == ERROR_SUCCESS) && (Flags & FLG_ADDREG_NOCLOBBER))
237 return TRUE;
238
239 if ((Error != ERROR_SUCCESS) && (Flags & FLG_ADDREG_OVERWRITEONLY))
240 return TRUE;
241 }
242
243 switch (Flags & FLG_ADDREG_TYPE_MASK)
244 {
245 case FLG_ADDREG_TYPE_SZ:
246 Type = REG_SZ;
247 break;
248
249 case FLG_ADDREG_TYPE_MULTI_SZ:
250 Type = REG_MULTI_SZ;
251 break;
252
253 case FLG_ADDREG_TYPE_EXPAND_SZ:
254 Type = REG_EXPAND_SZ;
255 break;
256
257 case FLG_ADDREG_TYPE_BINARY:
258 Type = REG_BINARY;
259 break;
260
261 case FLG_ADDREG_TYPE_DWORD:
262 Type = REG_DWORD;
263 break;
264
265 case FLG_ADDREG_TYPE_NONE:
266 Type = REG_NONE;
267 break;
268
269 default:
270 Type = Flags >> 16;
271 break;
272 }
273
274 if (!(Flags & FLG_ADDREG_BINVALUETYPE) ||
275 (Type == REG_DWORD && InfHostGetFieldCount(Context) == 5))
276 {
277 PWCHAR Str = NULL;
278
279 if (Type == REG_MULTI_SZ)
280 {
281 if (InfHostGetMultiSzField(Context, 5, NULL, 0, &Size) != 0)
282 Size = 0;
283
284 if (Size)
285 {
286 Str = malloc(Size * sizeof(WCHAR));
287 if (Str == NULL)
288 return FALSE;
289
290 InfHostGetMultiSzField(Context, 5, Str, Size, NULL);
291 }
292
293 if (Flags & FLG_ADDREG_APPEND)
294 {
295 if (Str == NULL)
296 return TRUE;
297
298 DPRINT("append_multi_sz_value(ValueName = '%S')\n", ValueName);
299 append_multi_sz_value(KeyHandle,
300 ValueName,
301 Str,
302 Size);
303
304 free(Str);
305 return TRUE;
306 }
307 /* else fall through to normal string handling */
308 }
309 else
310 {
311 if (InfHostGetStringField(Context, 5, NULL, 0, &Size) != 0)
312 Size = 0;
313
314 if (Size)
315 {
316 Str = malloc(Size * sizeof(WCHAR));
317 if (Str == NULL)
318 return FALSE;
319
320 InfHostGetStringField(Context, 5, Str, Size, NULL);
321 }
322 }
323
324 if (Type == REG_DWORD)
325 {
326 ULONG dw = Str ? strtoulW(Str, NULL, 0) : 0;
327
328 DPRINT("setting dword '%S' to %x\n", ValueName, dw);
329
330 RegSetValueExW(KeyHandle,
331 ValueName,
332 0,
333 Type,
334 (const PUCHAR)&dw,
335 sizeof(ULONG));
336 }
337 else
338 {
339 DPRINT("setting value '%S' to '%S'\n", ValueName, Str);
340
341 if (Str)
342 {
343 RegSetValueExW(KeyHandle,
344 ValueName,
345 0,
346 Type,
347 (PVOID)Str,
348 Size * sizeof(WCHAR));
349 }
350 else
351 {
352 RegSetValueExW(KeyHandle,
353 ValueName,
354 0,
355 Type,
356 (PVOID)&EmptyStr,
357 sizeof(WCHAR));
358 }
359 }
360 free(Str);
361 }
362 else /* get the binary data */
363 {
364 PUCHAR Data = NULL;
365
366 if (InfHostGetBinaryField(Context, 5, NULL, 0, &Size) != 0)
367 Size = 0;
368
369 if (Size)
370 {
371 Data = malloc(Size);
372 if (Data == NULL)
373 return FALSE;
374
375 DPRINT("setting binary data '%S' len %d\n", ValueName, Size);
376 InfHostGetBinaryField(Context, 5, Data, Size, NULL);
377 }
378
379 RegSetValueExW(KeyHandle,
380 ValueName,
381 0,
382 Type,
383 (PVOID)Data,
384 Size);
385
386 free(Data);
387 }
388
389 return TRUE;
390 }
391
392 /***********************************************************************
393 * registry_callback
394 *
395 * Called once for each AddReg and DelReg entry in a given section.
396 */
397 static BOOL
398 registry_callback(HINF hInf, PCWSTR Section, BOOL Delete)
399 {
400 WCHAR Buffer[MAX_INF_STRING_LENGTH];
401 PWCHAR ValuePtr;
402 ULONG Flags;
403 size_t Length;
404
405 PINFCONTEXT Context = NULL;
406 HKEY KeyHandle;
407 BOOL Ok;
408
409 Ok = InfHostFindFirstLine(hInf, Section, NULL, &Context) == 0;
410 if (!Ok)
411 return TRUE; /* Don't fail if the section isn't present */
412
413 for (Ok = TRUE; Ok; Ok = (InfHostFindNextLine(Context, Context) == 0))
414 {
415 /* Get root */
416 if (InfHostGetStringField(Context, 1, Buffer, sizeof(Buffer)/sizeof(WCHAR), NULL) != 0)
417 continue;
418 if (!get_root_key(Buffer))
419 continue;
420
421 /* Get key */
422 Length = strlenW(Buffer);
423 if (InfHostGetStringField(Context, 2, Buffer + Length, sizeof(Buffer)/sizeof(WCHAR) - (ULONG)Length, NULL) != 0)
424 *Buffer = 0;
425
426 DPRINT("KeyName: <%S>\n", Buffer);
427
428 /* Get flags */
429 if (InfHostGetIntField(Context, 4, (INT*)&Flags) != 0)
430 Flags = 0;
431
432 if (Delete)
433 {
434 if (!Flags)
435 Flags = FLG_ADDREG_DELREG_BIT;
436 else if (!(Flags & FLG_ADDREG_DELREG_BIT))
437 continue; /* ignore this entry */
438 }
439 else
440 {
441 if (Flags & FLG_ADDREG_DELREG_BIT)
442 continue; /* ignore this entry */
443 }
444
445 DPRINT("Flags: 0x%x\n", Flags);
446
447 if (Delete || (Flags & FLG_ADDREG_OVERWRITEONLY))
448 {
449 if (RegOpenKeyW(NULL, Buffer, &KeyHandle) != ERROR_SUCCESS)
450 {
451 DPRINT("RegOpenKey(%S) failed\n", Buffer);
452 continue; /* ignore if it doesn't exist */
453 }
454 }
455 else
456 {
457 if (RegCreateKeyW(NULL, Buffer, &KeyHandle) != ERROR_SUCCESS)
458 {
459 DPRINT("RegCreateKey(%S) failed\n", Buffer);
460 continue;
461 }
462 }
463
464 /* Get value name */
465 if (InfHostGetStringField(Context, 3, Buffer, sizeof(Buffer)/sizeof(WCHAR), NULL) == 0)
466 {
467 ValuePtr = Buffer;
468 }
469 else
470 {
471 ValuePtr = NULL;
472 }
473
474 /* And now do it */
475 if (!do_reg_operation(KeyHandle, ValuePtr, Context, Flags))
476 {
477 RegCloseKey(KeyHandle);
478 return FALSE;
479 }
480
481 RegCloseKey(KeyHandle);
482 }
483
484 InfHostFreeContext(Context);
485
486 return TRUE;
487 }
488
489
490 BOOL
491 ImportRegistryFile(PCHAR FileName)
492 {
493 HINF hInf;
494 ULONG ErrorLine;
495
496 /* Load inf file from install media. */
497 if (InfHostOpenFile(&hInf, FileName, 0, &ErrorLine) != 0)
498 {
499 DPRINT1("InfHostOpenFile(%s) failed\n", FileName);
500 return FALSE;
501 }
502
503 if (!registry_callback(hInf, (PWCHAR)DelReg, TRUE))
504 {
505 DPRINT1("registry_callback() for DelReg failed\n");
506 InfHostCloseFile(hInf);
507 return FALSE;
508 }
509
510 if (!registry_callback(hInf, (PWCHAR)AddReg, FALSE))
511 {
512 DPRINT1("registry_callback() for AddReg failed\n");
513 InfHostCloseFile(hInf);
514 return FALSE;
515 }
516
517 InfHostCloseFile(hInf);
518 return TRUE;
519 }
520
521 /* EOF */