[SETUPAPI] Fix broken control definition.
[reactos.git] / dll / win32 / setupapi / parser.c
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 */
29 static const WCHAR BackSlash[] = {'\\',0};
30 static const WCHAR Class[] = {'C','l','a','s','s',0};
31 static const WCHAR ClassGUID[] = {'C','l','a','s','s','G','U','I','D',0};
32 static const WCHAR InfDirectory[] = {'i','n','f','\\',0};
33 static 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
43 struct field
44 {
45 const WCHAR *text; /* field text */
46 };
47
48 struct 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
55 struct 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
63 struct 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
80 enum 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
94 struct 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
112 typedef const WCHAR * (*parser_state_func)( struct parser *parser, const WCHAR *pos );
113
114 /* parser state machine functions */
115 static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos );
116 static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos );
117 static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos );
118 static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos );
119 static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos );
120 static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos );
121 static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos );
122 static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos );
123 static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos );
124
125 static 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 */
140 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
141 static const WCHAR Signature[] = {'S','i','g','n','a','t','u','r','e',0};
142 static const WCHAR Chicago[] = {'$','C','h','i','c','a','g','o','$',0};
143 static const WCHAR WindowsNT[] = {'$','W','i','n','d','o','w','s',' ','N','T','$',0};
144 static const WCHAR Windows95[] = {'$','W','i','n','d','o','w','s',' ','9','5','$',0};
145 static const WCHAR LayoutFile[] = {'L','a','y','o','u','t','F','i','l','e',0};
146
147 /* extend an array, allocating more memory if necessary */
148 static 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) */
168 static 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 */
177 static 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 */
188 static 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 */
206 static 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 */
225 static 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 = &section->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 */
249 static 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 &section->lines[line_index];
258 }
259
260
261 /* retrieve a given field from section/line/field index */
262 static 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 */
280 static 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 */
296 static 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 */
309 static 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 */
403 static 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 */
456 static 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 */
474 static 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 */
484 static 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 */
492 static 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 */
500 static 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 */
509 static 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 */
516 static 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 */
523 static 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 */
540 static 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 */
566 static 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 */
602 static 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 */
617 static 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 */
656 static 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 */
677 static 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 */
731 static 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 */
784 static 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 */
817 static 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 */
847 static 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 */
868 static 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 */
887 static 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
897 static 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 */
911 static 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 */
970 static 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 */
989 static 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 */
1084 const 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 */
1096 WCHAR *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 */
1116 WCHAR *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 */
1139 HINF 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
1160 static BOOL
1161 PARSER_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
1220 cleanup:
1221 TRACE("Returning %d\n", ret);
1222 return ret;
1223 }
1224
1225
1226 /***********************************************************************
1227 * SetupOpenInfFileW (SETUPAPI.@)
1228 */
1229 HINF 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 if (style & ~(INF_STYLE_OLDNT | INF_STYLE_WIN4))
1239 {
1240 SetLastError(ERROR_INVALID_PARAMETER);
1241 return (HINF)INVALID_HANDLE_VALUE;
1242 }
1243
1244 if (strchrW( name, '\\' ) || strchrW( name, '/' ))
1245 {
1246 if (!(len = GetFullPathNameW( name, 0, NULL, NULL ))) return INVALID_HANDLE_VALUE;
1247 if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1248 {
1249 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1250 return INVALID_HANDLE_VALUE;
1251 }
1252 GetFullPathNameW( name, len, path, NULL );
1253 handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1254 }
1255 else /* try Windows directory */
1256 {
1257 static const WCHAR Inf[] = {'\\','i','n','f','\\',0};
1258 static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
1259
1260 len = GetWindowsDirectoryW( NULL, 0 ) + strlenW(name) + 12;
1261 if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1262 {
1263 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1264 return INVALID_HANDLE_VALUE;
1265 }
1266 GetWindowsDirectoryW( path, len );
1267 p = path + strlenW(path);
1268 strcpyW( p, Inf );
1269 strcatW( p, name );
1270 handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1271 if (handle == INVALID_HANDLE_VALUE)
1272 {
1273 strcpyW( p, System32 );
1274 strcatW( p, name );
1275 handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1276 }
1277 }
1278
1279 if (handle != INVALID_HANDLE_VALUE)
1280 {
1281 file = parse_file( handle, error, style);
1282 CloseHandle( handle );
1283 }
1284 if (!file)
1285 {
1286 HeapFree( GetProcessHeap(), 0, path );
1287 return INVALID_HANDLE_VALUE;
1288 }
1289 TRACE( "%s -> %p\n", debugstr_w(path), file );
1290 file->filename = path;
1291
1292 if (class)
1293 {
1294 GUID ClassGuid;
1295 LPWSTR ClassName = HeapAlloc(GetProcessHeap(), 0, (strlenW(class) + 1) * sizeof(WCHAR));
1296 if (!ClassName)
1297 {
1298 /* Not enough memory */
1299 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1300 SetupCloseInfFile((HINF)file);
1301 return INVALID_HANDLE_VALUE;
1302 }
1303 else if (!PARSER_GetInfClassW((HINF)file, &ClassGuid, ClassName, strlenW(class) + 1, NULL))
1304 {
1305 /* Unable to get class name in .inf file */
1306 HeapFree(GetProcessHeap(), 0, ClassName);
1307 SetLastError(ERROR_CLASS_MISMATCH);
1308 SetupCloseInfFile((HINF)file);
1309 return INVALID_HANDLE_VALUE;
1310 }
1311 else if (strcmpW(class, ClassName) != 0)
1312 {
1313 /* Provided name name is not the expected one */
1314 HeapFree(GetProcessHeap(), 0, ClassName);
1315 SetLastError(ERROR_CLASS_MISMATCH);
1316 SetupCloseInfFile((HINF)file);
1317 return INVALID_HANDLE_VALUE;
1318 }
1319 HeapFree(GetProcessHeap(), 0, ClassName);
1320 }
1321
1322 SetLastError( 0 );
1323 return file;
1324 }
1325
1326
1327 /***********************************************************************
1328 * SetupOpenAppendInfFileA (SETUPAPI.@)
1329 */
1330 BOOL WINAPI SetupOpenAppendInfFileA( PCSTR name, HINF parent_hinf, UINT *error )
1331 {
1332 HINF child_hinf;
1333
1334 if (!name) return SetupOpenAppendInfFileW( NULL, parent_hinf, error );
1335 child_hinf = SetupOpenInfFileA( name, NULL, INF_STYLE_WIN4, error );
1336 if (child_hinf == INVALID_HANDLE_VALUE) return FALSE;
1337 append_inf_file( parent_hinf, child_hinf );
1338 TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_a(name), child_hinf );
1339 return TRUE;
1340 }
1341
1342
1343 /***********************************************************************
1344 * SetupOpenAppendInfFileW (SETUPAPI.@)
1345 */
1346 BOOL WINAPI SetupOpenAppendInfFileW( PCWSTR name, HINF parent_hinf, UINT *error )
1347 {
1348 HINF child_hinf;
1349
1350 if (!name)
1351 {
1352 INFCONTEXT context;
1353 WCHAR filename[MAX_PATH];
1354 int idx = 1;
1355
1356 if (!SetupFindFirstLineW( parent_hinf, Version, LayoutFile, &context )) return FALSE;
1357 while (SetupGetStringFieldW( &context, idx++, filename,
1358 sizeof(filename)/sizeof(WCHAR), NULL ))
1359 {
1360 child_hinf = SetupOpenInfFileW( filename, NULL, INF_STYLE_WIN4, error );
1361 if (child_hinf == INVALID_HANDLE_VALUE) return FALSE;
1362 append_inf_file( parent_hinf, child_hinf );
1363 TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(filename), child_hinf );
1364 }
1365 return TRUE;
1366 }
1367 child_hinf = SetupOpenInfFileW( name, 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(name), child_hinf );
1371 return TRUE;
1372 }
1373
1374
1375 /***********************************************************************
1376 * SetupOpenMasterInf (SETUPAPI.@)
1377 */
1378 HINF WINAPI SetupOpenMasterInf( VOID )
1379 {
1380 static const WCHAR Layout[] = {'\\','i','n','f','\\', 'l', 'a', 'y', 'o', 'u', 't', '.', 'i', 'n', 'f', 0};
1381 WCHAR Buffer[MAX_PATH];
1382
1383 GetWindowsDirectoryW( Buffer, MAX_PATH );
1384 strcatW( Buffer, Layout );
1385 return SetupOpenInfFileW( Buffer, NULL, INF_STYLE_WIN4, NULL);
1386 }
1387
1388
1389
1390 /***********************************************************************
1391 * SetupCloseInfFile (SETUPAPI.@)
1392 */
1393 void WINAPI SetupCloseInfFile( HINF hinf )
1394 {
1395 struct inf_file *file = hinf;
1396
1397 if (!hinf || (hinf == INVALID_HANDLE_VALUE)) return;
1398
1399 free_inf_file( file );
1400 }
1401
1402
1403 /***********************************************************************
1404 * SetupEnumInfSectionsA (SETUPAPI.@)
1405 */
1406 BOOL WINAPI SetupEnumInfSectionsA( HINF hinf, UINT index, PSTR buffer, DWORD size, DWORD *need )
1407 {
1408 struct inf_file *file = hinf;
1409
1410 for (file = hinf; file; file = file->next)
1411 {
1412 if (index < file->nb_sections)
1413 {
1414 DWORD len = WideCharToMultiByte( CP_ACP, 0, file->sections[index]->name, -1,
1415 NULL, 0, NULL, NULL );
1416 if (need) *need = len;
1417 if (!buffer)
1418 {
1419 if (!size) return TRUE;
1420 SetLastError( ERROR_INVALID_USER_BUFFER );
1421 return FALSE;
1422 }
1423 if (len > size)
1424 {
1425 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1426 return FALSE;
1427 }
1428 WideCharToMultiByte( CP_ACP, 0, file->sections[index]->name, -1, buffer, size, NULL, NULL );
1429 return TRUE;
1430 }
1431 index -= file->nb_sections;
1432 }
1433 SetLastError( ERROR_NO_MORE_ITEMS );
1434 return FALSE;
1435 }
1436
1437
1438 /***********************************************************************
1439 * SetupEnumInfSectionsW (SETUPAPI.@)
1440 */
1441 BOOL WINAPI SetupEnumInfSectionsW( HINF hinf, UINT index, PWSTR buffer, DWORD size, DWORD *need )
1442 {
1443 struct inf_file *file = hinf;
1444
1445 for (file = hinf; file; file = file->next)
1446 {
1447 if (index < file->nb_sections)
1448 {
1449 DWORD len = strlenW( file->sections[index]->name ) + 1;
1450 if (need) *need = len;
1451 if (!buffer)
1452 {
1453 if (!size) return TRUE;
1454 SetLastError( ERROR_INVALID_USER_BUFFER );
1455 return FALSE;
1456 }
1457 if (len > size)
1458 {
1459 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1460 return FALSE;
1461 }
1462 memcpy( buffer, file->sections[index]->name, len * sizeof(WCHAR) );
1463 return TRUE;
1464 }
1465 index -= file->nb_sections;
1466 }
1467 SetLastError( ERROR_NO_MORE_ITEMS );
1468 return FALSE;
1469 }
1470
1471
1472 /***********************************************************************
1473 * SetupGetLineCountA (SETUPAPI.@)
1474 */
1475 LONG WINAPI SetupGetLineCountA( HINF hinf, PCSTR name )
1476 {
1477 UNICODE_STRING sectionW;
1478 LONG ret = -1;
1479
1480 if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, name ))
1481 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1482 else
1483 {
1484 ret = SetupGetLineCountW( hinf, sectionW.Buffer );
1485 RtlFreeUnicodeString( &sectionW );
1486 }
1487 return ret;
1488 }
1489
1490
1491 /***********************************************************************
1492 * SetupGetLineCountW (SETUPAPI.@)
1493 */
1494 LONG WINAPI SetupGetLineCountW( HINF hinf, PCWSTR section )
1495 {
1496 struct inf_file *file = hinf;
1497 int section_index;
1498 LONG ret = -1;
1499
1500 for (file = hinf; file; file = file->next)
1501 {
1502 if ((section_index = find_section( file, section )) == -1) continue;
1503 if (ret == -1) ret = 0;
1504 ret += file->sections[section_index]->nb_lines;
1505 }
1506 TRACE( "(%p,%s) returning %d\n", hinf, debugstr_w(section), ret );
1507 SetLastError( (ret == -1) ? ERROR_SECTION_NOT_FOUND : 0 );
1508 return ret;
1509 }
1510
1511
1512 /***********************************************************************
1513 * SetupGetLineByIndexA (SETUPAPI.@)
1514 */
1515 BOOL WINAPI SetupGetLineByIndexA( HINF hinf, PCSTR section, DWORD index, INFCONTEXT *context )
1516 {
1517 UNICODE_STRING sectionW;
1518 BOOL ret = FALSE;
1519
1520 if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1521 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1522 else
1523 {
1524 ret = SetupGetLineByIndexW( hinf, sectionW.Buffer, index, context );
1525 RtlFreeUnicodeString( &sectionW );
1526 }
1527 return ret;
1528 }
1529
1530
1531 /***********************************************************************
1532 * SetupGetLineByIndexW (SETUPAPI.@)
1533 */
1534 BOOL WINAPI SetupGetLineByIndexW( HINF hinf, PCWSTR section, DWORD index, INFCONTEXT *context )
1535 {
1536 struct inf_file *file = hinf;
1537 int section_index;
1538
1539 for (file = hinf; file; file = file->next)
1540 {
1541 if ((section_index = find_section( file, section )) == -1) continue;
1542 if (index < file->sections[section_index]->nb_lines)
1543 {
1544 context->Inf = hinf;
1545 context->CurrentInf = file;
1546 context->Section = section_index;
1547 context->Line = index;
1548 SetLastError( 0 );
1549 TRACE( "(%p,%s): returning %d/%d\n",
1550 hinf, debugstr_w(section), section_index, index );
1551 return TRUE;
1552 }
1553 index -= file->sections[section_index]->nb_lines;
1554 }
1555 TRACE( "(%p,%s) not found\n", hinf, debugstr_w(section) );
1556 SetLastError( ERROR_LINE_NOT_FOUND );
1557 return FALSE;
1558 }
1559
1560
1561 /***********************************************************************
1562 * SetupFindFirstLineA (SETUPAPI.@)
1563 */
1564 BOOL WINAPI SetupFindFirstLineA( HINF hinf, PCSTR section, PCSTR key, INFCONTEXT *context )
1565 {
1566 UNICODE_STRING sectionW, keyW;
1567 BOOL ret = FALSE;
1568
1569 if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1570 {
1571 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1572 return FALSE;
1573 }
1574
1575 if (!key) ret = SetupFindFirstLineW( hinf, sectionW.Buffer, NULL, context );
1576 else
1577 {
1578 if (RtlCreateUnicodeStringFromAsciiz( &keyW, key ))
1579 {
1580 ret = SetupFindFirstLineW( hinf, sectionW.Buffer, keyW.Buffer, context );
1581 RtlFreeUnicodeString( &keyW );
1582 }
1583 else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1584 }
1585 RtlFreeUnicodeString( &sectionW );
1586 return ret;
1587 }
1588
1589
1590 /***********************************************************************
1591 * SetupFindFirstLineW (SETUPAPI.@)
1592 */
1593 BOOL WINAPI SetupFindFirstLineW( HINF hinf, PCWSTR section, PCWSTR key, INFCONTEXT *context )
1594 {
1595 struct inf_file *file;
1596 int section_index;
1597
1598 for (file = hinf; file; file = file->next)
1599 {
1600 if ((section_index = find_section( file, section )) == -1) continue;
1601 if (key)
1602 {
1603 INFCONTEXT ctx;
1604 ctx.Inf = hinf;
1605 ctx.CurrentInf = file;
1606 ctx.Section = section_index;
1607 ctx.Line = -1;
1608 return SetupFindNextMatchLineW( &ctx, key, context );
1609 }
1610 if (file->sections[section_index]->nb_lines)
1611 {
1612 context->Inf = hinf;
1613 context->CurrentInf = file;
1614 context->Section = section_index;
1615 context->Line = 0;
1616 SetLastError( 0 );
1617 TRACE( "(%p,%s,%s): returning %d/0\n",
1618 hinf, debugstr_w(section), debugstr_w(key), section_index );
1619 return TRUE;
1620 }
1621 }
1622 TRACE( "(%p,%s,%s): not found\n", hinf, debugstr_w(section), debugstr_w(key) );
1623 SetLastError( ERROR_LINE_NOT_FOUND );
1624 return FALSE;
1625 }
1626
1627
1628 /***********************************************************************
1629 * SetupFindNextLine (SETUPAPI.@)
1630 */
1631 BOOL WINAPI SetupFindNextLine( PINFCONTEXT context_in, PINFCONTEXT context_out )
1632 {
1633 struct inf_file *file = context_in->CurrentInf;
1634 struct section *section;
1635
1636 if (context_in->Section >= file->nb_sections) goto error;
1637
1638 section = file->sections[context_in->Section];
1639 if (context_in->Line+1 < section->nb_lines)
1640 {
1641 if (context_out != context_in) *context_out = *context_in;
1642 context_out->Line++;
1643 SetLastError( 0 );
1644 return TRUE;
1645 }
1646
1647 /* now search the appended files */
1648
1649 for (file = file->next; file; file = file->next)
1650 {
1651 int section_index = find_section( file, section->name );
1652 if (section_index == -1) continue;
1653 if (file->sections[section_index]->nb_lines)
1654 {
1655 context_out->Inf = context_in->Inf;
1656 context_out->CurrentInf = file;
1657 context_out->Section = section_index;
1658 context_out->Line = 0;
1659 SetLastError( 0 );
1660 return TRUE;
1661 }
1662 }
1663 error:
1664 SetLastError( ERROR_LINE_NOT_FOUND );
1665 return FALSE;
1666 }
1667
1668
1669 /***********************************************************************
1670 * SetupFindNextMatchLineA (SETUPAPI.@)
1671 */
1672 BOOL WINAPI SetupFindNextMatchLineA( PINFCONTEXT context_in, PCSTR key,
1673 PINFCONTEXT context_out )
1674 {
1675 UNICODE_STRING keyW;
1676 BOOL ret = FALSE;
1677
1678 if (!key) return SetupFindNextLine( context_in, context_out );
1679
1680 if (!RtlCreateUnicodeStringFromAsciiz( &keyW, key ))
1681 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1682 else
1683 {
1684 ret = SetupFindNextMatchLineW( context_in, keyW.Buffer, context_out );
1685 RtlFreeUnicodeString( &keyW );
1686 }
1687 return ret;
1688 }
1689
1690
1691 /***********************************************************************
1692 * SetupFindNextMatchLineW (SETUPAPI.@)
1693 */
1694 BOOL WINAPI SetupFindNextMatchLineW( PINFCONTEXT context_in, PCWSTR key,
1695 PINFCONTEXT context_out )
1696 {
1697 struct inf_file *file = context_in->CurrentInf;
1698 struct section *section;
1699 struct line *line;
1700 unsigned int i;
1701
1702 if (!key) return SetupFindNextLine( context_in, context_out );
1703
1704 if (context_in->Section >= file->nb_sections) goto error;
1705
1706 section = file->sections[context_in->Section];
1707
1708 for (i = context_in->Line+1, line = &section->lines[i]; i < section->nb_lines; i++, line++)
1709 {
1710 if (line->key_field == -1) continue;
1711 if (!strcmpiW( key, file->fields[line->key_field].text ))
1712 {
1713 if (context_out != context_in) *context_out = *context_in;
1714 context_out->Line = i;
1715 SetLastError( 0 );
1716 TRACE( "(%p,%s,%s): returning %d\n",
1717 file, debugstr_w(section->name), debugstr_w(key), i );
1718 return TRUE;
1719 }
1720 }
1721
1722 /* now search the appended files */
1723
1724 for (file = file->next; file; file = file->next)
1725 {
1726 int section_index = find_section( file, section->name );
1727 if (section_index == -1) continue;
1728 section = file->sections[section_index];
1729 for (i = 0, line = section->lines; i < section->nb_lines; i++, line++)
1730 {
1731 if (line->key_field == -1) continue;
1732 if (!strcmpiW( key, file->fields[line->key_field].text ))
1733 {
1734 context_out->Inf = context_in->Inf;
1735 context_out->CurrentInf = file;
1736 context_out->Section = section_index;
1737 context_out->Line = i;
1738 SetLastError( 0 );
1739 TRACE( "(%p,%s,%s): returning %d/%d\n",
1740 file, debugstr_w(section->name), debugstr_w(key), section_index, i );
1741 return TRUE;
1742 }
1743 }
1744 }
1745 TRACE( "(%p,%s,%s): not found\n",
1746 context_in->CurrentInf, debugstr_w(section->name), debugstr_w(key) );
1747 error:
1748 SetLastError( ERROR_LINE_NOT_FOUND );
1749 return FALSE;
1750 }
1751
1752
1753 /***********************************************************************
1754 * SetupGetLineTextW (SETUPAPI.@)
1755 */
1756 BOOL WINAPI SetupGetLineTextW( PINFCONTEXT context, HINF hinf, PCWSTR section_name,
1757 PCWSTR key_name, PWSTR buffer, DWORD size, PDWORD required )
1758 {
1759 struct inf_file *file;
1760 struct line *line;
1761 struct field *field;
1762 int i;
1763 DWORD total = 0;
1764
1765 if (!context)
1766 {
1767 INFCONTEXT new_context;
1768 if (!SetupFindFirstLineW( hinf, section_name, key_name, &new_context )) return FALSE;
1769 file = new_context.CurrentInf;
1770 line = get_line( file, new_context.Section, new_context.Line );
1771 }
1772 else
1773 {
1774 file = context->CurrentInf;
1775 if (!(line = get_line( file, context->Section, context->Line )))
1776 {
1777 SetLastError( ERROR_LINE_NOT_FOUND );
1778 return FALSE;
1779 }
1780 }
1781
1782 for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1783 total += PARSER_string_substW( file, field->text, NULL, 0 ) + 1;
1784
1785 if (required) *required = total;
1786 if (buffer)
1787 {
1788 if (total > size)
1789 {
1790 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1791 return FALSE;
1792 }
1793 for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1794 {
1795 unsigned int len = PARSER_string_substW( file, field->text, buffer, size );
1796 if (i+1 < line->nb_fields) buffer[len] = ',';
1797 buffer += len + 1;
1798 }
1799 }
1800 return TRUE;
1801 }
1802
1803
1804 /***********************************************************************
1805 * SetupGetLineTextA (SETUPAPI.@)
1806 */
1807 BOOL WINAPI SetupGetLineTextA( PINFCONTEXT context, HINF hinf, PCSTR section_name,
1808 PCSTR key_name, PSTR buffer, DWORD size, PDWORD required )
1809 {
1810 struct inf_file *file;
1811 struct line *line;
1812 struct field *field;
1813 int i;
1814 DWORD total = 0;
1815
1816 if (!context)
1817 {
1818 INFCONTEXT new_context;
1819 if (!SetupFindFirstLineA( hinf, section_name, key_name, &new_context )) return FALSE;
1820 file = new_context.CurrentInf;
1821 line = get_line( file, new_context.Section, new_context.Line );
1822 }
1823 else
1824 {
1825 file = context->CurrentInf;
1826 if (!(line = get_line( file, context->Section, context->Line )))
1827 {
1828 SetLastError( ERROR_LINE_NOT_FOUND );
1829 return FALSE;
1830 }
1831 }
1832
1833 for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1834 total += PARSER_string_substA( file, field->text, NULL, 0 ) + 1;
1835
1836 if (required) *required = total;
1837 if (buffer)
1838 {
1839 if (total > size)
1840 {
1841 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1842 return FALSE;
1843 }
1844 for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1845 {
1846 unsigned int len = PARSER_string_substA( file, field->text, buffer, size );
1847 if (i+1 < line->nb_fields) buffer[len] = ',';
1848 buffer += len + 1;
1849 }
1850 }
1851 return TRUE;
1852 }
1853
1854
1855 /***********************************************************************
1856 * SetupGetFieldCount (SETUPAPI.@)
1857 */
1858 DWORD WINAPI SetupGetFieldCount( PINFCONTEXT context )
1859 {
1860 struct inf_file *file = context->CurrentInf;
1861 struct line *line = get_line( file, context->Section, context->Line );
1862
1863 if (!line) return 0;
1864 return line->nb_fields;
1865 }
1866
1867
1868 /***********************************************************************
1869 * SetupGetStringFieldA (SETUPAPI.@)
1870 */
1871 BOOL WINAPI SetupGetStringFieldA( PINFCONTEXT context, DWORD index, PSTR buffer,
1872 DWORD size, PDWORD required )
1873 {
1874 struct inf_file *file = context->CurrentInf;
1875 struct field *field = get_field( file, context->Section, context->Line, index );
1876 unsigned int len;
1877
1878 SetLastError(0);
1879 if (!field) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
1880 len = PARSER_string_substA( file, field->text, NULL, 0 );
1881 if (required) *required = len + 1;
1882 if (buffer)
1883 {
1884 if (size <= len)
1885 {
1886 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1887 return FALSE;
1888 }
1889 PARSER_string_substA( file, field->text, buffer, size );
1890
1891 TRACE( "context %p/%p/%d/%d index %d returning %s\n",
1892 context->Inf, context->CurrentInf, context->Section, context->Line,
1893 index, debugstr_a(buffer) );
1894 }
1895 return TRUE;
1896 }
1897
1898
1899 /***********************************************************************
1900 * SetupGetStringFieldW (SETUPAPI.@)
1901 */
1902 BOOL WINAPI SetupGetStringFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer,
1903 DWORD size, PDWORD required )
1904 {
1905 struct inf_file *file = context->CurrentInf;
1906 struct field *field = get_field( file, context->Section, context->Line, index );
1907 unsigned int len;
1908
1909 SetLastError(0);
1910 if (!field) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
1911 len = PARSER_string_substW( file, field->text, NULL, 0 );
1912 if (required) *required = len + 1;
1913 if (buffer)
1914 {
1915 if (size <= len)
1916 {
1917 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1918 return FALSE;
1919 }
1920 PARSER_string_substW( file, field->text, buffer, size );
1921
1922 TRACE( "context %p/%p/%d/%d index %d returning %s\n",
1923 context->Inf, context->CurrentInf, context->Section, context->Line,
1924 index, debugstr_w(buffer) );
1925 }
1926 return TRUE;
1927 }
1928
1929
1930 /***********************************************************************
1931 * SetupGetIntField (SETUPAPI.@)
1932 */
1933 BOOL WINAPI SetupGetIntField( PINFCONTEXT context, DWORD index, PINT result )
1934 {
1935 char localbuff[20];
1936 char *end, *buffer = localbuff;
1937 DWORD required;
1938 INT res;
1939 BOOL ret;
1940
1941 if (!(ret = SetupGetStringFieldA( context, index, localbuff, sizeof(localbuff), &required )))
1942 {
1943 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
1944 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required ))) return FALSE;
1945 if (!(ret = SetupGetStringFieldA( context, index, buffer, required, NULL ))) goto done;
1946 }
1947 /* The call to SetupGetStringFieldA succeeded. If buffer is empty we have an optional field */
1948 if (!*buffer) *result = 0;
1949 else
1950 {
1951 res = strtol( buffer, &end, 0 );
1952 if (end != buffer && !*end) *result = res;
1953 else
1954 {
1955 SetLastError( ERROR_INVALID_DATA );
1956 ret = FALSE;
1957 }
1958 }
1959
1960 done:
1961 if (buffer != localbuff) HeapFree( GetProcessHeap(), 0, buffer );
1962 return ret;
1963 }
1964
1965
1966 /***********************************************************************
1967 * SetupGetBinaryField (SETUPAPI.@)
1968 */
1969 BOOL WINAPI SetupGetBinaryField( PINFCONTEXT context, DWORD index, BYTE *buffer,
1970 DWORD size, LPDWORD required )
1971 {
1972 struct inf_file *file = context->CurrentInf;
1973 struct line *line = get_line( file, context->Section, context->Line );
1974 struct field *field;
1975 int i;
1976
1977 if (!line)
1978 {
1979 SetLastError( ERROR_LINE_NOT_FOUND );
1980 return FALSE;
1981 }
1982 if (!index || index > line->nb_fields)
1983 {
1984 SetLastError( ERROR_INVALID_PARAMETER );
1985 return FALSE;
1986 }
1987 index--; /* fields start at 0 */
1988 if (required) *required = line->nb_fields - index;
1989 if (!buffer) return TRUE;
1990 if (size < line->nb_fields - index)
1991 {
1992 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1993 return FALSE;
1994 }
1995 field = &file->fields[line->first_field + index];
1996 for (i = index; i < line->nb_fields; i++, field++)
1997 {
1998 const WCHAR *p;
1999 DWORD value = 0;
2000 for (p = field->text; *p && isxdigitW(*p); p++)
2001 {
2002 if ((value <<= 4) > 255)
2003 {
2004 SetLastError( ERROR_INVALID_DATA );
2005 return FALSE;
2006 }
2007 if (*p <= '9') value |= (*p - '0');
2008 else value |= (tolowerW(*p) - 'a' + 10);
2009 }
2010 buffer[i - index] = value;
2011 }
2012 if (TRACE_ON(setupapi))
2013 {
2014 TRACE( "%p/%p/%d/%d index %d returning:\n",
2015 context->Inf, context->CurrentInf, context->Section, context->Line, index );
2016 for (i = index; i < line->nb_fields; i++) TRACE( " %02x\n", buffer[i - index] );
2017 }
2018 return TRUE;
2019 }
2020
2021
2022 /***********************************************************************
2023 * SetupGetMultiSzFieldA (SETUPAPI.@)
2024 */
2025 BOOL WINAPI SetupGetMultiSzFieldA( PINFCONTEXT context, DWORD index, PSTR buffer,
2026 DWORD size, LPDWORD required )
2027 {
2028 struct inf_file *file = context->CurrentInf;
2029 struct line *line = get_line( file, context->Section, context->Line );
2030 struct field *field;
2031 unsigned int len;
2032 int i;
2033 DWORD total = 1;
2034
2035 if (!line)
2036 {
2037 SetLastError( ERROR_LINE_NOT_FOUND );
2038 return FALSE;
2039 }
2040 if (!index || index > line->nb_fields)
2041 {
2042 SetLastError( ERROR_INVALID_PARAMETER );
2043 return FALSE;
2044 }
2045 index--; /* fields start at 0 */
2046 field = &file->fields[line->first_field + index];
2047 for (i = index; i < line->nb_fields; i++, field++)
2048 {
2049 if (!(len = PARSER_string_substA( file, field->text, NULL, 0 ))) break;
2050 total += len + 1;
2051 }
2052
2053 if (required) *required = total;
2054 if (!buffer) return TRUE;
2055 if (total > size)
2056 {
2057 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2058 return FALSE;
2059 }
2060 field = &file->fields[line->first_field + index];
2061 for (i = index; i < line->nb_fields; i++, field++)
2062 {
2063 if (!(len = PARSER_string_substA( file, field->text, buffer, size ))) break;
2064 buffer += len + 1;
2065 }
2066 *buffer = 0; /* add final null */
2067 return TRUE;
2068 }
2069
2070
2071 /***********************************************************************
2072 * SetupGetMultiSzFieldW (SETUPAPI.@)
2073 */
2074 BOOL WINAPI SetupGetMultiSzFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer,
2075 DWORD size, LPDWORD required )
2076 {
2077 struct inf_file *file = context->CurrentInf;
2078 struct line *line = get_line( file, context->Section, context->Line );
2079 struct field *field;
2080 unsigned int len;
2081 int i;
2082 DWORD total = 1;
2083
2084 if (!line)
2085 {
2086 SetLastError( ERROR_LINE_NOT_FOUND );
2087 return FALSE;
2088 }
2089 if (!index || index > line->nb_fields)
2090 {
2091 SetLastError( ERROR_INVALID_PARAMETER );
2092 return FALSE;
2093 }
2094 index--; /* fields start at 0 */
2095 field = &file->fields[line->first_field + index];
2096 for (i = index; i < line->nb_fields; i++, field++)
2097 {
2098 if (!(len = PARSER_string_substW( file, field->text, NULL, 0 ))) break;
2099 total += len + 1;
2100 }
2101
2102 if (required) *required = total;
2103 if (!buffer) return TRUE;
2104 if (total > size)
2105 {
2106 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2107 return FALSE;
2108 }
2109 field = &file->fields[line->first_field + index];
2110 for (i = index; i < line->nb_fields; i++, field++)
2111 {
2112 if (!(len = PARSER_string_substW( file, field->text, buffer, size ))) break;
2113 buffer += len + 1;
2114 }
2115 *buffer = 0; /* add final null */
2116 return TRUE;
2117 }
2118
2119 /***********************************************************************
2120 * pSetupGetField (SETUPAPI.@)
2121 */
2122 LPCWSTR WINAPI pSetupGetField( PINFCONTEXT context, DWORD index )
2123 {
2124 struct inf_file *file = context->CurrentInf;
2125 struct field *field = get_field( file, context->Section, context->Line, index );
2126
2127 if (!field)
2128 {
2129 SetLastError( ERROR_INVALID_PARAMETER );
2130 return NULL;
2131 }
2132 return field->text;
2133 }
2134
2135 /***********************************************************************
2136 * SetupGetInfFileListW (SETUPAPI.@)
2137 */
2138 BOOL WINAPI
2139 SetupGetInfFileListW(
2140 IN PCWSTR DirectoryPath OPTIONAL,
2141 IN DWORD InfStyle,
2142 IN OUT PWSTR ReturnBuffer OPTIONAL,
2143 IN DWORD ReturnBufferSize OPTIONAL,
2144 OUT PDWORD RequiredSize OPTIONAL)
2145 {
2146 HANDLE hSearch;
2147 LPWSTR pFullFileName = NULL;
2148 LPWSTR pFileName; /* Pointer into pFullFileName buffer */
2149 LPWSTR pBuffer = ReturnBuffer;
2150 WIN32_FIND_DATAW wfdFileInfo;
2151 size_t len;
2152 DWORD requiredSize = 0;
2153 BOOL ret = FALSE;
2154
2155 TRACE("%s %lx %p %ld %p\n", debugstr_w(DirectoryPath), InfStyle,
2156 ReturnBuffer, ReturnBufferSize, RequiredSize);
2157
2158 if (InfStyle & ~(INF_STYLE_OLDNT | INF_STYLE_WIN4))
2159 {
2160 TRACE("Unknown flags: 0x%08lx\n", InfStyle & ~(INF_STYLE_OLDNT | INF_STYLE_WIN4));
2161 SetLastError(ERROR_INVALID_PARAMETER);
2162 goto cleanup;
2163 }
2164 else if (ReturnBufferSize == 0 && ReturnBuffer != NULL)
2165 {
2166 SetLastError(ERROR_INVALID_PARAMETER);
2167 goto cleanup;
2168 }
2169 else if (ReturnBufferSize > 0 && ReturnBuffer == NULL)
2170 {
2171 SetLastError(ERROR_INVALID_PARAMETER);
2172 goto cleanup;
2173 }
2174
2175 /* Allocate memory for file filter */
2176 if (DirectoryPath != NULL)
2177 /* "DirectoryPath\" form */
2178 len = strlenW(DirectoryPath) + 1 + 1;
2179 else
2180 /* "%SYSTEMROOT%\Inf\" form */
2181 len = MAX_PATH + 1 + strlenW(InfDirectory) + 1;
2182 len += MAX_PATH; /* To contain file name or "*.inf" string */
2183 pFullFileName = MyMalloc(len * sizeof(WCHAR));
2184 if (pFullFileName == NULL)
2185 {
2186 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2187 goto cleanup;
2188 }
2189
2190 /* Fill file filter buffer */
2191 if (DirectoryPath)
2192 {
2193 strcpyW(pFullFileName, DirectoryPath);
2194 if (*pFullFileName && pFullFileName[strlenW(pFullFileName) - 1] != '\\')
2195 strcatW(pFullFileName, BackSlash);
2196 }
2197 else
2198 {
2199 len = GetSystemWindowsDirectoryW(pFullFileName, MAX_PATH);
2200 if (len == 0 || len > MAX_PATH)
2201 goto cleanup;
2202 if (pFullFileName[strlenW(pFullFileName) - 1] != '\\')
2203 strcatW(pFullFileName, BackSlash);
2204 strcatW(pFullFileName, InfDirectory);
2205 }
2206 pFileName = &pFullFileName[strlenW(pFullFileName)];
2207
2208 /* Search for the first file */
2209 strcpyW(pFileName, InfFileSpecification);
2210 hSearch = FindFirstFileW(pFullFileName, &wfdFileInfo);
2211 if (hSearch == INVALID_HANDLE_VALUE)
2212 {
2213 TRACE("No file returned by %s\n", debugstr_w(pFullFileName));
2214 goto cleanup;
2215 }
2216
2217 do
2218 {
2219 HINF hInf;
2220
2221 strcpyW(pFileName, wfdFileInfo.cFileName);
2222 hInf = SetupOpenInfFileW(
2223 pFullFileName,
2224 NULL, /* Inf class */
2225 InfStyle,
2226 NULL /* Error line */);
2227 if (hInf == INVALID_HANDLE_VALUE)
2228 {
2229 if (GetLastError() == ERROR_CLASS_MISMATCH)
2230 {
2231 /* InfStyle was not correct. Skip this file */
2232 continue;
2233 }
2234 TRACE("Invalid .inf file %s\n", debugstr_w(pFullFileName));
2235 continue;
2236 }
2237
2238 len = strlenW(wfdFileInfo.cFileName) + 1;
2239 requiredSize += (DWORD)len;
2240 if (requiredSize <= ReturnBufferSize)
2241 {
2242 strcpyW(pBuffer, wfdFileInfo.cFileName);
2243 pBuffer = &pBuffer[len];
2244 }
2245 SetupCloseInfFile(hInf);
2246 } while (FindNextFileW(hSearch, &wfdFileInfo));
2247 FindClose(hSearch);
2248
2249 requiredSize += 1; /* Final NULL char */
2250 if (requiredSize <= ReturnBufferSize)
2251 {
2252 *pBuffer = '\0';
2253 ret = TRUE;
2254 }
2255 else
2256 {
2257 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2258 ret = FALSE;
2259 }
2260 if (RequiredSize)
2261 *RequiredSize = requiredSize;
2262
2263 cleanup:
2264 MyFree(pFullFileName);
2265 return ret;
2266 }
2267
2268 /***********************************************************************
2269 * SetupGetInfFileListA (SETUPAPI.@)
2270 */
2271 BOOL WINAPI
2272 SetupGetInfFileListA(
2273 IN PCSTR DirectoryPath OPTIONAL,
2274 IN DWORD InfStyle,
2275 IN OUT PSTR ReturnBuffer OPTIONAL,
2276 IN DWORD ReturnBufferSize OPTIONAL,
2277 OUT PDWORD RequiredSize OPTIONAL)
2278 {
2279 PWSTR DirectoryPathW = NULL;
2280 PWSTR ReturnBufferW = NULL;
2281 BOOL ret = FALSE;
2282
2283 TRACE("%s %lx %p %ld %p\n", debugstr_a(DirectoryPath), InfStyle,
2284 ReturnBuffer, ReturnBufferSize, RequiredSize);
2285
2286 if (DirectoryPath != NULL)
2287 {
2288 DirectoryPathW = pSetupMultiByteToUnicode(DirectoryPath, CP_ACP);
2289 if (DirectoryPathW == NULL) goto Cleanup;
2290 }
2291
2292 if (ReturnBuffer != NULL && ReturnBufferSize != 0)
2293 {
2294 ReturnBufferW = MyMalloc(ReturnBufferSize * sizeof(WCHAR));
2295 if (ReturnBufferW == NULL) goto Cleanup;
2296 }
2297
2298 ret = SetupGetInfFileListW(DirectoryPathW, InfStyle, ReturnBufferW, ReturnBufferSize, RequiredSize);
2299
2300 if (ret && ReturnBufferW != NULL)
2301 {
2302 ret = WideCharToMultiByte(CP_ACP, 0, ReturnBufferW, -1, ReturnBuffer, ReturnBufferSize, NULL, NULL) != 0;
2303 }
2304
2305 Cleanup:
2306 MyFree(DirectoryPathW);
2307 MyFree(ReturnBufferW);
2308
2309 return ret;
2310 }
2311
2312 /***********************************************************************
2313 * SetupDiGetINFClassW (SETUPAPI.@)
2314 */
2315 BOOL WINAPI
2316 SetupDiGetINFClassW(
2317 IN PCWSTR InfName,
2318 OUT LPGUID ClassGuid,
2319 OUT PWSTR ClassName,
2320 IN DWORD ClassNameSize,
2321 OUT PDWORD RequiredSize OPTIONAL)
2322 {
2323 HINF hInf = INVALID_HANDLE_VALUE;
2324 BOOL ret = FALSE;
2325
2326 TRACE("%s %p %p %ld %p\n", debugstr_w(InfName), ClassGuid,
2327 ClassName, ClassNameSize, RequiredSize);
2328
2329 /* Open .inf file */
2330 hInf = SetupOpenInfFileW(InfName, NULL, INF_STYLE_WIN4, NULL);
2331 if (hInf == INVALID_HANDLE_VALUE)
2332 goto cleanup;
2333
2334 ret = PARSER_GetInfClassW(hInf, ClassGuid, ClassName, ClassNameSize, RequiredSize);
2335
2336 cleanup:
2337 if (hInf != INVALID_HANDLE_VALUE)
2338 SetupCloseInfFile(hInf);
2339
2340 TRACE("Returning %d\n", ret);
2341 return ret;
2342 }
2343
2344 /***********************************************************************
2345 * SetupDiGetINFClassA (SETUPAPI.@)
2346 */
2347 BOOL WINAPI SetupDiGetINFClassA(
2348 IN PCSTR InfName,
2349 OUT LPGUID ClassGuid,
2350 OUT PSTR ClassName,
2351 IN DWORD ClassNameSize,
2352 OUT PDWORD RequiredSize OPTIONAL)
2353 {
2354 PWSTR InfNameW = NULL;
2355 PWSTR ClassNameW = NULL;
2356 BOOL ret = FALSE;
2357
2358 TRACE("%s %p %p %ld %p\n", debugstr_a(InfName), ClassGuid,
2359 ClassName, ClassNameSize, RequiredSize);
2360
2361 if (InfName != NULL)
2362 {
2363 InfNameW = pSetupMultiByteToUnicode(InfName, CP_ACP);
2364 if (InfNameW == NULL) goto Cleanup;
2365 }
2366
2367 if (ClassName != NULL && ClassNameSize != 0)
2368 {
2369 ClassNameW = MyMalloc(ClassNameSize * sizeof(WCHAR));
2370 if (ClassNameW == NULL) goto Cleanup;
2371 }
2372
2373 ret = SetupDiGetINFClassW(InfNameW, ClassGuid, ClassNameW, ClassNameSize, RequiredSize);
2374
2375 if (ret && ClassNameW != NULL)
2376 {
2377 ret = WideCharToMultiByte(CP_ACP, 0, ClassNameW, -1, ClassName, ClassNameSize, NULL, NULL) != 0;
2378 }
2379
2380 Cleanup:
2381 MyFree(InfNameW);
2382 MyFree(ClassNameW);
2383
2384 return ret;
2385 }
2386
2387 BOOL EnumerateSectionsStartingWith(
2388 IN HINF hInf,
2389 IN LPCWSTR pStr,
2390 IN FIND_CALLBACK Callback,
2391 IN PVOID Context)
2392 {
2393 struct inf_file *file = (struct inf_file *)hInf;
2394 size_t len = strlenW(pStr);
2395 unsigned int i;
2396
2397 for (i = 0; i < file->nb_sections; i++)
2398 {
2399 if (strncmpiW(pStr, file->sections[i]->name, len) == 0)
2400 {
2401 if (!Callback(file->sections[i]->name, Context))
2402 return FALSE;
2403 }
2404 }
2405 return TRUE;
2406 }