Stub implementation for MsiGetFileHashA/W.
[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_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, 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         err = ERROR_WRONG_INF_STYLE;
976     }
977
978  done:
979     UnmapViewOfFile( buffer );
980     if (err)
981     {
982         HeapFree( GetProcessHeap(), 0, file );
983         SetLastError( err );
984         file = NULL;
985     }
986     return file;
987 }
988
989
990 /***********************************************************************
991  *            PARSER_get_src_root
992  *
993  * Retrieve the source directory of an inf file.
994  */
995 const WCHAR *PARSER_get_src_root( HINF hinf )
996 {
997     struct inf_file *file = hinf;
998     return file->src_root;
999 }
1000
1001
1002 /***********************************************************************
1003  *            PARSER_get_dest_dir
1004  *
1005  * retrieve a destination dir of the form "dirid,relative_path" in the given entry.
1006  * returned buffer must be freed by caller.
1007  */
1008 WCHAR *PARSER_get_dest_dir( INFCONTEXT *context )
1009 {
1010     const WCHAR *dir;
1011     WCHAR *ptr, *ret;
1012     INT dirid;
1013     DWORD len1, len2;
1014
1015     if (!SetupGetIntField( context, 1, &dirid )) return NULL;
1016     if (!(dir = DIRID_get_string( context->Inf, dirid ))) return NULL;
1017     len1 = strlenW(dir) + 1;
1018     if (!SetupGetStringFieldW( context, 2, NULL, 0, &len2 )) len2 = 0;
1019     if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len1+len2) * sizeof(WCHAR) ))) return NULL;
1020     strcpyW( ret, dir );
1021     ptr = ret + strlenW(ret);
1022     if (len2 && ptr > ret && ptr[-1] != '\\') *ptr++ = '\\';
1023     if (!SetupGetStringFieldW( context, 2, ptr, len2, NULL )) *ptr = 0;
1024     return ret;
1025 }
1026
1027
1028 /***********************************************************************
1029  *            SetupOpenInfFileA   (SETUPAPI.@)
1030  */
1031 HINF WINAPI SetupOpenInfFileA( PCSTR name, PCSTR class, DWORD style, UINT *error )
1032 {
1033     UNICODE_STRING nameW, classW;
1034     HINF ret = (HINF)INVALID_HANDLE_VALUE;
1035
1036     classW.Buffer = NULL;
1037     if (class && !RtlCreateUnicodeStringFromAsciiz( &classW, class ))
1038     {
1039         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1040         return ret;
1041     }
1042     if (RtlCreateUnicodeStringFromAsciiz( &nameW, name ))
1043     {
1044         ret = SetupOpenInfFileW( nameW.Buffer, classW.Buffer, style, error );
1045         RtlFreeUnicodeString( &nameW );
1046     }
1047     RtlFreeUnicodeString( &classW );
1048     return ret;
1049 }
1050
1051
1052 /***********************************************************************
1053  *            SetupOpenInfFileW   (SETUPAPI.@)
1054  */
1055 HINF WINAPI SetupOpenInfFileW( PCWSTR name, PCWSTR class, DWORD style, UINT *error )
1056 {
1057     struct inf_file *file = NULL;
1058     HANDLE handle;
1059     WCHAR *path, *p;
1060     UINT len;
1061
1062     if (strchrW( name, '\\' ) || strchrW( name, '/' ))
1063     {
1064         if (!(len = GetFullPathNameW( name, 0, NULL, NULL ))) return (HINF)INVALID_HANDLE_VALUE;
1065         if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1066         {
1067             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1068             return (HINF)INVALID_HANDLE_VALUE;
1069         }
1070         GetFullPathNameW( name, len, path, NULL );
1071         handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1072     }
1073     else  /* try Windows directory */
1074     {
1075         static const WCHAR Inf[]      = {'\\','i','n','f','\\',0};
1076         static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
1077
1078         len = GetWindowsDirectoryW( NULL, 0 ) + strlenW(name) + 12;
1079         if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
1080         {
1081             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1082             return (HINF)INVALID_HANDLE_VALUE;
1083         }
1084         GetWindowsDirectoryW( path, len );
1085         p = path + strlenW(path);
1086         strcpyW( p, Inf );
1087         strcatW( p, name );
1088         handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1089         if (handle == INVALID_HANDLE_VALUE)
1090         {
1091             strcpyW( p, System32 );
1092             strcatW( p, name );
1093             handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1094         }
1095     }
1096
1097     if (handle != INVALID_HANDLE_VALUE)
1098     {
1099         file = parse_file( handle, class, error );
1100         CloseHandle( handle );
1101     }
1102     if (!file)
1103     {
1104         HeapFree( GetProcessHeap(), 0, path );
1105         return (HINF)INVALID_HANDLE_VALUE;
1106     }
1107     TRACE( "%s -> %p\n", debugstr_w(path), file );
1108     file->src_root = path;
1109     if ((p = strrchrW( path, '\\' ))) p[1] = 0;  /* remove file name */
1110     SetLastError( 0 );
1111     return (HINF)file;
1112 }
1113
1114
1115 /***********************************************************************
1116  *            SetupOpenAppendInfFileA    (SETUPAPI.@)
1117  */
1118 BOOL WINAPI SetupOpenAppendInfFileA( PCSTR name, HINF parent_hinf, UINT *error )
1119 {
1120     HINF child_hinf;
1121
1122     if (!name) return SetupOpenAppendInfFileW( NULL, parent_hinf, error );
1123     child_hinf = SetupOpenInfFileA( name, NULL, INF_STYLE_WIN4, error );
1124     if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE;
1125     append_inf_file( parent_hinf, child_hinf );
1126     TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_a(name), child_hinf );
1127     return TRUE;
1128 }
1129
1130
1131 /***********************************************************************
1132  *            SetupOpenAppendInfFileW    (SETUPAPI.@)
1133  */
1134 BOOL WINAPI SetupOpenAppendInfFileW( PCWSTR name, HINF parent_hinf, UINT *error )
1135 {
1136     HINF child_hinf;
1137
1138     if (!name)
1139     {
1140         INFCONTEXT context;
1141         WCHAR filename[MAX_PATH];
1142         int idx = 1;
1143
1144         if (!SetupFindFirstLineW( parent_hinf, Version, LayoutFile, &context )) return FALSE;
1145         while (SetupGetStringFieldW( &context, idx++, filename,
1146                                      sizeof(filename)/sizeof(WCHAR), NULL ))
1147         {
1148             child_hinf = SetupOpenInfFileW( filename, NULL, INF_STYLE_WIN4, error );
1149             if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE;
1150             append_inf_file( parent_hinf, child_hinf );
1151             TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(filename), child_hinf );
1152         }
1153         return TRUE;
1154     }
1155     child_hinf = SetupOpenInfFileW( name, NULL, INF_STYLE_WIN4, error );
1156     if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE;
1157     append_inf_file( parent_hinf, child_hinf );
1158     TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(name), child_hinf );
1159     return TRUE;
1160 }
1161
1162
1163 /***********************************************************************
1164  *            SetupOpenMasterInf   (SETUPAPI.@)
1165  */
1166 HINF WINAPI SetupOpenMasterInf( VOID )
1167 {
1168     static const WCHAR Layout[] = {'\\','i','n','f','\\', 'l', 'a', 'y', 'o', 'u', 't', '.', 'i', 'n', 'f', 0};
1169     WCHAR Buffer[MAX_PATH];
1170
1171     GetWindowsDirectoryW( Buffer, MAX_PATH );
1172     strcatW( Buffer, Layout );
1173     return SetupOpenInfFileW( Buffer, NULL, INF_STYLE_WIN4, NULL);
1174 }
1175
1176
1177
1178 /***********************************************************************
1179  *            SetupCloseInfFile   (SETUPAPI.@)
1180  */
1181 void WINAPI SetupCloseInfFile( HINF hinf )
1182 {
1183     struct inf_file *file = hinf;
1184     unsigned int i;
1185
1186     for (i = 0; i < file->nb_sections; i++) HeapFree( GetProcessHeap(), 0, file->sections[i] );
1187     HeapFree( GetProcessHeap(), 0, file->src_root );
1188     HeapFree( GetProcessHeap(), 0, file->sections );
1189     HeapFree( GetProcessHeap(), 0, file->fields );
1190     HeapFree( GetProcessHeap(), 0, file->strings );
1191     HeapFree( GetProcessHeap(), 0, file );
1192 }
1193
1194
1195 /***********************************************************************
1196  *            SetupGetLineCountA   (SETUPAPI.@)
1197  */
1198 LONG WINAPI SetupGetLineCountA( HINF hinf, PCSTR name )
1199 {
1200     UNICODE_STRING sectionW;
1201     LONG ret = -1;
1202
1203     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, name ))
1204         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1205     else
1206     {
1207         ret = SetupGetLineCountW( hinf, sectionW.Buffer );
1208         RtlFreeUnicodeString( &sectionW );
1209     }
1210     return ret;
1211 }
1212
1213
1214 /***********************************************************************
1215  *            SetupGetLineCountW   (SETUPAPI.@)
1216  */
1217 LONG WINAPI SetupGetLineCountW( HINF hinf, PCWSTR section )
1218 {
1219     struct inf_file *file = hinf;
1220     int section_index;
1221     LONG ret = -1;
1222
1223     for (file = hinf; file; file = file->next)
1224     {
1225         if ((section_index = find_section( file, section )) == -1) continue;
1226         if (ret == -1) ret = 0;
1227         ret += file->sections[section_index]->nb_lines;
1228     }
1229     TRACE( "(%p,%s) returning %ld\n", hinf, debugstr_w(section), ret );
1230     SetLastError( (ret == -1) ? ERROR_SECTION_NOT_FOUND : 0 );
1231     return ret;
1232 }
1233
1234
1235 /***********************************************************************
1236  *            SetupGetLineByIndexA   (SETUPAPI.@)
1237  */
1238 BOOL WINAPI SetupGetLineByIndexA( HINF hinf, PCSTR section, DWORD index, INFCONTEXT *context )
1239 {
1240     UNICODE_STRING sectionW;
1241     BOOL ret = FALSE;
1242
1243     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1244         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1245     else
1246     {
1247         ret = SetupGetLineByIndexW( hinf, sectionW.Buffer, index, context );
1248         RtlFreeUnicodeString( &sectionW );
1249     }
1250     return ret;
1251 }
1252
1253
1254 /***********************************************************************
1255  *            SetupGetLineByIndexW   (SETUPAPI.@)
1256  */
1257 BOOL WINAPI SetupGetLineByIndexW( HINF hinf, PCWSTR section, DWORD index, INFCONTEXT *context )
1258 {
1259     struct inf_file *file = hinf;
1260     int section_index;
1261
1262     SetLastError( ERROR_SECTION_NOT_FOUND );
1263     for (file = hinf; file; file = file->next)
1264     {
1265         if ((section_index = find_section( file, section )) == -1) continue;
1266         SetLastError( ERROR_LINE_NOT_FOUND );
1267         if (index < file->sections[section_index]->nb_lines)
1268         {
1269             context->Inf        = hinf;
1270             context->CurrentInf = file;
1271             context->Section    = section_index;
1272             context->Line       = index;
1273             SetLastError( 0 );
1274             TRACE( "(%p,%s): returning %d/%ld\n",
1275                    hinf, debugstr_w(section), section_index, index );
1276             return TRUE;
1277         }
1278         index -= file->sections[section_index]->nb_lines;
1279     }
1280     TRACE( "(%p,%s) not found\n", hinf, debugstr_w(section) );
1281     return FALSE;
1282 }
1283
1284
1285 /***********************************************************************
1286  *            SetupFindFirstLineA   (SETUPAPI.@)
1287  */
1288 BOOL WINAPI SetupFindFirstLineA( HINF hinf, PCSTR section, PCSTR key, INFCONTEXT *context )
1289 {
1290     UNICODE_STRING sectionW, keyW;
1291     BOOL ret = FALSE;
1292
1293     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1294     {
1295         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1296         return FALSE;
1297     }
1298
1299     if (!key) ret = SetupFindFirstLineW( hinf, sectionW.Buffer, NULL, context );
1300     else
1301     {
1302         if (RtlCreateUnicodeStringFromAsciiz( &keyW, key ))
1303         {
1304             ret = SetupFindFirstLineW( hinf, sectionW.Buffer, keyW.Buffer, context );
1305             RtlFreeUnicodeString( &keyW );
1306         }
1307         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1308     }
1309     RtlFreeUnicodeString( &sectionW );
1310     return ret;
1311 }
1312
1313
1314 /***********************************************************************
1315  *            SetupFindFirstLineW   (SETUPAPI.@)
1316  */
1317 BOOL WINAPI SetupFindFirstLineW( HINF hinf, PCWSTR section, PCWSTR key, INFCONTEXT *context )
1318 {
1319     struct inf_file *file;
1320     int section_index;
1321
1322     SetLastError( ERROR_SECTION_NOT_FOUND );
1323     for (file = hinf; file; file = file->next)
1324     {
1325         if ((section_index = find_section( file, section )) == -1) continue;
1326         if (key)
1327         {
1328             INFCONTEXT ctx;
1329             ctx.Inf        = hinf;
1330             ctx.CurrentInf = file;
1331             ctx.Section    = section_index;
1332             ctx.Line       = -1;
1333             return SetupFindNextMatchLineW( &ctx, key, context );
1334         }
1335         SetLastError( ERROR_LINE_NOT_FOUND );  /* found at least one section */
1336         if (file->sections[section_index]->nb_lines)
1337         {
1338             context->Inf        = hinf;
1339             context->CurrentInf = file;
1340             context->Section    = section_index;
1341             context->Line       = 0;
1342             SetLastError( 0 );
1343             TRACE( "(%p,%s,%s): returning %d/0\n",
1344                    hinf, debugstr_w(section), debugstr_w(key), section_index );
1345             return TRUE;
1346         }
1347     }
1348     TRACE( "(%p,%s,%s): not found\n", hinf, debugstr_w(section), debugstr_w(key) );
1349     return FALSE;
1350 }
1351
1352
1353 /***********************************************************************
1354  *            SetupFindNextLine   (SETUPAPI.@)
1355  */
1356 BOOL WINAPI SetupFindNextLine( PINFCONTEXT context_in, PINFCONTEXT context_out )
1357 {
1358     struct inf_file *file = context_in->CurrentInf;
1359     struct section *section;
1360
1361     if (context_in->Section >= file->nb_sections) goto error;
1362
1363     section = file->sections[context_in->Section];
1364     if (context_in->Line+1 < section->nb_lines)
1365     {
1366         if (context_out != context_in) *context_out = *context_in;
1367         context_out->Line++;
1368         SetLastError( 0 );
1369         return TRUE;
1370     }
1371
1372     /* now search the appended files */
1373
1374     for (file = file->next; file; file = file->next)
1375     {
1376         int section_index = find_section( file, section->name );
1377         if (section_index == -1) continue;
1378         if (file->sections[section_index]->nb_lines)
1379         {
1380             context_out->Inf        = context_in->Inf;
1381             context_out->CurrentInf = file;
1382             context_out->Section    = section_index;
1383             context_out->Line       = 0;
1384             SetLastError( 0 );
1385             return TRUE;
1386         }
1387     }
1388  error:
1389     SetLastError( ERROR_LINE_NOT_FOUND );
1390     return FALSE;
1391 }
1392
1393
1394 /***********************************************************************
1395  *            SetupFindNextMatchLineA   (SETUPAPI.@)
1396  */
1397 BOOL WINAPI SetupFindNextMatchLineA( PINFCONTEXT context_in, PCSTR key,
1398                                      PINFCONTEXT context_out )
1399 {
1400     UNICODE_STRING keyW;
1401     BOOL ret = FALSE;
1402
1403     if (!key) return SetupFindNextLine( context_in, context_out );
1404
1405     if (!RtlCreateUnicodeStringFromAsciiz( &keyW, key ))
1406         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1407     else
1408     {
1409         ret = SetupFindNextMatchLineW( context_in, keyW.Buffer, context_out );
1410         RtlFreeUnicodeString( &keyW );
1411     }
1412     return ret;
1413 }
1414
1415
1416 /***********************************************************************
1417  *            SetupFindNextMatchLineW   (SETUPAPI.@)
1418  */
1419 BOOL WINAPI SetupFindNextMatchLineW( PINFCONTEXT context_in, PCWSTR key,
1420                                      PINFCONTEXT context_out )
1421 {
1422     struct inf_file *file = context_in->CurrentInf;
1423     struct section *section;
1424     struct line *line;
1425     unsigned int i;
1426
1427     if (!key) return SetupFindNextLine( context_in, context_out );
1428
1429     if (context_in->Section >= file->nb_sections) goto error;
1430
1431     section = file->sections[context_in->Section];
1432
1433     for (i = context_in->Line+1, line = &section->lines[i]; i < section->nb_lines; i++, line++)
1434     {
1435         if (line->key_field == -1) continue;
1436         if (!strcmpiW( key, file->fields[line->key_field].text ))
1437         {
1438             if (context_out != context_in) *context_out = *context_in;
1439             context_out->Line = i;
1440             SetLastError( 0 );
1441             TRACE( "(%p,%s,%s): returning %d\n",
1442                    file, debugstr_w(section->name), debugstr_w(key), i );
1443             return TRUE;
1444         }
1445     }
1446
1447     /* now search the appended files */
1448
1449     for (file = file->next; file; file = file->next)
1450     {
1451         int section_index = find_section( file, section->name );
1452         if (section_index == -1) continue;
1453         section = file->sections[section_index];
1454         for (i = 0, line = section->lines; i < section->nb_lines; i++, line++)
1455         {
1456             if (line->key_field == -1) continue;
1457             if (!strcmpiW( key, file->fields[line->key_field].text ))
1458             {
1459                 context_out->Inf        = context_in->Inf;
1460                 context_out->CurrentInf = file;
1461                 context_out->Section    = section_index;
1462                 context_out->Line       = i;
1463                 SetLastError( 0 );
1464                 TRACE( "(%p,%s,%s): returning %d/%d\n",
1465                        file, debugstr_w(section->name), debugstr_w(key), section_index, i );
1466                 return TRUE;
1467             }
1468         }
1469     }
1470     TRACE( "(%p,%s,%s): not found\n",
1471            context_in->CurrentInf, debugstr_w(section->name), debugstr_w(key) );
1472  error:
1473     SetLastError( ERROR_LINE_NOT_FOUND );
1474     return FALSE;
1475 }
1476
1477
1478 /***********************************************************************
1479  *              SetupGetLineTextW    (SETUPAPI.@)
1480  */
1481 BOOL WINAPI SetupGetLineTextW( PINFCONTEXT context, HINF hinf, PCWSTR section_name,
1482                                PCWSTR key_name, PWSTR buffer, DWORD size, PDWORD required )
1483 {
1484     struct inf_file *file;
1485     struct line *line;
1486     struct field *field;
1487     int i;
1488     DWORD total = 0;
1489
1490     if (!context)
1491     {
1492         INFCONTEXT new_context;
1493         if (!SetupFindFirstLineW( hinf, section_name, key_name, &new_context )) return FALSE;
1494         file = new_context.CurrentInf;
1495         line = get_line( file, new_context.Section, new_context.Line );
1496     }
1497     else
1498     {
1499         file = context->CurrentInf;
1500         if (!(line = get_line( file, context->Section, context->Line )))
1501         {
1502             SetLastError( ERROR_LINE_NOT_FOUND );
1503             return FALSE;
1504         }
1505     }
1506
1507     for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1508         total += PARSER_string_substW( file, field->text, NULL, 0 ) + 1;
1509
1510     if (required) *required = total;
1511     if (buffer)
1512     {
1513         if (total > size)
1514         {
1515             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1516             return FALSE;
1517         }
1518         for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1519         {
1520             unsigned int len = PARSER_string_substW( file, field->text, buffer, size );
1521             if (i+1 < line->nb_fields) buffer[len] = ',';
1522             buffer += len + 1;
1523         }
1524     }
1525     return TRUE;
1526 }
1527
1528
1529 /***********************************************************************
1530  *              SetupGetLineTextA    (SETUPAPI.@)
1531  */
1532 BOOL WINAPI SetupGetLineTextA( PINFCONTEXT context, HINF hinf, PCSTR section_name,
1533                                PCSTR key_name, PSTR buffer, DWORD size, PDWORD required )
1534 {
1535     struct inf_file *file;
1536     struct line *line;
1537     struct field *field;
1538     int i;
1539     DWORD total = 0;
1540
1541     if (!context)
1542     {
1543         INFCONTEXT new_context;
1544         if (!SetupFindFirstLineA( hinf, section_name, key_name, &new_context )) return FALSE;
1545         file = new_context.CurrentInf;
1546         line = get_line( file, new_context.Section, new_context.Line );
1547     }
1548     else
1549     {
1550         file = context->CurrentInf;
1551         if (!(line = get_line( file, context->Section, context->Line )))
1552         {
1553             SetLastError( ERROR_LINE_NOT_FOUND );
1554             return FALSE;
1555         }
1556     }
1557
1558     for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1559         total += PARSER_string_substA( file, field->text, NULL, 0 ) + 1;
1560
1561     if (required) *required = total;
1562     if (buffer)
1563     {
1564         if (total > size)
1565         {
1566             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1567             return FALSE;
1568         }
1569         for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
1570         {
1571             unsigned int len = PARSER_string_substA( file, field->text, buffer, size );
1572             if (i+1 < line->nb_fields) buffer[len] = ',';
1573             buffer += len + 1;
1574         }
1575     }
1576     return TRUE;
1577 }
1578
1579
1580 /***********************************************************************
1581  *              SetupGetFieldCount    (SETUPAPI.@)
1582  */
1583 DWORD WINAPI SetupGetFieldCount( PINFCONTEXT context )
1584 {
1585     struct inf_file *file = context->CurrentInf;
1586     struct line *line = get_line( file, context->Section, context->Line );
1587
1588     if (!line) return 0;
1589     return line->nb_fields;
1590 }
1591
1592
1593 /***********************************************************************
1594  *              SetupGetStringFieldA    (SETUPAPI.@)
1595  */
1596 BOOL WINAPI SetupGetStringFieldA( PINFCONTEXT context, DWORD index, PSTR buffer,
1597                                   DWORD size, PDWORD required )
1598 {
1599     struct inf_file *file = context->CurrentInf;
1600     struct field *field = get_field( file, context->Section, context->Line, index );
1601     unsigned int len;
1602
1603     SetLastError(0);
1604     if (!field) return FALSE;
1605     len = PARSER_string_substA( file, field->text, NULL, 0 );
1606     if (required) *required = len + 1;
1607     if (buffer)
1608     {
1609         if (size <= len)
1610         {
1611             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1612             return FALSE;
1613         }
1614         PARSER_string_substA( file, field->text, buffer, size );
1615
1616         TRACE( "context %p/%p/%d/%d index %ld returning %s\n",
1617                context->Inf, context->CurrentInf, context->Section, context->Line,
1618                index, debugstr_a(buffer) );
1619     }
1620     return TRUE;
1621 }
1622
1623
1624 /***********************************************************************
1625  *              SetupGetStringFieldW    (SETUPAPI.@)
1626  */
1627 BOOL WINAPI SetupGetStringFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer,
1628                                   DWORD size, PDWORD required )
1629 {
1630     struct inf_file *file = context->CurrentInf;
1631     struct field *field = get_field( file, context->Section, context->Line, index );
1632     unsigned int len;
1633
1634     SetLastError(0);
1635     if (!field) return FALSE;
1636     len = PARSER_string_substW( file, field->text, NULL, 0 );
1637     if (required) *required = len + 1;
1638     if (buffer)
1639     {
1640         if (size <= len)
1641         {
1642             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1643             return FALSE;
1644         }
1645         PARSER_string_substW( file, field->text, buffer, size );
1646
1647         TRACE( "context %p/%p/%d/%d index %ld returning %s\n",
1648                context->Inf, context->CurrentInf, context->Section, context->Line,
1649                index, debugstr_w(buffer) );
1650     }
1651     return TRUE;
1652 }
1653
1654
1655 /***********************************************************************
1656  *              SetupGetIntField    (SETUPAPI.@)
1657  */
1658 BOOL WINAPI SetupGetIntField( PINFCONTEXT context, DWORD index, PINT result )
1659 {
1660     char localbuff[20];
1661     char *end, *buffer = localbuff;
1662     DWORD required;
1663     INT res;
1664     BOOL ret = FALSE;
1665
1666     if (!SetupGetStringFieldA( context, index, localbuff, sizeof(localbuff), &required ))
1667     {
1668         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
1669         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required ))) return FALSE;
1670         if (!SetupGetStringFieldA( context, index, buffer, required, NULL )) goto done;
1671     }
1672     res = strtol( buffer, &end, 0 );
1673     if (end != buffer && !*end)
1674     {
1675         *result = res;
1676         ret = TRUE;
1677     }
1678     else SetLastError( ERROR_INVALID_DATA );
1679
1680  done:
1681     if (buffer != localbuff) HeapFree( GetProcessHeap(), 0, buffer );
1682     return ret;
1683 }
1684
1685
1686 /***********************************************************************
1687  *              SetupGetBinaryField    (SETUPAPI.@)
1688  */
1689 BOOL WINAPI SetupGetBinaryField( PINFCONTEXT context, DWORD index, BYTE *buffer,
1690                                  DWORD size, LPDWORD required )
1691 {
1692     struct inf_file *file = context->CurrentInf;
1693     struct line *line = get_line( file, context->Section, context->Line );
1694     struct field *field;
1695     int i;
1696
1697     if (!line)
1698     {
1699         SetLastError( ERROR_LINE_NOT_FOUND );
1700         return FALSE;
1701     }
1702     if (!index || index > line->nb_fields)
1703     {
1704         SetLastError( ERROR_INVALID_PARAMETER );
1705         return FALSE;
1706     }
1707     index--;  /* fields start at 0 */
1708     if (required) *required = line->nb_fields - index;
1709     if (!buffer) return TRUE;
1710     if (size < line->nb_fields - index)
1711     {
1712         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1713         return FALSE;
1714     }
1715     field = &file->fields[line->first_field + index];
1716     for (i = index; i < line->nb_fields; i++, field++)
1717     {
1718         const WCHAR *p;
1719         DWORD value = 0;
1720         for (p = field->text; *p && isxdigitW(*p); p++)
1721         {
1722             if ((value <<= 4) > 255)
1723             {
1724                 SetLastError( ERROR_INVALID_DATA );
1725                 return FALSE;
1726             }
1727             if (*p <= '9') value |= (*p - '0');
1728             else value |= (tolowerW(*p) - 'a' + 10);
1729         }
1730         buffer[i - index] = value;
1731     }
1732     if (TRACE_ON(setupapi))
1733     {
1734         TRACE( "%p/%p/%d/%d index %ld returning",
1735                context->Inf, context->CurrentInf, context->Section, context->Line, index );
1736         for (i = index; i < line->nb_fields; i++) TRACE( " %02x", buffer[i - index] );
1737         TRACE( "\n" );
1738     }
1739     return TRUE;
1740 }
1741
1742
1743 /***********************************************************************
1744  *              SetupGetMultiSzFieldA    (SETUPAPI.@)
1745  */
1746 BOOL WINAPI SetupGetMultiSzFieldA( PINFCONTEXT context, DWORD index, PSTR buffer,
1747                                    DWORD size, LPDWORD required )
1748 {
1749     struct inf_file *file = context->CurrentInf;
1750     struct line *line = get_line( file, context->Section, context->Line );
1751     struct field *field;
1752     unsigned int len;
1753     int i;
1754     DWORD total = 1;
1755
1756     if (!line)
1757     {
1758         SetLastError( ERROR_LINE_NOT_FOUND );
1759         return FALSE;
1760     }
1761     if (!index || index > line->nb_fields)
1762     {
1763         SetLastError( ERROR_INVALID_PARAMETER );
1764         return FALSE;
1765     }
1766     index--;  /* fields start at 0 */
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, NULL, 0 ))) break;
1771         total += len + 1;
1772     }
1773
1774     if (required) *required = total;
1775     if (!buffer) return TRUE;
1776     if (total > size)
1777     {
1778         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1779         return FALSE;
1780     }
1781     field = &file->fields[line->first_field + index];
1782     for (i = index; i < line->nb_fields; i++, field++)
1783     {
1784         if (!(len = PARSER_string_substA( file, field->text, buffer, size ))) break;
1785         buffer += len + 1;
1786     }
1787     *buffer = 0;  /* add final null */
1788     return TRUE;
1789 }
1790
1791
1792 /***********************************************************************
1793  *              SetupGetMultiSzFieldW    (SETUPAPI.@)
1794  */
1795 BOOL WINAPI SetupGetMultiSzFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer,
1796                                    DWORD size, LPDWORD required )
1797 {
1798     struct inf_file *file = context->CurrentInf;
1799     struct line *line = get_line( file, context->Section, context->Line );
1800     struct field *field;
1801     unsigned int len;
1802     int i;
1803     DWORD total = 1;
1804
1805     if (!line)
1806     {
1807         SetLastError( ERROR_LINE_NOT_FOUND );
1808         return FALSE;
1809     }
1810     if (!index || index > line->nb_fields)
1811     {
1812         SetLastError( ERROR_INVALID_PARAMETER );
1813         return FALSE;
1814     }
1815     index--;  /* fields start at 0 */
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, NULL, 0 ))) break;
1820         total += len + 1;
1821     }
1822
1823     if (required) *required = total;
1824     if (!buffer) return TRUE;
1825     if (total > size)
1826     {
1827         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1828         return FALSE;
1829     }
1830     field = &file->fields[line->first_field + index];
1831     for (i = index; i < line->nb_fields; i++, field++)
1832     {
1833         if (!(len = PARSER_string_substW( file, field->text, buffer, size ))) break;
1834         buffer += len + 1;
1835     }
1836     *buffer = 0;  /* add final null */
1837     return TRUE;
1838 }