* Sync up to trunk r55544.
[reactos.git] / 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 DBG_DEFAULT_CHANNEL(INIFILE);
24
25 LIST_ENTRY IniFileSectionListHead;
26 BOOLEAN IniFileSectionInitialized = FALSE;
27 ULONG IniFileSectionCount = 0;
28 ULONG IniFileSettingCount = 0;
29
30
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 = MmHeapAlloc(IniFileLineSize);
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 MmHeapFree(IniFileLine);
67 IniFileLine = MmHeapAlloc(IniFileLineSize);
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 = MmHeapAlloc(sizeof(INI_SECTION));
90 if (!CurrentSection)
91 {
92 MmHeapFree(IniFileLine);
93 return FALSE;
94 }
95
96 RtlZeroMemory(CurrentSection, sizeof(INI_SECTION));
97
98 // Allocate the section name buffer
99 CurrentSection->SectionName = MmHeapAlloc(IniGetSectionNameSize(IniFileLine, LineLength));
100 if (!CurrentSection->SectionName)
101 {
102 MmHeapFree(CurrentSection);
103 MmHeapFree(IniFileLine);
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 printf("Error: freeldr.ini:%ld: Setting '%s' found outside of a [section].\n", CurrentLineNumber, IniFileLine);
126 printf("Press any key to continue...\n");
127 MachConsGetCh();
128 CurrentLineNumber++;
129 continue;
130 }
131
132 // Allocate a new item structure
133 CurrentItem = MmHeapAlloc(sizeof(INI_SECTION_ITEM));
134 if (!CurrentItem)
135 {
136 MmHeapFree(IniFileLine);
137 return FALSE;
138 }
139
140 RtlZeroMemory(CurrentItem, sizeof(INI_SECTION_ITEM));
141
142 // Allocate the setting name buffer
143 CurrentItem->ItemName = MmHeapAlloc(IniGetSettingNameSize(IniFileLine, LineLength));
144 if (!CurrentItem->ItemName)
145 {
146 MmHeapFree(CurrentItem);
147 MmHeapFree(IniFileLine);
148 return FALSE;
149 }
150
151 // Allocate the setting value buffer
152 CurrentItem->ItemValue = MmHeapAlloc(IniGetSettingValueSize(IniFileLine, LineLength));
153 if (!CurrentItem->ItemValue)
154 {
155 MmHeapFree(CurrentItem->ItemName);
156 MmHeapFree(CurrentItem);
157 MmHeapFree(IniFileLine);
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 TRACE("Parsed %d sections and %d settings.\n", IniFileSectionCount, IniFileSettingCount);
178 TRACE("IniParseFile() done.\n");
179
180 return TRUE;
181 }
182
183 ULONG IniGetNextLineSize(PCHAR IniFileData, ULONG IniFileSize, ULONG CurrentOffset)
184 {
185 ULONG LineCharCount = 0;
186
187 // Loop through counting chars until we hit the end of the
188 // file or we encounter a new line char
189 for (; (CurrentOffset < IniFileSize); CurrentOffset++)
190 {
191 // Increment the line character count
192 LineCharCount++;
193
194 // Check for new line char
195 if (IniFileData[CurrentOffset] == '\n')
196 {
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 }