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