Reactos
1/*
2 * INF file parsing
3 *
4 * Copyright 2002 Alexandre Julliard for CodeWeavers
5 * 2005-2006 Herv� Poussineau (hpoussin@reactos.org)
6 *
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.
11 *
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.
16 *
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
20 */
21
22/* Partially synced with Wine Staging 2.2 */
23
24#include "setupapi_private.h"
25
26#include <ndk/obfuncs.h>
27
28/* Unicode constants */
29static const WCHAR BackSlash[] = {'\\',0};
30static const WCHAR Class[] = {'C','l','a','s','s',0};
31static const WCHAR ClassGUID[] = {'C','l','a','s','s','G','U','I','D',0};
32static const WCHAR InfDirectory[] = {'i','n','f','\\',0};
33static const WCHAR InfFileSpecification[] = {'*','.','i','n','f',0};
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/* inf file structure definitions */
42
43struct field
44{
45 const WCHAR *text; /* field text */
46};
47
48struct line
49{
50 int first_field; /* index of first field in field array */
51 int nb_fields; /* number of fields in line */
52 int key_field; /* index of field for key or -1 if no key */
53};
54
55struct section
56{
57 const WCHAR *name; /* section name */
58 unsigned int nb_lines; /* number of used lines */
59 unsigned int alloc_lines; /* total number of allocated lines in array below */
60 struct line lines[16]; /* lines information (grown dynamically, 16 is initial size) */
61};
62
63struct inf_file
64{
65 struct inf_file *next; /* next appended file */
66 WCHAR *strings; /* buffer for string data (section names and field values) */
67 WCHAR *string_pos; /* position of next available string in buffer */
68 unsigned int nb_sections; /* number of used sections */
69 unsigned int alloc_sections; /* total number of allocated section pointers */
70 struct section **sections; /* section pointers array */
71 unsigned int nb_fields;
72 unsigned int alloc_fields;
73 struct field *fields;
74 int strings_section; /* index of [Strings] section or -1 if none */
75 WCHAR *filename; /* filename of the INF */
76};
77
78/* parser definitions */
79
80enum parser_state
81{
82 LINE_START, /* at beginning of a line */
83 SECTION_NAME, /* parsing a section name */
84 KEY_NAME, /* parsing a key name */
85 VALUE_NAME, /* parsing a value name */
86 EOL_BACKSLASH, /* backslash at end of line */
87 QUOTES, /* inside quotes */
88 LEADING_SPACES, /* leading spaces */
89 TRAILING_SPACES, /* trailing spaces */
90 COMMENT, /* inside a comment */
91 NB_PARSER_STATES
92};
93
94struct parser
95{
96 const WCHAR *start; /* start position of item being parsed */
97 const WCHAR *end; /* end of buffer */
98 struct inf_file *file; /* file being built */
99 enum parser_state state; /* current parser state */
100 enum parser_state stack[4]; /* state stack */
101 int stack_pos; /* current pos in stack */
102
103 int cur_section; /* index of section being parsed*/
104 struct line *line; /* current line */
105 unsigned int line_pos; /* current line position in file */
106 unsigned int broken_line; /* first line containing invalid data (if any) */
107 unsigned int error; /* error code */
108 unsigned int token_len; /* current token len */
109 WCHAR token[MAX_FIELD_LEN+1]; /* current token */
110};
111
112typedef const WCHAR * (*parser_state_func)( struct parser *parser, const WCHAR *pos );
113
114/* parser state machine functions */
115static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos );
116static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos );
117static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos );
118static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos );
119static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos );
120static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos );
121static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos );
122static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos );
123static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos );
124
125static const parser_state_func parser_funcs[NB_PARSER_STATES] =
126{
127 line_start_state, /* LINE_START */
128 section_name_state, /* SECTION_NAME */
129 key_name_state, /* KEY_NAME */
130 value_name_state, /* VALUE_NAME */
131 eol_backslash_state, /* EOL_BACKSLASH */
132 quotes_state, /* QUOTES */
133 leading_spaces_state, /* LEADING_SPACES */
134 trailing_spaces_state, /* TRAILING_SPACES */
135 comment_state /* COMMENT */
136};
137
138
139/* Unicode string constants */
140static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
141static const WCHAR Signature[] = {'S','i','g','n','a','t','u','r','e',0};
142static const WCHAR Chicago[] = {'$','C','h','i','c','a','g','o','$',0};
143static const WCHAR WindowsNT[] = {'$','W','i','n','d','o','w','s',' ','N','T','$',0};
144static const WCHAR Windows95[] = {'$','W','i','n','d','o','w','s',' ','9','5','$',0};
145static const WCHAR LayoutFile[] = {'L','a','y','o','u','t','F','i','l','e',0};
146
147/* extend an array, allocating more memory if necessary */
148static void *grow_array( void *array, unsigned int *count, size_t elem )
149{
150 void *new_array;
151 unsigned int new_count = *count + *count / 2;
152 if (new_count < 32) new_count = 32;
153
154 if (array)
155 new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, array, new_count * elem );
156 else
157 new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * elem );
158
159 if (new_array)
160 *count = new_count;
161 else
162 HeapFree( GetProcessHeap(), 0, array );
163 return new_array;
164}
165
166
167/* get the directory of the inf file (as counted string, not null-terminated) */
168static const WCHAR *get_inf_dir( const struct inf_file *file, unsigned int *len )
169{
170 const WCHAR *p = strrchrW( file->filename, '\\' );
171 *len = p ? (p + 1 - file->filename) : 0;
172 return file->filename;
173}
174
175
176/* find a section by name */
177static int find_section( const struct inf_file *file, const WCHAR *name )
178{
179 unsigned int i;
180
181 for (i = 0; i < file->nb_sections; i++)
182 if (!strcmpiW( name, file->sections[i]->name )) return i;
183 return -1;
184}
185
186
187/* find a line by name */
188static struct line *find_line( struct inf_file *file, int section_index, const WCHAR *name )
189{
190 struct section *section;
191 struct line *line;
192 unsigned int i;
193
194 if (section_index < 0 || section_index >= file->nb_sections) return NULL;
195 section = file->sections[section_index];
196 for (i = 0, line = section->lines; i < section->nb_lines; i++, line++)
197 {
198 if (line->key_field == -1) continue;
199 if (!strcmpiW( name, file->fields[line->key_field].text )) return line;
200 }
201 return NULL;
202}
203
204
205/* add a section to the file and return the section index */
206static int add_section( struct inf_file *file, const WCHAR *name )
207{
208 struct section *section;
209
210 if (file->nb_sections >= file->alloc_sections)
211 {
212 if (!(file->sections = grow_array( file->sections, &file->alloc_sections,
213 sizeof(file->sections[0]) ))) return -1;
214 }
215 if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) ))) return -1;
216 section->name = name;
217 section->nb_lines = 0;
218 section->alloc_lines = sizeof(section->lines)/sizeof(section->lines[0]);
219 file->sections[file->nb_sections] = section;
220 return file->nb_sections++;
221}
222
223
224/* add a line to a given section */
225static struct line *add_line( struct inf_file *file, int section_index )
226{
227 struct section *section;
228 struct line *line;
229
230 ASSERT( section_index >= 0 && section_index < file->nb_sections );
231
232 section = file->sections[section_index];
233 if (section->nb_lines == section->alloc_lines) /* need to grow the section */
234 {
235 int size = sizeof(*section) - sizeof(section->lines) + 2*section->alloc_lines*sizeof(*line);
236 if (!(section = HeapReAlloc( GetProcessHeap(), 0, section, size ))) return NULL;
237 section->alloc_lines *= 2;
238 file->sections[section_index] = section;
239 }
240 line = §ion->lines[section->nb_lines++];
241 line->first_field = file->nb_fields;
242 line->nb_fields = 0;
243 line->key_field = -1;
244 return line;
245}
246
247
248/* retrieve a given line from section/line index */
249static inline struct line *get_line( struct inf_file *file, unsigned int section_index,
250 unsigned int line_index )
251{
252 struct section *section;
253
254 if (section_index >= file->nb_sections) return NULL;
255 section = file->sections[section_index];
256 if (line_index >= section->nb_lines) return NULL;
257 return §ion->lines[line_index];
258}
259
260
261/* retrieve a given field from section/line/field index */
262static struct field *get_field( struct inf_file *file, int section_index, int line_index,
263 int field_index )
264{
265 struct line *line = get_line( file, section_index, line_index );
266
267 if (!line) return NULL;
268 if (!field_index) /* get the key */
269 {
270 if (line->key_field == -1) return NULL;
271 return &file->fields[line->key_field];
272 }
273 field_index--;
274 if (field_index >= line->nb_fields) return NULL;
275 return &file->fields[line->first_field + field_index];
276}
277
278
279/* allocate a new field, growing the array if necessary */
280static struct field *add_field( struct inf_file *file, const WCHAR *text )
281{
282 struct field *field;
283
284 if (file->nb_fields >= file->alloc_fields)
285 {
286 if (!(file->fields = grow_array( file->fields, &file->alloc_fields,
287 sizeof(file->fields[0]) ))) return NULL;
288 }
289 field = &file->fields[file->nb_fields++];
290 field->text = text;
291 return field;
292}
293
294
295/* retrieve the string substitution for a directory id */
296static const WCHAR *get_dirid_subst( const struct inf_file *file, int dirid, unsigned int *len )
297{
298 const WCHAR *ret;
299
300 if (dirid == DIRID_SRCPATH) return get_inf_dir( file, len );
301 ret = DIRID_get_string( dirid );
302 if (ret) *len = strlenW(ret);
303 return ret;
304}
305
306
307/* retrieve the string substitution for a given string, or NULL if not found */
308/* if found, len is set to the substitution length */
309static const WCHAR *get_string_subst( const struct inf_file *file, const WCHAR *str, unsigned int *len,
310 BOOL no_trailing_slash )
311{
312 static const WCHAR percent = '%';
313
314 struct section *strings_section;
315 struct line *line;
316 struct field *field;
317 unsigned int i, j;
318 int dirid;
319 WCHAR *dirid_str, *end;
320 const WCHAR *ret = NULL;
321 WCHAR StringLangId[13] = {'S','t','r','i','n','g','s','.',0};
322 TCHAR Lang[5];
323
324 if (!*len) /* empty string (%%) is replaced by single percent */
325 {
326 *len = 1;
327 return &percent;
328 }
329 if (file->strings_section == -1) goto not_found;
330 strings_section = file->sections[file->strings_section];
331 for (j = 0, line = strings_section->lines; j < strings_section->nb_lines; j++, line++)
332 {
333 if (line->key_field == -1) continue;
334 if (strncmpiW( str, file->fields[line->key_field].text, *len )) continue;
335 if (!file->fields[line->key_field].text[*len]) break;
336 }
337 if (j == strings_section->nb_lines || !line->nb_fields) goto not_found;
338 field = &file->fields[line->first_field];
339 GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_ILANGUAGE, Lang, sizeof(Lang)/sizeof(TCHAR)); // get the current system locale for translated strings
340
341 strcpyW(StringLangId + 8, Lang + 2);
342 // now you have e.g. Strings.07 for german neutral translations
343 for (i = 0; i < file->nb_sections; i++) // search in all sections
344 {
345 if (!strcmpiW(file->sections[i]->name,StringLangId)) // if the section is a Strings.* section
346 {
347 strings_section = file->sections[i]; // select this section for further use
348 for (j = 0, line = strings_section->lines; j < strings_section->nb_lines; j++, line++) // process all lines in this section
349 {
350 if (line->key_field == -1) continue; // if no key then skip
351 if (strncmpiW( str, file->fields[line->key_field].text, *len )) continue; // if wrong key name, then skip
352 if (!file->fields[line->key_field].text[*len]) // if value exist
353 {
354 field = &file->fields[line->first_field]; // then extract value and
355 break; // no more search necessary
356 }
357 }
358 }
359 }
360
361 strcpyW(StringLangId + 8, Lang); // append the Language identifier from GetLocaleInfo
362 // now you have e.g. Strings.0407 for german translations
363 for (i = 0; i < file->nb_sections; i++) // search in all sections
364 {
365 if (!strcmpiW(file->sections[i]->name,StringLangId)) // if the section is a Strings.* section
366 {
367 strings_section = file->sections[i]; // select this section for further use
368 for (j = 0, line = strings_section->lines; j < strings_section->nb_lines; j++, line++) // process all lines in this section
369 {
370 if (line->key_field == -1) continue; // if no key then skip
371 if (strncmpiW( str, file->fields[line->key_field].text, *len )) continue; // if wrong key name, then skip
372 if (!file->fields[line->key_field].text[*len]) // if value exist
373 {
374 field = &file->fields[line->first_field]; // then extract value and
375 break; // no more search necessary
376 }
377 }
378 }
379 }
380 *len = strlenW( field->text ); // set length
381 ret = field->text; // return the english or translated string
382 return ret;
383
384
385 not_found: /* check for integer id */
386 if ((dirid_str = HeapAlloc( GetProcessHeap(), 0, (*len+1) * sizeof(WCHAR) )))
387 {
388 memcpy( dirid_str, str, *len * sizeof(WCHAR) );
389 dirid_str[*len] = 0;
390 dirid = strtolW( dirid_str, &end, 10 );
391 if (!*end) ret = get_dirid_subst( file, dirid, len );
392 if (no_trailing_slash && ret && *len && ret[*len - 1] == '\\') *len -= 1;
393 HeapFree( GetProcessHeap(), 0, dirid_str );
394 return ret;
395 }
396 return NULL;
397}
398
399
400/* do string substitutions on the specified text */
401/* the buffer is assumed to be large enough */
402/* returns necessary length not including terminating null */
403static unsigned int PARSER_string_substW( const struct inf_file *file, const WCHAR *text,
404 WCHAR *buffer, unsigned int size )
405{
406 const WCHAR *start, *subst, *p;
407 unsigned int len, total = 0;
408 BOOL inside = FALSE;
409
410 if (!buffer) size = MAX_STRING_LEN + 1;
411 for (p = start = text; *p; p++)
412 {
413 if (*p != '%') continue;
414 inside = !inside;
415 if (inside) /* start of a %xx% string */
416 {
417 len = p - start;
418 if (len > size - 1) len = size - 1;
419 if (buffer) memcpy( buffer + total, start, len * sizeof(WCHAR) );
420 total += len;
421 size -= len;
422 start = p;
423 }
424 else /* end of the %xx% string, find substitution */
425 {
426 len = p - start - 1;
427 subst = get_string_subst( file, start + 1, &len, p[1] == '\\' );
428 if (!subst)
429 {
430 subst = start;
431 len = p - start + 1;
432 }
433 if (len > size - 1) len = size - 1;
434 if (buffer) memcpy( buffer + total, subst, len * sizeof(WCHAR) );
435 total += len;
436 size -= len;
437 start = p + 1;
438 }
439 }
440
441 if (start != p) /* unfinished string, copy it */
442 {
443 len = p - start;
444 if (len > size - 1) len = size - 1;
445 if (buffer) memcpy( buffer + total, start, len * sizeof(WCHAR) );
446 total += len;
447 }
448 if (buffer && size) buffer[total] = 0;
449 return total;
450}
451
452
453/* do string substitutions on the specified text */
454/* the buffer is assumed to be large enough */
455/* returns necessary length not including terminating null */
456static unsigned int PARSER_string_substA( const struct inf_file *file, const WCHAR *text,
457 char *buffer, unsigned int size )
458{
459 WCHAR buffW[MAX_STRING_LEN+1];
460 DWORD ret;
461
462 unsigned int len = PARSER_string_substW( file, text, buffW, sizeof(buffW)/sizeof(WCHAR) );
463 if (!buffer) RtlUnicodeToMultiByteSize( &ret, buffW, len * sizeof(WCHAR) );
464 else
465 {
466 RtlUnicodeToMultiByteN( buffer, size-1, &ret, buffW, len * sizeof(WCHAR) );
467 buffer[ret] = 0;
468 }
469 return ret;
470}
471
472
473/* push some string data into the strings buffer */
474static WCHAR *push_string( struct inf_file *file, const WCHAR *string )
475{
476 WCHAR *ret = file->string_pos;
477 strcpyW( ret, string );
478 file->string_pos += strlenW( ret ) + 1;
479 return ret;
480}
481
482
483/* push the current state on the parser stack */
484static inline void push_state( struct parser *parser, enum parser_state state )
485{
486 ASSERT( parser->stack_pos < sizeof(parser->stack)/sizeof(parser->stack[0]) );
487 parser->stack[parser->stack_pos++] = state;
488}
489
490
491/* pop the current state */
492static inline void pop_state( struct parser *parser )
493{
494 ASSERT( parser->stack_pos );
495 parser->state = parser->stack[--parser->stack_pos];
496}
497
498
499/* set the parser state and return the previous one */
500static inline enum parser_state set_state( struct parser *parser, enum parser_state state )
501{
502 enum parser_state ret = parser->state;
503 parser->state = state;
504 return ret;
505}
506
507
508/* check if the pointer points to an end of file */
509static inline BOOL is_eof( const struct parser *parser, const WCHAR *ptr )
510{
511 return (ptr >= parser->end || *ptr == CONTROL_Z);
512}
513
514
515/* check if the pointer points to an end of line */
516static inline BOOL is_eol( const struct parser *parser, const WCHAR *ptr )
517{
518 return (ptr >= parser->end || *ptr == CONTROL_Z || *ptr == '\n');
519}
520
521
522/* push data from current token start up to pos into the current token */
523static int push_token( struct parser *parser, const WCHAR *pos )
524{
525 int len = pos - parser->start;
526 const WCHAR *src = parser->start;
527 WCHAR *dst = parser->token + parser->token_len;
528
529 if (len > MAX_FIELD_LEN - parser->token_len) len = MAX_FIELD_LEN - parser->token_len;
530
531 parser->token_len += len;
532 for ( ; len > 0; len--, dst++, src++) *dst = *src ? *src : ' ';
533 *dst = 0;
534 parser->start = pos;
535 return 0;
536}
537
538
539/* add a section with the current token as name */
540static int add_section_from_token( struct parser *parser )
541{
542 int section_index;
543
544 if (parser->token_len > MAX_SECTION_NAME_LEN)
545 {
546 parser->error = ERROR_SECTION_NAME_TOO_LONG;
547 return -1;
548 }
549 if ((section_index = find_section( parser->file, parser->token )) == -1)
550 {
551 /* need to create a new one */
552 const WCHAR *name = push_string( parser->file, parser->token );
553 if ((section_index = add_section( parser->file, name )) == -1)
554 {
555 parser->error = ERROR_NOT_ENOUGH_MEMORY;
556 return -1;
557 }
558 }
559 parser->token_len = 0;
560 parser->cur_section = section_index;
561 return section_index;
562}
563
564
565/* add a field containing the current token to the current line */
566static struct field *add_field_from_token( struct parser *parser, BOOL is_key )
567{
568 struct field *field;
569 WCHAR *text;
570
571 if (!parser->line) /* need to start a new line */
572 {
573 if (parser->cur_section == -1) /* got a line before the first section */
574 {
575 parser->error = ERROR_EXPECTED_SECTION_NAME;
576 return NULL;
577 }
578 if (!(parser->line = add_line( parser->file, parser->cur_section ))) goto error;
579 }
580 else ASSERT(!is_key);
581
582 text = push_string( parser->file, parser->token );
583 if ((field = add_field( parser->file, text )))
584 {
585 if (!is_key) parser->line->nb_fields++;
586 else
587 {
588 /* replace first field by key field */
589 parser->line->key_field = parser->line->first_field;
590 parser->line->first_field++;
591 }
592 parser->token_len = 0;
593 return field;
594 }
595 error:
596 parser->error = ERROR_NOT_ENOUGH_MEMORY;
597 return NULL;
598}
599
600
601/* close the current line and prepare for parsing a new one */
602static void close_current_line( struct parser *parser )
603{
604 struct line *cur_line = parser->line;
605
606 if (cur_line)
607 {
608 /* if line has a single field and no key, the field is the key too */
609 if (cur_line->nb_fields == 1 && cur_line->key_field == -1)
610 cur_line->key_field = cur_line->first_field;
611 }
612 parser->line = NULL;
613}
614
615
616/* handler for parser LINE_START state */
617static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos )
618{
619 const WCHAR *p;
620
621 for (p = pos; !is_eof( parser, p ); p++)
622 {
623 switch(*p)
624 {
625 case '\n':
626 parser->line_pos++;
627 close_current_line( parser );
628 break;
629 case ';':
630 push_state( parser, LINE_START );
631 set_state( parser, COMMENT );
632 return p + 1;
633 case '[':
634 parser->start = p + 1;
635 set_state( parser, SECTION_NAME );
636 return p + 1;
637 default:
638 if (isspaceW(*p)) break;
639 if (parser->cur_section != -1)
640 {
641 parser->start = p;
642 set_state( parser, KEY_NAME );
643 return p;
644 }
645 if (!parser->broken_line)
646 parser->broken_line = parser->line_pos;
647 break;
648 }
649 }
650 close_current_line( parser );
651 return NULL;
652}
653
654
655/* handler for parser SECTION_NAME state */
656static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos )
657{
658 const WCHAR *p;
659
660 for (p = pos; !is_eol( parser, p ); p++)
661 {
662 if (*p == ']')
663 {
664 push_token( parser, p );
665 if (add_section_from_token( parser ) == -1) return NULL;
666 push_state( parser, LINE_START );
667 set_state( parser, COMMENT ); /* ignore everything else on the line */
668 return p + 1;
669 }
670 }
671 parser->error = ERROR_BAD_SECTION_NAME_LINE; /* unfinished section name */
672 return NULL;
673}
674
675
676/* handler for parser KEY_NAME state */
677static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos )
678{
679 const WCHAR *p, *token_end = parser->start;
680
681 for (p = pos; !is_eol( parser, p ); p++)
682 {
683 if (*p == ',') break;
684 switch(*p)
685 {
686
687 case '=':
688 push_token( parser, token_end );
689 if (!add_field_from_token( parser, TRUE )) return NULL;
690 parser->start = p + 1;
691 push_state( parser, VALUE_NAME );
692 set_state( parser, LEADING_SPACES );
693 return p + 1;
694 case ';':
695 push_token( parser, token_end );
696 if (!add_field_from_token( parser, FALSE )) return NULL;
697 push_state( parser, LINE_START );
698 set_state( parser, COMMENT );
699 return p + 1;
700 case '"':
701 push_token( parser, p );
702 parser->start = p + 1;
703 push_state( parser, KEY_NAME );
704 set_state( parser, QUOTES );
705 return p + 1;
706 case '\\':
707 push_token( parser, token_end );
708 parser->start = p;
709 push_state( parser, KEY_NAME );
710 set_state( parser, EOL_BACKSLASH );
711 return p;
712 default:
713 if (!isspaceW(*p)) token_end = p + 1;
714 else
715 {
716 push_token( parser, p );
717 push_state( parser, KEY_NAME );
718 set_state( parser, TRAILING_SPACES );
719 return p;
720 }
721 break;
722 }
723 }
724 push_token( parser, token_end );
725 set_state( parser, VALUE_NAME );
726 return p;
727}
728
729
730/* handler for parser VALUE_NAME state */
731static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos )
732{
733 const WCHAR *p, *token_end = parser->start;
734
735 for (p = pos; !is_eol( parser, p ); p++)
736 {
737 switch(*p)
738 {
739 case ';':
740 push_token( parser, token_end );
741 if (!add_field_from_token( parser, FALSE )) return NULL;
742 push_state( parser, LINE_START );
743 set_state( parser, COMMENT );
744 return p + 1;
745 case ',':
746 push_token( parser, token_end );
747 if (!add_field_from_token( parser, FALSE )) return NULL;
748 parser->start = p + 1;
749 push_state( parser, VALUE_NAME );
750 set_state( parser, LEADING_SPACES );
751 return p + 1;
752 case '"':
753 push_token( parser, p );
754 parser->start = p + 1;
755 push_state( parser, VALUE_NAME );
756 set_state( parser, QUOTES );
757 return p + 1;
758 case '\\':
759 push_token( parser, token_end );
760 parser->start = p;
761 push_state( parser, VALUE_NAME );
762 set_state( parser, EOL_BACKSLASH );
763 return p;
764 default:
765 if (!isspaceW(*p)) token_end = p + 1;
766 else
767 {
768 push_token( parser, p );
769 push_state( parser, VALUE_NAME );
770 set_state( parser, TRAILING_SPACES );
771 return p;
772 }
773 break;
774 }
775 }
776 push_token( parser, token_end );
777 if (!add_field_from_token( parser, FALSE )) return NULL;
778 set_state( parser, LINE_START );
779 return p;
780}
781
782
783/* handler for parser EOL_BACKSLASH state */
784static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos )
785{
786 const WCHAR *p;
787
788 for (p = pos; !is_eof( parser, p ); p++)
789 {
790 switch(*p)
791 {
792 case '\n':
793 parser->line_pos++;
794 parser->start = p + 1;
795 set_state( parser, LEADING_SPACES );
796 return p + 1;
797 case '\\':
798 continue;
799 case ';':
800 push_state( parser, EOL_BACKSLASH );
801 set_state( parser, COMMENT );
802 return p + 1;
803 default:
804 if (isspaceW(*p)) continue;
805 push_token( parser, p );
806 pop_state( parser );
807 return p;
808 }
809 }
810 parser->start = p;
811 pop_state( parser );
812 return p;
813}
814
815
816/* handler for parser QUOTES state */
817static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos )
818{
819 const WCHAR *p;
820
821 for (p = pos; !is_eol( parser, p ); p++)
822 {
823 if (*p == '"')
824 {
825 if (p+1 < parser->end && p[1] == '"') /* double quotes */
826 {
827 push_token( parser, p + 1 );
828 parser->start = p + 2;
829 p++;
830 }
831 else /* end of quotes */
832 {
833 push_token( parser, p );
834 parser->start = p + 1;
835 pop_state( parser );
836 return p + 1;
837 }
838 }
839 }
840 push_token( parser, p );
841 pop_state( parser );
842 return p;
843}
844
845
846/* handler for parser LEADING_SPACES state */
847static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos )
848{
849 const WCHAR *p;
850
851 for (p = pos; !is_eol( parser, p ); p++)
852 {
853 if (*p == '\\')
854 {
855 parser->start = p;
856 set_state( parser, EOL_BACKSLASH );
857 return p;
858 }
859 if (!isspaceW(*p)) break;
860 }
861 parser->start = p;
862 pop_state( parser );
863 return p;
864}
865
866
867/* handler for parser TRAILING_SPACES state */
868static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos )
869{
870 const WCHAR *p;
871
872 for (p = pos; !is_eol( parser, p ); p++)
873 {
874 if (*p == '\\')
875 {
876 set_state( parser, EOL_BACKSLASH );
877 return p;
878 }
879 if (!isspaceW(*p)) break;
880 }
881 pop_state( parser );
882 return p;
883}
884
885
886/* handler for parser COMMENT state */
887static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos )
888{
889 const WCHAR *p = pos;
890
891 while (!is_eol( parser, p )) p++;
892 pop_state( parser );
893 return p;
894}
895
896
897static void free_inf_file( struct inf_file *file )
898{
899 unsigned int i;
900
901 for (i = 0; i < file->nb_sections; i++) HeapFree( GetProcessHeap(), 0, file->sections[i] );
902 HeapFree( GetProcessHeap(), 0, file->filename );
903 HeapFree( GetProcessHeap(), 0, file->sections );
904 HeapFree( GetProcessHeap(), 0, file->fields );
905 HeapFree( GetProcessHeap(), 0, file->strings );
906 HeapFree( GetProcessHeap(), 0, file );
907}
908
909
910/* parse a complete buffer */
911static DWORD parse_buffer( struct inf_file *file, const WCHAR *buffer, const WCHAR *end,
912 UINT *error_line )
913{
914 static const WCHAR Strings[] = {'S','t','r','i','n','g','s',0};
915
916 struct parser parser;
917 const WCHAR *pos = buffer;
918
919 parser.start = buffer;
920 parser.end = end;
921 parser.file = file;
922 parser.line = NULL;
923 parser.state = LINE_START;
924 parser.stack_pos = 0;
925 parser.cur_section = -1;
926 parser.line_pos = 1;
927 parser.broken_line = 0;
928 parser.error = 0;
929 parser.token_len = 0;
930
931 /* parser main loop */
932 while (pos) pos = (parser_funcs[parser.state])( &parser, pos );
933
934 /* trim excess buffer space */
935 if (file->alloc_sections > file->nb_sections)
936 {
937 file->sections = HeapReAlloc( GetProcessHeap(), 0, file->sections,
938 file->nb_sections * sizeof(file->sections[0]) );
939 file->alloc_sections = file->nb_sections;
940 }
941 if (file->alloc_fields > file->nb_fields)
942 {
943 file->fields = HeapReAlloc( GetProcessHeap(), 0, file->fields,
944 file->nb_fields * sizeof(file->fields[0]) );
945 file->alloc_fields = file->nb_fields;
946 }
947 file->strings = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, file->strings,
948 (file->string_pos - file->strings) * sizeof(WCHAR) );
949
950 if (parser.error)
951 {
952 if (error_line) *error_line = parser.line_pos;
953 return parser.error;
954 }
955
956 /* find the [strings] section */
957 file->strings_section = find_section( file, Strings );
958
959 if (file->strings_section == -1 && parser.broken_line)
960 {
961 if (error_line) *error_line = parser.broken_line;
962 return ERROR_EXPECTED_SECTION_NAME;
963 }
964
965 return 0;
966}
967
968
969/* append a child INF file to its parent list, in a thread-safe manner */
970static void append_inf_file( struct inf_file *parent, struct inf_file *child )
971{
972 struct inf_file **ppnext = &parent->next;
973 child->next = NULL;
974
975 for (;;)
976 {
977 struct inf_file *next = InterlockedCompareExchangePointer( (void **)ppnext, child, NULL );
978 if (!next) return;
979 ppnext = &next->next;
980 }
981}
982
983
984/***********************************************************************
985 * parse_file
986 *
987 * parse an INF file.
988 */
989static struct inf_file *parse_file( HANDLE handle, UINT *error_line, DWORD style )
990{
991 void *buffer;
992 DWORD err = 0;
993 struct inf_file *file;
994
995 DWORD size = GetFileSize( handle, NULL );
996 HANDLE mapping = CreateFileMappingW( handle, NULL, PAGE_READONLY, 0, size, NULL );
997 if (!mapping) return NULL;
998 buffer = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, size );
999 NtClose( mapping );
1000 if (!buffer) return NULL;
1001
1002 if (!(file = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*file) )))
1003 {
1004 err = ERROR_NOT_ENOUGH_MEMORY;
1005 goto done;
1006 }
1007
1008 /* we won't need more strings space than the size of the file,
1009 * so we can preallocate it here
1010 */
1011 if (!(file->strings = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) )))
1012 {
1013 err = ERROR_NOT_ENOUGH_MEMORY;
1014 goto done;
1015 }
1016 file->string_pos = file->strings;
1017 file->strings_section = -1;
1018
1019 if (!RtlIsTextUnicode( buffer, size, NULL ))
1020 {
1021 static const BYTE utf8_bom[3] = { 0xef, 0xbb, 0xbf };
1022 WCHAR *new_buff;
1023 UINT codepage = CP_ACP;
1024 UINT offset = 0;
1025
1026 if (size > sizeof(utf8_bom) && !memcmp( buffer, utf8_bom, sizeof(utf8_bom) ))
1027 {
1028 codepage = CP_UTF8;
1029 offset = sizeof(utf8_bom);
1030 }
1031
1032 if ((new_buff = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) )))
1033 {
1034 DWORD len = MultiByteToWideChar( codepage, 0, (char *)buffer + offset,
1035 size - offset, new_buff, size );
1036 err = parse_buffer( file, new_buff, new_buff + len, error_line );
1037 HeapFree( GetProcessHeap(), 0, new_buff );
1038 }
1039 }
1040 else
1041 {
1042 WCHAR *new_buff = buffer;
1043 /* UCS-16 files should start with the Unicode BOM; we should skip it */
1044 if (*new_buff == 0xfeff)
1045 new_buff++;
1046 err = parse_buffer( file, new_buff, (WCHAR *)((char *)buffer + size), error_line );
1047 }
1048
1049 if (!err) /* now check signature */
1050 {
1051 int version_index = find_section( file, Version );
1052 if (version_index != -1)
1053 {
1054 struct line *line = find_line( file, version_index, Signature );
1055 if (line && line->nb_fields > 0)
1056 {
1057 struct field *field = file->fields + line->first_field;
1058 if (!strcmpiW( field->text, Chicago )) goto done;
1059 if (!strcmpiW( field->text, WindowsNT )) goto done;
1060 if (!strcmpiW( field->text, Windows95 )) goto done;
1061 }
1062 }
1063 if (error_line) *error_line = 0;
1064 if (style & INF_STYLE_WIN4) err = ERROR_WRONG_INF_STYLE;
1065 }
1066
1067 done:
1068 UnmapViewOfFile( buffer );
1069 if (err)
1070 {
1071 if (file) free_inf_file( file );
1072 SetLastError( err );
1073 file = NULL;
1074 }
1075 return file;
1076}
1077
1078
1079/***********************************************************************
1080 * PARSER_get_inf_filename
1081 *
1082 * Retrieve the filename of an inf file.
1083 */
1084const WCHAR *PARSER_get_inf_filename( HINF hinf )
1085{
1086 struct inf_file *file = hinf;
1087 return file->filename;
1088}
1089
1090
1091/***********************************************************************
1092 * PARSER_get_src_root
1093 *
1094 * Retrieve the source directory of an inf file.
1095 */
1096WCHAR *PARSER_get_src_root( HINF hinf )
1097{
1098 unsigned int len;
1099 const WCHAR *dir = get_inf_dir( hinf, &len );
1100 WCHAR *ret = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
1101 if (ret)
1102 {
1103 memcpy( ret, dir, len * sizeof(WCHAR) );
1104 ret[len] = 0;
1105 }
1106 return ret;
1107}
1108
1109
1110/***********************************************************************
1111 * PARSER_get_dest_dir
1112 *
1113 * retrieve a destination dir of the form "dirid,relative_path" in the given entry.
1114 * returned buffer must be freed by caller.
1115 */
1116WCHAR *PARSER_get_dest_dir( INFCONTEXT *context )
1117{
1118 const WCHAR *dir;
1119 WCHAR *ptr, *ret;
1120 INT dirid;
1121 unsigned int len1;
1122 DWORD len2;
1123
1124 if (!SetupGetIntField( context, 1, &dirid )) return NULL;
1125 if (!(dir = get_dirid_subst( context->Inf, dirid, &len1 ))) return NULL;
1126 if (!SetupGetStringFieldW( context, 2, NULL, 0, &len2 )) len2 = 0;
1127 if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len1+len2+1) * sizeof(WCHAR) ))) return NULL;
1128 memcpy( ret, dir, len1 * sizeof(WCHAR) );
1129 ptr = ret + len1;
1130 if (len2 && ptr > ret && ptr[-1] != '\\') *ptr++ = '\\';
1131 if (!SetupGetStringFieldW( context, 2, ptr, len2, NULL )) *ptr = 0;
1132 return ret;
1133}
1134
1135
1136/***********************************************************************
1137 * SetupOpenInfFileA (SETUPAPI.@)
1138 */
1139HINF WINAPI SetupOpenInfFileA( PCSTR name, PCSTR class, DWORD style, UINT *error )
1140{
1141 UNICODE_STRING nameW, classW;
1142 HINF ret = INVALID_HANDLE_VALUE;
1143
1144 classW.Buffer = NULL;
1145 if (class && !RtlCreateUnicodeStringFromAsciiz( &classW, class ))
1146 {
1147 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1148 return ret;
1149 }
1150 if (RtlCreateUnicodeStringFromAsciiz( &nameW, name ))
1151 {
1152 ret = SetupOpenInfFileW( nameW.Buffer, classW.Buffer, style, error );
1153 RtlFreeUnicodeString( &nameW );
1154 }
1155 RtlFreeUnicodeString( &classW );
1156 return ret;
1157}
1158
1159
1160static BOOL
1161PARSER_GetInfClassW(
1162 IN HINF hInf,
1163 OUT LPGUID ClassGuid,
1164 OUT PWSTR ClassName,
1165 IN DWORD ClassNameSize,
1166 OUT PDWORD RequiredSize OPTIONAL)
1167{
1168 DWORD requiredSize;
1169 WCHAR guidW[MAX_GUID_STRING_LEN + 1];
1170 BOOL ret = FALSE;
1171
1172 /* Read class Guid */
1173 if (!SetupGetLineTextW(NULL, hInf, Version, ClassGUID, guidW, sizeof(guidW), NULL))
1174 goto cleanup;
1175 guidW[37] = '\0'; /* Replace the } by a NULL character */
1176 if (UuidFromStringW(&guidW[1], ClassGuid) != RPC_S_OK)
1177 goto cleanup;
1178
1179 /* Read class name */
1180 ret = SetupGetLineTextW(NULL, hInf, Version, Class, ClassName, ClassNameSize, &requiredSize);
1181 if (ret && ClassName == NULL && ClassNameSize == 0)
1182 {
1183 if (RequiredSize)
1184 *RequiredSize = requiredSize;
1185 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1186 ret = FALSE;
1187 goto cleanup;
1188 }
1189 if (!ret)
1190 {
1191 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1192 {
1193 if (RequiredSize)
1194 *RequiredSize = requiredSize;
1195 goto cleanup;
1196 }
1197 else if (!SetupDiClassNameFromGuidW(ClassGuid, ClassName, ClassNameSize, &requiredSize))
1198 {
1199 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1200 {
1201 if (RequiredSize)
1202 *RequiredSize = requiredSize;
1203 goto cleanup;
1204 }
1205 /* Return a NULL class name */
1206 if (RequiredSize)
1207 *RequiredSize = 1;
1208 if (ClassNameSize < 1)
1209 {
1210 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1211 goto cleanup;
1212 }
1213 memcpy(ClassGuid, &GUID_NULL, sizeof(GUID));
1214 *ClassName = UNICODE_NULL;
1215 }
1216 }
1217
1218 ret = TRUE;
1219
1220cleanup:
1221 TRACE("Returning %d\n", ret);
1222 return ret;
1223}
1224
1225
1226/***********************************************************************
1227 * SetupOpenInfFileW (SETUPAPI.@)
1228 */
1229HINF WINAPI SetupOpenInfFileW( PCWSTR name, PCWSTR class, DWORD style, UINT *error )
1230{
1231 struct inf_file *file = NULL;
1232 HANDLE handle;
1233 WCHAR *path, *p;
1234 UINT len;
1235
1236 TRACE("%s %s %lx %p\n", debugstr_w(name), debugstr_w(class), style, error);
1237
1238#ifdef __REACTOS__
1239 if (style & ~(INF_STYLE_OLDNT | INF_STYLE_WIN4))
1240 {
1241 SetLastError(ERROR_INVALID_PARAMETER);
1242 return (HINF)INVALID_HANDLE_VALUE;
1243 }
1244 if (!name)
1245 {
1246 SetLastError(ERROR_INVALID_PARAMETER);
1247 return (HINF)INVALID_HANDLE_VALUE;
1248 }
1249#endif // __REACTOS__
1250
1251 if (strchrW( name, '\\' ) || strchrW( name, '/' ))
1252 {
1253 if (!(len = GetFullPathNameW( name, 0, NULL, NULL ))) return INVALID_HANDLE_VALUE;
1254 if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1255 {
1256 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1257 return INVALID_HANDLE_VALUE;
1258 }
1259 GetFullPathNameW( name, len, path, NULL );
1260 handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1261 }
1262 else /* try Windows directory */
1263 {
1264 static const WCHAR Inf[] = {'\\','i','n','f','\\',0};
1265 static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
1266
1267 len = GetWindowsDirectoryW( NULL, 0 ) + strlenW(name) + 12;
1268 if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1269 {
1270 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1271 return INVALID_HANDLE_VALUE;
1272 }
1273 GetWindowsDirectoryW( path, len );
1274 p = path + strlenW(path);
1275 strcpyW( p, Inf );
1276 strcatW( p, name );
1277 handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1278 if (handle == INVALID_HANDLE_VALUE)
1279 {
1280 strcpyW( p, System32 );
1281 strcatW( p, name );
1282 handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1283 }
1284 }
1285
1286 if (handle != INVALID_HANDLE_VALUE)
1287 {
1288 file = parse_file( handle, error, style);
1289 CloseHandle( handle );
1290 }
1291 if (!file)
1292 {
1293 HeapFree( GetProcessHeap(), 0, path );
1294 return INVALID_HANDLE_VALUE;
1295 }
1296 TRACE( "%s -> %p\n", debugstr_w(path), file );
1297 file->filename = path;
1298
1299 if (class)
1300 {
1301 GUID ClassGuid;
1302 LPWSTR ClassName = HeapAlloc(GetProcessHeap(), 0, (strlenW(class) + 1) * sizeof(WCHAR));
1303 if (!ClassName)
1304 {
1305 /* Not enough memory */
1306 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1307 SetupCloseInfFile((HINF)file);
1308 return INVALID_HANDLE_VALUE;
1309 }
1310 else if (!PARSER_GetInfClassW((HINF)file, &ClassGuid, ClassName, strlenW(class) + 1, NULL))
1311 {
1312 /* Unable to get class name in .inf file */
1313 HeapFree(GetProcessHeap(), 0, ClassName);
1314 SetLastError(ERROR_CLASS_MISMATCH);
1315 SetupCloseInfFile((HINF)file);
1316 return INVALID_HANDLE_VALUE;
1317 }
1318 else if (strcmpW(class, ClassName) != 0)
1319 {
1320 /* Provided name name is not the expected one */
1321 HeapFree(GetProcessHeap(), 0, ClassName);
1322 SetLastError(ERROR_CLASS_MISMATCH);
1323 SetupCloseInfFile((HINF)file);
1324 return INVALID_HANDLE_VALUE;
1325 }
1326 HeapFree(GetProcessHeap(), 0, ClassName);
1327 }
1328
1329 SetLastError( 0 );
1330 return file;
1331}
1332
1333
1334/***********************************************************************
1335 * SetupOpenAppendInfFileA (SETUPAPI.@)
1336 */
1337BOOL WINAPI SetupOpenAppendInfFileA( PCSTR name, HINF parent_hinf, UINT *error )
1338{
1339 HINF child_hinf;
1340
1341 if (!name) return SetupOpenAppendInfFileW( NULL, parent_hinf, error );
1342 child_hinf = SetupOpenInfFileA( name, NULL, INF_STYLE_WIN4, error );
1343 if (child_hinf == INVALID_HANDLE_VALUE) return FALSE;
1344 append_inf_file( parent_hinf, child_hinf );
1345 TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_a(name), child_hinf );
1346 return TRUE;
1347}
1348
1349
1350/***********************************************************************
1351 * SetupOpenAppendInfFileW (SETUPAPI.@)
1352 */
1353BOOL WINAPI SetupOpenAppendInfFileW( PCWSTR name, HINF parent_hinf, UINT *error )
1354{
1355 HINF child_hinf;
1356
1357 if (!name)
1358 {
1359 INFCONTEXT context;
1360 WCHAR filename[MAX_PATH];
1361 int idx = 1;
1362
1363 if (!SetupFindFirstLineW( parent_hinf, Version, LayoutFile, &context )) return FALSE;
1364 while (SetupGetStringFieldW( &context, idx++, filename,
1365 sizeof(filename)/sizeof(WCHAR), NULL ))
1366 {
1367 child_hinf = SetupOpenInfFileW( filename, NULL, INF_STYLE_WIN4, error );
1368 if (child_hinf == INVALID_HANDLE_VALUE) return FALSE;
1369 append_inf_file( parent_hinf, child_hinf );
1370 TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(filename), child_hinf );
1371 }
1372 return TRUE;
1373 }
1374 child_hinf = SetupOpenInfFileW( name, NULL, INF_STYLE_WIN4, error );
1375 if (child_hinf == INVALID_HANDLE_VALUE) return FALSE;
1376 append_inf_file( parent_hinf, child_hinf );
1377 TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(name), child_hinf );
1378 return TRUE;
1379}
1380
1381
1382/***********************************************************************
1383 * SetupOpenMasterInf (SETUPAPI.@)
1384 */
1385HINF WINAPI SetupOpenMasterInf( VOID )
1386{
1387 static const WCHAR Layout[] = {'\\','i','n','f','\\', 'l', 'a', 'y', 'o', 'u', 't', '.', 'i', 'n', 'f', 0};
1388 WCHAR Buffer[MAX_PATH];
1389
1390 GetWindowsDirectoryW( Buffer, MAX_PATH );
1391 strcatW( Buffer, Layout );
1392 return SetupOpenInfFileW( Buffer, NULL, INF_STYLE_WIN4, NULL);
1393}
1394
1395
1396
1397/***********************************************************************
1398 * SetupCloseInfFile (SETUPAPI.@)
1399 */
1400void WINAPI SetupCloseInfFile( HINF hinf )
1401{
1402 struct inf_file *file = hinf;
1403
1404 if (!hinf || (hinf == INVALID_HANDLE_VALUE)) return;
1405
1406 free_inf_file( file );
1407}
1408
1409
1410/***********************************************************************
1411 * SetupEnumInfSectionsA (SETUPAPI.@)
1412 */
1413BOOL WINAPI SetupEnumInfSectionsA( HINF hinf, UINT index, PSTR buffer, DWORD size, DWORD *need )
1414{
1415 struct inf_file *file = hinf;
1416
1417 for (file = hinf; file; file = file->next)
1418 {
1419 if (index < file->nb_sections)
1420 {
1421 DWORD len = WideCharToMultiByte( CP_ACP, 0, file->sections[index]->name, -1,
1422 NULL, 0, NULL, NULL );
1423 if (need) *need = len;
1424 if (!buffer)
1425 {
1426 if (!size) return TRUE;
1427 SetLastError( ERROR_INVALID_USER_BUFFER );
1428 return FALSE;
1429 }
1430 if (len > size)
1431 {
1432 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1433 return FALSE;
1434 }
1435 WideCharToMultiByte( CP_ACP, 0, file->sections[index]->name, -1, buffer, size, NULL, NULL );
1436 return TRUE;
1437 }
1438 index -= file->nb_sections;
1439 }
1440 SetLastError( ERROR_NO_MORE_ITEMS );
1441 return FALSE;
1442}
1443
1444
1445/***********************************************************************
1446 * SetupEnumInfSectionsW (SETUPAPI.@)
1447 */
1448BOOL WINAPI SetupEnumInfSectionsW( HINF hinf, UINT index, PWSTR buffer, DWORD size, DWORD *need )
1449{
1450 struct inf_file *file = hinf;
1451
1452 for (file = hinf; file; file = file->next)
1453 {
1454 if (index < file->nb_sections)
1455 {
1456 DWORD len = strlenW( file->sections[index]->name ) + 1;
1457 if (need) *need = len;
1458 if (!buffer)
1459 {
1460 if (!size) return TRUE;
1461 SetLastError( ERROR_INVALID_USER_BUFFER );
1462 return FALSE;
1463 }
1464 if (len > size)
1465 {
1466 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1467 return FALSE;
1468 }
1469 memcpy( buffer, file->sections[index]->name, len * sizeof(WCHAR) );
1470 return TRUE;
1471 }
1472 index -= file->nb_sections;
1473 }
1474 SetLastError( ERROR_NO_MORE_ITEMS );
1475 return FALSE;
1476}
1477
1478
1479/***********************************************************************
1480 * SetupGetLineCountA (SETUPAPI.@)
1481 */
1482LONG WINAPI SetupGetLineCountA( HINF hinf, PCSTR name )
1483{
1484 UNICODE_STRING sectionW;
1485 LONG ret = -1;
1486
1487 if (!RtlCreateUnicodeStringFromAsciiz( §ionW, name ))
1488 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1489 else
1490 {
1491 ret = SetupGetLineCountW( hinf, sectionW.Buffer );
1492 RtlFreeUnicodeString( §ionW );
1493 }
1494 return ret;
1495}
1496
1497
1498/***********************************************************************
1499 * SetupGetLineCountW (SETUPAPI.@)
1500 */
1501LONG WINAPI SetupGetLineCountW( HINF hinf, PCWSTR section )
1502{
1503 struct inf_file *file = hinf;
1504 int section_index;
1505 LONG ret = -1;
1506
1507 for (file = hinf; file; file = file->next)
1508 {
1509 if ((section_index = find_section( file, section )) == -1) continue;
1510 if (ret == -1) ret = 0;
1511 ret += file->sections[section_index]->nb_lines;
1512 }
1513 TRACE( "(%p,%s) returning %d\n", hinf, debugstr_w(section), ret );
1514 SetLastError( (ret == -1) ? ERROR_SECTION_NOT_FOUND : 0 );
1515 return ret;
1516}
1517
1518
1519/***********************************************************************
1520 * SetupGetLineByIndexA (SETUPAPI.@)
1521 */
1522BOOL WINAPI SetupGetLineByIndexA( HINF hinf, PCSTR section, DWORD index, INFCONTEXT *context )
1523{
1524 UNICODE_STRING sectionW;
1525 BOOL ret = FALSE;
1526
1527 if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section ))
1528 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1529 else
1530 {
1531 ret = SetupGetLineByIndexW( hinf, sectionW.Buffer, index, context );
1532 RtlFreeUnicodeString( §ionW );
1533 }
1534 return ret;
1535}
1536
1537
1538/***********************************************************************
1539 * SetupGetLineByIndexW (SETUPAPI.@)
1540 */
1541BOOL WINAPI SetupGetLineByIndexW( HINF hinf, PCWSTR section, DWORD index, INFCONTEXT *context )
1542{
1543 struct inf_file *file = hinf;
1544 int section_index;
1545
1546 for (file = hinf; file; file = file->next)
1547 {
1548 if ((section_index = find_section( file, section )) == -1) continue;
1549 if (index < file->sections[section_index]->nb_lines)
1550 {
1551 context->Inf = hinf;
1552 context->CurrentInf = file;
1553 context->Section = section_index;
1554 context->Line = index;
1555 SetLastError( 0 );
1556 TRACE( "(%p,%s): returning %d/%d\n",
1557 hinf, debugstr_w(section), section_index, index );
1558 return TRUE;
1559 }
1560 index -= file->sections[section_index]->nb_lines;
1561 }
1562 TRACE( "(%p,%s) not found\n", hinf, debugstr_w(section) );
1563 SetLastError( ERROR_LINE_NOT_FOUND );
1564 return FALSE;
1565}
1566
1567
1568/***********************************************************************
1569 * SetupFindFirstLineA (SETUPAPI.@)
1570 */
1571BOOL WINAPI SetupFindFirstLineA( HINF hinf, PCSTR section, PCSTR key, INFCONTEXT *context )
1572{
1573 UNICODE_STRING sectionW, keyW;
1574 BOOL ret = FALSE;
1575
1576 if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section ))
1577 {
1578 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1579 return FALSE;
1580 }
1581
1582 if (!key) ret = SetupFindFirstLineW( hinf, sectionW.Buffer, NULL, context );
1583 else
1584 {
1585 if (RtlCreateUnicodeStringFromAsciiz( &keyW, key ))
1586 {
1587 ret = SetupFindFirstLineW( hinf, sectionW.Buffer, keyW.Buffer, context );
1588 RtlFreeUnicodeString( &keyW );
1589 }
1590 else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1591 }
1592 RtlFreeUnicodeString( §ionW );
1593 return ret;
1594}
1595
1596
1597/***********************************************************************
1598 * SetupFindFirstLineW (SETUPAPI.@)
1599 */
1600BOOL WINAPI SetupFindFirstLineW( HINF hinf, PCWSTR section, PCWSTR key, INFCONTEXT *context )
1601{
1602 struct inf_file *file;
1603 int section_index;
1604
1605 for (file = hinf; file; file = file->next)
1606 {
1607 if ((section_index = find_section( file, section )) == -1) continue;
1608 if (key)
1609 {
1610 INFCONTEXT ctx;
1611 ctx.Inf = hinf;
1612 ctx.CurrentInf = file;
1613 ctx.Section = section_index;
1614 ctx.Line = -1;
1615 return SetupFindNextMatchLineW( &ctx, key, context );
1616 }
1617 if (file->sections[section_index]->nb_lines)
1618 {
1619 context->Inf = hinf;
1620 context->CurrentInf = file;
1621 context->Section = section_index;
1622 context->Line = 0;
1623 SetLastError( 0 );
1624 TRACE( "(%p,%s,%s): returning %d/0\n",
1625 hinf, debugstr_w(section), debugstr_w(key), section_index );
1626 return TRUE;
1627 }
1628 }
1629 TRACE( "(%p,%s,%s): not found\n", hinf, debugstr_w(section), debugstr_w(key) );
1630 SetLastError( ERROR_LINE_NOT_FOUND );
1631 return FALSE;
1632}
1633
1634
1635/***********************************************************************
1636 * SetupFindNextLine (SETUPAPI.@)
1637 */
1638BOOL WINAPI SetupFindNextLine( PINFCONTEXT context_in, PINFCONTEXT context_out )
1639{
1640 struct inf_file *file = context_in->CurrentInf;
1641 struct section *section;
1642
1643 if (context_in->Section >= file->nb_sections) goto error;
1644
1645 section = file->sections[context_in->Section];
1646 if (context_in->Line+1 < section->nb_lines)
1647 {
1648 if (context_out != context_in) *context_out = *context_in;
1649 context_out->Line++;
1650 SetLastError( 0 );
1651 return TRUE;
1652 }
1653
1654 /* now search the appended files */
1655
1656 for (file = file->next; file; file = file->next)
1657 {
1658 int section_index = find_section( file, section->name );
1659 if (section_index == -1) continue;
1660 if (file->sections[section_index]->nb_lines)
1661 {
1662 context_out->Inf = context_in->Inf;
1663 context_out->CurrentInf = file;
1664 context_out->Section = section_index;
1665 context_out->Line = 0;
1666 SetLastError( 0 );
1667 return TRUE;
1668 }
1669 }
1670 error:
1671 SetLastError( ERROR_LINE_NOT_FOUND );
1672 return FALSE;
1673}
1674
1675
1676/***********************************************************************
1677 * SetupFindNextMatchLineA (SETUPAPI.@)
1678 */
1679BOOL WINAPI SetupFindNextMatchLineA( PINFCONTEXT context_in, PCSTR key,
1680 PINFCONTEXT context_out )
1681{
1682 UNICODE_STRING keyW;
1683 BOOL ret = FALSE;
1684
1685 if (!key) return SetupFindNextLine( context_in, context_out );
1686
1687 if (!RtlCreateUnicodeStringFromAsciiz( &keyW, key ))
1688 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1689 else
1690 {
1691 ret = SetupFindNextMatchLineW( context_in, keyW.Buffer, context_out );
1692 RtlFreeUnicodeString( &keyW );
1693 }
1694 return ret;
1695}
1696
1697
1698/***********************************************************************
1699 * SetupFindNextMatchLineW (SETUPAPI.@)
1700 */
1701BOOL WINAPI SetupFindNextMatchLineW( PINFCONTEXT context_in, PCWSTR key,
1702 PINFCONTEXT context_out )
1703{
1704 struct inf_file *file = context_in->CurrentInf;
1705 struct section *section;
1706 struct line *line;
1707 unsigned int i;
1708
1709 if (!key) return SetupFindNextLine( context_in, context_out );
1710
1711 if (context_in->Section >= file->nb_sections) goto error;
1712
1713 section = file->sections[context_in->Section];
1714
1715 for (i = context_in->Line+1, line = §ion->lines[i]; i < section->nb_lines; i++, line++)
1716 {
1717 if (line->key_field == -1) continue;
1718 if (!strcmpiW( key, file->fields[line->key_field].text ))
1719 {
1720 if (context_out != context_in) *context_out = *context_in;
1721 context_out->Line = i;
1722 SetLastError( 0 );
1723 TRACE( "(%p,%s,%s): returning %d\n",
1724 file, debugstr_w(section->name), debugstr_w(key), i );
1725 return TRUE;
1726 }
1727 }
1728
1729 /* now search the appended files */
1730
1731 for (file = file->next; file; file = file->next)
1732 {
1733 int section_index = find_section( file, section->name );
1734 if (section_index == -1) continue;
1735 section = file->sections[section_index];
1736 for (i = 0, line = section->lines; i < section->nb_lines; i++, line++)
1737 {
1738 if (line->key_field == -1) continue;
1739 if (!strcmpiW( key, file->fields[line->key_field].text ))
1740 {
1741 context_out->Inf = context_in->Inf;
1742 context_out->CurrentInf = file;
1743 context_out->Section = section_index;
1744 context_out->Line = i;
1745 SetLastError( 0 );
1746 TRACE( "(%p,%s,%s): returning %d/%d\n",
1747 file, debugstr_w(section->name), debugstr_w(key), section_index, i );
1748 return TRUE;
1749 }
1750 }
1751 }
1752 TRACE( "(%p,%s,%s): not found\n",
1753 context_in->CurrentInf, debugstr_w(section->name), debugstr_w(key) );
1754 error:
1755 SetLastError( ERROR_LINE_NOT_FOUND );
1756 return FALSE;
1757}
1758
1759
1760/***********************************************************************
1761 * SetupGetLineTextW (SETUPAPI.@)
1762 */
1763BOOL WINAPI SetupGetLineTextW( PINFCONTEXT context, HINF hinf, PCWSTR section_name,
1764 PCWSTR key_name, PWSTR buffer, DWORD size, PDWORD required )
1765{
1766 struct inf_file *file;
1767 struct line *line;
1768 struct field *field;
1769 int i;
1770 DWORD total = 0;
1771
1772 if (!context)
1773 {
1774 INFCONTEXT new_context;
1775 if (!SetupFindFirstLineW( hinf, section_name, key_name, &new_context )) return FALSE;
1776 file = new_context.CurrentInf;
1777 line = get_line( file, new_context.Section, new_context.Line );
1778 }
1779 else
1780 {
1781 file = context->CurrentInf;
1782 if (!(line = get_line( file, context->Section, context->Line )))
1783 {
1784 SetLastError( ERROR_LINE_NOT_FOUND );
1785 return FALSE;
1786 }
1787 }
1788
1789 for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1790 total += PARSER_string_substW( file, field->text, NULL, 0 ) + 1;
1791
1792 if (required) *required = total;
1793 if (buffer)
1794 {
1795 if (total > size)
1796 {
1797 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1798 return FALSE;
1799 }
1800 for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1801 {
1802 unsigned int len = PARSER_string_substW( file, field->text, buffer, size );
1803 if (i+1 < line->nb_fields) buffer[len] = ',';
1804 buffer += len + 1;
1805 }
1806 }
1807 return TRUE;
1808}
1809
1810
1811/***********************************************************************
1812 * SetupGetLineTextA (SETUPAPI.@)
1813 */
1814BOOL WINAPI SetupGetLineTextA( PINFCONTEXT context, HINF hinf, PCSTR section_name,
1815 PCSTR key_name, PSTR buffer, DWORD size, PDWORD required )
1816{
1817 struct inf_file *file;
1818 struct line *line;
1819 struct field *field;
1820 int i;
1821 DWORD total = 0;
1822
1823 if (!context)
1824 {
1825 INFCONTEXT new_context;
1826 if (!SetupFindFirstLineA( hinf, section_name, key_name, &new_context )) return FALSE;
1827 file = new_context.CurrentInf;
1828 line = get_line( file, new_context.Section, new_context.Line );
1829 }
1830 else
1831 {
1832 file = context->CurrentInf;
1833 if (!(line = get_line( file, context->Section, context->Line )))
1834 {
1835 SetLastError( ERROR_LINE_NOT_FOUND );
1836 return FALSE;
1837 }
1838 }
1839
1840 for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1841 total += PARSER_string_substA( file, field->text, NULL, 0 ) + 1;
1842
1843 if (required) *required = total;
1844 if (buffer)
1845 {
1846 if (total > size)
1847 {
1848 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1849 return FALSE;
1850 }
1851 for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1852 {
1853 unsigned int len = PARSER_string_substA( file, field->text, buffer, size );
1854 if (i+1 < line->nb_fields) buffer[len] = ',';
1855 buffer += len + 1;
1856 }
1857 }
1858 return TRUE;
1859}
1860
1861
1862/***********************************************************************
1863 * SetupGetFieldCount (SETUPAPI.@)
1864 */
1865DWORD WINAPI SetupGetFieldCount( PINFCONTEXT context )
1866{
1867 struct inf_file *file = context->CurrentInf;
1868 struct line *line = get_line( file, context->Section, context->Line );
1869
1870 if (!line) return 0;
1871 return line->nb_fields;
1872}
1873
1874
1875/***********************************************************************
1876 * SetupGetStringFieldA (SETUPAPI.@)
1877 */
1878BOOL WINAPI SetupGetStringFieldA( PINFCONTEXT context, DWORD index, PSTR buffer,
1879 DWORD size, PDWORD required )
1880{
1881 struct inf_file *file = context->CurrentInf;
1882 struct field *field = get_field( file, context->Section, context->Line, index );
1883 unsigned int len;
1884
1885 SetLastError(0);
1886 if (!field) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
1887 len = PARSER_string_substA( file, field->text, NULL, 0 );
1888 if (required) *required = len + 1;
1889 if (buffer)
1890 {
1891 if (size <= len)
1892 {
1893 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1894 return FALSE;
1895 }
1896 PARSER_string_substA( file, field->text, buffer, size );
1897
1898 TRACE( "context %p/%p/%d/%d index %d returning %s\n",
1899 context->Inf, context->CurrentInf, context->Section, context->Line,
1900 index, debugstr_a(buffer) );
1901 }
1902 return TRUE;
1903}
1904
1905
1906/***********************************************************************
1907 * SetupGetStringFieldW (SETUPAPI.@)
1908 */
1909BOOL WINAPI SetupGetStringFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer,
1910 DWORD size, PDWORD required )
1911{
1912 struct inf_file *file = context->CurrentInf;
1913 struct field *field = get_field( file, context->Section, context->Line, index );
1914 unsigned int len;
1915
1916 SetLastError(0);
1917 if (!field) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
1918 len = PARSER_string_substW( file, field->text, NULL, 0 );
1919 if (required) *required = len + 1;
1920 if (buffer)
1921 {
1922 if (size <= len)
1923 {
1924 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1925 return FALSE;
1926 }
1927 PARSER_string_substW( file, field->text, buffer, size );
1928
1929 TRACE( "context %p/%p/%d/%d index %d returning %s\n",
1930 context->Inf, context->CurrentInf, context->Section, context->Line,
1931 index, debugstr_w(buffer) );
1932 }
1933 return TRUE;
1934}
1935
1936
1937/***********************************************************************
1938 * SetupGetIntField (SETUPAPI.@)
1939 */
1940BOOL WINAPI SetupGetIntField( PINFCONTEXT context, DWORD index, PINT result )
1941{
1942 char localbuff[20];
1943 char *end, *buffer = localbuff;
1944 DWORD required;
1945 INT res;
1946 BOOL ret;
1947
1948 if (!(ret = SetupGetStringFieldA( context, index, localbuff, sizeof(localbuff), &required )))
1949 {
1950 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
1951 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required ))) return FALSE;
1952 if (!(ret = SetupGetStringFieldA( context, index, buffer, required, NULL ))) goto done;
1953 }
1954 /* The call to SetupGetStringFieldA succeeded. If buffer is empty we have an optional field */
1955 if (!*buffer) *result = 0;
1956 else
1957 {
1958 res = strtol( buffer, &end, 0 );
1959 if (end != buffer && !*end) *result = res;
1960 else
1961 {
1962 SetLastError( ERROR_INVALID_DATA );
1963 ret = FALSE;
1964 }
1965 }
1966
1967 done:
1968 if (buffer != localbuff) HeapFree( GetProcessHeap(), 0, buffer );
1969 return ret;
1970}
1971
1972
1973/***********************************************************************
1974 * SetupGetBinaryField (SETUPAPI.@)
1975 */
1976BOOL WINAPI SetupGetBinaryField( PINFCONTEXT context, DWORD index, BYTE *buffer,
1977 DWORD size, LPDWORD required )
1978{
1979 struct inf_file *file = context->CurrentInf;
1980 struct line *line = get_line( file, context->Section, context->Line );
1981 struct field *field;
1982 int i;
1983
1984 if (!line)
1985 {
1986 SetLastError( ERROR_LINE_NOT_FOUND );
1987 return FALSE;
1988 }
1989 if (!index || index > line->nb_fields)
1990 {
1991 SetLastError( ERROR_INVALID_PARAMETER );
1992 return FALSE;
1993 }
1994 index--; /* fields start at 0 */
1995 if (required) *required = line->nb_fields - index;
1996 if (!buffer) return TRUE;
1997 if (size < line->nb_fields - index)
1998 {
1999 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2000 return FALSE;
2001 }
2002 field = &file->fields[line->first_field + index];
2003 for (i = index; i < line->nb_fields; i++, field++)
2004 {
2005 const WCHAR *p;
2006 DWORD value = 0;
2007 for (p = field->text; *p && isxdigitW(*p); p++)
2008 {
2009 if ((value <<= 4) > 255)
2010 {
2011 SetLastError( ERROR_INVALID_DATA );
2012 return FALSE;
2013 }
2014 if (*p <= '9') value |= (*p - '0');
2015 else value |= (tolowerW(*p) - 'a' + 10);
2016 }
2017 buffer[i - index] = value;
2018 }
2019 if (TRACE_ON(setupapi))
2020 {
2021 TRACE( "%p/%p/%d/%d index %d returning:\n",
2022 context->Inf, context->CurrentInf, context->Section, context->Line, index );
2023 for (i = index; i < line->nb_fields; i++) TRACE( " %02x\n", buffer[i - index] );
2024 }
2025 return TRUE;
2026}
2027
2028
2029/***********************************************************************
2030 * SetupGetMultiSzFieldA (SETUPAPI.@)
2031 */
2032BOOL WINAPI SetupGetMultiSzFieldA( PINFCONTEXT context, DWORD index, PSTR buffer,
2033 DWORD size, LPDWORD required )
2034{
2035 struct inf_file *file = context->CurrentInf;
2036 struct line *line = get_line( file, context->Section, context->Line );
2037 struct field *field;
2038 unsigned int len;
2039 int i;
2040 DWORD total = 1;
2041
2042 if (!line)
2043 {
2044 SetLastError( ERROR_LINE_NOT_FOUND );
2045 return FALSE;
2046 }
2047 if (!index || index > line->nb_fields)
2048 {
2049 SetLastError( ERROR_INVALID_PARAMETER );
2050 return FALSE;
2051 }
2052 index--; /* fields start at 0 */
2053 field = &file->fields[line->first_field + index];
2054 for (i = index; i < line->nb_fields; i++, field++)
2055 {
2056 if (!(len = PARSER_string_substA( file, field->text, NULL, 0 ))) break;
2057 total += len + 1;
2058 }
2059
2060 if (required) *required = total;
2061 if (!buffer) return TRUE;
2062 if (total > size)
2063 {
2064 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2065 return FALSE;
2066 }
2067 field = &file->fields[line->first_field + index];
2068 for (i = index; i < line->nb_fields; i++, field++)
2069 {
2070 if (!(len = PARSER_string_substA( file, field->text, buffer, size ))) break;
2071 buffer += len + 1;
2072 }
2073 *buffer = 0; /* add final null */
2074 return TRUE;
2075}
2076
2077
2078/***********************************************************************
2079 * SetupGetMultiSzFieldW (SETUPAPI.@)
2080 */
2081BOOL WINAPI SetupGetMultiSzFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer,
2082 DWORD size, LPDWORD required )
2083{
2084 struct inf_file *file = context->CurrentInf;
2085 struct line *line = get_line( file, context->Section, context->Line );
2086 struct field *field;
2087 unsigned int len;
2088 int i;
2089 DWORD total = 1;
2090
2091 if (!line)
2092 {
2093 SetLastError( ERROR_LINE_NOT_FOUND );
2094 return FALSE;
2095 }
2096 if (!index || index > line->nb_fields)
2097 {
2098 SetLastError( ERROR_INVALID_PARAMETER );
2099 return FALSE;
2100 }
2101 index--; /* fields start at 0 */
2102 field = &file->fields[line->first_field + index];
2103 for (i = index; i < line->nb_fields; i++, field++)
2104 {
2105 if (!(len = PARSER_string_substW( file, field->text, NULL, 0 ))) break;
2106 total += len + 1;
2107 }
2108
2109 if (required) *required = total;
2110 if (!buffer) return TRUE;
2111 if (total > size)
2112 {
2113 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2114 return FALSE;
2115 }
2116 field = &file->fields[line->first_field + index];
2117 for (i = index; i < line->nb_fields; i++, field++)
2118 {
2119 if (!(len = PARSER_string_substW( file, field->text, buffer, size ))) break;
2120 buffer += len + 1;
2121 }
2122 *buffer = 0; /* add final null */
2123 return TRUE;
2124}
2125
2126/***********************************************************************
2127 * pSetupGetField (SETUPAPI.@)
2128 */
2129LPCWSTR WINAPI pSetupGetField( PINFCONTEXT context, DWORD index )
2130{
2131 struct inf_file *file = context->CurrentInf;
2132 struct field *field = get_field( file, context->Section, context->Line, index );
2133
2134 if (!field)
2135 {
2136 SetLastError( ERROR_INVALID_PARAMETER );
2137 return NULL;
2138 }
2139 return field->text;
2140}
2141
2142/***********************************************************************
2143 * SetupGetInfFileListW (SETUPAPI.@)
2144 */
2145BOOL WINAPI
2146SetupGetInfFileListW(
2147 IN PCWSTR DirectoryPath OPTIONAL,
2148 IN DWORD InfStyle,
2149 IN OUT PWSTR ReturnBuffer OPTIONAL,
2150 IN DWORD ReturnBufferSize OPTIONAL,
2151 OUT PDWORD RequiredSize OPTIONAL)
2152{
2153 HANDLE hSearch;
2154 LPWSTR pFullFileName = NULL;
2155 LPWSTR pFileName; /* Pointer into pFullFileName buffer */
2156 LPWSTR pBuffer = ReturnBuffer;
2157 WIN32_FIND_DATAW wfdFileInfo;
2158 size_t len;
2159 DWORD requiredSize = 0;
2160 BOOL ret = FALSE;
2161
2162 TRACE("%s %lx %p %ld %p\n", debugstr_w(DirectoryPath), InfStyle,
2163 ReturnBuffer, ReturnBufferSize, RequiredSize);
2164
2165 if (InfStyle & ~(INF_STYLE_OLDNT | INF_STYLE_WIN4))
2166 {
2167 TRACE("Unknown flags: 0x%08lx\n", InfStyle & ~(INF_STYLE_OLDNT | INF_STYLE_WIN4));
2168 SetLastError(ERROR_INVALID_PARAMETER);
2169 goto cleanup;
2170 }
2171 else if (ReturnBufferSize == 0 && ReturnBuffer != NULL)
2172 {
2173 SetLastError(ERROR_INVALID_PARAMETER);
2174 goto cleanup;
2175 }
2176 else if (ReturnBufferSize > 0 && ReturnBuffer == NULL)
2177 {
2178 SetLastError(ERROR_INVALID_PARAMETER);
2179 goto cleanup;
2180 }
2181
2182 /* Allocate memory for file filter */
2183 if (DirectoryPath != NULL)
2184 /* "DirectoryPath\" form */
2185 len = strlenW(DirectoryPath) + 1 + 1;
2186 else
2187 /* "%SYSTEMROOT%\Inf\" form */
2188 len = MAX_PATH + 1 + strlenW(InfDirectory) + 1;
2189 len += MAX_PATH; /* To contain file name or "*.inf" string */
2190 pFullFileName = MyMalloc(len * sizeof(WCHAR));
2191 if (pFullFileName == NULL)
2192 {
2193 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2194 goto cleanup;
2195 }
2196
2197 /* Fill file filter buffer */
2198 if (DirectoryPath)
2199 {
2200 strcpyW(pFullFileName, DirectoryPath);
2201 if (*pFullFileName && pFullFileName[strlenW(pFullFileName) - 1] != '\\')
2202 strcatW(pFullFileName, BackSlash);
2203 }
2204 else
2205 {
2206 len = GetSystemWindowsDirectoryW(pFullFileName, MAX_PATH);
2207 if (len == 0 || len > MAX_PATH)
2208 goto cleanup;
2209 if (pFullFileName[strlenW(pFullFileName) - 1] != '\\')
2210 strcatW(pFullFileName, BackSlash);
2211 strcatW(pFullFileName, InfDirectory);
2212 }
2213 pFileName = &pFullFileName[strlenW(pFullFileName)];
2214
2215 /* Search for the first file */
2216 strcpyW(pFileName, InfFileSpecification);
2217 hSearch = FindFirstFileW(pFullFileName, &wfdFileInfo);
2218 if (hSearch == INVALID_HANDLE_VALUE)
2219 {
2220 TRACE("No file returned by %s\n", debugstr_w(pFullFileName));
2221 goto cleanup;
2222 }
2223
2224 do
2225 {
2226 HINF hInf;
2227
2228 strcpyW(pFileName, wfdFileInfo.cFileName);
2229 hInf = SetupOpenInfFileW(
2230 pFullFileName,
2231 NULL, /* Inf class */
2232 InfStyle,
2233 NULL /* Error line */);
2234 if (hInf == INVALID_HANDLE_VALUE)
2235 {
2236 if (GetLastError() == ERROR_CLASS_MISMATCH)
2237 {
2238 /* InfStyle was not correct. Skip this file */
2239 continue;
2240 }
2241 TRACE("Invalid .inf file %s\n", debugstr_w(pFullFileName));
2242 continue;
2243 }
2244
2245 len = strlenW(wfdFileInfo.cFileName) + 1;
2246 requiredSize += (DWORD)len;
2247 if (requiredSize <= ReturnBufferSize)
2248 {
2249 strcpyW(pBuffer, wfdFileInfo.cFileName);
2250 pBuffer = &pBuffer[len];
2251 }
2252 SetupCloseInfFile(hInf);
2253 } while (FindNextFileW(hSearch, &wfdFileInfo));
2254 FindClose(hSearch);
2255
2256 requiredSize += 1; /* Final NULL char */
2257 if (requiredSize <= ReturnBufferSize)
2258 {
2259 *pBuffer = '\0';
2260 ret = TRUE;
2261 }
2262 else
2263 {
2264 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2265 ret = FALSE;
2266 }
2267 if (RequiredSize)
2268 *RequiredSize = requiredSize;
2269
2270cleanup:
2271 MyFree(pFullFileName);
2272 return ret;
2273}
2274
2275/***********************************************************************
2276 * SetupGetInfFileListA (SETUPAPI.@)
2277 */
2278BOOL WINAPI
2279SetupGetInfFileListA(
2280 IN PCSTR DirectoryPath OPTIONAL,
2281 IN DWORD InfStyle,
2282 IN OUT PSTR ReturnBuffer OPTIONAL,
2283 IN DWORD ReturnBufferSize OPTIONAL,
2284 OUT PDWORD RequiredSize OPTIONAL)
2285{
2286 PWSTR DirectoryPathW = NULL;
2287 PWSTR ReturnBufferW = NULL;
2288 BOOL ret = FALSE;
2289
2290 TRACE("%s %lx %p %ld %p\n", debugstr_a(DirectoryPath), InfStyle,
2291 ReturnBuffer, ReturnBufferSize, RequiredSize);
2292
2293 if (DirectoryPath != NULL)
2294 {
2295 DirectoryPathW = pSetupMultiByteToUnicode(DirectoryPath, CP_ACP);
2296 if (DirectoryPathW == NULL) goto Cleanup;
2297 }
2298
2299 if (ReturnBuffer != NULL && ReturnBufferSize != 0)
2300 {
2301 ReturnBufferW = MyMalloc(ReturnBufferSize * sizeof(WCHAR));
2302 if (ReturnBufferW == NULL) goto Cleanup;
2303 }
2304
2305 ret = SetupGetInfFileListW(DirectoryPathW, InfStyle, ReturnBufferW, ReturnBufferSize, RequiredSize);
2306
2307 if (ret && ReturnBufferW != NULL)
2308 {
2309 ret = WideCharToMultiByte(CP_ACP, 0, ReturnBufferW, -1, ReturnBuffer, ReturnBufferSize, NULL, NULL) != 0;
2310 }
2311
2312Cleanup:
2313 MyFree(DirectoryPathW);
2314 MyFree(ReturnBufferW);
2315
2316 return ret;
2317}
2318
2319/***********************************************************************
2320 * SetupDiGetINFClassW (SETUPAPI.@)
2321 */
2322BOOL WINAPI
2323SetupDiGetINFClassW(
2324 IN PCWSTR InfName,
2325 OUT LPGUID ClassGuid,
2326 OUT PWSTR ClassName,
2327 IN DWORD ClassNameSize,
2328 OUT PDWORD RequiredSize OPTIONAL)
2329{
2330 HINF hInf = INVALID_HANDLE_VALUE;
2331 BOOL ret = FALSE;
2332
2333 TRACE("%s %p %p %ld %p\n", debugstr_w(InfName), ClassGuid,
2334 ClassName, ClassNameSize, RequiredSize);
2335
2336 if (!InfName || !ClassGuid || !ClassName || ClassNameSize == 0)
2337 {
2338 SetLastError(ERROR_INVALID_PARAMETER);
2339 return FALSE;
2340 }
2341
2342 /* Open .inf file */
2343 hInf = SetupOpenInfFileW(InfName, NULL, INF_STYLE_WIN4, NULL);
2344 if (hInf == INVALID_HANDLE_VALUE)
2345 goto cleanup;
2346
2347 ret = PARSER_GetInfClassW(hInf, ClassGuid, ClassName, ClassNameSize, RequiredSize);
2348
2349cleanup:
2350 if (hInf != INVALID_HANDLE_VALUE)
2351 SetupCloseInfFile(hInf);
2352
2353 TRACE("Returning %d\n", ret);
2354 return ret;
2355}
2356
2357/***********************************************************************
2358 * SetupDiGetINFClassA (SETUPAPI.@)
2359 */
2360BOOL WINAPI SetupDiGetINFClassA(
2361 IN PCSTR InfName,
2362 OUT LPGUID ClassGuid,
2363 OUT PSTR ClassName,
2364 IN DWORD ClassNameSize,
2365 OUT PDWORD RequiredSize OPTIONAL)
2366{
2367 PWSTR InfNameW = NULL;
2368 PWSTR ClassNameW = NULL;
2369 BOOL ret = FALSE;
2370
2371 TRACE("%s %p %p %ld %p\n", debugstr_a(InfName), ClassGuid,
2372 ClassName, ClassNameSize, RequiredSize);
2373
2374 if (InfName != NULL)
2375 {
2376 InfNameW = pSetupMultiByteToUnicode(InfName, CP_ACP);
2377 if (InfNameW == NULL) goto Cleanup;
2378 }
2379
2380 if (ClassName != NULL && ClassNameSize != 0)
2381 {
2382 ClassNameW = MyMalloc(ClassNameSize * sizeof(WCHAR));
2383 if (ClassNameW == NULL) goto Cleanup;
2384 }
2385
2386 ret = SetupDiGetINFClassW(InfNameW, ClassGuid, ClassNameW, ClassNameSize, RequiredSize);
2387
2388 if (ret && ClassNameW != NULL)
2389 {
2390 ret = WideCharToMultiByte(CP_ACP, 0, ClassNameW, -1, ClassName, ClassNameSize, NULL, NULL) != 0;
2391 }
2392
2393Cleanup:
2394 MyFree(InfNameW);
2395 MyFree(ClassNameW);
2396
2397 return ret;
2398}
2399
2400BOOL EnumerateSectionsStartingWith(
2401 IN HINF hInf,
2402 IN LPCWSTR pStr,
2403 IN FIND_CALLBACK Callback,
2404 IN PVOID Context)
2405{
2406 struct inf_file *file = (struct inf_file *)hInf;
2407 size_t len = strlenW(pStr);
2408 unsigned int i;
2409
2410 for (i = 0; i < file->nb_sections; i++)
2411 {
2412 if (strncmpiW(pStr, file->sections[i]->name, len) == 0)
2413 {
2414 if (!Callback(file->sections[i]->name, Context))
2415 return FALSE;
2416 }
2417 }
2418 return TRUE;
2419}