Files
stubser/src/stubser/cfile_parser.c
Martin Winkler 28fb99e4b6 - new directory traversal method
- using debug output from debug.h
2017-03-06 12:43:23 +00:00

653 lines
19 KiB
C

/*!
* @file cfile_parser.c
* @brief
* @details
* Project: \n
* Subsystem: \n
* Module: \n
* Code: GNU-C\n
*
* @date 28.02.2017
* @author SESA354004
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "xregex.h"
#include "xtypes.h"
#include "xstring.h"
#include "xmalloc.h"
#include "cfunction_if.h"
#include "cfile_parser_loc.h"
#include "debug.h"
#define CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS 10
/*! @brief Block detection return values */
typedef enum _CFILE_BLOCK_RETURN_T
{
CFILE_BLOCK_DETECT_ERROR = -1, /*!< @brief description */
CFILE_BLOCK_DETECT_NONE = 0, /*!< @brief description */
CFILE_BLOCK_DETECT_START = 1, /*!< @brief description */
CFILE_BLOCK_DETECT_END = 2, /*!< @brief description */
} cfile_block_return_t;
STATIC bool cfile_parser_initialized = false;
STATIC uint8_t cfile_parser_functionLine = 0;
STATIC cfunction_t *cfile_parser_function = NULL;
STATIC regex_t regX;
STATIC regex_t regXsl;
STATIC regex_t regXmlStart;
STATIC regex_t regXmlPar;
STATIC regex_t regXmlEnd;
STATIC regex_t regXmlProto;
STATIC regex_t regXproto;
STATIC regex_t regXprefix;
STATIC regex_t regXparameter;
int8_t cfile_parser_init()
{
if (0 > regcomp(&regX, FUNCTION_BASE, (REG_EXTENDED | REG_NEWLINE)))
{
perror("Error regex\n");
return -1;
}
if (0 > regcomp(&regXsl, FUNCTION_SL, (REG_EXTENDED | REG_NEWLINE)))
{
perror("Error regex\n");
return -1;
}
if (0 > regcomp(&regXmlStart, FUNCTION_ML_SOF, (REG_EXTENDED | REG_NEWLINE)))
{
perror("Error regex\n");
return -1;
}
if (0 > regcomp(&regXmlPar, FUNCTION_ML_PAR, (REG_EXTENDED | REG_NEWLINE)))
{
perror("Error regex\n");
return -1;
}
if (0 > regcomp(&regXmlEnd, FUNCTION_ML_END, (REG_EXTENDED | REG_NEWLINE)))
{
perror("Error regex\n");
return -1;
}
if (0 > regcomp(&regXmlProto, FUNCTION_ML_PROTO, (REG_EXTENDED | REG_NEWLINE)))
{
perror("Error regex\n");
return -1;
}
if (0 > regcomp(&regXproto, FUNCTION_PROTO, (REG_EXTENDED | REG_NEWLINE)))
{
perror("Error regex\n");
return -1;
}
if (0 > regcomp(&regXprefix, CPARS_REGEX_PREFIX, (REG_EXTENDED)))
{
perror("Error regex\n");
return -1;
}
if (0 > regcomp(&regXparameter, CPARS_REGEX_PARAMETER, (REG_EXTENDED)))
{
perror("Error regex\n");
return -1;
}
cfile_parser_initialized = true;
return 0;
}
#if(0)
STATIC void printMatchGroup(char *aString, regmatch_t *aMatchGroup)
{
char match[1024] =
{ 0};
uint8_t i = 0;
for (i = 0; i < CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS && 0 <= aMatchGroup[i].rm_so; ++i)
{
if (aMatchGroup[i].rm_so < aMatchGroup[i].rm_eo)
{
DEBUG_LOG_APPEND(1, "[%02d](%.3ld - %.3ld)[%03d]:", i, aMatchGroup[i].rm_so, aMatchGroup[i].rm_eo, (int) (aMatchGroup[i].rm_eo - aMatchGroup[i].rm_so));
strlcpy(match, &aString[aMatchGroup[i].rm_so], (size_t) (aMatchGroup[i].rm_eo - aMatchGroup[i].rm_so + 1));
DEBUG_LOG_APPEND(1, "%s", match);
DEBUG_LOG_APPEND(1, "\n");
}
}
}
#endif
STATIC void checkFunctionType(cfunction_t *aFunction)
{
if (NULL == aFunction->prefix)
{
return;
}
if ((NULL != strstr(aFunction->prefix, "static") || NULL != strstr(aFunction->prefix, "STATIC")) //
&& CFUNCTION_TYPE_PROTO != aFunction->type)
{
aFunction->type = CFUNCTION_TYPE_STATIC;
}
}
/*!
* @brief Extract function prefix and data type and removing trailing blanks
* @param [out] *aFunction Pointer to function structure
* @param [in] *aString Pointer to a substring containing data type and/or prefix
* @param [in] aSize Length of string
* @retval 0 processing successful
* @retval -1 processing failed
*/
STATIC int8_t matchFunctionStart(cfunction_t *aFunction, char *aString, size_t aSize)
{
regmatch_t matchGroup[CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS];
char *temp = NULL;
char *end = NULL;
if (NULL == aFunction || NULL == aString || 0 == aSize)
{
return -1;
}
temp = (char*) xmalloc(aSize + 1);
if (NULL == temp)
{
return -1;
}
strlcpy(temp, aString, aSize + 1);
// remove trailing spaces
end = temp + strlen(temp) - 1;
while (end > temp && isspace((uint8_t ) *end))
{
end--;
}
*(end + 1) = '\0';
if (0 == regexec(&regXprefix, temp, CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS, matchGroup, 0))
{
if (XREGEX_IS_MATCHGROUP(matchGroup, 1))
{
xmallocStrlcpy(&aFunction->prefix, &temp[matchGroup[1].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1));
checkFunctionType(aFunction);
}
if (XREGEX_IS_MATCHGROUP(matchGroup, 2))
{
xmallocStrlcpy(&aFunction->dataType, &temp[matchGroup[2].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 2));
}
}
else
{
xmallocStrlcpy(&aFunction->dataType, temp, strlen(temp));
}
free(temp);
return 0;
}
/*!
* @brief Find first given "start" or "end" string and return position
* @param [in] *aBlockStart Terminated string defining the start of a block
* @param [in] *aBlockEnd Terminated string defining the end of a block
* @param [in] *aLine Terminated line (e.g. read from a file getline())
* @param [out] **aPosition Pointing to found block string (realted to \ref aRemove) / NULL for CFILE_BLOCK_DETECT_NONE or CFILE_BLOCK_DETECT_ERROR
* @param [in] aRemove false - remove only content of block; true - also remove block strings
* @return cfile_block_return_t
* @retval CFILE_BLOCK_DETECT_START Start found
* @retval CFILE_BLOCK_DETECT_END End found
* @retval CFILE_BLOCK_DETECT_NONE No start or end found
* @retval CFILE_BLOCK_DETECT_ERROR Null pointer detected
*/
STATIC cfile_block_return_t cBlockDetection(const char *aBlockStart, const char *aBlockEnd, char *aLine, char **aPosition, bool aRemove)
{
char *blockStart = NULL;
char *blockEnd = NULL;
cfile_block_return_t ret = CFILE_BLOCK_DETECT_NONE;
if ( NULL == aBlockStart || NULL == aBlockEnd || NULL == aLine || NULL == aPosition)
{
perror("Null pointer");
return CFILE_BLOCK_DETECT_ERROR;
}
blockStart = strstr(aLine, aBlockStart);
blockEnd = strstr(aLine, aBlockEnd);
if (NULL == blockStart && NULL == blockEnd)
{
// no block element
ret = CFILE_BLOCK_DETECT_NONE;
}
else if (NULL == blockStart)
{
// last line of block
ret = CFILE_BLOCK_DETECT_END;
}
else if (NULL == blockEnd)
{
// first line of block
ret = CFILE_BLOCK_DETECT_START;
}
else if (blockStart < blockEnd)
{
// inline block
ret = CFILE_BLOCK_DETECT_START;
}
else
{
// last line of multi line block with further blocks in line
ret = CFILE_BLOCK_DETECT_END;
}
switch (ret)
{
case CFILE_BLOCK_DETECT_START:
{
*aPosition = (aRemove ? blockStart : blockStart + strlen(aBlockStart));
break;
}
case CFILE_BLOCK_DETECT_END:
{
*aPosition = (aRemove ? blockEnd + strlen(aBlockEnd) : blockEnd);
break;
}
case CFILE_BLOCK_DETECT_NONE:
{
*aPosition = NULL;
break;
}
default:
{
ret = CFILE_BLOCK_DETECT_ERROR;
*aPosition = NULL;
break;
}
}
return ret;
}
/*!
* @brief Remove blocks defined by "start" and "end"
* @attention May be called multiple time (for a whole file). *aHelper must not be changed within the process.
* @param [in] *aLine Terminated line (e.g. read from a file getline())
* @param [in] *aBlockStart Terminated string defining the start of a block
* @param [in] *aBlockEnd Terminated string defining the end of a block
* @param [in,out] *aHelper Storage for block traversal
* @param [in] aRemove false - remove only content of block; true - also remove block strings
* @retval 0 Blocks removed or none found
* @retval 1 Line only within block
* @retval -1 Invalid block ending ("end" without corresponding "start")
* @retval -2 Null pointer detected
*/
STATIC int8_t cBlockRemoval(char *aLine, const char *aBlockStart, const char *aBlockEnd, uint32_t *aHelper, bool aRemove)
{
const uint8_t depth = 1; // preparation to support which depth should be removed for nested block
bool blockLine = false;
int8_t ret = -1;
bool whileRun = true;
char *blockWorker = NULL;
char *blockMatch = NULL;
char *blockStart = NULL;
cfile_block_return_t blockDetected = CFILE_BLOCK_DETECT_NONE;
if (NULL == aLine || NULL == aBlockStart || NULL == aBlockEnd || NULL == aHelper)
{
perror("Null pointer");
return -2;
}
blockWorker = aLine;
if (depth <= *aHelper)
{
blockStart = aLine;
blockLine = true;
}
while (whileRun && CFILE_BLOCK_DETECT_NONE <= (blockDetected = cBlockDetection(aBlockStart, aBlockEnd, blockWorker, &blockMatch, aRemove)))
{
switch (blockDetected)
{
case CFILE_BLOCK_DETECT_START:
{
++*aHelper;
if (NULL == blockStart && depth <= *aHelper)
{
blockStart = blockMatch;
}
blockWorker = (aRemove ? blockMatch + strlen(aBlockStart) : blockMatch);
break;
}
case CFILE_BLOCK_DETECT_END:
{
if (NULL != blockStart)
{
// inline comment
if (depth < *aHelper)
{
// remove block elements within depth + n
blockMatch += (aRemove ? 0 : strlen(aBlockEnd));
}
strlcpy(blockStart, blockMatch, strlen(blockMatch) + 1);
blockMatch = NULL;
if (depth >= *aHelper)
{
// block removed when last end is hit
blockWorker = (aRemove ? blockStart : blockStart + strlen(aBlockEnd));
blockStart = NULL;
}
else
{
blockWorker = blockStart;
}
}
if (depth == *aHelper)
{
blockLine = false;
}
if (0 < *aHelper)
{
--*aHelper;
}
else
{
// error closing brace without beginning
whileRun = false;
ret = -1;
}
break;
}
case CFILE_BLOCK_DETECT_NONE:
{
if (NULL != blockStart && depth <= *aHelper)
{
// lines within a block
strlcpy(blockStart, CPARS_LINE_ENDING, strlen(CPARS_LINE_ENDING) + 1);
}
whileRun = false;
if (blockLine)
{
ret = 1;
}
else
{
ret = 0;
}
break;
}
default:
{
perror("Fatal unknown case");
whileRun = false;
ret = -1;
break;
}
}
}
return ret;
}
/*!
* @brief Remove comments from given line
* @param [in] *aLine terminated string
* @return see \ref cBlockRemoval()
* @retval -2 NULL pointer detected
*/
STATIC int8_t removeCcomments(char *aLine)
{
static uint32_t helper = 0;
char *comment = NULL;
if (NULL == aLine)
{
perror("Null pointer");
return -2;
}
// remove basic inline comments
comment = strstr(aLine, CPARS_COMMENT_INLINE);
if (NULL != comment)
{
strlcpy(comment, CPARS_LINE_ENDING, strlen(CPARS_LINE_ENDING) + 1);
comment = NULL;
}
return cBlockRemoval(aLine, CPARS_COMMENT_BLOCK_START, CPARS_COMMENT_BLOCK_END, &helper, true);
}
STATIC int8_t evaluateParameter(char *aParameter, cfunction_t *aFunction)
{
char *token = NULL;
regmatch_t matchGroup[CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS];
cfunction_parameter_t *tempParameter = NULL;
if (NULL == aParameter || NULL == aFunction)
{
return -1;
}
token = strtok(aParameter, ",");
while (token)
{
if (0 == regexec(&regXparameter, token, CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS, matchGroup, 0))
{
tempParameter = cfunction_newParameter(&aFunction->parameter);
if (XREGEX_IS_MATCHGROUP(matchGroup, 1))
{
xmallocStrlcpy(&tempParameter->type, &token[matchGroup[1].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1));
if (NULL != tempParameter->type)
{
xStringTrim(tempParameter->type, XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1));
}
}
if (XREGEX_IS_MATCHGROUP(matchGroup, 2))
{
xmallocStrlcpy(&tempParameter->name, &token[matchGroup[2].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 2));
if (NULL != tempParameter->name)
{
xStringTrim(tempParameter->name, XREGEX_SIZEOF_MATCHGROUP(matchGroup, 2));
}
}
}
token = strtok(NULL, ",");
}
return 0;
}
/*!
* @brief Process a string (e.g. from getline()) if it is a single or multiple line function definition
* @todo closing parenthesis within parameter list will break the detection
* @retval -1 Invalid regular expressions
* @retval -2 Given String not a function
* @retval 0 function evaluated
*/
STATIC cfunction_t* cfile_parser_evaluateLine(char *aLine)
{
static char *parameterStorage = NULL;
static uint32_t braceHelper = 0;
const size_t maxGroup = CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS;
regmatch_t matchGroup[maxGroup];
bool functionEnd = false;
cfunction_type_t tempFunctionType = CFUNCTION_TYPE_UNDEF;
if (0 != removeCcomments(aLine))
{
return NULL;
}
if (0 != cBlockRemoval(aLine, "{", "}", &braceHelper, false))
{
return NULL;
}
if (cfile_parser_functionLine)
{
if (0 == regexec(&regXmlEnd, aLine, maxGroup, matchGroup, REG_NOTEOL))
{
tempFunctionType = CFUNCTION_TYPE_REGULAR;
DEBUG_LOG_APPEND(1, "[ mle]");
functionEnd = true;
}
else if (0 == regexec(&regXmlProto, aLine, maxGroup, matchGroup, REG_NOTEOL))
{
tempFunctionType = CFUNCTION_TYPE_PROTO;
DEBUG_LOG_APPEND(1, "[ mlp]");
functionEnd = true;
}
else if (0 == regexec(&regXmlPar, aLine, maxGroup, matchGroup, REG_NOTEOL))
{
DEBUG_LOG_APPEND(1, "[ ml+]");
}
else
{
DEBUG_LOG_APPEND(1, "[ na ]");
tempFunctionType = CFUNCTION_TYPE_UNDEF;
functionEnd = true;
}
++cfile_parser_functionLine;
}
else if (0 == regexec(&regXsl, aLine, maxGroup, matchGroup, REG_NOTEOL) && CFILE_PARSER_IS_MATCHGROUP_FUNCTION(matchGroup))
{
tempFunctionType = CFUNCTION_TYPE_REGULAR;
DEBUG_LOG_APPEND(1, "[ sl ]");
}
else if (0 == regexec(&regXmlStart, aLine, maxGroup, matchGroup, REG_NOTEOL) && CFILE_PARSER_IS_MATCHGROUP_FUNCTION(matchGroup))
{
tempFunctionType = CFUNCTION_TYPE_REGULAR;
DEBUG_LOG_APPEND(1, "[ mls]");
++cfile_parser_functionLine;
}
else if (0 == regexec(&regXproto, aLine, maxGroup, matchGroup, REG_NOTEOL) && CFILE_PARSER_IS_MATCHGROUP_FUNCTION(matchGroup))
{
tempFunctionType = CFUNCTION_TYPE_PROTO;
DEBUG_LOG_APPEND(1, "[prot]");
}
else if (0 == regexec(&regX, aLine, maxGroup, matchGroup, REG_NOTEOL) && CFILE_PARSER_IS_MATCHGROUP_FUNCTION(matchGroup))
{
tempFunctionType = CFUNCTION_TYPE_UNDEF;
DEBUG_LOG_APPEND(1, "[ fuX]");
}
else
{
return NULL;
}
#if (0)
{
uint8_t i = 0;
char match[1024] =
{ 0};
for (i = 0; i < maxGroup && 0 <= matchGroup[i].rm_so; ++i)
{
if (matchGroup[i].rm_so < matchGroup[i].rm_eo)
{
if (0 < i)
DEBUG_LOG_APPEND(1, " ");
DEBUG_LOG_APPEND(1, "[%02d](%.3ld - %.3ld)[%03d]: ", i, matchGroup[i].rm_so, matchGroup[i].rm_eo, (int) (matchGroup[i].rm_eo - matchGroup[i].rm_so));
strlcpy(match, &aLine[matchGroup[i].rm_so], (size_t) (matchGroup[i].rm_eo - matchGroup[i].rm_so + 1));
DEBUG_LOG_APPEND(1, "%s", match);
DEBUG_LOG_APPEND(1, "\n");
}
}
if (functionEnd)
{
cfile_parser_functionLine = 0;
}
}
#else
switch (cfile_parser_functionLine)
{
case 0: // single line function definition
case 1: // first line of multi line function definition
{
// TODO multiple functions in one line
cfile_parser_function = cfunction_newFunction();
cfile_parser_function->type = tempFunctionType;
matchFunctionStart(cfile_parser_function, &aLine[matchGroup[1].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1));
xmallocStrlcpy(&cfile_parser_function->name, &aLine[matchGroup[2].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 2));
if (XREGEX_IS_MATCHGROUP(matchGroup, 3))
{
xmallocStrlcat(&parameterStorage, &aLine[matchGroup[3].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 3));
}
break;
}
default: // further lines
{
if (XREGEX_IS_MATCHGROUP(matchGroup, 1))
{
xmallocStrlcat(&parameterStorage, &aLine[matchGroup[1].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1));
}
if (functionEnd)
{
if (CFUNCTION_TYPE_UNDEF == tempFunctionType)
{
cfunction_freeFunction(&cfile_parser_function);
}
else
{
cfile_parser_function->type = tempFunctionType;
checkFunctionType(cfile_parser_function);
}
cfile_parser_functionLine = 0;
}
break;
}
}
// return evaluated function
if (0 == cfile_parser_functionLine)
{
evaluateParameter(parameterStorage, cfile_parser_function);
free(parameterStorage);
parameterStorage = NULL;
cfunction_t *funTemp = cfile_parser_function;
cfile_parser_function = NULL;
return funTemp;
}
#endif
return NULL;
}
int8_t cfile_parser(char *aPath, cfunction_list_t *aList)
{
FILE *fdesc;
ssize_t charRead = 0;
size_t lineSize = 0;
char *fileLine = NULL; // will be allocated by getline(); must be freed
if (false == cfile_parser_initialized)
{
cfile_parser_init();
}
fdesc = fopen(aPath, "r");
if (NULL == fdesc)
{
perror("Error open file");
return -1;
}
do
{
charRead = getline(&fileLine, &lineSize, fdesc);
if (0 <= charRead)
{
cfunction_t *function = NULL;
function = cfile_parser_evaluateLine(fileLine);
if (NULL != function)
{
(void) cfunction_addFunction(aList, function);
}
}
} while (0 <= charRead);
free(fileLine);
(void) fclose(fdesc);
return 0;
}