#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); } }