FreeRDP
Loading...
Searching...
No Matches
ini.c
1
20#include <winpr/config.h>
21#include <winpr/assert.h>
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include <errno.h>
28#include <winpr/wtypes.h>
29#include <winpr/crt.h>
30#include <winpr/file.h>
31
32#include <winpr/ini.h>
33
34typedef struct
35{
36 char* name;
37 char* value;
38} wIniFileKey;
39
40typedef struct
41{
42 char* name;
43 size_t nKeys;
44 size_t cKeys;
45 wIniFileKey** keys;
46} wIniFileSection;
47
48struct s_wIniFile
49{
50 char* line;
51 char* nextLine;
52 size_t lineLength;
53 char* tokctx;
54 char* buffer;
55 size_t buffersize;
56 char* filename;
57 BOOL readOnly;
58 size_t nSections;
59 size_t cSections;
60 wIniFileSection** sections;
61};
62
63static BOOL IniFile_Load_NextLine(wIniFile* ini, char* str)
64{
65 while (TRUE)
66 {
67 WINPR_ASSERT(ini);
68
69 ini->nextLine = strtok_s(str, "\n", &ini->tokctx);
70
71 if (!ini->nextLine)
72 return FALSE;
73
74 size_t length = strlen(ini->nextLine);
75 if (length == 0)
76 continue;
77
78 if (ini->nextLine[length - 1] == '\r')
79 {
80 ini->nextLine[length - 1] = '\0';
81 length--;
82 }
83
84 if (length > 1)
85 return TRUE;
86
87 ini->nextLine = nullptr;
88 }
89}
90
91static BOOL IniFile_BufferResize(wIniFile* ini, size_t size)
92{
93 WINPR_ASSERT(ini);
94 if (size > ini->buffersize)
95 {
96 const size_t diff = size - ini->buffersize;
97 char* tmp = realloc(ini->buffer, size);
98 if (!tmp)
99 return FALSE;
100
101 memset(&tmp[ini->buffersize], 0, diff * sizeof(char));
102 ini->buffer = tmp;
103 ini->buffersize = size;
104 }
105 return TRUE;
106}
107
108static BOOL IniFile_Load_String(wIniFile* ini, const char* iniString)
109{
110 size_t fileSize = 0;
111
112 WINPR_ASSERT(ini);
113
114 if (!iniString)
115 return FALSE;
116
117 ini->line = nullptr;
118 ini->nextLine = nullptr;
119 fileSize = strlen(iniString);
120
121 if (fileSize < 1)
122 return FALSE;
123
124 if (!IniFile_BufferResize(ini, fileSize + 2))
125 return FALSE;
126
127 CopyMemory(ini->buffer, iniString, fileSize);
128 ini->buffer[fileSize] = '\n';
129 IniFile_Load_NextLine(ini, ini->buffer);
130 return TRUE;
131}
132
133static void IniFile_Close_File(FILE* fp)
134{
135 if (fp)
136 (void)fclose(fp);
137}
138
139static FILE* IniFile_Open_File(wIniFile* ini, const char* filename)
140{
141 WINPR_ASSERT(ini);
142
143 if (!filename)
144 return nullptr;
145
146 if (ini->readOnly)
147 return winpr_fopen(filename, "rb");
148 else
149 return winpr_fopen(filename, "w+b");
150}
151
152static BOOL IniFile_Load_File(wIniFile* ini, const char* filename)
153{
154 BOOL rc = FALSE;
155
156 WINPR_ASSERT(ini);
157
158 FILE* fp = IniFile_Open_File(ini, filename);
159 if (!fp)
160 return FALSE;
161
162 if (_fseeki64(fp, 0, SEEK_END) < 0)
163 goto out_file;
164
165 {
166 const INT64 fileSize = _ftelli64(fp);
167 if (fileSize < 0)
168 goto out_file;
169
170 if (_fseeki64(fp, 0, SEEK_SET) < 0)
171 goto out_file;
172
173 ini->line = nullptr;
174 ini->nextLine = nullptr;
175
176 if (fileSize < 1)
177 goto out_file;
178
179 if (!IniFile_BufferResize(ini, (size_t)fileSize + 2))
180 goto out_file;
181
182 if (fread(ini->buffer, (size_t)fileSize, 1ul, fp) != 1)
183 goto out_file;
184
185 ini->buffer[fileSize] = '\n';
186 ini->buffer[fileSize + 1] = '\0';
187 }
188 IniFile_Load_NextLine(ini, ini->buffer);
189 rc = TRUE;
190
191out_file:
192 IniFile_Close_File(fp);
193 return rc;
194}
195
196static BOOL IniFile_Load_HasNextLine(wIniFile* ini)
197{
198 WINPR_ASSERT(ini);
199
200 return (ini->nextLine != nullptr);
201}
202
203static char* IniFile_Load_GetNextLine(wIniFile* ini)
204{
205 WINPR_ASSERT(ini);
206
207 ini->line = ini->nextLine;
208 ini->lineLength = strlen(ini->line);
209 IniFile_Load_NextLine(ini, nullptr);
210 return ini->line;
211}
212
213static void IniFile_Key_Free(wIniFileKey* key)
214{
215 if (!key)
216 return;
217
218 free(key->name);
219 free(key->value);
220 free(key);
221}
222
223static wIniFileKey* IniFile_Key_New(const char* name, const char* value)
224{
225 if (!name || !value)
226 return nullptr;
227
228 wIniFileKey* key = calloc(1, sizeof(wIniFileKey));
229
230 if (key)
231 {
232 key->name = _strdup(name);
233 key->value = _strdup(value);
234
235 if (!key->name || !key->value)
236 {
237 IniFile_Key_Free(key);
238 return nullptr;
239 }
240 }
241
242 return key;
243}
244
245static void IniFile_Section_Free(wIniFileSection* section)
246{
247 if (!section)
248 return;
249
250 free(section->name);
251
252 for (size_t index = 0; index < section->nKeys; index++)
253 {
254 IniFile_Key_Free(section->keys[index]);
255 }
256
257 free((void*)section->keys);
258 free(section);
259}
260
261static BOOL IniFile_SectionKeysResize(wIniFileSection* section, size_t count)
262{
263 WINPR_ASSERT(section);
264
265 if (section->nKeys + count >= section->cKeys)
266 {
267 const size_t new_size = section->cKeys + count + 1024;
268 const size_t diff = new_size - section->cKeys;
269 wIniFileKey** new_keys =
270 (wIniFileKey**)realloc((void*)section->keys, sizeof(wIniFileKey*) * new_size);
271
272 if (!new_keys)
273 return FALSE;
274
275 memset((void*)&new_keys[section->cKeys], 0, diff * sizeof(wIniFileKey*));
276 section->cKeys = new_size;
277 section->keys = new_keys;
278 }
279 return TRUE;
280}
281
282static wIniFileSection* IniFile_Section_New(const char* name)
283{
284 if (!name)
285 return nullptr;
286
287 wIniFileSection* section = calloc(1, sizeof(wIniFileSection));
288
289 if (!section)
290 goto fail;
291
292 section->name = _strdup(name);
293
294 if (!section->name)
295 goto fail;
296
297 if (!IniFile_SectionKeysResize(section, 64))
298 goto fail;
299
300 return section;
301
302fail:
303 IniFile_Section_Free(section);
304 return nullptr;
305}
306
307static wIniFileSection* IniFile_GetSection(wIniFile* ini, const char* name)
308{
309 wIniFileSection* section = nullptr;
310
311 WINPR_ASSERT(ini);
312
313 if (!name)
314 return nullptr;
315
316 for (size_t index = 0; index < ini->nSections; index++)
317 {
318 if (_stricmp(name, ini->sections[index]->name) == 0)
319 {
320 section = ini->sections[index];
321 break;
322 }
323 }
324
325 return section;
326}
327
328static BOOL IniFile_SectionResize(wIniFile* ini, size_t count)
329{
330 WINPR_ASSERT(ini);
331
332 if (ini->nSections + count >= ini->cSections)
333 {
334 const size_t new_size = ini->cSections + count + 1024;
335 const size_t diff = new_size - ini->cSections;
336 wIniFileSection** new_sect =
337 (wIniFileSection**)realloc((void*)ini->sections, sizeof(wIniFileSection*) * new_size);
338
339 if (!new_sect)
340 return FALSE;
341
342 memset((void*)&new_sect[ini->cSections], 0, diff * sizeof(wIniFileSection*));
343 ini->cSections = new_size;
344 ini->sections = new_sect;
345 }
346 return TRUE;
347}
348
349static wIniFileSection* IniFile_AddToSection(wIniFile* ini, const char* name)
350{
351 WINPR_ASSERT(ini);
352
353 if (!name)
354 return nullptr;
355
356 wIniFileSection* section = IniFile_GetSection(ini, name);
357
358 if (!section)
359 {
360 if (!IniFile_SectionResize(ini, 1))
361 return nullptr;
362
363 section = IniFile_Section_New(name);
364 if (!section)
365 return nullptr;
366 ini->sections[ini->nSections++] = section;
367 }
368
369 return section;
370}
371
372static wIniFileKey* IniFile_GetKey(wIniFileSection* section, const char* name)
373{
374 wIniFileKey* key = nullptr;
375
376 WINPR_ASSERT(section);
377
378 if (!name)
379 return nullptr;
380
381 for (size_t index = 0; index < section->nKeys; index++)
382 {
383 if (_stricmp(name, section->keys[index]->name) == 0)
384 {
385 key = section->keys[index];
386 break;
387 }
388 }
389
390 return key;
391}
392
393static wIniFileKey* IniFile_AddKey(wIniFileSection* section, const char* name, const char* value)
394{
395 WINPR_ASSERT(section);
396
397 if (!name || !value)
398 return nullptr;
399
400 wIniFileKey* key = IniFile_GetKey(section, name);
401
402 if (!key)
403 {
404 if (!IniFile_SectionKeysResize(section, 1))
405 return nullptr;
406
407 key = IniFile_Key_New(name, value);
408
409 if (!key)
410 return nullptr;
411
412 section->keys[section->nKeys++] = key;
413 }
414 else
415 {
416 free(key->value);
417 key->value = _strdup(value);
418
419 if (!key->value)
420 return nullptr;
421 }
422
423 return key;
424}
425
426static int IniFile_Load(wIniFile* ini)
427{
428 char* name = nullptr;
429 char* value = nullptr;
430 char* separator = nullptr;
431 char* beg = nullptr;
432 char* end = nullptr;
433 wIniFileSection* section = nullptr;
434
435 WINPR_ASSERT(ini);
436
437 while (IniFile_Load_HasNextLine(ini))
438 {
439 char* line = IniFile_Load_GetNextLine(ini);
440
441 if (line[0] == ';')
442 continue;
443
444 if (line[0] == '[')
445 {
446 beg = &line[1];
447 end = strchr(line, ']');
448
449 if (!end)
450 return -1;
451
452 *end = '\0';
453 IniFile_AddToSection(ini, beg);
454 section = ini->sections[ini->nSections - 1];
455 }
456 else
457 {
458 separator = strchr(line, '=');
459
460 if (separator == nullptr)
461 return -1;
462
463 end = separator;
464
465 while ((&end[-1] > line) && ((end[-1] == ' ') || (end[-1] == '\t')))
466 end--;
467
468 *end = '\0';
469 name = line;
470 beg = separator + 1;
471
472 while (*beg && ((*beg == ' ') || (*beg == '\t')))
473 beg++;
474
475 if (*beg == '"')
476 beg++;
477
478 end = &line[ini->lineLength];
479
480 while ((end > beg) && ((end[-1] == ' ') || (end[-1] == '\t')))
481 end--;
482
483 if (end[-1] == '"')
484 end[-1] = '\0';
485
486 value = beg;
487
488 if (!IniFile_AddKey(section, name, value))
489 return -1;
490 }
491 }
492
493 return 1;
494}
495
496static BOOL IniFile_SetFilename(wIniFile* ini, const char* name)
497{
498 WINPR_ASSERT(ini);
499 free(ini->filename);
500 ini->filename = nullptr;
501
502 if (!name)
503 return TRUE;
504 ini->filename = _strdup(name);
505 return ini->filename != nullptr;
506}
507
508int IniFile_ReadBuffer(wIniFile* ini, const char* buffer)
509{
510 BOOL status = 0;
511
512 WINPR_ASSERT(ini);
513
514 if (!buffer)
515 return -1;
516
517 ini->readOnly = TRUE;
518 status = IniFile_Load_String(ini, buffer);
519
520 if (!status)
521 return -1;
522
523 return IniFile_Load(ini);
524}
525
526int IniFile_ReadFile(wIniFile* ini, const char* filename)
527{
528 WINPR_ASSERT(ini);
529
530 ini->readOnly = TRUE;
531 if (!IniFile_SetFilename(ini, filename))
532 return -1;
533 if (!ini->filename)
534 return -1;
535
536 if (!IniFile_Load_File(ini, filename))
537 return -1;
538
539 return IniFile_Load(ini);
540}
541
542char** IniFile_GetSectionNames(wIniFile* ini, size_t* count)
543{
544 WINPR_ASSERT(ini);
545
546 if (!count)
547 return nullptr;
548
549 if (ini->nSections > INT_MAX)
550 return nullptr;
551
552 size_t length = (sizeof(char*) * ini->nSections) + sizeof(char);
553
554 for (size_t index = 0; index < ini->nSections; index++)
555 {
556 wIniFileSection* section = ini->sections[index];
557 const size_t nameLength = strlen(section->name);
558 length += (nameLength + 1);
559 }
560
561 char** sectionNames = (char**)calloc(length, sizeof(char*));
562
563 if (!sectionNames)
564 return nullptr;
565
566 char* p = (char*)&((BYTE*)sectionNames)[sizeof(char*) * ini->nSections];
567
568 for (size_t index = 0; index < ini->nSections; index++)
569 {
570 sectionNames[index] = p;
571 wIniFileSection* section = ini->sections[index];
572 const size_t nameLength = strlen(section->name);
573 CopyMemory(p, section->name, nameLength + 1);
574 p += (nameLength + 1);
575 }
576
577 *p = '\0';
578 *count = ini->nSections;
579 return sectionNames;
580}
581
582char** IniFile_GetSectionKeyNames(wIniFile* ini, const char* section, size_t* count)
583{
584 WINPR_ASSERT(ini);
585
586 if (!section || !count)
587 return nullptr;
588
589 wIniFileSection* pSection = IniFile_GetSection(ini, section);
590
591 if (!pSection)
592 return nullptr;
593
594 if (pSection->nKeys > INT_MAX)
595 return nullptr;
596
597 size_t length = (sizeof(char*) * pSection->nKeys) + sizeof(char);
598
599 for (size_t index = 0; index < pSection->nKeys; index++)
600 {
601 wIniFileKey* pKey = pSection->keys[index];
602 const size_t nameLength = strlen(pKey->name);
603 length += (nameLength + 1);
604 }
605
606 char** keyNames = (char**)calloc(length, sizeof(char*));
607
608 if (!keyNames)
609 return nullptr;
610
611 char* p = (char*)&((BYTE*)keyNames)[sizeof(char*) * pSection->nKeys];
612
613 for (size_t index = 0; index < pSection->nKeys; index++)
614 {
615 keyNames[index] = p;
616 wIniFileKey* pKey = pSection->keys[index];
617 const size_t nameLength = strlen(pKey->name);
618 CopyMemory(p, pKey->name, nameLength + 1);
619 p += (nameLength + 1);
620 }
621
622 *p = '\0';
623 *count = pSection->nKeys;
624 return keyNames;
625}
626
627const char* IniFile_GetKeyValueString(wIniFile* ini, const char* section, const char* key)
628{
629 const char* value = nullptr;
630 wIniFileKey* pKey = nullptr;
631 wIniFileSection* pSection = nullptr;
632
633 WINPR_ASSERT(ini);
634
635 pSection = IniFile_GetSection(ini, section);
636
637 if (!pSection)
638 return nullptr;
639
640 pKey = IniFile_GetKey(pSection, key);
641
642 if (!pKey)
643 return nullptr;
644
645 value = (const char*)pKey->value;
646 return value;
647}
648
649int IniFile_GetKeyValueInt(wIniFile* ini, const char* section, const char* key)
650{
651 int err = 0;
652 long value = 0;
653 wIniFileKey* pKey = nullptr;
654 wIniFileSection* pSection = nullptr;
655
656 WINPR_ASSERT(ini);
657
658 pSection = IniFile_GetSection(ini, section);
659
660 if (!pSection)
661 return 0;
662
663 pKey = IniFile_GetKey(pSection, key);
664
665 if (!pKey)
666 return 0;
667
668 err = errno;
669 errno = 0;
670 value = strtol(pKey->value, nullptr, 0);
671 if ((value < INT_MIN) || (value > INT_MAX) || (errno != 0))
672 {
673 errno = err;
674 return 0;
675 }
676 return (int)value;
677}
678
679int IniFile_SetKeyValueString(wIniFile* ini, const char* section, const char* key,
680 const char* value)
681{
682 wIniFileKey* pKey = nullptr;
683
684 WINPR_ASSERT(ini);
685 wIniFileSection* pSection = IniFile_GetSection(ini, section);
686
687 if (!pSection)
688 pSection = IniFile_AddToSection(ini, section);
689
690 if (!pSection)
691 return -1;
692
693 pKey = IniFile_AddKey(pSection, key, value);
694
695 if (!pKey)
696 return -1;
697
698 return 1;
699}
700
701int IniFile_SetKeyValueInt(wIniFile* ini, const char* section, const char* key, int value)
702{
703 char strVal[128] = WINPR_C_ARRAY_INIT;
704 wIniFileKey* pKey = nullptr;
705 wIniFileSection* pSection = nullptr;
706
707 WINPR_ASSERT(ini);
708
709 (void)sprintf_s(strVal, sizeof(strVal), "%d", value);
710 pSection = IniFile_GetSection(ini, section);
711
712 if (!pSection)
713 pSection = IniFile_AddToSection(ini, section);
714
715 if (!pSection)
716 return -1;
717
718 pKey = IniFile_AddKey(pSection, key, strVal);
719
720 if (!pKey)
721 return -1;
722
723 return 1;
724}
725
726char* IniFile_WriteBuffer(wIniFile* ini)
727{
728 size_t offset = 0;
729 size_t size = 0;
730 char* buffer = nullptr;
731
732 WINPR_ASSERT(ini);
733
734 for (size_t i = 0; i < ini->nSections; i++)
735 {
736 wIniFileSection* section = ini->sections[i];
737 size += (strlen(section->name) + 3);
738
739 for (size_t j = 0; j < section->nKeys; j++)
740 {
741 wIniFileKey* key = section->keys[j];
742 size += (strlen(key->name) + strlen(key->value) + 2);
743 }
744
745 size += 1;
746 }
747
748 size += 1;
749 buffer = calloc(size + 1, sizeof(char));
750
751 if (!buffer)
752 return nullptr;
753
754 offset = 0;
755
756 for (size_t i = 0; i < ini->nSections; i++)
757 {
758 wIniFileSection* section = ini->sections[i];
759 (void)sprintf_s(&buffer[offset], size - offset, "[%s]\n", section->name);
760 offset += (strlen(section->name) + 3);
761
762 for (size_t j = 0; j < section->nKeys; j++)
763 {
764 wIniFileKey* key = section->keys[j];
765 (void)sprintf_s(&buffer[offset], size - offset, "%s=%s\n", key->name, key->value);
766 offset += (strlen(key->name) + strlen(key->value) + 2);
767 }
768
769 (void)sprintf_s(&buffer[offset], size - offset, "\n");
770 offset += 1;
771 }
772
773 return buffer;
774}
775
776int IniFile_WriteFile(wIniFile* ini, const char* filename)
777{
778 int ret = -1;
779
780 WINPR_ASSERT(ini);
781
782 char* buffer = IniFile_WriteBuffer(ini);
783
784 if (!buffer)
785 return -1;
786
787 const size_t length = strlen(buffer);
788 ini->readOnly = FALSE;
789
790 if (!filename)
791 filename = ini->filename;
792
793 FILE* fp = IniFile_Open_File(ini, filename);
794 if (!fp)
795 goto fail;
796
797 if (fwrite((void*)buffer, length, 1, fp) != 1)
798 goto fail;
799
800 ret = 1;
801
802fail:
803 IniFile_Close_File(fp);
804 free(buffer);
805 return ret;
806}
807
808void IniFile_Free(wIniFile* ini)
809{
810 if (!ini)
811 return;
812
813 IniFile_SetFilename(ini, nullptr);
814
815 for (size_t index = 0; index < ini->nSections; index++)
816 IniFile_Section_Free(ini->sections[index]);
817
818 free((void*)ini->sections);
819 free(ini->buffer);
820 free(ini);
821}
822
823wIniFile* IniFile_New(void)
824{
825 wIniFile* ini = (wIniFile*)calloc(1, sizeof(wIniFile));
826
827 if (!ini)
828 goto fail;
829
830 if (!IniFile_SectionResize(ini, 64))
831 goto fail;
832
833 return ini;
834
835fail:
836 WINPR_PRAGMA_DIAG_PUSH
837 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
838 IniFile_Free(ini);
839 WINPR_PRAGMA_DIAG_POP
840 return nullptr;
841}
842
843wIniFile* IniFile_Clone(const wIniFile* ini)
844{
845 if (!ini)
846 return nullptr;
847
848 wIniFile* copy = IniFile_New();
849 if (!copy)
850 goto fail;
851
852 copy->lineLength = ini->lineLength;
853 if (!IniFile_SetFilename(copy, ini->filename))
854 goto fail;
855
856 if (ini->buffersize > 0)
857 {
858 if (!IniFile_BufferResize(copy, ini->buffersize))
859 goto fail;
860 memcpy(copy->buffer, ini->buffer, copy->buffersize);
861 }
862
863 copy->readOnly = ini->readOnly;
864
865 for (size_t x = 0; x < ini->nSections; x++)
866 {
867 const wIniFileSection* cur = ini->sections[x];
868 if (!cur)
869 goto fail;
870
871 wIniFileSection* scopy = IniFile_AddToSection(copy, cur->name);
872 if (!scopy)
873 goto fail;
874
875 for (size_t y = 0; y < cur->nKeys; y++)
876 {
877 const wIniFileKey* key = cur->keys[y];
878 if (!key)
879 goto fail;
880
881 IniFile_AddKey(scopy, key->name, key->value);
882 }
883 }
884 return copy;
885
886fail:
887 IniFile_Free(copy);
888 return nullptr;
889}