4 * Copyright 2002 Alexandre Julliard for CodeWeavers
5 * 2005-2006 Hervé Poussineau (hpoussin@reactos.org)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "setupapi_private.h"
24 #include <ndk/obfuncs.h>
26 /* Unicode constants */
27 static const WCHAR BackSlash
[] = {'\\',0};
28 static const WCHAR Class
[] = {'C','l','a','s','s',0};
29 static const WCHAR ClassGUID
[] = {'C','l','a','s','s','G','U','I','D',0};
30 static const WCHAR InfDirectory
[] = {'i','n','f','\\',0};
31 static const WCHAR InfFileSpecification
[] = {'*','.','i','n','f',0};
33 #define CONTROL_Z '\x1a'
34 #define MAX_SECTION_NAME_LEN 255
35 #define MAX_FIELD_LEN 511 /* larger fields get silently truncated */
36 /* actual string limit is MAX_INF_STRING_LENGTH+1 (plus terminating null) under Windows */
37 #define MAX_STRING_LEN (MAX_INF_STRING_LENGTH+1)
39 /* inf file structure definitions */
43 const WCHAR
*text
; /* field text */
48 int first_field
; /* index of first field in field array */
49 int nb_fields
; /* number of fields in line */
50 int key_field
; /* index of field for key or -1 if no key */
55 const WCHAR
*name
; /* section name */
56 unsigned int nb_lines
; /* number of used lines */
57 unsigned int alloc_lines
; /* total number of allocated lines in array below */
58 struct line lines
[16]; /* lines information (grown dynamically, 16 is initial size) */
63 struct inf_file
*next
; /* next appended file */
64 WCHAR
*strings
; /* buffer for string data (section names and field values) */
65 WCHAR
*string_pos
; /* position of next available string in buffer */
66 unsigned int nb_sections
; /* number of used sections */
67 unsigned int alloc_sections
; /* total number of allocated section pointers */
68 struct section
**sections
; /* section pointers array */
69 unsigned int nb_fields
;
70 unsigned int alloc_fields
;
72 int strings_section
; /* index of [Strings] section or -1 if none */
73 WCHAR
*filename
; /* filename of the INF */
76 /* parser definitions */
80 LINE_START
, /* at beginning of a line */
81 SECTION_NAME
, /* parsing a section name */
82 KEY_NAME
, /* parsing a key name */
83 VALUE_NAME
, /* parsing a value name */
84 EOL_BACKSLASH
, /* backslash at end of line */
85 QUOTES
, /* inside quotes */
86 LEADING_SPACES
, /* leading spaces */
87 TRAILING_SPACES
, /* trailing spaces */
88 COMMENT
, /* inside a comment */
94 const WCHAR
*start
; /* start position of item being parsed */
95 const WCHAR
*end
; /* end of buffer */
96 struct inf_file
*file
; /* file being built */
97 enum parser_state state
; /* current parser state */
98 enum parser_state stack
[4]; /* state stack */
99 int stack_pos
; /* current pos in stack */
101 int cur_section
; /* index of section being parsed*/
102 struct line
*line
; /* current line */
103 unsigned int line_pos
; /* current line position in file */
104 unsigned int error
; /* error code */
105 unsigned int token_len
; /* current token len */
106 WCHAR token
[MAX_FIELD_LEN
+1]; /* current token */
109 typedef const WCHAR
* (*parser_state_func
)( struct parser
*parser
, const WCHAR
*pos
);
111 /* parser state machine functions */
112 static const WCHAR
*line_start_state( struct parser
*parser
, const WCHAR
*pos
);
113 static const WCHAR
*section_name_state( struct parser
*parser
, const WCHAR
*pos
);
114 static const WCHAR
*key_name_state( struct parser
*parser
, const WCHAR
*pos
);
115 static const WCHAR
*value_name_state( struct parser
*parser
, const WCHAR
*pos
);
116 static const WCHAR
*eol_backslash_state( struct parser
*parser
, const WCHAR
*pos
);
117 static const WCHAR
*quotes_state( struct parser
*parser
, const WCHAR
*pos
);
118 static const WCHAR
*leading_spaces_state( struct parser
*parser
, const WCHAR
*pos
);
119 static const WCHAR
*trailing_spaces_state( struct parser
*parser
, const WCHAR
*pos
);
120 static const WCHAR
*comment_state( struct parser
*parser
, const WCHAR
*pos
);
122 static const parser_state_func parser_funcs
[NB_PARSER_STATES
] =
124 line_start_state
, /* LINE_START */
125 section_name_state
, /* SECTION_NAME */
126 key_name_state
, /* KEY_NAME */
127 value_name_state
, /* VALUE_NAME */
128 eol_backslash_state
, /* EOL_BACKSLASH */
129 quotes_state
, /* QUOTES */
130 leading_spaces_state
, /* LEADING_SPACES */
131 trailing_spaces_state
, /* TRAILING_SPACES */
132 comment_state
/* COMMENT */
136 /* Unicode string constants */
137 static const WCHAR Version
[] = {'V','e','r','s','i','o','n',0};
138 static const WCHAR Signature
[] = {'S','i','g','n','a','t','u','r','e',0};
139 static const WCHAR Chicago
[] = {'$','C','h','i','c','a','g','o','$',0};
140 static const WCHAR WindowsNT
[] = {'$','W','i','n','d','o','w','s',' ','N','T','$',0};
141 static const WCHAR Windows95
[] = {'$','W','i','n','d','o','w','s',' ','9','5','$',0};
142 static const WCHAR LayoutFile
[] = {'L','a','y','o','u','t','F','i','l','e',0};
144 /* extend an array, allocating more memory if necessary */
145 static void *grow_array( void *array
, unsigned int *count
, size_t elem
)
148 unsigned int new_count
= *count
+ *count
/ 2;
149 if (new_count
< 32) new_count
= 32;
152 new_array
= HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, array
, new_count
* elem
);
154 new_array
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, new_count
* elem
);
159 HeapFree( GetProcessHeap(), 0, array
);
164 /* get the directory of the inf file (as counted string, not null-terminated) */
165 static const WCHAR
*get_inf_dir( const struct inf_file
*file
, unsigned int *len
)
167 const WCHAR
*p
= strrchrW( file
->filename
, '\\' );
168 *len
= p
? (p
+ 1 - file
->filename
) : 0;
169 return file
->filename
;
173 /* find a section by name */
174 static int find_section( const struct inf_file
*file
, const WCHAR
*name
)
178 for (i
= 0; i
< file
->nb_sections
; i
++)
179 if (!strcmpiW( name
, file
->sections
[i
]->name
)) return i
;
184 /* find a line by name */
185 static struct line
*find_line( struct inf_file
*file
, int section_index
, const WCHAR
*name
)
187 struct section
*section
;
191 if (section_index
< 0 || section_index
>= file
->nb_sections
) return NULL
;
192 section
= file
->sections
[section_index
];
193 for (i
= 0, line
= section
->lines
; i
< section
->nb_lines
; i
++, line
++)
195 if (line
->key_field
== -1) continue;
196 if (!strcmpiW( name
, file
->fields
[line
->key_field
].text
)) return line
;
202 /* add a section to the file and return the section index */
203 static int add_section( struct inf_file
*file
, const WCHAR
*name
)
205 struct section
*section
;
207 if (file
->nb_sections
>= file
->alloc_sections
)
209 if (!(file
->sections
= grow_array( file
->sections
, &file
->alloc_sections
,
210 sizeof(file
->sections
[0]) ))) return -1;
212 if (!(section
= HeapAlloc( GetProcessHeap(), 0, sizeof(*section
) ))) return -1;
213 section
->name
= name
;
214 section
->nb_lines
= 0;
215 section
->alloc_lines
= sizeof(section
->lines
)/sizeof(section
->lines
[0]);
216 file
->sections
[file
->nb_sections
] = section
;
217 return file
->nb_sections
++;
221 /* add a line to a given section */
222 static struct line
*add_line( struct inf_file
*file
, int section_index
)
224 struct section
*section
;
227 ASSERT( section_index
>= 0 && section_index
< file
->nb_sections
);
229 section
= file
->sections
[section_index
];
230 if (section
->nb_lines
== section
->alloc_lines
) /* need to grow the section */
232 int size
= sizeof(*section
) - sizeof(section
->lines
) + 2*section
->alloc_lines
*sizeof(*line
);
233 if (!(section
= HeapReAlloc( GetProcessHeap(), 0, section
, size
))) return NULL
;
234 section
->alloc_lines
*= 2;
235 file
->sections
[section_index
] = section
;
237 line
= §ion
->lines
[section
->nb_lines
++];
238 line
->first_field
= file
->nb_fields
;
240 line
->key_field
= -1;
245 /* retrieve a given line from section/line index */
246 static inline struct line
*get_line( struct inf_file
*file
, unsigned int section_index
,
247 unsigned int line_index
)
249 struct section
*section
;
251 if (section_index
>= file
->nb_sections
) return NULL
;
252 section
= file
->sections
[section_index
];
253 if (line_index
>= section
->nb_lines
) return NULL
;
254 return §ion
->lines
[line_index
];
258 /* retrieve a given field from section/line/field index */
259 static struct field
*get_field( struct inf_file
*file
, int section_index
, int line_index
,
262 struct line
*line
= get_line( file
, section_index
, line_index
);
264 if (!line
) return NULL
;
265 if (!field_index
) /* get the key */
267 if (line
->key_field
== -1) return NULL
;
268 return &file
->fields
[line
->key_field
];
271 if (field_index
>= line
->nb_fields
) return NULL
;
272 return &file
->fields
[line
->first_field
+ field_index
];
276 /* allocate a new field, growing the array if necessary */
277 static struct field
*add_field( struct inf_file
*file
, const WCHAR
*text
)
281 if (file
->nb_fields
>= file
->alloc_fields
)
283 if (!(file
->fields
= grow_array( file
->fields
, &file
->alloc_fields
,
284 sizeof(file
->fields
[0]) ))) return NULL
;
286 field
= &file
->fields
[file
->nb_fields
++];
292 /* retrieve the string substitution for a directory id */
293 static const WCHAR
*get_dirid_subst( const struct inf_file
*file
, int dirid
, unsigned int *len
)
297 if (dirid
== DIRID_SRCPATH
) return get_inf_dir( file
, len
);
298 ret
= DIRID_get_string( dirid
);
299 if (ret
) *len
= strlenW(ret
);
304 /* retrieve the string substitution for a given string, or NULL if not found */
305 /* if found, len is set to the substitution length */
306 static const WCHAR
*get_string_subst( const struct inf_file
*file
, const WCHAR
*str
, unsigned int *len
,
307 BOOL no_trailing_slash
)
309 static const WCHAR percent
= '%';
311 struct section
*strings_section
;
316 WCHAR
*dirid_str
, *end
;
317 const WCHAR
*ret
= NULL
;
318 WCHAR StringLangId
[13] = {'S','t','r','i','n','g','s','.',0};
321 if (!*len
) /* empty string (%%) is replaced by single percent */
326 if (file
->strings_section
== -1) goto not_found
;
327 strings_section
= file
->sections
[file
->strings_section
];
328 for (j
= 0, line
= strings_section
->lines
; j
< strings_section
->nb_lines
; j
++, line
++)
330 if (line
->key_field
== -1) continue;
331 if (strncmpiW( str
, file
->fields
[line
->key_field
].text
, *len
)) continue;
332 if (!file
->fields
[line
->key_field
].text
[*len
]) break;
334 if (j
== strings_section
->nb_lines
|| !line
->nb_fields
) goto not_found
;
335 field
= &file
->fields
[line
->first_field
];
336 GetLocaleInfo(LOCALE_SYSTEM_DEFAULT
, LOCALE_ILANGUAGE
, Lang
, sizeof(Lang
)/sizeof(TCHAR
)); // get the current system locale for translated strings
337 strcatW(StringLangId
, Lang
); // append the Language identifier from GetLocaleInfo
338 // now you have e.g. Strings.0407 for german translations
339 for (i
= 0; i
< file
->nb_sections
; i
++) // search in all sections
341 if (!strcmpiW(file
->sections
[i
]->name
,StringLangId
)) // if the section is a Strings.* section
343 strings_section
= file
->sections
[i
]; // select this section for further use
344 for (j
= 0, line
= strings_section
->lines
; j
< strings_section
->nb_lines
; j
++, line
++) // process all lines in this section
346 if (line
->key_field
== -1) continue; // if no key then skip
347 if (strncmpiW( str
, file
->fields
[line
->key_field
].text
, *len
)) continue; // if wrong key name, then skip
348 if (!file
->fields
[line
->key_field
].text
[*len
]) // if value exist
350 field
= &file
->fields
[line
->first_field
]; // then extract value and
351 break; // no more search necessary
356 *len
= strlenW( field
->text
); // set length
357 ret
= field
->text
; // return the english or translated string
361 not_found
: /* check for integer id */
362 if ((dirid_str
= HeapAlloc( GetProcessHeap(), 0, (*len
+1) * sizeof(WCHAR
) )))
364 memcpy( dirid_str
, str
, *len
* sizeof(WCHAR
) );
366 dirid
= strtolW( dirid_str
, &end
, 10 );
367 if (!*end
) ret
= get_dirid_subst( file
, dirid
, len
);
368 if (no_trailing_slash
&& ret
&& *len
&& ret
[*len
- 1] == '\\') *len
-= 1;
369 HeapFree( GetProcessHeap(), 0, dirid_str
);
376 /* do string substitutions on the specified text */
377 /* the buffer is assumed to be large enough */
378 /* returns necessary length not including terminating null */
379 unsigned int PARSER_string_substW( const struct inf_file
*file
, const WCHAR
*text
, WCHAR
*buffer
,
382 const WCHAR
*start
, *subst
, *p
;
383 unsigned int len
, total
= 0;
386 if (!buffer
) size
= MAX_STRING_LEN
+ 1;
387 for (p
= start
= text
; *p
; p
++)
389 if (*p
!= '%') continue;
391 if (inside
) /* start of a %xx% string */
394 if (len
> size
- 1) len
= size
- 1;
395 if (buffer
) memcpy( buffer
+ total
, start
, len
* sizeof(WCHAR
) );
400 else /* end of the %xx% string, find substitution */
403 subst
= get_string_subst( file
, start
+ 1, &len
, p
[1] == '\\' );
409 if (len
> size
- 1) len
= size
- 1;
410 if (buffer
) memcpy( buffer
+ total
, subst
, len
* sizeof(WCHAR
) );
417 if (start
!= p
) /* unfinished string, copy it */
420 if (len
> size
- 1) len
= size
- 1;
421 if (buffer
) memcpy( buffer
+ total
, start
, len
* sizeof(WCHAR
) );
424 if (buffer
&& size
) buffer
[total
] = 0;
429 /* do string substitutions on the specified text */
430 /* the buffer is assumed to be large enough */
431 /* returns necessary length not including terminating null */
432 unsigned int PARSER_string_substA( const struct inf_file
*file
, const WCHAR
*text
, char *buffer
,
435 WCHAR buffW
[MAX_STRING_LEN
+1];
438 unsigned int len
= PARSER_string_substW( file
, text
, buffW
, sizeof(buffW
)/sizeof(WCHAR
) );
439 if (!buffer
) RtlUnicodeToMultiByteSize( &ret
, buffW
, len
* sizeof(WCHAR
) );
442 RtlUnicodeToMultiByteN( buffer
, size
-1, &ret
, buffW
, len
* sizeof(WCHAR
) );
449 /* push some string data into the strings buffer */
450 static WCHAR
*push_string( struct inf_file
*file
, const WCHAR
*string
)
452 WCHAR
*ret
= file
->string_pos
;
453 strcpyW( ret
, string
);
454 file
->string_pos
+= strlenW( ret
) + 1;
459 /* push the current state on the parser stack */
460 static inline void push_state( struct parser
*parser
, enum parser_state state
)
462 ASSERT( parser
->stack_pos
< sizeof(parser
->stack
)/sizeof(parser
->stack
[0]) );
463 parser
->stack
[parser
->stack_pos
++] = state
;
467 /* pop the current state */
468 static inline void pop_state( struct parser
*parser
)
470 ASSERT( parser
->stack_pos
);
471 parser
->state
= parser
->stack
[--parser
->stack_pos
];
475 /* set the parser state and return the previous one */
476 static inline enum parser_state
set_state( struct parser
*parser
, enum parser_state state
)
478 enum parser_state ret
= parser
->state
;
479 parser
->state
= state
;
484 /* check if the pointer points to an end of file */
485 static inline int is_eof( const struct parser
*parser
, const WCHAR
*ptr
)
487 return (ptr
>= parser
->end
|| *ptr
== CONTROL_Z
);
491 /* check if the pointer points to an end of line */
492 static inline int is_eol( const struct parser
*parser
, const WCHAR
*ptr
)
494 return (ptr
>= parser
->end
|| *ptr
== CONTROL_Z
|| *ptr
== '\n');
498 /* push data from current token start up to pos into the current token */
499 static int push_token( struct parser
*parser
, const WCHAR
*pos
)
501 int len
= pos
- parser
->start
;
502 const WCHAR
*src
= parser
->start
;
503 WCHAR
*dst
= parser
->token
+ parser
->token_len
;
505 if (len
> MAX_FIELD_LEN
- parser
->token_len
) len
= MAX_FIELD_LEN
- parser
->token_len
;
507 parser
->token_len
+= len
;
508 for ( ; len
> 0; len
--, dst
++, src
++) *dst
= *src
? *src
: ' ';
515 /* add a section with the current token as name */
516 static int add_section_from_token( struct parser
*parser
)
520 if (parser
->token_len
> MAX_SECTION_NAME_LEN
)
522 parser
->error
= ERROR_SECTION_NAME_TOO_LONG
;
525 if ((section_index
= find_section( parser
->file
, parser
->token
)) == -1)
527 /* need to create a new one */
528 const WCHAR
*name
= push_string( parser
->file
, parser
->token
);
529 if ((section_index
= add_section( parser
->file
, name
)) == -1)
531 parser
->error
= ERROR_NOT_ENOUGH_MEMORY
;
535 parser
->token_len
= 0;
536 parser
->cur_section
= section_index
;
537 return section_index
;
541 /* add a field containing the current token to the current line */
542 static struct field
*add_field_from_token( struct parser
*parser
, int is_key
)
547 if (!parser
->line
) /* need to start a new line */
549 if (parser
->cur_section
== -1) /* got a line before the first section */
551 parser
->error
= ERROR_EXPECTED_SECTION_NAME
;
554 if (!(parser
->line
= add_line( parser
->file
, parser
->cur_section
))) goto error
;
556 else ASSERT(!is_key
);
558 text
= push_string( parser
->file
, parser
->token
);
559 if ((field
= add_field( parser
->file
, text
)))
561 if (!is_key
) parser
->line
->nb_fields
++;
564 /* replace first field by key field */
565 parser
->line
->key_field
= parser
->line
->first_field
;
566 parser
->line
->first_field
++;
568 parser
->token_len
= 0;
572 parser
->error
= ERROR_NOT_ENOUGH_MEMORY
;
577 /* close the current line and prepare for parsing a new one */
578 static void close_current_line( struct parser
*parser
)
580 struct line
*cur_line
= parser
->line
;
584 /* if line has a single field and no key, the field is the key too */
585 if (cur_line
->nb_fields
== 1 && cur_line
->key_field
== -1)
586 cur_line
->key_field
= cur_line
->first_field
;
592 /* handler for parser LINE_START state */
593 static const WCHAR
*line_start_state( struct parser
*parser
, const WCHAR
*pos
)
597 for (p
= pos
; !is_eof( parser
, p
); p
++)
603 close_current_line( parser
);
606 push_state( parser
, LINE_START
);
607 set_state( parser
, COMMENT
);
610 parser
->start
= p
+ 1;
611 set_state( parser
, SECTION_NAME
);
617 set_state( parser
, KEY_NAME
);
623 close_current_line( parser
);
628 /* handler for parser SECTION_NAME state */
629 static const WCHAR
*section_name_state( struct parser
*parser
, const WCHAR
*pos
)
633 for (p
= pos
; !is_eol( parser
, p
); p
++)
637 push_token( parser
, p
);
638 if (add_section_from_token( parser
) == -1) return NULL
;
639 push_state( parser
, LINE_START
);
640 set_state( parser
, COMMENT
); /* ignore everything else on the line */
644 parser
->error
= ERROR_BAD_SECTION_NAME_LINE
; /* unfinished section name */
649 /* handler for parser KEY_NAME state */
650 static const WCHAR
*key_name_state( struct parser
*parser
, const WCHAR
*pos
)
652 const WCHAR
*p
, *token_end
= parser
->start
;
654 for (p
= pos
; !is_eol( parser
, p
); p
++)
656 if (*p
== ',') break;
661 push_token( parser
, token_end
);
662 if (!add_field_from_token( parser
, 1 )) return NULL
;
663 parser
->start
= p
+ 1;
664 push_state( parser
, VALUE_NAME
);
665 set_state( parser
, LEADING_SPACES
);
668 push_token( parser
, token_end
);
669 if (!add_field_from_token( parser
, 0 )) return NULL
;
670 push_state( parser
, LINE_START
);
671 set_state( parser
, COMMENT
);
674 push_token( parser
, p
);
675 parser
->start
= p
+ 1;
676 push_state( parser
, KEY_NAME
);
677 set_state( parser
, QUOTES
);
680 push_token( parser
, token_end
);
682 push_state( parser
, KEY_NAME
);
683 set_state( parser
, EOL_BACKSLASH
);
686 if (!isspaceW(*p
)) token_end
= p
+ 1;
689 push_token( parser
, p
);
690 push_state( parser
, KEY_NAME
);
691 set_state( parser
, TRAILING_SPACES
);
697 push_token( parser
, token_end
);
698 set_state( parser
, VALUE_NAME
);
703 /* handler for parser VALUE_NAME state */
704 static const WCHAR
*value_name_state( struct parser
*parser
, const WCHAR
*pos
)
706 const WCHAR
*p
, *token_end
= parser
->start
;
708 for (p
= pos
; !is_eol( parser
, p
); p
++)
713 push_token( parser
, token_end
);
714 if (!add_field_from_token( parser
, 0 )) return NULL
;
715 push_state( parser
, LINE_START
);
716 set_state( parser
, COMMENT
);
719 push_token( parser
, token_end
);
720 if (!add_field_from_token( parser
, 0 )) return NULL
;
721 parser
->start
= p
+ 1;
722 push_state( parser
, VALUE_NAME
);
723 set_state( parser
, LEADING_SPACES
);
726 push_token( parser
, p
);
727 parser
->start
= p
+ 1;
728 push_state( parser
, VALUE_NAME
);
729 set_state( parser
, QUOTES
);
732 push_token( parser
, token_end
);
734 push_state( parser
, VALUE_NAME
);
735 set_state( parser
, EOL_BACKSLASH
);
738 if (!isspaceW(*p
)) token_end
= p
+ 1;
741 push_token( parser
, p
);
742 push_state( parser
, VALUE_NAME
);
743 set_state( parser
, TRAILING_SPACES
);
749 push_token( parser
, token_end
);
750 if (!add_field_from_token( parser
, 0 )) return NULL
;
751 set_state( parser
, LINE_START
);
756 /* handler for parser EOL_BACKSLASH state */
757 static const WCHAR
*eol_backslash_state( struct parser
*parser
, const WCHAR
*pos
)
761 for (p
= pos
; !is_eof( parser
, p
); p
++)
767 parser
->start
= p
+ 1;
768 set_state( parser
, LEADING_SPACES
);
773 push_state( parser
, EOL_BACKSLASH
);
774 set_state( parser
, COMMENT
);
777 if (isspaceW(*p
)) continue;
778 push_token( parser
, p
);
789 /* handler for parser QUOTES state */
790 static const WCHAR
*quotes_state( struct parser
*parser
, const WCHAR
*pos
)
792 const WCHAR
*p
, *token_end
= parser
->start
;
794 for (p
= pos
; !is_eol( parser
, p
); p
++)
798 if (p
+1 < parser
->end
&& p
[1] == '"') /* double quotes */
800 push_token( parser
, p
+ 1 );
801 parser
->start
= token_end
= p
+ 2;
804 else /* end of quotes */
806 push_token( parser
, p
);
807 parser
->start
= p
+ 1;
813 push_token( parser
, p
);
819 /* handler for parser LEADING_SPACES state */
820 static const WCHAR
*leading_spaces_state( struct parser
*parser
, const WCHAR
*pos
)
824 for (p
= pos
; !is_eol( parser
, p
); p
++)
829 set_state( parser
, EOL_BACKSLASH
);
832 if (!isspaceW(*p
)) break;
840 /* handler for parser TRAILING_SPACES state */
841 static const WCHAR
*trailing_spaces_state( struct parser
*parser
, const WCHAR
*pos
)
845 for (p
= pos
; !is_eol( parser
, p
); p
++)
849 set_state( parser
, EOL_BACKSLASH
);
852 if (!isspaceW(*p
)) break;
859 /* handler for parser COMMENT state */
860 static const WCHAR
*comment_state( struct parser
*parser
, const WCHAR
*pos
)
862 const WCHAR
*p
= pos
;
864 while (!is_eol( parser
, p
)) p
++;
870 /* parse a complete buffer */
871 static DWORD
parse_buffer( struct inf_file
*file
, const WCHAR
*buffer
, const WCHAR
*end
,
874 static const WCHAR Strings
[] = {'S','t','r','i','n','g','s',0};
876 struct parser parser
;
877 const WCHAR
*pos
= buffer
;
879 parser
.start
= buffer
;
883 parser
.state
= LINE_START
;
884 parser
.stack_pos
= 0;
885 parser
.cur_section
= -1;
888 parser
.token_len
= 0;
890 /* parser main loop */
891 while (pos
) pos
= (parser_funcs
[parser
.state
])( &parser
, pos
);
893 /* trim excess buffer space */
894 if (file
->alloc_sections
> file
->nb_sections
)
896 file
->sections
= HeapReAlloc( GetProcessHeap(), 0, file
->sections
,
897 file
->nb_sections
* sizeof(file
->sections
[0]) );
898 file
->alloc_sections
= file
->nb_sections
;
900 if (file
->alloc_fields
> file
->nb_fields
)
902 file
->fields
= HeapReAlloc( GetProcessHeap(), 0, file
->fields
,
903 file
->nb_fields
* sizeof(file
->fields
[0]) );
904 file
->alloc_fields
= file
->nb_fields
;
906 file
->strings
= HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY
, file
->strings
,
907 (file
->string_pos
- file
->strings
) * sizeof(WCHAR
) );
911 if (error_line
) *error_line
= parser
.line_pos
;
915 /* find the [strings] section */
916 file
->strings_section
= find_section( file
, Strings
);
921 /* append a child INF file to its parent list, in a thread-safe manner */
922 static void append_inf_file( struct inf_file
*parent
, struct inf_file
*child
)
924 struct inf_file
**ppnext
= &parent
->next
;
929 struct inf_file
*next
= InterlockedCompareExchangePointer( (void **)ppnext
, child
, NULL
);
931 ppnext
= &next
->next
;
936 /***********************************************************************
941 static struct inf_file
*parse_file( HANDLE handle
, UINT
*error_line
, DWORD style
)
945 struct inf_file
*file
;
947 DWORD size
= GetFileSize( handle
, NULL
);
948 HANDLE mapping
= CreateFileMappingW( handle
, NULL
, PAGE_READONLY
, 0, size
, NULL
);
949 if (!mapping
) return NULL
;
950 buffer
= MapViewOfFile( mapping
, FILE_MAP_READ
, 0, 0, size
);
952 if (!buffer
) return NULL
;
954 if (!(file
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*file
) )))
956 err
= ERROR_NOT_ENOUGH_MEMORY
;
960 /* we won't need more strings space than the size of the file,
961 * so we can preallocate it here
963 if (!(file
->strings
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) )))
965 err
= ERROR_NOT_ENOUGH_MEMORY
;
968 file
->string_pos
= file
->strings
;
969 file
->strings_section
= -1;
971 if (!RtlIsTextUnicode( buffer
, size
, NULL
))
973 static const BYTE utf8_bom
[3] = { 0xef, 0xbb, 0xbf };
975 UINT codepage
= CP_ACP
;
978 if (size
> sizeof(utf8_bom
) && !memcmp( buffer
, utf8_bom
, sizeof(utf8_bom
) ))
981 offset
= sizeof(utf8_bom
);
984 if ((new_buff
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) )))
986 DWORD len
= MultiByteToWideChar( codepage
, 0, (char *)buffer
+ offset
,
987 size
- offset
, new_buff
, size
);
988 err
= parse_buffer( file
, new_buff
, new_buff
+ len
, error_line
);
989 HeapFree( GetProcessHeap(), 0, new_buff
);
994 WCHAR
*new_buff
= (WCHAR
*)buffer
;
995 /* UCS-16 files should start with the Unicode BOM; we should skip it */
996 if (*new_buff
== 0xfeff)
998 err
= parse_buffer( file
, new_buff
, (WCHAR
*)((char *)buffer
+ size
), error_line
);
1001 if (!err
) /* now check signature */
1003 int version_index
= find_section( file
, Version
);
1004 if (version_index
!= -1)
1006 struct line
*line
= find_line( file
, version_index
, Signature
);
1007 if (line
&& line
->nb_fields
> 0)
1009 struct field
*field
= file
->fields
+ line
->first_field
;
1010 if (!strcmpiW( field
->text
, Chicago
)) goto done
;
1011 if (!strcmpiW( field
->text
, WindowsNT
)) goto done
;
1012 if (!strcmpiW( field
->text
, Windows95
)) goto done
;
1015 if (error_line
) *error_line
= 0;
1016 if (style
& INF_STYLE_WIN4
) err
= ERROR_WRONG_INF_STYLE
;
1020 UnmapViewOfFile( buffer
);
1023 HeapFree( GetProcessHeap(), 0, file
);
1024 SetLastError( err
);
1031 /***********************************************************************
1032 * PARSER_get_inf_filename
1034 * Retrieve the filename of an inf file.
1036 const WCHAR
*PARSER_get_inf_filename( HINF hinf
)
1038 struct inf_file
*file
= hinf
;
1039 return file
->filename
;
1043 /***********************************************************************
1044 * PARSER_get_src_root
1046 * Retrieve the source directory of an inf file.
1048 WCHAR
*PARSER_get_src_root( HINF hinf
)
1051 const WCHAR
*dir
= get_inf_dir( hinf
, &len
);
1052 WCHAR
*ret
= HeapAlloc( GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) );
1055 memcpy( ret
, dir
, len
* sizeof(WCHAR
) );
1062 /***********************************************************************
1063 * PARSER_get_dest_dir
1065 * retrieve a destination dir of the form "dirid,relative_path" in the given entry.
1066 * returned buffer must be freed by caller.
1068 WCHAR
*PARSER_get_dest_dir( INFCONTEXT
*context
)
1076 if (!SetupGetIntField( context
, 1, &dirid
)) return NULL
;
1077 if (!(dir
= get_dirid_subst( context
->Inf
, dirid
, &len1
))) return NULL
;
1078 if (!SetupGetStringFieldW( context
, 2, NULL
, 0, &len2
)) len2
= 0;
1079 if (!(ret
= HeapAlloc( GetProcessHeap(), 0, (len1
+len2
+1) * sizeof(WCHAR
) ))) return NULL
;
1080 memcpy( ret
, dir
, len1
* sizeof(WCHAR
) );
1082 if (len2
&& ptr
> ret
&& ptr
[-1] != '\\') *ptr
++ = '\\';
1083 if (!SetupGetStringFieldW( context
, 2, ptr
, len2
, NULL
)) *ptr
= 0;
1088 /***********************************************************************
1089 * SetupOpenInfFileA (SETUPAPI.@)
1091 HINF WINAPI
SetupOpenInfFileA( PCSTR name
, PCSTR
class, DWORD style
, UINT
*error
)
1093 UNICODE_STRING nameW
, classW
;
1094 HINF ret
= INVALID_HANDLE_VALUE
;
1096 classW
.Buffer
= NULL
;
1097 if (class && !RtlCreateUnicodeStringFromAsciiz( &classW
, class ))
1099 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
1102 if (RtlCreateUnicodeStringFromAsciiz( &nameW
, name
))
1104 ret
= SetupOpenInfFileW( nameW
.Buffer
, classW
.Buffer
, style
, error
);
1105 RtlFreeUnicodeString( &nameW
);
1107 RtlFreeUnicodeString( &classW
);
1113 PARSER_GetInfClassW(
1115 OUT LPGUID ClassGuid
,
1116 OUT PWSTR ClassName
,
1117 IN DWORD ClassNameSize
,
1118 OUT PDWORD RequiredSize OPTIONAL
)
1121 WCHAR guidW
[MAX_GUID_STRING_LEN
+ 1];
1124 /* Read class Guid */
1125 if (!SetupGetLineTextW(NULL
, hInf
, Version
, ClassGUID
, guidW
, sizeof(guidW
), NULL
))
1127 guidW
[37] = '\0'; /* Replace the } by a NULL character */
1128 if (UuidFromStringW(&guidW
[1], ClassGuid
) != RPC_S_OK
)
1131 /* Read class name */
1132 ret
= SetupGetLineTextW(NULL
, hInf
, Version
, Class
, ClassName
, ClassNameSize
, &requiredSize
);
1133 if (ret
&& ClassName
== NULL
&& ClassNameSize
== 0)
1136 *RequiredSize
= requiredSize
;
1137 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1143 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
1146 *RequiredSize
= requiredSize
;
1149 else if (!SetupDiClassNameFromGuidW(ClassGuid
, ClassName
, ClassNameSize
, &requiredSize
))
1151 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
1154 *RequiredSize
= requiredSize
;
1157 /* Return a NULL class name */
1160 if (ClassNameSize
< 1)
1162 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1165 memcpy(ClassGuid
, &GUID_NULL
, sizeof(GUID
));
1166 *ClassName
= UNICODE_NULL
;
1173 TRACE("Returning %d\n", ret
);
1178 /***********************************************************************
1179 * SetupOpenInfFileW (SETUPAPI.@)
1181 HINF WINAPI
SetupOpenInfFileW( PCWSTR name
, PCWSTR
class, DWORD style
, UINT
*error
)
1183 struct inf_file
*file
= NULL
;
1188 TRACE("%s %s %lx %p\n", debugstr_w(name
), debugstr_w(class), style
, error
);
1190 if (style
& ~(INF_STYLE_OLDNT
| INF_STYLE_WIN4
))
1192 SetLastError(ERROR_INVALID_PARAMETER
);
1193 return (HINF
)INVALID_HANDLE_VALUE
;
1196 if (strchrW( name
, '\\' ) || strchrW( name
, '/' ))
1198 if (!(len
= GetFullPathNameW( name
, 0, NULL
, NULL
))) return INVALID_HANDLE_VALUE
;
1199 if (!(path
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
1201 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
1202 return INVALID_HANDLE_VALUE
;
1204 GetFullPathNameW( name
, len
, path
, NULL
);
1205 handle
= CreateFileW( path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0 );
1207 else /* try Windows directory */
1209 static const WCHAR Inf
[] = {'\\','i','n','f','\\',0};
1210 static const WCHAR System32
[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
1212 len
= GetWindowsDirectoryW( NULL
, 0 ) + strlenW(name
) + 12;
1213 if (!(path
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
1215 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
1216 return INVALID_HANDLE_VALUE
;
1218 GetWindowsDirectoryW( path
, len
);
1219 p
= path
+ strlenW(path
);
1222 handle
= CreateFileW( path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0 );
1223 if (handle
== INVALID_HANDLE_VALUE
)
1225 strcpyW( p
, System32
);
1227 handle
= CreateFileW( path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0 );
1231 if (handle
!= INVALID_HANDLE_VALUE
)
1233 file
= parse_file( handle
, error
, style
);
1234 CloseHandle( handle
);
1238 HeapFree( GetProcessHeap(), 0, path
);
1239 return INVALID_HANDLE_VALUE
;
1241 TRACE( "%s -> %p\n", debugstr_w(path
), file
);
1242 file
->filename
= path
;
1247 LPWSTR ClassName
= HeapAlloc(GetProcessHeap(), 0, (strlenW(class) + 1) * sizeof(WCHAR
));
1250 /* Not enough memory */
1251 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1252 SetupCloseInfFile((HINF
)file
);
1253 return INVALID_HANDLE_VALUE
;
1255 else if (!PARSER_GetInfClassW((HINF
)file
, &ClassGuid
, ClassName
, strlenW(class) + 1, NULL
))
1257 /* Unable to get class name in .inf file */
1258 HeapFree(GetProcessHeap(), 0, ClassName
);
1259 SetLastError(ERROR_CLASS_MISMATCH
);
1260 SetupCloseInfFile((HINF
)file
);
1261 return INVALID_HANDLE_VALUE
;
1263 else if (strcmpW(class, ClassName
) != 0)
1265 /* Provided name name is not the expected one */
1266 HeapFree(GetProcessHeap(), 0, ClassName
);
1267 SetLastError(ERROR_CLASS_MISMATCH
);
1268 SetupCloseInfFile((HINF
)file
);
1269 return INVALID_HANDLE_VALUE
;
1271 HeapFree(GetProcessHeap(), 0, ClassName
);
1279 /***********************************************************************
1280 * SetupOpenAppendInfFileA (SETUPAPI.@)
1282 BOOL WINAPI
SetupOpenAppendInfFileA( PCSTR name
, HINF parent_hinf
, UINT
*error
)
1286 if (!name
) return SetupOpenAppendInfFileW( NULL
, parent_hinf
, error
);
1287 child_hinf
= SetupOpenInfFileA( name
, NULL
, INF_STYLE_WIN4
, error
);
1288 if (child_hinf
== INVALID_HANDLE_VALUE
) return FALSE
;
1289 append_inf_file( parent_hinf
, child_hinf
);
1290 TRACE( "%p: appended %s (%p)\n", parent_hinf
, debugstr_a(name
), child_hinf
);
1295 /***********************************************************************
1296 * SetupOpenAppendInfFileW (SETUPAPI.@)
1298 BOOL WINAPI
SetupOpenAppendInfFileW( PCWSTR name
, HINF parent_hinf
, UINT
*error
)
1305 WCHAR filename
[MAX_PATH
];
1308 if (!SetupFindFirstLineW( parent_hinf
, Version
, LayoutFile
, &context
)) return FALSE
;
1309 while (SetupGetStringFieldW( &context
, idx
++, filename
,
1310 sizeof(filename
)/sizeof(WCHAR
), NULL
))
1312 child_hinf
= SetupOpenInfFileW( filename
, NULL
, INF_STYLE_WIN4
, error
);
1313 if (child_hinf
== INVALID_HANDLE_VALUE
) return FALSE
;
1314 append_inf_file( parent_hinf
, child_hinf
);
1315 TRACE( "%p: appended %s (%p)\n", parent_hinf
, debugstr_w(filename
), child_hinf
);
1319 child_hinf
= SetupOpenInfFileW( name
, NULL
, INF_STYLE_WIN4
, error
);
1320 if (child_hinf
== INVALID_HANDLE_VALUE
) return FALSE
;
1321 append_inf_file( parent_hinf
, child_hinf
);
1322 TRACE( "%p: appended %s (%p)\n", parent_hinf
, debugstr_w(name
), child_hinf
);
1327 /***********************************************************************
1328 * SetupOpenMasterInf (SETUPAPI.@)
1330 HINF WINAPI
SetupOpenMasterInf( VOID
)
1332 static const WCHAR Layout
[] = {'\\','i','n','f','\\', 'l', 'a', 'y', 'o', 'u', 't', '.', 'i', 'n', 'f', 0};
1333 WCHAR Buffer
[MAX_PATH
];
1335 GetWindowsDirectoryW( Buffer
, MAX_PATH
);
1336 strcatW( Buffer
, Layout
);
1337 return SetupOpenInfFileW( Buffer
, NULL
, INF_STYLE_WIN4
, NULL
);
1342 /***********************************************************************
1343 * SetupCloseInfFile (SETUPAPI.@)
1345 void WINAPI
SetupCloseInfFile( HINF hinf
)
1347 struct inf_file
*file
= hinf
;
1350 if (!hinf
|| (hinf
== INVALID_HANDLE_VALUE
)) return;
1352 for (i
= 0; i
< file
->nb_sections
; i
++) HeapFree( GetProcessHeap(), 0, file
->sections
[i
] );
1353 HeapFree( GetProcessHeap(), 0, file
->filename
);
1354 HeapFree( GetProcessHeap(), 0, file
->sections
);
1355 HeapFree( GetProcessHeap(), 0, file
->fields
);
1356 HeapFree( GetProcessHeap(), 0, file
->strings
);
1357 HeapFree( GetProcessHeap(), 0, file
);
1361 /***********************************************************************
1362 * SetupEnumInfSectionsA (SETUPAPI.@)
1364 BOOL WINAPI
SetupEnumInfSectionsA( HINF hinf
, UINT index
, PSTR buffer
, DWORD size
, DWORD
*need
)
1366 struct inf_file
*file
= hinf
;
1368 for (file
= hinf
; file
; file
= file
->next
)
1370 if (index
< file
->nb_sections
)
1372 DWORD len
= WideCharToMultiByte( CP_ACP
, 0, file
->sections
[index
]->name
, -1,
1373 NULL
, 0, NULL
, NULL
);
1374 if (need
) *need
= len
;
1377 if (!size
) return TRUE
;
1378 SetLastError( ERROR_INVALID_USER_BUFFER
);
1383 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1386 WideCharToMultiByte( CP_ACP
, 0, file
->sections
[index
]->name
, -1, buffer
, size
, NULL
, NULL
);
1389 index
-= file
->nb_sections
;
1391 SetLastError( ERROR_NO_MORE_ITEMS
);
1396 /***********************************************************************
1397 * SetupEnumInfSectionsW (SETUPAPI.@)
1399 BOOL WINAPI
SetupEnumInfSectionsW( HINF hinf
, UINT index
, PWSTR buffer
, DWORD size
, DWORD
*need
)
1401 struct inf_file
*file
= hinf
;
1403 for (file
= hinf
; file
; file
= file
->next
)
1405 if (index
< file
->nb_sections
)
1407 DWORD len
= strlenW( file
->sections
[index
]->name
) + 1;
1408 if (need
) *need
= len
;
1411 if (!size
) return TRUE
;
1412 SetLastError( ERROR_INVALID_USER_BUFFER
);
1417 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1420 memcpy( buffer
, file
->sections
[index
]->name
, len
* sizeof(WCHAR
) );
1423 index
-= file
->nb_sections
;
1425 SetLastError( ERROR_NO_MORE_ITEMS
);
1430 /***********************************************************************
1431 * SetupGetLineCountA (SETUPAPI.@)
1433 LONG WINAPI
SetupGetLineCountA( HINF hinf
, PCSTR name
)
1435 UNICODE_STRING sectionW
;
1438 if (!RtlCreateUnicodeStringFromAsciiz( §ionW
, name
))
1439 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
1442 ret
= SetupGetLineCountW( hinf
, sectionW
.Buffer
);
1443 RtlFreeUnicodeString( §ionW
);
1449 /***********************************************************************
1450 * SetupGetLineCountW (SETUPAPI.@)
1452 LONG WINAPI
SetupGetLineCountW( HINF hinf
, PCWSTR section
)
1454 struct inf_file
*file
= hinf
;
1458 for (file
= hinf
; file
; file
= file
->next
)
1460 if ((section_index
= find_section( file
, section
)) == -1) continue;
1461 if (ret
== -1) ret
= 0;
1462 ret
+= file
->sections
[section_index
]->nb_lines
;
1464 TRACE( "(%p,%s) returning %d\n", hinf
, debugstr_w(section
), ret
);
1465 SetLastError( (ret
== -1) ? ERROR_SECTION_NOT_FOUND
: 0 );
1470 /***********************************************************************
1471 * SetupGetLineByIndexA (SETUPAPI.@)
1473 BOOL WINAPI
SetupGetLineByIndexA( HINF hinf
, PCSTR section
, DWORD index
, INFCONTEXT
*context
)
1475 UNICODE_STRING sectionW
;
1478 if (!RtlCreateUnicodeStringFromAsciiz( §ionW
, section
))
1479 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
1482 ret
= SetupGetLineByIndexW( hinf
, sectionW
.Buffer
, index
, context
);
1483 RtlFreeUnicodeString( §ionW
);
1489 /***********************************************************************
1490 * SetupGetLineByIndexW (SETUPAPI.@)
1492 BOOL WINAPI
SetupGetLineByIndexW( HINF hinf
, PCWSTR section
, DWORD index
, INFCONTEXT
*context
)
1494 struct inf_file
*file
= hinf
;
1497 for (file
= hinf
; file
; file
= file
->next
)
1499 if ((section_index
= find_section( file
, section
)) == -1) continue;
1500 if (index
< file
->sections
[section_index
]->nb_lines
)
1502 context
->Inf
= hinf
;
1503 context
->CurrentInf
= file
;
1504 context
->Section
= section_index
;
1505 context
->Line
= index
;
1507 TRACE( "(%p,%s): returning %d/%d\n",
1508 hinf
, debugstr_w(section
), section_index
, index
);
1511 index
-= file
->sections
[section_index
]->nb_lines
;
1513 TRACE( "(%p,%s) not found\n", hinf
, debugstr_w(section
) );
1514 SetLastError( ERROR_LINE_NOT_FOUND
);
1519 /***********************************************************************
1520 * SetupFindFirstLineA (SETUPAPI.@)
1522 BOOL WINAPI
SetupFindFirstLineA( HINF hinf
, PCSTR section
, PCSTR key
, INFCONTEXT
*context
)
1524 UNICODE_STRING sectionW
, keyW
;
1527 if (!RtlCreateUnicodeStringFromAsciiz( §ionW
, section
))
1529 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
1533 if (!key
) ret
= SetupFindFirstLineW( hinf
, sectionW
.Buffer
, NULL
, context
);
1536 if (RtlCreateUnicodeStringFromAsciiz( &keyW
, key
))
1538 ret
= SetupFindFirstLineW( hinf
, sectionW
.Buffer
, keyW
.Buffer
, context
);
1539 RtlFreeUnicodeString( &keyW
);
1541 else SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
1543 RtlFreeUnicodeString( §ionW
);
1548 /***********************************************************************
1549 * SetupFindFirstLineW (SETUPAPI.@)
1551 BOOL WINAPI
SetupFindFirstLineW( HINF hinf
, PCWSTR section
, PCWSTR key
, INFCONTEXT
*context
)
1553 struct inf_file
*file
;
1556 for (file
= hinf
; file
; file
= file
->next
)
1558 if ((section_index
= find_section( file
, section
)) == -1) continue;
1563 ctx
.CurrentInf
= file
;
1564 ctx
.Section
= section_index
;
1566 return SetupFindNextMatchLineW( &ctx
, key
, context
);
1568 if (file
->sections
[section_index
]->nb_lines
)
1570 context
->Inf
= hinf
;
1571 context
->CurrentInf
= file
;
1572 context
->Section
= section_index
;
1575 TRACE( "(%p,%s,%s): returning %d/0\n",
1576 hinf
, debugstr_w(section
), debugstr_w(key
), section_index
);
1580 TRACE( "(%p,%s,%s): not found\n", hinf
, debugstr_w(section
), debugstr_w(key
) );
1581 SetLastError( ERROR_LINE_NOT_FOUND
);
1586 /***********************************************************************
1587 * SetupFindNextLine (SETUPAPI.@)
1589 BOOL WINAPI
SetupFindNextLine( PINFCONTEXT context_in
, PINFCONTEXT context_out
)
1591 struct inf_file
*file
= context_in
->CurrentInf
;
1592 struct section
*section
;
1594 if (context_in
->Section
>= file
->nb_sections
) goto error
;
1596 section
= file
->sections
[context_in
->Section
];
1597 if (context_in
->Line
+1 < section
->nb_lines
)
1599 if (context_out
!= context_in
) *context_out
= *context_in
;
1600 context_out
->Line
++;
1605 /* now search the appended files */
1607 for (file
= file
->next
; file
; file
= file
->next
)
1609 int section_index
= find_section( file
, section
->name
);
1610 if (section_index
== -1) continue;
1611 if (file
->sections
[section_index
]->nb_lines
)
1613 context_out
->Inf
= context_in
->Inf
;
1614 context_out
->CurrentInf
= file
;
1615 context_out
->Section
= section_index
;
1616 context_out
->Line
= 0;
1622 SetLastError( ERROR_LINE_NOT_FOUND
);
1627 /***********************************************************************
1628 * SetupFindNextMatchLineA (SETUPAPI.@)
1630 BOOL WINAPI
SetupFindNextMatchLineA( PINFCONTEXT context_in
, PCSTR key
,
1631 PINFCONTEXT context_out
)
1633 UNICODE_STRING keyW
;
1636 if (!key
) return SetupFindNextLine( context_in
, context_out
);
1638 if (!RtlCreateUnicodeStringFromAsciiz( &keyW
, key
))
1639 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
1642 ret
= SetupFindNextMatchLineW( context_in
, keyW
.Buffer
, context_out
);
1643 RtlFreeUnicodeString( &keyW
);
1649 /***********************************************************************
1650 * SetupFindNextMatchLineW (SETUPAPI.@)
1652 BOOL WINAPI
SetupFindNextMatchLineW( PINFCONTEXT context_in
, PCWSTR key
,
1653 PINFCONTEXT context_out
)
1655 struct inf_file
*file
= context_in
->CurrentInf
;
1656 struct section
*section
;
1660 if (!key
) return SetupFindNextLine( context_in
, context_out
);
1662 if (context_in
->Section
>= file
->nb_sections
) goto error
;
1664 section
= file
->sections
[context_in
->Section
];
1666 for (i
= context_in
->Line
+1, line
= §ion
->lines
[i
]; i
< section
->nb_lines
; i
++, line
++)
1668 if (line
->key_field
== -1) continue;
1669 if (!strcmpiW( key
, file
->fields
[line
->key_field
].text
))
1671 if (context_out
!= context_in
) *context_out
= *context_in
;
1672 context_out
->Line
= i
;
1674 TRACE( "(%p,%s,%s): returning %d\n",
1675 file
, debugstr_w(section
->name
), debugstr_w(key
), i
);
1680 /* now search the appended files */
1682 for (file
= file
->next
; file
; file
= file
->next
)
1684 int section_index
= find_section( file
, section
->name
);
1685 if (section_index
== -1) continue;
1686 section
= file
->sections
[section_index
];
1687 for (i
= 0, line
= section
->lines
; i
< section
->nb_lines
; i
++, line
++)
1689 if (line
->key_field
== -1) continue;
1690 if (!strcmpiW( key
, file
->fields
[line
->key_field
].text
))
1692 context_out
->Inf
= context_in
->Inf
;
1693 context_out
->CurrentInf
= file
;
1694 context_out
->Section
= section_index
;
1695 context_out
->Line
= i
;
1697 TRACE( "(%p,%s,%s): returning %d/%d\n",
1698 file
, debugstr_w(section
->name
), debugstr_w(key
), section_index
, i
);
1703 TRACE( "(%p,%s,%s): not found\n",
1704 context_in
->CurrentInf
, debugstr_w(section
->name
), debugstr_w(key
) );
1706 SetLastError( ERROR_LINE_NOT_FOUND
);
1711 /***********************************************************************
1712 * SetupGetLineTextW (SETUPAPI.@)
1714 BOOL WINAPI
SetupGetLineTextW( PINFCONTEXT context
, HINF hinf
, PCWSTR section_name
,
1715 PCWSTR key_name
, PWSTR buffer
, DWORD size
, PDWORD required
)
1717 struct inf_file
*file
;
1719 struct field
*field
;
1725 INFCONTEXT new_context
;
1726 if (!SetupFindFirstLineW( hinf
, section_name
, key_name
, &new_context
)) return FALSE
;
1727 file
= new_context
.CurrentInf
;
1728 line
= get_line( file
, new_context
.Section
, new_context
.Line
);
1732 file
= context
->CurrentInf
;
1733 if (!(line
= get_line( file
, context
->Section
, context
->Line
)))
1735 SetLastError( ERROR_LINE_NOT_FOUND
);
1740 for (i
= 0, field
= &file
->fields
[line
->first_field
]; i
< line
->nb_fields
; i
++, field
++)
1741 total
+= PARSER_string_substW( file
, field
->text
, NULL
, 0 ) + 1;
1743 if (required
) *required
= total
;
1748 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1751 for (i
= 0, field
= &file
->fields
[line
->first_field
]; i
< line
->nb_fields
; i
++, field
++)
1753 unsigned int len
= PARSER_string_substW( file
, field
->text
, buffer
, size
);
1754 if (i
+1 < line
->nb_fields
) buffer
[len
] = ',';
1762 /***********************************************************************
1763 * SetupGetLineTextA (SETUPAPI.@)
1765 BOOL WINAPI
SetupGetLineTextA( PINFCONTEXT context
, HINF hinf
, PCSTR section_name
,
1766 PCSTR key_name
, PSTR buffer
, DWORD size
, PDWORD required
)
1768 struct inf_file
*file
;
1770 struct field
*field
;
1776 INFCONTEXT new_context
;
1777 if (!SetupFindFirstLineA( hinf
, section_name
, key_name
, &new_context
)) return FALSE
;
1778 file
= new_context
.CurrentInf
;
1779 line
= get_line( file
, new_context
.Section
, new_context
.Line
);
1783 file
= context
->CurrentInf
;
1784 if (!(line
= get_line( file
, context
->Section
, context
->Line
)))
1786 SetLastError( ERROR_LINE_NOT_FOUND
);
1791 for (i
= 0, field
= &file
->fields
[line
->first_field
]; i
< line
->nb_fields
; i
++, field
++)
1792 total
+= PARSER_string_substA( file
, field
->text
, NULL
, 0 ) + 1;
1794 if (required
) *required
= total
;
1799 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1802 for (i
= 0, field
= &file
->fields
[line
->first_field
]; i
< line
->nb_fields
; i
++, field
++)
1804 unsigned int len
= PARSER_string_substA( file
, field
->text
, buffer
, size
);
1805 if (i
+1 < line
->nb_fields
) buffer
[len
] = ',';
1813 /***********************************************************************
1814 * SetupGetFieldCount (SETUPAPI.@)
1816 DWORD WINAPI
SetupGetFieldCount( PINFCONTEXT context
)
1818 struct inf_file
*file
= context
->CurrentInf
;
1819 struct line
*line
= get_line( file
, context
->Section
, context
->Line
);
1821 if (!line
) return 0;
1822 return line
->nb_fields
;
1826 /***********************************************************************
1827 * SetupGetStringFieldA (SETUPAPI.@)
1829 BOOL WINAPI
SetupGetStringFieldA( PINFCONTEXT context
, DWORD index
, PSTR buffer
,
1830 DWORD size
, PDWORD required
)
1832 struct inf_file
*file
= context
->CurrentInf
;
1833 struct field
*field
= get_field( file
, context
->Section
, context
->Line
, index
);
1837 if (!field
) { SetLastError(ERROR_INVALID_PARAMETER
); return FALSE
; }
1838 len
= PARSER_string_substA( file
, field
->text
, NULL
, 0 );
1839 if (required
) *required
= len
+ 1;
1844 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1847 PARSER_string_substA( file
, field
->text
, buffer
, size
);
1849 TRACE( "context %p/%p/%d/%d index %d returning %s\n",
1850 context
->Inf
, context
->CurrentInf
, context
->Section
, context
->Line
,
1851 index
, debugstr_a(buffer
) );
1857 /***********************************************************************
1858 * SetupGetStringFieldW (SETUPAPI.@)
1860 BOOL WINAPI
SetupGetStringFieldW( PINFCONTEXT context
, DWORD index
, PWSTR buffer
,
1861 DWORD size
, PDWORD required
)
1863 struct inf_file
*file
= context
->CurrentInf
;
1864 struct field
*field
= get_field( file
, context
->Section
, context
->Line
, index
);
1868 if (!field
) { SetLastError(ERROR_INVALID_PARAMETER
); return FALSE
; }
1869 len
= PARSER_string_substW( file
, field
->text
, NULL
, 0 );
1870 if (required
) *required
= len
+ 1;
1875 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1878 PARSER_string_substW( file
, field
->text
, buffer
, size
);
1880 TRACE( "context %p/%p/%d/%d index %d returning %s\n",
1881 context
->Inf
, context
->CurrentInf
, context
->Section
, context
->Line
,
1882 index
, debugstr_w(buffer
) );
1888 /***********************************************************************
1889 * SetupGetIntField (SETUPAPI.@)
1891 BOOL WINAPI
SetupGetIntField( PINFCONTEXT context
, DWORD index
, PINT result
)
1894 char *end
, *buffer
= localbuff
;
1899 if (!(ret
= SetupGetStringFieldA( context
, index
, localbuff
, sizeof(localbuff
), &required
)))
1901 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
) return FALSE
;
1902 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, required
))) return FALSE
;
1903 if (!(ret
= SetupGetStringFieldA( context
, index
, buffer
, required
, NULL
))) goto done
;
1905 /* The call to SetupGetStringFieldA succeeded. If buffer is empty we have an optional field */
1906 if (!*buffer
) *result
= 0;
1909 res
= strtol( buffer
, &end
, 0 );
1910 if (end
!= buffer
&& !*end
) *result
= res
;
1913 SetLastError( ERROR_INVALID_DATA
);
1919 if (buffer
!= localbuff
) HeapFree( GetProcessHeap(), 0, buffer
);
1924 /***********************************************************************
1925 * SetupGetBinaryField (SETUPAPI.@)
1927 BOOL WINAPI
SetupGetBinaryField( PINFCONTEXT context
, DWORD index
, BYTE
*buffer
,
1928 DWORD size
, LPDWORD required
)
1930 struct inf_file
*file
= context
->CurrentInf
;
1931 struct line
*line
= get_line( file
, context
->Section
, context
->Line
);
1932 struct field
*field
;
1937 SetLastError( ERROR_LINE_NOT_FOUND
);
1940 if (!index
|| index
> line
->nb_fields
)
1942 SetLastError( ERROR_INVALID_PARAMETER
);
1945 index
--; /* fields start at 0 */
1946 if (required
) *required
= line
->nb_fields
- index
;
1947 if (!buffer
) return TRUE
;
1948 if (size
< line
->nb_fields
- index
)
1950 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
1953 field
= &file
->fields
[line
->first_field
+ index
];
1954 for (i
= index
; i
< line
->nb_fields
; i
++, field
++)
1958 for (p
= field
->text
; *p
&& isxdigitW(*p
); p
++)
1960 if ((value
<<= 4) > 255)
1962 SetLastError( ERROR_INVALID_DATA
);
1965 if (*p
<= '9') value
|= (*p
- '0');
1966 else value
|= (tolowerW(*p
) - 'a' + 10);
1968 buffer
[i
- index
] = value
;
1970 if (TRACE_ON(setupapi
))
1972 TRACE( "%p/%p/%d/%d index %d returning:\n",
1973 context
->Inf
, context
->CurrentInf
, context
->Section
, context
->Line
, index
);
1974 for (i
= index
; i
< line
->nb_fields
; i
++) TRACE( " %02x\n", buffer
[i
- index
] );
1980 /***********************************************************************
1981 * SetupGetMultiSzFieldA (SETUPAPI.@)
1983 BOOL WINAPI
SetupGetMultiSzFieldA( PINFCONTEXT context
, DWORD index
, PSTR buffer
,
1984 DWORD size
, LPDWORD required
)
1986 struct inf_file
*file
= context
->CurrentInf
;
1987 struct line
*line
= get_line( file
, context
->Section
, context
->Line
);
1988 struct field
*field
;
1995 SetLastError( ERROR_LINE_NOT_FOUND
);
1998 if (!index
|| index
> line
->nb_fields
)
2000 SetLastError( ERROR_INVALID_PARAMETER
);
2003 index
--; /* fields start at 0 */
2004 field
= &file
->fields
[line
->first_field
+ index
];
2005 for (i
= index
; i
< line
->nb_fields
; i
++, field
++)
2007 if (!(len
= PARSER_string_substA( file
, field
->text
, NULL
, 0 ))) break;
2011 if (required
) *required
= total
;
2012 if (!buffer
) return TRUE
;
2015 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2018 field
= &file
->fields
[line
->first_field
+ index
];
2019 for (i
= index
; i
< line
->nb_fields
; i
++, field
++)
2021 if (!(len
= PARSER_string_substA( file
, field
->text
, buffer
, size
))) break;
2024 *buffer
= 0; /* add final null */
2029 /***********************************************************************
2030 * SetupGetMultiSzFieldW (SETUPAPI.@)
2032 BOOL WINAPI
SetupGetMultiSzFieldW( PINFCONTEXT context
, DWORD index
, PWSTR buffer
,
2033 DWORD size
, LPDWORD required
)
2035 struct inf_file
*file
= context
->CurrentInf
;
2036 struct line
*line
= get_line( file
, context
->Section
, context
->Line
);
2037 struct field
*field
;
2044 SetLastError( ERROR_LINE_NOT_FOUND
);
2047 if (!index
|| index
> line
->nb_fields
)
2049 SetLastError( ERROR_INVALID_PARAMETER
);
2052 index
--; /* fields start at 0 */
2053 field
= &file
->fields
[line
->first_field
+ index
];
2054 for (i
= index
; i
< line
->nb_fields
; i
++, field
++)
2056 if (!(len
= PARSER_string_substW( file
, field
->text
, NULL
, 0 ))) break;
2060 if (required
) *required
= total
;
2061 if (!buffer
) return TRUE
;
2064 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2067 field
= &file
->fields
[line
->first_field
+ index
];
2068 for (i
= index
; i
< line
->nb_fields
; i
++, field
++)
2070 if (!(len
= PARSER_string_substW( file
, field
->text
, buffer
, size
))) break;
2073 *buffer
= 0; /* add final null */
2077 /***********************************************************************
2078 * pSetupGetField (SETUPAPI.@)
2080 LPCWSTR WINAPI
pSetupGetField( PINFCONTEXT context
, DWORD index
)
2082 struct inf_file
*file
= context
->CurrentInf
;
2083 struct field
*field
= get_field( file
, context
->Section
, context
->Line
, index
);
2087 SetLastError( ERROR_INVALID_PARAMETER
);
2093 /***********************************************************************
2094 * SetupGetInfFileListW (SETUPAPI.@)
2097 SetupGetInfFileListW(
2098 IN PCWSTR DirectoryPath OPTIONAL
,
2100 IN OUT PWSTR ReturnBuffer OPTIONAL
,
2101 IN DWORD ReturnBufferSize OPTIONAL
,
2102 OUT PDWORD RequiredSize OPTIONAL
)
2105 LPWSTR pFullFileName
= NULL
;
2106 LPWSTR pFileName
; /* Pointer into pFullFileName buffer */
2107 LPWSTR pBuffer
= ReturnBuffer
;
2108 WIN32_FIND_DATAW wfdFileInfo
;
2110 DWORD requiredSize
= 0;
2113 TRACE("%s %lx %p %ld %p\n", debugstr_w(DirectoryPath
), InfStyle
,
2114 ReturnBuffer
, ReturnBufferSize
, RequiredSize
);
2116 if (InfStyle
& ~(INF_STYLE_OLDNT
| INF_STYLE_WIN4
))
2118 TRACE("Unknown flags: 0x%08lx\n", InfStyle
& ~(INF_STYLE_OLDNT
| INF_STYLE_WIN4
));
2119 SetLastError(ERROR_INVALID_PARAMETER
);
2122 else if (ReturnBufferSize
== 0 && ReturnBuffer
!= NULL
)
2124 SetLastError(ERROR_INVALID_PARAMETER
);
2127 else if (ReturnBufferSize
> 0 && ReturnBuffer
== NULL
)
2129 SetLastError(ERROR_INVALID_PARAMETER
);
2133 /* Allocate memory for file filter */
2134 if (DirectoryPath
!= NULL
)
2135 /* "DirectoryPath\" form */
2136 len
= strlenW(DirectoryPath
) + 1 + 1;
2138 /* "%SYSTEMROOT%\Inf\" form */
2139 len
= MAX_PATH
+ 1 + strlenW(InfDirectory
) + 1;
2140 len
+= MAX_PATH
; /* To contain file name or "*.inf" string */
2141 pFullFileName
= MyMalloc(len
* sizeof(WCHAR
));
2142 if (pFullFileName
== NULL
)
2144 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
2148 /* Fill file filter buffer */
2151 strcpyW(pFullFileName
, DirectoryPath
);
2152 if (*pFullFileName
&& pFullFileName
[strlenW(pFullFileName
) - 1] != '\\')
2153 strcatW(pFullFileName
, BackSlash
);
2157 len
= GetSystemWindowsDirectoryW(pFullFileName
, MAX_PATH
);
2158 if (len
== 0 || len
> MAX_PATH
)
2160 if (pFullFileName
[strlenW(pFullFileName
) - 1] != '\\')
2161 strcatW(pFullFileName
, BackSlash
);
2162 strcatW(pFullFileName
, InfDirectory
);
2164 pFileName
= &pFullFileName
[strlenW(pFullFileName
)];
2166 /* Search for the first file */
2167 strcpyW(pFileName
, InfFileSpecification
);
2168 hSearch
= FindFirstFileW(pFullFileName
, &wfdFileInfo
);
2169 if (hSearch
== INVALID_HANDLE_VALUE
)
2171 TRACE("No file returned by %s\n", debugstr_w(pFullFileName
));
2179 strcpyW(pFileName
, wfdFileInfo
.cFileName
);
2180 hInf
= SetupOpenInfFileW(
2182 NULL
, /* Inf class */
2184 NULL
/* Error line */);
2185 if (hInf
== INVALID_HANDLE_VALUE
)
2187 if (GetLastError() == ERROR_CLASS_MISMATCH
)
2189 /* InfStyle was not correct. Skip this file */
2192 TRACE("Invalid .inf file %s\n", debugstr_w(pFullFileName
));
2196 len
= strlenW(wfdFileInfo
.cFileName
) + 1;
2197 requiredSize
+= (DWORD
)(len
* sizeof(WCHAR
));
2198 if (requiredSize
<= ReturnBufferSize
)
2200 strcpyW(pBuffer
, wfdFileInfo
.cFileName
);
2201 pBuffer
= &pBuffer
[len
];
2203 SetupCloseInfFile(hInf
);
2204 } while (FindNextFileW(hSearch
, &wfdFileInfo
));
2207 requiredSize
+= sizeof(WCHAR
); /* Final NULL char */
2208 if (requiredSize
<= ReturnBufferSize
)
2215 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
2219 *RequiredSize
= requiredSize
;
2222 MyFree(pFullFileName
);
2226 /***********************************************************************
2227 * SetupGetInfFileListA (SETUPAPI.@)
2230 SetupGetInfFileListA(
2231 IN PCSTR DirectoryPath OPTIONAL
,
2233 IN OUT PSTR ReturnBuffer OPTIONAL
,
2234 IN DWORD ReturnBufferSize OPTIONAL
,
2235 OUT PDWORD RequiredSize OPTIONAL
)
2237 PWSTR DirectoryPathW
= NULL
;
2238 PWSTR ReturnBufferW
= NULL
;
2241 TRACE("%s %lx %p %ld %p\n", debugstr_a(DirectoryPath
), InfStyle
,
2242 ReturnBuffer
, ReturnBufferSize
, RequiredSize
);
2244 if (DirectoryPath
!= NULL
)
2246 DirectoryPathW
= pSetupMultiByteToUnicode(DirectoryPath
, CP_ACP
);
2247 if (DirectoryPathW
== NULL
) goto Cleanup
;
2250 if (ReturnBuffer
!= NULL
&& ReturnBufferSize
!= 0)
2252 ReturnBufferW
= MyMalloc(ReturnBufferSize
* sizeof(WCHAR
));
2253 if (ReturnBufferW
== NULL
) goto Cleanup
;
2256 ret
= SetupGetInfFileListW(DirectoryPathW
, InfStyle
, ReturnBufferW
, ReturnBufferSize
, RequiredSize
);
2258 if (ret
&& ReturnBufferW
!= NULL
)
2260 ret
= WideCharToMultiByte(CP_ACP
, 0, ReturnBufferW
, -1, ReturnBuffer
, ReturnBufferSize
, NULL
, NULL
) != 0;
2264 MyFree(DirectoryPathW
);
2265 MyFree(ReturnBufferW
);
2270 /***********************************************************************
2271 * SetupDiGetINFClassW (SETUPAPI.@)
2274 SetupDiGetINFClassW(
2276 OUT LPGUID ClassGuid
,
2277 OUT PWSTR ClassName
,
2278 IN DWORD ClassNameSize
,
2279 OUT PDWORD RequiredSize OPTIONAL
)
2281 HINF hInf
= INVALID_HANDLE_VALUE
;
2284 TRACE("%s %p %p %ld %p\n", debugstr_w(InfName
), ClassGuid
,
2285 ClassName
, ClassNameSize
, RequiredSize
);
2287 /* Open .inf file */
2288 hInf
= SetupOpenInfFileW(InfName
, NULL
, INF_STYLE_WIN4
, NULL
);
2289 if (hInf
== INVALID_HANDLE_VALUE
)
2292 ret
= PARSER_GetInfClassW(hInf
, ClassGuid
, ClassName
, ClassNameSize
, RequiredSize
);
2295 if (hInf
!= INVALID_HANDLE_VALUE
)
2296 SetupCloseInfFile(hInf
);
2298 TRACE("Returning %d\n", ret
);
2302 /***********************************************************************
2303 * SetupDiGetINFClassA (SETUPAPI.@)
2305 BOOL WINAPI
SetupDiGetINFClassA(
2307 OUT LPGUID ClassGuid
,
2309 IN DWORD ClassNameSize
,
2310 OUT PDWORD RequiredSize OPTIONAL
)
2312 PWSTR InfNameW
= NULL
;
2313 PWSTR ClassNameW
= NULL
;
2316 TRACE("%s %p %p %ld %p\n", debugstr_a(InfName
), ClassGuid
,
2317 ClassName
, ClassNameSize
, RequiredSize
);
2319 if (InfName
!= NULL
)
2321 InfNameW
= pSetupMultiByteToUnicode(InfName
, CP_ACP
);
2322 if (InfNameW
== NULL
) goto Cleanup
;
2325 if (ClassName
!= NULL
&& ClassNameSize
!= 0)
2327 ClassNameW
= MyMalloc(ClassNameSize
* sizeof(WCHAR
));
2328 if (ClassNameW
== NULL
) goto Cleanup
;
2331 ret
= SetupDiGetINFClassW(InfNameW
, ClassGuid
, ClassNameW
, ClassNameSize
, RequiredSize
);
2333 if (ret
&& ClassNameW
!= NULL
)
2335 ret
= WideCharToMultiByte(CP_ACP
, 0, ClassNameW
, -1, ClassName
, ClassNameSize
, NULL
, NULL
) != 0;
2345 BOOL
EnumerateSectionsStartingWith(
2348 IN FIND_CALLBACK Callback
,
2351 struct inf_file
*file
= (struct inf_file
*)hInf
;
2352 size_t len
= strlenW(pStr
);
2355 for (i
= 0; i
< file
->nb_sections
; i
++)
2356 if (strncmpiW(pStr
, file
->sections
[i
]->name
, len
) == 0)
2358 if (!Callback(file
->sections
[i
]->name
, Context
))