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