From 28fb99e4b65985c15334aaae48434b2955999d28 Mon Sep 17 00:00:00 2001 From: Martin Winkler Date: Mon, 6 Mar 2017 12:43:23 +0000 Subject: [PATCH] - new directory traversal method - using debug output from debug.h --- .cproject | 6 + src/ext/debug.h | 107 ++++++++++++++++++ src/main.c | 220 +++++++++++++++++++++++++++++-------- src/stubser/cfile_parser.c | 42 +++---- src/stubser/stubser.c | 43 ++++---- src/stubser/stubser_if.h | 2 +- src/stubser/stubser_loc.h | 2 +- 7 files changed, 335 insertions(+), 87 deletions(-) create mode 100644 src/ext/debug.h diff --git a/.cproject b/.cproject index 22dd8e5..d248e31 100644 --- a/.cproject +++ b/.cproject @@ -33,6 +33,9 @@ + @@ -80,6 +83,9 @@ diff --git a/src/ext/debug.h b/src/ext/debug.h new file mode 100644 index 0000000..7ba8258 --- /dev/null +++ b/src/ext/debug.h @@ -0,0 +1,107 @@ +/* + -------------------------------------------------------------------- + Project : Systemsoftware MiCOM 30 + Subsystem : + Modul : + -------------------------------------------------------------------- + Creation date : 03.02.2012 + Last change : + Version : under clearcase version control + Author : Seliger + Prog. Language : GNU-C + ------------------------------------------------------------------ + Description: + Some stuff to control debug messages + + Definition _DEBUG_OUTPUT must be set for compilation + + Activate debug messages with command line option -v + -v = DEBUG_LEVEL_INFO + -vv = DEBUG_LEVEL_WARNING + -vvv = DEBUG_LEVEL_ERROR + -------------------------------------------------------------------- + */ + +/* + -------------------------------------------------------------------- + Change log: + Seliger First version + 10.01.2012 + -------------------------------------------------------------------- + */ +#ifndef _DEBUG_H_INCLUDED +#define _DEBUG_H_INCLUDED + +#include +#include + +#define isDebugLevel(dflag) (_DEBUG_OUTPUT >= dflag) + +/*! + * @brief extract only filename from __FILE__ without base path + */ +#define _DEBUG_FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) + +/*! + * @brief Debug assert helper macro + * @param cond condition for output + * @param dflag eDebugLevel required to print output + * @param format format same as for printf + * @param args arguments for printf + * + * Condition as string is appended to the output.\n + * e.g. DEBUG_LOG_ASSERT(i >= 1, DEBUG_LEVEL_INFO, "Not allowed");\n + * New line must be appended manually + */ +#ifdef _DEBUG_OUTPUT +#define DEBUG_LOG_ASSERT(cond, dflag, format, args...) do { if(isDebugLevel(dflag) && (cond)) \ + printf("%s:%d:%s():(" #cond "): " format, \ + _DEBUG_FILENAME, __LINE__, __func__, ##args); } \ + while (0) +#else +#define DEBUG_LOG_ASSERT(cond, dflag, format, args...) do{/* nothing */}while(0) +#endif + +/*! + * @brief Debug output helper macro + * @param dflag eDebugLevel required to print output + * @param format format same as for printf + * @param args arguments for printf + * + * New line must be appended manually + */ +#ifdef _DEBUG_OUTPUT +#define DEBUG_LOG(dflag, format, args...) do { if(isDebugLevel(dflag)) \ + printf("%s:%d:%s(): " format, \ + _DEBUG_FILENAME, __LINE__, __func__, ##args); } \ + while (0) +#else +#define DEBUG_LOG(dflag, format, args...) do{/* nothing */}while(0) +#endif + +/*! + * @brief Debug output helper macro to append text without addition information + * @param dflag eDebugLevel required to print output + * @param format format same as for printf + * @param args arguments for printf + * + * New line must be appended manually + */ +#ifdef _DEBUG_OUTPUT +#define DEBUG_LOG_APPEND(dflag, format, args...) do { if(isDebugLevel(dflag)) \ + printf(format, ##args); } \ + while (0) +#else +#define DEBUG_LOG_APPEND(dflag, format, args...) do{/* nothing */}while(0) +#endif + +typedef enum _EDEBUGLEVEL +{ + DEBUG_LEVEL_ALWAYS = 0, /* DEBUG_LOGs are visible regardless of DEBUG_LEVEL */ + DEBUG_LEVEL_INFO = 1, /* Visible when application started with -v */ + DEBUG_LEVEL_WARNING = 2, /* Visible when application started with -vv */ + DEBUG_LEVEL_ERROR = 3, +/* Visible when application started with -vvv */ +} eDebugLevel; + +#endif /* #ifndef _DEBUG_H_INCLUDED */ diff --git a/src/main.c b/src/main.c index 6aecb38..cafab7f 100644 --- a/src/main.c +++ b/src/main.c @@ -12,67 +12,197 @@ */ #include #include -#include -#include +#include +#include + +#include "xmalloc.h" +#include "xregex.h" #include "xtypes.h" #include "xstring.h" #include "stubser/stubser_if.h" -STATIC int one(const struct dirent *unused) -{ - return 1; -} - -/* - * @brief Scan given folder for .c files - * @param argc amount of command line arguments - * @param **argv array of argments separated by spaces - * @retval -1 Directory not found or regex compilation error - * @retval 0 Direcotry scanned successfully - * @details - * see printf for usage +/* POSIX.1 says each process has at least 20 file descriptors. + * Three of those belong to the standard streams. + * Here, we use a conservative estimate of 15 available; + * assuming we use at most two for other uses in this program, + * we should never run into any problems. + * Most trees are shallower than that, so it is efficient. + * Deeper trees are traversed fine, just a bit slower. + * (Linux allows typically hundreds to thousands of open files, + * so you'll probably never see any issues even if you used + * a much higher value, say a couple of hundred, but + * 15 is a safe, reasonable value.) */ -int main(int argc, char **argv) +#ifndef USE_FDS +#define USE_FDS 15 +#endif + +#define MAIN_MAX_MATCHGROUP_FILENAME 2 +#define MAIN_CFILE_PATTERN "^(.*)\\.+[cC]$" + +STATIC regex_t main_regXcFile; +STATIC bool main_recurse = false; +STATIC char* main_outputDir = NULL; + +/*! + * @brief Using regular expressions to scan for c files, extract filename without suffix and start stub creation + * @details + * @par Parameter + * See gnu libc documentation for details (Data Type: __nftw_func_t). + */ +int evaluateEntry(const char *filepath, const struct stat *info, const int typeflag, struct FTW *pathinfo) { - struct dirent **eps; - int n; - regex_t regX; - char filePath[256] = - { 0 }; - const char *pattern = "^.*\\.[cC]"; - - printf("%s %s\n\n", argv[1], argv[2]); - - if (3 != argc) + regmatch_t matchGroup[MAIN_MAX_MATCHGROUP_FILENAME]; + char* noSuffix = NULL; + if (!main_recurse && 1 < pathinfo->level) { - printf("Usage:\n"); - printf(" %s \n", gnu_basename(argv[0])); - return -1; + return 0; + } + if (typeflag != FTW_F) + { + return 0; } - if (regcomp(®X, pattern, REG_NEWLINE)) + if (0 != regexec(&main_regXcFile, gnu_basename((char*) filepath), MAIN_MAX_MATCHGROUP_FILENAME, matchGroup, 0)) { - fprintf(stderr, "bad pattern: '%s'\n", pattern); - return -1; + return 0; } - n = scandir(argv[1], &eps, one, alphasort); - if (n >= 0) + if (XREGEX_IS_MATCHGROUP(matchGroup, 1)) { - int cnt; - for (cnt = 0; cnt < n; ++cnt) + xmallocStrlcpy(&noSuffix, &gnu_basename((char*) filepath)[matchGroup[1].rm_so], XREGEX_SIZEOF_MATCHGROUP(matchGroup, 1)); + if (NULL != noSuffix) { - if (0 == regexec(®X, eps[cnt]->d_name, 0, NULL, 0) // - && DT_REG == eps[cnt]->d_type) - { - sprintf(filePath, "%s/%s", argv[1], eps[cnt]->d_name); - printf("%s (%d)\n", filePath, eps[cnt]->d_type); - stubser_createStub(filePath, argv[2]); - } + stubser_createStub((char*) filepath, noSuffix, main_outputDir); } + free(noSuffix); } - else - perror("Couldn't open the directory"); return 0; } + +/*! + * @brief Scan given directory recursively + */ +int evaluateDirectoryTree(const char * const dirpath) +{ + int result; + + /* Invalid directory path? */ + if (dirpath == NULL || *dirpath == '\0') + return errno = EINVAL; + + result = nftw(dirpath, evaluateEntry, USE_FDS, FTW_PHYS); + if (result >= 0) + errno = result; + + return errno; +} + +void printUsage(char *aAppName) +{ + printf("Create stub c-files and c-header from c files for CUnit.\n\n"); + printf("Usage:\n"); + printf(" %s [options] [source dir] \n", aAppName); + printf("\nOptions:\n"); + printf(" -r\tSearch source Dir recursively\n"); +} + +/*! + * @brief Search (optionally recursive) for c file and create stubs + * @param [in] argc Amount of command line arguments + * @param [in] **argv Array of arguments + * @retval EXIT_SUCCESS stubs created successfully + * @retval EXIT_FAILURE error with arguments or input + */ +int main(int argc, char **argv) +{ + int i = 0; + int ret = EXIT_FAILURE; + int outputDirIndex = 0; + uint8_t dirs = 0; + + if (regcomp(&main_regXcFile, MAIN_CFILE_PATTERN, REG_EXTENDED | REG_NEWLINE)) + { + fprintf(stderr, "%s.\n", strerror(errno)); + ret = EXIT_FAILURE; + goto l_main_end; + } + + if (argc < 2) + { + printUsage(gnu_basename(argv[0])); + ret = EXIT_FAILURE; + goto l_main_end; + } + + // evaluate command line parameter + for (i = 1; i < argc && i < 4; ++i) + { + if (NULL != strstr(argv[i], "-r")) + { + main_recurse = true; + } + else + { + outputDirIndex = i; + ++dirs; + } + } + + if (0 == outputDirIndex) + { + // only options; no output directory provided + printUsage(gnu_basename(argv[0])); + ret = EXIT_FAILURE; + goto l_main_end; + } + + if (0 > mkdir(argv[outputDirIndex], S_IRWXU)) + { + // output directory not available + if (EEXIST != errno) + { + fprintf(stderr, "%s.\n", strerror(errno)); + ret = EXIT_FAILURE; + goto l_main_end; + } + } + + xmallocStrlcpy(&main_outputDir, argv[outputDirIndex], strlen(argv[outputDirIndex]) + 1); + if (NULL == main_outputDir) + { + fprintf(stderr, "%s.\n", strerror(errno)); + ret = EXIT_FAILURE; + goto l_main_end; + } + + if (1 == dirs) + { + // only output provided; input will be "." + if (evaluateDirectoryTree(".")) + { + fprintf(stderr, "%s.\n", strerror(errno)); + ret = EXIT_FAILURE; + goto l_main_end; + } + } + else + { + // take input directory from arguments + if (evaluateDirectoryTree(argv[outputDirIndex - 1])) + { + fprintf(stderr, "%s.\n", strerror(errno)); + ret = EXIT_FAILURE; + goto l_main_end; + } + } + + ret = EXIT_SUCCESS; + + l_main_end: // label for ending main + + free(main_outputDir); + + return ret; +} diff --git a/src/stubser/cfile_parser.c b/src/stubser/cfile_parser.c index a9842b1..d7c63d8 100644 --- a/src/stubser/cfile_parser.c +++ b/src/stubser/cfile_parser.c @@ -19,6 +19,7 @@ #include "xmalloc.h" #include "cfunction_if.h" #include "cfile_parser_loc.h" +#include "debug.h" #define CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS 10 @@ -107,10 +108,10 @@ STATIC void printMatchGroup(char *aString, regmatch_t *aMatchGroup) { if (aMatchGroup[i].rm_so < aMatchGroup[i].rm_eo) { - printf("[%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)); - printf("%s", match); - printf("\n"); + DEBUG_LOG_APPEND(1, "%s", match); + DEBUG_LOG_APPEND(1, "\n"); } } } @@ -417,18 +418,18 @@ STATIC int8_t removeCcomments(char *aLine) return cBlockRemoval(aLine, CPARS_COMMENT_BLOCK_START, CPARS_COMMENT_BLOCK_END, &helper, true); } -STATIC int8_t evaluateParameter(char **aParameter, cfunction_t *aFunction) +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 == *aParameter || NULL == aFunction) + if (NULL == aParameter || NULL == aFunction) { return -1; } - token = strtok(*aParameter, ","); + token = strtok(aParameter, ","); while (token) { if (0 == regexec(®Xparameter, token, CFILE_PARSER_MAX_REGEX_MATCHING_GROUPS, matchGroup, 0)) @@ -454,8 +455,6 @@ STATIC int8_t evaluateParameter(char **aParameter, cfunction_t *aFunction) token = strtok(NULL, ","); } - free(*aParameter); - *aParameter = NULL; return 0; } @@ -489,21 +488,22 @@ STATIC cfunction_t* cfile_parser_evaluateLine(char *aLine) if (0 == regexec(®XmlEnd, aLine, maxGroup, matchGroup, REG_NOTEOL)) { tempFunctionType = CFUNCTION_TYPE_REGULAR; - printf("[ mle]"); + DEBUG_LOG_APPEND(1, "[ mle]"); functionEnd = true; } else if (0 == regexec(®XmlProto, aLine, maxGroup, matchGroup, REG_NOTEOL)) { tempFunctionType = CFUNCTION_TYPE_PROTO; - printf("[ mlp]"); + DEBUG_LOG_APPEND(1, "[ mlp]"); functionEnd = true; } else if (0 == regexec(®XmlPar, aLine, maxGroup, matchGroup, REG_NOTEOL)) { - printf("[ ml+]"); + DEBUG_LOG_APPEND(1, "[ ml+]"); } else { + DEBUG_LOG_APPEND(1, "[ na ]"); tempFunctionType = CFUNCTION_TYPE_UNDEF; functionEnd = true; } @@ -513,23 +513,23 @@ STATIC cfunction_t* cfile_parser_evaluateLine(char *aLine) else if (0 == regexec(®Xsl, aLine, maxGroup, matchGroup, REG_NOTEOL) && CFILE_PARSER_IS_MATCHGROUP_FUNCTION(matchGroup)) { tempFunctionType = CFUNCTION_TYPE_REGULAR; - printf("[ sl ]"); + 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; - printf("[ mls]"); + 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; - printf("[prot]"); + 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; - printf("[ fuX]"); + DEBUG_LOG_APPEND(1, "[ fuX]"); } else { @@ -546,11 +546,11 @@ STATIC cfunction_t* cfile_parser_evaluateLine(char *aLine) if (matchGroup[i].rm_so < matchGroup[i].rm_eo) { if (0 < i) - printf(" "); - printf("[%02d](%.3ld - %.3ld)[%03d]: ", i, matchGroup[i].rm_so, matchGroup[i].rm_eo, (int) (matchGroup[i].rm_eo - matchGroup[i].rm_so)); + 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)); - printf("%s", match); - printf("\n"); + DEBUG_LOG_APPEND(1, "%s", match); + DEBUG_LOG_APPEND(1, "\n"); } } if (functionEnd) @@ -601,7 +601,9 @@ STATIC cfunction_t* cfile_parser_evaluateLine(char *aLine) // return evaluated function if (0 == cfile_parser_functionLine) { - evaluateParameter(¶meterStorage, cfile_parser_function); + evaluateParameter(parameterStorage, cfile_parser_function); + free(parameterStorage); + parameterStorage = NULL; cfunction_t *funTemp = cfile_parser_function; cfile_parser_function = NULL; return funTemp; diff --git a/src/stubser/stubser.c b/src/stubser/stubser.c index 49ecf5c..8c6be68 100644 --- a/src/stubser/stubser.c +++ b/src/stubser/stubser.c @@ -1,16 +1,17 @@ /*! - Name : stubi.c - Author : Martin Winkler - Version : - Copyright : - Description : Scan a folder for .c files and generate stubs from function definitions - - @todo complete function dissection - @todo output stub header and function - @attention Using regular expressions to scan c files for function definitions. No 100% - success rate and should be seen as helper. Result must be reviewed manually. + * @file stubser.c + * @brief Scan a folder for .c files and generate stubs from function definitions + * @attention Using regular expressions to scan c files for function definitions. No 100% + * success rate and should be seen as helper. Result must be reviewed manually. + * @details + * Project: stubser\n + * Subsystem: \n + * Module: \n + * Code: GNU-C\n + * + * @date 06.03.2017 + * @author SESA354004 */ - #include #include #include @@ -22,6 +23,7 @@ #include "cfile_parser_if.h" #include "stubser_if.h" #include "stubser_loc.h" +#include "debug.h" STATIC bool isDatatypeVoid(char *aType) { @@ -192,7 +194,7 @@ STATIC int8_t createStubFunctionBlock(FILE *aFile, FILE *aHeader, cfunction_t *a return 0; } -STATIC int8_t createStub(char *aOutput, cfunction_list_t *aFunctionList) +STATIC int8_t createStub(char *aOutput, char *aNoSuffix, cfunction_list_t *aFunctionList) { FILE *cfile; FILE *cheader; @@ -214,6 +216,7 @@ STATIC int8_t createStub(char *aOutput, cfunction_list_t *aFunctionList) if (NULL == cfile) { free(cFileName); + free(cHeaderName); perror("Error open file"); return -1; } @@ -236,8 +239,8 @@ STATIC int8_t createStub(char *aOutput, cfunction_list_t *aFunctionList) fprintf(cheader, " * @details"NEWLINES" * This is a stub header for CUnit.\\n"NEWLINES); fprintf(cheader, " * - generated by stubser -"NEWLINES" */" NEWLINES NEWLINES); - fprintf(cheader, "#ifndef _INCLUDE_%s_H"NEWLINES, strtok(gnu_basename(cHeaderName),".")); - fprintf(cheader, "#define _INCLUDE_%s_H"NEWLINES NEWLINES, strtok(gnu_basename(cHeaderName),".")); + fprintf(cheader, "#ifndef _INCLUDE_%s_H"NEWLINES, aNoSuffix); + fprintf(cheader, "#define _INCLUDE_%s_H"NEWLINES NEWLINES, aNoSuffix); function = aFunctionList->head; while (function) @@ -246,7 +249,7 @@ STATIC int8_t createStub(char *aOutput, cfunction_list_t *aFunctionList) function = function->next; } - fprintf(cheader, NEWLINES"#endif // _INCLUDE_%s_H"NEWLINES, strtok(gnu_basename(cHeaderName),".")); + fprintf(cheader, NEWLINES"#endif // _INCLUDE_%s_H"NEWLINES, aNoSuffix); free(cFileName); free(cHeaderName); @@ -261,16 +264,16 @@ STATIC int8_t createStub(char *aOutput, cfunction_list_t *aFunctionList) * @retval 0 matching complete * @retval -1 error opening file */ -int stubser_createStub(char *path, char *aOutput) +int stubser_createStub(char *path, char* aNoSuffix, char *aOutput) { int8_t ret = -1; cfunction_list_t functionList = CFUNCTION_LIST_DEFAULT; - char fileName[512] = + char fileName[PATH_MAX] = { '\0' }; ret = cfile_parser(path, &functionList); - printf("\n"); // todo remove when output from cfile_parser() is removed + DEBUG_LOG_APPEND(1, "\n"); if (0 != ret) { printf(STUB_CONSOLE_PARSE_ERROR_S2, gnu_basename(path), ret); @@ -279,8 +282,8 @@ int stubser_createStub(char *path, char *aOutput) } printf(STUB_CONSOLE_PARSE_OK_S1, gnu_basename(path)); - sprintf(fileName, "%s/stub_%s", aOutput, strtok(gnu_basename(path), ".")); // todo only cut suffix (last ".") - ret = createStub(fileName, &functionList); + sprintf(fileName, "%s/stub_%s", aOutput, aNoSuffix); + ret = createStub(fileName, aNoSuffix, &functionList); if (0 != ret) { printf(STUB_CONSOLE_CREATE_ERROR_S2, gnu_basename(path), ret); diff --git a/src/stubser/stubser_if.h b/src/stubser/stubser_if.h index 927928a..0234ab8 100644 --- a/src/stubser/stubser_if.h +++ b/src/stubser/stubser_if.h @@ -14,6 +14,6 @@ #ifndef STUBSER_STUBSER_IF_H_ #define STUBSER_STUBSER_IF_H_ -int stubser_createStub(char *path, char *aOutput); +int stubser_createStub(char *path, char* aNoSuffix, char *aOutput); #endif /* STUBSER_STUBSER_IF_H_ */ diff --git a/src/stubser/stubser_loc.h b/src/stubser/stubser_loc.h index 6863c17..78a77e4 100644 --- a/src/stubser/stubser_loc.h +++ b/src/stubser/stubser_loc.h @@ -42,6 +42,6 @@ #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 STUB_CONSOLE_FAIL " %s creation failed (%d)\n" +#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" #endif /* STUBSER_STUBSER_LOC_H_ */