xref: /reactos/boot/freeldr/freeldr/ntldr/inffile.c (revision eeb4cbcb)
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
InfpCacheFreeLine(PINFCACHELINE Line)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
InfpCacheFreeSection(PINFCACHESECTION Section)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
InfpCacheFindSection(PINFCACHE Cache,PCSTR Name)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
InfpCacheAddSection(PINFCACHE Cache,PCSTR Name)226 InfpCacheAddSection(
227     PINFCACHE Cache,
228     PCSTR 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
InfpCacheAddLine(PINFCACHESECTION Section)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
InfpAddKeyToLine(PINFCACHELINE Line,PCSTR Key)309 InfpAddKeyToLine(
310     PINFCACHELINE Line,
311     PCSTR 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
InfpAddFieldToLine(PINFCACHELINE Line,PCSTR Data)331 InfpAddFieldToLine(
332     PINFCACHELINE Line,
333     PCSTR 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
InfpCacheFindKeyLine(PINFCACHESECTION Section,PCSTR Key)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 */
push_state(struct parser * parser,enum parser_state state)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 */
pop_state(struct parser * parser)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 */
set_state(struct parser * parser,enum parser_state state)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 */
is_eof(struct parser * parser,const CHAR * ptr)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 */
is_eol(struct parser * parser,const CHAR * ptr)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
push_token(struct parser * parser,const CHAR * pos)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
add_section_from_token(struct parser * parser)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*
add_field_from_token(struct parser * parser,int is_key)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
close_current_line(struct parser * parser)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*
line_start_state(struct parser * parser,const CHAR * pos)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*
section_name_state(struct parser * parser,const CHAR * pos)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*
key_name_state(struct parser * parser,const CHAR * pos)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*
value_name_state(struct parser * parser,const CHAR * pos)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*
eol_backslash_state(struct parser * parser,const CHAR * pos)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*
quotes_state(struct parser * parser,const CHAR * pos)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*
leading_spaces_state(struct parser * parser,const CHAR * pos)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*
trailing_spaces_state(struct parser * parser,const CHAR * pos)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*
comment_state(struct parser * parser,const CHAR * pos)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
InfpParseBuffer(PINFCACHE file,PCCHAR buffer,PCCHAR end,PULONG error_line)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
InfOpenFile(PHINF InfHandle,PCSTR FileName,PULONG ErrorLine)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
InfCloseFile(HINF InfHandle)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
InfFindFirstLine(HINF InfHandle,PCSTR Section,PCSTR Key,PINFCONTEXT Context)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
InfFindNextLine(PINFCONTEXT ContextIn,PINFCONTEXT ContextOut)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
InfFindFirstMatchLine(PINFCONTEXT ContextIn,PCSTR Key,PINFCONTEXT ContextOut)1135 InfFindFirstMatchLine (
1136     PINFCONTEXT ContextIn,
1137     PCSTR 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
InfFindNextMatchLine(PINFCONTEXT ContextIn,PCSTR Key,PINFCONTEXT ContextOut)1172 InfFindNextMatchLine (
1173     PINFCONTEXT ContextIn,
1174     PCSTR 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
InfGetLineCount(HINF InfHandle,PCSTR Section)1209 InfGetLineCount(
1210     HINF InfHandle,
1211     PCSTR 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
InfGetFieldCount(PINFCONTEXT Context)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
InfGetBinaryField(PINFCONTEXT Context,ULONG FieldIndex,PUCHAR ReturnBuffer,ULONG ReturnBufferSize,PULONG RequiredSize)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
InfGetIntField(PINFCONTEXT Context,ULONG FieldIndex,PLONG IntegerValue)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
InfGetMultiSzField(PINFCONTEXT Context,ULONG FieldIndex,PCHAR ReturnBuffer,ULONG ReturnBufferSize,PULONG RequiredSize)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
InfGetStringField(PINFCONTEXT Context,ULONG FieldIndex,PCHAR ReturnBuffer,ULONG ReturnBufferSize,PULONG RequiredSize)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
InfGetData(PINFCONTEXT Context,PCSTR * Key,PCSTR * Data)1491 InfGetData (
1492     PINFCONTEXT Context,
1493     PCSTR *Key,
1494     PCSTR *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     *Data = (CacheKey->FirstField == NULL) ? NULL : CacheKey->FirstField->Data;
1509 
1510     return TRUE;
1511 }
1512 
1513 
1514 BOOLEAN
InfGetDataField(PINFCONTEXT Context,ULONG FieldIndex,PCSTR * Data)1515 InfGetDataField (
1516     PINFCONTEXT Context,
1517     ULONG FieldIndex,
1518     PCSTR *Data)
1519 {
1520     PINFCACHELINE CacheLine;
1521     PINFCACHEFIELD CacheField;
1522     ULONG Index;
1523 
1524     if ((Context == NULL) || (Context->Line == NULL) || (Data == NULL))
1525     {
1526 //      DPRINT("Invalid parameter\n");
1527         return FALSE;
1528     }
1529 
1530     CacheLine = (PINFCACHELINE)Context->Line;
1531 
1532     if (FieldIndex > CacheLine->FieldCount)
1533         return FALSE;
1534 
1535     if (FieldIndex == 0)
1536     {
1537         *Data = CacheLine->Key;
1538     }
1539     else
1540     {
1541         CacheField = CacheLine->FirstField;
1542         for (Index = 1; Index < FieldIndex; Index++)
1543             CacheField = CacheField->Next;
1544 
1545         *Data = CacheField->Data;
1546     }
1547 
1548     return TRUE;
1549 }
1550 
1551 
1552 /* EOF */
1553