forked from Telodendria/Telodendria
Add hdoc, a simple tool for generating documentation from a C header.
This is a very early prototype. It works, but it is probably not efficient or reliable. However, the documentation format it parses is stable, so I will begin moving the documentation into the headers.
This commit is contained in:
parent
fdcf7ec065
commit
2447bb63cc
4 changed files with 982 additions and 1 deletions
4
TODO.txt
4
TODO.txt
|
@ -37,10 +37,12 @@ Milestone: v0.3.0
|
|||
[ ] HashMap
|
||||
[ ] HttpRouter
|
||||
[ ] Str
|
||||
[ ] HeaderParser
|
||||
[ ] hdoc
|
||||
[x] telodendria-admin
|
||||
[x] telodendria-setup
|
||||
[x] telodendria-config
|
||||
[ ] Refactor dev pages so function description and
|
||||
[~] Refactor dev pages so function description and
|
||||
return value are in the same location.
|
||||
|
||||
[x] Debug memory leaks with caching
|
||||
|
|
552
src/HeaderParser.c
Normal file
552
src/HeaderParser.c
Normal file
|
@ -0,0 +1,552 @@
|
|||
#include <HeaderParser.h>
|
||||
|
||||
#include <Memory.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
static int
|
||||
HeaderConsumeWhitespace(HeaderExpr * expr)
|
||||
{
|
||||
int c;
|
||||
|
||||
while (1)
|
||||
{
|
||||
c = StreamGetc(expr->state.stream);
|
||||
|
||||
if (StreamEof(expr->state.stream) || StreamError(expr->state.stream))
|
||||
{
|
||||
expr->type = HP_EOF;
|
||||
expr->data.error.msg = "End of stream reached.";
|
||||
expr->data.error.lineNo = expr->state.lineNo;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isspace(c))
|
||||
{
|
||||
if (c == '\n')
|
||||
{
|
||||
expr->state.lineNo++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static char *
|
||||
HeaderConsumeWord(HeaderExpr * expr)
|
||||
{
|
||||
char *str = Malloc(16 * sizeof(char));
|
||||
int len = 16;
|
||||
int i;
|
||||
int c;
|
||||
|
||||
if (!str)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
c = HeaderConsumeWhitespace(expr);
|
||||
|
||||
i = 0;
|
||||
str[i] = c;
|
||||
i++;
|
||||
|
||||
while (!isspace(c = StreamGetc(expr->state.stream)))
|
||||
{
|
||||
if (i >= len)
|
||||
{
|
||||
len *= 2;
|
||||
str = Realloc(str, len * sizeof(char));
|
||||
}
|
||||
|
||||
str[i] = c;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i >= len)
|
||||
{
|
||||
len++;
|
||||
str = Realloc(str, len * sizeof(char));
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
|
||||
if (c != EOF)
|
||||
{
|
||||
StreamUngetc(expr->state.stream, c);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *
|
||||
HeaderConsumeAlnum(HeaderExpr * expr)
|
||||
{
|
||||
char *str = Malloc(16 * sizeof(char));
|
||||
int len = 16;
|
||||
int i;
|
||||
int c;
|
||||
|
||||
if (!str)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
c = HeaderConsumeWhitespace(expr);
|
||||
|
||||
i = 0;
|
||||
str[i] = c;
|
||||
i++;
|
||||
|
||||
while (isalnum(c = StreamGetc(expr->state.stream)))
|
||||
{
|
||||
if (i >= len)
|
||||
{
|
||||
len *= 2;
|
||||
str = Realloc(str, len * sizeof(char));
|
||||
}
|
||||
|
||||
str[i] = c;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i >= len)
|
||||
{
|
||||
len++;
|
||||
str = Realloc(str, len * sizeof(char));
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
|
||||
if (c != EOF)
|
||||
{
|
||||
StreamUngetc(expr->state.stream, c);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *
|
||||
HeaderConsumeArg(HeaderExpr * expr)
|
||||
{
|
||||
char *str = Malloc(16 * sizeof(char));
|
||||
int len = 16;
|
||||
int i;
|
||||
int c;
|
||||
int block = 0;
|
||||
|
||||
if (!str)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
c = HeaderConsumeWhitespace(expr);
|
||||
|
||||
i = 0;
|
||||
str[i] = c;
|
||||
i++;
|
||||
|
||||
while (((c = StreamGetc(expr->state.stream)) != ',' && c != ')') || block > 0)
|
||||
{
|
||||
if (i >= len)
|
||||
{
|
||||
len *= 2;
|
||||
str = Realloc(str, len * sizeof(char));
|
||||
}
|
||||
|
||||
str[i] = c;
|
||||
i++;
|
||||
|
||||
if (c == '(')
|
||||
{
|
||||
block++;
|
||||
}
|
||||
else if (c == ')')
|
||||
{
|
||||
block--;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= len)
|
||||
{
|
||||
len++;
|
||||
str = Realloc(str, len * sizeof(char));
|
||||
}
|
||||
|
||||
str[i] = '\0';
|
||||
|
||||
if (c != EOF)
|
||||
{
|
||||
StreamUngetc(expr->state.stream, c);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void
|
||||
HeaderParse(Stream * stream, HeaderExpr * expr)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (!expr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stream)
|
||||
{
|
||||
expr->type = HP_PARSE_ERROR;
|
||||
expr->data.error.msg = "NULL pointer to stream.";
|
||||
expr->data.error.lineNo = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (expr->type == HP_DECLARATION && expr->data.declaration.args)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ArraySize(expr->data.declaration.args); i++)
|
||||
{
|
||||
Free(ArrayGet(expr->data.declaration.args, i));
|
||||
}
|
||||
|
||||
ArrayFree(expr->data.declaration.args);
|
||||
}
|
||||
|
||||
expr->state.stream = stream;
|
||||
if (!expr->state.lineNo)
|
||||
{
|
||||
expr->state.lineNo = 1;
|
||||
}
|
||||
|
||||
c = HeaderConsumeWhitespace(expr);
|
||||
|
||||
if (StreamEof(stream) || StreamError(stream))
|
||||
{
|
||||
expr->type = HP_EOF;
|
||||
expr->data.error.msg = "End of stream reached.";
|
||||
expr->data.error.lineNo = expr->state.lineNo;
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == '/')
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
c = StreamGetc(expr->state.stream);
|
||||
if (c != '*')
|
||||
{
|
||||
expr->type = HP_SYNTAX_ERROR;
|
||||
expr->data.error.msg = "Expected comment opening.";
|
||||
expr->data.error.lineNo = expr->state.lineNo;
|
||||
return;
|
||||
}
|
||||
|
||||
expr->type = HP_COMMENT;
|
||||
while (1)
|
||||
{
|
||||
if (i >= HEADER_EXPR_MAX - 1)
|
||||
{
|
||||
expr->type = HP_PARSE_ERROR;
|
||||
expr->data.error.msg = "Memory limit exceeded while parsing comment.";
|
||||
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 comment.";
|
||||
expr->data.error.lineNo = expr->state.lineNo;
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == '*')
|
||||
{
|
||||
c = StreamGetc(expr->state.stream);
|
||||
if (c == '/')
|
||||
{
|
||||
expr->data.text[i] = '\0';
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
expr->data.text[i] = '*';
|
||||
i++;
|
||||
expr->data.text[i] = c;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
expr->data.text[i] = c;
|
||||
i++;
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
expr->state.lineNo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (c == '#')
|
||||
{
|
||||
int i = 0;
|
||||
char *word;
|
||||
|
||||
expr->type = HP_PREPROCESSOR_DIRECTIVE;
|
||||
expr->data.text[i] = '#';
|
||||
i++;
|
||||
|
||||
word = HeaderConsumeWord(expr);
|
||||
|
||||
strncpy(expr->data.text + i, word, HEADER_EXPR_MAX - i - 1);
|
||||
i += strlen(word);
|
||||
|
||||
if (strcmp(word, "include") == 0 ||
|
||||
strcmp(word, "undef") == 0 ||
|
||||
strcmp(word, "ifdef") == 0 ||
|
||||
strcmp(word, "ifndef") == 0)
|
||||
{
|
||||
/* Read one more word */
|
||||
Free(word);
|
||||
word = HeaderConsumeWord(expr);
|
||||
|
||||
if (i + strlen(word) + 1 >= HEADER_EXPR_MAX)
|
||||
{
|
||||
expr->type = HP_PARSE_ERROR;
|
||||
expr->data.error.msg = "Memory limit reached parsing preprocessor directive.";
|
||||
expr->data.error.lineNo = expr->state.lineNo;
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(expr->data.text + i + 1, word, HEADER_EXPR_MAX - i - 1);
|
||||
expr->data.text[i] = ' ';
|
||||
}
|
||||
|
||||
Free(word);
|
||||
}
|
||||
else if (strcmp(word, "define") == 0 ||
|
||||
strcmp(word, "if") == 0 ||
|
||||
strcmp(word, "elif") == 0 ||
|
||||
strcmp(word, "error") == 0)
|
||||
{
|
||||
Free(word);
|
||||
expr->data.text[i] = ' ';
|
||||
i++;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (i >= HEADER_EXPR_MAX - 1)
|
||||
{
|
||||
expr->type = HP_PARSE_ERROR;
|
||||
expr->data.error.msg = "Memory limit reached parsing preprocessor directive.";
|
||||
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 preprocessor directive.";
|
||||
expr->data.error.lineNo = expr->state.lineNo;
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: Handle backslash escapes */
|
||||
if (c == '\n')
|
||||
{
|
||||
expr->data.text[i] = '\0';
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
expr->data.text[i] = c;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strcmp(word, "else") == 0 ||
|
||||
strcmp(word, "endif") == 0)
|
||||
{
|
||||
/* Read no more words, that's the whole directive */
|
||||
}
|
||||
else
|
||||
{
|
||||
Free(word);
|
||||
|
||||
expr->type = HP_SYNTAX_ERROR;
|
||||
expr->data.error.msg = "Unknown preprocessor directive.";
|
||||
expr->data.error.lineNo = expr->state.lineNo;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char *word;
|
||||
|
||||
StreamUngetc(expr->state.stream, c);
|
||||
word = HeaderConsumeWord(expr);
|
||||
|
||||
if (strcmp(word, "typedef") == 0)
|
||||
{
|
||||
int block = 0;
|
||||
int i = 0;
|
||||
|
||||
expr->type = HP_TYPEDEF;
|
||||
strncpy(expr->data.text, word, HEADER_EXPR_MAX - 1);
|
||||
i += strlen(word);
|
||||
expr->data.text[i] = ' ';
|
||||
i++;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (i >= HEADER_EXPR_MAX - 1)
|
||||
{
|
||||
expr->type = HP_PARSE_ERROR;
|
||||
expr->data.error.msg = "Memory limit exceeded while parsing typedef.";
|
||||
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 typedef.";
|
||||
expr->data.error.lineNo = expr->state.lineNo;
|
||||
return;
|
||||
}
|
||||
|
||||
expr->data.text[i] = c;
|
||||
i++;
|
||||
|
||||
if (c == '{')
|
||||
{
|
||||
block++;
|
||||
}
|
||||
else if (c == '}')
|
||||
{
|
||||
block--;
|
||||
}
|
||||
|
||||
if (block <= 0 && c == ';')
|
||||
{
|
||||
expr->data.text[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strcmp(word, "extern") == 0)
|
||||
{
|
||||
int wordLimit = sizeof(expr->data.declaration.returnType) - 8;
|
||||
int wordLen;
|
||||
|
||||
Free(word);
|
||||
|
||||
word = HeaderConsumeWord(expr);
|
||||
wordLen = strlen(word);
|
||||
if (wordLen > wordLimit)
|
||||
{
|
||||
expr->type = HP_PARSE_ERROR;
|
||||
expr->data.error.msg = "Return of declaration exceeds length limit.";
|
||||
expr->data.error.lineNo = expr->state.lineNo;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = wordLen;
|
||||
|
||||
expr->type = HP_DECLARATION;
|
||||
strncpy(expr->data.declaration.returnType, word, wordLimit);
|
||||
|
||||
Free(word);
|
||||
|
||||
c = HeaderConsumeWhitespace(expr);
|
||||
if (c == '*')
|
||||
{
|
||||
expr->data.declaration.returnType[i] = ' ';
|
||||
i++;
|
||||
expr->data.declaration.returnType[i] = '*';
|
||||
i++;
|
||||
while ((c = HeaderConsumeWhitespace(expr)) == '*')
|
||||
{
|
||||
expr->data.declaration.returnType[i] = c;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
StreamUngetc(expr->state.stream, c);
|
||||
word = HeaderConsumeAlnum(expr);
|
||||
|
||||
wordLen = strlen(word);
|
||||
wordLimit = sizeof(expr->data.declaration.name) - 1;
|
||||
|
||||
if (wordLen > wordLimit)
|
||||
{
|
||||
expr->type = HP_SYNTAX_ERROR;
|
||||
expr->data.error.msg = "Function name too long.";
|
||||
expr->data.error.lineNo = expr->state.lineNo;
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(expr->data.declaration.name, word, wordLimit);
|
||||
Free(word);
|
||||
word = NULL;
|
||||
|
||||
c = HeaderConsumeWhitespace(expr);
|
||||
if (c != '(')
|
||||
{
|
||||
expr->type = HP_SYNTAX_ERROR;
|
||||
expr->data.error.msg = "Expected '('";
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
expr->type = HP_SYNTAX_ERROR;
|
||||
expr->data.error.msg = "Expected comment, typedef, or extern.";
|
||||
expr->data.error.lineNo = expr->state.lineNo;
|
||||
}
|
||||
|
||||
Free(word);
|
||||
}
|
||||
}
|
75
src/include/HeaderParser.h
Normal file
75
src/include/HeaderParser.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TELODENDRIA_HEADERPARSER_H
|
||||
#define TELODENDRIA_HEADERPARSER_H
|
||||
|
||||
#include <Stream.h>
|
||||
#include <Array.h>
|
||||
|
||||
/* Here's a comment */
|
||||
#define HEADER_EXPR_MAX 4096
|
||||
|
||||
typedef enum HeaderExprType
|
||||
{
|
||||
HP_COMMENT,
|
||||
HP_PREPROCESSOR_DIRECTIVE,
|
||||
HP_TYPEDEF,
|
||||
HP_DECLARATION,
|
||||
HP_SYNTAX_ERROR,
|
||||
HP_PARSE_ERROR,
|
||||
HP_EOF
|
||||
} HeaderExprType;
|
||||
|
||||
typedef struct HeaderDeclaration
|
||||
{
|
||||
char returnType[64];
|
||||
char name[32]; /* Enforced by ANSI C */
|
||||
Array *args;
|
||||
} HeaderDeclaration;
|
||||
|
||||
typedef struct HeaderExpr
|
||||
{
|
||||
HeaderExprType type;
|
||||
union
|
||||
{
|
||||
char text[HEADER_EXPR_MAX];
|
||||
HeaderDeclaration declaration;
|
||||
struct
|
||||
{
|
||||
int lineNo;
|
||||
char *msg;
|
||||
} error;
|
||||
} data;
|
||||
|
||||
struct
|
||||
{
|
||||
Stream *stream;
|
||||
int lineNo;
|
||||
} state;
|
||||
} HeaderExpr;
|
||||
|
||||
extern void
|
||||
HeaderParse(Stream *, HeaderExpr *);
|
||||
|
||||
#endif /* TELODENDRIA_HEADERPARSER_H */
|
352
tools/src/hdoc.c
Normal file
352
tools/src/hdoc.c
Normal file
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <HeaderParser.h>
|
||||
|
||||
#include <HashMap.h>
|
||||
#include <Str.h>
|
||||
#include <Memory.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef struct DocDecl
|
||||
{
|
||||
char docs[HEADER_EXPR_MAX];
|
||||
HeaderDeclaration decl;
|
||||
} DocDecl;
|
||||
|
||||
typedef struct DocTypedef
|
||||
{
|
||||
char docs[HEADER_EXPR_MAX];
|
||||
char text[HEADER_EXPR_MAX];
|
||||
} DocTypedef;
|
||||
|
||||
static void
|
||||
ParseMainBlock(HashMap * registers, Array * descr, char *comment)
|
||||
{
|
||||
char *line = strtok(comment, "\n");
|
||||
|
||||
while (line)
|
||||
{
|
||||
while (*line && (isspace(*line) || *line == '*'))
|
||||
{
|
||||
line++;
|
||||
}
|
||||
|
||||
if (!*line)
|
||||
{
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (*line == '@')
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
line++;
|
||||
|
||||
while (!isspace(line[i]))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
line[i] = '\0';
|
||||
|
||||
Free(HashMapSet(registers, line, StrDuplicate(line + i + 1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrayAdd(descr, StrDuplicate(line));
|
||||
}
|
||||
|
||||
next:
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
HeaderExpr expr;
|
||||
size_t i;
|
||||
char *val;
|
||||
int exit = EXIT_SUCCESS;
|
||||
|
||||
HashMap *registers = HashMapCreate();
|
||||
Array *descr = ArrayCreate();
|
||||
|
||||
Array *declarations = ArrayCreate();
|
||||
DocDecl *decl = NULL;
|
||||
|
||||
Array *typedefs = ArrayCreate();
|
||||
DocTypedef *type = NULL;
|
||||
|
||||
char comment[HEADER_EXPR_MAX];
|
||||
int isDocumented = 0;
|
||||
|
||||
memset(&expr, 0, sizeof(expr));
|
||||
|
||||
while (1)
|
||||
{
|
||||
HeaderParse(StreamStdin(), &expr);
|
||||
|
||||
switch (expr.type)
|
||||
{
|
||||
case HP_PREPROCESSOR_DIRECTIVE:
|
||||
/* Ignore */
|
||||
break;
|
||||
case HP_EOF:
|
||||
/* Done parsing */
|
||||
goto last;
|
||||
case HP_PARSE_ERROR:
|
||||
case HP_SYNTAX_ERROR:
|
||||
StreamPrintf(StreamStderr(), "Parse Error: (line %d) %s\n",
|
||||
expr.data.error.lineNo, expr.data.error.msg);
|
||||
exit = EXIT_FAILURE;
|
||||
goto finish;
|
||||
case HP_COMMENT:
|
||||
if (expr.data.text[0] != '*')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (strncmp(expr.data.text, "**", 2) == 0)
|
||||
{
|
||||
ParseMainBlock(registers, descr, expr.data.text);
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(comment, expr.data.text, sizeof(comment));
|
||||
isDocumented = 1;
|
||||
}
|
||||
break;
|
||||
case HP_TYPEDEF:
|
||||
if (!isDocumented)
|
||||
{
|
||||
StreamPrintf(StreamStderr(),
|
||||
"Error: Undocumented typedef:\n%s\n",
|
||||
expr.data.text);
|
||||
exit = EXIT_FAILURE;
|
||||
goto finish;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = Malloc(sizeof(DocTypedef));
|
||||
strncpy(type->docs, comment, sizeof(type->docs));
|
||||
strncpy(type->text, expr.data.text, sizeof(type->text));
|
||||
ArrayAdd(typedefs, type);
|
||||
isDocumented = 0;
|
||||
}
|
||||
break;
|
||||
case HP_DECLARATION:
|
||||
if (!isDocumented)
|
||||
{
|
||||
StreamPrintf(StreamStderr(),
|
||||
"Error: %s() is undocumented.\n",
|
||||
expr.data.declaration.name);
|
||||
exit = EXIT_FAILURE;
|
||||
goto finish;
|
||||
}
|
||||
else
|
||||
{
|
||||
decl = Malloc(sizeof(DocDecl));
|
||||
decl->decl = expr.data.declaration;
|
||||
decl->decl.args = ArrayCreate();
|
||||
strncpy(decl->docs, comment, sizeof(decl->docs));
|
||||
for (i = 0; i < ArraySize(expr.data.declaration.args); i++)
|
||||
{
|
||||
ArrayAdd(decl->decl.args, StrDuplicate(ArrayGet(expr.data.declaration.args, i)));
|
||||
}
|
||||
ArrayAdd(declarations, decl);
|
||||
isDocumented = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
StreamPrintf(StreamStderr(), "Unknown header type: %d\n", expr.type);
|
||||
StreamPrintf(StreamStderr(), "This is a programming error.\n");
|
||||
exit = EXIT_FAILURE;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
last:
|
||||
val = HashMapGet(registers, "Nm");
|
||||
if (!val)
|
||||
{
|
||||
HashMapSet(registers, "Nm", StrDuplicate("Unnamed"));
|
||||
}
|
||||
|
||||
val = HashMapGet(registers, "Dd");
|
||||
if (!val)
|
||||
{
|
||||
time_t currentTime;
|
||||
struct tm *timeInfo;
|
||||
char tsBuf[1024];
|
||||
|
||||
currentTime = time(NULL);
|
||||
timeInfo = localtime(¤tTime);
|
||||
strftime(tsBuf, sizeof(tsBuf), "%B %d %Y", timeInfo);
|
||||
|
||||
val = tsBuf;
|
||||
}
|
||||
StreamPrintf(StreamStdout(), ".Dd $%s: %s $\n", "Mdocdate", val);
|
||||
|
||||
val = HashMapGet(registers, "Os");
|
||||
if (val)
|
||||
{
|
||||
StreamPrintf(StreamStdout(), ".Os %s\n", val);
|
||||
}
|
||||
|
||||
val = HashMapGet(registers, "Nm");
|
||||
StreamPrintf(StreamStdout(), ".Dt %s 3\n", val);
|
||||
StreamPrintf(StreamStdout(), ".Sh NAME\n");
|
||||
StreamPrintf(StreamStdout(), ".Nm %s\n", val);
|
||||
|
||||
val = HashMapGet(registers, "Nd");
|
||||
if (!val)
|
||||
{
|
||||
val = "No Description.";
|
||||
}
|
||||
StreamPrintf(StreamStdout(), ".Nd %s\n", val);
|
||||
|
||||
StreamPrintf(StreamStdout(), ".Sh SYNOPSIS\n");
|
||||
val = HashMapGet(registers, "Nm");
|
||||
StreamPrintf(StreamStdout(), ".In %s.h\n", val);
|
||||
for (i = 0; i < ArraySize(declarations); i++)
|
||||
{
|
||||
size_t j;
|
||||
|
||||
decl = ArrayGet(declarations, i);
|
||||
StreamPrintf(StreamStdout(), ".Ft %s\n", decl->decl.returnType);
|
||||
StreamPrintf(StreamStdout(), ".Fn %s ", decl->decl.name);
|
||||
for (j = 0; j < ArraySize(decl->decl.args); j++)
|
||||
{
|
||||
StreamPrintf(StreamStdout(), "\"%s\" ", ArrayGet(decl->decl.args, j));
|
||||
}
|
||||
StreamPutc(StreamStdout(), '\n');
|
||||
}
|
||||
|
||||
if (ArraySize(typedefs))
|
||||
{
|
||||
StreamPrintf(StreamStdout(), ".Sh TYPE DECLARATIONS\n");
|
||||
for (i = 0; i < ArraySize(typedefs); i++)
|
||||
{
|
||||
char *line;
|
||||
|
||||
type = ArrayGet(typedefs, i);
|
||||
StreamPrintf(StreamStdout(), ".Bd -literal -offset indent\n");
|
||||
StreamPrintf(StreamStdout(), "%s\n", type->text);
|
||||
StreamPrintf(StreamStdout(), ".Ed\n.Pp\n");
|
||||
|
||||
line = strtok(type->docs, "\n");
|
||||
while (line)
|
||||
{
|
||||
while (*line && (isspace(*line) || *line == '*'))
|
||||
{
|
||||
line++;
|
||||
}
|
||||
|
||||
if (*line)
|
||||
{
|
||||
StreamPrintf(StreamStdout(), "%s\n", line);
|
||||
}
|
||||
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StreamPrintf(StreamStdout(), ".Sh DESCRIPTION\n");
|
||||
for (i = 0; i < ArraySize(descr); i++)
|
||||
{
|
||||
StreamPrintf(StreamStdout(), "%s\n", ArrayGet(descr, i));
|
||||
}
|
||||
|
||||
for (i = 0; i < ArraySize(declarations); i++)
|
||||
{
|
||||
size_t j;
|
||||
char *line;
|
||||
|
||||
decl = ArrayGet(declarations, i);
|
||||
StreamPrintf(StreamStdout(), ".Ss %s %s(",
|
||||
decl->decl.returnType, decl->decl.name);
|
||||
for (j = 0; j < ArraySize(decl->decl.args); j++)
|
||||
{
|
||||
StreamPrintf(StreamStdout(), "%s", ArrayGet(decl->decl.args, j));
|
||||
if (j < ArraySize(decl->decl.args) - 1)
|
||||
{
|
||||
StreamPuts(StreamStdout(), ", ");
|
||||
}
|
||||
StreamPuts(StreamStdout(), ")\n");
|
||||
}
|
||||
|
||||
line = strtok(decl->docs, "\n");
|
||||
while (line)
|
||||
{
|
||||
while (*line && (isspace(*line) || *line == '*'))
|
||||
{
|
||||
line++;
|
||||
}
|
||||
|
||||
if (*line)
|
||||
{
|
||||
StreamPrintf(StreamStdout(), "%s\n", line);
|
||||
}
|
||||
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
val = HashMapGet(registers, "Xr");
|
||||
if (val)
|
||||
{
|
||||
char *xr = strtok(val, " ");
|
||||
|
||||
StreamPrintf(StreamStdout(), ".Sh SEE ALSO\n");
|
||||
while (xr)
|
||||
{
|
||||
if (*xr)
|
||||
{
|
||||
StreamPrintf(StreamStdout(), ".Xr %s 3 ", xr);
|
||||
}
|
||||
|
||||
xr = strtok(NULL, " ");
|
||||
|
||||
if (xr)
|
||||
{
|
||||
StreamPutc(StreamStdout(), ',');
|
||||
}
|
||||
StreamPutc(StreamStdout(), '\n');
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
StreamClose(StreamStdin());
|
||||
StreamClose(StreamStdout());
|
||||
StreamClose(StreamStderr());
|
||||
|
||||
MemoryFreeAll();
|
||||
return exit;
|
||||
}
|
Loading…
Reference in a new issue