aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/iniutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/iniutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/iniutil.cpp768
1 files changed, 768 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/iniutil.cpp b/src/libs/dutil/WixToolset.DUtil/iniutil.cpp
new file mode 100644
index 00000000..70b62995
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/iniutil.cpp
@@ -0,0 +1,768 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// Exit macros
7#define IniExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__)
8#define IniExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__)
9#define IniExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__)
10#define IniExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__)
11#define IniExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__)
12#define IniExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_INIUTIL, x, s, __VA_ARGS__)
13#define IniExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__)
14#define IniExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__)
15#define IniExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_INIUTIL, p, x, e, s, __VA_ARGS__)
16#define IniExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_INIUTIL, p, x, s, __VA_ARGS__)
17#define IniExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_INIUTIL, e, x, s, __VA_ARGS__)
18#define IniExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_INIUTIL, g, x, s, __VA_ARGS__)
19
20const LPCWSTR wzSectionSeparator = L"\\";
21
22struct INI_STRUCT
23{
24 LPWSTR sczPath; // the path to the INI file to be parsed
25
26 LPWSTR sczOpenTagPrefix; // For regular ini, this would be '['
27 LPWSTR sczOpenTagPostfix; // For regular ini, this would be ']'
28
29 LPWSTR sczValuePrefix; // for regular ini, this would be NULL
30 LPWSTR sczValueSeparator; // for regular ini, this would be '='
31
32 LPWSTR *rgsczValueSeparatorExceptions;
33 DWORD cValueSeparatorExceptions;
34
35 LPWSTR sczCommentLinePrefix; // for regular ini, this would be ';'
36
37 INI_VALUE *rgivValues;
38 DWORD cValues;
39
40 LPWSTR *rgsczLines;
41 DWORD cLines;
42
43 FILE_ENCODING feEncoding;
44 BOOL fModified;
45};
46
47const int INI_HANDLE_BYTES = sizeof(INI_STRUCT);
48
49static HRESULT GetSectionPrefixFromName(
50 __in_z LPCWSTR wzName,
51 __deref_inout_z LPWSTR* psczOutput
52 );
53static void UninitializeIniValue(
54 INI_VALUE *pivValue
55 );
56
57extern "C" HRESULT DAPI IniInitialize(
58 __out_bcount(INI_HANDLE_BYTES) INI_HANDLE* piHandle
59 )
60{
61 HRESULT hr = S_OK;
62
63 // Allocate the handle
64 *piHandle = static_cast<INI_HANDLE>(MemAlloc(sizeof(INI_STRUCT), TRUE));
65 IniExitOnNull(*piHandle, hr, E_OUTOFMEMORY, "Failed to allocate ini object");
66
67LExit:
68 return hr;
69}
70
71extern "C" void DAPI IniUninitialize(
72 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle
73 )
74{
75 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
76
77 ReleaseStr(pi->sczPath);
78 ReleaseStr(pi->sczOpenTagPrefix);
79 ReleaseStr(pi->sczOpenTagPostfix);
80 ReleaseStr(pi->sczValuePrefix);
81 ReleaseStr(pi->sczValueSeparator);
82
83 for (DWORD i = 0; i < pi->cValueSeparatorExceptions; ++i)
84 {
85 ReleaseStr(pi->rgsczValueSeparatorExceptions + i);
86 }
87
88 ReleaseStr(pi->sczCommentLinePrefix);
89
90 for (DWORD i = 0; i < pi->cValues; ++i)
91 {
92 UninitializeIniValue(pi->rgivValues + i);
93 }
94 ReleaseMem(pi->rgivValues);
95
96 ReleaseStrArray(pi->rgsczLines, pi->cLines);
97
98 ReleaseMem(pi);
99}
100
101extern "C" HRESULT DAPI IniSetOpenTag(
102 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
103 __in_z_opt LPCWSTR wzOpenTagPrefix,
104 __in_z_opt LPCWSTR wzOpenTagPostfix
105 )
106{
107 HRESULT hr = S_OK;
108
109 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
110
111 if (wzOpenTagPrefix)
112 {
113 hr = StrAllocString(&pi->sczOpenTagPrefix, wzOpenTagPrefix, 0);
114 IniExitOnFailure(hr, "Failed to copy open tag prefix to ini struct: %ls", wzOpenTagPrefix);
115 }
116 else
117 {
118 ReleaseNullStr(pi->sczOpenTagPrefix);
119 }
120
121 if (wzOpenTagPostfix)
122 {
123 hr = StrAllocString(&pi->sczOpenTagPostfix, wzOpenTagPostfix, 0);
124 IniExitOnFailure(hr, "Failed to copy open tag postfix to ini struct: %ls", wzOpenTagPostfix);
125 }
126 else
127 {
128 ReleaseNullStr(pi->sczOpenTagPrefix);
129 }
130
131LExit:
132 return hr;
133}
134
135extern "C" HRESULT DAPI IniSetValueStyle(
136 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
137 __in_z_opt LPCWSTR wzValuePrefix,
138 __in_z_opt LPCWSTR wzValueSeparator
139 )
140{
141 HRESULT hr = S_OK;
142
143 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
144
145 if (wzValuePrefix)
146 {
147 hr = StrAllocString(&pi->sczValuePrefix, wzValuePrefix, 0);
148 IniExitOnFailure(hr, "Failed to copy value prefix to ini struct: %ls", wzValuePrefix);
149 }
150 else
151 {
152 ReleaseNullStr(pi->sczValuePrefix);
153 }
154
155 if (wzValueSeparator)
156 {
157 hr = StrAllocString(&pi->sczValueSeparator, wzValueSeparator, 0);
158 IniExitOnFailure(hr, "Failed to copy value separator to ini struct: %ls", wzValueSeparator);
159 }
160 else
161 {
162 ReleaseNullStr(pi->sczValueSeparator);
163 }
164
165LExit:
166 return hr;
167}
168
169extern "C" HRESULT DAPI IniSetValueSeparatorException(
170 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
171 __in_z LPCWSTR wzValueNamePrefix
172 )
173{
174 HRESULT hr = S_OK;
175 DWORD dwInsertedIndex = 0;
176
177 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
178
179 hr = MemEnsureArraySize(reinterpret_cast<void **>(&pi->rgsczValueSeparatorExceptions), pi->cValueSeparatorExceptions + 1, sizeof(LPWSTR), 5);
180 IniExitOnFailure(hr, "Failed to increase array size for value separator exceptions");
181 dwInsertedIndex = pi->cValueSeparatorExceptions;
182 ++pi->cValueSeparatorExceptions;
183
184 hr = StrAllocString(&pi->rgsczValueSeparatorExceptions[dwInsertedIndex], wzValueNamePrefix, 0);
185 IniExitOnFailure(hr, "Failed to copy value separator exception");
186
187LExit:
188 return hr;
189}
190
191extern "C" HRESULT DAPI IniSetCommentStyle(
192 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
193 __in_z_opt LPCWSTR wzLinePrefix
194 )
195{
196 HRESULT hr = S_OK;
197
198 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
199
200 if (wzLinePrefix)
201 {
202 hr = StrAllocString(&pi->sczCommentLinePrefix, wzLinePrefix, 0);
203 IniExitOnFailure(hr, "Failed to copy comment line prefix to ini struct: %ls", wzLinePrefix);
204 }
205 else
206 {
207 ReleaseNullStr(pi->sczCommentLinePrefix);
208 }
209
210LExit:
211 return hr;
212}
213
214extern "C" HRESULT DAPI IniParse(
215 __inout_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
216 __in LPCWSTR wzPath,
217 __out_opt FILE_ENCODING *pfeEncodingFound
218 )
219{
220 HRESULT hr = S_OK;
221 DWORD dwValuePrefixLength = 0;
222 DWORD dwValueSeparatorExceptionLength = 0;
223 LPWSTR sczContents = NULL;
224 LPWSTR sczCurrentSection = NULL;
225 LPWSTR sczName = NULL;
226 LPWSTR sczNameTrimmed = NULL;
227 LPWSTR sczValue = NULL;
228 LPWSTR sczValueTrimmed = NULL;
229 LPWSTR wzOpenTagPrefix = NULL;
230 LPWSTR wzOpenTagPostfix = NULL;
231 LPWSTR wzValuePrefix = NULL;
232 LPWSTR wzValueNameStart = NULL;
233 LPWSTR wzValueSeparator = NULL;
234 LPWSTR wzCommentLinePrefix = NULL;
235 LPWSTR wzValueBegin = NULL;
236 LPCWSTR wzTemp = NULL;
237
238 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
239
240 BOOL fSections = (NULL != pi->sczOpenTagPrefix) && (NULL != pi->sczOpenTagPostfix);
241 BOOL fValuePrefix = (NULL != pi->sczValuePrefix);
242
243 hr = StrAllocString(&pi->sczPath, wzPath, 0);
244 IniExitOnFailure(hr, "Failed to copy path to ini struct: %ls", wzPath);
245
246 hr = FileToString(pi->sczPath, &sczContents, &pi->feEncoding);
247 IniExitOnFailure(hr, "Failed to convert file to string: %ls", pi->sczPath);
248
249 if (pfeEncodingFound)
250 {
251 *pfeEncodingFound = pi->feEncoding;
252 }
253
254 if (!sczContents || !*sczContents)
255 {
256 // Empty string, nothing to parse
257 ExitFunction1(hr = S_OK);
258 }
259
260 dwValuePrefixLength = lstrlenW(pi->sczValuePrefix);
261 hr = StrSplitAllocArray(&pi->rgsczLines, reinterpret_cast<UINT *>(&pi->cLines), sczContents, L"\n");
262 IniExitOnFailure(hr, "Failed to split INI file into lines");
263
264 for (DWORD i = 0; i < pi->cLines; ++i)
265 {
266 if (!*pi->rgsczLines[i] || '\r' == *pi->rgsczLines[i])
267 {
268 continue;
269 }
270
271 if (pi->sczCommentLinePrefix)
272 {
273 wzCommentLinePrefix = wcsstr(pi->rgsczLines[i], pi->sczCommentLinePrefix);
274
275 if (wzCommentLinePrefix && wzCommentLinePrefix <= pi->rgsczLines[i] + 1)
276 {
277 continue;
278 }
279 }
280
281 if (pi->sczOpenTagPrefix)
282 {
283 wzOpenTagPrefix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPrefix);
284 if (wzOpenTagPrefix)
285 {
286 // If there is an open tag prefix but there is anything but whitespace before it, then it's NOT an open tag prefix
287 // This is important, for example, to support values with names like "Array[0]=blah" in INI format
288 for (wzTemp = pi->rgsczLines[i]; wzTemp < wzOpenTagPrefix; ++wzTemp)
289 {
290 if (*wzTemp != L' ' && *wzTemp != L'\t')
291 {
292 wzOpenTagPrefix = NULL;
293 break;
294 }
295 }
296 }
297 }
298
299 if (pi->sczOpenTagPostfix)
300 {
301 wzOpenTagPostfix = wcsstr(pi->rgsczLines[i], pi->sczOpenTagPostfix);
302 }
303
304 if (pi->sczValuePrefix)
305 {
306 wzValuePrefix = wcsstr(pi->rgsczLines[i], pi->sczValuePrefix);
307 if (wzValuePrefix != NULL)
308 {
309 wzValueNameStart = wzValuePrefix + dwValuePrefixLength;
310 }
311 }
312 else
313 {
314 wzValueNameStart = pi->rgsczLines[i];
315 }
316
317 if (pi->sczValueSeparator && NULL != wzValueNameStart && *wzValueNameStart != L'\0')
318 {
319 dwValueSeparatorExceptionLength = 0;
320 for (DWORD j = 0; j < pi->cValueSeparatorExceptions; ++j)
321 {
322 if (pi->rgsczLines[i] == wcsstr(pi->rgsczLines[i], pi->rgsczValueSeparatorExceptions[j]))
323 {
324 dwValueSeparatorExceptionLength = lstrlenW(pi->rgsczValueSeparatorExceptions[j]);
325 break;
326 }
327 }
328
329 wzValueSeparator = wcsstr(wzValueNameStart + dwValueSeparatorExceptionLength, pi->sczValueSeparator);
330 }
331
332 // Don't keep the endline
333 if (pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] == L'\r')
334 {
335 pi->rgsczLines[i][lstrlenW(pi->rgsczLines[i])-1] = L'\0';
336 }
337
338 if (fSections && wzOpenTagPrefix && wzOpenTagPostfix && wzOpenTagPrefix < wzOpenTagPostfix && (NULL == wzCommentLinePrefix || wzOpenTagPrefix < wzCommentLinePrefix))
339 {
340 // There is an section starting here, let's keep track of it and move on
341 hr = StrAllocString(&sczCurrentSection, wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix), wzOpenTagPostfix - (wzOpenTagPrefix + lstrlenW(pi->sczOpenTagPrefix)));
342 IniExitOnFailure(hr, "Failed to record section name for line: %ls of INI file: %ls", pi->rgsczLines[i], pi->sczPath);
343
344 // Sections will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output
345 ReleaseNullStr(pi->rgsczLines[i]);
346 }
347 else if (wzValueSeparator && (NULL == wzCommentLinePrefix || wzValueSeparator < wzCommentLinePrefix)
348 && (!fValuePrefix || wzValuePrefix))
349 {
350 if (fValuePrefix)
351 {
352 wzValueBegin = wzValuePrefix + lstrlenW(pi->sczValuePrefix);
353 }
354 else
355 {
356 wzValueBegin = pi->rgsczLines[i];
357 }
358
359 hr = MemEnsureArraySize(reinterpret_cast<void **>(&pi->rgivValues), pi->cValues + 1, sizeof(INI_VALUE), 100);
360 IniExitOnFailure(hr, "Failed to increase array size for value array");
361
362 if (sczCurrentSection)
363 {
364 hr = StrAllocString(&sczName, sczCurrentSection, 0);
365 IniExitOnFailure(hr, "Failed to copy current section name");
366
367 hr = StrAllocConcat(&sczName, wzSectionSeparator, 0);
368 IniExitOnFailure(hr, "Failed to copy current section name");
369 }
370
371 hr = StrAllocConcat(&sczName, wzValueBegin, wzValueSeparator - wzValueBegin);
372 IniExitOnFailure(hr, "Failed to copy name");
373
374 hr = StrAllocString(&sczValue, wzValueSeparator + lstrlenW(pi->sczValueSeparator), 0);
375 IniExitOnFailure(hr, "Failed to copy value");
376
377 hr = StrTrimWhitespace(&sczNameTrimmed, sczName);
378 IniExitOnFailure(hr, "Failed to trim whitespace from name");
379
380 hr = StrTrimWhitespace(&sczValueTrimmed, sczValue);
381 IniExitOnFailure(hr, "Failed to trim whitespace from value");
382
383 pi->rgivValues[pi->cValues].wzName = const_cast<LPCWSTR>(sczNameTrimmed);
384 sczNameTrimmed = NULL;
385 pi->rgivValues[pi->cValues].wzValue = const_cast<LPCWSTR>(sczValueTrimmed);
386 sczValueTrimmed = NULL;
387 pi->rgivValues[pi->cValues].dwLineNumber = i + 1;
388
389 ++pi->cValues;
390
391 // Values will be calculated dynamically after any set operations, so don't include this in the list of lines to remember for output
392 ReleaseNullStr(pi->rgsczLines[i]);
393 }
394 else
395 {
396 // Must be a comment, so ignore it and keep it in the list to output
397 }
398
399 ReleaseNullStr(sczName);
400 }
401
402LExit:
403 ReleaseStr(sczCurrentSection);
404 ReleaseStr(sczContents);
405 ReleaseStr(sczName);
406 ReleaseStr(sczNameTrimmed);
407 ReleaseStr(sczValue);
408 ReleaseStr(sczValueTrimmed);
409
410 return hr;
411}
412
413extern "C" HRESULT DAPI IniGetValueList(
414 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
415 __deref_out_ecount_opt(*pcValues) INI_VALUE** prgivValues,
416 __out DWORD *pcValues
417 )
418{
419 HRESULT hr = S_OK;
420
421 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
422
423 *prgivValues = pi->rgivValues;
424 *pcValues = pi->cValues;
425
426 return hr;
427}
428
429extern "C" HRESULT DAPI IniGetValue(
430 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
431 __in LPCWSTR wzValueName,
432 __deref_out_z LPWSTR* psczValue
433 )
434{
435 HRESULT hr = S_OK;
436
437 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
438 INI_VALUE *pValue = NULL;
439
440 for (DWORD i = 0; i < pi->cValues; ++i)
441 {
442 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1))
443 {
444 pValue = pi->rgivValues + i;
445 break;
446 }
447 }
448
449 if (NULL == pValue)
450 {
451 hr = E_NOTFOUND;
452 IniExitOnFailure(hr, "Failed to check for INI value: %ls", wzValueName);
453 }
454
455 if (NULL == pValue->wzValue)
456 {
457 ExitFunction1(hr = E_NOTFOUND);
458 }
459
460 hr = StrAllocString(psczValue, pValue->wzValue, 0);
461 IniExitOnFailure(hr, "Failed to make copy of value while looking up INI value named: %ls", wzValueName);
462
463LExit:
464 return hr;
465}
466
467extern "C" HRESULT DAPI IniSetValue(
468 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
469 __in LPCWSTR wzValueName,
470 __in_z_opt LPCWSTR wzValue
471 )
472{
473 HRESULT hr = S_OK;
474 LPWSTR sczSectionPrefix = NULL; // includes section name and backslash
475 LPWSTR sczName = NULL;
476 LPWSTR sczValue = NULL;
477 DWORD dwInsertIndex = DWORD_MAX;
478
479 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
480 INI_VALUE *pValue = NULL;
481
482 for (DWORD i = 0; i < pi->cValues; ++i)
483 {
484 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pi->rgivValues[i].wzName, -1, wzValueName, -1))
485 {
486 pValue = pi->rgivValues + i;
487 break;
488 }
489 }
490
491 // We're killing the value
492 if (NULL == wzValue)
493 {
494 if (pValue && pValue->wzValue)
495 {
496 pi->fModified = TRUE;
497 sczValue = const_cast<LPWSTR>(pValue->wzValue);
498 pValue->wzValue = NULL;
499 ReleaseNullStr(sczValue);
500 }
501
502 ExitFunction();
503 }
504 else
505 {
506 if (pValue)
507 {
508 if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, pValue->wzValue, -1, wzValue, -1))
509 {
510 pi->fModified = TRUE;
511 hr = StrAllocString(const_cast<LPWSTR *>(&pValue->wzValue), wzValue, 0);
512 IniExitOnFailure(hr, "Failed to update value INI value named: %ls", wzValueName);
513 }
514
515 ExitFunction1(hr = S_OK);
516 }
517 else
518 {
519 if (wzValueName)
520 {
521 hr = GetSectionPrefixFromName(wzValueName, &sczSectionPrefix);
522 IniExitOnFailure(hr, "Failed to get section prefix from value name: %ls", wzValueName);
523 }
524
525 // If we have a section prefix, figure out the index to insert it (at the end of the section it belongs in)
526 if (sczSectionPrefix)
527 {
528 for (DWORD i = 0; i < pi->cValues; ++i)
529 {
530 if (0 == wcsncmp(pi->rgivValues[i].wzName, sczSectionPrefix, lstrlenW(sczSectionPrefix)))
531 {
532 dwInsertIndex = i;
533 }
534 else if (DWORD_MAX != dwInsertIndex)
535 {
536 break;
537 }
538 }
539 }
540 else
541 {
542 for (DWORD i = 0; i < pi->cValues; ++i)
543 {
544 if (NULL == wcsstr(pi->rgivValues[i].wzName, wzSectionSeparator))
545 {
546 dwInsertIndex = i;
547 }
548 else if (DWORD_MAX != dwInsertIndex)
549 {
550 break;
551 }
552 }
553 }
554
555 // Otherwise, just add it to the end
556 if (DWORD_MAX == dwInsertIndex)
557 {
558 dwInsertIndex = pi->cValues;
559 }
560
561 pi->fModified = TRUE;
562 hr = MemInsertIntoArray(reinterpret_cast<void **>(&pi->rgivValues), dwInsertIndex, 1, pi->cValues + 1, sizeof(INI_VALUE), 100);
563 IniExitOnFailure(hr, "Failed to insert value into array");
564
565 hr = StrAllocString(&sczName, wzValueName, 0);
566 IniExitOnFailure(hr, "Failed to copy name");
567
568 hr = StrAllocString(&sczValue, wzValue, 0);
569 IniExitOnFailure(hr, "Failed to copy value");
570
571 pi->rgivValues[dwInsertIndex].wzName = const_cast<LPCWSTR>(sczName);
572 sczName = NULL;
573 pi->rgivValues[dwInsertIndex].wzValue = const_cast<LPCWSTR>(sczValue);
574 sczValue = NULL;
575
576 ++pi->cValues;
577 }
578 }
579
580LExit:
581 ReleaseStr(sczName);
582 ReleaseStr(sczValue);
583
584 return hr;
585}
586
587extern "C" HRESULT DAPI IniWriteFile(
588 __in_bcount(INI_HANDLE_BYTES) INI_HANDLE piHandle,
589 __in_z_opt LPCWSTR wzPath,
590 __in FILE_ENCODING feOverrideEncoding
591 )
592{
593 HRESULT hr = S_OK;
594 LPWSTR sczCurrentSectionPrefix = NULL;
595 LPWSTR sczNewSectionPrefix = NULL;
596 LPWSTR sczContents = NULL;
597 LPCWSTR wzName = NULL;
598 DWORD dwLineArrayIndex = 1;
599 FILE_ENCODING feEncoding;
600
601 INI_STRUCT *pi = static_cast<INI_STRUCT *>(piHandle);
602
603 if (FILE_ENCODING_UNSPECIFIED == feOverrideEncoding)
604 {
605 feEncoding = pi->feEncoding;
606 }
607 else
608 {
609 feEncoding = feOverrideEncoding;
610 }
611
612 if (FILE_ENCODING_UNSPECIFIED == feEncoding)
613 {
614 feEncoding = FILE_ENCODING_UTF16_WITH_BOM;
615 }
616
617 if (!pi->fModified)
618 {
619 ExitFunction1(hr = S_OK);
620 }
621 if (NULL == wzPath && NULL == pi->sczPath)
622 {
623 ExitFunction1(hr = E_NOTFOUND);
624 }
625
626 BOOL fSections = (pi->sczOpenTagPrefix) && (pi->sczOpenTagPostfix);
627
628 hr = StrAllocString(&sczContents, L"", 0);
629 IniExitOnFailure(hr, "Failed to begin contents string as empty string");
630
631 // Insert any beginning lines we didn't understand like comments
632 if (0 < pi->cLines)
633 {
634 while (pi->rgsczLines[dwLineArrayIndex])
635 {
636 hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex], 0);
637 IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory");
638
639 hr = StrAllocConcat(&sczContents, L"\r\n", 2);
640 IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory");
641
642 ++dwLineArrayIndex;
643 }
644 }
645
646 for (DWORD i = 0; i < pi->cValues; ++i)
647 {
648 // Skip if this value was killed off
649 if (NULL == pi->rgivValues[i].wzValue)
650 {
651 continue;
652 }
653
654 // Now generate any lines for the current value like value line and maybe also a new section line before it
655
656 // First see if we need to write a section line
657 hr = GetSectionPrefixFromName(pi->rgivValues[i].wzName, &sczNewSectionPrefix);
658 IniExitOnFailure(hr, "Failed to get section prefix from name: %ls", pi->rgivValues[i].wzName);
659
660 // If the new section prefix is different, write a section out for it
661 if (fSections && sczNewSectionPrefix && (NULL == sczCurrentSectionPrefix || CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczNewSectionPrefix, -1, sczCurrentSectionPrefix, -1)))
662 {
663 hr = StrAllocConcat(&sczContents, pi->sczOpenTagPrefix, 0);
664 IniExitOnFailure(hr, "Failed to concat open tag prefix to string");
665
666 // Exclude section separator (i.e. backslash) from new section prefix
667 hr = StrAllocConcat(&sczContents, sczNewSectionPrefix, lstrlenW(sczNewSectionPrefix)-lstrlenW(wzSectionSeparator));
668 IniExitOnFailure(hr, "Failed to concat section name to string");
669
670 hr = StrAllocConcat(&sczContents, pi->sczOpenTagPostfix, 0);
671 IniExitOnFailure(hr, "Failed to concat open tag postfix to string");
672
673 hr = StrAllocConcat(&sczContents, L"\r\n", 2);
674 IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory");
675
676 ReleaseNullStr(sczCurrentSectionPrefix);
677 sczCurrentSectionPrefix = sczNewSectionPrefix;
678 sczNewSectionPrefix = NULL;
679 }
680
681 // Inserting lines we read before the current value if appropriate
682 while (pi->rgivValues[i].dwLineNumber > dwLineArrayIndex)
683 {
684 // Skip any lines were purposely forgot
685 if (NULL == pi->rgsczLines[dwLineArrayIndex])
686 {
687 ++dwLineArrayIndex;
688 continue;
689 }
690
691 hr = StrAllocConcat(&sczContents, pi->rgsczLines[dwLineArrayIndex++], 0);
692 IniExitOnFailure(hr, "Failed to add previous line to ini output buffer in-memory");
693
694 hr = StrAllocConcat(&sczContents, L"\r\n", 2);
695 IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory");
696 }
697
698 wzName = pi->rgivValues[i].wzName;
699 if (fSections)
700 {
701 wzName += lstrlenW(sczCurrentSectionPrefix);
702 }
703
704 // OK, now just write the name/value pair, if it isn't deleted
705 if (pi->sczValuePrefix)
706 {
707 hr = StrAllocConcat(&sczContents, pi->sczValuePrefix, 0);
708 IniExitOnFailure(hr, "Failed to concat value prefix to ini output buffer");
709 }
710
711 hr = StrAllocConcat(&sczContents, wzName, 0);
712 IniExitOnFailure(hr, "Failed to concat value name to ini output buffer");
713
714 hr = StrAllocConcat(&sczContents, pi->sczValueSeparator, 0);
715 IniExitOnFailure(hr, "Failed to concat value separator to ini output buffer");
716
717 hr = StrAllocConcat(&sczContents, pi->rgivValues[i].wzValue, 0);
718 IniExitOnFailure(hr, "Failed to concat value to ini output buffer");
719
720 hr = StrAllocConcat(&sczContents, L"\r\n", 2);
721 IniExitOnFailure(hr, "Failed to add endline to ini output buffer in-memory");
722 }
723
724 // If no path was specified, use the path to the file we parsed
725 if (NULL == wzPath)
726 {
727 wzPath = pi->sczPath;
728 }
729
730 hr = FileFromString(wzPath, 0, sczContents, feEncoding);
731 IniExitOnFailure(hr, "Failed to write INI contents out to file: %ls", wzPath);
732
733LExit:
734 ReleaseStr(sczContents);
735 ReleaseStr(sczCurrentSectionPrefix);
736 ReleaseStr(sczNewSectionPrefix);
737
738 return hr;
739}
740
741static void UninitializeIniValue(
742 INI_VALUE *pivValue
743 )
744{
745 ReleaseStr(const_cast<LPWSTR>(pivValue->wzName));
746 ReleaseStr(const_cast<LPWSTR>(pivValue->wzValue));
747}
748
749static HRESULT GetSectionPrefixFromName(
750 __in_z LPCWSTR wzName,
751 __deref_inout_z LPWSTR* psczOutput
752 )
753{
754 HRESULT hr = S_OK;
755 LPCWSTR wzSectionDelimiter = NULL;
756
757 ReleaseNullStr(*psczOutput);
758
759 wzSectionDelimiter = wcsstr(wzName, wzSectionSeparator);
760 if (wzSectionDelimiter && wzSectionDelimiter != wzName)
761 {
762 hr = StrAllocString(psczOutput, wzName, wzSectionDelimiter - wzName + 1);
763 IniExitOnFailure(hr, "Failed to copy section prefix");
764 }
765
766LExit:
767 return hr;
768}