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