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