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