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