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