xref: /reactos/boot/freeldr/freeldr/lib/inifile/parse.c (revision b790cfce)
1 /*
2  *  FreeLoader
3  *  Copyright (C) 1998-2003  Brian Palmer  <brianp@sginet.com>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include <freeldr.h>
21 
22 #include <debug.h>
23 DBG_DEFAULT_CHANNEL(INIFILE);
24 
25 LIST_ENTRY IniFileSectionListHead = {&IniFileSectionListHead, &IniFileSectionListHead};
26 BOOLEAN IniFileSectionInitialized = FALSE;
27 ULONG   IniFileSectionCount = 0;
28 ULONG   IniFileSettingCount = 0;
29 
30 
IniParseFile(PCHAR IniFileData,ULONG IniFileSize)31 BOOLEAN IniParseFile(PCHAR IniFileData, ULONG IniFileSize)
32 {
33     ULONG                    CurrentOffset;
34     ULONG                    CurrentLineNumber;
35     PCHAR                IniFileLine;
36     ULONG                    IniFileLineSize;
37     ULONG                    LineLength;
38     PINI_SECTION        CurrentSection = NULL;
39     PINI_SECTION_ITEM    CurrentItem = NULL;
40 
41     TRACE("IniParseFile() IniFileSize: %d\n", IniFileSize);
42 
43     if (!IniFileSectionInitialized)
44     {
45         InitializeListHead(&IniFileSectionListHead);
46         IniFileSectionInitialized = TRUE;
47     }
48 
49     // Start with an 80-byte buffer
50     IniFileLineSize = 80;
51     IniFileLine = FrLdrTempAlloc(IniFileLineSize, TAG_INI_FILE);
52     if (!IniFileLine)
53     {
54         return FALSE;
55     }
56 
57     // Loop through each line and parse it
58     CurrentLineNumber = 0;
59     CurrentOffset = 0;
60     while (CurrentOffset < IniFileSize)
61     {
62         // First check the line size and increase our buffer if necessary
63         if (IniFileLineSize < IniGetNextLineSize(IniFileData, IniFileSize, CurrentOffset))
64         {
65             IniFileLineSize = IniGetNextLineSize(IniFileData, IniFileSize, CurrentOffset);
66             FrLdrTempFree(IniFileLine, TAG_INI_FILE);
67             IniFileLine = FrLdrTempAlloc(IniFileLineSize, TAG_INI_FILE);
68             if (!IniFileLine)
69             {
70                 return FALSE;
71             }
72         }
73 
74         // Get the line of data
75         CurrentOffset = IniGetNextLine(IniFileData, IniFileSize, IniFileLine, IniFileLineSize, CurrentOffset);
76         LineLength = (ULONG)strlen(IniFileLine);
77 
78         // If it is a blank line or a comment then skip it
79         if (IniIsLineEmpty(IniFileLine, LineLength) || IniIsCommentLine(IniFileLine, LineLength))
80         {
81             CurrentLineNumber++;
82             continue;
83         }
84 
85         // Check if it is a new section
86         if (IniIsSectionName(IniFileLine, LineLength))
87         {
88             // Allocate a new section structure
89             CurrentSection = FrLdrTempAlloc(sizeof(INI_SECTION), TAG_INI_SECTION);
90             if (!CurrentSection)
91             {
92                 FrLdrTempFree(IniFileLine, TAG_INI_FILE);
93                 return FALSE;
94             }
95 
96             RtlZeroMemory(CurrentSection, sizeof(INI_SECTION));
97 
98             // Allocate the section name buffer
99             CurrentSection->SectionName = FrLdrTempAlloc(IniGetSectionNameSize(IniFileLine, LineLength), TAG_INI_NAME);
100             if (!CurrentSection->SectionName)
101             {
102                 FrLdrTempFree(CurrentSection, TAG_INI_FILE);
103                 FrLdrTempFree(IniFileLine, TAG_INI_FILE);
104                 return FALSE;
105             }
106 
107             // Get the section name
108             IniExtractSectionName(CurrentSection->SectionName, IniFileLine, LineLength);
109             InitializeListHead(&CurrentSection->SectionItemList);
110 
111             // Add it to the section list head
112             IniFileSectionCount++;
113             InsertTailList(&IniFileSectionListHead, &CurrentSection->ListEntry);
114 
115             CurrentLineNumber++;
116             continue;
117         }
118 
119         // Check if it is a setting
120         if (IniIsSetting(IniFileLine, LineLength))
121         {
122             // First check to make sure we're inside a [section]
123             if (CurrentSection == NULL)
124             {
125                 ERR("Error: freeldr.ini:%lu: Setting '%s' found outside of a [section].\n", CurrentLineNumber, IniFileLine);
126 
127                 // Skip it
128                 CurrentLineNumber++;
129                 continue;
130             }
131 
132             // Allocate a new item structure
133             CurrentItem = FrLdrTempAlloc(sizeof(INI_SECTION_ITEM), TAG_INI_SECTION_ITEM);
134             if (!CurrentItem)
135             {
136                 FrLdrTempFree(IniFileLine, TAG_INI_FILE);
137                 return FALSE;
138             }
139 
140             RtlZeroMemory(CurrentItem, sizeof(INI_SECTION_ITEM));
141 
142             // Allocate the setting name buffer
143             CurrentItem->ItemName = FrLdrTempAlloc(IniGetSettingNameSize(IniFileLine, LineLength), TAG_INI_NAME);
144             if (!CurrentItem->ItemName)
145             {
146                 FrLdrTempFree(CurrentItem, TAG_INI_SECTION_ITEM);
147                 FrLdrTempFree(IniFileLine, TAG_INI_FILE);
148                 return FALSE;
149             }
150 
151             // Allocate the setting value buffer
152             CurrentItem->ItemValue = FrLdrTempAlloc(IniGetSettingValueSize(IniFileLine, LineLength), TAG_INI_VALUE);
153             if (!CurrentItem->ItemValue)
154             {
155                 FrLdrTempFree(CurrentItem->ItemName, TAG_INI_NAME);
156                 FrLdrTempFree(CurrentItem, TAG_INI_SECTION_ITEM);
157                 FrLdrTempFree(IniFileLine, TAG_INI_FILE);
158                 return FALSE;
159             }
160 
161             // Get the section name
162             IniExtractSettingName(CurrentItem->ItemName, IniFileLine, LineLength);
163             IniExtractSettingValue(CurrentItem->ItemValue, IniFileLine, LineLength);
164 
165             // Add it to the current section
166             IniFileSettingCount++;
167             CurrentSection->SectionItemCount++;
168             InsertTailList(&CurrentSection->SectionItemList, &CurrentItem->ListEntry);
169 
170             CurrentLineNumber++;
171             continue;
172         }
173 
174         CurrentLineNumber++;
175     }
176 
177     FrLdrTempFree(IniFileLine, TAG_INI_FILE);
178 
179     TRACE("Parsed %d sections and %d settings.\n", IniFileSectionCount, IniFileSettingCount);
180     TRACE("IniParseFile() done.\n");
181 
182     return TRUE;
183 }
184 
IniGetNextLineSize(PCHAR IniFileData,ULONG IniFileSize,ULONG CurrentOffset)185 ULONG IniGetNextLineSize(PCHAR IniFileData, ULONG IniFileSize, ULONG CurrentOffset)
186 {
187     ULONG        LineCharCount = 0;
188 
189     // Loop through counting chars until we hit the end of the
190     // file or we encounter a new line char
191     for (; (CurrentOffset < IniFileSize); CurrentOffset++)
192     {
193         // Increment the line character count
194         LineCharCount++;
195 
196         // Check for new line char
197         if (IniFileData[CurrentOffset] == '\n')
198         {
199             break;
200         }
201     }
202 
203     // Add one for the NULL-terminator
204     LineCharCount++;
205 
206     // Send back line character count
207     return LineCharCount;
208 }
209 
IniGetNextLine(PCHAR IniFileData,ULONG IniFileSize,PCHAR Buffer,ULONG BufferSize,ULONG CurrentOffset)210 ULONG IniGetNextLine(PCHAR IniFileData, ULONG IniFileSize, PCHAR Buffer, ULONG BufferSize, ULONG CurrentOffset)
211 {
212     ULONG        Idx;
213 
214     // Loop through grabbing chars until we hit the end of the
215     // file or we encounter a new line char
216     for (Idx=0; (CurrentOffset < IniFileSize); CurrentOffset++)
217     {
218         // If we haven't exceeded our buffer size yet
219         // then store another char
220         if (Idx < (BufferSize - 1))
221         {
222             Buffer[Idx++] = IniFileData[CurrentOffset];
223         }
224 
225         // Check for new line char
226         if (IniFileData[CurrentOffset] == '\n')
227         {
228             CurrentOffset++;
229             break;
230         }
231     }
232 
233     // Terminate the string
234     Buffer[Idx] = '\0';
235 
236     // Get rid of newline & linefeed characters (if any)
237     while(Idx && (Buffer[--Idx] == '\n' || Buffer[Idx] == '\r'))
238         Buffer[Idx] = '\0';
239 
240     // Send back new offset
241     return CurrentOffset;
242 }
243 
IniIsLineEmpty(PCHAR LineOfText,ULONG TextLength)244 BOOLEAN IniIsLineEmpty(PCHAR LineOfText, ULONG TextLength)
245 {
246     ULONG        Idx;
247 
248     // Check for text (skipping whitespace)
249     for (Idx=0; Idx<TextLength; Idx++)
250     {
251         if ((LineOfText[Idx] == ' ') ||
252             (LineOfText[Idx] == '\t') ||
253             (LineOfText[Idx] == '\n') ||
254             (LineOfText[Idx] == '\r'))
255         {
256             continue;
257         }
258         else
259         {
260             return FALSE;
261         }
262     }
263 
264     return TRUE;
265 }
266 
IniIsCommentLine(PCHAR LineOfText,ULONG TextLength)267 BOOLEAN IniIsCommentLine(PCHAR LineOfText, ULONG TextLength)
268 {
269     ULONG        Idx;
270 
271     // Check the first character (skipping whitespace)
272     // and make sure that it is an opening bracket
273     for (Idx=0; Idx<TextLength; Idx++)
274     {
275         if ((LineOfText[Idx] == ' ') ||
276             (LineOfText[Idx] == '\t'))
277         {
278             continue;
279         }
280         else if (LineOfText[Idx] == INI_FILE_COMMENT_CHAR)
281         {
282             return TRUE;
283         }
284         else
285         {
286             break;
287         }
288     }
289 
290     return FALSE;
291 }
292 
IniIsSectionName(PCHAR LineOfText,ULONG TextLength)293 BOOLEAN IniIsSectionName(PCHAR LineOfText, ULONG TextLength)
294 {
295     ULONG        Idx;
296 
297     // Check the first character (skipping whitespace)
298     // and make sure that it is an opening bracket
299     for (Idx=0; Idx<TextLength; Idx++)
300     {
301         if ((LineOfText[Idx] == ' ') ||
302             (LineOfText[Idx] == '\t'))
303         {
304             continue;
305         }
306         else if (LineOfText[Idx] == '[')
307         {
308             return TRUE;
309         }
310         else
311         {
312             break;
313         }
314     }
315 
316     return FALSE;
317 }
318 
IniGetSectionNameSize(PCHAR SectionNameLine,ULONG LineLength)319 ULONG IniGetSectionNameSize(PCHAR SectionNameLine, ULONG LineLength)
320 {
321     ULONG        Idx;
322     ULONG        NameSize;
323 
324     // Find the opening bracket (skipping whitespace)
325     for (Idx=0; Idx<LineLength; Idx++)
326     {
327         if ((SectionNameLine[Idx] == ' ') ||
328             (SectionNameLine[Idx] == '\t'))
329         {
330             continue;
331         }
332         else //if (SectionNameLine[Idx] == '[')
333         {
334             break;
335         }
336     }
337 
338     // Skip past the opening bracket
339     Idx++;
340 
341     // Count the characters up until the closing bracket or EOL
342     for (NameSize=0; Idx<LineLength; Idx++)
343     {
344         if ((SectionNameLine[Idx] == ']') ||
345             (SectionNameLine[Idx] == '\0'))
346         {
347             break;
348         }
349 
350         // Increment the count
351         NameSize++;
352     }
353 
354     // Add one for the NULL-terminator
355     NameSize++;
356 
357     return NameSize;
358 }
359 
IniExtractSectionName(PCHAR SectionName,PCHAR SectionNameLine,ULONG LineLength)360 VOID IniExtractSectionName(PCHAR SectionName, PCHAR SectionNameLine, ULONG LineLength)
361 {
362     ULONG        Idx;
363     ULONG        DestIdx;
364 
365     // Find the opening bracket (skipping whitespace)
366     for (Idx=0; Idx<LineLength; Idx++)
367     {
368         if ((SectionNameLine[Idx] == ' ') ||
369             (SectionNameLine[Idx] == '\t'))
370         {
371             continue;
372         }
373         else //if (SectionNameLine[Idx] == '[')
374         {
375             break;
376         }
377     }
378 
379     // Skip past the opening bracket
380     Idx++;
381 
382     // Count the characters up until the closing bracket or EOL
383     for (DestIdx=0; Idx<LineLength; Idx++)
384     {
385         if ((SectionNameLine[Idx] == ']') ||
386             (SectionNameLine[Idx] == '\0'))
387         {
388             break;
389         }
390 
391         // Grab a character and increment DestIdx
392         SectionName[DestIdx] = SectionNameLine[Idx];
393         DestIdx++;
394     }
395 
396     // Terminate the string
397     SectionName[DestIdx] = '\0';
398 }
399 
IniIsSetting(PCHAR LineOfText,ULONG TextLength)400 BOOLEAN IniIsSetting(PCHAR LineOfText, ULONG TextLength)
401 {
402     ULONG        Idx;
403 
404     // Basically just check for an '=' equals sign
405     for (Idx=0; Idx<TextLength; Idx++)
406     {
407         if (LineOfText[Idx] == '=')
408         {
409             return TRUE;
410         }
411     }
412 
413     return FALSE;
414 }
415 
IniGetSettingNameSize(PCHAR SettingNameLine,ULONG LineLength)416 ULONG IniGetSettingNameSize(PCHAR SettingNameLine, ULONG LineLength)
417 {
418     ULONG        Idx;
419     ULONG        NameSize;
420 
421     // Skip whitespace
422     for (Idx=0; Idx<LineLength; Idx++)
423     {
424         if ((SettingNameLine[Idx] == ' ') ||
425             (SettingNameLine[Idx] == '\t'))
426         {
427             continue;
428         }
429         else
430         {
431             break;
432         }
433     }
434 
435     // Count the characters up until the '=' equals sign or EOL
436     for (NameSize=0; Idx<LineLength; Idx++)
437     {
438         if ((SettingNameLine[Idx] == '=') ||
439             (SettingNameLine[Idx] == '\0'))
440         {
441             break;
442         }
443 
444         // Increment the count
445         NameSize++;
446     }
447 
448     // Add one for the NULL-terminator
449     NameSize++;
450 
451     return NameSize;
452 }
453 
IniGetSettingValueSize(PCHAR SettingValueLine,ULONG LineLength)454 ULONG IniGetSettingValueSize(PCHAR SettingValueLine, ULONG LineLength)
455 {
456     ULONG        Idx;
457     ULONG        ValueSize;
458 
459     // Skip whitespace
460     for (Idx=0; Idx<LineLength; Idx++)
461     {
462         if ((SettingValueLine[Idx] == ' ') ||
463             (SettingValueLine[Idx] == '\t'))
464         {
465             continue;
466         }
467         else
468         {
469             break;
470         }
471     }
472 
473     // Skip the characters up until the '=' equals sign or EOL
474     for (; Idx<LineLength; Idx++)
475     {
476         if (SettingValueLine[Idx] == '=')
477         {
478             Idx++;
479             break;
480         }
481 
482         // If we hit EOL then obviously the value size is zero
483         if (SettingValueLine[Idx] == '\0')
484         {
485             return 0;
486         }
487     }
488 
489     // Count the characters up until the EOL
490     for (ValueSize=0; Idx<LineLength; Idx++)
491     {
492         if (SettingValueLine[Idx] == '\0')
493         {
494             break;
495         }
496 
497         // Increment the count
498         ValueSize++;
499     }
500 
501     // Add one for the NULL-terminator
502     ValueSize++;
503 
504     return ValueSize;
505 }
506 
IniExtractSettingName(PCHAR SettingName,PCHAR SettingNameLine,ULONG LineLength)507 VOID IniExtractSettingName(PCHAR SettingName, PCHAR SettingNameLine, ULONG LineLength)
508 {
509     ULONG        Idx;
510     ULONG        DestIdx;
511 
512     // Skip whitespace
513     for (Idx=0; Idx<LineLength; Idx++)
514     {
515         if ((SettingNameLine[Idx] == ' ') ||
516             (SettingNameLine[Idx] == '\t'))
517         {
518             continue;
519         }
520         else
521         {
522             break;
523         }
524     }
525 
526     // Get the characters up until the '=' equals sign or EOL
527     for (DestIdx=0; Idx<LineLength; Idx++)
528     {
529         if ((SettingNameLine[Idx] == '=') ||
530             (SettingNameLine[Idx] == '\0'))
531         {
532             break;
533         }
534 
535         // Grab a character and increment DestIdx
536         SettingName[DestIdx] = SettingNameLine[Idx];
537         DestIdx++;
538     }
539 
540     // Terminate the string
541     SettingName[DestIdx] = '\0';
542 }
543 
IniExtractSettingValue(PCHAR SettingValue,PCHAR SettingValueLine,ULONG LineLength)544 VOID IniExtractSettingValue(PCHAR SettingValue, PCHAR SettingValueLine, ULONG LineLength)
545 {
546     ULONG        Idx;
547     ULONG        DestIdx;
548 
549     // Skip whitespace
550     for (Idx=0; Idx<LineLength; Idx++)
551     {
552         if ((SettingValueLine[Idx] == ' ') ||
553             (SettingValueLine[Idx] == '\t'))
554         {
555             continue;
556         }
557         else
558         {
559             break;
560         }
561     }
562 
563     // Skip the characters up until the '=' equals sign or EOL
564     for (; Idx<LineLength; Idx++)
565     {
566         if (SettingValueLine[Idx] == '=')
567         {
568             Idx++;
569             break;
570         }
571 
572         // If we hit EOL then obviously the value size is zero
573         if (SettingValueLine[Idx] == '\0')
574         {
575             SettingValue[0] = '\0';
576             return;
577         }
578     }
579 
580     // Get the characters up until the EOL
581     for (DestIdx=0; Idx<LineLength; Idx++)
582     {
583         if (SettingValueLine[Idx] == '\0')
584         {
585             break;
586         }
587 
588         // Grab a character and increment DestIdx
589         SettingValue[DestIdx] = SettingValueLine[Idx];
590         DestIdx++;
591     }
592 
593     // Terminate the string
594     SettingValue[DestIdx] = '\0';
595 }
596