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