xref: /reactos/dll/win32/setupapi/parser.c (revision c2c66aff)
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 */
grow_array(void * array,unsigned int * count,size_t elem)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) */
get_inf_dir(const struct inf_file * file,unsigned int * len)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 */
find_section(const struct inf_file * file,const WCHAR * 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 */
find_line(struct inf_file * file,int section_index,const WCHAR * 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 */
add_section(struct inf_file * file,const WCHAR * name)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 */
add_line(struct inf_file * file,int section_index)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 */
get_line(struct inf_file * file,unsigned int section_index,unsigned int 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 */
get_field(struct inf_file * file,int section_index,int line_index,int 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 */
add_field(struct inf_file * file,const WCHAR * text)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 */
get_dirid_subst(const struct inf_file * file,int dirid,unsigned int * len)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 */
get_string_subst(const struct inf_file * file,const WCHAR * str,unsigned int * len,BOOL no_trailing_slash)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 */
PARSER_string_substW(const struct inf_file * file,const WCHAR * text,WCHAR * buffer,unsigned int size)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 */
PARSER_string_substA(const struct inf_file * file,const WCHAR * text,char * buffer,unsigned int size)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 */
push_string(struct inf_file * file,const WCHAR * string)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 */
push_state(struct parser * parser,enum parser_state state)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 */
pop_state(struct parser * parser)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 */
set_state(struct parser * parser,enum parser_state state)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 */
is_eof(const struct parser * parser,const WCHAR * ptr)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 */
is_eol(const struct parser * parser,const WCHAR * ptr)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 */
push_token(struct parser * parser,const WCHAR * pos)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 */
add_section_from_token(struct parser * parser)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 */
add_field_from_token(struct parser * parser,BOOL is_key)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 */
close_current_line(struct parser * parser)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 */
line_start_state(struct parser * parser,const WCHAR * pos)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 */
section_name_state(struct parser * parser,const WCHAR * pos)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 */
key_name_state(struct parser * parser,const WCHAR * pos)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 */
value_name_state(struct parser * parser,const WCHAR * pos)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 */
eol_backslash_state(struct parser * parser,const WCHAR * pos)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 */
quotes_state(struct parser * parser,const WCHAR * pos)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 */
leading_spaces_state(struct parser * parser,const WCHAR * pos)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 */
trailing_spaces_state(struct parser * parser,const WCHAR * pos)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 */
comment_state(struct parser * parser,const WCHAR * pos)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 
free_inf_file(struct inf_file * file)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 */
parse_buffer(struct inf_file * file,const WCHAR * buffer,const WCHAR * end,UINT * error_line)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 */
append_inf_file(struct inf_file * parent,struct inf_file * child)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  */
parse_file(HANDLE handle,UINT * error_line,DWORD style)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  */
PARSER_get_inf_filename(HINF hinf)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  */
PARSER_get_src_root(HINF hinf)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  */
PARSER_get_dest_dir(INFCONTEXT * context)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  */
SetupOpenInfFileA(PCSTR name,PCSTR class,DWORD style,UINT * error)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
PARSER_GetInfClassW(IN HINF hInf,OUT LPGUID ClassGuid,OUT PWSTR ClassName,IN DWORD ClassNameSize,OUT PDWORD RequiredSize OPTIONAL)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  */
SetupOpenInfFileW(PCWSTR name,PCWSTR class,DWORD style,UINT * error)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  */
SetupOpenAppendInfFileA(PCSTR name,HINF parent_hinf,UINT * error)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  */
SetupOpenAppendInfFileW(PCWSTR name,HINF parent_hinf,UINT * error)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  */
SetupOpenMasterInf(VOID)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  */
SetupCloseInfFile(HINF hinf)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  */
SetupEnumInfSectionsA(HINF hinf,UINT index,PSTR buffer,DWORD size,DWORD * need)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  */
SetupEnumInfSectionsW(HINF hinf,UINT index,PWSTR buffer,DWORD size,DWORD * need)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  */
SetupGetLineCountA(HINF hinf,PCSTR name)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  */
SetupGetLineCountW(HINF hinf,PCWSTR section)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  */
SetupGetLineByIndexA(HINF hinf,PCSTR section,DWORD index,INFCONTEXT * context)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  */
SetupGetLineByIndexW(HINF hinf,PCWSTR section,DWORD index,INFCONTEXT * context)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  */
SetupFindFirstLineA(HINF hinf,PCSTR section,PCSTR key,INFCONTEXT * context)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  */
SetupFindFirstLineW(HINF hinf,PCWSTR section,PCWSTR key,INFCONTEXT * context)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  */
SetupFindNextLine(PINFCONTEXT context_in,PINFCONTEXT context_out)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  */
SetupFindNextMatchLineA(PINFCONTEXT context_in,PCSTR key,PINFCONTEXT context_out)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  */
SetupFindNextMatchLineW(PINFCONTEXT context_in,PCWSTR key,PINFCONTEXT context_out)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  */
SetupGetLineTextW(PINFCONTEXT context,HINF hinf,PCWSTR section_name,PCWSTR key_name,PWSTR buffer,DWORD size,PDWORD required)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  */
SetupGetLineTextA(PINFCONTEXT context,HINF hinf,PCSTR section_name,PCSTR key_name,PSTR buffer,DWORD size,PDWORD required)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  */
SetupGetFieldCount(PINFCONTEXT context)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  */
SetupGetStringFieldA(PINFCONTEXT context,DWORD index,PSTR buffer,DWORD size,PDWORD required)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  */
SetupGetStringFieldW(PINFCONTEXT context,DWORD index,PWSTR buffer,DWORD size,PDWORD required)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  */
SetupGetIntField(PINFCONTEXT context,DWORD index,PINT result)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  */
SetupGetBinaryField(PINFCONTEXT context,DWORD index,BYTE * buffer,DWORD size,LPDWORD required)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  */
SetupGetMultiSzFieldA(PINFCONTEXT context,DWORD index,PSTR buffer,DWORD size,LPDWORD required)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  */
SetupGetMultiSzFieldW(PINFCONTEXT context,DWORD index,PWSTR buffer,DWORD size,LPDWORD required)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  */
pSetupGetField(PINFCONTEXT context,DWORD index)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
SetupGetInfFileListW(IN PCWSTR DirectoryPath OPTIONAL,IN DWORD InfStyle,IN OUT PWSTR ReturnBuffer OPTIONAL,IN DWORD ReturnBufferSize OPTIONAL,OUT PDWORD RequiredSize OPTIONAL)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
SetupGetInfFileListA(IN PCSTR DirectoryPath OPTIONAL,IN DWORD InfStyle,IN OUT PSTR ReturnBuffer OPTIONAL,IN DWORD ReturnBufferSize OPTIONAL,OUT PDWORD RequiredSize OPTIONAL)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
SetupDiGetINFClassW(IN PCWSTR InfName,OUT LPGUID ClassGuid,OUT PWSTR ClassName,IN DWORD ClassNameSize,OUT PDWORD RequiredSize OPTIONAL)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  */
SetupDiGetINFClassA(IN PCSTR InfName,OUT LPGUID ClassGuid,OUT PSTR ClassName,IN DWORD ClassNameSize,OUT PDWORD RequiredSize OPTIONAL)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 
EnumerateSectionsStartingWith(IN HINF hInf,IN LPCWSTR pStr,IN FIND_CALLBACK Callback,IN PVOID Context)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 }
2407