Fix signature of SHLWAPI_334 as its string parameter really is an
[wine] / dlls / setupapi / parser.c
1 /*
2  * INF file parsing
3  *
4  * Copyright 2002 Alexandre Julliard for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <limits.h>
26 #include <string.h>
27 #include <stdlib.h>
28
29 #include "windef.h"
30 #include "winternl.h"
31 #include "winbase.h"
32 #include "winerror.h"
33 #include "wine/unicode.h"
34 #include "setupapi.h"
35 #include "setupx16.h"
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
39
40 #define CONTROL_Z  '\x1a'
41 #define MAX_SECTION_NAME_LEN  255
42 #define MAX_FIELD_LEN         511  /* larger fields get silently truncated */
43 /* actual string limit is MAX_INF_STRING_LENGTH+1 (plus terminating null) under Windows */
44 #define MAX_STRING_LEN        (MAX_INF_STRING_LENGTH+1)
45
46 /* inf file structure definitions */
47
48 struct field
49 {
50     const WCHAR *text;         /* field text */
51 };
52
53 struct line
54 {
55     int first_field;           /* index of first field in field array */
56     int nb_fields;             /* number of fields in line */
57     int key_field;             /* index of field for key or -1 if no key */
58 };
59
60 struct section
61 {
62     const WCHAR *name;         /* section name */
63     unsigned int nb_lines;     /* number of used lines */
64     unsigned int alloc_lines;  /* total number of allocated lines in array below */
65     struct line  lines[16];    /* lines information (grown dynamically, 16 is initial size) */
66 };
67
68 struct inf_file
69 {
70     struct inf_file *next;            /* next appended file */
71     WCHAR           *strings;         /* buffer for string data (section names and field values) */
72     WCHAR           *string_pos;      /* position of next available string in buffer */
73     unsigned int     nb_sections;     /* number of used sections */
74     unsigned int     alloc_sections;  /* total number of allocated section pointers */
75     struct section **sections;        /* section pointers array */
76     unsigned int     nb_fields;
77     unsigned int     alloc_fields;
78     struct field    *fields;
79     int              strings_section; /* index of [Strings] section or -1 if none */
80     WCHAR           *src_root;        /* source root directory */
81 };
82
83 /* parser definitions */
84
85 enum parser_state
86 {
87     LINE_START,      /* at beginning of a line */
88     SECTION_NAME,    /* parsing a section name */
89     KEY_NAME,        /* parsing a key name */
90     VALUE_NAME,      /* parsing a value name */
91     EOL_BACKSLASH,   /* backslash at end of line */
92     QUOTES,          /* inside quotes */
93     LEADING_SPACES,  /* leading spaces */
94     TRAILING_SPACES, /* trailing spaces */
95     COMMENT,         /* inside a comment */
96     NB_PARSER_STATES
97 };
98
99 struct parser
100 {
101     const WCHAR      *start;        /* start position of item being parsed */
102     const WCHAR      *end;          /* end of buffer */
103     struct inf_file  *file;         /* file being built */
104     enum parser_state state;        /* current parser state */
105     enum parser_state stack[4];     /* state stack */
106     int               stack_pos;    /* current pos in stack */
107
108     int               cur_section;  /* index of section being parsed*/
109     struct line      *line;         /* current line */
110     unsigned int      line_pos;     /* current line position in file */
111     unsigned int      error;        /* error code */
112     unsigned int      token_len;    /* current token len */
113     WCHAR token[MAX_FIELD_LEN+1];   /* current token */
114 };
115
116 typedef const WCHAR * (*parser_state_func)( struct parser *parser, const WCHAR *pos );
117
118 /* parser state machine functions */
119 static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos );
120 static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos );
121 static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos );
122 static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos );
123 static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos );
124 static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos );
125 static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos );
126 static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos );
127 static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos );
128
129 static const parser_state_func parser_funcs[NB_PARSER_STATES] =
130 {
131     line_start_state,      /* LINE_START */
132     section_name_state,    /* SECTION_NAME */
133     key_name_state,        /* KEY_NAME */
134     value_name_state,      /* VALUE_NAME */
135     eol_backslash_state,   /* EOL_BACKSLASH */
136     quotes_state,          /* QUOTES */
137     leading_spaces_state,  /* LEADING_SPACES */
138     trailing_spaces_state, /* TRAILING_SPACES */
139     comment_state          /* COMMENT */
140 };
141
142
143 /* Unicode string constants */
144 static const WCHAR Version[]    = {'V','e','r','s','i','o','n',0};
145 static const WCHAR Signature[]  = {'S','i','g','n','a','t','u','r','e',0};
146 static const WCHAR Chicago[]    = {'$','C','h','i','c','a','g','o','$',0};
147 static const WCHAR WindowsNT[]  = {'$','W','i','n','d','o','w','s',' ','N','T','$',0};
148 static const WCHAR Windows95[]  = {'$','W','i','n','d','o','w','s',' ','9','5','$',0};
149 static const WCHAR LayoutFile[] = {'L','a','y','o','u','t','F','i','l','e',0};
150
151 /* extend an array, allocating more memory if necessary */
152 static void *grow_array( void *array, unsigned int *count, size_t elem )
153 {
154     void *new_array;
155     unsigned int new_count = *count + *count / 2;
156     if (new_count < 32) new_count = 32;
157     if ((new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, array, new_count * elem )))
158         *count = new_count;
159     else
160         HeapFree( GetProcessHeap(), 0, array );
161     return new_array;
162 }
163
164
165 /* find a section by name */
166 static int find_section( struct inf_file *file, const WCHAR *name )
167 {
168     int i;
169
170     for (i = 0; i < file->nb_sections; i++)
171         if (!strcmpiW( name, file->sections[i]->name )) return i;
172     return -1;
173 }
174
175
176 /* find a line by name */
177 static struct line *find_line( struct inf_file *file, int section_index, const WCHAR *name )
178 {
179     struct section *section;
180     struct line *line;
181     int i;
182
183     if (section_index < 0 || section_index >= file->nb_sections) return NULL;
184     section = file->sections[section_index];
185     for (i = 0, line = section->lines; i < section->nb_lines; i++, line++)
186     {
187         if (line->key_field == -1) continue;
188         if (!strcmpiW( name, file->fields[line->key_field].text )) return line;
189     }
190     return NULL;
191 }
192
193
194 /* add a section to the file and return the section index */
195 static int add_section( struct inf_file *file, const WCHAR *name )
196 {
197     struct section *section;
198
199     if (file->nb_sections >= file->alloc_sections)
200     {
201         if (!(file->sections = grow_array( file->sections, &file->alloc_sections,
202                                            sizeof(file->sections[0]) ))) return -1;
203     }
204     if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) ))) return -1;
205     section->name        = name;
206     section->nb_lines    = 0;
207     section->alloc_lines = sizeof(section->lines)/sizeof(section->lines[0]);
208     file->sections[file->nb_sections] = section;
209     return file->nb_sections++;
210 }
211
212
213 /* add a line to a given section */
214 static struct line *add_line( struct inf_file *file, int section_index )
215 {
216     struct section *section;
217     struct line *line;
218
219     assert( section_index >= 0 && section_index < file->nb_sections );
220
221     section = file->sections[section_index];
222     if (section->nb_lines == section->alloc_lines)  /* need to grow the section */
223     {
224         int size = sizeof(*section) - sizeof(section->lines) + 2*section->alloc_lines*sizeof(*line);
225         if (!(section = HeapReAlloc( GetProcessHeap(), 0, section, size ))) return NULL;
226         section->alloc_lines *= 2;
227         file->sections[section_index] = section;
228     }
229     line = &section->lines[section->nb_lines++];
230     line->first_field = file->nb_fields;
231     line->nb_fields   = 0;
232     line->key_field   = -1;
233     return line;
234 }
235
236
237 /* retrieve a given line from section/line index */
238 inline static struct line *get_line( struct inf_file *file, unsigned int section_index,
239                                      unsigned int line_index )
240 {
241     struct section *section;
242
243     if (section_index >= file->nb_sections) return NULL;
244     section = file->sections[section_index];
245     if (line_index >= section->nb_lines) return NULL;
246     return &section->lines[line_index];
247 }
248
249
250 /* retrieve a given field from section/line/field index */
251 static struct field *get_field( struct inf_file *file, int section_index, int line_index,
252                                 int field_index )
253 {
254     struct line *line = get_line( file, section_index, line_index );
255
256     if (!line) return NULL;
257     if (!field_index)  /* get the key */
258     {
259         if (line->key_field == -1) return NULL;
260         return &file->fields[line->key_field];
261     }
262     field_index--;
263     if (field_index >= line->nb_fields) return NULL;
264     return &file->fields[line->first_field + field_index];
265 }
266
267
268 /* allocate a new field, growing the array if necessary */
269 static struct field *add_field( struct inf_file *file, const WCHAR *text )
270 {
271     struct field *field;
272
273     if (file->nb_fields >= file->alloc_fields)
274     {
275         if (!(file->fields = grow_array( file->fields, &file->alloc_fields,
276                                          sizeof(file->fields[0]) ))) return NULL;
277     }
278     field = &file->fields[file->nb_fields++];
279     field->text = text;
280     return field;
281 }
282
283
284 /* retrieve the string substitution for a directory id */
285 static const WCHAR *get_dirid_subst( int dirid, unsigned int *len )
286 {
287     extern const WCHAR *DIRID_get_string( HINF hinf, int dirid );
288     const WCHAR *ret = DIRID_get_string( 0, dirid );
289     if (ret) *len = strlenW(ret);
290     return ret;
291 }
292
293
294 /* retrieve the string substitution for a given string, or NULL if not found */
295 /* if found, len is set to the substitution length */
296 static const WCHAR *get_string_subst( struct inf_file *file, const WCHAR *str, unsigned int *len )
297 {
298     static const WCHAR percent = '%';
299
300     struct section *strings_section;
301     struct line *line;
302     struct field *field;
303     int i, dirid;
304     WCHAR *dirid_str, *end;
305     const WCHAR *ret = NULL;
306
307     if (!*len)  /* empty string (%%) is replaced by single percent */
308     {
309         *len = 1;
310         return &percent;
311     }
312     if (file->strings_section == -1) goto not_found;
313     strings_section = file->sections[file->strings_section];
314     for (i = 0, line = strings_section->lines; i < strings_section->nb_lines; i++, line++)
315     {
316         if (line->key_field == -1) continue;
317         if (strncmpiW( str, file->fields[line->key_field].text, *len )) continue;
318         if (!file->fields[line->key_field].text[*len]) break;
319     }
320     if (i == strings_section->nb_lines || !line->nb_fields) goto not_found;
321     field = &file->fields[line->first_field];
322     *len = strlenW( field->text );
323     return field->text;
324
325  not_found:  /* check for integer id */
326     if ((dirid_str = HeapAlloc( GetProcessHeap(), 0, (*len+1) * sizeof(WCHAR) )))
327     {
328         memcpy( dirid_str, str, *len * sizeof(WCHAR) );
329         dirid_str[*len] = 0;
330         dirid = strtolW( dirid_str, &end, 10 );
331         if (!*end) ret = get_dirid_subst( dirid, len );
332         HeapFree( GetProcessHeap(), 0, dirid_str );
333         return ret;
334     }
335     return NULL;
336 }
337
338
339 /* do string substitutions on the specified text */
340 /* the buffer is assumed to be large enough */
341 /* returns necessary length not including terminating null */
342 unsigned int PARSER_string_substW( struct inf_file *file, const WCHAR *text, WCHAR *buffer,
343                                    unsigned int size )
344 {
345     const WCHAR *start, *subst, *p;
346     unsigned int len, total = 0;
347     int inside = 0;
348
349     if (!buffer) size = MAX_STRING_LEN + 1;
350     for (p = start = text; *p; p++)
351     {
352         if (*p != '%') continue;
353         inside = !inside;
354         if (inside)  /* start of a %xx% string */
355         {
356             len = p - start;
357             if (len > size - 1) len = size - 1;
358             if (buffer) memcpy( buffer + total, start, len * sizeof(WCHAR) );
359             total += len;
360             size -= len;
361             start = p;
362         }
363         else /* end of the %xx% string, find substitution */
364         {
365             len = p - start - 1;
366             subst = get_string_subst( file, start + 1, &len );
367             if (!subst)
368             {
369                 subst = start;
370                 len = p - start + 1;
371             }
372             if (len > size - 1) len = size - 1;
373             if (buffer) memcpy( buffer + total, subst, len * sizeof(WCHAR) );
374             total += len;
375             size -= len;
376             start = p + 1;
377         }
378     }
379
380     if (start != p) /* unfinished string, copy it */
381     {
382         len = p - start;
383         if (len > size - 1) len = size - 1;
384         if (buffer) memcpy( buffer + total, start, len * sizeof(WCHAR) );
385         total += len;
386     }
387     if (buffer && size) buffer[total] = 0;
388     return total;
389 }
390
391
392 /* do string substitutions on the specified text */
393 /* the buffer is assumed to be large enough */
394 /* returns necessary length not including terminating null */
395 unsigned int PARSER_string_substA( struct inf_file *file, const WCHAR *text, char *buffer,
396                                    unsigned int size )
397 {
398     WCHAR buffW[MAX_STRING_LEN+1];
399     DWORD ret;
400
401     unsigned int len = PARSER_string_substW( file, text, buffW, sizeof(buffW)/sizeof(WCHAR) );
402     if (!buffer) RtlUnicodeToMultiByteSize( &ret, buffW, len * sizeof(WCHAR) );
403     else
404     {
405         RtlUnicodeToMultiByteN( buffer, size-1, &ret, buffW, len * sizeof(WCHAR) );
406         buffer[ret] = 0;
407     }
408     return ret;
409 }
410
411
412 /* push some string data into the strings buffer */
413 static WCHAR *push_string( struct inf_file *file, const WCHAR *string )
414 {
415     WCHAR *ret = file->string_pos;
416     strcpyW( ret, string );
417     file->string_pos += strlenW( ret ) + 1;
418     return ret;
419 }
420
421
422 /* push the current state on the parser stack */
423 inline static void push_state( struct parser *parser, enum parser_state state )
424 {
425     assert( parser->stack_pos < sizeof(parser->stack)/sizeof(parser->stack[0]) );
426     parser->stack[parser->stack_pos++] = state;
427 }
428
429
430 /* pop the current state */
431 inline static void pop_state( struct parser *parser )
432 {
433     assert( parser->stack_pos );
434     parser->state = parser->stack[--parser->stack_pos];
435 }
436
437
438 /* set the parser state and return the previous one */
439 inline static enum parser_state set_state( struct parser *parser, enum parser_state state )
440 {
441     enum parser_state ret = parser->state;
442     parser->state = state;
443     return ret;
444 }
445
446
447 /* check if the pointer points to an end of file */
448 inline static int is_eof( struct parser *parser, const WCHAR *ptr )
449 {
450     return (ptr >= parser->end || *ptr == CONTROL_Z);
451 }
452
453
454 /* check if the pointer points to an end of line */
455 inline static int is_eol( struct parser *parser, const WCHAR *ptr )
456 {
457     return (ptr >= parser->end || *ptr == CONTROL_Z || *ptr == '\n');
458 }
459
460
461 /* push data from current token start up to pos into the current token */
462 static int push_token( struct parser *parser, const WCHAR *pos )
463 {
464     int len = pos - parser->start;
465     const WCHAR *src = parser->start;
466     WCHAR *dst = parser->token + parser->token_len;
467
468     if (len > MAX_FIELD_LEN - parser->token_len) len = MAX_FIELD_LEN - parser->token_len;
469
470     parser->token_len += len;
471     for ( ; len > 0; len--, dst++, src++) *dst = *src ? *src : ' ';
472     *dst = 0;
473     parser->start = pos;
474     return 0;
475 }
476
477
478 /* add a section with the current token as name */
479 static int add_section_from_token( struct parser *parser )
480 {
481     int section_index;
482
483     if (parser->token_len > MAX_SECTION_NAME_LEN)
484     {
485         parser->error = ERROR_SECTION_NAME_TOO_LONG;
486         return -1;
487     }
488     if ((section_index = find_section( parser->file, parser->token )) == -1)
489     {
490         /* need to create a new one */
491         const WCHAR *name = push_string( parser->file, parser->token );
492         if ((section_index = add_section( parser->file, name )) == -1)
493         {
494             parser->error = ERROR_NOT_ENOUGH_MEMORY;
495             return -1;
496         }
497     }
498     parser->token_len = 0;
499     parser->cur_section = section_index;
500     return section_index;
501 }
502
503
504 /* add a field containing the current token to the current line */
505 static struct field *add_field_from_token( struct parser *parser, int is_key )
506 {
507     struct field *field;
508     WCHAR *text;
509
510     if (!parser->line)  /* need to start a new line */
511     {
512         if (parser->cur_section == -1)  /* got a line before the first section */
513         {
514             parser->error = ERROR_WRONG_INF_STYLE;
515             return NULL;
516         }
517         if (!(parser->line = add_line( parser->file, parser->cur_section ))) goto error;
518     }
519     else assert(!is_key);
520
521     text = push_string( parser->file, parser->token );
522     if ((field = add_field( parser->file, text )))
523     {
524         if (!is_key) parser->line->nb_fields++;
525         else
526         {
527             /* replace first field by key field */
528             parser->line->key_field = parser->line->first_field;
529             parser->line->first_field++;
530         }
531         parser->token_len = 0;
532         return field;
533     }
534  error:
535     parser->error = ERROR_NOT_ENOUGH_MEMORY;
536     return NULL;
537 }
538
539
540 /* close the current line and prepare for parsing a new one */
541 static void close_current_line( struct parser *parser )
542 {
543     struct line *cur_line = parser->line;
544
545     if (cur_line)
546     {
547         /* if line has a single field and no key, the field is the key too */
548         if (cur_line->nb_fields == 1 && cur_line->key_field == -1)
549             cur_line->key_field = cur_line->first_field;
550     }
551     parser->line = NULL;
552 }
553
554
555 /* handler for parser LINE_START state */
556 static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos )
557 {
558     const WCHAR *p;
559
560     for (p = pos; !is_eof( parser, p ); p++)
561     {
562         switch(*p)
563         {
564         case '\n':
565             parser->line_pos++;
566             close_current_line( parser );
567             break;
568         case ';':
569             push_state( parser, LINE_START );
570             set_state( parser, COMMENT );
571             return p + 1;
572         case '[':
573             parser->start = p + 1;
574             set_state( parser, SECTION_NAME );
575             return p + 1;
576         default:
577             if (!isspaceW(*p))
578             {
579                 parser->start = p;
580                 set_state( parser, KEY_NAME );
581                 return p;
582             }
583             break;
584         }
585     }
586     close_current_line( parser );
587     return NULL;
588 }
589
590
591 /* handler for parser SECTION_NAME state */
592 static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos )
593 {
594     const WCHAR *p;
595
596     for (p = pos; !is_eol( parser, p ); p++)
597     {
598         if (*p == ']')
599         {
600             push_token( parser, p );
601             if (add_section_from_token( parser ) == -1) return NULL;
602             push_state( parser, LINE_START );
603             set_state( parser, COMMENT );  /* ignore everything else on the line */
604             return p + 1;
605         }
606     }
607     parser->error = ERROR_BAD_SECTION_NAME_LINE; /* unfinished section name */
608     return NULL;
609 }
610
611
612 /* handler for parser KEY_NAME state */
613 static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos )
614 {
615     const WCHAR *p, *token_end = parser->start;
616
617     for (p = pos; !is_eol( parser, p ); p++)
618     {
619         if (*p == ',') break;
620         switch(*p)
621         {
622
623          case '=':
624             push_token( parser, token_end );
625             if (!add_field_from_token( parser, 1 )) return NULL;
626             parser->start = p + 1;
627             push_state( parser, VALUE_NAME );
628             set_state( parser, LEADING_SPACES );
629             return p + 1;
630         case ';':
631             push_token( parser, token_end );
632             if (!add_field_from_token( parser, 0 )) return NULL;
633             push_state( parser, LINE_START );
634             set_state( parser, COMMENT );
635             return p + 1;
636         case '"':
637             push_token( parser, token_end );
638             parser->start = p + 1;
639             push_state( parser, KEY_NAME );
640             set_state( parser, QUOTES );
641             return p + 1;
642         case '\\':
643             push_token( parser, token_end );
644             parser->start = p;
645             push_state( parser, KEY_NAME );
646             set_state( parser, EOL_BACKSLASH );
647             return p;
648         default:
649             if (!isspaceW(*p)) token_end = p + 1;
650             else
651             {
652                 push_token( parser, p );
653                 push_state( parser, KEY_NAME );
654                 set_state( parser, TRAILING_SPACES );
655                 return p;
656             }
657             break;
658         }
659     }
660     push_token( parser, token_end );
661     set_state( parser, VALUE_NAME );
662     return p;
663 }
664
665
666 /* handler for parser VALUE_NAME state */
667 static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos )
668 {
669     const WCHAR *p, *token_end = parser->start;
670
671     for (p = pos; !is_eol( parser, p ); p++)
672     {
673         switch(*p)
674         {
675         case ';':
676             push_token( parser, token_end );
677             if (!add_field_from_token( parser, 0 )) return NULL;
678             push_state( parser, LINE_START );
679             set_state( parser, COMMENT );
680             return p + 1;
681         case ',':
682             push_token( parser, token_end );
683             if (!add_field_from_token( parser, 0 )) return NULL;
684             parser->start = p + 1;
685             push_state( parser, VALUE_NAME );
686             set_state( parser, LEADING_SPACES );
687             return p + 1;
688         case '"':
689             push_token( parser, token_end );
690             parser->start = p + 1;
691             push_state( parser, VALUE_NAME );
692             set_state( parser, QUOTES );
693             return p + 1;
694         case '\\':
695             push_token( parser, token_end );
696             parser->start = p;
697             push_state( parser, VALUE_NAME );
698             set_state( parser, EOL_BACKSLASH );
699             return p;
700         default:
701             if (!isspaceW(*p)) token_end = p + 1;
702             else
703             {
704                 push_token( parser, p );
705                 push_state( parser, VALUE_NAME );
706                 set_state( parser, TRAILING_SPACES );
707                 return p;
708             }
709             break;
710         }
711     }
712     push_token( parser, token_end );
713     if (!add_field_from_token( parser, 0 )) return NULL;
714     set_state( parser, LINE_START );
715     return p;
716 }
717
718
719 /* handler for parser EOL_BACKSLASH state */
720 static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos )
721 {
722     const WCHAR *p;
723
724     for (p = pos; !is_eof( parser, p ); p++)
725     {
726         switch(*p)
727         {
728         case '\n':
729             parser->line_pos++;
730             parser->start = p + 1;
731             set_state( parser, LEADING_SPACES );
732             return p + 1;
733         case '\\':
734             continue;
735         case ';':
736             push_state( parser, EOL_BACKSLASH );
737             set_state( parser, COMMENT );
738             return p + 1;
739         default:
740             if (isspaceW(*p)) continue;
741             push_token( parser, p );
742             pop_state( parser );
743             return p;
744         }
745     }
746     parser->start = p;
747     pop_state( parser );
748     return p;
749 }
750
751
752 /* handler for parser QUOTES state */
753 static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos )
754 {
755     const WCHAR *p, *token_end = parser->start;
756
757     for (p = pos; !is_eol( parser, p ); p++)
758     {
759         if (*p == '"')
760         {
761             if (p+1 < parser->end && p[1] == '"')  /* double quotes */
762             {
763                 push_token( parser, p + 1 );
764                 parser->start = token_end = p + 2;
765                 p++;
766             }
767             else  /* end of quotes */
768             {
769                 push_token( parser, p );
770                 parser->start = p + 1;
771                 pop_state( parser );
772                 return p + 1;
773             }
774         }
775     }
776     push_token( parser, p );
777     pop_state( parser );
778     return p;
779 }
780
781
782 /* handler for parser LEADING_SPACES state */
783 static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos )
784 {
785     const WCHAR *p;
786
787     for (p = pos; !is_eol( parser, p ); p++)
788     {
789         if (*p == '\\')
790         {
791             parser->start = p;
792             set_state( parser, EOL_BACKSLASH );
793             return p;
794         }
795         if (!isspaceW(*p)) break;
796     }
797     parser->start = p;
798     pop_state( parser );
799     return p;
800 }
801
802
803 /* handler for parser TRAILING_SPACES state */
804 static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos )
805 {
806     const WCHAR *p;
807
808     for (p = pos; !is_eol( parser, p ); p++)
809     {
810         if (*p == '\\')
811         {
812             set_state( parser, EOL_BACKSLASH );
813             return p;
814         }
815         if (!isspaceW(*p)) break;
816     }
817     pop_state( parser );
818     return p;
819 }
820
821
822 /* handler for parser COMMENT state */
823 static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos )
824 {
825     const WCHAR *p = pos;
826
827     while (!is_eol( parser, p )) p++;
828     pop_state( parser );
829     return p;
830 }
831
832
833 /* parse a complete buffer */
834 static DWORD parse_buffer( struct inf_file *file, const WCHAR *buffer, const WCHAR *end,
835                            UINT *error_line )
836 {
837     static const WCHAR Strings[] = {'S','t','r','i','n','g','s',0};
838
839     struct parser parser;
840     const WCHAR *pos = buffer;
841
842     parser.start       = buffer;
843     parser.end         = end;
844     parser.file        = file;
845     parser.line        = NULL;
846     parser.state       = LINE_START;
847     parser.stack_pos   = 0;
848     parser.cur_section = -1;
849     parser.line_pos    = 1;
850     parser.error       = 0;
851     parser.token_len   = 0;
852
853     /* parser main loop */
854     while (pos) pos = (parser_funcs[parser.state])( &parser, pos );
855
856     /* trim excess buffer space */
857     if (file->alloc_sections > file->nb_sections)
858     {
859         file->sections = HeapReAlloc( GetProcessHeap(), 0, file->sections,
860                                       file->nb_sections * sizeof(file->sections[0]) );
861         file->alloc_sections = file->nb_sections;
862     }
863     if (file->alloc_fields > file->nb_fields)
864     {
865         file->fields = HeapReAlloc( GetProcessHeap(), 0, file->fields,
866                                     file->nb_fields * sizeof(file->fields[0]) );
867         file->alloc_fields = file->nb_fields;
868     }
869     file->strings = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, file->strings,
870                                  (file->string_pos - file->strings) * sizeof(WCHAR) );
871
872     if (parser.error)
873     {
874         if (error_line) *error_line = parser.line_pos;
875         return parser.error;
876     }
877
878     /* find the [strings] section */
879     file->strings_section = find_section( file, Strings );
880     return 0;
881 }
882
883
884 /* append a child INF file to its parent list, in a thread-safe manner */
885 static void append_inf_file( struct inf_file *parent, struct inf_file *child )
886 {
887     struct inf_file **ppnext = &parent->next;
888     child->next = NULL;
889
890     for (;;)
891     {
892         struct inf_file *next = InterlockedCompareExchangePointer( (void **)ppnext, child, NULL );
893         if (!next) return;
894         ppnext = &next->next;
895     }
896 }
897
898
899 /***********************************************************************
900  *            parse_file
901  *
902  * parse an INF file.
903  */
904 static struct inf_file *parse_file( HANDLE handle, const WCHAR *class, UINT *error_line )
905 {
906     void *buffer;
907     DWORD err = 0;
908     struct inf_file *file;
909
910     DWORD size = GetFileSize( handle, NULL );
911     HANDLE mapping = CreateFileMappingW( handle, NULL, PAGE_READONLY, 0, size, NULL );
912     if (!mapping) return NULL;
913     buffer = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, size );
914     NtClose( mapping );
915     if (!buffer) return NULL;
916
917     if (class) FIXME( "class %s not supported yet\n", debugstr_w(class) );
918
919     if (!(file = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*file) )))
920     {
921         err = ERROR_NOT_ENOUGH_MEMORY;
922         goto done;
923     }
924
925     /* we won't need more strings space than the size of the file,
926      * so we can preallocate it here
927      */
928     if (!(file->strings = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) )))
929     {
930         err = ERROR_NOT_ENOUGH_MEMORY;
931         goto done;
932     }
933     file->string_pos = file->strings;
934     file->strings_section = -1;
935
936     if (!RtlIsTextUnicode( buffer, size, NULL ))
937     {
938         WCHAR *new_buff = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) );
939         if (new_buff)
940         {
941             DWORD len = MultiByteToWideChar( CP_ACP, 0, buffer, size, new_buff,
942                                              size * sizeof(WCHAR) );
943             err = parse_buffer( file, new_buff, new_buff + len, error_line );
944             HeapFree( GetProcessHeap(), 0, new_buff );
945         }
946     }
947     else err = parse_buffer( file, buffer, (WCHAR *)((char *)buffer + size), error_line );
948
949     if (!err)  /* now check signature */
950     {
951         int version_index = find_section( file, Version );
952         if (version_index != -1)
953         {
954             struct line *line = find_line( file, version_index, Signature );
955             if (line && line->nb_fields > 0)
956             {
957                 struct field *field = file->fields + line->first_field;
958                 if (!strcmpiW( field->text, Chicago )) goto done;
959                 if (!strcmpiW( field->text, WindowsNT )) goto done;
960                 if (!strcmpiW( field->text, Windows95 )) goto done;
961             }
962         }
963         err = ERROR_WRONG_INF_STYLE;
964     }
965
966  done:
967     UnmapViewOfFile( buffer );
968     if (err)
969     {
970         HeapFree( GetProcessHeap(), 0, file );
971         SetLastError( err );
972         file = NULL;
973     }
974     return file;
975 }
976
977
978 /***********************************************************************
979  *            PARSER_get_src_root
980  *
981  * Retrieve the source directory of an inf file.
982  */
983 const WCHAR *PARSER_get_src_root( HINF hinf )
984 {
985     struct inf_file *file = hinf;
986     return file->src_root;
987 }
988
989
990 /***********************************************************************
991  *            SetupOpenInfFileA   (SETUPAPI.@)
992  */
993 HINF WINAPI SetupOpenInfFileA( PCSTR name, PCSTR class, DWORD style, UINT *error )
994 {
995     UNICODE_STRING nameW, classW;
996     HINF ret = (HINF)INVALID_HANDLE_VALUE;
997
998     classW.Buffer = NULL;
999     if (class && !RtlCreateUnicodeStringFromAsciiz( &classW, class ))
1000     {
1001         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1002         return ret;
1003     }
1004     if (RtlCreateUnicodeStringFromAsciiz( &nameW, name ))
1005     {
1006         ret = SetupOpenInfFileW( nameW.Buffer, classW.Buffer, style, error );
1007         RtlFreeUnicodeString( &nameW );
1008     }
1009     RtlFreeUnicodeString( &classW );
1010     return ret;
1011 }
1012
1013
1014 /***********************************************************************
1015  *            SetupOpenInfFileW   (SETUPAPI.@)
1016  */
1017 HINF WINAPI SetupOpenInfFileW( PCWSTR name, PCWSTR class, DWORD style, UINT *error )
1018 {
1019     struct inf_file *file = NULL;
1020     HANDLE handle;
1021     WCHAR *path, *p;
1022     UINT len;
1023
1024     if (strchrW( name, '\\' ) || strchrW( name, '/' ))
1025     {
1026         if (!(len = GetFullPathNameW( name, 0, NULL, NULL ))) return (HINF)INVALID_HANDLE_VALUE;
1027         if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1028         {
1029             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1030             return (HINF)INVALID_HANDLE_VALUE;
1031         }
1032         GetFullPathNameW( name, len, path, NULL );
1033         handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1034     }
1035     else  /* try Windows directory */
1036     {
1037         static const WCHAR Inf[]      = {'\\','i','n','f','\\',0};
1038         static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
1039
1040         len = GetWindowsDirectoryW( NULL, 0 ) + strlenW(name) + 12;
1041         if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1042         {
1043             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1044             return (HINF)INVALID_HANDLE_VALUE;
1045         }
1046         GetWindowsDirectoryW( path, len );
1047         p = path + strlenW(path);
1048         strcpyW( p, Inf );
1049         strcatW( p, name );
1050         handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1051         if (handle == INVALID_HANDLE_VALUE)
1052         {
1053             strcpyW( p, System32 );
1054             strcatW( p, name );
1055             handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1056         }
1057     }
1058
1059     if (handle != INVALID_HANDLE_VALUE)
1060     {
1061         file = parse_file( handle, class, error );
1062         CloseHandle( handle );
1063     }
1064     if (!file)
1065     {
1066         HeapFree( GetProcessHeap(), 0, path );
1067         return (HINF)INVALID_HANDLE_VALUE;
1068     }
1069     TRACE( "%s -> %p\n", debugstr_w(path), file );
1070     file->src_root = path;
1071     if ((p = strrchrW( path, '\\' ))) p[1] = 0;  /* remove file name */
1072     SetLastError( 0 );
1073     return (HINF)file;
1074 }
1075
1076
1077 /***********************************************************************
1078  *            SetupOpenAppendInfFileA    (SETUPAPI.@)
1079  */
1080 BOOL WINAPI SetupOpenAppendInfFileA( PCSTR name, HINF parent_hinf, UINT *error )
1081 {
1082     HINF child_hinf;
1083
1084     if (!name) return SetupOpenAppendInfFileW( NULL, parent_hinf, error );
1085     child_hinf = SetupOpenInfFileA( name, NULL, INF_STYLE_WIN4, error );
1086     if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE;
1087     append_inf_file( parent_hinf, child_hinf );
1088     TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_a(name), child_hinf );
1089     return TRUE;
1090 }
1091
1092
1093 /***********************************************************************
1094  *            SetupOpenAppendInfFileW    (SETUPAPI.@)
1095  */
1096 BOOL WINAPI SetupOpenAppendInfFileW( PCWSTR name, HINF parent_hinf, UINT *error )
1097 {
1098     HINF child_hinf;
1099
1100     if (!name)
1101     {
1102         INFCONTEXT context;
1103         WCHAR filename[MAX_PATH];
1104         int idx = 1;
1105
1106         if (!SetupFindFirstLineW( parent_hinf, Version, LayoutFile, &context )) return FALSE;
1107         while (SetupGetStringFieldW( &context, idx++, filename,
1108                                      sizeof(filename)/sizeof(WCHAR), NULL ))
1109         {
1110             child_hinf = SetupOpenInfFileW( filename, NULL, INF_STYLE_WIN4, error );
1111             if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE;
1112             append_inf_file( parent_hinf, child_hinf );
1113             TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(filename), child_hinf );
1114         }
1115         return TRUE;
1116     }
1117     child_hinf = SetupOpenInfFileW( name, NULL, INF_STYLE_WIN4, error );
1118     if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE;
1119     append_inf_file( parent_hinf, child_hinf );
1120     TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(name), child_hinf );
1121     return TRUE;
1122 }
1123
1124
1125 /***********************************************************************
1126  *            SetupCloseInfFile   (SETUPAPI.@)
1127  */
1128 void WINAPI SetupCloseInfFile( HINF hinf )
1129 {
1130     struct inf_file *file = hinf;
1131     int i;
1132
1133     for (i = 0; i < file->nb_sections; i++) HeapFree( GetProcessHeap(), 0, file->sections[i] );
1134     HeapFree( GetProcessHeap(), 0, file->src_root );
1135     HeapFree( GetProcessHeap(), 0, file->sections );
1136     HeapFree( GetProcessHeap(), 0, file->fields );
1137     HeapFree( GetProcessHeap(), 0, file->strings );
1138     HeapFree( GetProcessHeap(), 0, file );
1139 }
1140
1141
1142 /***********************************************************************
1143  *            SetupGetLineCountA   (SETUPAPI.@)
1144  */
1145 LONG WINAPI SetupGetLineCountA( HINF hinf, PCSTR name )
1146 {
1147     UNICODE_STRING sectionW;
1148     LONG ret = -1;
1149
1150     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, name ))
1151         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1152     else
1153     {
1154         ret = SetupGetLineCountW( hinf, sectionW.Buffer );
1155         RtlFreeUnicodeString( &sectionW );
1156     }
1157     return ret;
1158 }
1159
1160
1161 /***********************************************************************
1162  *            SetupGetLineCountW   (SETUPAPI.@)
1163  */
1164 LONG WINAPI SetupGetLineCountW( HINF hinf, PCWSTR section )
1165 {
1166     struct inf_file *file = hinf;
1167     int section_index;
1168     LONG ret = -1;
1169
1170     for (file = hinf; file; file = file->next)
1171     {
1172         if ((section_index = find_section( file, section )) == -1) continue;
1173         if (ret == -1) ret = 0;
1174         ret += file->sections[section_index]->nb_lines;
1175     }
1176     TRACE( "(%p,%s) returning %ld\n", hinf, debugstr_w(section), ret );
1177     SetLastError( (ret == -1) ? ERROR_SECTION_NOT_FOUND : 0 );
1178     return ret;
1179 }
1180
1181
1182 /***********************************************************************
1183  *            SetupGetLineByIndexA   (SETUPAPI.@)
1184  */
1185 BOOL WINAPI SetupGetLineByIndexA( HINF hinf, PCSTR section, DWORD index, INFCONTEXT *context )
1186 {
1187     UNICODE_STRING sectionW;
1188     BOOL ret = FALSE;
1189
1190     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1191         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1192     else
1193     {
1194         ret = SetupGetLineByIndexW( hinf, sectionW.Buffer, index, context );
1195         RtlFreeUnicodeString( &sectionW );
1196     }
1197     return ret;
1198 }
1199
1200
1201 /***********************************************************************
1202  *            SetupGetLineByIndexW   (SETUPAPI.@)
1203  */
1204 BOOL WINAPI SetupGetLineByIndexW( HINF hinf, PCWSTR section, DWORD index, INFCONTEXT *context )
1205 {
1206     struct inf_file *file = hinf;
1207     int section_index;
1208
1209     SetLastError( ERROR_SECTION_NOT_FOUND );
1210     for (file = hinf; file; file = file->next)
1211     {
1212         if ((section_index = find_section( file, section )) == -1) continue;
1213         SetLastError( ERROR_LINE_NOT_FOUND );
1214         if (index < file->sections[section_index]->nb_lines)
1215         {
1216             context->Inf        = hinf;
1217             context->CurrentInf = file;
1218             context->Section    = section_index;
1219             context->Line       = index;
1220             SetLastError( 0 );
1221             TRACE( "(%p,%s): returning %d/%ld\n",
1222                    hinf, debugstr_w(section), section_index, index );
1223             return TRUE;
1224         }
1225         index -= file->sections[section_index]->nb_lines;
1226     }
1227     TRACE( "(%p,%s) not found\n", hinf, debugstr_w(section) );
1228     return FALSE;
1229 }
1230
1231
1232 /***********************************************************************
1233  *            SetupFindFirstLineA   (SETUPAPI.@)
1234  */
1235 BOOL WINAPI SetupFindFirstLineA( HINF hinf, PCSTR section, PCSTR key, INFCONTEXT *context )
1236 {
1237     UNICODE_STRING sectionW, keyW;
1238     BOOL ret = FALSE;
1239
1240     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1241     {
1242         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1243         return FALSE;
1244     }
1245
1246     if (!key) ret = SetupFindFirstLineW( hinf, sectionW.Buffer, NULL, context );
1247     else
1248     {
1249         if (RtlCreateUnicodeStringFromAsciiz( &keyW, key ))
1250         {
1251             ret = SetupFindFirstLineW( hinf, sectionW.Buffer, keyW.Buffer, context );
1252             RtlFreeUnicodeString( &keyW );
1253         }
1254         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1255     }
1256     RtlFreeUnicodeString( &sectionW );
1257     return ret;
1258 }
1259
1260
1261 /***********************************************************************
1262  *            SetupFindFirstLineW   (SETUPAPI.@)
1263  */
1264 BOOL WINAPI SetupFindFirstLineW( HINF hinf, PCWSTR section, PCWSTR key, INFCONTEXT *context )
1265 {
1266     struct inf_file *file;
1267     int section_index;
1268
1269     SetLastError( ERROR_SECTION_NOT_FOUND );
1270     for (file = hinf; file; file = file->next)
1271     {
1272         if ((section_index = find_section( file, section )) == -1) continue;
1273         if (key)
1274         {
1275             INFCONTEXT ctx;
1276             ctx.Inf        = hinf;
1277             ctx.CurrentInf = file;
1278             ctx.Section    = section_index;
1279             ctx.Line       = -1;
1280             return SetupFindNextMatchLineW( &ctx, key, context );
1281         }
1282         SetLastError( ERROR_LINE_NOT_FOUND );  /* found at least one section */
1283         if (file->sections[section_index]->nb_lines)
1284         {
1285             context->Inf        = hinf;
1286             context->CurrentInf = file;
1287             context->Section    = section_index;
1288             context->Line       = 0;
1289             SetLastError( 0 );
1290             TRACE( "(%p,%s,%s): returning %d/0\n",
1291                    hinf, debugstr_w(section), debugstr_w(key), section_index );
1292             return TRUE;
1293         }
1294     }
1295     TRACE( "(%p,%s,%s): not found\n", hinf, debugstr_w(section), debugstr_w(key) );
1296     return FALSE;
1297 }
1298
1299
1300 /***********************************************************************
1301  *            SetupFindNextLine   (SETUPAPI.@)
1302  */
1303 BOOL WINAPI SetupFindNextLine( const INFCONTEXT *context_in, INFCONTEXT *context_out )
1304 {
1305     struct inf_file *file = context_in->CurrentInf;
1306     struct section *section;
1307
1308     if (context_in->Section >= file->nb_sections) goto error;
1309
1310     section = file->sections[context_in->Section];
1311     if (context_in->Line+1 < section->nb_lines)
1312     {
1313         if (context_out != context_in) *context_out = *context_in;
1314         context_out->Line++;
1315         SetLastError( 0 );
1316         return TRUE;
1317     }
1318
1319     /* now search the appended files */
1320
1321     for (file = file->next; file; file = file->next)
1322     {
1323         int section_index = find_section( file, section->name );
1324         if (section_index == -1) continue;
1325         if (file->sections[section_index]->nb_lines)
1326         {
1327             context_out->Inf        = context_in->Inf;
1328             context_out->CurrentInf = file;
1329             context_out->Section    = section_index;
1330             context_out->Line       = 0;
1331             SetLastError( 0 );
1332             return TRUE;
1333         }
1334     }
1335  error:
1336     SetLastError( ERROR_LINE_NOT_FOUND );
1337     return FALSE;
1338 }
1339
1340
1341 /***********************************************************************
1342  *            SetupFindNextMatchLineA   (SETUPAPI.@)
1343  */
1344 BOOL WINAPI SetupFindNextMatchLineA( const INFCONTEXT *context_in, PCSTR key,
1345                                      INFCONTEXT *context_out )
1346 {
1347     UNICODE_STRING keyW;
1348     BOOL ret = FALSE;
1349
1350     if (!key) return SetupFindNextLine( context_in, context_out );
1351
1352     if (!RtlCreateUnicodeStringFromAsciiz( &keyW, key ))
1353         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1354     else
1355     {
1356         ret = SetupFindNextMatchLineW( context_in, keyW.Buffer, context_out );
1357         RtlFreeUnicodeString( &keyW );
1358     }
1359     return ret;
1360 }
1361
1362
1363 /***********************************************************************
1364  *            SetupFindNextMatchLineW   (SETUPAPI.@)
1365  */
1366 BOOL WINAPI SetupFindNextMatchLineW( const INFCONTEXT *context_in, PCWSTR key,
1367                                      INFCONTEXT *context_out )
1368 {
1369     struct inf_file *file = context_in->CurrentInf;
1370     struct section *section;
1371     struct line *line;
1372     unsigned int i;
1373
1374     if (!key) return SetupFindNextLine( context_in, context_out );
1375
1376     if (context_in->Section >= file->nb_sections) goto error;
1377
1378     section = file->sections[context_in->Section];
1379
1380     for (i = context_in->Line+1, line = &section->lines[i]; i < section->nb_lines; i++, line++)
1381     {
1382         if (line->key_field == -1) continue;
1383         if (!strcmpiW( key, file->fields[line->key_field].text ))
1384         {
1385             if (context_out != context_in) *context_out = *context_in;
1386             context_out->Line = i;
1387             SetLastError( 0 );
1388             TRACE( "(%p,%s,%s): returning %d\n",
1389                    file, debugstr_w(section->name), debugstr_w(key), i );
1390             return TRUE;
1391         }
1392     }
1393
1394     /* now search the appended files */
1395
1396     for (file = file->next; file; file = file->next)
1397     {
1398         int section_index = find_section( file, section->name );
1399         if (section_index == -1) continue;
1400         section = file->sections[section_index];
1401         for (i = 0, line = section->lines; i < section->nb_lines; i++, line++)
1402         {
1403             if (line->key_field == -1) continue;
1404             if (!strcmpiW( key, file->fields[line->key_field].text ))
1405             {
1406                 context_out->Inf        = context_in->Inf;
1407                 context_out->CurrentInf = file;
1408                 context_out->Section    = section_index;
1409                 context_out->Line       = i;
1410                 SetLastError( 0 );
1411                 TRACE( "(%p,%s,%s): returning %d/%d\n",
1412                        file, debugstr_w(section->name), debugstr_w(key), section_index, i );
1413                 return TRUE;
1414             }
1415         }
1416     }
1417     TRACE( "(%p,%s,%s): not found\n",
1418            context_in->CurrentInf, debugstr_w(section->name), debugstr_w(key) );
1419  error:
1420     SetLastError( ERROR_LINE_NOT_FOUND );
1421     return FALSE;
1422 }
1423
1424
1425 /***********************************************************************
1426  *              SetupGetLineTextW    (SETUPAPI.@)
1427  */
1428 BOOL WINAPI SetupGetLineTextW( const INFCONTEXT *context, HINF hinf, PCWSTR section_name,
1429                                PCWSTR key_name, PWSTR buffer, DWORD size, DWORD *required )
1430 {
1431     struct inf_file *file;
1432     struct line *line;
1433     struct field *field;
1434     int i;
1435     DWORD total = 0;
1436
1437     if (!context)
1438     {
1439         INFCONTEXT new_context;
1440         if (!SetupFindFirstLineW( hinf, section_name, key_name, &new_context )) return FALSE;
1441         file = new_context.CurrentInf;
1442         line = get_line( file, new_context.Section, new_context.Line );
1443     }
1444     else
1445     {
1446         file = context->CurrentInf;
1447         if (!(line = get_line( file, context->Section, context->Line )))
1448         {
1449             SetLastError( ERROR_LINE_NOT_FOUND );
1450             return FALSE;
1451         }
1452     }
1453
1454     for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1455         total += PARSER_string_substW( file, field->text, NULL, 0 ) + 1;
1456
1457     if (required) *required = total;
1458     if (buffer)
1459     {
1460         if (total > size)
1461         {
1462             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1463             return FALSE;
1464         }
1465         for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1466         {
1467             unsigned int len = PARSER_string_substW( file, field->text, buffer, size );
1468             if (i+1 < line->nb_fields) buffer[len] = ',';
1469             buffer += len + 1;
1470         }
1471     }
1472     return TRUE;
1473 }
1474
1475
1476 /***********************************************************************
1477  *              SetupGetLineTextA    (SETUPAPI.@)
1478  */
1479 BOOL WINAPI SetupGetLineTextA( const INFCONTEXT *context, HINF hinf, PCSTR section_name,
1480                                PCSTR key_name, PSTR buffer, DWORD size, DWORD *required )
1481 {
1482     struct inf_file *file;
1483     struct line *line;
1484     struct field *field;
1485     int i;
1486     DWORD total = 0;
1487
1488     if (!context)
1489     {
1490         INFCONTEXT new_context;
1491         if (!SetupFindFirstLineA( hinf, section_name, key_name, &new_context )) return FALSE;
1492         file = new_context.CurrentInf;
1493         line = get_line( file, new_context.Section, new_context.Line );
1494     }
1495     else
1496     {
1497         file = context->CurrentInf;
1498         if (!(line = get_line( file, context->Section, context->Line )))
1499         {
1500             SetLastError( ERROR_LINE_NOT_FOUND );
1501             return FALSE;
1502         }
1503     }
1504
1505     for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1506         total += PARSER_string_substA( file, field->text, NULL, 0 ) + 1;
1507
1508     if (required) *required = total;
1509     if (buffer)
1510     {
1511         if (total > size)
1512         {
1513             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1514             return FALSE;
1515         }
1516         for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1517         {
1518             unsigned int len = PARSER_string_substA( file, field->text, buffer, size );
1519             if (i+1 < line->nb_fields) buffer[len] = ',';
1520             buffer += len + 1;
1521         }
1522     }
1523     return TRUE;
1524 }
1525
1526
1527 /***********************************************************************
1528  *              SetupGetFieldCount    (SETUPAPI.@)
1529  */
1530 DWORD WINAPI SetupGetFieldCount( const INFCONTEXT *context )
1531 {
1532     struct inf_file *file = context->CurrentInf;
1533     struct line *line = get_line( file, context->Section, context->Line );
1534
1535     if (!line) return 0;
1536     return line->nb_fields;
1537 }
1538
1539
1540 /***********************************************************************
1541  *              SetupGetStringFieldA    (SETUPAPI.@)
1542  */
1543 BOOL WINAPI SetupGetStringFieldA( const INFCONTEXT *context, DWORD index, PSTR buffer,
1544                                   DWORD size, DWORD *required )
1545 {
1546     struct inf_file *file = context->CurrentInf;
1547     struct field *field = get_field( file, context->Section, context->Line, index );
1548     unsigned int len;
1549
1550     SetLastError(0);
1551     if (!field) return FALSE;
1552     len = PARSER_string_substA( file, field->text, NULL, 0 );
1553     if (required) *required = len + 1;
1554     if (buffer)
1555     {
1556         if (size <= len)
1557         {
1558             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1559             return FALSE;
1560         }
1561         PARSER_string_substA( file, field->text, buffer, size );
1562
1563         TRACE( "context %p/%p/%d/%d index %ld returning %s\n",
1564                context->Inf, context->CurrentInf, context->Section, context->Line,
1565                index, debugstr_a(buffer) );
1566     }
1567     return TRUE;
1568 }
1569
1570
1571 /***********************************************************************
1572  *              SetupGetStringFieldW    (SETUPAPI.@)
1573  */
1574 BOOL WINAPI SetupGetStringFieldW( const INFCONTEXT *context, DWORD index, PWSTR buffer,
1575                                   DWORD size, DWORD *required )
1576 {
1577     struct inf_file *file = context->CurrentInf;
1578     struct field *field = get_field( file, context->Section, context->Line, index );
1579     unsigned int len;
1580
1581     SetLastError(0);
1582     if (!field) return FALSE;
1583     len = PARSER_string_substW( file, field->text, NULL, 0 );
1584     if (required) *required = len + 1;
1585     if (buffer)
1586     {
1587         if (size <= len)
1588         {
1589             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1590             return FALSE;
1591         }
1592         PARSER_string_substW( file, field->text, buffer, size );
1593
1594         TRACE( "context %p/%p/%d/%d index %ld returning %s\n",
1595                context->Inf, context->CurrentInf, context->Section, context->Line,
1596                index, debugstr_w(buffer) );
1597     }
1598     return TRUE;
1599 }
1600
1601
1602 /***********************************************************************
1603  *              SetupGetIntField    (SETUPAPI.@)
1604  */
1605 BOOL WINAPI SetupGetIntField( const INFCONTEXT *context, DWORD index, INT *result )
1606 {
1607     char localbuff[20];
1608     char *end, *buffer = localbuff;
1609     DWORD required;
1610     INT res;
1611     BOOL ret = FALSE;
1612
1613     if (!SetupGetStringFieldA( context, index, localbuff, sizeof(localbuff), &required ))
1614     {
1615         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
1616         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required ))) return FALSE;
1617         if (!SetupGetStringFieldA( context, index, buffer, required, NULL )) goto done;
1618     }
1619     res = strtol( buffer, &end, 0 );
1620     if (end != buffer && !*end)
1621     {
1622         *result = res;
1623         ret = TRUE;
1624     }
1625     else SetLastError( ERROR_INVALID_DATA );
1626
1627  done:
1628     if (buffer != localbuff) HeapFree( GetProcessHeap(), 0, buffer );
1629     return ret;
1630 }
1631
1632
1633 /***********************************************************************
1634  *              SetupGetBinaryField    (SETUPAPI.@)
1635  */
1636 BOOL WINAPI SetupGetBinaryField( const INFCONTEXT *context, DWORD index, BYTE *buffer,
1637                                  DWORD size, DWORD *required )
1638 {
1639     struct inf_file *file = context->CurrentInf;
1640     struct line *line = get_line( file, context->Section, context->Line );
1641     struct field *field;
1642     int i;
1643
1644     if (!line)
1645     {
1646         SetLastError( ERROR_LINE_NOT_FOUND );
1647         return FALSE;
1648     }
1649     if (!index || index >= line->nb_fields)
1650     {
1651         SetLastError( ERROR_INVALID_PARAMETER );
1652         return FALSE;
1653     }
1654     index--;  /* fields start at 0 */
1655     if (required) *required = line->nb_fields - index;
1656     if (!buffer) return TRUE;
1657     if (size < line->nb_fields - index)
1658     {
1659         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1660         return FALSE;
1661     }
1662     field = &file->fields[line->first_field + index];
1663     for (i = index; i < line->nb_fields; i++, field++)
1664     {
1665         const WCHAR *p;
1666         DWORD value = 0;
1667         for (p = field->text; *p && isxdigitW(*p); p++)
1668         {
1669             if ((value <<= 4) > 255)
1670             {
1671                 SetLastError( ERROR_INVALID_DATA );
1672                 return FALSE;
1673             }
1674             if (*p <= '9') value |= (*p - '0');
1675             else value |= (tolowerW(*p) - 'a' + 10);
1676         }
1677         buffer[i - index] = value;
1678     }
1679     if (TRACE_ON(setupapi))
1680     {
1681         TRACE( "%p/%p/%d/%d index %ld returning",
1682                context->Inf, context->CurrentInf, context->Section, context->Line, index );
1683         for (i = index; i < line->nb_fields; i++) DPRINTF( " %02x", buffer[i - index] );
1684         DPRINTF( "\n" );
1685     }
1686     return TRUE;
1687 }
1688
1689
1690 /***********************************************************************
1691  *              SetupGetMultiSzFieldA    (SETUPAPI.@)
1692  */
1693 BOOL WINAPI SetupGetMultiSzFieldA( const INFCONTEXT *context, DWORD index, PSTR buffer,
1694                                    DWORD size, DWORD *required )
1695 {
1696     struct inf_file *file = context->CurrentInf;
1697     struct line *line = get_line( file, context->Section, context->Line );
1698     struct field *field;
1699     unsigned int len;
1700     int i;
1701     DWORD total = 1;
1702
1703     if (!line)
1704     {
1705         SetLastError( ERROR_LINE_NOT_FOUND );
1706         return FALSE;
1707     }
1708     if (!index || index >= line->nb_fields)
1709     {
1710         SetLastError( ERROR_INVALID_PARAMETER );
1711         return FALSE;
1712     }
1713     index--;  /* fields start at 0 */
1714     field = &file->fields[line->first_field + index];
1715     for (i = index; i < line->nb_fields; i++, field++)
1716     {
1717         if (!(len = PARSER_string_substA( file, field->text, NULL, 0 ))) break;
1718         total += len + 1;
1719     }
1720
1721     if (required) *required = total;
1722     if (!buffer) return TRUE;
1723     if (total > size)
1724     {
1725         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1726         return FALSE;
1727     }
1728     field = &file->fields[line->first_field + index];
1729     for (i = index; i < line->nb_fields; i++, field++)
1730     {
1731         if (!(len = PARSER_string_substA( file, field->text, buffer, size ))) break;
1732         buffer += len + 1;
1733     }
1734     *buffer = 0;  /* add final null */
1735     return TRUE;
1736 }
1737
1738
1739 /***********************************************************************
1740  *              SetupGetMultiSzFieldW    (SETUPAPI.@)
1741  */
1742 BOOL WINAPI SetupGetMultiSzFieldW( const INFCONTEXT *context, DWORD index, PWSTR buffer,
1743                                    DWORD size, DWORD *required )
1744 {
1745     struct inf_file *file = context->CurrentInf;
1746     struct line *line = get_line( file, context->Section, context->Line );
1747     struct field *field;
1748     unsigned int len;
1749     int i;
1750     DWORD total = 1;
1751
1752     if (!line)
1753     {
1754         SetLastError( ERROR_LINE_NOT_FOUND );
1755         return FALSE;
1756     }
1757     if (!index || index >= line->nb_fields)
1758     {
1759         SetLastError( ERROR_INVALID_PARAMETER );
1760         return FALSE;
1761     }
1762     index--;  /* fields start at 0 */
1763     field = &file->fields[line->first_field + index];
1764     for (i = index; i < line->nb_fields; i++, field++)
1765     {
1766         if (!(len = PARSER_string_substW( file, field->text, NULL, 0 ))) break;
1767         total += len + 1;
1768     }
1769
1770     if (required) *required = total;
1771     if (!buffer) return TRUE;
1772     if (total > size)
1773     {
1774         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1775         return FALSE;
1776     }
1777     field = &file->fields[line->first_field + index];
1778     for (i = index; i < line->nb_fields; i++, field++)
1779     {
1780         if (!(len = PARSER_string_substW( file, field->text, buffer, size ))) break;
1781         buffer += len + 1;
1782     }
1783     *buffer = 0;  /* add final null */
1784     return TRUE;
1785 }