2010-04-25 15:58:34 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: .inf file parser
|
|
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
|
|
* PROGRAMMER: Royce Mitchell III
|
|
|
|
* Eric Kohl
|
|
|
|
* Ge van Geldorp <gvg@reactos.org>
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
|
|
|
|
#include "inflib.h"
|
|
|
|
|
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
|
|
|
#define CONTROL_Z '\x1a'
|
|
|
|
#define MAX_SECTION_NAME_LEN 255
|
|
|
|
#define MAX_FIELD_LEN 511 /* larger fields get silently truncated */
|
|
|
|
/* actual string limit is MAX_INF_STRING_LENGTH+1 (plus terminating null) under Windows */
|
|
|
|
#define MAX_STRING_LEN (MAX_INF_STRING_LENGTH+1)
|
|
|
|
|
|
|
|
|
|
|
|
/* parser definitions */
|
|
|
|
|
|
|
|
enum parser_state
|
|
|
|
{
|
|
|
|
LINE_START, /* at beginning of a line */
|
|
|
|
SECTION_NAME, /* parsing a section name */
|
|
|
|
KEY_NAME, /* parsing a key name */
|
|
|
|
VALUE_NAME, /* parsing a value name */
|
|
|
|
EOL_BACKSLASH, /* backslash at end of line */
|
|
|
|
QUOTES, /* inside quotes */
|
|
|
|
LEADING_SPACES, /* leading spaces */
|
|
|
|
TRAILING_SPACES, /* trailing spaces */
|
|
|
|
COMMENT, /* inside a comment */
|
|
|
|
NB_PARSER_STATES
|
|
|
|
};
|
|
|
|
|
|
|
|
struct parser
|
|
|
|
{
|
|
|
|
const WCHAR *start; /* start position of item being parsed */
|
|
|
|
const WCHAR *end; /* end of buffer */
|
|
|
|
PINFCACHE file; /* file being built */
|
|
|
|
enum parser_state state; /* current parser state */
|
|
|
|
enum parser_state stack[4]; /* state stack */
|
|
|
|
int stack_pos; /* current pos in stack */
|
|
|
|
|
|
|
|
PINFCACHESECTION cur_section; /* pointer to the section being parsed*/
|
|
|
|
PINFCACHELINE line; /* current line */
|
|
|
|
unsigned int line_pos; /* current line position in file */
|
|
|
|
INFSTATUS error; /* error code */
|
|
|
|
unsigned int token_len; /* current token len */
|
|
|
|
WCHAR token[MAX_FIELD_LEN+1]; /* current token */
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef const WCHAR * (*parser_state_func)( struct parser *parser, const WCHAR *pos );
|
|
|
|
|
|
|
|
/* parser state machine functions */
|
|
|
|
static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos );
|
|
|
|
static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos );
|
|
|
|
static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos );
|
|
|
|
static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos );
|
|
|
|
static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos );
|
|
|
|
static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos );
|
|
|
|
static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos );
|
|
|
|
static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos );
|
|
|
|
static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos );
|
|
|
|
|
|
|
|
static const parser_state_func parser_funcs[NB_PARSER_STATES] =
|
|
|
|
{
|
|
|
|
line_start_state, /* LINE_START */
|
|
|
|
section_name_state, /* SECTION_NAME */
|
|
|
|
key_name_state, /* KEY_NAME */
|
|
|
|
value_name_state, /* VALUE_NAME */
|
|
|
|
eol_backslash_state, /* EOL_BACKSLASH */
|
|
|
|
quotes_state, /* QUOTES */
|
|
|
|
leading_spaces_state, /* LEADING_SPACES */
|
|
|
|
trailing_spaces_state, /* TRAILING_SPACES */
|
|
|
|
comment_state /* COMMENT */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* PRIVATE FUNCTIONS ********************************************************/
|
|
|
|
|
|
|
|
static PINFCACHELINE
|
|
|
|
InfpFreeLine (PINFCACHELINE Line)
|
|
|
|
{
|
|
|
|
PINFCACHELINE Next;
|
|
|
|
PINFCACHEFIELD Field;
|
|
|
|
|
|
|
|
if (Line == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Next = Line->Next;
|
|
|
|
if (Line->Key != NULL)
|
|
|
|
{
|
|
|
|
FREE (Line->Key);
|
|
|
|
Line->Key = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove data fields */
|
|
|
|
while (Line->FirstField != NULL)
|
|
|
|
{
|
|
|
|
Field = Line->FirstField->Next;
|
|
|
|
FREE (Line->FirstField);
|
|
|
|
Line->FirstField = Field;
|
|
|
|
}
|
|
|
|
Line->LastField = NULL;
|
|
|
|
|
|
|
|
FREE (Line);
|
|
|
|
|
|
|
|
return Next;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PINFCACHESECTION
|
|
|
|
InfpFreeSection (PINFCACHESECTION Section)
|
|
|
|
{
|
|
|
|
PINFCACHESECTION Next;
|
|
|
|
|
|
|
|
if (Section == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release all keys */
|
|
|
|
Next = Section->Next;
|
|
|
|
while (Section->FirstLine != NULL)
|
|
|
|
{
|
|
|
|
Section->FirstLine = InfpFreeLine (Section->FirstLine);
|
|
|
|
}
|
|
|
|
Section->LastLine = NULL;
|
|
|
|
|
|
|
|
FREE (Section);
|
|
|
|
|
|
|
|
return Next;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PINFCACHESECTION
|
|
|
|
InfpFindSection(PINFCACHE Cache,
|
|
|
|
PCWSTR Name)
|
|
|
|
{
|
|
|
|
PINFCACHESECTION Section = NULL;
|
|
|
|
|
|
|
|
if (Cache == NULL || Name == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* iterate through list of sections */
|
|
|
|
Section = Cache->FirstSection;
|
|
|
|
while (Section != NULL)
|
|
|
|
{
|
2010-05-01 10:43:39 +00:00
|
|
|
if (strcmpiW(Section->Name, Name) == 0)
|
2010-04-25 15:58:34 +00:00
|
|
|
{
|
|
|
|
return Section;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get the next section*/
|
|
|
|
Section = Section->Next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PINFCACHESECTION
|
|
|
|
InfpAddSection(PINFCACHE Cache,
|
|
|
|
PCWSTR Name)
|
|
|
|
{
|
|
|
|
PINFCACHESECTION Section = NULL;
|
|
|
|
ULONG Size;
|
|
|
|
|
|
|
|
if (Cache == NULL || Name == NULL)
|
|
|
|
{
|
|
|
|
DPRINT("Invalid parameter\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate and initialize the new section */
|
|
|
|
Size = (ULONG)FIELD_OFFSET(INFCACHESECTION,
|
2010-05-01 10:43:39 +00:00
|
|
|
Name[strlenW(Name) + 1]);
|
2010-04-25 15:58:34 +00:00
|
|
|
Section = (PINFCACHESECTION)MALLOC(Size);
|
|
|
|
if (Section == NULL)
|
|
|
|
{
|
|
|
|
DPRINT("MALLOC() failed\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ZEROMEMORY (Section,
|
|
|
|
Size);
|
2019-06-23 20:35:19 +00:00
|
|
|
Section->Id = ++Cache->NextSectionId;
|
2010-04-25 15:58:34 +00:00
|
|
|
|
|
|
|
/* Copy section name */
|
2010-05-01 10:43:39 +00:00
|
|
|
strcpyW(Section->Name, Name);
|
2010-04-25 15:58:34 +00:00
|
|
|
|
|
|
|
/* Append section */
|
|
|
|
if (Cache->FirstSection == NULL)
|
|
|
|
{
|
|
|
|
Cache->FirstSection = Section;
|
|
|
|
Cache->LastSection = Section;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Cache->LastSection->Next = Section;
|
|
|
|
Section->Prev = Cache->LastSection;
|
|
|
|
Cache->LastSection = Section;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Section;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PINFCACHELINE
|
|
|
|
InfpAddLine(PINFCACHESECTION Section)
|
|
|
|
{
|
|
|
|
PINFCACHELINE Line;
|
|
|
|
|
|
|
|
if (Section == NULL)
|
|
|
|
{
|
|
|
|
DPRINT("Invalid parameter\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Line = (PINFCACHELINE)MALLOC(sizeof(INFCACHELINE));
|
|
|
|
if (Line == NULL)
|
|
|
|
{
|
|
|
|
DPRINT("MALLOC() failed\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ZEROMEMORY(Line,
|
|
|
|
sizeof(INFCACHELINE));
|
2019-06-23 20:35:19 +00:00
|
|
|
Line->Id = ++Section->NextLineId;
|
2010-04-25 15:58:34 +00:00
|
|
|
|
|
|
|
/* Append line */
|
|
|
|
if (Section->FirstLine == NULL)
|
|
|
|
{
|
|
|
|
Section->FirstLine = Line;
|
|
|
|
Section->LastLine = Line;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Section->LastLine->Next = Line;
|
|
|
|
Line->Prev = Section->LastLine;
|
|
|
|
Section->LastLine = Line;
|
|
|
|
}
|
|
|
|
Section->LineCount++;
|
|
|
|
|
|
|
|
return Line;
|
|
|
|
}
|
|
|
|
|
2019-06-23 20:35:19 +00:00
|
|
|
PINFCACHESECTION
|
|
|
|
InfpFindSectionById(PINFCACHE Cache, UINT Id)
|
|
|
|
{
|
|
|
|
PINFCACHESECTION Section;
|
|
|
|
|
|
|
|
for (Section = Cache->FirstSection;
|
|
|
|
Section != NULL;
|
|
|
|
Section = Section->Next)
|
|
|
|
{
|
|
|
|
if (Section->Id == Id)
|
|
|
|
{
|
|
|
|
return Section;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
PINFCACHESECTION
|
|
|
|
InfpGetSectionForContext(PINFCONTEXT Context)
|
|
|
|
{
|
|
|
|
PINFCACHE Cache;
|
|
|
|
|
|
|
|
if (Context == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Cache = (PINFCACHE)Context->Inf;
|
|
|
|
if (Cache == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return InfpFindSectionById(Cache, Context->Section);
|
|
|
|
}
|
|
|
|
|
|
|
|
PINFCACHELINE
|
|
|
|
InfpFindLineById(PINFCACHESECTION Section, UINT Id)
|
|
|
|
{
|
|
|
|
PINFCACHELINE Line;
|
|
|
|
|
|
|
|
for (Line = Section->FirstLine;
|
|
|
|
Line != NULL;
|
|
|
|
Line = Line->Next)
|
|
|
|
{
|
|
|
|
if (Line->Id == Id)
|
|
|
|
{
|
|
|
|
return Line;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
PINFCACHELINE
|
|
|
|
InfpGetLineForContext(PINFCONTEXT Context)
|
|
|
|
{
|
|
|
|
PINFCACHESECTION Section;
|
|
|
|
|
|
|
|
Section = InfpGetSectionForContext(Context);
|
|
|
|
if (Section == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return InfpFindLineById(Section, Context->Line);
|
|
|
|
}
|
2010-04-25 15:58:34 +00:00
|
|
|
|
|
|
|
PVOID
|
|
|
|
InfpAddKeyToLine(PINFCACHELINE Line,
|
|
|
|
PCWSTR Key)
|
|
|
|
{
|
|
|
|
if (Line == NULL)
|
|
|
|
{
|
|
|
|
DPRINT1("Invalid Line\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Line->Key != NULL)
|
|
|
|
{
|
|
|
|
DPRINT1("Line already has a key\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-05-01 10:43:39 +00:00
|
|
|
Line->Key = (PWCHAR)MALLOC((strlenW(Key) + 1) * sizeof(WCHAR));
|
2010-04-25 15:58:34 +00:00
|
|
|
if (Line->Key == NULL)
|
|
|
|
{
|
|
|
|
DPRINT1("MALLOC() failed\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-05-01 10:43:39 +00:00
|
|
|
strcpyW(Line->Key, Key);
|
2010-04-25 15:58:34 +00:00
|
|
|
|
|
|
|
return (PVOID)Line->Key;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PVOID
|
|
|
|
InfpAddFieldToLine(PINFCACHELINE Line,
|
|
|
|
PCWSTR Data)
|
|
|
|
{
|
|
|
|
PINFCACHEFIELD Field;
|
|
|
|
ULONG Size;
|
|
|
|
|
|
|
|
Size = (ULONG)FIELD_OFFSET(INFCACHEFIELD,
|
2010-05-01 10:43:39 +00:00
|
|
|
Data[strlenW(Data) + 1]);
|
2010-04-25 15:58:34 +00:00
|
|
|
Field = (PINFCACHEFIELD)MALLOC(Size);
|
|
|
|
if (Field == NULL)
|
|
|
|
{
|
|
|
|
DPRINT1("MALLOC() failed\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ZEROMEMORY (Field,
|
|
|
|
Size);
|
2010-05-01 10:43:39 +00:00
|
|
|
strcpyW(Field->Data, Data);
|
2010-04-25 15:58:34 +00:00
|
|
|
|
|
|
|
/* Append key */
|
|
|
|
if (Line->FirstField == NULL)
|
|
|
|
{
|
|
|
|
Line->FirstField = Field;
|
|
|
|
Line->LastField = Field;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Line->LastField->Next = Field;
|
|
|
|
Field->Prev = Line->LastField;
|
|
|
|
Line->LastField = Field;
|
|
|
|
}
|
|
|
|
Line->FieldCount++;
|
|
|
|
|
|
|
|
return (PVOID)Field;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PINFCACHELINE
|
|
|
|
InfpFindKeyLine(PINFCACHESECTION Section,
|
|
|
|
PCWSTR Key)
|
|
|
|
{
|
|
|
|
PINFCACHELINE Line;
|
|
|
|
|
|
|
|
Line = Section->FirstLine;
|
|
|
|
while (Line != NULL)
|
|
|
|
{
|
2010-05-01 10:43:39 +00:00
|
|
|
if (Line->Key != NULL && strcmpiW(Line->Key, Key) == 0)
|
2010-04-25 15:58:34 +00:00
|
|
|
{
|
|
|
|
return Line;
|
|
|
|
}
|
|
|
|
|
|
|
|
Line = Line->Next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* push the current state on the parser stack */
|
|
|
|
__inline static void push_state( struct parser *parser, enum parser_state state )
|
|
|
|
{
|
|
|
|
// assert( parser->stack_pos < sizeof(parser->stack)/sizeof(parser->stack[0]) );
|
|
|
|
parser->stack[parser->stack_pos++] = state;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* pop the current state */
|
|
|
|
__inline static void pop_state( struct parser *parser )
|
|
|
|
{
|
|
|
|
// assert( parser->stack_pos );
|
|
|
|
parser->state = parser->stack[--parser->stack_pos];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* set the parser state and return the previous one */
|
|
|
|
__inline static enum parser_state set_state( struct parser *parser, enum parser_state state )
|
|
|
|
{
|
|
|
|
enum parser_state ret = parser->state;
|
|
|
|
parser->state = state;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* check if the pointer points to an end of file */
|
|
|
|
__inline static int is_eof( struct parser *parser, const WCHAR *ptr )
|
|
|
|
{
|
2010-04-28 11:35:34 +00:00
|
|
|
return (ptr >= parser->end || *ptr == CONTROL_Z || *ptr == 0);
|
2010-04-25 15:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* check if the pointer points to an end of line */
|
|
|
|
__inline static int is_eol( struct parser *parser, const WCHAR *ptr )
|
|
|
|
{
|
|
|
|
return (ptr >= parser->end ||
|
|
|
|
*ptr == CONTROL_Z ||
|
|
|
|
*ptr == '\n' ||
|
2010-04-28 11:35:34 +00:00
|
|
|
(*ptr == '\r' && *(ptr + 1) == '\n') ||
|
|
|
|
*ptr == 0);
|
2010-04-25 15:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* push data from current token start up to pos into the current token */
|
|
|
|
static int push_token( struct parser *parser, const WCHAR *pos )
|
|
|
|
{
|
|
|
|
UINT len = (UINT)(pos - parser->start);
|
|
|
|
const WCHAR *src = parser->start;
|
|
|
|
WCHAR *dst = parser->token + parser->token_len;
|
|
|
|
|
|
|
|
if (len > MAX_FIELD_LEN - parser->token_len)
|
|
|
|
len = MAX_FIELD_LEN - parser->token_len;
|
|
|
|
|
|
|
|
parser->token_len += len;
|
|
|
|
for ( ; len > 0; len--, dst++, src++)
|
|
|
|
{
|
|
|
|
if (*src)
|
|
|
|
{
|
|
|
|
*dst = *src;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*dst = ' ';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*dst = 0;
|
|
|
|
parser->start = pos;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* add a section with the current token as name */
|
|
|
|
static PVOID add_section_from_token( struct parser *parser )
|
|
|
|
{
|
|
|
|
PINFCACHESECTION Section;
|
|
|
|
|
|
|
|
if (parser->token_len > MAX_SECTION_NAME_LEN)
|
|
|
|
{
|
|
|
|
parser->error = INF_STATUS_SECTION_NAME_TOO_LONG;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Section = InfpFindSection(parser->file,
|
|
|
|
parser->token);
|
|
|
|
if (Section == NULL)
|
|
|
|
{
|
|
|
|
/* need to create a new one */
|
|
|
|
Section= InfpAddSection(parser->file,
|
|
|
|
parser->token);
|
|
|
|
if (Section == NULL)
|
|
|
|
{
|
|
|
|
parser->error = INF_STATUS_NOT_ENOUGH_MEMORY;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
parser->token_len = 0;
|
|
|
|
parser->cur_section = Section;
|
|
|
|
|
|
|
|
return (PVOID)Section;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* add a field containing the current token to the current line */
|
|
|
|
static struct field *add_field_from_token( struct parser *parser, int is_key )
|
|
|
|
{
|
|
|
|
PVOID field;
|
|
|
|
|
|
|
|
if (!parser->line) /* need to start a new line */
|
|
|
|
{
|
|
|
|
if (parser->cur_section == NULL) /* got a line before the first section */
|
|
|
|
{
|
|
|
|
parser->error = INF_STATUS_WRONG_INF_STYLE;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
parser->line = InfpAddLine(parser->cur_section);
|
|
|
|
if (parser->line == NULL)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// assert(!is_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_key)
|
|
|
|
{
|
|
|
|
field = InfpAddKeyToLine(parser->line, parser->token);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
field = InfpAddFieldToLine(parser->line, parser->token);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (field != NULL)
|
|
|
|
{
|
|
|
|
parser->token_len = 0;
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
|
|
|
parser->error = INF_STATUS_NOT_ENOUGH_MEMORY;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* close the current line and prepare for parsing a new one */
|
|
|
|
static void close_current_line( struct parser *parser )
|
|
|
|
{
|
|
|
|
parser->line = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* handler for parser LINE_START state */
|
|
|
|
static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos )
|
|
|
|
{
|
|
|
|
const WCHAR *p;
|
|
|
|
|
|
|
|
for (p = pos; !is_eof( parser, p ); p++)
|
|
|
|
{
|
|
|
|
switch(*p)
|
|
|
|
{
|
|
|
|
case '\r':
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case '\n':
|
|
|
|
parser->line_pos++;
|
|
|
|
close_current_line( parser );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ';':
|
|
|
|
push_state( parser, LINE_START );
|
|
|
|
set_state( parser, COMMENT );
|
|
|
|
return p + 1;
|
|
|
|
|
|
|
|
case '[':
|
|
|
|
parser->start = p + 1;
|
|
|
|
set_state( parser, SECTION_NAME );
|
|
|
|
return p + 1;
|
|
|
|
|
|
|
|
default:
|
2010-05-01 10:43:39 +00:00
|
|
|
if (!isspaceW(*p))
|
2010-04-25 15:58:34 +00:00
|
|
|
{
|
|
|
|
parser->start = p;
|
|
|
|
set_state( parser, KEY_NAME );
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close_current_line( parser );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* handler for parser SECTION_NAME state */
|
|
|
|
static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos )
|
|
|
|
{
|
|
|
|
const WCHAR *p;
|
|
|
|
|
|
|
|
for (p = pos; !is_eol( parser, p ); p++)
|
|
|
|
{
|
|
|
|
if (*p == ']')
|
|
|
|
{
|
|
|
|
push_token( parser, p );
|
|
|
|
if (add_section_from_token( parser ) == NULL)
|
|
|
|
return NULL;
|
|
|
|
push_state( parser, LINE_START );
|
|
|
|
set_state( parser, COMMENT ); /* ignore everything else on the line */
|
|
|
|
return p + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parser->error = INF_STATUS_BAD_SECTION_NAME_LINE; /* unfinished section name */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* handler for parser KEY_NAME state */
|
|
|
|
static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos )
|
|
|
|
{
|
|
|
|
const WCHAR *p, *token_end = parser->start;
|
|
|
|
|
|
|
|
for (p = pos; !is_eol( parser, p ); p++)
|
|
|
|
{
|
|
|
|
if (*p == ',') break;
|
|
|
|
switch(*p)
|
|
|
|
{
|
|
|
|
|
|
|
|
case '=':
|
|
|
|
push_token( parser, token_end );
|
|
|
|
if (!add_field_from_token( parser, 1 )) return NULL;
|
|
|
|
parser->start = p + 1;
|
|
|
|
push_state( parser, VALUE_NAME );
|
|
|
|
set_state( parser, LEADING_SPACES );
|
|
|
|
return p + 1;
|
|
|
|
case ';':
|
|
|
|
push_token( parser, token_end );
|
|
|
|
if (!add_field_from_token( parser, 0 )) return NULL;
|
|
|
|
push_state( parser, LINE_START );
|
|
|
|
set_state( parser, COMMENT );
|
|
|
|
return p + 1;
|
|
|
|
case '"':
|
|
|
|
push_token( parser, token_end );
|
|
|
|
parser->start = p + 1;
|
|
|
|
push_state( parser, KEY_NAME );
|
|
|
|
set_state( parser, QUOTES );
|
|
|
|
return p + 1;
|
|
|
|
case '\\':
|
|
|
|
push_token( parser, token_end );
|
|
|
|
parser->start = p;
|
|
|
|
push_state( parser, KEY_NAME );
|
|
|
|
set_state( parser, EOL_BACKSLASH );
|
|
|
|
return p;
|
|
|
|
default:
|
2010-05-01 10:43:39 +00:00
|
|
|
if (!isspaceW(*p)) token_end = p + 1;
|
2010-04-25 15:58:34 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
push_token( parser, p );
|
|
|
|
push_state( parser, KEY_NAME );
|
|
|
|
set_state( parser, TRAILING_SPACES );
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
push_token( parser, token_end );
|
|
|
|
set_state( parser, VALUE_NAME );
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* handler for parser VALUE_NAME state */
|
|
|
|
static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos )
|
|
|
|
{
|
|
|
|
const WCHAR *p, *token_end = parser->start;
|
|
|
|
|
|
|
|
for (p = pos; !is_eol( parser, p ); p++)
|
|
|
|
{
|
|
|
|
switch(*p)
|
|
|
|
{
|
|
|
|
case ';':
|
|
|
|
push_token( parser, token_end );
|
|
|
|
if (!add_field_from_token( parser, 0 )) return NULL;
|
|
|
|
push_state( parser, LINE_START );
|
|
|
|
set_state( parser, COMMENT );
|
|
|
|
return p + 1;
|
|
|
|
case ',':
|
|
|
|
push_token( parser, token_end );
|
|
|
|
if (!add_field_from_token( parser, 0 )) return NULL;
|
|
|
|
parser->start = p + 1;
|
|
|
|
push_state( parser, VALUE_NAME );
|
|
|
|
set_state( parser, LEADING_SPACES );
|
|
|
|
return p + 1;
|
|
|
|
case '"':
|
|
|
|
push_token( parser, token_end );
|
|
|
|
parser->start = p + 1;
|
|
|
|
push_state( parser, VALUE_NAME );
|
|
|
|
set_state( parser, QUOTES );
|
|
|
|
return p + 1;
|
|
|
|
case '\\':
|
|
|
|
push_token( parser, token_end );
|
|
|
|
parser->start = p;
|
|
|
|
push_state( parser, VALUE_NAME );
|
|
|
|
set_state( parser, EOL_BACKSLASH );
|
|
|
|
return p;
|
|
|
|
default:
|
2010-05-01 10:43:39 +00:00
|
|
|
if (!isspaceW(*p)) token_end = p + 1;
|
2010-04-25 15:58:34 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
push_token( parser, p );
|
|
|
|
push_state( parser, VALUE_NAME );
|
|
|
|
set_state( parser, TRAILING_SPACES );
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
push_token( parser, token_end );
|
|
|
|
if (!add_field_from_token( parser, 0 )) return NULL;
|
|
|
|
set_state( parser, LINE_START );
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* handler for parser EOL_BACKSLASH state */
|
|
|
|
static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos )
|
|
|
|
{
|
|
|
|
const WCHAR *p;
|
|
|
|
|
|
|
|
for (p = pos; !is_eof( parser, p ); p++)
|
|
|
|
{
|
|
|
|
switch(*p)
|
|
|
|
{
|
|
|
|
case '\r':
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case '\n':
|
|
|
|
parser->line_pos++;
|
|
|
|
parser->start = p + 1;
|
|
|
|
set_state( parser, LEADING_SPACES );
|
|
|
|
return p + 1;
|
|
|
|
|
|
|
|
case '\\':
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case ';':
|
|
|
|
push_state( parser, EOL_BACKSLASH );
|
|
|
|
set_state( parser, COMMENT );
|
|
|
|
return p + 1;
|
|
|
|
|
|
|
|
default:
|
2010-05-01 10:43:39 +00:00
|
|
|
if (isspaceW(*p))
|
2010-04-25 15:58:34 +00:00
|
|
|
continue;
|
|
|
|
push_token( parser, p );
|
|
|
|
pop_state( parser );
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parser->start = p;
|
|
|
|
pop_state( parser );
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* handler for parser QUOTES state */
|
|
|
|
static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos )
|
|
|
|
{
|
|
|
|
const WCHAR *p, *token_end = parser->start;
|
|
|
|
|
|
|
|
for (p = pos; !is_eol( parser, p ); p++)
|
|
|
|
{
|
|
|
|
if (*p == '"')
|
|
|
|
{
|
|
|
|
if (p+1 < parser->end && p[1] == '"') /* double quotes */
|
|
|
|
{
|
|
|
|
push_token( parser, p + 1 );
|
|
|
|
parser->start = token_end = p + 2;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
else /* end of quotes */
|
|
|
|
{
|
|
|
|
push_token( parser, p );
|
|
|
|
parser->start = p + 1;
|
|
|
|
pop_state( parser );
|
|
|
|
return p + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
push_token( parser, p );
|
|
|
|
pop_state( parser );
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* handler for parser LEADING_SPACES state */
|
|
|
|
static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos )
|
|
|
|
{
|
|
|
|
const WCHAR *p;
|
|
|
|
|
|
|
|
for (p = pos; !is_eol( parser, p ); p++)
|
|
|
|
{
|
|
|
|
if (*p == '\\')
|
|
|
|
{
|
|
|
|
parser->start = p;
|
|
|
|
set_state( parser, EOL_BACKSLASH );
|
|
|
|
return p;
|
|
|
|
}
|
2010-05-01 10:43:39 +00:00
|
|
|
if (!isspaceW(*p))
|
2010-04-25 15:58:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
parser->start = p;
|
|
|
|
pop_state( parser );
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* handler for parser TRAILING_SPACES state */
|
|
|
|
static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos )
|
|
|
|
{
|
|
|
|
const WCHAR *p;
|
|
|
|
|
|
|
|
for (p = pos; !is_eol( parser, p ); p++)
|
|
|
|
{
|
|
|
|
if (*p == '\\')
|
|
|
|
{
|
|
|
|
set_state( parser, EOL_BACKSLASH );
|
|
|
|
return p;
|
|
|
|
}
|
2010-05-01 10:43:39 +00:00
|
|
|
if (!isspaceW(*p))
|
2010-04-25 15:58:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
pop_state( parser );
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* handler for parser COMMENT state */
|
|
|
|
static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos )
|
|
|
|
{
|
|
|
|
const WCHAR *p = pos;
|
|
|
|
|
|
|
|
while (!is_eol( parser, p ))
|
|
|
|
p++;
|
|
|
|
pop_state( parser );
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* parse a complete buffer */
|
|
|
|
INFSTATUS
|
|
|
|
InfpParseBuffer (PINFCACHE file,
|
|
|
|
const WCHAR *buffer,
|
|
|
|
const WCHAR *end,
|
|
|
|
PULONG error_line)
|
|
|
|
{
|
|
|
|
struct parser parser;
|
|
|
|
const WCHAR *pos = buffer;
|
|
|
|
|
|
|
|
parser.start = buffer;
|
|
|
|
parser.end = end;
|
|
|
|
parser.file = file;
|
|
|
|
parser.line = NULL;
|
|
|
|
parser.state = LINE_START;
|
|
|
|
parser.stack_pos = 0;
|
|
|
|
parser.cur_section = NULL;
|
|
|
|
parser.line_pos = 1;
|
|
|
|
parser.error = 0;
|
|
|
|
parser.token_len = 0;
|
|
|
|
|
|
|
|
/* parser main loop */
|
|
|
|
while (pos)
|
|
|
|
pos = (parser_funcs[parser.state])(&parser, pos);
|
|
|
|
|
|
|
|
if (parser.error)
|
|
|
|
{
|
|
|
|
if (error_line)
|
|
|
|
*error_line = parser.line_pos;
|
|
|
|
return parser.error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find the [strings] section */
|
|
|
|
file->StringsSection = InfpFindSection(file,
|
|
|
|
L"Strings");
|
|
|
|
|
|
|
|
return INF_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* EOF */
|