* Sync up to trunk HEAD (r62975).
[reactos.git] / base / applications / rapps / parser.c
1 /*
2 * PROJECT: ReactOS Applications Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/rapps/parser.c
5 * PURPOSE: Parser functions
6 * PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org)
7 */
8
9 #include "rapps.h"
10
11 typedef LONG NTSTATUS;
12
13 typedef struct _UNICODE_STRING {
14 USHORT Length;
15 USHORT MaximumLength;
16 PWSTR Buffer;
17 } UNICODE_STRING, *PUNICODE_STRING;
18
19 void WINAPI RtlInitUnicodeString(PUNICODE_STRING,PCWSTR);
20 NTSTATUS WINAPI RtlUnicodeStringToInteger(const UNICODE_STRING*,ULONG,ULONG*);
21 BOOLEAN WINAPI RtlIsTextUnicode(LPCVOID,INT,INT*);
22
23 static const char bom_utf8[] = {0xEF,0xBB,0xBF};
24
25 typedef enum
26 {
27 ENCODING_UTF8 = 1,
28 ENCODING_UTF16LE,
29 ENCODING_UTF16BE
30 } ENCODING;
31
32 typedef struct tagSECTIONKEY
33 {
34 WCHAR *value;
35 struct tagSECTIONKEY *next;
36 WCHAR name[1];
37 } SECTIONKEY;
38
39 typedef struct tagSECTION
40 {
41 struct tagSECTIONKEY *key;
42 struct tagSECTION *next;
43 WCHAR name[1];
44 } SECTION;
45
46 typedef struct
47 {
48 BOOL changed;
49 SECTION *section;
50 WCHAR *filename;
51 ENCODING encoding;
52 } ITEMS;
53
54
55 #define N_CACHED_ITEMS 10
56 static ITEMS *ItemsArray[N_CACHED_ITEMS] = {NULL};
57 #define CurProfile (ItemsArray[0])
58 #define IS_ENTRY_COMMENT(str) ((str)[0] == ';')
59 #define ParserIsSpace(c) (iswspace(c) || c == 0x1a)
60
61
62 static
63 WCHAR*
64 memchrW(const WCHAR *ptr, WCHAR ch, size_t n)
65 {
66 const WCHAR *end;
67 for (end = ptr + n; ptr < end; ptr++)
68 if (*ptr == ch)
69 return (WCHAR *)(ULONG_PTR)ptr;
70 return NULL;
71 }
72
73 static
74 WCHAR
75 *memrchrW(const WCHAR *ptr, WCHAR ch, size_t n)
76 {
77 const WCHAR *end;
78 WCHAR *ret = NULL;
79 for (end = ptr + n; ptr < end; ptr++)
80 if (*ptr == ch)
81 ret = (WCHAR *)(ULONG_PTR)ptr;
82 return ret;
83 }
84
85 static
86 void
87 ParserCopyEntry(LPWSTR buffer, LPCWSTR value, int len, BOOL strip_quote)
88 {
89 WCHAR quote = '\0';
90
91 if (!buffer) return;
92
93 if (strip_quote && ((*value == '\'') || (*value == '\"')))
94 {
95 if (value[1] && (value[wcslen(value)-1] == *value))
96 quote = *value++;
97 }
98
99 lstrcpynW(buffer, value, len);
100 if (quote && (len >= (int)wcslen(value))) buffer[wcslen(buffer)-1] = '\0';
101 }
102
103 static
104 void
105 ParserByteSwapShortBuffer(WCHAR * buffer, int len)
106 {
107 int i;
108 USHORT * shortbuffer = buffer;
109 for (i = 0; i < len; i++)
110 shortbuffer[i] = (shortbuffer[i] >> 8) | (shortbuffer[i] << 8);
111 }
112
113 static
114 void
115 ParserWriteMarker(HANDLE hFile, ENCODING encoding)
116 {
117 DWORD dwBytesWritten;
118 WCHAR bom;
119
120 switch (encoding)
121 {
122 case ENCODING_UTF8:
123 WriteFile(hFile, bom_utf8, sizeof(bom_utf8), &dwBytesWritten, NULL);
124 break;
125
126 case ENCODING_UTF16LE:
127 bom = 0xFEFF;
128 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
129 break;
130
131 case ENCODING_UTF16BE:
132 bom = 0xFFFE;
133 WriteFile(hFile, &bom, sizeof(bom), &dwBytesWritten, NULL);
134 break;
135 }
136 }
137
138 static
139 void
140 ParserWriteLine(HANDLE hFile, WCHAR * szLine, int len, ENCODING encoding)
141 {
142 char * write_buffer;
143 int write_buffer_len;
144 DWORD dwBytesWritten;
145
146 switch (encoding)
147 {
148 case ENCODING_UTF8:
149 write_buffer_len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, NULL, 0, NULL, NULL);
150 write_buffer = HeapAlloc(GetProcessHeap(), 0, write_buffer_len);
151 if (!write_buffer) return;
152 len = WideCharToMultiByte(CP_UTF8, 0, szLine, len, write_buffer, write_buffer_len, NULL, NULL);
153 WriteFile(hFile, write_buffer, len, &dwBytesWritten, NULL);
154 HeapFree(GetProcessHeap(), 0, write_buffer);
155 break;
156
157 case ENCODING_UTF16LE:
158 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
159 break;
160
161 case ENCODING_UTF16BE:
162 ParserByteSwapShortBuffer(szLine, len);
163 WriteFile(hFile, szLine, len * sizeof(WCHAR), &dwBytesWritten, NULL);
164 break;
165 }
166 }
167
168 static
169 void
170 ParserSave(HANDLE hFile, const SECTION *section, ENCODING encoding)
171 {
172 SECTIONKEY *key;
173 WCHAR *buffer, *p;
174
175 ParserWriteMarker(hFile, encoding);
176
177 for ( ; section; section = section->next)
178 {
179 size_t len = 0;
180 size_t remaining;
181
182 if (section->name[0]) len += wcslen(section->name) + 4;
183
184 for (key = section->key; key; key = key->next)
185 {
186 len += wcslen(key->name) + 2;
187 if (key->value) len += wcslen(key->value) + 1;
188 }
189
190 buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
191 if (!buffer) return;
192
193 p = buffer;
194 remaining = len;
195 if (section->name[0])
196 {
197 StringCchPrintfExW(p, remaining, &p, &remaining, 0,
198 L"[%ls]\r\n",
199 section->name);
200 }
201
202 for (key = section->key; key; key = key->next)
203 {
204 if (key->value)
205 {
206 StringCchPrintfExW(p, remaining, &p, &remaining, 0,
207 L"%ls=%ls\r\n",
208 key->name, key->value);
209 }
210 else
211 {
212 StringCchPrintfExW(p, remaining, &p, &remaining, 0,
213 L"%ls\r\n",
214 key->name);
215 }
216 }
217 ParserWriteLine(hFile, buffer, len, encoding);
218 HeapFree(GetProcessHeap(), 0, buffer);
219 }
220 }
221
222 static
223 void
224 ParserFree(SECTION *section)
225 {
226 SECTION *next_section;
227 SECTIONKEY *key, *next_key;
228
229 for ( ; section; section = next_section)
230 {
231 for (key = section->key; key; key = next_key)
232 {
233 next_key = key->next;
234 HeapFree(GetProcessHeap(), 0, key->value);
235 HeapFree(GetProcessHeap(), 0, key);
236 }
237 next_section = section->next;
238 HeapFree(GetProcessHeap(), 0, section);
239 }
240 }
241
242 static
243 ENCODING
244 ParserDetectTextEncoding(const void * buffer, int * len)
245 {
246 INT flags = IS_TEXT_UNICODE_SIGNATURE |
247 IS_TEXT_UNICODE_REVERSE_SIGNATURE |
248 IS_TEXT_UNICODE_ODD_LENGTH;
249
250 if (*len >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
251 {
252 *len = sizeof(bom_utf8);
253 return ENCODING_UTF8;
254 }
255
256 RtlIsTextUnicode((void *)buffer, *len, &flags);
257
258 if (flags & IS_TEXT_UNICODE_SIGNATURE)
259 {
260 *len = sizeof(WCHAR);
261 return ENCODING_UTF16LE;
262 }
263
264 if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
265 {
266 *len = sizeof(WCHAR);
267 return ENCODING_UTF16BE;
268 }
269
270 *len = 0;
271
272 return ENCODING_UTF8;
273 }
274
275 static
276 SECTION
277 *ParserLoad(HANDLE hFile, ENCODING * pEncoding)
278 {
279 void *buffer_base, *pBuffer;
280 WCHAR * szFile;
281 const WCHAR *szLineStart, *szLineEnd;
282 const WCHAR *szValueStart, *szEnd, *next_line;
283 int line = 0, len;
284 SECTION *section, *first_section;
285 SECTION **next_section;
286 SECTIONKEY *key, *prev_key, **next_key;
287 DWORD dwFileSize;
288
289 dwFileSize = GetFileSize(hFile, NULL);
290 if (dwFileSize == INVALID_FILE_SIZE || dwFileSize == 0)
291 return NULL;
292
293 buffer_base = HeapAlloc(GetProcessHeap(), 0 , dwFileSize);
294 if (!buffer_base)
295 return NULL;
296
297 if (!ReadFile(hFile, buffer_base, dwFileSize, &dwFileSize, NULL))
298 {
299 HeapFree(GetProcessHeap(), 0, buffer_base);
300 return NULL;
301 }
302
303 len = dwFileSize;
304 *pEncoding = ParserDetectTextEncoding(buffer_base, &len);
305
306 pBuffer = (char *)buffer_base + len;
307 dwFileSize -= len;
308
309 switch (*pEncoding)
310 {
311 case ENCODING_UTF8:
312 len = MultiByteToWideChar(CP_UTF8, 0, pBuffer, dwFileSize, NULL, 0);
313 szFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
314 if (!szFile)
315 {
316 HeapFree(GetProcessHeap(), 0, buffer_base);
317 return NULL;
318 }
319 MultiByteToWideChar(CP_UTF8, 0, pBuffer, dwFileSize, szFile, len);
320 szEnd = szFile + len;
321 break;
322
323 case ENCODING_UTF16LE:
324 szFile = pBuffer;
325 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
326 break;
327
328 case ENCODING_UTF16BE:
329 szFile = pBuffer;
330 szEnd = (WCHAR *)((char *)pBuffer + dwFileSize);
331 ParserByteSwapShortBuffer(szFile, dwFileSize / sizeof(WCHAR));
332 break;
333
334 default:
335 HeapFree(GetProcessHeap(), 0, buffer_base);
336 return NULL;
337 }
338
339 first_section = HeapAlloc(GetProcessHeap(), 0, sizeof(*section));
340 if (first_section == NULL)
341 {
342 if (szFile != pBuffer)
343 HeapFree(GetProcessHeap(), 0, szFile);
344 HeapFree(GetProcessHeap(), 0, buffer_base);
345 return NULL;
346 }
347
348 first_section->name[0] = 0;
349 first_section->key = NULL;
350 first_section->next = NULL;
351 next_section = &first_section->next;
352 next_key = &first_section->key;
353 prev_key = NULL;
354 next_line = szFile;
355
356 while (next_line < szEnd)
357 {
358 szLineStart = next_line;
359 next_line = memchrW(szLineStart, '\n', szEnd - szLineStart);
360 if (!next_line) next_line = memchrW(szLineStart, '\r', szEnd - szLineStart);
361 if (!next_line) next_line = szEnd;
362 else next_line++;
363 szLineEnd = next_line;
364
365 line++;
366
367 while (szLineStart < szLineEnd && ParserIsSpace(*szLineStart)) szLineStart++;
368 while ((szLineEnd > szLineStart) && ParserIsSpace(szLineEnd[-1])) szLineEnd--;
369
370 if (szLineStart >= szLineEnd)
371 continue;
372
373 if (*szLineStart == '[')
374 {
375 const WCHAR * szSectionEnd;
376 if ((szSectionEnd = memrchrW(szLineStart, ']', szLineEnd - szLineStart)))
377 {
378 szLineStart++;
379 len = (int)(szSectionEnd - szLineStart);
380 if (!(section = HeapAlloc(GetProcessHeap(), 0, sizeof(*section) + len * sizeof(WCHAR))))
381 break;
382 memcpy(section->name, szLineStart, len * sizeof(WCHAR));
383 section->name[len] = '\0';
384 section->key = NULL;
385 section->next = NULL;
386 *next_section = section;
387 next_section = &section->next;
388 next_key = &section->key;
389 prev_key = NULL;
390
391 continue;
392 }
393 }
394
395 len = szLineEnd - szLineStart;
396 if ((szValueStart = memchrW(szLineStart, '=', szLineEnd - szLineStart)) != NULL)
397 {
398 const WCHAR *szNameEnd = szValueStart;
399 while ((szNameEnd > szLineStart) && ParserIsSpace(szNameEnd[-1])) szNameEnd--;
400 len = szNameEnd - szLineStart;
401 szValueStart++;
402 while (szValueStart < szLineEnd && ParserIsSpace(*szValueStart)) szValueStart++;
403 }
404
405 if (len || !prev_key || *prev_key->name)
406 {
407 if (!(key = HeapAlloc(GetProcessHeap(), 0, sizeof(*key) + len * sizeof(WCHAR)))) break;
408 memcpy(key->name, szLineStart, len * sizeof(WCHAR));
409 key->name[len] = '\0';
410 if (szValueStart)
411 {
412 len = (int)(szLineEnd - szValueStart);
413 key->value = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
414 memcpy(key->value, szValueStart, len * sizeof(WCHAR));
415 key->value[len] = '\0';
416 }
417 else key->value = NULL;
418
419 key->next = NULL;
420 *next_key = key;
421 next_key = &key->next;
422 prev_key = key;
423 }
424 }
425
426 if (szFile != pBuffer)
427 HeapFree(GetProcessHeap(), 0, szFile);
428 HeapFree(GetProcessHeap(), 0, buffer_base);
429
430 return first_section;
431 }
432
433 static
434 SECTIONKEY
435 *ParserFind(SECTION **section, LPCWSTR section_name, LPCWSTR key_name, BOOL create, BOOL create_always)
436 {
437 LPCWSTR p;
438 DWORD cch;
439 int seclen, keylen;
440
441 while (ParserIsSpace(*section_name)) section_name++;
442 if (*section_name)
443 p = section_name + wcslen(section_name) - 1;
444 else
445 p = section_name;
446
447 while ((p > section_name) && ParserIsSpace(*p)) p--;
448 seclen = p - section_name + 1;
449
450 while (ParserIsSpace(*key_name)) key_name++;
451 if (*key_name)
452 p = key_name + wcslen(key_name) - 1;
453 else
454 p = key_name;
455
456 while ((p > key_name) && ParserIsSpace(*p)) p--;
457 keylen = p - key_name + 1;
458
459 while (*section)
460 {
461 if (((*section)->name[0])
462 && (!(_wcsnicmp((*section)->name, section_name, seclen)))
463 && (((*section)->name)[seclen] == '\0'))
464 {
465 SECTIONKEY **key = &(*section)->key;
466
467 while (*key)
468 {
469 if(!create_always)
470 {
471 if ((!(_wcsnicmp((*key)->name, key_name, keylen)))
472 && (((*key)->name)[keylen] == '\0'))
473 return *key;
474 }
475 key = &(*key)->next;
476 }
477 if (!create)
478 return NULL;
479 cch = wcslen(key_name) + 1;
480 if (!(*key = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(SECTIONKEY, name) + cch * sizeof(WCHAR))))
481 return NULL;
482 StringCchCopyW((*key)->name, cch, key_name);
483 (*key)->value = NULL;
484 (*key)->next = NULL;
485 return *key;
486 }
487 section = &(*section)->next;
488 }
489 if (!create) return NULL;
490 cch = wcslen(section_name) + 1;
491 *section = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(SECTION, name) + cch * sizeof(WCHAR));
492 if (*section == NULL) return NULL;
493 StringCchCopyW((*section)->name, cch, section_name);
494 (*section)->next = NULL;
495 cch = wcslen(key_name) + 1;
496 if (!((*section)->key = HeapAlloc(GetProcessHeap(), 0,
497 FIELD_OFFSET(SECTIONKEY, name) + cch * sizeof(WCHAR))))
498 {
499 HeapFree(GetProcessHeap(), 0, *section);
500 return NULL;
501 }
502 StringCchCopyW((*section)->key->name, cch, key_name);
503 (*section)->key->value = NULL;
504 (*section)->key->next = NULL;
505 return (*section)->key;
506 }
507
508 static
509 BOOL
510 ParserFlushFile(void)
511 {
512 HANDLE hFile = NULL;
513
514 if (!CurProfile) return FALSE;
515
516 if (!CurProfile->changed) return TRUE;
517
518 hFile = CreateFileW(CurProfile->filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
519 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
520
521 if (hFile == INVALID_HANDLE_VALUE) return FALSE;
522
523 ParserSave(hFile, CurProfile->section, CurProfile->encoding);
524
525 CloseHandle(hFile);
526 CurProfile->changed = FALSE;
527 return TRUE;
528 }
529
530 static
531 void
532 ParserReleaseFile(void)
533 {
534 ParserFlushFile();
535 ParserFree(CurProfile->section);
536 HeapFree(GetProcessHeap(), 0, CurProfile->filename);
537 CurProfile->changed = FALSE;
538 CurProfile->section = NULL;
539 CurProfile->filename = NULL;
540 CurProfile->encoding = ENCODING_UTF8;
541 }
542
543 static
544 BOOL
545 ParserOpen(LPCWSTR filename, BOOL write_access)
546 {
547 WCHAR szDir[MAX_PATH];
548 WCHAR buffer[MAX_PATH];
549 DWORD cch;
550 HANDLE hFile = INVALID_HANDLE_VALUE;
551 int i, j;
552 ITEMS *tempProfile;
553
554 if (!CurProfile)
555 for (i = 0; i < N_CACHED_ITEMS; i++)
556 {
557 ItemsArray[i] = HeapAlloc(GetProcessHeap(), 0, sizeof(ITEMS));
558 if (ItemsArray[i] == NULL) break;
559 ItemsArray[i]->changed = FALSE;
560 ItemsArray[i]->section = NULL;
561 ItemsArray[i]->filename = NULL;
562 ItemsArray[i]->encoding = ENCODING_UTF8;
563 }
564
565 if (!GetStorageDirectory(szDir, sizeof(szDir) / sizeof(szDir[0])))
566 return FALSE;
567
568 if (FAILED(StringCbPrintfW(buffer, sizeof(buffer),
569 L"%ls\\rapps\\%ls",
570 szDir, filename)))
571 {
572 return FALSE;
573 }
574
575 hFile = CreateFileW(buffer, GENERIC_READ | (write_access ? GENERIC_WRITE : 0),
576 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
577 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
578
579 if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() != ERROR_FILE_NOT_FOUND))
580 {
581 return FALSE;
582 }
583
584 for (i = 0; i < N_CACHED_ITEMS; i++)
585 {
586 if ((ItemsArray[i]->filename && !wcscmp(buffer, ItemsArray[i]->filename)))
587 {
588 if (i)
589 {
590 ParserFlushFile();
591 tempProfile = ItemsArray[i];
592 for (j = i; j > 0; j--)
593 ItemsArray[j] = ItemsArray[j - 1];
594 CurProfile = tempProfile;
595 }
596 if (hFile != INVALID_HANDLE_VALUE)
597 {
598 CloseHandle(hFile);
599 }
600
601 return TRUE;
602 }
603 }
604
605 ParserFlushFile();
606
607 if (i == N_CACHED_ITEMS)
608 {
609 tempProfile = ItemsArray[N_CACHED_ITEMS - 1];
610 for (i = N_CACHED_ITEMS - 1; i > 0; i--)
611 ItemsArray[i] = ItemsArray[i - 1];
612 CurProfile = tempProfile;
613 }
614
615 if (CurProfile->filename) ParserReleaseFile();
616
617 cch = wcslen(buffer) + 1;
618 CurProfile->filename = HeapAlloc(GetProcessHeap(), 0, cch * sizeof(WCHAR));
619 if (CurProfile->filename == NULL)
620 {
621 if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
622 return FALSE;
623 }
624
625 StringCchCopyW(CurProfile->filename, cch, buffer);
626
627 if (hFile != INVALID_HANDLE_VALUE)
628 {
629 CurProfile->section = ParserLoad(hFile, &CurProfile->encoding);
630 CloseHandle(hFile);
631 }
632 return TRUE;
633 }
634
635 static
636 INT
637 ParserGetSection(SECTION *section, LPCWSTR section_name, LPWSTR buffer, UINT len, BOOL return_values)
638 {
639 SECTIONKEY *key;
640
641 if (!buffer)
642 return 0;
643
644 while (section)
645 {
646 if (section->name[0] && !_wcsicmp(section->name, section_name))
647 {
648 UINT oldlen = len;
649 for (key = section->key; key; key = key->next)
650 {
651 if (len <= 2) break;
652 if (!*key->name) continue; /* Skip empty lines */
653 if (IS_ENTRY_COMMENT(key->name)) continue; /* Skip comments */
654 if (!return_values && !key->value) continue; /* Skip lines w.o. '=' */
655
656 ParserCopyEntry(buffer, key->name, len - 1, 0);
657 len -= wcslen(buffer) + 1;
658 buffer += wcslen(buffer) + 1;
659
660 if (len < 2) break;
661 if (return_values && key->value)
662 {
663 buffer[-1] = '=';
664 ParserCopyEntry(buffer, key->value, len - 1, 0);
665 len -= wcslen(buffer) + 1;
666 buffer += wcslen(buffer) + 1;
667 }
668 }
669 *buffer = '\0';
670 if (len <= 1)
671 {
672 buffer[-1] = '\0';
673 return oldlen - 2;
674 }
675 return oldlen - len;
676 }
677 section = section->next;
678 }
679 buffer[0] = buffer[1] = '\0';
680 return 0;
681 }
682
683 static
684 INT
685 ParserInternalGetString(LPCWSTR section, LPCWSTR key_name, LPWSTR buffer, UINT len)
686 {
687 SECTIONKEY *key = NULL;
688 static const WCHAR empty_strW[] = { 0 };
689
690 if (!buffer || !len) return 0;
691
692 if (key_name)
693 {
694 if (!key_name[0])
695 {
696 ParserCopyEntry(buffer, empty_strW, len, TRUE);
697 return wcslen(buffer);
698 }
699 key = ParserFind(&CurProfile->section, section, key_name, FALSE, FALSE);
700 ParserCopyEntry(buffer, (key && key->value) ? key->value : empty_strW,
701 len, TRUE);
702 return wcslen(buffer);
703 }
704
705 if (section && section[0])
706 {
707 INT ret = ParserGetSection(CurProfile->section, section, buffer, len, FALSE);
708 if (!buffer[0])
709 {
710 ParserCopyEntry(buffer, empty_strW, len, TRUE);
711 ret = wcslen(buffer);
712 }
713 return ret;
714 }
715
716 buffer[0] = '\0';
717 return 0;
718 }
719
720 INT
721 ParserGetString(LPCWSTR Section, LPCWSTR ValueName, LPWSTR Buffer, UINT Len, LPCWSTR FileName)
722 {
723 if (Section == NULL) return 0;
724
725 if (ParserOpen(FileName, FALSE))
726 return ParserInternalGetString(Section, ValueName, Buffer, Len);
727
728 return 0;
729 }
730
731 UINT
732 ParserGetInt(LPCWSTR Section, LPCWSTR ValueName, LPCWSTR FileName)
733 {
734 WCHAR Buffer[30];
735 UNICODE_STRING BufferW;
736 ULONG Result;
737
738 if (!ParserGetString(Section,
739 ValueName,
740 Buffer,
741 sizeof(Buffer) / sizeof(WCHAR),
742 FileName))
743 return -1;
744
745 if (!Buffer[0]) return -1;
746
747 RtlInitUnicodeString(&BufferW, Buffer);
748 RtlUnicodeStringToInteger(&BufferW, 0, &Result);
749 return Result;
750 }