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 INFSTATUS 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
= (ULONG
)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
= (ULONG
)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 UINT len
= (UINT
)(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
++)
413 /* add a section with the current token as name */
414 static PVOID
add_section_from_token( struct parser
*parser
)
416 PINFCACHESECTION Section
;
418 if (parser
->token_len
> MAX_SECTION_NAME_LEN
)
420 parser
->error
= INF_STATUS_SECTION_NAME_TOO_LONG
;
424 Section
= InfpFindSection(parser
->file
,
428 /* need to create a new one */
429 Section
= InfpAddSection(parser
->file
,
433 parser
->error
= INF_STATUS_NOT_ENOUGH_MEMORY
;
438 parser
->token_len
= 0;
439 parser
->cur_section
= Section
;
441 return (PVOID
)Section
;
445 /* add a field containing the current token to the current line */
446 static struct field
*add_field_from_token( struct parser
*parser
, int is_key
)
450 if (!parser
->line
) /* need to start a new line */
452 if (parser
->cur_section
== NULL
) /* got a line before the first section */
454 parser
->error
= INF_STATUS_WRONG_INF_STYLE
;
458 parser
->line
= InfpAddLine(parser
->cur_section
);
459 if (parser
->line
== NULL
)
469 field
= InfpAddKeyToLine(parser
->line
, parser
->token
);
473 field
= InfpAddFieldToLine(parser
->line
, parser
->token
);
478 parser
->token_len
= 0;
483 parser
->error
= INF_STATUS_NOT_ENOUGH_MEMORY
;
488 /* close the current line and prepare for parsing a new one */
489 static void close_current_line( struct parser
*parser
)
496 /* handler for parser LINE_START state */
497 static const CHAR
*line_start_state( struct parser
*parser
, const CHAR
*pos
)
501 for (p
= pos
; !is_eof( parser
, p
); p
++)
510 close_current_line( parser
);
514 push_state( parser
, LINE_START
);
515 set_state( parser
, COMMENT
);
519 parser
->start
= p
+ 1;
520 set_state( parser
, SECTION_NAME
);
527 set_state( parser
, KEY_NAME
);
533 close_current_line( parser
);
538 /* handler for parser SECTION_NAME state */
539 static const CHAR
*section_name_state( struct parser
*parser
, const CHAR
*pos
)
543 for (p
= pos
; !is_eol( parser
, p
); p
++)
547 push_token( parser
, p
);
548 if (add_section_from_token( parser
) == NULL
)
550 push_state( parser
, LINE_START
);
551 set_state( parser
, COMMENT
); /* ignore everything else on the line */
555 parser
->error
= INF_STATUS_BAD_SECTION_NAME_LINE
; /* unfinished section name */
560 /* handler for parser KEY_NAME state */
561 static const CHAR
*key_name_state( struct parser
*parser
, const CHAR
*pos
)
563 const CHAR
*p
, *token_end
= parser
->start
;
565 for (p
= pos
; !is_eol( parser
, p
); p
++)
567 if (*p
== ',') break;
572 push_token( parser
, token_end
);
573 if (!add_field_from_token( parser
, 1 )) return NULL
;
574 parser
->start
= p
+ 1;
575 push_state( parser
, VALUE_NAME
);
576 set_state( parser
, LEADING_SPACES
);
579 push_token( parser
, token_end
);
580 if (!add_field_from_token( parser
, 0 )) return NULL
;
581 push_state( parser
, LINE_START
);
582 set_state( parser
, COMMENT
);
585 push_token( parser
, token_end
);
586 parser
->start
= p
+ 1;
587 push_state( parser
, KEY_NAME
);
588 set_state( parser
, QUOTES
);
591 push_token( parser
, token_end
);
593 push_state( parser
, KEY_NAME
);
594 set_state( parser
, EOL_BACKSLASH
);
597 if (!isspace(*p
)) token_end
= p
+ 1;
600 push_token( parser
, p
);
601 push_state( parser
, KEY_NAME
);
602 set_state( parser
, TRAILING_SPACES
);
608 push_token( parser
, token_end
);
609 set_state( parser
, VALUE_NAME
);
614 /* handler for parser VALUE_NAME state */
615 static const CHAR
*value_name_state( struct parser
*parser
, const CHAR
*pos
)
617 const CHAR
*p
, *token_end
= parser
->start
;
619 for (p
= pos
; !is_eol( parser
, p
); p
++)
624 push_token( parser
, token_end
);
625 if (!add_field_from_token( parser
, 0 )) return NULL
;
626 push_state( parser
, LINE_START
);
627 set_state( parser
, COMMENT
);
630 push_token( parser
, token_end
);
631 if (!add_field_from_token( parser
, 0 )) return NULL
;
632 parser
->start
= p
+ 1;
633 push_state( parser
, VALUE_NAME
);
634 set_state( parser
, LEADING_SPACES
);
637 push_token( parser
, token_end
);
638 parser
->start
= p
+ 1;
639 push_state( parser
, VALUE_NAME
);
640 set_state( parser
, QUOTES
);
643 push_token( parser
, token_end
);
645 push_state( parser
, VALUE_NAME
);
646 set_state( parser
, EOL_BACKSLASH
);
649 if (!isspace(*p
)) token_end
= p
+ 1;
652 push_token( parser
, p
);
653 push_state( parser
, VALUE_NAME
);
654 set_state( parser
, TRAILING_SPACES
);
660 push_token( parser
, token_end
);
661 if (!add_field_from_token( parser
, 0 )) return NULL
;
662 set_state( parser
, LINE_START
);
667 /* handler for parser EOL_BACKSLASH state */
668 static const CHAR
*eol_backslash_state( struct parser
*parser
, const CHAR
*pos
)
672 for (p
= pos
; !is_eof( parser
, p
); p
++)
681 parser
->start
= p
+ 1;
682 set_state( parser
, LEADING_SPACES
);
689 push_state( parser
, EOL_BACKSLASH
);
690 set_state( parser
, COMMENT
);
696 push_token( parser
, p
);
708 /* handler for parser QUOTES state */
709 static const CHAR
*quotes_state( struct parser
*parser
, const CHAR
*pos
)
711 const CHAR
*p
, *token_end
= parser
->start
;
713 for (p
= pos
; !is_eol( parser
, p
); p
++)
717 if (p
+1 < parser
->end
&& p
[1] == '"') /* double quotes */
719 push_token( parser
, p
+ 1 );
720 parser
->start
= token_end
= p
+ 2;
723 else /* end of quotes */
725 push_token( parser
, p
);
726 parser
->start
= p
+ 1;
732 push_token( parser
, p
);
738 /* handler for parser LEADING_SPACES state */
739 static const CHAR
*leading_spaces_state( struct parser
*parser
, const CHAR
*pos
)
743 for (p
= pos
; !is_eol( parser
, p
); p
++)
748 set_state( parser
, EOL_BACKSLASH
);
760 /* handler for parser TRAILING_SPACES state */
761 static const CHAR
*trailing_spaces_state( struct parser
*parser
, const CHAR
*pos
)
765 for (p
= pos
; !is_eol( parser
, p
); p
++)
769 set_state( parser
, EOL_BACKSLASH
);
780 /* handler for parser COMMENT state */
781 static const CHAR
*comment_state( struct parser
*parser
, const CHAR
*pos
)
785 while (!is_eol( parser
, p
))
792 /* parse a complete buffer */
794 InfpParseBuffer (PINFCACHE file
,
799 struct parser parser
;
800 const CHAR
*pos
= buffer
;
802 parser
.start
= buffer
;
806 parser
.state
= LINE_START
;
807 parser
.stack_pos
= 0;
808 parser
.cur_section
= NULL
;
811 parser
.token_len
= 0;
813 /* parser main loop */
815 pos
= (parser_funcs
[parser
.state
])(&parser
, pos
);
820 *error_line
= parser
.line_pos
;
824 /* find the [strings] section */
825 file
->StringsSection
= InfpFindSection(file
,
828 return INF_STATUS_SUCCESS
;