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