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