/*! * @file cfile_parser.c * @brief * @details * Project: \n * Subsystem: \n * Module: \n * Code: GNU-C\n * * @date 28.02.2017 * @author SESA354004 */ #include #include #include #include "xregex.h" #include "xtypes.h" #include "xstring.h" #include "xmalloc.h" #include "cfile_if.h" #include "cfile_parser_if.h" #include "cfile_parser_loc.h" #include "cfile_parser_worker_loc.h" #include "debug.h" /*! @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 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; int8_t cfile_parser_init() { if (0 > regcomp(®XExpressionStart, CPARS_EXPRESSION_START, (REG_EXTENDED | REG_NEWLINE))) { perror("Error regex\n"); return -1; } if (0 > regcomp(®XExpressionEnd, CPARS_EXPRESSION_END, (REG_EXTENDED | REG_NEWLINE))) { perror("Error regex\n"); return -1; } if (0 > regcomp(®Xfunction, CPARS_REGEX_FUNCTION, (REG_EXTENDED | REG_NEWLINE))) { perror("Error regex\n"); return -1; } if (0 > regcomp(®Xprefix, CPARS_REGEX_PREFIX, (REG_EXTENDED))) { perror("Error regex\n"); return -1; } if (0 > regcomp(®Xparameter, CPARS_REGEX_PARAMETER, (REG_EXTENDED))) { 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 isDebugLevel(1) 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"); } } } #else #define printMatchGroup(aString, aMatchgroup) do { /*nothing*/ } while(0) #endif 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, 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 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 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 == 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) { 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 (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(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(type, &temp[matchGroup[2].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 2)); } } else { 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); 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, bool aIgnore) { 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: { // additional starts within depth are ignored if aIgnore is true // e.g. C comments are not allowed to be nested if (!(aIgnore && depth <= *aHelper)) { ++*aHelper; if (NULL == blockStart && depth <= *aHelper) { blockStart = blockMatch; } } else { printf(CPARS_WARNING_START"Nested block start \"%s\": %s", aBlockStart, aLine); } 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) { 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, &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; regmatch_t matchGroup[CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS]; cfunction_parameter_t *tempParameter = NULL; if (NULL == aParameter || NULL == aFunction) { return -1; } prepareFunctionPointer(aParameter); token = strtok(aParameter, ","); while (token) { 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->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, 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) { 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)); // contains array information if available "[10]" 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); } } } 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; } STATIC uint32_t cfile_parser_evaluateLine(uint32_t aNumber, char *aLine, cfile_t *aCfile) { 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; regex_t *matchStart = ®XExpressionStart; regex_t *matchEnd = ®XExpressionEnd; ret = removeCcomments(aLine); if (-1 == ret) { ++warnings; } else if (0 != ret) { 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", aNumber, aLine); tempChar = strrchr(aLine, '\n'); if (tempChar) { *tempChar = '\0'; } tempChar = strrchr(aLine, '\r'); if (tempChar) { *tempChar = '\0'; } while (position) { if (NULL != (tempMatch = strstr(position, "#include"))) { // TODO save include information DEBUG_LOG_APPEND(1, "[ Re %04u][%zu] %s\n", aNumber, strlen(tempMatch), tempMatch); position = NULL; continue; } warnings += regExWorker(&match, &position, matchStart, matchEnd, &depth, false); if (match && 0 == depth) { 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); } } return warnings; } 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) { 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) { lineNumber++; warningCounter += cfile_parser_evaluateLine(lineNumber, fileLine, aCfile); } } while (0 <= charRead); free(fileLine); (void) fclose(fdesc); // error detection for block removal if (0 != cfile_parser_removeCommentHelper || 0 != cfile_parser_removeBraceHelper) { 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; ++warningCounter; } // if (warningCounter) // { // ret = -4; // } return warningCounter; }