[NTOS:CM] CmpCmdHiveOpen(): Resolve FileAttributes->RootDirectory when a hive file...
[reactos.git] / sdk / lib / inflib / infcore.c
1 /*
2 * PROJECT: .inf file parser
3 * LICENSE: GPL - See COPYING in the top level directory
4 * PROGRAMMER: Royce Mitchell III
5 * Eric Kohl
6 * Ge van Geldorp <gvg@reactos.org>
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include "inflib.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 #define CONTROL_Z '\x1a'
17 #define MAX_SECTION_NAME_LEN 255
18 #define MAX_FIELD_LEN 511 /* larger fields get silently truncated */
19 /* actual string limit is MAX_INF_STRING_LENGTH+1 (plus terminating null) under Windows */
20 #define MAX_STRING_LEN (MAX_INF_STRING_LENGTH+1)
21
22
23 /* parser definitions */
24
25 enum parser_state
26 {
27 LINE_START, /* at beginning of a line */
28 SECTION_NAME, /* parsing a section name */
29 KEY_NAME, /* parsing a key name */
30 VALUE_NAME, /* parsing a value name */
31 EOL_BACKSLASH, /* backslash at end of line */
32 QUOTES, /* inside quotes */
33 LEADING_SPACES, /* leading spaces */
34 TRAILING_SPACES, /* trailing spaces */
35 COMMENT, /* inside a comment */
36 NB_PARSER_STATES
37 };
38
39 struct parser
40 {
41 const WCHAR *start; /* start position of item being parsed */
42 const WCHAR *end; /* end of buffer */
43 PINFCACHE file; /* file being built */
44 enum parser_state state; /* current parser state */
45 enum parser_state stack[4]; /* state stack */
46 int stack_pos; /* current pos in stack */
47
48 PINFCACHESECTION cur_section; /* pointer to the section being parsed*/
49 PINFCACHELINE line; /* current line */
50 unsigned int line_pos; /* current line position in file */
51 INFSTATUS error; /* error code */
52 unsigned int token_len; /* current token len */
53 WCHAR token[MAX_FIELD_LEN+1]; /* current token */
54 };
55
56 typedef const WCHAR * (*parser_state_func)( struct parser *parser, const WCHAR *pos );
57
58 /* parser state machine functions */
59 static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos );
60 static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos );
61 static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos );
62 static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos );
63 static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos );
64 static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos );
65 static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos );
66 static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos );
67 static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos );
68
69 static const parser_state_func parser_funcs[NB_PARSER_STATES] =
70 {
71 line_start_state, /* LINE_START */
72 section_name_state, /* SECTION_NAME */
73 key_name_state, /* KEY_NAME */
74 value_name_state, /* VALUE_NAME */
75 eol_backslash_state, /* EOL_BACKSLASH */
76 quotes_state, /* QUOTES */
77 leading_spaces_state, /* LEADING_SPACES */
78 trailing_spaces_state, /* TRAILING_SPACES */
79 comment_state /* COMMENT */
80 };
81
82
83 /* PRIVATE FUNCTIONS ********************************************************/
84
85 static PINFCACHELINE
86 InfpFreeLine (PINFCACHELINE Line)
87 {
88 PINFCACHELINE Next;
89 PINFCACHEFIELD Field;
90
91 if (Line == NULL)
92 {
93 return NULL;
94 }
95
96 Next = Line->Next;
97 if (Line->Key != NULL)
98 {
99 FREE (Line->Key);
100 Line->Key = NULL;
101 }
102
103 /* Remove data fields */
104 while (Line->FirstField != NULL)
105 {
106 Field = Line->FirstField->Next;
107 FREE (Line->FirstField);
108 Line->FirstField = Field;
109 }
110 Line->LastField = NULL;
111
112 FREE (Line);
113
114 return Next;
115 }
116
117
118 PINFCACHESECTION
119 InfpFreeSection (PINFCACHESECTION Section)
120 {
121 PINFCACHESECTION Next;
122
123 if (Section == NULL)
124 {
125 return NULL;
126 }
127
128 /* Release all keys */
129 Next = Section->Next;
130 while (Section->FirstLine != NULL)
131 {
132 Section->FirstLine = InfpFreeLine (Section->FirstLine);
133 }
134 Section->LastLine = NULL;
135
136 FREE (Section);
137
138 return Next;
139 }
140
141
142 PINFCACHESECTION
143 InfpFindSection(PINFCACHE Cache,
144 PCWSTR Name)
145 {
146 PINFCACHESECTION Section = NULL;
147
148 if (Cache == NULL || Name == NULL)
149 {
150 return NULL;
151 }
152
153 /* iterate through list of sections */
154 Section = Cache->FirstSection;
155 while (Section != NULL)
156 {
157 if (strcmpiW(Section->Name, Name) == 0)
158 {
159 return Section;
160 }
161
162 /* get the next section*/
163 Section = Section->Next;
164 }
165
166 return NULL;
167 }
168
169
170 PINFCACHESECTION
171 InfpAddSection(PINFCACHE Cache,
172 PCWSTR Name)
173 {
174 PINFCACHESECTION Section = NULL;
175 ULONG Size;
176
177 if (Cache == NULL || Name == NULL)
178 {
179 DPRINT("Invalid parameter\n");
180 return NULL;
181 }
182
183 /* Allocate and initialize the new section */
184 Size = (ULONG)FIELD_OFFSET(INFCACHESECTION,
185 Name[strlenW(Name) + 1]);
186 Section = (PINFCACHESECTION)MALLOC(Size);
187 if (Section == NULL)
188 {
189 DPRINT("MALLOC() failed\n");
190 return NULL;
191 }
192 ZEROMEMORY (Section,
193 Size);
194
195 /* Copy section name */
196 strcpyW(Section->Name, Name);
197
198 /* Append section */
199 if (Cache->FirstSection == NULL)
200 {
201 Cache->FirstSection = Section;
202 Cache->LastSection = Section;
203 }
204 else
205 {
206 Cache->LastSection->Next = Section;
207 Section->Prev = Cache->LastSection;
208 Cache->LastSection = Section;
209 }
210
211 return Section;
212 }
213
214
215 PINFCACHELINE
216 InfpAddLine(PINFCACHESECTION Section)
217 {
218 PINFCACHELINE Line;
219
220 if (Section == NULL)
221 {
222 DPRINT("Invalid parameter\n");
223 return NULL;
224 }
225
226 Line = (PINFCACHELINE)MALLOC(sizeof(INFCACHELINE));
227 if (Line == NULL)
228 {
229 DPRINT("MALLOC() failed\n");
230 return NULL;
231 }
232 ZEROMEMORY(Line,
233 sizeof(INFCACHELINE));
234
235 /* Append line */
236 if (Section->FirstLine == NULL)
237 {
238 Section->FirstLine = Line;
239 Section->LastLine = Line;
240 }
241 else
242 {
243 Section->LastLine->Next = Line;
244 Line->Prev = Section->LastLine;
245 Section->LastLine = Line;
246 }
247 Section->LineCount++;
248
249 return Line;
250 }
251
252
253 PVOID
254 InfpAddKeyToLine(PINFCACHELINE Line,
255 PCWSTR Key)
256 {
257 if (Line == NULL)
258 {
259 DPRINT1("Invalid Line\n");
260 return NULL;
261 }
262
263 if (Line->Key != NULL)
264 {
265 DPRINT1("Line already has a key\n");
266 return NULL;
267 }
268
269 Line->Key = (PWCHAR)MALLOC((strlenW(Key) + 1) * sizeof(WCHAR));
270 if (Line->Key == NULL)
271 {
272 DPRINT1("MALLOC() failed\n");
273 return NULL;
274 }
275
276 strcpyW(Line->Key, Key);
277
278 return (PVOID)Line->Key;
279 }
280
281
282 PVOID
283 InfpAddFieldToLine(PINFCACHELINE Line,
284 PCWSTR Data)
285 {
286 PINFCACHEFIELD Field;
287 ULONG Size;
288
289 Size = (ULONG)FIELD_OFFSET(INFCACHEFIELD,
290 Data[strlenW(Data) + 1]);
291 Field = (PINFCACHEFIELD)MALLOC(Size);
292 if (Field == NULL)
293 {
294 DPRINT1("MALLOC() failed\n");
295 return NULL;
296 }
297 ZEROMEMORY (Field,
298 Size);
299 strcpyW(Field->Data, Data);
300
301 /* Append key */
302 if (Line->FirstField == NULL)
303 {
304 Line->FirstField = Field;
305 Line->LastField = Field;
306 }
307 else
308 {
309 Line->LastField->Next = Field;
310 Field->Prev = Line->LastField;
311 Line->LastField = Field;
312 }
313 Line->FieldCount++;
314
315 return (PVOID)Field;
316 }
317
318
319 PINFCACHELINE
320 InfpFindKeyLine(PINFCACHESECTION Section,
321 PCWSTR Key)
322 {
323 PINFCACHELINE Line;
324
325 Line = Section->FirstLine;
326 while (Line != NULL)
327 {
328 if (Line->Key != NULL && strcmpiW(Line->Key, Key) == 0)
329 {
330 return Line;
331 }
332
333 Line = Line->Next;
334 }
335
336 return NULL;
337 }
338
339
340 /* push the current state on the parser stack */
341 __inline static void push_state( struct parser *parser, enum parser_state state )
342 {
343 // assert( parser->stack_pos < sizeof(parser->stack)/sizeof(parser->stack[0]) );
344 parser->stack[parser->stack_pos++] = state;
345 }
346
347
348 /* pop the current state */
349 __inline static void pop_state( struct parser *parser )
350 {
351 // assert( parser->stack_pos );
352 parser->state = parser->stack[--parser->stack_pos];
353 }
354
355
356 /* set the parser state and return the previous one */
357 __inline static enum parser_state set_state( struct parser *parser, enum parser_state state )
358 {
359 enum parser_state ret = parser->state;
360 parser->state = state;
361 return ret;
362 }
363
364
365 /* check if the pointer points to an end of file */
366 __inline static int is_eof( struct parser *parser, const WCHAR *ptr )
367 {
368 return (ptr >= parser->end || *ptr == CONTROL_Z || *ptr == 0);
369 }
370
371
372 /* check if the pointer points to an end of line */
373 __inline static int is_eol( struct parser *parser, const WCHAR *ptr )
374 {
375 return (ptr >= parser->end ||
376 *ptr == CONTROL_Z ||
377 *ptr == '\n' ||
378 (*ptr == '\r' && *(ptr + 1) == '\n') ||
379 *ptr == 0);
380 }
381
382
383 /* push data from current token start up to pos into the current token */
384 static int push_token( struct parser *parser, const WCHAR *pos )
385 {
386 UINT len = (UINT)(pos - parser->start);
387 const WCHAR *src = parser->start;
388 WCHAR *dst = parser->token + parser->token_len;
389
390 if (len > MAX_FIELD_LEN - parser->token_len)
391 len = MAX_FIELD_LEN - parser->token_len;
392
393 parser->token_len += len;
394 for ( ; len > 0; len--, dst++, src++)
395 {
396 if (*src)
397 {
398 *dst = *src;
399 }
400 else
401 {
402 *dst = ' ';
403 }
404 }
405
406 *dst = 0;
407 parser->start = pos;
408
409 return 0;
410 }
411
412
413
414 /* add a section with the current token as name */
415 static PVOID add_section_from_token( struct parser *parser )
416 {
417 PINFCACHESECTION Section;
418
419 if (parser->token_len > MAX_SECTION_NAME_LEN)
420 {
421 parser->error = INF_STATUS_SECTION_NAME_TOO_LONG;
422 return NULL;
423 }
424
425 Section = InfpFindSection(parser->file,
426 parser->token);
427 if (Section == NULL)
428 {
429 /* need to create a new one */
430 Section= InfpAddSection(parser->file,
431 parser->token);
432 if (Section == NULL)
433 {
434 parser->error = INF_STATUS_NOT_ENOUGH_MEMORY;
435 return NULL;
436 }
437 }
438
439 parser->token_len = 0;
440 parser->cur_section = Section;
441
442 return (PVOID)Section;
443 }
444
445
446 /* add a field containing the current token to the current line */
447 static struct field *add_field_from_token( struct parser *parser, int is_key )
448 {
449 PVOID field;
450
451 if (!parser->line) /* need to start a new line */
452 {
453 if (parser->cur_section == NULL) /* got a line before the first section */
454 {
455 parser->error = INF_STATUS_WRONG_INF_STYLE;
456 return NULL;
457 }
458
459 parser->line = InfpAddLine(parser->cur_section);
460 if (parser->line == NULL)
461 goto error;
462 }
463 else
464 {
465 // assert(!is_key);
466 }
467
468 if (is_key)
469 {
470 field = InfpAddKeyToLine(parser->line, parser->token);
471 }
472 else
473 {
474 field = InfpAddFieldToLine(parser->line, parser->token);
475 }
476
477 if (field != NULL)
478 {
479 parser->token_len = 0;
480 return field;
481 }
482
483 error:
484 parser->error = INF_STATUS_NOT_ENOUGH_MEMORY;
485 return NULL;
486 }
487
488
489 /* close the current line and prepare for parsing a new one */
490 static void close_current_line( struct parser *parser )
491 {
492 parser->line = NULL;
493 }
494
495
496
497 /* handler for parser LINE_START state */
498 static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos )
499 {
500 const WCHAR *p;
501
502 for (p = pos; !is_eof( parser, p ); p++)
503 {
504 switch(*p)
505 {
506 case '\r':
507 continue;
508
509 case '\n':
510 parser->line_pos++;
511 close_current_line( parser );
512 break;
513
514 case ';':
515 push_state( parser, LINE_START );
516 set_state( parser, COMMENT );
517 return p + 1;
518
519 case '[':
520 parser->start = p + 1;
521 set_state( parser, SECTION_NAME );
522 return p + 1;
523
524 default:
525 if (!isspaceW(*p))
526 {
527 parser->start = p;
528 set_state( parser, KEY_NAME );
529 return p;
530 }
531 break;
532 }
533 }
534 close_current_line( parser );
535 return NULL;
536 }
537
538
539 /* handler for parser SECTION_NAME state */
540 static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos )
541 {
542 const WCHAR *p;
543
544 for (p = pos; !is_eol( parser, p ); p++)
545 {
546 if (*p == ']')
547 {
548 push_token( parser, p );
549 if (add_section_from_token( parser ) == NULL)
550 return NULL;
551 push_state( parser, LINE_START );
552 set_state( parser, COMMENT ); /* ignore everything else on the line */
553 return p + 1;
554 }
555 }
556 parser->error = INF_STATUS_BAD_SECTION_NAME_LINE; /* unfinished section name */
557 return NULL;
558 }
559
560
561 /* handler for parser KEY_NAME state */
562 static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos )
563 {
564 const WCHAR *p, *token_end = parser->start;
565
566 for (p = pos; !is_eol( parser, p ); p++)
567 {
568 if (*p == ',') break;
569 switch(*p)
570 {
571
572 case '=':
573 push_token( parser, token_end );
574 if (!add_field_from_token( parser, 1 )) return NULL;
575 parser->start = p + 1;
576 push_state( parser, VALUE_NAME );
577 set_state( parser, LEADING_SPACES );
578 return p + 1;
579 case ';':
580 push_token( parser, token_end );
581 if (!add_field_from_token( parser, 0 )) return NULL;
582 push_state( parser, LINE_START );
583 set_state( parser, COMMENT );
584 return p + 1;
585 case '"':
586 push_token( parser, token_end );
587 parser->start = p + 1;
588 push_state( parser, KEY_NAME );
589 set_state( parser, QUOTES );
590 return p + 1;
591 case '\\':
592 push_token( parser, token_end );
593 parser->start = p;
594 push_state( parser, KEY_NAME );
595 set_state( parser, EOL_BACKSLASH );
596 return p;
597 default:
598 if (!isspaceW(*p)) token_end = p + 1;
599 else
600 {
601 push_token( parser, p );
602 push_state( parser, KEY_NAME );
603 set_state( parser, TRAILING_SPACES );
604 return p;
605 }
606 break;
607 }
608 }
609 push_token( parser, token_end );
610 set_state( parser, VALUE_NAME );
611 return p;
612 }
613
614
615 /* handler for parser VALUE_NAME state */
616 static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos )
617 {
618 const WCHAR *p, *token_end = parser->start;
619
620 for (p = pos; !is_eol( parser, p ); p++)
621 {
622 switch(*p)
623 {
624 case ';':
625 push_token( parser, token_end );
626 if (!add_field_from_token( parser, 0 )) return NULL;
627 push_state( parser, LINE_START );
628 set_state( parser, COMMENT );
629 return p + 1;
630 case ',':
631 push_token( parser, token_end );
632 if (!add_field_from_token( parser, 0 )) return NULL;
633 parser->start = p + 1;
634 push_state( parser, VALUE_NAME );
635 set_state( parser, LEADING_SPACES );
636 return p + 1;
637 case '"':
638 push_token( parser, token_end );
639 parser->start = p + 1;
640 push_state( parser, VALUE_NAME );
641 set_state( parser, QUOTES );
642 return p + 1;
643 case '\\':
644 push_token( parser, token_end );
645 parser->start = p;
646 push_state( parser, VALUE_NAME );
647 set_state( parser, EOL_BACKSLASH );
648 return p;
649 default:
650 if (!isspaceW(*p)) token_end = p + 1;
651 else
652 {
653 push_token( parser, p );
654 push_state( parser, VALUE_NAME );
655 set_state( parser, TRAILING_SPACES );
656 return p;
657 }
658 break;
659 }
660 }
661 push_token( parser, token_end );
662 if (!add_field_from_token( parser, 0 )) return NULL;
663 set_state( parser, LINE_START );
664 return p;
665 }
666
667
668 /* handler for parser EOL_BACKSLASH state */
669 static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos )
670 {
671 const WCHAR *p;
672
673 for (p = pos; !is_eof( parser, p ); p++)
674 {
675 switch(*p)
676 {
677 case '\r':
678 continue;
679
680 case '\n':
681 parser->line_pos++;
682 parser->start = p + 1;
683 set_state( parser, LEADING_SPACES );
684 return p + 1;
685
686 case '\\':
687 continue;
688
689 case ';':
690 push_state( parser, EOL_BACKSLASH );
691 set_state( parser, COMMENT );
692 return p + 1;
693
694 default:
695 if (isspaceW(*p))
696 continue;
697 push_token( parser, p );
698 pop_state( parser );
699 return p;
700 }
701 }
702 parser->start = p;
703 pop_state( parser );
704
705 return p;
706 }
707
708
709 /* handler for parser QUOTES state */
710 static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos )
711 {
712 const WCHAR *p, *token_end = parser->start;
713
714 for (p = pos; !is_eol( parser, p ); p++)
715 {
716 if (*p == '"')
717 {
718 if (p+1 < parser->end && p[1] == '"') /* double quotes */
719 {
720 push_token( parser, p + 1 );
721 parser->start = token_end = p + 2;
722 p++;
723 }
724 else /* end of quotes */
725 {
726 push_token( parser, p );
727 parser->start = p + 1;
728 pop_state( parser );
729 return p + 1;
730 }
731 }
732 }
733 push_token( parser, p );
734 pop_state( parser );
735 return p;
736 }
737
738
739 /* handler for parser LEADING_SPACES state */
740 static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos )
741 {
742 const WCHAR *p;
743
744 for (p = pos; !is_eol( parser, p ); p++)
745 {
746 if (*p == '\\')
747 {
748 parser->start = p;
749 set_state( parser, EOL_BACKSLASH );
750 return p;
751 }
752 if (!isspaceW(*p))
753 break;
754 }
755 parser->start = p;
756 pop_state( parser );
757 return p;
758 }
759
760
761 /* handler for parser TRAILING_SPACES state */
762 static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos )
763 {
764 const WCHAR *p;
765
766 for (p = pos; !is_eol( parser, p ); p++)
767 {
768 if (*p == '\\')
769 {
770 set_state( parser, EOL_BACKSLASH );
771 return p;
772 }
773 if (!isspaceW(*p))
774 break;
775 }
776 pop_state( parser );
777 return p;
778 }
779
780
781 /* handler for parser COMMENT state */
782 static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos )
783 {
784 const WCHAR *p = pos;
785
786 while (!is_eol( parser, p ))
787 p++;
788 pop_state( parser );
789 return p;
790 }
791
792
793 /* parse a complete buffer */
794 INFSTATUS
795 InfpParseBuffer (PINFCACHE file,
796 const WCHAR *buffer,
797 const WCHAR *end,
798 PULONG error_line)
799 {
800 struct parser parser;
801 const WCHAR *pos = buffer;
802
803 parser.start = buffer;
804 parser.end = end;
805 parser.file = file;
806 parser.line = NULL;
807 parser.state = LINE_START;
808 parser.stack_pos = 0;
809 parser.cur_section = NULL;
810 parser.line_pos = 1;
811 parser.error = 0;
812 parser.token_len = 0;
813
814 /* parser main loop */
815 while (pos)
816 pos = (parser_funcs[parser.state])(&parser, pos);
817
818 if (parser.error)
819 {
820 if (error_line)
821 *error_line = parser.line_pos;
822 return parser.error;
823 }
824
825 /* find the [strings] section */
826 file->StringsSection = InfpFindSection(file,
827 L"Strings");
828
829 return INF_STATUS_SUCCESS;
830 }
831
832 /* EOF */