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