VBoxAutostartCfg.cpp revision 91b827c98a305956e75cb1618af6ae17e450fb88
/* $Id$ */
/** @file
* VBoxAutostart - VirtualBox Autostart service, configuration parser.
*/
/*
* Copyright (C) 2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "VBoxAutostart.h"
/*******************************************************************************
* Constants And Macros, Structures and Typedefs *
*******************************************************************************/
/**
* Token type.
*/
typedef enum CFGTOKENTYPE
{
/** Invalid token type. */
CFGTOKENTYPE_INVALID = 0,
/** Identifier. */
/** Comma. */
/** Equal sign. */
/** Open curly brackets. */
/** Closing curly brackets. */
/** End of file. */
/** 32bit hack. */
CFGTOKENTYPE_32BIT_HACK = 0x7fffffff
} CFGTOKENTYPE;
/** Pointer to a token type. */
typedef CFGTOKENTYPE *PCFGTOKENTYPE;
/** Pointer to a const token type. */
typedef const CFGTOKENTYPE *PCCFGTOKENTYPE;
/**
* A token.
*/
typedef struct CFGTOKEN
{
/** Type of the token. */
/** Line number of the token. */
unsigned iLine;
/** Starting character of the token in the stream. */
unsigned cchStart;
/** Type dependen token data. */
union
{
/** Data for the ID type. */
struct
{
/** Size of the id in characters, excluding the \0 terminator. */
/** Token data, variable size (given by cchToken member). */
char achToken[1];
} Id;
} u;
} CFGTOKEN;
/** Pointer to a token. */
/** Pointer to a const token. */
typedef const CFGTOKEN *PCCFGTOKEN;
/**
* Tokenizer instance data for the config data.
*/
typedef struct CFGTOKENIZER
{
/** Config file handle. */
/** String buffer for the current line we are operating in. */
char *pszLine;
/** Size of the string buffer. */
/** Current position in the line. */
char *pszLineCurr;
/** Current line in the config file. */
unsigned iLine;
/** Current character of the line. */
unsigned cchCurr;
/** Flag whether the end of the config stream is reached. */
bool fEof;
/** Pointer to the next token in the stream (used to peek). */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Free a config token.
*
* @returns nothing.
* @param pCfgTokenizer The config tokenizer.
* @param pToken The token to free.
*/
{
}
/**
* Reads the next line from the config stream.
*
* @returns VBox status code.
* @param pCfgTokenizer The config tokenizer.
*/
{
int rc = VINF_SUCCESS;
if (pCfgTokenizer->fEof)
return VERR_EOF;
do
{
if (rc == VERR_BUFFER_OVERFLOW)
{
char *pszTmp;
if (pszTmp)
else
rc = VERR_NO_MEMORY;
}
} while (rc == VERR_BUFFER_OVERFLOW);
if ( RT_SUCCESS(rc)
{
pCfgTokenizer->iLine++;
pCfgTokenizer->fEof = true;
}
return rc;
}
/**
* Get the next token from the config stream and create a token structure.
*
* @returns VBox status code.
* @param pCfgTokenizer The config tokenizer data.
* @param pCfgTokenUse Allocated token structure to use or NULL to allocate
* a new one. It will bee freed if an error is encountered.
* @param ppCfgToken Where to store the pointer to the next token on success.
*/
{
size_t cchAdvance = 0;
int rc = VINF_SUCCESS;
for (;;)
{
/* Skip all spaces. */
while (RT_C_IS_BLANK(*pszToken))
{
pszToken++;
cchAdvance++;
}
/* Check if we have to read a new line. */
if ( *pszToken == '\0'
|| *pszToken == '#')
{
{
rc = VINF_SUCCESS;
break;
}
else if (RT_FAILURE(rc))
break;
/* start from the beginning. */
cchAdvance = 0;
}
else if (*pszToken == '=')
{
break;
}
else if (*pszToken == ',')
{
break;
}
else if (*pszToken == '{')
{
break;
}
else if (*pszToken == '}')
{
break;
}
else
{
cchToken = 0;
/* Get the complete token. */
while ( RT_C_IS_ALNUM(*pszTmp)
|| *pszTmp == '_'
|| *pszTmp == '.')
{
pszTmp++;
cchToken++;
}
break;
}
}
if (RT_SUCCESS(rc))
{
/* Free the given token if it is an ID or the current one is an ID token. */
if ( pCfgTokenUse
|| enmType == CFGTOKENTYPE_ID))
{
pCfgTokenUse = NULL;
}
if (!pCfgTokenUse)
{
if (enmType == CFGTOKENTYPE_ID)
if (!pCfgTokenUse)
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS(rc))
{
/* Copy token data. */
if (enmType == CFGTOKENTYPE_ID)
{
}
}
else if (pCfgTokenUse)
if (RT_SUCCESS(rc))
{
/* Set new position in config stream. */
}
}
return rc;
}
/**
* Destroys the given config tokenizer.
*
* @returns nothing.
* @param pCfgTokenizer The config tokenizer to destroy.
*/
{
if (pCfgTokenizer->pszLine)
if (pCfgTokenizer->hStrmConfig)
if (pCfgTokenizer->pTokenNext)
}
/**
* Creates the config tokenizer from the given filename.
*
* @returns VBox status code.
* @param pszFilename Config filename.
* @param ppCfgTokenizer Where to store the pointer to the config tokenizer on
* success.
*/
{
int rc = VINF_SUCCESS;
if (pCfgTokenizer)
{
pCfgTokenizer->iLine = 0;
if (pCfgTokenizer->pszLine)
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
}
}
else
rc = VERR_NO_MEMORY;
}
else
rc = VERR_NO_MEMORY;
if (RT_SUCCESS(rc))
else if ( RT_FAILURE(rc)
&& pCfgTokenizer)
return rc;
}
/**
* Return the next token from the config stream.
*
* @returns VBox status code.
* @param pCfgTokenizer The config tokenizer.
* @param ppCfgToken Where to store the next token.
*/
{
}
{
{
case CFGTOKENTYPE_COMMA:
return ",";
case CFGTOKENTYPE_EQUAL:
return "=";
case CFGTOKENTYPE_CURLY_OPEN:
return "{";
return "}";
case CFGTOKENTYPE_EOF:
return "<EOF>";
case CFGTOKENTYPE_ID:
default:
AssertFailed();
return "<Invalid>";
}
AssertFailed();
return NULL;
}
{
{
case CFGTOKENTYPE_COMMA:
case CFGTOKENTYPE_EQUAL:
case CFGTOKENTYPE_CURLY_OPEN:
return 1;
case CFGTOKENTYPE_EOF:
return 0;
case CFGTOKENTYPE_ID:
default:
AssertFailed();
return 0;
}
AssertFailed();
return 0;
}
{
RTMsgError("Unexpected token '%s' at %d:%d.%d, expected '%s'",
}
/**
* Verfies a token and consumes it.
*
* @returns VBox status code.
* @param pCfgTokenizer The config tokenizer.
* @param pszTokenCheck The token to check for.
*/
static int autostartConfigTokenizerCheckAndConsume(PCFGTOKENIZER pCfgTokenizer, CFGTOKENTYPE enmType)
{
int rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
{
}
}
return rc;
}
/**
* Consumes the next token in the stream.
*
* @returns VBox status code.
* @param pCfgTokenizer Tokenizer instance data.
*/
{
int rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
return rc;
}
/**
* Returns the start of the next token without consuming it.
*
* @returns The next token without consuming it.
* @param pCfgTokenizer Tokenizer instance data.
*/
{
return pCfgTokenizer->pTokenNext;
}
/**
* Check whether the next token is equal to the given one.
*
* @returns true if the next token in the stream is equal to the given one
* false otherwise.
* @param pszToken The token to check for.
*/
DECLINLINE(bool) autostartConfigTokenizerPeekIsEqual(PCFGTOKENIZER pCfgTokenizer, CFGTOKENTYPE enmType)
{
}
/**
* Parse a key value node and returns the AST.
*
* @returns VBox status code.
* @param pCfgTokenizer The tokenizer for the config stream.
* @param pszKey The key for the pair.
* @param ppCfgAst Where to store the resulting AST on success.
*/
{
int rc = VINF_SUCCESS;
if ( RT_SUCCESS(rc)
{
pCfgAst = (PCFGAST)RTMemAllocZ(RT_OFFSETOF(CFGAST, u.KeyValue.aszValue[pToken->u.Id.cchToken + 1]));
if (!pCfgAst)
return VERR_NO_MEMORY;
{
return VERR_NO_MEMORY;
}
}
else
{
}
return rc;
}
/**
* Parses a compound node constructing the AST and returning it on success.
*
* @returns VBox status code.
* @param pCfgTokenizer The tokenizer for the config stream.
* @param pszScopeId The scope ID of the compound node.
* @param ppCfgAst Where to store the resulting AST on success.
*/
{
int rc = VINF_SUCCESS;
unsigned cAstNodesMax = 10;
unsigned idxAstNodeCur = 0;
if (!pCfgAst)
return VERR_NO_MEMORY;
{
return VERR_NO_MEMORY;
}
do
{
break;
if ( RT_SUCCESS(rc)
{
/* Next must be a = token in all cases at this place. */
if (RT_SUCCESS(rc))
{
/* Check whether this is a compound node. */
{
if (RT_SUCCESS(rc))
&pAstNode);
if (RT_SUCCESS(rc))
}
else
&pAstNode);
}
}
else if (RT_SUCCESS(rc))
{
}
/* Add to the current compound node. */
if (RT_SUCCESS(rc))
{
/** @todo: realloc if array is getting to small. */
}
} while (RT_SUCCESS(rc));
if (RT_SUCCESS(rc))
{
}
else
return rc;
}
{
int rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
}
if (pCfgTokenizer)
if (RT_SUCCESS(rc))
return rc;
}
{
{
case CFGASTNODETYPE_KEYVALUE:
{
break;
}
case CFGASTNODETYPE_COMPOUND:
{
break;
}
case CFGASTNODETYPE_LIST:
default:
}
}
{
if (!pCfgAst)
return NULL;
{
return pNode;
}
return NULL;
}