crypt32: Fix a test that fails on Windows.
[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         HeapFree( GetProcessHeap(), 0, array );
173     return new_array;
174 }
175
176
177 /* find a section by name */
178 static int find_section( struct inf_file *file, const WCHAR *name )
179 {
180     unsigned int i;
181
182     for (i = 0; i < file->nb_sections; i++)
183         if (!strcmpiW( name, file->sections[i]->name )) return i;
184     return -1;
185 }
186
187
188 /* find a line by name */
189 static struct line *find_line( struct inf_file *file, int section_index, const WCHAR *name )
190 {
191     struct section *section;
192     struct line *line;
193     int i;
194
195     if (section_index < 0 || section_index >= file->nb_sections) return NULL;
196     section = file->sections[section_index];
197     for (i = 0, line = section->lines; i < section->nb_lines; i++, line++)
198     {
199         if (line->key_field == -1) continue;
200         if (!strcmpiW( name, file->fields[line->key_field].text )) return line;
201     }
202     return NULL;
203 }
204
205
206 /* add a section to the file and return the section index */
207 static int add_section( struct inf_file *file, const WCHAR *name )
208 {
209     struct section *section;
210
211     if (file->nb_sections >= file->alloc_sections)
212     {
213         if (!(file->sections = grow_array( file->sections, &file->alloc_sections,
214                                            sizeof(file->sections[0]) ))) return -1;
215     }
216     if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) ))) return -1;
217     section->name        = name;
218     section->nb_lines    = 0;
219     section->alloc_lines = sizeof(section->lines)/sizeof(section->lines[0]);
220     file->sections[file->nb_sections] = section;
221     return file->nb_sections++;
222 }
223
224
225 /* add a line to a given section */
226 static struct line *add_line( struct inf_file *file, int section_index )
227 {
228     struct section *section;
229     struct line *line;
230
231     assert( section_index >= 0 && section_index < file->nb_sections );
232
233     section = file->sections[section_index];
234     if (section->nb_lines == section->alloc_lines)  /* need to grow the section */
235     {
236         int size = sizeof(*section) - sizeof(section->lines) + 2*section->alloc_lines*sizeof(*line);
237         if (!(section = HeapReAlloc( GetProcessHeap(), 0, section, size ))) return NULL;
238         section->alloc_lines *= 2;
239         file->sections[section_index] = section;
240     }
241     line = &section->lines[section->nb_lines++];
242     line->first_field = file->nb_fields;
243     line->nb_fields   = 0;
244     line->key_field   = -1;
245     return line;
246 }
247
248
249 /* retrieve a given line from section/line index */
250 inline static struct line *get_line( struct inf_file *file, unsigned int section_index,
251                                      unsigned int line_index )
252 {
253     struct section *section;
254
255     if (section_index >= file->nb_sections) return NULL;
256     section = file->sections[section_index];
257     if (line_index >= section->nb_lines) return NULL;
258     return &section->lines[line_index];
259 }
260
261
262 /* retrieve a given field from section/line/field index */
263 static struct field *get_field( struct inf_file *file, int section_index, int line_index,
264                                 int field_index )
265 {
266     struct line *line = get_line( file, section_index, line_index );
267
268     if (!line) return NULL;
269     if (!field_index)  /* get the key */
270     {
271         if (line->key_field == -1) return NULL;
272         return &file->fields[line->key_field];
273     }
274     field_index--;
275     if (field_index >= line->nb_fields) return NULL;
276     return &file->fields[line->first_field + field_index];
277 }
278
279
280 /* allocate a new field, growing the array if necessary */
281 static struct field *add_field( struct inf_file *file, const WCHAR *text )
282 {
283     struct field *field;
284
285     if (file->nb_fields >= file->alloc_fields)
286     {
287         if (!(file->fields = grow_array( file->fields, &file->alloc_fields,
288                                          sizeof(file->fields[0]) ))) return NULL;
289     }
290     field = &file->fields[file->nb_fields++];
291     field->text = text;
292     return field;
293 }
294
295
296 /* retrieve the string substitution for a directory id */
297 static const WCHAR *get_dirid_subst( int dirid, unsigned int *len )
298 {
299     extern const WCHAR *DIRID_get_string( HINF hinf, int dirid );
300     const WCHAR *ret = DIRID_get_string( 0, dirid );
301     if (ret) *len = strlenW(ret);
302     return ret;
303 }
304
305
306 /* retrieve the string substitution for a given string, or NULL if not found */
307 /* if found, len is set to the substitution length */
308 static const WCHAR *get_string_subst( struct inf_file *file, const WCHAR *str, unsigned int *len )
309 {
310     static const WCHAR percent = '%';
311
312     struct section *strings_section;
313     struct line *line;
314     struct field *field;
315     unsigned int i;
316     int 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_EXPECTED_SECTION_NAME;
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, p );
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, p );
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, size );
955             err = parse_buffer( file, new_buff, new_buff + len, error_line );
956             HeapFree( GetProcessHeap(), 0, new_buff );
957         }
958     }
959     else err = parse_buffer( file, buffer, (WCHAR *)((char *)buffer + size), error_line );
960
961     if (!err)  /* now check signature */
962     {
963         int version_index = find_section( file, Version );
964         if (version_index != -1)
965         {
966             struct line *line = find_line( file, version_index, Signature );
967             if (line && line->nb_fields > 0)
968             {
969                 struct field *field = file->fields + line->first_field;
970                 if (!strcmpiW( field->text, Chicago )) goto done;
971                 if (!strcmpiW( field->text, WindowsNT )) goto done;
972                 if (!strcmpiW( field->text, Windows95 )) goto done;
973             }
974         }
975         if (error_line) *error_line = 0;
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  *            SetupOpenMasterInf   (SETUPAPI.@)
1166  */
1167 HINF WINAPI SetupOpenMasterInf( VOID )
1168 {
1169     static const WCHAR Layout[] = {'\\','i','n','f','\\', 'l', 'a', 'y', 'o', 'u', 't', '.', 'i', 'n', 'f', 0};
1170     WCHAR Buffer[MAX_PATH];
1171
1172     GetWindowsDirectoryW( Buffer, MAX_PATH );
1173     strcatW( Buffer, Layout );
1174     return SetupOpenInfFileW( Buffer, NULL, INF_STYLE_WIN4, NULL);
1175 }
1176
1177
1178
1179 /***********************************************************************
1180  *            SetupCloseInfFile   (SETUPAPI.@)
1181  */
1182 void WINAPI SetupCloseInfFile( HINF hinf )
1183 {
1184     struct inf_file *file = hinf;
1185     unsigned int i;
1186
1187     for (i = 0; i < file->nb_sections; i++) HeapFree( GetProcessHeap(), 0, file->sections[i] );
1188     HeapFree( GetProcessHeap(), 0, file->src_root );
1189     HeapFree( GetProcessHeap(), 0, file->sections );
1190     HeapFree( GetProcessHeap(), 0, file->fields );
1191     HeapFree( GetProcessHeap(), 0, file->strings );
1192     HeapFree( GetProcessHeap(), 0, file );
1193 }
1194
1195
1196 /***********************************************************************
1197  *            SetupGetLineCountA   (SETUPAPI.@)
1198  */
1199 LONG WINAPI SetupGetLineCountA( HINF hinf, PCSTR name )
1200 {
1201     UNICODE_STRING sectionW;
1202     LONG ret = -1;
1203
1204     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, name ))
1205         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1206     else
1207     {
1208         ret = SetupGetLineCountW( hinf, sectionW.Buffer );
1209         RtlFreeUnicodeString( &sectionW );
1210     }
1211     return ret;
1212 }
1213
1214
1215 /***********************************************************************
1216  *            SetupGetLineCountW   (SETUPAPI.@)
1217  */
1218 LONG WINAPI SetupGetLineCountW( HINF hinf, PCWSTR section )
1219 {
1220     struct inf_file *file = hinf;
1221     int section_index;
1222     LONG ret = -1;
1223
1224     for (file = hinf; file; file = file->next)
1225     {
1226         if ((section_index = find_section( file, section )) == -1) continue;
1227         if (ret == -1) ret = 0;
1228         ret += file->sections[section_index]->nb_lines;
1229     }
1230     TRACE( "(%p,%s) returning %ld\n", hinf, debugstr_w(section), ret );
1231     SetLastError( (ret == -1) ? ERROR_SECTION_NOT_FOUND : 0 );
1232     return ret;
1233 }
1234
1235
1236 /***********************************************************************
1237  *            SetupGetLineByIndexA   (SETUPAPI.@)
1238  */
1239 BOOL WINAPI SetupGetLineByIndexA( HINF hinf, PCSTR section, DWORD index, INFCONTEXT *context )
1240 {
1241     UNICODE_STRING sectionW;
1242     BOOL ret = FALSE;
1243
1244     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1245         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1246     else
1247     {
1248         ret = SetupGetLineByIndexW( hinf, sectionW.Buffer, index, context );
1249         RtlFreeUnicodeString( &sectionW );
1250     }
1251     return ret;
1252 }
1253
1254
1255 /***********************************************************************
1256  *            SetupGetLineByIndexW   (SETUPAPI.@)
1257  */
1258 BOOL WINAPI SetupGetLineByIndexW( HINF hinf, PCWSTR section, DWORD index, INFCONTEXT *context )
1259 {
1260     struct inf_file *file = hinf;
1261     int section_index;
1262
1263     SetLastError( ERROR_SECTION_NOT_FOUND );
1264     for (file = hinf; file; file = file->next)
1265     {
1266         if ((section_index = find_section( file, section )) == -1) continue;
1267         SetLastError( ERROR_LINE_NOT_FOUND );
1268         if (index < file->sections[section_index]->nb_lines)
1269         {
1270             context->Inf        = hinf;
1271             context->CurrentInf = file;
1272             context->Section    = section_index;
1273             context->Line       = index;
1274             SetLastError( 0 );
1275             TRACE( "(%p,%s): returning %d/%ld\n",
1276                    hinf, debugstr_w(section), section_index, index );
1277             return TRUE;
1278         }
1279         index -= file->sections[section_index]->nb_lines;
1280     }
1281     TRACE( "(%p,%s) not found\n", hinf, debugstr_w(section) );
1282     return FALSE;
1283 }
1284
1285
1286 /***********************************************************************
1287  *            SetupFindFirstLineA   (SETUPAPI.@)
1288  */
1289 BOOL WINAPI SetupFindFirstLineA( HINF hinf, PCSTR section, PCSTR key, INFCONTEXT *context )
1290 {
1291     UNICODE_STRING sectionW, keyW;
1292     BOOL ret = FALSE;
1293
1294     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1295     {
1296         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1297         return FALSE;
1298     }
1299
1300     if (!key) ret = SetupFindFirstLineW( hinf, sectionW.Buffer, NULL, context );
1301     else
1302     {
1303         if (RtlCreateUnicodeStringFromAsciiz( &keyW, key ))
1304         {
1305             ret = SetupFindFirstLineW( hinf, sectionW.Buffer, keyW.Buffer, context );
1306             RtlFreeUnicodeString( &keyW );
1307         }
1308         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1309     }
1310     RtlFreeUnicodeString( &sectionW );
1311     return ret;
1312 }
1313
1314
1315 /***********************************************************************
1316  *            SetupFindFirstLineW   (SETUPAPI.@)
1317  */
1318 BOOL WINAPI SetupFindFirstLineW( HINF hinf, PCWSTR section, PCWSTR key, INFCONTEXT *context )
1319 {
1320     struct inf_file *file;
1321     int section_index;
1322
1323     SetLastError( ERROR_SECTION_NOT_FOUND );
1324     for (file = hinf; file; file = file->next)
1325     {
1326         if ((section_index = find_section( file, section )) == -1) continue;
1327         if (key)
1328         {
1329             INFCONTEXT ctx;
1330             ctx.Inf        = hinf;
1331             ctx.CurrentInf = file;
1332             ctx.Section    = section_index;
1333             ctx.Line       = -1;
1334             return SetupFindNextMatchLineW( &ctx, key, context );
1335         }
1336         SetLastError( ERROR_LINE_NOT_FOUND );  /* found at least one section */
1337         if (file->sections[section_index]->nb_lines)
1338         {
1339             context->Inf        = hinf;
1340             context->CurrentInf = file;
1341             context->Section    = section_index;
1342             context->Line       = 0;
1343             SetLastError( 0 );
1344             TRACE( "(%p,%s,%s): returning %d/0\n",
1345                    hinf, debugstr_w(section), debugstr_w(key), section_index );
1346             return TRUE;
1347         }
1348     }
1349     TRACE( "(%p,%s,%s): not found\n", hinf, debugstr_w(section), debugstr_w(key) );
1350     return FALSE;
1351 }
1352
1353
1354 /***********************************************************************
1355  *            SetupFindNextLine   (SETUPAPI.@)
1356  */
1357 BOOL WINAPI SetupFindNextLine( PINFCONTEXT context_in, PINFCONTEXT context_out )
1358 {
1359     struct inf_file *file = context_in->CurrentInf;
1360     struct section *section;
1361
1362     if (context_in->Section >= file->nb_sections) goto error;
1363
1364     section = file->sections[context_in->Section];
1365     if (context_in->Line+1 < section->nb_lines)
1366     {
1367         if (context_out != context_in) *context_out = *context_in;
1368         context_out->Line++;
1369         SetLastError( 0 );
1370         return TRUE;
1371     }
1372
1373     /* now search the appended files */
1374
1375     for (file = file->next; file; file = file->next)
1376     {
1377         int section_index = find_section( file, section->name );
1378         if (section_index == -1) continue;
1379         if (file->sections[section_index]->nb_lines)
1380         {
1381             context_out->Inf        = context_in->Inf;
1382             context_out->CurrentInf = file;
1383             context_out->Section    = section_index;
1384             context_out->Line       = 0;
1385             SetLastError( 0 );
1386             return TRUE;
1387         }
1388     }
1389  error:
1390     SetLastError( ERROR_LINE_NOT_FOUND );
1391     return FALSE;
1392 }
1393
1394
1395 /***********************************************************************
1396  *            SetupFindNextMatchLineA   (SETUPAPI.@)
1397  */
1398 BOOL WINAPI SetupFindNextMatchLineA( PINFCONTEXT context_in, PCSTR key,
1399                                      PINFCONTEXT context_out )
1400 {
1401     UNICODE_STRING keyW;
1402     BOOL ret = FALSE;
1403
1404     if (!key) return SetupFindNextLine( context_in, context_out );
1405
1406     if (!RtlCreateUnicodeStringFromAsciiz( &keyW, key ))
1407         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1408     else
1409     {
1410         ret = SetupFindNextMatchLineW( context_in, keyW.Buffer, context_out );
1411         RtlFreeUnicodeString( &keyW );
1412     }
1413     return ret;
1414 }
1415
1416
1417 /***********************************************************************
1418  *            SetupFindNextMatchLineW   (SETUPAPI.@)
1419  */
1420 BOOL WINAPI SetupFindNextMatchLineW( PINFCONTEXT context_in, PCWSTR key,
1421                                      PINFCONTEXT context_out )
1422 {
1423     struct inf_file *file = context_in->CurrentInf;
1424     struct section *section;
1425     struct line *line;
1426     unsigned int i;
1427
1428     if (!key) return SetupFindNextLine( context_in, context_out );
1429
1430     if (context_in->Section >= file->nb_sections) goto error;
1431
1432     section = file->sections[context_in->Section];
1433
1434     for (i = context_in->Line+1, line = &section->lines[i]; i < section->nb_lines; i++, line++)
1435     {
1436         if (line->key_field == -1) continue;
1437         if (!strcmpiW( key, file->fields[line->key_field].text ))
1438         {
1439             if (context_out != context_in) *context_out = *context_in;
1440             context_out->Line = i;
1441             SetLastError( 0 );
1442             TRACE( "(%p,%s,%s): returning %d\n",
1443                    file, debugstr_w(section->name), debugstr_w(key), i );
1444             return TRUE;
1445         }
1446     }
1447
1448     /* now search the appended files */
1449
1450     for (file = file->next; file; file = file->next)
1451     {
1452         int section_index = find_section( file, section->name );
1453         if (section_index == -1) continue;
1454         section = file->sections[section_index];
1455         for (i = 0, line = section->lines; i < section->nb_lines; i++, line++)
1456         {
1457             if (line->key_field == -1) continue;
1458             if (!strcmpiW( key, file->fields[line->key_field].text ))
1459             {
1460                 context_out->Inf        = context_in->Inf;
1461                 context_out->CurrentInf = file;
1462                 context_out->Section    = section_index;
1463                 context_out->Line       = i;
1464                 SetLastError( 0 );
1465                 TRACE( "(%p,%s,%s): returning %d/%d\n",
1466                        file, debugstr_w(section->name), debugstr_w(key), section_index, i );
1467                 return TRUE;
1468             }
1469         }
1470     }
1471     TRACE( "(%p,%s,%s): not found\n",
1472            context_in->CurrentInf, debugstr_w(section->name), debugstr_w(key) );
1473  error:
1474     SetLastError( ERROR_LINE_NOT_FOUND );
1475     return FALSE;
1476 }
1477
1478
1479 /***********************************************************************
1480  *              SetupGetLineTextW    (SETUPAPI.@)
1481  */
1482 BOOL WINAPI SetupGetLineTextW( PINFCONTEXT context, HINF hinf, PCWSTR section_name,
1483                                PCWSTR key_name, PWSTR buffer, DWORD size, PDWORD required )
1484 {
1485     struct inf_file *file;
1486     struct line *line;
1487     struct field *field;
1488     int i;
1489     DWORD total = 0;
1490
1491     if (!context)
1492     {
1493         INFCONTEXT new_context;
1494         if (!SetupFindFirstLineW( hinf, section_name, key_name, &new_context )) return FALSE;
1495         file = new_context.CurrentInf;
1496         line = get_line( file, new_context.Section, new_context.Line );
1497     }
1498     else
1499     {
1500         file = context->CurrentInf;
1501         if (!(line = get_line( file, context->Section, context->Line )))
1502         {
1503             SetLastError( ERROR_LINE_NOT_FOUND );
1504             return FALSE;
1505         }
1506     }
1507
1508     for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1509         total += PARSER_string_substW( file, field->text, NULL, 0 ) + 1;
1510
1511     if (required) *required = total;
1512     if (buffer)
1513     {
1514         if (total > size)
1515         {
1516             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1517             return FALSE;
1518         }
1519         for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1520         {
1521             unsigned int len = PARSER_string_substW( file, field->text, buffer, size );
1522             if (i+1 < line->nb_fields) buffer[len] = ',';
1523             buffer += len + 1;
1524         }
1525     }
1526     return TRUE;
1527 }
1528
1529
1530 /***********************************************************************
1531  *              SetupGetLineTextA    (SETUPAPI.@)
1532  */
1533 BOOL WINAPI SetupGetLineTextA( PINFCONTEXT context, HINF hinf, PCSTR section_name,
1534                                PCSTR key_name, PSTR buffer, DWORD size, PDWORD required )
1535 {
1536     struct inf_file *file;
1537     struct line *line;
1538     struct field *field;
1539     int i;
1540     DWORD total = 0;
1541
1542     if (!context)
1543     {
1544         INFCONTEXT new_context;
1545         if (!SetupFindFirstLineA( hinf, section_name, key_name, &new_context )) return FALSE;
1546         file = new_context.CurrentInf;
1547         line = get_line( file, new_context.Section, new_context.Line );
1548     }
1549     else
1550     {
1551         file = context->CurrentInf;
1552         if (!(line = get_line( file, context->Section, context->Line )))
1553         {
1554             SetLastError( ERROR_LINE_NOT_FOUND );
1555             return FALSE;
1556         }
1557     }
1558
1559     for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1560         total += PARSER_string_substA( file, field->text, NULL, 0 ) + 1;
1561
1562     if (required) *required = total;
1563     if (buffer)
1564     {
1565         if (total > size)
1566         {
1567             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1568             return FALSE;
1569         }
1570         for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1571         {
1572             unsigned int len = PARSER_string_substA( file, field->text, buffer, size );
1573             if (i+1 < line->nb_fields) buffer[len] = ',';
1574             buffer += len + 1;
1575         }
1576     }
1577     return TRUE;
1578 }
1579
1580
1581 /***********************************************************************
1582  *              SetupGetFieldCount    (SETUPAPI.@)
1583  */
1584 DWORD WINAPI SetupGetFieldCount( PINFCONTEXT context )
1585 {
1586     struct inf_file *file = context->CurrentInf;
1587     struct line *line = get_line( file, context->Section, context->Line );
1588
1589     if (!line) return 0;
1590     return line->nb_fields;
1591 }
1592
1593
1594 /***********************************************************************
1595  *              SetupGetStringFieldA    (SETUPAPI.@)
1596  */
1597 BOOL WINAPI SetupGetStringFieldA( PINFCONTEXT context, DWORD index, PSTR buffer,
1598                                   DWORD size, PDWORD required )
1599 {
1600     struct inf_file *file = context->CurrentInf;
1601     struct field *field = get_field( file, context->Section, context->Line, index );
1602     unsigned int len;
1603
1604     SetLastError(0);
1605     if (!field) return FALSE;
1606     len = PARSER_string_substA( file, field->text, NULL, 0 );
1607     if (required) *required = len + 1;
1608     if (buffer)
1609     {
1610         if (size <= len)
1611         {
1612             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1613             return FALSE;
1614         }
1615         PARSER_string_substA( file, field->text, buffer, size );
1616
1617         TRACE( "context %p/%p/%d/%d index %ld returning %s\n",
1618                context->Inf, context->CurrentInf, context->Section, context->Line,
1619                index, debugstr_a(buffer) );
1620     }
1621     return TRUE;
1622 }
1623
1624
1625 /***********************************************************************
1626  *              SetupGetStringFieldW    (SETUPAPI.@)
1627  */
1628 BOOL WINAPI SetupGetStringFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer,
1629                                   DWORD size, PDWORD required )
1630 {
1631     struct inf_file *file = context->CurrentInf;
1632     struct field *field = get_field( file, context->Section, context->Line, index );
1633     unsigned int len;
1634
1635     SetLastError(0);
1636     if (!field) return FALSE;
1637     len = PARSER_string_substW( file, field->text, NULL, 0 );
1638     if (required) *required = len + 1;
1639     if (buffer)
1640     {
1641         if (size <= len)
1642         {
1643             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1644             return FALSE;
1645         }
1646         PARSER_string_substW( file, field->text, buffer, size );
1647
1648         TRACE( "context %p/%p/%d/%d index %ld returning %s\n",
1649                context->Inf, context->CurrentInf, context->Section, context->Line,
1650                index, debugstr_w(buffer) );
1651     }
1652     return TRUE;
1653 }
1654
1655
1656 /***********************************************************************
1657  *              SetupGetIntField    (SETUPAPI.@)
1658  */
1659 BOOL WINAPI SetupGetIntField( PINFCONTEXT context, DWORD index, PINT result )
1660 {
1661     char localbuff[20];
1662     char *end, *buffer = localbuff;
1663     DWORD required;
1664     INT res;
1665     BOOL ret = FALSE;
1666
1667     if (!SetupGetStringFieldA( context, index, localbuff, sizeof(localbuff), &required ))
1668     {
1669         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
1670         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required ))) return FALSE;
1671         if (!SetupGetStringFieldA( context, index, buffer, required, NULL )) goto done;
1672     }
1673     res = strtol( buffer, &end, 0 );
1674     if (end != buffer && !*end)
1675     {
1676         *result = res;
1677         ret = TRUE;
1678     }
1679     else SetLastError( ERROR_INVALID_DATA );
1680
1681  done:
1682     if (buffer != localbuff) HeapFree( GetProcessHeap(), 0, buffer );
1683     return ret;
1684 }
1685
1686
1687 /***********************************************************************
1688  *              SetupGetBinaryField    (SETUPAPI.@)
1689  */
1690 BOOL WINAPI SetupGetBinaryField( PINFCONTEXT context, DWORD index, BYTE *buffer,
1691                                  DWORD size, LPDWORD required )
1692 {
1693     struct inf_file *file = context->CurrentInf;
1694     struct line *line = get_line( file, context->Section, context->Line );
1695     struct field *field;
1696     int i;
1697
1698     if (!line)
1699     {
1700         SetLastError( ERROR_LINE_NOT_FOUND );
1701         return FALSE;
1702     }
1703     if (!index || index > line->nb_fields)
1704     {
1705         SetLastError( ERROR_INVALID_PARAMETER );
1706         return FALSE;
1707     }
1708     index--;  /* fields start at 0 */
1709     if (required) *required = line->nb_fields - index;
1710     if (!buffer) return TRUE;
1711     if (size < line->nb_fields - index)
1712     {
1713         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1714         return FALSE;
1715     }
1716     field = &file->fields[line->first_field + index];
1717     for (i = index; i < line->nb_fields; i++, field++)
1718     {
1719         const WCHAR *p;
1720         DWORD value = 0;
1721         for (p = field->text; *p && isxdigitW(*p); p++)
1722         {
1723             if ((value <<= 4) > 255)
1724             {
1725                 SetLastError( ERROR_INVALID_DATA );
1726                 return FALSE;
1727             }
1728             if (*p <= '9') value |= (*p - '0');
1729             else value |= (tolowerW(*p) - 'a' + 10);
1730         }
1731         buffer[i - index] = value;
1732     }
1733     if (TRACE_ON(setupapi))
1734     {
1735         TRACE( "%p/%p/%d/%d index %ld returning",
1736                context->Inf, context->CurrentInf, context->Section, context->Line, index );
1737         for (i = index; i < line->nb_fields; i++) TRACE( " %02x", buffer[i - index] );
1738         TRACE( "\n" );
1739     }
1740     return TRUE;
1741 }
1742
1743
1744 /***********************************************************************
1745  *              SetupGetMultiSzFieldA    (SETUPAPI.@)
1746  */
1747 BOOL WINAPI SetupGetMultiSzFieldA( PINFCONTEXT context, DWORD index, PSTR buffer,
1748                                    DWORD size, LPDWORD required )
1749 {
1750     struct inf_file *file = context->CurrentInf;
1751     struct line *line = get_line( file, context->Section, context->Line );
1752     struct field *field;
1753     unsigned int len;
1754     int i;
1755     DWORD total = 1;
1756
1757     if (!line)
1758     {
1759         SetLastError( ERROR_LINE_NOT_FOUND );
1760         return FALSE;
1761     }
1762     if (!index || index > line->nb_fields)
1763     {
1764         SetLastError( ERROR_INVALID_PARAMETER );
1765         return FALSE;
1766     }
1767     index--;  /* fields start at 0 */
1768     field = &file->fields[line->first_field + index];
1769     for (i = index; i < line->nb_fields; i++, field++)
1770     {
1771         if (!(len = PARSER_string_substA( file, field->text, NULL, 0 ))) break;
1772         total += len + 1;
1773     }
1774
1775     if (required) *required = total;
1776     if (!buffer) return TRUE;
1777     if (total > size)
1778     {
1779         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1780         return FALSE;
1781     }
1782     field = &file->fields[line->first_field + index];
1783     for (i = index; i < line->nb_fields; i++, field++)
1784     {
1785         if (!(len = PARSER_string_substA( file, field->text, buffer, size ))) break;
1786         buffer += len + 1;
1787     }
1788     *buffer = 0;  /* add final null */
1789     return TRUE;
1790 }
1791
1792
1793 /***********************************************************************
1794  *              SetupGetMultiSzFieldW    (SETUPAPI.@)
1795  */
1796 BOOL WINAPI SetupGetMultiSzFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer,
1797                                    DWORD size, LPDWORD required )
1798 {
1799     struct inf_file *file = context->CurrentInf;
1800     struct line *line = get_line( file, context->Section, context->Line );
1801     struct field *field;
1802     unsigned int len;
1803     int i;
1804     DWORD total = 1;
1805
1806     if (!line)
1807     {
1808         SetLastError( ERROR_LINE_NOT_FOUND );
1809         return FALSE;
1810     }
1811     if (!index || index > line->nb_fields)
1812     {
1813         SetLastError( ERROR_INVALID_PARAMETER );
1814         return FALSE;
1815     }
1816     index--;  /* fields start at 0 */
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, NULL, 0 ))) break;
1821         total += len + 1;
1822     }
1823
1824     if (required) *required = total;
1825     if (!buffer) return TRUE;
1826     if (total > size)
1827     {
1828         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1829         return FALSE;
1830     }
1831     field = &file->fields[line->first_field + index];
1832     for (i = index; i < line->nb_fields; i++, field++)
1833     {
1834         if (!(len = PARSER_string_substW( file, field->text, buffer, size ))) break;
1835         buffer += len + 1;
1836     }
1837     *buffer = 0;  /* add final null */
1838     return TRUE;
1839 }