From a00ded6d06a4f94f03df6aec277e25071e873b06 Mon Sep 17 00:00:00 2001 From: Jordan Bancino Date: Thu, 27 Apr 2023 20:04:15 +0000 Subject: [PATCH] Add support for parsing global variables, not just function declarations. --- src/HeaderParser.c | 125 ++++++++++++++++++++++++++----------- src/include/HeaderParser.h | 8 +++ src/include/Telodendria.h | 72 ++++++++++++++++++--- tools/bin/td | 1 + tools/src/hdoc.c | 55 ++++++++++++++++ 5 files changed, 217 insertions(+), 44 deletions(-) diff --git a/src/HeaderParser.c b/src/HeaderParser.c index 07dedb3..1546e88 100644 --- a/src/HeaderParser.c +++ b/src/HeaderParser.c @@ -505,8 +505,8 @@ HeaderParse(Stream * stream, HeaderExpr * expr) { int i = wordLen; - expr->type = HP_DECLARATION; - strncpy(expr->data.declaration.returnType, word, wordLimit); + expr->type = HP_GLOBAL; + strncpy(expr->data.global.type, word, wordLimit); if (strcmp(word, "struct") == 0 || strcmp(word, "enum") == 0 || @@ -515,8 +515,9 @@ HeaderParse(Stream * stream, HeaderExpr * expr) Free(word); word = HeaderConsumeWord(expr); wordLen = strlen(word); - expr->data.declaration.returnType[i] = ' '; - strncpy(expr->data.declaration.returnType + i + 1, word, wordLen + 1); + expr->data.global.type[i] = ' '; + + strncpy(expr->data.global.type + i + 1, word, wordLen + 1); i += wordLen + 1; } @@ -525,13 +526,16 @@ HeaderParse(Stream * stream, HeaderExpr * expr) c = HeaderConsumeWhitespace(expr); if (c == '*') { - expr->data.declaration.returnType[i] = ' '; + expr->data.global.type[i] = ' '; + i++; - expr->data.declaration.returnType[i] = '*'; + expr->data.global.type[i] = '*'; + i++; while ((c = HeaderConsumeWhitespace(expr)) == '*') { - expr->data.declaration.returnType[i] = c; + expr->data.global.type[i] = c; + i++; } } @@ -550,45 +554,94 @@ HeaderParse(Stream * stream, HeaderExpr * expr) } else { - strncpy(expr->data.declaration.name, word, wordLimit); + strncpy(expr->data.global.name, word, wordLimit); Free(word); word = NULL; c = HeaderConsumeWhitespace(expr); - if (c != '(') + + if (c == ';') + { + /* That's the end of the global. */ + } + else if (c == '[') + { + /* Looks like we have an array. Slurp all the dimensions */ + int block = 1; + int i = wordLen; + + expr->data.global.name[i] = '['; + i++; + + while (1) + { + if (i >= HEADER_EXPR_MAX - wordLen) + { + expr->type = HP_PARSE_ERROR; + expr->data.error.msg = "Memory limit exceeded while parsing global array."; + expr->data.error.lineNo = expr->state.lineNo; + return; + } + + c = StreamGetc(expr->state.stream); + + if (StreamEof(expr->state.stream) || StreamError(expr->state.stream)) + { + expr->type = HP_SYNTAX_ERROR; + expr->data.error.msg = "Unterminated global array."; + expr->data.error.lineNo = expr->state.lineNo; + return; + } + + if (c == ';') + { + expr->data.global.name[i] = '\0'; + break; + } + else + { + expr->data.global.name[i] = c; + i++; + } + } + } + else if (c == '(') + { + expr->type = HP_DECLARATION; + expr->data.declaration.args = ArrayCreate(); + do + { + word = HeaderConsumeArg(expr); + ArrayAdd(expr->data.declaration.args, word); + word = NULL; + } + while ((!StreamEof(expr->state.stream)) && ((c = HeaderConsumeWhitespace(expr)) != ')')); + + if (StreamEof(expr->state.stream)) + { + expr->type = HP_SYNTAX_ERROR; + expr->data.error.msg = "End of file reached before ')'."; + expr->data.error.lineNo = expr->state.lineNo; + return; + } + + c = HeaderConsumeWhitespace(expr); + if (c != ';') + { + expr->type = HP_SYNTAX_ERROR; + expr->data.error.msg = "Expected ';'."; + expr->data.error.lineNo = expr->state.lineNo; + return; + } + } + else { expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "Expected '('"; + expr->data.error.msg = "Expected ';', '[', or '('"; expr->data.error.lineNo = expr->state.lineNo; return; } - expr->data.declaration.args = ArrayCreate(); - - do - { - word = HeaderConsumeArg(expr); - ArrayAdd(expr->data.declaration.args, word); - word = NULL; - } - while ((!StreamEof(expr->state.stream)) && ((c = HeaderConsumeWhitespace(expr)) != ')')); - - if (StreamEof(expr->state.stream)) - { - expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "End of file reached before ')'."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } - - c = HeaderConsumeWhitespace(expr); - if (c != ';') - { - expr->type = HP_SYNTAX_ERROR; - expr->data.error.msg = "Expected ';'."; - expr->data.error.lineNo = expr->state.lineNo; - return; - } } } } diff --git a/src/include/HeaderParser.h b/src/include/HeaderParser.h index a0ee6e3..6c3b02a 100644 --- a/src/include/HeaderParser.h +++ b/src/include/HeaderParser.h @@ -36,6 +36,7 @@ typedef enum HeaderExprType HP_PREPROCESSOR_DIRECTIVE, HP_TYPEDEF, HP_DECLARATION, + HP_GLOBAL, HP_SYNTAX_ERROR, HP_PARSE_ERROR, HP_EOF @@ -48,6 +49,12 @@ typedef struct HeaderDeclaration Array *args; } HeaderDeclaration; +typedef struct HeaderGlobal +{ + char type[64]; + char name[HEADER_EXPR_MAX - 64]; +} HeaderGlobal; + typedef struct HeaderExpr { HeaderExprType type; @@ -55,6 +62,7 @@ typedef struct HeaderExpr { char text[HEADER_EXPR_MAX]; HeaderDeclaration declaration; + HeaderGlobal global; struct { int lineNo; diff --git a/src/include/Telodendria.h b/src/include/Telodendria.h index 3480270..ecb33ea 100644 --- a/src/include/Telodendria.h +++ b/src/include/Telodendria.h @@ -24,6 +24,20 @@ #ifndef TELODENDRIA_H #define TELODENDRIA_H +/*** + * @Nm Telodendria + * @Nd Branding and callback functions specific to Telodendria. + * @Dd March 12 2023 + * @Xr Memory Log + * + * This API provides the callbacks used to hook Telodendria into the + * various other APIs. It exists primarily to be called by + * .Fn main , + * but these functions are not static so + * .Fn main + * can be in a separate compilation unit. + */ + #include #include #include @@ -31,22 +45,64 @@ #define TELODENDRIA_LOGO_WIDTH 56 #define TELODENDRIA_LOGO_HEIGHT 22 +/** + * This holds the Telodendria ASCII art logo. + * .Va TELODENDRIA_LOGO_HEIGHT + * and + * .Va TELODENDRIA_LOGO_WIDTH + * are the sizes of each dimension of the two-dimensional array. + * .Va TELODENDRIA_LOGO_HEIGHT + * is the number of lines the logo contains, and + * .Va TELODENDRIA_LOGO_WIDTH + * is the number of characters in each line. + * .Pp + * .Sy NOTE: + * the Telodendria logo belongs solely to the Telodendria project. If + * this code is modified and distributed as something other than a + * packaging of the official Telodendria source package, the logo + * must be replaced with a different one, or removed entirely. Consult + * the licensing section of + * .Xr telodendria 7 + * for details. + */ extern const char - TelodendriaLogo[TELODENDRIA_LOGO_HEIGHT][TELODENDRIA_LOGO_WIDTH]; +TelodendriaLogo[TELODENDRIA_LOGO_HEIGHT][TELODENDRIA_LOGO_WIDTH]; #define TELODENDRIA_HEADER_WIDTH 56 #define TELODENDRIA_HEADER_HEIGHT 6 +/** + * This holds the Telodendria ASCII art header. It follows the same + * conventions as the logo, but is not under any particular + * restrictions other than those spelled out in the license. + */ extern const char - TelodendriaHeader[TELODENDRIA_HEADER_HEIGHT][TELODENDRIA_HEADER_WIDTH]; +TelodendriaHeader[TELODENDRIA_HEADER_HEIGHT][TELODENDRIA_HEADER_WIDTH]; -extern void - TelodendriaMemoryHook(MemoryAction, MemoryInfo *, void *); +/** + * This function follows the function prototype required by + * .Fn MemoryHook . + * It is executed every time an allocation, re-allocation, or free + * occurs, and is responsible for logging memory operations to the + * log. + */ +extern void TelodendriaMemoryHook(MemoryAction, MemoryInfo *, void *); -extern void - TelodendriaGenerateMemReport(void); +/** + * Generate a memory report in the current working directory. This + * function is intended to be called after all memory has supposedly + * been freed. It allocates no new memory of its own (except what is + * required by FILE pointers) and simply dumps all of the allocated + * memory out to a file if there is any memory allocated. + * .Pp + * This function is used to detect and fix memory leaks. + */ +extern void TelodendriaGenerateMemReport(void); -extern void - TelodendriaPrintHeader(void); +/** + * Print the logo and header, along with the copyright year and holder, + * and the version number, out to the global log. + */ +extern void TelodendriaPrintHeader(void); #endif diff --git a/tools/bin/td b/tools/bin/td index 2993d7c..2e9e87c 100644 --- a/tools/bin/td +++ b/tools/bin/td @@ -118,6 +118,7 @@ recipe_docs() { if [ $(mod_time "$header") -ge $(mod_time "$man") ]; then echo "DOC $basename" if ! hdoc -D Os=Telodendria -i "$header" -o "$man"; then + rm "$man" exit 1 fi fi diff --git a/tools/src/hdoc.c b/tools/src/hdoc.c index 96613f6..14fa170 100644 --- a/tools/src/hdoc.c +++ b/tools/src/hdoc.c @@ -46,6 +46,12 @@ typedef struct DocTypedef char text[HEADER_EXPR_MAX]; } DocTypedef; +typedef struct DocGlobal +{ + char docs[HEADER_EXPR_MAX]; + HeaderGlobal global; +} DocGlobal; + static void ParseMainBlock(HashMap * registers, Array * descr, char *comment) { @@ -105,6 +111,9 @@ main(int argc, char **argv) Array *typedefs = ArrayCreate(); DocTypedef *type = NULL; + Array *globals = ArrayCreate(); + DocGlobal *global = NULL; + char comment[HEADER_EXPR_MAX]; int isDocumented = 0; @@ -287,6 +296,24 @@ main(int argc, char **argv) isDocumented = 0; } break; + case HP_GLOBAL: + if (!isDocumented) + { + StreamPrintf(StreamStderr(), + "Error: Global %s is undocumented.\n", + expr.data.global.name); + exit = EXIT_FAILURE; + goto finish; + } + else + { + global = Malloc(sizeof(DocGlobal)); + global->global = expr.data.global; + strncpy(global->docs, comment, sizeof(global->docs)); + ArrayAdd(globals, global); + isDocumented = 0; + } + break; default: StreamPrintf(StreamStderr(), "Unknown header type: %d\n", expr.type); StreamPrintf(StreamStderr(), "This is a programming error.\n"); @@ -352,6 +379,34 @@ last: StreamPutc(out, '\n'); } + if (ArraySize(globals)) + { + StreamPrintf(out, ".Sh GLOBALS\n"); + for (i = 0; i < ArraySize(globals); i++) + { + char *line; + global = ArrayGet(globals, i); + + StreamPrintf(out, ".Ss %s %s\n", global->global.type, global->global.name); + + line = strtok(global->docs, "\n"); + while (line) + { + while (*line && (isspace(*line) || *line == '*')) + { + line++; + } + + if (*line) + { + StreamPrintf(out, "%s\n", line); + } + + line = strtok(NULL, "\n"); + } + } + } + if (ArraySize(typedefs)) { StreamPrintf(out, ".Sh TYPE DECLARATIONS\n");