[FREELDR] INFFILE Parser: Fix while-loop conditions, this bug existed for 16+ years...
[reactos.git] / boot / freeldr / freeldr / ntldr / inffile.c
1 /*
2 * PROJECT: FreeLoader
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: INF file parser that caches contents of INF file in memory.
5 * COPYRIGHT: Copyright 2002-2006 Royce Mitchell III
6 * Copyright 2003-2019 Eric Kohl
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include <freeldr.h>
12 #include "inffile.h"
13
14 #define CONTROL_Z '\x1a'
15 #define MAX_SECTION_NAME_LEN 255
16 #define MAX_FIELD_LEN 511 /* larger fields get silently truncated */
17 /* actual string limit is MAX_INF_STRING_LENGTH+1 (plus terminating null) under Windows */
18 #define MAX_STRING_LEN (MAX_INF_STRING_LENGTH+1)
19
20 #define TAG_INF_KEY 'KfnI'
21 #define TAG_INF_FIELD 'ffnI'
22 #define TAG_INF_LINE 'LfnI'
23 #define TAG_INF_SECTION 'SfnI'
24 #define TAG_INF_CACHE 'CfnI'
25 #define TAG_INF_FILE 'FfnI'
26
27 typedef struct _INFCACHEFIELD
28 {
29 struct _INFCACHEFIELD *Next;
30 struct _INFCACHEFIELD *Prev;
31
32 CHAR Data[1];
33 } INFCACHEFIELD, *PINFCACHEFIELD;
34
35 typedef struct _INFCACHELINE
36 {
37 struct _INFCACHELINE *Next;
38 struct _INFCACHELINE *Prev;
39
40 ULONG FieldCount;
41
42 PCHAR Key;
43
44 PINFCACHEFIELD FirstField;
45 PINFCACHEFIELD LastField;
46
47 } INFCACHELINE, *PINFCACHELINE;
48
49 typedef struct _INFCACHESECTION
50 {
51 struct _INFCACHESECTION *Next;
52 struct _INFCACHESECTION *Prev;
53
54 PINFCACHELINE FirstLine;
55 PINFCACHELINE LastLine;
56
57 LONG LineCount;
58
59 CHAR Name[1];
60 } INFCACHESECTION, *PINFCACHESECTION;
61
62 typedef struct _INFCACHE
63 {
64 PINFCACHESECTION FirstSection;
65 PINFCACHESECTION LastSection;
66
67 PINFCACHESECTION StringsSection;
68 } INFCACHE, *PINFCACHE;
69
70 /* parser definitions */
71 enum parser_state
72 {
73 LINE_START, /* at beginning of a line */
74 SECTION_NAME, /* parsing a section name */
75 KEY_NAME, /* parsing a key name */
76 VALUE_NAME, /* parsing a value name */
77 EOL_BACKSLASH, /* backslash at end of line */
78 QUOTES, /* inside quotes */
79 LEADING_SPACES, /* leading spaces */
80 TRAILING_SPACES, /* trailing spaces */
81 COMMENT, /* inside a comment */
82 NB_PARSER_STATES
83 };
84
85 struct parser
86 {
87 const CHAR *start; /* start position of item being parsed */
88 const CHAR *end; /* end of buffer */
89 PINFCACHE file; /* file being built */
90 enum parser_state state; /* current parser state */
91 enum parser_state stack[4]; /* state stack */
92 int stack_pos; /* current pos in stack */
93
94 PINFCACHESECTION cur_section; /* pointer to the section being parsed*/
95 PINFCACHELINE line; /* current line */
96 unsigned int line_pos; /* current line position in file */
97 unsigned int error; /* error code */
98 unsigned int token_len; /* current token len */
99 CHAR token[MAX_FIELD_LEN + 1]; /* current token */
100 };
101
102 typedef const CHAR * (*parser_state_func)(struct parser *parser, const CHAR *pos);
103
104 /* parser state machine functions */
105 static const CHAR *line_start_state(struct parser *parser, const CHAR *pos);
106 static const CHAR *section_name_state(struct parser *parser, const CHAR *pos);
107 static const CHAR *key_name_state(struct parser *parser, const CHAR *pos);
108 static const CHAR *value_name_state(struct parser *parser, const CHAR *pos);
109 static const CHAR *eol_backslash_state(struct parser *parser, const CHAR *pos);
110 static const CHAR *quotes_state(struct parser *parser, const CHAR *pos);
111 static const CHAR *leading_spaces_state(struct parser *parser, const CHAR *pos);
112 static const CHAR *trailing_spaces_state(struct parser *parser, const CHAR *pos);
113 static const CHAR *comment_state(struct parser *parser, const CHAR *pos);
114
115 static
116 const parser_state_func
117 parser_funcs[NB_PARSER_STATES] =
118 {
119 line_start_state, /* LINE_START */
120 section_name_state, /* SECTION_NAME */
121 key_name_state, /* KEY_NAME */
122 value_name_state, /* VALUE_NAME */
123 eol_backslash_state, /* EOL_BACKSLASH */
124 quotes_state, /* QUOTES */
125 leading_spaces_state, /* LEADING_SPACES */
126 trailing_spaces_state, /* TRAILING_SPACES */
127 comment_state /* COMMENT */
128 };
129
130
131 /* PRIVATE FUNCTIONS ********************************************************/
132
133 static
134 PINFCACHELINE
135 InfpCacheFreeLine(
136 PINFCACHELINE Line)
137 {
138 PINFCACHELINE Next;
139 PINFCACHEFIELD Field;
140
141 if (Line == NULL)
142 {
143 return NULL;
144 }
145
146 Next = Line->Next;
147 if (Line->Key != NULL)
148 {
149 FrLdrTempFree(Line->Key, TAG_INF_KEY);
150 Line->Key = NULL;
151 }
152
153 /* Remove data fields */
154 while (Line->FirstField != NULL)
155 {
156 Field = Line->FirstField->Next;
157 FrLdrTempFree(Line->FirstField, TAG_INF_FIELD);
158 Line->FirstField = Field;
159 }
160 Line->LastField = NULL;
161
162 FrLdrTempFree(Line, TAG_INF_LINE);
163
164 return Next;
165 }
166
167
168 static
169 PINFCACHESECTION
170 InfpCacheFreeSection(
171 PINFCACHESECTION Section)
172 {
173 PINFCACHESECTION Next;
174
175 if (Section == NULL)
176 {
177 return NULL;
178 }
179
180 /* Release all keys */
181 Next = Section->Next;
182 while (Section->FirstLine != NULL)
183 {
184 Section->FirstLine = InfpCacheFreeLine(Section->FirstLine);
185 }
186 Section->LastLine = NULL;
187
188 FrLdrTempFree(Section, TAG_INF_SECTION);
189
190 return Next;
191 }
192
193
194 static
195 PINFCACHESECTION
196 InfpCacheFindSection(
197 PINFCACHE Cache,
198 PCSTR Name)
199 {
200 PINFCACHESECTION Section = NULL;
201
202 if (Cache == NULL || Name == NULL)
203 {
204 return NULL;
205 }
206
207 /* iterate through list of sections */
208 Section = Cache->FirstSection;
209 while (Section != NULL)
210 {
211 if (_stricmp(Section->Name, Name) == 0)
212 {
213 return Section;
214 }
215
216 /* get the next section*/
217 Section = Section->Next;
218 }
219
220 return NULL;
221 }
222
223
224 static
225 PINFCACHESECTION
226 InfpCacheAddSection(
227 PINFCACHE Cache,
228 PCHAR Name)
229 {
230 PINFCACHESECTION Section = NULL;
231 SIZE_T Size;
232
233 if ((Cache == NULL) || (Name == NULL))
234 {
235 // DPRINT("Invalid parameter\n");
236 return NULL;
237 }
238
239 /* Allocate and initialize the new section */
240 Size = sizeof(INFCACHESECTION) + strlen(Name);
241 Section = (PINFCACHESECTION)FrLdrTempAlloc(Size, TAG_INF_SECTION);
242 if (Section == NULL)
243 {
244 // DPRINT("RtlAllocateHeap() failed\n");
245 return NULL;
246 }
247 memset(Section, 0, Size);
248
249 /* Copy section name */
250 strcpy(Section->Name, Name);
251
252 /* Append section */
253 if (Cache->FirstSection == NULL)
254 {
255 Cache->FirstSection = Section;
256 Cache->LastSection = Section;
257 }
258 else
259 {
260 Cache->LastSection->Next = Section;
261 Section->Prev = Cache->LastSection;
262 Cache->LastSection = Section;
263 }
264
265 return Section;
266 }
267
268
269 static
270 PINFCACHELINE
271 InfpCacheAddLine(PINFCACHESECTION Section)
272 {
273 PINFCACHELINE Line;
274
275 if (Section == NULL)
276 {
277 // DPRINT("Invalid parameter\n");
278 return NULL;
279 }
280
281 Line = (PINFCACHELINE)FrLdrTempAlloc(sizeof(INFCACHELINE), TAG_INF_LINE);
282 if (Line == NULL)
283 {
284 // DPRINT("RtlAllocateHeap() failed\n");
285 return NULL;
286 }
287 memset(Line, 0, sizeof(INFCACHELINE));
288
289 /* Append line */
290 if (Section->FirstLine == NULL)
291 {
292 Section->FirstLine = Line;
293 Section->LastLine = Line;
294 }
295 else
296 {
297 Section->LastLine->Next = Line;
298 Line->Prev = Section->LastLine;
299 Section->LastLine = Line;
300 }
301 Section->LineCount++;
302
303 return Line;
304 }
305
306
307 static
308 PVOID
309 InfpAddKeyToLine(
310 PINFCACHELINE Line,
311 PCHAR Key)
312 {
313 if (Line == NULL)
314 return NULL;
315
316 if (Line->Key != NULL)
317 return NULL;
318
319 Line->Key = FrLdrTempAlloc(strlen(Key) + 1, TAG_INF_KEY);
320 if (Line->Key == NULL)
321 return NULL;
322
323 strcpy(Line->Key, Key);
324
325 return (PVOID)Line->Key;
326 }
327
328
329 static
330 PVOID
331 InfpAddFieldToLine(
332 PINFCACHELINE Line,
333 PCHAR Data)
334 {
335 PINFCACHEFIELD Field;
336 SIZE_T Size;
337
338 Size = sizeof(INFCACHEFIELD) + strlen(Data);
339 Field = (PINFCACHEFIELD)FrLdrTempAlloc(Size, TAG_INF_FIELD);
340 if (Field == NULL)
341 {
342 return NULL;
343 }
344 memset(Field, 0, Size);
345
346 strcpy(Field->Data, Data);
347
348 /* Append key */
349 if (Line->FirstField == NULL)
350 {
351 Line->FirstField = Field;
352 Line->LastField = Field;
353 }
354 else
355 {
356 Line->LastField->Next = Field;
357 Field->Prev = Line->LastField;
358 Line->LastField = Field;
359 }
360 Line->FieldCount++;
361
362 return (PVOID)Field;
363 }
364
365
366 static
367 PINFCACHELINE
368 InfpCacheFindKeyLine(
369 PINFCACHESECTION Section,
370 PCSTR Key)
371 {
372 PINFCACHELINE Line;
373
374 Line = Section->FirstLine;
375 while (Line != NULL)
376 {
377 if ((Line->Key != NULL) && (_stricmp(Line->Key, Key) == 0))
378 {
379 return Line;
380 }
381
382 Line = Line->Next;
383 }
384
385 return NULL;
386 }
387
388
389 /* push the current state on the parser stack */
390 __inline static void push_state(struct parser *parser, enum parser_state state)
391 {
392 // assert(parser->stack_pos < sizeof(parser->stack)/sizeof(parser->stack[0]));
393 parser->stack[parser->stack_pos++] = state;
394 }
395
396
397 /* pop the current state */
398 __inline static void pop_state(struct parser *parser)
399 {
400 // assert( parser->stack_pos );
401 parser->state = parser->stack[--parser->stack_pos];
402 }
403
404
405 /* set the parser state and return the previous one */
406 __inline static enum parser_state set_state(struct parser *parser, enum parser_state state)
407 {
408 enum parser_state ret = parser->state;
409 parser->state = state;
410 return ret;
411 }
412
413
414 /* check if the pointer points to an end of file */
415 __inline static int is_eof(struct parser *parser, const CHAR *ptr)
416 {
417 return (ptr >= parser->end || *ptr == CONTROL_Z);
418 }
419
420
421 /* check if the pointer points to an end of line */
422 __inline static int is_eol(struct parser *parser, const CHAR *ptr)
423 {
424 return ((ptr >= parser->end) ||
425 (*ptr == CONTROL_Z) ||
426 (*ptr == '\n') ||
427 ((*ptr == '\r') && (*(ptr + 1) == '\n')));
428 }
429
430
431 /* push data from current token start up to pos into the current token */
432 static
433 int
434 push_token(
435 struct parser *parser,
436 const CHAR *pos)
437 {
438 SIZE_T len = pos - parser->start;
439 const CHAR *src = parser->start;
440 CHAR *dst = parser->token + parser->token_len;
441
442 if (len > MAX_FIELD_LEN - parser->token_len)
443 len = MAX_FIELD_LEN - parser->token_len;
444
445 parser->token_len += (ULONG)len;
446 for ( ; len > 0; len--, dst++, src++)
447 *dst = *src ? (CHAR)*src : L' ';
448 *dst = 0;
449 parser->start = pos;
450
451 return 0;
452 }
453
454
455 /* add a section with the current token as name */
456 static
457 PVOID
458 add_section_from_token(struct parser *parser)
459 {
460 PINFCACHESECTION Section;
461
462 if (parser->token_len > MAX_SECTION_NAME_LEN)
463 {
464 parser->error = FALSE;
465 return NULL;
466 }
467
468 Section = InfpCacheFindSection(parser->file, parser->token);
469 if (Section == NULL)
470 {
471 /* need to create a new one */
472 Section = InfpCacheAddSection(parser->file, parser->token);
473 if (Section == NULL)
474 {
475 parser->error = FALSE;
476 return NULL;
477 }
478 }
479
480 parser->token_len = 0;
481 parser->cur_section = Section;
482
483 return (PVOID)Section;
484 }
485
486
487 /* add a field containing the current token to the current line */
488 static
489 struct field*
490 add_field_from_token(
491 struct parser *parser,
492 int is_key)
493 {
494 PVOID field;
495
496 if (!parser->line) /* need to start a new line */
497 {
498 if (parser->cur_section == NULL) /* got a line before the first section */
499 {
500 parser->error = STATUS_WRONG_INF_STYLE;
501 return NULL;
502 }
503
504 parser->line = InfpCacheAddLine(parser->cur_section);
505 if (parser->line == NULL)
506 goto error;
507 }
508 else
509 {
510 // assert(!is_key);
511 }
512
513 if (is_key)
514 {
515 field = InfpAddKeyToLine(parser->line, parser->token);
516 }
517 else
518 {
519 field = InfpAddFieldToLine(parser->line, parser->token);
520 }
521
522 if (field != NULL)
523 {
524 parser->token_len = 0;
525 return field;
526 }
527
528 error:
529 parser->error = FALSE;
530 return NULL;
531 }
532
533
534 /* close the current line and prepare for parsing a new one */
535 static
536 VOID
537 close_current_line(struct parser *parser)
538 {
539 parser->line = NULL;
540 }
541
542
543 /* handler for parser LINE_START state */
544 static
545 const CHAR*
546 line_start_state(
547 struct parser *parser,
548 const CHAR *pos)
549 {
550 const CHAR *p;
551
552 for (p = pos; !is_eof(parser, p); p++)
553 {
554 switch(*p)
555 {
556 case '\r':
557 continue;
558
559 case '\n':
560 parser->line_pos++;
561 close_current_line(parser);
562 break;
563
564 case ';':
565 push_state(parser, LINE_START);
566 set_state(parser, COMMENT);
567 return p + 1;
568
569 case '[':
570 parser->start = p + 1;
571 set_state(parser, SECTION_NAME);
572 return p + 1;
573
574 default:
575 if (!isspace((unsigned char)*p))
576 {
577 parser->start = p;
578 set_state(parser, KEY_NAME);
579 return p;
580 }
581 break;
582 }
583 }
584 close_current_line(parser);
585 return NULL;
586 }
587
588
589 /* handler for parser SECTION_NAME state */
590 static
591 const CHAR*
592 section_name_state(
593 struct parser *parser,
594 const CHAR *pos)
595 {
596 const CHAR *p;
597
598 for (p = pos; !is_eol(parser, p); p++)
599 {
600 if (*p == ']')
601 {
602 push_token(parser, p);
603 if (add_section_from_token(parser) == NULL)
604 return NULL;
605 push_state(parser, LINE_START);
606 set_state(parser, COMMENT); /* ignore everything else on the line */
607 return p + 1;
608 }
609 }
610 parser->error = STATUS_BAD_SECTION_NAME_LINE; /* unfinished section name */
611 return NULL;
612 }
613
614
615 /* handler for parser KEY_NAME state */
616 static
617 const CHAR*
618 key_name_state(
619 struct parser *parser,
620 const CHAR *pos)
621 {
622 const CHAR *p, *token_end = parser->start;
623
624 for (p = pos; !is_eol(parser, p); p++)
625 {
626 if (*p == ',') break;
627 switch(*p)
628 {
629
630 case '=':
631 push_token(parser, token_end);
632 if (!add_field_from_token(parser, 1)) return NULL;
633 parser->start = p + 1;
634 push_state(parser, VALUE_NAME);
635 set_state(parser, LEADING_SPACES);
636 return p + 1;
637 case ';':
638 push_token(parser, token_end);
639 if (!add_field_from_token(parser, 0)) return NULL;
640 push_state(parser, LINE_START);
641 set_state(parser, COMMENT);
642 return p + 1;
643 case '"':
644 push_token(parser, token_end);
645 parser->start = p + 1;
646 push_state(parser, KEY_NAME);
647 set_state(parser, QUOTES);
648 return p + 1;
649 case '\\':
650 push_token(parser, token_end);
651 parser->start = p;
652 push_state(parser, KEY_NAME);
653 set_state(parser, EOL_BACKSLASH);
654 return p;
655 default:
656 if (!isspace((unsigned char)*p)) token_end = p + 1;
657 else
658 {
659 push_token(parser, p);
660 push_state(parser, KEY_NAME);
661 set_state(parser, TRAILING_SPACES);
662 return p;
663 }
664 break;
665 }
666 }
667 push_token(parser, token_end);
668 set_state(parser, VALUE_NAME);
669 return p;
670 }
671
672
673 /* handler for parser VALUE_NAME state */
674 static
675 const CHAR*
676 value_name_state(
677 struct parser *parser,
678 const CHAR *pos)
679 {
680 const CHAR *p, *token_end = parser->start;
681
682 for (p = pos; !is_eol(parser, p); p++)
683 {
684 switch(*p)
685 {
686 case ';':
687 push_token(parser, token_end);
688 if (!add_field_from_token(parser, 0)) return NULL;
689 push_state(parser, LINE_START);
690 set_state(parser, COMMENT);
691 return p + 1;
692 case ',':
693 push_token(parser, token_end);
694 if (!add_field_from_token(parser, 0)) return NULL;
695 parser->start = p + 1;
696 push_state(parser, VALUE_NAME);
697 set_state(parser, LEADING_SPACES);
698 return p + 1;
699 case '"':
700 push_token(parser, token_end);
701 parser->start = p + 1;
702 push_state(parser, VALUE_NAME);
703 set_state(parser, QUOTES);
704 return p + 1;
705 case '\\':
706 push_token(parser, token_end);
707 parser->start = p;
708 push_state(parser, VALUE_NAME);
709 set_state(parser, EOL_BACKSLASH);
710 return p;
711 default:
712 if (!isspace((unsigned char)*p)) token_end = p + 1;
713 else
714 {
715 push_token(parser, p);
716 push_state(parser, VALUE_NAME);
717 set_state(parser, TRAILING_SPACES);
718 return p;
719 }
720 break;
721 }
722 }
723 push_token(parser, token_end);
724 if (!add_field_from_token(parser, 0)) return NULL;
725 set_state(parser, LINE_START);
726 return p;
727 }
728
729
730 /* handler for parser EOL_BACKSLASH state */
731 static
732 const CHAR*
733 eol_backslash_state(
734 struct parser *parser,
735 const CHAR *pos )
736 {
737 const CHAR *p;
738
739 for (p = pos; !is_eof(parser, p); p++)
740 {
741 switch(*p)
742 {
743 case '\r':
744 continue;
745
746 case '\n':
747 parser->line_pos++;
748 parser->start = p + 1;
749 set_state(parser, LEADING_SPACES);
750 return p + 1;
751
752 case '\\':
753 continue;
754
755 case ';':
756 push_state(parser, EOL_BACKSLASH);
757 set_state(parser, COMMENT);
758 return p + 1;
759
760 default:
761 if (isspace((unsigned char)*p))
762 continue;
763 push_token(parser, p);
764 pop_state(parser);
765 return p;
766 }
767 }
768 parser->start = p;
769 pop_state(parser);
770
771 return p;
772 }
773
774
775 /* handler for parser QUOTES state */
776 static
777 const CHAR*
778 quotes_state(
779 struct parser *parser,
780 const CHAR *pos )
781 {
782 const CHAR *p, *token_end = parser->start;
783
784 for (p = pos; !is_eol(parser, p); p++)
785 {
786 if (*p == '"')
787 {
788 if (p + 1 < parser->end && p[1] == '"') /* double quotes */
789 {
790 push_token(parser, p + 1);
791 parser->start = token_end = p + 2;
792 p++;
793 }
794 else /* end of quotes */
795 {
796 push_token(parser, p);
797 parser->start = p + 1;
798 pop_state(parser);
799 return p + 1;
800 }
801 }
802 }
803 push_token(parser, p);
804 pop_state(parser);
805 return p;
806 }
807
808
809 /* handler for parser LEADING_SPACES state */
810 static
811 const CHAR*
812 leading_spaces_state(
813 struct parser *parser,
814 const CHAR *pos )
815 {
816 const CHAR *p;
817
818 for (p = pos; !is_eol(parser, p); p++)
819 {
820 if (*p == '\\')
821 {
822 parser->start = p;
823 set_state(parser, EOL_BACKSLASH);
824 return p;
825 }
826 if (!isspace((unsigned char)*p))
827 break;
828 }
829 parser->start = p;
830 pop_state(parser);
831 return p;
832 }
833
834
835 /* handler for parser TRAILING_SPACES state */
836 static
837 const CHAR*
838 trailing_spaces_state(
839 struct parser *parser,
840 const CHAR *pos )
841 {
842 const CHAR *p;
843
844 for (p = pos; !is_eol(parser, p); p++)
845 {
846 if (*p == '\\')
847 {
848 set_state(parser, EOL_BACKSLASH);
849 return p;
850 }
851 if (!isspace((unsigned char)*p))
852 break;
853 }
854 pop_state(parser);
855 return p;
856 }
857
858
859 /* handler for parser COMMENT state */
860 static
861 const CHAR*
862 comment_state(
863 struct parser *parser,
864 const CHAR *pos )
865 {
866 const CHAR *p = pos;
867
868 while (!is_eol(parser, p))
869 p++;
870 pop_state(parser);
871 return p;
872 }
873
874
875 /* parse a complete buffer */
876 static
877 BOOLEAN
878 InfpParseBuffer (
879 PINFCACHE file,
880 PCCHAR buffer,
881 PCCHAR end,
882 PULONG error_line)
883 {
884 struct parser parser;
885 const CHAR* pos = buffer;
886
887 parser.start = buffer;
888 parser.end = end;
889 parser.file = file;
890 parser.line = NULL;
891 parser.state = LINE_START;
892 parser.stack_pos = 0;
893 parser.cur_section = NULL;
894 parser.line_pos = 1;
895 parser.error = TRUE;
896 parser.token_len = 0;
897
898 /* parser main loop */
899 while (pos)
900 pos = (parser_funcs[parser.state])(&parser, pos);
901
902 if (parser.error)
903 {
904 if (error_line)
905 *error_line = parser.line_pos;
906 return parser.error;
907 }
908
909 /* find the [strings] section */
910 file->StringsSection = InfpCacheFindSection(file, "Strings");
911
912 return TRUE;
913 }
914
915 /* PUBLIC FUNCTIONS *********************************************************/
916
917 BOOLEAN
918 InfOpenFile(
919 PHINF InfHandle,
920 PCSTR FileName,
921 PULONG ErrorLine)
922 {
923 FILEINFORMATION Information;
924 ULONG FileId;
925 PCHAR FileBuffer;
926 ULONG FileSize, Count;
927 PINFCACHE Cache;
928 BOOLEAN Success;
929 ARC_STATUS Status;
930
931 *InfHandle = NULL;
932 *ErrorLine = (ULONG) - 1;
933
934 //
935 // Open the .inf file
936 //
937 Status = ArcOpen((PSTR)FileName, OpenReadOnly, &FileId);
938 if (Status != ESUCCESS)
939 {
940 return FALSE;
941 }
942
943 //
944 // Query file size
945 //
946 Status = ArcGetFileInformation(FileId, &Information);
947 if ((Status != ESUCCESS) || (Information.EndingAddress.HighPart != 0))
948 {
949 ArcClose(FileId);
950 return FALSE;
951 }
952 FileSize = Information.EndingAddress.LowPart;
953
954 //
955 // Allocate buffer to cache the file
956 //
957 FileBuffer = FrLdrTempAlloc(FileSize + 1, TAG_INF_FILE);
958 if (!FileBuffer)
959 {
960 ArcClose(FileId);
961 return FALSE;
962 }
963
964 //
965 // Read file into memory
966 //
967 Status = ArcRead(FileId, FileBuffer, FileSize, &Count);
968 if ((Status != ESUCCESS) || (Count != FileSize))
969 {
970 ArcClose(FileId);
971 FrLdrTempFree(FileBuffer, TAG_INF_FILE);
972 return FALSE;
973 }
974
975 //
976 // We don't need the file anymore. Close it
977 //
978 ArcClose(FileId);
979
980 //
981 // Append string terminator
982 //
983 FileBuffer[FileSize] = 0;
984
985 //
986 // Allocate infcache header
987 //
988 Cache = (PINFCACHE)FrLdrTempAlloc(sizeof(INFCACHE), TAG_INF_CACHE);
989 if (!Cache)
990 {
991 FrLdrTempFree(FileBuffer, TAG_INF_FILE);
992 return FALSE;
993 }
994
995 //
996 // Initialize inicache header
997 //
998 RtlZeroMemory(Cache, sizeof(INFCACHE));
999
1000 //
1001 // Parse the inf buffer
1002 //
1003 Success = InfpParseBuffer(Cache,
1004 FileBuffer,
1005 FileBuffer + FileSize,
1006 ErrorLine);
1007 if (!Success)
1008 {
1009 FrLdrTempFree(Cache, TAG_INF_CACHE);
1010 Cache = NULL;
1011 }
1012
1013 //
1014 // Free file buffer, as it has been parsed
1015 //
1016 FrLdrTempFree(FileBuffer, TAG_INF_FILE);
1017
1018 //
1019 // Return .inf parsed contents
1020 //
1021 *InfHandle = (HINF)Cache;
1022
1023 return Success;
1024 }
1025
1026
1027 VOID
1028 InfCloseFile(HINF InfHandle)
1029 {
1030 PINFCACHE Cache;
1031
1032 Cache = (PINFCACHE)InfHandle;
1033
1034 if (Cache == NULL)
1035 {
1036 return;
1037 }
1038
1039 while (Cache->FirstSection != NULL)
1040 {
1041 Cache->FirstSection = InfpCacheFreeSection(Cache->FirstSection);
1042 }
1043 Cache->LastSection = NULL;
1044
1045 FrLdrTempFree(Cache, TAG_INF_CACHE);
1046 }
1047
1048
1049 BOOLEAN
1050 InfFindFirstLine (
1051 HINF InfHandle,
1052 PCSTR Section,
1053 PCSTR Key,
1054 PINFCONTEXT Context)
1055 {
1056 PINFCACHE Cache;
1057 PINFCACHESECTION CacheSection;
1058 PINFCACHELINE CacheLine;
1059
1060 if ((InfHandle == NULL) || (Section == NULL) || (Context == NULL))
1061 {
1062 // DPRINT("Invalid parameter\n");
1063 return FALSE;
1064 }
1065
1066 Cache = (PINFCACHE)InfHandle;
1067
1068 /* Iterate through list of sections */
1069 CacheSection = Cache->FirstSection;
1070 while (CacheSection != NULL)
1071 {
1072 // DPRINT("Comparing '%s' and '%s'\n", CacheSection->Name, Section);
1073
1074 /* Are the section names the same? */
1075 if (_stricmp(CacheSection->Name, Section) == 0)
1076 {
1077 if (Key != NULL)
1078 {
1079 CacheLine = InfpCacheFindKeyLine(CacheSection, Key);
1080 }
1081 else
1082 {
1083 CacheLine = CacheSection->FirstLine;
1084 }
1085
1086 if (CacheLine == NULL)
1087 return FALSE;
1088
1089 Context->Inf = (PVOID)Cache;
1090 Context->Section = (PVOID)CacheSection;
1091 Context->Line = (PVOID)CacheLine;
1092
1093 return TRUE;
1094 }
1095
1096 /* Get the next section */
1097 CacheSection = CacheSection->Next;
1098 }
1099
1100 // DPRINT("Section not found\n");
1101
1102 return FALSE;
1103 }
1104
1105
1106 BOOLEAN
1107 InfFindNextLine (
1108 PINFCONTEXT ContextIn,
1109 PINFCONTEXT ContextOut)
1110 {
1111 PINFCACHELINE CacheLine;
1112
1113 if ((ContextIn == NULL) || (ContextOut == NULL))
1114 return FALSE;
1115
1116 if (ContextIn->Line == NULL)
1117 return FALSE;
1118
1119 CacheLine = (PINFCACHELINE)ContextIn->Line;
1120 if (CacheLine->Next == NULL)
1121 return FALSE;
1122
1123 if (ContextIn != ContextOut)
1124 {
1125 ContextOut->Inf = ContextIn->Inf;
1126 ContextOut->Section = ContextIn->Section;
1127 }
1128 ContextOut->Line = (PVOID)(CacheLine->Next);
1129
1130 return TRUE;
1131 }
1132
1133
1134 BOOLEAN
1135 InfFindFirstMatchLine (
1136 PINFCONTEXT ContextIn,
1137 PCHAR Key,
1138 PINFCONTEXT ContextOut)
1139 {
1140 PINFCACHELINE CacheLine;
1141
1142 if ((ContextIn == NULL) || (ContextOut == NULL) || (Key == NULL) || (*Key == 0))
1143 return FALSE;
1144
1145 if (ContextIn->Inf == NULL || ContextIn->Section == NULL)
1146 return FALSE;
1147
1148 CacheLine = ((PINFCACHESECTION)(ContextIn->Section))->FirstLine;
1149 while (CacheLine != NULL)
1150 {
1151 if ((CacheLine->Key != NULL) && (_stricmp(CacheLine->Key, Key) == 0))
1152 {
1153
1154 if (ContextIn != ContextOut)
1155 {
1156 ContextOut->Inf = ContextIn->Inf;
1157 ContextOut->Section = ContextIn->Section;
1158 }
1159 ContextOut->Line = (PVOID)CacheLine;
1160
1161 return TRUE;
1162 }
1163
1164 CacheLine = CacheLine->Next;
1165 }
1166
1167 return FALSE;
1168 }
1169
1170
1171 BOOLEAN
1172 InfFindNextMatchLine (
1173 PINFCONTEXT ContextIn,
1174 PCHAR Key,
1175 PINFCONTEXT ContextOut)
1176 {
1177 PINFCACHELINE CacheLine;
1178
1179 if ((ContextIn == NULL) || (ContextOut == NULL) || (Key == NULL) || (*Key == 0))
1180 return FALSE;
1181
1182 if ((ContextIn->Inf == NULL) || (ContextIn->Section == NULL) || (ContextIn->Line == NULL))
1183 return FALSE;
1184
1185 CacheLine = (PINFCACHELINE)ContextIn->Line;
1186 while (CacheLine != NULL)
1187 {
1188 if ((CacheLine->Key != NULL) && (_stricmp(CacheLine->Key, Key) == 0))
1189 {
1190
1191 if (ContextIn != ContextOut)
1192 {
1193 ContextOut->Inf = ContextIn->Inf;
1194 ContextOut->Section = ContextIn->Section;
1195 }
1196 ContextOut->Line = (PVOID)CacheLine;
1197
1198 return TRUE;
1199 }
1200
1201 CacheLine = CacheLine->Next;
1202 }
1203
1204 return FALSE;
1205 }
1206
1207
1208 LONG
1209 InfGetLineCount(
1210 HINF InfHandle,
1211 PCHAR Section)
1212 {
1213 PINFCACHE Cache;
1214 PINFCACHESECTION CacheSection;
1215
1216 if ((InfHandle == NULL) || (Section == NULL))
1217 {
1218 // DPRINT("Invalid parameter\n");
1219 return -1;
1220 }
1221
1222 Cache = (PINFCACHE)InfHandle;
1223
1224 /* Iterate through list of sections */
1225 CacheSection = Cache->FirstSection;
1226 while (CacheSection != NULL)
1227 {
1228 // DPRINT("Comparing '%s' and '%s'\n", CacheSection->Name, Section);
1229
1230 /* Are the section names the same? */
1231 if (_stricmp(CacheSection->Name, Section) == 0)
1232 {
1233 return CacheSection->LineCount;
1234 }
1235
1236 /* Get the next section */
1237 CacheSection = CacheSection->Next;
1238 }
1239
1240 // DPRINT("Section not found\n");
1241
1242 return -1;
1243 }
1244
1245
1246 /* InfGetLineText */
1247
1248
1249 LONG
1250 InfGetFieldCount(PINFCONTEXT Context)
1251 {
1252 if ((Context == NULL) || (Context->Line == NULL))
1253 return 0;
1254
1255 return ((PINFCACHELINE)Context->Line)->FieldCount;
1256 }
1257
1258
1259 BOOLEAN
1260 InfGetBinaryField (
1261 PINFCONTEXT Context,
1262 ULONG FieldIndex,
1263 PUCHAR ReturnBuffer,
1264 ULONG ReturnBufferSize,
1265 PULONG RequiredSize)
1266 {
1267 PINFCACHELINE CacheLine;
1268 PINFCACHEFIELD CacheField;
1269 ULONG Index;
1270 ULONG Size;
1271 PUCHAR Ptr;
1272
1273 if ((Context == NULL) || (Context->Line == NULL) || (FieldIndex == 0))
1274 {
1275 // DPRINT("Invalid parameter\n");
1276 return FALSE;
1277 }
1278
1279 if (RequiredSize != NULL)
1280 *RequiredSize = 0;
1281
1282 CacheLine = (PINFCACHELINE)Context->Line;
1283
1284 if (FieldIndex > CacheLine->FieldCount)
1285 return FALSE;
1286
1287 CacheField = CacheLine->FirstField;
1288 for (Index = 1; Index < FieldIndex; Index++)
1289 CacheField = CacheField->Next;
1290
1291 Size = CacheLine->FieldCount - FieldIndex + 1;
1292
1293 if (RequiredSize != NULL)
1294 *RequiredSize = Size;
1295
1296 if (ReturnBuffer != NULL)
1297 {
1298 if (ReturnBufferSize < Size)
1299 return FALSE;
1300
1301 /* Copy binary data */
1302 Ptr = ReturnBuffer;
1303 while (CacheField != NULL)
1304 {
1305 *Ptr = (UCHAR)atoi(CacheField->Data); //strtoul(CacheField->Data, NULL, 16);
1306
1307 Ptr++;
1308 CacheField = CacheField->Next;
1309 }
1310 }
1311
1312 return TRUE;
1313 }
1314
1315
1316 BOOLEAN
1317 InfGetIntField (
1318 PINFCONTEXT Context,
1319 ULONG FieldIndex,
1320 PLONG IntegerValue)
1321 {
1322 PINFCACHELINE CacheLine;
1323 PINFCACHEFIELD CacheField;
1324 ULONG Index;
1325 PCHAR Ptr;
1326
1327 if ((Context == NULL) || (Context->Line == NULL) || (IntegerValue == NULL))
1328 {
1329 // DPRINT("Invalid parameter\n");
1330 return FALSE;
1331 }
1332
1333 CacheLine = (PINFCACHELINE)Context->Line;
1334
1335 if (FieldIndex > CacheLine->FieldCount)
1336 {
1337 // DPRINT("Invalid parameter\n");
1338 return FALSE;
1339 }
1340
1341 if (FieldIndex == 0)
1342 {
1343 Ptr = CacheLine->Key;
1344 }
1345 else
1346 {
1347 CacheField = CacheLine->FirstField;
1348 for (Index = 1; Index < FieldIndex; Index++)
1349 CacheField = CacheField->Next;
1350
1351 Ptr = CacheField->Data;
1352 }
1353
1354 *IntegerValue = atoi(Ptr); //strtol(Ptr, NULL, 0);
1355
1356 return TRUE;
1357 }
1358
1359
1360 BOOLEAN
1361 InfGetMultiSzField (
1362 PINFCONTEXT Context,
1363 ULONG FieldIndex,
1364 PCHAR ReturnBuffer,
1365 ULONG ReturnBufferSize,
1366 PULONG RequiredSize)
1367 {
1368 PINFCACHELINE CacheLine;
1369 PINFCACHEFIELD CacheField;
1370 PINFCACHEFIELD FieldPtr;
1371 ULONG Index;
1372 SIZE_T Size;
1373 PCHAR Ptr;
1374
1375 if ((Context == NULL) || (Context->Line == NULL) || (FieldIndex == 0))
1376 {
1377 // DPRINT("Invalid parameter\n");
1378 return FALSE;
1379 }
1380
1381 if (RequiredSize != NULL)
1382 *RequiredSize = 0;
1383
1384 CacheLine = (PINFCACHELINE)Context->Line;
1385
1386 if (FieldIndex > CacheLine->FieldCount)
1387 return FALSE;
1388
1389 CacheField = CacheLine->FirstField;
1390 for (Index = 1; Index < FieldIndex; Index++)
1391 CacheField = CacheField->Next;
1392
1393 /* Calculate the required buffer size */
1394 FieldPtr = CacheField;
1395 Size = 0;
1396 while (FieldPtr != NULL)
1397 {
1398 Size += (strlen(FieldPtr->Data) + 1);
1399 FieldPtr = FieldPtr->Next;
1400 }
1401 Size++;
1402
1403 if (RequiredSize != NULL)
1404 *RequiredSize = (ULONG)Size;
1405
1406 if (ReturnBuffer != NULL)
1407 {
1408 if (ReturnBufferSize < Size)
1409 return FALSE;
1410
1411 /* Copy multi-sz string */
1412 Ptr = ReturnBuffer;
1413 FieldPtr = CacheField;
1414 while (FieldPtr != NULL)
1415 {
1416 Size = strlen(FieldPtr->Data) + 1;
1417
1418 strcpy(Ptr, FieldPtr->Data);
1419
1420 Ptr = Ptr + Size;
1421 FieldPtr = FieldPtr->Next;
1422 }
1423 *Ptr = 0;
1424 }
1425
1426 return TRUE;
1427 }
1428
1429
1430 BOOLEAN
1431 InfGetStringField (
1432 PINFCONTEXT Context,
1433 ULONG FieldIndex,
1434 PCHAR ReturnBuffer,
1435 ULONG ReturnBufferSize,
1436 PULONG RequiredSize)
1437 {
1438 PINFCACHELINE CacheLine;
1439 PINFCACHEFIELD CacheField;
1440 ULONG Index;
1441 PCHAR Ptr;
1442 SIZE_T Size;
1443
1444 if ((Context == NULL) || (Context->Line == NULL))
1445 {
1446 // DPRINT("Invalid parameter\n");
1447 return FALSE;
1448 }
1449
1450 if (RequiredSize != NULL)
1451 *RequiredSize = 0;
1452
1453 CacheLine = (PINFCACHELINE)Context->Line;
1454
1455 if (FieldIndex > CacheLine->FieldCount)
1456 return FALSE;
1457
1458 if (FieldIndex == 0)
1459 {
1460 Ptr = CacheLine->Key;
1461 }
1462 else
1463 {
1464 CacheField = CacheLine->FirstField;
1465 for (Index = 1; Index < FieldIndex; Index++)
1466 CacheField = CacheField->Next;
1467
1468 Ptr = CacheField->Data;
1469 }
1470
1471 Size = strlen(Ptr) + 1;
1472
1473 if (RequiredSize != NULL)
1474 *RequiredSize = (ULONG)Size;
1475
1476 if (ReturnBuffer != NULL)
1477 {
1478 if (ReturnBufferSize < Size)
1479 return FALSE;
1480
1481 strcpy(ReturnBuffer, Ptr);
1482 }
1483
1484 return TRUE;
1485 }
1486
1487
1488
1489
1490 BOOLEAN
1491 InfGetData (
1492 PINFCONTEXT Context,
1493 PCHAR *Key,
1494 PCHAR *Data)
1495 {
1496 PINFCACHELINE CacheKey;
1497
1498 if ((Context == NULL) || (Context->Line == NULL) || (Data == NULL))
1499 {
1500 // DPRINT("Invalid parameter\n");
1501 return FALSE;
1502 }
1503
1504 CacheKey = (PINFCACHELINE)Context->Line;
1505 if (Key != NULL)
1506 *Key = CacheKey->Key;
1507
1508 if (Data != NULL)
1509 {
1510 if (CacheKey->FirstField == NULL)
1511 {
1512 *Data = NULL;
1513 }
1514 else
1515 {
1516 *Data = CacheKey->FirstField->Data;
1517 }
1518 }
1519
1520 return TRUE;
1521 }
1522
1523
1524 BOOLEAN
1525 InfGetDataField (
1526 PINFCONTEXT Context,
1527 ULONG FieldIndex,
1528 PCSTR *Data)
1529 {
1530 PINFCACHELINE CacheLine;
1531 PINFCACHEFIELD CacheField;
1532 ULONG Index;
1533
1534 if ((Context == NULL) || (Context->Line == NULL) || (Data == NULL))
1535 {
1536 // DPRINT("Invalid parameter\n");
1537 return FALSE;
1538 }
1539
1540 CacheLine = (PINFCACHELINE)Context->Line;
1541
1542 if (FieldIndex > CacheLine->FieldCount)
1543 return FALSE;
1544
1545 if (FieldIndex == 0)
1546 {
1547 *Data = CacheLine->Key;
1548 }
1549 else
1550 {
1551 CacheField = CacheLine->FirstField;
1552 for (Index = 1; Index < FieldIndex; Index++)
1553 CacheField = CacheField->Next;
1554
1555 *Data = CacheField->Data;
1556 }
1557
1558 return TRUE;
1559 }
1560
1561
1562 /* EOF */