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