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 CHAR
*start
; /* start position of item being parsed */
42 const CHAR
*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 unsigned int error
; /* error code */
52 unsigned int token_len
; /* current token len */
53 TCHAR token
[MAX_FIELD_LEN
+1]; /* current token */
56 typedef const CHAR
* (*parser_state_func
)( struct parser
*parser
, const CHAR
*pos
);
58 /* parser state machine functions */
59 static const CHAR
*line_start_state( struct parser
*parser
, const CHAR
*pos
);
60 static const CHAR
*section_name_state( struct parser
*parser
, const CHAR
*pos
);
61 static const CHAR
*key_name_state( struct parser
*parser
, const CHAR
*pos
);
62 static const CHAR
*value_name_state( struct parser
*parser
, const CHAR
*pos
);
63 static const CHAR
*eol_backslash_state( struct parser
*parser
, const CHAR
*pos
);
64 static const CHAR
*quotes_state( struct parser
*parser
, const CHAR
*pos
);
65 static const CHAR
*leading_spaces_state( struct parser
*parser
, const CHAR
*pos
);
66 static const CHAR
*trailing_spaces_state( struct parser
*parser
, const CHAR
*pos
);
67 static const CHAR
*comment_state( struct parser
*parser
, const CHAR
*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 (_tcsicmp (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
= FIELD_OFFSET(INFCACHESECTION
,
185 Name
[_tcslen (Name
) + 1]);
186 Section
= (PINFCACHESECTION
)MALLOC (Size
);
189 DPRINT("MALLOC() failed\n");
195 /* Copy section name */
196 _tcscpy (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
= (PTCHAR
)MALLOC((_tcslen(Key
) + 1) * sizeof(TCHAR
));
270 if (Line
->Key
== NULL
)
272 DPRINT1("MALLOC() failed\n");
276 _tcscpy(Line
->Key
, Key
);
278 return (PVOID
)Line
->Key
;
283 InfpAddFieldToLine(PINFCACHELINE Line
,
286 PINFCACHEFIELD Field
;
289 Size
= FIELD_OFFSET(INFCACHEFIELD
,
290 Data
[_tcslen(Data
) + 1]);
291 Field
= (PINFCACHEFIELD
)MALLOC(Size
);
294 DPRINT1("MALLOC() failed\n");
299 _tcscpy (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
&& _tcsicmp (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 CHAR
*ptr
)
368 return (ptr
>= parser
->end
|| *ptr
== CONTROL_Z
);
372 /* check if the pointer points to an end of line */
373 __inline
static int is_eol( struct parser
*parser
, const CHAR
*ptr
)
375 return (ptr
>= parser
->end
||
378 (*ptr
== '\r' && *(ptr
+ 1) == '\n'));
382 /* push data from current token start up to pos into the current token */
383 static int push_token( struct parser
*parser
, const CHAR
*pos
)
385 unsigned int len
= pos
- parser
->start
;
386 const CHAR
*src
= parser
->start
;
387 TCHAR
*dst
= parser
->token
+ parser
->token_len
;
389 if (len
> MAX_FIELD_LEN
- parser
->token_len
)
390 len
= MAX_FIELD_LEN
- parser
->token_len
;
392 parser
->token_len
+= len
;
393 for ( ; len
> 0; len
--, dst
++, src
++)
394 *dst
= *src
? (TCHAR
)*src
: L
' ';
403 /* add a section with the current token as name */
404 static PVOID
add_section_from_token( struct parser
*parser
)
406 PINFCACHESECTION Section
;
408 if (parser
->token_len
> MAX_SECTION_NAME_LEN
)
410 parser
->error
= INF_STATUS_SECTION_NAME_TOO_LONG
;
414 Section
= InfpFindSection(parser
->file
,
418 /* need to create a new one */
419 Section
= InfpAddSection(parser
->file
,
423 parser
->error
= INF_STATUS_NOT_ENOUGH_MEMORY
;
428 parser
->token_len
= 0;
429 parser
->cur_section
= Section
;
431 return (PVOID
)Section
;
435 /* add a field containing the current token to the current line */
436 static struct field
*add_field_from_token( struct parser
*parser
, int is_key
)
440 if (!parser
->line
) /* need to start a new line */
442 if (parser
->cur_section
== NULL
) /* got a line before the first section */
444 parser
->error
= INF_STATUS_WRONG_INF_STYLE
;
448 parser
->line
= InfpAddLine(parser
->cur_section
);
449 if (parser
->line
== NULL
)
459 field
= InfpAddKeyToLine(parser
->line
, parser
->token
);
463 field
= InfpAddFieldToLine(parser
->line
, parser
->token
);
468 parser
->token_len
= 0;
473 parser
->error
= INF_STATUS_NOT_ENOUGH_MEMORY
;
478 /* close the current line and prepare for parsing a new one */
479 static void close_current_line( struct parser
*parser
)
486 /* handler for parser LINE_START state */
487 static const CHAR
*line_start_state( struct parser
*parser
, const CHAR
*pos
)
491 for (p
= pos
; !is_eof( parser
, p
); p
++)
500 close_current_line( parser
);
504 push_state( parser
, LINE_START
);
505 set_state( parser
, COMMENT
);
509 parser
->start
= p
+ 1;
510 set_state( parser
, SECTION_NAME
);
517 set_state( parser
, KEY_NAME
);
523 close_current_line( parser
);
528 /* handler for parser SECTION_NAME state */
529 static const CHAR
*section_name_state( struct parser
*parser
, const CHAR
*pos
)
533 for (p
= pos
; !is_eol( parser
, p
); p
++)
537 push_token( parser
, p
);
538 if (add_section_from_token( parser
) == NULL
)
540 push_state( parser
, LINE_START
);
541 set_state( parser
, COMMENT
); /* ignore everything else on the line */
545 parser
->error
= INF_STATUS_BAD_SECTION_NAME_LINE
; /* unfinished section name */
550 /* handler for parser KEY_NAME state */
551 static const CHAR
*key_name_state( struct parser
*parser
, const CHAR
*pos
)
553 const CHAR
*p
, *token_end
= parser
->start
;
555 for (p
= pos
; !is_eol( parser
, p
); p
++)
557 if (*p
== ',') break;
562 push_token( parser
, token_end
);
563 if (!add_field_from_token( parser
, 1 )) return NULL
;
564 parser
->start
= p
+ 1;
565 push_state( parser
, VALUE_NAME
);
566 set_state( parser
, LEADING_SPACES
);
569 push_token( parser
, token_end
);
570 if (!add_field_from_token( parser
, 0 )) return NULL
;
571 push_state( parser
, LINE_START
);
572 set_state( parser
, COMMENT
);
575 push_token( parser
, token_end
);
576 parser
->start
= p
+ 1;
577 push_state( parser
, KEY_NAME
);
578 set_state( parser
, QUOTES
);
581 push_token( parser
, token_end
);
583 push_state( parser
, KEY_NAME
);
584 set_state( parser
, EOL_BACKSLASH
);
587 if (!isspace(*p
)) token_end
= p
+ 1;
590 push_token( parser
, p
);
591 push_state( parser
, KEY_NAME
);
592 set_state( parser
, TRAILING_SPACES
);
598 push_token( parser
, token_end
);
599 set_state( parser
, VALUE_NAME
);
604 /* handler for parser VALUE_NAME state */
605 static const CHAR
*value_name_state( struct parser
*parser
, const CHAR
*pos
)
607 const CHAR
*p
, *token_end
= parser
->start
;
609 for (p
= pos
; !is_eol( parser
, p
); p
++)
614 push_token( parser
, token_end
);
615 if (!add_field_from_token( parser
, 0 )) return NULL
;
616 push_state( parser
, LINE_START
);
617 set_state( parser
, COMMENT
);
620 push_token( parser
, token_end
);
621 if (!add_field_from_token( parser
, 0 )) return NULL
;
622 parser
->start
= p
+ 1;
623 push_state( parser
, VALUE_NAME
);
624 set_state( parser
, LEADING_SPACES
);
627 push_token( parser
, token_end
);
628 parser
->start
= p
+ 1;
629 push_state( parser
, VALUE_NAME
);
630 set_state( parser
, QUOTES
);
633 push_token( parser
, token_end
);
635 push_state( parser
, VALUE_NAME
);
636 set_state( parser
, EOL_BACKSLASH
);
639 if (!isspace(*p
)) token_end
= p
+ 1;
642 push_token( parser
, p
);
643 push_state( parser
, VALUE_NAME
);
644 set_state( parser
, TRAILING_SPACES
);
650 push_token( parser
, token_end
);
651 if (!add_field_from_token( parser
, 0 )) return NULL
;
652 set_state( parser
, LINE_START
);
657 /* handler for parser EOL_BACKSLASH state */
658 static const CHAR
*eol_backslash_state( struct parser
*parser
, const CHAR
*pos
)
662 for (p
= pos
; !is_eof( parser
, p
); p
++)
671 parser
->start
= p
+ 1;
672 set_state( parser
, LEADING_SPACES
);
679 push_state( parser
, EOL_BACKSLASH
);
680 set_state( parser
, COMMENT
);
686 push_token( parser
, p
);
698 /* handler for parser QUOTES state */
699 static const CHAR
*quotes_state( struct parser
*parser
, const CHAR
*pos
)
701 const CHAR
*p
, *token_end
= parser
->start
;
703 for (p
= pos
; !is_eol( parser
, p
); p
++)
707 if (p
+1 < parser
->end
&& p
[1] == '"') /* double quotes */
709 push_token( parser
, p
+ 1 );
710 parser
->start
= token_end
= p
+ 2;
713 else /* end of quotes */
715 push_token( parser
, p
);
716 parser
->start
= p
+ 1;
722 push_token( parser
, p
);
728 /* handler for parser LEADING_SPACES state */
729 static const CHAR
*leading_spaces_state( struct parser
*parser
, const CHAR
*pos
)
733 for (p
= pos
; !is_eol( parser
, p
); p
++)
738 set_state( parser
, EOL_BACKSLASH
);
750 /* handler for parser TRAILING_SPACES state */
751 static const CHAR
*trailing_spaces_state( struct parser
*parser
, const CHAR
*pos
)
755 for (p
= pos
; !is_eol( parser
, p
); p
++)
759 set_state( parser
, EOL_BACKSLASH
);
770 /* handler for parser COMMENT state */
771 static const CHAR
*comment_state( struct parser
*parser
, const CHAR
*pos
)
775 while (!is_eol( parser
, p
))
782 /* parse a complete buffer */
784 InfpParseBuffer (PINFCACHE file
,
789 struct parser parser
;
790 const CHAR
*pos
= buffer
;
792 parser
.start
= buffer
;
796 parser
.state
= LINE_START
;
797 parser
.stack_pos
= 0;
798 parser
.cur_section
= NULL
;
801 parser
.token_len
= 0;
803 /* parser main loop */
805 pos
= (parser_funcs
[parser
.state
])(&parser
, pos
);
810 *error_line
= parser
.line_pos
;
814 /* find the [strings] section */
815 file
->StringsSection
= InfpFindSection(file
,
818 return INF_STATUS_SUCCESS
;