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