update for HEAD-2003091401
[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 ||
430           *ptr == CONTROL_Z ||
431           *ptr == '\n' ||
432           (*ptr == '\r' && *(ptr + 1) == '\n'));
433 }
434
435
436 /* push data from current token start up to pos into the current token */
437 static int push_token( struct parser *parser, const CHAR *pos )
438 {
439   int len = pos - parser->start;
440   const CHAR *src = parser->start;
441   CHAR *dst = parser->token + parser->token_len;
442
443   if (len > MAX_FIELD_LEN - parser->token_len)
444     len = MAX_FIELD_LEN - parser->token_len;
445
446   parser->token_len += len;
447   for ( ; len > 0; len--, dst++, src++)
448     *dst = *src ? *src : ' ';
449   *dst = 0;
450   parser->start = pos;
451
452   return 0;
453 }
454
455
456
457 /* add a section with the current token as name */
458 static PVOID 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 = STATUS_SECTION_NAME_TOO_LONG;
465       return NULL;
466     }
467
468   Section = InfpCacheFindSection (parser->file,
469                                   parser->token);
470   if (Section == NULL)
471     {
472       /* need to create a new one */
473       Section= InfpCacheAddSection (parser->file,
474                                     parser->token);
475       if (Section == NULL)
476         {
477           parser->error = STATUS_NOT_ENOUGH_MEMORY;
478           return NULL;
479         }
480     }
481
482   parser->token_len = 0;
483   parser->cur_section = Section;
484
485   return (PVOID)Section;
486 }
487
488
489 /* add a field containing the current token to the current line */
490 static struct field *add_field_from_token( struct parser *parser, int is_key )
491 {
492   PVOID field;
493   CHAR *text;
494
495   if (!parser->line)  /* need to start a new line */
496     {
497       if (parser->cur_section == NULL)  /* got a line before the first section */
498         {
499           parser->error = STATUS_WRONG_INF_STYLE;
500           return NULL;
501         }
502
503       parser->line = InfpCacheAddLine (parser->cur_section);
504       if (parser->line == NULL)
505         goto error;
506     }
507   else
508     {
509 //      assert(!is_key);
510     }
511
512   if (is_key)
513     {
514       field = InfpAddKeyToLine(parser->line, parser->token);
515     }
516   else
517     {
518       field = InfpAddFieldToLine(parser->line, parser->token);
519     }
520
521   if (field != NULL)
522     {
523       parser->token_len = 0;
524       return field;
525     }
526
527 error:
528   parser->error = STATUS_NOT_ENOUGH_MEMORY;
529   return NULL;
530 }
531
532
533 /* close the current line and prepare for parsing a new one */
534 static void close_current_line( struct parser *parser )
535 {
536   parser->line = NULL;
537 }
538
539
540
541 /* handler for parser LINE_START state */
542 static const CHAR *line_start_state( struct parser *parser, const CHAR *pos )
543 {
544   const CHAR *p;
545
546   for (p = pos; !is_eof( parser, p ); p++)
547     {
548       switch(*p)
549         {
550           case '\r':
551             continue;
552
553           case '\n':
554             parser->line_pos++;
555             close_current_line( parser );
556             break;
557
558           case ';':
559             push_state( parser, LINE_START );
560             set_state( parser, COMMENT );
561             return p + 1;
562
563           case '[':
564             parser->start = p + 1;
565             set_state( parser, SECTION_NAME );
566             return p + 1;
567
568           default:
569             if (!isspace(*p))
570               {
571                 parser->start = p;
572                 set_state( parser, KEY_NAME );
573                 return p;
574               }
575             break;
576         }
577     }
578   close_current_line( parser );
579   return NULL;
580 }
581
582
583 /* handler for parser SECTION_NAME state */
584 static const CHAR *section_name_state( struct parser *parser, const CHAR *pos )
585 {
586   const CHAR *p;
587
588   for (p = pos; !is_eol( parser, p ); p++)
589     {
590       if (*p == ']')
591         {
592           push_token( parser, p );
593           if (add_section_from_token( parser ) == NULL)
594             return NULL;
595           push_state( parser, LINE_START );
596           set_state( parser, COMMENT );  /* ignore everything else on the line */
597           return p + 1;
598         }
599     }
600   parser->error = STATUS_BAD_SECTION_NAME_LINE; /* unfinished section name */
601   return NULL;
602 }
603
604
605 /* handler for parser KEY_NAME state */
606 static const CHAR *key_name_state( struct parser *parser, const CHAR *pos )
607 {
608     const CHAR *p, *token_end = parser->start;
609
610     for (p = pos; !is_eol( parser, p ); p++)
611     {
612         if (*p == ',') break;
613         switch(*p)
614         {
615
616          case '=':
617             push_token( parser, token_end );
618             if (!add_field_from_token( parser, 1 )) return NULL;
619             parser->start = p + 1;
620             push_state( parser, VALUE_NAME );
621             set_state( parser, LEADING_SPACES );
622             return p + 1;
623         case ';':
624             push_token( parser, token_end );
625             if (!add_field_from_token( parser, 0 )) return NULL;
626             push_state( parser, LINE_START );
627             set_state( parser, COMMENT );
628             return p + 1;
629         case '"':
630             push_token( parser, token_end );
631             parser->start = p + 1;
632             push_state( parser, KEY_NAME );
633             set_state( parser, QUOTES );
634             return p + 1;
635         case '\\':
636             push_token( parser, token_end );
637             parser->start = p;
638             push_state( parser, KEY_NAME );
639             set_state( parser, EOL_BACKSLASH );
640             return p;
641         default:
642             if (!isspace(*p)) token_end = p + 1;
643             else
644             {
645                 push_token( parser, p );
646                 push_state( parser, KEY_NAME );
647                 set_state( parser, TRAILING_SPACES );
648                 return p;
649             }
650             break;
651         }
652     }
653     push_token( parser, token_end );
654     set_state( parser, VALUE_NAME );
655     return p;
656 }
657
658
659 /* handler for parser VALUE_NAME state */
660 static const CHAR *value_name_state( struct parser *parser, const CHAR *pos )
661 {
662     const CHAR *p, *token_end = parser->start;
663
664     for (p = pos; !is_eol( parser, p ); p++)
665     {
666         switch(*p)
667         {
668         case ';':
669             push_token( parser, token_end );
670             if (!add_field_from_token( parser, 0 )) return NULL;
671             push_state( parser, LINE_START );
672             set_state( parser, COMMENT );
673             return p + 1;
674         case ',':
675             push_token( parser, token_end );
676             if (!add_field_from_token( parser, 0 )) return NULL;
677             parser->start = p + 1;
678             push_state( parser, VALUE_NAME );
679             set_state( parser, LEADING_SPACES );
680             return p + 1;
681         case '"':
682             push_token( parser, token_end );
683             parser->start = p + 1;
684             push_state( parser, VALUE_NAME );
685             set_state( parser, QUOTES );
686             return p + 1;
687         case '\\':
688             push_token( parser, token_end );
689             parser->start = p;
690             push_state( parser, VALUE_NAME );
691             set_state( parser, EOL_BACKSLASH );
692             return p;
693         default:
694             if (!isspace(*p)) token_end = p + 1;
695             else
696             {
697                 push_token( parser, p );
698                 push_state( parser, VALUE_NAME );
699                 set_state( parser, TRAILING_SPACES );
700                 return p;
701             }
702             break;
703         }
704     }
705     push_token( parser, token_end );
706     if (!add_field_from_token( parser, 0 )) return NULL;
707     set_state( parser, LINE_START );
708     return p;
709 }
710
711
712 /* handler for parser EOL_BACKSLASH state */
713 static const CHAR *eol_backslash_state( struct parser *parser, const CHAR *pos )
714 {
715   const CHAR *p;
716
717   for (p = pos; !is_eof( parser, p ); p++)
718     {
719       switch(*p)
720         {
721           case '\r':
722             continue;
723
724           case '\n':
725             parser->line_pos++;
726             parser->start = p + 1;
727             set_state( parser, LEADING_SPACES );
728             return p + 1;
729
730           case '\\':
731             continue;
732
733           case ';':
734             push_state( parser, EOL_BACKSLASH );
735             set_state( parser, COMMENT );
736             return p + 1;
737
738           default:
739             if (isspace(*p))
740               continue;
741             push_token( parser, p );
742             pop_state( parser );
743             return p;
744         }
745     }
746   parser->start = p;
747   pop_state( parser );
748
749   return p;
750 }
751
752
753 /* handler for parser QUOTES state */
754 static const CHAR *quotes_state( struct parser *parser, const CHAR *pos )
755 {
756   const CHAR *p, *token_end = parser->start;
757
758   for (p = pos; !is_eol( parser, p ); p++)
759     {
760       if (*p == '"')
761         {
762           if (p+1 < parser->end && p[1] == '"')  /* double quotes */
763             {
764               push_token( parser, p + 1 );
765               parser->start = token_end = p + 2;
766               p++;
767             }
768           else  /* end of quotes */
769             {
770               push_token( parser, p );
771               parser->start = p + 1;
772               pop_state( parser );
773               return p + 1;
774             }
775         }
776     }
777   push_token( parser, p );
778   pop_state( parser );
779   return p;
780 }
781
782
783 /* handler for parser LEADING_SPACES state */
784 static const CHAR *leading_spaces_state( struct parser *parser, const CHAR *pos )
785 {
786   const CHAR *p;
787
788   for (p = pos; !is_eol( parser, p ); p++)
789     {
790       if (*p == '\\')
791         {
792           parser->start = p;
793           set_state( parser, EOL_BACKSLASH );
794           return p;
795         }
796       if (!isspace(*p))
797         break;
798     }
799   parser->start = p;
800   pop_state( parser );
801   return p;
802 }
803
804
805 /* handler for parser TRAILING_SPACES state */
806 static const CHAR *trailing_spaces_state( struct parser *parser, const CHAR *pos )
807 {
808   const CHAR *p;
809
810   for (p = pos; !is_eol( parser, p ); p++)
811     {
812       if (*p == '\\')
813         {
814           set_state( parser, EOL_BACKSLASH );
815           return p;
816         }
817       if (!isspace(*p))
818         break;
819     }
820   pop_state( parser );
821   return p;
822 }
823
824
825 /* handler for parser COMMENT state */
826 static const CHAR *comment_state( struct parser *parser, const CHAR *pos )
827 {
828   const CHAR *p = pos;
829
830   while (!is_eol( parser, p ))
831      p++;
832   pop_state( parser );
833   return p;
834 }
835
836
837 /* parse a complete buffer */
838 static BOOL
839 InfpParseBuffer (PINFCACHE file,
840                  const CHAR *buffer,
841                  const CHAR *end,
842                  PULONG error_line)
843 {
844   struct parser parser;
845   const CHAR *pos = buffer;
846
847   parser.start       = buffer;
848   parser.end         = end;
849   parser.file        = file;
850   parser.line        = NULL;
851   parser.state       = LINE_START;
852   parser.stack_pos   = 0;
853   parser.cur_section = NULL;
854   parser.line_pos    = 1;
855   parser.error       = 0;
856   parser.token_len   = 0;
857
858   /* parser main loop */
859   while (pos)
860     pos = (parser_funcs[parser.state])(&parser, pos);
861
862   if (parser.error)
863     {
864       if (error_line)
865         *error_line = parser.line_pos;
866       return parser.error;
867     }
868
869   /* find the [strings] section */
870   file->StringsSection = InfpCacheFindSection (file,
871                                                "Strings");
872
873   return TRUE;
874 }
875
876
877
878 /* PUBLIC FUNCTIONS *********************************************************/
879
880 BOOL
881 InfOpenFile(PHINF InfHandle,
882             PCHAR FileName,
883             PULONG ErrorLine)
884 {
885   FILE *File;
886   PCHAR FileBuffer;
887   ULONG FileLength;
888   PINFCACHE Cache;
889
890   *InfHandle = NULL;
891   *ErrorLine = (ULONG)-1;
892
893   /* Open the inf file */
894   File = fopen (FileName, "rb");
895   if (File == NULL)
896     {
897       DPRINT("fopen() failed\n");
898       return FALSE;
899     }
900
901   DPRINT("fopen() successful\n");
902
903   /* Query file size */
904   fseek (File, 0, SEEK_END);
905   FileLength = ftell (File);
906   fseek (File, 0, SEEK_SET);
907
908   DPRINT("File size: %lu\n", FileLength);
909
910   /* Allocate file buffer */
911   FileBuffer = malloc (FileLength + 1);
912   if (FileBuffer == NULL)
913     {
914       DPRINT1("malloc() failed\n");
915       fclose (File);
916       return FALSE;
917     }
918
919   /* Read file */
920   if (fread (FileBuffer, FileLength, 1, File) < 0)
921     {
922       DPRINT ("fread() failed\n");
923       fclose (File);
924       free (FileBuffer);
925       return FALSE;
926     }
927
928   fclose (File);
929
930   /* Append string terminator */
931   FileBuffer[FileLength] = 0;
932
933   /* Allocate infcache header */
934   Cache = (PINFCACHE)malloc (sizeof(INFCACHE));
935   if (Cache == NULL)
936     {
937       DPRINT("malloc() failed\n");
938       free (FileBuffer);
939       return FALSE;
940     }
941
942   /* Initialize inicache header */
943   memset (Cache, 0, sizeof(INFCACHE));
944
945   /* Parse the inf buffer */
946   if (!InfpParseBuffer (Cache,
947                         FileBuffer,
948                         FileBuffer + FileLength,
949                         ErrorLine))
950     {
951       free (Cache);
952       free (FileBuffer);
953       return FALSE;
954     }
955
956   /* Free file buffer */
957   free (FileBuffer);
958
959   *InfHandle = (HINF)Cache;
960
961   return TRUE;
962 }
963
964
965 VOID
966 InfCloseFile(HINF InfHandle)
967 {
968   PINFCACHE Cache;
969
970   Cache = (PINFCACHE)InfHandle;
971
972   if (Cache == NULL)
973     {
974       return;
975     }
976
977   while (Cache->FirstSection != NULL)
978     {
979       Cache->FirstSection = InfpCacheFreeSection(Cache->FirstSection);
980     }
981   Cache->LastSection = NULL;
982
983   free (Cache);
984 }
985
986
987 BOOL
988 InfFindFirstLine (HINF InfHandle,
989                   PCHAR Section,
990                   PCHAR Key,
991                   PINFCONTEXT Context)
992 {
993   PINFCACHE Cache;
994   PINFCACHESECTION CacheSection;
995   PINFCACHELINE CacheLine;
996
997   if (InfHandle == NULL || Section == NULL || Context == NULL)
998     {
999       DPRINT("Invalid parameter\n");
1000       return FALSE;
1001     }
1002
1003   Cache = (PINFCACHE)InfHandle;
1004
1005   /* Iterate through list of sections */
1006   CacheSection = Cache->FirstSection;
1007   while (Section != NULL)
1008     {
1009       DPRINT("Comparing '%s' and '%s'\n", CacheSection->Name, Section);
1010
1011       /* Are the section names the same? */
1012       if (strcasecmp(CacheSection->Name, Section) == 0)
1013         {
1014           if (Key != NULL)
1015             {
1016               CacheLine = InfpCacheFindKeyLine (CacheSection, (PCHAR)Key);
1017             }
1018           else
1019             {
1020               CacheLine = CacheSection->FirstLine;
1021             }
1022
1023           if (CacheLine == NULL)
1024             return FALSE;
1025
1026           Context->Inf = (PVOID)Cache;
1027           Context->Section = (PVOID)CacheSection;
1028           Context->Line = (PVOID)CacheLine;
1029
1030           return TRUE;
1031         }
1032
1033       /* Get the next section */
1034       CacheSection = CacheSection->Next;
1035     }
1036
1037   DPRINT("Section not found\n");
1038
1039   return FALSE;
1040 }
1041
1042
1043 BOOL
1044 InfFindNextLine (PINFCONTEXT ContextIn,
1045                  PINFCONTEXT ContextOut)
1046 {
1047   PINFCACHELINE CacheLine;
1048
1049   if (ContextIn == NULL || ContextOut == NULL)
1050     return FALSE;
1051
1052   if (ContextIn->Line == NULL)
1053     return FALSE;
1054
1055   CacheLine = (PINFCACHELINE)ContextIn->Line;
1056   if (CacheLine->Next == NULL)
1057     return FALSE;
1058
1059   if (ContextIn != ContextOut)
1060     {
1061       ContextOut->Inf = ContextIn->Inf;
1062       ContextOut->Section = ContextIn->Section;
1063     }
1064   ContextOut->Line = (PVOID)(CacheLine->Next);
1065
1066   return TRUE;
1067 }
1068
1069
1070 BOOL
1071 InfFindFirstMatchLine (PINFCONTEXT ContextIn,
1072                        PCHAR Key,
1073                        PINFCONTEXT ContextOut)
1074 {
1075   PINFCACHELINE CacheLine;
1076
1077   if (ContextIn == NULL || ContextOut == NULL || Key == NULL || *Key == 0)
1078     return FALSE;
1079
1080   if (ContextIn->Inf == NULL || ContextIn->Section == NULL)
1081     return FALSE;
1082
1083   CacheLine = ((PINFCACHESECTION)(ContextIn->Section))->FirstLine;
1084   while (CacheLine != NULL)
1085     {
1086       if (CacheLine->Key != NULL && strcasecmp (CacheLine->Key, Key) == 0)
1087         {
1088
1089           if (ContextIn != ContextOut)
1090             {
1091               ContextOut->Inf = ContextIn->Inf;
1092               ContextOut->Section = ContextIn->Section;
1093             }
1094           ContextOut->Line = (PVOID)CacheLine;
1095
1096           return TRUE;
1097         }
1098
1099       CacheLine = CacheLine->Next;
1100     }
1101
1102   return FALSE;
1103 }
1104
1105
1106 BOOL
1107 InfFindNextMatchLine (PINFCONTEXT ContextIn,
1108                       PCHAR Key,
1109                       PINFCONTEXT ContextOut)
1110 {
1111   PINFCACHELINE CacheLine;
1112
1113   if (ContextIn == NULL || ContextOut == NULL || Key == NULL || *Key == 0)
1114     return FALSE;
1115
1116   if (ContextIn->Inf == NULL || ContextIn->Section == NULL || ContextIn->Line == NULL)
1117     return FALSE;
1118
1119   CacheLine = (PINFCACHELINE)ContextIn->Line;
1120   while (CacheLine != NULL)
1121     {
1122       if (CacheLine->Key != NULL && strcasecmp (CacheLine->Key, Key) == 0)
1123         {
1124
1125           if (ContextIn != ContextOut)
1126             {
1127               ContextOut->Inf = ContextIn->Inf;
1128               ContextOut->Section = ContextIn->Section;
1129             }
1130           ContextOut->Line = (PVOID)CacheLine;
1131
1132           return TRUE;
1133         }
1134
1135       CacheLine = CacheLine->Next;
1136     }
1137
1138   return FALSE;
1139 }
1140
1141
1142 LONG
1143 InfGetLineCount(HINF InfHandle,
1144                 PCHAR Section)
1145 {
1146   PINFCACHE Cache;
1147   PINFCACHESECTION CacheSection;
1148
1149   if (InfHandle == NULL || Section == NULL)
1150     {
1151       DPRINT("Invalid parameter\n");
1152       return -1;
1153     }
1154
1155   Cache = (PINFCACHE)InfHandle;
1156
1157   /* Iterate through list of sections */
1158   CacheSection = Cache->FirstSection;
1159   while (Section != NULL)
1160     {
1161       DPRINT("Comparing '%s' and '%s'\n", CacheSection->Name, Section);
1162
1163       /* Are the section names the same? */
1164       if (strcasecmp(CacheSection->Name, Section) == 0)
1165         {
1166           return CacheSection->LineCount;
1167         }
1168
1169       /* Get the next section */
1170       CacheSection = CacheSection->Next;
1171     }
1172
1173   DPRINT("Section not found\n");
1174
1175   return -1;
1176 }
1177
1178
1179 /* InfGetLineText */
1180
1181
1182 LONG
1183 InfGetFieldCount(PINFCONTEXT Context)
1184 {
1185   if (Context == NULL || Context->Line == NULL)
1186     return 0;
1187
1188   return ((PINFCACHELINE)Context->Line)->FieldCount;
1189 }
1190
1191
1192 BOOL
1193 InfGetBinaryField (PINFCONTEXT Context,
1194                    ULONG FieldIndex,
1195                    PUCHAR ReturnBuffer,
1196                    ULONG ReturnBufferSize,
1197                    PULONG RequiredSize)
1198 {
1199   PINFCACHELINE CacheLine;
1200   PINFCACHEFIELD CacheField;
1201   ULONG Index;
1202   ULONG Size;
1203   PUCHAR Ptr;
1204
1205   if (Context == NULL || Context->Line == NULL || FieldIndex == 0)
1206     {
1207       DPRINT("Invalid parameter\n");
1208       return FALSE;
1209     }
1210
1211   if (RequiredSize != NULL)
1212     *RequiredSize = 0;
1213
1214   CacheLine = (PINFCACHELINE)Context->Line;
1215
1216   if (FieldIndex > CacheLine->FieldCount)
1217     return FALSE;
1218
1219   CacheField = CacheLine->FirstField;
1220   for (Index = 1; Index < FieldIndex; Index++)
1221     CacheField = CacheField->Next;
1222
1223   Size = CacheLine->FieldCount - FieldIndex + 1;
1224
1225   if (RequiredSize != NULL)
1226     *RequiredSize = Size;
1227
1228   if (ReturnBuffer != NULL)
1229     {
1230       if (ReturnBufferSize < Size)
1231         return FALSE;
1232
1233       /* Copy binary data */
1234       Ptr = ReturnBuffer;
1235       while (CacheField != NULL)
1236         {
1237           *Ptr = (UCHAR)strtoul (CacheField->Data, NULL, 16);
1238
1239           Ptr++;
1240           CacheField = CacheField->Next;
1241         }
1242     }
1243
1244   return TRUE;
1245 }
1246
1247
1248 BOOL
1249 InfGetIntField (PINFCONTEXT Context,
1250                 ULONG FieldIndex,
1251                 PLONG IntegerValue)
1252 {
1253   PINFCACHELINE CacheLine;
1254   PINFCACHEFIELD CacheField;
1255   ULONG Index;
1256   PCHAR Ptr;
1257
1258   if (Context == NULL || Context->Line == NULL || IntegerValue == NULL)
1259     {
1260       DPRINT("Invalid parameter\n");
1261       return FALSE;
1262     }
1263
1264   CacheLine = (PINFCACHELINE)Context->Line;
1265
1266   if (FieldIndex > CacheLine->FieldCount)
1267     {
1268       DPRINT("Invalid parameter\n");
1269       return FALSE;
1270     }
1271
1272   if (FieldIndex == 0)
1273     {
1274       Ptr = CacheLine->Key;
1275     }
1276   else
1277     {
1278       CacheField = CacheLine->FirstField;
1279       for (Index = 1; Index < FieldIndex; Index++)
1280         CacheField = CacheField->Next;
1281
1282       Ptr = CacheField->Data;
1283     }
1284
1285   *IntegerValue = strtol (Ptr, NULL, 0);
1286
1287   return TRUE;
1288 }
1289
1290
1291 BOOL
1292 InfGetMultiSzField (PINFCONTEXT Context,
1293                     ULONG FieldIndex,
1294                     PCHAR ReturnBuffer,
1295                     ULONG ReturnBufferSize,
1296                     PULONG RequiredSize)
1297 {
1298   PINFCACHELINE CacheLine;
1299   PINFCACHEFIELD CacheField;
1300   PINFCACHEFIELD FieldPtr;
1301   ULONG Index;
1302   ULONG Size;
1303   PCHAR Ptr;
1304
1305   if (Context == NULL || Context->Line == NULL || FieldIndex == 0)
1306     {
1307       DPRINT("Invalid parameter\n");
1308       return FALSE;
1309     }
1310
1311   if (RequiredSize != NULL)
1312     *RequiredSize = 0;
1313
1314   CacheLine = (PINFCACHELINE)Context->Line;
1315
1316   if (FieldIndex > CacheLine->FieldCount)
1317     return FALSE;
1318
1319   CacheField = CacheLine->FirstField;
1320   for (Index = 1; Index < FieldIndex; Index++)
1321     CacheField = CacheField->Next;
1322
1323   /* Calculate the required buffer size */
1324   FieldPtr = CacheField;
1325   Size = 0;
1326   while (FieldPtr != NULL)
1327     {
1328       Size += (strlen (FieldPtr->Data) + 1);
1329       FieldPtr = FieldPtr->Next;
1330     }
1331   Size++;
1332
1333   if (RequiredSize != NULL)
1334     *RequiredSize = Size;
1335
1336   if (ReturnBuffer != NULL)
1337     {
1338       if (ReturnBufferSize < Size)
1339         return FALSE;
1340
1341       /* Copy multi-sz string */
1342       Ptr = ReturnBuffer;
1343       FieldPtr = CacheField;
1344       while (FieldPtr != NULL)
1345         {
1346           Size = strlen (FieldPtr->Data) + 1;
1347
1348           strcpy (Ptr, FieldPtr->Data);
1349
1350           Ptr = Ptr + Size;
1351           FieldPtr = FieldPtr->Next;
1352         }
1353       *Ptr = 0;
1354     }
1355
1356   return TRUE;
1357 }
1358
1359
1360 BOOL
1361 InfGetStringField (PINFCONTEXT Context,
1362                    ULONG FieldIndex,
1363                    PCHAR ReturnBuffer,
1364                    ULONG ReturnBufferSize,
1365                    PULONG RequiredSize)
1366 {
1367   PINFCACHELINE CacheLine;
1368   PINFCACHEFIELD CacheField;
1369   ULONG Index;
1370   PCHAR Ptr;
1371   ULONG Size;
1372
1373   if (Context == NULL || Context->Line == NULL || FieldIndex == 0)
1374     {
1375       DPRINT("Invalid parameter\n");
1376       return FALSE;
1377     }
1378
1379   if (RequiredSize != NULL)
1380     *RequiredSize = 0;
1381
1382   CacheLine = (PINFCACHELINE)Context->Line;
1383
1384   if (FieldIndex > CacheLine->FieldCount)
1385     return FALSE;
1386
1387   if (FieldIndex == 0)
1388     {
1389       Ptr = CacheLine->Key;
1390     }
1391   else
1392     {
1393       CacheField = CacheLine->FirstField;
1394       for (Index = 1; Index < FieldIndex; Index++)
1395         CacheField = CacheField->Next;
1396
1397       Ptr = CacheField->Data;
1398     }
1399
1400   Size = strlen (Ptr) + 1;
1401
1402   if (RequiredSize != NULL)
1403     *RequiredSize = Size;
1404
1405   if (ReturnBuffer != NULL)
1406     {
1407       if (ReturnBufferSize < Size)
1408         return FALSE;
1409
1410       strcpy (ReturnBuffer, Ptr);
1411     }
1412
1413   return TRUE;
1414 }
1415
1416
1417
1418
1419 BOOL
1420 InfGetData (PINFCONTEXT Context,
1421             PCHAR *Key,
1422             PCHAR *Data)
1423 {
1424   PINFCACHELINE CacheKey;
1425
1426   if (Context == NULL || Context->Line == NULL || Data == NULL)
1427     {
1428       DPRINT("Invalid parameter\n");
1429       return FALSE;
1430     }
1431
1432   CacheKey = (PINFCACHELINE)Context->Line;
1433   if (Key != NULL)
1434     *Key = CacheKey->Key;
1435
1436   if (Data != NULL)
1437     {
1438       if (CacheKey->FirstField == NULL)
1439         {
1440           *Data = NULL;
1441         }
1442       else
1443         {
1444           *Data = CacheKey->FirstField->Data;
1445         }
1446     }
1447
1448   return TRUE;
1449 }
1450
1451
1452 BOOL
1453 InfGetDataField (PINFCONTEXT Context,
1454                  ULONG FieldIndex,
1455                  PCHAR *Data)
1456 {
1457   PINFCACHELINE CacheLine;
1458   PINFCACHEFIELD CacheField;
1459   ULONG Index;
1460
1461   if (Context == NULL || Context->Line == NULL || Data == NULL)
1462     {
1463       DPRINT("Invalid parameter\n");
1464       return FALSE;
1465     }
1466
1467   CacheLine = (PINFCACHELINE)Context->Line;
1468
1469   if (FieldIndex > CacheLine->FieldCount)
1470     return FALSE;
1471
1472   if (FieldIndex == 0)
1473     {
1474       *Data = CacheLine->Key;
1475     }
1476   else
1477     {
1478       CacheField = CacheLine->FirstField;
1479       for (Index = 1; Index < FieldIndex; Index++)
1480         CacheField = CacheField->Next;
1481
1482       *Data = CacheField->Data;
1483     }
1484
1485   return TRUE;
1486 }
1487
1488
1489 /* EOF */