diff --git a/.cproject b/.cproject index 7f2148c..242b6c7 100644 --- a/.cproject +++ b/.cproject @@ -30,6 +30,9 @@ @@ -109,4 +112,6 @@ + + diff --git a/src/ext/xstring.c b/src/ext/xstring.c new file mode 100644 index 0000000..a78a7be --- /dev/null +++ b/src/ext/xstring.c @@ -0,0 +1,75 @@ +/*! + * @file xstring.c + * @brief + * @details + * Project: \n + * Subsystem: \n + * Module: \n + * Code: GNU-C\n + * + * @date 27.02.2017 + * @author SESA354004 + */ +#include "xstring.h" +#include "ctype.h" + +char* gnu_basename(char *path) +{ + char *base = strrchr(path, '/'); + return base ? base + 1 : path; +} + +/*! + * @brief Trim leading and trailing whitespaces + * @param [in] *str Input string + * @retval char* pointer to the trimmed substring + * + * @details + * This function returns a pointer to a substring of the original string. + * If the given string was allocated dynamically, the caller must not overwrite + * that pointer with the returned value, since the original pointer must be + * deallocated using the same allocator with which it was allocated. The return + * value must NOT be deallocated using free() etc. + */ +char* strntrimStatic(char *aStr, size_t aMaxLength) +{ + char *end; + + // Trim leading space + while (isspace((unsigned char)*aStr) && 0 < aMaxLength) + { + if (--aMaxLength > 0) + { + ++aStr; + } + } + + // All spaces at termination or end of max length + if (*aStr == '\0' || 0 == aMaxLength) + { + return aStr; + } + + // Trim trailing space + if (strlen(aStr) >= aMaxLength) + { + end = aStr + aMaxLength - 1; + } + else + { + end = aStr + strlen(aStr) - 1; + } + + while (end > aStr && isspace((unsigned char)*end)) + { + end--; + } + + // Write new null terminator (if length is enough) + if (end < (aStr + aMaxLength - 1)) + { + *(end + 1) = '\0'; + } + + return aStr; +} diff --git a/src/ext/xstring.h b/src/ext/xstring.h new file mode 100644 index 0000000..6d777cc --- /dev/null +++ b/src/ext/xstring.h @@ -0,0 +1,21 @@ +/*! + * @file xstring.h + * @brief + * @details + * Project: \n + * Subsystem: \n + * Module: \n + * Code: GNU-C\n + * + * @date 27.02.2017 + * @author SESA354004 + */ +#ifndef XSTRING_H_ +#define XSTRING_H_ + +#include + +char* gnu_basename(char *path); +char* strntrimStatic(char *aStr, size_t aMaxLength); + +#endif /* XSTRING_H_ */ diff --git a/src/ext/xtime.h b/src/ext/xtime.h new file mode 100644 index 0000000..7725efc --- /dev/null +++ b/src/ext/xtime.h @@ -0,0 +1,32 @@ +/*! + * @file xtime.h + * @brief Provides additional definitions for working with delays + * + * Project: \n + * Subsystem: \n + * Module: mst\n + * Code: GNU-C\n + * + * @date 08.06.2016 + * @author SESA354004 + */ +#ifndef XTIME_H_ +#define XTIME_H_ + +#include + +/*! @brief Convert milliseconds to nanoseconds */ +#define XTIME_MSTONS(milliseconds) (milliseconds * 1000000L) +/*! @brief Convert microseconds to nanoseconds */ +#define XTIME_USTONS(microseconds) (microseconds * 1000L) +/*! @brief Convert nanoseconds to milliseconds*/ +#define XTIME_NSTOMS(nanoseconds) (nanoseconds / 1000000L) +/*! @brief Convert nanoseconds to microseconds*/ +#define XTIME_NSTOUS(nanoseconds) (nanoseconds / 1000L) + +/*! @brief Only microseconds up to 1000000 are supported */ +#define XTIME_TIMESPEC_US(seconds, microseconds) (const struct timespec[]){{seconds, XTIME_USTONS(microseconds)}} +/*! @brief Only milliseconds up to 1000 are supported */ +#define XTIME_TIMESPEC_MS(seconds, milliseconds) (const struct timespec[]){{seconds, XTIME_MSTONS(milliseconds)}} + +#endif /* XTIME_H_ */ diff --git a/src/ext/xtypes.h b/src/ext/xtypes.h new file mode 100644 index 0000000..39cb61b --- /dev/null +++ b/src/ext/xtypes.h @@ -0,0 +1,41 @@ +/*! + * @file xtypes.h + * @brief + * @details + * Project: \n + * Subsystem: \n + * Module: \n + * Code: GNU-C\n + * + * @date 27.02.2017 + * @author SESA354004 + */ + +#ifndef XTYPES_H_ +#define XTYPES_H_ + +#include // definition of NULL +#include // fixed width types +#include // definition of bool + +#define STRINGIZE_2(s) #s +/*! + * @brief Stringify the content of a definition + * @details + * Usage example: + * @code + #define VERSION_MAJOR 4 + #define VERSION_MINOR 47 + + #define VERSION_STRING "v" STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR) + @endcode + */ +#define STRINGIZE(s) STRINGIZE_2(s) + +#if defined(UNIT_TEST) || defined(_DEBUG) +#define STATIC +#else +#define STATIC static +#endif + +#endif /* XTYPES_H_ */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..ffbe1cb --- /dev/null +++ b/src/main.c @@ -0,0 +1,78 @@ +/*! + * @file main.c + * @brief + * @details + * Project: \n + * Subsystem: \n + * Module: \n + * Code: GNU-C\n + * + * @date 27.02.2017 + * @author SESA354004 + */ +#include +#include +#include +#include +#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 + */ +int main(int argc, char **argv) +{ + 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) + { + printf("Usage:\n"); + printf(" %s \n", gnu_basename(argv[0])); + return -1; + } + + if (regcomp(®X, pattern, REG_NEWLINE)) + { + fprintf(stderr, "bad pattern: '%s'\n", pattern); + return -1; + } + + n = scandir(argv[1], &eps, one, alphasort); + if (n >= 0) + { + int cnt; + for (cnt = 0; cnt < n; ++cnt) + { + 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); + scanFile(filePath); + } + } + } + else + perror("Couldn't open the directory"); + + return 0; +} diff --git a/src/stubi.c b/src/stubi.c deleted file mode 100644 index 69e9c60..0000000 --- a/src/stubi.c +++ /dev/null @@ -1,271 +0,0 @@ -/*! - 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. - */ - -#include -#include -#include -#include -#include - -#define STUBI_FUNCTION_REGEX "^[ \\t]*([ _\\*[:alnum:]]*) ([_\\*[:alnum:]]*)[ \\t]*\\(([^\\)]*)" -#define STUBI_FUNCTION_MULTI "[ \\t]*(.*)" -#define STUBI_FUNCTION_MULTI_END "[ \\t]*(.*)\\)[ \\t\\{;]*" - -static int one(const struct dirent *unused) -{ - return 1; -} - -static char *gnu_basename(char *path) -{ - char *base = strrchr(path, '/'); - return base ? base + 1 : path; -} - -/*! - * @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 regula expressions - * @retval -2 Given String not a function - * @retval 0 function evaluated - */ -static int matchFunction(char *aLine) -{ - const size_t maxGroup = 10; - regmatch_t matchGroup[maxGroup]; - const char *pattern = - STUBI_FUNCTION_REGEX; - const char *patternend = - STUBI_FUNCTION_REGEX "\\)"; - regex_t regX; - regex_t regXEnd; - regex_t regXMulti; - regex_t regXMultiEnd; - uint8_t i = 0; - char funPrefix[256] = - { 0 }; - static uint8_t multiParamFlag = 0; - uint8_t multiParamEndFlag = 0; - - if (0 > regcomp(®X, pattern, REG_EXTENDED | REG_NEWLINE)) - { - perror("Error regex\n"); - return -1; - } - - if (0 > regcomp(®XEnd, patternend, REG_EXTENDED | REG_NEWLINE)) - { - perror("Error regexend\n"); - return -1; - } - - if (0 > regcomp(®XMulti, STUBI_FUNCTION_MULTI, REG_EXTENDED | REG_NEWLINE)) - { - perror("Error regXMulti\n"); - return -1; - } - - if (0 > regcomp(®XMultiEnd, STUBI_FUNCTION_MULTI_END, REG_EXTENDED | REG_NEWLINE)) - { - perror("Error regXMultiEnd\n"); - return -1; - } - - if (multiParamFlag) - { - if (0 == regexec(®XMultiEnd, aLine, maxGroup, matchGroup, 0)) - { - // end of multi line function - ++multiParamFlag; - multiParamEndFlag = 1; - } - else - { - // continuation of multi line function - (void) regexec(®XMulti, aLine, maxGroup, matchGroup, 0); - ++multiParamFlag; - } - } - else if (0 == regexec(®XEnd, aLine, maxGroup, matchGroup, 0)) - { - // single line function - multiParamFlag = 0; - } - else if (0 == regexec(®X, aLine, maxGroup, matchGroup, 0)) - { - // start of multi line function - ++multiParamFlag; - printf("[ mls][%d]:", multiParamFlag); - } - else - { - // no fit - return -2; - } - - switch (multiParamFlag) - { - case 0: // single line function definition - case 1: // first line of multi line function definition - { - (void) strlcpy(funPrefix, &aLine[matchGroup[1].rm_so], (size_t) (matchGroup[1].rm_eo - matchGroup[1].rm_so) + 1); - - if ( // - NULL == strstr(funPrefix, "extern") // - && NULL == strstr(funPrefix, "EXTERN") // - && NULL == strstr(funPrefix, "static") // - && NULL == strstr(funPrefix, "STATIC") // - && matchGroup[2].rm_so < matchGroup[2].rm_eo) - { - printf("[ sl ]:%s", funPrefix); - for (i = 2; i < maxGroup && 0 <= matchGroup[i].rm_so; ++i) - { - printf(" (%.3ld - %.3ld): ", matchGroup[i].rm_so, matchGroup[i].rm_eo); - printf("%.*s", (int) (matchGroup[i].rm_eo - matchGroup[i].rm_so), &aLine[matchGroup[i].rm_so]); - } - printf("\n"); - } - // TODO special handling for static functions (as extern in stub header) - else if (NULL != strstr(funPrefix, "static") || NULL != strstr(funPrefix, "STATIC")) - { - printf("[todo] %s %.*s\n", funPrefix, (int) (matchGroup[2].rm_eo - matchGroup[2].rm_so), &aLine[matchGroup[2].rm_so]); - } - // ignore functions declared as extern - else if (NULL != strstr(funPrefix, "extern") || NULL != strstr(funPrefix, "EXTERN")) - { - printf("[ nu ] %s %.*s\n", funPrefix, (int) (matchGroup[2].rm_eo - matchGroup[2].rm_so), &aLine[matchGroup[2].rm_so]); - } - break; - } - default: // further lines - { - if (multiParamEndFlag) - { - printf("[ mle][%d]:", multiParamFlag); - } - else - { - printf("[ ml ][%d]:", multiParamFlag); - } - - if (matchGroup[1].rm_so < matchGroup[1].rm_eo) - { - for (i = 1; i < maxGroup && 0 <= matchGroup[i].rm_so; ++i) - { - printf("(%.3ld-%.3ld):", matchGroup[i].rm_so, matchGroup[i].rm_eo); - printf("%.*s", (int) (matchGroup[i].rm_eo - matchGroup[i].rm_so), &aLine[matchGroup[i].rm_so]); - } - printf("\n"); - } - - if (multiParamEndFlag) - { - multiParamFlag = 0; - multiParamEndFlag = 0; - } - - break; - } - } - - return 0; -} - -/* - * @brief Opens given file for line by line function matching - * @param *path file path - * @retval 0 matching complete - * @retval -1 error opening file - */ -static int scanFile(char *path) -{ - FILE *fdesc; - ssize_t charRead = 0; - size_t lineSize = 0; - char *fileLine = NULL; // will be allocated by getline(); must be freed - - fdesc = fopen(path, "r"); - if (NULL == fdesc) - { - perror("Error open file"); - return -1; - } - - do - { - charRead = getline(&fileLine, &lineSize, fdesc); - if (0 <= charRead) - { - (void) matchFunction(fileLine); - } - } while (0 <= charRead); - - free(fileLine); - (void) fclose(fdesc); - return 0; -} - -/* - * @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 - */ -int main(int argc, char **argv) -{ - 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) - { - printf("Usage:\n"); - printf(" %s \n", gnu_basename(argv[0])); - return -1; - } - - if (regcomp(®X, pattern, REG_NEWLINE)) - { - fprintf(stderr, "bad pattern: '%s'\n", pattern); - return -1; - } - - n = scandir(argv[1], &eps, one, alphasort); - if (n >= 0) - { - int cnt; - for (cnt = 0; cnt < n; ++cnt) - { - 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); - scanFile(filePath); - } - } - } - else - perror("Couldn't open the directory"); - - return 0; -} - diff --git a/src/stubser/stubser.c b/src/stubser/stubser.c new file mode 100644 index 0000000..5b99369 --- /dev/null +++ b/src/stubser/stubser.c @@ -0,0 +1,207 @@ +/*! + 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. + */ + +#include +#include +#include +#include +#include "xtypes.h" +#include "xstring.h" + +#define STUBI_FUNCTION_REGEX "^[ \\t]*([ _\\*[:alnum:]]*) ([_\\*[:alnum:]]*)[ \\t]*\\(([^\\)]*)" +#define STUBI_FUNCTION_MULTI "[ \\t]*(.*)" +#define STUBI_FUNCTION_MULTI_END "[ \\t]*(.*)\\)[ \\t\\{;]*" + +/*! + * @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 regula expressions + * @retval -2 Given String not a function + * @retval 0 function evaluated + */ +STATIC int matchFunction(char *aLine) +{ + const size_t maxGroup = 10; + regmatch_t matchGroup[maxGroup]; + const char *pattern = + STUBI_FUNCTION_REGEX; + const char *patternend = + STUBI_FUNCTION_REGEX "\\)"; + regex_t regX; + regex_t regXEnd; + regex_t regXMulti; + regex_t regXMultiEnd; + uint8_t i = 0; + char funPrefix[256] = + { 0 }; + static uint8_t multiParamFlag = 0; + uint8_t multiParamEndFlag = 0; + + if (0 > regcomp(®X, pattern, REG_EXTENDED | REG_NEWLINE)) + { + perror("Error regex\n"); + return -1; + } + + if (0 > regcomp(®XEnd, patternend, REG_EXTENDED | REG_NEWLINE)) + { + perror("Error regexend\n"); + return -1; + } + + if (0 > regcomp(®XMulti, STUBI_FUNCTION_MULTI, REG_EXTENDED | REG_NEWLINE)) + { + perror("Error regXMulti\n"); + return -1; + } + + if (0 > regcomp(®XMultiEnd, STUBI_FUNCTION_MULTI_END, REG_EXTENDED | REG_NEWLINE)) + { + perror("Error regXMultiEnd\n"); + return -1; + } + + if (multiParamFlag) + { + if (0 == regexec(®XMultiEnd, aLine, maxGroup, matchGroup, 0)) + { + // end of multi line function + ++multiParamFlag; + multiParamEndFlag = 1; + } + else + { + // continuation of multi line function + (void) regexec(®XMulti, aLine, maxGroup, matchGroup, 0); + ++multiParamFlag; + } + } + else if (0 == regexec(®XEnd, aLine, maxGroup, matchGroup, 0)) + { + // single line function + multiParamFlag = 0; + } + else if (0 == regexec(®X, aLine, maxGroup, matchGroup, 0)) + { + // start of multi line function + ++multiParamFlag; + printf("[ mls][%d]:", multiParamFlag); + } + else + { + // no fit + return -2; + } + + switch (multiParamFlag) + { + case 0: // single line function definition + case 1: // first line of multi line function definition + { + (void) strlcpy(funPrefix, &aLine[matchGroup[1].rm_so], (size_t) (matchGroup[1].rm_eo - matchGroup[1].rm_so) + 1); + + if ( // + NULL == strstr(funPrefix, "extern") // + && NULL == strstr(funPrefix, "EXTERN") // + && NULL == strstr(funPrefix, "static") // + && NULL == strstr(funPrefix, "STATIC") // + && matchGroup[2].rm_so < matchGroup[2].rm_eo) + { + printf("[ sl ]:%s", funPrefix); + for (i = 2; i < maxGroup && 0 <= matchGroup[i].rm_so; ++i) + { + printf(" (%.3ld - %.3ld): ", matchGroup[i].rm_so, matchGroup[i].rm_eo); + printf("%.*s", (int) (matchGroup[i].rm_eo - matchGroup[i].rm_so), &aLine[matchGroup[i].rm_so]); + } + printf("\n"); + } + // TODO special handling for static functions (as extern in stub header) + else if (NULL != strstr(funPrefix, "static") || NULL != strstr(funPrefix, "STATIC")) + { + printf("[todo] %s %.*s\n", funPrefix, (int) (matchGroup[2].rm_eo - matchGroup[2].rm_so), &aLine[matchGroup[2].rm_so]); + } + // ignore functions declared as extern + else if (NULL != strstr(funPrefix, "extern") || NULL != strstr(funPrefix, "EXTERN")) + { + printf("[ nu ] %s %.*s\n", funPrefix, (int) (matchGroup[2].rm_eo - matchGroup[2].rm_so), &aLine[matchGroup[2].rm_so]); + } + break; + } + default: // further lines + { + if (multiParamEndFlag) + { + printf("[ mle][%d]:", multiParamFlag); + } + else + { + printf("[ ml ][%d]:", multiParamFlag); + } + + if (matchGroup[1].rm_so < matchGroup[1].rm_eo) + { + for (i = 1; i < maxGroup && 0 <= matchGroup[i].rm_so; ++i) + { + printf("(%.3ld-%.3ld):", matchGroup[i].rm_so, matchGroup[i].rm_eo); + printf("%.*s", (int) (matchGroup[i].rm_eo - matchGroup[i].rm_so), &aLine[matchGroup[i].rm_so]); + } + printf("\n"); + } + + if (multiParamEndFlag) + { + multiParamFlag = 0; + multiParamEndFlag = 0; + } + + break; + } + } + + return 0; +} + +/* + * @brief Opens given file for line by line function matching + * @param *path file path + * @retval 0 matching complete + * @retval -1 error opening file + */ +int scanFile(char *path) +{ + FILE *fdesc; + ssize_t charRead = 0; + size_t lineSize = 0; + char *fileLine = NULL; // will be allocated by getline(); must be freed + + fdesc = fopen(path, "r"); + if (NULL == fdesc) + { + perror("Error open file"); + return -1; + } + + do + { + charRead = getline(&fileLine, &lineSize, fdesc); + if (0 <= charRead) + { + (void) matchFunction(fileLine); + } + } while (0 <= charRead); + + free(fileLine); + (void) fclose(fdesc); + return 0; +} + diff --git a/src/stubser/stubser_if.h b/src/stubser/stubser_if.h new file mode 100644 index 0000000..e3aae09 --- /dev/null +++ b/src/stubser/stubser_if.h @@ -0,0 +1,19 @@ +/*! + * @file stubser_if.h + * @brief + * @details + * Project: \n + * Subsystem: \n + * Module: \n + * Code: GNU-C\n + * + * @date 27.02.2017 + * @author SESA354004 + */ + +#ifndef STUBSER_STUBSER_IF_H_ +#define STUBSER_STUBSER_IF_H_ + +int scanFile(char *path); + +#endif /* STUBSER_STUBSER_IF_H_ */