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