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