2 * PROJECT: .inf file parser
3 * LICENSE: GPL - See COPYING in the top level directory
4 * PROGRAMMER: Royce Mitchell III
6 * Ge van Geldorp <gvg@reactos.org>
9 /* INCLUDES *****************************************************************/
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)
23 /* parser definitions */
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 */
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 */
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 */
56 typedef const WCHAR
* (*parser_state_func
)( struct parser
*parser
, const WCHAR
*pos
);
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
);
69 static const parser_state_func parser_funcs
[NB_PARSER_STATES
] =
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 */
83 /* PRIVATE FUNCTIONS ********************************************************/
86 InfpFreeLine (PINFCACHELINE Line
)
97 if (Line
->Key
!= NULL
)
103 /* Remove data fields */
104 while (Line
->FirstField
!= NULL
)
106 Field
= Line
->FirstField
->Next
;
107 FREE (Line
->FirstField
);
108 Line
->FirstField
= Field
;
110 Line
->LastField
= NULL
;
119 InfpFreeSection (PINFCACHESECTION Section
)
121 PINFCACHESECTION Next
;
128 /* Release all keys */
129 Next
= Section
->Next
;
130 while (Section
->FirstLine
!= NULL
)
132 Section
->FirstLine
= InfpFreeLine (Section
->FirstLine
);
134 Section
->LastLine
= NULL
;
143 InfpFindSection(PINFCACHE Cache
,
146 PINFCACHESECTION Section
= NULL
;
148 if (Cache
== NULL
|| Name
== NULL
)
153 /* iterate through list of sections */
154 Section
= Cache
->FirstSection
;
155 while (Section
!= NULL
)
157 if (strcmpiW(Section
->Name
, Name
) == 0)
162 /* get the next section*/
163 Section
= Section
->Next
;
171 InfpAddSection(PINFCACHE Cache
,
174 PINFCACHESECTION Section
= NULL
;
177 if (Cache
== NULL
|| Name
== NULL
)
179 DPRINT("Invalid parameter\n");
183 /* Allocate and initialize the new section */
184 Size
= (ULONG
)FIELD_OFFSET(INFCACHESECTION
,
185 Name
[strlenW(Name
) + 1]);
186 Section
= (PINFCACHESECTION
)MALLOC(Size
);
189 DPRINT("MALLOC() failed\n");
195 /* Copy section name */
196 strcpyW(Section
->Name
, Name
);
199 if (Cache
->FirstSection
== NULL
)
201 Cache
->FirstSection
= Section
;
202 Cache
->LastSection
= Section
;
206 Cache
->LastSection
->Next
= Section
;
207 Section
->Prev
= Cache
->LastSection
;
208 Cache
->LastSection
= Section
;
216 InfpAddLine(PINFCACHESECTION Section
)
222 DPRINT("Invalid parameter\n");
226 Line
= (PINFCACHELINE
)MALLOC(sizeof(INFCACHELINE
));
229 DPRINT("MALLOC() failed\n");
233 sizeof(INFCACHELINE
));
236 if (Section
->FirstLine
== NULL
)
238 Section
->FirstLine
= Line
;
239 Section
->LastLine
= Line
;
243 Section
->LastLine
->Next
= Line
;
244 Line
->Prev
= Section
->LastLine
;
245 Section
->LastLine
= Line
;
247 Section
->LineCount
++;
254 InfpAddKeyToLine(PINFCACHELINE Line
,
259 DPRINT1("Invalid Line\n");
263 if (Line
->Key
!= NULL
)
265 DPRINT1("Line already has a key\n");
269 Line
->Key
= (PWCHAR
)MALLOC((strlenW(Key
) + 1) * sizeof(WCHAR
));
270 if (Line
->Key
== NULL
)
272 DPRINT1("MALLOC() failed\n");
276 strcpyW(Line
->Key
, Key
);
278 return (PVOID
)Line
->Key
;
283 InfpAddFieldToLine(PINFCACHELINE Line
,
286 PINFCACHEFIELD Field
;
289 Size
= (ULONG
)FIELD_OFFSET(INFCACHEFIELD
,
290 Data
[strlenW(Data
) + 1]);
291 Field
= (PINFCACHEFIELD
)MALLOC(Size
);
294 DPRINT1("MALLOC() failed\n");
299 strcpyW(Field
->Data
, Data
);
302 if (Line
->FirstField
== NULL
)
304 Line
->FirstField
= Field
;
305 Line
->LastField
= Field
;
309 Line
->LastField
->Next
= Field
;
310 Field
->Prev
= Line
->LastField
;
311 Line
->LastField
= Field
;
320 InfpFindKeyLine(PINFCACHESECTION Section
,
325 Line
= Section
->FirstLine
;
328 if (Line
->Key
!= NULL
&& strcmpiW(Line
->Key
, Key
) == 0)
340 /* push the current state on the parser stack */
341 __inline
static void push_state( struct parser
*parser
, enum parser_state state
)
343 // assert( parser->stack_pos < sizeof(parser->stack)/sizeof(parser->stack[0]) );
344 parser
->stack
[parser
->stack_pos
++] = state
;
348 /* pop the current state */
349 __inline
static void pop_state( struct parser
*parser
)
351 // assert( parser->stack_pos );
352 parser
->state
= parser
->stack
[--parser
->stack_pos
];
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
)
359 enum parser_state ret
= parser
->state
;
360 parser
->state
= state
;
365 /* check if the pointer points to an end of file */
366 __inline
static int is_eof( struct parser
*parser
, const WCHAR
*ptr
)
368 return (ptr
>= parser
->end
|| *ptr
== CONTROL_Z
|| *ptr
== 0);
372 /* check if the pointer points to an end of line */
373 __inline
static int is_eol( struct parser
*parser
, const WCHAR
*ptr
)
375 return (ptr
>= parser
->end
||
378 (*ptr
== '\r' && *(ptr
+ 1) == '\n') ||
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
)
386 UINT len
= (UINT
)(pos
- parser
->start
);
387 const WCHAR
*src
= parser
->start
;
388 WCHAR
*dst
= parser
->token
+ parser
->token_len
;
390 if (len
> MAX_FIELD_LEN
- parser
->token_len
)
391 len
= MAX_FIELD_LEN
- parser
->token_len
;
393 parser
->token_len
+= len
;
394 for ( ; len
> 0; len
--, dst
++, src
++)
414 /* add a section with the current token as name */
415 static PVOID
add_section_from_token( struct parser
*parser
)
417 PINFCACHESECTION Section
;
419 if (parser
->token_len
> MAX_SECTION_NAME_LEN
)
421 parser
->error
= INF_STATUS_SECTION_NAME_TOO_LONG
;
425 Section
= InfpFindSection(parser
->file
,
429 /* need to create a new one */
430 Section
= InfpAddSection(parser
->file
,
434 parser
->error
= INF_STATUS_NOT_ENOUGH_MEMORY
;
439 parser
->token_len
= 0;
440 parser
->cur_section
= Section
;
442 return (PVOID
)Section
;
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
)
451 if (!parser
->line
) /* need to start a new line */
453 if (parser
->cur_section
== NULL
) /* got a line before the first section */
455 parser
->error
= INF_STATUS_WRONG_INF_STYLE
;
459 parser
->line
= InfpAddLine(parser
->cur_section
);
460 if (parser
->line
== NULL
)
470 field
= InfpAddKeyToLine(parser
->line
, parser
->token
);
474 field
= InfpAddFieldToLine(parser
->line
, parser
->token
);
479 parser
->token_len
= 0;
484 parser
->error
= INF_STATUS_NOT_ENOUGH_MEMORY
;
489 /* close the current line and prepare for parsing a new one */
490 static void close_current_line( struct parser
*parser
)
497 /* handler for parser LINE_START state */
498 static const WCHAR
*line_start_state( struct parser
*parser
, const WCHAR
*pos
)
502 for (p
= pos
; !is_eof( parser
, p
); p
++)
511 close_current_line( parser
);
515 push_state( parser
, LINE_START
);
516 set_state( parser
, COMMENT
);
520 parser
->start
= p
+ 1;
521 set_state( parser
, SECTION_NAME
);
528 set_state( parser
, KEY_NAME
);
534 close_current_line( parser
);
539 /* handler for parser SECTION_NAME state */
540 static const WCHAR
*section_name_state( struct parser
*parser
, const WCHAR
*pos
)
544 for (p
= pos
; !is_eol( parser
, p
); p
++)
548 push_token( parser
, p
);
549 if (add_section_from_token( parser
) == NULL
)
551 push_state( parser
, LINE_START
);
552 set_state( parser
, COMMENT
); /* ignore everything else on the line */
556 parser
->error
= INF_STATUS_BAD_SECTION_NAME_LINE
; /* unfinished section name */
561 /* handler for parser KEY_NAME state */
562 static const WCHAR
*key_name_state( struct parser
*parser
, const WCHAR
*pos
)
564 const WCHAR
*p
, *token_end
= parser
->start
;
566 for (p
= pos
; !is_eol( parser
, p
); p
++)
568 if (*p
== ',') break;
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
);
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
);
586 push_token( parser
, token_end
);
587 parser
->start
= p
+ 1;
588 push_state( parser
, KEY_NAME
);
589 set_state( parser
, QUOTES
);
592 push_token( parser
, token_end
);
594 push_state( parser
, KEY_NAME
);
595 set_state( parser
, EOL_BACKSLASH
);
598 if (!isspaceW(*p
)) token_end
= p
+ 1;
601 push_token( parser
, p
);
602 push_state( parser
, KEY_NAME
);
603 set_state( parser
, TRAILING_SPACES
);
609 push_token( parser
, token_end
);
610 set_state( parser
, VALUE_NAME
);
615 /* handler for parser VALUE_NAME state */
616 static const WCHAR
*value_name_state( struct parser
*parser
, const WCHAR
*pos
)
618 const WCHAR
*p
, *token_end
= parser
->start
;
620 for (p
= pos
; !is_eol( parser
, p
); p
++)
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
);
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
);
638 push_token( parser
, token_end
);
639 parser
->start
= p
+ 1;
640 push_state( parser
, VALUE_NAME
);
641 set_state( parser
, QUOTES
);
644 push_token( parser
, token_end
);
646 push_state( parser
, VALUE_NAME
);
647 set_state( parser
, EOL_BACKSLASH
);
650 if (!isspaceW(*p
)) token_end
= p
+ 1;
653 push_token( parser
, p
);
654 push_state( parser
, VALUE_NAME
);
655 set_state( parser
, TRAILING_SPACES
);
661 push_token( parser
, token_end
);
662 if (!add_field_from_token( parser
, 0 )) return NULL
;
663 set_state( parser
, LINE_START
);
668 /* handler for parser EOL_BACKSLASH state */
669 static const WCHAR
*eol_backslash_state( struct parser
*parser
, const WCHAR
*pos
)
673 for (p
= pos
; !is_eof( parser
, p
); p
++)
682 parser
->start
= p
+ 1;
683 set_state( parser
, LEADING_SPACES
);
690 push_state( parser
, EOL_BACKSLASH
);
691 set_state( parser
, COMMENT
);
697 push_token( parser
, p
);
709 /* handler for parser QUOTES state */
710 static const WCHAR
*quotes_state( struct parser
*parser
, const WCHAR
*pos
)
712 const WCHAR
*p
, *token_end
= parser
->start
;
714 for (p
= pos
; !is_eol( parser
, p
); p
++)
718 if (p
+1 < parser
->end
&& p
[1] == '"') /* double quotes */
720 push_token( parser
, p
+ 1 );
721 parser
->start
= token_end
= p
+ 2;
724 else /* end of quotes */
726 push_token( parser
, p
);
727 parser
->start
= p
+ 1;
733 push_token( parser
, p
);
739 /* handler for parser LEADING_SPACES state */
740 static const WCHAR
*leading_spaces_state( struct parser
*parser
, const WCHAR
*pos
)
744 for (p
= pos
; !is_eol( parser
, p
); p
++)
749 set_state( parser
, EOL_BACKSLASH
);
761 /* handler for parser TRAILING_SPACES state */
762 static const WCHAR
*trailing_spaces_state( struct parser
*parser
, const WCHAR
*pos
)
766 for (p
= pos
; !is_eol( parser
, p
); p
++)
770 set_state( parser
, EOL_BACKSLASH
);
781 /* handler for parser COMMENT state */
782 static const WCHAR
*comment_state( struct parser
*parser
, const WCHAR
*pos
)
784 const WCHAR
*p
= pos
;
786 while (!is_eol( parser
, p
))
793 /* parse a complete buffer */
795 InfpParseBuffer (PINFCACHE file
,
800 struct parser parser
;
801 const WCHAR
*pos
= buffer
;
803 parser
.start
= buffer
;
807 parser
.state
= LINE_START
;
808 parser
.stack_pos
= 0;
809 parser
.cur_section
= NULL
;
812 parser
.token_len
= 0;
814 /* parser main loop */
816 pos
= (parser_funcs
[parser
.state
])(&parser
, pos
);
821 *error_line
= parser
.line_pos
;
825 /* find the [strings] section */
826 file
->StringsSection
= InfpFindSection(file
,
829 return INF_STATUS_SUCCESS
;