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