diff --git a/.cproject b/.cproject index ce988b9..5d41863 100644 --- a/.cproject +++ b/.cproject @@ -14,7 +14,7 @@ - + @@ -67,7 +67,7 @@ - + diff --git a/.project b/.project index f649d94..c8414af 100644 --- a/.project +++ b/.project @@ -1,6 +1,6 @@ - stubi + stubser diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/src/ext/xmalloc.c b/src/ext/xmalloc.c index ea215dc..189327a 100644 --- a/src/ext/xmalloc.c +++ b/src/ext/xmalloc.c @@ -8,7 +8,7 @@ * Code: GNU-C\n * * @date 28.02.2017 - * @author SESA354004 + * @author Martin Winkler */ #include @@ -71,3 +71,9 @@ char* xmallocStrlcat(char **aDest, char *aSource, size_t aSize) return *aDest; } +void xfree(void **aDest) +{ + free(*aDest); + *aDest = NULL; +} + diff --git a/src/ext/xmalloc.h b/src/ext/xmalloc.h index fe5e5c9..54f7380 100644 --- a/src/ext/xmalloc.h +++ b/src/ext/xmalloc.h @@ -8,7 +8,7 @@ * Code: GNU-C\n * * @date 28.02.2017 - * @author SESA354004 + * @author Martin Winkler */ #ifndef EXT_XMALLOC_H_ @@ -17,6 +17,7 @@ #include void *xmalloc(size_t size); +void xfree(void**); char* xmallocStrlcpy(char **aDest, char *aSource, size_t aSize); char* xmallocStrlcat(char **aDest, char *aSource, size_t aSize); diff --git a/src/ext/xregex.h b/src/ext/xregex.h index ac734ec..6d01817 100644 --- a/src/ext/xregex.h +++ b/src/ext/xregex.h @@ -8,7 +8,7 @@ * Code: GNU-C\n * * @date 01.03.2017 - * @author SESA354004 + * @author Martin Winkler */ #ifndef EXT_XREGEX_H_ diff --git a/src/ext/xstring.c b/src/ext/xstring.c index db96d1b..6816054 100644 --- a/src/ext/xstring.c +++ b/src/ext/xstring.c @@ -8,8 +8,9 @@ * Code: GNU-C\n * * @date 27.02.2017 - * @author SESA354004 + * @author Martin Winkler */ +#include "xtypes.h" #include "xstring.h" #include "ctype.h" @@ -24,6 +25,29 @@ char* gnu_basename(char *path) return base ? base + 1 : path; } +uint32_t xStrCount(char *aStr, char aC) +{ + uint32_t count = 0; + char *temp = NULL; + + if (NULL == aStr) + { + return 0; + } + temp = aStr; + + while (*temp) + { + if (aC == *temp) + { + ++count; + } + temp++; + } + + return count; +} + /*! * @brief Trim leading and trailing whitespaces * @param [in] *str Input string diff --git a/src/ext/xstring.h b/src/ext/xstring.h index 324bb76..f3c8919 100644 --- a/src/ext/xstring.h +++ b/src/ext/xstring.h @@ -8,7 +8,7 @@ * Code: GNU-C\n * * @date 27.02.2017 - * @author SESA354004 + * @author Martin Winkler */ #ifndef XSTRING_H_ #define XSTRING_H_ @@ -16,6 +16,7 @@ #include char* gnu_basename(char *path); +uint32_t xStrCount(char *aStr, char aC); char* strntrimStatic(char *aStr, size_t aMaxLength); void xStringTrim(char *aStr, size_t aMaxLength); diff --git a/src/ext/xtime.h b/src/ext/xtime.h index 7725efc..d881a39 100644 --- a/src/ext/xtime.h +++ b/src/ext/xtime.h @@ -8,7 +8,7 @@ * Code: GNU-C\n * * @date 08.06.2016 - * @author SESA354004 + * @author Martin Winkler */ #ifndef XTIME_H_ #define XTIME_H_ diff --git a/src/ext/xtypes.h b/src/ext/xtypes.h index 39cb61b..6c21383 100644 --- a/src/ext/xtypes.h +++ b/src/ext/xtypes.h @@ -8,7 +8,7 @@ * Code: GNU-C\n * * @date 27.02.2017 - * @author SESA354004 + * @author Martin Winkler */ #ifndef XTYPES_H_ diff --git a/src/main.c b/src/main.c index 0fc9bf2..3a132bb 100644 --- a/src/main.c +++ b/src/main.c @@ -8,7 +8,7 @@ * Code: GNU-C\n * * @date 27.02.2017 - * @author SESA354004 + * @author Martin Winkler */ #include #include diff --git a/src/stubser/cfile.c b/src/stubser/cfile.c new file mode 100644 index 0000000..b04ecb3 --- /dev/null +++ b/src/stubser/cfile.c @@ -0,0 +1,99 @@ +/*! + * @file cfile.c + * @brief + * @details + * Project: \n + * Subsystem: \n + * Module: \n + * Code: GNU-C\n + * + * @date 08.03.2017 + * @author Martin Winkler + */ +#include +#include +#include "xtypes.h" +#include "xmalloc.h" +#include "cfile_if.h" + +cfile_variable_t* cfile_newVariable(cfile_variableList_t *aList) +{ + cfile_variable_t *new = NULL; + + if (NULL == aList) + { + perror("Null pointer"); + return NULL; + } + + new = (cfile_variable_t*) xmalloc(sizeof(cfile_variable_t)); + memset(new, 0, sizeof(cfile_variable_t)); + + if (NULL == aList->head) + { + aList->head = new; + aList->current = new; + } + else + { + aList->current->next = new; + aList->current = new; + } + ++aList->amount; + return new; +} + +int8_t cfile_freeVariables(cfile_variableList_t *aVariable) +{ + cfile_variable_t *work = NULL; + cfile_variable_t *next = NULL; + + if (NULL == aVariable) + { + perror("Null pointer"); + return -1; + } + + work = aVariable->head; + + while (work) + { + next = work->next; + free(work->dataType); + free(work->name); + free(work); + work = next; + } + + aVariable->amount = 0; + aVariable->head = NULL; + aVariable->current = NULL; + + return 0; +} + +void cfile_free(cfile_t *aCfile) +{ + cfile_freeVariables(&aCfile->variables); + cfunction_freeList(&aCfile->functions); +} + +void cfile_print(cfile_t *aCfile) +{ + cfile_variable_t *work = NULL; + + if (NULL == aCfile) + { + return; + } + + work = aCfile->variables.head; + + while (work) + { + printf("[ cva]%d:<%s> (%s) %s %s\n", work->type, work->prefix, work->dataType, work->name, work->array); + work = work->next; + } + + cfunction_printList(&aCfile->functions); +} diff --git a/src/stubser/cfile_if.h b/src/stubser/cfile_if.h new file mode 100644 index 0000000..676a565 --- /dev/null +++ b/src/stubser/cfile_if.h @@ -0,0 +1,74 @@ +/*! + * @file cfile_if.h + * @brief + * @details + * Project: \n + * Subsystem: \n + * Module: \n + * Code: GNU-C\n + * + * @date 08.03.2017 + * @author Martin Winkler + */ + +#ifndef STUBSER_CFILE_IF_H_ +#define STUBSER_CFILE_IF_H_ + +#include "cfunction_if.h" + +#define CVARIABLE_CHECK_TYPE(var, type) (type == (var & type)) +#define CVARIABLE_SET_TYPE(var, type) do{\ + var &= ~type; \ + var |= type; \ + } while(0) + +/*! @brief Type of function definition for bit field usage */ +typedef enum _CFILE_VARIABLE_TYPE_T +{ + CVARIABLE_TYPE_UNDEF = 0, /*!< @brief undefined */ + CVARIABLE_TYPE_REGULAR = 0x01, /*!< @brief Regular C variable definition */ + CVARIABLE_TYPE_STATIC = 0x02, /*!< @brief Static C variable definition */ + CVARIABLE_TYPE_EXTERN = 0x04, /*!< @brief External C variable definition */ + CVARIABLE_TYPE_FUPTR = 0x08, /*!< @brief Function pointer as variable */ + CVARIABLE_TYPE_CONST = 0x10, /*!< @brief Const C variable */ + CVARIABLE_TYPE_LAST_ENUM +} cfile_variable_type_t; + +/*! @brief Parameter list node */ +typedef struct _CFILE_VARIABLE_T +{ + cfile_variable_type_t type; + char* prefix; /*!< @brief variable prefix (e.g. static) */ + char* dataType; /*!< @brief data type */ + char* name; /*!< @brief name */ + char* array; /*!< @brief array information (e.g. [10] or [DEF_MAX]) */ + struct _CFILE_VARIABLE_T *next; +} cfile_variable_t; + +/*! @brief parameter array */ +typedef struct _CFILE_VARIABLE_LIST_T +{ + uint8_t amount; + cfile_variable_t *head; + cfile_variable_t *current; +} cfile_variableList_t; + +/*! @brief brief_description */ +typedef struct _CFILE_T +{ + cfile_variableList_t variables; /*! @brief C variables */ + cfunction_list_t functions; /*!< @brief C functions */ +} cfile_t; + +#define CFILE_T_DEFAULT {\ + .variables.amount = 0, \ + .variables.head = NULL, \ + .variables.current = NULL, \ + .functions = CFUNCTION_LIST_DEFAULT } + +cfile_variable_t* cfile_newVariable(cfile_variableList_t *aList); +int8_t cfile_freeVariables(cfile_variableList_t *aVariable); +void cfile_free(cfile_t *aCfile); +void cfile_print(cfile_t *aCfile); + +#endif /* STUBSER_CFILE_IF_H_ */ diff --git a/src/stubser/cfile_parser.c b/src/stubser/cfile_parser.c index 2a3dbf0..391b00f 100644 --- a/src/stubser/cfile_parser.c +++ b/src/stubser/cfile_parser.c @@ -8,7 +8,7 @@ * Code: GNU-C\n * * @date 28.02.2017 - * @author SESA354004 + * @author Martin Winkler */ #include #include @@ -17,12 +17,12 @@ #include "xtypes.h" #include "xstring.h" #include "xmalloc.h" -#include "cfunction_if.h" +#include "cfile_if.h" +#include "cfile_parser_if.h" #include "cfile_parser_loc.h" +#include "cfile_parser_worker_loc.h" #include "debug.h" -#define CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS 10 - /*! @brief Block detection return values */ typedef enum _CFILE_BLOCK_RETURN_T { @@ -33,56 +33,33 @@ typedef enum _CFILE_BLOCK_RETURN_T } 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 regXfunction; STATIC regex_t regXprefix; STATIC regex_t regXparameter; +STATIC regex_t regXfuPtrSeparator; +STATIC regex_t regXfunctionPointer; +STATIC regex_t regXvariable; + +STATIC regex_t regXExpressionStart; +STATIC regex_t regXExpressionEnd; // line evaluation related variables STATIC uint32_t cfile_parser_removeCommentHelper = 0; STATIC uint32_t cfile_parser_removeBraceHelper = 0; -STATIC char *cfile_parser_parameterStorage = NULL; int8_t cfile_parser_init() { - if (0 > regcomp(®X, FUNCTION_BASE, (REG_EXTENDED | REG_NEWLINE))) + if (0 > regcomp(®XExpressionStart, CPARS_EXPRESSION_START, (REG_EXTENDED | REG_NEWLINE))) { perror("Error regex\n"); return -1; } - if (0 > regcomp(®Xsl, FUNCTION_SL, (REG_EXTENDED | REG_NEWLINE))) + if (0 > regcomp(®XExpressionEnd, CPARS_EXPRESSION_END, (REG_EXTENDED | REG_NEWLINE))) { perror("Error regex\n"); return -1; } - if (0 > regcomp(®XmlStart, FUNCTION_ML_SOF, (REG_EXTENDED | REG_NEWLINE))) - { - perror("Error regex\n"); - return -1; - } - if (0 > regcomp(®XmlPar, FUNCTION_ML_PAR, (REG_EXTENDED | REG_NEWLINE))) - { - perror("Error regex\n"); - return -1; - } - if (0 > regcomp(®XmlEnd, FUNCTION_ML_END, (REG_EXTENDED | REG_NEWLINE))) - { - perror("Error regex\n"); - return -1; - } - if (0 > regcomp(®XmlProto, FUNCTION_ML_PROTO, (REG_EXTENDED | REG_NEWLINE))) - { - perror("Error regex\n"); - return -1; - } - if (0 > regcomp(®Xproto, FUNCTION_PROTO, (REG_EXTENDED | REG_NEWLINE))) + if (0 > regcomp(®Xfunction, CPARS_REGEX_FUNCTION, (REG_EXTENDED | REG_NEWLINE))) { perror("Error regex\n"); return -1; @@ -97,12 +74,27 @@ int8_t cfile_parser_init() perror("Error regex\n"); return -1; } + if (0 > regcomp(®XfuPtrSeparator, CPARS_REGEX_PARAMETER_FUPTR_SEPA, (REG_EXTENDED))) + { + perror("Error regex\n"); + return -1; + } + if (0 > regcomp(®XfunctionPointer, CPARS_REGEX_FUNCTIONPOINTER, (REG_EXTENDED))) + { + perror("Error regex\n"); + return -1; + } + if (0 > regcomp(®Xvariable, CPARS_REGEX_VARIABLE, (REG_EXTENDED | REG_NEWLINE))) + { + perror("Error regex\n"); + return -1; + } cfile_parser_initialized = true; return 0; } -#if(0) +#if isDebugLevel(1) STATIC void printMatchGroup(char *aString, regmatch_t *aMatchGroup) { char match[1024] = @@ -113,47 +105,92 @@ STATIC void printMatchGroup(char *aString, regmatch_t *aMatchGroup) { 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)); + 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"); } } } +#else +#define printMatchGroup(aString, aMatchgroup) do { /*nothing*/ } while(0) #endif -STATIC void checkFunctionType(cfunction_t *aFunction) +STATIC void checkFunctionType(cfunction_t *aFunction, char *aExpression) { + char *tempChar = NULL; + + if (NULL == aFunction || NULL == aExpression) + { + return; + } + + aFunction->type = CFUNCTION_TYPE_REGULAR; + + tempChar = strrchr(aExpression, CPARS_PROTOTYPE_END_C); + if (tempChar) + { + aFunction->type = CFUNCTION_TYPE_PROTO; + } + if (NULL == aFunction->prefix) { return; } - if ((NULL != strstr(aFunction->prefix, "static") || NULL != strstr(aFunction->prefix, "STATIC")) // - && CFUNCTION_TYPE_PROTO != aFunction->type) + if (NULL != strstr(aFunction->prefix, CPARS_PREFIX_EXTERN_S)) + { + aFunction->type = CFUNCTION_TYPE_EXTERN; + } + else if ((NULL != strstr(aFunction->prefix, CPARS_PREFIX_STATIC_S) || NULL != strstr(aFunction->prefix, CPARS_PREFIX_STATIC2_S)) + && 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 + * @brief Extract C expression prefix and data type and removing trailing blanks + * @param [out] *aElement Pointer to C element structure + * @param [in] aElementType C element type (CELEMENT_TYPE_VARIABLE, CELEMENT_TYPE_FUNCTION supported) * @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) +STATIC int8_t matchPrefix(void *aElement, celement_type_t aElementType, char *aString, size_t aSize) { regmatch_t matchGroup[CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS]; char *temp = NULL; char *end = NULL; + char **prefix = NULL; + char **type = NULL; - if (NULL == aFunction || NULL == aString || 0 == aSize) + if (NULL == aElement || NULL == aString || 0 == aSize) { return -1; } + switch (aElementType) + { + case CELEMENT_TYPE_VARIABLE: + { + prefix = &((cfile_variable_t*) aElement)->prefix; + type = &((cfile_variable_t*) aElement)->dataType; + break; + } + case CELEMENT_TYPE_FUNCTION: + { + prefix = &((cfunction_t*) aElement)->prefix; + type = &((cfunction_t*) aElement)->dataType; + break; + } + default: + { + return -1; + } + } + temp = (char*) xmalloc(aSize + 1); if (NULL == temp) { @@ -169,21 +206,44 @@ STATIC int8_t matchFunctionStart(cfunction_t *aFunction, char *aString, size_t a } *(end + 1) = '\0'; + if (CELEMENT_TYPE_VARIABLE == aElementType) + { + ((cfile_variable_t*) aElement)->type = CVARIABLE_TYPE_REGULAR; + } + if (0 == regexec(®Xprefix, 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); + xmallocStrlcpy(prefix, &temp[matchGroup[1].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1)); + if (CELEMENT_TYPE_VARIABLE == aElementType) + { + if (*prefix && NULL != strstr(*prefix, CPARS_PREFIX_EXTERN_S)) + { + ((cfile_variable_t*) aElement)->type = CVARIABLE_TYPE_EXTERN; + } + else if (*prefix && (NULL != strstr(*prefix, CPARS_PREFIX_STATIC_S) || NULL != strstr(*prefix, CPARS_PREFIX_STATIC2_S))) + { + ((cfile_variable_t*) aElement)->type = CVARIABLE_TYPE_STATIC; + } + } } if (XREGEX_IS_MATCHGROUP(matchGroup, 2)) { - xmallocStrlcpy(&aFunction->dataType, &temp[matchGroup[2].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 2)); + xmallocStrlcpy(type, &temp[matchGroup[2].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 2)); } } else { - xmallocStrlcpy(&aFunction->dataType, temp, strlen(temp)); + xmallocStrlcpy(type, temp, strlen(temp)); + } + + if (CELEMENT_TYPE_VARIABLE == aElementType) + { + if (*type && NULL != strstr(*type, CPARS_ELEMENT_CONST)) + { + CVARIABLE_SET_TYPE(((cfile_variable_t* ) aElement)->type, CVARIABLE_TYPE_CONST); + } } free(temp); @@ -195,7 +255,7 @@ STATIC int8_t matchFunctionStart(cfunction_t *aFunction, char *aString, size_t a * @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 [out] **aPosition Pointing to found block string (related 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 @@ -328,7 +388,7 @@ STATIC int8_t cBlockRemoval(char *aLine, const char *aBlockStart, const char *aB } else { - printf("[WARN] Nested block start (see file below): %s", aLine); + printf(CPARS_WARNING_START"Nested block start \"%s\": %s", aBlockStart, aLine); } blockWorker = (aRemove ? blockMatch + strlen(aBlockStart) : blockMatch); break; @@ -431,6 +491,42 @@ STATIC int8_t removeCcomments(char *aLine) return cBlockRemoval(aLine, CPARS_COMMENT_BLOCK_START, CPARS_COMMENT_BLOCK_END, &cfile_parser_removeCommentHelper, true, true); } +/*! + * @brief Replace ',' within function pointer parameter with '#' + * @attention must be reverted (\ref revertFunctionPointer) before usage + * @param [in,out] *aParameter String of parameter + * @retval uint32_t Amount of ',' replaced + */ +uint32_t prepareFunctionPointer(char *aParameter) +{ + uint32_t matches = 0; + char *worker = aParameter; + regmatch_t matchGroup[CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS]; + while (0 == regexec(®XfuPtrSeparator, worker, CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS, matchGroup, 0) && XREGEX_IS_MATCHGROUP(matchGroup, 1)) + { + ++matches; + worker[matchGroup[1].rm_so] = '#'; + } + return matches; +} + +/*! + * @brief Revert '#' within function pointer parameter to ',' + * @param [in,out] *aParameter String of parameter + * @retval uint32_t Amount of '#' replaced + */ +uint32_t revertFunctionPointer(char *aParameter) +{ + uint32_t matches = 0; + char *worker = aParameter; + while ((worker = strstr(worker, "#"))) + { + ++matches; + *worker = ','; + } + return matches; +} + STATIC int8_t evaluateParameter(char *aParameter, cfunction_t *aFunction) { char *token = NULL; @@ -441,19 +537,24 @@ STATIC int8_t evaluateParameter(char *aParameter, cfunction_t *aFunction) { return -1; } - + prepareFunctionPointer(aParameter); token = strtok(aParameter, ","); while (token) { - if (0 == regexec(®Xparameter, token, CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS, matchGroup, 0)) + if (0 == regexec(®XfunctionPointer, token, CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS, matchGroup, 0)) { + // TODO only "1 dimensional" function pointer supported (e.g. void (**func)(void) NOT SUPPORTED) + (void) revertFunctionPointer(token); + tempParameter = cfunction_newParameter(&aFunction->parameter); + tempParameter->type = CPARAMETER_TYPE_FUNCPTR; + if (XREGEX_IS_MATCHGROUP(matchGroup, 1)) { - xmallocStrlcpy(&tempParameter->type, &token[matchGroup[1].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1)); - if (NULL != tempParameter->type) + xmallocStrlcpy(&tempParameter->dataType, &token[matchGroup[1].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1)); + if (NULL != tempParameter->dataType) { - xStringTrim(tempParameter->type, XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1)); + xStringTrim(tempParameter->dataType, XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1)); } } if (XREGEX_IS_MATCHGROUP(matchGroup, 2)) @@ -464,172 +565,229 @@ STATIC int8_t evaluateParameter(char *aParameter, cfunction_t *aFunction) xStringTrim(tempParameter->name, XREGEX_SIZEOF_MATCHGROUP(matchGroup, 2)); } } + if (XREGEX_IS_MATCHGROUP(matchGroup, 3)) + { + xmallocStrlcpy(&tempParameter->array, &token[matchGroup[3].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 3)); + if (NULL != tempParameter->array) + { + xStringTrim(tempParameter->array, XREGEX_SIZEOF_MATCHGROUP(matchGroup, 3)); + } + } + + } + else if (0 == regexec(®Xparameter, token, CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS, matchGroup, 0)) + { + tempParameter = cfunction_newParameter(&aFunction->parameter); + tempParameter->type = CPARAMETER_TYPE_REGULAR; + if (XREGEX_IS_MATCHGROUP(matchGroup, 1)) + { + xmallocStrlcpy(&tempParameter->dataType, &token[matchGroup[1].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1)); + if (NULL != tempParameter->dataType) + { + xStringTrim(tempParameter->dataType, 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)); + } + } + if (XREGEX_IS_MATCHGROUP(matchGroup, 3)) + { + xmallocStrlcpy(&tempParameter->array, &token[matchGroup[3].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 3)); + if (NULL != tempParameter->array) + { + xStringTrim(tempParameter->array, XREGEX_SIZEOF_MATCHGROUP(matchGroup, 2)); + } + } } token = strtok(NULL, ","); } + return 0; +} + +STATIC int8_t cfile_parser_evaluateExpression(char *aExpression, cfile_t *aCfile) +{ + uint16_t tempInt16 = 0; + char *tempChar = NULL; + cfile_variable_t *variable = NULL; + cfunction_t *function = NULL; + regmatch_t matchGroup[CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS]; + + if (NULL == aExpression || NULL == aCfile) + { + return -1; + } + + if (0 == regexec(®Xfunction, aExpression, CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS, matchGroup, + REG_NOTEOL) && CFILE_PARSER_IS_MATCHGROUP_FUNCTION(matchGroup)) + { + printMatchGroup(aExpression, matchGroup); + function = cfunction_addNewFunction(&aCfile->functions); + (void) matchPrefix(function, CELEMENT_TYPE_FUNCTION, &aExpression[matchGroup[1].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1)); + checkFunctionType(function, aExpression); + xmallocStrlcpy(&function->name, &aExpression[matchGroup[2].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 2)); + if (XREGEX_IS_MATCHGROUP(matchGroup, 3)) + { + xmallocStrlcat(&tempChar, &aExpression[matchGroup[3].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 3)); + if (NULL != tempChar) + { + evaluateParameter(tempChar, function); + } + xfree((void**) &tempChar); + } + } + else if (0 == regexec(®Xvariable, aExpression, CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS, matchGroup, + REG_NOTEOL) && XREGEX_IS_MATCHGROUP(matchGroup, 1)) + { + printMatchGroup(aExpression, matchGroup); + variable = cfile_newVariable(&aCfile->variables); + if (variable) + { + (void) matchPrefix(variable, CELEMENT_TYPE_VARIABLE, &aExpression[matchGroup[1].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1)); + xmallocStrlcpy(&variable->name, &aExpression[matchGroup[2].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 2)); + + // group 3 contains array information, if available + if (XREGEX_IS_MATCHGROUP(matchGroup, 3)) + { + xmallocStrlcpy(&tempChar, &aExpression[matchGroup[3].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 3)); + if (NULL != tempChar) + { + xStringTrim(tempChar, strlen(tempChar)); + } + + if (2 == strlen(tempChar) - xStrCount(tempChar, ' ')) + { + // array without size definition + xmallocStrlcat(&variable->dataType, "*", 1); + } + else + { + xmallocStrlcpy(&variable->array, tempChar, strlen(tempChar)); + } + xfree((void**) &tempChar); + } + + // would contain more variables separated by ',' + if (XREGEX_IS_MATCHGROUP(matchGroup, 4)) + { + if (NULL != (tempChar = strstr(&aExpression[matchGroup[4].rm_so], ","))) + { + // remove leading , + tempInt16 = tempChar - &aExpression[matchGroup[4].rm_so]; + matchGroup[4].rm_so += tempInt16 + 1; + tempChar = NULL; + + DEBUG_LOG_APPEND(1, "Variable list: %s\n", &aExpression[matchGroup[4].rm_so]); + xmallocStrlcpy(&tempChar, aCfile->variables.head->dataType, strlen(aCfile->variables.head->dataType)); + xmallocStrlcat(&tempChar, &aExpression[matchGroup[4].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 4) + 1); + DEBUG_LOG_APPEND(1, "Result: %s\n", tempChar); + + cfile_parser_evaluateExpression(tempChar, aCfile); + } + } + xfree((void**) &tempChar); + } + } + else if (0 == regexec(®XfunctionPointer, aExpression, CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS, matchGroup, 0) // + && XREGEX_IS_MATCHGROUP(matchGroup, 1) // + && XREGEX_IS_MATCHGROUP(matchGroup, 2) // + && XREGEX_IS_MATCHGROUP(matchGroup, 3)) + { + // TODO only "1 dimensional" function pointer supported (e.g. void (**func)(void) NOT SUPPORTED) + printMatchGroup(aExpression, matchGroup); + variable = cfile_newVariable(&aCfile->variables); + if (variable) + { + (void) matchPrefix(variable, CELEMENT_TYPE_VARIABLE, &aExpression[matchGroup[1].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1)); + CVARIABLE_SET_TYPE(variable->type, CVARIABLE_TYPE_FUPTR); + xmallocStrlcpy(&variable->name, &aExpression[matchGroup[2].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 2)); + // contains array information if available "[10]" + if (XREGEX_IS_MATCHGROUP(matchGroup, 3)) + { + xmallocStrlcpy(&variable->array, &aExpression[matchGroup[3].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 3)); + } + } + } 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 uint32_t cfile_parser_evaluateLine(uint32_t aNumber, char *aLine, cfile_t *aCfile) { - const size_t maxGroup = CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS; - regmatch_t matchGroup[maxGroup]; - bool functionEnd = false; - cfunction_type_t tempFunctionType = CFUNCTION_TYPE_UNDEF; + char *position = aLine; + uint32_t warnings = 0; + static char *match = NULL; + static uint8_t depth = 0; + char *tempMatch = NULL; + char *tempChar = NULL; + int8_t ret = 0; - if (0 != removeCcomments(aLine)) + regex_t *matchStart = ®XExpressionStart; + regex_t *matchEnd = ®XExpressionEnd; + + ret = removeCcomments(aLine); + if (-1 == ret) { - return NULL; + ++warnings; } - if (0 != cBlockRemoval(aLine, "{", "}", &cfile_parser_removeBraceHelper, false, false)) + else if (0 != ret) { - return NULL; + return 0; + } + ret = cBlockRemoval(aLine, "{", "}", &cfile_parser_removeBraceHelper, false, false); + if (-1 == ret) + { + ++warnings; + } + else if (0 != ret) + { + return 0; + } + DEBUG_LOG_APPEND(2, "[ Li %04u] %s\n", aNumber, aLine); + tempChar = strrchr(aLine, '\n'); + if (tempChar) + { + *tempChar = '\0'; + } + tempChar = strrchr(aLine, '\r'); + if (tempChar) + { + *tempChar = '\0'; } - if (cfile_parser_functionLine) + while (position) { - if (0 == regexec(®XmlEnd, aLine, maxGroup, matchGroup, REG_NOTEOL)) + if (NULL != (tempMatch = strstr(position, "#include"))) { - tempFunctionType = CFUNCTION_TYPE_REGULAR; - DEBUG_LOG_APPEND(1, "[ mle]"); - functionEnd = true; + // TODO save include information + DEBUG_LOG_APPEND(1, "[ Re %04u][%zu] %s\n", aNumber, strlen(tempMatch), tempMatch); + position = NULL; + continue; } - else if (0 == regexec(®XmlProto, aLine, maxGroup, matchGroup, REG_NOTEOL)) + warnings += regExWorker(&match, &position, matchStart, matchEnd, &depth, false); + if (match && 0 == depth) { - tempFunctionType = CFUNCTION_TYPE_PROTO; - DEBUG_LOG_APPEND(1, "[ mlp]"); - functionEnd = true; - } - else if (0 == regexec(®XmlPar, 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(®Xsl, aLine, maxGroup, matchGroup, REG_NOTEOL) && CFILE_PARSER_IS_MATCHGROUP_FUNCTION(matchGroup)) - { - tempFunctionType = CFUNCTION_TYPE_REGULAR; - DEBUG_LOG_APPEND(1, "[ sl ]"); - } - else if (0 == regexec(®XmlStart, 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(®Xproto, aLine, maxGroup, matchGroup, REG_NOTEOL) && CFILE_PARSER_IS_MATCHGROUP_FUNCTION(matchGroup)) - { - tempFunctionType = CFUNCTION_TYPE_PROTO; - DEBUG_LOG_APPEND(1, "[prot]"); - } - else if (0 == regexec(®X, 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; + DEBUG_LOG_APPEND(1, "[ Re %04u][%zu] %s\n", aNumber, strlen(match), match); + // Evaluate C expression + (void) cfile_parser_evaluateExpression(match, aCfile); + xfree((void**) &match); } } -#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(&cfile_parser_parameterStorage, &aLine[matchGroup[3].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 3)); - } - break; - } - default: // further lines - { - if (XREGEX_IS_MATCHGROUP(matchGroup, 1)) - { - xmallocStrlcat(&cfile_parser_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(cfile_parser_parameterStorage, cfile_parser_function); - free(cfile_parser_parameterStorage); - cfile_parser_parameterStorage = NULL; - cfunction_t *funTemp = cfile_parser_function; - cfile_parser_function = NULL; - return funTemp; - } -#endif - - return NULL; + return warnings; } -int8_t cfile_parser(char *aPath, cfunction_list_t *aList) +uint32_t cfile_parser(char *aPath, cfile_t *aCfile) { FILE *fdesc; ssize_t charRead = 0; size_t lineSize = 0; + uint32_t lineNumber = 0; char *fileLine = NULL; // will be allocated by getline(); must be freed + uint32_t warningCounter = 0; if (false == cfile_parser_initialized) { @@ -648,36 +806,27 @@ int8_t cfile_parser(char *aPath, cfunction_list_t *aList) 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); - } + lineNumber++; + warningCounter += cfile_parser_evaluateLine(lineNumber, fileLine, aCfile); } } while (0 <= charRead); free(fileLine); (void) fclose(fdesc); - // error detection for block removal +// error detection for block removal if (0 != cfile_parser_removeCommentHelper || 0 != cfile_parser_removeBraceHelper) { - DEBUG_LOG_APPEND(1, "Helper (%d/%d) %s %s\n", cfile_parser_removeCommentHelper, cfile_parser_removeBraceHelper, gnu_basename(aPath), - cfile_parser_parameterStorage); + DEBUG_LOG_APPEND(1, "Helper (%d/%d) %s\n", cfile_parser_removeCommentHelper, cfile_parser_removeBraceHelper, gnu_basename(aPath)); cfile_parser_removeCommentHelper = 0; cfile_parser_removeBraceHelper = 0; - return -2; + ++warningCounter; } - // error detection for function parameter detection - if (NULL != cfile_parser_parameterStorage) - { - DEBUG_LOG_APPEND(1, "Function parameter not free: %s\n", cfile_parser_parameterStorage); - free(cfile_parser_parameterStorage); - cfile_parser_parameterStorage = NULL; - return -3; - } +// if (warningCounter) +// { +// ret = -4; +// } - return 0; + return warningCounter; } diff --git a/src/stubser/cfile_parser_if.h b/src/stubser/cfile_parser_if.h index 0d93cf9..d092ebf 100644 --- a/src/stubser/cfile_parser_if.h +++ b/src/stubser/cfile_parser_if.h @@ -8,12 +8,14 @@ * Code: GNU-C\n * * @date 28.02.2017 - * @author SESA354004 + * @author Martin Winkler */ #ifndef STUBSER_CFILE_PARSER_IF_H_ #define STUBSER_CFILE_PARSER_IF_H_ -int8_t cfile_parser(char *aPath, cfunction_list_t *aList); +#define CPARS_ELEMENT_CONST "const" + +uint32_t cfile_parser(char *aPath, cfile_t *aList); #endif /* STUBSER_CFILE_PARSER_IF_H_ */ diff --git a/src/stubser/cfile_parser_loc.h b/src/stubser/cfile_parser_loc.h index 8620025..467d7d1 100644 --- a/src/stubser/cfile_parser_loc.h +++ b/src/stubser/cfile_parser_loc.h @@ -8,29 +8,49 @@ * Code: GNU-C\n * * @date 28.02.2017 - * @author SESA354004 + * @author Martin Winkler */ - #ifndef STUBSER_CFILE_PARSER_LOC_H_ #define STUBSER_CFILE_PARSER_LOC_H_ +#define CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS 10 + #define CPARS_LINE_ENDING "\r\n" #define CPARS_COMMENT_INLINE "//" #define CPARS_COMMENT_BLOCK_START "/*" #define CPARS_COMMENT_BLOCK_END "*/" -#define FUNCTION_BASE "^[[:blank:]]*([ _\\*[:alnum:]]* +\\**)([_\\*[:alnum:]]*)[[:blank:]]*\\([[:blank:]]*([^\r\\)]*)" -#define FUNCTION_SL FUNCTION_BASE "\\)[^;]*$" -#define FUNCTION_ML_SOF FUNCTION_BASE "[\r]*$" -#define FUNCTION_ML_PAR "^[[:blank:]]*([^\r]*)" -#define FUNCTION_ML_END FUNCTION_ML_PAR "\\)[^;]*$" -#define FUNCTION_ML_PROTO FUNCTION_ML_PAR "\\).*[[:blank:]]*;" -#define FUNCTION_PROTO FUNCTION_BASE "\\).*[[:blank:]]*;" +#define CPARS_PROTOTYPE_END_C ';' +#define CPARS_PREFIX_STATIC_S "static" +#define CPARS_PREFIX_STATIC2_S "STATIC" +#define CPARS_PREFIX_EXTERN_S "extern" -#define CPARS_REGEX_PARAMETER "[[:blank:]]*([ _\\*[:alnum:]]* +\\**)([_\\*[:alnum:]]*)[[:blank:]]*" +#define CPARS_EXPRESSION_BASE "^[[:blank:]]*([ _\\*[:alnum:]]* +\\**)([_\\*[:alnum:]]+)" +#define CPARS_REGEX_FUNCTIONPOINTER "^[[:blank:]]*([ _\\*[:alnum:]]* +\\**)\\(([_\\*[:alnum:]]+)\\)(\\([^\\)]*\\))" -#define CPARS_REGEX_PREFIX "(extern|EXTERN|static|STATIC|volatile|near|far)[[:blank:]]+([^\\*]*\\**)" +#define FUNCTION_BASE CPARS_EXPRESSION_BASE "[[:blank:]]*\\([[:blank:]]*" +#define CPARS_REGEX_FUNCTION FUNCTION_BASE "(.*)\\)[[:blank:]\\{\\};]+[[:blank:]]*" +#define CPARS_REGEX_VARIABLE CPARS_EXPRESSION_BASE "[[:blank:]]*(\\[*[^;=,]*)[[:blank:]]*(\\[*[^;=]*[;=])" +#define CPARS_REGEX_PARAMETER CPARS_EXPRESSION_BASE "[[:blank:]]*(\\[*[^;=]*)" +#define CPARS_REGEX_PARAMETER_FUPTR_SEPA "\\([^\\,)]*(,)" // function pointer parameter separation +#define CPARS_REGEX_PREFIX "("CPARS_PREFIX_EXTERN_S"|EXTERN|"CPARS_PREFIX_STATIC_S"|STATIC|volatile|near|far)[[:blank:]]*(.*)" + +#define CPARS_EXPRESSION_START "^[[:blank:]]*([A-Za-z\\_][\\_\\*[:alnum:]]*)" +#define CPARS_EXPRESSION_END "[[:blank:]]*([\\};]+)" + +#define CPARS_WARNING_START " W-> " #define CFILE_PARSER_IS_MATCHGROUP_FUNCTION(matchGroup) (XREGEX_IS_MATCHGROUP(matchGroup, 1) && XREGEX_IS_MATCHGROUP(matchGroup, 2)) +/*! @brief Type of function definition */ +typedef enum _CELEMENT_TYPE_T +{ + CELEMENT_TYPE_UNDEF = 0, /*!< @brief undefined */ + CELEMENT_TYPE_INCLUDE = 1, /*!< @brief C #include */ + CELEMENT_TYPE_DEFINE = 2, /*!< @brief C #define */ + CELEMENT_TYPE_VARIABLE = 3, /*!< @brief Prototype C variable definition */ + CELEMENT_TYPE_FUNCTION = 4, /*!< @brief Prototype C function definition */ + CELEMENT_TYPE_LAST_ENUM +} celement_type_t; + #endif /* STUBSER_CFILE_PARSER_LOC_H_ */ diff --git a/src/stubser/cfile_parser_worker.c b/src/stubser/cfile_parser_worker.c new file mode 100644 index 0000000..f92917b --- /dev/null +++ b/src/stubser/cfile_parser_worker.c @@ -0,0 +1,152 @@ +/*! + * @file cfile_parser_worker.c + * @brief + * @details + * Project: \n + * Subsystem: \n + * Module: \n + * Code: GNU-C\n + * + * @date 09.03.2017 + * @author Martin Winkler + */ + +#include "xtypes.h" +#include "xregex.h" +#include "xmalloc.h" +#include "debug.h" +#include "cfile_parser_loc.h" +#include "cfile_parser_worker_loc.h" + +/*! + * @brief String block detection using regular expressions + * @attention Recursive function (calls itself to find block end on same line) + * @param [out] **aOutput Matched group content across multiple calls (will be allocated as needed) + * @param [in,out] **aInput Char pointer to search string (will be moved to position after match) + * @param [in] *aStart Regular expression defining block start + * @param [in] *aEnd Regular expression defining block end + * @param [out] *aBlockDepth Current depth of parsing + * @param [in] aMultiLevel false - starts within block are ignored + * @return Matched string and various information about the parsing + * @retval uint32_t amount of warnings during parser run + * + * @details + * - Parsing done when (match && 0 == aBlockDepth) + * - Orphan end markers are ignored and skipped. + */ +uint32_t regExWorker(char **aOutput, char **aInput, const regex_t *aStart, const regex_t *aEnd, uint8_t *aBlockDepth, bool aMultiLevel) +{ + uint32_t warningCounter = 0; + regmatch_t matchStart[2]; + regmatch_t matchEnd[2]; + regmatch_t *match = NULL; + char *begin = NULL; + size_t size = 0; + + if (!**aInput || NULL == *aInput) + { + if (*aBlockDepth) + { + xmallocStrlcat(aOutput, " ", 2); + } + *aInput = NULL; + goto regExWorker_end; + } + + // start + if (0 == regexec(aStart, *aInput, 2, matchStart, REG_NOTEOL) && XREGEX_IS_MATCHGROUP(matchStart, 1)) + { + match = matchStart; + } + + // end < start + if (0 == regexec(aEnd, *aInput, 2, matchEnd, REG_NOTEOL) && XREGEX_IS_MATCHGROUP(matchEnd, 1)) + { + if (match && match->rm_so > matchEnd[1].rm_so) + { + match = matchEnd; + } + else if (!match) + { + match = matchEnd; + } + } + + if (*aBlockDepth) + { + begin = *aInput; + size = strlen(*aInput); + if (!match) + { + // complete line within block + *aInput += strlen(*aInput); + goto regExWorker_end; + } + } + else + { + begin = NULL; + // no findings + if (!match) + { + // skip remaining character within line without match + *aInput += strlen(*aInput); + goto regExWorker_end; + } + } + + if (match == matchStart) + { + if ((*aBlockDepth && aMultiLevel) || 0 == *aBlockDepth) + { + DEBUG_LOG_APPEND(2, "[ CEs]"); + ++*aBlockDepth; + size = XREGEX_SIZEOF_MATCHGROUP(match, 1); + //begin = *aInput + match[1].rm_eo; // don't include start marker in result + begin = *aInput + match[1].rm_so; // include start marker in result + *aInput += match[1].rm_eo; + } + else + { + // ignore starts within block + size = match->rm_eo; + *aInput += match->rm_eo; + } + goto regExWorker_end; + } + + if (match == matchEnd) + { + DEBUG_LOG_APPEND(2, "[ CEe]"); + //size = match[1].rm_so; // don't include end marker in result + size = match[1].rm_eo; // include end marker in result + *aInput += match[1].rm_eo; + if (0 < *aBlockDepth) + { + --*aBlockDepth; + } + // TODO orphan end markers + // must be evaluated depending on expression + // e.g. array[]={}; -> must have } and ; + // function{} -> must not have ; +// else +// { +// // orphan end marker +// ++warningCounter; +// } + goto regExWorker_end; + } + + regExWorker_end: + + if (begin) + { + xmallocStrlcat(aOutput, begin, size); + if (*aBlockDepth) + { + warningCounter += regExWorker(aOutput, aInput, aStart, aEnd, aBlockDepth, aMultiLevel); + } + } + + return warningCounter; +} diff --git a/src/stubser/cfile_parser_worker_loc.h b/src/stubser/cfile_parser_worker_loc.h new file mode 100644 index 0000000..a34bf09 --- /dev/null +++ b/src/stubser/cfile_parser_worker_loc.h @@ -0,0 +1,19 @@ +/*! + * @file cfile_parser_worker_loc.h + * @brief + * @details + * Project: \n + * Subsystem: \n + * Module: \n + * Code: GNU-C\n + * + * @date 09.03.2017 + * @author Martin Winkler + */ + +#ifndef STUBSER_CFILE_PARSER_WORKER_LOC_H_ +#define STUBSER_CFILE_PARSER_WORKER_LOC_H_ + +uint32_t regExWorker(char **aOutput, char **aInput, const regex_t *aStart, const regex_t *aEnd, uint8_t *aBlockDepth, bool aMultiLevel); + +#endif /* STUBSER_CFILE_PARSER_WORKER_LOC_H_ */ diff --git a/src/stubser/cfunction.c b/src/stubser/cfunction.c index 2f21d22..68711d3 100644 --- a/src/stubser/cfunction.c +++ b/src/stubser/cfunction.c @@ -8,7 +8,7 @@ * Code: GNU-C\n * * @date 28.02.2017 - * @author SESA354004 + * @author Martin Winkler */ #include #include @@ -16,39 +16,35 @@ #include "xmalloc.h" #include "cfunction_if.h" -cfunction_t* cfunction_newFunction() +cfunction_t* cfunction_addNewFunction(cfunction_list_t *aList) { cfunction_t *new = NULL; + if (NULL == aList) + { + perror("Null pointer"); + return NULL; + } + new = (cfunction_t*) xmalloc(sizeof(cfunction_t)); memset(new, 0, sizeof(cfunction_t)); - return new; -} - -int8_t cfunction_addFunction(cfunction_list_t *aList, cfunction_t *aNew) -{ - if (NULL == aList || NULL == aNew) - { - perror("Null pointer"); - return -1; - } - if (NULL == aList->head) { - aList->head = aNew; - aList->current = aNew; + aList->amount = 0; + aList->head = new; + aList->current = new; } else { - aList->current->next = aNew; - aList->current = aNew; + aList->current->next = new; + aList->current = new; } - - return 0; + ++aList->amount; + return new; } -cfunction_parameter_t* cfunction_newParameter(struct _CFUNCTION_PARAMETER_LIST_T *aList) +cfunction_parameter_t* cfunction_newParameter(cfunction_parameterList_t *aList) { cfunction_parameter_t *new = NULL; @@ -63,6 +59,7 @@ cfunction_parameter_t* cfunction_newParameter(struct _CFUNCTION_PARAMETER_LIST_T if (NULL == aList->head) { + aList->amount = 0; aList->head = new; aList->current = new; } @@ -91,7 +88,7 @@ STATIC int8_t cfunction_freeParameter(struct _CFUNCTION_PARAMETER_LIST_T *aParam while (work) { next = work->next; - free(work->type); + free(work->dataType); free(work->name); free(work); work = next; @@ -162,14 +159,14 @@ void cfunction_printList(cfunction_list_t *aList) while (work) { param = work->parameter.head; - printf("[ cfu]%d:%s %s %s [%d] ", work->type, work->prefix, work->dataType, work->name, work->parameter.amount); + printf("[ cfu]%d:<%s> (%s) %s [%d] ", work->type, work->prefix, work->dataType, work->name, work->parameter.amount); while (param) { - printf("%s|%s,", param->type, param->name); + printf("%d(%s)%s%s%s", param->type, param->dataType, param->name, (param->array ? param->array : ""), (param->next ? "," : "")); param = param->next; } printf("\n"); work = work->next; - } + printf(" %d Functions\n", aList->amount); } diff --git a/src/stubser/cfunction_if.h b/src/stubser/cfunction_if.h index 2a23859..5403c39 100644 --- a/src/stubser/cfunction_if.h +++ b/src/stubser/cfunction_if.h @@ -8,7 +8,7 @@ * Code: GNU-C\n * * @date 28.02.2017 - * @author SESA354004 + * @author Martin Winkler */ #ifndef STUBSER_CFUNCTION_IF_H_ @@ -18,20 +18,40 @@ typedef enum _CFUNCTION_TYPE_T { CFUNCTION_TYPE_UNDEF = 0, /*!< @brief undefined */ - CFUNCTION_TYPE_REGULAR = 1, /*!< @brief Regular c function definition */ - CFUNCTION_TYPE_STATIC = 2, /*!< @brief Static c function definition */ - CFUNCTION_TYPE_PROTO = 3, /*!< @brief Prototype c function definition */ + CFUNCTION_TYPE_REGULAR = 1, /*!< @brief Regular C function definition */ + CFUNCTION_TYPE_STATIC = 2, /*!< @brief Static C function definition */ + CFUNCTION_TYPE_PROTO = 3, /*!< @brief Prototype C function definition */ + CFUNCTION_TYPE_EXTERN = 4, /*!< @brief External C function definition */ CFUNCTION_TYPE_LAST_ENUM } cfunction_type_t; +/*! @brief Type of function definition */ +typedef enum _CPARAMETER_TYPE_T +{ + CPARAMETER_TYPE_UNDEF = 0, /*!< @brief undefined */ + CPARAMETER_TYPE_REGULAR = 1, /*!< @brief Regular C function parameter */ + CPARAMETER_TYPE_FUNCPTR = 2, /*!< @brief Function pointer as parameter */ + CPARAMETER_TYPE_LAST_ENUM +} cparameter_type_t; + /*! @brief Parameter list node */ typedef struct _CFUNCTION_PARAMETER_T { - char* type; /*!< @brief data type */ + cparameter_type_t type; /*!< @brief Parameter type */ + char* dataType; /*!< @brief data type */ char* name; /*!< @brief name */ + char* array; /*!< @brief array information (e.g. [10] or [DEF_MAX]) */ struct _CFUNCTION_PARAMETER_T *next; } cfunction_parameter_t; +/*! @brief parameter array */ +typedef struct _CFUNCTION_PARAMETER_LIST_T +{ + uint8_t amount; + cfunction_parameter_t *head; + cfunction_parameter_t *current; +} cfunction_parameterList_t; + /*! @brief brief_description */ typedef struct _CFUNCTION_T { @@ -39,29 +59,25 @@ typedef struct _CFUNCTION_T char *prefix; /*!< @brief prefix like static or extern */ char *dataType; /*!< @brief return type */ char *name; /*!< @brief name */ - struct _CFUNCTION_PARAMETER_LIST_T - { - uint8_t amount; - cfunction_parameter_t *head; - cfunction_parameter_t *current; - } parameter;/*!< @brief parameter array */ + cfunction_parameterList_t parameter;/*!< @brief parameter array */ struct _CFUNCTION_T *next; } cfunction_t; /*! @brief Dynamic function list */ typedef struct _CFUNCTION_LIST_T { + uint16_t amount; cfunction_t *head; cfunction_t *current; } cfunction_list_t; #define CFUNCTION_LIST_DEFAULT {\ + .amount = 0, \ .head = NULL, \ .current = NULL } -cfunction_t* cfunction_newFunction(); -int8_t cfunction_addFunction(cfunction_list_t *aList, cfunction_t *aNew); -cfunction_parameter_t* cfunction_newParameter(struct _CFUNCTION_PARAMETER_LIST_T *aList); +cfunction_t* cfunction_addNewFunction(cfunction_list_t *aList); +cfunction_parameter_t* cfunction_newParameter(cfunction_parameterList_t *aList); int8_t cfunction_freeFunction(cfunction_t **aFunction); int8_t cfunction_freeList(cfunction_list_t *aList); void cfunction_printList(cfunction_list_t *aList); diff --git a/src/stubser/stubser.c b/src/stubser/stubser.c index 9a22fe8..325f714 100644 --- a/src/stubser/stubser.c +++ b/src/stubser/stubser.c @@ -10,16 +10,18 @@ * Code: GNU-C\n * * @date 06.03.2017 - * @author SESA354004 + * @author Martin Winkler */ #include #include +#include #include #include +#include #include "xtypes.h" #include "xstring.h" #include "xmalloc.h" -#include "cfunction_if.h" +#include "cfile_if.h" #include "cfile_parser_if.h" #include "stubser_if.h" #include "stubser_loc.h" @@ -37,17 +39,394 @@ STATIC bool isDatatypeVoid(char *aType) return true; } +STATIC bool isDatatypeStandard(char *aDataType) +{ + regex_t regXvariable; + + if (0 > regcomp(®Xvariable, STUB_REGEX_STD_DATATYPE, (REG_EXTENDED))) + { + perror("Error regex\n"); + return false; + } + + if (0 == regexec(®Xvariable, aDataType, 0, NULL, 0)) + { + return true; + } + + return false; +} + +STATIC void addPrintfFormat(FILE *aFile, const char* aType) +{ + if (NULL != strstr(aType, "double")) + { + fprintf(aFile, ("\"%%e\"")); + } + else if (NULL != strstr(aType, "int32_t")) + { + fprintf(aFile, ("\"%%d\"")); + } + else if (NULL != strstr(aType, "32") || NULL != strstr(aType, "long")) + { + fprintf(aFile, ("\"%%ld\"")); + } + else + { + fprintf(aFile, ("\"%%d\"")); + } +} + +STATIC void createVariableSpecificInit(const char* aVariableTemplate, FILE *aFile, cfile_variable_t *aVariable) +{ + char *tempChar = NULL; + + if (NULL != strstr(aVariable->dataType, CPARS_ELEMENT_CONST)) + { + return; + } + + if (NULL != strstr(aVariable->dataType, "*") || aVariable->array) + { + if (CVARIABLE_CHECK_TYPE(aVariable->type, CVARIABLE_TYPE_FUPTR)) + { + // removing all "*" for function pointer initialization + tempChar = strrchr(aVariable->name, '*'); + if (NULL != tempChar) + { + ++tempChar; // skip last '*' + } + else + { + tempChar = aVariable->name; + } + fprintf(aFile, "\t"); + fprintf(aFile, aVariableTemplate, tempChar); + fprintf(aFile, " = NULL;" NEWLINES); + } + else if (aVariable->array) + { + fprintf(aFile, "\t(void) memset("); + fprintf(aFile, aVariableTemplate, aVariable->name); + fprintf(aFile, ", "STUBDEFAULT_VALUE_VARIABLE_S", sizeof(%s)); /* %s */" NEWLINES, aVariable->name, aVariable->dataType); + } + else + { + fprintf(aFile, "\t"); + fprintf(aFile, aVariableTemplate, aVariable->name); + fprintf(aFile, " = NULL;" NEWLINES); + } + + } + else if (isDatatypeStandard(aVariable->dataType)) + { + fprintf(aFile, "\t"); + fprintf(aFile, aVariableTemplate, aVariable->name); + fprintf(aFile, " = "STUBDEFAULT_VALUE_VARIABLE_S";" NEWLINES); + } + else + { + fprintf(aFile, "\t(void) memset(&"); + fprintf(aFile, aVariableTemplate, aVariable->name); + fprintf(aFile, ", "STUBDEFAULT_VALUE_VARIABLE_S", sizeof(%s)); /* %s */" NEWLINES, aVariable->name, aVariable->dataType); + } +} + +STATIC void createVariableSpecificCheck(FILE *aFile, cfile_variable_t *aVariable) +{ + char *tempChar = NULL; + + if (NULL != strstr(aVariable->dataType, "const")) + { + return; + } + + if (NULL != strstr(aVariable->dataType, "*") || aVariable->array) + { + if (CVARIABLE_CHECK_TYPE(aVariable->type, CVARIABLE_TYPE_FUPTR)) + { + // removing all "*" for function pointer ASSERTs + tempChar = strrchr(aVariable->name, '*'); + if (NULL != tempChar) + { + ++tempChar; // skip last '*' + } + else + { + tempChar = aVariable->name; + } + fprintf(aFile, "\tCU_ASSERT_PTR_EQUAL(%s, "STUBVARIABLE_EXPECTED_S1");"NEWLINES, tempChar, tempChar); + } + else if (aVariable->array && !CVARIABLE_CHECK_TYPE(aVariable->type, CVARIABLE_TYPE_FUPTR)) + { + fprintf(aFile, "\tCU_ASSERT_MEMORY_CHECK(\"%s\", %s, "STUBVARIABLE_EXPECTED_S1", sizeof(%s));"NEWLINES, aVariable->name, aVariable->name, + aVariable->name, aVariable->name); + } + else + { + fprintf(aFile, "\tCU_ASSERT_PTR_EQUAL(%s, "STUBVARIABLE_EXPECTED_S1");"NEWLINES, aVariable->name, aVariable->name); + } + + } + else if (isDatatypeStandard(aVariable->dataType)) + { + fprintf(aFile, "\tCU_ASSERT_EQUAL_OUTPUT(%s, "STUBVARIABLE_EXPECTED_S1", ", aVariable->name, aVariable->name); + addPrintfFormat(aFile, aVariable->dataType); + fprintf(aFile, ");" NEWLINES); + } + else + { + fprintf(aFile, "\tCU_ASSERT_MEMORY_CHECK(\"%s\", &%s, &"STUBVARIABLE_EXPECTED_S1", sizeof(%s));"NEWLINES, aVariable->name, aVariable->name, + aVariable->name, aVariable->name); + } +} + +STATIC void createParameterSpecificCheck(FILE *aFile, cfunction_t *aFunction, cfunction_parameter_t *aParameter, char aParameterIndex) +{ + if (CPARAMETER_TYPE_FUNCPTR == aParameter->type) + { + // function pointer + fprintf(aFile, "\tCU_ASSERT_PTR_EQUAL("); + fprintf(aFile, STUBINIT_PARAM_PARAMETER_S1", "STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1, aParameterIndex, aFunction->name, + aFunction->name, aParameterIndex); + fprintf(aFile, ");"NEWLINES); + fprintf(aFile, "\t"STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1" = (%s) "STUBDEFAULT_VALUE_POINTER_S";" NEWLINES, aFunction->name, + aFunction->name, aParameterIndex, aParameter->dataType); + } + else if (NULL != strstr(aParameter->dataType, "*") && NULL != strstr(aParameter->dataType, "char")) + { + // char array with string comparison + fprintf(aFile, "\tif (" // + STUBPARAMETER_POINTERHANDLING_ADDRESS_S " == " STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_POINTERHANDLING_S// + ")" NEWLINES "\t{" NEWLINES, aFunction->name, aFunction->name); + // only assert if result should not be ignored (_size parameter) + fprintf(aFile, "\t\tif ("STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1"_size)" NEWLINES, aFunction->name, aFunction->name, + aParameterIndex); + fprintf(aFile, "\t\t\tCU_ASSERT_MEMORY_CHECK(\"%s(%s)\", ", aFunction->name, aParameter->name); + fprintf(aFile, STUBINIT_PARAM_PARAMETER_S1", "STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1, aParameterIndex, aFunction->name, + aFunction->name, aParameterIndex); + fprintf(aFile, ", strlen("STUBINIT_PARAM_PARAMETER_S1"));"NEWLINES, aParameterIndex); + fprintf(aFile, "\t}" NEWLINES); + // injection only for non constant and non NULL parameter + if (NULL == strstr(aParameter->dataType, "const")) + { + fprintf(aFile, "\telse if (NULL != " STUBINIT_PARAM_PARAMETER_S1 ")" NEWLINES "\t{" NEWLINES, aParameterIndex); + fprintf(aFile, "\t\tmemcpy(" STUBINIT_PARAM_PARAMETER_S1 ", "STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1", " // + STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1"_size",// + aParameterIndex, aFunction->name, aFunction->name, aParameterIndex, aFunction->name, aFunction->name, aParameterIndex); + fprintf(aFile, ");" NEWLINES); + fprintf(aFile, "\t}" NEWLINES); + } + fprintf(aFile, "\t"STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1" = (%s) "STUBDEFAULT_VALUE_POINTER_S";" NEWLINES, aFunction->name, + aFunction->name, aParameterIndex, aParameter->dataType); + fprintf(aFile, "\t"STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1"_size = "STUBDEFAULT_VALUE_POINTERSIZE_S";" NEWLINES, + aFunction->name, aFunction->name, aParameterIndex); + } + else if (NULL != strstr(aParameter->dataType, "*") || NULL != aParameter->array) + { + // pointer + fprintf(aFile, "\tif (" // + STUBPARAMETER_POINTERHANDLING_ADDRESS_S " == " STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_POINTERHANDLING_S// + ")" NEWLINES "\t{" NEWLINES, aFunction->name, aFunction->name); + // only assert if result should not be ignored (_size parameter) + fprintf(aFile, "\t\tif ("STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1"_size)" NEWLINES, aFunction->name, aFunction->name, + aParameterIndex); + fprintf(aFile, "\t\t\tCU_ASSERT_PTR_EQUAL("); + fprintf(aFile, STUBINIT_PARAM_PARAMETER_S1", "STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1, aParameterIndex, aFunction->name, + aFunction->name, aParameterIndex); + fprintf(aFile, ");" NEWLINES); + fprintf(aFile, "\t}" NEWLINES); + // injection only for non constant and non NULL parameter + if (NULL == strstr(aParameter->dataType, "const")) + { + fprintf(aFile, "\telse if (NULL != " STUBINIT_PARAM_PARAMETER_S1 ")" NEWLINES "\t{" NEWLINES, aParameterIndex); + fprintf(aFile, "\t\tmemcpy(" STUBINIT_PARAM_PARAMETER_S1 ", "STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1", " // + STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1"_size",// + aParameterIndex, aFunction->name, aFunction->name, aParameterIndex, aFunction->name, aFunction->name, aParameterIndex); + fprintf(aFile, ");" NEWLINES); + fprintf(aFile, "\t}" NEWLINES); + } + fprintf(aFile, "\t"STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1" = (%s) "STUBDEFAULT_VALUE_POINTER_S";" NEWLINES, aFunction->name, + aFunction->name, aParameterIndex, aParameter->dataType); + fprintf(aFile, "\t"STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1"_size = "STUBDEFAULT_VALUE_POINTERSIZE_S";" NEWLINES, + aFunction->name, aFunction->name, aParameterIndex); + } + else if (isDatatypeStandard(aParameter->dataType)) + { + fprintf(aFile, "\tCU_ASSERT_EQUAL_OUTPUT("); + fprintf(aFile, STUBINIT_PARAM_PARAMETER_S1", "STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1", ", aParameterIndex, aFunction->name, + aFunction->name, aParameterIndex); + addPrintfFormat(aFile, aParameter->dataType); + fprintf(aFile, ");"NEWLINES); + fprintf(aFile, "\t"STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1" = (%s) "STUBDEFAULT_VALUE_VARIABLE_S";" NEWLINES, aFunction->name, + aFunction->name, aParameterIndex, aParameter->dataType); + } + else + { + fprintf(aFile, "\tCU_ASSERT_MEMORY_CHECK(\"%s(%s)\", ", aFunction->name, aParameter->name); + fprintf(aFile, "&"STUBINIT_PARAM_PARAMETER_S1", &"STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1, aParameterIndex, aFunction->name, + aFunction->name, aParameterIndex); + fprintf(aFile, ", sizeof("STUBINIT_PARAM_PARAMETER_S1"));"NEWLINES, aParameterIndex); + fprintf(aFile, "\t"STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1" = (%s) "STUBDEFAULT_VALUE_VARIABLE_S";" NEWLINES, aFunction->name, + aFunction->name, aParameterIndex, aParameter->dataType); + } +} + +STATIC int8_t createStubExpected(char *aNoSuffix, FILE *aFile, FILE *aHeader, cfile_variableList_t *aList) +{ + cfile_variable_t *work = NULL; + if (NULL == aFile || NULL == aHeader || NULL == aList) + { + return -1; + } + + work = aList->head; + while (work) + { + if (!CVARIABLE_CHECK_TYPE(work->type, CVARIABLE_TYPE_REGULAR) || NULL != strstr(work->dataType, "const")) + { + work = work->next; + continue; + } + if (CVARIABLE_CHECK_TYPE(work->type, CVARIABLE_TYPE_FUPTR)) + { + fprintf(aFile, "%s ("STUBVARIABLE_EXPECTED_S1")%s;"NEWLINES, work->dataType, work->name, (work->array ? work->array : "")); + fprintf(aHeader, "extern %s ("STUBVARIABLE_EXPECTED_S1")%s;"NEWLINES, work->dataType, work->name, (work->array ? work->array : "")); + } + else + { + fprintf(aFile, "%s "STUBVARIABLE_EXPECTED_S1"%s;"NEWLINES, work->dataType, work->name, (work->array ? work->array : "")); + fprintf(aHeader, "extern %s "STUBVARIABLE_EXPECTED_S1"%s;"NEWLINES, work->dataType, work->name, (work->array ? work->array : "")); + } + work = work->next; + } + + fprintf(aHeader, "void "STUBFUNCTION_INITEXPECTED_S1"();"NEWLINES NEWLINES, aNoSuffix); + fprintf(aFile, "void "STUBFUNCTION_INITEXPECTED_S1"()"NEWLINES"{"NEWLINES, aNoSuffix); + + work = aList->head; + while (work) + { + if (!CVARIABLE_CHECK_TYPE(work->type, CVARIABLE_TYPE_REGULAR)) + { + work = work->next; + continue; + } + createVariableSpecificInit(STUBVARIABLE_EXPECTED_S1, aFile, work); + work = work->next; + } + fprintf(aFile, "\t"STUBFUNCTION_USER_INITEXPECTED_S1"();"NEWLINES, aNoSuffix); + fprintf(aFile, "}"NEWLINES NEWLINES); + + return 0; +} + +STATIC int8_t createStubGlobals(char *aNoSuffix, FILE *aFile, FILE *aHeader, cfile_variableList_t *aList) +{ + cfile_variable_t *work = NULL; + if (NULL == aFile || NULL == aHeader || NULL == aList) + { + return -1; + } + + work = aList->head; + while (work) + { + if (!CVARIABLE_CHECK_TYPE(work->type, CVARIABLE_TYPE_REGULAR)) + { + work = work->next; + continue; + } + if (CVARIABLE_CHECK_TYPE(work->type, CVARIABLE_TYPE_FUPTR)) + { + fprintf(aFile, "%s (%s)%s;"NEWLINES, work->dataType, work->name, (work->array ? work->array : "")); + fprintf(aHeader, "extern %s (%s)%s;"NEWLINES, work->dataType, work->name, (work->array ? work->array : "")); + } + else + { + fprintf(aFile, "%s %s%s;"NEWLINES, work->dataType, work->name, (work->array ? work->array : "")); + fprintf(aHeader, "extern %s %s%s;"NEWLINES, work->dataType, work->name, (work->array ? work->array : "")); + } + + work = work->next; + } + + fprintf(aFile, "void "STUBFUNCTION_INITGLOBALS_S1"()"NEWLINES"{"NEWLINES, aNoSuffix); + + work = aList->head; + while (work) + { + if (!CVARIABLE_CHECK_TYPE(work->type, CVARIABLE_TYPE_REGULAR)) + { + work = work->next; + continue; + } + createVariableSpecificInit("%s", aFile, work); + work = work->next; + } + fprintf(aFile, "\t"STUBFUNCTION_USER_INITGLOBALS_S1"();"NEWLINES, aNoSuffix); + fprintf(aFile, "}"NEWLINES); + fprintf(aHeader, "void "STUBFUNCTION_INITGLOBALS_S1"();"NEWLINES, aNoSuffix); + fprintf(aHeader, "void "STUBFUNCTION_INIT_S1"();"NEWLINES NEWLINES, aNoSuffix); + + fprintf(aFile, NEWLINES"void "STUBFUNCTION_INIT_S1"()"NEWLINES"{"NEWLINES, aNoSuffix); + fprintf(aFile, "\t"STUBFUNCTION_INITEXPECTED_S1"();"NEWLINES, aNoSuffix); + fprintf(aFile, "\t"STUBFUNCTION_INITGLOBALS_S1"();"NEWLINES, aNoSuffix); + fprintf(aFile, "}"NEWLINES NEWLINES); + return 0; +} + +STATIC int8_t createStubCheck(char *aNoSuffix, FILE *aFile, FILE *aHeader, cfile_variableList_t *aList) +{ + cfile_variable_t *work = NULL; + if (NULL == aFile || NULL == aHeader || NULL == aList) + { + return -1; + } + + fprintf(aHeader, "void "STUBFUNCTION_CHECK_S1"();"NEWLINES NEWLINES, aNoSuffix); + fprintf(aFile, "void "STUBFUNCTION_CHECK_S1"()"NEWLINES"{"NEWLINES, aNoSuffix); + + work = aList->head; + while (work) + { + if (!CVARIABLE_CHECK_TYPE(work->type, CVARIABLE_TYPE_REGULAR)) + { + work = work->next; + continue; + } + createVariableSpecificCheck(aFile, work); + work = work->next; + } + fprintf(aFile, "\t"STUBFUNCTION_USER_CHECK_S1"();"NEWLINES, aNoSuffix); + fprintf(aFile, "}"NEWLINES NEWLINES); + + return 0; +} + STATIC int8_t createStubFunctionBlock(FILE *aFile, FILE *aHeader, cfunction_t *aFunction) { cfunction_parameter_t *parameter = NULL; char parameterIndex = 'a'; + bool pointerAvailable = false; + + parameter = aFunction->parameter.head; + while (parameter) + { + if (NULL != strstr(parameter->dataType, "*") || parameter->array) + { + pointerAvailable = true; + break; + } + parameter = parameter->next; + } if (NULL == aFile || NULL == aHeader || NULL == aFunction) { return -1; } - if (CFUNCTION_TYPE_REGULAR != aFunction->type && CFUNCTION_TYPE_STATIC != aFunction->type) + if (CFUNCTION_TYPE_REGULAR != aFunction->type) { return 1; } @@ -63,34 +442,127 @@ STATIC int8_t createStubFunctionBlock(FILE *aFile, FILE *aHeader, cfunction_t *a fprintf(aFile, "// Expected parameter and return value structures" NEWLINES); fprintf(aFile, "typedef struct _STUBPARAMETER_%s" NEWLINES, aFunction->name); fprintf(aFile, "{" NEWLINES); - fprintf(aFile, "\tuint8_t "STUBPARAMETER_CALLTEST_S"; /**< @brief If stub call is a STUB_CALL_PASS or STUB_CALL_FAIL */" NEWLINES); + fprintf(aFile, "\tuint8_t "STUBPARAMETER_CALLTEST_S"; /*!< @brief Stub call is a STUB_CALL_PASS or STUB_CALL_FAIL */" NEWLINES); + if (pointerAvailable) + { + fprintf(aFile, "\t%s "STUBPARAMETER_POINTERHANDLING_S"; /*!< @brief pointer handling */" NEWLINES, STUBPARAMETER_POINTERHANDLING_ENUM_S); + } + if (!isDatatypeVoid(aFunction->dataType)) + { + fprintf(aFile, "\t%s "STUBPARAMETER_RETURN_S"; /*!< @brief return value */" NEWLINES, aFunction->dataType); + } parameter = aFunction->parameter.head; parameterIndex = 'a'; while (parameter) { - fprintf(aFile, "\t%s "STUBPARAMETER_PARAM_S1"; /*!< @brief %s */" NEWLINES, NCHECK(parameter->type), parameterIndex++, NCHECK(parameter->name)); + if (CPARAMETER_TYPE_FUNCPTR == parameter->type) + { + fprintf(aFile, "\t%s (*"STUBPARAMETER_PARAM_S1")%s; /*!< @brief \"%s\" */" NEWLINES, NCHECK(parameter->dataType), parameterIndex++, + NCHECK(parameter->array), NCHECK(parameter->name)); + } + else if (parameter->array) + { + fprintf(aFile, "\t%s *"STUBPARAMETER_PARAM_S1"; /*!< @brief \"%s\" */" NEWLINES, NCHECK(parameter->dataType), parameterIndex, NCHECK(parameter->name)); + fprintf(aFile, "\tuint32_t "STUBPARAMETER_PARAM_S1"_size; /*!< @brief \"%s\" size to copy in case of parameter injection */" NEWLINES, + parameterIndex++, NCHECK(parameter->name)); + } + else if (NULL != strstr(parameter->dataType, "*")) + { + fprintf(aFile, "\t%s "STUBPARAMETER_PARAM_S1"; /*!< @brief \"%s\" */" NEWLINES, NCHECK(parameter->dataType), parameterIndex, NCHECK(parameter->name)); + fprintf(aFile, "\tuint32_t "STUBPARAMETER_PARAM_S1"_size; /*!< @brief \"%s\" size to copy in case of parameter injection */" NEWLINES, + parameterIndex++, NCHECK(parameter->name)); + } + else if (CPARAMETER_TYPE_REGULAR == parameter->type) + { + fprintf(aFile, "\t%s "STUBPARAMETER_PARAM_S1"; /*!< @brief \"%s\" */" NEWLINES, NCHECK(parameter->dataType), parameterIndex++, + NCHECK(parameter->name)); + } parameter = parameter->next; } - if (!isDatatypeVoid(aFunction->dataType)) - { - fprintf(aFile, "\t%s "STUBPARAMETER_RETURN_S"; /**< @brief return value */" NEWLINES, aFunction->dataType); - } + fprintf(aFile, "} "STUBPARAMETER_TYPEDEF_S1";" NEWLINES, aFunction->name); NEWLINE(aFile); // Instance counter fprintf(aFile, "// Instance counter for stub functions" NEWLINES); - fprintf(aFile, "uint8_t "STUBINSTANCE_S1" = 0;" NEWLINES, aFunction->name); + fprintf(aFile, STUBINSTANCE_DATATYPE_S" "STUBINSTANCE_S1" = 0;" NEWLINES, aFunction->name); NEWLINE(aFile); // expected parameter fprintf(aFile, "// Expected parameter and return value arrays for in-stub tests" NEWLINES); - fprintf(aFile, STUBPARAMETER_TYPEDEF_S1 " " STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_AMOUNT_S1"];" NEWLINES, aFunction->name, aFunction->name, - aFunction->name); + fprintf(aFile, STUBPARAMETER_TYPEDEF_S1 " " STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_AMOUNT_S1"]", aFunction->name, aFunction->name, aFunction->name); + if (pointerAvailable) + { + fprintf(aFile, " = {" NEWLINES); + // pointer address check by default + + parameter = aFunction->parameter.head; + parameterIndex = 'a'; + while (parameter) + { + // initialize _size parameter to 1 (default to check addresses) + if (CPARAMETER_TYPE_FUNCPTR != parameter->type && (parameter->array || NULL != strstr(parameter->dataType, "*"))) + { + fprintf(aFile, "\t[0 ... %u]."STUBPARAMETER_PARAM_S1" = (%s) "STUBDEFAULT_VALUE_POINTER_S"," NEWLINES, STUB_INSTANCES_AMOUNT - 1, parameterIndex, + parameter->dataType); + fprintf(aFile, "\t[0 ... %u]."STUBPARAMETER_PARAM_S1"_size = 1," NEWLINES, STUB_INSTANCES_AMOUNT - 1, parameterIndex); + } + else + { + fprintf(aFile, "\t[0 ... %u]."STUBPARAMETER_PARAM_S1" = (%s) "STUBDEFAULT_VALUE_VARIABLE_S"," NEWLINES, STUB_INSTANCES_AMOUNT - 1, parameterIndex, + parameter->dataType); + } + + ++parameterIndex; + parameter = parameter->next; + } + fprintf(aFile, "};" NEWLINES); + } + else + { + fprintf(aFile, ";" NEWLINES); + } NEWLINE(aFile); - // stub function init header - fprintf(aHeader, "void init_%s(uint8_t "STUBINIT_PARAM_INSTANCE_S", uint8_t "STUBINIT_PARAM_CALLTEST_S, aFunction->name); +// stub function init header; function comment + fprintf(aHeader, "/*! @brief Stub initialization of init_%s" NEWLINES, aFunction->name); + if (pointerAvailable) + { + fprintf(aHeader, + " * @attention In case of address comparison, ..._size parameter = 0 ignores result (@ref " STUBINIT_PARAM_POINTERHANDLING_S ")." NEWLINES); + } + fprintf(aHeader, " * @param " STUBINIT_PARAM_INSTANCE_S " Which stub call to initialize" NEWLINES); + fprintf(aHeader, " * @param " STUBINIT_PARAM_CALLTEST_S " Is stub call allowed (STUB_CALL_FAIL, STUB_CALL_PASS)" NEWLINES); + if (pointerAvailable) + { + fprintf(aHeader, + " * @param " STUBINIT_PARAM_POINTERHANDLING_S " How to handle pointer to data (STUB_PARAMETER_POINTER_HANDLING_ADDRESS, STUB_PARAMETER_POINTER_HANDLING_INJECT" NEWLINES); + } + if (!isDatatypeVoid(aFunction->dataType)) + { + fprintf(aHeader, " * @param " STUBINIT_PARAM_RETURN_S " Return value to inject" NEWLINES); + } + parameter = aFunction->parameter.head; + parameterIndex = 'a'; + while (parameter) + { + if (NULL != strstr(parameter->dataType, "*") || parameter->array) + { + fprintf(aHeader, + " * @param " STUBINIT_PARAM_PARAMETER_S1 "_size 0 to ignore pointer for address comparison and injection (e.g. test subject references to internal variables)" NEWLINES, + parameterIndex); + } + parameterIndex++; + parameter = parameter->next; + } + fprintf(aHeader, " */" NEWLINES); + +// stub function init header + fprintf(aHeader, "void init_%s("STUBINSTANCE_DATATYPE_S " " STUBINIT_PARAM_INSTANCE_S", uint8_t "STUBINIT_PARAM_CALLTEST_S, aFunction->name); + if (pointerAvailable) + { + fprintf(aHeader, ", %s "STUBINIT_PARAM_POINTERHANDLING_S, STUBPARAMETER_POINTERHANDLING_ENUM_S); + } if (!isDatatypeVoid(aFunction->dataType)) { fprintf(aHeader, ", %s "STUBINIT_PARAM_RETURN_S, aFunction->dataType); @@ -99,13 +571,29 @@ STATIC int8_t createStubFunctionBlock(FILE *aFile, FILE *aHeader, cfunction_t *a parameterIndex = 'a'; while (parameter) { - fprintf(aHeader, ", %s "STUBINIT_PARAM_PARAMETER_S1, NCHECK(parameter->type), parameterIndex++); + if (CPARAMETER_TYPE_FUNCPTR == parameter->type) + { + fprintf(aHeader, ", %s (*"STUBINIT_PARAM_PARAMETER_S1")%s", NCHECK(parameter->dataType), parameterIndex++, NCHECK(parameter->array)); + } + else if (NULL != strstr(parameter->dataType, "*") || parameter->array) + { + fprintf(aHeader, ", %s "STUBINIT_PARAM_PARAMETER_S1"%s", NCHECK(parameter->dataType), parameterIndex, NCHECK(parameter->array)); + fprintf(aHeader, ", uint32_t "STUBINIT_PARAM_PARAMETER_S1"_size", parameterIndex++); + } + else + { + fprintf(aHeader, ", %s "STUBINIT_PARAM_PARAMETER_S1"%s", NCHECK(parameter->dataType), parameterIndex++, NCHECK(parameter->array)); + } parameter = parameter->next; } fprintf(aHeader, ");" NEWLINES); // stub function init - fprintf(aFile, "void init_%s(uint8_t "STUBINIT_PARAM_INSTANCE_S", uint8_t "STUBINIT_PARAM_CALLTEST_S, aFunction->name); + fprintf(aFile, "void init_%s("STUBINSTANCE_DATATYPE_S " " STUBINIT_PARAM_INSTANCE_S", uint8_t "STUBINIT_PARAM_CALLTEST_S, aFunction->name); + if (pointerAvailable) + { + fprintf(aFile, ", %s "STUBINIT_PARAM_POINTERHANDLING_S, STUBPARAMETER_POINTERHANDLING_ENUM_S); + } if (!isDatatypeVoid(aFunction->dataType)) { fprintf(aFile, ", %s "STUBINIT_PARAM_RETURN_S, aFunction->dataType); @@ -114,7 +602,19 @@ STATIC int8_t createStubFunctionBlock(FILE *aFile, FILE *aHeader, cfunction_t *a parameterIndex = 'a'; while (parameter) { - fprintf(aFile, ", %s "STUBINIT_PARAM_PARAMETER_S1, NCHECK(parameter->type), parameterIndex++); + if (CPARAMETER_TYPE_FUNCPTR == parameter->type) + { + fprintf(aFile, ", %s (*"STUBINIT_PARAM_PARAMETER_S1")%s", NCHECK(parameter->dataType), parameterIndex++, NCHECK(parameter->array)); + } + else if (NULL != strstr(parameter->dataType, "*") || parameter->array) + { + fprintf(aFile, ", %s "STUBINIT_PARAM_PARAMETER_S1"%s", NCHECK(parameter->dataType), parameterIndex, NCHECK(parameter->array)); + fprintf(aFile, ", uint32_t "STUBINIT_PARAM_PARAMETER_S1"_size", parameterIndex++); + } + else + { + fprintf(aFile, ", %s "STUBINIT_PARAM_PARAMETER_S1"%s", NCHECK(parameter->dataType), parameterIndex++, NCHECK(parameter->array)); + } parameter = parameter->next; } fprintf(aFile, ")" NEWLINES); @@ -123,21 +623,72 @@ STATIC int8_t createStubFunctionBlock(FILE *aFile, FILE *aHeader, cfunction_t *a fprintf(aFile, "\t{" NEWLINES); fprintf(aFile, "\t\tCU_FAIL_FATAL(\"init_%s() - "STUBINIT_PARAM_INSTANCE_S" out of bounce\");" NEWLINES, aFunction->name); fprintf(aFile, "\t}" NEWLINES); + // setting parameter fprintf(aFile, "\t"STUBINSTANCE_S1" = 0;" NEWLINES, aFunction->name); + fprintf(aFile, "\t"STUBPARAMETER_STRUCT_S1"["STUBINIT_PARAM_INSTANCE_S"]."STUBPARAMETER_CALLTEST_S" = "STUBINIT_PARAM_CALLTEST_S";" NEWLINES, + aFunction->name); + + // if STUB_CALL_PASS is requested init with parameter + fprintf(aFile, "\tif ( STUB_CALL_PASS == " STUBINIT_PARAM_CALLTEST_S ")" NEWLINES "\t{" NEWLINES); + if (pointerAvailable) + { + fprintf(aFile, + "\t\t"STUBPARAMETER_STRUCT_S1"["STUBINIT_PARAM_INSTANCE_S"]."STUBPARAMETER_POINTERHANDLING_S" = "STUBINIT_PARAM_POINTERHANDLING_S";" NEWLINES, + aFunction->name); + } if (!isDatatypeVoid(aFunction->dataType)) { - fprintf(aFile, "\t"STUBPARAMETER_STRUCT_S1"["STUBINIT_PARAM_INSTANCE_S"]."STUBPARAMETER_RETURN_S" = "STUBINIT_PARAM_RETURN_S";" NEWLINES, + fprintf(aFile, "\t\t"STUBPARAMETER_STRUCT_S1"["STUBINIT_PARAM_INSTANCE_S"]."STUBPARAMETER_RETURN_S" = "STUBINIT_PARAM_RETURN_S";" NEWLINES, aFunction->name); } parameter = aFunction->parameter.head; parameterIndex = 'a'; while (parameter) { - fprintf(aFile, "\t"STUBPARAMETER_STRUCT_S1"["STUBINIT_PARAM_INSTANCE_S"]."STUBPARAMETER_PARAM_S1" = "STUBINIT_PARAM_PARAMETER_S1";" NEWLINES, + fprintf(aFile, "\t\t"STUBPARAMETER_STRUCT_S1"["STUBINIT_PARAM_INSTANCE_S"]."STUBPARAMETER_PARAM_S1" = "STUBINIT_PARAM_PARAMETER_S1";" NEWLINES, aFunction->name, parameterIndex, parameterIndex); + if ((NULL != strstr(parameter->dataType, "*") || parameter->array) && CPARAMETER_TYPE_FUNCPTR != parameter->type) + { + fprintf(aFile, + "\t\t"STUBPARAMETER_STRUCT_S1"["STUBINIT_PARAM_INSTANCE_S"]."STUBPARAMETER_PARAM_S1"_size = "STUBINIT_PARAM_PARAMETER_S1"_size;" NEWLINES, + aFunction->name, parameterIndex, parameterIndex); + } ++parameterIndex; parameter = parameter->next; } + fprintf(aFile, "\t}" NEWLINES); + // if STUB_CALL_FAIL is requested init with default parameter + fprintf(aFile, "\telse" NEWLINES "\t{" NEWLINES); + if (pointerAvailable) + { + fprintf(aFile, + "\t\t"STUBPARAMETER_STRUCT_S1"["STUBINIT_PARAM_INSTANCE_S"]."STUBPARAMETER_POINTERHANDLING_S" = "STUBPARAMETER_POINTERHANDLING_ADDRESS_S";" NEWLINES, + aFunction->name); + } + parameter = aFunction->parameter.head; + parameterIndex = 'a'; + while (parameter) + { + // initialize _size parameter to 1 (default to check addresses) + if (CPARAMETER_TYPE_FUNCPTR != parameter->type && (parameter->array || NULL != strstr(parameter->dataType, "*"))) + { + fprintf(aFile, "\t\t"STUBPARAMETER_STRUCT_S1"["STUBINIT_PARAM_INSTANCE_S"]."STUBPARAMETER_PARAM_S1" = (%s) "STUBDEFAULT_VALUE_POINTER_S";" NEWLINES, + aFunction->name, parameterIndex, parameter->dataType); + fprintf(aFile, + "\t\t"STUBPARAMETER_STRUCT_S1"["STUBINIT_PARAM_INSTANCE_S"]."STUBPARAMETER_PARAM_S1"_size = "STUBDEFAULT_VALUE_POINTERSIZE_S";" NEWLINES, + aFunction->name, parameterIndex); + } + else + { + fprintf(aFile, "\t\t"STUBPARAMETER_STRUCT_S1"["STUBINIT_PARAM_INSTANCE_S"]."STUBPARAMETER_PARAM_S1" = (%s) "STUBDEFAULT_VALUE_VARIABLE_S";" NEWLINES, + aFunction->name, parameterIndex, parameter->dataType); + } + + ++parameterIndex; + parameter = parameter->next; + } + fprintf(aFile, "\t}" NEWLINES); + fprintf(aFile, "}" NEWLINES); NEWLINE(aFile); @@ -147,7 +698,17 @@ STATIC int8_t createStubFunctionBlock(FILE *aFile, FILE *aHeader, cfunction_t *a parameterIndex = 'a'; while (parameter) { - fprintf(aFile, "%s "STUBINIT_PARAM_PARAMETER_S1"%s", NCHECK(parameter->type), parameterIndex++, (NULL != parameter->next ? ", " : "")); + if (CPARAMETER_TYPE_FUNCPTR == parameter->type) + { + fprintf(aFile, "%s (*"STUBINIT_PARAM_PARAMETER_S1")%s%s", NCHECK(parameter->dataType), parameterIndex++, NCHECK(parameter->array), + (NULL != parameter->next ? ", " : "")); + } + else + { + fprintf(aFile, "%s "STUBINIT_PARAM_PARAMETER_S1"%s%s", NCHECK(parameter->dataType), parameterIndex++, NCHECK(parameter->array), + (NULL != parameter->next ? ", " : "")); + } + parameter = parameter->next; } fprintf(aFile, ")" NEWLINES); @@ -157,26 +718,27 @@ STATIC int8_t createStubFunctionBlock(FILE *aFile, FILE *aHeader, cfunction_t *a fprintf(aFile, "\t\tCU_FAIL(\"Inadvertent %s() call.\");"NEWLINES"\t\tbreak;"NEWLINES, aFunction->name); fprintf(aFile, "\tcase STUB_CALL_PASS:" NEWLINES); fprintf(aFile, "\t\tCU_PASS(\"%s() called.\");"NEWLINES"\t\tbreak;"NEWLINES, aFunction->name); - fprintf(aFile, "\tdefault:"NEWLINES"\t\tbreak;"NEWLINES); + fprintf(aFile, "\tdefault:"NEWLINES); + fprintf(aFile, + "\t\tCU_FAIL_OUTPUT_LESS(\"Invalid STUB_CALL test value: \","STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_CALLTEST_S", \"%%u\");"NEWLINES, + aFunction->name, aFunction->name); + fprintf(aFile, "\t\tbreak;"NEWLINES); fprintf(aFile, "\t}" NEWLINES); + fprintf(aFile, "\t"STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_CALLTEST_S" = STUB_CALL_FAIL;" NEWLINES, aFunction->name, aFunction->name); parameter = aFunction->parameter.head; parameterIndex = 'a'; while (parameter) { - if (NULL != strstr(parameter->type, "*")) - { - fprintf(aFile, "\tCU_ASSERT_PTR_EQUAL("); - } - else - { - fprintf(aFile, "\tCU_ASSERT_EQUAL("); - } - fprintf(aFile, STUBINIT_PARAM_PARAMETER_S1", "STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_PARAM_S1");" NEWLINES, parameterIndex, - aFunction->name, aFunction->name, parameterIndex); + createParameterSpecificCheck(aFile, aFunction, parameter, parameterIndex); ++parameterIndex; parameter = parameter->next; } + if (pointerAvailable) + { + fprintf(aFile, "\t"STUBPARAMETER_STRUCT_S1"["STUBINSTANCE_S1"]."STUBPARAMETER_POINTERHANDLING_S" = " STUBPARAMETER_POINTERHANDLING_ADDRESS_S ";" NEWLINES, + aFunction->name, aFunction->name); + } if (!isDatatypeVoid(aFunction->dataType)) { @@ -194,7 +756,86 @@ STATIC int8_t createStubFunctionBlock(FILE *aFile, FILE *aHeader, cfunction_t *a return 0; } -STATIC int8_t createStub(char *aOutput, char *aNoSuffix, cfunction_list_t *aFunctionList) +/*! + * @brief Check if file exists using stat + * @param [in] *filename Path to file + * @retval 0 file does not exist + * @retval 1 file exists + */ +int file_exist(const char *filename) +{ + struct stat buffer; + return (stat(filename, &buffer) == 0); +} + +STATIC void createUserHeader(char *aOutput, char *aNoSuffix) +{ + FILE *fileDesc = NULL; + char *cHeaderName = NULL; + char *cFileName = NULL; + xmallocStrlcpy(&cHeaderName, aOutput, strlen(aOutput)); + xmallocStrlcat(&cHeaderName, "_user.h", 8); + xmallocStrlcpy(&cFileName, aOutput, strlen(aOutput)); + xmallocStrlcat(&cFileName, "_user.c", 8); + + if (!file_exist(cHeaderName)) + { + // user header doesn't exist + fileDesc = fopen(cHeaderName, "w"); + if (NULL != fileDesc) + { + + fprintf(fileDesc, "/*! @file %s"NEWLINES, gnu_basename(cHeaderName)); + fprintf(fileDesc, " * @details"NEWLINES" * This is a user defined stub header.\\n"NEWLINES); + fprintf(fileDesc, " * Define subject header needed for compilation of stub (e.g. typedefs, structures, ...).\\n"NEWLINES); + fprintf(fileDesc, " * - generated by stubser -"NEWLINES" */" NEWLINES NEWLINES); + + fprintf(fileDesc, "#ifndef _STUB_%s_USER_H"NEWLINES, aNoSuffix); + fprintf(fileDesc, "#define _STUB_%s_USER_H"NEWLINES, aNoSuffix); + + fprintf(fileDesc, NEWLINES"// predefined stub user functions"NEWLINES); + fprintf(fileDesc, "void "STUBFUNCTION_USER_INITEXPECTED_S1"();"NEWLINES, aNoSuffix); + fprintf(fileDesc, "void "STUBFUNCTION_USER_INITGLOBALS_S1"();"NEWLINES, aNoSuffix); + fprintf(fileDesc, "void "STUBFUNCTION_USER_CHECK_S1"();"NEWLINES, aNoSuffix); + + fprintf(fileDesc, NEWLINES"#endif // _STUB_%s_USER_H"NEWLINES, aNoSuffix); + + fclose(fileDesc); + fileDesc = NULL; + } + } + free(cHeaderName); + + if (!file_exist(cFileName)) + { + // user code doesn't exist + fileDesc = fopen(cFileName, "w"); + if (NULL != fileDesc) + { + + fprintf(fileDesc, "/*! @file %s"NEWLINES, gnu_basename(cFileName)); + fprintf(fileDesc, " * @details"NEWLINES" * This is a user defined stub.\\n"NEWLINES); + fprintf(fileDesc, " * Define additional elements which need to be tested, or helper functions.\\n"NEWLINES); + fprintf(fileDesc, " * - generated by stubser -"NEWLINES" */" NEWLINES NEWLINES); + + fprintf(fileDesc, "#include "NEWLINES); + fprintf(fileDesc, "#include \"xasserts.h\""NEWLINES); + fprintf(fileDesc, "#include \"xtypes.h\""NEWLINES); + fprintf(fileDesc, "#include \"stub.h\""NEWLINES); + fprintf(fileDesc, "#include \"stub_%s_user.h\""NEWLINES, aNoSuffix); + + fprintf(fileDesc, "// predefined stub user functions"NEWLINES); + fprintf(fileDesc, "void "STUBFUNCTION_USER_INITEXPECTED_S1"()"NEWLINES"{"NEWLINES"\t"NEWLINES"}"NEWLINES NEWLINES, aNoSuffix); + fprintf(fileDesc, "void "STUBFUNCTION_USER_INITGLOBALS_S1"()"NEWLINES"{"NEWLINES"\t"NEWLINES"}"NEWLINES NEWLINES, aNoSuffix); + fprintf(fileDesc, "void "STUBFUNCTION_USER_CHECK_S1"()"NEWLINES"{"NEWLINES"\t"NEWLINES"}"NEWLINES NEWLINES, aNoSuffix); + + fclose(fileDesc); + } + } + free(cFileName); +} + +STATIC int8_t createStub(char *aOutput, char *aNoSuffix, cfile_t *aCfile) { FILE *cfile; FILE *cheader; @@ -202,7 +843,7 @@ STATIC int8_t createStub(char *aOutput, char *aNoSuffix, cfunction_list_t *aFunc char *cHeaderName = NULL; cfunction_t *function = NULL; - if (NULL == aOutput || NULL == aFunctionList) + if (NULL == aOutput || NULL == aCfile) { return -1; } @@ -231,22 +872,30 @@ STATIC int8_t createStub(char *aOutput, char *aNoSuffix, cfunction_list_t *aFunc return -1; } - fprintf(cfile, "/* @file %s"NEWLINES, gnu_basename(cFileName)); + createUserHeader(aOutput, aNoSuffix); + + fprintf(cfile, "/*! @file %s"NEWLINES, gnu_basename(cFileName)); fprintf(cfile, " * @details"NEWLINES" * This is a stub for CUnit.\\n"NEWLINES); fprintf(cfile, " * - generated by stubser -"NEWLINES" */" NEWLINES NEWLINES); fprintf(cfile, "#include "NEWLINES); + fprintf(cfile, "#include \"xasserts.h\""NEWLINES); fprintf(cfile, "#include \"xtypes.h\""NEWLINES); fprintf(cfile, "#include \"stub.h\""NEWLINES); + fprintf(cfile, "#include \"stub_%s_user.h\""NEWLINES, aNoSuffix); fprintf(cfile, "#include \"%s\""NEWLINES NEWLINES, gnu_basename(cHeaderName)); - fprintf(cheader, "/* @file %s"NEWLINES, gnu_basename(cHeaderName)); + fprintf(cheader, "/*! @file %s"NEWLINES, gnu_basename(cHeaderName)); fprintf(cheader, " * @details"NEWLINES" * This is a stub header.\\n"NEWLINES); fprintf(cheader, " * - generated by stubser -"NEWLINES" */" NEWLINES NEWLINES); fprintf(cheader, "#ifndef _STUB_%s_H"NEWLINES, aNoSuffix); fprintf(cheader, "#define _STUB_%s_H"NEWLINES NEWLINES, aNoSuffix); - function = aFunctionList->head; + createStubExpected(aNoSuffix, cfile, cheader, &aCfile->variables); + createStubGlobals(aNoSuffix, cfile, cheader, &aCfile->variables); + createStubCheck(aNoSuffix, cfile, cheader, &aCfile->variables); + + function = aCfile->functions.head; while (function) { (void) createStubFunctionBlock(cfile, cheader, function); @@ -271,38 +920,37 @@ STATIC int8_t createStub(char *aOutput, char *aNoSuffix, cfunction_list_t *aFunc int stubser_createStub(char *path, char* aNoSuffix, char *aOutput) { int8_t ret = -1; - cfunction_list_t functionList = - CFUNCTION_LIST_DEFAULT; + cfile_t cFile = + CFILE_T_DEFAULT; + uint32_t warnings = 0; char fileName[PATH_MAX] = { '\0' }; - ret = cfile_parser(path, &functionList); +#if isDebugLevel(1) + printf(STUB_CONSOLE_PARSE_RUN_S1, path); +#else + printf(STUB_CONSOLE_PARSE_RUN_S1, gnu_basename(path)); +#endif + warnings = cfile_parser(path, &cFile); DEBUG_LOG_APPEND(1, "\n"); - if (0 != ret) + if (0 != warnings) { #if isDebugLevel(1) - printf(STUB_CONSOLE_PARSE_ERROR_S2, path, ret); + printf(STUB_CONSOLE_PARSE_ERROR_S2, path, warnings); #else - printf(STUB_CONSOLE_PARSE_ERROR_S2, gnu_basename(path), ret); + printf(STUB_CONSOLE_PARSE_ERROR_S2, gnu_basename(path), warnings); #endif - if (-2 == ret) - { - printf(" Block removal error (likely nested comments).\n"); - } - else if (-3 == ret) - { - printf(" Function parameter detection error\n"); - } - ret = -1; - goto end_stubser_createStub; } + else + { #if isDebugLevel(1) - printf(STUB_CONSOLE_PARSE_OK_S1, path); + printf(STUB_CONSOLE_PARSE_OK_S1, path); #else - printf(STUB_CONSOLE_PARSE_OK_S1, gnu_basename(path)); + printf(STUB_CONSOLE_PARSE_OK_S1, gnu_basename(path)); #endif + } sprintf(fileName, "%s/stub_%s", aOutput, aNoSuffix); - ret = createStub(fileName, aNoSuffix, &functionList); + ret = createStub(fileName, aNoSuffix, &cFile); if (0 != ret) { printf(STUB_CONSOLE_CREATE_ERROR_S2, gnu_basename(path), ret); @@ -311,9 +959,13 @@ int stubser_createStub(char *path, char* aNoSuffix, char *aOutput) } printf(STUB_CONSOLE_CREATE_OK_S1, fileName); +#if isDebugLevel(1) + cfile_print(&cFile); +#endif + end_stubser_createStub: // function end target - cfunction_freeList(&functionList); + cfile_free(&cFile); return ret; } diff --git a/src/stubser/stubser_if.h b/src/stubser/stubser_if.h index 0234ab8..d8e3ccf 100644 --- a/src/stubser/stubser_if.h +++ b/src/stubser/stubser_if.h @@ -8,7 +8,7 @@ * Code: GNU-C\n * * @date 27.02.2017 - * @author SESA354004 + * @author Martin Winkler */ #ifndef STUBSER_STUBSER_IF_H_ diff --git a/src/stubser/stubser_loc.h b/src/stubser/stubser_loc.h index 78a77e4..bb1731f 100644 --- a/src/stubser/stubser_loc.h +++ b/src/stubser/stubser_loc.h @@ -8,7 +8,7 @@ * Code: GNU-C\n * * @date 04.03.2017 - * @author SESA354004 + * @author Martin Winkler */ #ifndef STUBSER_STUBSER_LOC_H_ @@ -31,17 +31,44 @@ #define STUBPARAMETER_STRUCT_S1 "stubparameter_%s" // stubparameter_modulfunction_1 #define STUBPARAMETER_PARAM_S1 "parameter_%c" // "stubparameter_modulfunction_1".parameter_a #define STUBPARAMETER_CALLTEST_S "stubCallTest" +#define STUBPARAMETER_POINTERHANDLING_S "stubPointerHandling" +#define STUBPARAMETER_POINTERHANDLING_ENUM_S "stub_parameter_pointer_handling_t" +#define STUBPARAMETER_POINTERHANDLING_ADDRESS_S "STUB_PARAMETER_POINTER_HANDLING_ADDRESS" +#define STUBPARAMETER_POINTERHANDLING_INJECT_S "STUB_PARAMETER_POINTER_HANDLING_INJECT" #define STUBPARAMETER_RETURN_S "returnValue" // "stubparameter_modulfunction_1".returnValue #define STUBINSTANCE_S1 "stubInstance_%s" // stubInstance_modulfunction_1 +#define STUBINSTANCE_DATATYPE_S "uint8_t" #define STUBINIT_PARAM_INSTANCE_S "aInstance" #define STUBINIT_PARAM_CALLTEST_S "aStubCallTest" #define STUBINIT_PARAM_PARAMETER_S1 "aParameter_%c" // aParameter_a #define STUBINIT_PARAM_RETURN_S "aReturnValue" +#define STUBINIT_PARAM_POINTERHANDLING_S "aPointerHandling" +#define STUBDEFAULT_VALUE_FUNCTIONPOINTER_S "0xAA" +#define STUBDEFAULT_VALUE_POINTER_S "0xAA" +#define STUBDEFAULT_VALUE_POINTERSIZE_S "1" +#define STUBDEFAULT_VALUE_VARIABLE_S "0xAA" + +#define STUBFUNCTION_USER_INITEXPECTED_S1 "stub_%s_user_initExpected" // stub_testfunction_initExpected +#define STUBFUNCTION_USER_INITGLOBALS_S1 "stub_%s_user_initGlobals" // stub_testfunction_initGlobals +#define STUBFUNCTION_USER_CHECK_S1 "stub_%s_user_checkGlobals" // stub_testfunction_checkGlobals + +#define STUBFUNCTION_INITEXPECTED_S1 "stub_%s_initExpected" // stub_testfunction_initExpected +#define STUBFUNCTION_INITGLOBALS_S1 "stub_%s_initGlobals" // stub_testfunction_initGlobals +#define STUBFUNCTION_CHECK_S1 "stub_%s_checkGlobals" // stub_testfunction_checkGlobals +#define STUBFUNCTION_INIT_S1 "stub_%s_init" // stub_testfunction_init +#define STUBVARIABLE_EXPECTED_S1 "%s_expected" // variable_expected + +#define STUB_CONSOLE_GOBACK "\033[2K\r" +#define STUB_CONSOLE_RUN "[ ]" #define STUB_CONSOLE_OK "[ OK ]" +#define STUB_CONSOLE_WARN "[WARN]" #define STUB_CONSOLE_FAIL "[FAIL]" -#define STUB_CONSOLE_PARSE_ERROR_S2 STUB_CONSOLE_FAIL " %s parsing failed (%d)\n" -#define STUB_CONSOLE_PARSE_OK_S1 STUB_CONSOLE_OK " %s parsing..." -#define STUB_CONSOLE_CREATE_ERROR_S2 "fail\n"STUB_CONSOLE_FAIL " %s creation failed (%d)\n" -#define STUB_CONSOLE_CREATE_OK_S1 "ok > %s\n" +#define STUB_CONSOLE_PARSE_ERROR_S2 "\n" STUB_CONSOLE_WARN " %s parsing with %d warnings (check output manually)" +#define STUB_CONSOLE_PARSE_RUN_S1 STUB_CONSOLE_RUN " %s parsing..." +#define STUB_CONSOLE_PARSE_OK_S1 STUB_CONSOLE_GOBACK STUB_CONSOLE_OK " %s parsing..." +#define STUB_CONSOLE_CREATE_ERROR_S2 "...stub creation failed\n"STUB_CONSOLE_FAIL " %s creation failed (%d)\n" +#define STUB_CONSOLE_CREATE_OK_S1 "...stub created > %s\n" + +#define STUB_REGEX_STD_DATATYPE "(^|[^_[:alnum:]])([ui]+nt[0-9]{1,2}_t|char|uchar|word[12346]{2}|int[12346]{0,2}|byte)([^_[:alnum:]]|$)" #endif /* STUBSER_STUBSER_LOC_H_ */