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